skia2/include/codec/SkScanlineDecoder.h
scroggo 9b2cdbf481 Allow creating multiple scanline decoders.
Make getScanlineDecoder return a new object each time, which is
owned by the caller, and independent from any existing scanline
decoders and the SkCodec itself.

Since the SkCodec already contains the entire state machine, and it
is used by the scanline decoders, simply create a new SkCodec which
is now owned by the scanline decoder.

Move code that cleans up after using a scanline decoder into its
destructor

One side effect is that creating the first scanline decoder requires
a duplication of the stream and re-reading the header. (With some
more complexity/changes, we could pass the state machine to the
scanline decoder and make the SkCodec recreate its own state machine
instead.) The typical client of the scanline decoder (region decoder)
uses an SkMemoryStream, so the duplication is cheap, although we
should consider the extra time to reread the header/recreate the state
machine. (If/when we use the scanline decoder for other purposes,
where the stream may not be cheaply duplicated, we should consider
passing the state machine.)

One (intended) result of this change is that a client can create a
new scanline decoder in a new thread, and decode different pieces of
the image simultaneously.

In SkPngCodec::decodePalette, use fBitDepth rather than a parameter.

Review URL: https://codereview.chromium.org/1230033004
2015-07-10 12:07:02 -07:00

107 lines
3.8 KiB
C++

/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkScanlineDecoder_DEFINED
#define SkScanlineDecoder_DEFINED
#include "SkTypes.h"
#include "SkCodec.h"
#include "SkTemplates.h"
#include "SkImageInfo.h"
class SkScanlineDecoder : public SkNoncopyable {
public:
/**
* Clean up after reading/skipping scanlines.
*
* It is possible that not all scanlines will have been read/skipped. In
* fact, in the case of subset decodes, it is likely that there will be
* scanlines at the bottom of the image that have been ignored.
*/
virtual ~SkScanlineDecoder() {}
/**
* Write the next countLines scanlines into dst.
*
* @param dst Must be non-null, and large enough to hold countLines
* scanlines of size rowBytes.
* @param countLines Number of lines to write.
* @param rowBytes Number of bytes per row. Must be large enough to hold
* a scanline based on the SkImageInfo used to create this object.
*/
SkCodec::Result getScanlines(void* dst, int countLines, size_t rowBytes) {
if ((rowBytes < fDstInfo.minRowBytes() && countLines > 1 ) || countLines <= 0
|| fCurrScanline + countLines > fDstInfo.height()) {
return SkCodec::kInvalidParameters;
}
const SkCodec::Result result = this->onGetScanlines(dst, countLines, rowBytes);
fCurrScanline += countLines;
return result;
}
/**
* Skip count scanlines.
*
* The default version just calls onGetScanlines and discards the dst.
* NOTE: If skipped lines are the only lines with alpha, this default
* will make reallyHasAlpha return true, when it could have returned
* false.
*/
SkCodec::Result skipScanlines(int countLines) {
if (fCurrScanline + countLines > fDstInfo.height()) {
// Arguably, we could just skip the scanlines which are remaining,
// and return kSuccess. We choose to return invalid so the client
// can catch their bug.
return SkCodec::kInvalidParameters;
}
const SkCodec::Result result = this->onSkipScanlines(countLines);
fCurrScanline += countLines;
return result;
}
/**
* Some images may initially report that they have alpha due to the format
* of the encoded data, but then never use any colors which have alpha
* less than 100%. This function can be called *after* decoding to
* determine if such an image truly had alpha. Calling it before decoding
* is undefined.
* FIXME: see skbug.com/3582.
*/
bool reallyHasAlpha() const {
return this->onReallyHasAlpha();
}
protected:
SkScanlineDecoder(const SkImageInfo& requested)
: fDstInfo(requested)
, fCurrScanline(0) {}
virtual bool onReallyHasAlpha() const { return false; }
const SkImageInfo& dstInfo() const { return fDstInfo; }
private:
const SkImageInfo fDstInfo;
int fCurrScanline;
// Naive default version just calls onGetScanlines on temp memory.
virtual SkCodec::Result onSkipScanlines(int countLines) {
SkAutoMalloc storage(fDstInfo.minRowBytes());
// Note that we pass 0 to rowBytes so we continue to use the same memory.
// Also note that while getScanlines checks that rowBytes is big enough,
// onGetScanlines bypasses that check.
// Calling the virtual method also means we do not double count
// countLines.
return this->onGetScanlines(storage.get(), countLines, 0);
}
virtual SkCodec::Result onGetScanlines(void* dst, int countLines,
size_t rowBytes) = 0;
};
#endif // SkScanlineDecoder_DEFINED