Index: src/pdf/SkPDFImage.cpp |
diff --git a/src/pdf/SkPDFImage.cpp b/src/pdf/SkPDFImage.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e3971aa57ac76bddc39889a11e0d501e0b130eec |
--- /dev/null |
+++ b/src/pdf/SkPDFImage.cpp |
@@ -0,0 +1,727 @@ |
+/* |
+ * 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 "SkPDFBitmap.h" |
+#include "SkPDFCatalog.h" |
+#include "SkPixelRef.h" |
+#include "SkRect.h" |
+#include "SkStream.h" |
+#include "SkString.h" |
+#include "SkUnPreMultiply.h" |
+ |
+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: |
+ case kGray_8_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_gray8_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++) { |
+ uint8_t* src = bitmap.getAddr8(0, y); |
+ for (int x = srcRect.fLeft; x < srcRect.fRight; x++) { |
+ dst[0] = dst[1] = dst[2] = src[x]; |
+ dst += 3; |
+ } |
+ } |
+ return stream; |
+} |
+ |
+static uint32_t get_argb8888_neighbor_avg_color(const SkBitmap& bitmap, |
+ int xOrig, |
+ int yOrig); |
+ |
+static SkStream* extract_argb8888_data(const SkBitmap& bitmap, |
+ const SkIRect& srcRect, |
+ bool extractAlpha, |
+ bool* isOpaque, |
+ bool* isTransparent) { |
+ size_t streamSize = extractAlpha ? srcRect.width() * srcRect.height() |
+ : get_uncompressed_size(bitmap, srcRect); |
+ SkStream* stream = SkNEW_ARGS(SkMemoryStream, (streamSize)); |
+ uint8_t* dst = (uint8_t*)stream->getMemoryBase(); |
+ |
+ const SkUnPreMultiply::Scale* scaleTable = SkUnPreMultiply::GetScaleTable(); |
+ |
+ 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++) { |
+ SkPMColor c = src[x]; |
+ U8CPU alpha = SkGetPackedA32(c); |
+ if (extractAlpha) { |
+ *isOpaque &= alpha == SK_AlphaOPAQUE; |
+ *isTransparent &= alpha == SK_AlphaTRANSPARENT; |
+ *dst++ = alpha; |
+ } else { |
+ if (SK_AlphaTRANSPARENT == alpha) { |
+ // 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. |
+ c = get_argb8888_neighbor_avg_color(bitmap, x, y); |
+ *dst++ = SkGetPackedR32(c); |
+ *dst++ = SkGetPackedG32(c); |
+ *dst++ = SkGetPackedB32(c); |
+ } else { |
+ SkUnPreMultiply::Scale s = scaleTable[alpha]; |
+ *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c)); |
+ *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(c)); |
+ *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c)); |
+ } |
+ } |
+ } |
+ } |
+ SkASSERT(dst == streamSize + (uint8_t*)stream->getMemoryBase()); |
+ 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 || |
+ kGray_8_SkColorType == colorType)) { |
+ if (isTransparent != NULL) { |
+ *isTransparent = false; |
+ } |
+ return NULL; |
+ } |
+ |
+ SkAutoLockPixels lock(bitmap); |
+ if (NULL == bitmap.getPixels()) { |
+ return NULL; |
+ } |
+ |
+ bool isOpaque = true; |
+ bool transparent = extractAlpha; |
+ SkAutoTDelete<SkStream> stream; |
+ |
+ switch (colorType) { |
+ case kIndex_8_SkColorType: |
+ if (!extractAlpha) { |
+ stream.reset(extract_index8_image(bitmap, srcRect)); |
+ } |
+ break; |
+ case kARGB_4444_SkColorType: |
+ stream.reset(extract_argb4444_data(bitmap, srcRect, extractAlpha, |
+ &isOpaque, &transparent)); |
+ break; |
+ case kRGB_565_SkColorType: |
+ if (!extractAlpha) { |
+ stream.reset(extract_rgb565_image(bitmap, srcRect)); |
+ } |
+ break; |
+ case kGray_8_SkColorType: |
+ if (!extractAlpha) { |
+ stream.reset(extract_gray8_image(bitmap, srcRect)); |
+ } |
+ break; |
+ case kN32_SkColorType: |
+ stream.reset(extract_argb8888_data(bitmap, srcRect, extractAlpha, |
+ &isOpaque, &transparent)); |
+ break; |
+ case kAlpha_8_SkColorType: |
+ if (!extractAlpha) { |
+ stream.reset(create_black_image()); |
+ } else { |
+ stream.reset(extract_a8_alpha(bitmap, srcRect, |
+ &isOpaque, &transparent)); |
+ } |
+ break; |
+ default: |
+ SkASSERT(false); |
+ } |
+ |
+ if (isTransparent != NULL) { |
+ *isTransparent = transparent; |
+ } |
+ if (extractAlpha && (transparent || isOpaque)) { |
+ return NULL; |
+ } |
+ return stream.detach(); |
+} |
+ |
+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; |
+ |
+ SkAutoLockPixels outBitmapPixelLock(outBitmap); |
+ SkAutoLockPixels bitmapPixelLock(bitmap); |
+ 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); |
+ } |
+ |
+ outBitmap.setImmutable(); |
+ |
+ return outBitmap; |
+} |
+ |
+// static |
+SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap, |
+ const SkIRect& srcRect) { |
+ if (bitmap.colorType() == kUnknown_SkColorType) { |
+ return NULL; |
+ } |
+ |
+ bool isTransparent = false; |
+ SkAutoTDelete<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)) { |
+ if (kN32_SkColorType == colorType) { |
+ image = SkNEW_ARGS(SkPDFImage, (NULL, bitmap, false, |
+ SkIRect::MakeWH(srcRect.width(), |
+ srcRect.height()))); |
+ } else { |
+ SkBitmap unpremulBitmap = unpremultiply_bitmap(bitmap, srcRect); |
+ image = SkNEW_ARGS(SkPDFImage, (NULL, unpremulBitmap, false, |
+ SkIRect::MakeWH(srcRect.width(), |
+ srcRect.height()))); |
+ } |
+ } else { |
+ image = SkNEW_ARGS(SkPDFImage, (NULL, bitmap, false, srcRect)); |
+ } |
+ if (alphaData.get() != NULL) { |
+ SkAutoTUnref<SkPDFImage> mask( |
+ SkNEW_ARGS(SkPDFImage, (alphaData.get(), bitmap, true, srcRect))); |
+ image->insert("SMask", new SkPDFObjRef(mask))->unref(); |
+ } |
+ return image; |
+} |
+ |
+SkPDFImage::~SkPDFImage() {} |
+ |
+SkPDFImage::SkPDFImage(SkStream* stream, |
+ const SkBitmap& bitmap, |
+ bool isAlpha, |
+ const SkIRect& srcRect) |
+ : fIsAlpha(isAlpha), |
+ fSrcRect(srcRect) { |
+ |
+ 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), |
+ 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. |
+ // Fallback method |
+ if (!fStreamValid) { |
+ SkAutoTDelete<SkStream> stream( |
+ extract_image_data(fBitmap, fSrcRect, fIsAlpha, NULL)); |
+ this->setData(stream); |
+ fStreamValid = true; |
+ } |
+ return INHERITED::populate(catalog); |
+ } |
+#ifndef SK_NO_FLATE |
+ else if (getState() == kNoCompression_State) { |
+ // 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; |
+ } |
+#endif // SK_NO_FLATE |
+ return true; |
+} |
+ |
+#if 0 // reenable when we can figure out the JPEG colorspace |
+namespace { |
+/** |
+ * This PDFObject assumes that its constructor was handed |
+ * Jpeg-encoded data that can be directly embedded into a PDF. |
+ */ |
+class PDFJPEGImage : public SkPDFObject { |
+ SkAutoTUnref<SkData> fData; |
+ int fWidth; |
+ int fHeight; |
+public: |
+ PDFJPEGImage(SkData* data, int width, int height) |
+ : fData(SkRef(data)), fWidth(width), fHeight(height) {} |
+ virtual void emitObject( |
+ SkWStream* stream, |
+ SkPDFCatalog* catalog, bool indirect) SK_OVERRIDE { |
+ if (indirect) { |
+ this->emitIndirectObject(stream, catalog); |
+ return; |
+ } |
+ SkASSERT(fData.get()); |
+ const char kPrefaceFormat[] = |
+ "<<" |
+ "/Type /XObject\n" |
+ "/Subtype /Image\n" |
+ "/Width %d\n" |
+ "/Height %d\n" |
+ "/ColorSpace /DeviceRGB\n" // or DeviceGray |
+ "/BitsPerComponent 8\n" |
+ "/Filter /DCTDecode\n" |
+ "/ColorTransform 0\n" |
+ "/Length " SK_SIZE_T_SPECIFIER "\n" |
+ ">> stream\n"; |
+ SkString preface( |
+ SkStringPrintf(kPrefaceFormat, fWidth, fHeight, fData->size())); |
+ const char kPostface[] = "\nendstream"; |
+ stream->write(preface.c_str(), preface.size()); |
+ stream->write(fData->data(), fData->size()); |
+ stream->write(kPostface, sizeof(kPostface)); |
+ } |
+}; |
+ |
+/** |
+ * If the bitmap is not subsetted, return its encoded data, if |
+ * availible. |
+ */ |
+static inline SkData* ref_encoded_data(const SkBitmap& bm) { |
+ if ((NULL == bm.pixelRef()) |
+ || !bm.pixelRefOrigin().isZero() |
+ || (bm.info().dimensions() != bm.pixelRef()->info().dimensions())) { |
+ return NULL; |
+ } |
+ return bm.pixelRef()->refEncodedData(); |
+} |
+ |
+/* |
+ * This functions may give false negatives but no false positives. |
+ */ |
+static bool is_jfif_jpeg(SkData* data) { |
+ if (!data || (data->size() < 11)) { |
+ return false; |
+ } |
+ const uint8_t bytesZeroToThree[] = {0xFF, 0xD8, 0xFF, 0xE0}; |
+ const uint8_t bytesSixToTen[] = {'J', 'F', 'I', 'F', 0}; |
+ // 0 1 2 3 4 5 6 7 8 9 10 |
+ // FF D8 FF E0 ?? ?? 'J' 'F' 'I' 'F' 00 ... |
+ return ((0 == memcmp(data->bytes(), bytesZeroToThree, |
+ sizeof(bytesZeroToThree))) |
+ && (0 == memcmp(data->bytes() + 6, bytesSixToTen, |
+ sizeof(bytesSixToTen)))); |
+} |
+} // namespace |
+#endif |
+ |
+SkPDFObject* SkPDFCreateImageObject(SkPDFCanon* canon, |
+ const SkBitmap& bitmap, |
+ const SkIRect& subset) { |
+ if (SkPDFObject* pdfBitmap = SkPDFBitmap::Create(canon, bitmap, subset)) { |
+ return pdfBitmap; |
+ } |
+#if 0 // reenable when we can figure out the JPEG colorspace |
+ if (SkIRect::MakeWH(bitmap.width(), bitmap.height()) == subset) { |
+ SkAutoTUnref<SkData> encodedData(ref_encoded_data(bitmap)); |
+ if (is_jfif_jpeg(encodedData)) { |
+ return SkNEW_ARGS(PDFJPEGImage, |
+ (encodedData, bitmap.width(), bitmap.height())); |
+ } |
+ } |
+#endif |
+ return SkPDFImage::CreateImage(bitmap, subset); |
+} |