Scanline decoding for wbmp

We are also changing the wbmp to use SkSwizzler.  This
will allow us to take advantage of the sampling routines
that are being implemented in SkSwizzler.

The image in this upload came from:
https://commons.wikimedia.org/wiki/File:Android_robot.png

It is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license.

BUG=skia:

Review URL: https://codereview.chromium.org/1254483004
This commit is contained in:
msarett 2015-08-05 12:58:26 -07:00 committed by Commit bot
parent dfcba473b0
commit 99f567e617
10 changed files with 353 additions and 88 deletions

BIN
resources/index8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -133,10 +133,5 @@ SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t
}
SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes) {
SkASSERT(kIndex_8_SkColorType != info.colorType());
if (kIndex_8_SkColorType == info.colorType()) {
return kInvalidConversion;
}
return this->getPixels(info, pixels, rowBytes, NULL, NULL, NULL);
}

View File

@ -30,6 +30,13 @@
#define COMPUTE_RESULT_ALPHA \
SkSwizzler::GetResult(zeroAlpha, maxAlpha);
/*
* If there is a color table, get a pointer to the colors, otherwise return NULL
*/
static const SkPMColor* get_color_ptr(SkColorTable* colorTable) {
return NULL != colorTable ? colorTable->readColors() : NULL;
}
/*
*
* Copy the codec color table back to the client when kIndex8 color type is requested

View File

@ -471,7 +471,7 @@ SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo,
copy_color_table(requestedInfo, fColorTable, ctable, ctableCount);
// Create the swizzler. SkPngCodec retains ownership of the color table.
const SkPMColor* colors = fColorTable ? fColorTable->readColors() : NULL;
const SkPMColor* colors = get_color_ptr(fColorTable.get());
fSwizzler.reset(SkSwizzler::CreateSwizzler(fSrcConfig, colors, requestedInfo,
options.fZeroInitialized));
if (!fSwizzler) {

View File

@ -6,10 +6,29 @@
*/
#include "SkCodec.h"
#include "SkCodecPriv.h"
#include "SkColorPriv.h"
#include "SkColorTable.h"
#include "SkStream.h"
#include "SkCodec_wbmp.h"
// Each bit represents a pixel, so width is actually a number of bits.
// A row will always be stored in bytes, so we round width up to the
// nearest multiple of 8 to get the number of bits actually in the row.
// We then divide by 8 to convert to bytes.
static inline size_t get_src_row_bytes(int width) {
return SkAlign8(width) >> 3;
}
static inline void setup_color_table(SkColorType colorType,
SkPMColor* colorPtr, int* colorCount) {
if (kIndex_8_SkColorType == colorType) {
colorPtr[0] = SK_ColorBLACK;
colorPtr[1] = SK_ColorWHITE;
*colorCount = 2;
}
}
// http://en.wikipedia.org/wiki/Variable-length_quantity
static bool read_mbf(SkStream* stream, uint64_t* value) {
uint64_t n = 0;
@ -38,7 +57,7 @@ static bool read_header(SkStream* stream, SkISize* size) {
if (!read_mbf(stream, &width) || width > 0xFFFF || !width) {
return false;
}
if (!read_mbf(stream, &height) || width > 0xFFFF || !height) {
if (!read_mbf(stream, &height) || height > 0xFFFF || !height) {
return false;
}
if (size) {
@ -47,70 +66,59 @@ static bool read_header(SkStream* stream, SkISize* size) {
return true;
}
#define BLACK SkPackARGB32NoCheck(0xFF, 0, 0, 0)
#define WHITE SkPackARGB32NoCheck(0xFF, 0xFF, 0xFF, 0xFF)
#define GRAYSCALE_BLACK 0
#define GRAYSCALE_WHITE 0xFF
#define RGB565_BLACK 0
#define RGB565_WHITE 0xFFFF
static SkPMColor bit_to_pmcolor(U8CPU bit) { return bit ? WHITE : BLACK; }
static uint8_t bit_to_bit(U8CPU bit) { return bit; }
static uint8_t bit_to_grayscale(U8CPU bit) {
return bit ? GRAYSCALE_WHITE : GRAYSCALE_BLACK;
}
static uint16_t bit_to_rgb565(U8CPU bit) {
return bit ? RGB565_WHITE : RGB565_BLACK;
}
typedef void (*ExpandProc)(uint8_t*, const uint8_t*, int);
// TODO(halcanary): Add this functionality (grayscale and indexed output) to
// SkSwizzler and use it here.
template <typename T, T (*TRANSFORM)(U8CPU)>
static void expand_bits_to_T(uint8_t* dstptr, const uint8_t* src, int bits) {
T* dst = reinterpret_cast<T*>(dstptr);
int bytes = bits >> 3;
for (int i = 0; i < bytes; i++) {
U8CPU mask = *src++;
for (int j = 0; j < 8; j++) {
dst[j] = TRANSFORM((mask >> (7 - j)) & 1);
bool SkWbmpCodec::handleRewind() {
SkCodec::RewindState rewindState = this->rewindIfNeeded();
if (rewindState == kCouldNotRewind_RewindState) {
return false;
} else if (rewindState == kRewound_RewindState) {
if (!read_header(this->stream(), NULL)) {
return false;
}
dst += 8;
}
bits &= 7;
if (bits > 0) {
U8CPU mask = *src;
do {
*dst++ = TRANSFORM((mask >> 7) & 1);
mask <<= 1;
} while (--bits != 0);
return true;
}
SkSwizzler* SkWbmpCodec::initializeSwizzler(const SkImageInfo& info,
const SkPMColor* ctable, const Options& opts) {
// TODO (msarett): Reenable support for 565 if it is desired
// skbug.com/3683
// Create the swizzler based on the desired color type
switch (info.colorType()) {
case kIndex_8_SkColorType:
case kN32_SkColorType:
case kGray_8_SkColorType:
return SkSwizzler::CreateSwizzler(
SkSwizzler::kBit, ctable, info, opts.fZeroInitialized);
default:
return NULL;
}
}
SkCodec::Result SkWbmpCodec::readRow(uint8_t* row) {
if (this->stream()->read(row, fSrcRowBytes) != fSrcRowBytes) {
return kIncompleteInput;
}
return kSuccess;
}
SkWbmpCodec::SkWbmpCodec(const SkImageInfo& info, SkStream* stream)
: INHERITED(info, stream) {}
: INHERITED(info, stream)
, fSrcRowBytes(get_src_row_bytes(this->getInfo().width()))
{}
SkEncodedFormat SkWbmpCodec::onGetEncodedFormat() const {
return kWBMP_SkEncodedFormat;
}
SkCodec::Result SkWbmpCodec::onGetPixels(const SkImageInfo& info,
void* pixels,
void* dst,
size_t rowBytes,
const Options& options,
SkPMColor ctable[],
int* ctableCount) {
SkCodec::RewindState rewindState = this->rewindIfNeeded();
if (rewindState == kCouldNotRewind_RewindState) {
if (!this->handleRewind()) {
return kCouldNotRewind;
} else if (rewindState == kRewound_RewindState) {
(void)read_header(this->stream(), NULL);
}
if (options.fSubset) {
// Subsets are not supported.
@ -119,36 +127,28 @@ SkCodec::Result SkWbmpCodec::onGetPixels(const SkImageInfo& info,
if (info.dimensions() != this->getInfo().dimensions()) {
return kInvalidScale;
}
ExpandProc proc = NULL;
switch (info.colorType()) {
case kGray_8_SkColorType:
proc = expand_bits_to_T<uint8_t, bit_to_grayscale>;
break;
case kN32_SkColorType:
proc = expand_bits_to_T<SkPMColor, bit_to_pmcolor>;
break;
case kIndex_8_SkColorType:
ctable[0] = BLACK;
ctable[1] = WHITE;
*ctableCount = 2;
proc = expand_bits_to_T<uint8_t, bit_to_bit>;
break;
case kRGB_565_SkColorType:
proc = expand_bits_to_T<uint16_t, bit_to_rgb565>;
break;
default:
return kInvalidConversion;
// Prepare a color table if necessary
setup_color_table(info.colorType(), ctable, ctableCount);
// Initialize the swizzler
SkAutoTDelete<SkSwizzler> swizzler(this->initializeSwizzler(info, ctable, options));
if (NULL == swizzler.get()) {
return kInvalidConversion;
}
// Perform the decode
SkISize size = info.dimensions();
uint8_t* dst = static_cast<uint8_t*>(pixels);
size_t srcRowBytes = SkAlign8(size.width()) >> 3;
SkAutoTMalloc<uint8_t> src(srcRowBytes);
SkAutoTMalloc<uint8_t> src(fSrcRowBytes);
void* dstRow = dst;
for (int y = 0; y < size.height(); ++y) {
if (this->stream()->read(src.get(), srcRowBytes) != srcRowBytes) {
return kIncompleteInput;
Result rowResult = this->readRow(src.get());
if (kSuccess != rowResult) {
return rowResult;
}
proc(dst, src.get(), size.width());
dst += rowBytes;
swizzler->swizzle(dstRow, src.get());
dstRow = SkTAddOffset<void>(dstRow, rowBytes);
}
return kSuccess;
}
@ -163,8 +163,85 @@ SkCodec* SkWbmpCodec::NewFromStream(SkStream* stream) {
if (!read_header(stream, &size)) {
return NULL;
}
SkImageInfo info =
SkImageInfo::Make(size.width(), size.height(), kGray_8_SkColorType,
kOpaque_SkAlphaType);
SkImageInfo info = SkImageInfo::Make(size.width(), size.height(),
kGray_8_SkColorType, kOpaque_SkAlphaType);
return SkNEW_ARGS(SkWbmpCodec, (info, streamDeleter.detach()));
}
class SkWbmpScanlineDecoder : public SkScanlineDecoder {
public:
/*
* Takes ownership of all pointer paramters.
*/
SkWbmpScanlineDecoder(SkWbmpCodec* codec)
: INHERITED(codec->getInfo())
, fCodec(codec)
, fColorTable(NULL)
, fSwizzler(NULL)
, fSrcBuffer(codec->fSrcRowBytes)
{}
SkCodec::Result onGetScanlines(void* dst, int count, size_t dstRowBytes) override {
void* dstRow = dst;
for (int y = 0; y < count; ++y) {
SkCodec::Result rowResult = fCodec->readRow(fSrcBuffer.get());
if (SkCodec::kSuccess != rowResult) {
return rowResult;
}
fSwizzler->swizzle(dstRow, fSrcBuffer.get());
dstRow = SkTAddOffset<void>(dstRow, dstRowBytes);
}
return SkCodec::kSuccess;
}
SkCodec::Result onStart(const SkImageInfo& dstInfo,
const SkCodec::Options& options, SkPMColor inputColorTable[],
int* inputColorCount) {
if (!fCodec->handleRewind()) {
return SkCodec::kCouldNotRewind;
}
if (options.fSubset) {
// Subsets are not supported.
return SkCodec::kUnimplemented;
}
if (dstInfo.dimensions() != this->getInfo().dimensions()) {
return SkCodec::kInvalidScale;
}
// Fill in the color table
setup_color_table(dstInfo.colorType(), inputColorTable, inputColorCount);
// Copy the color table to a pointer that can be owned by the scanline decoder
if (kIndex_8_SkColorType == dstInfo.colorType()) {
fColorTable.reset(SkNEW_ARGS(SkColorTable, (inputColorTable, 2)));
}
// Initialize the swizzler
fSwizzler.reset(fCodec->initializeSwizzler(dstInfo,
get_color_ptr(fColorTable.get()), options));
if (NULL == fSwizzler.get()) {
return SkCodec::kInvalidInput;
}
return SkCodec::kSuccess;
}
private:
SkAutoTDelete<SkWbmpCodec> fCodec;
SkAutoTUnref<SkColorTable> fColorTable;
SkAutoTDelete<SkSwizzler> fSwizzler;
SkAutoTMalloc<uint8_t> fSrcBuffer;
typedef SkScanlineDecoder INHERITED;
};
SkScanlineDecoder* SkWbmpCodec::NewSDFromStream(SkStream* stream) {
SkAutoTDelete<SkWbmpCodec> codec(static_cast<SkWbmpCodec*>(
SkWbmpCodec::NewFromStream(stream)));
if (!codec) {
return NULL;
}
// Return the new scanline decoder
return SkNEW_ARGS(SkWbmpScanlineDecoder, (codec.detach()));
}

View File

@ -9,17 +9,53 @@
#define SkCodec_wbmp_DEFINED
#include "SkCodec.h"
#include "SkScanlineDecoder.h"
#include "SkSwizzler.h"
class SkWbmpCodec final : public SkCodec {
public:
static bool IsWbmp(SkStream*);
/*
* Assumes IsWbmp was called and returned true
* Creates a wbmp codec
* Takes ownership of the stream
*/
static SkCodec* NewFromStream(SkStream*);
/*
* Assumes IsWbmp was called and returned true
* Creates a wbmp scanline decoder
* Takes ownership of the stream
*/
static SkScanlineDecoder* NewSDFromStream(SkStream*);
protected:
SkEncodedFormat onGetEncodedFormat() const override;
Result onGetPixels(const SkImageInfo&, void*, size_t,
const Options&, SkPMColor[], int*) override;
private:
/*
* Calls rewindIfNeeded and returns true if the decoder can continue
*/
bool handleRewind();
/*
* Returns a swizzler on success, NULL on failure
*/
SkSwizzler* initializeSwizzler(const SkImageInfo& info, const SkPMColor* ctable,
const Options& opts);
/*
* Read a src row from the encoded stream
*/
Result readRow(uint8_t* row);
SkWbmpCodec(const SkImageInfo&, SkStream*);
const size_t fSrcRowBytes;
friend class SkWbmpScanlineDecoder;
typedef SkCodec INHERITED;
};

View File

@ -7,6 +7,7 @@
#include "SkScanlineDecoder.h"
#include "SkCodec_libpng.h"
#include "SkCodec_wbmp.h"
#include "SkCodecPriv.h"
#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
#include "SkJpegCodec.h"
@ -22,6 +23,7 @@ static const DecoderProc gDecoderProcs[] = {
#ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK
{ SkJpegCodec::IsJpeg, SkJpegCodec::NewSDFromStream },
#endif
{ SkWbmpCodec::IsWbmp, SkWbmpCodec::NewSDFromStream },
};
SkScanlineDecoder* SkScanlineDecoder::NewFromStream(SkStream* stream) {
@ -66,6 +68,19 @@ SkScanlineDecoder* SkScanlineDecoder::NewFromData(SkData* data) {
SkCodec::Result SkScanlineDecoder::start(const SkImageInfo& dstInfo,
const SkCodec::Options* options, SkPMColor ctable[], int* ctableCount) {
// Ensure that valid color ptrs are passed in for kIndex8 color type
if (kIndex_8_SkColorType == dstInfo.colorType()) {
if (NULL == ctable || NULL == ctableCount) {
return SkCodec::kInvalidParameters;
}
} else {
if (ctableCount) {
*ctableCount = 0;
}
ctableCount = NULL;
ctable = NULL;
}
// Set options.
SkCodec::Options optsStorage;
if (NULL == options) {
@ -83,10 +98,6 @@ SkCodec::Result SkScanlineDecoder::start(const SkImageInfo& dstInfo,
}
SkCodec::Result SkScanlineDecoder::start(const SkImageInfo& dstInfo) {
SkASSERT(kIndex_8_SkColorType != dstInfo.colorType());
if (kIndex_8_SkColorType == dstInfo.colorType()) {
return SkCodec::kInvalidConversion;
}
return this->start(dstInfo, NULL, NULL, NULL);
}

View File

@ -19,6 +19,93 @@ SkSwizzler::ResultAlpha SkSwizzler::GetResult(uint8_t zeroAlpha,
return (((uint16_t) maxAlpha) << 8) | zeroAlpha;
}
// kBit
// These routines exclusively choose between white and black
#define GRAYSCALE_BLACK 0
#define GRAYSCALE_WHITE 0xFF
static SkSwizzler::ResultAlpha swizzle_bit_to_grayscale(
void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
int /*bitsPerPixel*/, const SkPMColor* /*ctable*/) {
uint8_t* SK_RESTRICT dst = (uint8_t*) dstRow;
// Determine how many full bytes are in the row
int bytesInRow = width >> 3;
int i;
for (i = 0; i < bytesInRow; i++) {
U8CPU currByte = src[i];
for (int j = 0; j < 8; j++) {
dst[j] = ((currByte >> (7 - j)) & 1) ? GRAYSCALE_WHITE : GRAYSCALE_BLACK;
}
dst += 8;
}
// Finish the remaining bits
width &= 7;
U8CPU currByte = src[i];
for (int j = 0; j < width; j++) {
dst[j] = ((currByte >> 7) & 1) ? GRAYSCALE_WHITE : GRAYSCALE_BLACK;
currByte <<= 1;
}
return SkSwizzler::kOpaque_ResultAlpha;
}
#undef GRAYSCALE_BLACK
#undef GRAYSCALE_WHITE
static SkSwizzler::ResultAlpha swizzle_bit_to_index(
void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
int /*bitsPerPixel*/, const SkPMColor* /*ctable*/) {
uint8_t* SK_RESTRICT dst = (uint8_t*) dstRow;
// Determine how many full bytes are in the row
int bytesInRow = width >> 3;
int i;
for (i = 0; i < bytesInRow; i++) {
U8CPU currByte = src[i];
for (int j = 0; j < 8; j++) {
dst[j] = (currByte >> (7 - j)) & 1;
}
dst += 8;
}
// Finish the remaining bits
width &= 7;
U8CPU currByte = src[i];
for (int j = 0; j < width; j++) {
dst[j] = ((currByte >> 7) & 1);
currByte <<= 1;
}
return SkSwizzler::kOpaque_ResultAlpha;
}
static SkSwizzler::ResultAlpha swizzle_bit_to_n32(
void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width,
int /*bitsPerPixel*/, const SkPMColor* /*ctable*/) {
SkPMColor* SK_RESTRICT dst = (SkPMColor*) dstRow;
// Determine how many full bytes are in the row
int bytesInRow = width >> 3;
int i;
for (i = 0; i < bytesInRow; i++) {
U8CPU currByte = src[i];
for (int j = 0; j < 8; j++) {
dst[j] = ((currByte >> (7 - j)) & 1) ? SK_ColorWHITE : SK_ColorBLACK;
}
dst += 8;
}
// Finish the remaining bits
width &= 7;
U8CPU currByte = src[i];
for (int j = 0; j < width; j++) {
dst[j] = ((currByte >> 7) & 1) ? SK_ColorWHITE : SK_ColorBLACK;
currByte <<= 1;
}
return SkSwizzler::kOpaque_ResultAlpha;
}
// kIndex1, kIndex2, kIndex4
static SkSwizzler::ResultAlpha swizzle_small_index_to_index(
@ -284,6 +371,21 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc,
}
RowProc proc = NULL;
switch (sc) {
case kBit:
switch (info.colorType()) {
case kN32_SkColorType:
proc = &swizzle_bit_to_n32;
break;
case kIndex_8_SkColorType:
proc = &swizzle_bit_to_index;
break;
case kGray_8_SkColorType:
proc = &swizzle_bit_to_grayscale;
break;
default:
break;
}
break;
case kIndex1:
case kIndex2:
case kIndex4:

View File

@ -19,6 +19,7 @@ public:
*/
enum SrcConfig {
kUnknown, // Invalid type.
kBit, // A single bit to distinguish between white and black
kGray,
kIndex1,
kIndex2,
@ -76,6 +77,7 @@ public:
*/
static int BitsPerPixel(SrcConfig sc) {
switch (sc) {
case kBit:
case kIndex1:
return 1;
case kIndex2:

View File

@ -152,7 +152,7 @@ static void check(skiatest::Reporter* r,
DEF_TEST(Codec, r) {
// WBMP
check(r, "mandrill.wbmp", SkISize::Make(512, 512), false, false);
check(r, "mandrill.wbmp", SkISize::Make(512, 512), true, false);
// WEBP
check(r, "baby_tux.webp", SkISize::Make(386, 395), false, true);
@ -289,3 +289,38 @@ DEF_TEST(Codec_Empty, r) {
test_empty(r, "empty_images/zero-width.wbmp");
test_empty(r, "empty_images/zero-height.wbmp");
}
static void test_invalid_parameters(skiatest::Reporter* r, const char path[]) {
SkAutoTDelete<SkStream> stream(resource(path));
if (!stream) {
SkDebugf("Missing resource '%s'\n", path);
return;
}
SkAutoTDelete<SkScanlineDecoder> decoder(SkScanlineDecoder::NewFromStream(
stream.detach()));
// This should return kSuccess because kIndex8 is supported.
SkPMColor colorStorage[256];
int colorCount;
SkCodec::Result result = decoder->start(
decoder->getInfo().makeColorType(kIndex_8_SkColorType), NULL, colorStorage, &colorCount);
REPORTER_ASSERT(r, SkCodec::kSuccess == result);
// The rest of the test is uninteresting if kIndex8 is not supported
if (SkCodec::kSuccess != result) {
return;
}
// This should return kInvalidParameters because, in kIndex_8 mode, we must pass in a valid
// colorPtr and a valid colorCountPtr.
result = decoder->start(
decoder->getInfo().makeColorType(kIndex_8_SkColorType), NULL, NULL, NULL);
REPORTER_ASSERT(r, SkCodec::kInvalidParameters == result);
result = decoder->start(
decoder->getInfo().makeColorType(kIndex_8_SkColorType));
REPORTER_ASSERT(r, SkCodec::kInvalidParameters == result);
}
DEF_TEST(Codec_Params, r) {
test_invalid_parameters(r, "index8.png");
test_invalid_parameters(r, "mandrill.wbmp");
}