blob: 668f7dede0fc7495d877d35f262129a1a5d2815c [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
reed1b600d32015-03-20 10:03:36 -070028static size_t pixel_count(const SkBitmap& bm) {
29 return SkToSizeT(bm.width()) * SkToSizeT(bm.height());
30}
halcanary1b5c6042015-02-18 11:29:56 -080031
halcanary1b5c6042015-02-18 11:29:56 -080032// write a single byte to a stream n times.
33static void fill_stream(SkWStream* out, char value, size_t n) {
34 char buffer[4096];
35 memset(buffer, value, sizeof(buffer));
reed1b600d32015-03-20 10:03:36 -070036 while (n) {
37 size_t k = SkTMin(n, sizeof(buffer));
38 out->write(buffer, k);
39 n -= k;
halcanary1b5c6042015-02-18 11:29:56 -080040 }
41}
42
reed1b600d32015-03-20 10:03:36 -070043static SkPMColor get_pmcolor_neighbor_avg_color(const SkBitmap& bitmap,
44 int xOrig,
45 int yOrig) {
46 SkASSERT(kN32_SkColorType == bitmap.colorType());
47 SkASSERT(bitmap.getPixels());
48 uint8_t count = 0;
49 unsigned r = 0;
50 unsigned g = 0;
51 unsigned b = 0;
52 for (int y = yOrig - 1; y <= yOrig + 1; ++y) {
53 if (y < 0 || y >= bitmap.height()) {
54 continue;
55 }
56 uint32_t* src = bitmap.getAddr32(0, y);
57 for (int x = xOrig - 1; x <= xOrig + 1; ++x) {
58 if (x < 0 || x >= bitmap.width()) {
59 continue;
60 }
61 SkPMColor pmColor = src[x];
62 U8CPU alpha = SkGetPackedA32(pmColor);
63 if (alpha != SK_AlphaTRANSPARENT) {
64 uint32_t s = SkUnPreMultiply::GetScale(alpha);
65 r += SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(pmColor));
66 g += SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(pmColor));
67 b += SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(pmColor));
68 ++count;
69 }
halcanary1b5c6042015-02-18 11:29:56 -080070 }
71 }
reed1b600d32015-03-20 10:03:36 -070072 if (count == 0) {
73 return SkPackARGB32NoCheck(SK_AlphaOPAQUE, 0, 0, 0);
halcanary1b5c6042015-02-18 11:29:56 -080074 } else {
reed1b600d32015-03-20 10:03:36 -070075 return SkPackARGB32NoCheck(
76 SK_AlphaOPAQUE, r / count, g / count, b / count);
halcanary1b5c6042015-02-18 11:29:56 -080077 }
78}
79
reed1b600d32015-03-20 10:03:36 -070080static void pmcolor_to_rgb24(const SkBitmap& bm, SkWStream* out) {
81 SkASSERT(kN32_SkColorType == bm.colorType());
82 if (!bm.getPixels()) {
83 fill_stream(out, '\xFF', 3 * pixel_count(bm));
halcanary1b5c6042015-02-18 11:29:56 -080084 return;
85 }
reed1b600d32015-03-20 10:03:36 -070086 size_t scanlineLength = 3 * bm.width();
87 SkAutoTMalloc<uint8_t> scanline(scanlineLength);
88 for (int y = 0; y < bm.height(); ++y) {
89 uint8_t* dst = scanline.get();
90 const SkPMColor* src = bm.getAddr32(0, y);
91 for (int x = 0; x < bm.width(); ++x) {
92 SkPMColor color = *src++;
93 U8CPU alpha = SkGetPackedA32(color);
94 if (alpha != SK_AlphaTRANSPARENT) {
95 uint32_t s = SkUnPreMultiply::GetScale(alpha);
96 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(color));
97 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(color));
98 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(color));
99 } else {
100 /* It is necessary to average the color component of
101 transparent pixels with their surrounding neighbors
102 since the PDF renderer may separately re-sample the
103 alpha and color channels when the image is not
104 displayed at its native resolution. Since an alpha
105 of zero gives no information about the color
106 component, the pathological case is a white image
107 with sharp transparency bounds - the color channel
108 goes to black, and the should-be-transparent pixels
109 are rendered as grey because of the separate soft
110 mask and color resizing. e.g.: gm/bitmappremul.cpp */
111 color = get_pmcolor_neighbor_avg_color(bm, x, y);
112 *dst++ = SkGetPackedR32(color);
113 *dst++ = SkGetPackedG32(color);
114 *dst++ = SkGetPackedB32(color);
halcanary1b5c6042015-02-18 11:29:56 -0800115 }
116 }
reed1b600d32015-03-20 10:03:36 -0700117 out->write(scanline.get(), scanlineLength);
halcanary1b5c6042015-02-18 11:29:56 -0800118 }
119}
120
reed1b600d32015-03-20 10:03:36 -0700121static void pmcolor_alpha_to_a8(const SkBitmap& bm, SkWStream* out) {
122 SkASSERT(kN32_SkColorType == bm.colorType());
123 if (!bm.getPixels()) {
124 fill_stream(out, '\xFF', pixel_count(bm));
halcanary1b5c6042015-02-18 11:29:56 -0800125 return;
126 }
reed1b600d32015-03-20 10:03:36 -0700127 size_t scanlineLength = bm.width();
128 SkAutoTMalloc<uint8_t> scanline(scanlineLength);
129 for (int y = 0; y < bm.height(); ++y) {
130 uint8_t* dst = scanline.get();
131 const SkPMColor* src = bm.getAddr32(0, y);
132 for (int x = 0; x < bm.width(); ++x) {
133 *dst++ = SkGetPackedA32(*src++);
halcanary1b5c6042015-02-18 11:29:56 -0800134 }
reed1b600d32015-03-20 10:03:36 -0700135 out->write(scanline.get(), scanlineLength);
halcanary1b5c6042015-02-18 11:29:56 -0800136 }
137}
138
139////////////////////////////////////////////////////////////////////////////////
140
141namespace {
142// This SkPDFObject only outputs the alpha layer of the given bitmap.
143class PDFAlphaBitmap : public SkPDFObject {
144public:
145 PDFAlphaBitmap(const SkBitmap& bm) : fBitmap(bm) {}
146 ~PDFAlphaBitmap() {}
147 void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE;
reed1b600d32015-03-20 10:03:36 -0700148 void addResources(SkTSet<SkPDFObject*>*, SkPDFCatalog*) const SK_OVERRIDE {}
halcanary1b5c6042015-02-18 11:29:56 -0800149
150private:
151 const SkBitmap fBitmap;
reed1b600d32015-03-20 10:03:36 -0700152 void emitDict(SkWStream*, SkPDFCatalog*, size_t, bool) const;
halcanary1b5c6042015-02-18 11:29:56 -0800153};
154
155void PDFAlphaBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
156 SkAutoLockPixels autoLockPixels(fBitmap);
157
reed1b600d32015-03-20 10:03:36 -0700158#ifndef SK_NO_FLATE
halcanary1b5c6042015-02-18 11:29:56 -0800159 // Write to a temporary buffer to get the compressed length.
160 SkDynamicMemoryWStream buffer;
161 SkDeflateWStream deflateWStream(&buffer);
reed1b600d32015-03-20 10:03:36 -0700162 pmcolor_alpha_to_a8(fBitmap, &deflateWStream);
halcanary1b5c6042015-02-18 11:29:56 -0800163 deflateWStream.finalize(); // call before detachAsStream().
164 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
165
reed1b600d32015-03-20 10:03:36 -0700166 this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true);
halcanary1b5c6042015-02-18 11:29:56 -0800167 pdf_stream_begin(stream);
168 stream->writeStream(asset.get(), asset->getLength());
169 pdf_stream_end(stream);
reed1b600d32015-03-20 10:03:36 -0700170#else
171 this->emitDict(stream, catalog, pixel_count(fBitmap), /*deflate=*/false);
172 pdf_stream_begin(stream);
173 pmcolor_alpha_to_a8(fBitmap, stream);
174 pdf_stream_end(stream);
175#endif // SK_NO_FLATE
halcanary1b5c6042015-02-18 11:29:56 -0800176}
177
178void PDFAlphaBitmap::emitDict(SkWStream* stream,
179 SkPDFCatalog* catalog,
reed1b600d32015-03-20 10:03:36 -0700180 size_t length,
181 bool deflate) const {
halcanary1b5c6042015-02-18 11:29:56 -0800182 SkPDFDict pdfDict("XObject");
183 pdfDict.insertName("Subtype", "Image");
184 pdfDict.insertInt("Width", fBitmap.width());
185 pdfDict.insertInt("Height", fBitmap.height());
186 pdfDict.insertName("ColorSpace", "DeviceGray");
187 pdfDict.insertInt("BitsPerComponent", 8);
reed1b600d32015-03-20 10:03:36 -0700188 if (deflate) {
189 pdfDict.insertName("Filter", "FlateDecode");
190 }
halcanary1b5c6042015-02-18 11:29:56 -0800191 pdfDict.insertInt("Length", length);
192 pdfDict.emitObject(stream, catalog);
193}
194} // namespace
195
196////////////////////////////////////////////////////////////////////////////////
197
198void SkPDFBitmap::addResources(SkTSet<SkPDFObject*>* resourceSet,
199 SkPDFCatalog* catalog) const {
200 if (fSMask.get()) {
reed1b600d32015-03-20 10:03:36 -0700201 resourceSet->add(fSMask.get());
halcanary1b5c6042015-02-18 11:29:56 -0800202 }
203}
204
205void SkPDFBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
206 SkAutoLockPixels autoLockPixels(fBitmap);
207
reed1b600d32015-03-20 10:03:36 -0700208#ifndef SK_NO_FLATE
halcanary1b5c6042015-02-18 11:29:56 -0800209 // Write to a temporary buffer to get the compressed length.
210 SkDynamicMemoryWStream buffer;
211 SkDeflateWStream deflateWStream(&buffer);
reed1b600d32015-03-20 10:03:36 -0700212 pmcolor_to_rgb24(fBitmap, &deflateWStream);
halcanary1b5c6042015-02-18 11:29:56 -0800213 deflateWStream.finalize(); // call before detachAsStream().
214 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
215
reed1b600d32015-03-20 10:03:36 -0700216 this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true);
halcanary1b5c6042015-02-18 11:29:56 -0800217 pdf_stream_begin(stream);
218 stream->writeStream(asset.get(), asset->getLength());
219 pdf_stream_end(stream);
reed1b600d32015-03-20 10:03:36 -0700220#else
221 this->emitDict(stream, catalog, 3 * pixel_count(fBitmap), /*deflate=*/false);
222 pdf_stream_begin(stream);
223 pmcolor_to_rgb24(fBitmap, stream);
224 pdf_stream_end(stream);
225 return;
226#endif // SK_NO_FLATE
halcanary1b5c6042015-02-18 11:29:56 -0800227}
228
229void SkPDFBitmap::emitDict(SkWStream* stream,
230 SkPDFCatalog* catalog,
reed1b600d32015-03-20 10:03:36 -0700231 size_t length,
232 bool deflate) const {
halcanary1b5c6042015-02-18 11:29:56 -0800233 SkPDFDict pdfDict("XObject");
234 pdfDict.insertName("Subtype", "Image");
235 pdfDict.insertInt("Width", fBitmap.width());
236 pdfDict.insertInt("Height", fBitmap.height());
reed1b600d32015-03-20 10:03:36 -0700237 pdfDict.insertName("ColorSpace", "DeviceRGB");
halcanary1b5c6042015-02-18 11:29:56 -0800238 pdfDict.insertInt("BitsPerComponent", 8);
239 if (fSMask) {
240 pdfDict.insert("SMask", new SkPDFObjRef(fSMask))->unref();
241 }
reed1b600d32015-03-20 10:03:36 -0700242 if (deflate) {
243 pdfDict.insertName("Filter", "FlateDecode");
244 }
halcanary1b5c6042015-02-18 11:29:56 -0800245 pdfDict.insertInt("Length", length);
246 pdfDict.emitObject(stream, catalog);
247}
248
halcanary2e3f9d82015-02-27 12:41:03 -0800249SkPDFBitmap::SkPDFBitmap(const SkBitmap& bm,
halcanary792c80f2015-02-20 07:21:05 -0800250 SkPDFObject* smask)
halcanary2e3f9d82015-02-27 12:41:03 -0800251 : fBitmap(bm), fSMask(smask) {}
halcanary1b5c6042015-02-18 11:29:56 -0800252
halcanary2e3f9d82015-02-27 12:41:03 -0800253SkPDFBitmap::~SkPDFBitmap() {}
halcanary1b5c6042015-02-18 11:29:56 -0800254
255////////////////////////////////////////////////////////////////////////////////
reed1b600d32015-03-20 10:03:36 -0700256static bool is_transparent(const SkBitmap& bm) {
257 SkAutoLockPixels autoLockPixels(bm);
258 if (NULL == bm.getPixels()) {
259 return true;
halcanary1b5c6042015-02-18 11:29:56 -0800260 }
reed1b600d32015-03-20 10:03:36 -0700261 SkASSERT(kN32_SkColorType == bm.colorType());
262 for (int y = 0; y < bm.height(); ++y) {
263 U8CPU alpha = 0;
264 const SkPMColor* src = bm.getAddr32(0, y);
265 for (int x = 0; x < bm.width(); ++x) {
266 alpha |= SkGetPackedA32(*src++);
267 }
268 if (alpha) {
269 return false;
270 }
271 }
272 return true;
halcanary1b5c6042015-02-18 11:29:56 -0800273}
274
reed1b600d32015-03-20 10:03:36 -0700275SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon,
276 const SkBitmap& bitmap,
277 const SkIRect& subset) {
halcanary792c80f2015-02-20 07:21:05 -0800278 SkASSERT(canon);
reed1b600d32015-03-20 10:03:36 -0700279 if (kN32_SkColorType != bitmap.colorType()) {
280 // TODO(halcanary): support other colortypes.
halcanary1b5c6042015-02-18 11:29:56 -0800281 return NULL;
282 }
reed1b600d32015-03-20 10:03:36 -0700283 SkBitmap bm;
284 // Should extractSubset be done by the SkPDFDevice?
285 if (!bitmap.extractSubset(&bm, subset)) {
286 return NULL;
287 }
halcanary1b5c6042015-02-18 11:29:56 -0800288 if (bm.drawsNothing()) {
289 return NULL;
290 }
reed1b600d32015-03-20 10:03:36 -0700291 if (!bm.isImmutable()) {
292 SkBitmap copy;
293 if (!bm.copyTo(&copy)) {
294 return NULL;
295 }
296 copy.setImmutable();
297 bm = copy;
298 }
299
300 SkPDFBitmap* pdfBitmap = canon->findBitmap(bm);
301 if (pdfBitmap) {
302 return SkRef(pdfBitmap);
halcanary1b5c6042015-02-18 11:29:56 -0800303 }
304 SkPDFObject* smask = NULL;
305 if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) {
reed1b600d32015-03-20 10:03:36 -0700306 if (is_transparent(bm)) {
307 return NULL;
308 }
309 // PDFAlphaBitmaps do not get directly canonicalized (they
310 // are refed by the SkPDFBitmap).
halcanary1b5c6042015-02-18 11:29:56 -0800311 smask = SkNEW_ARGS(PDFAlphaBitmap, (bm));
312 }
reed1b600d32015-03-20 10:03:36 -0700313 pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (bm, smask));
halcanary792c80f2015-02-20 07:21:05 -0800314 canon->addBitmap(pdfBitmap);
halcanary1b5c6042015-02-18 11:29:56 -0800315 return pdfBitmap;
316}