Home | History | Annotate | Download | only in pdf
      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