• 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/core/SkBitmap.h"
11 #include "include/core/SkData.h"
12 #include "include/core/SkEncodedImageFormat.h"
13 #include "include/core/SkExecutor.h"
14 #include "include/core/SkImage.h"
15 #include "include/core/SkStream.h"
16 #include "include/private/SkColorData.h"
17 #include "include/private/base/SkTo.h"
18 #include "src/core/SkImageInfoPriv.h"
19 #include "src/pdf/SkDeflate.h"
20 #include "src/pdf/SkJpegInfo.h"
21 #include "src/pdf/SkPDFDocumentPriv.h"
22 #include "src/pdf/SkPDFTypes.h"
23 #include "src/pdf/SkPDFUtils.h"
24 
25 ////////////////////////////////////////////////////////////////////////////////
26 
27 // write a single byte to a stream n times.
fill_stream(SkWStream * out,char value,size_t n)28 static void fill_stream(SkWStream* out, char value, size_t n) {
29     char buffer[4096];
30     memset(buffer, value, sizeof(buffer));
31     for (size_t i = 0; i < n / sizeof(buffer); ++i) {
32         out->write(buffer, sizeof(buffer));
33     }
34     out->write(buffer, n % sizeof(buffer));
35 }
36 
37 /* It is necessary to average the color component of transparent
38    pixels with their surrounding neighbors since the PDF renderer may
39    separately re-sample the alpha and color channels when the image is
40    not displayed at its native resolution. Since an alpha of zero
41    gives no information about the color component, the pathological
42    case is a white image with sharp transparency bounds - the color
43    channel goes to black, and the should-be-transparent pixels are
44    rendered as grey because of the separate soft mask and color
45    resizing. e.g.: gm/bitmappremul.cpp */
get_neighbor_avg_color(const SkPixmap & bm,int xOrig,int yOrig)46 static SkColor get_neighbor_avg_color(const SkPixmap& bm, int xOrig, int yOrig) {
47     SkASSERT(kBGRA_8888_SkColorType == bm.colorType());
48     unsigned r = 0, g = 0, b = 0, n = 0;
49     // Clamp the range to the edge of the bitmap.
50     int ymin = std::max(0, yOrig - 1);
51     int ymax = std::min(yOrig + 1, bm.height() - 1);
52     int xmin = std::max(0, xOrig - 1);
53     int xmax = std::min(xOrig + 1, bm.width() - 1);
54     for (int y = ymin; y <= ymax; ++y) {
55         const SkColor* scanline = bm.addr32(0, y);
56         for (int x = xmin; x <= xmax; ++x) {
57             SkColor color = scanline[x];
58             if (color != SK_ColorTRANSPARENT) {
59                 r += SkColorGetR(color);
60                 g += SkColorGetG(color);
61                 b += SkColorGetB(color);
62                 n++;
63             }
64         }
65     }
66     return n > 0 ? SkColorSetRGB(SkToU8(r / n), SkToU8(g / n), SkToU8(b / n))
67                  : SK_ColorTRANSPARENT;
68 }
69 
70 namespace {
71 enum class SkPDFStreamFormat { DCT, Flate, Uncompressed };
72 }
73 
74 template <typename T>
emit_image_stream(SkPDFDocument * doc,SkPDFIndirectReference ref,T writeStream,SkISize size,const char * colorSpace,SkPDFIndirectReference sMask,int length,SkPDFStreamFormat format)75 static void emit_image_stream(SkPDFDocument* doc,
76                               SkPDFIndirectReference ref,
77                               T writeStream,
78                               SkISize size,
79                               const char* colorSpace,
80                               SkPDFIndirectReference sMask,
81                               int length,
82                               SkPDFStreamFormat format) {
83     SkPDFDict pdfDict("XObject");
84     pdfDict.insertName("Subtype", "Image");
85     pdfDict.insertInt("Width", size.width());
86     pdfDict.insertInt("Height", size.height());
87     pdfDict.insertName("ColorSpace", colorSpace);
88     if (sMask) {
89         pdfDict.insertRef("SMask", sMask);
90     }
91     pdfDict.insertInt("BitsPerComponent", 8);
92     #ifdef SK_PDF_BASE85_BINARY
93     auto filters = SkPDFMakeArray();
94     filters->appendName("ASCII85Decode");
95     switch (format) {
96         case SkPDFStreamFormat::DCT: filters->appendName("DCTDecode"); break;
97         case SkPDFStreamFormat::Flate: filters->appendName("FlateDecode"); break;
98         case SkPDFStreamFormat::Uncompressed: break;
99     }
100     pdfDict.insertObject("Filter", std::move(filters));
101     #else
102     switch (format) {
103         case SkPDFStreamFormat::DCT: pdfDict.insertName("Filter", "DCTDecode"); break;
104         case SkPDFStreamFormat::Flate: pdfDict.insertName("Filter", "FlateDecode"); break;
105         case SkPDFStreamFormat::Uncompressed: break;
106     }
107     #endif
108     if (format == SkPDFStreamFormat::DCT) {
109         pdfDict.insertInt("ColorTransform", 0);
110     }
111     pdfDict.insertInt("Length", length);
112     doc->emitStream(pdfDict, std::move(writeStream), ref);
113 }
114 
do_deflated_alpha(const SkPixmap & pm,SkPDFDocument * doc,SkPDFIndirectReference ref)115 static void do_deflated_alpha(const SkPixmap& pm, SkPDFDocument* doc, SkPDFIndirectReference ref) {
116     SkPDF::Metadata::CompressionLevel compressionLevel = doc->metadata().fCompressionLevel;
117     SkPDFStreamFormat format = compressionLevel == SkPDF::Metadata::CompressionLevel::None
118                              ? SkPDFStreamFormat::Uncompressed
119                              : SkPDFStreamFormat::Flate;
120     SkDynamicMemoryWStream buffer;
121     SkWStream* stream = &buffer;
122     std::optional<SkDeflateWStream> deflateWStream;
123     if (format == SkPDFStreamFormat::Flate) {
124         deflateWStream.emplace(&buffer, SkToInt(compressionLevel));
125         stream = &*deflateWStream;
126     }
127     if (kAlpha_8_SkColorType == pm.colorType()) {
128         SkASSERT(pm.rowBytes() == (size_t)pm.width());
129         stream->write(pm.addr8(), pm.width() * pm.height());
130     } else {
131         SkASSERT(pm.alphaType() == kUnpremul_SkAlphaType);
132         SkASSERT(pm.colorType() == kBGRA_8888_SkColorType);
133         SkASSERT(pm.rowBytes() == (size_t)pm.width() * 4);
134         const uint32_t* ptr = pm.addr32();
135         const uint32_t* stop = ptr + pm.height() * pm.width();
136 
137         uint8_t byteBuffer[4092];
138         uint8_t* bufferStop = byteBuffer + std::size(byteBuffer);
139         uint8_t* dst = byteBuffer;
140         while (ptr != stop) {
141             *dst++ = 0xFF & ((*ptr++) >> SK_BGRA_A32_SHIFT);
142             if (dst == bufferStop) {
143                 stream->write(byteBuffer, sizeof(byteBuffer));
144                 dst = byteBuffer;
145             }
146         }
147         stream->write(byteBuffer, dst - byteBuffer);
148     }
149     if (deflateWStream) {
150         deflateWStream->finalize();
151     }
152 
153     #ifdef SK_PDF_BASE85_BINARY
154     SkPDFUtils::Base85Encode(buffer.detachAsStream(), &buffer);
155     #endif
156     int length = SkToInt(buffer.bytesWritten());
157     emit_image_stream(doc, ref, [&buffer](SkWStream* stream) { buffer.writeToAndReset(stream); },
158                       pm.info().dimensions(), "DeviceGray", SkPDFIndirectReference(),
159                       length, format);
160 }
161 
do_deflated_image(const SkPixmap & pm,SkPDFDocument * doc,bool isOpaque,SkPDFIndirectReference ref)162 static void do_deflated_image(const SkPixmap& pm,
163                               SkPDFDocument* doc,
164                               bool isOpaque,
165                               SkPDFIndirectReference ref) {
166     SkPDFIndirectReference sMask;
167     if (!isOpaque) {
168         sMask = doc->reserveRef();
169     }
170     SkPDF::Metadata::CompressionLevel compressionLevel = doc->metadata().fCompressionLevel;
171     SkPDFStreamFormat format = compressionLevel == SkPDF::Metadata::CompressionLevel::None
172                              ? SkPDFStreamFormat::Uncompressed
173                              : SkPDFStreamFormat::Flate;
174     SkDynamicMemoryWStream buffer;
175     SkWStream* stream = &buffer;
176     std::optional<SkDeflateWStream> deflateWStream;
177     if (format == SkPDFStreamFormat::Flate) {
178         deflateWStream.emplace(&buffer, SkToInt(compressionLevel));
179         stream = &*deflateWStream;
180     }
181     const char* colorSpace = "DeviceGray";
182     switch (pm.colorType()) {
183         case kAlpha_8_SkColorType:
184             fill_stream(stream, '\x00', pm.width() * pm.height());
185             break;
186         case kGray_8_SkColorType:
187             SkASSERT(sMask.fValue = -1);
188             SkASSERT(pm.rowBytes() == (size_t)pm.width());
189             stream->write(pm.addr8(), pm.width() * pm.height());
190             break;
191         default:
192             colorSpace = "DeviceRGB";
193             SkASSERT(pm.alphaType() == kUnpremul_SkAlphaType);
194             SkASSERT(pm.colorType() == kBGRA_8888_SkColorType);
195             SkASSERT(pm.rowBytes() == (size_t)pm.width() * 4);
196             uint8_t byteBuffer[3072];
197             static_assert(std::size(byteBuffer) % 3 == 0, "");
198             uint8_t* bufferStop = byteBuffer + std::size(byteBuffer);
199             uint8_t* dst = byteBuffer;
200             for (int y = 0; y < pm.height(); ++y) {
201                 const SkColor* src = pm.addr32(0, y);
202                 for (int x = 0; x < pm.width(); ++x) {
203                     SkColor color = *src++;
204                     if (SkColorGetA(color) == SK_AlphaTRANSPARENT) {
205                         color = get_neighbor_avg_color(pm, x, y);
206                     }
207                     *dst++ = SkColorGetR(color);
208                     *dst++ = SkColorGetG(color);
209                     *dst++ = SkColorGetB(color);
210                     if (dst == bufferStop) {
211                         stream->write(byteBuffer, sizeof(byteBuffer));
212                         dst = byteBuffer;
213                     }
214                 }
215             }
216             stream->write(byteBuffer, dst - byteBuffer);
217     }
218     if (deflateWStream) {
219         deflateWStream->finalize();
220     }
221     #ifdef SK_PDF_BASE85_BINARY
222     SkPDFUtils::Base85Encode(buffer.detachAsStream(), &buffer);
223     #endif
224     int length = SkToInt(buffer.bytesWritten());
225     emit_image_stream(doc, ref, [&buffer](SkWStream* stream) { buffer.writeToAndReset(stream); },
226                       pm.info().dimensions(), colorSpace, sMask, length, format);
227     if (!isOpaque) {
228         do_deflated_alpha(pm, doc, sMask);
229     }
230 }
231 
do_jpeg(sk_sp<SkData> data,SkPDFDocument * doc,SkISize size,SkPDFIndirectReference ref)232 static bool do_jpeg(sk_sp<SkData> data, SkPDFDocument* doc, SkISize size,
233                     SkPDFIndirectReference ref) {
234     SkISize jpegSize;
235     SkEncodedInfo::Color jpegColorType;
236     SkEncodedOrigin exifOrientation;
237     if (!SkGetJpegInfo(data->data(), data->size(), &jpegSize,
238                        &jpegColorType, &exifOrientation)) {
239         return false;
240     }
241     bool yuv = jpegColorType == SkEncodedInfo::kYUV_Color;
242     bool goodColorType = yuv || jpegColorType == SkEncodedInfo::kGray_Color;
243     if (jpegSize != size  // Safety check.
244             || !goodColorType
245             || kTopLeft_SkEncodedOrigin != exifOrientation) {
246         return false;
247     }
248     #ifdef SK_PDF_BASE85_BINARY
249     SkDynamicMemoryWStream buffer;
250     SkPDFUtils::Base85Encode(SkMemoryStream::MakeDirect(data->data(), data->size()), &buffer);
251     data = buffer.detachAsData();
252     #endif
253 
254     emit_image_stream(doc, ref,
255                       [&data](SkWStream* dst) { dst->write(data->data(), data->size()); },
256                       jpegSize, yuv ? "DeviceRGB" : "DeviceGray",
257                       SkPDFIndirectReference(), SkToInt(data->size()), SkPDFStreamFormat::DCT);
258     return true;
259 }
260 
to_pixels(const SkImage * image)261 static SkBitmap to_pixels(const SkImage* image) {
262     SkBitmap bm;
263     int w = image->width(),
264         h = image->height();
265     switch (image->colorType()) {
266         case kAlpha_8_SkColorType:
267             bm.allocPixels(SkImageInfo::MakeA8(w, h));
268             break;
269         case kGray_8_SkColorType:
270             bm.allocPixels(SkImageInfo::Make(w, h, kGray_8_SkColorType, kOpaque_SkAlphaType));
271             break;
272         default: {
273             // TODO: makeColorSpace(sRGB) or actually tag the images
274             SkAlphaType at = bm.isOpaque() ? kOpaque_SkAlphaType : kUnpremul_SkAlphaType;
275             bm.allocPixels(SkImageInfo::Make(w, h, kBGRA_8888_SkColorType, at));
276         }
277     }
278     // TODO: support GPU images in PDFs
279     if (!image->readPixels(nullptr, bm.pixmap(), 0, 0)) {
280         bm.eraseColor(SkColorSetARGB(0xFF, 0, 0, 0));
281     }
282     return bm;
283 }
284 
serialize_image(const SkImage * img,int encodingQuality,SkPDFDocument * doc,SkPDFIndirectReference ref)285 void serialize_image(const SkImage* img,
286                      int encodingQuality,
287                      SkPDFDocument* doc,
288                      SkPDFIndirectReference ref) {
289     SkASSERT(img);
290     SkASSERT(doc);
291     SkASSERT(encodingQuality >= 0);
292     SkISize dimensions = img->dimensions();
293     if (sk_sp<SkData> data = img->refEncodedData()) {
294         if (do_jpeg(std::move(data), doc, dimensions, ref)) {
295             return;
296         }
297     }
298     SkBitmap bm = to_pixels(img);
299     const SkPixmap& pm = bm.pixmap();
300     bool isOpaque = pm.isOpaque() || pm.computeIsOpaque();
301     if (encodingQuality <= 100 && isOpaque) {
302         if (sk_sp<SkData> data = img->encodeToData(SkEncodedImageFormat::kJPEG, encodingQuality)) {
303             if (do_jpeg(std::move(data), doc, dimensions, ref)) {
304                 return;
305             }
306         }
307     }
308     do_deflated_image(pm, doc, isOpaque, ref);
309 }
310 
SkPDFSerializeImage(const SkImage * img,SkPDFDocument * doc,int encodingQuality)311 SkPDFIndirectReference SkPDFSerializeImage(const SkImage* img,
312                                            SkPDFDocument* doc,
313                                            int encodingQuality) {
314     SkASSERT(img);
315     SkASSERT(doc);
316     SkPDFIndirectReference ref = doc->reserveRef();
317     if (SkExecutor* executor = doc->executor()) {
318         SkRef(img);
319         doc->incrementJobCount();
320         executor->add([img, encodingQuality, doc, ref]() {
321             serialize_image(img, encodingQuality, doc, ref);
322             SkSafeUnref(img);
323             doc->signalJobComplete();
324         });
325         return ref;
326     }
327     serialize_image(img, encodingQuality, doc, ref);
328     return ref;
329 }
330