blob: 5774360a7cce07cf8ab2e118ed0c421bafdebf10 [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"
13#include "SkPDFDocument.h"
14#include "SkStream.h"
15#include "SkUnPreMultiply.h"
16
17////////////////////////////////////////////////////////////////////////////////
18
19static void pdf_stream_begin(SkWStream* stream) {
20 static const char streamBegin[] = " stream\n";
21 stream->write(streamBegin, strlen(streamBegin));
22}
23
24static void pdf_stream_end(SkWStream* stream) {
25 static const char streamEnd[] = "\nendstream";
26 stream->write(streamEnd, strlen(streamEnd));
27}
28
29static size_t pixel_count(const SkBitmap& bm) {
30 return SkToSizeT(bm.width()) * SkToSizeT(bm.height());
31}
32
halcanary1b5c6042015-02-18 11:29:56 -080033// write a single byte to a stream n times.
34static void fill_stream(SkWStream* out, char value, size_t n) {
35 char buffer[4096];
36 memset(buffer, value, sizeof(buffer));
37 while (n) {
38 size_t k = SkTMin(n, sizeof(buffer));
39 out->write(buffer, k);
40 n -= k;
41 }
42}
43
44static SkPMColor get_pmcolor_neighbor_avg_color(const SkBitmap& bitmap,
45 int xOrig,
46 int yOrig) {
47 SkASSERT(kN32_SkColorType == bitmap.colorType());
48 SkASSERT(bitmap.getPixels());
49 uint8_t count = 0;
50 unsigned r = 0;
51 unsigned g = 0;
52 unsigned b = 0;
53 for (int y = yOrig - 1; y <= yOrig + 1; ++y) {
54 if (y < 0 || y >= bitmap.height()) {
55 continue;
56 }
57 uint32_t* src = bitmap.getAddr32(0, y);
58 for (int x = xOrig - 1; x <= xOrig + 1; ++x) {
59 if (x < 0 || x >= bitmap.width()) {
60 continue;
61 }
62 SkPMColor pmColor = src[x];
63 U8CPU alpha = SkGetPackedA32(pmColor);
64 if (alpha != SK_AlphaTRANSPARENT) {
65 uint32_t s = SkUnPreMultiply::GetScale(alpha);
66 r += SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(pmColor));
67 g += SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(pmColor));
68 b += SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(pmColor));
69 ++count;
70 }
71 }
72 }
73 if (count == 0) {
74 return SkPackARGB32NoCheck(SK_AlphaOPAQUE, 0, 0, 0);
75 } else {
76 return SkPackARGB32NoCheck(
77 SK_AlphaOPAQUE, r / count, g / count, b / count);
78 }
79}
80
81static void pmcolor_to_rgb24(const SkBitmap& bm, SkWStream* out) {
82 SkASSERT(kN32_SkColorType == bm.colorType());
83 if (!bm.getPixels()) {
84 fill_stream(out, '\xFF', 3 * pixel_count(bm));
85 return;
86 }
87 size_t scanlineLength = 3 * bm.width();
88 SkAutoTMalloc<uint8_t> scanline(scanlineLength);
89 for (int y = 0; y < bm.height(); ++y) {
90 uint8_t* dst = scanline.get();
91 const SkPMColor* src = bm.getAddr32(0, y);
92 for (int x = 0; x < bm.width(); ++x) {
93 SkPMColor color = *src++;
94 U8CPU alpha = SkGetPackedA32(color);
95 if (alpha != SK_AlphaTRANSPARENT) {
96 uint32_t s = SkUnPreMultiply::GetScale(alpha);
97 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(color));
98 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(color));
99 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(color));
100 } else {
101 /* It is necessary to average the color component of
102 transparent pixels with their surrounding neighbors
103 since the PDF renderer may separately re-sample the
104 alpha and color channels when the image is not
105 displayed at its native resolution. Since an alpha
106 of zero gives no information about the color
107 component, the pathological case is a white image
108 with sharp transparency bounds - the color channel
109 goes to black, and the should-be-transparent pixels
110 are rendered as grey because of the separate soft
111 mask and color resizing. e.g.: gm/bitmappremul.cpp */
112 color = get_pmcolor_neighbor_avg_color(bm, x, y);
113 *dst++ = SkGetPackedR32(color);
114 *dst++ = SkGetPackedG32(color);
115 *dst++ = SkGetPackedB32(color);
116 }
117 }
118 out->write(scanline.get(), scanlineLength);
119 }
120}
121
122static void pmcolor_alpha_to_a8(const SkBitmap& bm, SkWStream* out) {
123 SkASSERT(kN32_SkColorType == bm.colorType());
124 if (!bm.getPixels()) {
125 fill_stream(out, '\xFF', pixel_count(bm));
126 return;
127 }
128 size_t scanlineLength = bm.width();
129 SkAutoTMalloc<uint8_t> scanline(scanlineLength);
130 for (int y = 0; y < bm.height(); ++y) {
131 uint8_t* dst = scanline.get();
132 const SkPMColor* src = bm.getAddr32(0, y);
133 for (int x = 0; x < bm.width(); ++x) {
134 *dst++ = SkGetPackedA32(*src++);
135 }
136 out->write(scanline.get(), scanlineLength);
137 }
138}
139
140////////////////////////////////////////////////////////////////////////////////
141
142namespace {
143// This SkPDFObject only outputs the alpha layer of the given bitmap.
144class PDFAlphaBitmap : public SkPDFObject {
145public:
146 PDFAlphaBitmap(const SkBitmap& bm) : fBitmap(bm) {}
147 ~PDFAlphaBitmap() {}
148 void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE;
149 void addResources(SkTSet<SkPDFObject*>*, SkPDFCatalog*) const SK_OVERRIDE {}
150
151private:
152 const SkBitmap fBitmap;
153 void emitDict(SkWStream*, SkPDFCatalog*, size_t, bool) const;
154};
155
156void PDFAlphaBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
157 SkAutoLockPixels autoLockPixels(fBitmap);
158
halcanary1b5c6042015-02-18 11:29:56 -0800159#ifndef SK_NO_FLATE
160 // Write to a temporary buffer to get the compressed length.
161 SkDynamicMemoryWStream buffer;
162 SkDeflateWStream deflateWStream(&buffer);
163 pmcolor_alpha_to_a8(fBitmap, &deflateWStream);
164 deflateWStream.finalize(); // call before detachAsStream().
165 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
166
167 this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true);
168 pdf_stream_begin(stream);
169 stream->writeStream(asset.get(), asset->getLength());
170 pdf_stream_end(stream);
mtkleincabc08c2015-02-19 08:29:24 -0800171#else
172 this->emitDict(stream, catalog, pixel_count(fBitmap), /*deflate=*/false);
173 pdf_stream_begin(stream);
174 pmcolor_alpha_to_a8(fBitmap, stream);
175 pdf_stream_end(stream);
halcanary1b5c6042015-02-18 11:29:56 -0800176#endif // SK_NO_FLATE
177}
178
179void PDFAlphaBitmap::emitDict(SkWStream* stream,
180 SkPDFCatalog* catalog,
181 size_t length,
182 bool deflate) const {
183 SkPDFDict pdfDict("XObject");
184 pdfDict.insertName("Subtype", "Image");
185 pdfDict.insertInt("Width", fBitmap.width());
186 pdfDict.insertInt("Height", fBitmap.height());
187 pdfDict.insertName("ColorSpace", "DeviceGray");
188 pdfDict.insertInt("BitsPerComponent", 8);
189 if (deflate) {
190 pdfDict.insertName("Filter", "FlateDecode");
191 }
192 pdfDict.insertInt("Length", length);
193 pdfDict.emitObject(stream, catalog);
194}
195} // namespace
196
197////////////////////////////////////////////////////////////////////////////////
198
199void SkPDFBitmap::addResources(SkTSet<SkPDFObject*>* resourceSet,
200 SkPDFCatalog* catalog) const {
201 if (fSMask.get()) {
202 resourceSet->add(fSMask.get());
203 }
204}
205
206void SkPDFBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
207 SkAutoLockPixels autoLockPixels(fBitmap);
208
halcanary1b5c6042015-02-18 11:29:56 -0800209#ifndef SK_NO_FLATE
210 // Write to a temporary buffer to get the compressed length.
211 SkDynamicMemoryWStream buffer;
212 SkDeflateWStream deflateWStream(&buffer);
213 pmcolor_to_rgb24(fBitmap, &deflateWStream);
214 deflateWStream.finalize(); // call before detachAsStream().
215 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
216
217 this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true);
218 pdf_stream_begin(stream);
219 stream->writeStream(asset.get(), asset->getLength());
220 pdf_stream_end(stream);
mtkleincabc08c2015-02-19 08:29:24 -0800221#else
222 this->emitDict(stream, catalog, 3 * pixel_count(fBitmap), /*deflate=*/false);
223 pdf_stream_begin(stream);
224 pmcolor_to_rgb24(fBitmap, stream);
225 pdf_stream_end(stream);
226 return;
halcanary1b5c6042015-02-18 11:29:56 -0800227#endif // SK_NO_FLATE
228}
229
230void SkPDFBitmap::emitDict(SkWStream* stream,
231 SkPDFCatalog* catalog,
232 size_t length,
233 bool deflate) const {
234 SkPDFDict pdfDict("XObject");
235 pdfDict.insertName("Subtype", "Image");
236 pdfDict.insertInt("Width", fBitmap.width());
237 pdfDict.insertInt("Height", fBitmap.height());
238 pdfDict.insertName("ColorSpace", "DeviceRGB");
239 pdfDict.insertInt("BitsPerComponent", 8);
240 if (fSMask) {
241 pdfDict.insert("SMask", new SkPDFObjRef(fSMask))->unref();
242 }
243 if (deflate) {
244 pdfDict.insertName("Filter", "FlateDecode");
245 }
246 pdfDict.insertInt("Length", length);
247 pdfDict.emitObject(stream, catalog);
248}
249
halcanary2e3f9d82015-02-27 12:41:03 -0800250SkPDFBitmap::SkPDFBitmap(const SkBitmap& bm,
halcanary792c80f2015-02-20 07:21:05 -0800251 SkPDFObject* smask)
halcanary2e3f9d82015-02-27 12:41:03 -0800252 : fBitmap(bm), fSMask(smask) {}
halcanary1b5c6042015-02-18 11:29:56 -0800253
halcanary2e3f9d82015-02-27 12:41:03 -0800254SkPDFBitmap::~SkPDFBitmap() {}
halcanary1b5c6042015-02-18 11:29:56 -0800255
256////////////////////////////////////////////////////////////////////////////////
257static bool is_transparent(const SkBitmap& bm) {
258 SkAutoLockPixels autoLockPixels(bm);
259 if (NULL == bm.getPixels()) {
260 return true;
261 }
262 SkASSERT(kN32_SkColorType == bm.colorType());
263 for (int y = 0; y < bm.height(); ++y) {
264 U8CPU alpha = 0;
265 const SkPMColor* src = bm.getAddr32(0, y);
266 for (int x = 0; x < bm.width(); ++x) {
267 alpha |= SkGetPackedA32(*src++);
268 }
269 if (alpha) {
270 return false;
271 }
272 }
273 return true;
274}
275
halcanary792c80f2015-02-20 07:21:05 -0800276SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon,
277 const SkBitmap& bitmap,
halcanary1b5c6042015-02-18 11:29:56 -0800278 const SkIRect& subset) {
halcanary792c80f2015-02-20 07:21:05 -0800279 SkASSERT(canon);
halcanary1b5c6042015-02-18 11:29:56 -0800280 if (kN32_SkColorType != bitmap.colorType()) {
281 // TODO(halcanary): support other colortypes.
282 return NULL;
283 }
284 SkBitmap bm;
285 // Should extractSubset be done by the SkPDFDevice?
286 if (!bitmap.extractSubset(&bm, subset)) {
287 return NULL;
288 }
289 if (bm.drawsNothing()) {
290 return NULL;
291 }
292 if (!bm.isImmutable()) {
293 SkBitmap copy;
294 if (!bm.copyTo(&copy)) {
295 return NULL;
296 }
297 copy.setImmutable();
298 bm = copy;
299 }
300
halcanary792c80f2015-02-20 07:21:05 -0800301 SkPDFBitmap* pdfBitmap = canon->findBitmap(bm);
halcanary1b5c6042015-02-18 11:29:56 -0800302 if (pdfBitmap) {
303 return SkRef(pdfBitmap);
304 }
305 SkPDFObject* smask = NULL;
306 if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) {
307 if (is_transparent(bm)) {
308 return NULL;
309 }
310 // PDFAlphaBitmaps do not get directly canonicalized (they
311 // are refed by the SkPDFBitmap).
312 smask = SkNEW_ARGS(PDFAlphaBitmap, (bm));
313 }
halcanary2e3f9d82015-02-27 12:41:03 -0800314 pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (bm, smask));
halcanary792c80f2015-02-20 07:21:05 -0800315 canon->addBitmap(pdfBitmap);
halcanary1b5c6042015-02-18 11:29:56 -0800316 return pdfBitmap;
317}