blob: 2a9deb6d0bdaf433fe95951cbe5fab847a57903d [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"
halcanary1b5c6042015-02-18 11:29:56 -080012#include "SkStream.h"
13#include "SkUnPreMultiply.h"
14
15////////////////////////////////////////////////////////////////////////////////
16
17static void pdf_stream_begin(SkWStream* stream) {
18 static const char streamBegin[] = " stream\n";
19 stream->write(streamBegin, strlen(streamBegin));
20}
21
22static void pdf_stream_end(SkWStream* stream) {
23 static const char streamEnd[] = "\nendstream";
24 stream->write(streamEnd, strlen(streamEnd));
25}
26
halcanarydb0dcc72015-03-20 12:31:52 -070027////////////////////////////////////////////////////////////////////////////////
halcanary1b5c6042015-02-18 11:29:56 -080028
halcanary1b5c6042015-02-18 11:29:56 -080029// write a single byte to a stream n times.
30static void fill_stream(SkWStream* out, char value, size_t n) {
31 char buffer[4096];
32 memset(buffer, value, sizeof(buffer));
halcanarydb0dcc72015-03-20 12:31:52 -070033 for (size_t i = 0; i < n / sizeof(buffer); ++i) {
34 out->write(buffer, sizeof(buffer));
halcanary1b5c6042015-02-18 11:29:56 -080035 }
halcanarydb0dcc72015-03-20 12:31:52 -070036 out->write(buffer, n % sizeof(buffer));
halcanary1b5c6042015-02-18 11:29:56 -080037}
38
halcanarydb0dcc72015-03-20 12:31:52 -070039// unpremultiply and extract R, G, B components.
40static void pmcolor_to_rgb24(SkPMColor pmColor, uint8_t* rgb) {
41 uint32_t s = SkUnPreMultiply::GetScale(SkGetPackedA32(pmColor));
42 rgb[0] = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(pmColor));
43 rgb[1] = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(pmColor));
44 rgb[2] = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(pmColor));
45}
46
47/* It is necessary to average the color component of transparent
48 pixels with their surrounding neighbors since the PDF renderer may
49 separately re-sample the alpha and color channels when the image is
50 not displayed at its native resolution. Since an alpha of zero
51 gives no information about the color component, the pathological
52 case is a white image with sharp transparency bounds - the color
53 channel goes to black, and the should-be-transparent pixels are
54 rendered as grey because of the separate soft mask and color
55 resizing. e.g.: gm/bitmappremul.cpp */
56static void get_neighbor_avg_color(const SkBitmap& bm,
57 int xOrig,
58 int yOrig,
59 uint8_t rgb[3]) {
60 SkASSERT(kN32_SkColorType == bm.colorType());
61 unsigned a = 0, r = 0, g = 0, b = 0;
62 // Clamp the range to the edge of the bitmap.
63 int ymin = SkTMax(0, yOrig - 1);
64 int ymax = SkTMin(yOrig + 1, bm.height() - 1);
65 int xmin = SkTMax(0, xOrig - 1);
66 int xmax = SkTMin(xOrig + 1, bm.width() - 1);
67 for (int y = ymin; y <= ymax; ++y) {
68 SkPMColor* scanline = bm.getAddr32(0, y);
69 for (int x = xmin; x <= xmax; ++x) {
70 SkPMColor pmColor = scanline[x];
71 a += SkGetPackedA32(pmColor);
72 r += SkGetPackedR32(pmColor);
73 g += SkGetPackedG32(pmColor);
74 b += SkGetPackedB32(pmColor);
halcanary1b5c6042015-02-18 11:29:56 -080075 }
76 }
halcanarydb0dcc72015-03-20 12:31:52 -070077 if (a > 0) {
78 rgb[0] = SkToU8(255 * r / a);
79 rgb[1] = SkToU8(255 * g / a);
80 rgb[2] = SkToU8(255 * b / a);
halcanary1b5c6042015-02-18 11:29:56 -080081 } else {
halcanarydb0dcc72015-03-20 12:31:52 -070082 rgb[0] = rgb[1] = rgb[2] = 0;
halcanary1b5c6042015-02-18 11:29:56 -080083 }
84}
85
halcanarydb0dcc72015-03-20 12:31:52 -070086static size_t pixel_count(const SkBitmap& bm) {
87 return SkToSizeT(bm.width()) * SkToSizeT(bm.height());
88}
89
90static const SkBitmap& not4444(const SkBitmap& input, SkBitmap* copy) {
91 if (input.colorType() != kARGB_4444_SkColorType) {
92 return input;
93 }
94 // ARGB_4444 is rarely used, so we can do a wasteful tmp copy.
95 SkAssertResult(input.copyTo(copy, kN32_SkColorType));
96 copy->setImmutable();
97 return *copy;
98}
99
100static size_t pdf_color_component_count(SkColorType ct) {
101 switch (ct) {
102 case kN32_SkColorType:
103 case kRGB_565_SkColorType:
104 case kARGB_4444_SkColorType:
105 return 3;
106 case kAlpha_8_SkColorType:
107 case kIndex_8_SkColorType:
108 case kGray_8_SkColorType:
109 return 1;
110 case kUnknown_SkColorType:
111 default:
112 SkDEBUGFAIL("unexpected color type");
113 return 0;
114 }
115}
116
117static void bitmap_to_pdf_pixels(const SkBitmap& bitmap, SkWStream* out) {
118 if (!bitmap.getPixels()) {
119 size_t size = pixel_count(bitmap) *
120 pdf_color_component_count(bitmap.colorType());
121 fill_stream(out, '\x00', size);
halcanary1b5c6042015-02-18 11:29:56 -0800122 return;
123 }
halcanarydb0dcc72015-03-20 12:31:52 -0700124 SkBitmap copy;
125 const SkBitmap& bm = not4444(bitmap, &copy);
126 SkAutoLockPixels autoLockPixels(bm);
127 switch (bm.colorType()) {
128 case kN32_SkColorType: {
129 SkASSERT(3 == pdf_color_component_count(bitmap.colorType()));
130 SkAutoTMalloc<uint8_t> scanline(3 * bm.width());
131 for (int y = 0; y < bm.height(); ++y) {
132 const SkPMColor* src = bm.getAddr32(0, y);
133 uint8_t* dst = scanline.get();
134 for (int x = 0; x < bm.width(); ++x) {
135 SkPMColor color = *src++;
136 U8CPU alpha = SkGetPackedA32(color);
137 if (alpha != SK_AlphaTRANSPARENT) {
138 pmcolor_to_rgb24(color, dst);
139 } else {
140 get_neighbor_avg_color(bm, x, y, dst);
141 }
142 dst += 3;
143 }
144 out->write(scanline.get(), 3 * bm.width());
halcanary1b5c6042015-02-18 11:29:56 -0800145 }
halcanarydb0dcc72015-03-20 12:31:52 -0700146 return;
halcanary1b5c6042015-02-18 11:29:56 -0800147 }
halcanarydb0dcc72015-03-20 12:31:52 -0700148 case kRGB_565_SkColorType: {
149 SkASSERT(3 == pdf_color_component_count(bitmap.colorType()));
150 SkAutoTMalloc<uint8_t> scanline(3 * bm.width());
151 for (int y = 0; y < bm.height(); ++y) {
152 const uint16_t* src = bm.getAddr16(0, y);
153 uint8_t* dst = scanline.get();
154 for (int x = 0; x < bm.width(); ++x) {
155 U16CPU color565 = *src++;
156 *dst++ = SkPacked16ToR32(color565);
157 *dst++ = SkPacked16ToG32(color565);
158 *dst++ = SkPacked16ToB32(color565);
159 }
160 out->write(scanline.get(), 3 * bm.width());
161 }
162 return;
163 }
164 case kAlpha_8_SkColorType:
165 SkASSERT(1 == pdf_color_component_count(bitmap.colorType()));
166 fill_stream(out, '\x00', pixel_count(bm));
167 return;
168 case kGray_8_SkColorType:
169 case kIndex_8_SkColorType:
170 SkASSERT(1 == pdf_color_component_count(bitmap.colorType()));
171 // these two formats need no transformation to serialize.
172 for (int y = 0; y < bm.height(); ++y) {
173 out->write(bm.getAddr8(0, y), bm.width());
174 }
175 return;
176 case kUnknown_SkColorType:
177 case kARGB_4444_SkColorType:
178 default:
179 SkDEBUGFAIL("unexpected color type");
halcanary1b5c6042015-02-18 11:29:56 -0800180 }
181}
182
halcanarydb0dcc72015-03-20 12:31:52 -0700183////////////////////////////////////////////////////////////////////////////////
184
185static void bitmap_alpha_to_a8(const SkBitmap& bitmap, SkWStream* out) {
186 if (!bitmap.getPixels()) {
187 fill_stream(out, '\xFF', pixel_count(bitmap));
halcanary1b5c6042015-02-18 11:29:56 -0800188 return;
189 }
halcanarydb0dcc72015-03-20 12:31:52 -0700190 SkBitmap copy;
191 const SkBitmap& bm = not4444(bitmap, &copy);
192 SkAutoLockPixels autoLockPixels(bm);
193 switch (bm.colorType()) {
194 case kN32_SkColorType: {
195 SkAutoTMalloc<uint8_t> scanline(bm.width());
196 for (int y = 0; y < bm.height(); ++y) {
197 uint8_t* dst = scanline.get();
198 const SkPMColor* src = bm.getAddr32(0, y);
199 for (int x = 0; x < bm.width(); ++x) {
200 *dst++ = SkGetPackedA32(*src++);
201 }
202 out->write(scanline.get(), bm.width());
203 }
204 return;
halcanary1b5c6042015-02-18 11:29:56 -0800205 }
halcanarydb0dcc72015-03-20 12:31:52 -0700206 case kAlpha_8_SkColorType:
207 for (int y = 0; y < bm.height(); ++y) {
208 out->write(bm.getAddr8(0, y), bm.width());
209 }
210 return;
211 case kIndex_8_SkColorType: {
212 SkColorTable* ct = bm.getColorTable();
213 SkASSERT(ct);
214 SkAutoTMalloc<uint8_t> scanline(bm.width());
215 for (int y = 0; y < bm.height(); ++y) {
216 uint8_t* dst = scanline.get();
217 const uint8_t* src = bm.getAddr8(0, y);
218 for (int x = 0; x < bm.width(); ++x) {
219 *dst++ = SkGetPackedA32((*ct)[*src++]);
220 }
221 out->write(scanline.get(), bm.width());
222 }
223 return;
224 }
225 case kRGB_565_SkColorType:
226 case kGray_8_SkColorType:
227 SkDEBUGFAIL("color type has no alpha");
228 return;
229 case kARGB_4444_SkColorType:
230 SkDEBUGFAIL("4444 color type should have been converted to N32");
231 return;
232 case kUnknown_SkColorType:
233 default:
234 SkDEBUGFAIL("unexpected color type");
halcanary1b5c6042015-02-18 11:29:56 -0800235 }
236}
237
238////////////////////////////////////////////////////////////////////////////////
239
240namespace {
241// This SkPDFObject only outputs the alpha layer of the given bitmap.
242class PDFAlphaBitmap : public SkPDFObject {
243public:
244 PDFAlphaBitmap(const SkBitmap& bm) : fBitmap(bm) {}
245 ~PDFAlphaBitmap() {}
halcanary37c46ca2015-03-31 12:30:20 -0700246 void emitObject(SkWStream*,
247 const SkPDFObjNumMap&,
248 const SkPDFSubstituteMap&) override;
halcanary1b5c6042015-02-18 11:29:56 -0800249
250private:
251 const SkBitmap fBitmap;
halcanary1b5c6042015-02-18 11:29:56 -0800252};
253
halcanary37c46ca2015-03-31 12:30:20 -0700254void PDFAlphaBitmap::emitObject(SkWStream* stream,
255 const SkPDFObjNumMap& objNumMap,
256 const SkPDFSubstituteMap& substitutes) {
halcanary1b5c6042015-02-18 11:29:56 -0800257 SkAutoLockPixels autoLockPixels(fBitmap);
halcanarydb0dcc72015-03-20 12:31:52 -0700258 SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType ||
259 fBitmap.getColorTable());
halcanary1b5c6042015-02-18 11:29:56 -0800260
halcanary1b5c6042015-02-18 11:29:56 -0800261 // Write to a temporary buffer to get the compressed length.
262 SkDynamicMemoryWStream buffer;
263 SkDeflateWStream deflateWStream(&buffer);
halcanarydb0dcc72015-03-20 12:31:52 -0700264 bitmap_alpha_to_a8(fBitmap, &deflateWStream);
halcanary1b5c6042015-02-18 11:29:56 -0800265 deflateWStream.finalize(); // call before detachAsStream().
266 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
267
halcanary1b5c6042015-02-18 11:29:56 -0800268 SkPDFDict pdfDict("XObject");
269 pdfDict.insertName("Subtype", "Image");
270 pdfDict.insertInt("Width", fBitmap.width());
271 pdfDict.insertInt("Height", fBitmap.height());
272 pdfDict.insertName("ColorSpace", "DeviceGray");
273 pdfDict.insertInt("BitsPerComponent", 8);
halcanarydb0dcc72015-03-20 12:31:52 -0700274 pdfDict.insertName("Filter", "FlateDecode");
halcanary37c46ca2015-03-31 12:30:20 -0700275 pdfDict.insertInt("Length", asset->getLength());
276 pdfDict.emitObject(stream, objNumMap, substitutes);
277
278 pdf_stream_begin(stream);
279 stream->writeStream(asset.get(), asset->getLength());
280 pdf_stream_end(stream);
halcanary1b5c6042015-02-18 11:29:56 -0800281}
282} // namespace
283
284////////////////////////////////////////////////////////////////////////////////
285
halcanary37c46ca2015-03-31 12:30:20 -0700286void SkPDFBitmap::addResources(SkPDFObjNumMap* catalog,
287 const SkPDFSubstituteMap& substitutes) const {
halcanary1b5c6042015-02-18 11:29:56 -0800288 if (fSMask.get()) {
halcanary37c46ca2015-03-31 12:30:20 -0700289 SkPDFObject* obj = substitutes.getSubstitute(fSMask.get());
290 SkASSERT(obj);
291 if (catalog->addObject(obj)) {
292 obj->addResources(catalog, substitutes);
halcanarydb0dcc72015-03-20 12:31:52 -0700293 }
halcanary1b5c6042015-02-18 11:29:56 -0800294 }
295}
296
halcanarydb0dcc72015-03-20 12:31:52 -0700297static SkPDFArray* make_indexed_color_space(const SkColorTable* table) {
298 SkPDFArray* result = SkNEW(SkPDFArray);
299 result->reserve(4);
300 result->appendName("Indexed");
301 result->appendName("DeviceRGB");
302 SkASSERT(table);
303 if (table->count() < 1) {
304 result->appendInt(0);
305 char shortTableArray[3] = {0, 0, 0};
306 SkString tableString(shortTableArray, SK_ARRAY_COUNT(shortTableArray));
307 result->append(new SkPDFString(tableString))->unref();
308 return result;
309 }
310 result->appendInt(table->count() - 1); // maximum color index.
311
312 // Potentially, this could be represented in fewer bytes with a stream.
313 // Max size as a string is 1.5k.
314 char tableArray[256 * 3];
315 SkASSERT(3u * table->count() <= SK_ARRAY_COUNT(tableArray));
316 uint8_t* tablePtr = reinterpret_cast<uint8_t*>(tableArray);
317 const SkPMColor* colors = table->readColors();
318 for (int i = 0; i < table->count(); i++) {
319 pmcolor_to_rgb24(colors[i], tablePtr);
320 tablePtr += 3;
321 }
322 SkString tableString(tableArray, 3 * table->count());
323 result->append(new SkPDFString(tableString))->unref();
324 return result;
halcanary1b5c6042015-02-18 11:29:56 -0800325}
326
halcanary37c46ca2015-03-31 12:30:20 -0700327void SkPDFBitmap::emitObject(SkWStream* stream,
328 const SkPDFObjNumMap& objNumMap,
329 const SkPDFSubstituteMap& substitutes) {
330 SkAutoLockPixels autoLockPixels(fBitmap);
331 SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType ||
332 fBitmap.getColorTable());
333
334 // Write to a temporary buffer to get the compressed length.
335 SkDynamicMemoryWStream buffer;
336 SkDeflateWStream deflateWStream(&buffer);
337 bitmap_to_pdf_pixels(fBitmap, &deflateWStream);
338 deflateWStream.finalize(); // call before detachAsStream().
339 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
340
halcanary1b5c6042015-02-18 11:29:56 -0800341 SkPDFDict pdfDict("XObject");
342 pdfDict.insertName("Subtype", "Image");
343 pdfDict.insertInt("Width", fBitmap.width());
344 pdfDict.insertInt("Height", fBitmap.height());
halcanarydb0dcc72015-03-20 12:31:52 -0700345 if (fBitmap.colorType() == kIndex_8_SkColorType) {
346 SkASSERT(1 == pdf_color_component_count(fBitmap.colorType()));
347 pdfDict.insert("ColorSpace", make_indexed_color_space(
348 fBitmap.getColorTable()))->unref();
349 } else if (1 == pdf_color_component_count(fBitmap.colorType())) {
350 pdfDict.insertName("ColorSpace", "DeviceGray");
351 } else {
352 pdfDict.insertName("ColorSpace", "DeviceRGB");
353 }
halcanary1b5c6042015-02-18 11:29:56 -0800354 pdfDict.insertInt("BitsPerComponent", 8);
355 if (fSMask) {
356 pdfDict.insert("SMask", new SkPDFObjRef(fSMask))->unref();
357 }
halcanarydb0dcc72015-03-20 12:31:52 -0700358 pdfDict.insertName("Filter", "FlateDecode");
halcanary37c46ca2015-03-31 12:30:20 -0700359 pdfDict.insertInt("Length", asset->getLength());
360 pdfDict.emitObject(stream, objNumMap,substitutes);
361
362 pdf_stream_begin(stream);
363 stream->writeStream(asset.get(), asset->getLength());
364 pdf_stream_end(stream);
halcanary1b5c6042015-02-18 11:29:56 -0800365}
366
halcanary2e3f9d82015-02-27 12:41:03 -0800367SkPDFBitmap::SkPDFBitmap(const SkBitmap& bm,
halcanary792c80f2015-02-20 07:21:05 -0800368 SkPDFObject* smask)
halcanary2e3f9d82015-02-27 12:41:03 -0800369 : fBitmap(bm), fSMask(smask) {}
halcanary1b5c6042015-02-18 11:29:56 -0800370
halcanary2e3f9d82015-02-27 12:41:03 -0800371SkPDFBitmap::~SkPDFBitmap() {}
halcanary1b5c6042015-02-18 11:29:56 -0800372
373////////////////////////////////////////////////////////////////////////////////
halcanarydb0dcc72015-03-20 12:31:52 -0700374
375static const SkBitmap& immutable_bitmap(const SkBitmap& bm, SkBitmap* copy) {
376 if (bm.isImmutable()) {
377 return bm;
halcanary1b5c6042015-02-18 11:29:56 -0800378 }
halcanarydb0dcc72015-03-20 12:31:52 -0700379 bm.copyTo(copy);
380 copy->setImmutable();
381 return *copy;
halcanary1b5c6042015-02-18 11:29:56 -0800382}
383
halcanarydb0dcc72015-03-20 12:31:52 -0700384SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon, const SkBitmap& bitmap) {
halcanary792c80f2015-02-20 07:21:05 -0800385 SkASSERT(canon);
halcanarydb0dcc72015-03-20 12:31:52 -0700386 if (!SkColorTypeIsValid(bitmap.colorType()) ||
387 kUnknown_SkColorType == bitmap.colorType()) {
halcanary1b5c6042015-02-18 11:29:56 -0800388 return NULL;
389 }
halcanarydb0dcc72015-03-20 12:31:52 -0700390 SkBitmap copy;
391 const SkBitmap& bm = immutable_bitmap(bitmap, &copy);
halcanary1b5c6042015-02-18 11:29:56 -0800392 if (bm.drawsNothing()) {
393 return NULL;
394 }
halcanarydb0dcc72015-03-20 12:31:52 -0700395 if (SkPDFBitmap* canonBitmap = canon->findBitmap(bm)) {
396 return SkRef(canonBitmap);
halcanary1b5c6042015-02-18 11:29:56 -0800397 }
398 SkPDFObject* smask = NULL;
399 if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) {
halcanary1b5c6042015-02-18 11:29:56 -0800400 smask = SkNEW_ARGS(PDFAlphaBitmap, (bm));
401 }
halcanarydb0dcc72015-03-20 12:31:52 -0700402 SkPDFBitmap* pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (bm, smask));
halcanary792c80f2015-02-20 07:21:05 -0800403 canon->addBitmap(pdfBitmap);
halcanary1b5c6042015-02-18 11:29:56 -0800404 return pdfBitmap;
405}