• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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