blob: e483e341d948ae6dc3919b5a0740ef00dbe1c3c9 [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"
halcanary1b5c6042015-02-18 11:29:56 -080015#include "SkStream.h"
16#include "SkUnPreMultiply.h"
17
halcanary7a14b312015-10-01 07:28:13 -070018void image_get_ro_pixels(const SkImage* image, SkBitmap* dst) {
19 if(as_IB(image)->getROPixels(dst)
20 && dst->dimensions() == image->dimensions()) {
halcanary80ec3db2015-11-10 09:48:40 -080021 if (dst->colorType() != kIndex_8_SkColorType) {
22 return;
23 }
24 // We must check to see if the bitmap has a color table.
25 SkAutoLockPixels autoLockPixels(*dst);
26 if (!dst->getColorTable()) {
27 // We can't use an indexed bitmap with no colortable.
28 dst->reset();
29 } else {
30 return;
31 }
halcanary7a14b312015-10-01 07:28:13 -070032 }
33 // no pixels or wrong size: fill with zeros.
34 SkAlphaType at = image->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
35 dst->setInfo(SkImageInfo::MakeN32(image->width(), image->height(), at));
36}
37
38bool image_compute_is_opaque(const SkImage* image) {
39 if (image->isOpaque()) {
40 return true;
41 }
42 // keep output PDF small at cost of possible resource use.
43 SkBitmap bm;
44 image_get_ro_pixels(image, &bm);
45 return SkBitmap::ComputeIsOpaque(bm);
46}
47
halcanary1b5c6042015-02-18 11:29:56 -080048////////////////////////////////////////////////////////////////////////////////
49
50static void pdf_stream_begin(SkWStream* stream) {
51 static const char streamBegin[] = " stream\n";
52 stream->write(streamBegin, strlen(streamBegin));
53}
54
55static void pdf_stream_end(SkWStream* stream) {
56 static const char streamEnd[] = "\nendstream";
57 stream->write(streamEnd, strlen(streamEnd));
58}
59
halcanarydb0dcc72015-03-20 12:31:52 -070060////////////////////////////////////////////////////////////////////////////////
halcanary1b5c6042015-02-18 11:29:56 -080061
halcanary1b5c6042015-02-18 11:29:56 -080062// write a single byte to a stream n times.
63static void fill_stream(SkWStream* out, char value, size_t n) {
64 char buffer[4096];
65 memset(buffer, value, sizeof(buffer));
halcanarydb0dcc72015-03-20 12:31:52 -070066 for (size_t i = 0; i < n / sizeof(buffer); ++i) {
67 out->write(buffer, sizeof(buffer));
halcanary1b5c6042015-02-18 11:29:56 -080068 }
halcanarydb0dcc72015-03-20 12:31:52 -070069 out->write(buffer, n % sizeof(buffer));
halcanary1b5c6042015-02-18 11:29:56 -080070}
71
halcanaryaa4ba902015-11-06 07:27:23 -080072// TODO(reed@): Decide if these five functions belong in SkColorPriv.h
73static bool SkIsBGRA(SkColorType ct) {
74 SkASSERT(kBGRA_8888_SkColorType == ct || kRGBA_8888_SkColorType == ct);
75 return kBGRA_8888_SkColorType == ct;
76}
77
78// Interpret value as the given 4-byte SkColorType (BGRA_8888 or
79// RGBA_8888) and return the appropriate component. Each component
80// should be interpreted according to the associated SkAlphaType and
81// SkColorProfileType.
82static U8CPU SkGetA32Component(uint32_t value, SkColorType ct) {
83 return (value >> (SkIsBGRA(ct) ? SK_BGRA_A32_SHIFT : SK_RGBA_A32_SHIFT)) & 0xFF;
84}
85static U8CPU SkGetR32Component(uint32_t value, SkColorType ct) {
86 return (value >> (SkIsBGRA(ct) ? SK_BGRA_R32_SHIFT : SK_RGBA_R32_SHIFT)) & 0xFF;
87}
88static U8CPU SkGetG32Component(uint32_t value, SkColorType ct) {
89 return (value >> (SkIsBGRA(ct) ? SK_BGRA_G32_SHIFT : SK_RGBA_G32_SHIFT)) & 0xFF;
90}
91static U8CPU SkGetB32Component(uint32_t value, SkColorType ct) {
92 return (value >> (SkIsBGRA(ct) ? SK_BGRA_B32_SHIFT : SK_RGBA_B32_SHIFT)) & 0xFF;
93}
94
halcanarydb0dcc72015-03-20 12:31:52 -070095// unpremultiply and extract R, G, B components.
halcanaryaa4ba902015-11-06 07:27:23 -080096static void pmcolor_to_rgb24(uint32_t color, uint8_t* rgb, SkColorType ct) {
97 uint32_t s = SkUnPreMultiply::GetScale(SkGetA32Component(color, ct));
98 rgb[0] = SkUnPreMultiply::ApplyScale(s, SkGetR32Component(color, ct));
99 rgb[1] = SkUnPreMultiply::ApplyScale(s, SkGetG32Component(color, ct));
100 rgb[2] = SkUnPreMultiply::ApplyScale(s, SkGetB32Component(color, ct));
halcanarydb0dcc72015-03-20 12:31:52 -0700101}
102
103/* It is necessary to average the color component of transparent
104 pixels with their surrounding neighbors since the PDF renderer may
105 separately re-sample the alpha and color channels when the image is
106 not displayed at its native resolution. Since an alpha of zero
107 gives no information about the color component, the pathological
108 case is a white image with sharp transparency bounds - the color
109 channel goes to black, and the should-be-transparent pixels are
110 rendered as grey because of the separate soft mask and color
111 resizing. e.g.: gm/bitmappremul.cpp */
112static void get_neighbor_avg_color(const SkBitmap& bm,
113 int xOrig,
114 int yOrig,
halcanaryaa4ba902015-11-06 07:27:23 -0800115 uint8_t rgb[3],
116 SkColorType ct) {
halcanarydb0dcc72015-03-20 12:31:52 -0700117 unsigned a = 0, r = 0, g = 0, b = 0;
118 // Clamp the range to the edge of the bitmap.
119 int ymin = SkTMax(0, yOrig - 1);
120 int ymax = SkTMin(yOrig + 1, bm.height() - 1);
121 int xmin = SkTMax(0, xOrig - 1);
122 int xmax = SkTMin(xOrig + 1, bm.width() - 1);
123 for (int y = ymin; y <= ymax; ++y) {
halcanaryaa4ba902015-11-06 07:27:23 -0800124 uint32_t* scanline = bm.getAddr32(0, y);
halcanarydb0dcc72015-03-20 12:31:52 -0700125 for (int x = xmin; x <= xmax; ++x) {
halcanaryaa4ba902015-11-06 07:27:23 -0800126 uint32_t color = scanline[x];
127 a += SkGetA32Component(color, ct);
128 r += SkGetR32Component(color, ct);
129 g += SkGetG32Component(color, ct);
130 b += SkGetB32Component(color, ct);
halcanary1b5c6042015-02-18 11:29:56 -0800131 }
132 }
halcanarydb0dcc72015-03-20 12:31:52 -0700133 if (a > 0) {
134 rgb[0] = SkToU8(255 * r / a);
135 rgb[1] = SkToU8(255 * g / a);
136 rgb[2] = SkToU8(255 * b / a);
halcanary1b5c6042015-02-18 11:29:56 -0800137 } else {
halcanarydb0dcc72015-03-20 12:31:52 -0700138 rgb[0] = rgb[1] = rgb[2] = 0;
halcanary1b5c6042015-02-18 11:29:56 -0800139 }
140}
141
halcanarydb0dcc72015-03-20 12:31:52 -0700142static size_t pixel_count(const SkBitmap& bm) {
143 return SkToSizeT(bm.width()) * SkToSizeT(bm.height());
144}
145
146static const SkBitmap& not4444(const SkBitmap& input, SkBitmap* copy) {
147 if (input.colorType() != kARGB_4444_SkColorType) {
148 return input;
149 }
150 // ARGB_4444 is rarely used, so we can do a wasteful tmp copy.
151 SkAssertResult(input.copyTo(copy, kN32_SkColorType));
152 copy->setImmutable();
153 return *copy;
154}
155
156static size_t pdf_color_component_count(SkColorType ct) {
157 switch (ct) {
halcanarydb0dcc72015-03-20 12:31:52 -0700158 case kRGB_565_SkColorType:
159 case kARGB_4444_SkColorType:
halcanaryaa4ba902015-11-06 07:27:23 -0800160 case kRGBA_8888_SkColorType:
161 case kBGRA_8888_SkColorType:
halcanarydb0dcc72015-03-20 12:31:52 -0700162 return 3;
163 case kAlpha_8_SkColorType:
164 case kIndex_8_SkColorType:
165 case kGray_8_SkColorType:
166 return 1;
167 case kUnknown_SkColorType:
168 default:
169 SkDEBUGFAIL("unexpected color type");
170 return 0;
171 }
172}
173
174static void bitmap_to_pdf_pixels(const SkBitmap& bitmap, SkWStream* out) {
175 if (!bitmap.getPixels()) {
176 size_t size = pixel_count(bitmap) *
177 pdf_color_component_count(bitmap.colorType());
178 fill_stream(out, '\x00', size);
halcanary1b5c6042015-02-18 11:29:56 -0800179 return;
180 }
halcanarydb0dcc72015-03-20 12:31:52 -0700181 SkBitmap copy;
182 const SkBitmap& bm = not4444(bitmap, &copy);
183 SkAutoLockPixels autoLockPixels(bm);
halcanaryaa4ba902015-11-06 07:27:23 -0800184 SkColorType colorType = bm.colorType();
halcanary7b9eabb2016-06-03 08:57:03 -0700185 SkAlphaType alphaType = bm.alphaType();
halcanaryaa4ba902015-11-06 07:27:23 -0800186 switch (colorType) {
187 case kRGBA_8888_SkColorType:
188 case kBGRA_8888_SkColorType: {
189 SkASSERT(3 == pdf_color_component_count(colorType));
halcanarydb0dcc72015-03-20 12:31:52 -0700190 SkAutoTMalloc<uint8_t> scanline(3 * bm.width());
191 for (int y = 0; y < bm.height(); ++y) {
halcanaryaa4ba902015-11-06 07:27:23 -0800192 const uint32_t* src = bm.getAddr32(0, y);
halcanarydb0dcc72015-03-20 12:31:52 -0700193 uint8_t* dst = scanline.get();
194 for (int x = 0; x < bm.width(); ++x) {
halcanary7b9eabb2016-06-03 08:57:03 -0700195 if (alphaType == kPremul_SkAlphaType) {
196 uint32_t color = *src++;
197 U8CPU alpha = SkGetA32Component(color, colorType);
198 if (alpha != SK_AlphaTRANSPARENT) {
199 pmcolor_to_rgb24(color, dst, colorType);
200 } else {
201 get_neighbor_avg_color(bm, x, y, dst, colorType);
202 }
203 dst += 3;
halcanarydb0dcc72015-03-20 12:31:52 -0700204 } else {
halcanary7b9eabb2016-06-03 08:57:03 -0700205 uint32_t color = *src++;
206 *dst++ = SkGetR32Component(color, colorType);
207 *dst++ = SkGetG32Component(color, colorType);
208 *dst++ = SkGetB32Component(color, colorType);
halcanarydb0dcc72015-03-20 12:31:52 -0700209 }
halcanarydb0dcc72015-03-20 12:31:52 -0700210 }
211 out->write(scanline.get(), 3 * bm.width());
halcanary1b5c6042015-02-18 11:29:56 -0800212 }
halcanarydb0dcc72015-03-20 12:31:52 -0700213 return;
halcanary1b5c6042015-02-18 11:29:56 -0800214 }
halcanarydb0dcc72015-03-20 12:31:52 -0700215 case kRGB_565_SkColorType: {
halcanaryaa4ba902015-11-06 07:27:23 -0800216 SkASSERT(3 == pdf_color_component_count(colorType));
halcanarydb0dcc72015-03-20 12:31:52 -0700217 SkAutoTMalloc<uint8_t> scanline(3 * bm.width());
218 for (int y = 0; y < bm.height(); ++y) {
219 const uint16_t* src = bm.getAddr16(0, y);
220 uint8_t* dst = scanline.get();
221 for (int x = 0; x < bm.width(); ++x) {
222 U16CPU color565 = *src++;
223 *dst++ = SkPacked16ToR32(color565);
224 *dst++ = SkPacked16ToG32(color565);
225 *dst++ = SkPacked16ToB32(color565);
226 }
227 out->write(scanline.get(), 3 * bm.width());
228 }
229 return;
230 }
231 case kAlpha_8_SkColorType:
halcanaryaa4ba902015-11-06 07:27:23 -0800232 SkASSERT(1 == pdf_color_component_count(colorType));
halcanarydb0dcc72015-03-20 12:31:52 -0700233 fill_stream(out, '\x00', pixel_count(bm));
234 return;
235 case kGray_8_SkColorType:
236 case kIndex_8_SkColorType:
halcanaryaa4ba902015-11-06 07:27:23 -0800237 SkASSERT(1 == pdf_color_component_count(colorType));
halcanarydb0dcc72015-03-20 12:31:52 -0700238 // these two formats need no transformation to serialize.
239 for (int y = 0; y < bm.height(); ++y) {
240 out->write(bm.getAddr8(0, y), bm.width());
241 }
242 return;
243 case kUnknown_SkColorType:
244 case kARGB_4444_SkColorType:
245 default:
246 SkDEBUGFAIL("unexpected color type");
halcanary1b5c6042015-02-18 11:29:56 -0800247 }
248}
249
halcanarydb0dcc72015-03-20 12:31:52 -0700250////////////////////////////////////////////////////////////////////////////////
251
252static void bitmap_alpha_to_a8(const SkBitmap& bitmap, SkWStream* out) {
253 if (!bitmap.getPixels()) {
254 fill_stream(out, '\xFF', pixel_count(bitmap));
halcanary1b5c6042015-02-18 11:29:56 -0800255 return;
256 }
halcanarydb0dcc72015-03-20 12:31:52 -0700257 SkBitmap copy;
258 const SkBitmap& bm = not4444(bitmap, &copy);
259 SkAutoLockPixels autoLockPixels(bm);
halcanaryaa4ba902015-11-06 07:27:23 -0800260 SkColorType colorType = bm.colorType();
261 switch (colorType) {
262 case kRGBA_8888_SkColorType:
263 case kBGRA_8888_SkColorType: {
halcanarydb0dcc72015-03-20 12:31:52 -0700264 SkAutoTMalloc<uint8_t> scanline(bm.width());
265 for (int y = 0; y < bm.height(); ++y) {
266 uint8_t* dst = scanline.get();
267 const SkPMColor* src = bm.getAddr32(0, y);
268 for (int x = 0; x < bm.width(); ++x) {
halcanaryaa4ba902015-11-06 07:27:23 -0800269 *dst++ = SkGetA32Component(*src++, colorType);
halcanarydb0dcc72015-03-20 12:31:52 -0700270 }
271 out->write(scanline.get(), bm.width());
272 }
273 return;
halcanary1b5c6042015-02-18 11:29:56 -0800274 }
halcanarydb0dcc72015-03-20 12:31:52 -0700275 case kAlpha_8_SkColorType:
276 for (int y = 0; y < bm.height(); ++y) {
277 out->write(bm.getAddr8(0, y), bm.width());
278 }
279 return;
280 case kIndex_8_SkColorType: {
281 SkColorTable* ct = bm.getColorTable();
282 SkASSERT(ct);
283 SkAutoTMalloc<uint8_t> scanline(bm.width());
284 for (int y = 0; y < bm.height(); ++y) {
285 uint8_t* dst = scanline.get();
286 const uint8_t* src = bm.getAddr8(0, y);
287 for (int x = 0; x < bm.width(); ++x) {
288 *dst++ = SkGetPackedA32((*ct)[*src++]);
289 }
290 out->write(scanline.get(), bm.width());
291 }
292 return;
293 }
294 case kRGB_565_SkColorType:
295 case kGray_8_SkColorType:
296 SkDEBUGFAIL("color type has no alpha");
297 return;
298 case kARGB_4444_SkColorType:
299 SkDEBUGFAIL("4444 color type should have been converted to N32");
300 return;
301 case kUnknown_SkColorType:
302 default:
303 SkDEBUGFAIL("unexpected color type");
halcanary1b5c6042015-02-18 11:29:56 -0800304 }
305}
306
halcanary7b9eabb2016-06-03 08:57:03 -0700307static sk_sp<SkPDFArray> make_indexed_color_space(
308 const SkColorTable* table,
309 SkAlphaType alphaType) {
halcanary8103a342016-03-08 15:10:16 -0800310 auto result = sk_make_sp<SkPDFArray>();
halcanarydb0dcc72015-03-20 12:31:52 -0700311 result->reserve(4);
312 result->appendName("Indexed");
313 result->appendName("DeviceRGB");
314 SkASSERT(table);
315 if (table->count() < 1) {
316 result->appendInt(0);
317 char shortTableArray[3] = {0, 0, 0};
318 SkString tableString(shortTableArray, SK_ARRAY_COUNT(shortTableArray));
halcanarya25b3372015-04-27 14:00:09 -0700319 result->appendString(tableString);
halcanary51d04d32016-03-08 13:03:55 -0800320 return result;
halcanarydb0dcc72015-03-20 12:31:52 -0700321 }
322 result->appendInt(table->count() - 1); // maximum color index.
323
324 // Potentially, this could be represented in fewer bytes with a stream.
325 // Max size as a string is 1.5k.
326 char tableArray[256 * 3];
327 SkASSERT(3u * table->count() <= SK_ARRAY_COUNT(tableArray));
328 uint8_t* tablePtr = reinterpret_cast<uint8_t*>(tableArray);
329 const SkPMColor* colors = table->readColors();
330 for (int i = 0; i < table->count(); i++) {
halcanary7b9eabb2016-06-03 08:57:03 -0700331 if (alphaType == kPremul_SkAlphaType) {
332 pmcolor_to_rgb24(colors[i], tablePtr, kN32_SkColorType);
333 tablePtr += 3;
334 } else {
335 *tablePtr++ = SkGetR32Component(colors[i], kN32_SkColorType);
336 *tablePtr++ = SkGetG32Component(colors[i], kN32_SkColorType);
337 *tablePtr++ = SkGetB32Component(colors[i], kN32_SkColorType);
338 }
halcanarydb0dcc72015-03-20 12:31:52 -0700339 }
340 SkString tableString(tableArray, 3 * table->count());
halcanarya25b3372015-04-27 14:00:09 -0700341 result->appendString(tableString);
halcanary51d04d32016-03-08 13:03:55 -0800342 return result;
halcanary1b5c6042015-02-18 11:29:56 -0800343}
344
halcanary7a14b312015-10-01 07:28:13 -0700345static void emit_image_xobject(SkWStream* stream,
346 const SkImage* image,
347 bool alpha,
halcanary8103a342016-03-08 15:10:16 -0800348 const sk_sp<SkPDFObject>& smask,
halcanary7a14b312015-10-01 07:28:13 -0700349 const SkPDFObjNumMap& objNumMap,
350 const SkPDFSubstituteMap& substitutes) {
351 SkBitmap bitmap;
352 image_get_ro_pixels(image, &bitmap); // TODO(halcanary): test
353 SkAutoLockPixels autoLockPixels(bitmap); // with malformed images.
halcanary37c46ca2015-03-31 12:30:20 -0700354
355 // Write to a temporary buffer to get the compressed length.
356 SkDynamicMemoryWStream buffer;
357 SkDeflateWStream deflateWStream(&buffer);
halcanary7a14b312015-10-01 07:28:13 -0700358 if (alpha) {
359 bitmap_alpha_to_a8(bitmap, &deflateWStream);
360 } else {
361 bitmap_to_pdf_pixels(bitmap, &deflateWStream);
362 }
halcanary37c46ca2015-03-31 12:30:20 -0700363 deflateWStream.finalize(); // call before detachAsStream().
halcanaryb8fb9932016-03-28 07:58:30 -0700364 std::unique_ptr<SkStreamAsset> asset(buffer.detachAsStream());
halcanary37c46ca2015-03-31 12:30:20 -0700365
halcanary1b5c6042015-02-18 11:29:56 -0800366 SkPDFDict pdfDict("XObject");
367 pdfDict.insertName("Subtype", "Image");
halcanary7a14b312015-10-01 07:28:13 -0700368 pdfDict.insertInt("Width", bitmap.width());
369 pdfDict.insertInt("Height", bitmap.height());
370 if (alpha) {
371 pdfDict.insertName("ColorSpace", "DeviceGray");
372 } else if (bitmap.colorType() == kIndex_8_SkColorType) {
373 SkASSERT(1 == pdf_color_component_count(bitmap.colorType()));
halcanarya25b3372015-04-27 14:00:09 -0700374 pdfDict.insertObject("ColorSpace",
halcanary7b9eabb2016-06-03 08:57:03 -0700375 make_indexed_color_space(bitmap.getColorTable(),
376 bitmap.alphaType()));
halcanary7a14b312015-10-01 07:28:13 -0700377 } else if (1 == pdf_color_component_count(bitmap.colorType())) {
halcanarydb0dcc72015-03-20 12:31:52 -0700378 pdfDict.insertName("ColorSpace", "DeviceGray");
379 } else {
380 pdfDict.insertName("ColorSpace", "DeviceRGB");
381 }
halcanary7a14b312015-10-01 07:28:13 -0700382 if (smask) {
halcanary8103a342016-03-08 15:10:16 -0800383 pdfDict.insertObjRef("SMask", smask);
halcanary1b5c6042015-02-18 11:29:56 -0800384 }
halcanary7a14b312015-10-01 07:28:13 -0700385 pdfDict.insertInt("BitsPerComponent", 8);
halcanarydb0dcc72015-03-20 12:31:52 -0700386 pdfDict.insertName("Filter", "FlateDecode");
halcanary37c46ca2015-03-31 12:30:20 -0700387 pdfDict.insertInt("Length", asset->getLength());
halcanarya8448bc2015-04-17 13:27:24 -0700388 pdfDict.emitObject(stream, objNumMap, substitutes);
halcanary37c46ca2015-03-31 12:30:20 -0700389
390 pdf_stream_begin(stream);
391 stream->writeStream(asset.get(), asset->getLength());
392 pdf_stream_end(stream);
halcanary1b5c6042015-02-18 11:29:56 -0800393}
394
halcanary1b5c6042015-02-18 11:29:56 -0800395////////////////////////////////////////////////////////////////////////////////
halcanarydb0dcc72015-03-20 12:31:52 -0700396
halcanary7a14b312015-10-01 07:28:13 -0700397namespace {
398// This SkPDFObject only outputs the alpha layer of the given bitmap.
halcanary70d15542015-11-22 12:55:04 -0800399class PDFAlphaBitmap final : public SkPDFObject {
halcanary7a14b312015-10-01 07:28:13 -0700400public:
halcanarya50151d2016-03-25 11:57:49 -0700401 PDFAlphaBitmap(sk_sp<SkImage> image) : fImage(std::move(image)) { SkASSERT(fImage); }
halcanary7a14b312015-10-01 07:28:13 -0700402 void emitObject(SkWStream* stream,
403 const SkPDFObjNumMap& objNumMap,
404 const SkPDFSubstituteMap& subs) const override {
halcanarybae235e2016-03-21 10:05:23 -0700405 SkASSERT(fImage);
halcanaryfcad44b2016-03-06 14:47:10 -0800406 emit_image_xobject(stream, fImage.get(), true, nullptr, objNumMap, subs);
halcanary1b5c6042015-02-18 11:29:56 -0800407 }
halcanarybae235e2016-03-21 10:05:23 -0700408 void drop() override { fImage = nullptr; }
halcanary7a14b312015-10-01 07:28:13 -0700409
410private:
halcanarya50151d2016-03-25 11:57:49 -0700411 sk_sp<SkImage> fImage;
halcanary7a14b312015-10-01 07:28:13 -0700412};
413
414} // namespace
415
416////////////////////////////////////////////////////////////////////////////////
417
418namespace {
halcanary70d15542015-11-22 12:55:04 -0800419class PDFDefaultBitmap final : public SkPDFObject {
halcanary7a14b312015-10-01 07:28:13 -0700420public:
halcanary34422612015-10-12 10:11:18 -0700421 void emitObject(SkWStream* stream,
halcanary7a14b312015-10-01 07:28:13 -0700422 const SkPDFObjNumMap& objNumMap,
halcanary34422612015-10-12 10:11:18 -0700423 const SkPDFSubstituteMap& subs) const override {
halcanarybae235e2016-03-21 10:05:23 -0700424 SkASSERT(fImage);
halcanary8103a342016-03-08 15:10:16 -0800425 emit_image_xobject(stream, fImage.get(), false, fSMask, objNumMap, subs);
halcanary7a14b312015-10-01 07:28:13 -0700426 }
427 void addResources(SkPDFObjNumMap* catalog,
halcanary34422612015-10-12 10:11:18 -0700428 const SkPDFSubstituteMap& subs) const override {
halcanarybae235e2016-03-21 10:05:23 -0700429 SkASSERT(fImage);
halcanary7a14b312015-10-01 07:28:13 -0700430 if (fSMask.get()) {
halcanary34422612015-10-12 10:11:18 -0700431 SkPDFObject* obj = subs.getSubstitute(fSMask.get());
halcanary7a14b312015-10-01 07:28:13 -0700432 SkASSERT(obj);
halcanary34422612015-10-12 10:11:18 -0700433 catalog->addObjectRecursively(obj, subs);
halcanary7a14b312015-10-01 07:28:13 -0700434 }
435 }
halcanarybae235e2016-03-21 10:05:23 -0700436 void drop() override { fImage = nullptr; fSMask = nullptr; }
halcanarya50151d2016-03-25 11:57:49 -0700437 PDFDefaultBitmap(sk_sp<SkImage> image, sk_sp<SkPDFObject> smask)
438 : fImage(std::move(image)), fSMask(std::move(smask)) { SkASSERT(fImage); }
halcanary7a14b312015-10-01 07:28:13 -0700439
440private:
halcanarya50151d2016-03-25 11:57:49 -0700441 sk_sp<SkImage> fImage;
halcanarybae235e2016-03-21 10:05:23 -0700442 sk_sp<SkPDFObject> fSMask;
halcanary7a14b312015-10-01 07:28:13 -0700443};
444} // namespace
445
446////////////////////////////////////////////////////////////////////////////////
halcanary1b5c6042015-02-18 11:29:56 -0800447
halcanarya8448bc2015-04-17 13:27:24 -0700448namespace {
449/**
halcanary7a14b312015-10-01 07:28:13 -0700450 * This PDFObject assumes that its constructor was handed YUV or
451 * Grayscale JFIF Jpeg-encoded data that can be directly embedded
452 * into a PDF.
halcanarya8448bc2015-04-17 13:27:24 -0700453 */
halcanary70d15542015-11-22 12:55:04 -0800454class PDFJpegBitmap final : public SkPDFObject {
halcanarya8448bc2015-04-17 13:27:24 -0700455public:
halcanary7a14b312015-10-01 07:28:13 -0700456 SkISize fSize;
halcanary48810a02016-03-07 14:57:50 -0800457 sk_sp<SkData> fData;
halcanary96287f72015-05-07 11:46:59 -0700458 bool fIsYUV;
halcanary7a14b312015-10-01 07:28:13 -0700459 PDFJpegBitmap(SkISize size, SkData* data, bool isYUV)
halcanarybae235e2016-03-21 10:05:23 -0700460 : fSize(size), fData(SkRef(data)), fIsYUV(isYUV) { SkASSERT(data); }
halcanarya8448bc2015-04-17 13:27:24 -0700461 void emitObject(SkWStream*,
462 const SkPDFObjNumMap&,
halcanarya060eba2015-08-19 12:26:46 -0700463 const SkPDFSubstituteMap&) const override;
halcanarybae235e2016-03-21 10:05:23 -0700464 void drop() override { fData = nullptr; }
halcanarya8448bc2015-04-17 13:27:24 -0700465};
466
467void PDFJpegBitmap::emitObject(SkWStream* stream,
468 const SkPDFObjNumMap& objNumMap,
halcanarya060eba2015-08-19 12:26:46 -0700469 const SkPDFSubstituteMap& substituteMap) const {
halcanarybae235e2016-03-21 10:05:23 -0700470 SkASSERT(fData);
halcanarya8448bc2015-04-17 13:27:24 -0700471 SkPDFDict pdfDict("XObject");
472 pdfDict.insertName("Subtype", "Image");
halcanary7a14b312015-10-01 07:28:13 -0700473 pdfDict.insertInt("Width", fSize.width());
474 pdfDict.insertInt("Height", fSize.height());
halcanary96287f72015-05-07 11:46:59 -0700475 if (fIsYUV) {
476 pdfDict.insertName("ColorSpace", "DeviceRGB");
477 } else {
478 pdfDict.insertName("ColorSpace", "DeviceGray");
479 }
halcanarya8448bc2015-04-17 13:27:24 -0700480 pdfDict.insertInt("BitsPerComponent", 8);
481 pdfDict.insertName("Filter", "DCTDecode");
482 pdfDict.insertInt("ColorTransform", 0);
483 pdfDict.insertInt("Length", SkToInt(fData->size()));
484 pdfDict.emitObject(stream, objNumMap, substituteMap);
485 pdf_stream_begin(stream);
486 stream->write(fData->data(), fData->size());
487 pdf_stream_end(stream);
488}
489} // namespace
490
491////////////////////////////////////////////////////////////////////////////////
492
halcanarya50151d2016-03-25 11:57:49 -0700493sk_sp<SkPDFObject> SkPDFCreateBitmapObject(sk_sp<SkImage> image,
494 SkPixelSerializer* pixelSerializer) {
495 SkASSERT(image);
halcanary48810a02016-03-07 14:57:50 -0800496 sk_sp<SkData> data(image->refEncoded());
halcanary7a14b312015-10-01 07:28:13 -0700497 SkJFIFInfo info;
halcanaryfcad44b2016-03-06 14:47:10 -0800498 if (data && SkIsJFIF(data.get(), &info) &&
halcanary7363e132016-02-25 17:21:40 -0800499 (!pixelSerializer ||
500 pixelSerializer->useEncodedData(data->data(), data->size()))) {
501 // If there is a SkPixelSerializer, give it a chance to
502 // re-encode the JPEG with more compression by returning false
503 // from useEncodedData.
halcanary7a14b312015-10-01 07:28:13 -0700504 bool yuv = info.fType == SkJFIFInfo::kYCbCr;
505 if (info.fSize == image->dimensions()) { // Sanity check.
506 // hold on to data, not image.
507 #ifdef SK_PDF_IMAGE_STATS
508 gJpegImageObjects.fetch_add(1);
509 #endif
halcanarya50151d2016-03-25 11:57:49 -0700510 return sk_make_sp<PDFJpegBitmap>(info.fSize, data.get(), yuv);
halcanarya8448bc2015-04-17 13:27:24 -0700511 }
512 }
halcanary712fdf72015-12-10 08:59:43 -0800513
514 if (pixelSerializer) {
515 SkBitmap bm;
516 SkAutoPixmapUnlock apu;
halcanarya50151d2016-03-25 11:57:49 -0700517 if (as_IB(image.get())->getROPixels(&bm) && bm.requestLock(&apu)) {
halcanary712fdf72015-12-10 08:59:43 -0800518 data.reset(pixelSerializer->encode(apu.pixmap()));
halcanaryfcad44b2016-03-06 14:47:10 -0800519 if (data && SkIsJFIF(data.get(), &info)) {
halcanary712fdf72015-12-10 08:59:43 -0800520 bool yuv = info.fType == SkJFIFInfo::kYCbCr;
521 if (info.fSize == image->dimensions()) { // Sanity check.
halcanarya50151d2016-03-25 11:57:49 -0700522 return sk_make_sp<PDFJpegBitmap>(info.fSize, data.get(), yuv);
halcanary712fdf72015-12-10 08:59:43 -0800523 }
524 }
525 }
526 }
527
halcanarya50151d2016-03-25 11:57:49 -0700528 sk_sp<SkPDFObject> smask;
529 if (!image_compute_is_opaque(image.get())) {
530 smask = sk_make_sp<PDFAlphaBitmap>(image);
531 }
halcanary7a14b312015-10-01 07:28:13 -0700532 #ifdef SK_PDF_IMAGE_STATS
533 gRegularImageObjects.fetch_add(1);
534 #endif
halcanarya50151d2016-03-25 11:57:49 -0700535 return sk_make_sp<PDFDefaultBitmap>(std::move(image), std::move(smask));
halcanary1b5c6042015-02-18 11:29:56 -0800536}