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 21static const int kNoColorTransform = 0; 22 23static 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) { 30 switch (bitmap.colorType()) { 31 case kIndex_8_SkColorType: 32 return srcRect.width() * srcRect.height(); 33 case kARGB_4444_SkColorType: 34 return ((srcRect.width() * 3 + 1) / 2) * srcRect.height(); 35 case kRGB_565_SkColorType: 36 return srcRect.width() * 3 * srcRect.height(); 37 case kRGBA_8888_SkColorType: 38 case kBGRA_8888_SkColorType: 39 return srcRect.width() * 3 * srcRect.height(); 40 case kAlpha_8_SkColorType: 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 169static SkStream* extract_a8_alpha(const SkBitmap& bitmap, 170 const SkIRect& srcRect, 171 bool* isOpaque, 172 bool* isTransparent) { 173 const int alphaRowBytes = srcRect.width(); 174 SkStream* stream = SkNEW_ARGS(SkMemoryStream, 175 (alphaRowBytes * srcRect.height())); 176 uint8_t* alphaDst = (uint8_t*)stream->getMemoryBase(); 177 178 for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { 179 uint8_t* src = bitmap.getAddr8(0, y); 180 for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { 181 alphaDst[0] = src[x]; 182 *isOpaque &= alphaDst[0] == SK_AlphaOPAQUE; 183 *isTransparent &= alphaDst[0] == SK_AlphaTRANSPARENT; 184 alphaDst++; 185 } 186 } 187 return stream; 188} 189 190static SkStream* create_black_image() { 191 SkStream* stream = SkNEW_ARGS(SkMemoryStream, (1)); 192 ((uint8_t*)stream->getMemoryBase())[0] = 0; 193 return stream; 194} 195 196/** 197 * Extract either the color or image data from a SkBitmap into a SkStream. 198 * @param bitmap Bitmap to extract data from. 199 * @param srcRect Region in the bitmap to extract. 200 * @param extractAlpha Set to true to extract the alpha data or false to 201 * extract the color data. 202 * @param isTransparent Pointer to a bool to output whether the alpha is 203 * completely transparent. May be NULL. Only valid when 204 * extractAlpha == true. 205 * @return Unencoded image data, or NULL if either data was not 206 * available or alpha data was requested but the image was 207 * entirely transparent or opaque. 208 */ 209static SkStream* extract_image_data(const SkBitmap& bitmap, 210 const SkIRect& srcRect, 211 bool extractAlpha, bool* isTransparent) { 212 SkColorType colorType = bitmap.colorType(); 213 if (extractAlpha && (kIndex_8_SkColorType == colorType || 214 kRGB_565_SkColorType == colorType)) { 215 if (isTransparent != NULL) { 216 *isTransparent = false; 217 } 218 return NULL; 219 } 220 bool isOpaque = true; 221 bool transparent = extractAlpha; 222 SkStream* stream = NULL; 223 224 bitmap.lockPixels(); 225 switch (colorType) { 226 case kIndex_8_SkColorType: 227 if (!extractAlpha) { 228 stream = extract_index8_image(bitmap, srcRect); 229 } 230 break; 231 case kARGB_4444_SkColorType: 232 stream = extract_argb4444_data(bitmap, srcRect, extractAlpha, 233 &isOpaque, &transparent); 234 break; 235 case kRGB_565_SkColorType: 236 if (!extractAlpha) { 237 stream = extract_rgb565_image(bitmap, srcRect); 238 } 239 break; 240 case kN32_SkColorType: 241 stream = extract_argb8888_data(bitmap, srcRect, extractAlpha, 242 &isOpaque, &transparent); 243 break; 244 case kAlpha_8_SkColorType: 245 if (!extractAlpha) { 246 stream = create_black_image(); 247 } else { 248 stream = extract_a8_alpha(bitmap, srcRect, 249 &isOpaque, &transparent); 250 } 251 break; 252 default: 253 SkASSERT(false); 254 } 255 bitmap.unlockPixels(); 256 257 if (isTransparent != NULL) { 258 *isTransparent = transparent; 259 } 260 if (extractAlpha && (transparent || isOpaque)) { 261 SkSafeUnref(stream); 262 return NULL; 263 } 264 return stream; 265} 266 267static SkPDFArray* make_indexed_color_space(SkColorTable* table) { 268 SkPDFArray* result = new SkPDFArray(); 269 result->reserve(4); 270 result->appendName("Indexed"); 271 result->appendName("DeviceRGB"); 272 result->appendInt(table->count() - 1); 273 274 // Potentially, this could be represented in fewer bytes with a stream. 275 // Max size as a string is 1.5k. 276 SkString index; 277 for (int i = 0; i < table->count(); i++) { 278 char buf[3]; 279 SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]); 280 buf[0] = SkGetPackedR32(color); 281 buf[1] = SkGetPackedG32(color); 282 buf[2] = SkGetPackedB32(color); 283 index.append(buf, 3); 284 } 285 result->append(new SkPDFString(index))->unref(); 286 return result; 287} 288 289/** 290 * Removes the alpha component of an ARGB color (including unpremultiply) while 291 * keeping the output in the same format as the input. 292 */ 293static uint32_t remove_alpha_argb8888(uint32_t pmColor) { 294 SkColor color = SkUnPreMultiply::PMColorToColor(pmColor); 295 return SkPackARGB32NoCheck(SK_AlphaOPAQUE, 296 SkColorGetR(color), 297 SkColorGetG(color), 298 SkColorGetB(color)); 299} 300 301static uint16_t remove_alpha_argb4444(uint16_t pmColor) { 302 return SkPixel32ToPixel4444( 303 remove_alpha_argb8888(SkPixel4444ToPixel32(pmColor))); 304} 305 306static uint32_t get_argb8888_neighbor_avg_color(const SkBitmap& bitmap, 307 int xOrig, int yOrig) { 308 uint8_t count = 0; 309 uint16_t r = 0; 310 uint16_t g = 0; 311 uint16_t b = 0; 312 313 for (int y = yOrig - 1; y <= yOrig + 1; y++) { 314 if (y < 0 || y >= bitmap.height()) { 315 continue; 316 } 317 uint32_t* src = bitmap.getAddr32(0, y); 318 for (int x = xOrig - 1; x <= xOrig + 1; x++) { 319 if (x < 0 || x >= bitmap.width()) { 320 continue; 321 } 322 if (SkGetPackedA32(src[x]) != SK_AlphaTRANSPARENT) { 323 uint32_t color = remove_alpha_argb8888(src[x]); 324 r += SkGetPackedR32(color); 325 g += SkGetPackedG32(color); 326 b += SkGetPackedB32(color); 327 count++; 328 } 329 } 330 } 331 332 if (count == 0) { 333 return SkPackARGB32NoCheck(SK_AlphaOPAQUE, 0, 0, 0); 334 } else { 335 return SkPackARGB32NoCheck(SK_AlphaOPAQUE, 336 r / count, g / count, b / count); 337 } 338} 339 340static uint16_t get_argb4444_neighbor_avg_color(const SkBitmap& bitmap, 341 int xOrig, int yOrig) { 342 uint8_t count = 0; 343 uint8_t r = 0; 344 uint8_t g = 0; 345 uint8_t b = 0; 346 347 for (int y = yOrig - 1; y <= yOrig + 1; y++) { 348 if (y < 0 || y >= bitmap.height()) { 349 continue; 350 } 351 uint16_t* src = bitmap.getAddr16(0, y); 352 for (int x = xOrig - 1; x <= xOrig + 1; x++) { 353 if (x < 0 || x >= bitmap.width()) { 354 continue; 355 } 356 if ((SkGetPackedA4444(src[x]) & 0x0F) != SK_AlphaTRANSPARENT) { 357 uint16_t color = remove_alpha_argb4444(src[x]); 358 r += SkGetPackedR4444(color); 359 g += SkGetPackedG4444(color); 360 b += SkGetPackedB4444(color); 361 count++; 362 } 363 } 364 } 365 366 if (count == 0) { 367 return SkPackARGB4444(SK_AlphaOPAQUE & 0x0F, 0, 0, 0); 368 } else { 369 return SkPackARGB4444(SK_AlphaOPAQUE & 0x0F, 370 r / count, g / count, b / count); 371 } 372} 373 374static SkBitmap unpremultiply_bitmap(const SkBitmap& bitmap, 375 const SkIRect& srcRect) { 376 SkBitmap outBitmap; 377 outBitmap.allocPixels(bitmap.info().makeWH(srcRect.width(), srcRect.height())); 378 int dstRow = 0; 379 380 outBitmap.lockPixels(); 381 bitmap.lockPixels(); 382 switch (bitmap.colorType()) { 383 case kARGB_4444_SkColorType: { 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 kN32_SkColorType: { 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 440SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap, 441 const SkIRect& srcRect, 442 SkPicture::EncodeBitmap encoder) { 443 if (bitmap.colorType() == kUnknown_SkColorType) { 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 SkColorType colorType = bitmap.colorType(); 463 if (alphaData.get() != NULL && (kN32_SkColorType == colorType || 464 kARGB_4444_SkColorType == colorType)) { 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 482SkPDFImage::~SkPDFImage() { 483 fResources.unrefAll(); 484} 485 486SkPDFImage* SkPDFImage::addSMask(SkPDFImage* mask) { 487 fResources.push(mask); 488 mask->ref(); 489 insert("SMask", new SkPDFObjRef(mask))->unref(); 490 return mask; 491} 492 493void SkPDFImage::getResources(const SkTSet<SkPDFObject*>& knownResourceObjects, 494 SkTSet<SkPDFObject*>* newResourceObjects) { 495 GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects); 496} 497 498SkPDFImage::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 SkColorType colorType = fBitmap.colorType(); 522 523 insertName("Type", "XObject"); 524 insertName("Subtype", "Image"); 525 526 bool alphaOnly = (kAlpha_8_SkColorType == colorType); 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 (kIndex_8_SkColorType == colorType) { 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 (kARGB_4444_SkColorType == colorType) { 551 bitsPerComp = 4; 552 } 553 insertInt("BitsPerComponent", bitsPerComp); 554 555 if (kRGB_565_SkColorType == colorType) { 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 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 && 591 get_uncompressed_size(fBitmap, fSrcRect) > 1) { 592 SkBitmap subset; 593 // Extract subset 594 if (!fBitmap.extractSubset(&subset, fSrcRect)) { 595 return false; 596 } 597 size_t pixelRefOffset = 0; 598 SkAutoTUnref<SkData> data(fEncoder(&pixelRefOffset, subset)); 599 if (data.get() && data->size() < get_uncompressed_size(fBitmap, 600 fSrcRect)) { 601 SkAutoTUnref<SkStream> stream(SkNEW_ARGS(SkMemoryStream, 602 (data))); 603 setData(stream.get()); 604 605 insertName("Filter", "DCTDecode"); 606 insertInt("ColorTransform", kNoColorTransform); 607 insertInt("Length", getData()->getLength()); 608 setState(kCompressed_State); 609 return true; 610 } 611 } 612 // Fallback method 613 if (!fStreamValid) { 614 SkAutoTUnref<SkStream> stream( 615 extract_image_data(fBitmap, fSrcRect, fIsAlpha, NULL)); 616 setData(stream); 617 fStreamValid = true; 618 } 619 return INHERITED::populate(catalog); 620 } else if (getState() == kNoCompression_State && 621 !skip_compression(catalog) && 622 (SkFlate::HaveFlate() || fEncoder)) { 623 // Compression has not been requested when the stream was first created, 624 // but the new catalog wants it compressed. 625 if (!getSubstitute()) { 626 SkPDFStream* substitute = SkNEW_ARGS(SkPDFImage, (*this)); 627 setSubstitute(substitute); 628 catalog->setSubstitute(this, substitute); 629 } 630 return false; 631 } 632 return true; 633} 634