diff options
author | commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-08-23 19:06:53 +0000 |
---|---|---|
committer | commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-08-23 19:06:53 +0000 |
commit | 181fcb4a21cb87292eded6675a1df05cf9c3aacc (patch) | |
tree | 79ecd5a526d018110cf7528a9c2e970328139c4d | |
parent | cbddc114197928b99cbe968504d82a2623e746c4 (diff) |
Refactor SkPDFImage
R=vandebo@chromium.org, edisonn@google.com
Author: richardlin@chromium.org
Review URL: https://chromiumcodereview.appspot.com/22889020
git-svn-id: http://skia.googlecode.com/svn/trunk@10896 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r-- | gyp/pdf.gypi | 2 | ||||
-rw-r--r-- | src/pdf/SkPDFImage.cpp | 573 | ||||
-rw-r--r-- | src/pdf/SkPDFImage.h | 42 | ||||
-rw-r--r-- | src/pdf/SkPDFImageStream.cpp | 79 | ||||
-rw-r--r-- | src/pdf/SkPDFImageStream.h | 53 |
5 files changed, 408 insertions, 341 deletions
diff --git a/gyp/pdf.gypi b/gyp/pdf.gypi index ae4a032505..a4b339214e 100644 --- a/gyp/pdf.gypi +++ b/gyp/pdf.gypi @@ -23,8 +23,6 @@ '<(skia_src_path)/pdf/SkPDFGraphicState.h', '<(skia_src_path)/pdf/SkPDFImage.cpp', '<(skia_src_path)/pdf/SkPDFImage.h', - '<(skia_src_path)/pdf/SkPDFImageStream.cpp', - '<(skia_src_path)/pdf/SkPDFImageStream.h', '<(skia_src_path)/pdf/SkPDFPage.cpp', '<(skia_src_path)/pdf/SkPDFPage.h', '<(skia_src_path)/pdf/SkPDFResourceDict.cpp', diff --git a/src/pdf/SkPDFImage.cpp b/src/pdf/SkPDFImage.cpp index a5cb4c20d1..6ede7aaf51 100644 --- a/src/pdf/SkPDFImage.cpp +++ b/src/pdf/SkPDFImage.cpp @@ -10,204 +10,311 @@ #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" -namespace { +static const int kNoColorTransform = 0; -void extractImageData(const SkBitmap& bitmap, const SkIRect& srcRect, - SkStream** imageData, SkStream** alphaData) { - SkMemoryStream* image = NULL; - SkMemoryStream* alpha = NULL; - bool hasAlpha = false; - bool isTransparent = false; +static bool skip_compression(SkPDFCatalog* catalog) { + return SkToBool(catalog->getDocumentFlags() & + SkPDFDocument::kFavorSpeedOverSize_Flags); +} - bitmap.lockPixels(); +static size_t get_uncompressed_size(const SkBitmap& bitmap, + const SkIRect& srcRect) { 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); - dst += rowBytes; + case SkBitmap::kIndex8_Config: + return srcRect.width() * srcRect.height(); + case SkBitmap::kARGB_4444_Config: + return ((srcRect.width() * 3 + 1) / 2) * srcRect.height(); + case SkBitmap::kRGB_565_Config: + return srcRect.width() * 3 * srcRect.height(); + case SkBitmap::kARGB_8888_Config: + return srcRect.width() * 3 * srcRect.height(); + case SkBitmap::kA1_Config: + case SkBitmap::kA8_Config: + 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; } - 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; - for (x = srcRect.fLeft; x + 1 < srcRect.fRight; x += 2) { - 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; - 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++; - } + 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; } - 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); - 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_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; } - 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++) { - dst[0] = SkGetPackedR32(src[x]); - dst[1] = SkGetPackedG32(src[x]); - dst[2] = SkGetPackedB32(src[x]); - dst += 3; - alphaDst[0] = SkGetPackedA32(src[x]); - if (alphaDst[0] != 0xFF) { - hasAlpha = true; - } - if (alphaDst[0]) { - isTransparent = false; - } - alphaDst++; - } + } + return stream; +} + +static SkStream* extract_a1_alpha(const SkBitmap& bitmap, + const SkIRect& srcRect, + bool* isOpaque, + bool* isTransparent) { + const int alphaRowBytes = (srcRect.width() + 7) / 8; + SkStream* stream = SkNEW_ARGS(SkMemoryStream, + (alphaRowBytes * srcRect.height())); + uint8_t* alphaDst = (uint8_t*)stream->getMemoryBase(); + + int offset1 = srcRect.fLeft % 8; + int offset2 = 8 - offset1; + + for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { + uint8_t* src = bitmap.getAddr1(0, y); + // This may read up to one byte after src, but the + // potentially invalid bits are never used for computation. + for (int x = srcRect.fLeft; x < srcRect.fRight; x += 8) { + if (offset1) { + alphaDst[0] = src[x / 8] << offset1 | + src[x / 8 + 1] >> offset2; + } else { + alphaDst[0] = src[x / 8]; } - break; + if (x + 7 < srcRect.fRight) { + *isOpaque &= alphaDst[0] == SK_AlphaOPAQUE; + *isTransparent &= alphaDst[0] == SK_AlphaTRANSPARENT; + } + alphaDst++; + } + // Calculate the mask of bits we're interested in within the + // last byte of alphaDst. + // width mod 8 == 1 -> 0x80 ... width mod 8 == 7 -> 0xFE + uint8_t mask = ~((1 << (8 - (srcRect.width() % 8))) - 1); + if (srcRect.width() % 8) { + *isOpaque &= (alphaDst[-1] & mask) == (SK_AlphaOPAQUE & mask); + *isTransparent &= + (alphaDst[-1] & mask) == (SK_AlphaTRANSPARENT & mask); } - 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(); - int offset1 = srcRect.fLeft % 8; - int offset2 = 8 - offset1; - for (int y = srcRect.fTop; y < srcRect.fBottom; y++) { - uint8_t* src = bitmap.getAddr1(0, y); - // This may read up to one byte after src, but the potentially - // invalid bits are never used for computation. - for (int x = srcRect.fLeft; x < srcRect.fRight; x += 8) { - if (offset1) { - alphaDst[0] = src[x / 8] << offset1 | - src[x / 8 + 1] >> offset2; - } else { - alphaDst[0] = src[x / 8]; - } - if (x + 7 < srcRect.fRight && alphaDst[0] != 0xFF) { - hasAlpha = true; - } - if (x + 7 < srcRect.fRight && alphaDst[0]) { - isTransparent = false; - } - alphaDst++; - } - // Calculate the mask of bits we're interested in within the - // last byte of alphaDst. - // width mod 8 == 1 -> 0x80 ... width mod 8 == 7 -> 0xFE - uint8_t mask = ~((1 << (8 - (srcRect.width() % 8))) - 1); - if (srcRect.width() % 8 && (alphaDst[-1] & mask) != mask) { - hasAlpha = true; - } - if (srcRect.width() % 8 && (alphaDst[-1] & mask)) { - isTransparent = false; - } + } + 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) { + SkBitmap::Config config = bitmap.config(); + if (extractAlpha && (config == SkBitmap::kIndex8_Config || + config == SkBitmap::kRGB_565_Config)) { + if (isTransparent != NULL) { + *isTransparent = false; + } + return NULL; + } + bool isOpaque = true; + bool transparent = extractAlpha; + SkStream* stream = NULL; + + bitmap.lockPixels(); + switch (config) { + case SkBitmap::kIndex8_Config: + if (!extractAlpha) { + stream = extract_index8_image(bitmap, srcRect); } 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(); - 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]; - if (alphaDst[0] != 0xFF) { - hasAlpha = true; - } - if (alphaDst[0]) { - isTransparent = false; - } - alphaDst++; - } + case SkBitmap::kARGB_4444_Config: + stream = extract_argb4444_data(bitmap, srcRect, extractAlpha, + &isOpaque, &transparent); + break; + case SkBitmap::kRGB_565_Config: + if (!extractAlpha) { + stream = extract_rgb565_image(bitmap, srcRect); + } + break; + case SkBitmap::kARGB_8888_Config: + stream = extract_argb8888_data(bitmap, srcRect, extractAlpha, + &isOpaque, &transparent); + break; + case SkBitmap::kA1_Config: + if (!extractAlpha) { + stream = create_black_image(); + } else { + stream = extract_a1_alpha(bitmap, srcRect, + &isOpaque, &transparent); + } + break; + case SkBitmap::kA8_Config: + if (!extractAlpha) { + stream = create_black_image(); + } else { + stream = extract_a8_alpha(bitmap, srcRect, + &isOpaque, &transparent); } break; - } default: SkASSERT(false); } bitmap.unlockPixels(); - if (isTransparent) { - SkSafeUnref(image); - } else { - *imageData = image; + if (isTransparent != NULL) { + *isTransparent = transparent; } - - if (isTransparent || !hasAlpha) { - SkSafeUnref(alpha); - } else { - *alphaData = alpha; + if (extractAlpha && (transparent || isOpaque)) { + SkSafeUnref(stream); + return NULL; } + return stream; } -SkPDFArray* makeIndexedColorSpace(SkColorTable* table) { +static SkPDFArray* make_indexed_color_space(SkColorTable* table) { SkPDFArray* result = new SkPDFArray(); result->reserve(4); result->appendName("Indexed"); @@ -229,8 +336,6 @@ SkPDFArray* makeIndexedColorSpace(SkColorTable* table) { return result; } -}; // namespace - // static SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap, const SkIRect& srcRect, @@ -239,24 +344,29 @@ SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap, return NULL; } - SkStream* imageData = NULL; - SkStream* alphaData = NULL; - extractImageData(bitmap, srcRect, &imageData, &alphaData); - SkAutoUnref unrefImageData(imageData); - SkAutoUnref unrefAlphaData(alphaData); - if (!imageData) { - SkASSERT(!alphaData); + 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 = - 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(); + SkPDFImage* 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; } @@ -276,51 +386,62 @@ void SkPDFImage::getResources(const SkTSet<SkPDFObject*>& knownResourceObjects, GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects); } -SkPDFImage::SkPDFImage(SkStream* imageData, +SkPDFImage::SkPDFImage(SkStream* stream, const SkBitmap& bitmap, + bool isAlpha, 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), + fIsAlpha(isAlpha), + fSrcRect(srcRect), + fEncoder(encoder) { + + if (stream != NULL) { + setData(stream); + fStreamValid = true; + } else { + fStreamValid = false; + } + + 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(); + make_indexed_color_space(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 +458,57 @@ SkPDFImage::SkPDFImage(SkStream* imageData, 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 && + fEncoder(&dctCompressedWStream, fBitmap, fSrcRect) && + dctCompressedWStream.getOffset() < + get_uncompressed_size(fBitmap, fSrcRect)) { + SkAutoTUnref<SkData> data(dctCompressedWStream.copyToData()); + SkAutoTUnref<SkStream> stream(SkNEW_ARGS(SkMemoryStream, (data))); + setData(stream.get()); + + insertName("Filter", "DCTDecode"); + insertInt("ColorTransform", kNoColorTransform); + insertInt("Length", getData()->getLength()); + setState(kCompressed_State); + return true; + } + // Fallback method + if (!fStreamValid) { + SkAutoTUnref<SkStream> stream( + extract_image_data(fBitmap, fSrcRect, fIsAlpha, NULL)); + 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; +} diff --git a/src/pdf/SkPDFImage.h b/src/pdf/SkPDFImage.h index 31f894086e..52d323d0ee 100644 --- a/src/pdf/SkPDFImage.h +++ b/src/pdf/SkPDFImage.h @@ -11,7 +11,7 @@ #define SkPDFImage_DEFINED #include "SkPDFDevice.h" -#include "SkPDFImageStream.h" +#include "SkPDFStream.h" #include "SkPDFTypes.h" #include "SkRefCnt.h" @@ -27,7 +27,7 @@ struct SkIRect; // We could play the same trick here as is done in SkPDFGraphicState, storing // a copy of the Bitmap object (not the pixels), the pixel generation number, // and settings used from the paint to canonicalize image objects. -class SkPDFImage : public SkPDFImageStream { +class SkPDFImage : public SkPDFStream { public: /** Create a new Image XObject to represent the passed bitmap. * @param bitmap The image to encode. @@ -48,24 +48,50 @@ public: */ SkPDFImage* addSMask(SkPDFImage* mask); + bool isEmpty() { + return fSrcRect.isEmpty(); + } + // The SkPDFObject interface. virtual void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects, SkTSet<SkPDFObject*>* newResourceObjects); private: + SkBitmap fBitmap; + bool fIsAlpha; + SkIRect fSrcRect; + EncodeToDCTStream fEncoder; + bool fStreamValid; + SkTDArray<SkPDFObject*> fResources; /** Create a PDF image XObject. Entries for the image properties are * automatically added to the stream dictionary. - * @param imageData The final raw bits representing the image. - * @param bitmap The image parameters to use (Config, etc). + * @param stream The image stream. May be NULL. Otherwise, this + * (instead of the input bitmap) will be used as the + * PDF's content stream, possibly with lossless encoding. + * @param bitmap The image. If a stream is not given, its color data + * will be used as the image. If a stream is given, this + * is used for configuration only. + * @param isAlpha Whether or not this is the alpha of an image. * @param srcRect The clipping applied to bitmap before generating * imageData. - * @param alpha Is this the alpha channel of the bitmap. - * @param paint Used to calculate alpha, masks, etc. + * @param encoder A function used to encode the bitmap for compression. + * May be NULL. + */ + SkPDFImage(SkStream* stream, const SkBitmap& bitmap, bool isAlpha, + const SkIRect& srcRect, EncodeToDCTStream encoder); + + /** Copy constructor, used to generate substitutes. + * @param image The SkPDFImage to copy. */ - SkPDFImage(SkStream* imageData, const SkBitmap& bitmap, - const SkIRect& srcRect, bool alpha, EncodeToDCTStream encoder); + SkPDFImage(SkPDFImage& pdfImage); + + // Populate the stream dictionary. This method returns false if + // fSubstitute should be used. + virtual bool populate(SkPDFCatalog* catalog); + + typedef SkPDFStream INHERITED; }; #endif diff --git a/src/pdf/SkPDFImageStream.cpp b/src/pdf/SkPDFImageStream.cpp deleted file mode 100644 index 7130f2b7ca..0000000000 --- a/src/pdf/SkPDFImageStream.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#include "SkData.h" -#include "SkFlate.h" -#include "SkPDFCatalog.h" -#include "SkPDFImageStream.h" -#include "SkStream.h" - -#define kNoColorTransform 0 - -static bool skip_compression(SkPDFCatalog* catalog) { - return SkToBool(catalog->getDocumentFlags() & - SkPDFDocument::kFavorSpeedOverSize_Flags); -} - -// TODO(edisonn): Use SkData (after removing deprecated constructor in SkPDFStream). -SkPDFImageStream::SkPDFImageStream(SkStream* stream, - const SkBitmap& bitmap, - const SkIRect& srcRect, - EncodeToDCTStream encoder) - : SkPDFStream(stream), - fBitmap(bitmap), - fSrcRect(srcRect), - fEncoder(encoder) { -} - -SkPDFImageStream::SkPDFImageStream(const SkPDFImageStream& pdfStream) - : SkPDFStream(pdfStream), - fBitmap(pdfStream.fBitmap), - fSrcRect(pdfStream.fSrcRect), - fEncoder(pdfStream.fEncoder) { -} - -SkPDFImageStream::~SkPDFImageStream() {} - -bool SkPDFImageStream::populate(SkPDFCatalog* catalog) { - if (getState() == kUnused_State) { - if (!skip_compression(catalog)) { - SkDynamicMemoryWStream dctCompressedWStream; - if (!fEncoder || !fEncoder(&dctCompressedWStream, fBitmap, fSrcRect)) { - return INHERITED::populate(catalog); - } - - if (dctCompressedWStream.getOffset() < getData()->getLength()) { - SkData* data = dctCompressedWStream.copyToData(); - SkMemoryStream* stream = SkNEW_ARGS(SkMemoryStream, (data)); - setData(stream); - stream->unref(); - if (data) { - // copyToData and new SkMemoryStream both call ref(), supress one. - data->unref(); - } - - insertName("Filter", "DCTDecode"); - insertInt("ColorTransform", kNoColorTransform); - setState(kCompressed_State); - } - } - setState(kNoCompression_State); - insertInt("Length", getData()->getLength()); - } 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()) { - SkPDFImageStream* substitute = SkNEW_ARGS(SkPDFImageStream, (*this)); - setSubstitute(substitute); - catalog->setSubstitute(this, substitute); - } - return false; - } - return true; -} diff --git a/src/pdf/SkPDFImageStream.h b/src/pdf/SkPDFImageStream.h deleted file mode 100644 index c518081518..0000000000 --- a/src/pdf/SkPDFImageStream.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkPDFImageStream_DEFINED -#define SkPDFImageStream_DEFINED - -#include "SkBitmap.h" -#include "SkPDFDevice.h" -#include "SkPDFStream.h" -#include "SkPDFTypes.h" -#include "SkRect.h" -#include "SkRefCnt.h" -#include "SkStream.h" -#include "SkTemplates.h" - -class SkPDFCatalog; - -/** \class SkPDFImageStream - - An image stream object in a PDF. Note, all streams must be indirect objects - (via SkObjRef). - This class is similar to SkPDFStream, but it is also able to use image - specific compression. Currently we support DCT(jpeg) and flate(zip). -*/ -class SkPDFImageStream : public SkPDFStream { -public: - /** Create a PDF stream with the same content and dictionary entries - * as the passed one. - */ - explicit SkPDFImageStream(const SkPDFImageStream& pdfStream); - virtual ~SkPDFImageStream(); - -protected: - SkPDFImageStream(SkStream* stream, const SkBitmap& bitmap, - const SkIRect& srcRect, EncodeToDCTStream encoder); - - // Populate the stream dictionary. This method returns false if - // fSubstitute should be used. - virtual bool populate(SkPDFCatalog* catalog); - -private: - const SkBitmap fBitmap; - const SkIRect fSrcRect; - EncodeToDCTStream fEncoder; - - typedef SkPDFStream INHERITED; -}; - -#endif |