blob: e4580fcb98e69f7ec5b4f3eb63b69fd34bb207ab [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
halcanarydb0dcc72015-03-20 12:31:52 -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));
halcanarydb0dcc72015-03-20 12:31:52 -070034 for (size_t i = 0; i < n / sizeof(buffer); ++i) {
35 out->write(buffer, sizeof(buffer));
halcanary1b5c6042015-02-18 11:29:56 -080036 }
halcanarydb0dcc72015-03-20 12:31:52 -070037 out->write(buffer, n % sizeof(buffer));
halcanary1b5c6042015-02-18 11:29:56 -080038}
39
halcanarydb0dcc72015-03-20 12:31:52 -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 }
halcanarydb0dcc72015-03-20 12:31:52 -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 {
halcanarydb0dcc72015-03-20 12:31:52 -070083 rgb[0] = rgb[1] = rgb[2] = 0;
halcanary1b5c6042015-02-18 11:29:56 -080084 }
85}
86
halcanarydb0dcc72015-03-20 12:31:52 -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 }
halcanarydb0dcc72015-03-20 12:31:52 -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 }
halcanarydb0dcc72015-03-20 12:31:52 -0700147 return;
halcanary1b5c6042015-02-18 11:29:56 -0800148 }
halcanarydb0dcc72015-03-20 12:31:52 -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
halcanarydb0dcc72015-03-20 12:31:52 -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 }
halcanarydb0dcc72015-03-20 12:31:52 -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 }
halcanarydb0dcc72015-03-20 12:31:52 -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() {}
mtklein36352bf2015-03-25 18:17:31 -0700247 void emitObject(SkWStream*, SkPDFCatalog*) override;
halcanary1b5c6042015-02-18 11:29:56 -0800248
249private:
250 const SkBitmap fBitmap;
halcanarydb0dcc72015-03-20 12:31:52 -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);
halcanarydb0dcc72015-03-20 12:31:52 -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);
halcanarydb0dcc72015-03-20 12:31:52 -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
halcanarydb0dcc72015-03-20 12:31:52 -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,
halcanarydb0dcc72015-03-20 12:31:52 -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);
halcanarydb0dcc72015-03-20 12:31:52 -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
halcanaryd4714af2015-03-25 13:23:13 -0700289void SkPDFBitmap::addResources(SkPDFCatalog* catalog) const {
halcanary1b5c6042015-02-18 11:29:56 -0800290 if (fSMask.get()) {
halcanaryd4714af2015-03-25 13:23:13 -0700291 if (catalog->addObject(fSMask.get())) {
292 fSMask->addResources(catalog);
halcanarydb0dcc72015-03-20 12:31:52 -0700293 }
halcanary1b5c6042015-02-18 11:29:56 -0800294 }
295}
296
297void SkPDFBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
298 SkAutoLockPixels autoLockPixels(fBitmap);
halcanarydb0dcc72015-03-20 12:31:52 -0700299 SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType ||
300 fBitmap.getColorTable());
halcanary1b5c6042015-02-18 11:29:56 -0800301
halcanary1b5c6042015-02-18 11:29:56 -0800302 // Write to a temporary buffer to get the compressed length.
303 SkDynamicMemoryWStream buffer;
304 SkDeflateWStream deflateWStream(&buffer);
halcanarydb0dcc72015-03-20 12:31:52 -0700305 bitmap_to_pdf_pixels(fBitmap, &deflateWStream);
halcanary1b5c6042015-02-18 11:29:56 -0800306 deflateWStream.finalize(); // call before detachAsStream().
307 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
308
halcanarydb0dcc72015-03-20 12:31:52 -0700309 this->emitDict(stream, catalog, asset->getLength());
halcanary1b5c6042015-02-18 11:29:56 -0800310 pdf_stream_begin(stream);
311 stream->writeStream(asset.get(), asset->getLength());
312 pdf_stream_end(stream);
halcanarydb0dcc72015-03-20 12:31:52 -0700313}
314
315static SkPDFArray* make_indexed_color_space(const SkColorTable* table) {
316 SkPDFArray* result = SkNEW(SkPDFArray);
317 result->reserve(4);
318 result->appendName("Indexed");
319 result->appendName("DeviceRGB");
320 SkASSERT(table);
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();
326 return result;
327 }
328 result->appendInt(table->count() - 1); // maximum color index.
329
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);
338 tablePtr += 3;
339 }
340 SkString tableString(tableArray, 3 * table->count());
341 result->append(new SkPDFString(tableString))->unref();
342 return result;
halcanary1b5c6042015-02-18 11:29:56 -0800343}
344
345void SkPDFBitmap::emitDict(SkWStream* stream,
346 SkPDFCatalog* catalog,
halcanarydb0dcc72015-03-20 12:31:52 -0700347 size_t length) const {
halcanary1b5c6042015-02-18 11:29:56 -0800348 SkPDFDict pdfDict("XObject");
349 pdfDict.insertName("Subtype", "Image");
350 pdfDict.insertInt("Width", fBitmap.width());
351 pdfDict.insertInt("Height", fBitmap.height());
halcanarydb0dcc72015-03-20 12:31:52 -0700352 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");
358 } else {
359 pdfDict.insertName("ColorSpace", "DeviceRGB");
360 }
halcanary1b5c6042015-02-18 11:29:56 -0800361 pdfDict.insertInt("BitsPerComponent", 8);
362 if (fSMask) {
363 pdfDict.insert("SMask", new SkPDFObjRef(fSMask))->unref();
364 }
halcanarydb0dcc72015-03-20 12:31:52 -0700365 pdfDict.insertName("Filter", "FlateDecode");
halcanary1b5c6042015-02-18 11:29:56 -0800366 pdfDict.insertInt("Length", length);
367 pdfDict.emitObject(stream, catalog);
368}
369
halcanary2e3f9d82015-02-27 12:41:03 -0800370SkPDFBitmap::SkPDFBitmap(const SkBitmap& bm,
halcanary792c80f2015-02-20 07:21:05 -0800371 SkPDFObject* smask)
halcanary2e3f9d82015-02-27 12:41:03 -0800372 : fBitmap(bm), fSMask(smask) {}
halcanary1b5c6042015-02-18 11:29:56 -0800373
halcanary2e3f9d82015-02-27 12:41:03 -0800374SkPDFBitmap::~SkPDFBitmap() {}
halcanary1b5c6042015-02-18 11:29:56 -0800375
376////////////////////////////////////////////////////////////////////////////////
halcanarydb0dcc72015-03-20 12:31:52 -0700377
378static const SkBitmap& immutable_bitmap(const SkBitmap& bm, SkBitmap* copy) {
379 if (bm.isImmutable()) {
380 return bm;
halcanary1b5c6042015-02-18 11:29:56 -0800381 }
halcanarydb0dcc72015-03-20 12:31:52 -0700382 bm.copyTo(copy);
383 copy->setImmutable();
384 return *copy;
halcanary1b5c6042015-02-18 11:29:56 -0800385}
386
halcanarydb0dcc72015-03-20 12:31:52 -0700387SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon, const SkBitmap& bitmap) {
halcanary792c80f2015-02-20 07:21:05 -0800388 SkASSERT(canon);
halcanarydb0dcc72015-03-20 12:31:52 -0700389 if (!SkColorTypeIsValid(bitmap.colorType()) ||
390 kUnknown_SkColorType == bitmap.colorType()) {
halcanary1b5c6042015-02-18 11:29:56 -0800391 return NULL;
392 }
halcanarydb0dcc72015-03-20 12:31:52 -0700393 SkBitmap copy;
394 const SkBitmap& bm = immutable_bitmap(bitmap, &copy);
halcanary1b5c6042015-02-18 11:29:56 -0800395 if (bm.drawsNothing()) {
396 return NULL;
397 }
halcanarydb0dcc72015-03-20 12:31:52 -0700398 if (SkPDFBitmap* canonBitmap = canon->findBitmap(bm)) {
399 return SkRef(canonBitmap);
halcanary1b5c6042015-02-18 11:29:56 -0800400 }
401 SkPDFObject* smask = NULL;
402 if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) {
halcanary1b5c6042015-02-18 11:29:56 -0800403 smask = SkNEW_ARGS(PDFAlphaBitmap, (bm));
404 }
halcanarydb0dcc72015-03-20 12:31:52 -0700405 SkPDFBitmap* pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (bm, smask));
halcanary792c80f2015-02-20 07:21:05 -0800406 canon->addBitmap(pdfBitmap);
halcanary1b5c6042015-02-18 11:29:56 -0800407 return pdfBitmap;
408}