PDF: remove last use of SkPDFImage
Add a GM. BUG=skia:255 Review URL: https://codereview.chromium.org/950633003
This commit is contained in:
parent
bf0c56f82b
commit
db0dcc7436
193
gm/all_bitmap_configs.cpp
Normal file
193
gm/all_bitmap_configs.cpp
Normal file
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#include "SkMath.h"
|
||||
#include "SkColorPriv.h"
|
||||
|
||||
static SkBitmap copy_bitmap(const SkBitmap& src, SkColorType colorType) {
|
||||
SkBitmap copy;
|
||||
src.copyTo(©, colorType);
|
||||
copy.setImmutable();
|
||||
return copy;
|
||||
}
|
||||
|
||||
#define SCALE 128
|
||||
|
||||
// Make either A8 or gray8 bitmap.
|
||||
static SkBitmap make_bitmap(SkColorType ct) {
|
||||
SkBitmap bm;
|
||||
switch (ct) {
|
||||
case kAlpha_8_SkColorType:
|
||||
bm.allocPixels(SkImageInfo::MakeA8(SCALE, SCALE));
|
||||
break;
|
||||
case kGray_8_SkColorType:
|
||||
bm.allocPixels(
|
||||
SkImageInfo::Make(SCALE, SCALE, ct, kOpaque_SkAlphaType));
|
||||
break;
|
||||
default:
|
||||
SkASSERT(false);
|
||||
return bm;
|
||||
}
|
||||
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_center_letter(char c,
|
||||
SkPaint* p,
|
||||
SkColor color,
|
||||
SkScalar x,
|
||||
SkScalar y,
|
||||
SkCanvas* canvas) {
|
||||
SkRect bounds;
|
||||
p->setColor(color);
|
||||
p->measureText(&c, 1, &bounds);
|
||||
canvas->drawText(&c, 1, x - bounds.centerX(), y - bounds.centerY(), *p);
|
||||
}
|
||||
|
||||
static void color_wheel_native(SkCanvas* canvas) {
|
||||
SkAutoCanvasRestore autoCanvasRestore(canvas, true);
|
||||
canvas->translate(0.5f * SCALE, 0.5f * SCALE);
|
||||
SkPaint p;
|
||||
p.setAntiAlias(false);
|
||||
p.setColor(SK_ColorWHITE);
|
||||
canvas->drawCircle(0.0f, 0.0f, SCALE * 0.5f, p);
|
||||
|
||||
const double sqrt_3_over_2 = 0.8660254037844387;
|
||||
const SkScalar Z = 0.0f;
|
||||
const SkScalar D = 0.3f * SkIntToScalar(SCALE);
|
||||
const SkScalar X = SkDoubleToScalar(D * sqrt_3_over_2);
|
||||
const SkScalar Y = D * SK_ScalarHalf;
|
||||
sk_tool_utils::set_portable_typeface(&p, NULL, SkTypeface::kBold);
|
||||
p.setTextSize(0.28125f * SCALE);
|
||||
draw_center_letter('K', &p, SK_ColorBLACK, Z, Z, canvas);
|
||||
draw_center_letter('R', &p, SK_ColorRED, Z, D, canvas);
|
||||
draw_center_letter('G', &p, SK_ColorGREEN, -X, -Y, canvas);
|
||||
draw_center_letter('B', &p, SK_ColorBLUE, X, -Y, canvas);
|
||||
draw_center_letter('C', &p, SK_ColorCYAN, Z, -D, canvas);
|
||||
draw_center_letter('M', &p, SK_ColorMAGENTA, X, Y, canvas);
|
||||
draw_center_letter('Y', &p, SK_ColorYELLOW, -X, Y, canvas);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
int find(T* array, int N, T item) {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
if (array[i] == item) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static SkPMColor premultiply_color(SkColor c) {
|
||||
return SkPremultiplyARGBInline(SkColorGetA(c), SkColorGetR(c),
|
||||
SkColorGetG(c), SkColorGetB(c));
|
||||
}
|
||||
|
||||
static SkBitmap indexed_bitmap() {
|
||||
SkBitmap n32bitmap;
|
||||
n32bitmap.allocN32Pixels(SCALE, SCALE);
|
||||
n32bitmap.eraseColor(SK_ColorTRANSPARENT);
|
||||
|
||||
SkCanvas canvas(n32bitmap);
|
||||
color_wheel_native(&canvas);
|
||||
const SkColor colors[] = {
|
||||
SK_ColorTRANSPARENT,
|
||||
SK_ColorWHITE,
|
||||
SK_ColorBLACK,
|
||||
SK_ColorRED,
|
||||
SK_ColorGREEN,
|
||||
SK_ColorBLUE,
|
||||
SK_ColorCYAN,
|
||||
SK_ColorMAGENTA,
|
||||
SK_ColorYELLOW,
|
||||
};
|
||||
SkPMColor pmColors[SK_ARRAY_COUNT(colors)];
|
||||
for (size_t i = 0; i < SK_ARRAY_COUNT(colors); ++i) {
|
||||
pmColors[i] = premultiply_color(colors[i]);
|
||||
}
|
||||
SkBitmap bm;
|
||||
SkAutoTUnref<SkColorTable> ctable(
|
||||
SkNEW_ARGS(SkColorTable, (pmColors, SK_ARRAY_COUNT(pmColors))));
|
||||
SkImageInfo info = SkImageInfo::Make(SCALE, SCALE, kIndex_8_SkColorType,
|
||||
kPremul_SkAlphaType);
|
||||
bm.allocPixels(info, NULL, ctable);
|
||||
SkAutoLockPixels autoLockPixels1(n32bitmap);
|
||||
SkAutoLockPixels autoLockPixels2(bm);
|
||||
for (int y = 0; y < SCALE; ++y) {
|
||||
for (int x = 0; x < SCALE; ++x) {
|
||||
SkPMColor c = *n32bitmap.getAddr32(x, y);
|
||||
int idx = find(pmColors, SK_ARRAY_COUNT(pmColors), c);
|
||||
*bm.getAddr8(x, y) = SkClampMax(idx, SK_ARRAY_COUNT(pmColors) - 1);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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 = indexed_bitmap();
|
||||
draw(canvas, p, bitmapIndexed, kIndex_8_SkColorType, "Index 8");
|
||||
|
||||
canvas->translate(0.0f, SkIntToScalar(SCALE));
|
||||
SkBitmap bitmapA8 = make_bitmap(kAlpha_8_SkColorType);
|
||||
draw(canvas, p, bitmapA8, kAlpha_8_SkColorType, "Alpha 8");
|
||||
|
||||
p.setColor(SK_ColorRED);
|
||||
canvas->translate(0.0f, SkIntToScalar(SCALE));
|
||||
SkBitmap bitmapG8 = make_bitmap(kGray_8_SkColorType);
|
||||
draw(canvas, p, bitmapG8, kGray_8_SkColorType, "Gray 8");
|
||||
}
|
@ -16,6 +16,7 @@
|
||||
'../gm/aaclip.cpp',
|
||||
'../gm/aarectmodes.cpp',
|
||||
'../gm/addarc.cpp',
|
||||
'../gm/all_bitmap_configs.cpp',
|
||||
'../gm/alphagradients.cpp',
|
||||
'../gm/arcofzorro.cpp',
|
||||
'../gm/arithmode.cpp',
|
||||
|
@ -25,8 +25,6 @@
|
||||
'<(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',
|
||||
|
@ -25,114 +25,214 @@ 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));
|
||||
while (n) {
|
||||
size_t k = SkTMin(n, sizeof(buffer));
|
||||
out->write(buffer, k);
|
||||
n -= k;
|
||||
for (size_t i = 0; i < n / sizeof(buffer); ++i) {
|
||||
out->write(buffer, sizeof(buffer));
|
||||
}
|
||||
out->write(buffer, n % sizeof(buffer));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
if (count == 0) {
|
||||
return SkPackARGB32NoCheck(SK_AlphaOPAQUE, 0, 0, 0);
|
||||
if (a > 0) {
|
||||
rgb[0] = SkToU8(255 * r / a);
|
||||
rgb[1] = SkToU8(255 * g / a);
|
||||
rgb[2] = SkToU8(255 * b / a);
|
||||
} else {
|
||||
return SkPackARGB32NoCheck(
|
||||
SK_AlphaOPAQUE, r / count, g / count, b / count);
|
||||
rgb[0] = rgb[1] = rgb[2] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
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);
|
||||
return;
|
||||
}
|
||||
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);
|
||||
SkBitmap copy;
|
||||
const SkBitmap& bm = not4444(bitmap, ©);
|
||||
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());
|
||||
}
|
||||
return;
|
||||
}
|
||||
out->write(scanline.get(), scanlineLength);
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void bitmap_alpha_to_a8(const SkBitmap& bitmap, SkWStream* out) {
|
||||
if (!bitmap.getPixels()) {
|
||||
fill_stream(out, '\xFF', pixel_count(bitmap));
|
||||
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++);
|
||||
SkBitmap copy;
|
||||
const SkBitmap& bm = not4444(bitmap, ©);
|
||||
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;
|
||||
}
|
||||
out->write(scanline.get(), scanlineLength);
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,49 +245,40 @@ 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, bool) const;
|
||||
void emitDict(SkWStream*, SkPDFCatalog*, size_t) 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);
|
||||
pmcolor_alpha_to_a8(fBitmap, &deflateWStream);
|
||||
bitmap_alpha_to_a8(fBitmap, &deflateWStream);
|
||||
deflateWStream.finalize(); // call before detachAsStream().
|
||||
SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
|
||||
|
||||
this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true);
|
||||
this->emitDict(stream, catalog, asset->getLength());
|
||||
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,
|
||||
bool deflate) const {
|
||||
size_t length) 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);
|
||||
if (deflate) {
|
||||
pdfDict.insertName("Filter", "FlateDecode");
|
||||
}
|
||||
pdfDict.insertName("Filter", "FlateDecode");
|
||||
pdfDict.insertInt("Length", length);
|
||||
pdfDict.emitObject(stream, catalog);
|
||||
}
|
||||
@ -198,50 +289,81 @@ void PDFAlphaBitmap::emitDict(SkWStream* stream,
|
||||
void SkPDFBitmap::addResources(SkTSet<SkPDFObject*>* resourceSet,
|
||||
SkPDFCatalog* catalog) const {
|
||||
if (fSMask.get()) {
|
||||
resourceSet->add(fSMask.get());
|
||||
if (resourceSet->add(fSMask.get())) {
|
||||
fSMask->addResources(resourceSet, catalog);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
pmcolor_to_rgb24(fBitmap, &deflateWStream);
|
||||
bitmap_to_pdf_pixels(fBitmap, &deflateWStream);
|
||||
deflateWStream.finalize(); // call before detachAsStream().
|
||||
SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
|
||||
|
||||
this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true);
|
||||
this->emitDict(stream, catalog, asset->getLength());
|
||||
pdf_stream_begin(stream);
|
||||
stream->writeStream(asset.get(), asset->getLength());
|
||||
pdf_stream_end(stream);
|
||||
#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
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void SkPDFBitmap::emitDict(SkWStream* stream,
|
||||
SkPDFCatalog* catalog,
|
||||
size_t length,
|
||||
bool deflate) const {
|
||||
size_t length) const {
|
||||
SkPDFDict pdfDict("XObject");
|
||||
pdfDict.insertName("Subtype", "Image");
|
||||
pdfDict.insertInt("Width", fBitmap.width());
|
||||
pdfDict.insertInt("Height", fBitmap.height());
|
||||
pdfDict.insertName("ColorSpace", "DeviceRGB");
|
||||
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.insertInt("BitsPerComponent", 8);
|
||||
if (fSMask) {
|
||||
pdfDict.insert("SMask", new SkPDFObjRef(fSMask))->unref();
|
||||
}
|
||||
if (deflate) {
|
||||
pdfDict.insertName("Filter", "FlateDecode");
|
||||
}
|
||||
pdfDict.insertName("Filter", "FlateDecode");
|
||||
pdfDict.insertInt("Length", length);
|
||||
pdfDict.emitObject(stream, catalog);
|
||||
}
|
||||
@ -253,64 +375,35 @@ SkPDFBitmap::SkPDFBitmap(const SkBitmap& bm,
|
||||
SkPDFBitmap::~SkPDFBitmap() {}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
static bool is_transparent(const SkBitmap& bm) {
|
||||
SkAutoLockPixels autoLockPixels(bm);
|
||||
if (NULL == bm.getPixels()) {
|
||||
return true;
|
||||
|
||||
static const SkBitmap& immutable_bitmap(const SkBitmap& bm, SkBitmap* copy) {
|
||||
if (bm.isImmutable()) {
|
||||
return bm;
|
||||
}
|
||||
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;
|
||||
bm.copyTo(copy);
|
||||
copy->setImmutable();
|
||||
return *copy;
|
||||
}
|
||||
|
||||
SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon,
|
||||
const SkBitmap& bitmap,
|
||||
const SkIRect& subset) {
|
||||
SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon, const SkBitmap& bitmap) {
|
||||
SkASSERT(canon);
|
||||
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)) {
|
||||
if (!SkColorTypeIsValid(bitmap.colorType()) ||
|
||||
kUnknown_SkColorType == bitmap.colorType()) {
|
||||
return NULL;
|
||||
}
|
||||
SkBitmap copy;
|
||||
const SkBitmap& bm = immutable_bitmap(bitmap, ©);
|
||||
if (bm.drawsNothing()) {
|
||||
return NULL;
|
||||
}
|
||||
if (!bm.isImmutable()) {
|
||||
SkBitmap copy;
|
||||
if (!bm.copyTo(©)) {
|
||||
return NULL;
|
||||
}
|
||||
copy.setImmutable();
|
||||
bm = copy;
|
||||
}
|
||||
|
||||
SkPDFBitmap* pdfBitmap = canon->findBitmap(bm);
|
||||
if (pdfBitmap) {
|
||||
return SkRef(pdfBitmap);
|
||||
if (SkPDFBitmap* canonBitmap = canon->findBitmap(bm)) {
|
||||
return SkRef(canonBitmap);
|
||||
}
|
||||
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));
|
||||
}
|
||||
pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (bm, smask));
|
||||
SkPDFBitmap* pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (bm, smask));
|
||||
canon->addBitmap(pdfBitmap);
|
||||
return pdfBitmap;
|
||||
}
|
||||
|
@ -17,18 +17,15 @@ 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.
|
||||
*
|
||||
* As of now, it only supports 8888 bitmaps (the most common case).
|
||||
* If !bitmap.isImmutable(), then a copy of the bitmap must be made;
|
||||
* there is no way around this.
|
||||
*
|
||||
* The SkPDFBitmap::Create function will check the canon for duplicates.
|
||||
*/
|
||||
class SkPDFBitmap : public SkPDFObject {
|
||||
public:
|
||||
// Returns NULL on unsupported bitmap;
|
||||
// TODO(halcanary): support other bitmap colortypes and replace
|
||||
// SkPDFImage.
|
||||
static SkPDFBitmap* Create(SkPDFCanon*,
|
||||
const SkBitmap&,
|
||||
const SkIRect& subset);
|
||||
static SkPDFBitmap* Create(SkPDFCanon*, const SkBitmap&);
|
||||
~SkPDFBitmap();
|
||||
void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE;
|
||||
void addResources(SkTSet<SkPDFObject*>* resourceSet,
|
||||
@ -43,7 +40,7 @@ private:
|
||||
const SkBitmap fBitmap;
|
||||
const SkAutoTUnref<SkPDFObject> fSMask;
|
||||
SkPDFBitmap(const SkBitmap&, SkPDFObject*);
|
||||
void emitDict(SkWStream*, SkPDFCatalog*, size_t, bool) const;
|
||||
void emitDict(SkWStream*, SkPDFCatalog*, size_t) const;
|
||||
};
|
||||
|
||||
#endif // SkPDFBitmap_DEFINED
|
||||
|
@ -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,8 +2134,12 @@ void SkPDFDevice::internalDrawBitmap(const SkMatrix& origMatrix,
|
||||
return;
|
||||
}
|
||||
|
||||
SkAutoTUnref<SkPDFObject> image(
|
||||
SkPDFCreateImageObject(fCanon, *bitmap, subset));
|
||||
SkBitmap subsetBitmap;
|
||||
// Should extractSubset be done by the SkPDFDevice?
|
||||
if (!bitmap->extractSubset(&subsetBitmap, subset)) {
|
||||
return;
|
||||
}
|
||||
SkAutoTUnref<SkPDFObject> image(SkPDFBitmap::Create(fCanon, subsetBitmap));
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
|
@ -1,727 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
|
||||
/*
|
||||
* 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
|
Loading…
Reference in New Issue
Block a user