|
1 /* |
|
2 * Copyright 2010 The Android Open Source Project |
|
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 "SkPDFImage.h" |
|
9 |
|
10 #include "SkBitmap.h" |
|
11 #include "SkColor.h" |
|
12 #include "SkColorPriv.h" |
|
13 #include "SkData.h" |
|
14 #include "SkFlate.h" |
|
15 #include "SkPDFCatalog.h" |
|
16 #include "SkRect.h" |
|
17 #include "SkStream.h" |
|
18 #include "SkString.h" |
|
19 #include "SkUnPreMultiply.h" |
|
20 |
|
21 static const int kNoColorTransform = 0; |
|
22 |
|
23 static bool skip_compression(SkPDFCatalog* catalog) { |
|
24 return SkToBool(catalog->getDocumentFlags() & |
|
25 SkPDFDocument::kFavorSpeedOverSize_Flags); |
|
26 } |
|
27 |
|
28 static size_t get_uncompressed_size(const SkBitmap& bitmap, |
|
29 const SkIRect& srcRect) { |
|
30 switch (bitmap.config()) { |
|
31 case SkBitmap::kIndex8_Config: |
|
32 return srcRect.width() * srcRect.height(); |
|
33 case SkBitmap::kARGB_4444_Config: |
|
34 return ((srcRect.width() * 3 + 1) / 2) * srcRect.height(); |
|
35 case SkBitmap::kRGB_565_Config: |
|
36 return srcRect.width() * 3 * srcRect.height(); |
|
37 case SkBitmap::kARGB_8888_Config: |
|
38 return srcRect.width() * 3 * srcRect.height(); |
|
39 case SkBitmap::kA8_Config: |
|
40 return 1; |
|
41 default: |
|
42 SkASSERT(false); |
|
43 return 0; |
|
44 } |
|
45 } |
|
46 |
|
47 static SkStream* extract_index8_image(const SkBitmap& bitmap, |
|
48 const SkIRect& srcRect) { |
|
49 const int rowBytes = srcRect.width(); |
|
50 SkStream* stream = SkNEW_ARGS(SkMemoryStream, |
|
51 (get_uncompressed_size(bitmap, srcRect))); |
|
52 uint8_t* dst = (uint8_t*)stream->getMemoryBase(); |
|
53 |
|
54 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { |
|
55 memcpy(dst, bitmap.getAddr8(srcRect.fLeft, y), rowBytes); |
|
56 dst += rowBytes; |
|
57 } |
|
58 return stream; |
|
59 } |
|
60 |
|
61 static SkStream* extract_argb4444_data(const SkBitmap& bitmap, |
|
62 const SkIRect& srcRect, |
|
63 bool extractAlpha, |
|
64 bool* isOpaque, |
|
65 bool* isTransparent) { |
|
66 SkStream* stream; |
|
67 uint8_t* dst = NULL; |
|
68 if (extractAlpha) { |
|
69 const int alphaRowBytes = (srcRect.width() + 1) / 2; |
|
70 stream = SkNEW_ARGS(SkMemoryStream, |
|
71 (alphaRowBytes * srcRect.height())); |
|
72 } else { |
|
73 stream = SkNEW_ARGS(SkMemoryStream, |
|
74 (get_uncompressed_size(bitmap, srcRect))); |
|
75 } |
|
76 dst = (uint8_t*)stream->getMemoryBase(); |
|
77 |
|
78 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { |
|
79 uint16_t* src = bitmap.getAddr16(0, y); |
|
80 int x; |
|
81 for (x = srcRect.fLeft; x + 1 < srcRect.fRight; x += 2) { |
|
82 if (extractAlpha) { |
|
83 dst[0] = (SkGetPackedA4444(src[x]) << 4) | |
|
84 SkGetPackedA4444(src[x + 1]); |
|
85 *isOpaque &= dst[0] == SK_AlphaOPAQUE; |
|
86 *isTransparent &= dst[0] == SK_AlphaTRANSPARENT; |
|
87 dst++; |
|
88 } else { |
|
89 dst[0] = (SkGetPackedR4444(src[x]) << 4) | |
|
90 SkGetPackedG4444(src[x]); |
|
91 dst[1] = (SkGetPackedB4444(src[x]) << 4) | |
|
92 SkGetPackedR4444(src[x + 1]); |
|
93 dst[2] = (SkGetPackedG4444(src[x + 1]) << 4) | |
|
94 SkGetPackedB4444(src[x + 1]); |
|
95 dst += 3; |
|
96 } |
|
97 } |
|
98 if (srcRect.width() & 1) { |
|
99 if (extractAlpha) { |
|
100 dst[0] = (SkGetPackedA4444(src[x]) << 4); |
|
101 *isOpaque &= dst[0] == (SK_AlphaOPAQUE & 0xF0); |
|
102 *isTransparent &= dst[0] == (SK_AlphaTRANSPARENT & 0xF0); |
|
103 dst++; |
|
104 |
|
105 } else { |
|
106 dst[0] = (SkGetPackedR4444(src[x]) << 4) | |
|
107 SkGetPackedG4444(src[x]); |
|
108 dst[1] = (SkGetPackedB4444(src[x]) << 4); |
|
109 dst += 2; |
|
110 } |
|
111 } |
|
112 } |
|
113 return stream; |
|
114 } |
|
115 |
|
116 static SkStream* extract_rgb565_image(const SkBitmap& bitmap, |
|
117 const SkIRect& srcRect) { |
|
118 SkStream* stream = SkNEW_ARGS(SkMemoryStream, |
|
119 (get_uncompressed_size(bitmap, |
|
120 srcRect))); |
|
121 uint8_t* dst = (uint8_t*)stream->getMemoryBase(); |
|
122 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { |
|
123 uint16_t* src = bitmap.getAddr16(0, y); |
|
124 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { |
|
125 dst[0] = SkGetPackedR16(src[x]); |
|
126 dst[1] = SkGetPackedG16(src[x]); |
|
127 dst[2] = SkGetPackedB16(src[x]); |
|
128 dst += 3; |
|
129 } |
|
130 } |
|
131 return stream; |
|
132 } |
|
133 |
|
134 static SkStream* extract_argb8888_data(const SkBitmap& bitmap, |
|
135 const SkIRect& srcRect, |
|
136 bool extractAlpha, |
|
137 bool* isOpaque, |
|
138 bool* isTransparent) { |
|
139 SkStream* stream; |
|
140 if (extractAlpha) { |
|
141 stream = SkNEW_ARGS(SkMemoryStream, |
|
142 (srcRect.width() * srcRect.height())); |
|
143 } else { |
|
144 stream = SkNEW_ARGS(SkMemoryStream, |
|
145 (get_uncompressed_size(bitmap, srcRect))); |
|
146 } |
|
147 uint8_t* dst = (uint8_t*)stream->getMemoryBase(); |
|
148 |
|
149 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { |
|
150 uint32_t* src = bitmap.getAddr32(0, y); |
|
151 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { |
|
152 if (extractAlpha) { |
|
153 dst[0] = SkGetPackedA32(src[x]); |
|
154 *isOpaque &= dst[0] == SK_AlphaOPAQUE; |
|
155 *isTransparent &= dst[0] == SK_AlphaTRANSPARENT; |
|
156 dst++; |
|
157 } else { |
|
158 dst[0] = SkGetPackedR32(src[x]); |
|
159 dst[1] = SkGetPackedG32(src[x]); |
|
160 dst[2] = SkGetPackedB32(src[x]); |
|
161 dst += 3; |
|
162 } |
|
163 } |
|
164 } |
|
165 return stream; |
|
166 } |
|
167 |
|
168 static SkStream* extract_a8_alpha(const SkBitmap& bitmap, |
|
169 const SkIRect& srcRect, |
|
170 bool* isOpaque, |
|
171 bool* isTransparent) { |
|
172 const int alphaRowBytes = srcRect.width(); |
|
173 SkStream* stream = SkNEW_ARGS(SkMemoryStream, |
|
174 (alphaRowBytes * srcRect.height())); |
|
175 uint8_t* alphaDst = (uint8_t*)stream->getMemoryBase(); |
|
176 |
|
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 *isOpaque &= alphaDst[0] == SK_AlphaOPAQUE; |
|
182 *isTransparent &= alphaDst[0] == SK_AlphaTRANSPARENT; |
|
183 alphaDst++; |
|
184 } |
|
185 } |
|
186 return stream; |
|
187 } |
|
188 |
|
189 static SkStream* create_black_image() { |
|
190 SkStream* stream = SkNEW_ARGS(SkMemoryStream, (1)); |
|
191 ((uint8_t*)stream->getMemoryBase())[0] = 0; |
|
192 return stream; |
|
193 } |
|
194 |
|
195 /** |
|
196 * Extract either the color or image data from a SkBitmap into a SkStream. |
|
197 * @param bitmap Bitmap to extract data from. |
|
198 * @param srcRect Region in the bitmap to extract. |
|
199 * @param extractAlpha Set to true to extract the alpha data or false to |
|
200 * extract the color data. |
|
201 * @param isTransparent Pointer to a bool to output whether the alpha is |
|
202 * completely transparent. May be NULL. Only valid when |
|
203 * extractAlpha == true. |
|
204 * @return Unencoded image data, or NULL if either data was not |
|
205 * available or alpha data was requested but the image was |
|
206 * entirely transparent or opaque. |
|
207 */ |
|
208 static SkStream* extract_image_data(const SkBitmap& bitmap, |
|
209 const SkIRect& srcRect, |
|
210 bool extractAlpha, bool* isTransparent) { |
|
211 SkBitmap::Config config = bitmap.config(); |
|
212 if (extractAlpha && (config == SkBitmap::kIndex8_Config || |
|
213 config == SkBitmap::kRGB_565_Config)) { |
|
214 if (isTransparent != NULL) { |
|
215 *isTransparent = false; |
|
216 } |
|
217 return NULL; |
|
218 } |
|
219 bool isOpaque = true; |
|
220 bool transparent = extractAlpha; |
|
221 SkStream* stream = NULL; |
|
222 |
|
223 bitmap.lockPixels(); |
|
224 switch (config) { |
|
225 case SkBitmap::kIndex8_Config: |
|
226 if (!extractAlpha) { |
|
227 stream = extract_index8_image(bitmap, srcRect); |
|
228 } |
|
229 break; |
|
230 case SkBitmap::kARGB_4444_Config: |
|
231 stream = extract_argb4444_data(bitmap, srcRect, extractAlpha, |
|
232 &isOpaque, &transparent); |
|
233 break; |
|
234 case SkBitmap::kRGB_565_Config: |
|
235 if (!extractAlpha) { |
|
236 stream = extract_rgb565_image(bitmap, srcRect); |
|
237 } |
|
238 break; |
|
239 case SkBitmap::kARGB_8888_Config: |
|
240 stream = extract_argb8888_data(bitmap, srcRect, extractAlpha, |
|
241 &isOpaque, &transparent); |
|
242 break; |
|
243 case SkBitmap::kA8_Config: |
|
244 if (!extractAlpha) { |
|
245 stream = create_black_image(); |
|
246 } else { |
|
247 stream = extract_a8_alpha(bitmap, srcRect, |
|
248 &isOpaque, &transparent); |
|
249 } |
|
250 break; |
|
251 default: |
|
252 SkASSERT(false); |
|
253 } |
|
254 bitmap.unlockPixels(); |
|
255 |
|
256 if (isTransparent != NULL) { |
|
257 *isTransparent = transparent; |
|
258 } |
|
259 if (extractAlpha && (transparent || isOpaque)) { |
|
260 SkSafeUnref(stream); |
|
261 return NULL; |
|
262 } |
|
263 return stream; |
|
264 } |
|
265 |
|
266 static SkPDFArray* make_indexed_color_space(SkColorTable* table) { |
|
267 SkPDFArray* result = new SkPDFArray(); |
|
268 result->reserve(4); |
|
269 result->appendName("Indexed"); |
|
270 result->appendName("DeviceRGB"); |
|
271 result->appendInt(table->count() - 1); |
|
272 |
|
273 // Potentially, this could be represented in fewer bytes with a stream. |
|
274 // Max size as a string is 1.5k. |
|
275 SkString index; |
|
276 for (int i = 0; i < table->count(); i++) { |
|
277 char buf[3]; |
|
278 SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]); |
|
279 buf[0] = SkGetPackedR32(color); |
|
280 buf[1] = SkGetPackedG32(color); |
|
281 buf[2] = SkGetPackedB32(color); |
|
282 index.append(buf, 3); |
|
283 } |
|
284 result->append(new SkPDFString(index))->unref(); |
|
285 return result; |
|
286 } |
|
287 |
|
288 /** |
|
289 * Removes the alpha component of an ARGB color (including unpremultiply) while |
|
290 * keeping the output in the same format as the input. |
|
291 */ |
|
292 static uint32_t remove_alpha_argb8888(uint32_t pmColor) { |
|
293 SkColor color = SkUnPreMultiply::PMColorToColor(pmColor); |
|
294 return SkPackARGB32NoCheck(SK_AlphaOPAQUE, |
|
295 SkColorGetR(color), |
|
296 SkColorGetG(color), |
|
297 SkColorGetB(color)); |
|
298 } |
|
299 |
|
300 static uint16_t remove_alpha_argb4444(uint16_t pmColor) { |
|
301 return SkPixel32ToPixel4444( |
|
302 remove_alpha_argb8888(SkPixel4444ToPixel32(pmColor))); |
|
303 } |
|
304 |
|
305 static uint32_t get_argb8888_neighbor_avg_color(const SkBitmap& bitmap, |
|
306 int xOrig, int yOrig) { |
|
307 uint8_t count = 0; |
|
308 uint16_t r = 0; |
|
309 uint16_t g = 0; |
|
310 uint16_t b = 0; |
|
311 |
|
312 for (int y = yOrig - 1; y <= yOrig + 1; y++) { |
|
313 if (y < 0 || y >= bitmap.height()) { |
|
314 continue; |
|
315 } |
|
316 uint32_t* src = bitmap.getAddr32(0, y); |
|
317 for (int x = xOrig - 1; x <= xOrig + 1; x++) { |
|
318 if (x < 0 || x >= bitmap.width()) { |
|
319 continue; |
|
320 } |
|
321 if (SkGetPackedA32(src[x]) != SK_AlphaTRANSPARENT) { |
|
322 uint32_t color = remove_alpha_argb8888(src[x]); |
|
323 r += SkGetPackedR32(color); |
|
324 g += SkGetPackedG32(color); |
|
325 b += SkGetPackedB32(color); |
|
326 count++; |
|
327 } |
|
328 } |
|
329 } |
|
330 |
|
331 if (count == 0) { |
|
332 return SkPackARGB32NoCheck(SK_AlphaOPAQUE, 0, 0, 0); |
|
333 } else { |
|
334 return SkPackARGB32NoCheck(SK_AlphaOPAQUE, |
|
335 r / count, g / count, b / count); |
|
336 } |
|
337 } |
|
338 |
|
339 static uint16_t get_argb4444_neighbor_avg_color(const SkBitmap& bitmap, |
|
340 int xOrig, int yOrig) { |
|
341 uint8_t count = 0; |
|
342 uint8_t r = 0; |
|
343 uint8_t g = 0; |
|
344 uint8_t b = 0; |
|
345 |
|
346 for (int y = yOrig - 1; y <= yOrig + 1; y++) { |
|
347 if (y < 0 || y >= bitmap.height()) { |
|
348 continue; |
|
349 } |
|
350 uint16_t* src = bitmap.getAddr16(0, y); |
|
351 for (int x = xOrig - 1; x <= xOrig + 1; x++) { |
|
352 if (x < 0 || x >= bitmap.width()) { |
|
353 continue; |
|
354 } |
|
355 if ((SkGetPackedA4444(src[x]) & 0x0F) != SK_AlphaTRANSPARENT) { |
|
356 uint16_t color = remove_alpha_argb4444(src[x]); |
|
357 r += SkGetPackedR4444(color); |
|
358 g += SkGetPackedG4444(color); |
|
359 b += SkGetPackedB4444(color); |
|
360 count++; |
|
361 } |
|
362 } |
|
363 } |
|
364 |
|
365 if (count == 0) { |
|
366 return SkPackARGB4444(SK_AlphaOPAQUE & 0x0F, 0, 0, 0); |
|
367 } else { |
|
368 return SkPackARGB4444(SK_AlphaOPAQUE & 0x0F, |
|
369 r / count, g / count, b / count); |
|
370 } |
|
371 } |
|
372 |
|
373 static SkBitmap unpremultiply_bitmap(const SkBitmap& bitmap, |
|
374 const SkIRect& srcRect) { |
|
375 SkBitmap outBitmap; |
|
376 outBitmap.setConfig(bitmap.config(), srcRect.width(), srcRect.height()); |
|
377 outBitmap.allocPixels(); |
|
378 int dstRow = 0; |
|
379 |
|
380 outBitmap.lockPixels(); |
|
381 bitmap.lockPixels(); |
|
382 switch (bitmap.config()) { |
|
383 case SkBitmap::kARGB_4444_Config: { |
|
384 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { |
|
385 uint16_t* dst = outBitmap.getAddr16(0, dstRow); |
|
386 uint16_t* src = bitmap.getAddr16(0, y); |
|
387 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { |
|
388 uint8_t a = SkGetPackedA4444(src[x]); |
|
389 // It is necessary to average the color component of |
|
390 // transparent pixels with their surrounding neighbors |
|
391 // since the PDF renderer may separately re-sample the |
|
392 // alpha and color channels when the image is not |
|
393 // displayed at its native resolution. Since an alpha of |
|
394 // zero gives no information about the color component, |
|
395 // the pathological case is a white image with sharp |
|
396 // transparency bounds - the color channel goes to black, |
|
397 // and the should-be-transparent pixels are rendered |
|
398 // as grey because of the separate soft mask and color |
|
399 // resizing. |
|
400 if (a == (SK_AlphaTRANSPARENT & 0x0F)) { |
|
401 *dst = get_argb4444_neighbor_avg_color(bitmap, x, y); |
|
402 } else { |
|
403 *dst = remove_alpha_argb4444(src[x]); |
|
404 } |
|
405 dst++; |
|
406 } |
|
407 dstRow++; |
|
408 } |
|
409 break; |
|
410 } |
|
411 case SkBitmap::kARGB_8888_Config: { |
|
412 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { |
|
413 uint32_t* dst = outBitmap.getAddr32(0, dstRow); |
|
414 uint32_t* src = bitmap.getAddr32(0, y); |
|
415 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { |
|
416 uint8_t a = SkGetPackedA32(src[x]); |
|
417 if (a == SK_AlphaTRANSPARENT) { |
|
418 *dst = get_argb8888_neighbor_avg_color(bitmap, x, y); |
|
419 } else { |
|
420 *dst = remove_alpha_argb8888(src[x]); |
|
421 } |
|
422 dst++; |
|
423 } |
|
424 dstRow++; |
|
425 } |
|
426 break; |
|
427 } |
|
428 default: |
|
429 SkASSERT(false); |
|
430 } |
|
431 bitmap.unlockPixels(); |
|
432 outBitmap.unlockPixels(); |
|
433 |
|
434 outBitmap.setImmutable(); |
|
435 |
|
436 return outBitmap; |
|
437 } |
|
438 |
|
439 // static |
|
440 SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap, |
|
441 const SkIRect& srcRect, |
|
442 SkPicture::EncodeBitmap encoder) { |
|
443 if (bitmap.config() == SkBitmap::kNo_Config) { |
|
444 return NULL; |
|
445 } |
|
446 |
|
447 bool isTransparent = false; |
|
448 SkAutoTUnref<SkStream> alphaData; |
|
449 if (!bitmap.isOpaque()) { |
|
450 // Note that isOpaque is not guaranteed to return false for bitmaps |
|
451 // with alpha support but a completely opaque alpha channel, |
|
452 // so alphaData may still be NULL if we have a completely opaque |
|
453 // (or transparent) bitmap. |
|
454 alphaData.reset( |
|
455 extract_image_data(bitmap, srcRect, true, &isTransparent)); |
|
456 } |
|
457 if (isTransparent) { |
|
458 return NULL; |
|
459 } |
|
460 |
|
461 SkPDFImage* image; |
|
462 SkBitmap::Config config = bitmap.config(); |
|
463 if (alphaData.get() != NULL && (config == SkBitmap::kARGB_8888_Config || |
|
464 config == SkBitmap::kARGB_4444_Config)) { |
|
465 SkBitmap unpremulBitmap = unpremultiply_bitmap(bitmap, srcRect); |
|
466 image = SkNEW_ARGS(SkPDFImage, (NULL, unpremulBitmap, false, |
|
467 SkIRect::MakeWH(srcRect.width(), srcRect.height()), |
|
468 encoder)); |
|
469 } else { |
|
470 image = SkNEW_ARGS(SkPDFImage, (NULL, bitmap, false, srcRect, encoder)); |
|
471 } |
|
472 if (alphaData.get() != NULL) { |
|
473 SkAutoTUnref<SkPDFImage> mask( |
|
474 SkNEW_ARGS(SkPDFImage, (alphaData.get(), bitmap, |
|
475 true, srcRect, NULL))); |
|
476 image->addSMask(mask); |
|
477 } |
|
478 |
|
479 return image; |
|
480 } |
|
481 |
|
482 SkPDFImage::~SkPDFImage() { |
|
483 fResources.unrefAll(); |
|
484 } |
|
485 |
|
486 SkPDFImage* SkPDFImage::addSMask(SkPDFImage* mask) { |
|
487 fResources.push(mask); |
|
488 mask->ref(); |
|
489 insert("SMask", new SkPDFObjRef(mask))->unref(); |
|
490 return mask; |
|
491 } |
|
492 |
|
493 void SkPDFImage::getResources(const SkTSet<SkPDFObject*>& knownResourceObjects, |
|
494 SkTSet<SkPDFObject*>* newResourceObjects) { |
|
495 GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects); |
|
496 } |
|
497 |
|
498 SkPDFImage::SkPDFImage(SkStream* stream, |
|
499 const SkBitmap& bitmap, |
|
500 bool isAlpha, |
|
501 const SkIRect& srcRect, |
|
502 SkPicture::EncodeBitmap encoder) |
|
503 : fIsAlpha(isAlpha), |
|
504 fSrcRect(srcRect), |
|
505 fEncoder(encoder) { |
|
506 |
|
507 if (bitmap.isImmutable()) { |
|
508 fBitmap = bitmap; |
|
509 } else { |
|
510 bitmap.deepCopyTo(&fBitmap); |
|
511 fBitmap.setImmutable(); |
|
512 } |
|
513 |
|
514 if (stream != NULL) { |
|
515 setData(stream); |
|
516 fStreamValid = true; |
|
517 } else { |
|
518 fStreamValid = false; |
|
519 } |
|
520 |
|
521 SkBitmap::Config config = fBitmap.config(); |
|
522 |
|
523 insertName("Type", "XObject"); |
|
524 insertName("Subtype", "Image"); |
|
525 |
|
526 bool alphaOnly = (config == SkBitmap::kA8_Config); |
|
527 |
|
528 if (!isAlpha && alphaOnly) { |
|
529 // For alpha only images, we stretch a single pixel of black for |
|
530 // the color/shape part. |
|
531 SkAutoTUnref<SkPDFInt> one(new SkPDFInt(1)); |
|
532 insert("Width", one.get()); |
|
533 insert("Height", one.get()); |
|
534 } else { |
|
535 insertInt("Width", fSrcRect.width()); |
|
536 insertInt("Height", fSrcRect.height()); |
|
537 } |
|
538 |
|
539 if (isAlpha || alphaOnly) { |
|
540 insertName("ColorSpace", "DeviceGray"); |
|
541 } else if (config == SkBitmap::kIndex8_Config) { |
|
542 SkAutoLockPixels alp(fBitmap); |
|
543 insert("ColorSpace", |
|
544 make_indexed_color_space(fBitmap.getColorTable()))->unref(); |
|
545 } else { |
|
546 insertName("ColorSpace", "DeviceRGB"); |
|
547 } |
|
548 |
|
549 int bitsPerComp = 8; |
|
550 if (config == SkBitmap::kARGB_4444_Config) { |
|
551 bitsPerComp = 4; |
|
552 } |
|
553 insertInt("BitsPerComponent", bitsPerComp); |
|
554 |
|
555 if (config == SkBitmap::kRGB_565_Config) { |
|
556 SkASSERT(!isAlpha); |
|
557 SkAutoTUnref<SkPDFInt> zeroVal(new SkPDFInt(0)); |
|
558 SkAutoTUnref<SkPDFScalar> scale5Val( |
|
559 new SkPDFScalar(8.2258f)); // 255/2^5-1 |
|
560 SkAutoTUnref<SkPDFScalar> scale6Val( |
|
561 new SkPDFScalar(4.0476f)); // 255/2^6-1 |
|
562 SkAutoTUnref<SkPDFArray> decodeValue(new SkPDFArray()); |
|
563 decodeValue->reserve(6); |
|
564 decodeValue->append(zeroVal.get()); |
|
565 decodeValue->append(scale5Val.get()); |
|
566 decodeValue->append(zeroVal.get()); |
|
567 decodeValue->append(scale6Val.get()); |
|
568 decodeValue->append(zeroVal.get()); |
|
569 decodeValue->append(scale5Val.get()); |
|
570 insert("Decode", decodeValue.get()); |
|
571 } |
|
572 } |
|
573 |
|
574 SkPDFImage::SkPDFImage(SkPDFImage& pdfImage) |
|
575 : SkPDFStream(pdfImage), |
|
576 fBitmap(pdfImage.fBitmap), |
|
577 fIsAlpha(pdfImage.fIsAlpha), |
|
578 fSrcRect(pdfImage.fSrcRect), |
|
579 fEncoder(pdfImage.fEncoder), |
|
580 fStreamValid(pdfImage.fStreamValid) { |
|
581 // Nothing to do here - the image params are already copied in SkPDFStream's |
|
582 // constructor, and the bitmap will be regenerated and encoded in |
|
583 // populate. |
|
584 } |
|
585 |
|
586 bool SkPDFImage::populate(SkPDFCatalog* catalog) { |
|
587 if (getState() == kUnused_State) { |
|
588 // Initializing image data for the first time. |
|
589 SkDynamicMemoryWStream dctCompressedWStream; |
|
590 if (!skip_compression(catalog) && fEncoder && |
|
591 get_uncompressed_size(fBitmap, fSrcRect) > 1) { |
|
592 SkBitmap subset; |
|
593 // Extract subset |
|
594 if (!fBitmap.extractSubset(&subset, fSrcRect)) { |
|
595 // TODO(edisonn) It fails only for kA1_Config, if that is a |
|
596 // major concern we will fix it later, so far it is NYI. |
|
597 return false; |
|
598 } |
|
599 size_t pixelRefOffset = 0; |
|
600 SkAutoTUnref<SkData> data(fEncoder(&pixelRefOffset, subset)); |
|
601 if (data.get() && data->size() < get_uncompressed_size(fBitmap, |
|
602 fSrcRect)) { |
|
603 SkAutoTUnref<SkStream> stream(SkNEW_ARGS(SkMemoryStream, |
|
604 (data))); |
|
605 setData(stream.get()); |
|
606 |
|
607 insertName("Filter", "DCTDecode"); |
|
608 insertInt("ColorTransform", kNoColorTransform); |
|
609 insertInt("Length", getData()->getLength()); |
|
610 setState(kCompressed_State); |
|
611 return true; |
|
612 } |
|
613 } |
|
614 // Fallback method |
|
615 if (!fStreamValid) { |
|
616 SkAutoTUnref<SkStream> stream( |
|
617 extract_image_data(fBitmap, fSrcRect, fIsAlpha, NULL)); |
|
618 setData(stream); |
|
619 fStreamValid = true; |
|
620 } |
|
621 return INHERITED::populate(catalog); |
|
622 } else if (getState() == kNoCompression_State && |
|
623 !skip_compression(catalog) && |
|
624 (SkFlate::HaveFlate() || fEncoder)) { |
|
625 // Compression has not been requested when the stream was first created, |
|
626 // but the new catalog wants it compressed. |
|
627 if (!getSubstitute()) { |
|
628 SkPDFStream* substitute = SkNEW_ARGS(SkPDFImage, (*this)); |
|
629 setSubstitute(substitute); |
|
630 catalog->setSubstitute(this, substitute); |
|
631 } |
|
632 return false; |
|
633 } |
|
634 return true; |
|
635 } |