blob: 52b65c01d460d2701b0d59113ccd1ae45938940e [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
halcanarydb0dcc72015-03-20 12:31:52 -070062// unpremultiply and extract R, G, B components.
63static void pmcolor_to_rgb24(SkPMColor pmColor, uint8_t* rgb) {
64 uint32_t s = SkUnPreMultiply::GetScale(SkGetPackedA32(pmColor));
65 rgb[0] = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(pmColor));
66 rgb[1] = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(pmColor));
67 rgb[2] = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(pmColor));
68}
69
70/* It is necessary to average the color component of transparent
71 pixels with their surrounding neighbors since the PDF renderer may
72 separately re-sample the alpha and color channels when the image is
73 not displayed at its native resolution. Since an alpha of zero
74 gives no information about the color component, the pathological
75 case is a white image with sharp transparency bounds - the color
76 channel goes to black, and the should-be-transparent pixels are
77 rendered as grey because of the separate soft mask and color
78 resizing. e.g.: gm/bitmappremul.cpp */
79static void get_neighbor_avg_color(const SkBitmap& bm,
80 int xOrig,
81 int yOrig,
82 uint8_t rgb[3]) {
83 SkASSERT(kN32_SkColorType == bm.colorType());
84 unsigned a = 0, r = 0, g = 0, b = 0;
85 // Clamp the range to the edge of the bitmap.
86 int ymin = SkTMax(0, yOrig - 1);
87 int ymax = SkTMin(yOrig + 1, bm.height() - 1);
88 int xmin = SkTMax(0, xOrig - 1);
89 int xmax = SkTMin(xOrig + 1, bm.width() - 1);
90 for (int y = ymin; y <= ymax; ++y) {
91 SkPMColor* scanline = bm.getAddr32(0, y);
92 for (int x = xmin; x <= xmax; ++x) {
93 SkPMColor pmColor = scanline[x];
94 a += SkGetPackedA32(pmColor);
95 r += SkGetPackedR32(pmColor);
96 g += SkGetPackedG32(pmColor);
97 b += SkGetPackedB32(pmColor);
halcanary1b5c6042015-02-18 11:29:56 -080098 }
99 }
halcanarydb0dcc72015-03-20 12:31:52 -0700100 if (a > 0) {
101 rgb[0] = SkToU8(255 * r / a);
102 rgb[1] = SkToU8(255 * g / a);
103 rgb[2] = SkToU8(255 * b / a);
halcanary1b5c6042015-02-18 11:29:56 -0800104 } else {
halcanarydb0dcc72015-03-20 12:31:52 -0700105 rgb[0] = rgb[1] = rgb[2] = 0;
halcanary1b5c6042015-02-18 11:29:56 -0800106 }
107}
108
halcanarydb0dcc72015-03-20 12:31:52 -0700109static size_t pixel_count(const SkBitmap& bm) {
110 return SkToSizeT(bm.width()) * SkToSizeT(bm.height());
111}
112
113static const SkBitmap& not4444(const SkBitmap& input, SkBitmap* copy) {
114 if (input.colorType() != kARGB_4444_SkColorType) {
115 return input;
116 }
117 // ARGB_4444 is rarely used, so we can do a wasteful tmp copy.
118 SkAssertResult(input.copyTo(copy, kN32_SkColorType));
119 copy->setImmutable();
120 return *copy;
121}
122
123static size_t pdf_color_component_count(SkColorType ct) {
124 switch (ct) {
125 case kN32_SkColorType:
126 case kRGB_565_SkColorType:
127 case kARGB_4444_SkColorType:
128 return 3;
129 case kAlpha_8_SkColorType:
130 case kIndex_8_SkColorType:
131 case kGray_8_SkColorType:
132 return 1;
133 case kUnknown_SkColorType:
134 default:
135 SkDEBUGFAIL("unexpected color type");
136 return 0;
137 }
138}
139
140static void bitmap_to_pdf_pixels(const SkBitmap& bitmap, SkWStream* out) {
141 if (!bitmap.getPixels()) {
142 size_t size = pixel_count(bitmap) *
143 pdf_color_component_count(bitmap.colorType());
144 fill_stream(out, '\x00', size);
halcanary1b5c6042015-02-18 11:29:56 -0800145 return;
146 }
halcanarydb0dcc72015-03-20 12:31:52 -0700147 SkBitmap copy;
148 const SkBitmap& bm = not4444(bitmap, &copy);
149 SkAutoLockPixels autoLockPixels(bm);
150 switch (bm.colorType()) {
151 case kN32_SkColorType: {
152 SkASSERT(3 == pdf_color_component_count(bitmap.colorType()));
153 SkAutoTMalloc<uint8_t> scanline(3 * bm.width());
154 for (int y = 0; y < bm.height(); ++y) {
155 const SkPMColor* src = bm.getAddr32(0, y);
156 uint8_t* dst = scanline.get();
157 for (int x = 0; x < bm.width(); ++x) {
158 SkPMColor color = *src++;
159 U8CPU alpha = SkGetPackedA32(color);
160 if (alpha != SK_AlphaTRANSPARENT) {
161 pmcolor_to_rgb24(color, dst);
162 } else {
163 get_neighbor_avg_color(bm, x, y, dst);
164 }
165 dst += 3;
166 }
167 out->write(scanline.get(), 3 * bm.width());
halcanary1b5c6042015-02-18 11:29:56 -0800168 }
halcanarydb0dcc72015-03-20 12:31:52 -0700169 return;
halcanary1b5c6042015-02-18 11:29:56 -0800170 }
halcanarydb0dcc72015-03-20 12:31:52 -0700171 case kRGB_565_SkColorType: {
172 SkASSERT(3 == pdf_color_component_count(bitmap.colorType()));
173 SkAutoTMalloc<uint8_t> scanline(3 * bm.width());
174 for (int y = 0; y < bm.height(); ++y) {
175 const uint16_t* src = bm.getAddr16(0, y);
176 uint8_t* dst = scanline.get();
177 for (int x = 0; x < bm.width(); ++x) {
178 U16CPU color565 = *src++;
179 *dst++ = SkPacked16ToR32(color565);
180 *dst++ = SkPacked16ToG32(color565);
181 *dst++ = SkPacked16ToB32(color565);
182 }
183 out->write(scanline.get(), 3 * bm.width());
184 }
185 return;
186 }
187 case kAlpha_8_SkColorType:
188 SkASSERT(1 == pdf_color_component_count(bitmap.colorType()));
189 fill_stream(out, '\x00', pixel_count(bm));
190 return;
191 case kGray_8_SkColorType:
192 case kIndex_8_SkColorType:
193 SkASSERT(1 == pdf_color_component_count(bitmap.colorType()));
194 // these two formats need no transformation to serialize.
195 for (int y = 0; y < bm.height(); ++y) {
196 out->write(bm.getAddr8(0, y), bm.width());
197 }
198 return;
199 case kUnknown_SkColorType:
200 case kARGB_4444_SkColorType:
201 default:
202 SkDEBUGFAIL("unexpected color type");
halcanary1b5c6042015-02-18 11:29:56 -0800203 }
204}
205
halcanarydb0dcc72015-03-20 12:31:52 -0700206////////////////////////////////////////////////////////////////////////////////
207
208static void bitmap_alpha_to_a8(const SkBitmap& bitmap, SkWStream* out) {
209 if (!bitmap.getPixels()) {
210 fill_stream(out, '\xFF', pixel_count(bitmap));
halcanary1b5c6042015-02-18 11:29:56 -0800211 return;
212 }
halcanarydb0dcc72015-03-20 12:31:52 -0700213 SkBitmap copy;
214 const SkBitmap& bm = not4444(bitmap, &copy);
215 SkAutoLockPixels autoLockPixels(bm);
216 switch (bm.colorType()) {
217 case kN32_SkColorType: {
218 SkAutoTMalloc<uint8_t> scanline(bm.width());
219 for (int y = 0; y < bm.height(); ++y) {
220 uint8_t* dst = scanline.get();
221 const SkPMColor* src = bm.getAddr32(0, y);
222 for (int x = 0; x < bm.width(); ++x) {
223 *dst++ = SkGetPackedA32(*src++);
224 }
225 out->write(scanline.get(), bm.width());
226 }
227 return;
halcanary1b5c6042015-02-18 11:29:56 -0800228 }
halcanarydb0dcc72015-03-20 12:31:52 -0700229 case kAlpha_8_SkColorType:
230 for (int y = 0; y < bm.height(); ++y) {
231 out->write(bm.getAddr8(0, y), bm.width());
232 }
233 return;
234 case kIndex_8_SkColorType: {
235 SkColorTable* ct = bm.getColorTable();
236 SkASSERT(ct);
237 SkAutoTMalloc<uint8_t> scanline(bm.width());
238 for (int y = 0; y < bm.height(); ++y) {
239 uint8_t* dst = scanline.get();
240 const uint8_t* src = bm.getAddr8(0, y);
241 for (int x = 0; x < bm.width(); ++x) {
242 *dst++ = SkGetPackedA32((*ct)[*src++]);
243 }
244 out->write(scanline.get(), bm.width());
245 }
246 return;
247 }
248 case kRGB_565_SkColorType:
249 case kGray_8_SkColorType:
250 SkDEBUGFAIL("color type has no alpha");
251 return;
252 case kARGB_4444_SkColorType:
253 SkDEBUGFAIL("4444 color type should have been converted to N32");
254 return;
255 case kUnknown_SkColorType:
256 default:
257 SkDEBUGFAIL("unexpected color type");
halcanary1b5c6042015-02-18 11:29:56 -0800258 }
259}
260
halcanarydb0dcc72015-03-20 12:31:52 -0700261static SkPDFArray* make_indexed_color_space(const SkColorTable* table) {
halcanary385fe4d2015-08-26 13:07:48 -0700262 SkPDFArray* result = new SkPDFArray;
halcanarydb0dcc72015-03-20 12:31:52 -0700263 result->reserve(4);
264 result->appendName("Indexed");
265 result->appendName("DeviceRGB");
266 SkASSERT(table);
267 if (table->count() < 1) {
268 result->appendInt(0);
269 char shortTableArray[3] = {0, 0, 0};
270 SkString tableString(shortTableArray, SK_ARRAY_COUNT(shortTableArray));
halcanarya25b3372015-04-27 14:00:09 -0700271 result->appendString(tableString);
halcanarydb0dcc72015-03-20 12:31:52 -0700272 return result;
273 }
274 result->appendInt(table->count() - 1); // maximum color index.
275
276 // Potentially, this could be represented in fewer bytes with a stream.
277 // Max size as a string is 1.5k.
278 char tableArray[256 * 3];
279 SkASSERT(3u * table->count() <= SK_ARRAY_COUNT(tableArray));
280 uint8_t* tablePtr = reinterpret_cast<uint8_t*>(tableArray);
281 const SkPMColor* colors = table->readColors();
282 for (int i = 0; i < table->count(); i++) {
283 pmcolor_to_rgb24(colors[i], tablePtr);
284 tablePtr += 3;
285 }
286 SkString tableString(tableArray, 3 * table->count());
halcanarya25b3372015-04-27 14:00:09 -0700287 result->appendString(tableString);
halcanarydb0dcc72015-03-20 12:31:52 -0700288 return result;
halcanary1b5c6042015-02-18 11:29:56 -0800289}
290
halcanary7a14b312015-10-01 07:28:13 -0700291static void emit_image_xobject(SkWStream* stream,
292 const SkImage* image,
293 bool alpha,
294 SkPDFObject* smask,
295 const SkPDFObjNumMap& objNumMap,
296 const SkPDFSubstituteMap& substitutes) {
297 SkBitmap bitmap;
298 image_get_ro_pixels(image, &bitmap); // TODO(halcanary): test
299 SkAutoLockPixels autoLockPixels(bitmap); // with malformed images.
300 SkASSERT(bitmap.colorType() != kIndex_8_SkColorType ||
301 bitmap.getColorTable());
halcanary37c46ca2015-03-31 12:30:20 -0700302
303 // Write to a temporary buffer to get the compressed length.
304 SkDynamicMemoryWStream buffer;
305 SkDeflateWStream deflateWStream(&buffer);
halcanary7a14b312015-10-01 07:28:13 -0700306 if (alpha) {
307 bitmap_alpha_to_a8(bitmap, &deflateWStream);
308 } else {
309 bitmap_to_pdf_pixels(bitmap, &deflateWStream);
310 }
halcanary37c46ca2015-03-31 12:30:20 -0700311 deflateWStream.finalize(); // call before detachAsStream().
312 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
313
halcanary1b5c6042015-02-18 11:29:56 -0800314 SkPDFDict pdfDict("XObject");
315 pdfDict.insertName("Subtype", "Image");
halcanary7a14b312015-10-01 07:28:13 -0700316 pdfDict.insertInt("Width", bitmap.width());
317 pdfDict.insertInt("Height", bitmap.height());
318 if (alpha) {
319 pdfDict.insertName("ColorSpace", "DeviceGray");
320 } else if (bitmap.colorType() == kIndex_8_SkColorType) {
321 SkASSERT(1 == pdf_color_component_count(bitmap.colorType()));
halcanarya25b3372015-04-27 14:00:09 -0700322 pdfDict.insertObject("ColorSpace",
halcanary7a14b312015-10-01 07:28:13 -0700323 make_indexed_color_space(bitmap.getColorTable()));
324 } else if (1 == pdf_color_component_count(bitmap.colorType())) {
halcanarydb0dcc72015-03-20 12:31:52 -0700325 pdfDict.insertName("ColorSpace", "DeviceGray");
326 } else {
327 pdfDict.insertName("ColorSpace", "DeviceRGB");
328 }
halcanary7a14b312015-10-01 07:28:13 -0700329 if (smask) {
330 pdfDict.insertObjRef("SMask", SkRef(smask));
halcanary1b5c6042015-02-18 11:29:56 -0800331 }
halcanary7a14b312015-10-01 07:28:13 -0700332 pdfDict.insertInt("BitsPerComponent", 8);
halcanarydb0dcc72015-03-20 12:31:52 -0700333 pdfDict.insertName("Filter", "FlateDecode");
halcanary37c46ca2015-03-31 12:30:20 -0700334 pdfDict.insertInt("Length", asset->getLength());
halcanarya8448bc2015-04-17 13:27:24 -0700335 pdfDict.emitObject(stream, objNumMap, substitutes);
halcanary37c46ca2015-03-31 12:30:20 -0700336
337 pdf_stream_begin(stream);
338 stream->writeStream(asset.get(), asset->getLength());
339 pdf_stream_end(stream);
halcanary1b5c6042015-02-18 11:29:56 -0800340}
341
halcanary1b5c6042015-02-18 11:29:56 -0800342////////////////////////////////////////////////////////////////////////////////
halcanarydb0dcc72015-03-20 12:31:52 -0700343
halcanary7a14b312015-10-01 07:28:13 -0700344namespace {
345// This SkPDFObject only outputs the alpha layer of the given bitmap.
346class PDFAlphaBitmap : public SkPDFObject {
347public:
348 PDFAlphaBitmap(const SkImage* image) : fImage(SkRef(image)) {}
349 ~PDFAlphaBitmap() {}
350 void emitObject(SkWStream* stream,
351 const SkPDFObjNumMap& objNumMap,
352 const SkPDFSubstituteMap& subs) const override {
353 emit_image_xobject(stream, fImage, true, nullptr, objNumMap, subs);
halcanary1b5c6042015-02-18 11:29:56 -0800354 }
halcanary7a14b312015-10-01 07:28:13 -0700355
356private:
357 SkAutoTUnref<const SkImage> fImage;
358};
359
360} // namespace
361
362////////////////////////////////////////////////////////////////////////////////
363
364namespace {
365class PDFDefaultBitmap : public SkPDFObject {
366public:
halcanary34422612015-10-12 10:11:18 -0700367 void emitObject(SkWStream* stream,
halcanary7a14b312015-10-01 07:28:13 -0700368 const SkPDFObjNumMap& objNumMap,
halcanary34422612015-10-12 10:11:18 -0700369 const SkPDFSubstituteMap& subs) const override {
370 emit_image_xobject(stream, fImage, false, fSMask, objNumMap, subs);
halcanary7a14b312015-10-01 07:28:13 -0700371 }
372 void addResources(SkPDFObjNumMap* catalog,
halcanary34422612015-10-12 10:11:18 -0700373 const SkPDFSubstituteMap& subs) const override {
halcanary7a14b312015-10-01 07:28:13 -0700374 if (fSMask.get()) {
halcanary34422612015-10-12 10:11:18 -0700375 SkPDFObject* obj = subs.getSubstitute(fSMask.get());
halcanary7a14b312015-10-01 07:28:13 -0700376 SkASSERT(obj);
halcanary34422612015-10-12 10:11:18 -0700377 catalog->addObjectRecursively(obj, subs);
halcanary7a14b312015-10-01 07:28:13 -0700378 }
379 }
380 PDFDefaultBitmap(const SkImage* image, SkPDFObject* smask)
381 : fImage(SkRef(image)), fSMask(smask) {}
382
383private:
384 SkAutoTUnref<const SkImage> fImage;
385 const SkAutoTUnref<SkPDFObject> fSMask;
386};
387} // namespace
388
389////////////////////////////////////////////////////////////////////////////////
halcanary1b5c6042015-02-18 11:29:56 -0800390
halcanarya8448bc2015-04-17 13:27:24 -0700391namespace {
392/**
halcanary7a14b312015-10-01 07:28:13 -0700393 * This PDFObject assumes that its constructor was handed YUV or
394 * Grayscale JFIF Jpeg-encoded data that can be directly embedded
395 * into a PDF.
halcanarya8448bc2015-04-17 13:27:24 -0700396 */
halcanary7a14b312015-10-01 07:28:13 -0700397class PDFJpegBitmap : public SkPDFObject {
halcanarya8448bc2015-04-17 13:27:24 -0700398public:
halcanary7a14b312015-10-01 07:28:13 -0700399 SkISize fSize;
halcanarya8448bc2015-04-17 13:27:24 -0700400 SkAutoTUnref<SkData> fData;
halcanary96287f72015-05-07 11:46:59 -0700401 bool fIsYUV;
halcanary7a14b312015-10-01 07:28:13 -0700402 PDFJpegBitmap(SkISize size, SkData* data, bool isYUV)
403 : fSize(size), fData(SkRef(data)), fIsYUV(isYUV) {}
halcanarya8448bc2015-04-17 13:27:24 -0700404 void emitObject(SkWStream*,
405 const SkPDFObjNumMap&,
halcanarya060eba2015-08-19 12:26:46 -0700406 const SkPDFSubstituteMap&) const override;
halcanarya8448bc2015-04-17 13:27:24 -0700407};
408
409void PDFJpegBitmap::emitObject(SkWStream* stream,
410 const SkPDFObjNumMap& objNumMap,
halcanarya060eba2015-08-19 12:26:46 -0700411 const SkPDFSubstituteMap& substituteMap) const {
halcanarya8448bc2015-04-17 13:27:24 -0700412 SkPDFDict pdfDict("XObject");
413 pdfDict.insertName("Subtype", "Image");
halcanary7a14b312015-10-01 07:28:13 -0700414 pdfDict.insertInt("Width", fSize.width());
415 pdfDict.insertInt("Height", fSize.height());
halcanary96287f72015-05-07 11:46:59 -0700416 if (fIsYUV) {
417 pdfDict.insertName("ColorSpace", "DeviceRGB");
418 } else {
419 pdfDict.insertName("ColorSpace", "DeviceGray");
420 }
halcanarya8448bc2015-04-17 13:27:24 -0700421 pdfDict.insertInt("BitsPerComponent", 8);
422 pdfDict.insertName("Filter", "DCTDecode");
423 pdfDict.insertInt("ColorTransform", 0);
424 pdfDict.insertInt("Length", SkToInt(fData->size()));
425 pdfDict.emitObject(stream, objNumMap, substituteMap);
426 pdf_stream_begin(stream);
427 stream->write(fData->data(), fData->size());
428 pdf_stream_end(stream);
429}
430} // namespace
431
432////////////////////////////////////////////////////////////////////////////////
433
halcanary7a14b312015-10-01 07:28:13 -0700434SkPDFObject* SkPDFCreateBitmapObject(const SkImage* image) {
435 SkAutoTUnref<SkData> data(image->refEncoded());
436 SkJFIFInfo info;
437 if (data && SkIsJFIF(data, &info)) {
438 bool yuv = info.fType == SkJFIFInfo::kYCbCr;
439 if (info.fSize == image->dimensions()) { // Sanity check.
440 // hold on to data, not image.
441 #ifdef SK_PDF_IMAGE_STATS
442 gJpegImageObjects.fetch_add(1);
443 #endif
444 return new PDFJpegBitmap(info.fSize, data, yuv);
halcanarya8448bc2015-04-17 13:27:24 -0700445 }
446 }
halcanary7a14b312015-10-01 07:28:13 -0700447 SkPDFObject* smask =
448 image_compute_is_opaque(image) ? nullptr : new PDFAlphaBitmap(image);
449 #ifdef SK_PDF_IMAGE_STATS
450 gRegularImageObjects.fetch_add(1);
451 #endif
452 return new PDFDefaultBitmap(image, smask);
halcanary1b5c6042015-02-18 11:29:56 -0800453}