blob: a99c9fe3903cb438df878a2760768a90cd8a5abe [file] [log] [blame]
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2010 The Android Open Source Project
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +00006 */
7
8#include "SkPDFImage.h"
9
10#include "SkBitmap.h"
11#include "SkColor.h"
12#include "SkColorPriv.h"
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +000013#include "SkData.h"
14#include "SkFlate.h"
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000015#include "SkPDFCatalog.h"
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +000016#include "SkRect.h"
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000017#include "SkStream.h"
18#include "SkString.h"
19#include "SkUnPreMultiply.h"
20
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +000021static const int kNoColorTransform = 0;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +000022
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +000023static bool skip_compression(SkPDFCatalog* catalog) {
24 return SkToBool(catalog->getDocumentFlags() &
25 SkPDFDocument::kFavorSpeedOverSize_Flags);
26}
27
28static size_t get_uncompressed_size(const SkBitmap& bitmap,
29 const SkIRect& srcRect) {
reed@google.com44699382013-10-31 17:28:30 +000030 switch (bitmap.config()) {
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +000031 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();
reed@google.com72e78082013-11-25 20:54:56 +000039 case SkBitmap::kA1_Config:
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +000040 case SkBitmap::kA8_Config:
41 return 1;
42 default:
43 SkASSERT(false);
44 return 0;
45 }
46}
47
48static SkStream* extract_index8_image(const SkBitmap& bitmap,
49 const SkIRect& srcRect) {
50 const int rowBytes = srcRect.width();
51 SkStream* stream = SkNEW_ARGS(SkMemoryStream,
52 (get_uncompressed_size(bitmap, srcRect)));
53 uint8_t* dst = (uint8_t*)stream->getMemoryBase();
54
55 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
56 memcpy(dst, bitmap.getAddr8(srcRect.fLeft, y), rowBytes);
57 dst += rowBytes;
58 }
59 return stream;
60}
61
62static SkStream* extract_argb4444_data(const SkBitmap& bitmap,
63 const SkIRect& srcRect,
64 bool extractAlpha,
65 bool* isOpaque,
66 bool* isTransparent) {
67 SkStream* stream;
68 uint8_t* dst = NULL;
69 if (extractAlpha) {
70 const int alphaRowBytes = (srcRect.width() + 1) / 2;
71 stream = SkNEW_ARGS(SkMemoryStream,
72 (alphaRowBytes * srcRect.height()));
73 } else {
74 stream = SkNEW_ARGS(SkMemoryStream,
75 (get_uncompressed_size(bitmap, srcRect)));
76 }
77 dst = (uint8_t*)stream->getMemoryBase();
78
79 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
80 uint16_t* src = bitmap.getAddr16(0, y);
81 int x;
82 for (x = srcRect.fLeft; x + 1 < srcRect.fRight; x += 2) {
83 if (extractAlpha) {
84 dst[0] = (SkGetPackedA4444(src[x]) << 4) |
85 SkGetPackedA4444(src[x + 1]);
86 *isOpaque &= dst[0] == SK_AlphaOPAQUE;
87 *isTransparent &= dst[0] == SK_AlphaTRANSPARENT;
88 dst++;
89 } else {
90 dst[0] = (SkGetPackedR4444(src[x]) << 4) |
91 SkGetPackedG4444(src[x]);
92 dst[1] = (SkGetPackedB4444(src[x]) << 4) |
93 SkGetPackedR4444(src[x + 1]);
94 dst[2] = (SkGetPackedG4444(src[x + 1]) << 4) |
95 SkGetPackedB4444(src[x + 1]);
96 dst += 3;
97 }
98 }
99 if (srcRect.width() & 1) {
100 if (extractAlpha) {
101 dst[0] = (SkGetPackedA4444(src[x]) << 4);
102 *isOpaque &= dst[0] == (SK_AlphaOPAQUE & 0xF0);
103 *isTransparent &= dst[0] == (SK_AlphaTRANSPARENT & 0xF0);
104 dst++;
105
106 } else {
107 dst[0] = (SkGetPackedR4444(src[x]) << 4) |
108 SkGetPackedG4444(src[x]);
109 dst[1] = (SkGetPackedB4444(src[x]) << 4);
110 dst += 2;
111 }
112 }
113 }
114 return stream;
115}
116
117static SkStream* extract_rgb565_image(const SkBitmap& bitmap,
118 const SkIRect& srcRect) {
119 SkStream* stream = SkNEW_ARGS(SkMemoryStream,
120 (get_uncompressed_size(bitmap,
121 srcRect)));
122 uint8_t* dst = (uint8_t*)stream->getMemoryBase();
123 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
124 uint16_t* src = bitmap.getAddr16(0, y);
125 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
126 dst[0] = SkGetPackedR16(src[x]);
127 dst[1] = SkGetPackedG16(src[x]);
128 dst[2] = SkGetPackedB16(src[x]);
129 dst += 3;
130 }
131 }
132 return stream;
133}
134
135static SkStream* extract_argb8888_data(const SkBitmap& bitmap,
136 const SkIRect& srcRect,
137 bool extractAlpha,
138 bool* isOpaque,
139 bool* isTransparent) {
140 SkStream* stream;
141 if (extractAlpha) {
142 stream = SkNEW_ARGS(SkMemoryStream,
143 (srcRect.width() * srcRect.height()));
144 } else {
145 stream = SkNEW_ARGS(SkMemoryStream,
146 (get_uncompressed_size(bitmap, srcRect)));
147 }
148 uint8_t* dst = (uint8_t*)stream->getMemoryBase();
149
150 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
151 uint32_t* src = bitmap.getAddr32(0, y);
152 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
153 if (extractAlpha) {
154 dst[0] = SkGetPackedA32(src[x]);
155 *isOpaque &= dst[0] == SK_AlphaOPAQUE;
156 *isTransparent &= dst[0] == SK_AlphaTRANSPARENT;
157 dst++;
158 } else {
159 dst[0] = SkGetPackedR32(src[x]);
160 dst[1] = SkGetPackedG32(src[x]);
161 dst[2] = SkGetPackedB32(src[x]);
162 dst += 3;
163 }
164 }
165 }
166 return stream;
167}
168
reed@google.com72e78082013-11-25 20:54:56 +0000169static SkStream* extract_a1_alpha(const SkBitmap& bitmap,
170 const SkIRect& srcRect,
171 bool* isOpaque,
172 bool* isTransparent) {
173 const int alphaRowBytes = (srcRect.width() + 7) / 8;
174 SkStream* stream = SkNEW_ARGS(SkMemoryStream,
175 (alphaRowBytes * srcRect.height()));
176 uint8_t* alphaDst = (uint8_t*)stream->getMemoryBase();
177
178 int offset1 = srcRect.fLeft % 8;
179 int offset2 = 8 - offset1;
180
181 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
182 uint8_t* src = bitmap.getAddr1(0, y);
183 // This may read up to one byte after src, but the
184 // potentially invalid bits are never used for computation.
185 for (int x = srcRect.fLeft; x < srcRect.fRight; x += 8) {
186 if (offset1) {
187 alphaDst[0] = src[x / 8] << offset1 |
188 src[x / 8 + 1] >> offset2;
189 } else {
190 alphaDst[0] = src[x / 8];
191 }
192 if (x + 7 < srcRect.fRight) {
193 *isOpaque &= alphaDst[0] == SK_AlphaOPAQUE;
194 *isTransparent &= alphaDst[0] == SK_AlphaTRANSPARENT;
195 }
196 alphaDst++;
197 }
198 // Calculate the mask of bits we're interested in within the
199 // last byte of alphaDst.
200 // width mod 8 == 1 -> 0x80 ... width mod 8 == 7 -> 0xFE
201 uint8_t mask = ~((1 << (8 - (srcRect.width() % 8))) - 1);
202 if (srcRect.width() % 8) {
203 *isOpaque &= (alphaDst[-1] & mask) == (SK_AlphaOPAQUE & mask);
204 *isTransparent &=
205 (alphaDst[-1] & mask) == (SK_AlphaTRANSPARENT & mask);
206 }
207 }
208 return stream;
209}
210
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000211static SkStream* extract_a8_alpha(const SkBitmap& bitmap,
212 const SkIRect& srcRect,
213 bool* isOpaque,
214 bool* isTransparent) {
215 const int alphaRowBytes = srcRect.width();
216 SkStream* stream = SkNEW_ARGS(SkMemoryStream,
217 (alphaRowBytes * srcRect.height()));
218 uint8_t* alphaDst = (uint8_t*)stream->getMemoryBase();
219
220 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
221 uint8_t* src = bitmap.getAddr8(0, y);
222 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
223 alphaDst[0] = src[x];
224 *isOpaque &= alphaDst[0] == SK_AlphaOPAQUE;
225 *isTransparent &= alphaDst[0] == SK_AlphaTRANSPARENT;
226 alphaDst++;
227 }
228 }
229 return stream;
230}
231
232static SkStream* create_black_image() {
233 SkStream* stream = SkNEW_ARGS(SkMemoryStream, (1));
234 ((uint8_t*)stream->getMemoryBase())[0] = 0;
235 return stream;
236}
237
238/**
239 * Extract either the color or image data from a SkBitmap into a SkStream.
240 * @param bitmap Bitmap to extract data from.
241 * @param srcRect Region in the bitmap to extract.
242 * @param extractAlpha Set to true to extract the alpha data or false to
243 * extract the color data.
244 * @param isTransparent Pointer to a bool to output whether the alpha is
245 * completely transparent. May be NULL. Only valid when
246 * extractAlpha == true.
247 * @return Unencoded image data, or NULL if either data was not
248 * available or alpha data was requested but the image was
249 * entirely transparent or opaque.
250 */
251static SkStream* extract_image_data(const SkBitmap& bitmap,
252 const SkIRect& srcRect,
253 bool extractAlpha, bool* isTransparent) {
254 SkBitmap::Config config = bitmap.config();
255 if (extractAlpha && (config == SkBitmap::kIndex8_Config ||
256 config == SkBitmap::kRGB_565_Config)) {
257 if (isTransparent != NULL) {
258 *isTransparent = false;
259 }
260 return NULL;
261 }
262 bool isOpaque = true;
263 bool transparent = extractAlpha;
264 SkStream* stream = NULL;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000265
vandebo@chromium.orgad114952010-10-26 19:43:14 +0000266 bitmap.lockPixels();
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000267 switch (config) {
268 case SkBitmap::kIndex8_Config:
269 if (!extractAlpha) {
270 stream = extract_index8_image(bitmap, srcRect);
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000271 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000272 break;
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000273 case SkBitmap::kARGB_4444_Config:
274 stream = extract_argb4444_data(bitmap, srcRect, extractAlpha,
275 &isOpaque, &transparent);
276 break;
277 case SkBitmap::kRGB_565_Config:
278 if (!extractAlpha) {
279 stream = extract_rgb565_image(bitmap, srcRect);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000280 }
281 break;
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000282 case SkBitmap::kARGB_8888_Config:
283 stream = extract_argb8888_data(bitmap, srcRect, extractAlpha,
284 &isOpaque, &transparent);
285 break;
reed@google.com72e78082013-11-25 20:54:56 +0000286 case SkBitmap::kA1_Config:
287 if (!extractAlpha) {
288 stream = create_black_image();
289 } else {
290 stream = extract_a1_alpha(bitmap, srcRect,
291 &isOpaque, &transparent);
292 }
293 break;
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000294 case SkBitmap::kA8_Config:
295 if (!extractAlpha) {
296 stream = create_black_image();
297 } else {
298 stream = extract_a8_alpha(bitmap, srcRect,
299 &isOpaque, &transparent);
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000300 }
301 break;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000302 default:
303 SkASSERT(false);
304 }
vandebo@chromium.orgad114952010-10-26 19:43:14 +0000305 bitmap.unlockPixels();
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000306
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000307 if (isTransparent != NULL) {
308 *isTransparent = transparent;
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000309 }
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000310 if (extractAlpha && (transparent || isOpaque)) {
311 SkSafeUnref(stream);
312 return NULL;
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000313 }
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000314 return stream;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000315}
316
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000317static SkPDFArray* make_indexed_color_space(SkColorTable* table) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000318 SkPDFArray* result = new SkPDFArray();
319 result->reserve(4);
vandebo@chromium.org06f7f402011-07-20 18:39:20 +0000320 result->appendName("Indexed");
321 result->appendName("DeviceRGB");
322 result->appendInt(table->count() - 1);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000323
324 // Potentially, this could be represented in fewer bytes with a stream.
325 // Max size as a string is 1.5k.
326 SkString index;
327 for (int i = 0; i < table->count(); i++) {
328 char buf[3];
329 SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]);
330 buf[0] = SkGetPackedR32(color);
331 buf[1] = SkGetPackedG32(color);
332 buf[2] = SkGetPackedB32(color);
333 index.append(buf, 3);
334 }
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000335 result->append(new SkPDFString(index))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000336 return result;
337}
338
vandebo@chromium.orgece95c32013-10-24 15:20:00 +0000339/**
340 * Removes the alpha component of an ARGB color (including unpremultiply) while
341 * keeping the output in the same format as the input.
342 */
343static uint32_t remove_alpha_argb8888(uint32_t pmColor) {
344 SkColor color = SkUnPreMultiply::PMColorToColor(pmColor);
345 return SkPackARGB32NoCheck(SK_AlphaOPAQUE,
346 SkColorGetR(color),
347 SkColorGetG(color),
348 SkColorGetB(color));
349}
350
351static uint16_t remove_alpha_argb4444(uint16_t pmColor) {
352 return SkPixel32ToPixel4444(
353 remove_alpha_argb8888(SkPixel4444ToPixel32(pmColor)));
354}
355
356static uint32_t get_argb8888_neighbor_avg_color(const SkBitmap& bitmap,
357 int xOrig, int yOrig) {
358 uint8_t count = 0;
359 uint16_t r = 0;
360 uint16_t g = 0;
361 uint16_t b = 0;
362
363 for (int y = yOrig - 1; y <= yOrig + 1; y++) {
364 if (y < 0 || y >= bitmap.height()) {
365 continue;
366 }
367 uint32_t* src = bitmap.getAddr32(0, y);
368 for (int x = xOrig - 1; x <= xOrig + 1; x++) {
369 if (x < 0 || x >= bitmap.width()) {
370 continue;
371 }
372 if (SkGetPackedA32(src[x]) != SK_AlphaTRANSPARENT) {
373 uint32_t color = remove_alpha_argb8888(src[x]);
374 r += SkGetPackedR32(color);
375 g += SkGetPackedG32(color);
376 b += SkGetPackedB32(color);
377 count++;
378 }
379 }
380 }
381
382 if (count == 0) {
383 return SkPackARGB32NoCheck(SK_AlphaOPAQUE, 0, 0, 0);
384 } else {
385 return SkPackARGB32NoCheck(SK_AlphaOPAQUE,
386 r / count, g / count, b / count);
387 }
388}
389
390static uint16_t get_argb4444_neighbor_avg_color(const SkBitmap& bitmap,
391 int xOrig, int yOrig) {
392 uint8_t count = 0;
393 uint8_t r = 0;
394 uint8_t g = 0;
395 uint8_t b = 0;
396
397 for (int y = yOrig - 1; y <= yOrig + 1; y++) {
398 if (y < 0 || y >= bitmap.height()) {
399 continue;
400 }
401 uint16_t* src = bitmap.getAddr16(0, y);
402 for (int x = xOrig - 1; x <= xOrig + 1; x++) {
403 if (x < 0 || x >= bitmap.width()) {
404 continue;
405 }
406 if ((SkGetPackedA4444(src[x]) & 0x0F) != SK_AlphaTRANSPARENT) {
407 uint16_t color = remove_alpha_argb4444(src[x]);
408 r += SkGetPackedR4444(color);
409 g += SkGetPackedG4444(color);
410 b += SkGetPackedB4444(color);
411 count++;
412 }
413 }
414 }
415
416 if (count == 0) {
417 return SkPackARGB4444(SK_AlphaOPAQUE & 0x0F, 0, 0, 0);
418 } else {
419 return SkPackARGB4444(SK_AlphaOPAQUE & 0x0F,
420 r / count, g / count, b / count);
421 }
422}
423
424static SkBitmap unpremultiply_bitmap(const SkBitmap& bitmap,
425 const SkIRect& srcRect) {
426 SkBitmap outBitmap;
427 outBitmap.setConfig(bitmap.config(), srcRect.width(), srcRect.height());
428 outBitmap.allocPixels();
robertphillips@google.coma4662862013-11-21 14:24:16 +0000429 int dstRow = 0;
vandebo@chromium.orgece95c32013-10-24 15:20:00 +0000430
431 outBitmap.lockPixels();
432 bitmap.lockPixels();
433 switch (bitmap.config()) {
434 case SkBitmap::kARGB_4444_Config: {
435 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
436 uint16_t* dst = outBitmap.getAddr16(0, dstRow);
437 uint16_t* src = bitmap.getAddr16(0, y);
438 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
439 uint8_t a = SkGetPackedA4444(src[x]);
440 // It is necessary to average the color component of
441 // transparent pixels with their surrounding neighbors
442 // since the PDF renderer may separately re-sample the
443 // alpha and color channels when the image is not
444 // displayed at its native resolution. Since an alpha of
445 // zero gives no information about the color component,
446 // the pathological case is a white image with sharp
447 // transparency bounds - the color channel goes to black,
448 // and the should-be-transparent pixels are rendered
449 // as grey because of the separate soft mask and color
450 // resizing.
451 if (a == (SK_AlphaTRANSPARENT & 0x0F)) {
452 *dst = get_argb4444_neighbor_avg_color(bitmap, x, y);
453 } else {
454 *dst = remove_alpha_argb4444(src[x]);
455 }
456 dst++;
457 }
458 dstRow++;
459 }
460 break;
461 }
462 case SkBitmap::kARGB_8888_Config: {
463 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
464 uint32_t* dst = outBitmap.getAddr32(0, dstRow);
465 uint32_t* src = bitmap.getAddr32(0, y);
466 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
467 uint8_t a = SkGetPackedA32(src[x]);
468 if (a == SK_AlphaTRANSPARENT) {
469 *dst = get_argb8888_neighbor_avg_color(bitmap, x, y);
470 } else {
471 *dst = remove_alpha_argb8888(src[x]);
472 }
473 dst++;
474 }
475 dstRow++;
476 }
477 break;
478 }
479 default:
480 SkASSERT(false);
481 }
482 bitmap.unlockPixels();
483 outBitmap.unlockPixels();
484
485 outBitmap.setImmutable();
486
487 return outBitmap;
488}
489
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000490// static
491SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap,
edisonn@google.comd9dfa182013-04-24 13:01:01 +0000492 const SkIRect& srcRect,
commit-bot@chromium.org608ea652013-10-03 19:29:21 +0000493 SkPicture::EncodeBitmap encoder) {
reed@google.com44699382013-10-31 17:28:30 +0000494 if (bitmap.config() == SkBitmap::kNo_Config) {
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000495 return NULL;
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000496 }
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000497
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000498 bool isTransparent = false;
499 SkAutoTUnref<SkStream> alphaData;
500 if (!bitmap.isOpaque()) {
501 // Note that isOpaque is not guaranteed to return false for bitmaps
502 // with alpha support but a completely opaque alpha channel,
503 // so alphaData may still be NULL if we have a completely opaque
504 // (or transparent) bitmap.
505 alphaData.reset(
506 extract_image_data(bitmap, srcRect, true, &isTransparent));
507 }
508 if (isTransparent) {
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000509 return NULL;
510 }
511
vandebo@chromium.orgece95c32013-10-24 15:20:00 +0000512 SkPDFImage* image;
513 SkBitmap::Config config = bitmap.config();
514 if (alphaData.get() != NULL && (config == SkBitmap::kARGB_8888_Config ||
515 config == SkBitmap::kARGB_4444_Config)) {
516 SkBitmap unpremulBitmap = unpremultiply_bitmap(bitmap, srcRect);
517 image = SkNEW_ARGS(SkPDFImage, (NULL, unpremulBitmap, false,
518 SkIRect::MakeWH(srcRect.width(), srcRect.height()),
519 encoder));
520 } else {
521 image = SkNEW_ARGS(SkPDFImage, (NULL, bitmap, false, srcRect, encoder));
522 }
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000523 if (alphaData.get() != NULL) {
524 SkAutoTUnref<SkPDFImage> mask(
525 SkNEW_ARGS(SkPDFImage, (alphaData.get(), bitmap,
526 true, srcRect, NULL)));
527 image->addSMask(mask);
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000528 }
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000529
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000530 return image;
531}
532
533SkPDFImage::~SkPDFImage() {
534 fResources.unrefAll();
535}
536
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000537SkPDFImage* SkPDFImage::addSMask(SkPDFImage* mask) {
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000538 fResources.push(mask);
539 mask->ref();
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000540 insert("SMask", new SkPDFObjRef(mask))->unref();
541 return mask;
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000542}
543
edisonn@google.com6addb192013-04-02 15:33:08 +0000544void SkPDFImage::getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
545 SkTSet<SkPDFObject*>* newResourceObjects) {
546 GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects);
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000547}
548
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000549SkPDFImage::SkPDFImage(SkStream* stream,
edisonn@google.comd9dfa182013-04-24 13:01:01 +0000550 const SkBitmap& bitmap,
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000551 bool isAlpha,
edisonn@google.comd9dfa182013-04-24 13:01:01 +0000552 const SkIRect& srcRect,
commit-bot@chromium.org608ea652013-10-03 19:29:21 +0000553 SkPicture::EncodeBitmap encoder)
commit-bot@chromium.org06822ea2013-08-29 20:26:41 +0000554 : fIsAlpha(isAlpha),
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000555 fSrcRect(srcRect),
556 fEncoder(encoder) {
557
commit-bot@chromium.org06822ea2013-08-29 20:26:41 +0000558 if (bitmap.isImmutable()) {
559 fBitmap = bitmap;
560 } else {
561 bitmap.deepCopyTo(&fBitmap, bitmap.config());
562 fBitmap.setImmutable();
563 }
564
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000565 if (stream != NULL) {
566 setData(stream);
567 fStreamValid = true;
568 } else {
569 fStreamValid = false;
570 }
571
reed@google.com44699382013-10-31 17:28:30 +0000572 SkBitmap::Config config = fBitmap.config();
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000573
vandebo@chromium.org06f7f402011-07-20 18:39:20 +0000574 insertName("Type", "XObject");
575 insertName("Subtype", "Image");
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000576
reed@google.com72e78082013-11-25 20:54:56 +0000577 bool alphaOnly = (config == SkBitmap::kA1_Config ||
578 config == SkBitmap::kA8_Config);
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000579
580 if (!isAlpha && alphaOnly) {
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000581 // For alpha only images, we stretch a single pixel of black for
582 // the color/shape part.
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000583 SkAutoTUnref<SkPDFInt> one(new SkPDFInt(1));
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000584 insert("Width", one.get());
585 insert("Height", one.get());
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000586 } else {
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000587 insertInt("Width", fSrcRect.width());
588 insertInt("Height", fSrcRect.height());
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000589 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000590
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000591 if (isAlpha || alphaOnly) {
vandebo@chromium.org06f7f402011-07-20 18:39:20 +0000592 insertName("ColorSpace", "DeviceGray");
reed@google.com2cb14802013-06-26 14:35:02 +0000593 } else if (config == SkBitmap::kIndex8_Config) {
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000594 SkAutoLockPixels alp(fBitmap);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000595 insert("ColorSpace",
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000596 make_indexed_color_space(fBitmap.getColorTable()))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000597 } else {
vandebo@chromium.org06f7f402011-07-20 18:39:20 +0000598 insertName("ColorSpace", "DeviceRGB");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000599 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000600
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000601 int bitsPerComp = 8;
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000602 if (config == SkBitmap::kARGB_4444_Config) {
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000603 bitsPerComp = 4;
reed@google.com72e78082013-11-25 20:54:56 +0000604 } else if (isAlpha && config == SkBitmap::kA1_Config) {
605 bitsPerComp = 1;
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000606 }
vandebo@chromium.org06f7f402011-07-20 18:39:20 +0000607 insertInt("BitsPerComponent", bitsPerComp);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000608
609 if (config == SkBitmap::kRGB_565_Config) {
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000610 SkASSERT(!isAlpha);
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000611 SkAutoTUnref<SkPDFInt> zeroVal(new SkPDFInt(0));
612 SkAutoTUnref<SkPDFScalar> scale5Val(
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000613 new SkPDFScalar(8.2258f)); // 255/2^5-1
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000614 SkAutoTUnref<SkPDFScalar> scale6Val(
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000615 new SkPDFScalar(4.0476f)); // 255/2^6-1
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000616 SkAutoTUnref<SkPDFArray> decodeValue(new SkPDFArray());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000617 decodeValue->reserve(6);
618 decodeValue->append(zeroVal.get());
619 decodeValue->append(scale5Val.get());
620 decodeValue->append(zeroVal.get());
621 decodeValue->append(scale6Val.get());
622 decodeValue->append(zeroVal.get());
623 decodeValue->append(scale5Val.get());
624 insert("Decode", decodeValue.get());
625 }
626}
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000627
628SkPDFImage::SkPDFImage(SkPDFImage& pdfImage)
629 : SkPDFStream(pdfImage),
630 fBitmap(pdfImage.fBitmap),
631 fIsAlpha(pdfImage.fIsAlpha),
632 fSrcRect(pdfImage.fSrcRect),
633 fEncoder(pdfImage.fEncoder),
634 fStreamValid(pdfImage.fStreamValid) {
635 // Nothing to do here - the image params are already copied in SkPDFStream's
636 // constructor, and the bitmap will be regenerated and encoded in
637 // populate.
638}
639
640bool SkPDFImage::populate(SkPDFCatalog* catalog) {
641 if (getState() == kUnused_State) {
642 // Initializing image data for the first time.
643 SkDynamicMemoryWStream dctCompressedWStream;
644 if (!skip_compression(catalog) && fEncoder &&
commit-bot@chromium.org608ea652013-10-03 19:29:21 +0000645 get_uncompressed_size(fBitmap, fSrcRect) > 1) {
646 SkBitmap subset;
647 // Extract subset
648 if (!fBitmap.extractSubset(&subset, fSrcRect)) {
649 // TODO(edisonn) It fails only for kA1_Config, if that is a
650 // major concern we will fix it later, so far it is NYI.
651 return false;
652 }
653 size_t pixelRefOffset = 0;
654 SkAutoTUnref<SkData> data(fEncoder(&pixelRefOffset, subset));
655 if (data.get() && data->size() < get_uncompressed_size(fBitmap,
656 fSrcRect)) {
657 SkAutoTUnref<SkStream> stream(SkNEW_ARGS(SkMemoryStream,
658 (data)));
659 setData(stream.get());
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000660
commit-bot@chromium.org608ea652013-10-03 19:29:21 +0000661 insertName("Filter", "DCTDecode");
662 insertInt("ColorTransform", kNoColorTransform);
663 insertInt("Length", getData()->getLength());
664 setState(kCompressed_State);
665 return true;
666 }
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000667 }
668 // Fallback method
669 if (!fStreamValid) {
670 SkAutoTUnref<SkStream> stream(
671 extract_image_data(fBitmap, fSrcRect, fIsAlpha, NULL));
672 setData(stream);
673 fStreamValid = true;
674 }
675 return INHERITED::populate(catalog);
676 } else if (getState() == kNoCompression_State &&
677 !skip_compression(catalog) &&
678 (SkFlate::HaveFlate() || fEncoder)) {
679 // Compression has not been requested when the stream was first created,
680 // but the new catalog wants it compressed.
681 if (!getSubstitute()) {
682 SkPDFStream* substitute = SkNEW_ARGS(SkPDFImage, (*this));
683 setSubstitute(substitute);
684 catalog->setSubstitute(this, substitute);
685 }
686 return false;
687 }
688 return true;
689}