blob: 0a9453df43ed45cbfdfeda7f5220bcbad21c7ed8 [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()) {
21 return;
22 }
23 // no pixels or wrong size: fill with zeros.
24 SkAlphaType at = image->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
25 dst->setInfo(SkImageInfo::MakeN32(image->width(), image->height(), at));
26}
27
28bool image_compute_is_opaque(const SkImage* image) {
29 if (image->isOpaque()) {
30 return true;
31 }
32 // keep output PDF small at cost of possible resource use.
33 SkBitmap bm;
34 image_get_ro_pixels(image, &bm);
35 return SkBitmap::ComputeIsOpaque(bm);
36}
37
halcanary1b5c6042015-02-18 11:29:56 -080038////////////////////////////////////////////////////////////////////////////////
39
40static void pdf_stream_begin(SkWStream* stream) {
41 static const char streamBegin[] = " stream\n";
42 stream->write(streamBegin, strlen(streamBegin));
43}
44
45static void pdf_stream_end(SkWStream* stream) {
46 static const char streamEnd[] = "\nendstream";
47 stream->write(streamEnd, strlen(streamEnd));
48}
49
halcanarydb0dcc72015-03-20 12:31:52 -070050////////////////////////////////////////////////////////////////////////////////
halcanary1b5c6042015-02-18 11:29:56 -080051
halcanary1b5c6042015-02-18 11:29:56 -080052// write a single byte to a stream n times.
53static void fill_stream(SkWStream* out, char value, size_t n) {
54 char buffer[4096];
55 memset(buffer, value, sizeof(buffer));
halcanarydb0dcc72015-03-20 12:31:52 -070056 for (size_t i = 0; i < n / sizeof(buffer); ++i) {
57 out->write(buffer, sizeof(buffer));
halcanary1b5c6042015-02-18 11:29:56 -080058 }
halcanarydb0dcc72015-03-20 12:31:52 -070059 out->write(buffer, n % sizeof(buffer));
halcanary1b5c6042015-02-18 11:29:56 -080060}
61
halcanaryaa4ba902015-11-06 07:27:23 -080062// TODO(reed@): Decide if these five functions belong in SkColorPriv.h
63static bool SkIsBGRA(SkColorType ct) {
64 SkASSERT(kBGRA_8888_SkColorType == ct || kRGBA_8888_SkColorType == ct);
65 return kBGRA_8888_SkColorType == ct;
66}
67
68// Interpret value as the given 4-byte SkColorType (BGRA_8888 or
69// RGBA_8888) and return the appropriate component. Each component
70// should be interpreted according to the associated SkAlphaType and
71// SkColorProfileType.
72static U8CPU SkGetA32Component(uint32_t value, SkColorType ct) {
73 return (value >> (SkIsBGRA(ct) ? SK_BGRA_A32_SHIFT : SK_RGBA_A32_SHIFT)) & 0xFF;
74}
75static U8CPU SkGetR32Component(uint32_t value, SkColorType ct) {
76 return (value >> (SkIsBGRA(ct) ? SK_BGRA_R32_SHIFT : SK_RGBA_R32_SHIFT)) & 0xFF;
77}
78static U8CPU SkGetG32Component(uint32_t value, SkColorType ct) {
79 return (value >> (SkIsBGRA(ct) ? SK_BGRA_G32_SHIFT : SK_RGBA_G32_SHIFT)) & 0xFF;
80}
81static U8CPU SkGetB32Component(uint32_t value, SkColorType ct) {
82 return (value >> (SkIsBGRA(ct) ? SK_BGRA_B32_SHIFT : SK_RGBA_B32_SHIFT)) & 0xFF;
83}
84
85
halcanarydb0dcc72015-03-20 12:31:52 -070086// unpremultiply and extract R, G, B components.
halcanaryaa4ba902015-11-06 07:27:23 -080087static void pmcolor_to_rgb24(uint32_t color, uint8_t* rgb, SkColorType ct) {
88 uint32_t s = SkUnPreMultiply::GetScale(SkGetA32Component(color, ct));
89 rgb[0] = SkUnPreMultiply::ApplyScale(s, SkGetR32Component(color, ct));
90 rgb[1] = SkUnPreMultiply::ApplyScale(s, SkGetG32Component(color, ct));
91 rgb[2] = SkUnPreMultiply::ApplyScale(s, SkGetB32Component(color, ct));
halcanarydb0dcc72015-03-20 12:31:52 -070092}
93
94/* It is necessary to average the color component of transparent
95 pixels with their surrounding neighbors since the PDF renderer may
96 separately re-sample the alpha and color channels when the image is
97 not displayed at its native resolution. Since an alpha of zero
98 gives no information about the color component, the pathological
99 case is a white image with sharp transparency bounds - the color
100 channel goes to black, and the should-be-transparent pixels are
101 rendered as grey because of the separate soft mask and color
102 resizing. e.g.: gm/bitmappremul.cpp */
103static void get_neighbor_avg_color(const SkBitmap& bm,
104 int xOrig,
105 int yOrig,
halcanaryaa4ba902015-11-06 07:27:23 -0800106 uint8_t rgb[3],
107 SkColorType ct) {
halcanarydb0dcc72015-03-20 12:31:52 -0700108 unsigned a = 0, r = 0, g = 0, b = 0;
109 // Clamp the range to the edge of the bitmap.
110 int ymin = SkTMax(0, yOrig - 1);
111 int ymax = SkTMin(yOrig + 1, bm.height() - 1);
112 int xmin = SkTMax(0, xOrig - 1);
113 int xmax = SkTMin(xOrig + 1, bm.width() - 1);
114 for (int y = ymin; y <= ymax; ++y) {
halcanaryaa4ba902015-11-06 07:27:23 -0800115 uint32_t* scanline = bm.getAddr32(0, y);
halcanarydb0dcc72015-03-20 12:31:52 -0700116 for (int x = xmin; x <= xmax; ++x) {
halcanaryaa4ba902015-11-06 07:27:23 -0800117 uint32_t color = scanline[x];
118 a += SkGetA32Component(color, ct);
119 r += SkGetR32Component(color, ct);
120 g += SkGetG32Component(color, ct);
121 b += SkGetB32Component(color, ct);
halcanary1b5c6042015-02-18 11:29:56 -0800122 }
123 }
halcanarydb0dcc72015-03-20 12:31:52 -0700124 if (a > 0) {
125 rgb[0] = SkToU8(255 * r / a);
126 rgb[1] = SkToU8(255 * g / a);
127 rgb[2] = SkToU8(255 * b / a);
halcanary1b5c6042015-02-18 11:29:56 -0800128 } else {
halcanarydb0dcc72015-03-20 12:31:52 -0700129 rgb[0] = rgb[1] = rgb[2] = 0;
halcanary1b5c6042015-02-18 11:29:56 -0800130 }
131}
132
halcanarydb0dcc72015-03-20 12:31:52 -0700133static size_t pixel_count(const SkBitmap& bm) {
134 return SkToSizeT(bm.width()) * SkToSizeT(bm.height());
135}
136
137static const SkBitmap& not4444(const SkBitmap& input, SkBitmap* copy) {
138 if (input.colorType() != kARGB_4444_SkColorType) {
139 return input;
140 }
141 // ARGB_4444 is rarely used, so we can do a wasteful tmp copy.
142 SkAssertResult(input.copyTo(copy, kN32_SkColorType));
143 copy->setImmutable();
144 return *copy;
145}
146
147static size_t pdf_color_component_count(SkColorType ct) {
148 switch (ct) {
halcanarydb0dcc72015-03-20 12:31:52 -0700149 case kRGB_565_SkColorType:
150 case kARGB_4444_SkColorType:
halcanaryaa4ba902015-11-06 07:27:23 -0800151 case kRGBA_8888_SkColorType:
152 case kBGRA_8888_SkColorType:
halcanarydb0dcc72015-03-20 12:31:52 -0700153 return 3;
154 case kAlpha_8_SkColorType:
155 case kIndex_8_SkColorType:
156 case kGray_8_SkColorType:
157 return 1;
158 case kUnknown_SkColorType:
159 default:
160 SkDEBUGFAIL("unexpected color type");
161 return 0;
162 }
163}
164
165static void bitmap_to_pdf_pixels(const SkBitmap& bitmap, SkWStream* out) {
166 if (!bitmap.getPixels()) {
167 size_t size = pixel_count(bitmap) *
168 pdf_color_component_count(bitmap.colorType());
169 fill_stream(out, '\x00', size);
halcanary1b5c6042015-02-18 11:29:56 -0800170 return;
171 }
halcanarydb0dcc72015-03-20 12:31:52 -0700172 SkBitmap copy;
173 const SkBitmap& bm = not4444(bitmap, &copy);
174 SkAutoLockPixels autoLockPixels(bm);
halcanaryaa4ba902015-11-06 07:27:23 -0800175 SkColorType colorType = bm.colorType();
176 switch (colorType) {
177 case kRGBA_8888_SkColorType:
178 case kBGRA_8888_SkColorType: {
179 SkASSERT(3 == pdf_color_component_count(colorType));
halcanarydb0dcc72015-03-20 12:31:52 -0700180 SkAutoTMalloc<uint8_t> scanline(3 * bm.width());
181 for (int y = 0; y < bm.height(); ++y) {
halcanaryaa4ba902015-11-06 07:27:23 -0800182 const uint32_t* src = bm.getAddr32(0, y);
halcanarydb0dcc72015-03-20 12:31:52 -0700183 uint8_t* dst = scanline.get();
184 for (int x = 0; x < bm.width(); ++x) {
halcanaryaa4ba902015-11-06 07:27:23 -0800185 uint32_t color = *src++;
186 U8CPU alpha = SkGetA32Component(color, colorType);
halcanarydb0dcc72015-03-20 12:31:52 -0700187 if (alpha != SK_AlphaTRANSPARENT) {
halcanaryaa4ba902015-11-06 07:27:23 -0800188 pmcolor_to_rgb24(color, dst, colorType);
halcanarydb0dcc72015-03-20 12:31:52 -0700189 } else {
halcanaryaa4ba902015-11-06 07:27:23 -0800190 get_neighbor_avg_color(bm, x, y, dst, colorType);
halcanarydb0dcc72015-03-20 12:31:52 -0700191 }
192 dst += 3;
193 }
194 out->write(scanline.get(), 3 * bm.width());
halcanary1b5c6042015-02-18 11:29:56 -0800195 }
halcanarydb0dcc72015-03-20 12:31:52 -0700196 return;
halcanary1b5c6042015-02-18 11:29:56 -0800197 }
halcanarydb0dcc72015-03-20 12:31:52 -0700198 case kRGB_565_SkColorType: {
halcanaryaa4ba902015-11-06 07:27:23 -0800199 SkASSERT(3 == pdf_color_component_count(colorType));
halcanarydb0dcc72015-03-20 12:31:52 -0700200 SkAutoTMalloc<uint8_t> scanline(3 * bm.width());
201 for (int y = 0; y < bm.height(); ++y) {
202 const uint16_t* src = bm.getAddr16(0, y);
203 uint8_t* dst = scanline.get();
204 for (int x = 0; x < bm.width(); ++x) {
205 U16CPU color565 = *src++;
206 *dst++ = SkPacked16ToR32(color565);
207 *dst++ = SkPacked16ToG32(color565);
208 *dst++ = SkPacked16ToB32(color565);
209 }
210 out->write(scanline.get(), 3 * bm.width());
211 }
212 return;
213 }
214 case kAlpha_8_SkColorType:
halcanaryaa4ba902015-11-06 07:27:23 -0800215 SkASSERT(1 == pdf_color_component_count(colorType));
halcanarydb0dcc72015-03-20 12:31:52 -0700216 fill_stream(out, '\x00', pixel_count(bm));
217 return;
218 case kGray_8_SkColorType:
219 case kIndex_8_SkColorType:
halcanaryaa4ba902015-11-06 07:27:23 -0800220 SkASSERT(1 == pdf_color_component_count(colorType));
halcanarydb0dcc72015-03-20 12:31:52 -0700221 // these two formats need no transformation to serialize.
222 for (int y = 0; y < bm.height(); ++y) {
223 out->write(bm.getAddr8(0, y), bm.width());
224 }
225 return;
226 case kUnknown_SkColorType:
227 case kARGB_4444_SkColorType:
228 default:
229 SkDEBUGFAIL("unexpected color type");
halcanary1b5c6042015-02-18 11:29:56 -0800230 }
231}
232
halcanarydb0dcc72015-03-20 12:31:52 -0700233////////////////////////////////////////////////////////////////////////////////
234
235static void bitmap_alpha_to_a8(const SkBitmap& bitmap, SkWStream* out) {
236 if (!bitmap.getPixels()) {
237 fill_stream(out, '\xFF', pixel_count(bitmap));
halcanary1b5c6042015-02-18 11:29:56 -0800238 return;
239 }
halcanarydb0dcc72015-03-20 12:31:52 -0700240 SkBitmap copy;
241 const SkBitmap& bm = not4444(bitmap, &copy);
242 SkAutoLockPixels autoLockPixels(bm);
halcanaryaa4ba902015-11-06 07:27:23 -0800243 SkColorType colorType = bm.colorType();
244 switch (colorType) {
245 case kRGBA_8888_SkColorType:
246 case kBGRA_8888_SkColorType: {
halcanarydb0dcc72015-03-20 12:31:52 -0700247 SkAutoTMalloc<uint8_t> scanline(bm.width());
248 for (int y = 0; y < bm.height(); ++y) {
249 uint8_t* dst = scanline.get();
250 const SkPMColor* src = bm.getAddr32(0, y);
251 for (int x = 0; x < bm.width(); ++x) {
halcanaryaa4ba902015-11-06 07:27:23 -0800252 *dst++ = SkGetA32Component(*src++, colorType);
halcanarydb0dcc72015-03-20 12:31:52 -0700253 }
254 out->write(scanline.get(), bm.width());
255 }
256 return;
halcanary1b5c6042015-02-18 11:29:56 -0800257 }
halcanarydb0dcc72015-03-20 12:31:52 -0700258 case kAlpha_8_SkColorType:
259 for (int y = 0; y < bm.height(); ++y) {
260 out->write(bm.getAddr8(0, y), bm.width());
261 }
262 return;
263 case kIndex_8_SkColorType: {
264 SkColorTable* ct = bm.getColorTable();
265 SkASSERT(ct);
266 SkAutoTMalloc<uint8_t> scanline(bm.width());
267 for (int y = 0; y < bm.height(); ++y) {
268 uint8_t* dst = scanline.get();
269 const uint8_t* src = bm.getAddr8(0, y);
270 for (int x = 0; x < bm.width(); ++x) {
271 *dst++ = SkGetPackedA32((*ct)[*src++]);
272 }
273 out->write(scanline.get(), bm.width());
274 }
275 return;
276 }
277 case kRGB_565_SkColorType:
278 case kGray_8_SkColorType:
279 SkDEBUGFAIL("color type has no alpha");
280 return;
281 case kARGB_4444_SkColorType:
282 SkDEBUGFAIL("4444 color type should have been converted to N32");
283 return;
284 case kUnknown_SkColorType:
285 default:
286 SkDEBUGFAIL("unexpected color type");
halcanary1b5c6042015-02-18 11:29:56 -0800287 }
288}
289
halcanarydb0dcc72015-03-20 12:31:52 -0700290static SkPDFArray* make_indexed_color_space(const SkColorTable* table) {
halcanary385fe4d2015-08-26 13:07:48 -0700291 SkPDFArray* result = new SkPDFArray;
halcanarydb0dcc72015-03-20 12:31:52 -0700292 result->reserve(4);
293 result->appendName("Indexed");
294 result->appendName("DeviceRGB");
295 SkASSERT(table);
296 if (table->count() < 1) {
297 result->appendInt(0);
298 char shortTableArray[3] = {0, 0, 0};
299 SkString tableString(shortTableArray, SK_ARRAY_COUNT(shortTableArray));
halcanarya25b3372015-04-27 14:00:09 -0700300 result->appendString(tableString);
halcanarydb0dcc72015-03-20 12:31:52 -0700301 return result;
302 }
303 result->appendInt(table->count() - 1); // maximum color index.
304
305 // Potentially, this could be represented in fewer bytes with a stream.
306 // Max size as a string is 1.5k.
307 char tableArray[256 * 3];
308 SkASSERT(3u * table->count() <= SK_ARRAY_COUNT(tableArray));
309 uint8_t* tablePtr = reinterpret_cast<uint8_t*>(tableArray);
310 const SkPMColor* colors = table->readColors();
311 for (int i = 0; i < table->count(); i++) {
halcanaryaa4ba902015-11-06 07:27:23 -0800312 pmcolor_to_rgb24(colors[i], tablePtr, kN32_SkColorType);
halcanarydb0dcc72015-03-20 12:31:52 -0700313 tablePtr += 3;
314 }
315 SkString tableString(tableArray, 3 * table->count());
halcanarya25b3372015-04-27 14:00:09 -0700316 result->appendString(tableString);
halcanarydb0dcc72015-03-20 12:31:52 -0700317 return result;
halcanary1b5c6042015-02-18 11:29:56 -0800318}
319
halcanary7a14b312015-10-01 07:28:13 -0700320static void emit_image_xobject(SkWStream* stream,
321 const SkImage* image,
322 bool alpha,
323 SkPDFObject* smask,
324 const SkPDFObjNumMap& objNumMap,
325 const SkPDFSubstituteMap& substitutes) {
326 SkBitmap bitmap;
327 image_get_ro_pixels(image, &bitmap); // TODO(halcanary): test
328 SkAutoLockPixels autoLockPixels(bitmap); // with malformed images.
329 SkASSERT(bitmap.colorType() != kIndex_8_SkColorType ||
330 bitmap.getColorTable());
halcanary37c46ca2015-03-31 12:30:20 -0700331
332 // Write to a temporary buffer to get the compressed length.
333 SkDynamicMemoryWStream buffer;
334 SkDeflateWStream deflateWStream(&buffer);
halcanary7a14b312015-10-01 07:28:13 -0700335 if (alpha) {
336 bitmap_alpha_to_a8(bitmap, &deflateWStream);
337 } else {
338 bitmap_to_pdf_pixels(bitmap, &deflateWStream);
339 }
halcanary37c46ca2015-03-31 12:30:20 -0700340 deflateWStream.finalize(); // call before detachAsStream().
341 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
342
halcanary1b5c6042015-02-18 11:29:56 -0800343 SkPDFDict pdfDict("XObject");
344 pdfDict.insertName("Subtype", "Image");
halcanary7a14b312015-10-01 07:28:13 -0700345 pdfDict.insertInt("Width", bitmap.width());
346 pdfDict.insertInt("Height", bitmap.height());
347 if (alpha) {
348 pdfDict.insertName("ColorSpace", "DeviceGray");
349 } else if (bitmap.colorType() == kIndex_8_SkColorType) {
350 SkASSERT(1 == pdf_color_component_count(bitmap.colorType()));
halcanarya25b3372015-04-27 14:00:09 -0700351 pdfDict.insertObject("ColorSpace",
halcanary7a14b312015-10-01 07:28:13 -0700352 make_indexed_color_space(bitmap.getColorTable()));
353 } else if (1 == pdf_color_component_count(bitmap.colorType())) {
halcanarydb0dcc72015-03-20 12:31:52 -0700354 pdfDict.insertName("ColorSpace", "DeviceGray");
355 } else {
356 pdfDict.insertName("ColorSpace", "DeviceRGB");
357 }
halcanary7a14b312015-10-01 07:28:13 -0700358 if (smask) {
359 pdfDict.insertObjRef("SMask", SkRef(smask));
halcanary1b5c6042015-02-18 11:29:56 -0800360 }
halcanary7a14b312015-10-01 07:28:13 -0700361 pdfDict.insertInt("BitsPerComponent", 8);
halcanarydb0dcc72015-03-20 12:31:52 -0700362 pdfDict.insertName("Filter", "FlateDecode");
halcanary37c46ca2015-03-31 12:30:20 -0700363 pdfDict.insertInt("Length", asset->getLength());
halcanarya8448bc2015-04-17 13:27:24 -0700364 pdfDict.emitObject(stream, objNumMap, substitutes);
halcanary37c46ca2015-03-31 12:30:20 -0700365
366 pdf_stream_begin(stream);
367 stream->writeStream(asset.get(), asset->getLength());
368 pdf_stream_end(stream);
halcanary1b5c6042015-02-18 11:29:56 -0800369}
370
halcanary1b5c6042015-02-18 11:29:56 -0800371////////////////////////////////////////////////////////////////////////////////
halcanarydb0dcc72015-03-20 12:31:52 -0700372
halcanary7a14b312015-10-01 07:28:13 -0700373namespace {
374// This SkPDFObject only outputs the alpha layer of the given bitmap.
375class PDFAlphaBitmap : public SkPDFObject {
376public:
377 PDFAlphaBitmap(const SkImage* image) : fImage(SkRef(image)) {}
378 ~PDFAlphaBitmap() {}
379 void emitObject(SkWStream* stream,
380 const SkPDFObjNumMap& objNumMap,
381 const SkPDFSubstituteMap& subs) const override {
382 emit_image_xobject(stream, fImage, true, nullptr, objNumMap, subs);
halcanary1b5c6042015-02-18 11:29:56 -0800383 }
halcanary7a14b312015-10-01 07:28:13 -0700384
385private:
386 SkAutoTUnref<const SkImage> fImage;
387};
388
389} // namespace
390
391////////////////////////////////////////////////////////////////////////////////
392
393namespace {
394class PDFDefaultBitmap : public SkPDFObject {
395public:
halcanary34422612015-10-12 10:11:18 -0700396 void emitObject(SkWStream* stream,
halcanary7a14b312015-10-01 07:28:13 -0700397 const SkPDFObjNumMap& objNumMap,
halcanary34422612015-10-12 10:11:18 -0700398 const SkPDFSubstituteMap& subs) const override {
399 emit_image_xobject(stream, fImage, false, fSMask, objNumMap, subs);
halcanary7a14b312015-10-01 07:28:13 -0700400 }
401 void addResources(SkPDFObjNumMap* catalog,
halcanary34422612015-10-12 10:11:18 -0700402 const SkPDFSubstituteMap& subs) const override {
halcanary7a14b312015-10-01 07:28:13 -0700403 if (fSMask.get()) {
halcanary34422612015-10-12 10:11:18 -0700404 SkPDFObject* obj = subs.getSubstitute(fSMask.get());
halcanary7a14b312015-10-01 07:28:13 -0700405 SkASSERT(obj);
halcanary34422612015-10-12 10:11:18 -0700406 catalog->addObjectRecursively(obj, subs);
halcanary7a14b312015-10-01 07:28:13 -0700407 }
408 }
409 PDFDefaultBitmap(const SkImage* image, SkPDFObject* smask)
410 : fImage(SkRef(image)), fSMask(smask) {}
411
412private:
413 SkAutoTUnref<const SkImage> fImage;
414 const SkAutoTUnref<SkPDFObject> fSMask;
415};
416} // namespace
417
418////////////////////////////////////////////////////////////////////////////////
halcanary1b5c6042015-02-18 11:29:56 -0800419
halcanarya8448bc2015-04-17 13:27:24 -0700420namespace {
421/**
halcanary7a14b312015-10-01 07:28:13 -0700422 * This PDFObject assumes that its constructor was handed YUV or
423 * Grayscale JFIF Jpeg-encoded data that can be directly embedded
424 * into a PDF.
halcanarya8448bc2015-04-17 13:27:24 -0700425 */
halcanary7a14b312015-10-01 07:28:13 -0700426class PDFJpegBitmap : public SkPDFObject {
halcanarya8448bc2015-04-17 13:27:24 -0700427public:
halcanary7a14b312015-10-01 07:28:13 -0700428 SkISize fSize;
halcanarya8448bc2015-04-17 13:27:24 -0700429 SkAutoTUnref<SkData> fData;
halcanary96287f72015-05-07 11:46:59 -0700430 bool fIsYUV;
halcanary7a14b312015-10-01 07:28:13 -0700431 PDFJpegBitmap(SkISize size, SkData* data, bool isYUV)
432 : fSize(size), fData(SkRef(data)), fIsYUV(isYUV) {}
halcanarya8448bc2015-04-17 13:27:24 -0700433 void emitObject(SkWStream*,
434 const SkPDFObjNumMap&,
halcanarya060eba2015-08-19 12:26:46 -0700435 const SkPDFSubstituteMap&) const override;
halcanarya8448bc2015-04-17 13:27:24 -0700436};
437
438void PDFJpegBitmap::emitObject(SkWStream* stream,
439 const SkPDFObjNumMap& objNumMap,
halcanarya060eba2015-08-19 12:26:46 -0700440 const SkPDFSubstituteMap& substituteMap) const {
halcanarya8448bc2015-04-17 13:27:24 -0700441 SkPDFDict pdfDict("XObject");
442 pdfDict.insertName("Subtype", "Image");
halcanary7a14b312015-10-01 07:28:13 -0700443 pdfDict.insertInt("Width", fSize.width());
444 pdfDict.insertInt("Height", fSize.height());
halcanary96287f72015-05-07 11:46:59 -0700445 if (fIsYUV) {
446 pdfDict.insertName("ColorSpace", "DeviceRGB");
447 } else {
448 pdfDict.insertName("ColorSpace", "DeviceGray");
449 }
halcanarya8448bc2015-04-17 13:27:24 -0700450 pdfDict.insertInt("BitsPerComponent", 8);
451 pdfDict.insertName("Filter", "DCTDecode");
452 pdfDict.insertInt("ColorTransform", 0);
453 pdfDict.insertInt("Length", SkToInt(fData->size()));
454 pdfDict.emitObject(stream, objNumMap, substituteMap);
455 pdf_stream_begin(stream);
456 stream->write(fData->data(), fData->size());
457 pdf_stream_end(stream);
458}
459} // namespace
460
461////////////////////////////////////////////////////////////////////////////////
462
halcanary7a14b312015-10-01 07:28:13 -0700463SkPDFObject* SkPDFCreateBitmapObject(const SkImage* image) {
464 SkAutoTUnref<SkData> data(image->refEncoded());
465 SkJFIFInfo info;
466 if (data && SkIsJFIF(data, &info)) {
467 bool yuv = info.fType == SkJFIFInfo::kYCbCr;
468 if (info.fSize == image->dimensions()) { // Sanity check.
469 // hold on to data, not image.
470 #ifdef SK_PDF_IMAGE_STATS
471 gJpegImageObjects.fetch_add(1);
472 #endif
473 return new PDFJpegBitmap(info.fSize, data, yuv);
halcanarya8448bc2015-04-17 13:27:24 -0700474 }
475 }
halcanary7a14b312015-10-01 07:28:13 -0700476 SkPDFObject* smask =
477 image_compute_is_opaque(image) ? nullptr : new PDFAlphaBitmap(image);
478 #ifdef SK_PDF_IMAGE_STATS
479 gRegularImageObjects.fetch_add(1);
480 #endif
481 return new PDFDefaultBitmap(image, smask);
halcanary1b5c6042015-02-18 11:29:56 -0800482}