• 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/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