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.
26static 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 */
44static 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
68template <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) {
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
101static 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
137static 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
196static 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
225static 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
248void 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
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);
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