|  | /* | 
|  | * Copyright 2010 The Android Open Source Project | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #include "SkPDFImage.h" | 
|  |  | 
|  | #include "SkBitmap.h" | 
|  | #include "SkColor.h" | 
|  | #include "SkColorPriv.h" | 
|  | #include "SkData.h" | 
|  | #include "SkFlate.h" | 
|  | #include "SkPDFCatalog.h" | 
|  | #include "SkRect.h" | 
|  | #include "SkStream.h" | 
|  | #include "SkString.h" | 
|  | #include "SkUnPreMultiply.h" | 
|  |  | 
|  | static const int kNoColorTransform = 0; | 
|  |  | 
|  | static bool skip_compression(SkPDFCatalog* catalog) { | 
|  | return SkToBool(catalog->getDocumentFlags() & | 
|  | SkPDFDocument::kFavorSpeedOverSize_Flags); | 
|  | } | 
|  |  | 
|  | static size_t get_uncompressed_size(const SkBitmap& bitmap, | 
|  | const SkIRect& srcRect) { | 
|  | switch (bitmap.colorType()) { | 
|  | case kIndex_8_SkColorType: | 
|  | return srcRect.width() * srcRect.height(); | 
|  | case kARGB_4444_SkColorType: | 
|  | return ((srcRect.width() * 3 + 1) / 2) * srcRect.height(); | 
|  | case kRGB_565_SkColorType: | 
|  | return srcRect.width() * 3 * srcRect.height(); | 
|  | case kRGBA_8888_SkColorType: | 
|  | case kBGRA_8888_SkColorType: | 
|  | return srcRect.width() * 3 * srcRect.height(); | 
|  | case kAlpha_8_SkColorType: | 
|  | return 1; | 
|  | default: | 
|  | SkASSERT(false); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | static SkStream* extract_index8_image(const SkBitmap& bitmap, | 
|  | const SkIRect& srcRect) { | 
|  | const int rowBytes = srcRect.width(); | 
|  | SkStream* stream = SkNEW_ARGS(SkMemoryStream, | 
|  | (get_uncompressed_size(bitmap, srcRect))); | 
|  | uint8_t* dst = (uint8_t*)stream->getMemoryBase(); | 
|  |  | 
|  | for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | 
|  | memcpy(dst, bitmap.getAddr8(srcRect.fLeft, y), rowBytes); | 
|  | dst += rowBytes; | 
|  | } | 
|  | return stream; | 
|  | } | 
|  |  | 
|  | static SkStream* extract_argb4444_data(const SkBitmap& bitmap, | 
|  | const SkIRect& srcRect, | 
|  | bool extractAlpha, | 
|  | bool* isOpaque, | 
|  | bool* isTransparent) { | 
|  | SkStream* stream; | 
|  | uint8_t* dst = NULL; | 
|  | if (extractAlpha) { | 
|  | const int alphaRowBytes = (srcRect.width() + 1) / 2; | 
|  | stream = SkNEW_ARGS(SkMemoryStream, | 
|  | (alphaRowBytes * srcRect.height())); | 
|  | } else { | 
|  | stream = SkNEW_ARGS(SkMemoryStream, | 
|  | (get_uncompressed_size(bitmap, srcRect))); | 
|  | } | 
|  | dst = (uint8_t*)stream->getMemoryBase(); | 
|  |  | 
|  | for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | 
|  | uint16_t* src = bitmap.getAddr16(0, y); | 
|  | int x; | 
|  | for (x = srcRect.fLeft; x + 1 < srcRect.fRight; x += 2) { | 
|  | if (extractAlpha) { | 
|  | dst[0] = (SkGetPackedA4444(src[x]) << 4) | | 
|  | SkGetPackedA4444(src[x + 1]); | 
|  | *isOpaque &= dst[0] == SK_AlphaOPAQUE; | 
|  | *isTransparent &= dst[0] == SK_AlphaTRANSPARENT; | 
|  | dst++; | 
|  | } else { | 
|  | dst[0] = (SkGetPackedR4444(src[x]) << 4) | | 
|  | SkGetPackedG4444(src[x]); | 
|  | dst[1] = (SkGetPackedB4444(src[x]) << 4) | | 
|  | SkGetPackedR4444(src[x + 1]); | 
|  | dst[2] = (SkGetPackedG4444(src[x + 1]) << 4) | | 
|  | SkGetPackedB4444(src[x + 1]); | 
|  | dst += 3; | 
|  | } | 
|  | } | 
|  | if (srcRect.width() & 1) { | 
|  | if (extractAlpha) { | 
|  | dst[0] = (SkGetPackedA4444(src[x]) << 4); | 
|  | *isOpaque &= dst[0] == (SK_AlphaOPAQUE & 0xF0); | 
|  | *isTransparent &= dst[0] == (SK_AlphaTRANSPARENT & 0xF0); | 
|  | dst++; | 
|  |  | 
|  | } else { | 
|  | dst[0] = (SkGetPackedR4444(src[x]) << 4) | | 
|  | SkGetPackedG4444(src[x]); | 
|  | dst[1] = (SkGetPackedB4444(src[x]) << 4); | 
|  | dst += 2; | 
|  | } | 
|  | } | 
|  | } | 
|  | return stream; | 
|  | } | 
|  |  | 
|  | static SkStream* extract_rgb565_image(const SkBitmap& bitmap, | 
|  | const SkIRect& srcRect) { | 
|  | SkStream* stream = SkNEW_ARGS(SkMemoryStream, | 
|  | (get_uncompressed_size(bitmap, | 
|  | srcRect))); | 
|  | uint8_t* dst = (uint8_t*)stream->getMemoryBase(); | 
|  | for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | 
|  | uint16_t* src = bitmap.getAddr16(0, y); | 
|  | for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { | 
|  | dst[0] = SkGetPackedR16(src[x]); | 
|  | dst[1] = SkGetPackedG16(src[x]); | 
|  | dst[2] = SkGetPackedB16(src[x]); | 
|  | dst += 3; | 
|  | } | 
|  | } | 
|  | return stream; | 
|  | } | 
|  |  | 
|  | static SkStream* extract_argb8888_data(const SkBitmap& bitmap, | 
|  | const SkIRect& srcRect, | 
|  | bool extractAlpha, | 
|  | bool* isOpaque, | 
|  | bool* isTransparent) { | 
|  | SkStream* stream; | 
|  | if (extractAlpha) { | 
|  | stream = SkNEW_ARGS(SkMemoryStream, | 
|  | (srcRect.width() * srcRect.height())); | 
|  | } else { | 
|  | stream = SkNEW_ARGS(SkMemoryStream, | 
|  | (get_uncompressed_size(bitmap, srcRect))); | 
|  | } | 
|  | uint8_t* dst = (uint8_t*)stream->getMemoryBase(); | 
|  |  | 
|  | for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | 
|  | uint32_t* src = bitmap.getAddr32(0, y); | 
|  | for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { | 
|  | if (extractAlpha) { | 
|  | dst[0] = SkGetPackedA32(src[x]); | 
|  | *isOpaque &= dst[0] == SK_AlphaOPAQUE; | 
|  | *isTransparent &= dst[0] == SK_AlphaTRANSPARENT; | 
|  | dst++; | 
|  | } else { | 
|  | dst[0] = SkGetPackedR32(src[x]); | 
|  | dst[1] = SkGetPackedG32(src[x]); | 
|  | dst[2] = SkGetPackedB32(src[x]); | 
|  | dst += 3; | 
|  | } | 
|  | } | 
|  | } | 
|  | return stream; | 
|  | } | 
|  |  | 
|  | static SkStream* extract_a8_alpha(const SkBitmap& bitmap, | 
|  | const SkIRect& srcRect, | 
|  | bool* isOpaque, | 
|  | bool* isTransparent) { | 
|  | const int alphaRowBytes = srcRect.width(); | 
|  | SkStream* stream = SkNEW_ARGS(SkMemoryStream, | 
|  | (alphaRowBytes * srcRect.height())); | 
|  | uint8_t* alphaDst = (uint8_t*)stream->getMemoryBase(); | 
|  |  | 
|  | for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | 
|  | uint8_t* src = bitmap.getAddr8(0, y); | 
|  | for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { | 
|  | alphaDst[0] = src[x]; | 
|  | *isOpaque &= alphaDst[0] == SK_AlphaOPAQUE; | 
|  | *isTransparent &= alphaDst[0] == SK_AlphaTRANSPARENT; | 
|  | alphaDst++; | 
|  | } | 
|  | } | 
|  | return stream; | 
|  | } | 
|  |  | 
|  | static SkStream* create_black_image() { | 
|  | SkStream* stream = SkNEW_ARGS(SkMemoryStream, (1)); | 
|  | ((uint8_t*)stream->getMemoryBase())[0] = 0; | 
|  | return stream; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Extract either the color or image data from a SkBitmap into a SkStream. | 
|  | * @param bitmap        Bitmap to extract data from. | 
|  | * @param srcRect       Region in the bitmap to extract. | 
|  | * @param extractAlpha  Set to true to extract the alpha data or false to | 
|  | *                      extract the color data. | 
|  | * @param isTransparent Pointer to a bool to output whether the alpha is | 
|  | *                      completely transparent. May be NULL. Only valid when | 
|  | *                      extractAlpha == true. | 
|  | * @return              Unencoded image data, or NULL if either data was not | 
|  | *                      available or alpha data was requested but the image was | 
|  | *                      entirely transparent or opaque. | 
|  | */ | 
|  | static SkStream* extract_image_data(const SkBitmap& bitmap, | 
|  | const SkIRect& srcRect, | 
|  | bool extractAlpha, bool* isTransparent) { | 
|  | SkColorType colorType = bitmap.colorType(); | 
|  | if (extractAlpha && (kIndex_8_SkColorType == colorType || | 
|  | kRGB_565_SkColorType == colorType)) { | 
|  | if (isTransparent != NULL) { | 
|  | *isTransparent = false; | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  | bool isOpaque = true; | 
|  | bool transparent = extractAlpha; | 
|  | SkStream* stream = NULL; | 
|  |  | 
|  | bitmap.lockPixels(); | 
|  | switch (colorType) { | 
|  | case kIndex_8_SkColorType: | 
|  | if (!extractAlpha) { | 
|  | stream = extract_index8_image(bitmap, srcRect); | 
|  | } | 
|  | break; | 
|  | case kARGB_4444_SkColorType: | 
|  | stream = extract_argb4444_data(bitmap, srcRect, extractAlpha, | 
|  | &isOpaque, &transparent); | 
|  | break; | 
|  | case kRGB_565_SkColorType: | 
|  | if (!extractAlpha) { | 
|  | stream = extract_rgb565_image(bitmap, srcRect); | 
|  | } | 
|  | break; | 
|  | case kN32_SkColorType: | 
|  | stream = extract_argb8888_data(bitmap, srcRect, extractAlpha, | 
|  | &isOpaque, &transparent); | 
|  | break; | 
|  | case kAlpha_8_SkColorType: | 
|  | if (!extractAlpha) { | 
|  | stream = create_black_image(); | 
|  | } else { | 
|  | stream = extract_a8_alpha(bitmap, srcRect, | 
|  | &isOpaque, &transparent); | 
|  | } | 
|  | break; | 
|  | default: | 
|  | SkASSERT(false); | 
|  | } | 
|  | bitmap.unlockPixels(); | 
|  |  | 
|  | if (isTransparent != NULL) { | 
|  | *isTransparent = transparent; | 
|  | } | 
|  | if (extractAlpha && (transparent || isOpaque)) { | 
|  | SkSafeUnref(stream); | 
|  | return NULL; | 
|  | } | 
|  | return stream; | 
|  | } | 
|  |  | 
|  | static SkPDFArray* make_indexed_color_space(SkColorTable* table) { | 
|  | SkPDFArray* result = new SkPDFArray(); | 
|  | result->reserve(4); | 
|  | result->appendName("Indexed"); | 
|  | result->appendName("DeviceRGB"); | 
|  | result->appendInt(table->count() - 1); | 
|  |  | 
|  | // Potentially, this could be represented in fewer bytes with a stream. | 
|  | // Max size as a string is 1.5k. | 
|  | SkString index; | 
|  | for (int i = 0; i < table->count(); i++) { | 
|  | char buf[3]; | 
|  | SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]); | 
|  | buf[0] = SkGetPackedR32(color); | 
|  | buf[1] = SkGetPackedG32(color); | 
|  | buf[2] = SkGetPackedB32(color); | 
|  | index.append(buf, 3); | 
|  | } | 
|  | result->append(new SkPDFString(index))->unref(); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Removes the alpha component of an ARGB color (including unpremultiply) while | 
|  | * keeping the output in the same format as the input. | 
|  | */ | 
|  | static uint32_t remove_alpha_argb8888(uint32_t pmColor) { | 
|  | SkColor color = SkUnPreMultiply::PMColorToColor(pmColor); | 
|  | return SkPackARGB32NoCheck(SK_AlphaOPAQUE, | 
|  | SkColorGetR(color), | 
|  | SkColorGetG(color), | 
|  | SkColorGetB(color)); | 
|  | } | 
|  |  | 
|  | static uint16_t remove_alpha_argb4444(uint16_t pmColor) { | 
|  | return SkPixel32ToPixel4444( | 
|  | remove_alpha_argb8888(SkPixel4444ToPixel32(pmColor))); | 
|  | } | 
|  |  | 
|  | static uint32_t get_argb8888_neighbor_avg_color(const SkBitmap& bitmap, | 
|  | int xOrig, int yOrig) { | 
|  | uint8_t count = 0; | 
|  | uint16_t r = 0; | 
|  | uint16_t g = 0; | 
|  | uint16_t b = 0; | 
|  |  | 
|  | for (int y = yOrig - 1; y <= yOrig + 1; y++) { | 
|  | if (y < 0 || y >= bitmap.height()) { | 
|  | continue; | 
|  | } | 
|  | uint32_t* src = bitmap.getAddr32(0, y); | 
|  | for (int x = xOrig - 1; x <= xOrig + 1; x++) { | 
|  | if (x < 0 || x >= bitmap.width()) { | 
|  | continue; | 
|  | } | 
|  | if (SkGetPackedA32(src[x]) != SK_AlphaTRANSPARENT) { | 
|  | uint32_t color = remove_alpha_argb8888(src[x]); | 
|  | r += SkGetPackedR32(color); | 
|  | g += SkGetPackedG32(color); | 
|  | b += SkGetPackedB32(color); | 
|  | count++; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (count == 0) { | 
|  | return SkPackARGB32NoCheck(SK_AlphaOPAQUE, 0, 0, 0); | 
|  | } else { | 
|  | return SkPackARGB32NoCheck(SK_AlphaOPAQUE, | 
|  | r / count, g / count, b / count); | 
|  | } | 
|  | } | 
|  |  | 
|  | static uint16_t get_argb4444_neighbor_avg_color(const SkBitmap& bitmap, | 
|  | int xOrig, int yOrig) { | 
|  | uint8_t count = 0; | 
|  | uint8_t r = 0; | 
|  | uint8_t g = 0; | 
|  | uint8_t b = 0; | 
|  |  | 
|  | for (int y = yOrig - 1; y <= yOrig + 1; y++) { | 
|  | if (y < 0 || y >= bitmap.height()) { | 
|  | continue; | 
|  | } | 
|  | uint16_t* src = bitmap.getAddr16(0, y); | 
|  | for (int x = xOrig - 1; x <= xOrig + 1; x++) { | 
|  | if (x < 0 || x >= bitmap.width()) { | 
|  | continue; | 
|  | } | 
|  | if ((SkGetPackedA4444(src[x]) & 0x0F) != SK_AlphaTRANSPARENT) { | 
|  | uint16_t color = remove_alpha_argb4444(src[x]); | 
|  | r += SkGetPackedR4444(color); | 
|  | g += SkGetPackedG4444(color); | 
|  | b += SkGetPackedB4444(color); | 
|  | count++; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (count == 0) { | 
|  | return SkPackARGB4444(SK_AlphaOPAQUE & 0x0F, 0, 0, 0); | 
|  | } else { | 
|  | return SkPackARGB4444(SK_AlphaOPAQUE & 0x0F, | 
|  | r / count, g / count, b / count); | 
|  | } | 
|  | } | 
|  |  | 
|  | static SkBitmap unpremultiply_bitmap(const SkBitmap& bitmap, | 
|  | const SkIRect& srcRect) { | 
|  | SkBitmap outBitmap; | 
|  | outBitmap.allocPixels(bitmap.info().makeWH(srcRect.width(), srcRect.height())); | 
|  | int dstRow = 0; | 
|  |  | 
|  | outBitmap.lockPixels(); | 
|  | bitmap.lockPixels(); | 
|  | switch (bitmap.colorType()) { | 
|  | case kARGB_4444_SkColorType: { | 
|  | for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | 
|  | uint16_t* dst = outBitmap.getAddr16(0, dstRow); | 
|  | uint16_t* src = bitmap.getAddr16(0, y); | 
|  | for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { | 
|  | uint8_t a = SkGetPackedA4444(src[x]); | 
|  | // It is necessary to average the color component of | 
|  | // transparent pixels with their surrounding neighbors | 
|  | // since the PDF renderer may separately re-sample the | 
|  | // alpha and color channels when the image is not | 
|  | // displayed at its native resolution. Since an alpha of | 
|  | // zero gives no information about the color component, | 
|  | // the pathological case is a white image with sharp | 
|  | // transparency bounds - the color channel goes to black, | 
|  | // and the should-be-transparent pixels are rendered | 
|  | // as grey because of the separate soft mask and color | 
|  | // resizing. | 
|  | if (a == (SK_AlphaTRANSPARENT & 0x0F)) { | 
|  | *dst = get_argb4444_neighbor_avg_color(bitmap, x, y); | 
|  | } else { | 
|  | *dst = remove_alpha_argb4444(src[x]); | 
|  | } | 
|  | dst++; | 
|  | } | 
|  | dstRow++; | 
|  | } | 
|  | break; | 
|  | } | 
|  | case kN32_SkColorType: { | 
|  | for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { | 
|  | uint32_t* dst = outBitmap.getAddr32(0, dstRow); | 
|  | uint32_t* src = bitmap.getAddr32(0, y); | 
|  | for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { | 
|  | uint8_t a = SkGetPackedA32(src[x]); | 
|  | if (a == SK_AlphaTRANSPARENT) { | 
|  | *dst = get_argb8888_neighbor_avg_color(bitmap, x, y); | 
|  | } else { | 
|  | *dst = remove_alpha_argb8888(src[x]); | 
|  | } | 
|  | dst++; | 
|  | } | 
|  | dstRow++; | 
|  | } | 
|  | break; | 
|  | } | 
|  | default: | 
|  | SkASSERT(false); | 
|  | } | 
|  | bitmap.unlockPixels(); | 
|  | outBitmap.unlockPixels(); | 
|  |  | 
|  | outBitmap.setImmutable(); | 
|  |  | 
|  | return outBitmap; | 
|  | } | 
|  |  | 
|  | // static | 
|  | SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap, | 
|  | const SkIRect& srcRect, | 
|  | SkPicture::EncodeBitmap encoder) { | 
|  | if (bitmap.colorType() == kUnknown_SkColorType) { | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | bool isTransparent = false; | 
|  | SkAutoTUnref<SkStream> alphaData; | 
|  | if (!bitmap.isOpaque()) { | 
|  | // Note that isOpaque is not guaranteed to return false for bitmaps | 
|  | // with alpha support but a completely opaque alpha channel, | 
|  | // so alphaData may still be NULL if we have a completely opaque | 
|  | // (or transparent) bitmap. | 
|  | alphaData.reset( | 
|  | extract_image_data(bitmap, srcRect, true, &isTransparent)); | 
|  | } | 
|  | if (isTransparent) { | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | SkPDFImage* image; | 
|  | SkColorType colorType = bitmap.colorType(); | 
|  | if (alphaData.get() != NULL && (kN32_SkColorType == colorType || | 
|  | kARGB_4444_SkColorType == colorType)) { | 
|  | SkBitmap unpremulBitmap = unpremultiply_bitmap(bitmap, srcRect); | 
|  | image = SkNEW_ARGS(SkPDFImage, (NULL, unpremulBitmap, false, | 
|  | SkIRect::MakeWH(srcRect.width(), srcRect.height()), | 
|  | encoder)); | 
|  | } else { | 
|  | image = SkNEW_ARGS(SkPDFImage, (NULL, bitmap, false, srcRect, encoder)); | 
|  | } | 
|  | if (alphaData.get() != NULL) { | 
|  | SkAutoTUnref<SkPDFImage> mask( | 
|  | SkNEW_ARGS(SkPDFImage, (alphaData.get(), bitmap, | 
|  | true, srcRect, NULL))); | 
|  | image->addSMask(mask); | 
|  | } | 
|  |  | 
|  | return image; | 
|  | } | 
|  |  | 
|  | SkPDFImage::~SkPDFImage() { | 
|  | fResources.unrefAll(); | 
|  | } | 
|  |  | 
|  | SkPDFImage* SkPDFImage::addSMask(SkPDFImage* mask) { | 
|  | fResources.push(mask); | 
|  | mask->ref(); | 
|  | insert("SMask", new SkPDFObjRef(mask))->unref(); | 
|  | return mask; | 
|  | } | 
|  |  | 
|  | void SkPDFImage::getResources(const SkTSet<SkPDFObject*>& knownResourceObjects, | 
|  | SkTSet<SkPDFObject*>* newResourceObjects) { | 
|  | GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects); | 
|  | } | 
|  |  | 
|  | SkPDFImage::SkPDFImage(SkStream* stream, | 
|  | const SkBitmap& bitmap, | 
|  | bool isAlpha, | 
|  | const SkIRect& srcRect, | 
|  | SkPicture::EncodeBitmap encoder) | 
|  | : fIsAlpha(isAlpha), | 
|  | fSrcRect(srcRect), | 
|  | fEncoder(encoder) { | 
|  |  | 
|  | if (bitmap.isImmutable()) { | 
|  | fBitmap = bitmap; | 
|  | } else { | 
|  | bitmap.deepCopyTo(&fBitmap); | 
|  | fBitmap.setImmutable(); | 
|  | } | 
|  |  | 
|  | if (stream != NULL) { | 
|  | this->setData(stream); | 
|  | fStreamValid = true; | 
|  | } else { | 
|  | fStreamValid = false; | 
|  | } | 
|  |  | 
|  | SkColorType colorType = fBitmap.colorType(); | 
|  |  | 
|  | insertName("Type", "XObject"); | 
|  | insertName("Subtype", "Image"); | 
|  |  | 
|  | bool alphaOnly = (kAlpha_8_SkColorType == colorType); | 
|  |  | 
|  | if (!isAlpha && alphaOnly) { | 
|  | // For alpha only images, we stretch a single pixel of black for | 
|  | // the color/shape part. | 
|  | SkAutoTUnref<SkPDFInt> one(new SkPDFInt(1)); | 
|  | insert("Width", one.get()); | 
|  | insert("Height", one.get()); | 
|  | } else { | 
|  | insertInt("Width", fSrcRect.width()); | 
|  | insertInt("Height", fSrcRect.height()); | 
|  | } | 
|  |  | 
|  | if (isAlpha || alphaOnly) { | 
|  | insertName("ColorSpace", "DeviceGray"); | 
|  | } else if (kIndex_8_SkColorType == colorType) { | 
|  | SkAutoLockPixels alp(fBitmap); | 
|  | insert("ColorSpace", | 
|  | make_indexed_color_space(fBitmap.getColorTable()))->unref(); | 
|  | } else { | 
|  | insertName("ColorSpace", "DeviceRGB"); | 
|  | } | 
|  |  | 
|  | int bitsPerComp = 8; | 
|  | if (kARGB_4444_SkColorType == colorType) { | 
|  | bitsPerComp = 4; | 
|  | } | 
|  | insertInt("BitsPerComponent", bitsPerComp); | 
|  |  | 
|  | if (kRGB_565_SkColorType == colorType) { | 
|  | SkASSERT(!isAlpha); | 
|  | SkAutoTUnref<SkPDFInt> zeroVal(new SkPDFInt(0)); | 
|  | SkAutoTUnref<SkPDFScalar> scale5Val( | 
|  | new SkPDFScalar(8.2258f));  // 255/2^5-1 | 
|  | SkAutoTUnref<SkPDFScalar> scale6Val( | 
|  | new SkPDFScalar(4.0476f));  // 255/2^6-1 | 
|  | SkAutoTUnref<SkPDFArray> decodeValue(new SkPDFArray()); | 
|  | decodeValue->reserve(6); | 
|  | decodeValue->append(zeroVal.get()); | 
|  | decodeValue->append(scale5Val.get()); | 
|  | decodeValue->append(zeroVal.get()); | 
|  | decodeValue->append(scale6Val.get()); | 
|  | decodeValue->append(zeroVal.get()); | 
|  | decodeValue->append(scale5Val.get()); | 
|  | insert("Decode", decodeValue.get()); | 
|  | } | 
|  | } | 
|  |  | 
|  | SkPDFImage::SkPDFImage(SkPDFImage& pdfImage) | 
|  | : SkPDFStream(pdfImage), | 
|  | fBitmap(pdfImage.fBitmap), | 
|  | fIsAlpha(pdfImage.fIsAlpha), | 
|  | fSrcRect(pdfImage.fSrcRect), | 
|  | fEncoder(pdfImage.fEncoder), | 
|  | fStreamValid(pdfImage.fStreamValid) { | 
|  | // Nothing to do here - the image params are already copied in SkPDFStream's | 
|  | // constructor, and the bitmap will be regenerated and encoded in | 
|  | // populate. | 
|  | } | 
|  |  | 
|  | bool SkPDFImage::populate(SkPDFCatalog* catalog) { | 
|  | if (getState() == kUnused_State) { | 
|  | // Initializing image data for the first time. | 
|  | SkDynamicMemoryWStream dctCompressedWStream; | 
|  | if (!skip_compression(catalog) && fEncoder && | 
|  | get_uncompressed_size(fBitmap, fSrcRect) > 1) { | 
|  | SkBitmap subset; | 
|  | // Extract subset | 
|  | if (!fBitmap.extractSubset(&subset, fSrcRect)) { | 
|  | return false; | 
|  | } | 
|  | size_t pixelRefOffset = 0; | 
|  | SkAutoTUnref<SkData> data(fEncoder(&pixelRefOffset, subset)); | 
|  | if (data.get() && data->size() < get_uncompressed_size(fBitmap, | 
|  | fSrcRect)) { | 
|  | this->setData(data.get()); | 
|  |  | 
|  | insertName("Filter", "DCTDecode"); | 
|  | insertInt("ColorTransform", kNoColorTransform); | 
|  | insertInt("Length", this->dataSize()); | 
|  | setState(kCompressed_State); | 
|  | return true; | 
|  | } | 
|  | } | 
|  | // Fallback method | 
|  | if (!fStreamValid) { | 
|  | SkAutoTUnref<SkStream> stream( | 
|  | extract_image_data(fBitmap, fSrcRect, fIsAlpha, NULL)); | 
|  | this->setData(stream); | 
|  | fStreamValid = true; | 
|  | } | 
|  | return INHERITED::populate(catalog); | 
|  | } else if (getState() == kNoCompression_State && | 
|  | !skip_compression(catalog) && | 
|  | (SkFlate::HaveFlate() || fEncoder)) { | 
|  | // Compression has not been requested when the stream was first created, | 
|  | // but the new catalog wants it compressed. | 
|  | if (!getSubstitute()) { | 
|  | SkPDFStream* substitute = SkNEW_ARGS(SkPDFImage, (*this)); | 
|  | setSubstitute(substitute); | 
|  | catalog->setSubstitute(this, substitute); | 
|  | } | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } |