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