blob: beaf08846dbbcef32d21851385d29ccfe78c2a20 [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "src/pdf/SkPDFBitmap.h"
Hal Canary4f29c202017-07-18 10:28:31 -04009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#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"
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 Canary2111d5f2019-01-02 09:44:41 -050068template <typename T>
69static 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) {
Hal Canarya1211832018-11-13 16:45:14 -050077 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);
Hal Canary2111d5f2019-01-02 09:44:41 -050082 if (sMask) {
83 pdfDict.insertRef("SMask", sMask);
Hal Canarya1211832018-11-13 16:45:14 -050084 }
85 pdfDict.insertInt("BitsPerComponent", 8);
Hal Canaryfde18752018-12-19 13:49:23 -050086 #ifdef SK_PDF_BASE85_BINARY
87 auto filters = SkPDFMakeArray();
88 filters->appendName("ASCII85Decode");
Hal Canary2111d5f2019-01-02 09:44:41 -050089 filters->appendName(isJpeg ? "DCTDecode" : "FlateDecode");
Hal Canaryfde18752018-12-19 13:49:23 -050090 pdfDict.insertObject("Filter", std::move(filters));
91 #else
Hal Canary2111d5f2019-01-02 09:44:41 -050092 pdfDict.insertName("Filter", isJpeg ? "DCTDecode" : "FlateDecode");
Hal Canaryfde18752018-12-19 13:49:23 -050093 #endif
Hal Canary2111d5f2019-01-02 09:44:41 -050094 if (isJpeg) {
95 pdfDict.insertInt("ColorTransform", 0);
96 }
Hal Canarya1211832018-11-13 16:45:14 -050097 pdfDict.insertInt("Length", length);
Hal Canary2111d5f2019-01-02 09:44:41 -050098 doc->emitStream(pdfDict, std::move(writeStream), ref);
halcanarydb0dcc72015-03-20 12:31:52 -070099}
100
Hal Canary2111d5f2019-01-02 09:44:41 -0500101static void do_deflated_alpha(const SkPixmap& pm, SkPDFDocument* doc, SkPDFIndirectReference ref) {
Hal Canarya1211832018-11-13 16:45:14 -0500102 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());
Brian Osmanab350ca2018-11-02 10:20:50 -0400107 } else {
Hal Canarya1211832018-11-13 16:45:14 -0500108 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();
Hal Canaryfde18752018-12-19 13:49:23 -0500127
128 #ifdef SK_PDF_BASE85_BINARY
Hal Canaryd104cc42018-12-20 16:15:17 -0500129 SkPDFUtils::Base85Encode(buffer.detachAsStream(), &buffer);
Hal Canaryfde18752018-12-19 13:49:23 -0500130 #endif
Hal Canary2111d5f2019-01-02 09:44:41 -0500131 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);
Hal Canarya1211832018-11-13 16:45:14 -0500135}
136
Hal Canary2111d5f2019-01-02 09:44:41 -0500137static void do_deflated_image(const SkPixmap& pm,
138 SkPDFDocument* doc,
139 bool isOpaque,
140 SkPDFIndirectReference ref) {
Hal Canarya1211832018-11-13 16:45:14 -0500141 SkPDFIndirectReference sMask;
142 if (!isOpaque) {
Hal Canary9a3f5542018-12-10 19:59:07 -0500143 sMask = doc->reserveRef();
Hal Canarya1211832018-11-13 16:45:14 -0500144 }
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) {
Brian Osmanab350ca2018-11-02 10:20:50 -0400169 SkColor color = *src++;
170 if (SkColorGetA(color) == SK_AlphaTRANSPARENT) {
Hal Canarya1211832018-11-13 16:45:14 -0500171 color = get_neighbor_avg_color(pm, x, y);
halcanarydb0dcc72015-03-20 12:31:52 -0700172 }
Brian Osmanab350ca2018-11-02 10:20:50 -0400173 *dst++ = SkColorGetR(color);
174 *dst++ = SkColorGetG(color);
175 *dst++ = SkColorGetB(color);
Hal Canarya1211832018-11-13 16:45:14 -0500176 if (dst == bufferStop) {
177 deflateWStream.write(byteBuffer, sizeof(byteBuffer));
178 dst = byteBuffer;
179 }
halcanarydb0dcc72015-03-20 12:31:52 -0700180 }
halcanary1b5c6042015-02-18 11:29:56 -0800181 }
Hal Canarya1211832018-11-13 16:45:14 -0500182 deflateWStream.write(byteBuffer, dst - byteBuffer);
halcanary1b5c6042015-02-18 11:29:56 -0800183 }
Hal Canarya1211832018-11-13 16:45:14 -0500184 deflateWStream.finalize();
Hal Canaryfde18752018-12-19 13:49:23 -0500185 #ifdef SK_PDF_BASE85_BINARY
Hal Canaryd104cc42018-12-20 16:15:17 -0500186 SkPDFUtils::Base85Encode(buffer.detachAsStream(), &buffer);
Hal Canaryfde18752018-12-19 13:49:23 -0500187 #endif
Hal Canary2111d5f2019-01-02 09:44:41 -0500188 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);
Hal Canarya1211832018-11-13 16:45:14 -0500191 if (!isOpaque) {
192 do_deflated_alpha(pm, doc, sMask);
193 }
halcanary1b5c6042015-02-18 11:29:56 -0800194}
195
Hal Canary2111d5f2019-01-02 09:44:41 -0500196static bool do_jpeg(sk_sp<SkData> data, SkPDFDocument* doc, SkISize size,
Hal Canary9a3f5542018-12-10 19:59:07 -0500197 SkPDFIndirectReference ref) {
Hal Canarya1211832018-11-13 16:45:14 -0500198 SkISize jpegSize;
199 SkEncodedInfo::Color jpegColorType;
200 SkEncodedOrigin exifOrientation;
Hal Canary2111d5f2019-01-02 09:44:41 -0500201 if (!SkGetJpegInfo(data->data(), data->size(), &jpegSize,
Hal Canarya1211832018-11-13 16:45:14 -0500202 &jpegColorType, &exifOrientation)) {
203 return false;
Brian Osmanab350ca2018-11-02 10:20:50 -0400204 }
Hal Canarya1211832018-11-13 16:45:14 -0500205 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;
halcanary7a14b312015-10-01 07:28:13 -0700211 }
Hal Canaryd104cc42018-12-20 16:15:17 -0500212 #ifdef SK_PDF_BASE85_BINARY
213 SkDynamicMemoryWStream buffer;
Hal Canary2111d5f2019-01-02 09:44:41 -0500214 SkPDFUtils::Base85Encode(SkMemoryStream::MakeDirect(data->data(), data->size()), &buffer);
215 data = buffer.detachAsData();
Hal Canaryd104cc42018-12-20 16:15:17 -0500216 #endif
Hal Canaryd104cc42018-12-20 16:15:17 -0500217
Hal Canary2111d5f2019-01-02 09:44:41 -0500218 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);
Hal Canarya1211832018-11-13 16:45:14 -0500222 return true;
halcanarya8448bc2015-04-17 13:27:24 -0700223}
halcanarya8448bc2015-04-17 13:27:24 -0700224
Hal Canarya1211832018-11-13 16:45:14 -0500225static 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));
halcanarya8448bc2015-04-17 13:27:24 -0700240 }
241 }
Hal Canarya1211832018-11-13 16:45:14 -0500242 if (!image->readPixels(bm.pixmap(), 0, 0)) {
243 bm.eraseColor(SkColorSetARGB(0xFF, 0, 0, 0));
244 }
245 return bm;
Hal Canary83e0f1b2018-04-05 16:58:41 -0400246}
247
Hal Canary9a3f5542018-12-10 19:59:07 -0500248void serialize_image(const SkImage* img,
249 int encodingQuality,
250 SkPDFDocument* doc,
251 SkPDFIndirectReference ref) {
Hal Canarya1211832018-11-13 16:45:14 -0500252 SkASSERT(img);
253 SkASSERT(doc);
Hal Canary83e0f1b2018-04-05 16:58:41 -0400254 SkASSERT(encodingQuality >= 0);
Hal Canarya1211832018-11-13 16:45:14 -0500255 SkISize dimensions = img->dimensions();
256 sk_sp<SkData> data = img->refEncodedData();
Hal Canary2111d5f2019-01-02 09:44:41 -0500257 if (data && do_jpeg(std::move(data), doc, dimensions, ref)) {
Hal Canary9a3f5542018-12-10 19:59:07 -0500258 return;
Hal Canary83e0f1b2018-04-05 16:58:41 -0400259 }
Hal Canarya1211832018-11-13 16:45:14 -0500260 SkBitmap bm = to_pixels(img);
261 SkPixmap pm = bm.pixmap();
262 bool isOpaque = pm.isOpaque() || pm.computeIsOpaque();
Mike Reeda4daf192017-12-14 13:25:04 -0500263 if (encodingQuality <= 100 && isOpaque) {
Hal Canarya1211832018-11-13 16:45:14 -0500264 sk_sp<SkData> data = img->encodeToData(SkEncodedImageFormat::kJPEG, encodingQuality);
Hal Canary2111d5f2019-01-02 09:44:41 -0500265 if (data && do_jpeg(std::move(data), doc, dimensions, ref)) {
Hal Canary9a3f5542018-12-10 19:59:07 -0500266 return;
Mike Reeda4daf192017-12-14 13:25:04 -0500267 }
268 }
Hal Canary9a3f5542018-12-10 19:59:07 -0500269 do_deflated_image(pm, doc, isOpaque, ref);
270}
271
272SkPDFIndirectReference 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);
Hal Canarydd9003a2018-12-21 13:44:31 -0500280 doc->incrementJobCount();
Hal Canary9a3f5542018-12-10 19:59:07 -0500281 executor->add([img, encodingQuality, doc, ref]() {
282 serialize_image(img, encodingQuality, doc, ref);
283 SkSafeUnref(img);
Hal Canarydd9003a2018-12-21 13:44:31 -0500284 doc->signalJobComplete();
Hal Canary9a3f5542018-12-10 19:59:07 -0500285 });
286 return ref;
287 }
288 serialize_image(img, encodingQuality, doc, ref);
289 return ref;
halcanary1b5c6042015-02-18 11:29:56 -0800290}