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

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: 2015-02-12 (Thursday) 16:27:28 EST 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
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 return SkToBool(flag & SkPDFDocument::kFavorSpeedOverSize_Flags);
35 }
36
37 static bool is_better_compressed(const SkStreamAsset* asset,
38 const SkBitmap& bitmap) {
39 return asset->getLength() + strlen("/Filter /FlateDecode\n")
40 <= pixel_count(bitmap);
41 }
42
43
44
45 static void pmcolor_to_rgb24(const SkBitmap& bm, SkWStream* out) {
46 SkASSERT(kN32_SkColorType == bm.colorType());
47 size_t scanlineLength = 3 * bm.width();
48 SkAutoTMalloc<uint8_t> scanline(scanlineLength);
49 for (int y = 0; y < bm.height(); ++y) {
50 uint8_t* dst = scanline.get();
51 const SkPMColor* src = bm.getAddr32(0, y);
52 for (int x = 0; x < bm.width(); ++x) {
53 SkPMColor pmColor = *src++;
54 uint32_t s = SkUnPreMultiply::GetScale(SkGetPackedA32(pmColor));
55 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(pmColor));
56 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(pmColor));
57 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(pmColor));
58 }
59 out->write(scanline.get(), scanlineLength);
60 }
61 }
62
63 static void pmcolor_alpha_to_a8(const SkBitmap& bm, SkWStream* out) {
64 SkASSERT(kN32_SkColorType == bm.colorType());
65 size_t scanlineLength = bm.width();
66 SkAutoTMalloc<uint8_t> scanline(scanlineLength);
67 for (int y = 0; y < bm.height(); ++y) {
68 uint8_t* dst = scanline.get();
69 const SkPMColor* src = bm.getAddr32(0, y);
70 for (int x = 0; x < bm.width(); ++x) {
71 *dst++ = SkGetPackedA32(*src++);
72 }
73 out->write(scanline.get(), scanlineLength);
74 }
75 }
76
77 ////////////////////////////////////////////////////////////////////////////////
78
79 namespace {
80 // This SkPDFObject only outputs the alpha layer of the given bitmap.
81 class PDFAlphaBitmap : public SkPDFObject {
82 public:
83 PDFAlphaBitmap(const SkBitmap& bm) : fBitmap(bm) {}
84 ~PDFAlphaBitmap() {}
85 void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE;
86 void addResources(SkTSet<SkPDFObject*>*, SkPDFCatalog*) const SK_OVERRIDE {}
87
88 private:
89 const SkBitmap fBitmap;
90 void emitFast(SkWStream*, SkPDFCatalog*) const;
91 void emitDict(SkWStream*, SkPDFCatalog*, size_t, bool) const;
92 };
93
94 void PDFAlphaBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
95 SkASSERT(kN32_SkColorType == fBitmap.colorType());
96 SkAutoLockPixels autoLockPixels(fBitmap);
97 SkASSERT(fBitmap.getPixels());
98
99 #ifdef SK_HAS_ZLIB
100 if (skip_compression(catalog->getDocumentFlags())) {
101 this->emitFast(stream, catalog);
102 return;
103 }
104
105 // Write to a temporary buffer to get the compressed length.
106 SkDynamicMemoryWStream buffer;
107 SkDeflateWStream deflateWStream(&buffer);
108 pmcolor_alpha_to_a8(fBitmap, &deflateWStream);
109 deflateWStream.finalize(); // call before detachAsStream().
110 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
111
112 if (is_better_compressed(asset, fBitmap)) {
113 this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true);
114 pdf_stream_begin(stream);
115 stream->writeStream(asset.get(), asset->getLength());
116 pdf_stream_end(stream);
117 } else {
118 this->emitFast(stream, catalog);
119 }
120 #else
121 this->emitFast(stream, catalog);
122 #endif
123 }
124 void PDFAlphaBitmap::emitFast(SkWStream* stream,
125 SkPDFCatalog* catalog) const {
126 this->emitDict(stream, catalog, pixel_count(fBitmap), /*deflate=*/false);
127 pdf_stream_begin(stream);
128 pmcolor_alpha_to_a8(fBitmap, stream);
129 pdf_stream_end(stream);
130 }
131
132 void PDFAlphaBitmap::emitDict(SkWStream* stream,
133 SkPDFCatalog* catalog,
134 size_t length,
135 bool deflate) const {
136 SkPDFDict pdfDict("Xobject");
137 pdfDict.insertName("Subtype", "Image");
138 pdfDict.insertInt("Width", fBitmap.width());
139 pdfDict.insertInt("Height", fBitmap.height());
140 pdfDict.insertName("ColorSpace", "DeviceGray");
141 pdfDict.insertInt("BitsPerComponent", 8);
142 pdfDict.insertInt("Length", length);
143 if (deflate) {
144 pdfDict.insertName("Filter", "FlateDecode");
145 }
146 pdfDict.emitObject(stream, catalog);
147 }
148 } // namespace
149
150 ////////////////////////////////////////////////////////////////////////////////
151
152 void SkPDFBitmap::addResources(SkTSet<SkPDFObject*>* resourceSet,
153 SkPDFCatalog* catalog) const {
154 if (fSMask.get()) {
155 resourceSet->add(fSMask.get());
156 }
157 }
158
159 void SkPDFBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
160 SkASSERT(kN32_SkColorType == fBitmap.colorType());
161 SkAutoLockPixels autoLockPixels(fBitmap);
162 SkASSERT(fBitmap.getPixels());
163
164 #ifdef SK_HAS_ZLIB
165 if (skip_compression(catalog->getDocumentFlags())) {
166 this->emitFast(stream, catalog);
167 return;
168 }
169 // Write to a temporary buffer to get the compressed length.
170 SkDynamicMemoryWStream buffer;
171 SkDeflateWStream deflateWStream(&buffer);
172 pmcolor_to_rgb24(fBitmap, &deflateWStream);
173 deflateWStream.finalize(); // call before detachAsStream().
174 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
175
176 if (is_better_compressed(asset, fBitmap)) {
177 this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true);
178 pdf_stream_begin(stream);
179 stream->writeStream(asset.get(), asset->getLength());
180 pdf_stream_end(stream);
181 } else {
182 // probably a very small bitmap: output_pmcolor() is cheap.
183 this->emitFast(stream, catalog);
184 }
185 #else
186 this->emitFast(stream, catalog);
187 #endif
188 }
189
190 void SkPDFBitmap::emitFast(SkWStream* stream, SkPDFCatalog* catalog) const {
191 this->emitDict(stream, catalog, 3 * pixel_count(fBitmap),
192 /*deflate=*/false);
193 pdf_stream_begin(stream);
194 pmcolor_to_rgb24(fBitmap, stream);
195 pdf_stream_end(stream);
196 }
197
198 void SkPDFBitmap::emitDict(SkWStream* stream,
199 SkPDFCatalog* catalog,
200 size_t length,
201 bool deflate) const {
202 SkPDFDict pdfDict("Xobject");
203 pdfDict.insertName("Subtype", "Image");
204 pdfDict.insertInt("Width", fBitmap.width());
205 pdfDict.insertInt("Height", fBitmap.height());
206 pdfDict.insertName("ColorSpace", "DeviceRGB");
207 pdfDict.insertInt("BitsPerComponent", 8);
208 if (fSMask) {
209 pdfDict.insert("SMask", new SkPDFObjRef(fSMask))->unref();
210 }
211 pdfDict.insertInt("Length", length);
212 if (deflate) {
213 pdfDict.insertName("Filter", "FlateDecode");
214 }
215 pdfDict.emitObject(stream, catalog);
216 }
217
218 SkPDFBitmap::SkPDFBitmap(const SkBitmap& bm, SkPDFObject* smask)
219 : fBitmap(bm), fSMask(smask) {}
220
221 SkPDFBitmap::~SkPDFBitmap() {
222 SkAutoMutexAcquire autoMutexAcquire(SkPDFCanon::GetBitmapMutex());
223 SkPDFCanon::GetCanon().removeBitmap(this);
224 }
225
226 ////////////////////////////////////////////////////////////////////////////////
227
228 // TODO(halcanary): SkPDFBitmap::Create should take a SkPDFCanon* parameter.
229 SkPDFBitmap* SkPDFBitmap::Create(const SkBitmap& bitmap,
230 const SkIRect& subset) {
231 if (kN32_SkColorType != bitmap.colorType()) {
232 // TODO(halcanary): support other colortypes.
233 return NULL;
234 }
235 SkBitmap bm;
236 // Should extractSubset be done by the SkPDFDevice?
237 if (!bitmap.extractSubset(&bm, subset)) {
238 return NULL;
239 }
240 if (bm.drawsNothing()) {
241 return NULL;
242 }
243 SkAutoMutexAcquire autoMutexAcquire(SkPDFCanon::GetBitmapMutex());
244 SkPDFCanon& canon = SkPDFCanon::GetCanon();
245 SkPDFBitmap* pdfBitmap = canon.findBitmap(bm);
246 if (pdfBitmap) {
247 return SkRef(pdfBitmap);
248 }
249 SkPDFObject* smask = NULL;
250 if (!bitmap.isOpaque() && !SkBitmap::ComputeIsOpaque(bitmap)) {
251 // PDFAlphaBitmap do not get directly canonicalized (they
252 // are refed by the SkPDFBitmap).
253 smask = SkNEW_ARGS(PDFAlphaBitmap, (bm));
254 }
255 pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (bm, smask));
256 canon.addBitmap(pdfBitmap);
257 return pdfBitmap;
258 }
OLDNEW
« src/pdf/SkPDFBitmap.h ('K') | « src/pdf/SkPDFBitmap.h ('k') | src/pdf/SkPDFCanon.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698