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