2 * Copyright 2015 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "SkColorPriv.h"
10 #include "SkPDFBitmap.h"
11 #include "SkPDFCanon.h"
12 #include "SkPDFCatalog.h"
14 #include "SkUnPreMultiply.h"
16 ////////////////////////////////////////////////////////////////////////////////
18 static void pdf_stream_begin(SkWStream* stream) {
19 static const char streamBegin[] = " stream\n";
20 stream->write(streamBegin, strlen(streamBegin));
23 static void pdf_stream_end(SkWStream* stream) {
24 static const char streamEnd[] = "\nendstream";
25 stream->write(streamEnd, strlen(streamEnd));
28 ////////////////////////////////////////////////////////////////////////////////
30 // write a single byte to a stream n times.
31 static void fill_stream(SkWStream* out, char value, size_t n) {
33 memset(buffer, value, sizeof(buffer));
34 for (size_t i = 0; i < n / sizeof(buffer); ++i) {
35 out->write(buffer, sizeof(buffer));
37 out->write(buffer, n % sizeof(buffer));
40 // unpremultiply and extract R, G, B components.
41 static void pmcolor_to_rgb24(SkPMColor pmColor, uint8_t* rgb) {
42 uint32_t s = SkUnPreMultiply::GetScale(SkGetPackedA32(pmColor));
43 rgb[0] = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(pmColor));
44 rgb[1] = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(pmColor));
45 rgb[2] = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(pmColor));
48 /* It is necessary to average the color component of transparent
49 pixels with their surrounding neighbors since the PDF renderer may
50 separately re-sample the alpha and color channels when the image is
51 not displayed at its native resolution. Since an alpha of zero
52 gives no information about the color component, the pathological
53 case is a white image with sharp transparency bounds - the color
54 channel goes to black, and the should-be-transparent pixels are
55 rendered as grey because of the separate soft mask and color
56 resizing. e.g.: gm/bitmappremul.cpp */
57 static void get_neighbor_avg_color(const SkBitmap& bm,
61 SkASSERT(kN32_SkColorType == bm.colorType());
62 unsigned a = 0, r = 0, g = 0, b = 0;
63 // Clamp the range to the edge of the bitmap.
64 int ymin = SkTMax(0, yOrig - 1);
65 int ymax = SkTMin(yOrig + 1, bm.height() - 1);
66 int xmin = SkTMax(0, xOrig - 1);
67 int xmax = SkTMin(xOrig + 1, bm.width() - 1);
68 for (int y = ymin; y <= ymax; ++y) {
69 SkPMColor* scanline = bm.getAddr32(0, y);
70 for (int x = xmin; x <= xmax; ++x) {
71 SkPMColor pmColor = scanline[x];
72 a += SkGetPackedA32(pmColor);
73 r += SkGetPackedR32(pmColor);
74 g += SkGetPackedG32(pmColor);
75 b += SkGetPackedB32(pmColor);
79 rgb[0] = SkToU8(255 * r / a);
80 rgb[1] = SkToU8(255 * g / a);
81 rgb[2] = SkToU8(255 * b / a);
83 rgb[0] = rgb[1] = rgb[2] = 0;
87 static size_t pixel_count(const SkBitmap& bm) {
88 return SkToSizeT(bm.width()) * SkToSizeT(bm.height());
91 static const SkBitmap& not4444(const SkBitmap& input, SkBitmap* copy) {
92 if (input.colorType() != kARGB_4444_SkColorType) {
95 // ARGB_4444 is rarely used, so we can do a wasteful tmp copy.
96 SkAssertResult(input.copyTo(copy, kN32_SkColorType));
101 static size_t pdf_color_component_count(SkColorType ct) {
103 case kN32_SkColorType:
104 case kRGB_565_SkColorType:
105 case kARGB_4444_SkColorType:
107 case kAlpha_8_SkColorType:
108 case kIndex_8_SkColorType:
109 case kGray_8_SkColorType:
111 case kUnknown_SkColorType:
113 SkDEBUGFAIL("unexpected color type");
118 static void bitmap_to_pdf_pixels(const SkBitmap& bitmap, SkWStream* out) {
119 if (!bitmap.getPixels()) {
120 size_t size = pixel_count(bitmap) *
121 pdf_color_component_count(bitmap.colorType());
122 fill_stream(out, '\x00', size);
126 const SkBitmap& bm = not4444(bitmap, ©);
127 SkAutoLockPixels autoLockPixels(bm);
128 switch (bm.colorType()) {
129 case kN32_SkColorType: {
130 SkASSERT(3 == pdf_color_component_count(bitmap.colorType()));
131 SkAutoTMalloc<uint8_t> scanline(3 * bm.width());
132 for (int y = 0; y < bm.height(); ++y) {
133 const SkPMColor* src = bm.getAddr32(0, y);
134 uint8_t* dst = scanline.get();
135 for (int x = 0; x < bm.width(); ++x) {
136 SkPMColor color = *src++;
137 U8CPU alpha = SkGetPackedA32(color);
138 if (alpha != SK_AlphaTRANSPARENT) {
139 pmcolor_to_rgb24(color, dst);
141 get_neighbor_avg_color(bm, x, y, dst);
145 out->write(scanline.get(), 3 * bm.width());
149 case kRGB_565_SkColorType: {
150 SkASSERT(3 == pdf_color_component_count(bitmap.colorType()));
151 SkAutoTMalloc<uint8_t> scanline(3 * bm.width());
152 for (int y = 0; y < bm.height(); ++y) {
153 const uint16_t* src = bm.getAddr16(0, y);
154 uint8_t* dst = scanline.get();
155 for (int x = 0; x < bm.width(); ++x) {
156 U16CPU color565 = *src++;
157 *dst++ = SkPacked16ToR32(color565);
158 *dst++ = SkPacked16ToG32(color565);
159 *dst++ = SkPacked16ToB32(color565);
161 out->write(scanline.get(), 3 * bm.width());
165 case kAlpha_8_SkColorType:
166 SkASSERT(1 == pdf_color_component_count(bitmap.colorType()));
167 fill_stream(out, '\x00', pixel_count(bm));
169 case kGray_8_SkColorType:
170 case kIndex_8_SkColorType:
171 SkASSERT(1 == pdf_color_component_count(bitmap.colorType()));
172 // these two formats need no transformation to serialize.
173 for (int y = 0; y < bm.height(); ++y) {
174 out->write(bm.getAddr8(0, y), bm.width());
177 case kUnknown_SkColorType:
178 case kARGB_4444_SkColorType:
180 SkDEBUGFAIL("unexpected color type");
184 ////////////////////////////////////////////////////////////////////////////////
186 static void bitmap_alpha_to_a8(const SkBitmap& bitmap, SkWStream* out) {
187 if (!bitmap.getPixels()) {
188 fill_stream(out, '\xFF', pixel_count(bitmap));
192 const SkBitmap& bm = not4444(bitmap, ©);
193 SkAutoLockPixels autoLockPixels(bm);
194 switch (bm.colorType()) {
195 case kN32_SkColorType: {
196 SkAutoTMalloc<uint8_t> scanline(bm.width());
197 for (int y = 0; y < bm.height(); ++y) {
198 uint8_t* dst = scanline.get();
199 const SkPMColor* src = bm.getAddr32(0, y);
200 for (int x = 0; x < bm.width(); ++x) {
201 *dst++ = SkGetPackedA32(*src++);
203 out->write(scanline.get(), bm.width());
207 case kAlpha_8_SkColorType:
208 for (int y = 0; y < bm.height(); ++y) {
209 out->write(bm.getAddr8(0, y), bm.width());
212 case kIndex_8_SkColorType: {
213 SkColorTable* ct = bm.getColorTable();
215 SkAutoTMalloc<uint8_t> scanline(bm.width());
216 for (int y = 0; y < bm.height(); ++y) {
217 uint8_t* dst = scanline.get();
218 const uint8_t* src = bm.getAddr8(0, y);
219 for (int x = 0; x < bm.width(); ++x) {
220 *dst++ = SkGetPackedA32((*ct)[*src++]);
222 out->write(scanline.get(), bm.width());
226 case kRGB_565_SkColorType:
227 case kGray_8_SkColorType:
228 SkDEBUGFAIL("color type has no alpha");
230 case kARGB_4444_SkColorType:
231 SkDEBUGFAIL("4444 color type should have been converted to N32");
233 case kUnknown_SkColorType:
235 SkDEBUGFAIL("unexpected color type");
239 ////////////////////////////////////////////////////////////////////////////////
242 // This SkPDFObject only outputs the alpha layer of the given bitmap.
243 class PDFAlphaBitmap : public SkPDFObject {
245 PDFAlphaBitmap(const SkBitmap& bm) : fBitmap(bm) {}
247 void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE;
250 const SkBitmap fBitmap;
251 void emitDict(SkWStream*, SkPDFCatalog*, size_t) const;
254 void PDFAlphaBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
255 SkAutoLockPixels autoLockPixels(fBitmap);
256 SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType ||
257 fBitmap.getColorTable());
259 // Write to a temporary buffer to get the compressed length.
260 SkDynamicMemoryWStream buffer;
261 SkDeflateWStream deflateWStream(&buffer);
262 bitmap_alpha_to_a8(fBitmap, &deflateWStream);
263 deflateWStream.finalize(); // call before detachAsStream().
264 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
266 this->emitDict(stream, catalog, asset->getLength());
267 pdf_stream_begin(stream);
268 stream->writeStream(asset.get(), asset->getLength());
269 pdf_stream_end(stream);
272 void PDFAlphaBitmap::emitDict(SkWStream* stream,
273 SkPDFCatalog* catalog,
274 size_t length) const {
275 SkPDFDict pdfDict("XObject");
276 pdfDict.insertName("Subtype", "Image");
277 pdfDict.insertInt("Width", fBitmap.width());
278 pdfDict.insertInt("Height", fBitmap.height());
279 pdfDict.insertName("ColorSpace", "DeviceGray");
280 pdfDict.insertInt("BitsPerComponent", 8);
281 pdfDict.insertName("Filter", "FlateDecode");
282 pdfDict.insertInt("Length", length);
283 pdfDict.emitObject(stream, catalog);
287 ////////////////////////////////////////////////////////////////////////////////
289 void SkPDFBitmap::addResources(SkPDFCatalog* catalog) const {
291 if (catalog->addObject(fSMask.get())) {
292 fSMask->addResources(catalog);
297 void SkPDFBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
298 SkAutoLockPixels autoLockPixels(fBitmap);
299 SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType ||
300 fBitmap.getColorTable());
302 // Write to a temporary buffer to get the compressed length.
303 SkDynamicMemoryWStream buffer;
304 SkDeflateWStream deflateWStream(&buffer);
305 bitmap_to_pdf_pixels(fBitmap, &deflateWStream);
306 deflateWStream.finalize(); // call before detachAsStream().
307 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
309 this->emitDict(stream, catalog, asset->getLength());
310 pdf_stream_begin(stream);
311 stream->writeStream(asset.get(), asset->getLength());
312 pdf_stream_end(stream);
315 static SkPDFArray* make_indexed_color_space(const SkColorTable* table) {
316 SkPDFArray* result = SkNEW(SkPDFArray);
318 result->appendName("Indexed");
319 result->appendName("DeviceRGB");
321 if (table->count() < 1) {
322 result->appendInt(0);
323 char shortTableArray[3] = {0, 0, 0};
324 SkString tableString(shortTableArray, SK_ARRAY_COUNT(shortTableArray));
325 result->append(new SkPDFString(tableString))->unref();
328 result->appendInt(table->count() - 1); // maximum color index.
330 // Potentially, this could be represented in fewer bytes with a stream.
331 // Max size as a string is 1.5k.
332 char tableArray[256 * 3];
333 SkASSERT(3u * table->count() <= SK_ARRAY_COUNT(tableArray));
334 uint8_t* tablePtr = reinterpret_cast<uint8_t*>(tableArray);
335 const SkPMColor* colors = table->readColors();
336 for (int i = 0; i < table->count(); i++) {
337 pmcolor_to_rgb24(colors[i], tablePtr);
340 SkString tableString(tableArray, 3 * table->count());
341 result->append(new SkPDFString(tableString))->unref();
345 void SkPDFBitmap::emitDict(SkWStream* stream,
346 SkPDFCatalog* catalog,
347 size_t length) const {
348 SkPDFDict pdfDict("XObject");
349 pdfDict.insertName("Subtype", "Image");
350 pdfDict.insertInt("Width", fBitmap.width());
351 pdfDict.insertInt("Height", fBitmap.height());
352 if (fBitmap.colorType() == kIndex_8_SkColorType) {
353 SkASSERT(1 == pdf_color_component_count(fBitmap.colorType()));
354 pdfDict.insert("ColorSpace", make_indexed_color_space(
355 fBitmap.getColorTable()))->unref();
356 } else if (1 == pdf_color_component_count(fBitmap.colorType())) {
357 pdfDict.insertName("ColorSpace", "DeviceGray");
359 pdfDict.insertName("ColorSpace", "DeviceRGB");
361 pdfDict.insertInt("BitsPerComponent", 8);
363 pdfDict.insert("SMask", new SkPDFObjRef(fSMask))->unref();
365 pdfDict.insertName("Filter", "FlateDecode");
366 pdfDict.insertInt("Length", length);
367 pdfDict.emitObject(stream, catalog);
370 SkPDFBitmap::SkPDFBitmap(const SkBitmap& bm,
372 : fBitmap(bm), fSMask(smask) {}
374 SkPDFBitmap::~SkPDFBitmap() {}
376 ////////////////////////////////////////////////////////////////////////////////
378 static const SkBitmap& immutable_bitmap(const SkBitmap& bm, SkBitmap* copy) {
379 if (bm.isImmutable()) {
383 copy->setImmutable();
387 SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon, const SkBitmap& bitmap) {
389 if (!SkColorTypeIsValid(bitmap.colorType()) ||
390 kUnknown_SkColorType == bitmap.colorType()) {
394 const SkBitmap& bm = immutable_bitmap(bitmap, ©);
395 if (bm.drawsNothing()) {
398 if (SkPDFBitmap* canonBitmap = canon->findBitmap(bm)) {
399 return SkRef(canonBitmap);
401 SkPDFObject* smask = NULL;
402 if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) {
403 smask = SkNEW_ARGS(PDFAlphaBitmap, (bm));
405 SkPDFBitmap* pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (bm, smask));
406 canon->addBitmap(pdfBitmap);