Index: src/pdf/SkPDFImage.cpp |
diff --git a/src/pdf/SkPDFImage.cpp b/src/pdf/SkPDFImage.cpp |
index a5cb4c20d1b9e187948cc6654d590b219df4d686..d92afd1fe7145bf3cb74c0715721c1b7ff505025 100644 |
--- a/src/pdf/SkPDFImage.cpp |
+++ b/src/pdf/SkPDFImage.cpp |
@@ -10,6 +10,8 @@ |
#include "SkBitmap.h" |
#include "SkColor.h" |
#include "SkColorPriv.h" |
+#include "SkData.h" |
+#include "SkFlate.h" |
#include "SkPDFCatalog.h" |
#include "SkRect.h" |
#include "SkStream.h" |
@@ -18,18 +20,58 @@ |
namespace { |
-void extractImageData(const SkBitmap& bitmap, const SkIRect& srcRect, |
- SkStream** imageData, SkStream** alphaData) { |
- SkMemoryStream* image = NULL; |
- SkMemoryStream* alpha = NULL; |
- bool hasAlpha = false; |
- bool isTransparent = false; |
+#define kNoColorTransform 0 |
+ |
+static bool skip_compression(SkPDFCatalog* catalog) { |
+ return SkToBool(catalog->getDocumentFlags() & |
+ SkPDFDocument::kFavorSpeedOverSize_Flags); |
+} |
+ |
+static size_t get_row_bytes(const SkBitmap& bitmap, |
+ const SkIRect& srcRect) { |
+ switch (bitmap.getConfig()) { |
+ case SkBitmap::kIndex8_Config: |
+ return srcRect.width(); |
+ case SkBitmap::kARGB_4444_Config: |
+ return (srcRect.width() * 3 + 1) / 2; |
+ case SkBitmap::kRGB_565_Config: |
+ return srcRect.width() * 3; |
+ case SkBitmap::kARGB_8888_Config: |
+ return srcRect.width() * 3; |
+ case SkBitmap::kA1_Config: |
+ case SkBitmap::kA8_Config: |
+ return 1; |
+ default: |
+ SkASSERT(false); |
+ return 0; |
+ } |
+} |
+static size_t get_uncompressed_size(const SkBitmap& bitmap, |
+ const SkIRect& srcRect) { |
+ switch (bitmap.getConfig()) { |
+ case SkBitmap::kIndex8_Config: |
+ case SkBitmap::kARGB_4444_Config: |
+ case SkBitmap::kRGB_565_Config: |
+ case SkBitmap::kARGB_8888_Config: |
+ return get_row_bytes(bitmap, srcRect) * srcRect.height(); |
+ case SkBitmap::kA1_Config: |
+ case SkBitmap::kA8_Config: |
+ return 1; |
+ default: |
+ SkASSERT(false); |
+ return 0; |
+ } |
+} |
+ |
+static SkStream* extract_image_data(const SkBitmap& bitmap, |
+ const SkIRect& srcRect) { |
+ const int rowBytes = get_row_bytes(bitmap, srcRect); |
+ SkMemoryStream* image = new SkMemoryStream(get_uncompressed_size(bitmap, |
+ srcRect)); |
bitmap.lockPixels(); |
switch (bitmap.getConfig()) { |
case SkBitmap::kIndex8_Config: { |
- const int rowBytes = srcRect.width(); |
- image = new SkMemoryStream(rowBytes * srcRect.height()); |
uint8_t* dst = (uint8_t*)image->getMemoryBase(); |
for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { |
memcpy(dst, bitmap.getAddr8(srcRect.fLeft, y), rowBytes); |
@@ -38,13 +80,7 @@ void extractImageData(const SkBitmap& bitmap, const SkIRect& srcRect, |
break; |
} |
case SkBitmap::kARGB_4444_Config: { |
- isTransparent = true; |
- const int rowBytes = (srcRect.width() * 3 + 1) / 2; |
- const int alphaRowBytes = (srcRect.width() + 1) / 2; |
- image = new SkMemoryStream(rowBytes * srcRect.height()); |
- alpha = new SkMemoryStream(alphaRowBytes * srcRect.height()); |
uint8_t* dst = (uint8_t*)image->getMemoryBase(); |
- uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase(); |
for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { |
uint16_t* src = bitmap.getAddr16(0, y); |
int x; |
@@ -56,36 +92,17 @@ void extractImageData(const SkBitmap& bitmap, const SkIRect& srcRect, |
dst[2] = (SkGetPackedG4444(src[x + 1]) << 4) | |
SkGetPackedB4444(src[x + 1]); |
dst += 3; |
- alphaDst[0] = (SkGetPackedA4444(src[x]) << 4) | |
- SkGetPackedA4444(src[x + 1]); |
- if (alphaDst[0] != 0xFF) { |
- hasAlpha = true; |
- } |
- if (alphaDst[0]) { |
- isTransparent = false; |
- } |
- alphaDst++; |
} |
if (srcRect.width() & 1) { |
dst[0] = (SkGetPackedR4444(src[x]) << 4) | |
SkGetPackedG4444(src[x]); |
dst[1] = (SkGetPackedB4444(src[x]) << 4); |
dst += 2; |
- alphaDst[0] = (SkGetPackedA4444(src[x]) << 4); |
- if (alphaDst[0] != 0xF0) { |
- hasAlpha = true; |
- } |
- if (alphaDst[0] & 0xF0) { |
- isTransparent = false; |
- } |
- alphaDst++; |
} |
} |
break; |
} |
case SkBitmap::kRGB_565_Config: { |
- const int rowBytes = srcRect.width() * 3; |
- image = new SkMemoryStream(rowBytes * srcRect.height()); |
uint8_t* dst = (uint8_t*)image->getMemoryBase(); |
for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { |
uint16_t* src = bitmap.getAddr16(0, y); |
@@ -99,12 +116,7 @@ void extractImageData(const SkBitmap& bitmap, const SkIRect& srcRect, |
break; |
} |
case SkBitmap::kARGB_8888_Config: { |
- isTransparent = true; |
- const int rowBytes = srcRect.width() * 3; |
- image = new SkMemoryStream(rowBytes * srcRect.height()); |
- alpha = new SkMemoryStream(srcRect.width() * srcRect.height()); |
uint8_t* dst = (uint8_t*)image->getMemoryBase(); |
- uint8_t* alphaDst = (uint8_t*)alpha->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++) { |
@@ -112,12 +124,79 @@ void extractImageData(const SkBitmap& bitmap, const SkIRect& srcRect, |
dst[1] = SkGetPackedG32(src[x]); |
dst[2] = SkGetPackedB32(src[x]); |
dst += 3; |
+ } |
+ } |
+ break; |
+ } |
+ case SkBitmap::kA1_Config: |
+ case SkBitmap::kA8_Config: { |
+ ((uint8_t*)image->getMemoryBase())[0] = 0; |
+ break; |
+ } |
+ default: |
+ SkASSERT(false); |
+ } |
+ bitmap.unlockPixels(); |
+ |
+ return image; |
+} |
+ |
+// Extract the alpha data from a SkBitmap and output it in a SkStream. |
+// alphaData may be NULL if there was no alpha data to extract (image |
+// completely opaque). |
+// isTransparent outputs true if the alpha is completely transparent. |
+static SkStream* extract_alpha_data(const SkBitmap& bitmap, |
+ const SkIRect& srcRect, |
+ bool* isTransparent) { |
+ SkMemoryStream* alpha = NULL; |
+ bool hasAlpha = false; |
+ *isTransparent = true; |
+ |
+ bitmap.lockPixels(); |
+ switch (bitmap.getConfig()) { |
+ case SkBitmap::kARGB_4444_Config: { |
+ const int alphaRowBytes = (srcRect.width() + 1) / 2; |
+ alpha = new SkMemoryStream(alphaRowBytes * srcRect.height()); |
+ uint8_t* alphaDst = (uint8_t*)alpha->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) { |
+ alphaDst[0] = (SkGetPackedA4444(src[x]) << 4) | |
+ SkGetPackedA4444(src[x + 1]); |
+ if (alphaDst[0] != 0xFF) { |
edisonn
2013/08/21 18:40:51
alphaDst[0] != 0xFF
if (alphaDst[0]) {
alphaDst[0]
ducky
2013/08/21 19:27:53
Done.
I think the old code was written before thin
|
+ hasAlpha = true; |
+ } |
+ if (alphaDst[0]) { |
+ *isTransparent = false; |
+ } |
+ alphaDst++; |
+ } |
+ if (srcRect.width() & 1) { |
+ alphaDst[0] = (SkGetPackedA4444(src[x]) << 4); |
+ if (alphaDst[0] != 0xF0) { |
+ hasAlpha = true; |
+ } |
+ if (alphaDst[0] & 0xF0) { |
+ *isTransparent = false; |
+ } |
+ alphaDst++; |
+ } |
+ } |
+ break; |
+ } |
+ case SkBitmap::kARGB_8888_Config: { |
+ alpha = new SkMemoryStream(srcRect.width() * srcRect.height()); |
+ uint8_t* alphaDst = (uint8_t*)alpha->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++) { |
alphaDst[0] = SkGetPackedA32(src[x]); |
if (alphaDst[0] != 0xFF) { |
hasAlpha = true; |
} |
if (alphaDst[0]) { |
- isTransparent = false; |
+ *isTransparent = false; |
} |
alphaDst++; |
} |
@@ -125,10 +204,6 @@ void extractImageData(const SkBitmap& bitmap, const SkIRect& srcRect, |
break; |
} |
case SkBitmap::kA1_Config: { |
- isTransparent = true; |
- image = new SkMemoryStream(1); |
- ((uint8_t*)image->getMemoryBase())[0] = 0; |
- |
const int alphaRowBytes = (srcRect.width() + 7) / 8; |
alpha = new SkMemoryStream(alphaRowBytes * srcRect.height()); |
uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase(); |
@@ -149,7 +224,7 @@ void extractImageData(const SkBitmap& bitmap, const SkIRect& srcRect, |
hasAlpha = true; |
} |
if (x + 7 < srcRect.fRight && alphaDst[0]) { |
- isTransparent = false; |
+ *isTransparent = false; |
} |
alphaDst++; |
} |
@@ -161,16 +236,12 @@ void extractImageData(const SkBitmap& bitmap, const SkIRect& srcRect, |
hasAlpha = true; |
} |
if (srcRect.width() % 8 && (alphaDst[-1] & mask)) { |
- isTransparent = false; |
+ *isTransparent = false; |
} |
} |
break; |
} |
case SkBitmap::kA8_Config: { |
- isTransparent = true; |
- image = new SkMemoryStream(1); |
- ((uint8_t*)image->getMemoryBase())[0] = 0; |
- |
const int alphaRowBytes = srcRect.width(); |
alpha = new SkMemoryStream(alphaRowBytes * srcRect.height()); |
uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase(); |
@@ -182,28 +253,28 @@ void extractImageData(const SkBitmap& bitmap, const SkIRect& srcRect, |
hasAlpha = true; |
} |
if (alphaDst[0]) { |
- isTransparent = false; |
+ *isTransparent = false; |
} |
alphaDst++; |
} |
} |
break; |
} |
+ case SkBitmap::kRGB_565_Config: |
+ case SkBitmap::kIndex8_Config: { |
+ *isTransparent = false; |
+ break; |
+ } |
default: |
SkASSERT(false); |
} |
bitmap.unlockPixels(); |
- if (isTransparent) { |
- SkSafeUnref(image); |
- } else { |
- *imageData = image; |
- } |
- |
- if (isTransparent || !hasAlpha) { |
+ if (!hasAlpha || *isTransparent) { |
SkSafeUnref(alpha); |
+ return NULL; |
} else { |
- *alphaData = alpha; |
+ return alpha; |
} |
} |
@@ -238,26 +309,14 @@ SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap, |
if (bitmap.getConfig() == SkBitmap::kNo_Config) { |
return NULL; |
} |
+ SkPDFImage* image = SkNEW_ARGS(SkPDFImage, (bitmap, srcRect, encoder)); |
- SkStream* imageData = NULL; |
- SkStream* alphaData = NULL; |
- extractImageData(bitmap, srcRect, &imageData, &alphaData); |
- SkAutoUnref unrefImageData(imageData); |
- SkAutoUnref unrefAlphaData(alphaData); |
- if (!imageData) { |
- SkASSERT(!alphaData); |
+ if (image->isEmpty()) { |
+ image->unref(); |
return NULL; |
+ } else { |
+ return image; |
} |
- |
- SkPDFImage* image = |
- SkNEW_ARGS(SkPDFImage, (imageData, bitmap, srcRect, false, encoder)); |
- |
- if (alphaData != NULL) { |
- // Don't try to use DCT compression with alpha because alpha is small |
- // anyway and it could lead to artifacts. |
- image->addSMask(SkNEW_ARGS(SkPDFImage, (alphaData, bitmap, srcRect, true, NULL)))->unref(); |
- } |
- return image; |
} |
SkPDFImage::~SkPDFImage() { |
@@ -276,51 +335,89 @@ void SkPDFImage::getResources(const SkTSet<SkPDFObject*>& knownResourceObjects, |
GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects); |
} |
-SkPDFImage::SkPDFImage(SkStream* imageData, |
- const SkBitmap& bitmap, |
+SkPDFImage::SkPDFImage(const SkBitmap& bitmap, |
const SkIRect& srcRect, |
- bool doingAlpha, |
EncodeToDCTStream encoder) |
- : SkPDFImageStream(imageData, bitmap, srcRect, encoder) { |
- SkBitmap::Config config = bitmap.getConfig(); |
- bool alphaOnly = (config == SkBitmap::kA1_Config || |
- config == SkBitmap::kA8_Config); |
+ : fBitmap(bitmap), |
+ fSrcRect(srcRect), |
+ fEncoder(encoder) { |
+ bool isTransparent; |
+ SkAutoTUnref<SkStream> alphaData(extract_alpha_data(bitmap, srcRect, |
+ &isTransparent)); |
+ if (isTransparent) { |
+ fSrcRect = SkIRect::MakeEmpty(); |
+ return; |
+ } |
+ if (alphaData.get() != NULL) { |
+ addSMask(SkNEW_ARGS(SkPDFImage, |
+ (alphaData.get(), bitmap, srcRect)))->unref(); |
+ } |
+ |
+ initImageParams(false); |
+} |
+ |
+SkPDFImage::SkPDFImage(SkStream* stream, const SkBitmap& bitmap, |
+ const SkIRect& srcRect) |
+ : fBitmap(bitmap), |
+ fSrcRect(srcRect), |
+ fEncoder(NULL) { |
+ setData(stream); |
+ insertInt("Length", getData()->getLength()); |
+ setState(kNoCompression_State); |
+ |
+ initImageParams(true); |
+} |
+ |
+SkPDFImage::SkPDFImage(SkPDFImage& pdfImage) |
+ : SkPDFStream(pdfImage), |
+ fBitmap(pdfImage.fBitmap), |
+ fSrcRect(pdfImage.fSrcRect), |
+ fEncoder(pdfImage.fEncoder){ |
+ // Nothing to do here - the image params are already copied in SkPDFStream's |
+ // constructor, and the bitmap will be regenerated and re-encoded in |
+ // populate. |
+} |
+ |
+void SkPDFImage::initImageParams(bool isAlpha) { |
+ SkBitmap::Config config = fBitmap.getConfig(); |
insertName("Type", "XObject"); |
insertName("Subtype", "Image"); |
- if (!doingAlpha && alphaOnly) { |
+ bool alphaOnly = (config == SkBitmap::kA1_Config || |
+ config == SkBitmap::kA8_Config); |
+ |
+ 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", srcRect.width()); |
- insertInt("Height", srcRect.height()); |
+ insertInt("Width", fSrcRect.width()); |
+ insertInt("Height", fSrcRect.height()); |
} |
- // if (!image mask) { |
- if (doingAlpha || alphaOnly) { |
+ if (isAlpha || alphaOnly) { |
insertName("ColorSpace", "DeviceGray"); |
} else if (config == SkBitmap::kIndex8_Config) { |
- SkAutoLockPixels alp(bitmap); |
+ SkAutoLockPixels alp(fBitmap); |
insert("ColorSpace", |
- makeIndexedColorSpace(bitmap.getColorTable()))->unref(); |
+ makeIndexedColorSpace(fBitmap.getColorTable()))->unref(); |
} else { |
insertName("ColorSpace", "DeviceRGB"); |
} |
- // } |
int bitsPerComp = 8; |
if (config == SkBitmap::kARGB_4444_Config) { |
bitsPerComp = 4; |
- } else if (doingAlpha && config == SkBitmap::kA1_Config) { |
+ } else if (isAlpha && config == SkBitmap::kA1_Config) { |
bitsPerComp = 1; |
} |
insertInt("BitsPerComponent", bitsPerComp); |
if (config == SkBitmap::kRGB_565_Config) { |
+ SkASSERT(!isAlpha); |
SkAutoTUnref<SkPDFInt> zeroVal(new SkPDFInt(0)); |
SkAutoTUnref<SkPDFScalar> scale5Val( |
new SkPDFScalar(SkFloatToScalar(8.2258f))); // 255/2^5-1 |
@@ -337,3 +434,54 @@ SkPDFImage::SkPDFImage(SkStream* imageData, |
insert("Decode", decodeValue.get()); |
} |
} |
+ |
+SkStream* SkPDFImage::getCompressedStream() { |
+ SkDynamicMemoryWStream dctCompressedWStream; |
+ if (fEncoder && fEncoder(&dctCompressedWStream, fBitmap, fSrcRect)) { |
+ // Ensure compressed version is smaller than the uncompressed version |
+ if (dctCompressedWStream.getOffset() < |
+ get_uncompressed_size(fBitmap, fSrcRect)) { |
+ SkData* data = dctCompressedWStream.copyToData(); |
+ SkMemoryStream* stream = SkNEW_ARGS(SkMemoryStream, (data)); |
+ data->unref(); |
+ return stream; |
+ } |
+ } |
+ return NULL; |
+} |
+ |
+bool SkPDFImage::populate(SkPDFCatalog* catalog) { |
+ if (getState() == kUnused_State) { |
+ // Initializing image data for the first time. |
+ if (!skip_compression(catalog)) { |
+ SkAutoTUnref<SkStream> stream(getCompressedStream()); |
+ if (stream.get() != NULL) { |
+ setData(stream.get()); |
+ insertName("Filter", "DCTDecode"); |
+ insertInt("ColorTransform", kNoColorTransform); |
+ insertInt("Length", getData()->getLength()); |
+ setState(kCompressed_State); |
+ } |
+ } |
+ |
+ // Fallback if it doesn't work. |
+ if (getState() == kUnused_State) { |
+ SkAutoTUnref<SkStream> stream(extract_image_data(fBitmap, |
+ fSrcRect)); |
+ setData(stream.get()); |
+ 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 a new Catalog would want it compressed. |
+ if (!getSubstitute()) { |
+ SkPDFStream* substitute = SkNEW_ARGS(SkPDFImage, (*this)); |
+ setSubstitute(substitute); |
+ catalog->setSubstitute(this, substitute); |
+ } |
+ return false; |
+ } |
+ return true; |
+} |