blob: 4f7ae95e8e46436ce307f2de2c4e0d0ba9c45238 [file] [log] [blame]
halcanary1b5c6042015-02-18 11:29:56 -08001/*
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
Hal Canary4f29c202017-07-18 10:28:31 -04008#include "SkPDFBitmap.h"
9
Cary Clarka4083c92017-09-15 11:59:23 -040010#include "SkColorData.h"
halcanarya8448bc2015-04-17 13:27:24 -070011#include "SkData.h"
halcanaryd9e57152015-08-12 11:24:40 -070012#include "SkDeflate.h"
Hal Canary9a3f5542018-12-10 19:59:07 -050013#include "SkExecutor.h"
Hal Canary4f29c202017-07-18 10:28:31 -040014#include "SkImage.h"
Brian Osmanab350ca2018-11-02 10:20:50 -040015#include "SkImageInfoPriv.h"
halcanary96287f72015-05-07 11:46:59 -070016#include "SkJpegInfo.h"
Hal Canarya1211832018-11-13 16:45:14 -050017#include "SkPDFDocumentPriv.h"
martina.kollarovab8d6af12016-06-29 05:12:31 -070018#include "SkPDFTypes.h"
Hal Canary4f29c202017-07-18 10:28:31 -040019#include "SkPDFUtils.h"
halcanary1b5c6042015-02-18 11:29:56 -080020#include "SkStream.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040021#include "SkTo.h"
halcanary1b5c6042015-02-18 11:29:56 -080022
halcanarydb0dcc72015-03-20 12:31:52 -070023////////////////////////////////////////////////////////////////////////////////
halcanary1b5c6042015-02-18 11:29:56 -080024
halcanary1b5c6042015-02-18 11:29:56 -080025// write a single byte to a stream n times.
26static void fill_stream(SkWStream* out, char value, size_t n) {
27 char buffer[4096];
28 memset(buffer, value, sizeof(buffer));
halcanarydb0dcc72015-03-20 12:31:52 -070029 for (size_t i = 0; i < n / sizeof(buffer); ++i) {
30 out->write(buffer, sizeof(buffer));
halcanary1b5c6042015-02-18 11:29:56 -080031 }
halcanarydb0dcc72015-03-20 12:31:52 -070032 out->write(buffer, n % sizeof(buffer));
halcanary1b5c6042015-02-18 11:29:56 -080033}
34
halcanarydb0dcc72015-03-20 12:31:52 -070035/* 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 */
Hal Canarya1211832018-11-13 16:45:14 -050044static SkColor get_neighbor_avg_color(const SkPixmap& bm, int xOrig, int yOrig) {
Brian Osmanab350ca2018-11-02 10:20:50 -040045 SkASSERT(kBGRA_8888_SkColorType == bm.colorType());
46 unsigned r = 0, g = 0, b = 0, n = 0;
halcanarydb0dcc72015-03-20 12:31:52 -070047 // Clamp the range to the edge of the bitmap.
48 int ymin = SkTMax(0, yOrig - 1);
49 int ymax = SkTMin(yOrig + 1, bm.height() - 1);
50 int xmin = SkTMax(0, xOrig - 1);
51 int xmax = SkTMin(xOrig + 1, bm.width() - 1);
52 for (int y = ymin; y <= ymax; ++y) {
Hal Canarya1211832018-11-13 16:45:14 -050053 const SkColor* scanline = bm.addr32(0, y);
halcanarydb0dcc72015-03-20 12:31:52 -070054 for (int x = xmin; x <= xmax; ++x) {
Brian Osmanab350ca2018-11-02 10:20:50 -040055 SkColor color = scanline[x];
56 if (color != SK_ColorTRANSPARENT) {
57 r += SkColorGetR(color);
58 g += SkColorGetG(color);
59 b += SkColorGetB(color);
60 n++;
61 }
halcanary1b5c6042015-02-18 11:29:56 -080062 }
63 }
Brian Osmanab350ca2018-11-02 10:20:50 -040064 return n > 0 ? SkColorSetRGB(SkToU8(r / n), SkToU8(g / n), SkToU8(b / n))
65 : SK_ColorTRANSPARENT;
halcanary1b5c6042015-02-18 11:29:56 -080066}
67
Hal Canarya1211832018-11-13 16:45:14 -050068static void emit_stream(SkDynamicMemoryWStream* src, SkWStream* dst) {
69 dst->writeText(" stream\n");
70 src->writeToAndReset(dst);
71 dst->writeText("\nendstream");
halcanarydb0dcc72015-03-20 12:31:52 -070072}
73
Hal Canarya1211832018-11-13 16:45:14 -050074static void emit_dict(SkWStream* stream, SkISize size, const char* colorSpace,
75 const SkPDFIndirectReference* smask, int length) {
76 SkPDFDict pdfDict("XObject");
77 pdfDict.insertName("Subtype", "Image");
78 pdfDict.insertInt("Width", size.width());
79 pdfDict.insertInt("Height", size.height());
80 pdfDict.insertName("ColorSpace", colorSpace);
81 if (smask) {
82 pdfDict.insertRef("SMask", *smask);
83 }
84 pdfDict.insertInt("BitsPerComponent", 8);
Hal Canaryfde18752018-12-19 13:49:23 -050085 #ifdef SK_PDF_BASE85_BINARY
86 auto filters = SkPDFMakeArray();
87 filters->appendName("ASCII85Decode");
88 filters->appendName("FlateDecode");
89 pdfDict.insertObject("Filter", std::move(filters));
90 #else
Hal Canarya1211832018-11-13 16:45:14 -050091 pdfDict.insertName("Filter", "FlateDecode");
Hal Canaryfde18752018-12-19 13:49:23 -050092 #endif
Hal Canarya1211832018-11-13 16:45:14 -050093 pdfDict.insertInt("Length", length);
94 pdfDict.emitObject(stream);
halcanarydb0dcc72015-03-20 12:31:52 -070095}
96
Hal Canarya1211832018-11-13 16:45:14 -050097static SkPDFIndirectReference do_deflated_alpha(const SkPixmap& pm, SkPDFDocument* doc,
98 SkPDFIndirectReference ref) {
99 SkDynamicMemoryWStream buffer;
100 SkDeflateWStream deflateWStream(&buffer);
101 if (kAlpha_8_SkColorType == pm.colorType()) {
102 SkASSERT(pm.rowBytes() == (size_t)pm.width());
103 buffer.write(pm.addr8(), pm.width() * pm.height());
Brian Osmanab350ca2018-11-02 10:20:50 -0400104 } else {
Hal Canarya1211832018-11-13 16:45:14 -0500105 SkASSERT(pm.alphaType() == kUnpremul_SkAlphaType);
106 SkASSERT(pm.colorType() == kBGRA_8888_SkColorType);
107 SkASSERT(pm.rowBytes() == (size_t)pm.width() * 4);
108 const uint32_t* ptr = pm.addr32();
109 const uint32_t* stop = ptr + pm.height() * pm.width();
110
111 uint8_t byteBuffer[4092];
112 uint8_t* bufferStop = byteBuffer + SK_ARRAY_COUNT(byteBuffer);
113 uint8_t* dst = byteBuffer;
114 while (ptr != stop) {
115 *dst++ = 0xFF & ((*ptr++) >> SK_BGRA_A32_SHIFT);
116 if (dst == bufferStop) {
117 deflateWStream.write(byteBuffer, sizeof(byteBuffer));
118 dst = byteBuffer;
119 }
120 }
121 deflateWStream.write(byteBuffer, dst - byteBuffer);
122 }
123 deflateWStream.finalize();
Hal Canaryfde18752018-12-19 13:49:23 -0500124
125 #ifdef SK_PDF_BASE85_BINARY
Hal Canaryd104cc42018-12-20 16:15:17 -0500126 SkPDFUtils::Base85Encode(buffer.detachAsStream(), &buffer);
Hal Canaryfde18752018-12-19 13:49:23 -0500127 #endif
Hal Canarya1211832018-11-13 16:45:14 -0500128 SkWStream* stream = doc->beginObject(ref);
129 emit_dict(stream, pm.info().dimensions(), "DeviceGray", nullptr, buffer.bytesWritten());
130 emit_stream(&buffer, stream);
131 doc->endObject();
132 return ref;
133}
134
Hal Canary9a3f5542018-12-10 19:59:07 -0500135static void do_deflated_image(const SkPixmap& pm,
136 SkPDFDocument* doc,
137 bool isOpaque,
138 SkPDFIndirectReference ref) {
Hal Canarya1211832018-11-13 16:45:14 -0500139 SkPDFIndirectReference sMask;
140 if (!isOpaque) {
Hal Canary9a3f5542018-12-10 19:59:07 -0500141 sMask = doc->reserveRef();
Hal Canarya1211832018-11-13 16:45:14 -0500142 }
143 SkDynamicMemoryWStream buffer;
144 SkDeflateWStream deflateWStream(&buffer);
145 const char* colorSpace = "DeviceGray";
146 switch (pm.colorType()) {
147 case kAlpha_8_SkColorType:
148 fill_stream(&deflateWStream, '\x00', pm.width() * pm.height());
149 break;
150 case kGray_8_SkColorType:
151 SkASSERT(sMask.fValue = -1);
152 SkASSERT(pm.rowBytes() == (size_t)pm.width());
153 deflateWStream.write(pm.addr8(), pm.width() * pm.height());
154 break;
155 default:
156 colorSpace = "DeviceRGB";
157 SkASSERT(pm.alphaType() == kUnpremul_SkAlphaType);
158 SkASSERT(pm.colorType() == kBGRA_8888_SkColorType);
159 SkASSERT(pm.rowBytes() == (size_t)pm.width() * 4);
160 uint8_t byteBuffer[3072];
161 static_assert(SK_ARRAY_COUNT(byteBuffer) % 3 == 0, "");
162 uint8_t* bufferStop = byteBuffer + SK_ARRAY_COUNT(byteBuffer);
163 uint8_t* dst = byteBuffer;
164 for (int y = 0; y < pm.height(); ++y) {
165 const SkColor* src = pm.addr32(0, y);
166 for (int x = 0; x < pm.width(); ++x) {
Brian Osmanab350ca2018-11-02 10:20:50 -0400167 SkColor color = *src++;
168 if (SkColorGetA(color) == SK_AlphaTRANSPARENT) {
Hal Canarya1211832018-11-13 16:45:14 -0500169 color = get_neighbor_avg_color(pm, x, y);
halcanarydb0dcc72015-03-20 12:31:52 -0700170 }
Brian Osmanab350ca2018-11-02 10:20:50 -0400171 *dst++ = SkColorGetR(color);
172 *dst++ = SkColorGetG(color);
173 *dst++ = SkColorGetB(color);
Hal Canarya1211832018-11-13 16:45:14 -0500174 if (dst == bufferStop) {
175 deflateWStream.write(byteBuffer, sizeof(byteBuffer));
176 dst = byteBuffer;
177 }
halcanarydb0dcc72015-03-20 12:31:52 -0700178 }
halcanary1b5c6042015-02-18 11:29:56 -0800179 }
Hal Canarya1211832018-11-13 16:45:14 -0500180 deflateWStream.write(byteBuffer, dst - byteBuffer);
halcanary1b5c6042015-02-18 11:29:56 -0800181 }
Hal Canarya1211832018-11-13 16:45:14 -0500182 deflateWStream.finalize();
Hal Canaryfde18752018-12-19 13:49:23 -0500183 #ifdef SK_PDF_BASE85_BINARY
Hal Canaryd104cc42018-12-20 16:15:17 -0500184 SkPDFUtils::Base85Encode(buffer.detachAsStream(), &buffer);
Hal Canaryfde18752018-12-19 13:49:23 -0500185 #endif
Hal Canarya1211832018-11-13 16:45:14 -0500186 SkWStream* stream = doc->beginObject(ref);
187 emit_dict(stream, pm.info().dimensions(), colorSpace,
188 sMask.fValue != -1 ? &sMask : nullptr,
189 buffer.bytesWritten());
190 emit_stream(&buffer, stream);
191 doc->endObject();
192 if (!isOpaque) {
193 do_deflated_alpha(pm, doc, sMask);
194 }
halcanary1b5c6042015-02-18 11:29:56 -0800195}
196
Hal Canarya1211832018-11-13 16:45:14 -0500197static bool do_jpeg(const SkData& data, SkPDFDocument* doc, SkISize size,
Hal Canary9a3f5542018-12-10 19:59:07 -0500198 SkPDFIndirectReference ref) {
Hal Canarya1211832018-11-13 16:45:14 -0500199 SkISize jpegSize;
200 SkEncodedInfo::Color jpegColorType;
201 SkEncodedOrigin exifOrientation;
202 if (!SkGetJpegInfo(data.data(), data.size(), &jpegSize,
203 &jpegColorType, &exifOrientation)) {
204 return false;
Brian Osmanab350ca2018-11-02 10:20:50 -0400205 }
Hal Canarya1211832018-11-13 16:45:14 -0500206 bool yuv = jpegColorType == SkEncodedInfo::kYUV_Color;
207 bool goodColorType = yuv || jpegColorType == SkEncodedInfo::kGray_Color;
208 if (jpegSize != size // Sanity check.
209 || !goodColorType
210 || kTopLeft_SkEncodedOrigin != exifOrientation) {
211 return false;
halcanary7a14b312015-10-01 07:28:13 -0700212 }
Hal Canarya1211832018-11-13 16:45:14 -0500213 #ifdef SK_PDF_IMAGE_STATS
214 gJpegImageObjects.fetch_add(1);
215 #endif
halcanary37c46ca2015-03-31 12:30:20 -0700216
Hal Canaryd104cc42018-12-20 16:15:17 -0500217 #ifdef SK_PDF_BASE85_BINARY
218 SkDynamicMemoryWStream buffer;
219 SkPDFUtils::Base85Encode(SkMemoryStream::MakeDirect(data.data(), data.size()), &buffer);
220 int length = SkToInt(buffer.bytesWritten());
221 #else
222 int length = SkToInt(data.size());
223 #endif
halcanary1b5c6042015-02-18 11:29:56 -0800224 SkPDFDict pdfDict("XObject");
225 pdfDict.insertName("Subtype", "Image");
Hal Canarya1211832018-11-13 16:45:14 -0500226 pdfDict.insertInt("Width", jpegSize.width());
227 pdfDict.insertInt("Height", jpegSize.height());
228 if (yuv) {
halcanary96287f72015-05-07 11:46:59 -0700229 pdfDict.insertName("ColorSpace", "DeviceRGB");
230 } else {
231 pdfDict.insertName("ColorSpace", "DeviceGray");
232 }
halcanarya8448bc2015-04-17 13:27:24 -0700233 pdfDict.insertInt("BitsPerComponent", 8);
Hal Canaryd104cc42018-12-20 16:15:17 -0500234 #ifdef SK_PDF_BASE85_BINARY
235 auto filters = SkPDFMakeArray();
236 filters->appendName("ASCII85Decode");
237 filters->appendName("DCTDecode");
238 pdfDict.insertObject("Filter", std::move(filters));
239 #else
halcanarya8448bc2015-04-17 13:27:24 -0700240 pdfDict.insertName("Filter", "DCTDecode");
Hal Canaryd104cc42018-12-20 16:15:17 -0500241 #endif
halcanarya8448bc2015-04-17 13:27:24 -0700242 pdfDict.insertInt("ColorTransform", 0);
Hal Canaryd104cc42018-12-20 16:15:17 -0500243 pdfDict.insertInt("Length", length);
244
245 SkWStream* stream = doc->beginObject(ref);
Hal Canaryf6462c42018-11-13 16:19:59 -0500246 pdfDict.emitObject(stream);
Hal Canarya1211832018-11-13 16:45:14 -0500247 stream->writeText(" stream\n");
Hal Canaryd104cc42018-12-20 16:15:17 -0500248 #ifdef SK_PDF_BASE85_BINARY
249 buffer.writeToAndReset(stream);
250 #else
Hal Canarya1211832018-11-13 16:45:14 -0500251 stream->write(data.data(), data.size());
Hal Canaryd104cc42018-12-20 16:15:17 -0500252 #endif
Hal Canarya1211832018-11-13 16:45:14 -0500253 stream->writeText("\nendstream");
254 doc->endObject();
255 return true;
halcanarya8448bc2015-04-17 13:27:24 -0700256}
halcanarya8448bc2015-04-17 13:27:24 -0700257
Hal Canarya1211832018-11-13 16:45:14 -0500258static SkBitmap to_pixels(const SkImage* image) {
259 SkBitmap bm;
260 int w = image->width(),
261 h = image->height();
262 switch (image->colorType()) {
263 case kAlpha_8_SkColorType:
264 bm.allocPixels(SkImageInfo::MakeA8(w, h));
265 break;
266 case kGray_8_SkColorType:
267 bm.allocPixels(SkImageInfo::Make(w, h, kGray_8_SkColorType, kOpaque_SkAlphaType));
268 break;
269 default: {
270 // TODO: makeColorSpace(sRGB) or actually tag the images
271 SkAlphaType at = bm.isOpaque() ? kOpaque_SkAlphaType : kUnpremul_SkAlphaType;
272 bm.allocPixels(SkImageInfo::Make(w, h, kBGRA_8888_SkColorType, at));
halcanarya8448bc2015-04-17 13:27:24 -0700273 }
274 }
Hal Canarya1211832018-11-13 16:45:14 -0500275 if (!image->readPixels(bm.pixmap(), 0, 0)) {
276 bm.eraseColor(SkColorSetARGB(0xFF, 0, 0, 0));
277 }
278 return bm;
Hal Canary83e0f1b2018-04-05 16:58:41 -0400279}
280
Hal Canary9a3f5542018-12-10 19:59:07 -0500281void serialize_image(const SkImage* img,
282 int encodingQuality,
283 SkPDFDocument* doc,
284 SkPDFIndirectReference ref) {
Hal Canarya1211832018-11-13 16:45:14 -0500285 SkASSERT(img);
286 SkASSERT(doc);
Hal Canary83e0f1b2018-04-05 16:58:41 -0400287 SkASSERT(encodingQuality >= 0);
Hal Canarya1211832018-11-13 16:45:14 -0500288 SkISize dimensions = img->dimensions();
289 sk_sp<SkData> data = img->refEncodedData();
Hal Canary9a3f5542018-12-10 19:59:07 -0500290 if (data && do_jpeg(*data, doc, dimensions, ref)) {
291 return;
Hal Canary83e0f1b2018-04-05 16:58:41 -0400292 }
Hal Canarya1211832018-11-13 16:45:14 -0500293 SkBitmap bm = to_pixels(img);
294 SkPixmap pm = bm.pixmap();
295 bool isOpaque = pm.isOpaque() || pm.computeIsOpaque();
Mike Reeda4daf192017-12-14 13:25:04 -0500296 if (encodingQuality <= 100 && isOpaque) {
Hal Canarya1211832018-11-13 16:45:14 -0500297 sk_sp<SkData> data = img->encodeToData(SkEncodedImageFormat::kJPEG, encodingQuality);
Hal Canary9a3f5542018-12-10 19:59:07 -0500298 if (data && do_jpeg(*data, doc, dimensions, ref)) {
299 return;
Mike Reeda4daf192017-12-14 13:25:04 -0500300 }
301 }
Hal Canary9a3f5542018-12-10 19:59:07 -0500302 do_deflated_image(pm, doc, isOpaque, ref);
303}
304
305SkPDFIndirectReference SkPDFSerializeImage(const SkImage* img,
306 SkPDFDocument* doc,
307 int encodingQuality) {
308 SkASSERT(img);
309 SkASSERT(doc);
310 SkPDFIndirectReference ref = doc->reserveRef();
311 if (SkExecutor* executor = doc->executor()) {
312 SkRef(img);
Hal Canarydd9003a2018-12-21 13:44:31 -0500313 doc->incrementJobCount();
Hal Canary9a3f5542018-12-10 19:59:07 -0500314 executor->add([img, encodingQuality, doc, ref]() {
315 serialize_image(img, encodingQuality, doc, ref);
316 SkSafeUnref(img);
Hal Canarydd9003a2018-12-21 13:44:31 -0500317 doc->signalJobComplete();
Hal Canary9a3f5542018-12-10 19:59:07 -0500318 });
319 return ref;
320 }
321 serialize_image(img, encodingQuality, doc, ref);
322 return ref;
halcanary1b5c6042015-02-18 11:29:56 -0800323}