Delete dead SkImageDecoder::buildTileIndex and decodeSubset code

This approach to subset decoding is no longer supported.
We have replaced it with an implementation that does not
depend on forked libraries.
https://codereview.chromium.org/1406153015/

BUG=skia:

Review URL: https://codereview.chromium.org/1426943009
This commit is contained in:
msarett 2015-11-10 15:38:23 -08:00 committed by Commit bot
parent 5c9e34a350
commit 5c351f38fa
6 changed files with 0 additions and 925 deletions

View File

@ -237,26 +237,6 @@ public:
return this->decode(stream, bitmap, kUnknown_SkColorType, mode);
}
/**
* Given a stream, build an index for doing tile-based decode.
* The built index will be saved in the decoder, and the image size will
* be returned in width and height.
*
* Takes ownership of the SkStreamRewindable, on success or failure.
*
* Return true for success or false on failure.
*/
bool buildTileIndex(SkStreamRewindable*, int *width, int *height);
/**
* Decode a rectangle subset in the image.
* The method can only be called after buildTileIndex().
*
* Return true for success.
* Return false if the index is never built or failing in decoding.
*/
bool decodeSubset(SkBitmap* bm, const SkIRect& subset, SkColorType pref);
/** Given a stream, this will try to find an appropriate decoder object.
If none is found, the method returns NULL.
*/
@ -308,16 +288,6 @@ protected:
// must be overridden in subclasses. This guy is called by decode(...)
virtual Result onDecode(SkStream*, SkBitmap* bitmap, Mode) = 0;
// If the decoder wants to support tiled based decoding, this method must be overridden.
// This is called by buildTileIndex(...)
virtual bool onBuildTileIndex(SkStreamRewindable*, int* /*width*/, int* /*height*/);
// If the decoder wants to support tiled based decoding,
// this method must be overridden. This guy is called by decodeRegion(...)
virtual bool onDecodeSubset(SkBitmap*, const SkIRect&) {
return false;
}
/** If planes or rowBytes is NULL, decodes the header and computes componentSizes
for memory allocation.
Otherwise, decodes the YUV planes into the provided image planes and
@ -330,25 +300,6 @@ protected:
return false;
}
/*
* Crop a rectangle from the src Bitmap to the dest Bitmap. src and dst are
* both sampled by sampleSize from an original Bitmap.
*
* @param dst the destination bitmap.
* @param src the source bitmap that is sampled by sampleSize from the
* original bitmap.
* @param sampleSize the sample size that src is sampled from the original bitmap.
* @param (dstX, dstY) the upper-left point of the dest bitmap in terms of
* the coordinate in the original bitmap.
* @param (width, height) the width and height of the unsampled dst.
* @param (srcX, srcY) the upper-left point of the src bitmap in terms of
* the coordinate in the original bitmap.
* @return bool Whether or not it succeeded.
*/
bool cropBitmap(SkBitmap *dst, SkBitmap *src, int sampleSize,
int dstX, int dstY, int width, int height,
int srcX, int srcY);
/**
* Copy all fields on this decoder to the other decoder. Used by subclasses
* to decode a subimage using a different decoder, but with the same settings.

View File

@ -142,76 +142,6 @@ SkImageDecoder::Result SkImageDecoder::decode(SkStream* stream, SkBitmap* bm, Sk
return result;
}
bool SkImageDecoder::decodeSubset(SkBitmap* bm, const SkIRect& rect, SkColorType pref) {
// we reset this to false before calling onDecodeSubset
fShouldCancelDecode = false;
// assign this, for use by getPrefColorType(), in case fUsePrefTable is false
fDefaultPref = pref;
return this->onDecodeSubset(bm, rect);
}
bool SkImageDecoder::buildTileIndex(SkStreamRewindable* stream, int *width, int *height) {
// we reset this to false before calling onBuildTileIndex
fShouldCancelDecode = false;
return this->onBuildTileIndex(stream, width, height);
}
bool SkImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, int* /*width*/,
int* /*height*/) {
delete stream;
return false;
}
bool SkImageDecoder::cropBitmap(SkBitmap *dst, SkBitmap *src, int sampleSize,
int dstX, int dstY, int width, int height,
int srcX, int srcY) {
int w = width / sampleSize;
int h = height / sampleSize;
if (src->colorType() == kIndex_8_SkColorType) {
// kIndex8 does not allow drawing via an SkCanvas, as is done below.
// Instead, use extractSubset. Note that this shares the SkPixelRef and
// SkColorTable.
// FIXME: Since src is discarded in practice, this holds on to more
// pixels than is strictly necessary. Switch to a copy if memory
// savings are more important than speed here. This also means
// that the pixels in dst can not be reused (though there is no
// allocation, which was already done on src).
int x = (dstX - srcX) / sampleSize;
int y = (dstY - srcY) / sampleSize;
SkIRect subset = SkIRect::MakeXYWH(x, y, w, h);
return src->extractSubset(dst, subset);
}
// if the destination has no pixels then we must allocate them.
if (dst->isNull()) {
dst->setInfo(src->info().makeWH(w, h));
if (!this->allocPixelRef(dst, nullptr)) {
SkDEBUGF(("failed to allocate pixels needed to crop the bitmap"));
return false;
}
}
// check to see if the destination is large enough to decode the desired
// region. If this assert fails we will just draw as much of the source
// into the destination that we can.
if (dst->width() < w || dst->height() < h) {
SkDEBUGF(("SkImageDecoder::cropBitmap does not have a large enough bitmap.\n"));
}
// Set the Src_Mode for the paint to prevent transparency issue in the
// dest in the event that the dest was being re-used.
SkPaint paint;
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
SkCanvas canvas(*dst);
canvas.drawBitmap(*src, SkIntToScalar((srcX - dstX) / sampleSize),
SkIntToScalar((srcY - dstY) / sampleSize),
&paint);
return true;
}
///////////////////////////////////////////////////////////////////////////////
bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm, SkColorType pref, Mode mode,

View File

@ -85,154 +85,20 @@ static void initialize_info(jpeg_decompress_struct* cinfo, skjpeg_source_mgr* sr
}
}
#ifdef SK_JPEG_INDEX_SUPPORTED
class SkJPEGImageIndex {
public:
// Takes ownership of stream.
SkJPEGImageIndex(SkStreamRewindable* stream, SkImageDecoder* decoder)
: fSrcMgr(stream, decoder)
, fStream(stream)
, fInfoInitialized(false)
, fHuffmanCreated(false)
, fDecompressStarted(false)
{
SkDEBUGCODE(fReadHeaderSucceeded = false;)
}
~SkJPEGImageIndex() {
if (fHuffmanCreated) {
// Set to false before calling the libjpeg function, in case
// the libjpeg function calls longjmp. Our setjmp handler may
// attempt to delete this SkJPEGImageIndex, thus entering this
// destructor again. Setting fHuffmanCreated to false first
// prevents an infinite loop.
fHuffmanCreated = false;
jpeg_destroy_huffman_index(&fHuffmanIndex);
}
if (fDecompressStarted) {
// Like fHuffmanCreated, set to false before calling libjpeg
// function to prevent potential infinite loop.
fDecompressStarted = false;
jpeg_finish_decompress(&fCInfo);
}
if (fInfoInitialized) {
this->destroyInfo();
}
}
/**
* Destroy the cinfo struct.
* After this call, if a huffman index was already built, it
* can be used after calling initializeInfoAndReadHeader
* again. Must not be called after startTileDecompress except
* in the destructor.
*/
void destroyInfo() {
SkASSERT(fInfoInitialized);
SkASSERT(!fDecompressStarted);
// Like fHuffmanCreated, set to false before calling libjpeg
// function to prevent potential infinite loop.
fInfoInitialized = false;
jpeg_destroy_decompress(&fCInfo);
SkDEBUGCODE(fReadHeaderSucceeded = false;)
}
/**
* Initialize the cinfo struct.
* Calls jpeg_create_decompress, makes customizations, and
* finally calls jpeg_read_header. Returns true if jpeg_read_header
* returns JPEG_HEADER_OK.
* If cinfo was already initialized, destroyInfo must be called to
* destroy the old one. Must not be called after startTileDecompress.
*/
bool initializeInfoAndReadHeader() {
SkASSERT(!fInfoInitialized && !fDecompressStarted);
initialize_info(&fCInfo, &fSrcMgr);
fInfoInitialized = true;
const bool success = (JPEG_HEADER_OK == jpeg_read_header(&fCInfo, true));
SkDEBUGCODE(fReadHeaderSucceeded = success;)
return success;
}
jpeg_decompress_struct* cinfo() { return &fCInfo; }
huffman_index* huffmanIndex() { return &fHuffmanIndex; }
/**
* Build the index to be used for tile based decoding.
* Must only be called after a successful call to
* initializeInfoAndReadHeader and must not be called more
* than once.
*/
bool buildHuffmanIndex() {
SkASSERT(fReadHeaderSucceeded);
SkASSERT(!fHuffmanCreated);
jpeg_create_huffman_index(&fCInfo, &fHuffmanIndex);
SkASSERT(1 == fCInfo.scale_num && 1 == fCInfo.scale_denom);
fHuffmanCreated = jpeg_build_huffman_index(&fCInfo, &fHuffmanIndex);
return fHuffmanCreated;
}
/**
* Start tile based decoding. Must only be called after a
* successful call to buildHuffmanIndex, and must only be
* called once.
*/
bool startTileDecompress() {
SkASSERT(fHuffmanCreated);
SkASSERT(fReadHeaderSucceeded);
SkASSERT(!fDecompressStarted);
if (jpeg_start_tile_decompress(&fCInfo)) {
fDecompressStarted = true;
return true;
}
return false;
}
private:
skjpeg_source_mgr fSrcMgr;
SkAutoTDelete<SkStream> fStream;
jpeg_decompress_struct fCInfo;
huffman_index fHuffmanIndex;
bool fInfoInitialized;
bool fHuffmanCreated;
bool fDecompressStarted;
SkDEBUGCODE(bool fReadHeaderSucceeded;)
};
#endif
class SkJPEGImageDecoder : public SkImageDecoder {
public:
#ifdef SK_JPEG_INDEX_SUPPORTED
SkJPEGImageDecoder() {
fImageIndex = nullptr;
fImageWidth = 0;
fImageHeight = 0;
}
virtual ~SkJPEGImageDecoder() { delete fImageIndex; }
#endif
Format getFormat() const override {
return kJPEG_Format;
}
protected:
#ifdef SK_JPEG_INDEX_SUPPORTED
bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) override;
bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) override;
#endif
Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override;
bool onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3],
void* planes[3], size_t rowBytes[3],
SkYUVColorSpace* colorSpace) override;
private:
#ifdef SK_JPEG_INDEX_SUPPORTED
SkJPEGImageIndex* fImageIndex;
int fImageWidth;
int fImageHeight;
#endif
/**
* Determine the appropriate bitmap colortype and out_color_space based on
@ -295,20 +161,6 @@ static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer, int count
return true;
}
#ifdef SK_JPEG_INDEX_SUPPORTED
static bool skip_src_rows_tile(jpeg_decompress_struct* cinfo,
huffman_index *index, void* buffer, int count) {
for (int i = 0; i < count; i++) {
JSAMPLE* rowptr = (JSAMPLE*)buffer;
int row_count = jpeg_read_tile_scanline(cinfo, index, &rowptr);
if (1 != row_count) {
return false;
}
}
return true;
}
#endif
///////////////////////////////////////////////////////////////////////////////
// This guy exists just to aid in debugging, as it allows debuggers to just
@ -329,14 +181,6 @@ static bool return_false(const jpeg_decompress_struct& cinfo,
return false;
}
#ifdef SK_JPEG_INDEX_SUPPORTED
static bool return_false(const jpeg_decompress_struct& cinfo,
const SkBitmap& bm, const char caller[]) {
print_jpeg_decoder_errors(cinfo, bm.width(), bm.height(), caller);
return false;
}
#endif
static SkImageDecoder::Result return_failure(const jpeg_decompress_struct& cinfo,
const SkBitmap& bm, const char caller[]) {
print_jpeg_decoder_errors(cinfo, bm.width(), bm.height(), caller);
@ -939,240 +783,6 @@ bool SkJPEGImageDecoder::onDecodeYUV8Planes(SkStream* stream, SkISize componentS
///////////////////////////////////////////////////////////////////////////////
#ifdef SK_JPEG_INDEX_SUPPORTED
bool SkJPEGImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, int *width, int *height) {
SkAutoTDelete<SkJPEGImageIndex> imageIndex(new SkJPEGImageIndex(stream, this));
skjpeg_error_mgr sk_err;
set_error_mgr(imageIndex->cinfo(), &sk_err);
// All objects need to be instantiated before this setjmp call so that
// they will be cleaned up properly if an error occurs.
if (setjmp(sk_err.fJmpBuf)) {
return false;
}
// create the cinfo used to create/build the huffmanIndex
if (!imageIndex->initializeInfoAndReadHeader()) {
return false;
}
if (!imageIndex->buildHuffmanIndex()) {
return false;
}
// destroy the cinfo used to create/build the huffman index
imageIndex->destroyInfo();
// Init decoder to image decode mode
if (!imageIndex->initializeInfoAndReadHeader()) {
return false;
}
jpeg_decompress_struct* cinfo = imageIndex->cinfo();
// We have a new cinfo, so set the error mgr again.
set_error_mgr(cinfo, &sk_err);
// FIXME: This sets cinfo->out_color_space, which we may change later
// based on the config in onDecodeSubset. This should be fine, since
// jpeg_init_read_tile_scanline will check out_color_space again after
// that change (when it calls jinit_color_deconverter).
(void) this->getBitmapColorType(cinfo);
turn_off_visual_optimizations(cinfo);
// instead of jpeg_start_decompress() we start a tiled decompress
if (!imageIndex->startTileDecompress()) {
return false;
}
SkASSERT(1 == cinfo->scale_num);
fImageWidth = cinfo->output_width;
fImageHeight = cinfo->output_height;
if (width) {
*width = fImageWidth;
}
if (height) {
*height = fImageHeight;
}
delete fImageIndex;
fImageIndex = imageIndex.detach();
return true;
}
bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
if (nullptr == fImageIndex) {
return false;
}
jpeg_decompress_struct* cinfo = fImageIndex->cinfo();
SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);
if (!rect.intersect(region)) {
// If the requested region is entirely outside the image return false
return false;
}
skjpeg_error_mgr errorManager;
set_error_mgr(cinfo, &errorManager);
if (setjmp(errorManager.fJmpBuf)) {
return false;
}
int requestedSampleSize = this->getSampleSize();
cinfo->scale_denom = requestedSampleSize;
set_dct_method(*this, cinfo);
const SkColorType colorType = this->getBitmapColorType(cinfo);
adjust_out_color_space_and_dither(cinfo, colorType, *this);
int startX = rect.fLeft;
int startY = rect.fTop;
int width = rect.width();
int height = rect.height();
jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(),
&startX, &startY, &width, &height);
int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);
int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);
SkScaledBitmapSampler sampler(width, height, skiaSampleSize);
SkBitmap bitmap;
// Assume an A8 bitmap is not opaque to avoid the check of each
// individual pixel. It is very unlikely to be opaque, since
// an opaque A8 bitmap would not be very interesting.
// Otherwise, a jpeg image is opaque.
bitmap.setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(), colorType,
kAlpha_8_SkColorType == colorType ?
kPremul_SkAlphaType : kOpaque_SkAlphaType));
// Check ahead of time if the swap(dest, src) is possible or not.
// If yes, then we will stick to AllocPixelRef since it's cheaper with the
// swap happening. If no, then we will use alloc to allocate pixels to
// prevent garbage collection.
int w = rect.width() / actualSampleSize;
int h = rect.height() / actualSampleSize;
bool swapOnly = (rect == region) && bm->isNull() &&
(w == bitmap.width()) && (h == bitmap.height()) &&
((startX - rect.x()) / actualSampleSize == 0) &&
((startY - rect.y()) / actualSampleSize == 0);
if (swapOnly) {
if (!this->allocPixelRef(&bitmap, nullptr)) {
return return_false(*cinfo, bitmap, "allocPixelRef");
}
} else {
if (!bitmap.tryAllocPixels()) {
return return_false(*cinfo, bitmap, "allocPixels");
}
}
SkAutoLockPixels alp(bitmap);
#ifdef ANDROID_RGB
/* short-circuit the SkScaledBitmapSampler when possible, as this gives
a significant performance boost.
*/
if (skiaSampleSize == 1 &&
((kN32_SkColorType == colorType && cinfo->out_color_space == JCS_RGBA_8888) ||
(kRGB_565_SkColorType == colorType && cinfo->out_color_space == JCS_RGB_565)))
{
JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();
INT32 const bpr = bitmap.rowBytes();
int rowTotalCount = 0;
while (rowTotalCount < height) {
int rowCount = jpeg_read_tile_scanline(cinfo,
fImageIndex->huffmanIndex(),
&rowptr);
// if rowCount == 0, then we didn't get a scanline, so abort.
// onDecodeSubset() relies on onBuildTileIndex(), which
// needs a complete image to succeed.
if (0 == rowCount) {
return return_false(*cinfo, bitmap, "read_scanlines");
}
if (this->shouldCancelDecode()) {
return return_false(*cinfo, bitmap, "shouldCancelDecode");
}
rowTotalCount += rowCount;
rowptr += bpr;
}
if (swapOnly) {
bm->swap(bitmap);
return true;
}
return cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
region.width(), region.height(), startX, startY);
}
#endif
// check for supported formats
SkScaledBitmapSampler::SrcConfig sc;
int srcBytesPerPixel;
if (!get_src_config(*cinfo, &sc, &srcBytesPerPixel)) {
return return_false(*cinfo, *bm, "jpeg colorspace");
}
if (!sampler.begin(&bitmap, sc, *this)) {
return return_false(*cinfo, bitmap, "sampler.begin");
}
SkAutoMalloc srcStorage(width * srcBytesPerPixel);
uint8_t* srcRow = (uint8_t*)srcStorage.get();
// Possibly skip initial rows [sampler.srcY0]
if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) {
return return_false(*cinfo, bitmap, "skip rows");
}
// now loop through scanlines until y == bitmap->height() - 1
for (int y = 0;; y++) {
JSAMPLE* rowptr = (JSAMPLE*)srcRow;
int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);
// if row_count == 0, then we didn't get a scanline, so abort.
// onDecodeSubset() relies on onBuildTileIndex(), which
// needs a complete image to succeed.
if (0 == row_count) {
return return_false(*cinfo, bitmap, "read_scanlines");
}
if (this->shouldCancelDecode()) {
return return_false(*cinfo, bitmap, "shouldCancelDecode");
}
if (JCS_CMYK == cinfo->out_color_space) {
convert_CMYK_to_RGB(srcRow, width);
}
sampler.next(srcRow);
if (bitmap.height() - 1 == y) {
// we're done
break;
}
if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow,
sampler.srcDY() - 1)) {
return return_false(*cinfo, bitmap, "skip rows");
}
}
if (swapOnly) {
bm->swap(bitmap);
return true;
}
return cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),
region.width(), region.height(), startX, startY);
}
#endif
///////////////////////////////////////////////////////////////////////////////
#include "SkColorPriv.h"
// taken from jcolor.c in libjpeg

View File

@ -82,10 +82,6 @@ public:
virtual ~SkPNGImageDecoder() { delete fImageIndex; }
protected:
#ifdef SK_PNG_INDEX_SUPPORTED
bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) override;
bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& region) override;
#endif
Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override;
private:
@ -126,16 +122,6 @@ static void sk_read_fn(png_structp png_ptr, png_bytep data, png_size_t length) {
}
}
#ifdef SK_PNG_INDEX_SUPPORTED
static void sk_seek_fn(png_structp png_ptr, png_uint_32 offset) {
SkStreamRewindable* sk_stream = (SkStreamRewindable*) png_get_io_ptr(png_ptr);
if (!sk_stream->rewind()) {
png_error(png_ptr, "Failed to rewind stream!");
}
(void)sk_stream->skip(offset);
}
#endif
#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) {
SkImageDecoder::Peeker* peeker =
@ -256,9 +242,6 @@ bool SkPNGImageDecoder::onDecodeInit(SkStream* sk_stream, png_structp *png_ptrp,
* png_init_io() here you would call:
*/
png_set_read_fn(png_ptr, (void *)sk_stream, sk_read_fn);
#ifdef SK_PNG_INDEX_SUPPORTED
png_set_seek_fn(png_ptr, sk_seek_fn);
#endif
/* where user_io_ptr is a structure you want available to the callbacks */
/* If we have already read some of the signature */
// png_set_sig_bytes(png_ptr, 0 /* sig_read */ );
@ -720,267 +703,6 @@ bool SkPNGImageDecoder::decodePalette(png_structp png_ptr, png_infop info_ptr,
return true;
}
#ifdef SK_PNG_INDEX_SUPPORTED
bool SkPNGImageDecoder::onBuildTileIndex(SkStreamRewindable* sk_stream, int *width, int *height) {
SkAutoTDelete<SkStreamRewindable> streamDeleter(sk_stream);
png_structp png_ptr;
png_infop info_ptr;
if (!onDecodeInit(sk_stream, &png_ptr, &info_ptr)) {
return false;
}
if (setjmp(png_jmpbuf(png_ptr)) != 0) {
png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
return false;
}
png_uint_32 origWidth, origHeight;
int bitDepth, colorType;
png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
&colorType, int_p_NULL, int_p_NULL, int_p_NULL);
*width = origWidth;
*height = origHeight;
png_build_index(png_ptr);
if (fImageIndex) {
delete fImageIndex;
}
fImageIndex = new SkPNGImageIndex(streamDeleter.detach(), png_ptr, info_ptr);
return true;
}
bool SkPNGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {
if (nullptr == fImageIndex) {
return false;
}
png_structp png_ptr = fImageIndex->fPng_ptr;
png_infop info_ptr = fImageIndex->fInfo_ptr;
if (setjmp(png_jmpbuf(png_ptr))) {
return false;
}
png_uint_32 origWidth, origHeight;
int bitDepth, pngColorType, interlaceType;
png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
&pngColorType, &interlaceType, int_p_NULL, int_p_NULL);
SkIRect rect = SkIRect::MakeWH(origWidth, origHeight);
if (!rect.intersect(region)) {
// If the requested region is entirely outside the image, just
// returns false
return false;
}
SkColorType colorType;
bool hasAlpha = false;
SkPMColor theTranspColor = 0; // 0 tells us not to try to match
if (!this->getBitmapColorType(png_ptr, info_ptr, &colorType, &hasAlpha, &theTranspColor)) {
return false;
}
const int sampleSize = this->getSampleSize();
SkScaledBitmapSampler sampler(origWidth, rect.height(), sampleSize);
SkBitmap decodedBitmap;
decodedBitmap.setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(),
colorType, kPremul_SkAlphaType));
// from here down we are concerned with colortables and pixels
// we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype
// to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we
// draw lots faster if we can flag the bitmap has being opaque
bool reallyHasAlpha = false;
SkColorTable* colorTable = nullptr;
if (pngColorType == PNG_COLOR_TYPE_PALETTE) {
decodePalette(png_ptr, info_ptr, bitDepth, &hasAlpha, &reallyHasAlpha, &colorTable);
}
SkAutoUnref aur(colorTable);
// Check ahead of time if the swap(dest, src) is possible.
// If yes, then we will stick to AllocPixelRef since it's cheaper with the swap happening.
// If no, then we will use alloc to allocate pixels to prevent garbage collection.
int w = rect.width() / sampleSize;
int h = rect.height() / sampleSize;
const bool swapOnly = (rect == region) && (w == decodedBitmap.width()) &&
(h == decodedBitmap.height()) && bm->isNull();
const bool needColorTable = kIndex_8_SkColorType == colorType;
if (swapOnly) {
if (!this->allocPixelRef(&decodedBitmap, needColorTable ? colorTable : nullptr)) {
return false;
}
} else {
if (!decodedBitmap.tryAllocPixels(nullptr, needColorTable ? colorTable : nullptr)) {
return false;
}
}
SkAutoLockPixels alp(decodedBitmap);
/* Turn on interlace handling. REQUIRED if you are not using
* png_read_image(). To see how to handle interlacing passes,
* see the png_read_row() method below:
*/
const int number_passes = (interlaceType != PNG_INTERLACE_NONE) ?
png_set_interlace_handling(png_ptr) : 1;
/* Optional call to gamma correct and add the background to the palette
* and update info structure. REQUIRED if you are expecting libpng to
* update the palette for you (ie you selected such a transform above).
*/
// Direct access to png_ptr fields is deprecated in libpng > 1.2.
#if defined(PNG_1_0_X) || defined (PNG_1_2_X)
png_ptr->pass = 0;
#else
// FIXME: This sets pass as desired, but also sets iwidth. Is that ok?
png_set_interlaced_pass(png_ptr, 0);
#endif
png_read_update_info(png_ptr, info_ptr);
int actualTop = rect.fTop;
if ((kAlpha_8_SkColorType == colorType || kIndex_8_SkColorType == colorType)
&& 1 == sampleSize) {
if (kAlpha_8_SkColorType == colorType) {
// For an A8 bitmap, we assume there is an alpha for speed. It is
// possible the bitmap is opaque, but that is an unlikely use case
// since it would not be very interesting.
reallyHasAlpha = true;
// A8 is only allowed if the original was GRAY.
SkASSERT(PNG_COLOR_TYPE_GRAY == pngColorType);
}
for (int i = 0; i < number_passes; i++) {
png_configure_decoder(png_ptr, &actualTop, i);
for (int j = 0; j < rect.fTop - actualTop; j++) {
uint8_t* bmRow = decodedBitmap.getAddr8(0, 0);
png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
}
png_uint_32 bitmapHeight = (png_uint_32) decodedBitmap.height();
for (png_uint_32 y = 0; y < bitmapHeight; y++) {
uint8_t* bmRow = decodedBitmap.getAddr8(0, y);
png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
}
}
} else {
SkScaledBitmapSampler::SrcConfig sc;
int srcBytesPerPixel = 4;
if (colorTable != nullptr) {
sc = SkScaledBitmapSampler::kIndex;
srcBytesPerPixel = 1;
} else if (kAlpha_8_SkColorType == colorType) {
// A8 is only allowed if the original was GRAY.
SkASSERT(PNG_COLOR_TYPE_GRAY == pngColorType);
sc = SkScaledBitmapSampler::kGray;
srcBytesPerPixel = 1;
} else if (hasAlpha) {
sc = SkScaledBitmapSampler::kRGBA;
} else {
sc = SkScaledBitmapSampler::kRGBX;
}
/* We have to pass the colortable explicitly, since we may have one
even if our decodedBitmap doesn't, due to the request that we
upscale png's palette to a direct model
*/
const SkPMColor* colors = colorTable ? colorTable->readColors() : nullptr;
if (!sampler.begin(&decodedBitmap, sc, *this, colors)) {
return false;
}
const int height = decodedBitmap.height();
if (number_passes > 1) {
SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);
uint8_t* base = (uint8_t*)storage.get();
size_t rb = origWidth * srcBytesPerPixel;
for (int i = 0; i < number_passes; i++) {
png_configure_decoder(png_ptr, &actualTop, i);
for (int j = 0; j < rect.fTop - actualTop; j++) {
png_read_rows(png_ptr, &base, png_bytepp_NULL, 1);
}
uint8_t* row = base;
for (int32_t y = 0; y < rect.height(); y++) {
uint8_t* bmRow = row;
png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
row += rb;
}
}
// now sample it
base += sampler.srcY0() * rb;
for (int y = 0; y < height; y++) {
reallyHasAlpha |= sampler.next(base);
base += sampler.srcDY() * rb;
}
} else {
SkAutoMalloc storage(origWidth * srcBytesPerPixel);
uint8_t* srcRow = (uint8_t*)storage.get();
png_configure_decoder(png_ptr, &actualTop, 0);
skip_src_rows(png_ptr, srcRow, sampler.srcY0());
for (int i = 0; i < rect.fTop - actualTop; i++) {
png_read_rows(png_ptr, &srcRow, png_bytepp_NULL, 1);
}
for (int y = 0; y < height; y++) {
uint8_t* tmp = srcRow;
png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);
reallyHasAlpha |= sampler.next(srcRow);
if (y < height - 1) {
skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);
}
}
}
}
if (0 != theTranspColor) {
reallyHasAlpha |= substituteTranspColor(&decodedBitmap, theTranspColor);
}
if (reallyHasAlpha && this->getRequireUnpremultipliedColors()) {
switch (decodedBitmap.colorType()) {
case kIndex_8_SkColorType:
// Fall through.
case kARGB_4444_SkColorType:
// We have chosen not to support unpremul for these colortypess.
return false;
default: {
// Fall through to finish the decode. This config either
// supports unpremul or it is irrelevant because it has no
// alpha (or only alpha).
// These brackets prevent a warning.
}
}
}
SkAlphaType alphaType = kOpaque_SkAlphaType;
if (reallyHasAlpha) {
if (this->getRequireUnpremultipliedColors()) {
alphaType = kUnpremul_SkAlphaType;
} else {
alphaType = kPremul_SkAlphaType;
}
}
decodedBitmap.setAlphaType(alphaType);
if (swapOnly) {
bm->swap(decodedBitmap);
return true;
}
return this->cropBitmap(bm, &decodedBitmap, sampleSize, region.x(), region.y(),
region.width(), region.height(), 0, rect.y());
}
#endif
///////////////////////////////////////////////////////////////////////////////
#include "SkColorPriv.h"

View File

@ -103,8 +103,6 @@ public:
}
protected:
bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) override;
bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) override;
Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override;
private:
@ -160,11 +158,6 @@ static void print_webp_error(const SkBitmap& bm, const char msg[]) {
SkDEBUGF(("libwebp error %s [%d %d]", msg, bm.width(), bm.height()));
}
static bool return_false(const SkBitmap& bm, const char msg[]) {
print_webp_error(bm, msg);
return false; // must always return false
}
static SkImageDecoder::Result return_failure(const SkBitmap& bm, const char msg[]) {
print_webp_error(bm, msg);
return SkImageDecoder::kFailure; // must always return kFailure
@ -265,24 +258,6 @@ static bool webp_get_config_resize(WebPDecoderConfig* config,
return true;
}
static bool webp_get_config_resize_crop(WebPDecoderConfig* config,
SkBitmap* decodedBitmap,
const SkIRect& region, bool premultiply) {
if (!webp_get_config_resize(config, decodedBitmap, region.width(),
region.height(), premultiply)) {
return false;
}
config->options.use_cropping = 1;
config->options.crop_left = region.fLeft;
config->options.crop_top = region.fTop;
config->options.crop_width = region.width();
config->options.crop_height = region.height();
return true;
}
bool SkWEBPImageDecoder::setDecodeConfig(SkBitmap* decodedBitmap, int width, int height) {
SkColorType colorType = this->getPrefColorType(k32Bit_SrcDepth, SkToBool(fHasAlpha));
@ -308,99 +283,6 @@ bool SkWEBPImageDecoder::setDecodeConfig(SkBitmap* decodedBitmap, int width, int
return decodedBitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType));
}
bool SkWEBPImageDecoder::onBuildTileIndex(SkStreamRewindable* stream,
int *width, int *height) {
SkAutoTDelete<SkStreamRewindable> streamDeleter(stream);
int origWidth, origHeight, hasAlpha;
if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) {
return false;
}
if (!stream->rewind()) {
SkDebugf("Failed to rewind webp stream!");
return false;
}
*width = origWidth;
*height = origHeight;
this->fInputStream.reset(streamDeleter.detach());
this->fOrigWidth = origWidth;
this->fOrigHeight = origHeight;
this->fHasAlpha = hasAlpha;
return true;
}
static bool is_config_compatible(const SkBitmap& bitmap) {
const SkColorType ct = bitmap.colorType();
return ct == kARGB_4444_SkColorType || ct == kRGB_565_SkColorType || ct == kN32_SkColorType;
}
bool SkWEBPImageDecoder::onDecodeSubset(SkBitmap* decodedBitmap,
const SkIRect& region) {
SkIRect rect = SkIRect::MakeWH(fOrigWidth, fOrigHeight);
if (!rect.intersect(region)) {
// If the requested region is entirely outsides the image, return false
return false;
}
const int sampleSize = this->getSampleSize();
SkScaledBitmapSampler sampler(rect.width(), rect.height(), sampleSize);
const int width = sampler.scaledWidth();
const int height = sampler.scaledHeight();
// The image can be decoded directly to decodedBitmap if
// 1. the region is within the image range
// 2. bitmap's config is compatible
// 3. bitmap's size is same as the required region (after sampled)
bool directDecode = (rect == region) &&
(decodedBitmap->isNull() ||
(is_config_compatible(*decodedBitmap) &&
(decodedBitmap->width() == width) &&
(decodedBitmap->height() == height)));
SkBitmap tmpBitmap;
SkBitmap *bitmap = decodedBitmap;
if (!directDecode) {
bitmap = &tmpBitmap;
}
if (bitmap->isNull()) {
if (!setDecodeConfig(bitmap, width, height)) {
return false;
}
// alloc from native heap if it is a temp bitmap. (prevent GC)
bool allocResult = (bitmap == decodedBitmap)
? allocPixelRef(bitmap, nullptr)
: bitmap->tryAllocPixels();
if (!allocResult) {
return return_false(*decodedBitmap, "allocPixelRef");
}
}
SkAutoLockPixels alp(*bitmap);
WebPDecoderConfig config;
if (!webp_get_config_resize_crop(&config, bitmap, rect,
this->shouldPremultiply())) {
return false;
}
// Decode the WebP image data stream using WebP incremental decoding for
// the specified cropped image-region.
if (!webp_idecode(this->fInputStream, &config)) {
return false;
}
if (!directDecode) {
return cropBitmap(decodedBitmap, bitmap, sampleSize, region.x(), region.y(),
region.width(), region.height(), rect.x(), rect.y());
}
return true;
}
SkImageDecoder::Result SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
Mode mode) {
#ifdef TIME_DECODE

View File

@ -43,21 +43,6 @@ bool SkImageDecoder::DecodeMemory(const void*, size_t, SkBitmap*, SkColorType, M
return false;
}
bool SkImageDecoder::buildTileIndex(SkStreamRewindable*, int *width, int *height) {
return false;
}
bool SkImageDecoder::onBuildTileIndex(SkStreamRewindable* stream,
int* /*width*/, int* /*height*/) {
delete stream;
return false;
}
bool SkImageDecoder::decodeSubset(SkBitmap*, const SkIRect&, SkColorType) {
return false;
}
SkImageDecoder::Format SkImageDecoder::getFormat() const {
return kUnknown_Format;
}
@ -80,11 +65,6 @@ SkBitmap::Allocator* SkImageDecoder::setAllocator(SkBitmap::Allocator*) {
void SkImageDecoder::setSampleSize(int) {}
bool SkImageDecoder::cropBitmap(SkBitmap*, SkBitmap*, int, int, int, int, int,
int, int) {
return false;
}
bool SkImageDecoder::allocPixelRef(SkBitmap*, SkColorTable*) const {
return false;
}