aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--bench/CodecBench.cpp5
-rw-r--r--bench/CodecBenchPriv.h1
-rw-r--r--bench/nanobench.cpp8
-rw-r--r--dm/DMSrcSink.cpp64
-rw-r--r--fuzz/fuzz.cpp43
-rw-r--r--gm/animatedGif.cpp3
-rw-r--r--gm/makecolorspace.cpp3
-rw-r--r--include/codec/SkAndroidCodec.h33
-rw-r--r--include/codec/SkCodec.h56
-rw-r--r--include/codec/SkEncodedInfo.h2
-rw-r--r--include/core/SkColorTable.h10
-rw-r--r--src/android/SkBitmapRegionCodec.cpp17
-rw-r--r--src/codec/SkBmpCodec.cpp8
-rw-r--r--src/codec/SkBmpCodec.h17
-rw-r--r--src/codec/SkBmpMaskCodec.cpp6
-rw-r--r--src/codec/SkBmpMaskCodec.h7
-rw-r--r--src/codec/SkBmpRLECodec.cpp21
-rw-r--r--src/codec/SkBmpRLECodec.h9
-rw-r--r--src/codec/SkBmpStandardCodec.cpp25
-rw-r--r--src/codec/SkBmpStandardCodec.h10
-rw-r--r--src/codec/SkCodec.cpp61
-rw-r--r--src/codec/SkCodecImageGenerator.cpp3
-rw-r--r--src/codec/SkCodecPriv.h19
-rw-r--r--src/codec/SkGifCodec.cpp44
-rw-r--r--src/codec/SkGifCodec.h10
-rw-r--r--src/codec/SkIcoCodec.cpp18
-rw-r--r--src/codec/SkIcoCodec.h8
-rw-r--r--src/codec/SkJpegCodec.cpp4
-rw-r--r--src/codec/SkJpegCodec.h6
-rw-r--r--src/codec/SkPngCodec.cpp23
-rw-r--r--src/codec/SkPngCodec.h10
-rw-r--r--src/codec/SkRawAdapterCodec.cpp4
-rw-r--r--src/codec/SkRawCodec.cpp1
-rw-r--r--src/codec/SkRawCodec.h2
-rw-r--r--src/codec/SkSampledCodec.cpp12
-rw-r--r--src/codec/SkWbmpCodec.cpp34
-rw-r--r--src/codec/SkWbmpCodec.h9
-rw-r--r--src/codec/SkWebpAdapterCodec.cpp3
-rw-r--r--src/codec/SkWebpCodec.cpp3
-rw-r--r--src/codec/SkWebpCodec.h3
-rw-r--r--tests/CodecAnimTest.cpp4
-rw-r--r--tests/CodecPartialTest.cpp2
-rw-r--r--tests/CodecPriv.h16
-rw-r--r--tests/CodecTest.cpp71
-rw-r--r--tests/GifTest.cpp54
-rw-r--r--third_party/gif/SkGifImageReader.cpp2
-rw-r--r--third_party/gif/SkGifImageReader.h8
47 files changed, 616 insertions, 166 deletions
diff --git a/bench/CodecBench.cpp b/bench/CodecBench.cpp
index f72294e246..a1788ee0cc 100644
--- a/bench/CodecBench.cpp
+++ b/bench/CodecBench.cpp
@@ -51,17 +51,20 @@ void CodecBench::onDelayedSetup() {
void CodecBench::onDraw(int n, SkCanvas* canvas) {
std::unique_ptr<SkCodec> codec;
+ SkPMColor colorTable[256];
+ int colorCount;
SkCodec::Options options;
if (FLAGS_zero_init) {
options.fZeroInitialized = SkCodec::kYes_ZeroInitialized;
}
for (int i = 0; i < n; i++) {
+ colorCount = 256;
codec.reset(SkCodec::NewFromData(fData));
#ifdef SK_DEBUG
const SkCodec::Result result =
#endif
codec->getPixels(fInfo, fPixelStorage.get(), fInfo.minRowBytes(),
- &options);
+ &options, colorTable, &colorCount);
SkASSERT(result == SkCodec::kSuccess
|| result == SkCodec::kIncompleteInput);
}
diff --git a/bench/CodecBenchPriv.h b/bench/CodecBenchPriv.h
index d0eead33e0..5028573ad2 100644
--- a/bench/CodecBenchPriv.h
+++ b/bench/CodecBenchPriv.h
@@ -19,7 +19,6 @@ inline const char* color_type_to_str(SkColorType colorType) {
case kGray_8_SkColorType:
return "Gray8";
case kIndex_8_SkColorType:
- SkASSERT(false);
return "Index8";
case kAlpha_8_SkColorType:
return "Alpha8";
diff --git a/bench/nanobench.cpp b/bench/nanobench.cpp
index e67415e88c..6c60aa81af 100644
--- a/bench/nanobench.cpp
+++ b/bench/nanobench.cpp
@@ -630,6 +630,7 @@ public:
if (!FLAGS_simpleCodec) {
fColorTypes.push_back(kRGB_565_SkColorType);
fColorTypes.push_back(kAlpha_8_SkColorType);
+ fColorTypes.push_back(kIndex_8_SkColorType);
fColorTypes.push_back(kGray_8_SkColorType);
}
}
@@ -858,8 +859,13 @@ public:
const size_t rowBytes = info.minRowBytes();
SkAutoMalloc storage(info.getSafeSize(rowBytes));
+ // Used if fCurrentColorType is kIndex_8_SkColorType
+ int colorCount = 256;
+ SkPMColor colors[256];
+
const SkCodec::Result result = codec->getPixels(
- info, storage.get(), rowBytes);
+ info, storage.get(), rowBytes, nullptr, colors,
+ &colorCount);
switch (result) {
case SkCodec::kSuccess:
case SkCodec::kIncompleteInput:
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index fb78d84023..0febd2abb0 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -343,9 +343,12 @@ static void premultiply_if_necessary(SkBitmap& bitmap) {
SkOpts::RGBA_to_rgbA(row, row, bitmap.width());
}
break;
- case kIndex_8_SkColorType:
- SkASSERT(false);
+ case kIndex_8_SkColorType: {
+ SkColorTable* colorTable = bitmap.getColorTable();
+ SkPMColor* colorPtr = const_cast<SkPMColor*>(colorTable->readColors());
+ SkOpts::RGBA_to_rgbA(colorPtr, colorPtr, colorTable->count());
break;
+ }
default:
// No need to premultiply kGray or k565 outputs.
break;
@@ -397,10 +400,11 @@ static bool get_decode_info(SkImageInfo* decodeInfo, SkColorType canvasColorType
}
static void draw_to_canvas(SkCanvas* canvas, const SkImageInfo& info, void* pixels, size_t rowBytes,
- CodecSrc::DstColorType dstColorType,
+ SkPMColor* colorPtr, int colorCount, CodecSrc::DstColorType dstColorType,
SkScalar left = 0, SkScalar top = 0) {
+ sk_sp<SkColorTable> colorTable(new SkColorTable(colorPtr, colorCount));
SkBitmap bitmap;
- bitmap.installPixels(info, pixels, rowBytes);
+ bitmap.installPixels(info, pixels, rowBytes, colorTable.get(), nullptr, nullptr);
premultiply_if_necessary(bitmap);
swap_rb_if_necessary(bitmap, dstColorType);
canvas->drawBitmap(bitmap, left, top);
@@ -453,6 +457,8 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
const size_t rowBytes = size.width() * bpp;
const size_t safeSize = decodeInfo.getSafeSize(rowBytes);
SkAutoMalloc pixels(safeSize);
+ SkPMColor colorPtr[256];
+ int colorCount = 256;
SkCodec::Options options;
options.fPremulBehavior = canvas->imageInfo().colorSpace() ?
@@ -498,7 +504,8 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
options.fPriorFrame = SkCodec::kNone;
}
SkCodec::Result result = codec->getPixels(decodeInfo, pixels.get(),
- rowBytes, &options);
+ rowBytes, &options,
+ colorPtr, &colorCount);
if (SkCodec::kInvalidInput == result && i > 0) {
// Some of our test images have truncated later frames. Treat that
// the same as incomplete.
@@ -524,7 +531,8 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
const int xTranslate = (i % factor) * decodeInfo.width();
const int yTranslate = (i / factor) * decodeInfo.height();
canvas->translate(SkIntToScalar(xTranslate), SkIntToScalar(yTranslate));
- draw_to_canvas(canvas, bitmapInfo, pixels.get(), rowBytes, fDstColorType);
+ draw_to_canvas(canvas, bitmapInfo, pixels.get(), rowBytes,
+ colorPtr, colorCount, fDstColorType);
if (result != SkCodec::kSuccess) {
return "";
}
@@ -546,7 +554,8 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
}
case kCodecZeroInit_Mode:
case kCodec_Mode: {
- switch (codec->getPixels(decodeInfo, pixels.get(), rowBytes, &options)) {
+ switch (codec->getPixels(decodeInfo, pixels.get(), rowBytes, &options,
+ colorPtr, &colorCount)) {
case SkCodec::kSuccess:
// We consider these to be valid, since we should still decode what is
// available.
@@ -558,7 +567,8 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
return SkStringPrintf("Couldn't getPixels %s.", fPath.c_str());
}
- draw_to_canvas(canvas, bitmapInfo, pixels.get(), rowBytes, fDstColorType);
+ draw_to_canvas(canvas, bitmapInfo, pixels.get(), rowBytes, colorPtr, colorCount,
+ fDstColorType);
break;
}
case kScanline_Mode: {
@@ -579,7 +589,7 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
bool useOldScanlineMethod = !useIncremental && !ico;
if (useIncremental || ico) {
if (SkCodec::kSuccess == codec->startIncrementalDecode(decodeInfo, dst,
- rowBytes, &options)) {
+ rowBytes, &options, colorPtr, &colorCount)) {
int rowsDecoded;
auto result = codec->incrementalDecode(&rowsDecoded);
if (SkCodec::kIncompleteInput == result || SkCodec::kErrorInInput == result) {
@@ -599,7 +609,8 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
}
if (useOldScanlineMethod) {
- if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo)) {
+ if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, NULL, colorPtr,
+ &colorCount)) {
return "Could not start scanline decoder";
}
@@ -613,7 +624,7 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
}
}
- draw_to_canvas(canvas, bitmapInfo, dst, rowBytes, fDstColorType);
+ draw_to_canvas(canvas, bitmapInfo, dst, rowBytes, colorPtr, colorCount, fDstColorType);
break;
}
case kStripe_Mode: {
@@ -625,7 +636,8 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
void* dst = pixels.get();
// Decode odd stripes
- if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, &options)) {
+ if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, &options, colorPtr,
+ &colorCount)) {
return "Could not start scanline decoder";
}
@@ -650,7 +662,8 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
}
// Decode even stripes
- const SkCodec::Result startResult = codec->startScanlineDecode(decodeInfo);
+ const SkCodec::Result startResult = codec->startScanlineDecode(decodeInfo, nullptr,
+ colorPtr, &colorCount);
if (SkCodec::kSuccess != startResult) {
return "Failed to restart scanline decoder with same parameters.";
}
@@ -668,7 +681,7 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
}
}
- draw_to_canvas(canvas, bitmapInfo, dst, rowBytes, fDstColorType);
+ draw_to_canvas(canvas, bitmapInfo, dst, rowBytes, colorPtr, colorCount, fDstColorType);
break;
}
case kCroppedScanline_Mode: {
@@ -682,14 +695,16 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
for (int x = 0; x < width; x += tileSize) {
subset = SkIRect::MakeXYWH(x, 0, SkTMin(tileSize, width - x), height);
options.fSubset = &subset;
- if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, &options)) {
+ if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, &options,
+ colorPtr, &colorCount)) {
return "Could not start scanline decoder.";
}
codec->getScanlines(SkTAddOffset<void>(pixels.get(), x * bpp), height, rowBytes);
}
- draw_to_canvas(canvas, bitmapInfo, pixels.get(), rowBytes, fDstColorType);
+ draw_to_canvas(canvas, bitmapInfo, pixels.get(), rowBytes, colorPtr, colorCount,
+ fDstColorType);
break;
}
case kSubset_Mode: {
@@ -732,7 +747,7 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
SkImageInfo subsetBitmapInfo = bitmapInfo.makeWH(scaledW, scaledH);
size_t subsetRowBytes = subsetBitmapInfo.minRowBytes();
const SkCodec::Result result = codec->getPixels(decodeInfo, dst, subsetRowBytes,
- &options);
+ &options, colorPtr, &colorCount);
switch (result) {
case SkCodec::kSuccess:
case SkCodec::kErrorInInput:
@@ -744,8 +759,9 @@ Error CodecSrc::draw(SkCanvas* canvas) const {
x, y, decodeInfo.width(), decodeInfo.height(),
fPath.c_str(), W, H, result);
}
- draw_to_canvas(canvas, subsetBitmapInfo, dst, subsetRowBytes, fDstColorType,
- SkIntToScalar(left), SkIntToScalar(top));
+ draw_to_canvas(canvas, subsetBitmapInfo, dst, subsetRowBytes, colorPtr,
+ colorCount, fDstColorType, SkIntToScalar(left),
+ SkIntToScalar(top));
// translate by the scaled height.
top += decodeInfo.height();
@@ -849,6 +865,8 @@ Error AndroidCodecSrc::draw(SkCanvas* canvas) const {
int bpp = SkColorTypeBytesPerPixel(decodeInfo.colorType());
size_t rowBytes = size.width() * bpp;
SkAutoMalloc pixels(size.height() * rowBytes);
+ SkPMColor colorPtr[256];
+ int colorCount = 256;
SkBitmap bitmap;
SkImageInfo bitmapInfo = decodeInfo;
@@ -860,6 +878,8 @@ Error AndroidCodecSrc::draw(SkCanvas* canvas) const {
// Create options for the codec.
SkAndroidCodec::AndroidOptions options;
+ options.fColorPtr = colorPtr;
+ options.fColorCount = &colorCount;
options.fSampleSize = fSampleSize;
switch (codec->getAndroidPixels(decodeInfo, pixels.get(), rowBytes, &options)) {
@@ -870,7 +890,7 @@ Error AndroidCodecSrc::draw(SkCanvas* canvas) const {
default:
return SkStringPrintf("Couldn't getPixels %s.", fPath.c_str());
}
- draw_to_canvas(canvas, bitmapInfo, pixels.get(), rowBytes, fDstColorType);
+ draw_to_canvas(canvas, bitmapInfo, pixels.get(), rowBytes, colorPtr, colorCount, fDstColorType);
return "";
}
@@ -989,8 +1009,10 @@ Error ImageGenSrc::draw(SkCanvas* canvas) const {
return err;
}
+ SkPMColor colorPtr[256];
+ int colorCount = 256;
set_bitmap_color_space(&decodeInfo);
- draw_to_canvas(canvas, decodeInfo, pixels.get(), rowBytes,
+ draw_to_canvas(canvas, decodeInfo, pixels.get(), rowBytes, colorPtr, colorCount,
CodecSrc::kGetFromCanvas_DstColorType);
return "";
}
diff --git a/fuzz/fuzz.cpp b/fuzz/fuzz.cpp
index d6109b3f46..d451481049 100644
--- a/fuzz/fuzz.cpp
+++ b/fuzz/fuzz.cpp
@@ -202,14 +202,32 @@ static void fuzz_img(sk_sp<SkData> bytes, uint8_t scale, uint8_t mode) {
}
SkImageInfo decodeInfo = codec->getInfo();
+ if (4 == mode && decodeInfo.colorType() == kIndex_8_SkColorType) {
+ // 4 means animated. Frames beyond the first cannot be decoded to
+ // index 8.
+ decodeInfo = decodeInfo.makeColorType(kN32_SkColorType);
+ }
+
SkISize size = codec->getScaledDimensions(fscale);
decodeInfo = decodeInfo.makeWH(size.width(), size.height());
+ // Construct a color table for the decode if necessary
+ sk_sp<SkColorTable> colorTable(nullptr);
+ SkPMColor* colorPtr = nullptr;
+ int* colorCountPtr = nullptr;
+ int maxColors = 256;
+ if (kIndex_8_SkColorType == decodeInfo.colorType()) {
+ SkPMColor colors[256];
+ colorTable.reset(new SkColorTable(colors, maxColors));
+ colorPtr = const_cast<SkPMColor*>(colorTable->readColors());
+ colorCountPtr = &maxColors;
+ }
+
SkBitmap bitmap;
SkCodec::Options options;
options.fZeroInitialized = SkCodec::kYes_ZeroInitialized;
- if (!bitmap.tryAllocPixels(decodeInfo, nullptr, SkBitmap::kZeroPixels_AllocFlag)) {
+ if (!bitmap.tryAllocPixels(decodeInfo, colorTable, SkBitmap::kZeroPixels_AllocFlag)) {
SkDebugf("[terminated] Could not allocate memory. Image might be too large (%d x %d)",
decodeInfo.width(), decodeInfo.height());
return;
@@ -217,7 +235,8 @@ static void fuzz_img(sk_sp<SkData> bytes, uint8_t scale, uint8_t mode) {
switch (mode) {
case 0: {//kCodecZeroInit_Mode, kCodec_Mode
- switch (codec->getPixels(decodeInfo, bitmap.getPixels(), bitmap.rowBytes(), &options)) {
+ switch (codec->getPixels(decodeInfo, bitmap.getPixels(), bitmap.rowBytes(), &options,
+ colorPtr, colorCountPtr)) {
case SkCodec::kSuccess:
SkDebugf("[terminated] Success!\n");
break;
@@ -238,10 +257,11 @@ static void fuzz_img(sk_sp<SkData> bytes, uint8_t scale, uint8_t mode) {
break;
}
case 1: {//kScanline_Mode
- if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo)) {
- SkDebugf("[terminated] Could not start scanline decoder\n");
- return;
- }
+ if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, NULL, colorPtr,
+ colorCountPtr)) {
+ SkDebugf("[terminated] Could not start scanline decoder\n");
+ return;
+ }
void* dst = bitmap.getAddr(0, 0);
size_t rowBytes = bitmap.rowBytes();
@@ -265,7 +285,8 @@ static void fuzz_img(sk_sp<SkData> bytes, uint8_t scale, uint8_t mode) {
const int numStripes = (height + stripeHeight - 1) / stripeHeight;
// Decode odd stripes
- if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo)
+ if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, NULL, colorPtr,
+ colorCountPtr)
|| SkCodec::kTopDown_SkScanlineOrder != codec->getScanlineOrder()) {
// This mode was designed to test the new skip scanlines API in libjpeg-turbo.
// Jpegs have kTopDown_SkScanlineOrder, and at this time, it is not interesting
@@ -288,7 +309,8 @@ static void fuzz_img(sk_sp<SkData> bytes, uint8_t scale, uint8_t mode) {
}
// Decode even stripes
- const SkCodec::Result startResult = codec->startScanlineDecode(decodeInfo);
+ const SkCodec::Result startResult = codec->startScanlineDecode(decodeInfo, nullptr,
+ colorPtr, colorCountPtr);
if (SkCodec::kSuccess != startResult) {
SkDebugf("[terminated] Failed to restart scanline decoder with same parameters.\n");
return;
@@ -347,12 +369,13 @@ static void fuzz_img(sk_sp<SkData> bytes, uint8_t scale, uint8_t mode) {
SkTMax(1, SkScalarRoundToInt(preScaleW * fscale)),
SkTMax(1, SkScalarRoundToInt(preScaleH * fscale)));
size_t rowBytes = decodeInfo.minRowBytes();
- if (!subsetBm.installPixels(decodeInfo, pixels, rowBytes)) {
+ if (!subsetBm.installPixels(decodeInfo, pixels, rowBytes, colorTable.get(),
+ nullptr, nullptr)) {
SkDebugf("[terminated] Could not install pixels.\n");
return;
}
const SkCodec::Result result = codec->getPixels(decodeInfo, pixels, rowBytes,
- &opts);
+ &opts, colorPtr, colorCountPtr);
switch (result) {
case SkCodec::kSuccess:
case SkCodec::kIncompleteInput:
diff --git a/gm/animatedGif.cpp b/gm/animatedGif.cpp
index fc5217fa8d..80bcdd9ee2 100644
--- a/gm/animatedGif.cpp
+++ b/gm/animatedGif.cpp
@@ -66,7 +66,8 @@ private:
}
if (SkCodec::kSuccess != fCodec->getPixels(info, bm.getPixels(),
- bm.rowBytes(), &opts)) {
+ bm.rowBytes(), &opts,
+ nullptr, nullptr)) {
SkDebugf("Could not getPixels for frame %i: %s", frameIndex, FLAGS_animatedGif[0]);
return;
}
diff --git a/gm/makecolorspace.cpp b/gm/makecolorspace.cpp
index b3b937ec40..af0b3d4b19 100644
--- a/gm/makecolorspace.cpp
+++ b/gm/makecolorspace.cpp
@@ -22,7 +22,8 @@ sk_sp<SkImage> make_raster_image(const char* path, SkTransferFunctionBehavior be
SkCodec::Options opts;
opts.fPremulBehavior = behavior;
- codec->getPixels(codec->getInfo(), bitmap.getPixels(), bitmap.rowBytes(), &opts);
+ codec->getPixels(codec->getInfo(), bitmap.getPixels(), bitmap.rowBytes(), &opts,
+ nullptr, nullptr);
return SkImage::MakeFromBitmap(bitmap);
}
diff --git a/include/codec/SkAndroidCodec.h b/include/codec/SkAndroidCodec.h
index b4dd3854e6..87d514dfff 100644
--- a/include/codec/SkAndroidCodec.h
+++ b/include/codec/SkAndroidCodec.h
@@ -57,7 +57,7 @@ public:
* @param requestedColorType Color type requested by the client
*
* |requestedColorType| may be overriden. We will default to kF16
- * for high precision images.
+ * for high precision images and kIndex8 for GIF and WBMP.
*
* In the general case, if it is possible to decode to
* |requestedColorType|, this returns |requestedColorType|.
@@ -154,6 +154,8 @@ public:
AndroidOptions()
: fZeroInitialized(SkCodec::kNo_ZeroInitialized)
, fSubset(nullptr)
+ , fColorPtr(nullptr)
+ , fColorCount(nullptr)
, fSampleSize(1)
{}
@@ -177,6 +179,22 @@ public:
SkIRect* fSubset;
/**
+ * If the client has requested a decode to kIndex8_SkColorType
+ * (specified in the SkImageInfo), then the caller must provide
+ * storage for up to 256 SkPMColor values in fColorPtr. On success,
+ * the codec must copy N colors into that storage, (where N is the
+ * logical number of table entries) and set fColorCount to N.
+ *
+ * If the client does not request kIndex8_SkColorType, then the last
+ * two parameters may be NULL. If fColorCount is not null, it will be
+ * set to 0.
+ *
+ * The default is NULL for both pointers.
+ */
+ SkPMColor* fColorPtr;
+ int* fColorCount;
+
+ /**
* The client may provide an integer downscale factor for the decode.
* The codec may implement this downscaling by sampling or another
* method if it is more efficient.
@@ -206,6 +224,14 @@ public:
* to scale or subset. If the codec cannot perform this
* scaling or subsetting, it will return an error code.
*
+ * If info is kIndex8_SkColorType, then the caller must provide storage for up to 256
+ * SkPMColor values in options->fColorPtr. On success the codec must copy N colors into
+ * that storage, (where N is the logical number of table entries) and set
+ * options->fColorCount to N.
+ *
+ * If info is not kIndex8_SkColorType, options->fColorPtr and options->fColorCount may
+ * be nullptr.
+ *
* The AndroidOptions object is also used to specify any requested scaling or subsetting
* using options->fSampleSize and options->fSubset. If NULL, the defaults (as specified above
* for AndroidOptions) are used.
@@ -221,7 +247,10 @@ public:
/**
* Simplified version of getAndroidPixels() where we supply the default AndroidOptions as
- * specified above for AndroidOptions. It will not perform any scaling or subsetting.
+ * specified above for AndroidOptions.
+ *
+ * This will return an error if the info is kIndex_8_SkColorType and also will not perform
+ * any scaling or subsetting.
*/
SkCodec::Result getAndroidPixels(const SkImageInfo& info, void* pixels, size_t rowBytes);
diff --git a/include/codec/SkCodec.h b/include/codec/SkCodec.h
index b78ec69c74..2043d230b1 100644
--- a/include/codec/SkCodec.h
+++ b/include/codec/SkCodec.h
@@ -338,19 +338,26 @@ public:
* reported by the codec, the color space transformation is
* a no-op.
*
+ * If info is kIndex8_SkColorType, then the caller must provide storage for up to 256
+ * SkPMColor values in ctable. On success the generator must copy N colors into that storage,
+ * (where N is the logical number of table entries) and set ctableCount to N.
+ *
+ * If info is not kIndex8_SkColorType, then the last two parameters may be NULL. If ctableCount
+ * is not null, it will be set to 0.
+ *
* If a scanline decode is in progress, scanline mode will end, requiring the client to call
* startScanlineDecode() in order to return to decoding scanlines.
*
* @return Result kSuccess, or another value explaining the type of failure.
*/
- Result getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, const Options*);
+ Result getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, const Options*,
+ SkPMColor ctable[], int* ctableCount);
/**
- * Simplified version of getPixels() that uses the default Options.
+ * Simplified version of getPixels() that asserts that info is NOT kIndex8_SkColorType and
+ * uses the default Options.
*/
- Result getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes) {
- return this->getPixels(info, pixels, rowBytes, nullptr);
- }
+ Result getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes);
/**
* If decoding to YUV is supported, this returns true. Otherwise, this
@@ -403,13 +410,25 @@ public:
* if present, or the full image as described in dstInfo.
* @param options Contains decoding options, including if memory is zero
* initialized and whether to decode a subset.
+ * @param ctable A pointer to a color table. When dstInfo.colorType() is
+ * kIndex8, this should be non-NULL and have enough storage for 256
+ * colors. The color table will be populated after decoding the palette.
+ * @param ctableCount A pointer to the size of the color table. When
+ * dstInfo.colorType() is kIndex8, this should be non-NULL. It will
+ * be modified to the true size of the color table (<= 256) after
+ * decoding the palette.
* @return Enum representing success or reason for failure.
*/
Result startIncrementalDecode(const SkImageInfo& dstInfo, void* dst, size_t rowBytes,
- const Options*);
+ const Options*, SkPMColor* ctable, int* ctableCount);
+
+ Result startIncrementalDecode(const SkImageInfo& dstInfo, void* dst, size_t rowBytes,
+ const Options* options) {
+ return this->startIncrementalDecode(dstInfo, dst, rowBytes, options, nullptr, nullptr);
+ }
Result startIncrementalDecode(const SkImageInfo& dstInfo, void* dst, size_t rowBytes) {
- return this->startIncrementalDecode(dstInfo, dst, rowBytes, nullptr);
+ return this->startIncrementalDecode(dstInfo, dst, rowBytes, nullptr, nullptr, nullptr);
}
/**
@@ -460,16 +479,23 @@ public:
* those of getInfo, this implies a scale.
* @param options Contains decoding options, including if memory is zero
* initialized.
+ * @param ctable A pointer to a color table. When dstInfo.colorType() is
+ * kIndex8, this should be non-NULL and have enough storage for 256
+ * colors. The color table will be populated after decoding the palette.
+ * @param ctableCount A pointer to the size of the color table. When
+ * dstInfo.colorType() is kIndex8, this should be non-NULL. It will
+ * be modified to the true size of the color table (<= 256) after
+ * decoding the palette.
* @return Enum representing success or reason for failure.
*/
- Result startScanlineDecode(const SkImageInfo& dstInfo, const Options* options);
+ Result startScanlineDecode(const SkImageInfo& dstInfo, const Options* options,
+ SkPMColor ctable[], int* ctableCount);
/**
- * Simplified version of startScanlineDecode() that uses the default Options.
+ * Simplified version of startScanlineDecode() that asserts that info is NOT
+ * kIndex8_SkColorType and uses the default Options.
*/
- Result startScanlineDecode(const SkImageInfo& dstInfo) {
- return this->startScanlineDecode(dstInfo, nullptr);
- }
+ Result startScanlineDecode(const SkImageInfo& dstInfo);
/**
* Write the next countLines scanlines into dst.
@@ -713,6 +739,7 @@ protected:
*/
virtual Result onGetPixels(const SkImageInfo& info,
void* pixels, size_t rowBytes, const Options&,
+ SkPMColor ctable[], int* ctableCount,
int* rowsDecoded) = 0;
virtual bool onQueryYUV8(SkYUVSizeInfo*, SkYUVColorSpace*) const {
@@ -774,6 +801,7 @@ protected:
* kN32_SkColorType: Transparent or Black, depending on the src alpha type
* kRGB_565_SkColorType: Black
* kGray_8_SkColorType: Black
+ * kIndex_8_SkColorType: First color in color table
*/
virtual uint64_t onGetFillValue(const SkImageInfo& dstInfo) const;
@@ -873,12 +901,12 @@ private:
// Methods for scanline decoding.
virtual Result onStartScanlineDecode(const SkImageInfo& /*dstInfo*/,
- const Options& /*options*/) {
+ const Options& /*options*/, SkPMColor* /*ctable*/, int* /*ctableCount*/) {
return kUnimplemented;
}
virtual Result onStartIncrementalDecode(const SkImageInfo& /*dstInfo*/, void*, size_t,
- const Options&) {
+ const Options&, SkPMColor*, int*) {
return kUnimplemented;
}
diff --git a/include/codec/SkEncodedInfo.h b/include/codec/SkEncodedInfo.h
index 3b1ce48713..eb8c147a3b 100644
--- a/include/codec/SkEncodedInfo.h
+++ b/include/codec/SkEncodedInfo.h
@@ -130,7 +130,7 @@ public:
case kPalette_Color: {
SkAlphaType alphaType = (kOpaque_Alpha == fAlpha) ? kOpaque_SkAlphaType :
kUnpremul_SkAlphaType;
- return SkImageInfo::Make(width, height, kN32_SkColorType,
+ return SkImageInfo::Make(width, height, kIndex_8_SkColorType,
alphaType, colorSpace);
}
case kRGB_Color:
diff --git a/include/core/SkColorTable.h b/include/core/SkColorTable.h
index ca5efd1fe1..40919d3bac 100644
--- a/include/core/SkColorTable.h
+++ b/include/core/SkColorTable.h
@@ -71,6 +71,16 @@ private:
void init(const SkPMColor* colors, int count);
friend class SkImageGenerator;
+ friend class SkBitmapRegionCodec;
+ // Only call if no other thread or cache has seen this table.
+ void dangerous_overwriteColors(const SkPMColor newColors[], int count) {
+ if (count < 0 || count > fCount) {
+ sk_throw();
+ }
+ // assumes that f16BitCache nas NOT been initialized yet, so we don't try to update it
+ memcpy(fColors, newColors, count * sizeof(SkPMColor));
+ fCount = count; // update fCount, in case count is smaller
+ }
typedef SkRefCnt INHERITED;
};
diff --git a/src/android/SkBitmapRegionCodec.cpp b/src/android/SkBitmapRegionCodec.cpp
index c4e1ea124c..7ce9d26b4d 100644
--- a/src/android/SkBitmapRegionCodec.cpp
+++ b/src/android/SkBitmapRegionCodec.cpp
@@ -57,7 +57,13 @@ bool SkBitmapRegionCodec::decodeRegion(SkBitmap* bitmap, SkBRDAllocator* allocat
SkImageInfo decodeInfo = SkImageInfo::Make(scaledSize.width(), scaledSize.height(),
dstColorType, dstAlphaType, dstColorSpace);
- SkASSERT(dstColorType != kIndex_8_SkColorType);
+ // Construct a color table for the decode if necessary
+ sk_sp<SkColorTable> colorTable(nullptr);
+ int maxColors = 256;
+ SkPMColor colors[256];
+ if (kIndex_8_SkColorType == dstColorType) {
+ colorTable.reset(new SkColorTable(colors, maxColors));
+ }
// Initialize the destination bitmap
int scaledOutX = 0;
@@ -84,7 +90,7 @@ bool SkBitmapRegionCodec::decodeRegion(SkBitmap* bitmap, SkBRDAllocator* allocat
outInfo = outInfo.makeColorType(kAlpha_8_SkColorType).makeAlphaType(kPremul_SkAlphaType);
}
bitmap->setInfo(outInfo);
- if (!bitmap->tryAllocPixels(allocator, nullptr)) {
+ if (!bitmap->tryAllocPixels(allocator, colorTable.get())) {
SkCodecPrintf("Error: Could not allocate pixels.\n");
return false;
}
@@ -106,6 +112,8 @@ bool SkBitmapRegionCodec::decodeRegion(SkBitmap* bitmap, SkBRDAllocator* allocat
SkAndroidCodec::AndroidOptions options;
options.fSampleSize = sampleSize;
options.fSubset = &subset;
+ options.fColorPtr = colors;
+ options.fColorCount = &maxColors;
options.fZeroInitialized = zeroInit;
void* dst = bitmap->getAddr(scaledOutX, scaledOutY);
@@ -116,6 +124,11 @@ bool SkBitmapRegionCodec::decodeRegion(SkBitmap* bitmap, SkBRDAllocator* allocat
return false;
}
+ // Intialize the color table
+ if (kIndex_8_SkColorType == dstColorType) {
+ colorTable->dangerous_overwriteColors(colors, maxColors);
+ }
+
return true;
}
diff --git a/src/codec/SkBmpCodec.cpp b/src/codec/SkBmpCodec.cpp
index 3142f1e271..c21a863419 100644
--- a/src/codec/SkBmpCodec.cpp
+++ b/src/codec/SkBmpCodec.cpp
@@ -622,19 +622,19 @@ int32_t SkBmpCodec::getDstRow(int32_t y, int32_t height) const {
}
SkCodec::Result SkBmpCodec::prepareToDecode(const SkImageInfo& dstInfo,
- const SkCodec::Options& options) {
+ const SkCodec::Options& options, SkPMColor inputColorPtr[], int* inputColorCount) {
if (!conversion_possible(dstInfo, this->getInfo()) ||
!this->initializeColorXform(dstInfo, options.fPremulBehavior))
{
return kInvalidConversion;
}
- return this->onPrepareToDecode(dstInfo, options);
+ return this->onPrepareToDecode(dstInfo, options, inputColorPtr, inputColorCount);
}
SkCodec::Result SkBmpCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
- const SkCodec::Options& options) {
- return prepareToDecode(dstInfo, options);
+ const SkCodec::Options& options, SkPMColor inputColorPtr[], int* inputColorCount) {
+ return prepareToDecode(dstInfo, options, inputColorPtr, inputColorCount);
}
int SkBmpCodec::onGetScanlines(void* dst, int count, size_t rowBytes) {
diff --git a/src/codec/SkBmpCodec.h b/src/codec/SkBmpCodec.h
index cf1460582d..e956a9fc40 100644
--- a/src/codec/SkBmpCodec.h
+++ b/src/codec/SkBmpCodec.h
@@ -91,11 +91,20 @@ protected:
* @param dstInfo Contains output information. Height specifies
* the total number of rows that will be decoded.
* @param options Additonal options to pass to the decoder.
+ * @param inputColorPtr Client-provided memory for a color table. Must
+ * be enough for 256 colors. This will be
+ * populated with colors if the encoded image uses
+ * a color table.
+ * @param inputColorCount If the encoded image uses a color table, this
+ * will be set to the number of colors in the
+ * color table.
*/
virtual SkCodec::Result onPrepareToDecode(const SkImageInfo& dstInfo,
- const SkCodec::Options& options) = 0;
+ const SkCodec::Options& options, SkPMColor inputColorPtr[],
+ int* inputColorCount) = 0;
SkCodec::Result prepareToDecode(const SkImageInfo& dstInfo,
- const SkCodec::Options& options);
+ const SkCodec::Options& options, SkPMColor inputColorPtr[],
+ int* inputColorCount);
uint32_t* xformBuffer() const { return fXformBuffer.get(); }
void resetXformBuffer(int count) { fXformBuffer.reset(new uint32_t[count]); }
@@ -135,8 +144,8 @@ private:
virtual bool skipRows(int count);
- Result onStartScanlineDecode(const SkImageInfo& dstInfo,
- const SkCodec::Options&) override;
+ Result onStartScanlineDecode(const SkImageInfo& dstInfo, const SkCodec::Options&,
+ SkPMColor inputColorPtr[], int* inputColorCount) override;
int onGetScanlines(void* dst, int count, size_t rowBytes) override;
diff --git a/src/codec/SkBmpMaskCodec.cpp b/src/codec/SkBmpMaskCodec.cpp
index 51060e5e99..1fc98a2909 100644
--- a/src/codec/SkBmpMaskCodec.cpp
+++ b/src/codec/SkBmpMaskCodec.cpp
@@ -26,6 +26,8 @@ SkBmpMaskCodec::SkBmpMaskCodec(int width, int height, const SkEncodedInfo& info,
SkCodec::Result SkBmpMaskCodec::onGetPixels(const SkImageInfo& dstInfo,
void* dst, size_t dstRowBytes,
const Options& opts,
+ SkPMColor* inputColorPtr,
+ int* inputColorCount,
int* rowsDecoded) {
if (opts.fSubset) {
// Subsets are not supported.
@@ -36,7 +38,7 @@ SkCodec::Result SkBmpMaskCodec::onGetPixels(const SkImageInfo& dstInfo,
return kInvalidScale;
}
- Result result = this->prepareToDecode(dstInfo, opts);
+ Result result = this->prepareToDecode(dstInfo, opts, inputColorPtr, inputColorCount);
if (kSuccess != result) {
return result;
}
@@ -50,7 +52,7 @@ SkCodec::Result SkBmpMaskCodec::onGetPixels(const SkImageInfo& dstInfo,
}
SkCodec::Result SkBmpMaskCodec::onPrepareToDecode(const SkImageInfo& dstInfo,
- const SkCodec::Options& options) {
+ const SkCodec::Options& options, SkPMColor inputColorPtr[], int* inputColorCount) {
if (this->colorXform()) {
this->resetXformBuffer(dstInfo.width());
}
diff --git a/src/codec/SkBmpMaskCodec.h b/src/codec/SkBmpMaskCodec.h
index 8d8d64c168..2f8c060c7a 100644
--- a/src/codec/SkBmpMaskCodec.h
+++ b/src/codec/SkBmpMaskCodec.h
@@ -38,11 +38,12 @@ public:
protected:
Result onGetPixels(const SkImageInfo& dstInfo, void* dst,
- size_t dstRowBytes, const Options&,
- int*) override;
+ size_t dstRowBytes, const Options&, SkPMColor*,
+ int*, int*) override;
SkCodec::Result onPrepareToDecode(const SkImageInfo& dstInfo,
- const SkCodec::Options& options) override;
+ const SkCodec::Options& options, SkPMColor inputColorPtr[],
+ int* inputColorCount) override;
private:
diff --git a/src/codec/SkBmpRLECodec.cpp b/src/codec/SkBmpRLECodec.cpp
index 0fcd290add..c6d788b800 100644
--- a/src/codec/SkBmpRLECodec.cpp
+++ b/src/codec/SkBmpRLECodec.cpp
@@ -34,13 +34,15 @@ SkBmpRLECodec::SkBmpRLECodec(int width, int height, const SkEncodedInfo& info, S
SkCodec::Result SkBmpRLECodec::onGetPixels(const SkImageInfo& dstInfo,
void* dst, size_t dstRowBytes,
const Options& opts,
+ SkPMColor* inputColorPtr,
+ int* inputColorCount,
int* rowsDecoded) {
if (opts.fSubset) {
// Subsets are not supported.
return kUnimplemented;
}
- Result result = this->prepareToDecode(dstInfo, opts);
+ Result result = this->prepareToDecode(dstInfo, opts, inputColorPtr, inputColorCount);
if (kSuccess != result) {
return result;
}
@@ -61,13 +63,19 @@ SkCodec::Result SkBmpRLECodec::onGetPixels(const SkImageInfo& dstInfo,
/*
* Process the color table for the bmp input
*/
- bool SkBmpRLECodec::createColorTable(SkColorType dstColorType) {
+ bool SkBmpRLECodec::createColorTable(SkColorType dstColorType, 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 (nullptr != 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;
+ }
// Don't bother reading more than maxColors.
const uint32_t numColorsToRead =
fNumColors == 0 ? maxColors : SkTMin(fNumColors, maxColors);
@@ -233,7 +241,7 @@ void SkBmpRLECodec::setRGBPixel(void* dst, size_t dstRowBytes,
}
SkCodec::Result SkBmpRLECodec::onPrepareToDecode(const SkImageInfo& dstInfo,
- const SkCodec::Options& options) {
+ const SkCodec::Options& options, SkPMColor inputColorPtr[], int* inputColorCount) {
// FIXME: Support subsets for scanline decodes.
if (options.fSubset) {
// Subsets are not supported.
@@ -248,17 +256,20 @@ SkCodec::Result SkBmpRLECodec::onPrepareToDecode(const SkImageInfo& dstInfo,
SkColorType colorTableColorType = dstInfo.colorType();
if (this->colorXform()) {
// Just set a known colorType for the colorTable. No need to actually transform
- // the colors in the colorTable.
+ // the colors in the colorTable since we do not allow decoding RLE to kIndex8.
colorTableColorType = kBGRA_8888_SkColorType;
}
// 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(colorTableColorType)) {
+ if (!this->createColorTable(colorTableColorType, inputColorCount)) {
SkCodecPrintf("Error: could not create color table.\n");
return SkCodec::kInvalidInput;
}
+ // Copy the color table to the client if necessary
+ copy_color_table(dstInfo, fColorTable.get(), inputColorPtr, inputColorCount);
+
// Initialize a buffer for encoded RLE data
if (!this->initializeStreamBuffer()) {
SkCodecPrintf("Error: cannot initialize stream buffer.\n");
diff --git a/src/codec/SkBmpRLECodec.h b/src/codec/SkBmpRLECodec.h
index d6c17c3c68..030e82731f 100644
--- a/src/codec/SkBmpRLECodec.h
+++ b/src/codec/SkBmpRLECodec.h
@@ -44,11 +44,12 @@ public:
protected:
Result onGetPixels(const SkImageInfo& dstInfo, void* dst,
- size_t dstRowBytes, const Options&,
- int*) override;
+ size_t dstRowBytes, const Options&, SkPMColor*,
+ int*, int*) override;
SkCodec::Result onPrepareToDecode(const SkImageInfo& dstInfo,
- const SkCodec::Options& options) override;
+ const SkCodec::Options& options, SkPMColor inputColorPtr[],
+ int* inputColorCount) override;
private:
@@ -56,7 +57,7 @@ private:
* Creates the color table
* Sets colorCount to the new color count if it is non-nullptr
*/
- bool createColorTable(SkColorType dstColorType);
+ bool createColorTable(SkColorType dstColorType, int* colorCount);
bool initializeStreamBuffer();
diff --git a/src/codec/SkBmpStandardCodec.cpp b/src/codec/SkBmpStandardCodec.cpp
index 46a5715dc9..959e75ba5b 100644
--- a/src/codec/SkBmpStandardCodec.cpp
+++ b/src/codec/SkBmpStandardCodec.cpp
@@ -36,6 +36,8 @@ SkBmpStandardCodec::SkBmpStandardCodec(int width, int height, const SkEncodedInf
SkCodec::Result SkBmpStandardCodec::onGetPixels(const SkImageInfo& dstInfo,
void* dst, size_t dstRowBytes,
const Options& opts,
+ SkPMColor* inputColorPtr,
+ int* inputColorCount,
int* rowsDecoded) {
if (opts.fSubset) {
// Subsets are not supported.
@@ -46,7 +48,7 @@ SkCodec::Result SkBmpStandardCodec::onGetPixels(const SkImageInfo& dstInfo,
return kInvalidScale;
}
- Result result = this->prepareToDecode(dstInfo, opts);
+ Result result = this->prepareToDecode(dstInfo, opts, inputColorPtr, inputColorCount);
if (kSuccess != result) {
return result;
}
@@ -61,13 +63,20 @@ SkCodec::Result SkBmpStandardCodec::onGetPixels(const SkImageInfo& dstInfo,
/*
* Process the color table for the bmp input
*/
- bool SkBmpStandardCodec::createColorTable(SkColorType dstColorType, SkAlphaType dstAlphaType) {
+ bool SkBmpStandardCodec::createColorTable(SkColorType dstColorType, SkAlphaType dstAlphaType,
+ 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 (nullptr != 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;
+ }
// Don't bother reading more than maxColors.
const uint32_t numColorsToRead =
fNumColors == 0 ? maxColors : SkTMin(fNumColors, maxColors);
@@ -182,18 +191,21 @@ void SkBmpStandardCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Op
}
SkCodec::Result SkBmpStandardCodec::onPrepareToDecode(const SkImageInfo& dstInfo,
- const SkCodec::Options& options) {
+ const SkCodec::Options& options, SkPMColor inputColorPtr[], int* inputColorCount) {
if (this->xformOnDecode()) {
this->resetXformBuffer(dstInfo.width());
}
// 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.colorType(), dstInfo.alphaType())) {
+ if (!this->createColorTable(dstInfo.colorType(), dstInfo.alphaType(), inputColorCount)) {
SkCodecPrintf("Error: could not create color table.\n");
return SkCodec::kInvalidInput;
}
+ // Copy the color table to the client if necessary
+ copy_color_table(dstInfo, fColorTable.get(), inputColorPtr, inputColorCount);
+
// Initialize a swizzler
this->initializeSwizzler(dstInfo, options);
return SkCodec::kSuccess;
@@ -279,8 +291,9 @@ int SkBmpStandardCodec::decodeRows(const SkImageInfo& dstInfo, void* dst, size_t
void SkBmpStandardCodec::decodeIcoMask(SkStream* stream, const SkImageInfo& dstInfo,
void* dst, size_t dstRowBytes) {
- // BMP in ICO have transparency, so this cannot be 565. The below code depends
- // on the output being an SkPMColor.
+ // BMP in ICO have transparency, so this cannot be 565, and this mask
+ // prevents us from using kIndex8. The below code depends on the output
+ // being an SkPMColor.
SkASSERT(kRGBA_8888_SkColorType == dstInfo.colorType() ||
kBGRA_8888_SkColorType == dstInfo.colorType() ||
kRGBA_F16_SkColorType == dstInfo.colorType());
diff --git a/src/codec/SkBmpStandardCodec.h b/src/codec/SkBmpStandardCodec.h
index f9ce5c6839..ec3d707ab8 100644
--- a/src/codec/SkBmpStandardCodec.h
+++ b/src/codec/SkBmpStandardCodec.h
@@ -47,15 +47,16 @@ public:
protected:
Result onGetPixels(const SkImageInfo& dstInfo, void* dst,
- size_t dstRowBytes, const Options&,
- int*) override;
+ size_t dstRowBytes, const Options&, SkPMColor*,
+ int*, int*) override;
bool onInIco() const override {
return fInIco;
}
SkCodec::Result onPrepareToDecode(const SkImageInfo& dstInfo,
- const SkCodec::Options& options) override;
+ const SkCodec::Options& options, SkPMColor inputColorPtr[],
+ int* inputColorCount) override;
uint64_t onGetFillValue(const SkImageInfo&) const override;
@@ -69,8 +70,9 @@ private:
/*
* Creates the color table
+ * Sets colorCount to the new color count if it is non-nullptr
*/
- bool createColorTable(SkColorType colorType, SkAlphaType alphaType);
+ bool createColorTable(SkColorType colorType, SkAlphaType alphaType, int* colorCount);
void initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts);
diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp
index 4bd3917880..26b31ae11f 100644
--- a/src/codec/SkCodec.cpp
+++ b/src/codec/SkCodec.cpp
@@ -167,6 +167,19 @@ bool SkCodec::rewindIfNeeded() {
return this->onRewind();
}
+#define CHECK_COLOR_TABLE \
+ if (kIndex_8_SkColorType == info.colorType()) { \
+ if (nullptr == ctable || nullptr == ctableCount) { \
+ return SkCodec::kInvalidParameters; \
+ } \
+ } else { \
+ if (ctableCount) { \
+ *ctableCount = 0; \
+ } \
+ ctableCount = nullptr; \
+ ctable = nullptr; \
+ }
+
static void zero_rect(const SkImageInfo& dstInfo, void* pixels, size_t rowBytes,
SkIRect frameRect) {
if (!frameRect.intersect(dstInfo.bounds())) {
@@ -192,6 +205,11 @@ SkCodec::Result SkCodec::handleFrameIndex(const SkImageInfo& info, void* pixels,
return kInvalidParameters;
}
+ // index 8 is not supported beyond the first frame.
+ if (index < 0 || info.colorType() == kIndex_8_SkColorType) {
+ return kInvalidParameters;
+ }
+
if (index >= this->onGetFrameCount()) {
return kIncompleteInput;
}
@@ -234,7 +252,8 @@ SkCodec::Result SkCodec::handleFrameIndex(const SkImageInfo& info, void* pixels,
Options prevFrameOptions(options);
prevFrameOptions.fFrameIndex = requiredFrame;
prevFrameOptions.fZeroInitialized = kNo_ZeroInitialized;
- const Result result = this->getPixels(info, pixels, rowBytes, &prevFrameOptions);
+ const Result result = this->getPixels(info, pixels, rowBytes, &prevFrameOptions,
+ nullptr, nullptr);
if (result == kSuccess) {
const auto* prevFrame = frameHolder->getFrame(requiredFrame);
const auto disposalMethod = prevFrame->getDisposalMethod();
@@ -247,7 +266,7 @@ SkCodec::Result SkCodec::handleFrameIndex(const SkImageInfo& info, void* pixels,
}
SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
- const Options* options) {
+ const Options* options, SkPMColor ctable[], int* ctableCount) {
if (kUnknown_SkColorType == info.colorType()) {
return kInvalidConversion;
}
@@ -258,6 +277,8 @@ SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t
return kInvalidParameters;
}
+ CHECK_COLOR_TABLE;
+
if (!this->rewindIfNeeded()) {
return kCouldNotRewind;
}
@@ -293,7 +314,14 @@ SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t
// On an incomplete decode, the subclass will specify the number of scanlines that it decoded
// successfully.
int rowsDecoded = 0;
- const Result result = this->onGetPixels(info, pixels, rowBytes, *options, &rowsDecoded);
+ const Result result = this->onGetPixels(info, pixels, rowBytes, *options, ctable, ctableCount,
+ &rowsDecoded);
+
+ if (ctableCount) {
+ if (kIncompleteInput == result || kSuccess == result || kErrorInInput == result) {
+ SkASSERT(*ctableCount >= 0 && *ctableCount <= 256);
+ }
+ }
// A return value of kIncompleteInput indicates a truncated image stream.
// In this case, we will fill any uninitialized memory with a default value.
@@ -314,8 +342,12 @@ SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t
return result;
}
+SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes) {
+ return this->getPixels(info, pixels, rowBytes, nullptr, nullptr, nullptr);
+}
+
SkCodec::Result SkCodec::startIncrementalDecode(const SkImageInfo& info, void* pixels,
- size_t rowBytes, const SkCodec::Options* options) {
+ size_t rowBytes, const SkCodec::Options* options, SkPMColor* ctable, int* ctableCount) {
fStartedIncrementalDecode = false;
if (kUnknown_SkColorType == info.colorType()) {
@@ -325,6 +357,9 @@ SkCodec::Result SkCodec::startIncrementalDecode(const SkImageInfo& info, void* p
return kInvalidParameters;
}
+ // Ensure that valid color ptrs are passed in for kIndex8 color type
+ CHECK_COLOR_TABLE;
+
// FIXME: If the rows come after the rows of a previous incremental decode,
// we might be able to skip the rewind, but only the implementation knows
// that. (e.g. PNG will always need to rewind, since we called longjmp, but
@@ -364,7 +399,8 @@ SkCodec::Result SkCodec::startIncrementalDecode(const SkImageInfo& info, void* p
fDstInfo = info;
fOptions = *options;
- const Result result = this->onStartIncrementalDecode(info, pixels, rowBytes, fOptions);
+ const Result result = this->onStartIncrementalDecode(info, pixels, rowBytes,
+ fOptions, ctable, ctableCount);
if (kSuccess == result) {
fStartedIncrementalDecode = true;
} else if (kUnimplemented == result) {
@@ -382,9 +418,11 @@ SkCodec::Result SkCodec::startIncrementalDecode(const SkImageInfo& info, void* p
SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& info,
- const SkCodec::Options* options) {
+ const SkCodec::Options* options, SkPMColor ctable[], int* ctableCount) {
// Reset fCurrScanline in case of failure.
fCurrScanline = -1;
+ // Ensure that valid color ptrs are passed in for kIndex8 color type
+ CHECK_COLOR_TABLE;
if (!this->rewindIfNeeded()) {
return kCouldNotRewind;
@@ -412,7 +450,7 @@ SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& info,
return kInvalidScale;
}
- const Result result = this->onStartScanlineDecode(info, *options);
+ const Result result = this->onStartScanlineDecode(info, *options, ctable, ctableCount);
if (result != SkCodec::kSuccess) {
return result;
}
@@ -423,6 +461,12 @@ SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& info,
return kSuccess;
}
+#undef CHECK_COLOR_TABLE
+
+SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& info) {
+ return this->startScanlineDecode(info, nullptr, nullptr, nullptr);
+}
+
int SkCodec::getScanlines(void* dst, int countLines, size_t rowBytes) {
if (fCurrScanline < 0) {
return 0;
@@ -486,7 +530,7 @@ uint64_t SkCodec::onGetFillValue(const SkImageInfo& dstInfo) const {
return (kOpaque_SkAlphaType == fSrcInfo.alphaType()) ? opaqueColor : transparentColor;
}
default: {
- // This not only handles the kN32 case, but also k565, kGray8, since
+ // This not only handles the kN32 case, but also k565, kGray8, kIndex8, since
// the low bits are zeros.
return (kOpaque_SkAlphaType == fSrcInfo.alphaType()) ?
SK_ColorBLACK : SK_ColorTRANSPARENT;
@@ -539,6 +583,7 @@ static inline SkColorSpaceXform::ColorFormat select_xform_format_ct(SkColorType
case kBGRA_8888_SkColorType:
return SkColorSpaceXform::kBGRA_8888_ColorFormat;
case kRGB_565_SkColorType:
+ case kIndex_8_SkColorType:
#ifdef SK_PMCOLOR_IS_RGBA
return SkColorSpaceXform::kRGBA_8888_ColorFormat;
#else
diff --git a/src/codec/SkCodecImageGenerator.cpp b/src/codec/SkCodecImageGenerator.cpp
index 57f7fb865e..20d547ad36 100644
--- a/src/codec/SkCodecImageGenerator.cpp
+++ b/src/codec/SkCodecImageGenerator.cpp
@@ -44,7 +44,8 @@ bool SkCodecImageGenerator::onGetPixels(const SkImageInfo& info, void* pixels, s
const Options& opts) {
SkCodec::Options codecOpts;
codecOpts.fPremulBehavior = opts.fBehavior;
- SkCodec::Result result = fCodec->getPixels(info, pixels, rowBytes, &codecOpts);
+ SkCodec::Result result = fCodec->getPixels(info, pixels, rowBytes, &codecOpts, nullptr,
+ nullptr);
switch (result) {
case SkCodec::kSuccess:
case SkCodec::kIncompleteInput:
diff --git a/src/codec/SkCodecPriv.h b/src/codec/SkCodecPriv.h
index cb7cd94979..b69e48808e 100644
--- a/src/codec/SkCodecPriv.h
+++ b/src/codec/SkCodecPriv.h
@@ -128,6 +128,8 @@ static inline uint64_t get_color_table_fill_value(SkColorType dstColorType, SkAl
return colorPtr[fillIndex];
case kRGB_565_SkColorType:
return SkPixel32ToPixel16(colorPtr[fillIndex]);
+ case kIndex_8_SkColorType:
+ return fillIndex;
case kRGBA_F16_SkColorType: {
SkASSERT(colorXform);
uint64_t dstColor;
@@ -146,6 +148,20 @@ static inline uint64_t get_color_table_fill_value(SkColorType dstColorType, SkAl
}
/*
+ *
+ * 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) {
+ if (kIndex_8_SkColorType == dstInfo.colorType()) {
+ SkASSERT(nullptr != inputColorPtr);
+ SkASSERT(nullptr != inputColorCount);
+ SkASSERT(nullptr != colorTable);
+ memcpy(inputColorPtr, colorTable->readColors(), *inputColorCount * sizeof(SkPMColor));
+ }
+}
+
+/*
* Compute row bytes for an image using pixels per byte
*/
static inline size_t compute_row_bytes_ppb(int width, uint32_t pixelsPerByte) {
@@ -315,6 +331,7 @@ static inline SkAlphaType select_xform_alpha(SkAlphaType dstAlphaType, SkAlphaTy
* Color Type Conversions
* - Always support kRGBA_8888, kBGRA_8888
* - Support kRGBA_F16 when there is a linear dst color space
+ * - Support kIndex8 if it matches the src
* - Support k565 if kOpaque and color correction is not required
* - Support k565 if it matches the src, kOpaque, and color correction is not required
*/
@@ -331,6 +348,8 @@ static inline bool conversion_possible(const SkImageInfo& dst, const SkImageInfo
return true;
case kRGBA_F16_SkColorType:
return dst.colorSpace() && dst.colorSpace()->gammaIsLinear();
+ case kIndex_8_SkColorType:
+ return kIndex_8_SkColorType == src.colorType();
case kRGB_565_SkColorType:
return kOpaque_SkAlphaType == src.alphaType();
case kGray_8_SkColorType:
diff --git a/src/codec/SkGifCodec.cpp b/src/codec/SkGifCodec.cpp
index 081f237fe1..89889d2555 100644
--- a/src/codec/SkGifCodec.cpp
+++ b/src/codec/SkGifCodec.cpp
@@ -94,6 +94,10 @@ SkCodec* SkGifCodec::NewFromStream(SkStream* stream) {
// expanding to 8 bits and take advantage of the SkSwizzler to work from 4.
const auto encodedInfo = SkEncodedInfo::Make(SkEncodedInfo::kPalette_Color, alpha, 8);
+ // Although the encodedInfo is always kPalette_Color, it is possible that kIndex_8 is
+ // unsupported if the frame is subset and there is no transparent pixel.
+ const auto colorType = reader->firstFrameSupportsIndex8() ? kIndex_8_SkColorType
+ : kN32_SkColorType;
// The choice of unpremul versus premul is arbitrary, since all colors are either fully
// opaque or fully transparent (i.e. kBinary), but we stored the transparent colors as all
// zeroes, which is arguably premultiplied.
@@ -101,7 +105,7 @@ SkCodec* SkGifCodec::NewFromStream(SkStream* stream) {
: kOpaque_SkAlphaType;
const auto imageInfo = SkImageInfo::Make(reader->screenWidth(), reader->screenHeight(),
- kN32_SkColorType, alphaType,
+ colorType, alphaType,
SkColorSpace::MakeSRGB());
return new SkGifCodec(encodedInfo, imageInfo, reader.release());
}
@@ -185,7 +189,8 @@ void SkGifCodec::initializeColorTable(const SkImageInfo& dstInfo, int frameIndex
}
-SkCodec::Result SkGifCodec::prepareToDecode(const SkImageInfo& dstInfo, const Options& opts) {
+SkCodec::Result SkGifCodec::prepareToDecode(const SkImageInfo& dstInfo, SkPMColor* inputColorPtr,
+ int* inputColorCount, const Options& opts) {
if (opts.fSubset) {
return gif_error("Subsets not supported.\n", kUnimplemented);
}
@@ -246,6 +251,11 @@ SkCodec::Result SkGifCodec::prepareToDecode(const SkImageInfo& dstInfo, const Op
this->initializeSwizzler(dstInfo, frameIndex);
SkASSERT(fCurrColorTable);
+ if (inputColorCount) {
+ *inputColorCount = fCurrColorTable->count();
+ }
+ copy_color_table(dstInfo, fCurrColorTable.get(), inputColorPtr, inputColorCount);
+
return kSuccess;
}
@@ -287,8 +297,10 @@ void SkGifCodec::initializeSwizzler(const SkImageInfo& dstInfo, int frameIndex)
SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo,
void* pixels, size_t dstRowBytes,
const Options& opts,
+ SkPMColor* inputColorPtr,
+ int* inputColorCount,
int* rowsDecoded) {
- Result result = this->prepareToDecode(dstInfo, opts);
+ Result result = this->prepareToDecode(dstInfo, inputColorPtr, inputColorCount, opts);
switch (result) {
case kSuccess:
break;
@@ -316,8 +328,10 @@ SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo,
SkCodec::Result SkGifCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo,
void* pixels, size_t dstRowBytes,
- const SkCodec::Options& opts) {
- Result result = this->prepareToDecode(dstInfo, opts);
+ const SkCodec::Options& opts,
+ SkPMColor* inputColorPtr,
+ int* inputColorCount) {
+ Result result = this->prepareToDecode(dstInfo, inputColorPtr, inputColorCount, opts);
if (result != kSuccess) {
return result;
}
@@ -409,8 +423,28 @@ SkCodec::Result SkGifCodec::decodeFrame(bool firstAttempt, const Options& opts,
}
uint64_t SkGifCodec::onGetFillValue(const SkImageInfo& dstInfo) const {
+ // Note: Using fCurrColorTable relies on having called initializeColorTable already.
+ // This is (currently) safe because this method is only called when filling, after
+ // initializeColorTable has been called.
+ // FIXME: Is there a way to make this less fragile?
+ if (dstInfo.colorType() == kIndex_8_SkColorType && fCurrColorTableIsReal) {
+ // We only support index 8 for the first frame, for backwards
+ // compatibity on Android, so we are using the color table for the first frame.
+ SkASSERT(this->options().fFrameIndex == 0);
+ // Use the transparent index for the first frame.
+ const int transPixel = fReader->frameContext(0)->transparentPixel();
+ if (transPixel >= 0 && transPixel < fCurrColorTable->count()) {
+ return transPixel;
+ }
+ // Fall through to return SK_ColorTRANSPARENT (i.e. 0). This choice is arbitrary,
+ // but we have to pick something inside the color table, and this one is as good
+ // as any.
+ }
// Using transparent as the fill value matches the behavior in Chromium,
// which ignores the background color.
+ // If the colorType is kIndex_8, and there was no color table (i.e.
+ // fCurrColorTableIsReal is false), this value (zero) corresponds to the
+ // only entry in the dummy color table provided to the client.
return SK_ColorTRANSPARENT;
}
diff --git a/src/codec/SkGifCodec.h b/src/codec/SkGifCodec.h
index 7bb86f824b..33472d8ac1 100644
--- a/src/codec/SkGifCodec.h
+++ b/src/codec/SkGifCodec.h
@@ -40,7 +40,7 @@ protected:
* Performs the full gif decode
*/
Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&,
- int*) override;
+ SkPMColor*, int*, int*) override;
SkEncodedImageFormat onGetEncodedFormat() const override {
return SkEncodedImageFormat::kGIF;
@@ -55,7 +55,7 @@ protected:
int onGetRepetitionCount() override;
Result onStartIncrementalDecode(const SkImageInfo& /*dstInfo*/, void*, size_t,
- const SkCodec::Options&) override;
+ const SkCodec::Options&, SkPMColor*, int*) override;
Result onIncrementalDecode(int*) override;
@@ -74,9 +74,11 @@ private:
void initializeColorTable(const SkImageInfo& dstInfo, int frameIndex);
/*
- * Does necessary setup, including setting up the color table and swizzler.
+ * Does necessary setup, including setting up the color table and swizzler,
+ * and reports color info to the client.
*/
- Result prepareToDecode(const SkImageInfo& dstInfo, const Options& opts);
+ Result prepareToDecode(const SkImageInfo& dstInfo, SkPMColor* inputColorPtr,
+ int* inputColorCount, const Options& opts);
/*
* Initializes the swizzler.
diff --git a/src/codec/SkIcoCodec.cpp b/src/codec/SkIcoCodec.cpp
index d1cbed0377..cde233a1bd 100644
--- a/src/codec/SkIcoCodec.cpp
+++ b/src/codec/SkIcoCodec.cpp
@@ -255,8 +255,8 @@ bool SkIcoCodec::onDimensionsSupported(const SkISize& dim) {
*/
SkCodec::Result SkIcoCodec::onGetPixels(const SkImageInfo& dstInfo,
void* dst, size_t dstRowBytes,
- const Options& opts,
- int* rowsDecoded) {
+ const Options& opts, SkPMColor* colorTable,
+ int* colorCount, int* rowsDecoded) {
if (opts.fSubset) {
// Subsets are not supported.
return kUnimplemented;
@@ -271,7 +271,7 @@ SkCodec::Result SkIcoCodec::onGetPixels(const SkImageInfo& dstInfo,
}
SkCodec* embeddedCodec = fEmbeddedCodecs->operator[](index).get();
- result = embeddedCodec->getPixels(dstInfo, dst, dstRowBytes, &opts);
+ result = embeddedCodec->getPixels(dstInfo, dst, dstRowBytes, &opts, colorTable, colorCount);
switch (result) {
case kSuccess:
case kIncompleteInput:
@@ -292,7 +292,7 @@ SkCodec::Result SkIcoCodec::onGetPixels(const SkImageInfo& dstInfo,
}
SkCodec::Result SkIcoCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
- const SkCodec::Options& options) {
+ const SkCodec::Options& options, SkPMColor colorTable[], int* colorCount) {
int index = 0;
SkCodec::Result result = kInvalidScale;
while (true) {
@@ -302,7 +302,7 @@ SkCodec::Result SkIcoCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
}
SkCodec* embeddedCodec = fEmbeddedCodecs->operator[](index).get();
- result = embeddedCodec->startScanlineDecode(dstInfo, &options);
+ result = embeddedCodec->startScanlineDecode(dstInfo, &options, colorTable, colorCount);
if (kSuccess == result) {
fCurrScanlineCodec = embeddedCodec;
fCurrIncrementalCodec = nullptr;
@@ -327,7 +327,8 @@ bool SkIcoCodec::onSkipScanlines(int count) {
}
SkCodec::Result SkIcoCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo,
- void* pixels, size_t rowBytes, const SkCodec::Options& options) {
+ void* pixels, size_t rowBytes, const SkCodec::Options& options,
+ SkPMColor* colorTable, int* colorCount) {
int index = 0;
while (true) {
index = this->chooseCodec(dstInfo.dimensions(), index);
@@ -337,7 +338,7 @@ SkCodec::Result SkIcoCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo,
SkCodec* embeddedCodec = fEmbeddedCodecs->operator[](index).get();
switch (embeddedCodec->startIncrementalDecode(dstInfo,
- pixels, rowBytes, &options)) {
+ pixels, rowBytes, &options, colorTable, colorCount)) {
case kSuccess:
fCurrIncrementalCodec = embeddedCodec;
fCurrScanlineCodec = nullptr;
@@ -355,7 +356,8 @@ SkCodec::Result SkIcoCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo,
// valid for scanline decoding.
// Once BMP supports incremental decoding this workaround can go
// away.
- if (embeddedCodec->startScanlineDecode(dstInfo) == kSuccess) {
+ if (embeddedCodec->startScanlineDecode(dstInfo, nullptr,
+ colorTable, colorCount) == kSuccess) {
return kUnimplemented;
}
// Move on to the next embedded codec.
diff --git a/src/codec/SkIcoCodec.h b/src/codec/SkIcoCodec.h
index 9f2457aa0d..2a4efb50b0 100644
--- a/src/codec/SkIcoCodec.h
+++ b/src/codec/SkIcoCodec.h
@@ -40,7 +40,7 @@ protected:
* Initiates the Ico decode
*/
Result onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options&,
- int*) override;
+ SkPMColor*, int*, int*) override;
SkEncodedImageFormat onGetEncodedFormat() const override {
return SkEncodedImageFormat::kICO;
@@ -50,15 +50,15 @@ protected:
private:
- Result onStartScanlineDecode(const SkImageInfo& dstInfo,
- const SkCodec::Options& options) override;
+ Result onStartScanlineDecode(const SkImageInfo& dstInfo, const SkCodec::Options& options,
+ SkPMColor inputColorPtr[], int* inputColorCount) override;
int onGetScanlines(void* dst, int count, size_t rowBytes) override;
bool onSkipScanlines(int count) override;
Result onStartIncrementalDecode(const SkImageInfo& dstInfo, void* pixels, size_t rowBytes,
- const SkCodec::Options&) override;
+ const SkCodec::Options&, SkPMColor*, int*) override;
Result onIncrementalDecode(int* rowsDecoded) override;
diff --git a/src/codec/SkJpegCodec.cpp b/src/codec/SkJpegCodec.cpp
index 728298917a..4d97cce124 100644
--- a/src/codec/SkJpegCodec.cpp
+++ b/src/codec/SkJpegCodec.cpp
@@ -556,7 +556,7 @@ static inline bool needs_swizzler_to_convert_from_cmyk(J_COLOR_SPACE jpegColorTy
*/
SkCodec::Result SkJpegCodec::onGetPixels(const SkImageInfo& dstInfo,
void* dst, size_t dstRowBytes,
- const Options& options,
+ const Options& options, SkPMColor*, int*,
int* rowsDecoded) {
if (options.fSubset) {
// Subsets are not supported.
@@ -673,7 +673,7 @@ SkSampler* SkJpegCodec::getSampler(bool createIfNecessary) {
}
SkCodec::Result SkJpegCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
- const Options& options) {
+ const Options& options, SkPMColor ctable[], int* ctableCount) {
// Set the jump location for libjpeg errors
if (setjmp(fDecoderMgr->getJmpBuf())) {
SkCodecPrintf("setjmp: Error from libjpeg\n");
diff --git a/src/codec/SkJpegCodec.h b/src/codec/SkJpegCodec.h
index 2d6822e8af..0c2f4a1257 100644
--- a/src/codec/SkJpegCodec.h
+++ b/src/codec/SkJpegCodec.h
@@ -45,7 +45,7 @@ protected:
* Initiates the jpeg decode
*/
Result onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options&,
- int*) override;
+ SkPMColor*, int*, int*) override;
bool onQueryYUV8(SkYUVSizeInfo* sizeInfo, SkYUVColorSpace* colorSpace) const override;
@@ -120,8 +120,8 @@ private:
* Scanline decoding.
*/
SkSampler* getSampler(bool createIfNecessary) override;
- Result onStartScanlineDecode(const SkImageInfo& dstInfo,
- const Options& options) override;
+ Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options,
+ SkPMColor ctable[], int* ctableCount) override;
int onGetScanlines(void* dst, int count, size_t rowBytes) override;
bool onSkipScanlines(int count) override;
diff --git a/src/codec/SkPngCodec.cpp b/src/codec/SkPngCodec.cpp
index ea832ff688..d77fe4007d 100644
--- a/src/codec/SkPngCodec.cpp
+++ b/src/codec/SkPngCodec.cpp
@@ -245,7 +245,7 @@ void SkPngCodec::processData() {
static const SkColorType kXformSrcColorType = kRGBA_8888_SkColorType;
// Note: SkColorTable claims to store SkPMColors, which is not necessarily the case here.
-bool SkPngCodec::createColorTable(const SkImageInfo& dstInfo) {
+bool SkPngCodec::createColorTable(const SkImageInfo& dstInfo, int* ctableCount) {
int numColors;
png_color* palette;
@@ -308,6 +308,11 @@ bool SkPngCodec::createColorTable(const SkImageInfo& dstInfo) {
sk_memset32(colorTable + numColors, lastColor, maxColors - numColors);
}
+ // Set the new color count.
+ if (ctableCount != nullptr) {
+ *ctableCount = maxColors;
+ }
+
fColorTable.reset(new SkColorTable(colorTable, maxColors));
return true;
}
@@ -968,7 +973,8 @@ void SkPngCodec::destroyReadStruct() {
// Getting the pixels
///////////////////////////////////////////////////////////////////////////////
-SkCodec::Result SkPngCodec::initializeXforms(const SkImageInfo& dstInfo, const Options& options) {
+SkCodec::Result SkPngCodec::initializeXforms(const SkImageInfo& dstInfo, const Options& options,
+ SkPMColor ctable[], int* ctableCount) {
if (setjmp(PNG_JMPBUF((png_struct*)fPng_ptr))) {
SkCodecPrintf("Failed on png_read_update_info.\n");
return kInvalidInput;
@@ -1005,11 +1011,14 @@ SkCodec::Result SkPngCodec::initializeXforms(const SkImageInfo& dstInfo, const O
}
if (SkEncodedInfo::kPalette_Color == this->getEncodedInfo().color()) {
- if (!this->createColorTable(dstInfo)) {
+ if (!this->createColorTable(dstInfo, ctableCount)) {
return kInvalidInput;
}
}
+ // Copy the color table to the client if they request kIndex8 mode.
+ copy_color_table(dstInfo, fColorTable.get(), ctable, ctableCount);
+
this->initializeSwizzler(dstInfo, options, skipFormatConversion);
return kSuccess;
}
@@ -1083,12 +1092,13 @@ bool SkPngCodec::onRewind() {
SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst,
size_t rowBytes, const Options& options,
+ SkPMColor ctable[], int* ctableCount,
int* rowsDecoded) {
if (!conversion_possible(dstInfo, this->getInfo())) {
return kInvalidConversion;
}
- Result result = this->initializeXforms(dstInfo, options);
+ Result result = this->initializeXforms(dstInfo, options, ctable, ctableCount);
if (kSuccess != result) {
return result;
}
@@ -1103,12 +1113,13 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst,
}
SkCodec::Result SkPngCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo,
- void* dst, size_t rowBytes, const SkCodec::Options& options) {
+ void* dst, size_t rowBytes, const SkCodec::Options& options,
+ SkPMColor* ctable, int* ctableCount) {
if (!conversion_possible(dstInfo, this->getInfo())) {
return kInvalidConversion;
}
- Result result = this->initializeXforms(dstInfo, options);
+ Result result = this->initializeXforms(dstInfo, options, ctable, ctableCount);
if (kSuccess != result) {
return result;
}
diff --git a/src/codec/SkPngCodec.h b/src/codec/SkPngCodec.h
index 25a5f07877..b329fef1b6 100644
--- a/src/codec/SkPngCodec.h
+++ b/src/codec/SkPngCodec.h
@@ -47,7 +47,7 @@ protected:
SkPngCodec(const SkEncodedInfo&, const SkImageInfo&, SkStream*, SkPngChunkReader*,
void* png_ptr, void* info_ptr, int bitDepth);
- Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, int*)
+ Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, SkPMColor*, int*, int*)
override;
SkEncodedImageFormat onGetEncodedFormat() const override { return SkEncodedImageFormat::kPNG; }
bool onRewind() override;
@@ -73,7 +73,8 @@ protected:
void processData();
Result onStartIncrementalDecode(const SkImageInfo& dstInfo, void* pixels, size_t rowBytes,
- const SkCodec::Options&) override;
+ const SkCodec::Options&,
+ SkPMColor* ctable, int* ctableCount) override;
Result onIncrementalDecode(int*) override;
sk_sp<SkPngChunkReader> fPngChunkReader;
@@ -100,9 +101,10 @@ private:
kSwizzleColor_XformMode,
};
- bool createColorTable(const SkImageInfo& dstInfo);
+ bool createColorTable(const SkImageInfo& dstInfo, int* ctableCount);
// Helper to set up swizzler, color xforms, and color table. Also calls png_read_update_info.
- SkCodec::Result initializeXforms(const SkImageInfo& dstInfo, const Options&);
+ SkCodec::Result initializeXforms(const SkImageInfo& dstInfo, const Options&,
+ SkPMColor* colorPtr, int* colorCount);
void initializeSwizzler(const SkImageInfo& dstInfo, const Options&, bool skipFormatConversion);
void allocateStorage(const SkImageInfo& dstInfo);
void destroyReadStruct();
diff --git a/src/codec/SkRawAdapterCodec.cpp b/src/codec/SkRawAdapterCodec.cpp
index 49170911a4..76cbaa1a23 100644
--- a/src/codec/SkRawAdapterCodec.cpp
+++ b/src/codec/SkRawAdapterCodec.cpp
@@ -24,5 +24,7 @@ SkCodec::Result SkRawAdapterCodec::onGetAndroidPixels(
SkCodec::Options codecOptions;
codecOptions.fZeroInitialized = options.fZeroInitialized;
codecOptions.fSubset = options.fSubset;
- return this->codec()->getPixels(info, pixels, rowBytes, &codecOptions);
+ return this->codec()->getPixels(
+ info, pixels, rowBytes, &codecOptions, options.fColorPtr,
+ options.fColorCount);
}
diff --git a/src/codec/SkRawCodec.cpp b/src/codec/SkRawCodec.cpp
index 83b7947ab6..b8bcf19163 100644
--- a/src/codec/SkRawCodec.cpp
+++ b/src/codec/SkRawCodec.cpp
@@ -688,6 +688,7 @@ SkCodec* SkRawCodec::NewFromStream(SkStream* stream) {
SkCodec::Result SkRawCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst,
size_t dstRowBytes, const Options& options,
+ SkPMColor ctable[], int* ctableCount,
int* rowsDecoded) {
if (!conversion_possible(dstInfo, this->getInfo()) ||
!this->initializeColorXform(dstInfo, options.fPremulBehavior))
diff --git a/src/codec/SkRawCodec.h b/src/codec/SkRawCodec.h
index d2ef921f1d..51bb234b73 100644
--- a/src/codec/SkRawCodec.h
+++ b/src/codec/SkRawCodec.h
@@ -35,7 +35,7 @@ public:
protected:
Result onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options&,
- int*) override;
+ SkPMColor*, int*, int*) override;
SkEncodedImageFormat onGetEncodedFormat() const override {
return SkEncodedImageFormat::kDNG;
diff --git a/src/codec/SkSampledCodec.cpp b/src/codec/SkSampledCodec.cpp
index b8e2a56286..045f184f29 100644
--- a/src/codec/SkSampledCodec.cpp
+++ b/src/codec/SkSampledCodec.cpp
@@ -80,7 +80,8 @@ SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void
SkIRect* subset = options.fSubset;
if (!subset || subset->size() == this->codec()->getInfo().dimensions()) {
if (this->codec()->dimensionsSupported(info.dimensions())) {
- return this->codec()->getPixels(info, pixels, rowBytes, &codecOptions);
+ return this->codec()->getPixels(info, pixels, rowBytes, &codecOptions,
+ options.fColorPtr, options.fColorCount);
}
// If the native codec does not support the requested scale, scale by sampling.
@@ -111,7 +112,8 @@ SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void
scaledSubsetWidth, scaledSubsetHeight);
codecOptions.fSubset = &incrementalSubset;
const SkCodec::Result startResult = this->codec()->startIncrementalDecode(
- scaledInfo, pixels, rowBytes, &codecOptions);
+ scaledInfo, pixels, rowBytes, &codecOptions,
+ options.fColorPtr, options.fColorCount);
if (SkCodec::kSuccess == startResult) {
int rowsDecoded;
const SkCodec::Result incResult = this->codec()->incrementalDecode(&rowsDecoded);
@@ -138,7 +140,7 @@ SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void
codecOptions.fSubset = &scanlineSubset;
SkCodec::Result result = this->codec()->startScanlineDecode(scaledInfo,
- &codecOptions);
+ &codecOptions, options.fColorPtr, options.fColorCount);
if (SkCodec::kSuccess != result) {
return result;
}
@@ -228,7 +230,7 @@ SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pix
incrementalOptions.fSubset = &incrementalSubset;
}
const SkCodec::Result startResult = this->codec()->startIncrementalDecode(nativeInfo,
- pixels, rowBytes, &incrementalOptions);
+ pixels, rowBytes, &incrementalOptions, options.fColorPtr, options.fColorCount);
if (SkCodec::kSuccess == startResult) {
SkSampler* sampler = this->codec()->getSampler(true);
if (!sampler) {
@@ -262,7 +264,7 @@ SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pix
// Start the scanline decode.
SkCodec::Result result = this->codec()->startScanlineDecode(nativeInfo,
- &sampledOptions);
+ &sampledOptions, options.fColorPtr, options.fColorCount);
if (SkCodec::kSuccess != result) {
return result;
}
diff --git a/src/codec/SkWbmpCodec.cpp b/src/codec/SkWbmpCodec.cpp
index fe9a29133e..780ae5e5d0 100644
--- a/src/codec/SkWbmpCodec.cpp
+++ b/src/codec/SkWbmpCodec.cpp
@@ -21,6 +21,15 @@ 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;
+ }
+}
+
static inline bool valid_color_type(const SkImageInfo& dstInfo) {
switch (dstInfo.colorType()) {
case kRGBA_8888_SkColorType:
@@ -88,8 +97,9 @@ bool SkWbmpCodec::onRewind() {
return read_header(this->stream(), nullptr);
}
-SkSwizzler* SkWbmpCodec::initializeSwizzler(const SkImageInfo& info, const Options& opts) {
- return SkSwizzler::CreateSwizzler(this->getEncodedInfo(), nullptr, info, opts);
+SkSwizzler* SkWbmpCodec::initializeSwizzler(const SkImageInfo& info, const SkPMColor* ctable,
+ const Options& opts) {
+ return SkSwizzler::CreateSwizzler(this->getEncodedInfo(), ctable, info, opts);
}
bool SkWbmpCodec::readRow(uint8_t* row) {
@@ -102,6 +112,7 @@ SkWbmpCodec::SkWbmpCodec(int width, int height, const SkEncodedInfo& info, SkStr
stream, SkColorSpace::MakeSRGB())
, fSrcRowBytes(get_src_row_bytes(this->getInfo().width()))
, fSwizzler(nullptr)
+ , fColorTable(nullptr)
{}
SkEncodedImageFormat SkWbmpCodec::onGetEncodedFormat() const {
@@ -112,6 +123,8 @@ SkCodec::Result SkWbmpCodec::onGetPixels(const SkImageInfo& info,
void* dst,
size_t rowBytes,
const Options& options,
+ SkPMColor ctable[],
+ int* ctableCount,
int* rowsDecoded) {
if (options.fSubset) {
// Subsets are not supported.
@@ -122,8 +135,11 @@ SkCodec::Result SkWbmpCodec::onGetPixels(const SkImageInfo& info,
return kInvalidConversion;
}
+ // Prepare a color table if necessary
+ setup_color_table(info.colorType(), ctable, ctableCount);
+
// Initialize the swizzler
- std::unique_ptr<SkSwizzler> swizzler(this->initializeSwizzler(info, options));
+ std::unique_ptr<SkSwizzler> swizzler(this->initializeSwizzler(info, ctable, options));
SkASSERT(swizzler);
// Perform the decode
@@ -175,7 +191,7 @@ bool SkWbmpCodec::onSkipScanlines(int count) {
}
SkCodec::Result SkWbmpCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
- const Options& options) {
+ const Options& options, SkPMColor inputColorTable[], int* inputColorCount) {
if (options.fSubset) {
// Subsets are not supported.
return kUnimplemented;
@@ -187,8 +203,16 @@ SkCodec::Result SkWbmpCodec::onStartScanlineDecode(const SkImageInfo& dstInfo,
return kInvalidConversion;
}
+ // 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(new SkColorTable(inputColorTable, 2));
+ }
+
// Initialize the swizzler
- fSwizzler.reset(this->initializeSwizzler(dstInfo, options));
+ fSwizzler.reset(this->initializeSwizzler(dstInfo, get_color_ptr(fColorTable.get()), options));
SkASSERT(fSwizzler);
fSrcBuffer.reset(fSrcRowBytes);
diff --git a/src/codec/SkWbmpCodec.h b/src/codec/SkWbmpCodec.h
index e8a6e40aba..40f507e9f3 100644
--- a/src/codec/SkWbmpCodec.h
+++ b/src/codec/SkWbmpCodec.h
@@ -26,13 +26,13 @@ public:
protected:
SkEncodedImageFormat onGetEncodedFormat() const override;
Result onGetPixels(const SkImageInfo&, void*, size_t,
- const Options&, int*) override;
+ const Options&, SkPMColor[], int*, int*) override;
bool onRewind() override;
private:
/*
* Returns a swizzler on success, nullptr on failure
*/
- SkSwizzler* initializeSwizzler(const SkImageInfo& info,
+ SkSwizzler* initializeSwizzler(const SkImageInfo& info, const SkPMColor* ctable,
const Options& opts);
SkSampler* getSampler(bool createIfNecessary) override {
SkASSERT(fSwizzler || !createIfNecessary);
@@ -50,12 +50,13 @@ private:
// Used for scanline decodes:
std::unique_ptr<SkSwizzler> fSwizzler;
+ sk_sp<SkColorTable> fColorTable;
SkAutoTMalloc<uint8_t> fSrcBuffer;
int onGetScanlines(void* dst, int count, size_t dstRowBytes) override;
bool onSkipScanlines(int count) override;
- Result onStartScanlineDecode(const SkImageInfo& dstInfo,
- const Options& options) override;
+ Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options,
+ SkPMColor inputColorTable[], int* inputColorCount) override;
typedef SkCodec INHERITED;
};
diff --git a/src/codec/SkWebpAdapterCodec.cpp b/src/codec/SkWebpAdapterCodec.cpp
index 93400d0015..cf1f0e0e1f 100644
--- a/src/codec/SkWebpAdapterCodec.cpp
+++ b/src/codec/SkWebpAdapterCodec.cpp
@@ -41,5 +41,6 @@ SkCodec::Result SkWebpAdapterCodec::onGetAndroidPixels(const SkImageInfo& info,
codecOptions.fZeroInitialized = options.fZeroInitialized;
codecOptions.fSubset = options.fSubset;
codecOptions.fPremulBehavior = SkTransferFunctionBehavior::kIgnore;
- return this->codec()->getPixels(info, pixels, rowBytes, &codecOptions);
+ return this->codec()->getPixels(info, pixels, rowBytes, &codecOptions, options.fColorPtr,
+ options.fColorCount);
}
diff --git a/src/codec/SkWebpCodec.cpp b/src/codec/SkWebpCodec.cpp
index e8e409a486..d94011a867 100644
--- a/src/codec/SkWebpCodec.cpp
+++ b/src/codec/SkWebpCodec.cpp
@@ -378,7 +378,8 @@ static void blend_line(SkColorType dstCT, void* dst,
}
SkCodec::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t rowBytes,
- const Options& options, int* rowsDecodedPtr) {
+ const Options& options, SkPMColor*, int*,
+ int* rowsDecodedPtr) {
const int index = options.fFrameIndex;
SkASSERT(0 == index || index < fFrameHolder.size());
diff --git a/src/codec/SkWebpCodec.h b/src/codec/SkWebpCodec.h
index c911ebd585..fc9e6838a6 100644
--- a/src/codec/SkWebpCodec.h
+++ b/src/codec/SkWebpCodec.h
@@ -31,7 +31,8 @@ public:
static SkCodec* NewFromStream(SkStream*);
static bool IsWebp(const void*, size_t);
protected:
- Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, int*) override;
+ Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, SkPMColor*, int*, int*)
+ override;
SkEncodedImageFormat onGetEncodedFormat() const override { return SkEncodedImageFormat::kWEBP; }
SkISize onGetScaledDimensions(float desiredScale) const override;
diff --git a/tests/CodecAnimTest.cpp b/tests/CodecAnimTest.cpp
index 84dc9faa6b..27fe0038b5 100644
--- a/tests/CodecAnimTest.cpp
+++ b/tests/CodecAnimTest.cpp
@@ -60,7 +60,7 @@ DEF_TEST(Codec_565, r) {
options.fPriorFrame = SkCodec::kNone;
const auto result = codec->getPixels(info, bm.getPixels(), bm.rowBytes(),
- &options);
+ &options, nullptr, nullptr);
REPORTER_ASSERT(r, result == SkCodec::kSuccess);
}
@@ -329,7 +329,7 @@ DEF_TEST(Codec_frames, r) {
opts.fFrameIndex = index;
opts.fPriorFrame = cachedIndex;
const auto result = codec->getPixels(decodeInfo, bm->getPixels(), bm->rowBytes(),
- &opts);
+ &opts, nullptr, nullptr);
if (cachedIndex != SkCodec::kNone && restore_previous(frameInfos[cachedIndex])) {
if (result == SkCodec::kInvalidParameters) {
return true;
diff --git a/tests/CodecPartialTest.cpp b/tests/CodecPartialTest.cpp
index ee012711a6..975ca63ee9 100644
--- a/tests/CodecPartialTest.cpp
+++ b/tests/CodecPartialTest.cpp
@@ -209,7 +209,7 @@ DEF_TEST(Codec_partialAnim, r) {
SkCodec::Options opts;
opts.fFrameIndex = i;
const SkCodec::Result result = fullCodec->getPixels(info, frame.getPixels(),
- frame.rowBytes(), &opts);
+ frame.rowBytes(), &opts, nullptr, nullptr);
if (result == SkCodec::kIncompleteInput || result == SkCodec::kInvalidInput) {
// We need to distinguish between a partial frame and no more frames.
diff --git a/tests/CodecPriv.h b/tests/CodecPriv.h
index d80bb69672..cfa794ed68 100644
--- a/tests/CodecPriv.h
+++ b/tests/CodecPriv.h
@@ -17,9 +17,21 @@ inline bool decode_memory(const void* mem, size_t size, SkBitmap* bm) {
return false;
}
- bm->allocPixels(codec->getInfo());
+ // Construct a color table for the decode if necessary
+ sk_sp<SkColorTable> colorTable(nullptr);
+ SkPMColor* colorPtr = nullptr;
+ int* colorCountPtr = nullptr;
+ int maxColors = 256;
+ if (kIndex_8_SkColorType == codec->getInfo().colorType()) {
+ SkPMColor colors[256];
+ colorTable.reset(new SkColorTable(colors, maxColors));
+ colorPtr = const_cast<SkPMColor*>(colorTable->readColors());
+ colorCountPtr = &maxColors;
+ }
+
+ bm->allocPixels(codec->getInfo(), colorTable);
const SkCodec::Result result = codec->getPixels(codec->getInfo(), bm->getPixels(),
- bm->rowBytes());
+ bm->rowBytes(), nullptr, colorPtr, colorCountPtr);
return result == SkCodec::kSuccess || result == SkCodec::kIncompleteInput;
}
#endif // CodecPriv_DEFINED
diff --git a/tests/CodecTest.cpp b/tests/CodecTest.cpp
index 1d7d89aa34..7ec903728e 100644
--- a/tests/CodecTest.cpp
+++ b/tests/CodecTest.cpp
@@ -348,7 +348,8 @@ static void check(skiatest::Reporter* r,
SkIRect subset = SkIRect::MakeXYWH(2 * (width / 3), 0, width / 3, height);
options.fSubset = &subset;
- const auto partialStartResult = codec->startScanlineDecode(info, &options);
+ const SkCodec::Result partialStartResult = codec->startScanlineDecode(info, &options,
+ nullptr, nullptr);
REPORTER_ASSERT(r, partialStartResult == SkCodec::kSuccess);
for (int y = 0; y < height; y++) {
@@ -383,7 +384,8 @@ static void check(skiatest::Reporter* r,
SkImageInfo subsetInfo = info.makeWH(subset.width(), subset.height());
SkBitmap bm;
bm.allocPixels(subsetInfo);
- const auto result = codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes(), &opts);
+ const SkCodec::Result result = codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes(),
+ &opts, nullptr, nullptr);
if (supportsSubsetDecoding) {
if (expectedResult == SkCodec::kSuccess) {
@@ -666,6 +668,53 @@ DEF_TEST(Codec_Empty, r) {
#endif
}
+static void test_invalid_parameters(skiatest::Reporter* r, const char path[]) {
+ std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
+ if (!stream) {
+ return;
+ }
+ std::unique_ptr<SkCodec> decoder(SkCodec::NewFromStream(stream.release()));
+ if (!decoder) {
+ SkDebugf("Missing codec for %s\n", path);
+ return;
+ }
+
+ const SkImageInfo info = decoder->getInfo().makeColorType(kIndex_8_SkColorType);
+
+ // This should return kSuccess because kIndex8 is supported.
+ SkPMColor colorStorage[256];
+ int colorCount;
+ SkCodec::Result result = decoder->startScanlineDecode(info, nullptr, colorStorage,
+ &colorCount);
+ if (SkCodec::kSuccess == result) {
+ // This should return kInvalidParameters because, in kIndex_8 mode, we must pass in a valid
+ // colorPtr and a valid colorCountPtr.
+ result = decoder->startScanlineDecode(info, nullptr, nullptr, nullptr);
+ REPORTER_ASSERT(r, SkCodec::kInvalidParameters == result);
+ result = decoder->startScanlineDecode(info);
+ REPORTER_ASSERT(r, SkCodec::kInvalidParameters == result);
+ } else if (SkCodec::kUnimplemented == result) {
+ // New method should be supported:
+ SkBitmap bm;
+ bm.allocPixels(info, SkColorTable::Make(colorStorage, 256));
+ result = decoder->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes(), nullptr,
+ colorStorage, &colorCount);
+ REPORTER_ASSERT(r, SkCodec::kSuccess == result);
+ result = decoder->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes());
+ REPORTER_ASSERT(r, SkCodec::kInvalidParameters == result);
+ } else {
+ // The test is uninteresting if kIndex8 is not supported
+ ERRORF(r, "Should not call test_invalid_parameters for non-Index8 file: %s\n", path);
+ return;
+ }
+
+}
+
+DEF_TEST(Codec_Params, r) {
+ test_invalid_parameters(r, "index8.png");
+ test_invalid_parameters(r, "mandrill.wbmp");
+}
+
#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
#ifndef SK_PNG_DISABLE_TESTS // reading chunks does not work properly with older versions.
@@ -1053,8 +1102,14 @@ static bool alpha_type_match(SkAlphaType origAlphaType, SkAlphaType codecAlphaTy
static void check_round_trip(skiatest::Reporter* r, SkCodec* origCodec, const SkImageInfo& info) {
SkBitmap bm1;
- bm1.allocPixels(info);
- SkCodec::Result result = origCodec->getPixels(info, bm1.getPixels(), bm1.rowBytes());
+ SkPMColor colors[256];
+ sk_sp<SkColorTable> colorTable1 = SkColorTable::Make(colors, 256);
+ bm1.allocPixels(info, colorTable1);
+ int numColors;
+ SkCodec::Result result = origCodec->getPixels(info, bm1.getPixels(), bm1.rowBytes(), nullptr,
+ const_cast<SkPMColor*>(colorTable1->readColors()),
+ &numColors);
+ // This will fail to update colorTable1->count() but is fine for the purpose of this test.
REPORTER_ASSERT(r, SkCodec::kSuccess == result);
// Encode the image to png.
@@ -1066,8 +1121,10 @@ static void check_round_trip(skiatest::Reporter* r, SkCodec* origCodec, const Sk
REPORTER_ASSERT(r, alpha_type_match(info.alphaType(), codec->getInfo().alphaType()));
SkBitmap bm2;
- bm2.allocPixels(info);
- result = codec->getPixels(info, bm2.getPixels(), bm2.rowBytes());
+ sk_sp<SkColorTable> colorTable2 = SkColorTable::Make(colors, 256);
+ bm2.allocPixels(info, colorTable2);
+ result = codec->getPixels(info, bm2.getPixels(), bm2.rowBytes(), nullptr,
+ const_cast<SkPMColor*>(colorTable2->readColors()), &numColors);
REPORTER_ASSERT(r, SkCodec::kSuccess == result);
SkMD5::Digest d1, d2;
@@ -1194,7 +1251,7 @@ static void decode_frame(skiatest::Reporter* r, SkCodec* codec, size_t frame) {
SkCodec::Options opts;
opts.fFrameIndex = frame;
REPORTER_ASSERT(r, SkCodec::kSuccess == codec->getPixels(info,
- bm.getPixels(), bm.rowBytes(), &opts));
+ bm.getPixels(), bm.rowBytes(), &opts, nullptr, nullptr));
}
// For an animated GIF, we should only read enough to decode frame 0 if the
diff --git a/tests/GifTest.cpp b/tests/GifTest.cpp
index 7728d27dcb..0168d89d83 100644
--- a/tests/GifTest.cpp
+++ b/tests/GifTest.cpp
@@ -238,11 +238,25 @@ DEF_TEST(Gif_Sampled, r) {
return;
}
+ // Construct a color table for the decode if necessary
+ sk_sp<SkColorTable> colorTable(nullptr);
+ SkPMColor* colorPtr = nullptr;
+ int* colorCountPtr = nullptr;
+ int maxColors = 256;
+ if (kIndex_8_SkColorType == codec->getInfo().colorType()) {
+ SkPMColor colors[256];
+ colorTable.reset(new SkColorTable(colors, maxColors));
+ colorPtr = const_cast<SkPMColor*>(colorTable->readColors());
+ colorCountPtr = &maxColors;
+ }
+
SkAndroidCodec::AndroidOptions options;
options.fSampleSize = 4;
+ options.fColorPtr = colorPtr;
+ options.fColorCount = colorCountPtr;
SkBitmap bm;
- bm.allocPixels(codec->getInfo());
+ bm.allocPixels(codec->getInfo(), colorTable);
const SkCodec::Result result = codec->getAndroidPixels(codec->getInfo(), bm.getPixels(),
bm.rowBytes(), &options);
REPORTER_ASSERT(r, result == SkCodec::kSuccess);
@@ -259,3 +273,41 @@ DEF_TEST(Codec_GifTruncated, r) {
std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data));
REPORTER_ASSERT(r, !codec);
}
+
+// There was a bug where SkAndroidCodec::computeOutputColorType returned kIndex_8 for
+// GIFs that did not support kIndex_8. Verify that for such an image, the method computes
+// something that it can actually decode to.
+DEF_TEST(Codec_GifIndex8, r) {
+ std::unique_ptr<SkStream> stream(GetResourceAsStream("randPixelsOffset.gif"));
+ if (!stream) {
+ return;
+ }
+
+ std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(stream.release()));
+ REPORTER_ASSERT(r, codec);
+ if (!codec) {
+ return;
+ }
+
+ REPORTER_ASSERT(r, codec->getInfo().colorType() == kN32_SkColorType);
+ const SkColorType outputColorType = codec->computeOutputColorType(kN32_SkColorType);
+ REPORTER_ASSERT(r, outputColorType == kN32_SkColorType);
+
+ SkAndroidCodec::AndroidOptions options;
+ sk_sp<SkColorTable> colorTable(nullptr);
+ int maxColors = 256;
+ if (kIndex_8_SkColorType == outputColorType) {
+ SkPMColor colors[256];
+ colorTable.reset(new SkColorTable(colors, maxColors));
+ options.fColorPtr = const_cast<SkPMColor*>(colorTable->readColors());
+ options.fColorCount = &maxColors;
+ }
+
+ auto info = codec->getInfo().makeColorType(outputColorType);
+ SkBitmap bm;
+ bm.setInfo(info);
+ bm.allocPixels(colorTable.get());
+
+ REPORTER_ASSERT(r, SkCodec::kSuccess == codec->getAndroidPixels(info, bm.getPixels(),
+ bm.rowBytes(), &options));
+}
diff --git a/third_party/gif/SkGifImageReader.cpp b/third_party/gif/SkGifImageReader.cpp
index e76028dac9..76f3edcef0 100644
--- a/third_party/gif/SkGifImageReader.cpp
+++ b/third_party/gif/SkGifImageReader.cpp
@@ -762,11 +762,13 @@ bool SkGifImageReader::parse(SkGifImageReader::SkGIFParseQuery query)
isLocalColormapDefined, numColors))
{
m_firstFrameHasAlpha = true;
+ m_firstFrameSupportsIndex8 = true;
} else {
const bool frameIsSubset = xOffset > 0 || yOffset > 0
|| width < fScreenWidth
|| height < fScreenHeight;
m_firstFrameHasAlpha = frameIsSubset;
+ m_firstFrameSupportsIndex8 = !frameIsSubset;
}
}
diff --git a/third_party/gif/SkGifImageReader.h b/third_party/gif/SkGifImageReader.h
index 49655c23df..f105a1de87 100644
--- a/third_party/gif/SkGifImageReader.h
+++ b/third_party/gif/SkGifImageReader.h
@@ -289,6 +289,7 @@ public:
, m_streamBuffer(stream)
, m_parseCompleted(false)
, m_firstFrameHasAlpha(false)
+ , m_firstFrameSupportsIndex8(false)
{
}
@@ -366,6 +367,8 @@ public:
bool firstFrameHasAlpha() const { return m_firstFrameHasAlpha; }
+ bool firstFrameSupportsIndex8() const { return m_firstFrameSupportsIndex8; }
+
// Helper function that returns whether an SkGIFFrameContext has transparency.
// This method is sometimes called before creating one/parsing its color map,
// so it cannot rely on SkGIFFrameContext::transparentPixel or ::localColorMap().
@@ -407,9 +410,10 @@ private:
SkStreamBuffer m_streamBuffer;
bool m_parseCompleted;
- // This value can be computed before we create a SkGIFFrameContext, so we
- // store it here instead of on m_frames[0].
+ // These values can be computed before we create a SkGIFFrameContext, so we
+ // store them here instead of on m_frames[0].
bool m_firstFrameHasAlpha;
+ bool m_firstFrameSupportsIndex8;
};
#endif