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