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
icc_channel_mismatch(const skcms_ICCProfile * iccProfile,int expectedChannels)210 bool icc_channel_mismatch(const skcms_ICCProfile* iccProfile, int expectedChannels) {
211 int iccChannels = -1;
212 if (iccProfile) {
213 iccChannels = skcms_GetInputChannelCount(iccProfile);
214 }
215 return 0 < iccChannels && expectedChannels != iccChannels;
216 }
217
do_deflated_image(const SkPixmap & pm,SkPDFDocument * doc,bool isOpaque,SkPDFIndirectReference ref)218 void do_deflated_image(const SkPixmap& pm,
219 SkPDFDocument* doc,
220 bool isOpaque,
221 SkPDFIndirectReference ref) {
222 SkPDFIndirectReference sMask;
223 if (!isOpaque) {
224 sMask = doc->reserveRef();
225 }
226 SkPDF::Metadata::CompressionLevel compressionLevel = doc->metadata().fCompressionLevel;
227 SkPDFStreamFormat format = compressionLevel == SkPDF::Metadata::CompressionLevel::None
228 ? SkPDFStreamFormat::Uncompressed
229 : SkPDFStreamFormat::Flate;
230 SkDynamicMemoryWStream buffer;
231 SkWStream* stream = &buffer;
232 std::optional<SkDeflateWStream> deflateWStream;
233 if (format == SkPDFStreamFormat::Flate) {
234 deflateWStream.emplace(&buffer, SkToInt(compressionLevel));
235 stream = &*deflateWStream;
236 }
237 SkPDFUnion colorSpace = SkPDFUnion::Name("DeviceGray");
238 int channels;
239 switch (pm.colorType()) {
240 case kAlpha_8_SkColorType:
241 channels = 1;
242 fill_stream(stream, '\x00', pm.width() * pm.height());
243 break;
244 case kGray_8_SkColorType:
245 channels = 1;
246 SkASSERT(sMask.fValue = -1);
247 SkASSERT(pm.rowBytes() == (size_t)pm.width());
248 stream->write(pm.addr8(), pm.width() * pm.height());
249 break;
250 default:
251 colorSpace = SkPDFUnion::Name("DeviceRGB");
252 channels = 3;
253 SkASSERT(pm.alphaType() == kUnpremul_SkAlphaType);
254 SkASSERT(pm.colorType() == kBGRA_8888_SkColorType);
255 SkASSERT(pm.rowBytes() == (size_t)pm.width() * 4);
256 uint8_t byteBuffer[3072];
257 static_assert(std::size(byteBuffer) % 3 == 0, "");
258 uint8_t* bufferStop = byteBuffer + std::size(byteBuffer);
259 uint8_t* dst = byteBuffer;
260 for (int y = 0; y < pm.height(); ++y) {
261 const SkColor* src = pm.addr32(0, y);
262 for (int x = 0; x < pm.width(); ++x) {
263 SkColor color = *src++;
264 if (SkColorGetA(color) == SK_AlphaTRANSPARENT) {
265 color = get_neighbor_avg_color(pm, x, y);
266 }
267 *dst++ = SkColorGetR(color);
268 *dst++ = SkColorGetG(color);
269 *dst++ = SkColorGetB(color);
270 if (dst == bufferStop) {
271 stream->write(byteBuffer, sizeof(byteBuffer));
272 dst = byteBuffer;
273 }
274 }
275 }
276 stream->write(byteBuffer, dst - byteBuffer);
277 }
278 if (deflateWStream) {
279 deflateWStream->finalize();
280 }
281
282 if (pm.colorSpace()) {
283 skcms_ICCProfile iccProfile;
284 pm.colorSpace()->toProfile(&iccProfile);
285 if (!icc_channel_mismatch(&iccProfile, channels)) {
286 sk_sp<SkData> iccData = SkWriteICCProfile(&iccProfile, "");
287 colorSpace = write_icc_profile(doc, std::move(iccData), channels);
288 }
289 }
290
291 #ifdef SK_PDF_BASE85_BINARY
292 SkPDFUtils::Base85Encode(buffer.detachAsStream(), &buffer);
293 #endif
294 int length = SkToInt(buffer.bytesWritten());
295 emit_image_stream(doc, ref, [&buffer](SkWStream* stream) { buffer.writeToAndReset(stream); },
296 pm.info().dimensions(), std::move(colorSpace), sMask, length, format);
297 if (!isOpaque) {
298 do_deflated_alpha(pm, doc, sMask);
299 }
300 }
301
do_jpeg(sk_sp<SkData> data,SkColorSpace * imageColorSpace,SkPDFDocument * doc,SkISize size,SkPDFIndirectReference ref)302 bool do_jpeg(sk_sp<SkData> data, SkColorSpace* imageColorSpace, SkPDFDocument* doc, SkISize size,
303 SkPDFIndirectReference ref) {
304 static constexpr const SkCodecs::Decoder decoders[] = {
305 SkJpegDecoder::Decoder(),
306 };
307 std::unique_ptr<SkCodec> codec = SkCodec::MakeFromData(data, decoders);
308 if (!codec) {
309 return false;
310 }
311
312 SkISize jpegSize = codec->dimensions();
313 const SkEncodedInfo& encodedInfo = SkPDFBitmap::GetEncodedInfo(*codec);
314 SkEncodedInfo::Color jpegColorType = encodedInfo.color();
315 SkEncodedOrigin exifOrientation = codec->getOrigin();
316
317 bool yuv = jpegColorType == SkEncodedInfo::kYUV_Color;
318 bool goodColorType = yuv || jpegColorType == SkEncodedInfo::kGray_Color;
319 if (jpegSize != size // Safety check.
320 || !goodColorType
321 || kTopLeft_SkEncodedOrigin != exifOrientation) {
322 return false;
323 }
324 #ifdef SK_PDF_BASE85_BINARY
325 SkDynamicMemoryWStream buffer;
326 SkPDFUtils::Base85Encode(SkMemoryStream::MakeDirect(data->data(), data->size()), &buffer);
327 data = buffer.detachAsData();
328 #endif
329
330 int channels = yuv ? 3 : 1;
331 SkPDFUnion colorSpace = yuv ? SkPDFUnion::Name("DeviceRGB") : SkPDFUnion::Name("DeviceGray");
332
333 if (sk_sp<SkData> encodedIccProfileData = encodedInfo.profileData();
334 encodedIccProfileData && !icc_channel_mismatch(encodedInfo.profile(), channels))
335 {
336 colorSpace = write_icc_profile(doc, std::move(encodedIccProfileData), channels);
337 } else if (const skcms_ICCProfile* codecIccProfile = codec->getICCProfile();
338 codecIccProfile && !icc_channel_mismatch(codecIccProfile, channels))
339 {
340 sk_sp<SkData> codecIccData = SkWriteICCProfile(codecIccProfile, "");
341 colorSpace = write_icc_profile(doc, std::move(codecIccData), channels);
342 } else if (imageColorSpace) {
343 skcms_ICCProfile imageIccProfile;
344 imageColorSpace->toProfile(&imageIccProfile);
345 if (!icc_channel_mismatch(&imageIccProfile, channels)) {
346 sk_sp<SkData> imageIccData = SkWriteICCProfile(&imageIccProfile, "");
347 colorSpace = write_icc_profile(doc, std::move(imageIccData), channels);
348 }
349 }
350
351 emit_image_stream(doc, ref,
352 [&data](SkWStream* dst) { dst->write(data->data(), data->size()); },
353 jpegSize, std::move(colorSpace),
354 SkPDFIndirectReference(), SkToInt(data->size()), SkPDFStreamFormat::DCT);
355 return true;
356 }
357
to_pixels(const SkImage * image)358 SkBitmap to_pixels(const SkImage* image) {
359 SkBitmap bm;
360 int w = image->width(),
361 h = image->height();
362 switch (image->colorType()) {
363 case kAlpha_8_SkColorType:
364 bm.allocPixels(SkImageInfo::MakeA8(w, h));
365 break;
366 case kGray_8_SkColorType:
367 bm.allocPixels(SkImageInfo::Make(w, h, kGray_8_SkColorType, kOpaque_SkAlphaType));
368 break;
369 default: {
370 // TODO: makeColorSpace(sRGB) or actually tag the images
371 SkAlphaType at = bm.isOpaque() ? kOpaque_SkAlphaType : kUnpremul_SkAlphaType;
372 bm.allocPixels(
373 SkImageInfo::Make(w, h, kBGRA_8888_SkColorType, at, image->refColorSpace()));
374 }
375 }
376 // TODO: support GPU images in PDFs
377 if (!image->readPixels(nullptr, bm.pixmap(), 0, 0)) {
378 bm.eraseColor(SkColorSetARGB(0xFF, 0, 0, 0));
379 }
380 return bm;
381 }
382
serialize_image(const SkImage * img,int encodingQuality,SkPDFDocument * doc,SkPDFIndirectReference ref)383 void serialize_image(const SkImage* img,
384 int encodingQuality,
385 SkPDFDocument* doc,
386 SkPDFIndirectReference ref) {
387 SkASSERT(img);
388 SkASSERT(doc);
389 SkASSERT(encodingQuality >= 0);
390 SkISize dimensions = img->dimensions();
391
392 if (sk_sp<SkData> data = img->refEncodedData()) {
393 if (do_jpeg(std::move(data), img->colorSpace(), doc, dimensions, ref)) {
394 return;
395 }
396 }
397 SkBitmap bm = to_pixels(img);
398 const SkPixmap& pm = bm.pixmap();
399 bool isOpaque = pm.isOpaque() || pm.computeIsOpaque();
400 if (encodingQuality <= 100 && isOpaque) {
401 SkJpegEncoder::Options jOpts;
402 jOpts.fQuality = encodingQuality;
403 SkDynamicMemoryWStream stream;
404 if (SkJpegEncoder::Encode(&stream, pm, jOpts)) {
405 if (do_jpeg(stream.detachAsData(), pm.colorSpace(), doc, dimensions, ref)) {
406 return;
407 }
408 }
409 }
410 do_deflated_image(pm, doc, isOpaque, ref);
411 }
412
413 } // namespace
414
SkPDFSerializeImage(const SkImage * img,SkPDFDocument * doc,int encodingQuality)415 SkPDFIndirectReference SkPDFSerializeImage(const SkImage* img,
416 SkPDFDocument* doc,
417 int encodingQuality) {
418 SkASSERT(img);
419 SkASSERT(doc);
420 SkPDFIndirectReference ref = doc->reserveRef();
421 if (SkExecutor* executor = doc->executor()) {
422 SkRef(img);
423 doc->incrementJobCount();
424 executor->add([img, encodingQuality, doc, ref]() {
425 serialize_image(img, encodingQuality, doc, ref);
426 SkSafeUnref(img);
427 doc->signalJobComplete();
428 });
429 return ref;
430 }
431 serialize_image(img, encodingQuality, doc, ref);
432 return ref;
433 }
434