1 /*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "src/pdf/SkPDFBitmap.h"
9
10 #include "include/codec/SkCodec.h"
11 #include "include/codec/SkEncodedOrigin.h"
12 #include "include/codec/SkJpegDecoder.h"
13 #include "include/core/SkAlphaType.h"
14 #include "include/core/SkBitmap.h"
15 #include "include/core/SkColor.h"
16 #include "include/core/SkColorPriv.h"
17 #include "include/core/SkColorSpace.h"
18 #include "include/core/SkColorType.h"
19 #include "include/core/SkData.h"
20 #include "include/core/SkExecutor.h"
21 #include "include/core/SkImage.h"
22 #include "include/core/SkImageInfo.h"
23 #include "include/core/SkPixmap.h"
24 #include "include/core/SkSize.h"
25 #include "include/core/SkStream.h"
26 #include "include/docs/SkPDFDocument.h"
27 #include "include/encode/SkICC.h"
28 #include "include/encode/SkJpegEncoder.h"
29 #include "include/private/SkEncodedInfo.h"
30 #include "include/private/base/SkAssert.h"
31 #include "include/private/base/SkMutex.h"
32 #include "include/private/base/SkTo.h"
33 #include "modules/skcms/skcms.h"
34 #include "src/core/SkTHash.h"
35 #include "src/pdf/SkDeflate.h"
36 #include "src/pdf/SkPDFDocumentPriv.h"
37 #include "src/pdf/SkPDFTypes.h"
38 #include "src/pdf/SkPDFUnion.h"
39
40 #include <algorithm>
41 #include <array>
42 #include <cstring>
43 #include <functional>
44 #include <memory>
45 #include <optional>
46 #include <utility>
47
GetEncodedInfo(SkCodec & codec)48 /*static*/ const SkEncodedInfo& SkPDFBitmap::GetEncodedInfo(SkCodec& codec) {
49 return codec.getEncodedInfo();
50 }
51
52 namespace {
53
54 // write a single byte to a stream n times.
fill_stream(SkWStream * out,char value,size_t n)55 void fill_stream(SkWStream* out, char value, size_t n) {
56 char buffer[4096];
57 memset(buffer, value, sizeof(buffer));
58 for (size_t i = 0; i < n / sizeof(buffer); ++i) {
59 out->write(buffer, sizeof(buffer));
60 }
61 out->write(buffer, n % sizeof(buffer));
62 }
63
64 /* It is necessary to average the color component of transparent
65 pixels with their surrounding neighbors since the PDF renderer may
66 separately re-sample the alpha and color channels when the image is
67 not displayed at its native resolution. Since an alpha of zero
68 gives no information about the color component, the pathological
69 case is a white image with sharp transparency bounds - the color
70 channel goes to black, and the should-be-transparent pixels are
71 rendered as grey because of the separate soft mask and color
72 resizing. e.g.: gm/bitmappremul.cpp */
get_neighbor_avg_color(const SkPixmap & bm,int xOrig,int yOrig)73 SkColor get_neighbor_avg_color(const SkPixmap& bm, int xOrig, int yOrig) {
74 SkASSERT(kBGRA_8888_SkColorType == bm.colorType());
75 unsigned r = 0, g = 0, b = 0, n = 0;
76 // Clamp the range to the edge of the bitmap.
77 int ymin = std::max(0, yOrig - 1);
78 int ymax = std::min(yOrig + 1, bm.height() - 1);
79 int xmin = std::max(0, xOrig - 1);
80 int xmax = std::min(xOrig + 1, bm.width() - 1);
81 for (int y = ymin; y <= ymax; ++y) {
82 const SkColor* scanline = bm.addr32(0, y);
83 for (int x = xmin; x <= xmax; ++x) {
84 SkColor color = scanline[x];
85 if (color != SK_ColorTRANSPARENT) {
86 r += SkColorGetR(color);
87 g += SkColorGetG(color);
88 b += SkColorGetB(color);
89 n++;
90 }
91 }
92 }
93 return n > 0 ? SkColorSetRGB(SkToU8(r / n), SkToU8(g / n), SkToU8(b / n))
94 : SK_ColorTRANSPARENT;
95 }
96
97 enum class SkPDFStreamFormat { DCT, Flate, Uncompressed };
98
99 template <typename T>
emit_image_stream(SkPDFDocument * doc,SkPDFIndirectReference ref,T writeStream,SkISize size,SkPDFUnion && colorSpace,SkPDFIndirectReference sMask,int length,SkPDFStreamFormat format)100 void emit_image_stream(SkPDFDocument* doc,
101 SkPDFIndirectReference ref,
102 T writeStream,
103 SkISize size,
104 SkPDFUnion&& colorSpace,
105 SkPDFIndirectReference sMask,
106 int length,
107 SkPDFStreamFormat format) {
108 SkPDFDict pdfDict("XObject");
109 pdfDict.insertName("Subtype", "Image");
110 pdfDict.insertInt("Width", size.width());
111 pdfDict.insertInt("Height", size.height());
112 pdfDict.insertUnion("ColorSpace", std::move(colorSpace));
113 if (sMask) {
114 pdfDict.insertRef("SMask", sMask);
115 }
116 pdfDict.insertInt("BitsPerComponent", 8);
117 #ifdef SK_PDF_BASE85_BINARY
118 auto filters = SkPDFMakeArray();
119 filters->appendName("ASCII85Decode");
120 switch (format) {
121 case SkPDFStreamFormat::DCT: filters->appendName("DCTDecode"); break;
122 case SkPDFStreamFormat::Flate: filters->appendName("FlateDecode"); break;
123 case SkPDFStreamFormat::Uncompressed: break;
124 }
125 pdfDict.insertObject("Filter", std::move(filters));
126 #else
127 switch (format) {
128 case SkPDFStreamFormat::DCT: pdfDict.insertName("Filter", "DCTDecode"); break;
129 case SkPDFStreamFormat::Flate: pdfDict.insertName("Filter", "FlateDecode"); break;
130 case SkPDFStreamFormat::Uncompressed: break;
131 }
132 #endif
133 if (format == SkPDFStreamFormat::DCT) {
134 pdfDict.insertInt("ColorTransform", 0);
135 }
136 pdfDict.insertInt("Length", length);
137 doc->emitStream(pdfDict, std::move(writeStream), ref);
138 }
139
do_deflated_alpha(const SkPixmap & pm,SkPDFDocument * doc,SkPDFIndirectReference ref)140 void do_deflated_alpha(const SkPixmap& pm, SkPDFDocument* doc, SkPDFIndirectReference ref) {
141 SkPDF::Metadata::CompressionLevel compressionLevel = doc->metadata().fCompressionLevel;
142 SkPDFStreamFormat format = compressionLevel == SkPDF::Metadata::CompressionLevel::None
143 ? SkPDFStreamFormat::Uncompressed
144 : SkPDFStreamFormat::Flate;
145 SkDynamicMemoryWStream buffer;
146 SkWStream* stream = &buffer;
147 std::optional<SkDeflateWStream> deflateWStream;
148 if (format == SkPDFStreamFormat::Flate) {
149 deflateWStream.emplace(&buffer, SkToInt(compressionLevel));
150 stream = &*deflateWStream;
151 }
152 if (kAlpha_8_SkColorType == pm.colorType()) {
153 SkASSERT(pm.rowBytes() == (size_t)pm.width());
154 stream->write(pm.addr8(), pm.width() * pm.height());
155 } else {
156 SkASSERT(pm.alphaType() == kUnpremul_SkAlphaType);
157 SkASSERT(pm.colorType() == kBGRA_8888_SkColorType);
158 SkASSERT(pm.rowBytes() == (size_t)pm.width() * 4);
159 const uint32_t* ptr = pm.addr32();
160 const uint32_t* stop = ptr + pm.height() * pm.width();
161
162 uint8_t byteBuffer[4092];
163 uint8_t* bufferStop = byteBuffer + std::size(byteBuffer);
164 uint8_t* dst = byteBuffer;
165 while (ptr != stop) {
166 *dst++ = 0xFF & ((*ptr++) >> SK_BGRA_A32_SHIFT);
167 if (dst == bufferStop) {
168 stream->write(byteBuffer, sizeof(byteBuffer));
169 dst = byteBuffer;
170 }
171 }
172 stream->write(byteBuffer, dst - byteBuffer);
173 }
174 if (deflateWStream) {
175 deflateWStream->finalize();
176 }
177
178 #ifdef SK_PDF_BASE85_BINARY
179 SkPDFUtils::Base85Encode(buffer.detachAsStream(), &buffer);
180 #endif
181 int length = SkToInt(buffer.bytesWritten());
182 emit_image_stream(doc, ref, [&buffer](SkWStream* stream) { buffer.writeToAndReset(stream); },
183 pm.info().dimensions(), SkPDFUnion::Name("DeviceGray"),
184 SkPDFIndirectReference(), length, format);
185 }
186
write_icc_profile(SkPDFDocument * doc,sk_sp<SkData> && icc,int channels)187 SkPDFUnion write_icc_profile(SkPDFDocument* doc, sk_sp<SkData>&& icc, int channels) {
188 SkPDFIndirectReference iccStreamRef;
189 {
190 static SkMutex iccProfileMapMutex;
191 SkAutoMutexExclusive lock(iccProfileMapMutex);
192
193 SkPDFIndirectReference* ref = doc->fICCProfileMap.find(SkPDFIccProfileKey{icc, channels});
194 if (ref) {
195 iccStreamRef = *ref;
196 } else {
197 std::unique_ptr<SkPDFDict> iccStreamDict = SkPDFMakeDict();
198 iccStreamDict->insertInt("N", channels);
199 iccStreamRef = SkPDFStreamOut(std::move(iccStreamDict), SkMemoryStream::Make(icc), doc);
200 doc->fICCProfileMap.set(SkPDFIccProfileKey{icc, channels}, iccStreamRef);
201 }
202 }
203
204 std::unique_ptr<SkPDFArray> iccPDF = SkPDFMakeArray();
205 iccPDF->appendName("ICCBased");
206 iccPDF->appendRef(iccStreamRef);
207 return SkPDFUnion::Object(std::move(iccPDF));
208 }
209
do_deflated_image(const SkPixmap & pm,SkPDFDocument * doc,bool isOpaque,SkPDFIndirectReference ref)210 void do_deflated_image(const SkPixmap& pm,
211 SkPDFDocument* doc,
212 bool isOpaque,
213 SkPDFIndirectReference ref) {
214 SkPDFIndirectReference sMask;
215 if (!isOpaque) {
216 sMask = doc->reserveRef();
217 }
218 SkPDF::Metadata::CompressionLevel compressionLevel = doc->metadata().fCompressionLevel;
219 SkPDFStreamFormat format = compressionLevel == SkPDF::Metadata::CompressionLevel::None
220 ? SkPDFStreamFormat::Uncompressed
221 : SkPDFStreamFormat::Flate;
222 SkDynamicMemoryWStream buffer;
223 SkWStream* stream = &buffer;
224 std::optional<SkDeflateWStream> deflateWStream;
225 if (format == SkPDFStreamFormat::Flate) {
226 deflateWStream.emplace(&buffer, SkToInt(compressionLevel));
227 stream = &*deflateWStream;
228 }
229 SkPDFUnion colorSpace = SkPDFUnion::Name("DeviceGray");
230 int channels;
231 switch (pm.colorType()) {
232 case kAlpha_8_SkColorType:
233 channels = 1;
234 fill_stream(stream, '\x00', pm.width() * pm.height());
235 break;
236 case kGray_8_SkColorType:
237 channels = 1;
238 SkASSERT(sMask.fValue = -1);
239 SkASSERT(pm.rowBytes() == (size_t)pm.width());
240 stream->write(pm.addr8(), pm.width() * pm.height());
241 break;
242 default:
243 colorSpace = SkPDFUnion::Name("DeviceRGB");
244 channels = 3;
245 SkASSERT(pm.alphaType() == kUnpremul_SkAlphaType);
246 SkASSERT(pm.colorType() == kBGRA_8888_SkColorType);
247 SkASSERT(pm.rowBytes() == (size_t)pm.width() * 4);
248 uint8_t byteBuffer[3072];
249 static_assert(std::size(byteBuffer) % 3 == 0, "");
250 uint8_t* bufferStop = byteBuffer + std::size(byteBuffer);
251 uint8_t* dst = byteBuffer;
252 for (int y = 0; y < pm.height(); ++y) {
253 const SkColor* src = pm.addr32(0, y);
254 for (int x = 0; x < pm.width(); ++x) {
255 SkColor color = *src++;
256 if (SkColorGetA(color) == SK_AlphaTRANSPARENT) {
257 color = get_neighbor_avg_color(pm, x, y);
258 }
259 *dst++ = SkColorGetR(color);
260 *dst++ = SkColorGetG(color);
261 *dst++ = SkColorGetB(color);
262 if (dst == bufferStop) {
263 stream->write(byteBuffer, sizeof(byteBuffer));
264 dst = byteBuffer;
265 }
266 }
267 }
268 stream->write(byteBuffer, dst - byteBuffer);
269 }
270 if (deflateWStream) {
271 deflateWStream->finalize();
272 }
273
274 if (pm.colorSpace() && channels != 1) {
275 skcms_ICCProfile iccProfile;
276 pm.colorSpace()->toProfile(&iccProfile);
277 sk_sp<SkData> iccData = SkWriteICCProfile(&iccProfile, "");
278 colorSpace = write_icc_profile(doc, std::move(iccData), channels);
279 }
280
281 #ifdef SK_PDF_BASE85_BINARY
282 SkPDFUtils::Base85Encode(buffer.detachAsStream(), &buffer);
283 #endif
284 int length = SkToInt(buffer.bytesWritten());
285 emit_image_stream(doc, ref, [&buffer](SkWStream* stream) { buffer.writeToAndReset(stream); },
286 pm.info().dimensions(), std::move(colorSpace), sMask, length, format);
287 if (!isOpaque) {
288 do_deflated_alpha(pm, doc, sMask);
289 }
290 }
291
do_jpeg(sk_sp<SkData> data,SkColorSpace * imageColorSpace,SkPDFDocument * doc,SkISize size,SkPDFIndirectReference ref)292 bool do_jpeg(sk_sp<SkData> data, SkColorSpace* imageColorSpace, SkPDFDocument* doc, SkISize size,
293 SkPDFIndirectReference ref) {
294 static constexpr const SkCodecs::Decoder decoders[] = {
295 SkJpegDecoder::Decoder(),
296 };
297 std::unique_ptr<SkCodec> codec = SkCodec::MakeFromData(data, decoders);
298 if (!codec) {
299 return false;
300 }
301
302 SkISize jpegSize = codec->dimensions();
303 const SkEncodedInfo& encodedInfo = SkPDFBitmap::GetEncodedInfo(*codec);
304 SkEncodedInfo::Color jpegColorType = encodedInfo.color();
305 SkEncodedOrigin exifOrientation = codec->getOrigin();
306
307 bool yuv = jpegColorType == SkEncodedInfo::kYUV_Color;
308 bool goodColorType = yuv || jpegColorType == SkEncodedInfo::kGray_Color;
309 if (jpegSize != size // Safety check.
310 || !goodColorType
311 || kTopLeft_SkEncodedOrigin != exifOrientation) {
312 return false;
313 }
314 #ifdef SK_PDF_BASE85_BINARY
315 SkDynamicMemoryWStream buffer;
316 SkPDFUtils::Base85Encode(SkMemoryStream::MakeDirect(data->data(), data->size()), &buffer);
317 data = buffer.detachAsData();
318 #endif
319
320 int channels = yuv ? 3 : 1;
321 SkPDFUnion colorSpace = yuv ? SkPDFUnion::Name("DeviceRGB") : SkPDFUnion::Name("DeviceGray");
322 if (sk_sp<SkData> encodedIccProfileData = encodedInfo.profileData()) {
323 colorSpace = write_icc_profile(doc, std::move(encodedIccProfileData), channels);
324 } else if (const skcms_ICCProfile* codecIccProfile = codec->getICCProfile()) {
325 sk_sp<SkData> codecIccData = SkWriteICCProfile(codecIccProfile, "");
326 colorSpace = write_icc_profile(doc, std::move(codecIccData), channels);
327 } else if (imageColorSpace && channels != 1) {
328 skcms_ICCProfile imageIccProfile;
329 imageColorSpace->toProfile(&imageIccProfile);
330 sk_sp<SkData> imageIccData = SkWriteICCProfile(&imageIccProfile, "");
331 colorSpace = write_icc_profile(doc, std::move(imageIccData), channels);
332 }
333
334 emit_image_stream(doc, ref,
335 [&data](SkWStream* dst) { dst->write(data->data(), data->size()); },
336 jpegSize, std::move(colorSpace),
337 SkPDFIndirectReference(), SkToInt(data->size()), SkPDFStreamFormat::DCT);
338 return true;
339 }
340
to_pixels(const SkImage * image)341 SkBitmap to_pixels(const SkImage* image) {
342 SkBitmap bm;
343 int w = image->width(),
344 h = image->height();
345 switch (image->colorType()) {
346 case kAlpha_8_SkColorType:
347 bm.allocPixels(SkImageInfo::MakeA8(w, h));
348 break;
349 case kGray_8_SkColorType:
350 bm.allocPixels(SkImageInfo::Make(w, h, kGray_8_SkColorType, kOpaque_SkAlphaType));
351 break;
352 default: {
353 // TODO: makeColorSpace(sRGB) or actually tag the images
354 SkAlphaType at = bm.isOpaque() ? kOpaque_SkAlphaType : kUnpremul_SkAlphaType;
355 bm.allocPixels(
356 SkImageInfo::Make(w, h, kBGRA_8888_SkColorType, at, image->refColorSpace()));
357 }
358 }
359 // TODO: support GPU images in PDFs
360 if (!image->readPixels(nullptr, bm.pixmap(), 0, 0)) {
361 bm.eraseColor(SkColorSetARGB(0xFF, 0, 0, 0));
362 }
363 return bm;
364 }
365
serialize_image(const SkImage * img,int encodingQuality,SkPDFDocument * doc,SkPDFIndirectReference ref)366 void serialize_image(const SkImage* img,
367 int encodingQuality,
368 SkPDFDocument* doc,
369 SkPDFIndirectReference ref) {
370 SkASSERT(img);
371 SkASSERT(doc);
372 SkASSERT(encodingQuality >= 0);
373 SkISize dimensions = img->dimensions();
374
375 if (sk_sp<SkData> data = img->refEncodedData()) {
376 if (do_jpeg(std::move(data), img->colorSpace(), doc, dimensions, ref)) {
377 return;
378 }
379 }
380 SkBitmap bm = to_pixels(img);
381 const SkPixmap& pm = bm.pixmap();
382 bool isOpaque = pm.isOpaque() || pm.computeIsOpaque();
383 if (encodingQuality <= 100 && isOpaque) {
384 SkJpegEncoder::Options jOpts;
385 jOpts.fQuality = encodingQuality;
386 SkDynamicMemoryWStream stream;
387 if (SkJpegEncoder::Encode(&stream, pm, jOpts)) {
388 if (do_jpeg(stream.detachAsData(), pm.colorSpace(), doc, dimensions, ref)) {
389 return;
390 }
391 }
392 }
393 do_deflated_image(pm, doc, isOpaque, ref);
394 }
395
396 } // namespace
397
SkPDFSerializeImage(const SkImage * img,SkPDFDocument * doc,int encodingQuality)398 SkPDFIndirectReference SkPDFSerializeImage(const SkImage* img,
399 SkPDFDocument* doc,
400 int encodingQuality) {
401 SkASSERT(img);
402 SkASSERT(doc);
403 SkPDFIndirectReference ref = doc->reserveRef();
404 if (SkExecutor* executor = doc->executor()) {
405 SkRef(img);
406 doc->incrementJobCount();
407 executor->add([img, encodingQuality, doc, ref]() {
408 serialize_image(img, encodingQuality, doc, ref);
409 SkSafeUnref(img);
410 doc->signalJobComplete();
411 });
412 return ref;
413 }
414 serialize_image(img, encodingQuality, doc, ref);
415 return ref;
416 }
417