OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright 2010 The Android Open Source Project | 2 * Copyright 2010 The Android Open Source Project |
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 "SkPDFImage.h" | 8 #include "SkPDFImage.h" |
9 | 9 |
10 #include "SkBitmap.h" | 10 #include "SkBitmap.h" |
11 #include "SkColor.h" | 11 #include "SkColor.h" |
12 #include "SkColorPriv.h" | 12 #include "SkColorPriv.h" |
13 #include "SkData.h" | |
14 #include "SkFlate.h" | |
13 #include "SkPDFCatalog.h" | 15 #include "SkPDFCatalog.h" |
14 #include "SkRect.h" | 16 #include "SkRect.h" |
15 #include "SkStream.h" | 17 #include "SkStream.h" |
16 #include "SkString.h" | 18 #include "SkString.h" |
17 #include "SkUnPreMultiply.h" | 19 #include "SkUnPreMultiply.h" |
18 | 20 |
19 namespace { | 21 static const int kNoColorTransform = 0; |
20 | 22 |
21 void extractImageData(const SkBitmap& bitmap, const SkIRect& srcRect, | 23 static bool skip_compression(SkPDFCatalog* catalog) { |
22 SkStream** imageData, SkStream** alphaData) { | 24 return SkToBool(catalog->getDocumentFlags() & |
23 SkMemoryStream* image = NULL; | 25 SkPDFDocument::kFavorSpeedOverSize_Flags); |
24 SkMemoryStream* alpha = NULL; | 26 } |
27 | |
28 static size_t get_row_bytes(const SkBitmap& bitmap, | |
29 const SkIRect& srcRect) { | |
30 switch (bitmap.getConfig()) { | |
31 case SkBitmap::kIndex8_Config: | |
32 return srcRect.width(); | |
33 case SkBitmap::kARGB_4444_Config: | |
34 return (srcRect.width() * 3 + 1) / 2; | |
35 case SkBitmap::kRGB_565_Config: | |
36 return srcRect.width() * 3; | |
37 case SkBitmap::kARGB_8888_Config: | |
38 return srcRect.width() * 3; | |
39 case SkBitmap::kA1_Config: | |
40 case SkBitmap::kA8_Config: | |
41 return 1; | |
42 default: | |
43 SkASSERT(false); | |
44 return 0; | |
45 } | |
46 } | |
47 | |
48 static size_t get_uncompressed_size(const SkBitmap& bitmap, | |
49 const SkIRect& srcRect) { | |
50 switch (bitmap.getConfig()) { | |
51 case SkBitmap::kIndex8_Config: | |
52 case SkBitmap::kARGB_4444_Config: | |
53 case SkBitmap::kRGB_565_Config: | |
54 case SkBitmap::kARGB_8888_Config: | |
55 return get_row_bytes(bitmap, srcRect) * srcRect.height(); | |
vandebo (ex-Chrome)
2013/08/23 05:25:20
Here and extract_index8_image are the only place t
ducky
2013/08/23 06:59:08
Done.
| |
56 case SkBitmap::kA1_Config: | |
57 case SkBitmap::kA8_Config: | |
58 return 1; | |
59 default: | |
60 SkASSERT(false); | |
61 return 0; | |
62 } | |
63 } | |
64 | |
65 static SkStream* extract_index8_image(const SkBitmap& bitmap, | |
66 const SkIRect& srcRect) { | |
67 const int rowBytes = get_row_bytes(bitmap, srcRect); | |
68 SkStream* stream = SkNEW_ARGS(SkMemoryStream, | |
69 (rowBytes * srcRect.height())); | |
70 uint8_t* dst = (uint8_t*)stream->getMemoryBase(); | |
71 | |
72 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | |
73 memcpy(dst, bitmap.getAddr8(srcRect.fLeft, y), rowBytes); | |
74 dst += rowBytes; | |
75 } | |
76 return stream; | |
77 } | |
78 | |
79 static SkStream* extract_argb4444_data(const SkBitmap& bitmap, | |
80 const SkIRect& srcRect, | |
81 bool extractAlpha, | |
82 bool* hasAlpha, | |
vandebo (ex-Chrome)
2013/08/23 05:25:20
If it doesn't have alpha, or is completely transpa
ducky
2013/08/23 06:59:08
But they're necessary for the optimizations: you n
vandebo (ex-Chrome)
2013/08/23 15:45:35
Ok, but you don't need both of them. I would sugge
ducky
2013/08/23 17:59:50
Both are here so that the final check if transpare
| |
83 bool* isTransparent) { | |
84 SkStream* stream; | |
85 uint8_t* dst = NULL; | |
86 if (extractAlpha) { | |
87 const int alphaRowBytes = (srcRect.width() + 1) / 2; | |
88 stream = SkNEW_ARGS(SkMemoryStream, | |
89 (alphaRowBytes * srcRect.height())); | |
90 } else { | |
91 stream = SkNEW_ARGS(SkMemoryStream, | |
92 (get_uncompressed_size(bitmap, srcRect))); | |
93 } | |
94 dst = (uint8_t*)stream->getMemoryBase(); | |
95 | |
96 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | |
97 uint16_t* src = bitmap.getAddr16(0, y); | |
98 int x; | |
99 for (x = srcRect.fLeft; x + 1 < srcRect.fRight; x += 2) { | |
100 if (extractAlpha) { | |
101 dst[0] = (SkGetPackedA4444(src[x]) << 4) | | |
102 SkGetPackedA4444(src[x + 1]); | |
103 if (dst[0] != SK_AlphaOPAQUE) { | |
vandebo (ex-Chrome)
2013/08/23 05:25:20
This can probably made more streamlined....
hasAl
ducky
2013/08/23 06:59:08
Nice. Will do.
| |
104 *hasAlpha = true; | |
105 } | |
106 if (dst[0] != SK_AlphaTRANSPARENT) { | |
107 *isTransparent = false; | |
108 } | |
109 dst++; | |
110 } else { | |
111 dst[0] = (SkGetPackedR4444(src[x]) << 4) | | |
112 SkGetPackedG4444(src[x]); | |
113 dst[1] = (SkGetPackedB4444(src[x]) << 4) | | |
114 SkGetPackedR4444(src[x + 1]); | |
115 dst[2] = (SkGetPackedG4444(src[x + 1]) << 4) | | |
116 SkGetPackedB4444(src[x + 1]); | |
117 dst += 3; | |
118 } | |
119 } | |
120 if (srcRect.width() & 1) { | |
121 if (extractAlpha) { | |
122 dst[0] = (SkGetPackedA4444(src[x]) << 4); | |
123 if (dst[0] != (SK_AlphaOPAQUE & 0xF0)) { | |
124 *hasAlpha = true; | |
vandebo (ex-Chrome)
2013/08/23 05:25:20
A better name for hasAlpha is probably the opposit
ducky
2013/08/23 06:59:08
Ok. It does become more consistent with SkBitmap t
| |
125 } | |
126 if (dst[0] != (SK_AlphaTRANSPARENT & 0xF0)) { | |
127 *isTransparent = false; | |
128 } | |
129 dst++; | |
130 | |
131 } else { | |
132 dst[0] = (SkGetPackedR4444(src[x]) << 4) | | |
133 SkGetPackedG4444(src[x]); | |
134 dst[1] = (SkGetPackedB4444(src[x]) << 4); | |
135 dst += 2; | |
136 } | |
137 } | |
138 } | |
139 return stream; | |
140 } | |
141 | |
142 static SkStream* extract_rgb565_image(const SkBitmap& bitmap, | |
143 const SkIRect& srcRect) { | |
144 SkStream* stream = SkNEW_ARGS(SkMemoryStream, | |
145 (get_uncompressed_size(bitmap, | |
146 srcRect))); | |
147 uint8_t* dst = (uint8_t*)stream->getMemoryBase(); | |
148 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | |
149 uint16_t* src = bitmap.getAddr16(0, y); | |
150 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { | |
151 dst[0] = SkGetPackedR16(src[x]); | |
152 dst[1] = SkGetPackedG16(src[x]); | |
153 dst[2] = SkGetPackedB16(src[x]); | |
154 dst += 3; | |
155 } | |
156 } | |
157 return stream; | |
158 } | |
159 | |
160 static SkStream* extract_argb8888_data(const SkBitmap& bitmap, | |
161 const SkIRect& srcRect, | |
162 bool extractAlpha, | |
163 bool* hasAlpha, | |
164 bool* isTransparent) { | |
165 SkStream* stream; | |
166 if (extractAlpha) { | |
167 stream = SkNEW_ARGS(SkMemoryStream, | |
168 (srcRect.width() * srcRect.height())); | |
169 } else { | |
170 stream = SkNEW_ARGS(SkMemoryStream, | |
171 (get_uncompressed_size(bitmap, srcRect))); | |
172 } | |
173 uint8_t* dst = (uint8_t*)stream->getMemoryBase(); | |
174 | |
175 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | |
176 uint32_t* src = bitmap.getAddr32(0, y); | |
177 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { | |
178 if (extractAlpha) { | |
179 dst[0] = SkGetPackedA32(src[x]); | |
180 if (dst[0] != SK_AlphaOPAQUE) { | |
181 *hasAlpha = true; | |
182 } | |
183 if (dst[0] != SK_AlphaTRANSPARENT) { | |
184 *isTransparent = false; | |
185 } | |
186 dst++; | |
187 } else { | |
188 dst[0] = SkGetPackedR32(src[x]); | |
189 dst[1] = SkGetPackedG32(src[x]); | |
190 dst[2] = SkGetPackedB32(src[x]); | |
191 dst += 3; | |
192 } | |
193 } | |
194 } | |
195 return stream; | |
196 } | |
197 | |
198 static SkStream* extract_a1_alpha(const SkBitmap& bitmap, | |
199 const SkIRect& srcRect, | |
200 bool* hasAlpha, | |
201 bool* isTransparent) { | |
202 const int alphaRowBytes = (srcRect.width() + 7) / 8; | |
203 SkStream* stream = SkNEW_ARGS(SkMemoryStream, | |
204 (alphaRowBytes * srcRect.height())); | |
205 uint8_t* alphaDst = (uint8_t*)stream->getMemoryBase(); | |
206 | |
207 int offset1 = srcRect.fLeft % 8; | |
208 int offset2 = 8 - offset1; | |
209 | |
210 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | |
211 uint8_t* src = bitmap.getAddr1(0, y); | |
212 // This may read up to one byte after src, but the | |
213 // potentially invalid bits are never used for computation. | |
214 for (int x = srcRect.fLeft; x < srcRect.fRight; x += 8) { | |
215 if (offset1) { | |
216 alphaDst[0] = src[x / 8] << offset1 | | |
217 src[x / 8 + 1] >> offset2; | |
218 } else { | |
219 alphaDst[0] = src[x / 8]; | |
220 } | |
221 if (x + 7 < srcRect.fRight && alphaDst[0] != SK_AlphaOPAQUE) { | |
222 *hasAlpha = true; | |
223 } | |
224 if (x + 7 < srcRect.fRight && alphaDst[0] != SK_AlphaTRANSPARENT) { | |
225 *isTransparent = false; | |
226 } | |
227 alphaDst++; | |
228 } | |
229 // Calculate the mask of bits we're interested in within the | |
230 // last byte of alphaDst. | |
231 // width mod 8 == 1 -> 0x80 ... width mod 8 == 7 -> 0xFE | |
232 uint8_t mask = ~((1 << (8 - (srcRect.width() % 8))) - 1); | |
233 if (srcRect.width() % 8 && | |
234 (alphaDst[-1] & mask) != (SK_AlphaOPAQUE & mask)) { | |
235 *hasAlpha = true; | |
236 } | |
237 if (srcRect.width() % 8 && | |
238 (alphaDst[-1] & mask) != (SK_AlphaTRANSPARENT & mask)) { | |
239 *isTransparent = false; | |
240 } | |
241 } | |
242 return stream; | |
243 } | |
244 | |
245 static SkStream* extract_a8_alpha(const SkBitmap& bitmap, | |
246 const SkIRect& srcRect, | |
247 bool* hasAlpha, | |
248 bool* isTransparent) { | |
249 const int alphaRowBytes = srcRect.width(); | |
250 SkStream* stream = SkNEW_ARGS(SkMemoryStream, | |
251 (alphaRowBytes * srcRect.height())); | |
252 uint8_t* alphaDst = (uint8_t*)stream->getMemoryBase(); | |
253 | |
254 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | |
255 uint8_t* src = bitmap.getAddr8(0, y); | |
256 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { | |
257 alphaDst[0] = src[x]; | |
258 if (alphaDst[0] != SK_AlphaOPAQUE) { | |
259 *hasAlpha = true; | |
260 } | |
261 if (alphaDst[0] != SK_AlphaTRANSPARENT) { | |
262 *isTransparent = false; | |
263 } | |
264 alphaDst++; | |
265 } | |
266 } | |
267 return stream; | |
268 } | |
269 | |
270 static SkStream* create_black_image() { | |
271 SkStream* stream = SkNEW_ARGS(SkMemoryStream, (1)); | |
272 ((uint8_t*)stream->getMemoryBase())[0] = 0; | |
273 return stream; | |
274 } | |
275 | |
276 /** | |
277 * Extract image data to a SkStream. Interlaced alpha is separated into alpha | |
vandebo (ex-Chrome)
2013/08/23 05:25:20
Interlaced usually refers to lines/horizontal stri
ducky
2013/08/23 06:59:08
Done.
| |
278 * and image streams. | |
279 * @param bitmap Bitmap to extract data from. | |
280 * @param srcRect Region in the bitmap to extract. | |
281 * @param extractAlpha Set to true to extract the alpha data, or false to | |
282 * extract the color data. | |
283 * @param transparent Whether the alpha is completely transparent. Only valid | |
284 * when extractAlpha == true. | |
285 * @return Unencoded image data, or NULL if data was not available | |
286 * OR alpha data was requested but the image was entirely | |
287 * transparent or opaque. | |
vandebo (ex-Chrome)
2013/08/23 05:25:20
If Null is returned when the image is transparent,
ducky
2013/08/23 06:59:08
Necessary to differentiate from opaque case (where
| |
288 */ | |
289 static SkStream* extract_image_data(const SkBitmap& bitmap, | |
290 const SkIRect& srcRect, | |
291 bool extractAlpha, bool* isTransparent) { | |
292 SkStream* stream = NULL; | |
25 bool hasAlpha = false; | 293 bool hasAlpha = false; |
26 bool isTransparent = false; | 294 *isTransparent = true; |
27 | 295 |
vandebo (ex-Chrome)
2013/08/23 05:25:20
Probably easier to have an early return for the al
ducky
2013/08/23 06:59:08
Ehhhhh - the current style uses consistent mechani
vandebo (ex-Chrome)
2013/08/23 15:45:35
Disagree. Not sure what you mean by consistent me
ducky
2013/08/23 17:59:50
I see - done.
On 2013/08/23 15:45:35, vandebo wro
| |
28 bitmap.lockPixels(); | 296 bitmap.lockPixels(); |
29 switch (bitmap.getConfig()) { | 297 switch (bitmap.getConfig()) { |
30 case SkBitmap::kIndex8_Config: { | 298 case SkBitmap::kIndex8_Config: |
31 const int rowBytes = srcRect.width(); | 299 if (!extractAlpha) { |
32 image = new SkMemoryStream(rowBytes * srcRect.height()); | 300 stream = extract_index8_image(bitmap, srcRect); |
vandebo (ex-Chrome)
2013/08/23 05:25:20
Use of functions makes this one easier to follow.
ducky
2013/08/23 06:59:08
Yeah, the old one was getting super bloated...
| |
33 uint8_t* dst = (uint8_t*)image->getMemoryBase(); | 301 } else { |
34 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | 302 *isTransparent = false; |
35 memcpy(dst, bitmap.getAddr8(srcRect.fLeft, y), rowBytes); | 303 } |
36 dst += rowBytes; | 304 break; |
37 } | 305 case SkBitmap::kARGB_4444_Config: |
38 break; | 306 stream = extract_argb4444_data(bitmap, srcRect, extractAlpha, |
39 } | 307 &hasAlpha, isTransparent); |
40 case SkBitmap::kARGB_4444_Config: { | 308 break; |
41 isTransparent = true; | 309 case SkBitmap::kRGB_565_Config: |
42 const int rowBytes = (srcRect.width() * 3 + 1) / 2; | 310 if (!extractAlpha) { |
43 const int alphaRowBytes = (srcRect.width() + 1) / 2; | 311 stream = extract_rgb565_image(bitmap, srcRect); |
44 image = new SkMemoryStream(rowBytes * srcRect.height()); | 312 } else { |
45 alpha = new SkMemoryStream(alphaRowBytes * srcRect.height()); | 313 *isTransparent = false; |
46 uint8_t* dst = (uint8_t*)image->getMemoryBase(); | 314 } |
47 uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase(); | 315 break; |
48 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | 316 case SkBitmap::kARGB_8888_Config: |
49 uint16_t* src = bitmap.getAddr16(0, y); | 317 stream = extract_argb8888_data(bitmap, srcRect, extractAlpha, |
50 int x; | 318 &hasAlpha, isTransparent); |
51 for (x = srcRect.fLeft; x + 1 < srcRect.fRight; x += 2) { | 319 break; |
52 dst[0] = (SkGetPackedR4444(src[x]) << 4) | | 320 case SkBitmap::kA1_Config: |
53 SkGetPackedG4444(src[x]); | 321 if (extractAlpha) { |
54 dst[1] = (SkGetPackedB4444(src[x]) << 4) | | 322 stream = extract_a1_alpha(bitmap, srcRect, |
55 SkGetPackedR4444(src[x + 1]); | 323 &hasAlpha, isTransparent); |
56 dst[2] = (SkGetPackedG4444(src[x + 1]) << 4) | | 324 } else { |
57 SkGetPackedB4444(src[x + 1]); | 325 stream = create_black_image(); |
58 dst += 3; | 326 } |
59 alphaDst[0] = (SkGetPackedA4444(src[x]) << 4) | | 327 break; |
60 SkGetPackedA4444(src[x + 1]); | 328 case SkBitmap::kA8_Config: |
61 if (alphaDst[0] != 0xFF) { | 329 if (extractAlpha) { |
62 hasAlpha = true; | 330 stream = extract_a8_alpha(bitmap, srcRect, |
63 } | 331 &hasAlpha, isTransparent); |
64 if (alphaDst[0]) { | 332 } else { |
65 isTransparent = false; | 333 stream = create_black_image(); |
66 } | 334 } |
67 alphaDst++; | 335 break; |
68 } | |
69 if (srcRect.width() & 1) { | |
70 dst[0] = (SkGetPackedR4444(src[x]) << 4) | | |
71 SkGetPackedG4444(src[x]); | |
72 dst[1] = (SkGetPackedB4444(src[x]) << 4); | |
73 dst += 2; | |
74 alphaDst[0] = (SkGetPackedA4444(src[x]) << 4); | |
75 if (alphaDst[0] != 0xF0) { | |
76 hasAlpha = true; | |
77 } | |
78 if (alphaDst[0] & 0xF0) { | |
79 isTransparent = false; | |
80 } | |
81 alphaDst++; | |
82 } | |
83 } | |
84 break; | |
85 } | |
86 case SkBitmap::kRGB_565_Config: { | |
87 const int rowBytes = srcRect.width() * 3; | |
88 image = new SkMemoryStream(rowBytes * srcRect.height()); | |
89 uint8_t* dst = (uint8_t*)image->getMemoryBase(); | |
90 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | |
91 uint16_t* src = bitmap.getAddr16(0, y); | |
92 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { | |
93 dst[0] = SkGetPackedR16(src[x]); | |
94 dst[1] = SkGetPackedG16(src[x]); | |
95 dst[2] = SkGetPackedB16(src[x]); | |
96 dst += 3; | |
97 } | |
98 } | |
99 break; | |
100 } | |
101 case SkBitmap::kARGB_8888_Config: { | |
102 isTransparent = true; | |
103 const int rowBytes = srcRect.width() * 3; | |
104 image = new SkMemoryStream(rowBytes * srcRect.height()); | |
105 alpha = new SkMemoryStream(srcRect.width() * srcRect.height()); | |
106 uint8_t* dst = (uint8_t*)image->getMemoryBase(); | |
107 uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase(); | |
108 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | |
109 uint32_t* src = bitmap.getAddr32(0, y); | |
110 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { | |
111 dst[0] = SkGetPackedR32(src[x]); | |
112 dst[1] = SkGetPackedG32(src[x]); | |
113 dst[2] = SkGetPackedB32(src[x]); | |
114 dst += 3; | |
115 alphaDst[0] = SkGetPackedA32(src[x]); | |
116 if (alphaDst[0] != 0xFF) { | |
117 hasAlpha = true; | |
118 } | |
119 if (alphaDst[0]) { | |
120 isTransparent = false; | |
121 } | |
122 alphaDst++; | |
123 } | |
124 } | |
125 break; | |
126 } | |
127 case SkBitmap::kA1_Config: { | |
128 isTransparent = true; | |
129 image = new SkMemoryStream(1); | |
130 ((uint8_t*)image->getMemoryBase())[0] = 0; | |
131 | |
132 const int alphaRowBytes = (srcRect.width() + 7) / 8; | |
133 alpha = new SkMemoryStream(alphaRowBytes * srcRect.height()); | |
134 uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase(); | |
135 int offset1 = srcRect.fLeft % 8; | |
136 int offset2 = 8 - offset1; | |
137 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | |
138 uint8_t* src = bitmap.getAddr1(0, y); | |
139 // This may read up to one byte after src, but the potentially | |
140 // invalid bits are never used for computation. | |
141 for (int x = srcRect.fLeft; x < srcRect.fRight; x += 8) { | |
142 if (offset1) { | |
143 alphaDst[0] = src[x / 8] << offset1 | | |
144 src[x / 8 + 1] >> offset2; | |
145 } else { | |
146 alphaDst[0] = src[x / 8]; | |
147 } | |
148 if (x + 7 < srcRect.fRight && alphaDst[0] != 0xFF) { | |
149 hasAlpha = true; | |
150 } | |
151 if (x + 7 < srcRect.fRight && alphaDst[0]) { | |
152 isTransparent = false; | |
153 } | |
154 alphaDst++; | |
155 } | |
156 // Calculate the mask of bits we're interested in within the | |
157 // last byte of alphaDst. | |
158 // width mod 8 == 1 -> 0x80 ... width mod 8 == 7 -> 0xFE | |
159 uint8_t mask = ~((1 << (8 - (srcRect.width() % 8))) - 1); | |
160 if (srcRect.width() % 8 && (alphaDst[-1] & mask) != mask) { | |
161 hasAlpha = true; | |
162 } | |
163 if (srcRect.width() % 8 && (alphaDst[-1] & mask)) { | |
164 isTransparent = false; | |
165 } | |
166 } | |
167 break; | |
168 } | |
169 case SkBitmap::kA8_Config: { | |
170 isTransparent = true; | |
171 image = new SkMemoryStream(1); | |
172 ((uint8_t*)image->getMemoryBase())[0] = 0; | |
173 | |
174 const int alphaRowBytes = srcRect.width(); | |
175 alpha = new SkMemoryStream(alphaRowBytes * srcRect.height()); | |
176 uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase(); | |
177 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | |
178 uint8_t* src = bitmap.getAddr8(0, y); | |
179 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { | |
180 alphaDst[0] = src[x]; | |
181 if (alphaDst[0] != 0xFF) { | |
182 hasAlpha = true; | |
183 } | |
184 if (alphaDst[0]) { | |
185 isTransparent = false; | |
186 } | |
187 alphaDst++; | |
188 } | |
189 } | |
190 break; | |
191 } | |
192 default: | 336 default: |
193 SkASSERT(false); | 337 SkASSERT(false); |
194 } | 338 } |
195 bitmap.unlockPixels(); | 339 bitmap.unlockPixels(); |
196 | 340 |
197 if (isTransparent) { | 341 if (extractAlpha && (*isTransparent || !hasAlpha)) { |
198 SkSafeUnref(image); | 342 SkSafeUnref(stream); |
199 } else { | 343 return NULL; |
200 *imageData = image; | 344 } |
201 } | 345 return stream; |
202 | 346 } |
203 if (isTransparent || !hasAlpha) { | 347 |
204 SkSafeUnref(alpha); | 348 static SkPDFArray* makeIndexedColorSpace(SkColorTable* table) { |
vandebo (ex-Chrome)
2013/08/23 05:25:20
static functions should have hacker case: make_ind
ducky
2013/08/23 06:59:08
Missed updating this one... done.
| |
205 } else { | |
206 *alphaData = alpha; | |
207 } | |
208 } | |
209 | |
210 SkPDFArray* makeIndexedColorSpace(SkColorTable* table) { | |
211 SkPDFArray* result = new SkPDFArray(); | 349 SkPDFArray* result = new SkPDFArray(); |
212 result->reserve(4); | 350 result->reserve(4); |
213 result->appendName("Indexed"); | 351 result->appendName("Indexed"); |
214 result->appendName("DeviceRGB"); | 352 result->appendName("DeviceRGB"); |
215 result->appendInt(table->count() - 1); | 353 result->appendInt(table->count() - 1); |
216 | 354 |
217 // Potentially, this could be represented in fewer bytes with a stream. | 355 // Potentially, this could be represented in fewer bytes with a stream. |
218 // Max size as a string is 1.5k. | 356 // Max size as a string is 1.5k. |
219 SkString index; | 357 SkString index; |
220 for (int i = 0; i < table->count(); i++) { | 358 for (int i = 0; i < table->count(); i++) { |
221 char buf[3]; | 359 char buf[3]; |
222 SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]); | 360 SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]); |
223 buf[0] = SkGetPackedR32(color); | 361 buf[0] = SkGetPackedR32(color); |
224 buf[1] = SkGetPackedG32(color); | 362 buf[1] = SkGetPackedG32(color); |
225 buf[2] = SkGetPackedB32(color); | 363 buf[2] = SkGetPackedB32(color); |
226 index.append(buf, 3); | 364 index.append(buf, 3); |
227 } | 365 } |
228 result->append(new SkPDFString(index))->unref(); | 366 result->append(new SkPDFString(index))->unref(); |
229 return result; | 367 return result; |
230 } | 368 } |
231 | 369 |
232 }; // namespace | |
233 | |
234 // static | 370 // static |
235 SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap, | 371 SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap, |
236 const SkIRect& srcRect, | 372 const SkIRect& srcRect, |
237 EncodeToDCTStream encoder) { | 373 EncodeToDCTStream encoder) { |
238 if (bitmap.getConfig() == SkBitmap::kNo_Config) { | 374 if (bitmap.getConfig() == SkBitmap::kNo_Config) { |
239 return NULL; | 375 return NULL; |
240 } | 376 } |
241 | 377 |
242 SkStream* imageData = NULL; | 378 bool isTransparent; |
243 SkStream* alphaData = NULL; | 379 SkAutoTUnref<SkStream> alphaData( |
vandebo (ex-Chrome)
2013/08/23 05:45:10
nit: if bitmap.isOpaque() is set, then you can ski
ducky
2013/08/23 06:59:08
Done, along with cautionary comment.
Are there any
| |
244 extractImageData(bitmap, srcRect, &imageData, &alphaData); | 380 extract_image_data(bitmap, srcRect, true, &isTransparent)); |
245 SkAutoUnref unrefImageData(imageData); | 381 if (isTransparent) { |
246 SkAutoUnref unrefAlphaData(alphaData); | |
247 if (!imageData) { | |
248 SkASSERT(!alphaData); | |
249 return NULL; | 382 return NULL; |
250 } | 383 } |
251 | 384 |
252 SkPDFImage* image = | 385 SkPDFImage* image = SkNEW_ARGS(SkPDFImage, (bitmap, srcRect, encoder)); |
253 SkNEW_ARGS(SkPDFImage, (imageData, bitmap, srcRect, false, encoder)); | 386 if (alphaData.get() != NULL) { |
387 SkAutoTUnref<SkPDFImage> mask( | |
388 SkNEW_ARGS(SkPDFImage, (alphaData.get(), bitmap, srcRect))); | |
389 image->addSMask(mask); | |
390 } | |
254 | 391 |
255 if (alphaData != NULL) { | |
256 // Don't try to use DCT compression with alpha because alpha is small | |
257 // anyway and it could lead to artifacts. | |
258 image->addSMask(SkNEW_ARGS(SkPDFImage, (alphaData, bitmap, srcRect, true , NULL)))->unref(); | |
259 } | |
260 return image; | 392 return image; |
261 } | 393 } |
262 | 394 |
263 SkPDFImage::~SkPDFImage() { | 395 SkPDFImage::~SkPDFImage() { |
264 fResources.unrefAll(); | 396 fResources.unrefAll(); |
265 } | 397 } |
266 | 398 |
267 SkPDFImage* SkPDFImage::addSMask(SkPDFImage* mask) { | 399 SkPDFImage* SkPDFImage::addSMask(SkPDFImage* mask) { |
268 fResources.push(mask); | 400 fResources.push(mask); |
269 mask->ref(); | 401 mask->ref(); |
270 insert("SMask", new SkPDFObjRef(mask))->unref(); | 402 insert("SMask", new SkPDFObjRef(mask))->unref(); |
271 return mask; | 403 return mask; |
272 } | 404 } |
273 | 405 |
274 void SkPDFImage::getResources(const SkTSet<SkPDFObject*>& knownResourceObjects, | 406 void SkPDFImage::getResources(const SkTSet<SkPDFObject*>& knownResourceObjects, |
275 SkTSet<SkPDFObject*>* newResourceObjects) { | 407 SkTSet<SkPDFObject*>* newResourceObjects) { |
276 GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects); | 408 GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects); |
277 } | 409 } |
278 | 410 |
279 SkPDFImage::SkPDFImage(SkStream* imageData, | 411 SkPDFImage::SkPDFImage(const SkBitmap& bitmap, |
280 const SkBitmap& bitmap, | |
281 const SkIRect& srcRect, | 412 const SkIRect& srcRect, |
282 bool doingAlpha, | |
283 EncodeToDCTStream encoder) | 413 EncodeToDCTStream encoder) |
284 : SkPDFImageStream(imageData, bitmap, srcRect, encoder) { | 414 : fBitmap(bitmap), |
285 SkBitmap::Config config = bitmap.getConfig(); | 415 fSrcRect(srcRect), |
286 bool alphaOnly = (config == SkBitmap::kA1_Config || | 416 fEncoder(encoder), |
287 config == SkBitmap::kA8_Config); | 417 fStreamValid(false) { |
418 initImageParams(false); | |
419 } | |
420 | |
421 SkPDFImage::SkPDFImage(SkStream* stream, const SkBitmap& bitmap, | |
vandebo (ex-Chrome)
2013/08/23 05:25:20
This constructor is effectively the same as the pr
ducky
2013/08/23 06:59:08
Done - it is cleaner in many regards. I've clarifi
| |
422 const SkIRect& srcRect) | |
423 : fBitmap(bitmap), | |
424 fSrcRect(srcRect), | |
425 fEncoder(NULL), | |
426 fStreamValid(true) { | |
427 setData(stream); | |
428 initImageParams(true); | |
429 } | |
430 | |
431 SkPDFImage::SkPDFImage(SkPDFImage& pdfImage) | |
432 : SkPDFStream(pdfImage), | |
433 fBitmap(pdfImage.fBitmap), | |
434 fSrcRect(pdfImage.fSrcRect), | |
435 fEncoder(pdfImage.fEncoder), | |
436 fStreamValid(pdfImage.fStreamValid) { | |
437 // Nothing to do here - the image params are already copied in SkPDFStream's | |
438 // constructor, and the bitmap will be regenerated and re-encoded in | |
vandebo (ex-Chrome)
2013/08/23 05:25:20
"...regenerated and re-encoded..." => encoded
ducky
2013/08/23 06:59:08
Done.
| |
439 // populate. | |
440 } | |
441 | |
442 void SkPDFImage::initImageParams(bool isAlpha) { | |
443 SkBitmap::Config config = fBitmap.getConfig(); | |
288 | 444 |
289 insertName("Type", "XObject"); | 445 insertName("Type", "XObject"); |
290 insertName("Subtype", "Image"); | 446 insertName("Subtype", "Image"); |
291 | 447 |
292 if (!doingAlpha && alphaOnly) { | 448 bool alphaOnly = (config == SkBitmap::kA1_Config || |
449 config == SkBitmap::kA8_Config); | |
450 | |
451 if (!isAlpha && alphaOnly) { | |
293 // For alpha only images, we stretch a single pixel of black for | 452 // For alpha only images, we stretch a single pixel of black for |
294 // the color/shape part. | 453 // the color/shape part. |
295 SkAutoTUnref<SkPDFInt> one(new SkPDFInt(1)); | 454 SkAutoTUnref<SkPDFInt> one(new SkPDFInt(1)); |
296 insert("Width", one.get()); | 455 insert("Width", one.get()); |
297 insert("Height", one.get()); | 456 insert("Height", one.get()); |
298 } else { | 457 } else { |
299 insertInt("Width", srcRect.width()); | 458 insertInt("Width", fSrcRect.width()); |
300 insertInt("Height", srcRect.height()); | 459 insertInt("Height", fSrcRect.height()); |
301 } | 460 } |
302 | 461 |
303 // if (!image mask) { | 462 if (isAlpha || alphaOnly) { |
304 if (doingAlpha || alphaOnly) { | |
305 insertName("ColorSpace", "DeviceGray"); | 463 insertName("ColorSpace", "DeviceGray"); |
306 } else if (config == SkBitmap::kIndex8_Config) { | 464 } else if (config == SkBitmap::kIndex8_Config) { |
307 SkAutoLockPixels alp(bitmap); | 465 SkAutoLockPixels alp(fBitmap); |
308 insert("ColorSpace", | 466 insert("ColorSpace", |
309 makeIndexedColorSpace(bitmap.getColorTable()))->unref(); | 467 makeIndexedColorSpace(fBitmap.getColorTable()))->unref(); |
310 } else { | 468 } else { |
311 insertName("ColorSpace", "DeviceRGB"); | 469 insertName("ColorSpace", "DeviceRGB"); |
312 } | 470 } |
313 // } | |
314 | 471 |
315 int bitsPerComp = 8; | 472 int bitsPerComp = 8; |
316 if (config == SkBitmap::kARGB_4444_Config) { | 473 if (config == SkBitmap::kARGB_4444_Config) { |
317 bitsPerComp = 4; | 474 bitsPerComp = 4; |
318 } else if (doingAlpha && config == SkBitmap::kA1_Config) { | 475 } else if (isAlpha && config == SkBitmap::kA1_Config) { |
319 bitsPerComp = 1; | 476 bitsPerComp = 1; |
320 } | 477 } |
321 insertInt("BitsPerComponent", bitsPerComp); | 478 insertInt("BitsPerComponent", bitsPerComp); |
322 | 479 |
323 if (config == SkBitmap::kRGB_565_Config) { | 480 if (config == SkBitmap::kRGB_565_Config) { |
481 SkASSERT(!isAlpha); | |
324 SkAutoTUnref<SkPDFInt> zeroVal(new SkPDFInt(0)); | 482 SkAutoTUnref<SkPDFInt> zeroVal(new SkPDFInt(0)); |
325 SkAutoTUnref<SkPDFScalar> scale5Val( | 483 SkAutoTUnref<SkPDFScalar> scale5Val( |
326 new SkPDFScalar(SkFloatToScalar(8.2258f))); // 255/2^5-1 | 484 new SkPDFScalar(SkFloatToScalar(8.2258f))); // 255/2^5-1 |
327 SkAutoTUnref<SkPDFScalar> scale6Val( | 485 SkAutoTUnref<SkPDFScalar> scale6Val( |
328 new SkPDFScalar(SkFloatToScalar(4.0476f))); // 255/2^6-1 | 486 new SkPDFScalar(SkFloatToScalar(4.0476f))); // 255/2^6-1 |
329 SkAutoTUnref<SkPDFArray> decodeValue(new SkPDFArray()); | 487 SkAutoTUnref<SkPDFArray> decodeValue(new SkPDFArray()); |
330 decodeValue->reserve(6); | 488 decodeValue->reserve(6); |
331 decodeValue->append(zeroVal.get()); | 489 decodeValue->append(zeroVal.get()); |
332 decodeValue->append(scale5Val.get()); | 490 decodeValue->append(scale5Val.get()); |
333 decodeValue->append(zeroVal.get()); | 491 decodeValue->append(zeroVal.get()); |
334 decodeValue->append(scale6Val.get()); | 492 decodeValue->append(scale6Val.get()); |
335 decodeValue->append(zeroVal.get()); | 493 decodeValue->append(zeroVal.get()); |
336 decodeValue->append(scale5Val.get()); | 494 decodeValue->append(scale5Val.get()); |
337 insert("Decode", decodeValue.get()); | 495 insert("Decode", decodeValue.get()); |
338 } | 496 } |
339 } | 497 } |
498 | |
499 bool SkPDFImage::populate(SkPDFCatalog* catalog) { | |
500 if (getState() == kUnused_State) { | |
501 // Initializing image data for the first time. | |
502 SkDynamicMemoryWStream dctCompressedWStream; | |
503 if (!skip_compression(catalog) && fEncoder && | |
504 get_uncompressed_size(fBitmap, fSrcRect) > 1 && | |
505 fEncoder(&dctCompressedWStream, fBitmap, fSrcRect) && | |
506 dctCompressedWStream.getOffset() < | |
507 get_uncompressed_size(fBitmap, fSrcRect)) { | |
508 SkAutoTUnref<SkData> data(dctCompressedWStream.copyToData()); | |
509 SkAutoTUnref<SkStream> stream(SkNEW_ARGS(SkMemoryStream, (data))); | |
510 setData(stream.get()); | |
511 | |
512 insertName("Filter", "DCTDecode"); | |
513 insertInt("ColorTransform", kNoColorTransform); | |
514 insertInt("Length", getData()->getLength()); | |
515 setState(kCompressed_State); | |
516 return true; | |
517 } | |
518 // Fallback method | |
519 if (!fStreamValid) { | |
vandebo (ex-Chrome)
2013/08/23 05:25:20
Should this be a debug check? You always expect t
ducky
2013/08/23 06:59:08
No - when the alpha channel is generated, the stre
| |
520 bool dummy; | |
vandebo (ex-Chrome)
2013/08/23 05:25:20
dummy -> no_used, but generally I don't think you
ducky
2013/08/23 06:59:08
Done. Updated code and comments to reflect that th
vandebo (ex-Chrome)
2013/08/23 15:45:35
It should alway be able to be NULL.
ducky
2013/08/23 17:59:50
Done.
| |
521 SkAutoTUnref<SkStream> stream( | |
522 extract_image_data(fBitmap, fSrcRect, false, &dummy)); | |
523 setData(stream); | |
524 fStreamValid = true; | |
525 } | |
526 return INHERITED::populate(catalog); | |
527 } else if (getState() == kNoCompression_State && | |
528 !skip_compression(catalog) && | |
529 (SkFlate::HaveFlate() || fEncoder)) { | |
530 // Compression has not been requested when the stream was first created, | |
531 // but the new catalog wants it compressed. | |
532 if (!getSubstitute()) { | |
533 SkPDFStream* substitute = SkNEW_ARGS(SkPDFImage, (*this)); | |
534 setSubstitute(substitute); | |
535 catalog->setSubstitute(this, substitute); | |
536 } | |
537 return false; | |
538 } | |
539 return true; | |
540 } | |
OLD | NEW |