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