blob: 486dac44aa15655e7d9c32cc6a91c712cebb9519 [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"
mtkleine6cf9cb2015-02-26 13:25:05 -08009#include "SkFlate.h"
halcanary1b5c6042015-02-18 11:29:56 -080010#include "SkPDFBitmap.h"
11#include "SkPDFCanon.h"
12#include "SkPDFCatalog.h"
halcanary1b5c6042015-02-18 11:29:56 -080013#include "SkStream.h"
14#include "SkUnPreMultiply.h"
15
16////////////////////////////////////////////////////////////////////////////////
17
18static void pdf_stream_begin(SkWStream* stream) {
19 static const char streamBegin[] = " stream\n";
20 stream->write(streamBegin, strlen(streamBegin));
21}
22
23static void pdf_stream_end(SkWStream* stream) {
24 static const char streamEnd[] = "\nendstream";
25 stream->write(streamEnd, strlen(streamEnd));
26}
27
halcanary86ad8d62015-03-20 09:10:56 -070028////////////////////////////////////////////////////////////////////////////////
halcanary1b5c6042015-02-18 11:29:56 -080029
halcanary1b5c6042015-02-18 11:29:56 -080030// write a single byte to a stream n times.
31static void fill_stream(SkWStream* out, char value, size_t n) {
32 char buffer[4096];
33 memset(buffer, value, sizeof(buffer));
halcanary86ad8d62015-03-20 09:10:56 -070034 for (size_t i = 0; i < n / sizeof(buffer); ++i) {
35 out->write(buffer, sizeof(buffer));
halcanary1b5c6042015-02-18 11:29:56 -080036 }
halcanary86ad8d62015-03-20 09:10:56 -070037 out->write(buffer, n % sizeof(buffer));
halcanary1b5c6042015-02-18 11:29:56 -080038}
39
halcanary86ad8d62015-03-20 09:10:56 -070040// unpremultiply and extract R, G, B components.
41static 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));
46}
47
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 */
57static void get_neighbor_avg_color(const SkBitmap& bm,
58 int xOrig,
59 int yOrig,
60 uint8_t rgb[3]) {
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);
halcanary1b5c6042015-02-18 11:29:56 -080076 }
77 }
halcanary86ad8d62015-03-20 09:10:56 -070078 if (a > 0) {
79 rgb[0] = SkToU8(255 * r / a);
80 rgb[1] = SkToU8(255 * g / a);
81 rgb[2] = SkToU8(255 * b / a);
halcanary1b5c6042015-02-18 11:29:56 -080082 } else {
halcanary86ad8d62015-03-20 09:10:56 -070083 rgb[0] = rgb[1] = rgb[2] = 0;
halcanary1b5c6042015-02-18 11:29:56 -080084 }
85}
86
halcanary86ad8d62015-03-20 09:10:56 -070087static size_t pixel_count(const SkBitmap& bm) {
88 return SkToSizeT(bm.width()) * SkToSizeT(bm.height());
89}
90
91static const SkBitmap& not4444(const SkBitmap& input, SkBitmap* copy) {
92 if (input.colorType() != kARGB_4444_SkColorType) {
93 return input;
94 }
95 // ARGB_4444 is rarely used, so we can do a wasteful tmp copy.
96 SkAssertResult(input.copyTo(copy, kN32_SkColorType));
97 copy->setImmutable();
98 return *copy;
99}
100
101static size_t pdf_color_component_count(SkColorType ct) {
102 switch (ct) {
103 case kN32_SkColorType:
104 case kRGB_565_SkColorType:
105 case kARGB_4444_SkColorType:
106 return 3;
107 case kAlpha_8_SkColorType:
108 case kIndex_8_SkColorType:
109 case kGray_8_SkColorType:
110 return 1;
111 case kUnknown_SkColorType:
112 default:
113 SkDEBUGFAIL("unexpected color type");
114 return 0;
115 }
116}
117
118static 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);
halcanary1b5c6042015-02-18 11:29:56 -0800123 return;
124 }
halcanary86ad8d62015-03-20 09:10:56 -0700125 SkBitmap copy;
126 const SkBitmap& bm = not4444(bitmap, &copy);
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);
140 } else {
141 get_neighbor_avg_color(bm, x, y, dst);
142 }
143 dst += 3;
144 }
145 out->write(scanline.get(), 3 * bm.width());
halcanary1b5c6042015-02-18 11:29:56 -0800146 }
halcanary86ad8d62015-03-20 09:10:56 -0700147 return;
halcanary1b5c6042015-02-18 11:29:56 -0800148 }
halcanary86ad8d62015-03-20 09:10:56 -0700149 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);
160 }
161 out->write(scanline.get(), 3 * bm.width());
162 }
163 return;
164 }
165 case kAlpha_8_SkColorType:
166 SkASSERT(1 == pdf_color_component_count(bitmap.colorType()));
167 fill_stream(out, '\x00', pixel_count(bm));
168 return;
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());
175 }
176 return;
177 case kUnknown_SkColorType:
178 case kARGB_4444_SkColorType:
179 default:
180 SkDEBUGFAIL("unexpected color type");
halcanary1b5c6042015-02-18 11:29:56 -0800181 }
182}
183
halcanary86ad8d62015-03-20 09:10:56 -0700184////////////////////////////////////////////////////////////////////////////////
185
186static void bitmap_alpha_to_a8(const SkBitmap& bitmap, SkWStream* out) {
187 if (!bitmap.getPixels()) {
188 fill_stream(out, '\xFF', pixel_count(bitmap));
halcanary1b5c6042015-02-18 11:29:56 -0800189 return;
190 }
halcanary86ad8d62015-03-20 09:10:56 -0700191 SkBitmap copy;
192 const SkBitmap& bm = not4444(bitmap, &copy);
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++);
202 }
203 out->write(scanline.get(), bm.width());
204 }
205 return;
halcanary1b5c6042015-02-18 11:29:56 -0800206 }
halcanary86ad8d62015-03-20 09:10:56 -0700207 case kAlpha_8_SkColorType:
208 for (int y = 0; y < bm.height(); ++y) {
209 out->write(bm.getAddr8(0, y), bm.width());
210 }
211 return;
212 case kIndex_8_SkColorType: {
213 SkColorTable* ct = bm.getColorTable();
214 SkASSERT(ct);
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++]);
221 }
222 out->write(scanline.get(), bm.width());
223 }
224 return;
225 }
226 case kRGB_565_SkColorType:
227 case kGray_8_SkColorType:
228 SkDEBUGFAIL("color type has no alpha");
229 return;
230 case kARGB_4444_SkColorType:
231 SkDEBUGFAIL("4444 color type should have been converted to N32");
232 return;
233 case kUnknown_SkColorType:
234 default:
235 SkDEBUGFAIL("unexpected color type");
halcanary1b5c6042015-02-18 11:29:56 -0800236 }
237}
238
239////////////////////////////////////////////////////////////////////////////////
240
241namespace {
242// This SkPDFObject only outputs the alpha layer of the given bitmap.
243class PDFAlphaBitmap : public SkPDFObject {
244public:
245 PDFAlphaBitmap(const SkBitmap& bm) : fBitmap(bm) {}
246 ~PDFAlphaBitmap() {}
247 void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE;
halcanary1b5c6042015-02-18 11:29:56 -0800248
249private:
250 const SkBitmap fBitmap;
halcanary86ad8d62015-03-20 09:10:56 -0700251 void emitDict(SkWStream*, SkPDFCatalog*, size_t) const;
halcanary1b5c6042015-02-18 11:29:56 -0800252};
253
254void PDFAlphaBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
255 SkAutoLockPixels autoLockPixels(fBitmap);
halcanary86ad8d62015-03-20 09:10:56 -0700256 SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType ||
257 fBitmap.getColorTable());
halcanary1b5c6042015-02-18 11:29:56 -0800258
halcanary1b5c6042015-02-18 11:29:56 -0800259 // Write to a temporary buffer to get the compressed length.
260 SkDynamicMemoryWStream buffer;
261 SkDeflateWStream deflateWStream(&buffer);
halcanary86ad8d62015-03-20 09:10:56 -0700262 bitmap_alpha_to_a8(fBitmap, &deflateWStream);
halcanary1b5c6042015-02-18 11:29:56 -0800263 deflateWStream.finalize(); // call before detachAsStream().
264 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
265
halcanary86ad8d62015-03-20 09:10:56 -0700266 this->emitDict(stream, catalog, asset->getLength());
halcanary1b5c6042015-02-18 11:29:56 -0800267 pdf_stream_begin(stream);
268 stream->writeStream(asset.get(), asset->getLength());
269 pdf_stream_end(stream);
halcanary1b5c6042015-02-18 11:29:56 -0800270}
271
272void PDFAlphaBitmap::emitDict(SkWStream* stream,
273 SkPDFCatalog* catalog,
halcanary86ad8d62015-03-20 09:10:56 -0700274 size_t length) const {
halcanary1b5c6042015-02-18 11:29:56 -0800275 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);
halcanary86ad8d62015-03-20 09:10:56 -0700281 pdfDict.insertName("Filter", "FlateDecode");
halcanary1b5c6042015-02-18 11:29:56 -0800282 pdfDict.insertInt("Length", length);
283 pdfDict.emitObject(stream, catalog);
284}
285} // namespace
286
287////////////////////////////////////////////////////////////////////////////////
288
289void SkPDFBitmap::addResources(SkTSet<SkPDFObject*>* resourceSet,
290 SkPDFCatalog* catalog) const {
291 if (fSMask.get()) {
halcanary86ad8d62015-03-20 09:10:56 -0700292 if (resourceSet->add(fSMask.get())) {
293 fSMask->addResources(resourceSet, catalog);
294 }
halcanary1b5c6042015-02-18 11:29:56 -0800295 }
296}
297
298void SkPDFBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
299 SkAutoLockPixels autoLockPixels(fBitmap);
halcanary86ad8d62015-03-20 09:10:56 -0700300 SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType ||
301 fBitmap.getColorTable());
halcanary1b5c6042015-02-18 11:29:56 -0800302
halcanary1b5c6042015-02-18 11:29:56 -0800303 // Write to a temporary buffer to get the compressed length.
304 SkDynamicMemoryWStream buffer;
305 SkDeflateWStream deflateWStream(&buffer);
halcanary86ad8d62015-03-20 09:10:56 -0700306 bitmap_to_pdf_pixels(fBitmap, &deflateWStream);
halcanary1b5c6042015-02-18 11:29:56 -0800307 deflateWStream.finalize(); // call before detachAsStream().
308 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
309
halcanary86ad8d62015-03-20 09:10:56 -0700310 this->emitDict(stream, catalog, asset->getLength());
halcanary1b5c6042015-02-18 11:29:56 -0800311 pdf_stream_begin(stream);
312 stream->writeStream(asset.get(), asset->getLength());
313 pdf_stream_end(stream);
halcanary86ad8d62015-03-20 09:10:56 -0700314}
315
316static SkPDFArray* make_indexed_color_space(const SkColorTable* table) {
317 SkPDFArray* result = SkNEW(SkPDFArray);
318 result->reserve(4);
319 result->appendName("Indexed");
320 result->appendName("DeviceRGB");
321 SkASSERT(table);
322 if (table->count() < 1) {
323 result->appendInt(0);
324 char shortTableArray[3] = {0, 0, 0};
325 SkString tableString(shortTableArray, SK_ARRAY_COUNT(shortTableArray));
326 result->append(new SkPDFString(tableString))->unref();
327 return result;
328 }
329 result->appendInt(table->count() - 1); // maximum color index.
330
331 // Potentially, this could be represented in fewer bytes with a stream.
332 // Max size as a string is 1.5k.
333 char tableArray[256 * 3];
334 SkASSERT(3u * table->count() <= SK_ARRAY_COUNT(tableArray));
335 uint8_t* tablePtr = reinterpret_cast<uint8_t*>(tableArray);
336 const SkPMColor* colors = table->readColors();
337 for (int i = 0; i < table->count(); i++) {
338 pmcolor_to_rgb24(colors[i], tablePtr);
339 tablePtr += 3;
340 }
341 SkString tableString(tableArray, 3 * table->count());
342 result->append(new SkPDFString(tableString))->unref();
343 return result;
halcanary1b5c6042015-02-18 11:29:56 -0800344}
345
346void SkPDFBitmap::emitDict(SkWStream* stream,
347 SkPDFCatalog* catalog,
halcanary86ad8d62015-03-20 09:10:56 -0700348 size_t length) const {
halcanary1b5c6042015-02-18 11:29:56 -0800349 SkPDFDict pdfDict("XObject");
350 pdfDict.insertName("Subtype", "Image");
351 pdfDict.insertInt("Width", fBitmap.width());
352 pdfDict.insertInt("Height", fBitmap.height());
halcanary86ad8d62015-03-20 09:10:56 -0700353 if (fBitmap.colorType() == kIndex_8_SkColorType) {
354 SkASSERT(1 == pdf_color_component_count(fBitmap.colorType()));
355 pdfDict.insert("ColorSpace", make_indexed_color_space(
356 fBitmap.getColorTable()))->unref();
357 } else if (1 == pdf_color_component_count(fBitmap.colorType())) {
358 pdfDict.insertName("ColorSpace", "DeviceGray");
359 } else {
360 pdfDict.insertName("ColorSpace", "DeviceRGB");
361 }
halcanary1b5c6042015-02-18 11:29:56 -0800362 pdfDict.insertInt("BitsPerComponent", 8);
363 if (fSMask) {
364 pdfDict.insert("SMask", new SkPDFObjRef(fSMask))->unref();
365 }
halcanary86ad8d62015-03-20 09:10:56 -0700366 pdfDict.insertName("Filter", "FlateDecode");
halcanary1b5c6042015-02-18 11:29:56 -0800367 pdfDict.insertInt("Length", length);
368 pdfDict.emitObject(stream, catalog);
369}
370
halcanary2e3f9d82015-02-27 12:41:03 -0800371SkPDFBitmap::SkPDFBitmap(const SkBitmap& bm,
halcanary792c80f2015-02-20 07:21:05 -0800372 SkPDFObject* smask)
halcanary2e3f9d82015-02-27 12:41:03 -0800373 : fBitmap(bm), fSMask(smask) {}
halcanary1b5c6042015-02-18 11:29:56 -0800374
halcanary2e3f9d82015-02-27 12:41:03 -0800375SkPDFBitmap::~SkPDFBitmap() {}
halcanary1b5c6042015-02-18 11:29:56 -0800376
377////////////////////////////////////////////////////////////////////////////////
halcanary86ad8d62015-03-20 09:10:56 -0700378
379static const SkBitmap& immutable_bitmap(const SkBitmap& bm, SkBitmap* copy) {
380 if (bm.isImmutable()) {
381 return bm;
halcanary1b5c6042015-02-18 11:29:56 -0800382 }
halcanary86ad8d62015-03-20 09:10:56 -0700383 bm.copyTo(copy);
384 copy->setImmutable();
385 return *copy;
halcanary1b5c6042015-02-18 11:29:56 -0800386}
387
halcanary86ad8d62015-03-20 09:10:56 -0700388SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon, const SkBitmap& bitmap) {
halcanary792c80f2015-02-20 07:21:05 -0800389 SkASSERT(canon);
halcanary86ad8d62015-03-20 09:10:56 -0700390 if (!SkColorTypeIsValid(bitmap.colorType()) ||
391 kUnknown_SkColorType == bitmap.colorType()) {
halcanary1b5c6042015-02-18 11:29:56 -0800392 return NULL;
393 }
halcanary86ad8d62015-03-20 09:10:56 -0700394 SkBitmap copy;
395 const SkBitmap& bm = immutable_bitmap(bitmap, &copy);
halcanary1b5c6042015-02-18 11:29:56 -0800396 if (bm.drawsNothing()) {
397 return NULL;
398 }
halcanary86ad8d62015-03-20 09:10:56 -0700399 if (SkPDFBitmap* canonBitmap = canon->findBitmap(bm)) {
400 return SkRef(canonBitmap);
halcanary1b5c6042015-02-18 11:29:56 -0800401 }
402 SkPDFObject* smask = NULL;
403 if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) {
halcanary1b5c6042015-02-18 11:29:56 -0800404 smask = SkNEW_ARGS(PDFAlphaBitmap, (bm));
405 }
halcanary86ad8d62015-03-20 09:10:56 -0700406 SkPDFBitmap* pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (bm, smask));
halcanary792c80f2015-02-20 07:21:05 -0800407 canon->addBitmap(pdfBitmap);
halcanary1b5c6042015-02-18 11:29:56 -0800408 return pdfBitmap;
409}