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. |
26 | static 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 */ |
44 | static 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 | |
68 | template <typename T> |
69 | static 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 | |
101 | static 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 | |
137 | static 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 | |
196 | static 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 | |
225 | static 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 | |
248 | void 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 | |
272 | SkPDFIndirectReference 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 | |