OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright 2015 Google Inc. | 2 * Copyright 2015 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkColorPriv.h" | 8 #include "SkColorPriv.h" |
9 #include "SkDeflateWStream.h" | 9 #include "SkDeflateWStream.h" |
10 #include "SkPDFBitmap.h" | 10 #include "SkPDFBitmap.h" |
11 #include "SkPDFCanon.h" | 11 #include "SkPDFCanon.h" |
12 #include "SkPDFCatalog.h" | 12 #include "SkPDFCatalog.h" |
13 #include "SkPDFDocument.h" | 13 #include "SkPDFDocument.h" |
14 #include "SkStream.h" | 14 #include "SkStream.h" |
15 #include "SkUnPreMultiply.h" | 15 #include "SkUnPreMultiply.h" |
16 | 16 |
17 //////////////////////////////////////////////////////////////////////////////// | 17 //////////////////////////////////////////////////////////////////////////////// |
18 | 18 |
19 static void pdf_stream_begin(SkWStream* stream) { | 19 static void pdf_stream_begin(SkWStream* stream) { |
20 static const char streamBegin[] = " stream\n"; | 20 static const char streamBegin[] = " stream\n"; |
21 stream->write(streamBegin, strlen(streamBegin)); | 21 stream->write(streamBegin, strlen(streamBegin)); |
22 } | 22 } |
23 | 23 |
24 static void pdf_stream_end(SkWStream* stream) { | 24 static void pdf_stream_end(SkWStream* stream) { |
25 static const char streamEnd[] = "\nendstream"; | 25 static const char streamEnd[] = "\nendstream"; |
26 stream->write(streamEnd, strlen(streamEnd)); | 26 stream->write(streamEnd, strlen(streamEnd)); |
27 } | 27 } |
28 | 28 |
29 static size_t pixel_count(const SkBitmap& bm) { | 29 //////////////////////////////////////////////////////////////////////////////// |
30 return SkToSizeT(bm.width()) * SkToSizeT(bm.height()); | |
31 } | |
32 | 30 |
33 // write a single byte to a stream n times. | 31 // write a single byte to a stream n times. |
34 static void fill_stream(SkWStream* out, char value, size_t n) { | 32 static void fill_stream(SkWStream* out, char value, size_t n) { |
35 char buffer[4096]; | 33 char buffer[4096]; |
36 memset(buffer, value, sizeof(buffer)); | 34 memset(buffer, value, sizeof(buffer)); |
37 while (n) { | 35 while (n) { |
38 size_t k = SkTMin(n, sizeof(buffer)); | 36 size_t k = SkTMin(n, sizeof(buffer)); |
39 out->write(buffer, k); | 37 out->write(buffer, k); |
40 n -= k; | 38 n -= k; |
41 } | 39 } |
42 } | 40 } |
43 | 41 |
44 static SkPMColor get_pmcolor_neighbor_avg_color(const SkBitmap& bitmap, | 42 static void to_rgb24(SkPMColor color, U8CPU alpha, uint8_t* rgb) { |
mtklein
2015/02/23 14:26:52
This seems weird. Why do we pass alpha twice? It
hal.canary
2015/03/20 01:09:45
I didn't want to recalculate it, but that was too
| |
45 int xOrig, | 43 uint32_t s = SkUnPreMultiply::GetScale(alpha); |
46 int yOrig) { | 44 rgb[0] = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(color)); |
47 SkASSERT(kN32_SkColorType == bitmap.colorType()); | 45 rgb[1] = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(color)); |
48 SkASSERT(bitmap.getPixels()); | 46 rgb[2] = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(color)); |
49 uint8_t count = 0; | 47 } |
50 unsigned r = 0; | 48 |
51 unsigned g = 0; | 49 static void to_rgb24(SkPMColor color, uint8_t* rgb) { |
mtklein
2015/02/23 14:26:52
This is for the alpha == 0xFF case? Let's at leas
hal.canary
2015/03/20 01:09:45
this is removed.
| |
52 unsigned b = 0; | 50 rgb[0] = SkGetPackedR32(color); |
53 for (int y = yOrig - 1; y <= yOrig + 1; ++y) { | 51 rgb[1] = SkGetPackedG32(color); |
54 if (y < 0 || y >= bitmap.height()) { | 52 rgb[2] = SkGetPackedB32(color); |
55 continue; | 53 } |
56 } | 54 |
57 uint32_t* src = bitmap.getAddr32(0, y); | 55 static SkPMColor get_pmcolor(const SkBitmap& bm, int x, int y) { |
58 for (int x = xOrig - 1; x <= xOrig + 1; ++x) { | 56 switch (bm.colorType()) { |
59 if (x < 0 || x >= bitmap.width()) { | 57 case kARGB_4444_SkColorType: |
60 continue; | 58 return SkPixel4444ToPixel32(*(bm.getAddr16(x, y))); |
61 } | 59 case kN32_SkColorType: |
62 SkPMColor pmColor = src[x]; | 60 return *(bm.getAddr32(x, y)); |
61 default: | |
62 SkASSERT(false); | |
mtklein
2015/02/23 14:26:52
// We only use this to sample around transparent p
hal.canary
2015/03/20 01:09:46
Done.
| |
63 return 0; | |
64 } | |
65 } | |
66 | |
67 /* It is necessary to average the color component of transparent | |
68 pixels with their surrounding neighbors since the PDF renderer may | |
69 separately re-sample the alpha and color channels when the image is | |
70 not displayed at its native resolution. Since an alpha of zero | |
71 gives no information about the color component, the pathological | |
72 case is a white image with sharp transparency bounds - the color | |
73 channel goes to black, and the should-be-transparent pixels are | |
74 rendered as grey because of the separate soft mask and color | |
75 resizing. e.g.: gm/bitmappremul.cpp */ | |
76 static SkPMColor get_neighbor_avg_color(const SkBitmap& bm, | |
77 int xOrig, | |
78 int yOrig) { | |
79 SkASSERT(kARGB_4444_SkColorType == bm.colorType() || | |
80 kN32_SkColorType == bm.colorType()); | |
81 uint8_t n = 0; | |
mtklein
2015/02/23 14:26:53
This seems... unnecessarily precise? unsigned see
hal.canary
2015/03/20 01:09:45
Done.
| |
82 unsigned r = 0, g = 0, b = 0; | |
83 int yrange[2] = {SkTMax(yOrig - 1, 0), SkTMin(bm.height(), yOrig + 2)}; | |
mtklein
2015/02/23 14:26:53
Might want to swap the orders of the arguments to
hal.canary
2015/03/20 01:09:45
<= is unexpected in C.
| |
84 int xrange[2] = {SkTMax(xOrig - 1, 0), SkTMin(bm.width(), xOrig + 2)}; | |
85 for (int y = yrange[0]; y < yrange[1]; ++y) { | |
86 for (int x = xrange[0]; x < xrange[1]; ++x) { | |
mtklein
2015/02/23 14:26:53
Seems like when there are a lot of transparent pix
hal.canary
2015/03/20 01:09:45
I thinks so. I tried to think of how to do this i
| |
87 SkPMColor pmColor = get_pmcolor(bm, x, y); | |
63 U8CPU alpha = SkGetPackedA32(pmColor); | 88 U8CPU alpha = SkGetPackedA32(pmColor); |
64 if (alpha != SK_AlphaTRANSPARENT) { | 89 if (alpha != SK_AlphaTRANSPARENT) { |
65 uint32_t s = SkUnPreMultiply::GetScale(alpha); | 90 uint8_t rgb[3]; |
66 r += SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(pmColor)); | 91 to_rgb24(pmColor, alpha, rgb); |
67 g += SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(pmColor)); | 92 r += rgb[0]; |
68 b += SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(pmColor)); | 93 g += rgb[1]; |
69 ++count; | 94 b += rgb[2]; |
70 } | 95 ++n; |
71 } | 96 } |
72 } | 97 } |
73 if (count == 0) { | 98 } |
74 return SkPackARGB32NoCheck(SK_AlphaOPAQUE, 0, 0, 0); | 99 return (n > 0) ? SkPackARGB32NoCheck(SK_AlphaOPAQUE, r / n, g / n, b / n) |
100 : SkPackARGB32NoCheck(SK_AlphaOPAQUE, 0, 0, 0); | |
101 } | |
102 | |
103 static void convert_pixel( | |
104 const SkBitmap& bm, int x, int y, SkPMColor color, uint8_t* dst) { | |
105 U8CPU alpha = SkGetPackedA32(color); | |
106 if (alpha != SK_AlphaTRANSPARENT) { | |
107 to_rgb24(color, alpha, dst); | |
75 } else { | 108 } else { |
76 return SkPackARGB32NoCheck( | 109 to_rgb24(get_neighbor_avg_color(bm, x, y), dst); |
77 SK_AlphaOPAQUE, r / count, g / count, b / count); | 110 } |
78 } | 111 } |
79 } | 112 |
80 | 113 static void convert_scanline(const SkBitmap& bm, int y, uint8_t* dst) { |
81 static void pmcolor_to_rgb24(const SkBitmap& bm, SkWStream* out) { | 114 switch (bm.colorType()) { |
82 SkASSERT(kN32_SkColorType == bm.colorType()); | 115 case kARGB_4444_SkColorType: { |
116 const uint16_t* src = bm.getAddr16(0, y); | |
117 for (int x = 0; x < bm.width(); ++x) { | |
118 convert_pixel(bm, x, y, SkPixel4444ToPixel32(*src++), dst); | |
119 dst += 3; | |
120 } | |
121 return; | |
122 } | |
123 case kN32_SkColorType: { | |
124 const SkPMColor* src = bm.getAddr32(0, y); | |
125 for (int x = 0; x < bm.width(); ++x) { | |
126 convert_pixel(bm, x, y, *src++, dst); | |
127 dst += 3; | |
128 } | |
129 return; | |
130 } | |
131 case kRGB_565_SkColorType: { | |
132 const uint16_t* src = bm.getAddr16(0, y); | |
133 for (int x = 0; x < bm.width(); ++x) { | |
134 U16CPU color565 = *src++; | |
135 *dst++ = SkPacked16ToR32(color565); | |
136 *dst++ = SkPacked16ToG32(color565); | |
137 *dst++ = SkPacked16ToB32(color565); | |
138 } | |
139 return; | |
140 } | |
141 default: | |
142 SkASSERT(false); | |
143 } | |
144 } | |
145 | |
146 static size_t pixel_count(const SkBitmap& bm) { | |
147 return SkToSizeT(bm.width()) * SkToSizeT(bm.height()); | |
148 } | |
149 | |
150 static void bitmap_to_rgb24(const SkBitmap& bm, SkWStream* out) { | |
83 if (!bm.getPixels()) { | 151 if (!bm.getPixels()) { |
84 fill_stream(out, '\xFF', 3 * pixel_count(bm)); | 152 fill_stream(out, '\xFF', 3 * pixel_count(bm)); |
85 return; | 153 return; |
86 } | 154 } |
87 size_t scanlineLength = 3 * bm.width(); | 155 switch (bm.colorType()) { |
88 SkAutoTMalloc<uint8_t> scanline(scanlineLength); | 156 case kN32_SkColorType: |
157 case kARGB_4444_SkColorType: | |
158 case kRGB_565_SkColorType: { | |
159 SkAutoTMalloc<uint8_t> scanline(3 * bm.width()); | |
160 for (int y = 0; y < bm.height(); ++y) { | |
161 convert_scanline(bm, y, scanline.get()); | |
162 out->write(scanline.get(), 3 * bm.width()); | |
163 } | |
164 return; | |
165 } | |
166 case kAlpha_8_SkColorType: | |
167 fill_stream(out, '\x00', 3 * pixel_count(bm)); | |
168 return; | |
169 case kIndex_8_SkColorType: | |
170 for (int y = 0; y < bm.height(); ++y) { | |
171 out->write(bm.getAddr8(0, y), bm.width()); | |
172 } // not actually rgb24 format. | |
mtklein
2015/02/23 14:26:53
Let's explain what it is rather than what it's not
hal.canary
2015/03/20 01:09:45
Done.
| |
173 return; | |
174 case kUnknown_SkColorType: | |
175 default: | |
176 SkASSERT(false); | |
177 return; | |
178 } | |
179 } | |
180 | |
181 //////////////////////////////////////////////////////////////////////////////// | |
182 | |
183 static void convert_scanline_alpha(const SkBitmap& bm, int y, uint8_t* dst) { | |
184 switch (bm.colorType()) { | |
185 case kARGB_4444_SkColorType: { | |
186 const uint16_t* src = bm.getAddr16(0, y); | |
187 for (int x = 0; x < bm.width(); ++x) { | |
188 *dst++ = SkPacked4444ToA32(*src++); | |
189 } | |
190 return; | |
191 } | |
192 case kN32_SkColorType: { | |
193 const SkPMColor* src = bm.getAddr32(0, y); | |
194 for (int x = 0; x < bm.width(); ++x) { | |
195 *dst++ = SkGetPackedA32(*src++); | |
196 } | |
197 return; | |
198 } | |
199 default: | |
200 SkASSERT(false); | |
201 } | |
202 } | |
203 | |
204 static void index8_alpha_to_a8(const SkBitmap& bm, SkWStream* out) { | |
205 SkASSERT(kIndex_8_SkColorType == bm.colorType()); | |
206 SkColorTable* ct = bm.getColorTable(); | |
207 if (!ct) { | |
208 fill_stream(out, '\x00', pixel_count(bm)); | |
mtklein
2015/02/23 14:26:53
It seems like it's worth promoting this up to an a
hal.canary
2015/03/20 01:09:45
Done.
| |
209 return; | |
210 } | |
211 SkAutoTMalloc<uint8_t> scanline(bm.width()); | |
89 for (int y = 0; y < bm.height(); ++y) { | 212 for (int y = 0; y < bm.height(); ++y) { |
90 uint8_t* dst = scanline.get(); | 213 uint8_t* dst = scanline.get(); |
91 const SkPMColor* src = bm.getAddr32(0, y); | 214 const uint8_t* src = bm.getAddr8(0, y); |
92 for (int x = 0; x < bm.width(); ++x) { | 215 for (int x = 0; x < bm.width(); ++x) { |
93 SkPMColor color = *src++; | 216 *dst++ = SkGetPackedA32((*ct)[*src++]); |
94 U8CPU alpha = SkGetPackedA32(color); | 217 } |
95 if (alpha != SK_AlphaTRANSPARENT) { | 218 out->write(scanline.get(), bm.width()); |
96 uint32_t s = SkUnPreMultiply::GetScale(alpha); | 219 } |
97 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(color)); | 220 } |
98 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(color)); | 221 |
99 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(color)); | 222 static void bitmap_alpha_to_a8(const SkBitmap& bm, SkWStream* out) { |
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 | |
122 static void pmcolor_alpha_to_a8(const SkBitmap& bm, SkWStream* out) { | |
123 SkASSERT(kN32_SkColorType == bm.colorType()); | |
124 if (!bm.getPixels()) { | 223 if (!bm.getPixels()) { |
125 fill_stream(out, '\xFF', pixel_count(bm)); | 224 fill_stream(out, '\xFF', pixel_count(bm)); |
126 return; | 225 return; |
127 } | 226 } |
128 size_t scanlineLength = bm.width(); | 227 switch (bm.colorType()) { |
129 SkAutoTMalloc<uint8_t> scanline(scanlineLength); | 228 case kN32_SkColorType: |
130 for (int y = 0; y < bm.height(); ++y) { | 229 case kARGB_4444_SkColorType: { |
131 uint8_t* dst = scanline.get(); | 230 SkAutoTMalloc<uint8_t> scanline(bm.width()); |
132 const SkPMColor* src = bm.getAddr32(0, y); | 231 for (int y = 0; y < bm.height(); ++y) { |
133 for (int x = 0; x < bm.width(); ++x) { | 232 convert_scanline_alpha(bm, y, scanline.get()); |
134 *dst++ = SkGetPackedA32(*src++); | 233 out->write(scanline.get(), bm.width()); |
135 } | 234 } |
136 out->write(scanline.get(), scanlineLength); | 235 return; |
137 } | 236 } |
138 } | 237 case kAlpha_8_SkColorType: |
139 | 238 for (int y = 0; y < bm.height(); ++y) { |
239 out->write(bm.getAddr8(0, y), bm.width()); | |
240 } | |
241 return; | |
242 case kIndex_8_SkColorType: | |
243 index8_alpha_to_a8(bm, out); | |
244 return; | |
245 case kRGB_565_SkColorType: | |
246 case kUnknown_SkColorType: | |
247 default: | |
248 SkASSERT(false); | |
249 return; | |
250 } | |
251 } | |
252 | |
140 //////////////////////////////////////////////////////////////////////////////// | 253 //////////////////////////////////////////////////////////////////////////////// |
141 | 254 |
142 namespace { | 255 namespace { |
143 // This SkPDFObject only outputs the alpha layer of the given bitmap. | 256 // This SkPDFObject only outputs the alpha layer of the given bitmap. |
144 class PDFAlphaBitmap : public SkPDFObject { | 257 class PDFAlphaBitmap : public SkPDFObject { |
145 public: | 258 public: |
146 PDFAlphaBitmap(const SkBitmap& bm) : fBitmap(bm) {} | 259 PDFAlphaBitmap(const SkBitmap& bm) : fBitmap(bm) {} |
147 ~PDFAlphaBitmap() {} | 260 ~PDFAlphaBitmap() {} |
148 void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE; | 261 void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE; |
149 void addResources(SkTSet<SkPDFObject*>*, SkPDFCatalog*) const SK_OVERRIDE {} | 262 void addResources(SkTSet<SkPDFObject*>*, SkPDFCatalog*) const SK_OVERRIDE {} |
150 | 263 |
151 private: | 264 private: |
152 const SkBitmap fBitmap; | 265 const SkBitmap fBitmap; |
153 void emitDict(SkWStream*, SkPDFCatalog*, size_t, bool) const; | 266 void emitDict(SkWStream*, SkPDFCatalog*, size_t, bool) const; |
154 }; | 267 }; |
155 | 268 |
156 void PDFAlphaBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) { | 269 void PDFAlphaBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) { |
157 SkAutoLockPixels autoLockPixels(fBitmap); | 270 SkAutoLockPixels autoLockPixels(fBitmap); |
158 | 271 |
159 #ifndef SK_NO_FLATE | 272 #ifndef SK_NO_FLATE |
160 // Write to a temporary buffer to get the compressed length. | 273 // Write to a temporary buffer to get the compressed length. |
161 SkDynamicMemoryWStream buffer; | 274 SkDynamicMemoryWStream buffer; |
162 SkDeflateWStream deflateWStream(&buffer); | 275 SkDeflateWStream deflateWStream(&buffer); |
163 pmcolor_alpha_to_a8(fBitmap, &deflateWStream); | 276 bitmap_alpha_to_a8(fBitmap, &deflateWStream); |
164 deflateWStream.finalize(); // call before detachAsStream(). | 277 deflateWStream.finalize(); // call before detachAsStream(). |
165 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); | 278 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); |
166 | 279 |
167 this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true); | 280 this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true); |
168 pdf_stream_begin(stream); | 281 pdf_stream_begin(stream); |
169 stream->writeStream(asset.get(), asset->getLength()); | 282 stream->writeStream(asset.get(), asset->getLength()); |
170 pdf_stream_end(stream); | 283 pdf_stream_end(stream); |
171 #else | 284 #else |
172 this->emitDict(stream, catalog, pixel_count(fBitmap), /*deflate=*/false); | 285 this->emitDict(stream, catalog, pixel_count(fBitmap), /*deflate=*/false); |
173 pdf_stream_begin(stream); | 286 pdf_stream_begin(stream); |
174 pmcolor_alpha_to_a8(fBitmap, stream); | 287 bitmap_alpha_to_a8(fBitmap, stream); |
175 pdf_stream_end(stream); | 288 pdf_stream_end(stream); |
176 #endif // SK_NO_FLATE | 289 #endif // SK_NO_FLATE |
177 } | 290 } |
178 | 291 |
179 void PDFAlphaBitmap::emitDict(SkWStream* stream, | 292 void PDFAlphaBitmap::emitDict(SkWStream* stream, |
180 SkPDFCatalog* catalog, | 293 SkPDFCatalog* catalog, |
181 size_t length, | 294 size_t length, |
182 bool deflate) const { | 295 bool deflate) const { |
183 SkPDFDict pdfDict("XObject"); | 296 SkPDFDict pdfDict("XObject"); |
184 pdfDict.insertName("Subtype", "Image"); | 297 pdfDict.insertName("Subtype", "Image"); |
(...skipping 18 matching lines...) Expand all Loading... | |
203 } | 316 } |
204 } | 317 } |
205 | 318 |
206 void SkPDFBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) { | 319 void SkPDFBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) { |
207 SkAutoLockPixels autoLockPixels(fBitmap); | 320 SkAutoLockPixels autoLockPixels(fBitmap); |
208 | 321 |
209 #ifndef SK_NO_FLATE | 322 #ifndef SK_NO_FLATE |
210 // Write to a temporary buffer to get the compressed length. | 323 // Write to a temporary buffer to get the compressed length. |
211 SkDynamicMemoryWStream buffer; | 324 SkDynamicMemoryWStream buffer; |
212 SkDeflateWStream deflateWStream(&buffer); | 325 SkDeflateWStream deflateWStream(&buffer); |
213 pmcolor_to_rgb24(fBitmap, &deflateWStream); | 326 bitmap_to_rgb24(fBitmap, &deflateWStream); |
214 deflateWStream.finalize(); // call before detachAsStream(). | 327 deflateWStream.finalize(); // call before detachAsStream(). |
215 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); | 328 SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); |
216 | 329 |
217 this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true); | 330 this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true); |
218 pdf_stream_begin(stream); | 331 pdf_stream_begin(stream); |
219 stream->writeStream(asset.get(), asset->getLength()); | 332 stream->writeStream(asset.get(), asset->getLength()); |
220 pdf_stream_end(stream); | 333 pdf_stream_end(stream); |
221 #else | 334 #else |
222 this->emitDict(stream, catalog, 3 * pixel_count(fBitmap), /*deflate=*/false) ; | 335 this->emitDict(stream, catalog, 3 * pixel_count(fBitmap), /*deflate=*/false) ; |
223 pdf_stream_begin(stream); | 336 pdf_stream_begin(stream); |
224 pmcolor_to_rgb24(fBitmap, stream); | 337 bitmap_to_rgb24(fBitmap, stream); |
225 pdf_stream_end(stream); | 338 pdf_stream_end(stream); |
226 return; | 339 return; |
227 #endif // SK_NO_FLATE | 340 #endif // SK_NO_FLATE |
228 } | 341 } |
229 | 342 |
343 static SkPDFArray* make_indexed_color_space(const SkColorTable* table) { | |
344 SkPDFArray* result = SkNEW(SkPDFArray); | |
345 result->reserve(4); | |
346 result->appendName("Indexed"); | |
347 result->appendName("DeviceRGB"); | |
348 SkASSERT(table); | |
349 result->appendInt(table->count() - 1); | |
350 | |
351 // Potentially, this could be represented in fewer bytes with a stream. | |
352 // Max size as a string is 1.5k. | |
mtklein
2015/02/23 14:26:53
Some sort of assert here then?
hal.canary
2015/03/20 01:09:45
Done.
| |
353 SkString index; | |
354 for (int i = 0; i < table->count(); i++) { | |
355 SkPMColor color = (*table)[i]; | |
356 U8CPU alpha = SkGetPackedA32(color); | |
357 uint8_t rgb[3]; | |
358 to_rgb24(color, alpha, rgb); | |
359 index.append((char*)rgb, 3); | |
360 } | |
361 result->append(new SkPDFString(index))->unref(); | |
362 return result; | |
363 } | |
364 | |
230 void SkPDFBitmap::emitDict(SkWStream* stream, | 365 void SkPDFBitmap::emitDict(SkWStream* stream, |
231 SkPDFCatalog* catalog, | 366 SkPDFCatalog* catalog, |
232 size_t length, | 367 size_t length, |
233 bool deflate) const { | 368 bool deflate) const { |
234 SkPDFDict pdfDict("XObject"); | 369 SkPDFDict pdfDict("XObject"); |
235 pdfDict.insertName("Subtype", "Image"); | 370 pdfDict.insertName("Subtype", "Image"); |
236 pdfDict.insertInt("Width", fBitmap.width()); | 371 pdfDict.insertInt("Width", fBitmap.width()); |
237 pdfDict.insertInt("Height", fBitmap.height()); | 372 pdfDict.insertInt("Height", fBitmap.height()); |
238 pdfDict.insertName("ColorSpace", "DeviceRGB"); | 373 if (fBitmap.colorType() != kIndex_8_SkColorType) { |
374 pdfDict.insertName("ColorSpace", "DeviceRGB"); | |
375 } else { | |
376 pdfDict.insert("ColorSpace", make_indexed_color_space( | |
377 fBitmap.getColorTable()))->unref(); | |
378 } | |
239 pdfDict.insertInt("BitsPerComponent", 8); | 379 pdfDict.insertInt("BitsPerComponent", 8); |
240 if (fSMask) { | 380 if (fSMask) { |
241 pdfDict.insert("SMask", new SkPDFObjRef(fSMask))->unref(); | 381 pdfDict.insert("SMask", new SkPDFObjRef(fSMask))->unref(); |
242 } | 382 } |
243 if (deflate) { | 383 if (deflate) { |
244 pdfDict.insertName("Filter", "FlateDecode"); | 384 pdfDict.insertName("Filter", "FlateDecode"); |
245 } | 385 } |
246 pdfDict.insertInt("Length", length); | 386 pdfDict.insertInt("Length", length); |
247 pdfDict.emitObject(stream, catalog); | 387 pdfDict.emitObject(stream, catalog); |
248 } | 388 } |
249 | 389 |
250 SkPDFBitmap::SkPDFBitmap(SkPDFCanon* canon, | 390 SkPDFBitmap::SkPDFBitmap(SkPDFCanon* canon, |
251 const SkBitmap& bm, | 391 const SkBitmap& bm, |
252 SkPDFObject* smask) | 392 SkPDFObject* smask) |
253 : fCanon(canon), fBitmap(bm), fSMask(smask) {} | 393 : fCanon(canon), fBitmap(bm), fSMask(smask) {} |
254 | 394 |
255 SkPDFBitmap::~SkPDFBitmap() { fCanon->removeBitmap(this); } | 395 SkPDFBitmap::~SkPDFBitmap() { fCanon->removeBitmap(this); } |
256 | 396 |
257 //////////////////////////////////////////////////////////////////////////////// | 397 //////////////////////////////////////////////////////////////////////////////// |
258 static bool is_transparent(const SkBitmap& bm) { | 398 |
259 SkAutoLockPixels autoLockPixels(bm); | 399 static bool is_transparent_pixels(const SkBitmap& bm) { |
mtklein
2015/02/23 14:26:52
pixels_are_transparent?
hal.canary
2015/03/20 01:09:45
I've stoped this check. Why lock the bitmap unnec
| |
260 if (NULL == bm.getPixels()) { | |
261 return true; | |
262 } | |
263 SkASSERT(kN32_SkColorType == bm.colorType()); | |
264 for (int y = 0; y < bm.height(); ++y) { | 400 for (int y = 0; y < bm.height(); ++y) { |
265 U8CPU alpha = 0; | 401 U8CPU alpha = 0; |
266 const SkPMColor* src = bm.getAddr32(0, y); | 402 switch (bm.colorType()) { |
267 for (int x = 0; x < bm.width(); ++x) { | 403 case kN32_SkColorType: { |
268 alpha |= SkGetPackedA32(*src++); | 404 const SkPMColor* src = bm.getAddr32(0, y); |
405 for (int x = 0; x < bm.width(); ++x) { | |
406 alpha |= SkGetPackedA32(*src++); | |
mtklein
2015/02/23 14:26:53
Why don't we just bail out the first time we see a
hal.canary
2015/03/20 01:09:45
Acknowledged.
| |
407 } | |
408 break; | |
409 } | |
410 case kARGB_4444_SkColorType: { | |
411 const uint16_t* src = bm.getAddr16(0, y); | |
412 for (int x = 0; x < bm.width(); ++x) { | |
413 alpha |= SkPacked4444ToA32(*src++); | |
414 } | |
415 break; | |
416 } | |
417 case kAlpha_8_SkColorType: { | |
418 const uint8_t* src = bm.getAddr8(0, y); | |
419 for (int x = 0; x < bm.width(); ++x) { | |
420 alpha |= *src++; | |
421 } | |
422 break; | |
423 } | |
424 default: | |
425 SkASSERT(false); | |
269 } | 426 } |
270 if (alpha) { | 427 if (alpha) { |
271 return false; | 428 return false; |
272 } | 429 } |
273 } | 430 } |
274 return true; | 431 return true; |
275 } | 432 } |
276 | 433 |
434 static bool is_transparent(const SkBitmap& bm) { | |
435 SkAutoLockPixels autoLockPixels(bm); | |
436 if (NULL == bm.getPixels()) { | |
437 return true; | |
438 } | |
439 switch (bm.colorType()) { | |
440 case kN32_SkColorType: | |
441 case kARGB_4444_SkColorType: | |
442 case kAlpha_8_SkColorType: | |
443 return is_transparent_pixels(bm); | |
444 case kIndex_8_SkColorType: { | |
445 const SkColorTable* ct = bm.getColorTable(); | |
446 if (!ct) { | |
447 return true; | |
448 } | |
449 for (int y = 0; y < bm.height(); ++y) { | |
450 U8CPU alpha = 0; | |
451 const uint8_t* src = bm.getAddr8(0, y); | |
452 for (int x = 0; x < bm.width(); ++x) { | |
453 alpha |= SkGetPackedA32((*ct)[*src++]); | |
mtklein
2015/02/23 14:26:52
Same question?
hal.canary
2015/03/20 01:09:45
Acknowledged.
| |
454 } | |
455 if (alpha) { | |
456 return false; | |
457 } | |
458 } | |
459 return true; | |
460 } | |
461 default: | |
462 SkASSERT(false); | |
463 return false; | |
464 } | |
465 } | |
466 | |
277 SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon, | 467 SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon, |
278 const SkBitmap& bitmap, | 468 const SkBitmap& bitmap, |
279 const SkIRect& subset) { | 469 const SkIRect& subset) { |
280 SkASSERT(canon); | 470 SkASSERT(canon); |
281 if (kN32_SkColorType != bitmap.colorType()) { | 471 switch (bitmap.colorType()) { |
282 // TODO(halcanary): support other colortypes. | 472 case kN32_SkColorType: |
283 return NULL; | 473 case kRGB_565_SkColorType: |
474 case kARGB_4444_SkColorType: | |
475 case kAlpha_8_SkColorType: | |
476 case kIndex_8_SkColorType: | |
477 break; | |
478 case kUnknown_SkColorType: | |
479 default: | |
480 return NULL; | |
284 } | 481 } |
285 SkBitmap bm; | 482 SkBitmap bm; |
286 // Should extractSubset be done by the SkPDFDevice? | 483 // Should extractSubset be done by the SkPDFDevice? |
287 if (!bitmap.extractSubset(&bm, subset)) { | 484 if (!bitmap.extractSubset(&bm, subset)) { |
288 return NULL; | 485 return NULL; |
289 } | 486 } |
290 if (bm.drawsNothing()) { | 487 if (bm.drawsNothing()) { |
291 return NULL; | 488 return NULL; |
292 } | 489 } |
293 if (!bm.isImmutable()) { | 490 if (!bm.isImmutable()) { |
294 SkBitmap copy; | 491 SkBitmap copy; |
295 if (!bm.copyTo(©)) { | 492 if (!bm.copyTo(©)) { |
296 return NULL; | 493 return NULL; |
297 } | 494 } |
298 copy.setImmutable(); | 495 copy.setImmutable(); |
299 bm = copy; | 496 bm = copy; |
300 } | 497 } |
301 | 498 |
302 SkPDFBitmap* pdfBitmap = canon->findBitmap(bm); | 499 SkPDFBitmap* pdfBitmap = canon->findBitmap(bm); |
303 if (pdfBitmap) { | 500 if (pdfBitmap) { |
304 return SkRef(pdfBitmap); | 501 return SkRef(pdfBitmap); |
305 } | 502 } |
306 SkPDFObject* smask = NULL; | 503 SkPDFObject* smask = NULL; |
307 if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) { | 504 if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) { |
308 if (is_transparent(bm)) { | 505 if (is_transparent(bm)) { |
mtklein
2015/02/23 14:26:53
Just curious, do we really see fully transparent b
hal.canary
2015/03/20 01:09:46
Done.
| |
309 return NULL; | 506 return NULL; |
310 } | 507 } |
311 // PDFAlphaBitmaps do not get directly canonicalized (they | 508 // PDFAlphaBitmaps do not get directly canonicalized (they |
312 // are refed by the SkPDFBitmap). | 509 // are refed by the SkPDFBitmap). |
313 smask = SkNEW_ARGS(PDFAlphaBitmap, (bm)); | 510 smask = SkNEW_ARGS(PDFAlphaBitmap, (bm)); |
314 } | 511 } |
315 pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (canon, bm, smask)); | 512 pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (canon, bm, smask)); |
316 canon->addBitmap(pdfBitmap); | 513 canon->addBitmap(pdfBitmap); |
317 return pdfBitmap; | 514 return pdfBitmap; |
318 } | 515 } |
OLD | NEW |