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