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