blob: ea6887ac80d784df7a86c7f88865490a7eec0dbd [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) {
Brian Osman7992da32016-11-18 11:28:24 -050020 if(as_IB(image)->getROPixels(dst, SkDestinationSurfaceColorMode::kLegacy)
halcanary7a14b312015-10-01 07:28:13 -070021 && 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.
brianosman69c166d2016-08-17 14:01:05 -070035 dst->setInfo(SkImageInfo::MakeN32(image->width(), image->height(), image->alphaType()));
halcanary7a14b312015-10-01 07:28:13 -070036}
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,
halcanary530032a2016-08-18 14:22:52 -0700349 const SkPDFObjNumMap& objNumMap) {
halcanary7a14b312015-10-01 07:28:13 -0700350 SkBitmap bitmap;
351 image_get_ro_pixels(image, &bitmap); // TODO(halcanary): test
352 SkAutoLockPixels autoLockPixels(bitmap); // with malformed images.
halcanary37c46ca2015-03-31 12:30:20 -0700353
354 // Write to a temporary buffer to get the compressed length.
355 SkDynamicMemoryWStream buffer;
356 SkDeflateWStream deflateWStream(&buffer);
halcanary7a14b312015-10-01 07:28:13 -0700357 if (alpha) {
358 bitmap_alpha_to_a8(bitmap, &deflateWStream);
359 } else {
360 bitmap_to_pdf_pixels(bitmap, &deflateWStream);
361 }
halcanary37c46ca2015-03-31 12:30:20 -0700362 deflateWStream.finalize(); // call before detachAsStream().
halcanaryb8fb9932016-03-28 07:58:30 -0700363 std::unique_ptr<SkStreamAsset> asset(buffer.detachAsStream());
halcanary37c46ca2015-03-31 12:30:20 -0700364
halcanary1b5c6042015-02-18 11:29:56 -0800365 SkPDFDict pdfDict("XObject");
366 pdfDict.insertName("Subtype", "Image");
halcanary7a14b312015-10-01 07:28:13 -0700367 pdfDict.insertInt("Width", bitmap.width());
368 pdfDict.insertInt("Height", bitmap.height());
369 if (alpha) {
370 pdfDict.insertName("ColorSpace", "DeviceGray");
371 } else if (bitmap.colorType() == kIndex_8_SkColorType) {
372 SkASSERT(1 == pdf_color_component_count(bitmap.colorType()));
halcanarya25b3372015-04-27 14:00:09 -0700373 pdfDict.insertObject("ColorSpace",
halcanary7b9eabb2016-06-03 08:57:03 -0700374 make_indexed_color_space(bitmap.getColorTable(),
375 bitmap.alphaType()));
halcanary7a14b312015-10-01 07:28:13 -0700376 } else if (1 == pdf_color_component_count(bitmap.colorType())) {
halcanarydb0dcc72015-03-20 12:31:52 -0700377 pdfDict.insertName("ColorSpace", "DeviceGray");
378 } else {
379 pdfDict.insertName("ColorSpace", "DeviceRGB");
380 }
halcanary7a14b312015-10-01 07:28:13 -0700381 if (smask) {
halcanary8103a342016-03-08 15:10:16 -0800382 pdfDict.insertObjRef("SMask", smask);
halcanary1b5c6042015-02-18 11:29:56 -0800383 }
halcanary7a14b312015-10-01 07:28:13 -0700384 pdfDict.insertInt("BitsPerComponent", 8);
halcanarydb0dcc72015-03-20 12:31:52 -0700385 pdfDict.insertName("Filter", "FlateDecode");
halcanary37c46ca2015-03-31 12:30:20 -0700386 pdfDict.insertInt("Length", asset->getLength());
halcanary530032a2016-08-18 14:22:52 -0700387 pdfDict.emitObject(stream, objNumMap);
halcanary37c46ca2015-03-31 12:30:20 -0700388
389 pdf_stream_begin(stream);
390 stream->writeStream(asset.get(), asset->getLength());
391 pdf_stream_end(stream);
halcanary1b5c6042015-02-18 11:29:56 -0800392}
393
halcanary1b5c6042015-02-18 11:29:56 -0800394////////////////////////////////////////////////////////////////////////////////
halcanarydb0dcc72015-03-20 12:31:52 -0700395
halcanary7a14b312015-10-01 07:28:13 -0700396namespace {
397// This SkPDFObject only outputs the alpha layer of the given bitmap.
halcanary70d15542015-11-22 12:55:04 -0800398class PDFAlphaBitmap final : public SkPDFObject {
halcanary7a14b312015-10-01 07:28:13 -0700399public:
halcanarya50151d2016-03-25 11:57:49 -0700400 PDFAlphaBitmap(sk_sp<SkImage> image) : fImage(std::move(image)) { SkASSERT(fImage); }
halcanary7a14b312015-10-01 07:28:13 -0700401 void emitObject(SkWStream* stream,
halcanary530032a2016-08-18 14:22:52 -0700402 const SkPDFObjNumMap& objNumMap) const override {
halcanarybae235e2016-03-21 10:05:23 -0700403 SkASSERT(fImage);
halcanary530032a2016-08-18 14:22:52 -0700404 emit_image_xobject(stream, fImage.get(), true, nullptr, objNumMap);
halcanary1b5c6042015-02-18 11:29:56 -0800405 }
halcanarybae235e2016-03-21 10:05:23 -0700406 void drop() override { fImage = nullptr; }
halcanary7a14b312015-10-01 07:28:13 -0700407
408private:
halcanarya50151d2016-03-25 11:57:49 -0700409 sk_sp<SkImage> fImage;
halcanary7a14b312015-10-01 07:28:13 -0700410};
411
412} // namespace
413
414////////////////////////////////////////////////////////////////////////////////
415
416namespace {
halcanary70d15542015-11-22 12:55:04 -0800417class PDFDefaultBitmap final : public SkPDFObject {
halcanary7a14b312015-10-01 07:28:13 -0700418public:
halcanary34422612015-10-12 10:11:18 -0700419 void emitObject(SkWStream* stream,
halcanary530032a2016-08-18 14:22:52 -0700420 const SkPDFObjNumMap& objNumMap) const override {
halcanarybae235e2016-03-21 10:05:23 -0700421 SkASSERT(fImage);
halcanary530032a2016-08-18 14:22:52 -0700422 emit_image_xobject(stream, fImage.get(), false, fSMask, objNumMap);
halcanary7a14b312015-10-01 07:28:13 -0700423 }
halcanary530032a2016-08-18 14:22:52 -0700424 void addResources(SkPDFObjNumMap* catalog) const override {
425 catalog->addObjectRecursively(fSMask.get());
halcanary7a14b312015-10-01 07:28:13 -0700426 }
halcanarybae235e2016-03-21 10:05:23 -0700427 void drop() override { fImage = nullptr; fSMask = nullptr; }
halcanarya50151d2016-03-25 11:57:49 -0700428 PDFDefaultBitmap(sk_sp<SkImage> image, sk_sp<SkPDFObject> smask)
429 : fImage(std::move(image)), fSMask(std::move(smask)) { SkASSERT(fImage); }
halcanary7a14b312015-10-01 07:28:13 -0700430
431private:
halcanarya50151d2016-03-25 11:57:49 -0700432 sk_sp<SkImage> fImage;
halcanarybae235e2016-03-21 10:05:23 -0700433 sk_sp<SkPDFObject> fSMask;
halcanary7a14b312015-10-01 07:28:13 -0700434};
435} // namespace
436
437////////////////////////////////////////////////////////////////////////////////
halcanary1b5c6042015-02-18 11:29:56 -0800438
halcanarya8448bc2015-04-17 13:27:24 -0700439namespace {
440/**
halcanary7a14b312015-10-01 07:28:13 -0700441 * This PDFObject assumes that its constructor was handed YUV or
442 * Grayscale JFIF Jpeg-encoded data that can be directly embedded
443 * into a PDF.
halcanarya8448bc2015-04-17 13:27:24 -0700444 */
halcanary70d15542015-11-22 12:55:04 -0800445class PDFJpegBitmap final : public SkPDFObject {
halcanarya8448bc2015-04-17 13:27:24 -0700446public:
halcanary7a14b312015-10-01 07:28:13 -0700447 SkISize fSize;
halcanary48810a02016-03-07 14:57:50 -0800448 sk_sp<SkData> fData;
halcanary96287f72015-05-07 11:46:59 -0700449 bool fIsYUV;
halcanary7a14b312015-10-01 07:28:13 -0700450 PDFJpegBitmap(SkISize size, SkData* data, bool isYUV)
halcanarybae235e2016-03-21 10:05:23 -0700451 : fSize(size), fData(SkRef(data)), fIsYUV(isYUV) { SkASSERT(data); }
halcanary530032a2016-08-18 14:22:52 -0700452 void emitObject(SkWStream*, const SkPDFObjNumMap&) const override;
halcanarybae235e2016-03-21 10:05:23 -0700453 void drop() override { fData = nullptr; }
halcanarya8448bc2015-04-17 13:27:24 -0700454};
455
456void PDFJpegBitmap::emitObject(SkWStream* stream,
halcanary530032a2016-08-18 14:22:52 -0700457 const SkPDFObjNumMap& objNumMap) const {
halcanarybae235e2016-03-21 10:05:23 -0700458 SkASSERT(fData);
halcanarya8448bc2015-04-17 13:27:24 -0700459 SkPDFDict pdfDict("XObject");
460 pdfDict.insertName("Subtype", "Image");
halcanary7a14b312015-10-01 07:28:13 -0700461 pdfDict.insertInt("Width", fSize.width());
462 pdfDict.insertInt("Height", fSize.height());
halcanary96287f72015-05-07 11:46:59 -0700463 if (fIsYUV) {
464 pdfDict.insertName("ColorSpace", "DeviceRGB");
465 } else {
466 pdfDict.insertName("ColorSpace", "DeviceGray");
467 }
halcanarya8448bc2015-04-17 13:27:24 -0700468 pdfDict.insertInt("BitsPerComponent", 8);
469 pdfDict.insertName("Filter", "DCTDecode");
470 pdfDict.insertInt("ColorTransform", 0);
471 pdfDict.insertInt("Length", SkToInt(fData->size()));
halcanary530032a2016-08-18 14:22:52 -0700472 pdfDict.emitObject(stream, objNumMap);
halcanarya8448bc2015-04-17 13:27:24 -0700473 pdf_stream_begin(stream);
474 stream->write(fData->data(), fData->size());
475 pdf_stream_end(stream);
476}
477} // namespace
478
479////////////////////////////////////////////////////////////////////////////////
480
halcanarya50151d2016-03-25 11:57:49 -0700481sk_sp<SkPDFObject> SkPDFCreateBitmapObject(sk_sp<SkImage> image,
482 SkPixelSerializer* pixelSerializer) {
483 SkASSERT(image);
halcanary48810a02016-03-07 14:57:50 -0800484 sk_sp<SkData> data(image->refEncoded());
halcanary7a14b312015-10-01 07:28:13 -0700485 SkJFIFInfo info;
halcanaryfcad44b2016-03-06 14:47:10 -0800486 if (data && SkIsJFIF(data.get(), &info) &&
halcanary7363e132016-02-25 17:21:40 -0800487 (!pixelSerializer ||
488 pixelSerializer->useEncodedData(data->data(), data->size()))) {
489 // If there is a SkPixelSerializer, give it a chance to
490 // re-encode the JPEG with more compression by returning false
491 // from useEncodedData.
halcanary7a14b312015-10-01 07:28:13 -0700492 bool yuv = info.fType == SkJFIFInfo::kYCbCr;
493 if (info.fSize == image->dimensions()) { // Sanity check.
494 // hold on to data, not image.
495 #ifdef SK_PDF_IMAGE_STATS
496 gJpegImageObjects.fetch_add(1);
497 #endif
halcanarya50151d2016-03-25 11:57:49 -0700498 return sk_make_sp<PDFJpegBitmap>(info.fSize, data.get(), yuv);
halcanarya8448bc2015-04-17 13:27:24 -0700499 }
500 }
halcanary712fdf72015-12-10 08:59:43 -0800501
502 if (pixelSerializer) {
503 SkBitmap bm;
504 SkAutoPixmapUnlock apu;
Brian Osman7992da32016-11-18 11:28:24 -0500505 if (as_IB(image.get())->getROPixels(&bm, SkDestinationSurfaceColorMode::kLegacy) &&
506 bm.requestLock(&apu)) {
halcanary712fdf72015-12-10 08:59:43 -0800507 data.reset(pixelSerializer->encode(apu.pixmap()));
halcanaryfcad44b2016-03-06 14:47:10 -0800508 if (data && SkIsJFIF(data.get(), &info)) {
halcanary712fdf72015-12-10 08:59:43 -0800509 bool yuv = info.fType == SkJFIFInfo::kYCbCr;
510 if (info.fSize == image->dimensions()) { // Sanity check.
halcanarya50151d2016-03-25 11:57:49 -0700511 return sk_make_sp<PDFJpegBitmap>(info.fSize, data.get(), yuv);
halcanary712fdf72015-12-10 08:59:43 -0800512 }
513 }
514 }
515 }
516
halcanarya50151d2016-03-25 11:57:49 -0700517 sk_sp<SkPDFObject> smask;
518 if (!image_compute_is_opaque(image.get())) {
519 smask = sk_make_sp<PDFAlphaBitmap>(image);
520 }
halcanary7a14b312015-10-01 07:28:13 -0700521 #ifdef SK_PDF_IMAGE_STATS
522 gRegularImageObjects.fetch_add(1);
523 #endif
halcanarya50151d2016-03-25 11:57:49 -0700524 return sk_make_sp<PDFDefaultBitmap>(std::move(image), std::move(smask));
halcanary1b5c6042015-02-18 11:29:56 -0800525}