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
This commit is contained in:
commit-bot@chromium.org 2013-08-23 19:06:53 +00:00
parent cbddc11419
commit 181fcb4a21
5 changed files with 412 additions and 345 deletions

View File

@ -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',

View File

@ -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);
}
static size_t get_uncompressed_size(const SkBitmap& bitmap,
const SkIRect& srcRect) {
switch (bitmap.getConfig()) {
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;
}
}
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_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];
}
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);
}
}
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 (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;
switch (config) {
case SkBitmap::kIndex8_Config:
if (!extractAlpha) {
stream = extract_index8_image(bitmap, 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;
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++;
}
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::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;
}
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::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++;
}
case SkBitmap::kA8_Config:
if (!extractAlpha) {
stream = create_black_image();
} else {
stream = extract_a8_alpha(bitmap, srcRect,
&isOpaque, &transparent);
}
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();
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;
}
}
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++;
}
}
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;
}

View File

@ -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* imageData, const SkBitmap& bitmap,
const SkIRect& srcRect, bool alpha, EncodeToDCTStream encoder);
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(SkPDFImage& pdfImage);
// Populate the stream dictionary. This method returns false if
// fSubstitute should be used.
virtual bool populate(SkPDFCatalog* catalog);
typedef SkPDFStream INHERITED;
};
#endif

View File

@ -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;
}

View File

@ -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