Split SkBmpCodec into three separate classes
Will regress behavior on gold on test32bfv4.bmp, where we will no longer fix transparent decodes. TODO: Start fixing transparent decodes again, or decide that we don't want to fix them and remove isTransparent from SkSwizzler. I think this may become more clear when I start implementing the scanline decoder. BUG=skia: Review URL: https://codereview.chromium.org/1258863008
This commit is contained in:
parent
74992b5794
commit
4ab9d5f1bc
@ -33,6 +33,9 @@
|
||||
],
|
||||
'sources': [
|
||||
'../src/codec/SkBmpCodec.cpp',
|
||||
'../src/codec/SkBmpMaskCodec.cpp',
|
||||
'../src/codec/SkBmpRLECodec.cpp',
|
||||
'../src/codec/SkBmpStandardCodec.cpp',
|
||||
'../src/codec/SkCodec.cpp',
|
||||
'../src/codec/SkCodec_libgif.cpp',
|
||||
'../src/codec/SkCodec_libico.cpp',
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4,6 +4,8 @@
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#ifndef SkBmpCodec_DEFINED
|
||||
#define SkBmpCodec_DEFINED
|
||||
|
||||
#include "SkCodec.h"
|
||||
#include "SkColorTable.h"
|
||||
@ -13,19 +15,15 @@
|
||||
#include "SkSwizzler.h"
|
||||
#include "SkTypes.h"
|
||||
|
||||
// TODO: rename SkCodec_libbmp files to SkBmpCodec
|
||||
/*
|
||||
*
|
||||
* This class implements the decoding for bmp images
|
||||
*
|
||||
* This class enables code sharing between its bmp codec subclasses. The
|
||||
* subclasses actually do the work.
|
||||
*/
|
||||
class SkBmpCodec : public SkCodec {
|
||||
public:
|
||||
|
||||
/*
|
||||
*
|
||||
* Describes if rows of the input start at the top or bottom of the image
|
||||
*
|
||||
*/
|
||||
enum RowOrder {
|
||||
kTopDown_RowOrder,
|
||||
@ -33,161 +31,75 @@ public:
|
||||
};
|
||||
|
||||
/*
|
||||
*
|
||||
* Checks the start of the stream to see if the image is a bmp
|
||||
*
|
||||
*/
|
||||
static bool IsBmp(SkStream*);
|
||||
|
||||
/*
|
||||
*
|
||||
* Assumes IsBmp was called and returned true
|
||||
* Creates a bmp decoder
|
||||
* Reads enough of the stream to determine the image format
|
||||
*
|
||||
*/
|
||||
static SkCodec* NewFromStream(SkStream*);
|
||||
|
||||
/*
|
||||
*
|
||||
* Creates a bmp decoder for a bmp embedded in ico
|
||||
* Reads enough of the stream to determine the image format
|
||||
*
|
||||
*/
|
||||
static SkCodec* NewFromIco(SkStream*);
|
||||
|
||||
protected:
|
||||
|
||||
/*
|
||||
*
|
||||
* Initiates the bmp decode
|
||||
*
|
||||
*/
|
||||
Result onGetPixels(const SkImageInfo& dstInfo, void* dst,
|
||||
size_t dstRowBytes, const Options&, SkPMColor*,
|
||||
int*) override;
|
||||
SkBmpCodec(const SkImageInfo& info, SkStream* stream, uint16_t bitsPerPixel,
|
||||
RowOrder rowOrder);
|
||||
|
||||
SkEncodedFormat onGetEncodedFormat() const override { return kBMP_SkEncodedFormat; }
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
*
|
||||
* Used to define the input format of the bmp
|
||||
*
|
||||
*/
|
||||
enum BitmapInputFormat {
|
||||
kStandard_BitmapInputFormat,
|
||||
kRLE_BitmapInputFormat,
|
||||
kBitMask_BitmapInputFormat,
|
||||
kUnknown_BitmapInputFormat
|
||||
};
|
||||
|
||||
/*
|
||||
*
|
||||
* Creates the color table
|
||||
* Sets colorCount to the new color count if it is non-NULL
|
||||
*/
|
||||
bool createColorTable(SkAlphaType alphaType, int* colorCount);
|
||||
|
||||
/*
|
||||
*
|
||||
* Creates a bmp decoder
|
||||
* Reads enough of the stream to determine the image format
|
||||
*
|
||||
*/
|
||||
static SkCodec* NewFromStream(SkStream*, bool isIco);
|
||||
|
||||
/*
|
||||
*
|
||||
* Read enough of the stream to initialize the SkBmpCodec. Returns a bool
|
||||
* representing success or failure. If it returned true, and codecOut was
|
||||
* not NULL, it will be set to a new SkBmpCodec.
|
||||
* Does *not* take ownership of the passed in SkStream.
|
||||
*
|
||||
*/
|
||||
static bool ReadHeader(SkStream*, bool isIco, SkCodec** codecOut);
|
||||
static bool ReadHeader(SkStream*, bool inIco, SkCodec** codecOut);
|
||||
|
||||
/*
|
||||
*
|
||||
* Performs the bitmap decoding for bit masks input format
|
||||
*
|
||||
* Rewinds the image stream if necessary
|
||||
*/
|
||||
Result decodeMask(const SkImageInfo& dstInfo, void* dst,
|
||||
size_t dstRowBytes, const Options& opts);
|
||||
bool handleRewind(bool inIco);
|
||||
|
||||
/*
|
||||
*
|
||||
* Set an RLE pixel using the color table
|
||||
*
|
||||
* Get the destination row to start filling from
|
||||
* Used to fill the remainder of the image on incomplete input for bmps
|
||||
* This is tricky since bmps may be kTopDown or kBottomUp. For kTopDown,
|
||||
* we start filling from where we left off, but for kBottomUp we start
|
||||
* filling at the top of the image.
|
||||
*/
|
||||
void setRLEPixel(void* dst, size_t dstRowBytes,
|
||||
const SkImageInfo& dstInfo, uint32_t x, uint32_t y,
|
||||
uint8_t index);
|
||||
/*
|
||||
*
|
||||
* Set an RLE24 pixel from R, G, B values
|
||||
*
|
||||
*/
|
||||
void setRLE24Pixel(void* dst, size_t dstRowBytes,
|
||||
const SkImageInfo& dstInfo, uint32_t x, uint32_t y,
|
||||
uint8_t red, uint8_t green, uint8_t blue);
|
||||
void* getDstStartRow(void* dst, size_t dstRowBytes, int32_t y) const;
|
||||
|
||||
/*
|
||||
*
|
||||
* Performs the bitmap decoding for RLE input format
|
||||
*
|
||||
* Compute the number of colors in the color table
|
||||
*/
|
||||
Result decodeRLE(const SkImageInfo& dstInfo, void* dst,
|
||||
size_t dstRowBytes, const Options& opts);
|
||||
uint32_t computeNumColors(uint32_t numColors);
|
||||
|
||||
/*
|
||||
*
|
||||
* Performs the bitmap decoding for standard input format
|
||||
*
|
||||
* Accessors used by subclasses
|
||||
*/
|
||||
Result decode(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& opts);
|
||||
uint16_t bitsPerPixel() const { return fBitsPerPixel; }
|
||||
RowOrder rowOrder() const { return fRowOrder; }
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
*
|
||||
* Creates an instance of the decoder
|
||||
* Called only by NewFromStream
|
||||
*
|
||||
* @param srcInfo contains the source width and height
|
||||
* @param stream the stream of image data
|
||||
* @param bitsPerPixel the number of bits used to store each pixel
|
||||
* @param format the format of the bmp file
|
||||
* @param masks optional color masks for certain bmp formats, passes
|
||||
ownership to SkBmpCodec
|
||||
* @param numColors the number of colors in the color table
|
||||
* @param bytesPerColor the number of bytes in the stream used to represent
|
||||
each color in the color table
|
||||
* @param offset the offset of the image pixel data from the end of the
|
||||
* headers
|
||||
* @param rowOrder indicates whether rows are ordered top-down or bottom-up
|
||||
* @param RLEBytes used only for RLE decodes, as we must decode all
|
||||
* of the data at once rather than row by row
|
||||
* it indicates the amount of data left in the stream
|
||||
* after decoding the headers
|
||||
*
|
||||
* Creates a bmp decoder
|
||||
* Reads enough of the stream to determine the image format
|
||||
*/
|
||||
SkBmpCodec(const SkImageInfo& srcInfo, SkStream* stream,
|
||||
uint16_t bitsPerPixel, BitmapInputFormat format,
|
||||
SkMasks* masks, uint32_t numColors, uint32_t bytesPerColor,
|
||||
uint32_t offset, RowOrder rowOrder, size_t RLEBytes,
|
||||
bool isIco);
|
||||
static SkCodec* NewFromStream(SkStream*, bool inIco);
|
||||
|
||||
// Fields
|
||||
const uint16_t fBitsPerPixel;
|
||||
const BitmapInputFormat fInputFormat;
|
||||
SkAutoTDelete<SkMasks> fMasks; // owned
|
||||
SkAutoTUnref<SkColorTable> fColorTable; // owned
|
||||
uint32_t fNumColors;
|
||||
const uint32_t fBytesPerColor;
|
||||
const uint32_t fOffset;
|
||||
const RowOrder fRowOrder;
|
||||
const size_t fRLEBytes;
|
||||
const bool fIsIco;
|
||||
const uint16_t fBitsPerPixel;
|
||||
const RowOrder fRowOrder;
|
||||
|
||||
typedef SkCodec INHERITED;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
137
src/codec/SkBmpMaskCodec.cpp
Normal file
137
src/codec/SkBmpMaskCodec.cpp
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkBmpMaskCodec.h"
|
||||
#include "SkCodecPriv.h"
|
||||
#include "SkColorPriv.h"
|
||||
|
||||
/*
|
||||
* Checks if the conversion between the input image and the requested output
|
||||
* image has been implemented
|
||||
*/
|
||||
static bool conversion_possible(const SkImageInfo& dst,
|
||||
const SkImageInfo& src) {
|
||||
// Ensure that the profile type is unchanged
|
||||
if (dst.profileType() != src.profileType()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure the alpha type is valid
|
||||
if (!valid_alpha(dst.alphaType(), src.alphaType())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for supported color types
|
||||
switch (dst.colorType()) {
|
||||
// Allow output to kN32
|
||||
case kN32_SkColorType:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Creates an instance of the decoder
|
||||
*/
|
||||
SkBmpMaskCodec::SkBmpMaskCodec(const SkImageInfo& info, SkStream* stream,
|
||||
uint16_t bitsPerPixel, SkMasks* masks,
|
||||
SkBmpCodec::RowOrder rowOrder)
|
||||
: INHERITED(info, stream, bitsPerPixel, rowOrder)
|
||||
, fMasks(masks)
|
||||
, fMaskSwizzler(NULL)
|
||||
, fSrcBuffer(NULL)
|
||||
{}
|
||||
|
||||
/*
|
||||
* Initiates the bitmap decode
|
||||
*/
|
||||
SkCodec::Result SkBmpMaskCodec::onGetPixels(const SkImageInfo& dstInfo,
|
||||
void* dst, size_t dstRowBytes,
|
||||
const Options& opts,
|
||||
SkPMColor* inputColorPtr,
|
||||
int* inputColorCount) {
|
||||
if (!this->handleRewind(false)) {
|
||||
return kCouldNotRewind;
|
||||
}
|
||||
if (opts.fSubset) {
|
||||
// Subsets are not supported.
|
||||
return kUnimplemented;
|
||||
}
|
||||
if (dstInfo.dimensions() != this->getInfo().dimensions()) {
|
||||
SkCodecPrintf("Error: scaling not supported.\n");
|
||||
return kInvalidScale;
|
||||
}
|
||||
|
||||
if (!conversion_possible(dstInfo, this->getInfo())) {
|
||||
SkCodecPrintf("Error: cannot convert input type to output type.\n");
|
||||
return kInvalidConversion;
|
||||
}
|
||||
|
||||
// Initialize a the mask swizzler
|
||||
if (!this->initializeSwizzler(dstInfo)) {
|
||||
SkCodecPrintf("Error: cannot initialize swizzler.\n");
|
||||
return kInvalidConversion;
|
||||
}
|
||||
|
||||
return this->decode(dstInfo, dst, dstRowBytes, opts);
|
||||
}
|
||||
|
||||
bool SkBmpMaskCodec::initializeSwizzler(const SkImageInfo& dstInfo) {
|
||||
// Allocate space for a row buffer
|
||||
const size_t rowBytes = SkAlign4(compute_row_bytes(dstInfo.width(), this->bitsPerPixel()));
|
||||
fSrcBuffer.reset(SkNEW_ARRAY(uint8_t, rowBytes));
|
||||
|
||||
// Create the swizzler
|
||||
fMaskSwizzler.reset(SkMaskSwizzler::CreateMaskSwizzler(
|
||||
dstInfo, fMasks, this->bitsPerPixel()));
|
||||
|
||||
if (NULL == fMaskSwizzler.get()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Performs the decoding
|
||||
*/
|
||||
SkCodec::Result SkBmpMaskCodec::decode(const SkImageInfo& dstInfo,
|
||||
void* dst, size_t dstRowBytes,
|
||||
const Options& opts) {
|
||||
// Set constant values
|
||||
const int width = dstInfo.width();
|
||||
const int height = dstInfo.height();
|
||||
const size_t rowBytes = SkAlign4(compute_row_bytes(width, this->bitsPerPixel()));
|
||||
|
||||
// Iterate over rows of the image
|
||||
uint8_t* srcRow = fSrcBuffer.get();
|
||||
for (int y = 0; y < height; y++) {
|
||||
// Read a row of the input
|
||||
if (this->stream()->read(srcRow, rowBytes) != rowBytes) {
|
||||
SkCodecPrintf("Warning: incomplete input stream.\n");
|
||||
// Fill the destination image on failure
|
||||
SkPMColor fillColor = dstInfo.alphaType() == kOpaque_SkAlphaType ?
|
||||
SK_ColorBLACK : SK_ColorTRANSPARENT;
|
||||
if (kNo_ZeroInitialized == opts.fZeroInitialized || 0 != fillColor) {
|
||||
void* dstStart = this->getDstStartRow(dst, dstRowBytes, y);
|
||||
SkSwizzler::Fill(dstStart, dstInfo, dstRowBytes, dstInfo.height() - y, fillColor,
|
||||
NULL);
|
||||
}
|
||||
return kIncompleteInput;
|
||||
}
|
||||
|
||||
// Decode the row in destination format
|
||||
int row = SkBmpCodec::kBottomUp_RowOrder == this->rowOrder() ? height - 1 - y : y;
|
||||
void* dstRow = SkTAddOffset<void>(dst, row * dstRowBytes);
|
||||
fMaskSwizzler->swizzle(dstRow, srcRow);
|
||||
}
|
||||
|
||||
// Finished decoding the entire image
|
||||
return kSuccess;
|
||||
}
|
52
src/codec/SkBmpMaskCodec.h
Normal file
52
src/codec/SkBmpMaskCodec.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkBmpCodec.h"
|
||||
#include "SkImageInfo.h"
|
||||
#include "SkMaskSwizzler.h"
|
||||
#include "SkTypes.h"
|
||||
|
||||
/*
|
||||
* This class implements the decoding for bmp images using bit masks
|
||||
*/
|
||||
class SkBmpMaskCodec : public SkBmpCodec {
|
||||
public:
|
||||
|
||||
/*
|
||||
* Creates an instance of the decoder
|
||||
*
|
||||
* Called only by SkBmpCodec::NewFromStream
|
||||
* There should be no other callers despite this being public
|
||||
*
|
||||
* @param srcInfo contains the source width and height
|
||||
* @param stream the stream of encoded image data
|
||||
* @param bitsPerPixel the number of bits used to store each pixel
|
||||
* @param masks color masks for certain bmp formats
|
||||
* @param rowOrder indicates whether rows are ordered top-down or bottom-up
|
||||
*/
|
||||
SkBmpMaskCodec(const SkImageInfo& srcInfo, SkStream* stream,
|
||||
uint16_t bitsPerPixel, SkMasks* masks, RowOrder rowOrder);
|
||||
|
||||
protected:
|
||||
|
||||
Result onGetPixels(const SkImageInfo& dstInfo, void* dst,
|
||||
size_t dstRowBytes, const Options&, SkPMColor*,
|
||||
int*) override;
|
||||
|
||||
private:
|
||||
|
||||
bool initializeSwizzler(const SkImageInfo& dstInfo);
|
||||
|
||||
Result decode(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes,
|
||||
const Options& opts);
|
||||
|
||||
SkAutoTDelete<SkMasks> fMasks; // owned
|
||||
SkAutoTDelete<SkMaskSwizzler> fMaskSwizzler;
|
||||
SkAutoTDeleteArray<uint8_t> fSrcBuffer;
|
||||
|
||||
typedef SkBmpCodec INHERITED;
|
||||
};
|
430
src/codec/SkBmpRLECodec.cpp
Normal file
430
src/codec/SkBmpRLECodec.cpp
Normal file
@ -0,0 +1,430 @@
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkBmpRLECodec.h"
|
||||
#include "SkCodecPriv.h"
|
||||
#include "SkColorPriv.h"
|
||||
#include "SkScanlineDecoder.h"
|
||||
#include "SkStream.h"
|
||||
|
||||
/*
|
||||
* Checks if the conversion between the input image and the requested output
|
||||
* image has been implemented
|
||||
*/
|
||||
static bool conversion_possible(const SkImageInfo& dst,
|
||||
const SkImageInfo& src) {
|
||||
// Ensure that the profile type is unchanged
|
||||
if (dst.profileType() != src.profileType()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure the alpha type is valid
|
||||
if (!valid_alpha(dst.alphaType(), src.alphaType())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for supported color types
|
||||
switch (dst.colorType()) {
|
||||
// Allow output to kN32 from any type of input
|
||||
case kN32_SkColorType:
|
||||
return true;
|
||||
// Allow output to kIndex_8 from compatible inputs
|
||||
case kIndex_8_SkColorType:
|
||||
return kIndex_8_SkColorType == src.colorType();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates an instance of the decoder
|
||||
* Called only by NewFromStream
|
||||
*/
|
||||
SkBmpRLECodec::SkBmpRLECodec(const SkImageInfo& info, SkStream* stream,
|
||||
uint16_t bitsPerPixel, uint32_t numColors,
|
||||
uint32_t bytesPerColor, uint32_t offset,
|
||||
SkBmpCodec::RowOrder rowOrder, size_t RLEBytes)
|
||||
: INHERITED(info, stream, bitsPerPixel, rowOrder)
|
||||
, fColorTable(NULL)
|
||||
, fNumColors(this->computeNumColors(numColors))
|
||||
, fBytesPerColor(bytesPerColor)
|
||||
, fOffset(offset)
|
||||
, fStreamBuffer(SkNEW_ARRAY(uint8_t, RLEBytes))
|
||||
, fRLEBytes(RLEBytes)
|
||||
, fCurrRLEByte(0)
|
||||
{}
|
||||
|
||||
/*
|
||||
* Initiates the bitmap decode
|
||||
*/
|
||||
SkCodec::Result SkBmpRLECodec::onGetPixels(const SkImageInfo& dstInfo,
|
||||
void* dst, size_t dstRowBytes,
|
||||
const Options& opts,
|
||||
SkPMColor* inputColorPtr,
|
||||
int* inputColorCount) {
|
||||
if (!this->handleRewind(false)) {
|
||||
return kCouldNotRewind;
|
||||
}
|
||||
if (opts.fSubset) {
|
||||
// Subsets are not supported.
|
||||
return kUnimplemented;
|
||||
}
|
||||
if (dstInfo.dimensions() != this->getInfo().dimensions()) {
|
||||
SkCodecPrintf("Error: scaling not supported.\n");
|
||||
return kInvalidScale;
|
||||
}
|
||||
if (!conversion_possible(dstInfo, this->getInfo())) {
|
||||
SkCodecPrintf("Error: cannot convert input type to output type.\n");
|
||||
return kInvalidConversion;
|
||||
}
|
||||
|
||||
// Create the color table if necessary and prepare the stream for decode
|
||||
// Note that if it is non-NULL, inputColorCount will be modified
|
||||
if (!this->createColorTable(inputColorCount)) {
|
||||
SkCodecPrintf("Error: could not create color table.\n");
|
||||
return kInvalidInput;
|
||||
}
|
||||
|
||||
// Copy the color table to the client if necessary
|
||||
copy_color_table(dstInfo, fColorTable, inputColorPtr, inputColorCount);
|
||||
|
||||
// Initialize a swizzler if necessary
|
||||
if (!this->initializeStreamBuffer()) {
|
||||
SkCodecPrintf("Error: cannot initialize swizzler.\n");
|
||||
return kInvalidConversion;
|
||||
}
|
||||
|
||||
// Perform the decode
|
||||
return decode(dstInfo, dst, dstRowBytes, opts);
|
||||
}
|
||||
|
||||
/*
|
||||
* Process the color table for the bmp input
|
||||
*/
|
||||
bool SkBmpRLECodec::createColorTable(int* numColors) {
|
||||
// Allocate memory for color table
|
||||
uint32_t colorBytes = 0;
|
||||
SkPMColor colorTable[256];
|
||||
if (this->bitsPerPixel() <= 8) {
|
||||
// Inform the caller of the number of colors
|
||||
uint32_t maxColors = 1 << this->bitsPerPixel();
|
||||
if (NULL != numColors) {
|
||||
// We set the number of colors to maxColors in order to ensure
|
||||
// safe memory accesses. Otherwise, an invalid pixel could
|
||||
// access memory outside of our color table array.
|
||||
*numColors = maxColors;
|
||||
}
|
||||
|
||||
// Read the color table from the stream
|
||||
colorBytes = fNumColors * fBytesPerColor;
|
||||
SkAutoTDeleteArray<uint8_t> cBuffer(SkNEW_ARRAY(uint8_t, colorBytes));
|
||||
if (stream()->read(cBuffer.get(), colorBytes) != colorBytes) {
|
||||
SkCodecPrintf("Error: unable to read color table.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fill in the color table
|
||||
uint32_t i = 0;
|
||||
for (; i < fNumColors; i++) {
|
||||
uint8_t blue = get_byte(cBuffer.get(), i*fBytesPerColor);
|
||||
uint8_t green = get_byte(cBuffer.get(), i*fBytesPerColor + 1);
|
||||
uint8_t red = get_byte(cBuffer.get(), i*fBytesPerColor + 2);
|
||||
colorTable[i] = SkPackARGB32NoCheck(0xFF, red, green, blue);
|
||||
}
|
||||
|
||||
// To avoid segmentation faults on bad pixel data, fill the end of the
|
||||
// color table with black. This is the same the behavior as the
|
||||
// chromium decoder.
|
||||
for (; i < maxColors; i++) {
|
||||
colorTable[i] = SkPackARGB32NoCheck(0xFF, 0, 0, 0);
|
||||
}
|
||||
|
||||
// Set the color table
|
||||
fColorTable.reset(SkNEW_ARGS(SkColorTable, (colorTable, maxColors)));
|
||||
}
|
||||
|
||||
// Check that we have not read past the pixel array offset
|
||||
if(fOffset < colorBytes) {
|
||||
// This may occur on OS 2.1 and other old versions where the color
|
||||
// table defaults to max size, and the bmp tries to use a smaller
|
||||
// color table. This is invalid, and our decision is to indicate
|
||||
// an error, rather than try to guess the intended size of the
|
||||
// color table.
|
||||
SkCodecPrintf("Error: pixel data offset less than color table size.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// After reading the color table, skip to the start of the pixel array
|
||||
if (stream()->skip(fOffset - colorBytes) != fOffset - colorBytes) {
|
||||
SkCodecPrintf("Error: unable to skip to image data.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return true on success
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkBmpRLECodec::initializeStreamBuffer() {
|
||||
// Setup a buffer to contain the full input stream
|
||||
size_t totalBytes = this->stream()->read(fStreamBuffer.get(), fRLEBytes);
|
||||
if (totalBytes < fRLEBytes) {
|
||||
fRLEBytes = totalBytes;
|
||||
SkCodecPrintf("Warning: incomplete RLE file.\n");
|
||||
}
|
||||
if (fRLEBytes == 0) {
|
||||
SkCodecPrintf("Error: could not read RLE image data.\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set an RLE pixel using the color table
|
||||
*/
|
||||
void SkBmpRLECodec::setPixel(void* dst, size_t dstRowBytes,
|
||||
const SkImageInfo& dstInfo, uint32_t x, uint32_t y,
|
||||
uint8_t index) {
|
||||
// Set the row
|
||||
int height = dstInfo.height();
|
||||
int row;
|
||||
if (SkBmpCodec::kBottomUp_RowOrder == this->rowOrder()) {
|
||||
row = height - y - 1;
|
||||
} else {
|
||||
row = y;
|
||||
}
|
||||
|
||||
// Set the pixel based on destination color type
|
||||
switch (dstInfo.colorType()) {
|
||||
case kN32_SkColorType: {
|
||||
SkPMColor* dstRow = SkTAddOffset<SkPMColor>((SkPMColor*) dst,
|
||||
row * (int) dstRowBytes);
|
||||
dstRow[x] = fColorTable->operator[](index);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// This case should not be reached. We should catch an invalid
|
||||
// color type when we check that the conversion is possible.
|
||||
SkASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set an RLE pixel from R, G, B values
|
||||
*/
|
||||
void SkBmpRLECodec::setRGBPixel(void* dst, size_t dstRowBytes,
|
||||
const SkImageInfo& dstInfo, uint32_t x,
|
||||
uint32_t y, uint8_t red, uint8_t green,
|
||||
uint8_t blue) {
|
||||
// Set the row
|
||||
int height = dstInfo.height();
|
||||
int row;
|
||||
if (SkBmpCodec::kBottomUp_RowOrder == this->rowOrder()) {
|
||||
row = height - y - 1;
|
||||
} else {
|
||||
row = y;
|
||||
}
|
||||
|
||||
// Set the pixel based on destination color type
|
||||
switch (dstInfo.colorType()) {
|
||||
case kN32_SkColorType: {
|
||||
SkPMColor* dstRow = SkTAddOffset<SkPMColor>((SkPMColor*) dst,
|
||||
row * (int) dstRowBytes);
|
||||
dstRow[x] = SkPackARGB32NoCheck(0xFF, red, green, blue);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// This case should not be reached. We should catch an invalid
|
||||
// color type when we check that the conversion is possible.
|
||||
SkASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Performs the bitmap decoding for RLE input format
|
||||
* RLE decoding is performed all at once, rather than a one row at a time
|
||||
*/
|
||||
SkCodec::Result SkBmpRLECodec::decode(const SkImageInfo& dstInfo,
|
||||
void* dst, size_t dstRowBytes,
|
||||
const Options& opts) {
|
||||
// Set RLE flags
|
||||
static const uint8_t RLE_ESCAPE = 0;
|
||||
static const uint8_t RLE_EOL = 0;
|
||||
static const uint8_t RLE_EOF = 1;
|
||||
static const uint8_t RLE_DELTA = 2;
|
||||
|
||||
// Set constant values
|
||||
const int width = dstInfo.width();
|
||||
const int height = dstInfo.height();
|
||||
|
||||
// Destination parameters
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
|
||||
// Set the background as transparent. Then, if the RLE code skips pixels,
|
||||
// the skipped pixels will be transparent.
|
||||
// Because of the need for transparent pixels, kN32 is the only color
|
||||
// type that makes sense for the destination format.
|
||||
SkASSERT(kN32_SkColorType == dstInfo.colorType());
|
||||
if (kNo_ZeroInitialized == opts.fZeroInitialized) {
|
||||
SkSwizzler::Fill(dst, dstInfo, dstRowBytes, height, SK_ColorTRANSPARENT, NULL);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
// If we have reached a row that is beyond the requested height, we have
|
||||
// succeeded.
|
||||
if (y >= height) {
|
||||
// It would be better to check for the EOF marker before returning
|
||||
// success, but we may be performing a scanline decode, which
|
||||
// may require us to stop before decoding the full height.
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
// Every entry takes at least two bytes
|
||||
if ((int) fRLEBytes - fCurrRLEByte < 2) {
|
||||
SkCodecPrintf("Warning: incomplete RLE input.\n");
|
||||
return kIncompleteInput;
|
||||
}
|
||||
|
||||
// Read the next two bytes. These bytes have different meanings
|
||||
// depending on their values. In the first interpretation, the first
|
||||
// byte is an escape flag and the second byte indicates what special
|
||||
// task to perform.
|
||||
const uint8_t flag = fStreamBuffer.get()[fCurrRLEByte++];
|
||||
const uint8_t task = fStreamBuffer.get()[fCurrRLEByte++];
|
||||
|
||||
// Perform decoding
|
||||
if (RLE_ESCAPE == flag) {
|
||||
switch (task) {
|
||||
case RLE_EOL:
|
||||
x = 0;
|
||||
y++;
|
||||
break;
|
||||
case RLE_EOF:
|
||||
return kSuccess;
|
||||
case RLE_DELTA: {
|
||||
// Two bytes are needed to specify delta
|
||||
if ((int) fRLEBytes - fCurrRLEByte < 2) {
|
||||
SkCodecPrintf("Warning: incomplete RLE input\n");
|
||||
return kIncompleteInput;
|
||||
}
|
||||
// Modify x and y
|
||||
const uint8_t dx = fStreamBuffer.get()[fCurrRLEByte++];
|
||||
const uint8_t dy = fStreamBuffer.get()[fCurrRLEByte++];
|
||||
x += dx;
|
||||
y += dy;
|
||||
if (x > width || y > height) {
|
||||
SkCodecPrintf("Warning: invalid RLE input 1.\n");
|
||||
return kIncompleteInput;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// If task does not match any of the above signals, it
|
||||
// indicates that we have a sequence of non-RLE pixels.
|
||||
// Furthermore, the value of task is equal to the number
|
||||
// of pixels to interpret.
|
||||
uint8_t numPixels = task;
|
||||
const size_t rowBytes = compute_row_bytes(numPixels,
|
||||
this->bitsPerPixel());
|
||||
// Abort if setting numPixels moves us off the edge of the
|
||||
// image. Also abort if there are not enough bytes
|
||||
// remaining in the stream to set numPixels.
|
||||
if (x + numPixels > width ||
|
||||
(int) fRLEBytes - fCurrRLEByte < SkAlign2(rowBytes)) {
|
||||
SkCodecPrintf("Warning: invalid RLE input 2.\n");
|
||||
return kIncompleteInput;
|
||||
}
|
||||
// Set numPixels number of pixels
|
||||
while (numPixels > 0) {
|
||||
switch(this->bitsPerPixel()) {
|
||||
case 4: {
|
||||
SkASSERT(fCurrRLEByte < fRLEBytes);
|
||||
uint8_t val = fStreamBuffer.get()[fCurrRLEByte++];
|
||||
setPixel(dst, dstRowBytes, dstInfo, x++,
|
||||
y, val >> 4);
|
||||
numPixels--;
|
||||
if (numPixels != 0) {
|
||||
setPixel(dst, dstRowBytes, dstInfo,
|
||||
x++, y, val & 0xF);
|
||||
numPixels--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 8:
|
||||
SkASSERT(fCurrRLEByte < fRLEBytes);
|
||||
setPixel(dst, dstRowBytes, dstInfo, x++,
|
||||
y, fStreamBuffer.get()[fCurrRLEByte++]);
|
||||
numPixels--;
|
||||
break;
|
||||
case 24: {
|
||||
SkASSERT(fCurrRLEByte + 2 < fRLEBytes);
|
||||
uint8_t blue = fStreamBuffer.get()[fCurrRLEByte++];
|
||||
uint8_t green = fStreamBuffer.get()[fCurrRLEByte++];
|
||||
uint8_t red = fStreamBuffer.get()[fCurrRLEByte++];
|
||||
setRGBPixel(dst, dstRowBytes, dstInfo,
|
||||
x++, y, red, green, blue);
|
||||
numPixels--;
|
||||
}
|
||||
default:
|
||||
SkASSERT(false);
|
||||
return kInvalidInput;
|
||||
}
|
||||
}
|
||||
// Skip a byte if necessary to maintain alignment
|
||||
if (!SkIsAlign2(rowBytes)) {
|
||||
fCurrRLEByte++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If the first byte read is not a flag, it indicates the number of
|
||||
// pixels to set in RLE mode.
|
||||
const uint8_t numPixels = flag;
|
||||
const int endX = SkTMin<int>(x + numPixels, width);
|
||||
|
||||
if (24 == this->bitsPerPixel()) {
|
||||
// In RLE24, the second byte read is part of the pixel color.
|
||||
// There are two more required bytes to finish encoding the
|
||||
// color.
|
||||
if ((int) fRLEBytes - fCurrRLEByte < 2) {
|
||||
SkCodecPrintf("Warning: incomplete RLE input\n");
|
||||
return kIncompleteInput;
|
||||
}
|
||||
|
||||
// Fill the pixels up to endX with the specified color
|
||||
uint8_t blue = task;
|
||||
uint8_t green = fStreamBuffer.get()[fCurrRLEByte++];
|
||||
uint8_t red = fStreamBuffer.get()[fCurrRLEByte++];
|
||||
while (x < endX) {
|
||||
setRGBPixel(dst, dstRowBytes, dstInfo, x++, y, red,
|
||||
green, blue);
|
||||
}
|
||||
} else {
|
||||
// In RLE8 or RLE4, the second byte read gives the index in the
|
||||
// color table to look up the pixel color.
|
||||
// RLE8 has one color index that gets repeated
|
||||
// RLE4 has two color indexes in the upper and lower 4 bits of
|
||||
// the bytes, which are alternated
|
||||
uint8_t indices[2] = { task, task };
|
||||
if (4 == this->bitsPerPixel()) {
|
||||
indices[0] >>= 4;
|
||||
indices[1] &= 0xf;
|
||||
}
|
||||
|
||||
// Set the indicated number of pixels
|
||||
for (int which = 0; x < endX; x++) {
|
||||
setPixel(dst, dstRowBytes, dstInfo, x, y,
|
||||
indices[which]);
|
||||
which = !which;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
86
src/codec/SkBmpRLECodec.h
Normal file
86
src/codec/SkBmpRLECodec.h
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkBmpCodec.h"
|
||||
#include "SkColorTable.h"
|
||||
#include "SkImageInfo.h"
|
||||
#include "SkTypes.h"
|
||||
|
||||
/*
|
||||
* This class implements the decoding for bmp images that use an RLE encoding
|
||||
*/
|
||||
class SkBmpRLECodec : public SkBmpCodec {
|
||||
public:
|
||||
|
||||
/*
|
||||
* Creates an instance of the decoder
|
||||
*
|
||||
* Called only by SkBmpCodec::NewFromStream
|
||||
* There should be no other callers despite this being public
|
||||
*
|
||||
* @param srcInfo contains the source width and height
|
||||
* @param stream the stream of encoded image data
|
||||
* @param bitsPerPixel the number of bits used to store each pixel
|
||||
* @param numColors the number of colors in the color table
|
||||
* @param bytesPerColor the number of bytes in the stream used to represent
|
||||
each color in the color table
|
||||
* @param offset the offset of the image pixel data from the end of the
|
||||
* headers
|
||||
* @param rowOrder indicates whether rows are ordered top-down or bottom-up
|
||||
* @param RLEBytes indicates the amount of data left in the stream
|
||||
* after decoding the headers
|
||||
*/
|
||||
SkBmpRLECodec(const SkImageInfo& srcInfo, SkStream* stream,
|
||||
uint16_t bitsPerPixel, uint32_t numColors,
|
||||
uint32_t bytesPerColor, uint32_t offset,
|
||||
SkBmpCodec::RowOrder rowOrder, size_t RLEBytes);
|
||||
|
||||
protected:
|
||||
|
||||
Result onGetPixels(const SkImageInfo& dstInfo, void* dst,
|
||||
size_t dstRowBytes, const Options&, SkPMColor*,
|
||||
int*) override;
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
* Creates the color table
|
||||
* Sets colorCount to the new color count if it is non-NULL
|
||||
*/
|
||||
bool createColorTable(int* colorCount);
|
||||
|
||||
bool initializeStreamBuffer();
|
||||
|
||||
/*
|
||||
* Set an RLE pixel using the color table
|
||||
*/
|
||||
void setPixel(void* dst, size_t dstRowBytes,
|
||||
const SkImageInfo& dstInfo, uint32_t x, uint32_t y,
|
||||
uint8_t index);
|
||||
/*
|
||||
* Set an RLE24 pixel from R, G, B values
|
||||
*/
|
||||
void setRGBPixel(void* dst, size_t dstRowBytes,
|
||||
const SkImageInfo& dstInfo, uint32_t x, uint32_t y,
|
||||
uint8_t red, uint8_t green, uint8_t blue);
|
||||
|
||||
/*
|
||||
* Performs the bitmap decoding for RLE input format
|
||||
*/
|
||||
Result decode(const SkImageInfo& dstInfo, void* dst,
|
||||
size_t dstRowBytes, const Options& opts);
|
||||
|
||||
SkAutoTUnref<SkColorTable> fColorTable; // owned
|
||||
const uint32_t fNumColors;
|
||||
const uint32_t fBytesPerColor;
|
||||
const uint32_t fOffset;
|
||||
SkAutoTDeleteArray<uint8_t> fStreamBuffer;
|
||||
size_t fRLEBytes;
|
||||
uint32_t fCurrRLEByte;
|
||||
|
||||
typedef SkBmpCodec INHERITED;
|
||||
};
|
361
src/codec/SkBmpStandardCodec.cpp
Normal file
361
src/codec/SkBmpStandardCodec.cpp
Normal file
@ -0,0 +1,361 @@
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkBmpStandardCodec.h"
|
||||
#include "SkCodecPriv.h"
|
||||
#include "SkColorPriv.h"
|
||||
#include "SkScanlineDecoder.h"
|
||||
#include "SkStream.h"
|
||||
|
||||
/*
|
||||
* Checks if the conversion between the input image and the requested output
|
||||
* image has been implemented
|
||||
*/
|
||||
static bool conversion_possible(const SkImageInfo& dst,
|
||||
const SkImageInfo& src) {
|
||||
// Ensure that the profile type is unchanged
|
||||
if (dst.profileType() != src.profileType()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure the alpha type is valid
|
||||
if (!valid_alpha(dst.alphaType(), src.alphaType())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for supported color types
|
||||
switch (dst.colorType()) {
|
||||
// Allow output to kN32 from any type of input
|
||||
case kN32_SkColorType:
|
||||
return true;
|
||||
// Allow output to kIndex_8 from compatible inputs
|
||||
case kIndex_8_SkColorType:
|
||||
return kIndex_8_SkColorType == src.colorType();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates an instance of the decoder
|
||||
* Called only by NewFromStream
|
||||
*/
|
||||
SkBmpStandardCodec::SkBmpStandardCodec(const SkImageInfo& info, SkStream* stream,
|
||||
uint16_t bitsPerPixel, uint32_t numColors,
|
||||
uint32_t bytesPerColor, uint32_t offset,
|
||||
SkBmpCodec::RowOrder rowOrder, bool inIco)
|
||||
: INHERITED(info, stream, bitsPerPixel, rowOrder)
|
||||
, fColorTable(NULL)
|
||||
, fNumColors(this->computeNumColors(numColors))
|
||||
, fBytesPerColor(bytesPerColor)
|
||||
, fOffset(offset)
|
||||
, fSwizzler(NULL)
|
||||
, fSrcBuffer(NULL)
|
||||
, fInIco(inIco)
|
||||
{}
|
||||
|
||||
/*
|
||||
* Initiates the bitmap decode
|
||||
*/
|
||||
SkCodec::Result SkBmpStandardCodec::onGetPixels(const SkImageInfo& dstInfo,
|
||||
void* dst, size_t dstRowBytes,
|
||||
const Options& opts,
|
||||
SkPMColor* inputColorPtr,
|
||||
int* inputColorCount) {
|
||||
if (!this->handleRewind(fInIco)) {
|
||||
return kCouldNotRewind;
|
||||
}
|
||||
if (opts.fSubset) {
|
||||
// Subsets are not supported.
|
||||
return kUnimplemented;
|
||||
}
|
||||
if (dstInfo.dimensions() != this->getInfo().dimensions()) {
|
||||
SkCodecPrintf("Error: scaling not supported.\n");
|
||||
return kInvalidScale;
|
||||
}
|
||||
if (!conversion_possible(dstInfo, this->getInfo())) {
|
||||
SkCodecPrintf("Error: cannot convert input type to output type.\n");
|
||||
return kInvalidConversion;
|
||||
}
|
||||
|
||||
// Create the color table if necessary and prepare the stream for decode
|
||||
// Note that if it is non-NULL, inputColorCount will be modified
|
||||
if (!this->createColorTable(dstInfo.alphaType(), inputColorCount)) {
|
||||
SkCodecPrintf("Error: could not create color table.\n");
|
||||
return kInvalidInput;
|
||||
}
|
||||
|
||||
// Copy the color table to the client if necessary
|
||||
copy_color_table(dstInfo, fColorTable, inputColorPtr, inputColorCount);
|
||||
|
||||
// Initialize a swizzler if necessary
|
||||
if (!this->initializeSwizzler(dstInfo, opts)) {
|
||||
SkCodecPrintf("Error: cannot initialize swizzler.\n");
|
||||
return kInvalidConversion;
|
||||
}
|
||||
|
||||
return this->decode(dstInfo, dst, dstRowBytes, opts);
|
||||
}
|
||||
|
||||
/*
|
||||
* Process the color table for the bmp input
|
||||
*/
|
||||
bool SkBmpStandardCodec::createColorTable(SkAlphaType alphaType, int* numColors) {
|
||||
// Allocate memory for color table
|
||||
uint32_t colorBytes = 0;
|
||||
SkPMColor colorTable[256];
|
||||
if (this->bitsPerPixel() <= 8) {
|
||||
// Inform the caller of the number of colors
|
||||
uint32_t maxColors = 1 << this->bitsPerPixel();
|
||||
if (NULL != numColors) {
|
||||
// We set the number of colors to maxColors in order to ensure
|
||||
// safe memory accesses. Otherwise, an invalid pixel could
|
||||
// access memory outside of our color table array.
|
||||
*numColors = maxColors;
|
||||
}
|
||||
|
||||
// Read the color table from the stream
|
||||
colorBytes = fNumColors * fBytesPerColor;
|
||||
SkAutoTDeleteArray<uint8_t> cBuffer(SkNEW_ARRAY(uint8_t, colorBytes));
|
||||
if (stream()->read(cBuffer.get(), colorBytes) != colorBytes) {
|
||||
SkCodecPrintf("Error: unable to read color table.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Choose the proper packing function
|
||||
SkPMColor (*packARGB) (uint32_t, uint32_t, uint32_t, uint32_t);
|
||||
switch (alphaType) {
|
||||
case kOpaque_SkAlphaType:
|
||||
case kUnpremul_SkAlphaType:
|
||||
packARGB = &SkPackARGB32NoCheck;
|
||||
break;
|
||||
case kPremul_SkAlphaType:
|
||||
packARGB = &SkPreMultiplyARGB;
|
||||
break;
|
||||
default:
|
||||
// This should not be reached because conversion possible
|
||||
// should fail if the alpha type is not one of the above
|
||||
// values.
|
||||
SkASSERT(false);
|
||||
packARGB = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
// Fill in the color table
|
||||
uint32_t i = 0;
|
||||
for (; i < fNumColors; i++) {
|
||||
uint8_t blue = get_byte(cBuffer.get(), i*fBytesPerColor);
|
||||
uint8_t green = get_byte(cBuffer.get(), i*fBytesPerColor + 1);
|
||||
uint8_t red = get_byte(cBuffer.get(), i*fBytesPerColor + 2);
|
||||
uint8_t alpha;
|
||||
if (kOpaque_SkAlphaType == alphaType) {
|
||||
alpha = 0xFF;
|
||||
} else {
|
||||
alpha = get_byte(cBuffer.get(), i*fBytesPerColor + 3);
|
||||
}
|
||||
colorTable[i] = packARGB(alpha, red, green, blue);
|
||||
}
|
||||
|
||||
// To avoid segmentation faults on bad pixel data, fill the end of the
|
||||
// color table with black. This is the same the behavior as the
|
||||
// chromium decoder.
|
||||
for (; i < maxColors; i++) {
|
||||
colorTable[i] = SkPackARGB32NoCheck(0xFF, 0, 0, 0);
|
||||
}
|
||||
|
||||
// Set the color table
|
||||
fColorTable.reset(SkNEW_ARGS(SkColorTable, (colorTable, maxColors)));
|
||||
}
|
||||
|
||||
// Bmp-in-Ico files do not use an offset to indicate where the pixel data
|
||||
// begins. Pixel data always begins immediately after the color table.
|
||||
if (!fInIco) {
|
||||
// Check that we have not read past the pixel array offset
|
||||
if(fOffset < colorBytes) {
|
||||
// This may occur on OS 2.1 and other old versions where the color
|
||||
// table defaults to max size, and the bmp tries to use a smaller
|
||||
// color table. This is invalid, and our decision is to indicate
|
||||
// an error, rather than try to guess the intended size of the
|
||||
// color table.
|
||||
SkCodecPrintf("Error: pixel data offset less than color table size.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// After reading the color table, skip to the start of the pixel array
|
||||
if (stream()->skip(fOffset - colorBytes) != fOffset - colorBytes) {
|
||||
SkCodecPrintf("Error: unable to skip to image data.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Return true on success
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkBmpStandardCodec::initializeSwizzler(const SkImageInfo& dstInfo,
|
||||
const Options& opts) {
|
||||
// Allocate space for a row buffer
|
||||
const size_t rowBytes = SkAlign4(compute_row_bytes(dstInfo.width(), this->bitsPerPixel()));
|
||||
fSrcBuffer.reset(SkNEW_ARRAY(uint8_t, rowBytes));
|
||||
|
||||
// Get swizzler configuration
|
||||
SkSwizzler::SrcConfig config;
|
||||
switch (this->bitsPerPixel()) {
|
||||
case 1:
|
||||
config = SkSwizzler::kIndex1;
|
||||
break;
|
||||
case 2:
|
||||
config = SkSwizzler::kIndex2;
|
||||
break;
|
||||
case 4:
|
||||
config = SkSwizzler::kIndex4;
|
||||
break;
|
||||
case 8:
|
||||
config = SkSwizzler::kIndex;
|
||||
break;
|
||||
case 24:
|
||||
config = SkSwizzler::kBGR;
|
||||
break;
|
||||
case 32:
|
||||
if (kOpaque_SkAlphaType == dstInfo.alphaType()) {
|
||||
config = SkSwizzler::kBGRX;
|
||||
} else {
|
||||
config = SkSwizzler::kBGRA;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
SkASSERT(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get a pointer to the color table if it exists
|
||||
const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
|
||||
|
||||
// Create swizzler
|
||||
fSwizzler.reset(SkSwizzler::CreateSwizzler(config,
|
||||
colorPtr, dstInfo, opts.fZeroInitialized));
|
||||
|
||||
if (NULL == fSwizzler.get()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Choose a fill for failures due to an incomplete image. We will use zero as
|
||||
* the default palette index, black for opaque images, and transparent for
|
||||
* non-opaque images.
|
||||
*/
|
||||
static uint32_t get_fill_color_or_index(uint16_t bitsPerPixels, SkAlphaType alphaType) {
|
||||
uint32_t fillColorOrIndex;
|
||||
switch (bitsPerPixels) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
fillColorOrIndex = 0;
|
||||
break;
|
||||
case 24:
|
||||
fillColorOrIndex = SK_ColorBLACK;
|
||||
break;
|
||||
case 32:
|
||||
if (kOpaque_SkAlphaType == alphaType) {
|
||||
fillColorOrIndex = SK_ColorBLACK;
|
||||
} else {
|
||||
fillColorOrIndex = SK_ColorTRANSPARENT;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
SkASSERT(false);
|
||||
return 0;
|
||||
}
|
||||
return fillColorOrIndex;
|
||||
}
|
||||
|
||||
/*
|
||||
* Performs the bitmap decoding for standard input format
|
||||
*/
|
||||
SkCodec::Result SkBmpStandardCodec::decode(const SkImageInfo& dstInfo,
|
||||
void* dst, size_t dstRowBytes,
|
||||
const Options& opts) {
|
||||
// Set constant values
|
||||
const int width = dstInfo.width();
|
||||
const int height = dstInfo.height();
|
||||
const size_t rowBytes = SkAlign4(compute_row_bytes(width, this->bitsPerPixel()));
|
||||
|
||||
// Iterate over rows of the image
|
||||
for (int y = 0; y < height; y++) {
|
||||
// Read a row of the input
|
||||
if (this->stream()->read(fSrcBuffer.get(), rowBytes) != rowBytes) {
|
||||
SkCodecPrintf("Warning: incomplete input stream.\n");
|
||||
// Fill the destination image on failure
|
||||
// Get the fill color/index and check if it is 0
|
||||
uint32_t fillColorOrIndex = get_fill_color_or_index(this->bitsPerPixel(),
|
||||
dstInfo.alphaType());
|
||||
bool zeroFill = (0 == fillColorOrIndex);
|
||||
|
||||
if (kNo_ZeroInitialized == opts.fZeroInitialized || !zeroFill) {
|
||||
// Get a pointer to the color table if it exists
|
||||
const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
|
||||
|
||||
void* dstStart = this->getDstStartRow(dst, dstRowBytes, y);
|
||||
SkSwizzler::Fill(dstStart, dstInfo, dstRowBytes, dstInfo.height() - y,
|
||||
fillColorOrIndex, colorPtr);
|
||||
}
|
||||
return kIncompleteInput;
|
||||
}
|
||||
|
||||
// Decode the row in destination format
|
||||
uint32_t row;
|
||||
if (SkBmpCodec::kTopDown_RowOrder == this->rowOrder()) {
|
||||
row = y;
|
||||
} else {
|
||||
row = height - 1 - y;
|
||||
}
|
||||
|
||||
void* dstRow = SkTAddOffset<void>(dst, row * dstRowBytes);
|
||||
fSwizzler->swizzle(dstRow, fSrcBuffer.get());
|
||||
}
|
||||
|
||||
// Finally, apply the AND mask for bmp-in-ico images
|
||||
if (fInIco) {
|
||||
// The AND mask is always 1 bit per pixel
|
||||
const size_t rowBytes = SkAlign4(compute_row_bytes(width, 1));
|
||||
|
||||
SkPMColor* dstPtr = (SkPMColor*) dst;
|
||||
for (int y = 0; y < height; y++) {
|
||||
// The srcBuffer will at least be large enough
|
||||
if (stream()->read(fSrcBuffer.get(), rowBytes) != rowBytes) {
|
||||
SkCodecPrintf("Warning: incomplete AND mask for bmp-in-ico.\n");
|
||||
return kIncompleteInput;
|
||||
}
|
||||
|
||||
int row;
|
||||
if (SkBmpCodec::kBottomUp_RowOrder == this->rowOrder()) {
|
||||
row = height - y - 1;
|
||||
} else {
|
||||
row = y;
|
||||
}
|
||||
|
||||
SkPMColor* dstRow =
|
||||
SkTAddOffset<SkPMColor>(dstPtr, row * dstRowBytes);
|
||||
|
||||
for (int x = 0; x < width; x++) {
|
||||
int quotient;
|
||||
int modulus;
|
||||
SkTDivMod(x, 8, "ient, &modulus);
|
||||
uint32_t shift = 7 - modulus;
|
||||
uint32_t alphaBit =
|
||||
(fSrcBuffer.get()[quotient] >> shift) & 0x1;
|
||||
dstRow[x] &= alphaBit - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finished decoding the entire image
|
||||
return kSuccess;
|
||||
}
|
69
src/codec/SkBmpStandardCodec.h
Normal file
69
src/codec/SkBmpStandardCodec.h
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkBmpCodec.h"
|
||||
#include "SkColorTable.h"
|
||||
#include "SkImageInfo.h"
|
||||
#include "SkSwizzler.h"
|
||||
#include "SkTypes.h"
|
||||
|
||||
/*
|
||||
* This class implements the decoding for bmp images that use "standard" modes,
|
||||
* which essentially means they do not contain bit masks or RLE codes.
|
||||
*/
|
||||
class SkBmpStandardCodec : public SkBmpCodec {
|
||||
public:
|
||||
|
||||
/*
|
||||
* Creates an instance of the decoder
|
||||
*
|
||||
* Called only by SkBmpCodec::NewFromStream
|
||||
* There should be no other callers despite this being public
|
||||
*
|
||||
* @param srcInfo contains the source width and height
|
||||
* @param stream the stream of encoded image data
|
||||
* @param bitsPerPixel the number of bits used to store each pixel
|
||||
* @param format the format of the bmp file
|
||||
* @param numColors the number of colors in the color table
|
||||
* @param bytesPerColor the number of bytes in the stream used to represent
|
||||
each color in the color table
|
||||
* @param offset the offset of the image pixel data from the end of the
|
||||
* headers
|
||||
* @param rowOrder indicates whether rows are ordered top-down or bottom-up
|
||||
*/
|
||||
SkBmpStandardCodec(const SkImageInfo& srcInfo, SkStream* stream,
|
||||
uint16_t bitsPerPixel, uint32_t numColors, uint32_t bytesPerColor,
|
||||
uint32_t offset, SkBmpCodec::RowOrder rowOrder, bool isIco);
|
||||
|
||||
protected:
|
||||
|
||||
Result onGetPixels(const SkImageInfo& dstInfo, void* dst,
|
||||
size_t dstRowBytes, const Options&, SkPMColor*,
|
||||
int*) override;
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
* Creates the color table
|
||||
* Sets colorCount to the new color count if it is non-NULL
|
||||
*/
|
||||
bool createColorTable(SkAlphaType alphaType, int* colorCount);
|
||||
|
||||
bool initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts);
|
||||
|
||||
Result decode(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& opts);
|
||||
|
||||
SkAutoTUnref<SkColorTable> fColorTable; // owned
|
||||
const uint32_t fNumColors;
|
||||
const uint32_t fBytesPerColor;
|
||||
const uint32_t fOffset;
|
||||
SkAutoTDelete<SkSwizzler> fSwizzler;
|
||||
SkAutoTDeleteArray<uint8_t> fSrcBuffer;
|
||||
const bool fInIco;
|
||||
|
||||
typedef SkBmpCodec INHERITED;
|
||||
};
|
@ -30,6 +30,28 @@
|
||||
#define COMPUTE_RESULT_ALPHA \
|
||||
SkSwizzler::GetResult(zeroAlpha, maxAlpha);
|
||||
|
||||
static inline bool valid_alpha(SkAlphaType dstAlpha, SkAlphaType srcAlpha) {
|
||||
// Check for supported alpha types
|
||||
if (srcAlpha != dstAlpha) {
|
||||
if (kOpaque_SkAlphaType == srcAlpha) {
|
||||
// If the source is opaque, we must decode to opaque
|
||||
return false;
|
||||
}
|
||||
|
||||
// The source is not opaque
|
||||
switch (dstAlpha) {
|
||||
case kPremul_SkAlphaType:
|
||||
case kUnpremul_SkAlphaType:
|
||||
// The source is not opaque, so either of these is okay
|
||||
break;
|
||||
default:
|
||||
// We cannot decode a non-opaque image to opaque (or unknown)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is a color table, get a pointer to the colors, otherwise return NULL
|
||||
*/
|
||||
@ -40,7 +62,6 @@ static const SkPMColor* get_color_ptr(SkColorTable* colorTable) {
|
||||
/*
|
||||
*
|
||||
* Copy the codec color table back to the client when kIndex8 color type is requested
|
||||
*
|
||||
*/
|
||||
static inline void copy_color_table(const SkImageInfo& dstInfo, SkColorTable* colorTable,
|
||||
SkPMColor* inputColorPtr, int* inputColorCount) {
|
||||
@ -53,27 +74,21 @@ static inline void copy_color_table(const SkImageInfo& dstInfo, SkColorTable* co
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Compute row bytes for an image using pixels per byte
|
||||
*
|
||||
*/
|
||||
static inline size_t compute_row_bytes_ppb(int width, uint32_t pixelsPerByte) {
|
||||
return (width + pixelsPerByte - 1) / pixelsPerByte;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Compute row bytes for an image using bytes per pixel
|
||||
*
|
||||
*/
|
||||
static inline size_t compute_row_bytes_bpp(int width, uint32_t bytesPerPixel) {
|
||||
return width * bytesPerPixel;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Compute row bytes for an image
|
||||
*
|
||||
*/
|
||||
static inline size_t compute_row_bytes(int width, uint32_t bitsPerPixel) {
|
||||
if (bitsPerPixel < 16) {
|
||||
@ -88,20 +103,16 @@ static inline size_t compute_row_bytes(int width, uint32_t bitsPerPixel) {
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Get a byte from a buffer
|
||||
* This method is unsafe, the caller is responsible for performing a check
|
||||
*
|
||||
*/
|
||||
static inline uint8_t get_byte(uint8_t* buffer, uint32_t i) {
|
||||
return buffer[i];
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Get a short from a buffer
|
||||
* This method is unsafe, the caller is responsible for performing a check
|
||||
*
|
||||
*/
|
||||
static inline uint16_t get_short(uint8_t* buffer, uint32_t i) {
|
||||
uint16_t result;
|
||||
@ -114,10 +125,8 @@ static inline uint16_t get_short(uint8_t* buffer, uint32_t i) {
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Get an int from a buffer
|
||||
* This method is unsafe, the caller is responsible for performing a check
|
||||
*
|
||||
*/
|
||||
static inline uint32_t get_int(uint8_t* buffer, uint32_t i) {
|
||||
uint32_t result;
|
||||
|
@ -397,24 +397,11 @@ static bool conversion_possible(const SkImageInfo& dst, const SkImageInfo& src)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for supported alpha types
|
||||
if (src.alphaType() != dst.alphaType()) {
|
||||
if (kOpaque_SkAlphaType == src.alphaType()) {
|
||||
// If the source is opaque, we must decode to opaque
|
||||
return false;
|
||||
}
|
||||
|
||||
// The source is not opaque
|
||||
switch (dst.alphaType()) {
|
||||
case kPremul_SkAlphaType:
|
||||
case kUnpremul_SkAlphaType:
|
||||
// The source is not opaque, so either of these is okay
|
||||
break;
|
||||
default:
|
||||
// We cannot decode a non-opaque image to opaque (or unknown)
|
||||
return false;
|
||||
}
|
||||
// Ensure the alpha type is valid
|
||||
if (!valid_alpha(dst.alphaType(), src.alphaType())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for supported color types
|
||||
switch (dst.colorType()) {
|
||||
case kN32_SkColorType:
|
||||
|
@ -4,6 +4,8 @@
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#ifndef SkMaskSwizzler_DEFINED
|
||||
#define SkMaskSwizzler_DEFINED
|
||||
|
||||
#include "SkMasks.h"
|
||||
#include "SkSwizzler.h"
|
||||
@ -19,37 +21,29 @@ class SkMaskSwizzler {
|
||||
public:
|
||||
|
||||
/*
|
||||
*
|
||||
* Create a new swizzler
|
||||
* @param masks Unowned pointer to helper class
|
||||
*
|
||||
*/
|
||||
static SkMaskSwizzler* CreateMaskSwizzler(const SkImageInfo& imageInfo,
|
||||
SkMasks* masks,
|
||||
uint32_t bitsPerPixel);
|
||||
|
||||
/*
|
||||
*
|
||||
* Swizzle a row
|
||||
*
|
||||
*/
|
||||
SkSwizzler::ResultAlpha swizzle(void* dst, const uint8_t* SK_RESTRICT src);
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
*
|
||||
* Row procedure used for swizzle
|
||||
*
|
||||
*/
|
||||
typedef SkSwizzler::ResultAlpha (*RowProc)(
|
||||
void* dstRow, const uint8_t* srcRow, int width,
|
||||
SkMasks* masks);
|
||||
|
||||
/*
|
||||
*
|
||||
* Constructor for mask swizzler
|
||||
*
|
||||
*/
|
||||
SkMaskSwizzler(const SkImageInfo& info, SkMasks* masks, RowProc proc);
|
||||
|
||||
@ -58,3 +52,5 @@ private:
|
||||
SkMasks* fMasks; // unowned
|
||||
const RowProc fRowProc;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -4,6 +4,9 @@
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#ifndef SkMasks_DEFINED
|
||||
#define SkMasks_DEFINED
|
||||
|
||||
#include "SkTypes.h"
|
||||
|
||||
/*
|
||||
@ -79,3 +82,5 @@ private:
|
||||
const MaskInfo fBlue;
|
||||
const MaskInfo fAlpha;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user