Implemented onGetScanlines and onSkipScanlines for interlaced pngs

Modified DM tests to be faster when decoding interlaced pngs

BUG=skia:

Review URL: https://codereview.chromium.org/1194703002
This commit is contained in:
emmaleer 2015-06-22 10:40:21 -07:00 committed by Commit bot
parent 059ac00446
commit 0a4c3cbfd7
3 changed files with 136 additions and 30 deletions

View File

@ -140,7 +140,7 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
}
switch (fMode) {
case kNormal_Mode:
case kNormal_Mode: {
switch (codec->getPixels(decodeInfo, bitmap.getPixels(), bitmap.rowBytes(), NULL,
colorPtr, colorCountPtr)) {
case SkImageGenerator::kSuccess:
@ -156,23 +156,22 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
}
canvas->drawBitmap(bitmap, 0, 0);
break;
}
case kScanline_Mode: {
SkScanlineDecoder* scanlineDecoder = codec->getScanlineDecoder(decodeInfo, NULL,
colorPtr, colorCountPtr);
if (NULL == scanlineDecoder) {
return Error::Nonfatal("Cannot use scanline decoder for all images");
}
for (int y = 0; y < decodeInfo.height(); ++y) {
const SkImageGenerator::Result result = scanlineDecoder->getScanlines(
bitmap.getAddr(0, y), 1, 0);
switch (result) {
case SkImageGenerator::kSuccess:
case SkImageGenerator::kIncompleteInput:
break;
default:
return SkStringPrintf("%s failed after %d scanlines with error message %d",
fPath.c_str(), y-1, (int) result);
}
const SkImageGenerator::Result result = scanlineDecoder->getScanlines(
bitmap.getAddr(0, 0), decodeInfo.height(), bitmap.rowBytes());
switch (result) {
case SkImageGenerator::kSuccess:
case SkImageGenerator::kIncompleteInput:
break;
default:
return SkStringPrintf("%s failed with error message %d",
fPath.c_str(), (int) result);
}
canvas->drawBitmap(bitmap, 0, 0);
break;
@ -207,8 +206,9 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
return SkStringPrintf("Image(%s) is too large (%d x %d)\n", fPath.c_str(),
largestSubsetDecodeInfo.width(), largestSubsetDecodeInfo.height());
}
char* line = SkNEW_ARRAY(char, decodeInfo.minRowBytes());
SkAutoTDeleteArray<char> lineDeleter(line);
const size_t rowBytes = decodeInfo.minRowBytes();
char* buffer = SkNEW_ARRAY(char, largestSubsetDecodeInfo.height() * rowBytes);
SkAutoTDeleteArray<char> lineDeleter(buffer);
for (int col = 0; col < divisor; col++) {
//currentSubsetWidth may be larger than subsetWidth for rightmost subsets
const int currentSubsetWidth = (col + 1 == divisor) ?
@ -247,21 +247,33 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
bounds.setXYWH(0, 0, currentSubsetWidth, currentSubsetHeight);
SkAssertResult(largestSubsetBm.extractSubset(&subsetBm, bounds));
SkAutoLockPixels autlockSubsetBm(subsetBm, true);
for (int subsetY = 0; subsetY < currentSubsetHeight; ++subsetY) {
const SkImageGenerator::Result subsetResult =
subsetScanlineDecoder->getScanlines(line, 1, 0);
const size_t bpp = decodeInfo.bytesPerPixel();
//copy section of line based on x value
memcpy(subsetBm.getAddr(0, subsetY), line + x*bpp, currentSubsetWidth*bpp);
switch (subsetResult) {
case SkImageGenerator::kSuccess:
case SkImageGenerator::kIncompleteInput:
break;
default:
return SkStringPrintf("%s failed after %d scanlines with error"
"message %d", fPath.c_str(), y-1, (int) subsetResult);
}
const SkImageGenerator::Result subsetResult =
subsetScanlineDecoder->getScanlines(buffer, currentSubsetHeight, rowBytes);
switch (subsetResult) {
case SkImageGenerator::kSuccess:
case SkImageGenerator::kIncompleteInput:
break;
default:
return SkStringPrintf("%s failed with error message %d",
fPath.c_str(), (int) subsetResult);
}
const size_t bpp = decodeInfo.bytesPerPixel();
/*
* we copy all the lines at once becuase when calling getScanlines for
* interlaced pngs the entire image must be read regardless of the number
* of lines requested. Reading an interlaced png in a loop, line-by-line, would
* decode the entire image height times, which is very slow
* it is aknowledged that copying each line as you read it in a loop
* may be faster for other types of images. Since this is a correctness test
* that's okay.
*/
char* bufferRow = buffer;
for (int subsetY = 0; subsetY < currentSubsetHeight; ++subsetY) {
memcpy(subsetBm.getAddr(0, subsetY), bufferRow + x*bpp,
currentSubsetWidth*bpp);
bufferRow += rowBytes;
}
canvas->drawBitmap(subsetBm, SkIntToScalar(x), SkIntToScalar(y));
}
}
@ -341,6 +353,7 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
}
}
canvas->drawBitmap(bitmap, 0, 0);
break;
}
}
return "";

View File

@ -648,6 +648,98 @@ private:
typedef SkScanlineDecoder INHERITED;
};
class SkPngInterlacedScanlineDecoder : public SkScanlineDecoder {
public:
SkPngInterlacedScanlineDecoder(const SkImageInfo& dstInfo, SkPngCodec* codec)
: INHERITED(dstInfo)
, fCodec(codec)
, fHasAlpha(false)
, fCurrentRow(0)
, fHeight(dstInfo.height())
, fSrcRowBytes(dstInfo.minRowBytes())
, fRewindNeeded(false)
{
fGarbageRow.reset(fSrcRowBytes);
fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get());
}
SkImageGenerator::Result onGetScanlines(void* dst, int count, size_t dstRowBytes) override {
//rewind stream if have previously called onGetScanlines,
//since we need entire progressive image to get scanlines
if (fRewindNeeded) {
if(false == fCodec->handleRewind()) {
return SkImageGenerator::kCouldNotRewind;
}
} else {
fRewindNeeded = true;
}
if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
SkCodecPrintf("setjmp long jump!\n");
return SkImageGenerator::kInvalidInput;
}
const int number_passes = png_set_interlace_handling(fCodec->fPng_ptr);
SkAutoMalloc storage(count * fSrcRowBytes);
uint8_t* storagePtr = static_cast<uint8_t*>(storage.get());
uint8_t* srcRow;
for (int i = 0; i < number_passes; i++) {
//read rows we planned to skip into garbage row
for (int y = 0; y < fCurrentRow; y++){
png_read_rows(fCodec->fPng_ptr, &fGarbageRowPtr, png_bytepp_NULL, 1);
}
//read rows we care about into buffer
srcRow = storagePtr;
for (int y = 0; y < count; y++) {
png_read_rows(fCodec->fPng_ptr, &srcRow, png_bytepp_NULL, 1);
srcRow += fSrcRowBytes;
}
//read rows we don't want into garbage buffer
for (int y = 0; y < fHeight - fCurrentRow - count; y++) {
png_read_rows(fCodec->fPng_ptr, &fGarbageRowPtr, png_bytepp_NULL, 1);
}
}
//swizzle the rows we care about
srcRow = storagePtr;
for (int y = 0; y < count; y++) {
fCodec->fSwizzler->setDstRow(dst);
fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->next(srcRow));
dst = SkTAddOffset<void>(dst, dstRowBytes);
srcRow += fSrcRowBytes;
}
fCurrentRow += count;
return SkImageGenerator::kSuccess;
}
SkImageGenerator::Result onSkipScanlines(int count) override {
//when ongetScanlines is called it will skip to fCurrentRow
fCurrentRow += count;
return SkImageGenerator::kSuccess;
}
void onFinish() override {
fCodec->finish();
}
bool onReallyHasAlpha() const override { return fHasAlpha; }
private:
SkPngCodec* fCodec; // Unowned.
bool fHasAlpha;
int fCurrentRow;
int fHeight;
size_t fSrcRowBytes;
bool fRewindNeeded;
SkAutoMalloc fGarbageRow;
uint8_t* fGarbageRowPtr;
typedef SkScanlineDecoder INHERITED;
};
SkScanlineDecoder* SkPngCodec::onGetScanlineDecoder(const SkImageInfo& dstInfo,
const Options& options, SkPMColor ctable[], int* ctableCount) {
if (!this->handleRewind()) {
@ -675,8 +767,8 @@ SkScanlineDecoder* SkPngCodec::onGetScanlineDecoder(const SkImageInfo& dstInfo,
SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES);
if (fNumberPasses > 1) {
// We cannot efficiently do scanline decoding.
return NULL;
// interlaced image
return SkNEW_ARGS(SkPngInterlacedScanlineDecoder, (dstInfo, this));
}
return SkNEW_ARGS(SkPngScanlineDecoder, (dstInfo, this));

View File

@ -58,6 +58,7 @@ private:
void destroyReadStruct();
friend class SkPngScanlineDecoder;
friend class SkPngInterlacedScanlineDecoder;
typedef SkCodec INHERITED;
};