blob: b65709cf7c31f8629d187dd70ba56ed7fa08b75c [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"
9#include "SkDeflateWStream.h"
10#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
250SkPDFBitmap::SkPDFBitmap(const SkBitmap& bm, SkPDFObject* smask)
251 : fBitmap(bm), fSMask(smask) {}
252
253SkPDFBitmap::~SkPDFBitmap() {
254 SkAutoMutexAcquire autoMutexAcquire(SkPDFCanon::GetBitmapMutex());
255 SkPDFCanon::GetCanon().removeBitmap(this);
256}
257
258////////////////////////////////////////////////////////////////////////////////
259static bool is_transparent(const SkBitmap& bm) {
260 SkAutoLockPixels autoLockPixels(bm);
261 if (NULL == bm.getPixels()) {
262 return true;
263 }
264 SkASSERT(kN32_SkColorType == bm.colorType());
265 for (int y = 0; y < bm.height(); ++y) {
266 U8CPU alpha = 0;
267 const SkPMColor* src = bm.getAddr32(0, y);
268 for (int x = 0; x < bm.width(); ++x) {
269 alpha |= SkGetPackedA32(*src++);
270 }
271 if (alpha) {
272 return false;
273 }
274 }
275 return true;
276}
277
278// TODO(halcanary): SkPDFBitmap::Create should take a SkPDFCanon* parameter.
279SkPDFBitmap* SkPDFBitmap::Create(const SkBitmap& bitmap,
280 const SkIRect& subset) {
281 if (kN32_SkColorType != bitmap.colorType()) {
282 // TODO(halcanary): support other colortypes.
283 return NULL;
284 }
285 SkBitmap bm;
286 // Should extractSubset be done by the SkPDFDevice?
287 if (!bitmap.extractSubset(&bm, subset)) {
288 return NULL;
289 }
290 if (bm.drawsNothing()) {
291 return NULL;
292 }
293 if (!bm.isImmutable()) {
294 SkBitmap copy;
295 if (!bm.copyTo(&copy)) {
296 return NULL;
297 }
298 copy.setImmutable();
299 bm = copy;
300 }
301
302 SkAutoMutexAcquire autoMutexAcquire(SkPDFCanon::GetBitmapMutex());
303 SkPDFCanon& canon = SkPDFCanon::GetCanon();
304 SkPDFBitmap* pdfBitmap = canon.findBitmap(bm);
305 if (pdfBitmap) {
306 return SkRef(pdfBitmap);
307 }
308 SkPDFObject* smask = NULL;
309 if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) {
310 if (is_transparent(bm)) {
311 return NULL;
312 }
313 // PDFAlphaBitmaps do not get directly canonicalized (they
314 // are refed by the SkPDFBitmap).
315 smask = SkNEW_ARGS(PDFAlphaBitmap, (bm));
316 }
317 pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (bm, smask));
318 canon.addBitmap(pdfBitmap);
319 return pdfBitmap;
320}