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:
parent
cbddc11419
commit
181fcb4a21
@ -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',
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
@ -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
|
Loading…
Reference in New Issue
Block a user