Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Side by Side Diff: src/pdf/SkPDFBitmap.cpp

Issue 918813002: PDF: Add (low-memory) SkPDFBitmap class (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: mutable bitmaps get copied Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/pdf/SkPDFBitmap.h ('k') | src/pdf/SkPDFCanon.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
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
19 static void pdf_stream_begin(SkWStream* stream) {
20 static const char streamBegin[] = " stream\n";
21 stream->write(streamBegin, strlen(streamBegin));
22 }
23
24 static void pdf_stream_end(SkWStream* stream) {
25 static const char streamEnd[] = "\nendstream";
26 stream->write(streamEnd, strlen(streamEnd));
27 }
28
29 static size_t pixel_count(const SkBitmap& bm) {
30 return SkToSizeT(bm.width()) * SkToSizeT(bm.height());
31 }
32
33 static bool skip_compression(SkPDFDocument::Flags flag) {
34 #ifndef SK_NO_FLATE
35 return SkToBool(flag & SkPDFDocument::kFavorSpeedOverSize_Flags);
36 #else
37 return true;
38 #endif // SK_NO_FLATE
39 }
40
41 // write a single byte to a stream n times.
42 static void fill_stream(SkWStream* out, char value, size_t n) {
43 char buffer[4096];
44 memset(buffer, value, sizeof(buffer));
45 while (n) {
46 size_t k = SkTMin(n, sizeof(buffer));
47 out->write(buffer, k);
48 n -= k;
49 }
50 }
51
52 static SkPMColor get_pmcolor_neighbor_avg_color(const SkBitmap& bitmap,
53 int xOrig,
54 int yOrig) {
55 SkASSERT(kN32_SkColorType == bitmap.colorType());
56 SkASSERT(bitmap.getPixels());
57 uint8_t count = 0;
58 unsigned r = 0;
59 unsigned g = 0;
60 unsigned b = 0;
61 for (int y = yOrig - 1; y <= yOrig + 1; ++y) {
62 if (y < 0 || y >= bitmap.height()) {
63 continue;
64 }
65 uint32_t* src = bitmap.getAddr32(0, y);
66 for (int x = xOrig - 1; x <= xOrig + 1; ++x) {
67 if (x < 0 || x >= bitmap.width()) {
68 continue;
69 }
70 SkPMColor pmColor = src[x];
71 U8CPU alpha = SkGetPackedA32(pmColor);
72 if (alpha != SK_AlphaTRANSPARENT) {
73 uint32_t s = SkUnPreMultiply::GetScale(alpha);
74 r += SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(pmColor));
75 g += SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(pmColor));
76 b += SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(pmColor));
77 ++count;
78 }
79 }
80 }
81 if (count == 0) {
82 return SkPackARGB32NoCheck(SK_AlphaOPAQUE, 0, 0, 0);
83 } else {
84 return SkPackARGB32NoCheck(
85 SK_AlphaOPAQUE, r / count, g / count, b / count);
86 }
87 }
88
89 static void pmcolor_to_rgb24(const SkBitmap& bm, SkWStream* out) {
90 SkASSERT(kN32_SkColorType == bm.colorType());
91 if (!bm.getPixels()) {
92 fill_stream(out, '\xFF', 3 * pixel_count(bm));
93 return;
94 }
95 size_t scanlineLength = 3 * bm.width();
96 SkAutoTMalloc<uint8_t> scanline(scanlineLength);
97 for (int y = 0; y < bm.height(); ++y) {
98 uint8_t* dst = scanline.get();
99 const SkPMColor* src = bm.getAddr32(0, y);
100 for (int x = 0; x < bm.width(); ++x) {
101 SkPMColor color = *src++;
102 U8CPU alpha = SkGetPackedA32(color);
103 if (alpha != SK_AlphaTRANSPARENT) {
104 uint32_t s = SkUnPreMultiply::GetScale(alpha);
105 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(color));
106 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(color));
107 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(color));
108 } else {
109 /* It is necessary to average the color component of
110 transparent pixels with their surrounding neighbors
111 since the PDF renderer may separately re-sample the
112 alpha and color channels when the image is not
113 displayed at its native resolution. Since an alpha
114 of zero gives no information about the color
115 component, the pathological case is a white image
116 with sharp transparency bounds - the color channel
117 goes to black, and the should-be-transparent pixels
118 are rendered as grey because of the separate soft
119 mask and color resizing. e.g.: gm/bitmappremul.cpp */
120 color = get_pmcolor_neighbor_avg_color(bm, x, y);
121 *dst++ = SkGetPackedR32(color);
122 *dst++ = SkGetPackedG32(color);
123 *dst++ = SkGetPackedB32(color);
124 }
125 }
126 out->write(scanline.get(), scanlineLength);
127 }
128 }
129
130 static void pmcolor_alpha_to_a8(const SkBitmap& bm, SkWStream* out) {
131 SkASSERT(kN32_SkColorType == bm.colorType());
132 if (!bm.getPixels()) {
133 fill_stream(out, '\xFF', pixel_count(bm));
134 return;
135 }
136 size_t scanlineLength = bm.width();
137 SkAutoTMalloc<uint8_t> scanline(scanlineLength);
138 for (int y = 0; y < bm.height(); ++y) {
139 uint8_t* dst = scanline.get();
140 const SkPMColor* src = bm.getAddr32(0, y);
141 for (int x = 0; x < bm.width(); ++x) {
142 *dst++ = SkGetPackedA32(*src++);
143 }
144 out->write(scanline.get(), scanlineLength);
145 }
146 }
147
148 ////////////////////////////////////////////////////////////////////////////////
149
150 namespace {
151 // This SkPDFObject only outputs the alpha layer of the given bitmap.
152 class PDFAlphaBitmap : public SkPDFObject {
153 public:
154 PDFAlphaBitmap(const SkBitmap& bm) : fBitmap(bm) {}
155 ~PDFAlphaBitmap() {}
156 void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE;
157 void addResources(SkTSet<SkPDFObject*>*, SkPDFCatalog*) const SK_OVERRIDE {}
158
159 private:
160 const SkBitmap fBitmap;
161 void emitDict(SkWStream*, SkPDFCatalog*, size_t, bool) const;
162 };
163
164 void PDFAlphaBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
165 SkAutoLockPixels autoLockPixels(fBitmap);
166
167 if (skip_compression(catalog->getDocumentFlags())) {
168 this->emitDict(stream, catalog, pixel_count(fBitmap),
169 /*deflate=*/false);
170 pdf_stream_begin(stream);
171 pmcolor_alpha_to_a8(fBitmap, stream);
172 pdf_stream_end(stream);
173 return;
174 }
175 #ifndef SK_NO_FLATE
176 // Write to a temporary buffer to get the compressed length.
177 SkDynamicMemoryWStream buffer;
178 SkDeflateWStream deflateWStream(&buffer);
179 pmcolor_alpha_to_a8(fBitmap, &deflateWStream);
180 deflateWStream.finalize(); // call before detachAsStream().
181 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
182
183 this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true);
184 pdf_stream_begin(stream);
185 stream->writeStream(asset.get(), asset->getLength());
186 pdf_stream_end(stream);
187 #endif // SK_NO_FLATE
188 }
189
190 void PDFAlphaBitmap::emitDict(SkWStream* stream,
191 SkPDFCatalog* catalog,
192 size_t length,
193 bool deflate) const {
194 SkPDFDict pdfDict("XObject");
195 pdfDict.insertName("Subtype", "Image");
196 pdfDict.insertInt("Width", fBitmap.width());
197 pdfDict.insertInt("Height", fBitmap.height());
198 pdfDict.insertName("ColorSpace", "DeviceGray");
199 pdfDict.insertInt("BitsPerComponent", 8);
200 if (deflate) {
201 pdfDict.insertName("Filter", "FlateDecode");
202 }
203 pdfDict.insertInt("Length", length);
204 pdfDict.emitObject(stream, catalog);
205 }
206 } // namespace
207
208 ////////////////////////////////////////////////////////////////////////////////
209
210 void SkPDFBitmap::addResources(SkTSet<SkPDFObject*>* resourceSet,
211 SkPDFCatalog* catalog) const {
212 if (fSMask.get()) {
213 resourceSet->add(fSMask.get());
214 }
215 }
216
217 void SkPDFBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
218 SkAutoLockPixels autoLockPixels(fBitmap);
219
220 if (skip_compression(catalog->getDocumentFlags())) {
221 this->emitDict(stream, catalog, 3 * pixel_count(fBitmap),
222 /*deflate=*/false);
223 pdf_stream_begin(stream);
224 pmcolor_to_rgb24(fBitmap, stream);
225 pdf_stream_end(stream);
226 return;
227 }
228 #ifndef SK_NO_FLATE
229 // Write to a temporary buffer to get the compressed length.
230 SkDynamicMemoryWStream buffer;
231 SkDeflateWStream deflateWStream(&buffer);
232 pmcolor_to_rgb24(fBitmap, &deflateWStream);
233 deflateWStream.finalize(); // call before detachAsStream().
234 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
235
236 this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true);
237 pdf_stream_begin(stream);
238 stream->writeStream(asset.get(), asset->getLength());
239 pdf_stream_end(stream);
240 #endif // SK_NO_FLATE
241 }
242
243 void SkPDFBitmap::emitDict(SkWStream* stream,
244 SkPDFCatalog* catalog,
245 size_t length,
246 bool deflate) const {
247 SkPDFDict pdfDict("XObject");
248 pdfDict.insertName("Subtype", "Image");
249 pdfDict.insertInt("Width", fBitmap.width());
250 pdfDict.insertInt("Height", fBitmap.height());
251 pdfDict.insertName("ColorSpace", "DeviceRGB");
252 pdfDict.insertInt("BitsPerComponent", 8);
253 if (fSMask) {
254 pdfDict.insert("SMask", new SkPDFObjRef(fSMask))->unref();
255 }
256 if (deflate) {
257 pdfDict.insertName("Filter", "FlateDecode");
258 }
259 pdfDict.insertInt("Length", length);
260 pdfDict.emitObject(stream, catalog);
261 }
262
263 SkPDFBitmap::SkPDFBitmap(const SkBitmap& bm, SkPDFObject* smask)
264 : fBitmap(bm), fSMask(smask) {}
265
266 SkPDFBitmap::~SkPDFBitmap() {
267 SkAutoMutexAcquire autoMutexAcquire(SkPDFCanon::GetBitmapMutex());
268 SkPDFCanon::GetCanon().removeBitmap(this);
269 }
270
271 ////////////////////////////////////////////////////////////////////////////////
272 static bool is_transparent(const SkBitmap& bm) {
273 SkAutoLockPixels autoLockPixels(bm);
274 if (NULL == bm.getPixels()) {
275 return true;
276 }
277 SkASSERT(kN32_SkColorType == bm.colorType());
278 for (int y = 0; y < bm.height(); ++y) {
279 U8CPU alpha = 0;
280 const SkPMColor* src = bm.getAddr32(0, y);
281 for (int x = 0; x < bm.width(); ++x) {
282 alpha |= SkGetPackedA32(*src++);
283 }
284 if (alpha) {
285 return false;
286 }
287 }
288 return true;
289 }
290
291 // TODO(halcanary): SkPDFBitmap::Create should take a SkPDFCanon* parameter.
292 SkPDFBitmap* SkPDFBitmap::Create(const SkBitmap& bitmap,
293 const SkIRect& subset) {
294 if (kN32_SkColorType != bitmap.colorType()) {
295 // TODO(halcanary): support other colortypes.
296 return NULL;
297 }
298 SkBitmap bm;
299 // Should extractSubset be done by the SkPDFDevice?
300 if (!bitmap.extractSubset(&bm, subset)) {
301 return NULL;
302 }
303 if (bm.drawsNothing()) {
304 return NULL;
305 }
306 if (!bm.isImmutable()) {
307 SkBitmap copy;
308 if (!bm.copyTo(&copy)) {
309 return NULL;
310 }
311 copy.setImmutable();
312 bm = copy;
313 }
314
315 SkAutoMutexAcquire autoMutexAcquire(SkPDFCanon::GetBitmapMutex());
316 SkPDFCanon& canon = SkPDFCanon::GetCanon();
317 SkPDFBitmap* pdfBitmap = canon.findBitmap(bm);
318 if (pdfBitmap) {
319 return SkRef(pdfBitmap);
320 }
321 SkPDFObject* smask = NULL;
322 if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) {
323 if (is_transparent(bm)) {
324 return NULL;
325 }
326 // PDFAlphaBitmaps do not get directly canonicalized (they
327 // are refed by the SkPDFBitmap).
328 smask = SkNEW_ARGS(PDFAlphaBitmap, (bm));
329 }
330 pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (bm, smask));
331 canon.addBitmap(pdfBitmap);
332 return pdfBitmap;
333 }
OLDNEW
« no previous file with comments | « src/pdf/SkPDFBitmap.h ('k') | src/pdf/SkPDFCanon.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698