blob: 81adcc20d6b4c671229619c686794652d3d801cc [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();
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +000039 case SkBitmap::kA8_Config:
40 return 1;
41 default:
42 SkASSERT(false);
43 return 0;
44 }
45}
46
47static 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
61static 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
116static 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
134static 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
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000168static 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
189static 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 */
208static 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;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000222
vandebo@chromium.orgad114952010-10-26 19:43:14 +0000223 bitmap.lockPixels();
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000224 switch (config) {
225 case SkBitmap::kIndex8_Config:
226 if (!extractAlpha) {
227 stream = extract_index8_image(bitmap, srcRect);
vandebo@chromium.orgbefebb82011-01-29 01:38:50 +0000228 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000229 break;
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000230 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);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000237 }
238 break;
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000239 case SkBitmap::kARGB_8888_Config:
240 stream = extract_argb8888_data(bitmap, srcRect, extractAlpha,
241 &isOpaque, &transparent);
242 break;
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000243 case SkBitmap::kA8_Config:
244 if (!extractAlpha) {
245 stream = create_black_image();
246 } else {
247 stream = extract_a8_alpha(bitmap, srcRect,
248 &isOpaque, &transparent);
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000249 }
250 break;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000251 default:
252 SkASSERT(false);
253 }
vandebo@chromium.orgad114952010-10-26 19:43:14 +0000254 bitmap.unlockPixels();
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000255
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000256 if (isTransparent != NULL) {
257 *isTransparent = transparent;
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000258 }
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000259 if (extractAlpha && (transparent || isOpaque)) {
260 SkSafeUnref(stream);
261 return NULL;
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000262 }
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000263 return stream;
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000264}
265
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000266static SkPDFArray* make_indexed_color_space(SkColorTable* table) {
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000267 SkPDFArray* result = new SkPDFArray();
268 result->reserve(4);
vandebo@chromium.org06f7f402011-07-20 18:39:20 +0000269 result->appendName("Indexed");
270 result->appendName("DeviceRGB");
271 result->appendInt(table->count() - 1);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000272
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 }
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000284 result->append(new SkPDFString(index))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000285 return result;
286}
287
vandebo@chromium.orgece95c32013-10-24 15:20:00 +0000288/**
289 * Removes the alpha component of an ARGB color (including unpremultiply) while
290 * keeping the output in the same format as the input.
291 */
292static 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
300static uint16_t remove_alpha_argb4444(uint16_t pmColor) {
301 return SkPixel32ToPixel4444(
302 remove_alpha_argb8888(SkPixel4444ToPixel32(pmColor)));
303}
304
305static 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
339static 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
373static 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();
robertphillips@google.coma4662862013-11-21 14:24:16 +0000378 int dstRow = 0;
vandebo@chromium.orgece95c32013-10-24 15:20:00 +0000379
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
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000439// static
440SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap,
edisonn@google.comd9dfa182013-04-24 13:01:01 +0000441 const SkIRect& srcRect,
commit-bot@chromium.org608ea652013-10-03 19:29:21 +0000442 SkPicture::EncodeBitmap encoder) {
reed@google.com44699382013-10-31 17:28:30 +0000443 if (bitmap.config() == SkBitmap::kNo_Config) {
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000444 return NULL;
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000445 }
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000446
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000447 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) {
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000458 return NULL;
459 }
460
vandebo@chromium.orgece95c32013-10-24 15:20:00 +0000461 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 }
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000472 if (alphaData.get() != NULL) {
473 SkAutoTUnref<SkPDFImage> mask(
474 SkNEW_ARGS(SkPDFImage, (alphaData.get(), bitmap,
475 true, srcRect, NULL)));
476 image->addSMask(mask);
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000477 }
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000478
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000479 return image;
480}
481
482SkPDFImage::~SkPDFImage() {
483 fResources.unrefAll();
484}
485
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000486SkPDFImage* SkPDFImage::addSMask(SkPDFImage* mask) {
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000487 fResources.push(mask);
488 mask->ref();
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000489 insert("SMask", new SkPDFObjRef(mask))->unref();
490 return mask;
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000491}
492
edisonn@google.com6addb192013-04-02 15:33:08 +0000493void SkPDFImage::getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
494 SkTSet<SkPDFObject*>* newResourceObjects) {
495 GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects);
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000496}
497
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000498SkPDFImage::SkPDFImage(SkStream* stream,
edisonn@google.comd9dfa182013-04-24 13:01:01 +0000499 const SkBitmap& bitmap,
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000500 bool isAlpha,
edisonn@google.comd9dfa182013-04-24 13:01:01 +0000501 const SkIRect& srcRect,
commit-bot@chromium.org608ea652013-10-03 19:29:21 +0000502 SkPicture::EncodeBitmap encoder)
commit-bot@chromium.org06822ea2013-08-29 20:26:41 +0000503 : fIsAlpha(isAlpha),
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000504 fSrcRect(srcRect),
505 fEncoder(encoder) {
506
commit-bot@chromium.org06822ea2013-08-29 20:26:41 +0000507 if (bitmap.isImmutable()) {
508 fBitmap = bitmap;
509 } else {
510 bitmap.deepCopyTo(&fBitmap, bitmap.config());
511 fBitmap.setImmutable();
512 }
513
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000514 if (stream != NULL) {
515 setData(stream);
516 fStreamValid = true;
517 } else {
518 fStreamValid = false;
519 }
520
reed@google.com44699382013-10-31 17:28:30 +0000521 SkBitmap::Config config = fBitmap.config();
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000522
vandebo@chromium.org06f7f402011-07-20 18:39:20 +0000523 insertName("Type", "XObject");
524 insertName("Subtype", "Image");
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000525
rmistry@google.comd6bab022013-12-02 13:50:38 +0000526 bool alphaOnly = (config == SkBitmap::kA8_Config);
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000527
528 if (!isAlpha && alphaOnly) {
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000529 // For alpha only images, we stretch a single pixel of black for
530 // the color/shape part.
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000531 SkAutoTUnref<SkPDFInt> one(new SkPDFInt(1));
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000532 insert("Width", one.get());
533 insert("Height", one.get());
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000534 } else {
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000535 insertInt("Width", fSrcRect.width());
536 insertInt("Height", fSrcRect.height());
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000537 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000538
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000539 if (isAlpha || alphaOnly) {
vandebo@chromium.org06f7f402011-07-20 18:39:20 +0000540 insertName("ColorSpace", "DeviceGray");
reed@google.com2cb14802013-06-26 14:35:02 +0000541 } else if (config == SkBitmap::kIndex8_Config) {
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000542 SkAutoLockPixels alp(fBitmap);
vandebo@chromium.orgf7c15762011-02-01 22:19:44 +0000543 insert("ColorSpace",
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000544 make_indexed_color_space(fBitmap.getColorTable()))->unref();
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000545 } else {
vandebo@chromium.org06f7f402011-07-20 18:39:20 +0000546 insertName("ColorSpace", "DeviceRGB");
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000547 }
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000548
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000549 int bitsPerComp = 8;
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000550 if (config == SkBitmap::kARGB_4444_Config) {
vandebo@chromium.org1cfa2c42011-01-31 19:35:43 +0000551 bitsPerComp = 4;
ctguil@chromium.org769fa6a2011-08-20 00:36:18 +0000552 }
vandebo@chromium.org06f7f402011-07-20 18:39:20 +0000553 insertInt("BitsPerComponent", bitsPerComp);
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000554
555 if (config == SkBitmap::kRGB_565_Config) {
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000556 SkASSERT(!isAlpha);
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000557 SkAutoTUnref<SkPDFInt> zeroVal(new SkPDFInt(0));
558 SkAutoTUnref<SkPDFScalar> scale5Val(
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000559 new SkPDFScalar(8.2258f)); // 255/2^5-1
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000560 SkAutoTUnref<SkPDFScalar> scale6Val(
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000561 new SkPDFScalar(4.0476f)); // 255/2^6-1
vandebo@chromium.orgd96d17b2013-01-04 19:31:24 +0000562 SkAutoTUnref<SkPDFArray> decodeValue(new SkPDFArray());
vandebo@chromium.org9b49dc02010-10-20 22:23:29 +0000563 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}
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000573
574SkPDFImage::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
586bool 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 &&
commit-bot@chromium.org608ea652013-10-03 19:29:21 +0000591 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());
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000606
commit-bot@chromium.org608ea652013-10-03 19:29:21 +0000607 insertName("Filter", "DCTDecode");
608 insertInt("ColorTransform", kNoColorTransform);
609 insertInt("Length", getData()->getLength());
610 setState(kCompressed_State);
611 return true;
612 }
commit-bot@chromium.org181fcb42013-08-23 19:06:53 +0000613 }
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}