blob: 5e617decf4feb749d510975020fa2836bd64b6ec [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
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();
186 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) {
halcanaryaa4ba902015-11-06 07:27:23 -0800195 uint32_t color = *src++;
196 U8CPU alpha = SkGetA32Component(color, colorType);
halcanarydb0dcc72015-03-20 12:31:52 -0700197 if (alpha != SK_AlphaTRANSPARENT) {
halcanaryaa4ba902015-11-06 07:27:23 -0800198 pmcolor_to_rgb24(color, dst, colorType);
halcanarydb0dcc72015-03-20 12:31:52 -0700199 } else {
halcanaryaa4ba902015-11-06 07:27:23 -0800200 get_neighbor_avg_color(bm, x, y, dst, colorType);
halcanarydb0dcc72015-03-20 12:31:52 -0700201 }
202 dst += 3;
203 }
204 out->write(scanline.get(), 3 * bm.width());
halcanary1b5c6042015-02-18 11:29:56 -0800205 }
halcanarydb0dcc72015-03-20 12:31:52 -0700206 return;
halcanary1b5c6042015-02-18 11:29:56 -0800207 }
halcanarydb0dcc72015-03-20 12:31:52 -0700208 case kRGB_565_SkColorType: {
halcanaryaa4ba902015-11-06 07:27:23 -0800209 SkASSERT(3 == pdf_color_component_count(colorType));
halcanarydb0dcc72015-03-20 12:31:52 -0700210 SkAutoTMalloc<uint8_t> scanline(3 * bm.width());
211 for (int y = 0; y < bm.height(); ++y) {
212 const uint16_t* src = bm.getAddr16(0, y);
213 uint8_t* dst = scanline.get();
214 for (int x = 0; x < bm.width(); ++x) {
215 U16CPU color565 = *src++;
216 *dst++ = SkPacked16ToR32(color565);
217 *dst++ = SkPacked16ToG32(color565);
218 *dst++ = SkPacked16ToB32(color565);
219 }
220 out->write(scanline.get(), 3 * bm.width());
221 }
222 return;
223 }
224 case kAlpha_8_SkColorType:
halcanaryaa4ba902015-11-06 07:27:23 -0800225 SkASSERT(1 == pdf_color_component_count(colorType));
halcanarydb0dcc72015-03-20 12:31:52 -0700226 fill_stream(out, '\x00', pixel_count(bm));
227 return;
228 case kGray_8_SkColorType:
229 case kIndex_8_SkColorType:
halcanaryaa4ba902015-11-06 07:27:23 -0800230 SkASSERT(1 == pdf_color_component_count(colorType));
halcanarydb0dcc72015-03-20 12:31:52 -0700231 // these two formats need no transformation to serialize.
232 for (int y = 0; y < bm.height(); ++y) {
233 out->write(bm.getAddr8(0, y), bm.width());
234 }
235 return;
236 case kUnknown_SkColorType:
237 case kARGB_4444_SkColorType:
238 default:
239 SkDEBUGFAIL("unexpected color type");
halcanary1b5c6042015-02-18 11:29:56 -0800240 }
241}
242
halcanarydb0dcc72015-03-20 12:31:52 -0700243////////////////////////////////////////////////////////////////////////////////
244
245static void bitmap_alpha_to_a8(const SkBitmap& bitmap, SkWStream* out) {
246 if (!bitmap.getPixels()) {
247 fill_stream(out, '\xFF', pixel_count(bitmap));
halcanary1b5c6042015-02-18 11:29:56 -0800248 return;
249 }
halcanarydb0dcc72015-03-20 12:31:52 -0700250 SkBitmap copy;
251 const SkBitmap& bm = not4444(bitmap, &copy);
252 SkAutoLockPixels autoLockPixels(bm);
halcanaryaa4ba902015-11-06 07:27:23 -0800253 SkColorType colorType = bm.colorType();
254 switch (colorType) {
255 case kRGBA_8888_SkColorType:
256 case kBGRA_8888_SkColorType: {
halcanarydb0dcc72015-03-20 12:31:52 -0700257 SkAutoTMalloc<uint8_t> scanline(bm.width());
258 for (int y = 0; y < bm.height(); ++y) {
259 uint8_t* dst = scanline.get();
260 const SkPMColor* src = bm.getAddr32(0, y);
261 for (int x = 0; x < bm.width(); ++x) {
halcanaryaa4ba902015-11-06 07:27:23 -0800262 *dst++ = SkGetA32Component(*src++, colorType);
halcanarydb0dcc72015-03-20 12:31:52 -0700263 }
264 out->write(scanline.get(), bm.width());
265 }
266 return;
halcanary1b5c6042015-02-18 11:29:56 -0800267 }
halcanarydb0dcc72015-03-20 12:31:52 -0700268 case kAlpha_8_SkColorType:
269 for (int y = 0; y < bm.height(); ++y) {
270 out->write(bm.getAddr8(0, y), bm.width());
271 }
272 return;
273 case kIndex_8_SkColorType: {
274 SkColorTable* ct = bm.getColorTable();
275 SkASSERT(ct);
276 SkAutoTMalloc<uint8_t> scanline(bm.width());
277 for (int y = 0; y < bm.height(); ++y) {
278 uint8_t* dst = scanline.get();
279 const uint8_t* src = bm.getAddr8(0, y);
280 for (int x = 0; x < bm.width(); ++x) {
281 *dst++ = SkGetPackedA32((*ct)[*src++]);
282 }
283 out->write(scanline.get(), bm.width());
284 }
285 return;
286 }
287 case kRGB_565_SkColorType:
288 case kGray_8_SkColorType:
289 SkDEBUGFAIL("color type has no alpha");
290 return;
291 case kARGB_4444_SkColorType:
292 SkDEBUGFAIL("4444 color type should have been converted to N32");
293 return;
294 case kUnknown_SkColorType:
295 default:
296 SkDEBUGFAIL("unexpected color type");
halcanary1b5c6042015-02-18 11:29:56 -0800297 }
298}
299
halcanary8103a342016-03-08 15:10:16 -0800300static sk_sp<SkPDFArray> make_indexed_color_space(const SkColorTable* table) {
301 auto result = sk_make_sp<SkPDFArray>();
halcanarydb0dcc72015-03-20 12:31:52 -0700302 result->reserve(4);
303 result->appendName("Indexed");
304 result->appendName("DeviceRGB");
305 SkASSERT(table);
306 if (table->count() < 1) {
307 result->appendInt(0);
308 char shortTableArray[3] = {0, 0, 0};
309 SkString tableString(shortTableArray, SK_ARRAY_COUNT(shortTableArray));
halcanarya25b3372015-04-27 14:00:09 -0700310 result->appendString(tableString);
halcanary51d04d32016-03-08 13:03:55 -0800311 return result;
halcanarydb0dcc72015-03-20 12:31:52 -0700312 }
313 result->appendInt(table->count() - 1); // maximum color index.
314
315 // Potentially, this could be represented in fewer bytes with a stream.
316 // Max size as a string is 1.5k.
317 char tableArray[256 * 3];
318 SkASSERT(3u * table->count() <= SK_ARRAY_COUNT(tableArray));
319 uint8_t* tablePtr = reinterpret_cast<uint8_t*>(tableArray);
320 const SkPMColor* colors = table->readColors();
321 for (int i = 0; i < table->count(); i++) {
halcanaryaa4ba902015-11-06 07:27:23 -0800322 pmcolor_to_rgb24(colors[i], tablePtr, kN32_SkColorType);
halcanarydb0dcc72015-03-20 12:31:52 -0700323 tablePtr += 3;
324 }
325 SkString tableString(tableArray, 3 * table->count());
halcanarya25b3372015-04-27 14:00:09 -0700326 result->appendString(tableString);
halcanary51d04d32016-03-08 13:03:55 -0800327 return result;
halcanary1b5c6042015-02-18 11:29:56 -0800328}
329
halcanary7a14b312015-10-01 07:28:13 -0700330static void emit_image_xobject(SkWStream* stream,
331 const SkImage* image,
332 bool alpha,
halcanary8103a342016-03-08 15:10:16 -0800333 const sk_sp<SkPDFObject>& smask,
halcanary7a14b312015-10-01 07:28:13 -0700334 const SkPDFObjNumMap& objNumMap,
335 const SkPDFSubstituteMap& substitutes) {
336 SkBitmap bitmap;
337 image_get_ro_pixels(image, &bitmap); // TODO(halcanary): test
338 SkAutoLockPixels autoLockPixels(bitmap); // with malformed images.
halcanary37c46ca2015-03-31 12:30:20 -0700339
340 // Write to a temporary buffer to get the compressed length.
341 SkDynamicMemoryWStream buffer;
342 SkDeflateWStream deflateWStream(&buffer);
halcanary7a14b312015-10-01 07:28:13 -0700343 if (alpha) {
344 bitmap_alpha_to_a8(bitmap, &deflateWStream);
345 } else {
346 bitmap_to_pdf_pixels(bitmap, &deflateWStream);
347 }
halcanary37c46ca2015-03-31 12:30:20 -0700348 deflateWStream.finalize(); // call before detachAsStream().
349 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
350
halcanary1b5c6042015-02-18 11:29:56 -0800351 SkPDFDict pdfDict("XObject");
352 pdfDict.insertName("Subtype", "Image");
halcanary7a14b312015-10-01 07:28:13 -0700353 pdfDict.insertInt("Width", bitmap.width());
354 pdfDict.insertInt("Height", bitmap.height());
355 if (alpha) {
356 pdfDict.insertName("ColorSpace", "DeviceGray");
357 } else if (bitmap.colorType() == kIndex_8_SkColorType) {
358 SkASSERT(1 == pdf_color_component_count(bitmap.colorType()));
halcanarya25b3372015-04-27 14:00:09 -0700359 pdfDict.insertObject("ColorSpace",
halcanary7a14b312015-10-01 07:28:13 -0700360 make_indexed_color_space(bitmap.getColorTable()));
361 } else if (1 == pdf_color_component_count(bitmap.colorType())) {
halcanarydb0dcc72015-03-20 12:31:52 -0700362 pdfDict.insertName("ColorSpace", "DeviceGray");
363 } else {
364 pdfDict.insertName("ColorSpace", "DeviceRGB");
365 }
halcanary7a14b312015-10-01 07:28:13 -0700366 if (smask) {
halcanary8103a342016-03-08 15:10:16 -0800367 pdfDict.insertObjRef("SMask", smask);
halcanary1b5c6042015-02-18 11:29:56 -0800368 }
halcanary7a14b312015-10-01 07:28:13 -0700369 pdfDict.insertInt("BitsPerComponent", 8);
halcanarydb0dcc72015-03-20 12:31:52 -0700370 pdfDict.insertName("Filter", "FlateDecode");
halcanary37c46ca2015-03-31 12:30:20 -0700371 pdfDict.insertInt("Length", asset->getLength());
halcanarya8448bc2015-04-17 13:27:24 -0700372 pdfDict.emitObject(stream, objNumMap, substitutes);
halcanary37c46ca2015-03-31 12:30:20 -0700373
374 pdf_stream_begin(stream);
375 stream->writeStream(asset.get(), asset->getLength());
376 pdf_stream_end(stream);
halcanary1b5c6042015-02-18 11:29:56 -0800377}
378
halcanary1b5c6042015-02-18 11:29:56 -0800379////////////////////////////////////////////////////////////////////////////////
halcanarydb0dcc72015-03-20 12:31:52 -0700380
halcanary7a14b312015-10-01 07:28:13 -0700381namespace {
382// This SkPDFObject only outputs the alpha layer of the given bitmap.
halcanary70d15542015-11-22 12:55:04 -0800383class PDFAlphaBitmap final : public SkPDFObject {
halcanary7a14b312015-10-01 07:28:13 -0700384public:
halcanarybae235e2016-03-21 10:05:23 -0700385 PDFAlphaBitmap(const SkImage* image) : fImage(SkRef(image)) { SkASSERT(image); }
halcanary7a14b312015-10-01 07:28:13 -0700386 void emitObject(SkWStream* stream,
387 const SkPDFObjNumMap& objNumMap,
388 const SkPDFSubstituteMap& subs) const override {
halcanarybae235e2016-03-21 10:05:23 -0700389 SkASSERT(fImage);
halcanaryfcad44b2016-03-06 14:47:10 -0800390 emit_image_xobject(stream, fImage.get(), true, nullptr, objNumMap, subs);
halcanary1b5c6042015-02-18 11:29:56 -0800391 }
halcanarybae235e2016-03-21 10:05:23 -0700392 void drop() override { fImage = nullptr; }
halcanary7a14b312015-10-01 07:28:13 -0700393
394private:
halcanary48810a02016-03-07 14:57:50 -0800395 sk_sp<const SkImage> fImage;
halcanary7a14b312015-10-01 07:28:13 -0700396};
397
398} // namespace
399
400////////////////////////////////////////////////////////////////////////////////
401
402namespace {
halcanary70d15542015-11-22 12:55:04 -0800403class PDFDefaultBitmap final : public SkPDFObject {
halcanary7a14b312015-10-01 07:28:13 -0700404public:
halcanary34422612015-10-12 10:11:18 -0700405 void emitObject(SkWStream* stream,
halcanary7a14b312015-10-01 07:28:13 -0700406 const SkPDFObjNumMap& objNumMap,
halcanary34422612015-10-12 10:11:18 -0700407 const SkPDFSubstituteMap& subs) const override {
halcanarybae235e2016-03-21 10:05:23 -0700408 SkASSERT(fImage);
halcanary8103a342016-03-08 15:10:16 -0800409 emit_image_xobject(stream, fImage.get(), false, fSMask, objNumMap, subs);
halcanary7a14b312015-10-01 07:28:13 -0700410 }
411 void addResources(SkPDFObjNumMap* catalog,
halcanary34422612015-10-12 10:11:18 -0700412 const SkPDFSubstituteMap& subs) const override {
halcanarybae235e2016-03-21 10:05:23 -0700413 SkASSERT(fImage);
halcanary7a14b312015-10-01 07:28:13 -0700414 if (fSMask.get()) {
halcanary34422612015-10-12 10:11:18 -0700415 SkPDFObject* obj = subs.getSubstitute(fSMask.get());
halcanary7a14b312015-10-01 07:28:13 -0700416 SkASSERT(obj);
halcanary34422612015-10-12 10:11:18 -0700417 catalog->addObjectRecursively(obj, subs);
halcanary7a14b312015-10-01 07:28:13 -0700418 }
419 }
halcanarybae235e2016-03-21 10:05:23 -0700420 void drop() override { fImage = nullptr; fSMask = nullptr; }
halcanary7a14b312015-10-01 07:28:13 -0700421 PDFDefaultBitmap(const SkImage* image, SkPDFObject* smask)
halcanarybae235e2016-03-21 10:05:23 -0700422 : fImage(SkRef(image)), fSMask(smask) { SkASSERT(fImage); }
halcanary7a14b312015-10-01 07:28:13 -0700423
424private:
halcanary48810a02016-03-07 14:57:50 -0800425 sk_sp<const SkImage> fImage;
halcanarybae235e2016-03-21 10:05:23 -0700426 sk_sp<SkPDFObject> fSMask;
halcanary7a14b312015-10-01 07:28:13 -0700427};
428} // namespace
429
430////////////////////////////////////////////////////////////////////////////////
halcanary1b5c6042015-02-18 11:29:56 -0800431
halcanarya8448bc2015-04-17 13:27:24 -0700432namespace {
433/**
halcanary7a14b312015-10-01 07:28:13 -0700434 * This PDFObject assumes that its constructor was handed YUV or
435 * Grayscale JFIF Jpeg-encoded data that can be directly embedded
436 * into a PDF.
halcanarya8448bc2015-04-17 13:27:24 -0700437 */
halcanary70d15542015-11-22 12:55:04 -0800438class PDFJpegBitmap final : public SkPDFObject {
halcanarya8448bc2015-04-17 13:27:24 -0700439public:
halcanary7a14b312015-10-01 07:28:13 -0700440 SkISize fSize;
halcanary48810a02016-03-07 14:57:50 -0800441 sk_sp<SkData> fData;
halcanary96287f72015-05-07 11:46:59 -0700442 bool fIsYUV;
halcanary7a14b312015-10-01 07:28:13 -0700443 PDFJpegBitmap(SkISize size, SkData* data, bool isYUV)
halcanarybae235e2016-03-21 10:05:23 -0700444 : fSize(size), fData(SkRef(data)), fIsYUV(isYUV) { SkASSERT(data); }
halcanarya8448bc2015-04-17 13:27:24 -0700445 void emitObject(SkWStream*,
446 const SkPDFObjNumMap&,
halcanarya060eba2015-08-19 12:26:46 -0700447 const SkPDFSubstituteMap&) const override;
halcanarybae235e2016-03-21 10:05:23 -0700448 void drop() override { fData = nullptr; }
halcanarya8448bc2015-04-17 13:27:24 -0700449};
450
451void PDFJpegBitmap::emitObject(SkWStream* stream,
452 const SkPDFObjNumMap& objNumMap,
halcanarya060eba2015-08-19 12:26:46 -0700453 const SkPDFSubstituteMap& substituteMap) const {
halcanarybae235e2016-03-21 10:05:23 -0700454 SkASSERT(fData);
halcanarya8448bc2015-04-17 13:27:24 -0700455 SkPDFDict pdfDict("XObject");
456 pdfDict.insertName("Subtype", "Image");
halcanary7a14b312015-10-01 07:28:13 -0700457 pdfDict.insertInt("Width", fSize.width());
458 pdfDict.insertInt("Height", fSize.height());
halcanary96287f72015-05-07 11:46:59 -0700459 if (fIsYUV) {
460 pdfDict.insertName("ColorSpace", "DeviceRGB");
461 } else {
462 pdfDict.insertName("ColorSpace", "DeviceGray");
463 }
halcanarya8448bc2015-04-17 13:27:24 -0700464 pdfDict.insertInt("BitsPerComponent", 8);
465 pdfDict.insertName("Filter", "DCTDecode");
466 pdfDict.insertInt("ColorTransform", 0);
467 pdfDict.insertInt("Length", SkToInt(fData->size()));
468 pdfDict.emitObject(stream, objNumMap, substituteMap);
469 pdf_stream_begin(stream);
470 stream->write(fData->data(), fData->size());
471 pdf_stream_end(stream);
472}
473} // namespace
474
475////////////////////////////////////////////////////////////////////////////////
476
halcanary712fdf72015-12-10 08:59:43 -0800477SkPDFObject* SkPDFCreateBitmapObject(const SkImage* image,
478 SkPixelSerializer* pixelSerializer) {
halcanary48810a02016-03-07 14:57:50 -0800479 sk_sp<SkData> data(image->refEncoded());
halcanary7a14b312015-10-01 07:28:13 -0700480 SkJFIFInfo info;
halcanaryfcad44b2016-03-06 14:47:10 -0800481 if (data && SkIsJFIF(data.get(), &info) &&
halcanary7363e132016-02-25 17:21:40 -0800482 (!pixelSerializer ||
483 pixelSerializer->useEncodedData(data->data(), data->size()))) {
484 // If there is a SkPixelSerializer, give it a chance to
485 // re-encode the JPEG with more compression by returning false
486 // from useEncodedData.
halcanary7a14b312015-10-01 07:28:13 -0700487 bool yuv = info.fType == SkJFIFInfo::kYCbCr;
488 if (info.fSize == image->dimensions()) { // Sanity check.
489 // hold on to data, not image.
490 #ifdef SK_PDF_IMAGE_STATS
491 gJpegImageObjects.fetch_add(1);
492 #endif
halcanaryfcad44b2016-03-06 14:47:10 -0800493 return new PDFJpegBitmap(info.fSize, data.get(), yuv);
halcanarya8448bc2015-04-17 13:27:24 -0700494 }
495 }
halcanary712fdf72015-12-10 08:59:43 -0800496
497 if (pixelSerializer) {
498 SkBitmap bm;
499 SkAutoPixmapUnlock apu;
500 if (as_IB(image)->getROPixels(&bm) && bm.requestLock(&apu)) {
501 data.reset(pixelSerializer->encode(apu.pixmap()));
halcanaryfcad44b2016-03-06 14:47:10 -0800502 if (data && SkIsJFIF(data.get(), &info)) {
halcanary712fdf72015-12-10 08:59:43 -0800503 bool yuv = info.fType == SkJFIFInfo::kYCbCr;
504 if (info.fSize == image->dimensions()) { // Sanity check.
halcanaryfcad44b2016-03-06 14:47:10 -0800505 return new PDFJpegBitmap(info.fSize, data.get(), yuv);
halcanary712fdf72015-12-10 08:59:43 -0800506 }
507 }
508 }
509 }
510
halcanary7a14b312015-10-01 07:28:13 -0700511 SkPDFObject* smask =
512 image_compute_is_opaque(image) ? nullptr : new PDFAlphaBitmap(image);
513 #ifdef SK_PDF_IMAGE_STATS
514 gRegularImageObjects.fetch_add(1);
515 #endif
516 return new PDFDefaultBitmap(image, smask);
halcanary1b5c6042015-02-18 11:29:56 -0800517}