Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(936)

Unified Diff: src/codec/SkCodec_libpng.cpp

Issue 1365313002: Merge SkCodec with SkScanlineDecoder (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Skip ICO in SkScaledCodec for now Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/codec/SkCodec_libpng.h ('k') | src/codec/SkCodec_wbmp.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/codec/SkCodec_libpng.cpp
diff --git a/src/codec/SkCodec_libpng.cpp b/src/codec/SkCodec_libpng.cpp
index 2ba2d5bb9f2bed42209d9c2ca5b0ce4ce127bba0..faba79f6a2faffcc9fe87c50862df599be940594 100644
--- a/src/codec/SkCodec_libpng.cpp
+++ b/src/codec/SkCodec_libpng.cpp
@@ -12,7 +12,6 @@
#include "SkBitmap.h"
#include "SkMath.h"
#include "SkScaledCodec.h"
-#include "SkScanlineDecoder.h"
#include "SkSize.h"
#include "SkStream.h"
#include "SkSwizzler.h"
@@ -163,7 +162,10 @@ bool SkPngCodec::decodePalette(bool premultiply, int* ctableCount) {
palette++;
}
- fReallyHasAlpha = transLessThanFF < 0;
+ if (transLessThanFF >= 0) {
+ // No transparent colors were found.
+ fAlphaState = kOpaque_AlphaState;
+ }
for (; index < numPalette; index++) {
*colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue);
@@ -216,7 +218,8 @@ bool SkPngCodec::IsPng(SkStream* stream) {
// png_destroy_read_struct. If it returns false, the passed in fields (except
// stream) are unchanged.
static bool read_header(SkStream* stream, png_structp* png_ptrp,
- png_infop* info_ptrp, SkImageInfo* imageInfo, int* bitDepthPtr) {
+ png_infop* info_ptrp, SkImageInfo* imageInfo,
+ int* bitDepthPtr, int* numberPassesPtr) {
// The image is known to be a PNG. Decode enough to know the SkImageInfo.
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr,
sk_error_fn, sk_warning_fn);
@@ -333,6 +336,11 @@ static bool read_header(SkStream* stream, png_structp* png_ptrp,
SkASSERT(false);
}
+ int numberPasses = png_set_interlace_handling(png_ptr);
+ if (numberPassesPtr) {
+ *numberPassesPtr = numberPasses;
+ }
+
// FIXME: Also need to check for sRGB (skbug.com/3471).
if (imageInfo) {
@@ -349,29 +357,21 @@ static bool read_header(SkStream* stream, png_structp* png_ptrp,
return true;
}
-SkCodec* SkPngCodec::NewFromStream(SkStream* stream) {
- SkAutoTDelete<SkStream> streamDeleter(stream);
- png_structp png_ptr;
- png_infop info_ptr;
- SkImageInfo imageInfo;
- int bitDepth;
- if (read_header(stream, &png_ptr, &info_ptr, &imageInfo, &bitDepth)) {
- return new SkPngCodec(imageInfo, streamDeleter.detach(), png_ptr, info_ptr, bitDepth);
- }
- return nullptr;
-}
-
-#define INVALID_NUMBER_PASSES -1
SkPngCodec::SkPngCodec(const SkImageInfo& info, SkStream* stream,
- png_structp png_ptr, png_infop info_ptr, int bitDepth)
+ png_structp png_ptr, png_infop info_ptr, int bitDepth, int numberPasses)
: INHERITED(info, stream)
, fPng_ptr(png_ptr)
, fInfo_ptr(info_ptr)
, fSrcConfig(SkSwizzler::kUnknown)
- , fNumberPasses(INVALID_NUMBER_PASSES)
- , fReallyHasAlpha(false)
+ , fNumberPasses(numberPasses)
, fBitDepth(bitDepth)
-{}
+{
+ if (info.alphaType() == kOpaque_SkAlphaType) {
+ fAlphaState = kOpaque_AlphaState;
+ } else {
+ fAlphaState = kUnknown_AlphaState;
+ }
+}
SkPngCodec::~SkPngCodec() {
this->destroyReadStruct();
@@ -401,13 +401,9 @@ SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo,
SkCodecPrintf("setjmp long jump!\n");
return kInvalidInput;
}
- fNumberPasses = png_set_interlace_handling(fPng_ptr);
png_read_update_info(fPng_ptr, fInfo_ptr);
- // Set to the default before calling decodePalette, which may change it.
- fReallyHasAlpha = false;
-
- //srcColorType was determined in readHeader() which determined png color type
+ //srcColorType was determined in read_header() which determined png color type
const SkColorType srcColorType = this->getInfo().colorType();
switch (srcColorType) {
@@ -459,7 +455,7 @@ bool SkPngCodec::onRewind() {
png_structp png_ptr;
png_infop info_ptr;
- if (!read_header(this->stream(), &png_ptr, &info_ptr, nullptr, nullptr)) {
+ if (!read_header(this->stream(), &png_ptr, &info_ptr, nullptr, nullptr, nullptr)) {
return false;
}
@@ -498,7 +494,8 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
return kInvalidInput;
}
- SkASSERT(fNumberPasses != INVALID_NUMBER_PASSES);
+ bool hasAlpha = false;
+ // FIXME: We could split these out based on subclass.
SkAutoMalloc storage;
void* dstRow = dst;
if (fNumberPasses > 1) {
@@ -522,7 +519,7 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
// Now swizzle it.
uint8_t* srcRow = base;
for (int y = 0; y < height; y++) {
- fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow));
+ hasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow));
dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
srcRow += srcRowBytes;
}
@@ -531,11 +528,18 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
uint8_t* srcRow = static_cast<uint8_t*>(storage.get());
for (int y = 0; y < requestedInfo.height(); y++) {
png_read_rows(fPng_ptr, &srcRow, png_bytepp_NULL, 1);
- fReallyHasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow));
+ // FIXME: Only call IsOpaque once, outside the loop. Same for onGetScanlines.
+ hasAlpha |= !SkSwizzler::IsOpaque(fSwizzler->swizzle(dstRow, srcRow));
dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
}
}
+ if (hasAlpha) {
+ fAlphaState = kHasAlpha_AlphaState;
+ } else {
+ fAlphaState = kOpaque_AlphaState;
+ }
+
// FIXME: do we need substituteTranspColor? Note that we cannot do it for
// scanline decoding, but we could do it here. Alternatively, we could do
// it as we go, instead of in post-processing like SkPNGImageDecoder.
@@ -547,136 +551,170 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void*
// read rest of file, and get additional comment and time chunks in info_ptr
png_read_end(fPng_ptr, fInfo_ptr);
+
return kSuccess;
}
-class SkPngScanlineDecoder : public SkScanlineDecoder {
+bool SkPngCodec::onReallyHasAlpha() const {
+ switch (fAlphaState) {
+ case kOpaque_AlphaState:
+ return false;
+ case kUnknown_AlphaState:
+ // Maybe the subclass knows?
+ return this->alphaInScanlineDecode() == kHasAlpha_AlphaState;
+ case kHasAlpha_AlphaState:
+ switch (this->alphaInScanlineDecode()) {
+ case kUnknown_AlphaState:
+ // Scanline decoder must not have been used. Return our knowledge.
+ return true;
+ case kOpaque_AlphaState:
+ // Scanline decoder was used, and did not find alpha in its subset.
+ return false;
+ case kHasAlpha_AlphaState:
+ return true;
+ }
+ }
+
+ // All valid AlphaStates have been covered, so this should not be reached.
+ SkASSERT(false);
+ return true;
+}
+
+// Subclass of SkPngCodec which supports scanline decoding
+class SkPngScanlineDecoder : public SkPngCodec {
public:
- SkPngScanlineDecoder(const SkImageInfo& srcInfo, SkPngCodec* codec)
- : INHERITED(srcInfo)
- , fCodec(codec)
- , fHasAlpha(false)
+ SkPngScanlineDecoder(const SkImageInfo& srcInfo, SkStream* stream,
+ png_structp png_ptr, png_infop info_ptr, int bitDepth)
+ : INHERITED(srcInfo, stream, png_ptr, info_ptr, bitDepth, 1)
+ , fSrcRow(nullptr)
+ , fAlphaState(kUnknown_AlphaState)
{}
- SkCodec::Result onStart(const SkImageInfo& dstInfo,
- const SkCodec::Options& options,
- SkPMColor ctable[], int* ctableCount) override {
- if (!fCodec->rewindIfNeeded()) {
- return SkCodec::kCouldNotRewind;
+ Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options,
+ SkPMColor ctable[], int* ctableCount) override {
+ if (!this->rewindIfNeeded()) {
+ return kCouldNotRewind;
}
if (!conversion_possible(dstInfo, this->getInfo())) {
- return SkCodec::kInvalidConversion;
+ return kInvalidConversion;
}
// Check to see if scaling was requested.
if (dstInfo.dimensions() != this->getInfo().dimensions()) {
if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) {
- return SkCodec::kInvalidScale;
+ return kInvalidScale;
}
}
- const SkCodec::Result result = fCodec->initializeSwizzler(dstInfo, options, ctable,
- ctableCount);
- if (result != SkCodec::kSuccess) {
+ const Result result = this->initializeSwizzler(dstInfo, options, ctable,
+ ctableCount);
+ if (result != kSuccess) {
return result;
}
- fHasAlpha = false;
- fStorage.reset(this->getInfo().width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig));
+ fAlphaState = kUnknown_AlphaState;
+ fStorage.reset(this->getInfo().width() * SkSwizzler::BytesPerPixel(this->srcConfig()));
fSrcRow = static_cast<uint8_t*>(fStorage.get());
- return SkCodec::kSuccess;
+ return kSuccess;
}
- SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) override {
- if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
+ Result onGetScanlines(void* dst, int count, size_t rowBytes) override {
+ if (setjmp(png_jmpbuf(this->png_ptr()))) {
SkCodecPrintf("setjmp long jump!\n");
- return SkCodec::kInvalidInput;
+ return kInvalidInput;
}
void* dstRow = dst;
+ bool hasAlpha = false;
for (int i = 0; i < count; i++) {
- png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1);
- fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->swizzle(dstRow, fSrcRow));
+ png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1);
+ hasAlpha |= !SkSwizzler::IsOpaque(this->swizzler()->swizzle(dstRow, fSrcRow));
dstRow = SkTAddOffset<void>(dstRow, rowBytes);
}
- return SkCodec::kSuccess;
+
+ if (hasAlpha) {
+ fAlphaState = kHasAlpha_AlphaState;
+ } else {
+ if (kUnknown_AlphaState == fAlphaState) {
+ fAlphaState = kOpaque_AlphaState;
+ }
+ // Otherwise, the AlphaState is unchanged.
+ }
+
+ return kSuccess;
}
- SkCodec::Result onSkipScanlines(int count) override {
+ Result onSkipScanlines(int count) override {
// FIXME: Could we use the return value of setjmp to specify the type of
// error?
- if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
+ if (setjmp(png_jmpbuf(this->png_ptr()))) {
SkCodecPrintf("setjmp long jump!\n");
- return SkCodec::kInvalidInput;
+ return kInvalidInput;
}
//there is a potential tradeoff of memory vs speed created by putting this in a loop.
//calling png_read_rows in a loop is insignificantly slower than calling it once with count
//as png_read_rows has it's own loop which calls png_read_row count times.
for (int i = 0; i < count; i++) {
- png_read_rows(fCodec->fPng_ptr, &fSrcRow, png_bytepp_NULL, 1);
+ png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1);
}
return SkCodec::kSuccess;
}
- bool onReallyHasAlpha() const override { return fHasAlpha; }
-
- SkEncodedFormat onGetEncodedFormat() const override {
- return kPNG_SkEncodedFormat;
+ AlphaState alphaInScanlineDecode() const override {
+ return fAlphaState;
}
-
private:
- SkAutoTDelete<SkPngCodec> fCodec;
- bool fHasAlpha;
+ AlphaState fAlphaState;
SkAutoMalloc fStorage;
uint8_t* fSrcRow;
- typedef SkScanlineDecoder INHERITED;
+ typedef SkPngCodec INHERITED;
};
-class SkPngInterlacedScanlineDecoder : public SkScanlineDecoder {
+class SkPngInterlacedScanlineDecoder : public SkPngCodec {
public:
- SkPngInterlacedScanlineDecoder(const SkImageInfo& srcInfo, SkPngCodec* codec)
- : INHERITED(srcInfo)
- , fCodec(codec)
- , fHasAlpha(false)
- , fCurrentRow(0)
- , fHeight(srcInfo.height())
+ SkPngInterlacedScanlineDecoder(const SkImageInfo& srcInfo, SkStream* stream,
+ png_structp png_ptr, png_infop info_ptr, int bitDepth, int numberPasses)
+ : INHERITED(srcInfo, stream, png_ptr, info_ptr, bitDepth, numberPasses)
+ , fAlphaState(kUnknown_AlphaState)
+ , fHeight(-1)
, fCanSkipRewind(false)
- {}
+ {
+ SkASSERT(numberPasses != 1);
+ }
- SkCodec::Result onStart(const SkImageInfo& dstInfo,
- const SkCodec::Options& options,
- SkPMColor ctable[], int* ctableCount) override
+ Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options,
+ SkPMColor ctable[], int* ctableCount) override
{
- if (!fCodec->rewindIfNeeded()) {
- return SkCodec::kCouldNotRewind;
+ if (!this->rewindIfNeeded()) {
+ return kCouldNotRewind;
}
if (!conversion_possible(dstInfo, this->getInfo())) {
- return SkCodec::kInvalidConversion;
+ return kInvalidConversion;
}
// Check to see if scaling was requested.
if (dstInfo.dimensions() != this->getInfo().dimensions()) {
if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) {
- return SkCodec::kInvalidScale;
+ return kInvalidScale;
}
}
- const SkCodec::Result result = fCodec->initializeSwizzler(dstInfo, options, ctable,
- ctableCount);
- if (result != SkCodec::kSuccess) {
+ const Result result = this->initializeSwizzler(dstInfo, options, ctable,
+ ctableCount);
+ if (result != kSuccess) {
return result;
}
- fHasAlpha = false;
- fCurrentRow = 0;
+ fAlphaState = kUnknown_AlphaState;
fHeight = dstInfo.height();
- fSrcRowBytes = this->getInfo().width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig);
+ // FIXME: This need not be called on a second call to onStartScanlineDecode.
+ fSrcRowBytes = this->getInfo().width() * SkSwizzler::BytesPerPixel(this->srcConfig());
fGarbageRow.reset(fSrcRowBytes);
fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get());
fCanSkipRewind = true;
@@ -684,73 +722,90 @@ public:
return SkCodec::kSuccess;
}
- SkCodec::Result onGetScanlines(void* dst, int count, size_t dstRowBytes) override {
+ 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 (fCanSkipRewind) {
- // We already rewound in onStart, so there is no reason to rewind.
+ // We already rewound in onStartScanlineDecode, so there is no reason to rewind.
// Next time onGetScanlines is called, we will need to rewind.
fCanSkipRewind = false;
- } else if (!fCodec->rewindIfNeeded()) {
- return SkCodec::kCouldNotRewind;
+ } else {
+ // rewindIfNeeded resets fCurrScanline, since it assumes that start
+ // needs to be called again before scanline decoding. PNG scanline
+ // decoding is the exception, since it needs to rewind between
+ // calls to getScanlines. Keep track of fCurrScanline, to undo the
+ // reset.
+ const int currScanline = this->onNextScanline();
+ // This method would never be called if currScanline is -1
+ SkASSERT(currScanline != -1);
+
+ if (!this->rewindIfNeeded()) {
+ return kCouldNotRewind;
+ }
+ this->updateNextScanline(currScanline);
}
- if (setjmp(png_jmpbuf(fCodec->fPng_ptr))) {
+ if (setjmp(png_jmpbuf(this->png_ptr()))) {
SkCodecPrintf("setjmp long jump!\n");
- return SkCodec::kInvalidInput;
+ return 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);
+ const int startRow = this->onNextScanline();
+ for (int i = 0; i < this->numberPasses(); i++) {
+ // read rows we planned to skip into garbage row
+ for (int y = 0; y < startRow; y++){
+ png_read_rows(this->png_ptr(), &fGarbageRowPtr, png_bytepp_NULL, 1);
}
- //read rows we care about into buffer
+ // 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);
+ png_read_rows(this->png_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);
+ // read rows we don't want into garbage buffer
+ for (int y = 0; y < fHeight - startRow - count; y++) {
+ png_read_rows(this->png_ptr(), &fGarbageRowPtr, png_bytepp_NULL, 1);
}
}
//swizzle the rows we care about
srcRow = storagePtr;
void* dstRow = dst;
+ bool hasAlpha = false;
for (int y = 0; y < count; y++) {
- fHasAlpha |= !SkSwizzler::IsOpaque(fCodec->fSwizzler->swizzle(dstRow, srcRow));
+ hasAlpha |= !SkSwizzler::IsOpaque(this->swizzler()->swizzle(dstRow, srcRow));
dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
srcRow += fSrcRowBytes;
}
- fCurrentRow += count;
- return SkCodec::kSuccess;
+
+ if (hasAlpha) {
+ fAlphaState = kHasAlpha_AlphaState;
+ } else {
+ if (kUnknown_AlphaState == fAlphaState) {
+ fAlphaState = kOpaque_AlphaState;
+ }
+ // Otherwise, the AlphaState is unchanged.
+ }
+
+ return kSuccess;
}
SkCodec::Result onSkipScanlines(int count) override {
- //when ongetScanlines is called it will skip to fCurrentRow
- fCurrentRow += count;
+ // The non-virtual version will update fCurrScanline.
return SkCodec::kSuccess;
}
- bool onReallyHasAlpha() const override { return fHasAlpha; }
+ AlphaState alphaInScanlineDecode() const override {
+ return fAlphaState;
+ }
SkScanlineOrder onGetScanlineOrder() const override {
return kNone_SkScanlineOrder;
}
- SkEncodedFormat onGetEncodedFormat() const override {
- return kPNG_SkEncodedFormat;
- }
-
private:
- SkAutoTDelete<SkPngCodec> fCodec;
- bool fHasAlpha;
- int fCurrentRow;
+ AlphaState fAlphaState;
int fHeight;
size_t fSrcRowBytes;
SkAutoMalloc fGarbageRow;
@@ -759,30 +814,33 @@ private:
// is called whenever some action is taken that reads the stream and
// therefore the next call will require a rewind. So it modifies a boolean
// to note that the *next* time it is called a rewind is needed.
- // SkPngInterlacedScanlineDecoder has an extra wrinkle - calling onStart
- // followed by onGetScanlines does *not* require a rewind. Since
- // rewindIfNeeded does not have this flexibility, we need to add another
- // layer.
+ // SkPngInterlacedScanlineDecoder has an extra wrinkle - calling
+ // onStartScanlineDecode followed by onGetScanlines does *not* require a
+ // rewind. Since rewindIfNeeded does not have this flexibility, we need to
+ // add another layer.
bool fCanSkipRewind;
- typedef SkScanlineDecoder INHERITED;
+ typedef SkPngCodec INHERITED;
};
-SkScanlineDecoder* SkPngCodec::NewSDFromStream(SkStream* stream) {
- SkAutoTDelete<SkPngCodec> codec (static_cast<SkPngCodec*>(SkPngCodec::NewFromStream(stream)));
- if (!codec) {
+SkCodec* SkPngCodec::NewFromStream(SkStream* stream) {
+ SkAutoTDelete<SkStream> streamDeleter(stream);
+ png_structp png_ptr;
+ png_infop info_ptr;
+ SkImageInfo imageInfo;
+ int bitDepth;
+ int numberPasses;
+
+ if (!read_header(stream, &png_ptr, &info_ptr, &imageInfo, &bitDepth, &numberPasses)) {
return nullptr;
}
- codec->fNumberPasses = png_set_interlace_handling(codec->fPng_ptr);
- SkASSERT(codec->fNumberPasses != INVALID_NUMBER_PASSES);
-
- const SkImageInfo& srcInfo = codec->getInfo();
- if (codec->fNumberPasses > 1) {
- // interlaced image
- return new SkPngInterlacedScanlineDecoder(srcInfo, codec.detach());
+ if (1 == numberPasses) {
+ return new SkPngScanlineDecoder(imageInfo, streamDeleter.detach(), png_ptr, info_ptr,
+ bitDepth);
}
- return new SkPngScanlineDecoder(srcInfo, codec.detach());
+ return new SkPngInterlacedScanlineDecoder(imageInfo, streamDeleter.detach(), png_ptr,
+ info_ptr, bitDepth, numberPasses);
}
« no previous file with comments | « src/codec/SkCodec_libpng.h ('k') | src/codec/SkCodec_wbmp.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698