Revert of PDF: remove last use of SkPDFImage (patchset #5 id:120001 of https://codereview.chromium.org/950633003/)

Reason for revert:
static void draw(SkCanvas* canvas,
                 const SkPaint& p,
                 const SkBitmap& src,
                 SkColorType colorType,
                 const char text[]) {
    SkASSERT(src.colorType() == colorType);
    canvas->drawBitmap(src, 0.0f, 0.0f);
    canvas->drawText(text, strlen(text), 0.0f, 12.0f, p);
}

This assert is firing, at least on macs, where all images get decoded into 32bit at the moment.

Original issue's description:
> PDF: remove last use of SkPDFImage
>
> Add a GM.
>
> BUG=skia:255
>
> Committed: https://skia.googlesource.com/skia/+/86ad8d643624a55b02e529100bbe4e2940115fa1

TBR=mtklein@google.com,halcanary@google.com
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=skia:255

Review URL: https://codereview.chromium.org/1024113002
This commit is contained in:
reed 2015-03-20 10:03:36 -07:00 committed by Commit bot
parent b79ff56de2
commit 1b600d3446
8 changed files with 992 additions and 360 deletions

View File

@ -1,93 +0,0 @@
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "sk_tool_utils.h"
#include "SkSurface.h"
#include "Resources.h"
#include "gm.h"
static SkBitmap copy_bitmap(const SkBitmap& src, SkColorType colorType) {
SkBitmap copy;
src.copyTo(&copy, colorType);
copy.setImmutable();
return copy;
}
// Make either A8 or gray8 bitmap.
static SkBitmap make_bitmap(bool alpha) {
SkBitmap bm;
SkImageInfo info = alpha ? SkImageInfo::MakeA8(128, 128)
: SkImageInfo::Make(128, 128, kGray_8_SkColorType,
kOpaque_SkAlphaType);
bm.allocPixels(info);
SkAutoLockPixels autoLockPixels(bm);
uint8_t spectrum[256];
for (int y = 0; y < 256; ++y) {
spectrum[y] = y;
}
for (int y = 0; y < 128; ++y) {
// Shift over one byte each scanline.
memcpy(bm.getAddr8(0, y), &spectrum[y], 128);
}
bm.setImmutable();
return bm;
}
static void draw(SkCanvas* canvas,
const SkPaint& p,
const SkBitmap& src,
SkColorType colorType,
const char text[]) {
SkASSERT(src.colorType() == colorType);
canvas->drawBitmap(src, 0.0f, 0.0f);
canvas->drawText(text, strlen(text), 0.0f, 12.0f, p);
}
#define SCALE 128
DEF_SIMPLE_GM(all_bitmap_configs, canvas, SCALE, 6 * SCALE) {
SkAutoCanvasRestore autoCanvasRestore(canvas, true);
SkPaint p;
p.setColor(SK_ColorBLACK);
p.setAntiAlias(true);
sk_tool_utils::set_portable_typeface(&p, NULL, SkTypeface::kBold);
sk_tool_utils::draw_checkerboard(canvas, SK_ColorLTGRAY, SK_ColorWHITE, 8);
SkBitmap bitmap;
if (GetResourceAsBitmap("color_wheel.png", &bitmap)) {
bitmap.setImmutable();
draw(canvas, p, bitmap, kN32_SkColorType, "Native 32");
canvas->translate(0.0f, SkIntToScalar(SCALE));
SkBitmap copy565 = copy_bitmap(bitmap, kRGB_565_SkColorType);
p.setColor(SK_ColorRED);
draw(canvas, p, copy565, kRGB_565_SkColorType, "RGB 565");
p.setColor(SK_ColorBLACK);
canvas->translate(0.0f, SkIntToScalar(SCALE));
SkBitmap copy4444 = copy_bitmap(bitmap, kARGB_4444_SkColorType);
draw(canvas, p, copy4444, kARGB_4444_SkColorType, "ARGB 4444");
} else {
canvas->translate(0.0f, SkIntToScalar(2 * SCALE));
}
canvas->translate(0.0f, SkIntToScalar(SCALE));
SkBitmap bitmapIndexed;
if (GetResourceAsBitmap("color_wheel.gif", &bitmapIndexed)) {
bitmapIndexed.setImmutable();
draw(canvas, p, bitmapIndexed, kIndex_8_SkColorType, "Index 8");
}
canvas->translate(0.0f, SkIntToScalar(SCALE));
SkBitmap bitmapA8 = make_bitmap(true);
draw(canvas, p, bitmapA8, kAlpha_8_SkColorType, "Alpha 8");
p.setColor(SK_ColorRED);
canvas->translate(0.0f, SkIntToScalar(SCALE));
SkBitmap bitmapG8 = make_bitmap(false);
draw(canvas, p, bitmapG8, kGray_8_SkColorType, "Gray 8");
}

View File

@ -16,7 +16,6 @@
'../gm/aaclip.cpp',
'../gm/aarectmodes.cpp',
'../gm/addarc.cpp',
'../gm/all_bitmap_configs.cpp',
'../gm/alphagradients.cpp',
'../gm/arcofzorro.cpp',
'../gm/arithmode.cpp',

View File

@ -25,6 +25,8 @@
'<(skia_src_path)/pdf/SkPDFFormXObject.h',
'<(skia_src_path)/pdf/SkPDFGraphicState.cpp',
'<(skia_src_path)/pdf/SkPDFGraphicState.h',
'<(skia_src_path)/pdf/SkPDFImage.cpp',
'<(skia_src_path)/pdf/SkPDFImage.h',
'<(skia_src_path)/pdf/SkPDFPage.cpp',
'<(skia_src_path)/pdf/SkPDFPage.h',
'<(skia_src_path)/pdf/SkPDFResourceDict.cpp',

View File

@ -25,214 +25,114 @@ static void pdf_stream_end(SkWStream* stream) {
stream->write(streamEnd, strlen(streamEnd));
}
////////////////////////////////////////////////////////////////////////////////
static size_t pixel_count(const SkBitmap& bm) {
return SkToSizeT(bm.width()) * SkToSizeT(bm.height());
}
// write a single byte to a stream n times.
static void fill_stream(SkWStream* out, char value, size_t n) {
char buffer[4096];
memset(buffer, value, sizeof(buffer));
for (size_t i = 0; i < n / sizeof(buffer); ++i) {
out->write(buffer, sizeof(buffer));
while (n) {
size_t k = SkTMin(n, sizeof(buffer));
out->write(buffer, k);
n -= k;
}
out->write(buffer, n % sizeof(buffer));
}
// unpremultiply and extract R, G, B components.
static void pmcolor_to_rgb24(SkPMColor pmColor, uint8_t* rgb) {
uint32_t s = SkUnPreMultiply::GetScale(SkGetPackedA32(pmColor));
rgb[0] = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(pmColor));
rgb[1] = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(pmColor));
rgb[2] = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(pmColor));
}
/* It is necessary to average the color component of transparent
pixels with their surrounding neighbors since the PDF renderer may
separately re-sample the alpha and color channels when the image is
not displayed at its native resolution. Since an alpha of zero
gives no information about the color component, the pathological
case is a white image with sharp transparency bounds - the color
channel goes to black, and the should-be-transparent pixels are
rendered as grey because of the separate soft mask and color
resizing. e.g.: gm/bitmappremul.cpp */
static void get_neighbor_avg_color(const SkBitmap& bm,
int xOrig,
int yOrig,
uint8_t rgb[3]) {
SkASSERT(kN32_SkColorType == bm.colorType());
unsigned a = 0, r = 0, g = 0, b = 0;
// Clamp the range to the edge of the bitmap.
int ymin = SkTMax(0, yOrig - 1);
int ymax = SkTMin(yOrig + 1, bm.height() - 1);
int xmin = SkTMax(0, xOrig - 1);
int xmax = SkTMin(xOrig + 1, bm.width() - 1);
for (int y = ymin; y <= ymax; ++y) {
SkPMColor* scanline = bm.getAddr32(0, y);
for (int x = xmin; x <= xmax; ++x) {
SkPMColor pmColor = scanline[x];
a += SkGetPackedA32(pmColor);
r += SkGetPackedR32(pmColor);
g += SkGetPackedG32(pmColor);
b += SkGetPackedB32(pmColor);
static SkPMColor get_pmcolor_neighbor_avg_color(const SkBitmap& bitmap,
int xOrig,
int yOrig) {
SkASSERT(kN32_SkColorType == bitmap.colorType());
SkASSERT(bitmap.getPixels());
uint8_t count = 0;
unsigned r = 0;
unsigned g = 0;
unsigned b = 0;
for (int y = yOrig - 1; y <= yOrig + 1; ++y) {
if (y < 0 || y >= bitmap.height()) {
continue;
}
uint32_t* src = bitmap.getAddr32(0, y);
for (int x = xOrig - 1; x <= xOrig + 1; ++x) {
if (x < 0 || x >= bitmap.width()) {
continue;
}
SkPMColor pmColor = src[x];
U8CPU alpha = SkGetPackedA32(pmColor);
if (alpha != SK_AlphaTRANSPARENT) {
uint32_t s = SkUnPreMultiply::GetScale(alpha);
r += SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(pmColor));
g += SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(pmColor));
b += SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(pmColor));
++count;
}
}
}
if (a > 0) {
rgb[0] = SkToU8(255 * r / a);
rgb[1] = SkToU8(255 * g / a);
rgb[2] = SkToU8(255 * b / a);
if (count == 0) {
return SkPackARGB32NoCheck(SK_AlphaOPAQUE, 0, 0, 0);
} else {
rgb[0] = rgb[1] = rgb[2] = 0;
return SkPackARGB32NoCheck(
SK_AlphaOPAQUE, r / count, g / count, b / count);
}
}
static size_t pixel_count(const SkBitmap& bm) {
return SkToSizeT(bm.width()) * SkToSizeT(bm.height());
}
static const SkBitmap& not4444(const SkBitmap& input, SkBitmap* copy) {
if (input.colorType() != kARGB_4444_SkColorType) {
return input;
}
// ARGB_4444 is rarely used, so we can do a wasteful tmp copy.
SkAssertResult(input.copyTo(copy, kN32_SkColorType));
copy->setImmutable();
return *copy;
}
static size_t pdf_color_component_count(SkColorType ct) {
switch (ct) {
case kN32_SkColorType:
case kRGB_565_SkColorType:
case kARGB_4444_SkColorType:
return 3;
case kAlpha_8_SkColorType:
case kIndex_8_SkColorType:
case kGray_8_SkColorType:
return 1;
case kUnknown_SkColorType:
default:
SkDEBUGFAIL("unexpected color type");
return 0;
}
}
static void bitmap_to_pdf_pixels(const SkBitmap& bitmap, SkWStream* out) {
if (!bitmap.getPixels()) {
size_t size = pixel_count(bitmap) *
pdf_color_component_count(bitmap.colorType());
fill_stream(out, '\x00', size);
static void pmcolor_to_rgb24(const SkBitmap& bm, SkWStream* out) {
SkASSERT(kN32_SkColorType == bm.colorType());
if (!bm.getPixels()) {
fill_stream(out, '\xFF', 3 * pixel_count(bm));
return;
}
SkBitmap copy;
const SkBitmap& bm = not4444(bitmap, &copy);
SkAutoLockPixels autoLockPixels(bm);
switch (bm.colorType()) {
case kN32_SkColorType: {
SkASSERT(3 == pdf_color_component_count(bitmap.colorType()));
SkAutoTMalloc<uint8_t> scanline(3 * bm.width());
for (int y = 0; y < bm.height(); ++y) {
const SkPMColor* src = bm.getAddr32(0, y);
uint8_t* dst = scanline.get();
for (int x = 0; x < bm.width(); ++x) {
SkPMColor color = *src++;
U8CPU alpha = SkGetPackedA32(color);
if (alpha != SK_AlphaTRANSPARENT) {
pmcolor_to_rgb24(color, dst);
} else {
get_neighbor_avg_color(bm, x, y, dst);
}
dst += 3;
}
out->write(scanline.get(), 3 * bm.width());
size_t scanlineLength = 3 * bm.width();
SkAutoTMalloc<uint8_t> scanline(scanlineLength);
for (int y = 0; y < bm.height(); ++y) {
uint8_t* dst = scanline.get();
const SkPMColor* src = bm.getAddr32(0, y);
for (int x = 0; x < bm.width(); ++x) {
SkPMColor color = *src++;
U8CPU alpha = SkGetPackedA32(color);
if (alpha != SK_AlphaTRANSPARENT) {
uint32_t s = SkUnPreMultiply::GetScale(alpha);
*dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(color));
*dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(color));
*dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(color));
} else {
/* It is necessary to average the color component of
transparent pixels with their surrounding neighbors
since the PDF renderer may separately re-sample the
alpha and color channels when the image is not
displayed at its native resolution. Since an alpha
of zero gives no information about the color
component, the pathological case is a white image
with sharp transparency bounds - the color channel
goes to black, and the should-be-transparent pixels
are rendered as grey because of the separate soft
mask and color resizing. e.g.: gm/bitmappremul.cpp */
color = get_pmcolor_neighbor_avg_color(bm, x, y);
*dst++ = SkGetPackedR32(color);
*dst++ = SkGetPackedG32(color);
*dst++ = SkGetPackedB32(color);
}
return;
}
case kRGB_565_SkColorType: {
SkASSERT(3 == pdf_color_component_count(bitmap.colorType()));
SkAutoTMalloc<uint8_t> scanline(3 * bm.width());
for (int y = 0; y < bm.height(); ++y) {
const uint16_t* src = bm.getAddr16(0, y);
uint8_t* dst = scanline.get();
for (int x = 0; x < bm.width(); ++x) {
U16CPU color565 = *src++;
*dst++ = SkPacked16ToR32(color565);
*dst++ = SkPacked16ToG32(color565);
*dst++ = SkPacked16ToB32(color565);
}
out->write(scanline.get(), 3 * bm.width());
}
return;
}
case kAlpha_8_SkColorType:
SkASSERT(1 == pdf_color_component_count(bitmap.colorType()));
fill_stream(out, '\x00', pixel_count(bm));
return;
case kGray_8_SkColorType:
case kIndex_8_SkColorType:
SkASSERT(1 == pdf_color_component_count(bitmap.colorType()));
// these two formats need no transformation to serialize.
for (int y = 0; y < bm.height(); ++y) {
out->write(bm.getAddr8(0, y), bm.width());
}
return;
case kUnknown_SkColorType:
case kARGB_4444_SkColorType:
default:
SkDEBUGFAIL("unexpected color type");
out->write(scanline.get(), scanlineLength);
}
}
////////////////////////////////////////////////////////////////////////////////
static void bitmap_alpha_to_a8(const SkBitmap& bitmap, SkWStream* out) {
if (!bitmap.getPixels()) {
fill_stream(out, '\xFF', pixel_count(bitmap));
static void pmcolor_alpha_to_a8(const SkBitmap& bm, SkWStream* out) {
SkASSERT(kN32_SkColorType == bm.colorType());
if (!bm.getPixels()) {
fill_stream(out, '\xFF', pixel_count(bm));
return;
}
SkBitmap copy;
const SkBitmap& bm = not4444(bitmap, &copy);
SkAutoLockPixels autoLockPixels(bm);
switch (bm.colorType()) {
case kN32_SkColorType: {
SkAutoTMalloc<uint8_t> scanline(bm.width());
for (int y = 0; y < bm.height(); ++y) {
uint8_t* dst = scanline.get();
const SkPMColor* src = bm.getAddr32(0, y);
for (int x = 0; x < bm.width(); ++x) {
*dst++ = SkGetPackedA32(*src++);
}
out->write(scanline.get(), bm.width());
}
return;
size_t scanlineLength = bm.width();
SkAutoTMalloc<uint8_t> scanline(scanlineLength);
for (int y = 0; y < bm.height(); ++y) {
uint8_t* dst = scanline.get();
const SkPMColor* src = bm.getAddr32(0, y);
for (int x = 0; x < bm.width(); ++x) {
*dst++ = SkGetPackedA32(*src++);
}
case kAlpha_8_SkColorType:
for (int y = 0; y < bm.height(); ++y) {
out->write(bm.getAddr8(0, y), bm.width());
}
return;
case kIndex_8_SkColorType: {
SkColorTable* ct = bm.getColorTable();
SkASSERT(ct);
SkAutoTMalloc<uint8_t> scanline(bm.width());
for (int y = 0; y < bm.height(); ++y) {
uint8_t* dst = scanline.get();
const uint8_t* src = bm.getAddr8(0, y);
for (int x = 0; x < bm.width(); ++x) {
*dst++ = SkGetPackedA32((*ct)[*src++]);
}
out->write(scanline.get(), bm.width());
}
return;
}
case kRGB_565_SkColorType:
case kGray_8_SkColorType:
SkDEBUGFAIL("color type has no alpha");
return;
case kARGB_4444_SkColorType:
SkDEBUGFAIL("4444 color type should have been converted to N32");
return;
case kUnknown_SkColorType:
default:
SkDEBUGFAIL("unexpected color type");
out->write(scanline.get(), scanlineLength);
}
}
@ -245,40 +145,49 @@ public:
PDFAlphaBitmap(const SkBitmap& bm) : fBitmap(bm) {}
~PDFAlphaBitmap() {}
void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE;
void addResources(SkTSet<SkPDFObject*>*, SkPDFCatalog*) const SK_OVERRIDE {}
private:
const SkBitmap fBitmap;
void emitDict(SkWStream*, SkPDFCatalog*, size_t) const;
void emitDict(SkWStream*, SkPDFCatalog*, size_t, bool) const;
};
void PDFAlphaBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
SkAutoLockPixels autoLockPixels(fBitmap);
SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType ||
fBitmap.getColorTable());
#ifndef SK_NO_FLATE
// Write to a temporary buffer to get the compressed length.
SkDynamicMemoryWStream buffer;
SkDeflateWStream deflateWStream(&buffer);
bitmap_alpha_to_a8(fBitmap, &deflateWStream);
pmcolor_alpha_to_a8(fBitmap, &deflateWStream);
deflateWStream.finalize(); // call before detachAsStream().
SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
this->emitDict(stream, catalog, asset->getLength());
this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true);
pdf_stream_begin(stream);
stream->writeStream(asset.get(), asset->getLength());
pdf_stream_end(stream);
#else
this->emitDict(stream, catalog, pixel_count(fBitmap), /*deflate=*/false);
pdf_stream_begin(stream);
pmcolor_alpha_to_a8(fBitmap, stream);
pdf_stream_end(stream);
#endif // SK_NO_FLATE
}
void PDFAlphaBitmap::emitDict(SkWStream* stream,
SkPDFCatalog* catalog,
size_t length) const {
size_t length,
bool deflate) const {
SkPDFDict pdfDict("XObject");
pdfDict.insertName("Subtype", "Image");
pdfDict.insertInt("Width", fBitmap.width());
pdfDict.insertInt("Height", fBitmap.height());
pdfDict.insertName("ColorSpace", "DeviceGray");
pdfDict.insertInt("BitsPerComponent", 8);
pdfDict.insertName("Filter", "FlateDecode");
if (deflate) {
pdfDict.insertName("Filter", "FlateDecode");
}
pdfDict.insertInt("Length", length);
pdfDict.emitObject(stream, catalog);
}
@ -289,81 +198,50 @@ void PDFAlphaBitmap::emitDict(SkWStream* stream,
void SkPDFBitmap::addResources(SkTSet<SkPDFObject*>* resourceSet,
SkPDFCatalog* catalog) const {
if (fSMask.get()) {
if (resourceSet->add(fSMask.get())) {
fSMask->addResources(resourceSet, catalog);
}
resourceSet->add(fSMask.get());
}
}
void SkPDFBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
SkAutoLockPixels autoLockPixels(fBitmap);
SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType ||
fBitmap.getColorTable());
#ifndef SK_NO_FLATE
// Write to a temporary buffer to get the compressed length.
SkDynamicMemoryWStream buffer;
SkDeflateWStream deflateWStream(&buffer);
bitmap_to_pdf_pixels(fBitmap, &deflateWStream);
pmcolor_to_rgb24(fBitmap, &deflateWStream);
deflateWStream.finalize(); // call before detachAsStream().
SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
this->emitDict(stream, catalog, asset->getLength());
this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true);
pdf_stream_begin(stream);
stream->writeStream(asset.get(), asset->getLength());
pdf_stream_end(stream);
}
static SkPDFArray* make_indexed_color_space(const SkColorTable* table) {
SkPDFArray* result = SkNEW(SkPDFArray);
result->reserve(4);
result->appendName("Indexed");
result->appendName("DeviceRGB");
SkASSERT(table);
if (table->count() < 1) {
result->appendInt(0);
char shortTableArray[3] = {0, 0, 0};
SkString tableString(shortTableArray, SK_ARRAY_COUNT(shortTableArray));
result->append(new SkPDFString(tableString))->unref();
return result;
}
result->appendInt(table->count() - 1); // maximum color index.
// Potentially, this could be represented in fewer bytes with a stream.
// Max size as a string is 1.5k.
char tableArray[256 * 3];
SkASSERT(3u * table->count() <= SK_ARRAY_COUNT(tableArray));
uint8_t* tablePtr = reinterpret_cast<uint8_t*>(tableArray);
const SkPMColor* colors = table->readColors();
for (int i = 0; i < table->count(); i++) {
pmcolor_to_rgb24(colors[i], tablePtr);
tablePtr += 3;
}
SkString tableString(tableArray, 3 * table->count());
result->append(new SkPDFString(tableString))->unref();
return result;
#else
this->emitDict(stream, catalog, 3 * pixel_count(fBitmap), /*deflate=*/false);
pdf_stream_begin(stream);
pmcolor_to_rgb24(fBitmap, stream);
pdf_stream_end(stream);
return;
#endif // SK_NO_FLATE
}
void SkPDFBitmap::emitDict(SkWStream* stream,
SkPDFCatalog* catalog,
size_t length) const {
size_t length,
bool deflate) const {
SkPDFDict pdfDict("XObject");
pdfDict.insertName("Subtype", "Image");
pdfDict.insertInt("Width", fBitmap.width());
pdfDict.insertInt("Height", fBitmap.height());
if (fBitmap.colorType() == kIndex_8_SkColorType) {
SkASSERT(1 == pdf_color_component_count(fBitmap.colorType()));
pdfDict.insert("ColorSpace", make_indexed_color_space(
fBitmap.getColorTable()))->unref();
} else if (1 == pdf_color_component_count(fBitmap.colorType())) {
pdfDict.insertName("ColorSpace", "DeviceGray");
} else {
pdfDict.insertName("ColorSpace", "DeviceRGB");
}
pdfDict.insertName("ColorSpace", "DeviceRGB");
pdfDict.insertInt("BitsPerComponent", 8);
if (fSMask) {
pdfDict.insert("SMask", new SkPDFObjRef(fSMask))->unref();
}
pdfDict.insertName("Filter", "FlateDecode");
if (deflate) {
pdfDict.insertName("Filter", "FlateDecode");
}
pdfDict.insertInt("Length", length);
pdfDict.emitObject(stream, catalog);
}
@ -375,35 +253,64 @@ SkPDFBitmap::SkPDFBitmap(const SkBitmap& bm,
SkPDFBitmap::~SkPDFBitmap() {}
////////////////////////////////////////////////////////////////////////////////
static const SkBitmap& immutable_bitmap(const SkBitmap& bm, SkBitmap* copy) {
if (bm.isImmutable()) {
return bm;
static bool is_transparent(const SkBitmap& bm) {
SkAutoLockPixels autoLockPixels(bm);
if (NULL == bm.getPixels()) {
return true;
}
bm.copyTo(copy);
copy->setImmutable();
return *copy;
SkASSERT(kN32_SkColorType == bm.colorType());
for (int y = 0; y < bm.height(); ++y) {
U8CPU alpha = 0;
const SkPMColor* src = bm.getAddr32(0, y);
for (int x = 0; x < bm.width(); ++x) {
alpha |= SkGetPackedA32(*src++);
}
if (alpha) {
return false;
}
}
return true;
}
SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon, const SkBitmap& bitmap) {
SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon,
const SkBitmap& bitmap,
const SkIRect& subset) {
SkASSERT(canon);
if (!SkColorTypeIsValid(bitmap.colorType()) ||
kUnknown_SkColorType == bitmap.colorType()) {
if (kN32_SkColorType != bitmap.colorType()) {
// TODO(halcanary): support other colortypes.
return NULL;
}
SkBitmap bm;
// Should extractSubset be done by the SkPDFDevice?
if (!bitmap.extractSubset(&bm, subset)) {
return NULL;
}
SkBitmap copy;
const SkBitmap& bm = immutable_bitmap(bitmap, &copy);
if (bm.drawsNothing()) {
return NULL;
}
if (SkPDFBitmap* canonBitmap = canon->findBitmap(bm)) {
return SkRef(canonBitmap);
if (!bm.isImmutable()) {
SkBitmap copy;
if (!bm.copyTo(&copy)) {
return NULL;
}
copy.setImmutable();
bm = copy;
}
SkPDFBitmap* pdfBitmap = canon->findBitmap(bm);
if (pdfBitmap) {
return SkRef(pdfBitmap);
}
SkPDFObject* smask = NULL;
if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) {
if (is_transparent(bm)) {
return NULL;
}
// PDFAlphaBitmaps do not get directly canonicalized (they
// are refed by the SkPDFBitmap).
smask = SkNEW_ARGS(PDFAlphaBitmap, (bm));
}
SkPDFBitmap* pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (bm, smask));
pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (bm, smask));
canon->addBitmap(pdfBitmap);
return pdfBitmap;
}

View File

@ -17,15 +17,18 @@ class SkPDFCanon;
* It is designed to use a minimal amout of memory, aside from refing
* the bitmap's pixels, and its emitObject() does not cache any data.
*
* If !bitmap.isImmutable(), then a copy of the bitmap must be made;
* there is no way around this.
* As of now, it only supports 8888 bitmaps (the most common case).
*
* The SkPDFBitmap::Create function will check the canon for duplicates.
*/
class SkPDFBitmap : public SkPDFObject {
public:
// Returns NULL on unsupported bitmap;
static SkPDFBitmap* Create(SkPDFCanon*, const SkBitmap&);
// TODO(halcanary): support other bitmap colortypes and replace
// SkPDFImage.
static SkPDFBitmap* Create(SkPDFCanon*,
const SkBitmap&,
const SkIRect& subset);
~SkPDFBitmap();
void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE;
void addResources(SkTSet<SkPDFObject*>* resourceSet,
@ -40,7 +43,7 @@ private:
const SkBitmap fBitmap;
const SkAutoTUnref<SkPDFObject> fSMask;
SkPDFBitmap(const SkBitmap&, SkPDFObject*);
void emitDict(SkWStream*, SkPDFCatalog*, size_t) const;
void emitDict(SkWStream*, SkPDFCatalog*, size_t, bool) const;
};
#endif // SkPDFBitmap_DEFINED

View File

@ -17,10 +17,10 @@
#include "SkPaint.h"
#include "SkPath.h"
#include "SkPathOps.h"
#include "SkPDFBitmap.h"
#include "SkPDFFont.h"
#include "SkPDFFormXObject.h"
#include "SkPDFGraphicState.h"
#include "SkPDFImage.h"
#include "SkPDFResourceDict.h"
#include "SkPDFShader.h"
#include "SkPDFStream.h"
@ -2126,7 +2126,7 @@ void SkPDFDevice::internalDrawBitmap(const SkMatrix& origMatrix,
if (content.needShape()) {
SkPath shape;
shape.addRect(SkRect::MakeWH(SkIntToScalar(subset.width()),
SkIntToScalar(subset.height())));
SkIntToScalar( subset.height())));
shape.transform(matrix);
content.setShape(shape);
}
@ -2134,12 +2134,8 @@ void SkPDFDevice::internalDrawBitmap(const SkMatrix& origMatrix,
return;
}
SkBitmap subsetBitmap;
// Should extractSubset be done by the SkPDFDevice?
if (!bitmap->extractSubset(&subsetBitmap, subset)) {
return;
}
SkAutoTUnref<SkPDFObject> image(SkPDFBitmap::Create(fCanon, subsetBitmap));
SkAutoTUnref<SkPDFObject> image(
SkPDFCreateImageObject(fCanon, *bitmap, subset));
if (!image) {
return;
}

727
src/pdf/SkPDFImage.cpp Normal file
View File

@ -0,0 +1,727 @@
/*
* Copyright 2010 The Android Open Source Project
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkPDFImage.h"
#include "SkBitmap.h"
#include "SkColor.h"
#include "SkColorPriv.h"
#include "SkData.h"
#include "SkFlate.h"
#include "SkPDFBitmap.h"
#include "SkPDFCatalog.h"
#include "SkPixelRef.h"
#include "SkRect.h"
#include "SkStream.h"
#include "SkString.h"
#include "SkUnPreMultiply.h"
static size_t get_uncompressed_size(const SkBitmap& bitmap,
const SkIRect& srcRect) {
switch (bitmap.colorType()) {
case kIndex_8_SkColorType:
return srcRect.width() * srcRect.height();
case kARGB_4444_SkColorType:
return ((srcRect.width() * 3 + 1) / 2) * srcRect.height();
case kRGB_565_SkColorType:
return srcRect.width() * 3 * srcRect.height();
case kRGBA_8888_SkColorType:
case kBGRA_8888_SkColorType:
case kGray_8_SkColorType:
return srcRect.width() * 3 * srcRect.height();
case kAlpha_8_SkColorType:
return 1;
default:
SkASSERT(false);
return 0;
}
}
static SkStream* extract_index8_image(const SkBitmap& bitmap,
const SkIRect& srcRect) {
const int rowBytes = srcRect.width();
SkStream* stream = SkNEW_ARGS(SkMemoryStream,
(get_uncompressed_size(bitmap, srcRect)));
uint8_t* dst = (uint8_t*)stream->getMemoryBase();
for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
memcpy(dst, bitmap.getAddr8(srcRect.fLeft, y), rowBytes);
dst += rowBytes;
}
return stream;
}
static SkStream* extract_argb4444_data(const SkBitmap& bitmap,
const SkIRect& srcRect,
bool extractAlpha,
bool* isOpaque,
bool* isTransparent) {
SkStream* stream;
uint8_t* dst = NULL;
if (extractAlpha) {
const int alphaRowBytes = (srcRect.width() + 1) / 2;
stream = SkNEW_ARGS(SkMemoryStream,
(alphaRowBytes * srcRect.height()));
} else {
stream = SkNEW_ARGS(SkMemoryStream,
(get_uncompressed_size(bitmap, srcRect)));
}
dst = (uint8_t*)stream->getMemoryBase();
for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
uint16_t* src = bitmap.getAddr16(0, y);
int x;
for (x = srcRect.fLeft; x + 1 < srcRect.fRight; x += 2) {
if (extractAlpha) {
dst[0] = (SkGetPackedA4444(src[x]) << 4) |
SkGetPackedA4444(src[x + 1]);
*isOpaque &= dst[0] == SK_AlphaOPAQUE;
*isTransparent &= dst[0] == SK_AlphaTRANSPARENT;
dst++;
} else {
dst[0] = (SkGetPackedR4444(src[x]) << 4) |
SkGetPackedG4444(src[x]);
dst[1] = (SkGetPackedB4444(src[x]) << 4) |
SkGetPackedR4444(src[x + 1]);
dst[2] = (SkGetPackedG4444(src[x + 1]) << 4) |
SkGetPackedB4444(src[x + 1]);
dst += 3;
}
}
if (srcRect.width() & 1) {
if (extractAlpha) {
dst[0] = (SkGetPackedA4444(src[x]) << 4);
*isOpaque &= dst[0] == (SK_AlphaOPAQUE & 0xF0);
*isTransparent &= dst[0] == (SK_AlphaTRANSPARENT & 0xF0);
dst++;
} else {
dst[0] = (SkGetPackedR4444(src[x]) << 4) |
SkGetPackedG4444(src[x]);
dst[1] = (SkGetPackedB4444(src[x]) << 4);
dst += 2;
}
}
}
return stream;
}
static SkStream* extract_rgb565_image(const SkBitmap& bitmap,
const SkIRect& srcRect) {
SkStream* stream = SkNEW_ARGS(SkMemoryStream,
(get_uncompressed_size(bitmap,
srcRect)));
uint8_t* dst = (uint8_t*)stream->getMemoryBase();
for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
uint16_t* src = bitmap.getAddr16(0, y);
for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
dst[0] = SkGetPackedR16(src[x]);
dst[1] = SkGetPackedG16(src[x]);
dst[2] = SkGetPackedB16(src[x]);
dst += 3;
}
}
return stream;
}
static SkStream* extract_gray8_image(const SkBitmap& bitmap, const SkIRect& srcRect) {
SkStream* stream = SkNEW_ARGS(SkMemoryStream,
(get_uncompressed_size(bitmap, srcRect)));
uint8_t* dst = (uint8_t*)stream->getMemoryBase();
for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
uint8_t* src = bitmap.getAddr8(0, y);
for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
dst[0] = dst[1] = dst[2] = src[x];
dst += 3;
}
}
return stream;
}
static uint32_t get_argb8888_neighbor_avg_color(const SkBitmap& bitmap,
int xOrig,
int yOrig);
static SkStream* extract_argb8888_data(const SkBitmap& bitmap,
const SkIRect& srcRect,
bool extractAlpha,
bool* isOpaque,
bool* isTransparent) {
size_t streamSize = extractAlpha ? srcRect.width() * srcRect.height()
: get_uncompressed_size(bitmap, srcRect);
SkStream* stream = SkNEW_ARGS(SkMemoryStream, (streamSize));
uint8_t* dst = (uint8_t*)stream->getMemoryBase();
const SkUnPreMultiply::Scale* scaleTable = SkUnPreMultiply::GetScaleTable();
for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
uint32_t* src = bitmap.getAddr32(0, y);
for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
SkPMColor c = src[x];
U8CPU alpha = SkGetPackedA32(c);
if (extractAlpha) {
*isOpaque &= alpha == SK_AlphaOPAQUE;
*isTransparent &= alpha == SK_AlphaTRANSPARENT;
*dst++ = alpha;
} else {
if (SK_AlphaTRANSPARENT == alpha) {
// It is necessary to average the color component of
// transparent pixels with their surrounding neighbors
// since the PDF renderer may separately re-sample the
// alpha and color channels when the image is not
// displayed at its native resolution. Since an alpha of
// zero gives no information about the color component,
// the pathological case is a white image with sharp
// transparency bounds - the color channel goes to black,
// and the should-be-transparent pixels are rendered
// as grey because of the separate soft mask and color
// resizing.
c = get_argb8888_neighbor_avg_color(bitmap, x, y);
*dst++ = SkGetPackedR32(c);
*dst++ = SkGetPackedG32(c);
*dst++ = SkGetPackedB32(c);
} else {
SkUnPreMultiply::Scale s = scaleTable[alpha];
*dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(c));
*dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(c));
*dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(c));
}
}
}
}
SkASSERT(dst == streamSize + (uint8_t*)stream->getMemoryBase());
return stream;
}
static SkStream* extract_a8_alpha(const SkBitmap& bitmap,
const SkIRect& srcRect,
bool* isOpaque,
bool* isTransparent) {
const int alphaRowBytes = srcRect.width();
SkStream* stream = SkNEW_ARGS(SkMemoryStream,
(alphaRowBytes * srcRect.height()));
uint8_t* alphaDst = (uint8_t*)stream->getMemoryBase();
for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
uint8_t* src = bitmap.getAddr8(0, y);
for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
alphaDst[0] = src[x];
*isOpaque &= alphaDst[0] == SK_AlphaOPAQUE;
*isTransparent &= alphaDst[0] == SK_AlphaTRANSPARENT;
alphaDst++;
}
}
return stream;
}
static SkStream* create_black_image() {
SkStream* stream = SkNEW_ARGS(SkMemoryStream, (1));
((uint8_t*)stream->getMemoryBase())[0] = 0;
return stream;
}
/**
* Extract either the color or image data from a SkBitmap into a SkStream.
* @param bitmap Bitmap to extract data from.
* @param srcRect Region in the bitmap to extract.
* @param extractAlpha Set to true to extract the alpha data or false to
* extract the color data.
* @param isTransparent Pointer to a bool to output whether the alpha is
* completely transparent. May be NULL. Only valid when
* extractAlpha == true.
* @return Unencoded image data, or NULL if either data was not
* available or alpha data was requested but the image was
* entirely transparent or opaque.
*/
static SkStream* extract_image_data(const SkBitmap& bitmap,
const SkIRect& srcRect,
bool extractAlpha, bool* isTransparent) {
SkColorType colorType = bitmap.colorType();
if (extractAlpha && (kIndex_8_SkColorType == colorType ||
kRGB_565_SkColorType == colorType ||
kGray_8_SkColorType == colorType)) {
if (isTransparent != NULL) {
*isTransparent = false;
}
return NULL;
}
SkAutoLockPixels lock(bitmap);
if (NULL == bitmap.getPixels()) {
return NULL;
}
bool isOpaque = true;
bool transparent = extractAlpha;
SkAutoTDelete<SkStream> stream;
switch (colorType) {
case kIndex_8_SkColorType:
if (!extractAlpha) {
stream.reset(extract_index8_image(bitmap, srcRect));
}
break;
case kARGB_4444_SkColorType:
stream.reset(extract_argb4444_data(bitmap, srcRect, extractAlpha,
&isOpaque, &transparent));
break;
case kRGB_565_SkColorType:
if (!extractAlpha) {
stream.reset(extract_rgb565_image(bitmap, srcRect));
}
break;
case kGray_8_SkColorType:
if (!extractAlpha) {
stream.reset(extract_gray8_image(bitmap, srcRect));
}
break;
case kN32_SkColorType:
stream.reset(extract_argb8888_data(bitmap, srcRect, extractAlpha,
&isOpaque, &transparent));
break;
case kAlpha_8_SkColorType:
if (!extractAlpha) {
stream.reset(create_black_image());
} else {
stream.reset(extract_a8_alpha(bitmap, srcRect,
&isOpaque, &transparent));
}
break;
default:
SkASSERT(false);
}
if (isTransparent != NULL) {
*isTransparent = transparent;
}
if (extractAlpha && (transparent || isOpaque)) {
return NULL;
}
return stream.detach();
}
static SkPDFArray* make_indexed_color_space(SkColorTable* table) {
SkPDFArray* result = new SkPDFArray();
result->reserve(4);
result->appendName("Indexed");
result->appendName("DeviceRGB");
result->appendInt(table->count() - 1);
// Potentially, this could be represented in fewer bytes with a stream.
// Max size as a string is 1.5k.
SkString index;
for (int i = 0; i < table->count(); i++) {
char buf[3];
SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]);
buf[0] = SkGetPackedR32(color);
buf[1] = SkGetPackedG32(color);
buf[2] = SkGetPackedB32(color);
index.append(buf, 3);
}
result->append(new SkPDFString(index))->unref();
return result;
}
/**
* Removes the alpha component of an ARGB color (including unpremultiply) while
* keeping the output in the same format as the input.
*/
static uint32_t remove_alpha_argb8888(uint32_t pmColor) {
SkColor color = SkUnPreMultiply::PMColorToColor(pmColor);
return SkPackARGB32NoCheck(SK_AlphaOPAQUE,
SkColorGetR(color),
SkColorGetG(color),
SkColorGetB(color));
}
static uint16_t remove_alpha_argb4444(uint16_t pmColor) {
return SkPixel32ToPixel4444(
remove_alpha_argb8888(SkPixel4444ToPixel32(pmColor)));
}
static uint32_t get_argb8888_neighbor_avg_color(const SkBitmap& bitmap,
int xOrig, int yOrig) {
uint8_t count = 0;
uint16_t r = 0;
uint16_t g = 0;
uint16_t b = 0;
for (int y = yOrig - 1; y <= yOrig + 1; y++) {
if (y < 0 || y >= bitmap.height()) {
continue;
}
uint32_t* src = bitmap.getAddr32(0, y);
for (int x = xOrig - 1; x <= xOrig + 1; x++) {
if (x < 0 || x >= bitmap.width()) {
continue;
}
if (SkGetPackedA32(src[x]) != SK_AlphaTRANSPARENT) {
uint32_t color = remove_alpha_argb8888(src[x]);
r += SkGetPackedR32(color);
g += SkGetPackedG32(color);
b += SkGetPackedB32(color);
count++;
}
}
}
if (count == 0) {
return SkPackARGB32NoCheck(SK_AlphaOPAQUE, 0, 0, 0);
} else {
return SkPackARGB32NoCheck(SK_AlphaOPAQUE,
r / count, g / count, b / count);
}
}
static uint16_t get_argb4444_neighbor_avg_color(const SkBitmap& bitmap,
int xOrig, int yOrig) {
uint8_t count = 0;
uint8_t r = 0;
uint8_t g = 0;
uint8_t b = 0;
for (int y = yOrig - 1; y <= yOrig + 1; y++) {
if (y < 0 || y >= bitmap.height()) {
continue;
}
uint16_t* src = bitmap.getAddr16(0, y);
for (int x = xOrig - 1; x <= xOrig + 1; x++) {
if (x < 0 || x >= bitmap.width()) {
continue;
}
if ((SkGetPackedA4444(src[x]) & 0x0F) != SK_AlphaTRANSPARENT) {
uint16_t color = remove_alpha_argb4444(src[x]);
r += SkGetPackedR4444(color);
g += SkGetPackedG4444(color);
b += SkGetPackedB4444(color);
count++;
}
}
}
if (count == 0) {
return SkPackARGB4444(SK_AlphaOPAQUE & 0x0F, 0, 0, 0);
} else {
return SkPackARGB4444(SK_AlphaOPAQUE & 0x0F,
r / count, g / count, b / count);
}
}
static SkBitmap unpremultiply_bitmap(const SkBitmap& bitmap,
const SkIRect& srcRect) {
SkBitmap outBitmap;
outBitmap.allocPixels(bitmap.info().makeWH(srcRect.width(), srcRect.height()));
int dstRow = 0;
SkAutoLockPixels outBitmapPixelLock(outBitmap);
SkAutoLockPixels bitmapPixelLock(bitmap);
switch (bitmap.colorType()) {
case kARGB_4444_SkColorType: {
for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
uint16_t* dst = outBitmap.getAddr16(0, dstRow);
uint16_t* src = bitmap.getAddr16(0, y);
for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
uint8_t a = SkGetPackedA4444(src[x]);
// It is necessary to average the color component of
// transparent pixels with their surrounding neighbors
// since the PDF renderer may separately re-sample the
// alpha and color channels when the image is not
// displayed at its native resolution. Since an alpha of
// zero gives no information about the color component,
// the pathological case is a white image with sharp
// transparency bounds - the color channel goes to black,
// and the should-be-transparent pixels are rendered
// as grey because of the separate soft mask and color
// resizing.
if (a == (SK_AlphaTRANSPARENT & 0x0F)) {
*dst = get_argb4444_neighbor_avg_color(bitmap, x, y);
} else {
*dst = remove_alpha_argb4444(src[x]);
}
dst++;
}
dstRow++;
}
break;
}
case kN32_SkColorType: {
for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
uint32_t* dst = outBitmap.getAddr32(0, dstRow);
uint32_t* src = bitmap.getAddr32(0, y);
for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
uint8_t a = SkGetPackedA32(src[x]);
if (a == SK_AlphaTRANSPARENT) {
*dst = get_argb8888_neighbor_avg_color(bitmap, x, y);
} else {
*dst = remove_alpha_argb8888(src[x]);
}
dst++;
}
dstRow++;
}
break;
}
default:
SkASSERT(false);
}
outBitmap.setImmutable();
return outBitmap;
}
// static
SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap,
const SkIRect& srcRect) {
if (bitmap.colorType() == kUnknown_SkColorType) {
return NULL;
}
bool isTransparent = false;
SkAutoTDelete<SkStream> alphaData;
if (!bitmap.isOpaque()) {
// Note that isOpaque is not guaranteed to return false for bitmaps
// with alpha support but a completely opaque alpha channel,
// so alphaData may still be NULL if we have a completely opaque
// (or transparent) bitmap.
alphaData.reset(
extract_image_data(bitmap, srcRect, true, &isTransparent));
}
if (isTransparent) {
return NULL;
}
SkPDFImage* image;
SkColorType colorType = bitmap.colorType();
if (alphaData.get() != NULL && (kN32_SkColorType == colorType ||
kARGB_4444_SkColorType == colorType)) {
if (kN32_SkColorType == colorType) {
image = SkNEW_ARGS(SkPDFImage, (NULL, bitmap, false,
SkIRect::MakeWH(srcRect.width(),
srcRect.height())));
} else {
SkBitmap unpremulBitmap = unpremultiply_bitmap(bitmap, srcRect);
image = SkNEW_ARGS(SkPDFImage, (NULL, unpremulBitmap, false,
SkIRect::MakeWH(srcRect.width(),
srcRect.height())));
}
} else {
image = SkNEW_ARGS(SkPDFImage, (NULL, bitmap, false, srcRect));
}
if (alphaData.get() != NULL) {
SkAutoTUnref<SkPDFImage> mask(
SkNEW_ARGS(SkPDFImage, (alphaData.get(), bitmap, true, srcRect)));
image->insert("SMask", new SkPDFObjRef(mask))->unref();
}
return image;
}
SkPDFImage::~SkPDFImage() {}
SkPDFImage::SkPDFImage(SkStream* stream,
const SkBitmap& bitmap,
bool isAlpha,
const SkIRect& srcRect)
: fIsAlpha(isAlpha),
fSrcRect(srcRect) {
if (bitmap.isImmutable()) {
fBitmap = bitmap;
} else {
bitmap.deepCopyTo(&fBitmap);
fBitmap.setImmutable();
}
if (stream != NULL) {
this->setData(stream);
fStreamValid = true;
} else {
fStreamValid = false;
}
SkColorType colorType = fBitmap.colorType();
insertName("Type", "XObject");
insertName("Subtype", "Image");
bool alphaOnly = (kAlpha_8_SkColorType == colorType);
if (!isAlpha && alphaOnly) {
// For alpha only images, we stretch a single pixel of black for
// the color/shape part.
SkAutoTUnref<SkPDFInt> one(new SkPDFInt(1));
insert("Width", one.get());
insert("Height", one.get());
} else {
insertInt("Width", fSrcRect.width());
insertInt("Height", fSrcRect.height());
}
if (isAlpha || alphaOnly) {
insertName("ColorSpace", "DeviceGray");
} else if (kIndex_8_SkColorType == colorType) {
SkAutoLockPixels alp(fBitmap);
insert("ColorSpace",
make_indexed_color_space(fBitmap.getColorTable()))->unref();
} else {
insertName("ColorSpace", "DeviceRGB");
}
int bitsPerComp = 8;
if (kARGB_4444_SkColorType == colorType) {
bitsPerComp = 4;
}
insertInt("BitsPerComponent", bitsPerComp);
if (kRGB_565_SkColorType == colorType) {
SkASSERT(!isAlpha);
SkAutoTUnref<SkPDFInt> zeroVal(new SkPDFInt(0));
SkAutoTUnref<SkPDFScalar> scale5Val(
new SkPDFScalar(8.2258f)); // 255/2^5-1
SkAutoTUnref<SkPDFScalar> scale6Val(
new SkPDFScalar(4.0476f)); // 255/2^6-1
SkAutoTUnref<SkPDFArray> decodeValue(new SkPDFArray());
decodeValue->reserve(6);
decodeValue->append(zeroVal.get());
decodeValue->append(scale5Val.get());
decodeValue->append(zeroVal.get());
decodeValue->append(scale6Val.get());
decodeValue->append(zeroVal.get());
decodeValue->append(scale5Val.get());
insert("Decode", decodeValue.get());
}
}
SkPDFImage::SkPDFImage(SkPDFImage& pdfImage)
: SkPDFStream(pdfImage),
fBitmap(pdfImage.fBitmap),
fIsAlpha(pdfImage.fIsAlpha),
fSrcRect(pdfImage.fSrcRect),
fStreamValid(pdfImage.fStreamValid) {
// Nothing to do here - the image params are already copied in SkPDFStream's
// constructor, and the bitmap will be regenerated and encoded in
// populate.
}
bool SkPDFImage::populate(SkPDFCatalog* catalog) {
if (getState() == kUnused_State) {
// Initializing image data for the first time.
// Fallback method
if (!fStreamValid) {
SkAutoTDelete<SkStream> stream(
extract_image_data(fBitmap, fSrcRect, fIsAlpha, NULL));
this->setData(stream);
fStreamValid = true;
}
return INHERITED::populate(catalog);
}
#ifndef SK_NO_FLATE
else if (getState() == kNoCompression_State) {
// Compression has not been requested when the stream was first created,
// but the new catalog wants it compressed.
if (!getSubstitute()) {
SkPDFStream* substitute = SkNEW_ARGS(SkPDFImage, (*this));
setSubstitute(substitute);
catalog->setSubstitute(this, substitute);
}
return false;
}
#endif // SK_NO_FLATE
return true;
}
#if 0 // reenable when we can figure out the JPEG colorspace
namespace {
/**
* This PDFObject assumes that its constructor was handed
* Jpeg-encoded data that can be directly embedded into a PDF.
*/
class PDFJPEGImage : public SkPDFObject {
SkAutoTUnref<SkData> fData;
int fWidth;
int fHeight;
public:
PDFJPEGImage(SkData* data, int width, int height)
: fData(SkRef(data)), fWidth(width), fHeight(height) {}
virtual void emitObject(
SkWStream* stream,
SkPDFCatalog* catalog, bool indirect) SK_OVERRIDE {
if (indirect) {
this->emitIndirectObject(stream, catalog);
return;
}
SkASSERT(fData.get());
const char kPrefaceFormat[] =
"<<"
"/Type /XObject\n"
"/Subtype /Image\n"
"/Width %d\n"
"/Height %d\n"
"/ColorSpace /DeviceRGB\n" // or DeviceGray
"/BitsPerComponent 8\n"
"/Filter /DCTDecode\n"
"/ColorTransform 0\n"
"/Length " SK_SIZE_T_SPECIFIER "\n"
">> stream\n";
SkString preface(
SkStringPrintf(kPrefaceFormat, fWidth, fHeight, fData->size()));
const char kPostface[] = "\nendstream";
stream->write(preface.c_str(), preface.size());
stream->write(fData->data(), fData->size());
stream->write(kPostface, sizeof(kPostface));
}
};
/**
* If the bitmap is not subsetted, return its encoded data, if
* availible.
*/
static inline SkData* ref_encoded_data(const SkBitmap& bm) {
if ((NULL == bm.pixelRef())
|| !bm.pixelRefOrigin().isZero()
|| (bm.info().dimensions() != bm.pixelRef()->info().dimensions())) {
return NULL;
}
return bm.pixelRef()->refEncodedData();
}
/*
* This functions may give false negatives but no false positives.
*/
static bool is_jfif_jpeg(SkData* data) {
if (!data || (data->size() < 11)) {
return false;
}
const uint8_t bytesZeroToThree[] = {0xFF, 0xD8, 0xFF, 0xE0};
const uint8_t bytesSixToTen[] = {'J', 'F', 'I', 'F', 0};
// 0 1 2 3 4 5 6 7 8 9 10
// FF D8 FF E0 ?? ?? 'J' 'F' 'I' 'F' 00 ...
return ((0 == memcmp(data->bytes(), bytesZeroToThree,
sizeof(bytesZeroToThree)))
&& (0 == memcmp(data->bytes() + 6, bytesSixToTen,
sizeof(bytesSixToTen))));
}
} // namespace
#endif
SkPDFObject* SkPDFCreateImageObject(SkPDFCanon* canon,
const SkBitmap& bitmap,
const SkIRect& subset) {
if (SkPDFObject* pdfBitmap = SkPDFBitmap::Create(canon, bitmap, subset)) {
return pdfBitmap;
}
#if 0 // reenable when we can figure out the JPEG colorspace
if (SkIRect::MakeWH(bitmap.width(), bitmap.height()) == subset) {
SkAutoTUnref<SkData> encodedData(ref_encoded_data(bitmap));
if (is_jfif_jpeg(encodedData)) {
return SkNEW_ARGS(PDFJPEGImage,
(encodedData, bitmap.width(), bitmap.height()));
}
}
#endif
return SkPDFImage::CreateImage(bitmap, subset);
}

91
src/pdf/SkPDFImage.h Normal file
View File

@ -0,0 +1,91 @@
/*
* Copyright 2010 The Android Open Source Project
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkPDFImage_DEFINED
#define SkPDFImage_DEFINED
#include "SkPicture.h"
#include "SkPDFDevice.h"
#include "SkPDFStream.h"
#include "SkPDFTypes.h"
#include "SkRefCnt.h"
class SkBitmap;
class SkData;
class SkPDFCatalog;
struct SkIRect;
/**
* Return the mose efficient availible encoding of the given bitmap.
*/
SkPDFObject* SkPDFCreateImageObject(SkPDFCanon* canon,
const SkBitmap&,
const SkIRect& subset);
/** \class SkPDFImage
An image XObject.
*/
// 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 SkPDFStream {
public:
/** Create a new Image XObject to represent the passed bitmap.
* @param bitmap The image to encode.
* @param srcRect The rectangle to cut out of bitmap.
* @param paint Used to calculate alpha, masks, etc.
* @return The image XObject or NUll if there is nothing to draw for
* the given parameters.
*/
static SkPDFImage* CreateImage(const SkBitmap& bitmap,
const SkIRect& srcRect);
virtual ~SkPDFImage();
bool isEmpty() {
return fSrcRect.isEmpty();
}
private:
SkBitmap fBitmap;
bool fIsAlpha;
SkIRect fSrcRect;
bool fStreamValid;
/** Create a PDF image XObject. Entries for the image properties are
* automatically added to the stream dictionary.
* @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.
* Will be duplicated, and left in indeterminate state.
* @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.
*/
SkPDFImage(SkStream* stream, const SkBitmap& bitmap, bool isAlpha,
const SkIRect& srcRect);
/** 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