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:
parent
059ac00446
commit
0a4c3cbfd7
@ -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 "";
|
||||
|
@ -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));
|
||||
|
@ -58,6 +58,7 @@ private:
|
||||
void destroyReadStruct();
|
||||
|
||||
friend class SkPngScanlineDecoder;
|
||||
friend class SkPngInterlacedScanlineDecoder;
|
||||
|
||||
typedef SkCodec INHERITED;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user