OLD | NEW |
(Empty) | |
| 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 "SkPDFBitmap.h" |
| 16 #include "SkPDFCatalog.h" |
| 17 #include "SkPixelRef.h" |
| 18 #include "SkRect.h" |
| 19 #include "SkStream.h" |
| 20 #include "SkString.h" |
| 21 #include "SkUnPreMultiply.h" |
| 22 |
| 23 static size_t get_uncompressed_size(const SkBitmap& bitmap, |
| 24 const SkIRect& srcRect) { |
| 25 switch (bitmap.colorType()) { |
| 26 case kIndex_8_SkColorType: |
| 27 return srcRect.width() * srcRect.height(); |
| 28 case kARGB_4444_SkColorType: |
| 29 return ((srcRect.width() * 3 + 1) / 2) * srcRect.height(); |
| 30 case kRGB_565_SkColorType: |
| 31 return srcRect.width() * 3 * srcRect.height(); |
| 32 case kRGBA_8888_SkColorType: |
| 33 case kBGRA_8888_SkColorType: |
| 34 case kGray_8_SkColorType: |
| 35 return srcRect.width() * 3 * srcRect.height(); |
| 36 case kAlpha_8_SkColorType: |
| 37 return 1; |
| 38 default: |
| 39 SkASSERT(false); |
| 40 return 0; |
| 41 } |
| 42 } |
| 43 |
| 44 static SkStream* extract_index8_image(const SkBitmap& bitmap, |
| 45 const SkIRect& srcRect) { |
| 46 const int rowBytes = srcRect.width(); |
| 47 SkStream* stream = SkNEW_ARGS(SkMemoryStream, |
| 48 (get_uncompressed_size(bitmap, srcRect))); |
| 49 uint8_t* dst = (uint8_t*)stream->getMemoryBase(); |
| 50 |
| 51 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { |
| 52 memcpy(dst, bitmap.getAddr8(srcRect.fLeft, y), rowBytes); |
| 53 dst += rowBytes; |
| 54 } |
| 55 return stream; |
| 56 } |
| 57 |
| 58 static SkStream* extract_argb4444_data(const SkBitmap& bitmap, |
| 59 const SkIRect& srcRect, |
| 60 bool extractAlpha, |
| 61 bool* isOpaque, |
| 62 bool* isTransparent) { |
| 63 SkStream* stream; |
| 64 uint8_t* dst = NULL; |
| 65 if (extractAlpha) { |
| 66 const int alphaRowBytes = (srcRect.width() + 1) / 2; |
| 67 stream = SkNEW_ARGS(SkMemoryStream, |
| 68 (alphaRowBytes * srcRect.height())); |
| 69 } else { |
| 70 stream = SkNEW_ARGS(SkMemoryStream, |
| 71 (get_uncompressed_size(bitmap, srcRect))); |
| 72 } |
| 73 dst = (uint8_t*)stream->getMemoryBase(); |
| 74 |
| 75 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { |
| 76 uint16_t* src = bitmap.getAddr16(0, y); |
| 77 int x; |
| 78 for (x = srcRect.fLeft; x + 1 < srcRect.fRight; x += 2) { |
| 79 if (extractAlpha) { |
| 80 dst[0] = (SkGetPackedA4444(src[x]) << 4) | |
| 81 SkGetPackedA4444(src[x + 1]); |
| 82 *isOpaque &= dst[0] == SK_AlphaOPAQUE; |
| 83 *isTransparent &= dst[0] == SK_AlphaTRANSPARENT; |
| 84 dst++; |
| 85 } else { |
| 86 dst[0] = (SkGetPackedR4444(src[x]) << 4) | |
| 87 SkGetPackedG4444(src[x]); |
| 88 dst[1] = (SkGetPackedB4444(src[x]) << 4) | |
| 89 SkGetPackedR4444(src[x + 1]); |
| 90 dst[2] = (SkGetPackedG4444(src[x + 1]) << 4) | |
| 91 SkGetPackedB4444(src[x + 1]); |
| 92 dst += 3; |
| 93 } |
| 94 } |
| 95 if (srcRect.width() & 1) { |
| 96 if (extractAlpha) { |
| 97 dst[0] = (SkGetPackedA4444(src[x]) << 4); |
| 98 *isOpaque &= dst[0] == (SK_AlphaOPAQUE & 0xF0); |
| 99 *isTransparent &= dst[0] == (SK_AlphaTRANSPARENT & 0xF0); |
| 100 dst++; |
| 101 |
| 102 } else { |
| 103 dst[0] = (SkGetPackedR4444(src[x]) << 4) | |
| 104 SkGetPackedG4444(src[x]); |
| 105 dst[1] = (SkGetPackedB4444(src[x]) << 4); |
| 106 dst += 2; |
| 107 } |
| 108 } |
| 109 } |
| 110 return stream; |
| 111 } |
| 112 |
| 113 static SkStream* extract_rgb565_image(const SkBitmap& bitmap, |
| 114 const SkIRect& srcRect) { |
| 115 SkStream* stream = SkNEW_ARGS(SkMemoryStream, |
| 116 (get_uncompressed_size(bitmap, |
| 117 srcRect))); |
| 118 uint8_t* dst = (uint8_t*)stream->getMemoryBase(); |
| 119 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { |
| 120 uint16_t* src = bitmap.getAddr16(0, y); |
| 121 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { |
| 122 dst[0] = SkGetPackedR16(src[x]); |
| 123 dst[1] = SkGetPackedG16(src[x]); |
| 124 dst[2] = SkGetPackedB16(src[x]); |
| 125 dst += 3; |
| 126 } |
| 127 } |
| 128 return stream; |
| 129 } |
| 130 |
| 131 static SkStream* extract_gray8_image(const SkBitmap& bitmap, const SkIRect& srcR
ect) { |
| 132 SkStream* stream = SkNEW_ARGS(SkMemoryStream, |
| 133 (get_uncompressed_size(bitmap, srcRect))); |
| 134 uint8_t* dst = (uint8_t*)stream->getMemoryBase(); |
| 135 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { |
| 136 uint8_t* src = bitmap.getAddr8(0, y); |
| 137 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { |
| 138 dst[0] = dst[1] = dst[2] = src[x]; |
| 139 dst += 3; |
| 140 } |
| 141 } |
| 142 return stream; |
| 143 } |
| 144 |
| 145 static uint32_t get_argb8888_neighbor_avg_color(const SkBitmap& bitmap, |
| 146 int xOrig, |
| 147 int yOrig); |
| 148 |
| 149 static SkStream* extract_argb8888_data(const SkBitmap& bitmap, |
| 150 const SkIRect& srcRect, |
| 151 bool extractAlpha, |
| 152 bool* isOpaque, |
| 153 bool* isTransparent) { |
| 154 size_t streamSize = extractAlpha ? srcRect.width() * srcRect.height() |
| 155 : get_uncompressed_size(bitmap, srcRect); |
| 156 SkStream* stream = SkNEW_ARGS(SkMemoryStream, (streamSize)); |
| 157 uint8_t* dst = (uint8_t*)stream->getMemoryBase(); |
| 158 |
| 159 const SkUnPreMultiply::Scale* scaleTable = SkUnPreMultiply::GetScaleTable(); |
| 160 |
| 161 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { |
| 162 uint32_t* src = bitmap.getAddr32(0, y); |
| 163 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { |
| 164 SkPMColor c = src[x]; |
| 165 U8CPU alpha = SkGetPackedA32(c); |
| 166 if (extractAlpha) { |
| 167 *isOpaque &= alpha == SK_AlphaOPAQUE; |
| 168 *isTransparent &= alpha == SK_AlphaTRANSPARENT; |
| 169 *dst++ = alpha; |
| 170 } else { |
| 171 if (SK_AlphaTRANSPARENT == alpha) { |
| 172 // It is necessary to average the color component of |
| 173 // transparent pixels with their surrounding neighbors |
| 174 // since the PDF renderer may separately re-sample the |
| 175 // alpha and color channels when the image is not |
| 176 // displayed at its native resolution. Since an alpha of |
| 177 // zero gives no information about the color component, |
| 178 // the pathological case is a white image with sharp |
| 179 // transparency bounds - the color channel goes to black, |
| 180 // and the should-be-transparent pixels are rendered |
| 181 // as grey because of the separate soft mask and color |
| 182 // resizing. |
| 183 c = get_argb8888_neighbor_avg_color(bitmap, x, y); |
| 184 *dst++ = SkGetPackedR32(c); |
| 185 *dst++ = SkGetPackedG32(c); |
| 186 *dst++ = SkGetPackedB32(c); |
| 187 } else { |
| 188 SkUnPreMultiply::Scale s = scaleTable[alpha]; |
| 189 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c)); |
| 190 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(c)); |
| 191 *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c)); |
| 192 } |
| 193 } |
| 194 } |
| 195 } |
| 196 SkASSERT(dst == streamSize + (uint8_t*)stream->getMemoryBase()); |
| 197 return stream; |
| 198 } |
| 199 |
| 200 static SkStream* extract_a8_alpha(const SkBitmap& bitmap, |
| 201 const SkIRect& srcRect, |
| 202 bool* isOpaque, |
| 203 bool* isTransparent) { |
| 204 const int alphaRowBytes = srcRect.width(); |
| 205 SkStream* stream = SkNEW_ARGS(SkMemoryStream, |
| 206 (alphaRowBytes * srcRect.height())); |
| 207 uint8_t* alphaDst = (uint8_t*)stream->getMemoryBase(); |
| 208 |
| 209 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { |
| 210 uint8_t* src = bitmap.getAddr8(0, y); |
| 211 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { |
| 212 alphaDst[0] = src[x]; |
| 213 *isOpaque &= alphaDst[0] == SK_AlphaOPAQUE; |
| 214 *isTransparent &= alphaDst[0] == SK_AlphaTRANSPARENT; |
| 215 alphaDst++; |
| 216 } |
| 217 } |
| 218 return stream; |
| 219 } |
| 220 |
| 221 static SkStream* create_black_image() { |
| 222 SkStream* stream = SkNEW_ARGS(SkMemoryStream, (1)); |
| 223 ((uint8_t*)stream->getMemoryBase())[0] = 0; |
| 224 return stream; |
| 225 } |
| 226 |
| 227 /** |
| 228 * Extract either the color or image data from a SkBitmap into a SkStream. |
| 229 * @param bitmap Bitmap to extract data from. |
| 230 * @param srcRect Region in the bitmap to extract. |
| 231 * @param extractAlpha Set to true to extract the alpha data or false to |
| 232 * extract the color data. |
| 233 * @param isTransparent Pointer to a bool to output whether the alpha is |
| 234 * completely transparent. May be NULL. Only valid when |
| 235 * extractAlpha == true. |
| 236 * @return Unencoded image data, or NULL if either data was not |
| 237 * available or alpha data was requested but the image was |
| 238 * entirely transparent or opaque. |
| 239 */ |
| 240 static SkStream* extract_image_data(const SkBitmap& bitmap, |
| 241 const SkIRect& srcRect, |
| 242 bool extractAlpha, bool* isTransparent) { |
| 243 SkColorType colorType = bitmap.colorType(); |
| 244 if (extractAlpha && (kIndex_8_SkColorType == colorType || |
| 245 kRGB_565_SkColorType == colorType || |
| 246 kGray_8_SkColorType == colorType)) { |
| 247 if (isTransparent != NULL) { |
| 248 *isTransparent = false; |
| 249 } |
| 250 return NULL; |
| 251 } |
| 252 |
| 253 SkAutoLockPixels lock(bitmap); |
| 254 if (NULL == bitmap.getPixels()) { |
| 255 return NULL; |
| 256 } |
| 257 |
| 258 bool isOpaque = true; |
| 259 bool transparent = extractAlpha; |
| 260 SkAutoTDelete<SkStream> stream; |
| 261 |
| 262 switch (colorType) { |
| 263 case kIndex_8_SkColorType: |
| 264 if (!extractAlpha) { |
| 265 stream.reset(extract_index8_image(bitmap, srcRect)); |
| 266 } |
| 267 break; |
| 268 case kARGB_4444_SkColorType: |
| 269 stream.reset(extract_argb4444_data(bitmap, srcRect, extractAlpha, |
| 270 &isOpaque, &transparent)); |
| 271 break; |
| 272 case kRGB_565_SkColorType: |
| 273 if (!extractAlpha) { |
| 274 stream.reset(extract_rgb565_image(bitmap, srcRect)); |
| 275 } |
| 276 break; |
| 277 case kGray_8_SkColorType: |
| 278 if (!extractAlpha) { |
| 279 stream.reset(extract_gray8_image(bitmap, srcRect)); |
| 280 } |
| 281 break; |
| 282 case kN32_SkColorType: |
| 283 stream.reset(extract_argb8888_data(bitmap, srcRect, extractAlpha, |
| 284 &isOpaque, &transparent)); |
| 285 break; |
| 286 case kAlpha_8_SkColorType: |
| 287 if (!extractAlpha) { |
| 288 stream.reset(create_black_image()); |
| 289 } else { |
| 290 stream.reset(extract_a8_alpha(bitmap, srcRect, |
| 291 &isOpaque, &transparent)); |
| 292 } |
| 293 break; |
| 294 default: |
| 295 SkASSERT(false); |
| 296 } |
| 297 |
| 298 if (isTransparent != NULL) { |
| 299 *isTransparent = transparent; |
| 300 } |
| 301 if (extractAlpha && (transparent || isOpaque)) { |
| 302 return NULL; |
| 303 } |
| 304 return stream.detach(); |
| 305 } |
| 306 |
| 307 static SkPDFArray* make_indexed_color_space(SkColorTable* table) { |
| 308 SkPDFArray* result = new SkPDFArray(); |
| 309 result->reserve(4); |
| 310 result->appendName("Indexed"); |
| 311 result->appendName("DeviceRGB"); |
| 312 result->appendInt(table->count() - 1); |
| 313 |
| 314 // Potentially, this could be represented in fewer bytes with a stream. |
| 315 // Max size as a string is 1.5k. |
| 316 SkString index; |
| 317 for (int i = 0; i < table->count(); i++) { |
| 318 char buf[3]; |
| 319 SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]); |
| 320 buf[0] = SkGetPackedR32(color); |
| 321 buf[1] = SkGetPackedG32(color); |
| 322 buf[2] = SkGetPackedB32(color); |
| 323 index.append(buf, 3); |
| 324 } |
| 325 result->append(new SkPDFString(index))->unref(); |
| 326 return result; |
| 327 } |
| 328 |
| 329 /** |
| 330 * Removes the alpha component of an ARGB color (including unpremultiply) while |
| 331 * keeping the output in the same format as the input. |
| 332 */ |
| 333 static uint32_t remove_alpha_argb8888(uint32_t pmColor) { |
| 334 SkColor color = SkUnPreMultiply::PMColorToColor(pmColor); |
| 335 return SkPackARGB32NoCheck(SK_AlphaOPAQUE, |
| 336 SkColorGetR(color), |
| 337 SkColorGetG(color), |
| 338 SkColorGetB(color)); |
| 339 } |
| 340 |
| 341 static uint16_t remove_alpha_argb4444(uint16_t pmColor) { |
| 342 return SkPixel32ToPixel4444( |
| 343 remove_alpha_argb8888(SkPixel4444ToPixel32(pmColor))); |
| 344 } |
| 345 |
| 346 static uint32_t get_argb8888_neighbor_avg_color(const SkBitmap& bitmap, |
| 347 int xOrig, int yOrig) { |
| 348 uint8_t count = 0; |
| 349 uint16_t r = 0; |
| 350 uint16_t g = 0; |
| 351 uint16_t b = 0; |
| 352 |
| 353 for (int y = yOrig - 1; y <= yOrig + 1; y++) { |
| 354 if (y < 0 || y >= bitmap.height()) { |
| 355 continue; |
| 356 } |
| 357 uint32_t* src = bitmap.getAddr32(0, y); |
| 358 for (int x = xOrig - 1; x <= xOrig + 1; x++) { |
| 359 if (x < 0 || x >= bitmap.width()) { |
| 360 continue; |
| 361 } |
| 362 if (SkGetPackedA32(src[x]) != SK_AlphaTRANSPARENT) { |
| 363 uint32_t color = remove_alpha_argb8888(src[x]); |
| 364 r += SkGetPackedR32(color); |
| 365 g += SkGetPackedG32(color); |
| 366 b += SkGetPackedB32(color); |
| 367 count++; |
| 368 } |
| 369 } |
| 370 } |
| 371 |
| 372 if (count == 0) { |
| 373 return SkPackARGB32NoCheck(SK_AlphaOPAQUE, 0, 0, 0); |
| 374 } else { |
| 375 return SkPackARGB32NoCheck(SK_AlphaOPAQUE, |
| 376 r / count, g / count, b / count); |
| 377 } |
| 378 } |
| 379 |
| 380 static uint16_t get_argb4444_neighbor_avg_color(const SkBitmap& bitmap, |
| 381 int xOrig, int yOrig) { |
| 382 uint8_t count = 0; |
| 383 uint8_t r = 0; |
| 384 uint8_t g = 0; |
| 385 uint8_t b = 0; |
| 386 |
| 387 for (int y = yOrig - 1; y <= yOrig + 1; y++) { |
| 388 if (y < 0 || y >= bitmap.height()) { |
| 389 continue; |
| 390 } |
| 391 uint16_t* src = bitmap.getAddr16(0, y); |
| 392 for (int x = xOrig - 1; x <= xOrig + 1; x++) { |
| 393 if (x < 0 || x >= bitmap.width()) { |
| 394 continue; |
| 395 } |
| 396 if ((SkGetPackedA4444(src[x]) & 0x0F) != SK_AlphaTRANSPARENT) { |
| 397 uint16_t color = remove_alpha_argb4444(src[x]); |
| 398 r += SkGetPackedR4444(color); |
| 399 g += SkGetPackedG4444(color); |
| 400 b += SkGetPackedB4444(color); |
| 401 count++; |
| 402 } |
| 403 } |
| 404 } |
| 405 |
| 406 if (count == 0) { |
| 407 return SkPackARGB4444(SK_AlphaOPAQUE & 0x0F, 0, 0, 0); |
| 408 } else { |
| 409 return SkPackARGB4444(SK_AlphaOPAQUE & 0x0F, |
| 410 r / count, g / count, b / count); |
| 411 } |
| 412 } |
| 413 |
| 414 static SkBitmap unpremultiply_bitmap(const SkBitmap& bitmap, |
| 415 const SkIRect& srcRect) { |
| 416 SkBitmap outBitmap; |
| 417 outBitmap.allocPixels(bitmap.info().makeWH(srcRect.width(), srcRect.height()
)); |
| 418 int dstRow = 0; |
| 419 |
| 420 SkAutoLockPixels outBitmapPixelLock(outBitmap); |
| 421 SkAutoLockPixels bitmapPixelLock(bitmap); |
| 422 switch (bitmap.colorType()) { |
| 423 case kARGB_4444_SkColorType: { |
| 424 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { |
| 425 uint16_t* dst = outBitmap.getAddr16(0, dstRow); |
| 426 uint16_t* src = bitmap.getAddr16(0, y); |
| 427 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { |
| 428 uint8_t a = SkGetPackedA4444(src[x]); |
| 429 // It is necessary to average the color component of |
| 430 // transparent pixels with their surrounding neighbors |
| 431 // since the PDF renderer may separately re-sample the |
| 432 // alpha and color channels when the image is not |
| 433 // displayed at its native resolution. Since an alpha of |
| 434 // zero gives no information about the color component, |
| 435 // the pathological case is a white image with sharp |
| 436 // transparency bounds - the color channel goes to black, |
| 437 // and the should-be-transparent pixels are rendered |
| 438 // as grey because of the separate soft mask and color |
| 439 // resizing. |
| 440 if (a == (SK_AlphaTRANSPARENT & 0x0F)) { |
| 441 *dst = get_argb4444_neighbor_avg_color(bitmap, x, y); |
| 442 } else { |
| 443 *dst = remove_alpha_argb4444(src[x]); |
| 444 } |
| 445 dst++; |
| 446 } |
| 447 dstRow++; |
| 448 } |
| 449 break; |
| 450 } |
| 451 case kN32_SkColorType: { |
| 452 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { |
| 453 uint32_t* dst = outBitmap.getAddr32(0, dstRow); |
| 454 uint32_t* src = bitmap.getAddr32(0, y); |
| 455 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { |
| 456 uint8_t a = SkGetPackedA32(src[x]); |
| 457 if (a == SK_AlphaTRANSPARENT) { |
| 458 *dst = get_argb8888_neighbor_avg_color(bitmap, x, y); |
| 459 } else { |
| 460 *dst = remove_alpha_argb8888(src[x]); |
| 461 } |
| 462 dst++; |
| 463 } |
| 464 dstRow++; |
| 465 } |
| 466 break; |
| 467 } |
| 468 default: |
| 469 SkASSERT(false); |
| 470 } |
| 471 |
| 472 outBitmap.setImmutable(); |
| 473 |
| 474 return outBitmap; |
| 475 } |
| 476 |
| 477 // static |
| 478 SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap, |
| 479 const SkIRect& srcRect) { |
| 480 if (bitmap.colorType() == kUnknown_SkColorType) { |
| 481 return NULL; |
| 482 } |
| 483 |
| 484 bool isTransparent = false; |
| 485 SkAutoTDelete<SkStream> alphaData; |
| 486 if (!bitmap.isOpaque()) { |
| 487 // Note that isOpaque is not guaranteed to return false for bitmaps |
| 488 // with alpha support but a completely opaque alpha channel, |
| 489 // so alphaData may still be NULL if we have a completely opaque |
| 490 // (or transparent) bitmap. |
| 491 alphaData.reset( |
| 492 extract_image_data(bitmap, srcRect, true, &isTransparent)); |
| 493 } |
| 494 if (isTransparent) { |
| 495 return NULL; |
| 496 } |
| 497 |
| 498 SkPDFImage* image; |
| 499 SkColorType colorType = bitmap.colorType(); |
| 500 if (alphaData.get() != NULL && (kN32_SkColorType == colorType || |
| 501 kARGB_4444_SkColorType == colorType)) { |
| 502 if (kN32_SkColorType == colorType) { |
| 503 image = SkNEW_ARGS(SkPDFImage, (NULL, bitmap, false, |
| 504 SkIRect::MakeWH(srcRect.width(), |
| 505 srcRect.height()))); |
| 506 } else { |
| 507 SkBitmap unpremulBitmap = unpremultiply_bitmap(bitmap, srcRect); |
| 508 image = SkNEW_ARGS(SkPDFImage, (NULL, unpremulBitmap, false, |
| 509 SkIRect::MakeWH(srcRect.width(), |
| 510 srcRect.height()))); |
| 511 } |
| 512 } else { |
| 513 image = SkNEW_ARGS(SkPDFImage, (NULL, bitmap, false, srcRect)); |
| 514 } |
| 515 if (alphaData.get() != NULL) { |
| 516 SkAutoTUnref<SkPDFImage> mask( |
| 517 SkNEW_ARGS(SkPDFImage, (alphaData.get(), bitmap, true, srcRect))
); |
| 518 image->insert("SMask", new SkPDFObjRef(mask))->unref(); |
| 519 } |
| 520 return image; |
| 521 } |
| 522 |
| 523 SkPDFImage::~SkPDFImage() {} |
| 524 |
| 525 SkPDFImage::SkPDFImage(SkStream* stream, |
| 526 const SkBitmap& bitmap, |
| 527 bool isAlpha, |
| 528 const SkIRect& srcRect) |
| 529 : fIsAlpha(isAlpha), |
| 530 fSrcRect(srcRect) { |
| 531 |
| 532 if (bitmap.isImmutable()) { |
| 533 fBitmap = bitmap; |
| 534 } else { |
| 535 bitmap.deepCopyTo(&fBitmap); |
| 536 fBitmap.setImmutable(); |
| 537 } |
| 538 |
| 539 if (stream != NULL) { |
| 540 this->setData(stream); |
| 541 fStreamValid = true; |
| 542 } else { |
| 543 fStreamValid = false; |
| 544 } |
| 545 |
| 546 SkColorType colorType = fBitmap.colorType(); |
| 547 |
| 548 insertName("Type", "XObject"); |
| 549 insertName("Subtype", "Image"); |
| 550 |
| 551 bool alphaOnly = (kAlpha_8_SkColorType == colorType); |
| 552 |
| 553 if (!isAlpha && alphaOnly) { |
| 554 // For alpha only images, we stretch a single pixel of black for |
| 555 // the color/shape part. |
| 556 SkAutoTUnref<SkPDFInt> one(new SkPDFInt(1)); |
| 557 insert("Width", one.get()); |
| 558 insert("Height", one.get()); |
| 559 } else { |
| 560 insertInt("Width", fSrcRect.width()); |
| 561 insertInt("Height", fSrcRect.height()); |
| 562 } |
| 563 |
| 564 if (isAlpha || alphaOnly) { |
| 565 insertName("ColorSpace", "DeviceGray"); |
| 566 } else if (kIndex_8_SkColorType == colorType) { |
| 567 SkAutoLockPixels alp(fBitmap); |
| 568 insert("ColorSpace", |
| 569 make_indexed_color_space(fBitmap.getColorTable()))->unref(); |
| 570 } else { |
| 571 insertName("ColorSpace", "DeviceRGB"); |
| 572 } |
| 573 |
| 574 int bitsPerComp = 8; |
| 575 if (kARGB_4444_SkColorType == colorType) { |
| 576 bitsPerComp = 4; |
| 577 } |
| 578 insertInt("BitsPerComponent", bitsPerComp); |
| 579 |
| 580 if (kRGB_565_SkColorType == colorType) { |
| 581 SkASSERT(!isAlpha); |
| 582 SkAutoTUnref<SkPDFInt> zeroVal(new SkPDFInt(0)); |
| 583 SkAutoTUnref<SkPDFScalar> scale5Val( |
| 584 new SkPDFScalar(8.2258f)); // 255/2^5-1 |
| 585 SkAutoTUnref<SkPDFScalar> scale6Val( |
| 586 new SkPDFScalar(4.0476f)); // 255/2^6-1 |
| 587 SkAutoTUnref<SkPDFArray> decodeValue(new SkPDFArray()); |
| 588 decodeValue->reserve(6); |
| 589 decodeValue->append(zeroVal.get()); |
| 590 decodeValue->append(scale5Val.get()); |
| 591 decodeValue->append(zeroVal.get()); |
| 592 decodeValue->append(scale6Val.get()); |
| 593 decodeValue->append(zeroVal.get()); |
| 594 decodeValue->append(scale5Val.get()); |
| 595 insert("Decode", decodeValue.get()); |
| 596 } |
| 597 } |
| 598 |
| 599 SkPDFImage::SkPDFImage(SkPDFImage& pdfImage) |
| 600 : SkPDFStream(pdfImage), |
| 601 fBitmap(pdfImage.fBitmap), |
| 602 fIsAlpha(pdfImage.fIsAlpha), |
| 603 fSrcRect(pdfImage.fSrcRect), |
| 604 fStreamValid(pdfImage.fStreamValid) { |
| 605 // Nothing to do here - the image params are already copied in SkPDFStream's |
| 606 // constructor, and the bitmap will be regenerated and encoded in |
| 607 // populate. |
| 608 } |
| 609 |
| 610 bool SkPDFImage::populate(SkPDFCatalog* catalog) { |
| 611 if (getState() == kUnused_State) { |
| 612 // Initializing image data for the first time. |
| 613 // Fallback method |
| 614 if (!fStreamValid) { |
| 615 SkAutoTDelete<SkStream> stream( |
| 616 extract_image_data(fBitmap, fSrcRect, fIsAlpha, NULL)); |
| 617 this->setData(stream); |
| 618 fStreamValid = true; |
| 619 } |
| 620 return INHERITED::populate(catalog); |
| 621 } |
| 622 #ifndef SK_NO_FLATE |
| 623 else if (getState() == kNoCompression_State) { |
| 624 // Compression has not been requested when the stream was first created, |
| 625 // but the new catalog wants it compressed. |
| 626 if (!getSubstitute()) { |
| 627 SkPDFStream* substitute = SkNEW_ARGS(SkPDFImage, (*this)); |
| 628 setSubstitute(substitute); |
| 629 catalog->setSubstitute(this, substitute); |
| 630 } |
| 631 return false; |
| 632 } |
| 633 #endif // SK_NO_FLATE |
| 634 return true; |
| 635 } |
| 636 |
| 637 #if 0 // reenable when we can figure out the JPEG colorspace |
| 638 namespace { |
| 639 /** |
| 640 * This PDFObject assumes that its constructor was handed |
| 641 * Jpeg-encoded data that can be directly embedded into a PDF. |
| 642 */ |
| 643 class PDFJPEGImage : public SkPDFObject { |
| 644 SkAutoTUnref<SkData> fData; |
| 645 int fWidth; |
| 646 int fHeight; |
| 647 public: |
| 648 PDFJPEGImage(SkData* data, int width, int height) |
| 649 : fData(SkRef(data)), fWidth(width), fHeight(height) {} |
| 650 virtual void emitObject( |
| 651 SkWStream* stream, |
| 652 SkPDFCatalog* catalog, bool indirect) SK_OVERRIDE { |
| 653 if (indirect) { |
| 654 this->emitIndirectObject(stream, catalog); |
| 655 return; |
| 656 } |
| 657 SkASSERT(fData.get()); |
| 658 const char kPrefaceFormat[] = |
| 659 "<<" |
| 660 "/Type /XObject\n" |
| 661 "/Subtype /Image\n" |
| 662 "/Width %d\n" |
| 663 "/Height %d\n" |
| 664 "/ColorSpace /DeviceRGB\n" // or DeviceGray |
| 665 "/BitsPerComponent 8\n" |
| 666 "/Filter /DCTDecode\n" |
| 667 "/ColorTransform 0\n" |
| 668 "/Length " SK_SIZE_T_SPECIFIER "\n" |
| 669 ">> stream\n"; |
| 670 SkString preface( |
| 671 SkStringPrintf(kPrefaceFormat, fWidth, fHeight, fData->size())); |
| 672 const char kPostface[] = "\nendstream"; |
| 673 stream->write(preface.c_str(), preface.size()); |
| 674 stream->write(fData->data(), fData->size()); |
| 675 stream->write(kPostface, sizeof(kPostface)); |
| 676 } |
| 677 }; |
| 678 |
| 679 /** |
| 680 * If the bitmap is not subsetted, return its encoded data, if |
| 681 * availible. |
| 682 */ |
| 683 static inline SkData* ref_encoded_data(const SkBitmap& bm) { |
| 684 if ((NULL == bm.pixelRef()) |
| 685 || !bm.pixelRefOrigin().isZero() |
| 686 || (bm.info().dimensions() != bm.pixelRef()->info().dimensions())) { |
| 687 return NULL; |
| 688 } |
| 689 return bm.pixelRef()->refEncodedData(); |
| 690 } |
| 691 |
| 692 /* |
| 693 * This functions may give false negatives but no false positives. |
| 694 */ |
| 695 static bool is_jfif_jpeg(SkData* data) { |
| 696 if (!data || (data->size() < 11)) { |
| 697 return false; |
| 698 } |
| 699 const uint8_t bytesZeroToThree[] = {0xFF, 0xD8, 0xFF, 0xE0}; |
| 700 const uint8_t bytesSixToTen[] = {'J', 'F', 'I', 'F', 0}; |
| 701 // 0 1 2 3 4 5 6 7 8 9 10 |
| 702 // FF D8 FF E0 ?? ?? 'J' 'F' 'I' 'F' 00 ... |
| 703 return ((0 == memcmp(data->bytes(), bytesZeroToThree, |
| 704 sizeof(bytesZeroToThree))) |
| 705 && (0 == memcmp(data->bytes() + 6, bytesSixToTen, |
| 706 sizeof(bytesSixToTen)))); |
| 707 } |
| 708 } // namespace |
| 709 #endif |
| 710 |
| 711 SkPDFObject* SkPDFCreateImageObject(SkPDFCanon* canon, |
| 712 const SkBitmap& bitmap, |
| 713 const SkIRect& subset) { |
| 714 if (SkPDFObject* pdfBitmap = SkPDFBitmap::Create(canon, bitmap, subset)) { |
| 715 return pdfBitmap; |
| 716 } |
| 717 #if 0 // reenable when we can figure out the JPEG colorspace |
| 718 if (SkIRect::MakeWH(bitmap.width(), bitmap.height()) == subset) { |
| 719 SkAutoTUnref<SkData> encodedData(ref_encoded_data(bitmap)); |
| 720 if (is_jfif_jpeg(encodedData)) { |
| 721 return SkNEW_ARGS(PDFJPEGImage, |
| 722 (encodedData, bitmap.width(), bitmap.height())); |
| 723 } |
| 724 } |
| 725 #endif |
| 726 return SkPDFImage::CreateImage(bitmap, subset); |
| 727 } |
OLD | NEW |