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