blob: 887bca4ab0f427b5e845e821d64f19099ca59ec6 [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
8#include "SkColorPriv.h"
halcanarya8448bc2015-04-17 13:27:24 -07009#include "SkData.h"
halcanaryd9e57152015-08-12 11:24:40 -070010#include "SkDeflate.h"
halcanary7a14b312015-10-01 07:28:13 -070011#include "SkImage_Base.h"
halcanary96287f72015-05-07 11:46:59 -070012#include "SkJpegInfo.h"
halcanary1b5c6042015-02-18 11:29:56 -080013#include "SkPDFBitmap.h"
14#include "SkPDFCanon.h"
martina.kollarovab8d6af12016-06-29 05:12:31 -070015#include "SkPDFTypes.h"
halcanary1b5c6042015-02-18 11:29:56 -080016#include "SkStream.h"
17#include "SkUnPreMultiply.h"
18
halcanary7a14b312015-10-01 07:28:13 -070019void image_get_ro_pixels(const SkImage* image, SkBitmap* dst) {
20 if(as_IB(image)->getROPixels(dst)
21 && dst->dimensions() == image->dimensions()) {
halcanary80ec3db2015-11-10 09:48:40 -080022 if (dst->colorType() != kIndex_8_SkColorType) {
23 return;
24 }
25 // We must check to see if the bitmap has a color table.
26 SkAutoLockPixels autoLockPixels(*dst);
27 if (!dst->getColorTable()) {
28 // We can't use an indexed bitmap with no colortable.
29 dst->reset();
30 } else {
31 return;
32 }
halcanary7a14b312015-10-01 07:28:13 -070033 }
34 // no pixels or wrong size: fill with zeros.
35 SkAlphaType at = image->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
36 dst->setInfo(SkImageInfo::MakeN32(image->width(), image->height(), at));
37}
38
39bool image_compute_is_opaque(const SkImage* image) {
40 if (image->isOpaque()) {
41 return true;
42 }
43 // keep output PDF small at cost of possible resource use.
44 SkBitmap bm;
45 image_get_ro_pixels(image, &bm);
46 return SkBitmap::ComputeIsOpaque(bm);
47}
48
halcanary1b5c6042015-02-18 11:29:56 -080049////////////////////////////////////////////////////////////////////////////////
50
51static void pdf_stream_begin(SkWStream* stream) {
52 static const char streamBegin[] = " stream\n";
53 stream->write(streamBegin, strlen(streamBegin));
54}
55
56static void pdf_stream_end(SkWStream* stream) {
57 static const char streamEnd[] = "\nendstream";
58 stream->write(streamEnd, strlen(streamEnd));
59}
60
halcanarydb0dcc72015-03-20 12:31:52 -070061////////////////////////////////////////////////////////////////////////////////
halcanary1b5c6042015-02-18 11:29:56 -080062
halcanary1b5c6042015-02-18 11:29:56 -080063// write a single byte to a stream n times.
64static void fill_stream(SkWStream* out, char value, size_t n) {
65 char buffer[4096];
66 memset(buffer, value, sizeof(buffer));
halcanarydb0dcc72015-03-20 12:31:52 -070067 for (size_t i = 0; i < n / sizeof(buffer); ++i) {
68 out->write(buffer, sizeof(buffer));
halcanary1b5c6042015-02-18 11:29:56 -080069 }
halcanarydb0dcc72015-03-20 12:31:52 -070070 out->write(buffer, n % sizeof(buffer));
halcanary1b5c6042015-02-18 11:29:56 -080071}
72
halcanaryaa4ba902015-11-06 07:27:23 -080073// TODO(reed@): Decide if these five functions belong in SkColorPriv.h
74static bool SkIsBGRA(SkColorType ct) {
75 SkASSERT(kBGRA_8888_SkColorType == ct || kRGBA_8888_SkColorType == ct);
76 return kBGRA_8888_SkColorType == ct;
77}
78
79// Interpret value as the given 4-byte SkColorType (BGRA_8888 or
80// RGBA_8888) and return the appropriate component. Each component
81// should be interpreted according to the associated SkAlphaType and
82// SkColorProfileType.
83static U8CPU SkGetA32Component(uint32_t value, SkColorType ct) {
84 return (value >> (SkIsBGRA(ct) ? SK_BGRA_A32_SHIFT : SK_RGBA_A32_SHIFT)) & 0xFF;
85}
86static U8CPU SkGetR32Component(uint32_t value, SkColorType ct) {
87 return (value >> (SkIsBGRA(ct) ? SK_BGRA_R32_SHIFT : SK_RGBA_R32_SHIFT)) & 0xFF;
88}
89static U8CPU SkGetG32Component(uint32_t value, SkColorType ct) {
90 return (value >> (SkIsBGRA(ct) ? SK_BGRA_G32_SHIFT : SK_RGBA_G32_SHIFT)) & 0xFF;
91}
92static U8CPU SkGetB32Component(uint32_t value, SkColorType ct) {
93 return (value >> (SkIsBGRA(ct) ? SK_BGRA_B32_SHIFT : SK_RGBA_B32_SHIFT)) & 0xFF;
94}
95
halcanarydb0dcc72015-03-20 12:31:52 -070096// unpremultiply and extract R, G, B components.
halcanaryaa4ba902015-11-06 07:27:23 -080097static void pmcolor_to_rgb24(uint32_t color, uint8_t* rgb, SkColorType ct) {
98 uint32_t s = SkUnPreMultiply::GetScale(SkGetA32Component(color, ct));
99 rgb[0] = SkUnPreMultiply::ApplyScale(s, SkGetR32Component(color, ct));
100 rgb[1] = SkUnPreMultiply::ApplyScale(s, SkGetG32Component(color, ct));
101 rgb[2] = SkUnPreMultiply::ApplyScale(s, SkGetB32Component(color, ct));
halcanarydb0dcc72015-03-20 12:31:52 -0700102}
103
104/* It is necessary to average the color component of transparent
105 pixels with their surrounding neighbors since the PDF renderer may
106 separately re-sample the alpha and color channels when the image is
107 not displayed at its native resolution. Since an alpha of zero
108 gives no information about the color component, the pathological
109 case is a white image with sharp transparency bounds - the color
110 channel goes to black, and the should-be-transparent pixels are
111 rendered as grey because of the separate soft mask and color
112 resizing. e.g.: gm/bitmappremul.cpp */
113static void get_neighbor_avg_color(const SkBitmap& bm,
114 int xOrig,
115 int yOrig,
halcanaryaa4ba902015-11-06 07:27:23 -0800116 uint8_t rgb[3],
117 SkColorType ct) {
halcanarydb0dcc72015-03-20 12:31:52 -0700118 unsigned a = 0, r = 0, g = 0, b = 0;
119 // Clamp the range to the edge of the bitmap.
120 int ymin = SkTMax(0, yOrig - 1);
121 int ymax = SkTMin(yOrig + 1, bm.height() - 1);
122 int xmin = SkTMax(0, xOrig - 1);
123 int xmax = SkTMin(xOrig + 1, bm.width() - 1);
124 for (int y = ymin; y <= ymax; ++y) {
halcanaryaa4ba902015-11-06 07:27:23 -0800125 uint32_t* scanline = bm.getAddr32(0, y);
halcanarydb0dcc72015-03-20 12:31:52 -0700126 for (int x = xmin; x <= xmax; ++x) {
halcanaryaa4ba902015-11-06 07:27:23 -0800127 uint32_t color = scanline[x];
128 a += SkGetA32Component(color, ct);
129 r += SkGetR32Component(color, ct);
130 g += SkGetG32Component(color, ct);
131 b += SkGetB32Component(color, ct);
halcanary1b5c6042015-02-18 11:29:56 -0800132 }
133 }
halcanarydb0dcc72015-03-20 12:31:52 -0700134 if (a > 0) {
135 rgb[0] = SkToU8(255 * r / a);
136 rgb[1] = SkToU8(255 * g / a);
137 rgb[2] = SkToU8(255 * b / a);
halcanary1b5c6042015-02-18 11:29:56 -0800138 } else {
halcanarydb0dcc72015-03-20 12:31:52 -0700139 rgb[0] = rgb[1] = rgb[2] = 0;
halcanary1b5c6042015-02-18 11:29:56 -0800140 }
141}
142
halcanarydb0dcc72015-03-20 12:31:52 -0700143static size_t pixel_count(const SkBitmap& bm) {
144 return SkToSizeT(bm.width()) * SkToSizeT(bm.height());
145}
146
147static const SkBitmap& not4444(const SkBitmap& input, SkBitmap* copy) {
148 if (input.colorType() != kARGB_4444_SkColorType) {
149 return input;
150 }
151 // ARGB_4444 is rarely used, so we can do a wasteful tmp copy.
152 SkAssertResult(input.copyTo(copy, kN32_SkColorType));
153 copy->setImmutable();
154 return *copy;
155}
156
157static size_t pdf_color_component_count(SkColorType ct) {
158 switch (ct) {
halcanarydb0dcc72015-03-20 12:31:52 -0700159 case kRGB_565_SkColorType:
160 case kARGB_4444_SkColorType:
halcanaryaa4ba902015-11-06 07:27:23 -0800161 case kRGBA_8888_SkColorType:
162 case kBGRA_8888_SkColorType:
halcanarydb0dcc72015-03-20 12:31:52 -0700163 return 3;
164 case kAlpha_8_SkColorType:
165 case kIndex_8_SkColorType:
166 case kGray_8_SkColorType:
167 return 1;
168 case kUnknown_SkColorType:
169 default:
170 SkDEBUGFAIL("unexpected color type");
171 return 0;
172 }
173}
174
175static void bitmap_to_pdf_pixels(const SkBitmap& bitmap, SkWStream* out) {
176 if (!bitmap.getPixels()) {
177 size_t size = pixel_count(bitmap) *
178 pdf_color_component_count(bitmap.colorType());
179 fill_stream(out, '\x00', size);
halcanary1b5c6042015-02-18 11:29:56 -0800180 return;
181 }
halcanarydb0dcc72015-03-20 12:31:52 -0700182 SkBitmap copy;
183 const SkBitmap& bm = not4444(bitmap, &copy);
184 SkAutoLockPixels autoLockPixels(bm);
halcanaryaa4ba902015-11-06 07:27:23 -0800185 SkColorType colorType = bm.colorType();
halcanary7b9eabb2016-06-03 08:57:03 -0700186 SkAlphaType alphaType = bm.alphaType();
halcanaryaa4ba902015-11-06 07:27:23 -0800187 switch (colorType) {
188 case kRGBA_8888_SkColorType:
189 case kBGRA_8888_SkColorType: {
190 SkASSERT(3 == pdf_color_component_count(colorType));
halcanarydb0dcc72015-03-20 12:31:52 -0700191 SkAutoTMalloc<uint8_t> scanline(3 * bm.width());
192 for (int y = 0; y < bm.height(); ++y) {
halcanaryaa4ba902015-11-06 07:27:23 -0800193 const uint32_t* src = bm.getAddr32(0, y);
halcanarydb0dcc72015-03-20 12:31:52 -0700194 uint8_t* dst = scanline.get();
195 for (int x = 0; x < bm.width(); ++x) {
halcanary7b9eabb2016-06-03 08:57:03 -0700196 if (alphaType == kPremul_SkAlphaType) {
197 uint32_t color = *src++;
198 U8CPU alpha = SkGetA32Component(color, colorType);
199 if (alpha != SK_AlphaTRANSPARENT) {
200 pmcolor_to_rgb24(color, dst, colorType);
201 } else {
202 get_neighbor_avg_color(bm, x, y, dst, colorType);
203 }
204 dst += 3;
halcanarydb0dcc72015-03-20 12:31:52 -0700205 } else {
halcanary7b9eabb2016-06-03 08:57:03 -0700206 uint32_t color = *src++;
207 *dst++ = SkGetR32Component(color, colorType);
208 *dst++ = SkGetG32Component(color, colorType);
209 *dst++ = SkGetB32Component(color, colorType);
halcanarydb0dcc72015-03-20 12:31:52 -0700210 }
halcanarydb0dcc72015-03-20 12:31:52 -0700211 }
212 out->write(scanline.get(), 3 * bm.width());
halcanary1b5c6042015-02-18 11:29:56 -0800213 }
halcanarydb0dcc72015-03-20 12:31:52 -0700214 return;
halcanary1b5c6042015-02-18 11:29:56 -0800215 }
halcanarydb0dcc72015-03-20 12:31:52 -0700216 case kRGB_565_SkColorType: {
halcanaryaa4ba902015-11-06 07:27:23 -0800217 SkASSERT(3 == pdf_color_component_count(colorType));
halcanarydb0dcc72015-03-20 12:31:52 -0700218 SkAutoTMalloc<uint8_t> scanline(3 * bm.width());
219 for (int y = 0; y < bm.height(); ++y) {
220 const uint16_t* src = bm.getAddr16(0, y);
221 uint8_t* dst = scanline.get();
222 for (int x = 0; x < bm.width(); ++x) {
223 U16CPU color565 = *src++;
224 *dst++ = SkPacked16ToR32(color565);
225 *dst++ = SkPacked16ToG32(color565);
226 *dst++ = SkPacked16ToB32(color565);
227 }
228 out->write(scanline.get(), 3 * bm.width());
229 }
230 return;
231 }
232 case kAlpha_8_SkColorType:
halcanaryaa4ba902015-11-06 07:27:23 -0800233 SkASSERT(1 == pdf_color_component_count(colorType));
halcanarydb0dcc72015-03-20 12:31:52 -0700234 fill_stream(out, '\x00', pixel_count(bm));
235 return;
236 case kGray_8_SkColorType:
237 case kIndex_8_SkColorType:
halcanaryaa4ba902015-11-06 07:27:23 -0800238 SkASSERT(1 == pdf_color_component_count(colorType));
halcanarydb0dcc72015-03-20 12:31:52 -0700239 // these two formats need no transformation to serialize.
240 for (int y = 0; y < bm.height(); ++y) {
241 out->write(bm.getAddr8(0, y), bm.width());
242 }
243 return;
244 case kUnknown_SkColorType:
245 case kARGB_4444_SkColorType:
246 default:
247 SkDEBUGFAIL("unexpected color type");
halcanary1b5c6042015-02-18 11:29:56 -0800248 }
249}
250
halcanarydb0dcc72015-03-20 12:31:52 -0700251////////////////////////////////////////////////////////////////////////////////
252
253static void bitmap_alpha_to_a8(const SkBitmap& bitmap, SkWStream* out) {
254 if (!bitmap.getPixels()) {
255 fill_stream(out, '\xFF', pixel_count(bitmap));
halcanary1b5c6042015-02-18 11:29:56 -0800256 return;
257 }
halcanarydb0dcc72015-03-20 12:31:52 -0700258 SkBitmap copy;
259 const SkBitmap& bm = not4444(bitmap, &copy);
260 SkAutoLockPixels autoLockPixels(bm);
halcanaryaa4ba902015-11-06 07:27:23 -0800261 SkColorType colorType = bm.colorType();
262 switch (colorType) {
263 case kRGBA_8888_SkColorType:
264 case kBGRA_8888_SkColorType: {
halcanarydb0dcc72015-03-20 12:31:52 -0700265 SkAutoTMalloc<uint8_t> scanline(bm.width());
266 for (int y = 0; y < bm.height(); ++y) {
267 uint8_t* dst = scanline.get();
268 const SkPMColor* src = bm.getAddr32(0, y);
269 for (int x = 0; x < bm.width(); ++x) {
halcanaryaa4ba902015-11-06 07:27:23 -0800270 *dst++ = SkGetA32Component(*src++, colorType);
halcanarydb0dcc72015-03-20 12:31:52 -0700271 }
272 out->write(scanline.get(), bm.width());
273 }
274 return;
halcanary1b5c6042015-02-18 11:29:56 -0800275 }
halcanarydb0dcc72015-03-20 12:31:52 -0700276 case kAlpha_8_SkColorType:
277 for (int y = 0; y < bm.height(); ++y) {
278 out->write(bm.getAddr8(0, y), bm.width());
279 }
280 return;
281 case kIndex_8_SkColorType: {
282 SkColorTable* ct = bm.getColorTable();
283 SkASSERT(ct);
284 SkAutoTMalloc<uint8_t> scanline(bm.width());
285 for (int y = 0; y < bm.height(); ++y) {
286 uint8_t* dst = scanline.get();
287 const uint8_t* src = bm.getAddr8(0, y);
288 for (int x = 0; x < bm.width(); ++x) {
289 *dst++ = SkGetPackedA32((*ct)[*src++]);
290 }
291 out->write(scanline.get(), bm.width());
292 }
293 return;
294 }
295 case kRGB_565_SkColorType:
296 case kGray_8_SkColorType:
297 SkDEBUGFAIL("color type has no alpha");
298 return;
299 case kARGB_4444_SkColorType:
300 SkDEBUGFAIL("4444 color type should have been converted to N32");
301 return;
302 case kUnknown_SkColorType:
303 default:
304 SkDEBUGFAIL("unexpected color type");
halcanary1b5c6042015-02-18 11:29:56 -0800305 }
306}
307
halcanary7b9eabb2016-06-03 08:57:03 -0700308static sk_sp<SkPDFArray> make_indexed_color_space(
309 const SkColorTable* table,
310 SkAlphaType alphaType) {
halcanary8103a342016-03-08 15:10:16 -0800311 auto result = sk_make_sp<SkPDFArray>();
halcanarydb0dcc72015-03-20 12:31:52 -0700312 result->reserve(4);
313 result->appendName("Indexed");
314 result->appendName("DeviceRGB");
315 SkASSERT(table);
316 if (table->count() < 1) {
317 result->appendInt(0);
318 char shortTableArray[3] = {0, 0, 0};
319 SkString tableString(shortTableArray, SK_ARRAY_COUNT(shortTableArray));
halcanarya25b3372015-04-27 14:00:09 -0700320 result->appendString(tableString);
halcanary51d04d32016-03-08 13:03:55 -0800321 return result;
halcanarydb0dcc72015-03-20 12:31:52 -0700322 }
323 result->appendInt(table->count() - 1); // maximum color index.
324
325 // Potentially, this could be represented in fewer bytes with a stream.
326 // Max size as a string is 1.5k.
327 char tableArray[256 * 3];
328 SkASSERT(3u * table->count() <= SK_ARRAY_COUNT(tableArray));
329 uint8_t* tablePtr = reinterpret_cast<uint8_t*>(tableArray);
330 const SkPMColor* colors = table->readColors();
331 for (int i = 0; i < table->count(); i++) {
halcanary7b9eabb2016-06-03 08:57:03 -0700332 if (alphaType == kPremul_SkAlphaType) {
333 pmcolor_to_rgb24(colors[i], tablePtr, kN32_SkColorType);
334 tablePtr += 3;
335 } else {
336 *tablePtr++ = SkGetR32Component(colors[i], kN32_SkColorType);
337 *tablePtr++ = SkGetG32Component(colors[i], kN32_SkColorType);
338 *tablePtr++ = SkGetB32Component(colors[i], kN32_SkColorType);
339 }
halcanarydb0dcc72015-03-20 12:31:52 -0700340 }
341 SkString tableString(tableArray, 3 * table->count());
halcanarya25b3372015-04-27 14:00:09 -0700342 result->appendString(tableString);
halcanary51d04d32016-03-08 13:03:55 -0800343 return result;
halcanary1b5c6042015-02-18 11:29:56 -0800344}
345
halcanary7a14b312015-10-01 07:28:13 -0700346static void emit_image_xobject(SkWStream* stream,
347 const SkImage* image,
348 bool alpha,
halcanary8103a342016-03-08 15:10:16 -0800349 const sk_sp<SkPDFObject>& smask,
halcanary7a14b312015-10-01 07:28:13 -0700350 const SkPDFObjNumMap& objNumMap,
351 const SkPDFSubstituteMap& substitutes) {
352 SkBitmap bitmap;
353 image_get_ro_pixels(image, &bitmap); // TODO(halcanary): test
354 SkAutoLockPixels autoLockPixels(bitmap); // with malformed images.
halcanary37c46ca2015-03-31 12:30:20 -0700355
356 // Write to a temporary buffer to get the compressed length.
357 SkDynamicMemoryWStream buffer;
358 SkDeflateWStream deflateWStream(&buffer);
halcanary7a14b312015-10-01 07:28:13 -0700359 if (alpha) {
360 bitmap_alpha_to_a8(bitmap, &deflateWStream);
361 } else {
362 bitmap_to_pdf_pixels(bitmap, &deflateWStream);
363 }
halcanary37c46ca2015-03-31 12:30:20 -0700364 deflateWStream.finalize(); // call before detachAsStream().
halcanaryb8fb9932016-03-28 07:58:30 -0700365 std::unique_ptr<SkStreamAsset> asset(buffer.detachAsStream());
halcanary37c46ca2015-03-31 12:30:20 -0700366
halcanary1b5c6042015-02-18 11:29:56 -0800367 SkPDFDict pdfDict("XObject");
368 pdfDict.insertName("Subtype", "Image");
halcanary7a14b312015-10-01 07:28:13 -0700369 pdfDict.insertInt("Width", bitmap.width());
370 pdfDict.insertInt("Height", bitmap.height());
371 if (alpha) {
372 pdfDict.insertName("ColorSpace", "DeviceGray");
373 } else if (bitmap.colorType() == kIndex_8_SkColorType) {
374 SkASSERT(1 == pdf_color_component_count(bitmap.colorType()));
halcanarya25b3372015-04-27 14:00:09 -0700375 pdfDict.insertObject("ColorSpace",
halcanary7b9eabb2016-06-03 08:57:03 -0700376 make_indexed_color_space(bitmap.getColorTable(),
377 bitmap.alphaType()));
halcanary7a14b312015-10-01 07:28:13 -0700378 } else if (1 == pdf_color_component_count(bitmap.colorType())) {
halcanarydb0dcc72015-03-20 12:31:52 -0700379 pdfDict.insertName("ColorSpace", "DeviceGray");
380 } else {
381 pdfDict.insertName("ColorSpace", "DeviceRGB");
382 }
halcanary7a14b312015-10-01 07:28:13 -0700383 if (smask) {
halcanary8103a342016-03-08 15:10:16 -0800384 pdfDict.insertObjRef("SMask", smask);
halcanary1b5c6042015-02-18 11:29:56 -0800385 }
halcanary7a14b312015-10-01 07:28:13 -0700386 pdfDict.insertInt("BitsPerComponent", 8);
halcanarydb0dcc72015-03-20 12:31:52 -0700387 pdfDict.insertName("Filter", "FlateDecode");
halcanary37c46ca2015-03-31 12:30:20 -0700388 pdfDict.insertInt("Length", asset->getLength());
halcanarya8448bc2015-04-17 13:27:24 -0700389 pdfDict.emitObject(stream, objNumMap, substitutes);
halcanary37c46ca2015-03-31 12:30:20 -0700390
391 pdf_stream_begin(stream);
392 stream->writeStream(asset.get(), asset->getLength());
393 pdf_stream_end(stream);
halcanary1b5c6042015-02-18 11:29:56 -0800394}
395
halcanary1b5c6042015-02-18 11:29:56 -0800396////////////////////////////////////////////////////////////////////////////////
halcanarydb0dcc72015-03-20 12:31:52 -0700397
halcanary7a14b312015-10-01 07:28:13 -0700398namespace {
399// This SkPDFObject only outputs the alpha layer of the given bitmap.
halcanary70d15542015-11-22 12:55:04 -0800400class PDFAlphaBitmap final : public SkPDFObject {
halcanary7a14b312015-10-01 07:28:13 -0700401public:
halcanarya50151d2016-03-25 11:57:49 -0700402 PDFAlphaBitmap(sk_sp<SkImage> image) : fImage(std::move(image)) { SkASSERT(fImage); }
halcanary7a14b312015-10-01 07:28:13 -0700403 void emitObject(SkWStream* stream,
404 const SkPDFObjNumMap& objNumMap,
405 const SkPDFSubstituteMap& subs) const override {
halcanarybae235e2016-03-21 10:05:23 -0700406 SkASSERT(fImage);
halcanaryfcad44b2016-03-06 14:47:10 -0800407 emit_image_xobject(stream, fImage.get(), true, nullptr, objNumMap, subs);
halcanary1b5c6042015-02-18 11:29:56 -0800408 }
halcanarybae235e2016-03-21 10:05:23 -0700409 void drop() override { fImage = nullptr; }
halcanary7a14b312015-10-01 07:28:13 -0700410
411private:
halcanarya50151d2016-03-25 11:57:49 -0700412 sk_sp<SkImage> fImage;
halcanary7a14b312015-10-01 07:28:13 -0700413};
414
415} // namespace
416
417////////////////////////////////////////////////////////////////////////////////
418
419namespace {
halcanary70d15542015-11-22 12:55:04 -0800420class PDFDefaultBitmap final : public SkPDFObject {
halcanary7a14b312015-10-01 07:28:13 -0700421public:
halcanary34422612015-10-12 10:11:18 -0700422 void emitObject(SkWStream* stream,
halcanary7a14b312015-10-01 07:28:13 -0700423 const SkPDFObjNumMap& objNumMap,
halcanary34422612015-10-12 10:11:18 -0700424 const SkPDFSubstituteMap& subs) const override {
halcanarybae235e2016-03-21 10:05:23 -0700425 SkASSERT(fImage);
halcanary8103a342016-03-08 15:10:16 -0800426 emit_image_xobject(stream, fImage.get(), false, fSMask, objNumMap, subs);
halcanary7a14b312015-10-01 07:28:13 -0700427 }
428 void addResources(SkPDFObjNumMap* catalog,
halcanary34422612015-10-12 10:11:18 -0700429 const SkPDFSubstituteMap& subs) const override {
halcanarybae235e2016-03-21 10:05:23 -0700430 SkASSERT(fImage);
halcanary7a14b312015-10-01 07:28:13 -0700431 if (fSMask.get()) {
halcanary34422612015-10-12 10:11:18 -0700432 SkPDFObject* obj = subs.getSubstitute(fSMask.get());
halcanary7a14b312015-10-01 07:28:13 -0700433 SkASSERT(obj);
halcanary34422612015-10-12 10:11:18 -0700434 catalog->addObjectRecursively(obj, subs);
halcanary7a14b312015-10-01 07:28:13 -0700435 }
436 }
halcanarybae235e2016-03-21 10:05:23 -0700437 void drop() override { fImage = nullptr; fSMask = nullptr; }
halcanarya50151d2016-03-25 11:57:49 -0700438 PDFDefaultBitmap(sk_sp<SkImage> image, sk_sp<SkPDFObject> smask)
439 : fImage(std::move(image)), fSMask(std::move(smask)) { SkASSERT(fImage); }
halcanary7a14b312015-10-01 07:28:13 -0700440
441private:
halcanarya50151d2016-03-25 11:57:49 -0700442 sk_sp<SkImage> fImage;
halcanarybae235e2016-03-21 10:05:23 -0700443 sk_sp<SkPDFObject> fSMask;
halcanary7a14b312015-10-01 07:28:13 -0700444};
445} // namespace
446
447////////////////////////////////////////////////////////////////////////////////
halcanary1b5c6042015-02-18 11:29:56 -0800448
halcanarya8448bc2015-04-17 13:27:24 -0700449namespace {
450/**
halcanary7a14b312015-10-01 07:28:13 -0700451 * This PDFObject assumes that its constructor was handed YUV or
452 * Grayscale JFIF Jpeg-encoded data that can be directly embedded
453 * into a PDF.
halcanarya8448bc2015-04-17 13:27:24 -0700454 */
halcanary70d15542015-11-22 12:55:04 -0800455class PDFJpegBitmap final : public SkPDFObject {
halcanarya8448bc2015-04-17 13:27:24 -0700456public:
halcanary7a14b312015-10-01 07:28:13 -0700457 SkISize fSize;
halcanary48810a02016-03-07 14:57:50 -0800458 sk_sp<SkData> fData;
halcanary96287f72015-05-07 11:46:59 -0700459 bool fIsYUV;
halcanary7a14b312015-10-01 07:28:13 -0700460 PDFJpegBitmap(SkISize size, SkData* data, bool isYUV)
halcanarybae235e2016-03-21 10:05:23 -0700461 : fSize(size), fData(SkRef(data)), fIsYUV(isYUV) { SkASSERT(data); }
halcanarya8448bc2015-04-17 13:27:24 -0700462 void emitObject(SkWStream*,
463 const SkPDFObjNumMap&,
halcanarya060eba2015-08-19 12:26:46 -0700464 const SkPDFSubstituteMap&) const override;
halcanarybae235e2016-03-21 10:05:23 -0700465 void drop() override { fData = nullptr; }
halcanarya8448bc2015-04-17 13:27:24 -0700466};
467
468void PDFJpegBitmap::emitObject(SkWStream* stream,
469 const SkPDFObjNumMap& objNumMap,
halcanarya060eba2015-08-19 12:26:46 -0700470 const SkPDFSubstituteMap& substituteMap) const {
halcanarybae235e2016-03-21 10:05:23 -0700471 SkASSERT(fData);
halcanarya8448bc2015-04-17 13:27:24 -0700472 SkPDFDict pdfDict("XObject");
473 pdfDict.insertName("Subtype", "Image");
halcanary7a14b312015-10-01 07:28:13 -0700474 pdfDict.insertInt("Width", fSize.width());
475 pdfDict.insertInt("Height", fSize.height());
halcanary96287f72015-05-07 11:46:59 -0700476 if (fIsYUV) {
477 pdfDict.insertName("ColorSpace", "DeviceRGB");
478 } else {
479 pdfDict.insertName("ColorSpace", "DeviceGray");
480 }
halcanarya8448bc2015-04-17 13:27:24 -0700481 pdfDict.insertInt("BitsPerComponent", 8);
482 pdfDict.insertName("Filter", "DCTDecode");
483 pdfDict.insertInt("ColorTransform", 0);
484 pdfDict.insertInt("Length", SkToInt(fData->size()));
485 pdfDict.emitObject(stream, objNumMap, substituteMap);
486 pdf_stream_begin(stream);
487 stream->write(fData->data(), fData->size());
488 pdf_stream_end(stream);
489}
490} // namespace
491
492////////////////////////////////////////////////////////////////////////////////
493
halcanarya50151d2016-03-25 11:57:49 -0700494sk_sp<SkPDFObject> SkPDFCreateBitmapObject(sk_sp<SkImage> image,
495 SkPixelSerializer* pixelSerializer) {
496 SkASSERT(image);
halcanary48810a02016-03-07 14:57:50 -0800497 sk_sp<SkData> data(image->refEncoded());
halcanary7a14b312015-10-01 07:28:13 -0700498 SkJFIFInfo info;
halcanaryfcad44b2016-03-06 14:47:10 -0800499 if (data && SkIsJFIF(data.get(), &info) &&
halcanary7363e132016-02-25 17:21:40 -0800500 (!pixelSerializer ||
501 pixelSerializer->useEncodedData(data->data(), data->size()))) {
502 // If there is a SkPixelSerializer, give it a chance to
503 // re-encode the JPEG with more compression by returning false
504 // from useEncodedData.
halcanary7a14b312015-10-01 07:28:13 -0700505 bool yuv = info.fType == SkJFIFInfo::kYCbCr;
506 if (info.fSize == image->dimensions()) { // Sanity check.
507 // hold on to data, not image.
508 #ifdef SK_PDF_IMAGE_STATS
509 gJpegImageObjects.fetch_add(1);
510 #endif
halcanarya50151d2016-03-25 11:57:49 -0700511 return sk_make_sp<PDFJpegBitmap>(info.fSize, data.get(), yuv);
halcanarya8448bc2015-04-17 13:27:24 -0700512 }
513 }
halcanary712fdf72015-12-10 08:59:43 -0800514
515 if (pixelSerializer) {
516 SkBitmap bm;
517 SkAutoPixmapUnlock apu;
halcanarya50151d2016-03-25 11:57:49 -0700518 if (as_IB(image.get())->getROPixels(&bm) && bm.requestLock(&apu)) {
halcanary712fdf72015-12-10 08:59:43 -0800519 data.reset(pixelSerializer->encode(apu.pixmap()));
halcanaryfcad44b2016-03-06 14:47:10 -0800520 if (data && SkIsJFIF(data.get(), &info)) {
halcanary712fdf72015-12-10 08:59:43 -0800521 bool yuv = info.fType == SkJFIFInfo::kYCbCr;
522 if (info.fSize == image->dimensions()) { // Sanity check.
halcanarya50151d2016-03-25 11:57:49 -0700523 return sk_make_sp<PDFJpegBitmap>(info.fSize, data.get(), yuv);
halcanary712fdf72015-12-10 08:59:43 -0800524 }
525 }
526 }
527 }
528
halcanarya50151d2016-03-25 11:57:49 -0700529 sk_sp<SkPDFObject> smask;
530 if (!image_compute_is_opaque(image.get())) {
531 smask = sk_make_sp<PDFAlphaBitmap>(image);
532 }
halcanary7a14b312015-10-01 07:28:13 -0700533 #ifdef SK_PDF_IMAGE_STATS
534 gRegularImageObjects.fetch_add(1);
535 #endif
halcanarya50151d2016-03-25 11:57:49 -0700536 return sk_make_sp<PDFDefaultBitmap>(std::move(image), std::move(smask));
halcanary1b5c6042015-02-18 11:29:56 -0800537}