aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar scroggo@google.com <scroggo@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-07-18 19:34:49 +0000
committerGravatar scroggo@google.com <scroggo@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-07-18 19:34:49 +0000
commitf698c8262df397a7015662e91b1a727e1134c418 (patch)
treeadd081995101cc29152b343ab037b4adaf9c8950
parent97e49e97b6379cd2abb6c55b9e16246db0dad552 (diff)
Allow decoding JPEG into A8.
If the original image is grayscale, allow decoding into A8. Change the size of PrefConfigTable to allow for 8bit gray, a new source config. Add a new sampler to SkScaledBitmapSampler to 'convert' to A8. FIXME: Should there be a dithered option for gray scale? R=reed@google.com Review URL: https://codereview.chromium.org/18083026 git-svn-id: http://skia.googlecode.com/svn/trunk@10157 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--include/core/SkImageDecoder.h87
-rw-r--r--src/images/SkImageDecoder.cpp26
-rw-r--r--src/images/SkImageDecoder_libjpeg.cpp20
-rw-r--r--src/images/SkScaledBitmapSampler.cpp22
-rw-r--r--tests/ImageDecodingTest.cpp41
5 files changed, 156 insertions, 40 deletions
diff --git a/include/core/SkImageDecoder.h b/include/core/SkImageDecoder.h
index 191fe53f4c..7bdaaa8eb5 100644
--- a/include/core/SkImageDecoder.h
+++ b/include/core/SkImageDecoder.h
@@ -139,14 +139,17 @@ public:
Chooser* getChooser() const { return fChooser; }
Chooser* setChooser(Chooser*);
- /** This optional table describes the caller's preferred config based on
+ /**
+ @Deprecated. Use the struct version instead.
+
+ This optional table describes the caller's preferred config based on
information about the src data. For this table, the src attributes are
described in terms of depth (index (8), 16, 32/24) and if there is
per-pixel alpha. These inputs combine to create an index into the
pref[] table, which contains the caller's preferred config for that
input, or kNo_Config if there is no preference.
- To specify no preferrence, call setPrefConfigTable(NULL), which is
+ To specify no preference, call setPrefConfigTable(NULL), which is
the default.
Note, it is still at the discretion of the codec as to what output
@@ -166,6 +169,50 @@ public:
*/
void setPrefConfigTable(const SkBitmap::Config pref[6]);
+ /**
+ * Optional table describing the caller's preferred config based on
+ * information about the src data. Each field should be set to the
+ * preferred config for a src described in the name of the field. The
+ * src attributes are described in terms of depth (8-index,
+ * 8bit-grayscale, or 8-bits/component) and whether there is per-pixel
+ * alpha (does not apply to grayscale). If the caller has no preference
+ * for a particular src type, its slot should be set to kNo_Config.
+ *
+ * NOTE ABOUT PREFERRED CONFIGS:
+ * If a config is preferred, either using a pref table or as a parameter
+ * to some flavor of decode, it is still at the discretion of the codec
+ * as to what output config is actually returned, as it may not be able
+ * to support the caller's preference.
+ *
+ * If a bitmap is decoded into SkBitmap::A8_Config, the resulting bitmap
+ * will either be a conversion of the grayscale in the case of a
+ * grayscale source or the alpha channel in the case of a source with
+ * an alpha channel.
+ */
+ struct PrefConfigTable {
+ SkBitmap::Config fPrefFor_8Index_NoAlpha_src;
+ SkBitmap::Config fPrefFor_8Index_YesAlpha_src;
+ SkBitmap::Config fPrefFor_8Gray_src;
+ SkBitmap::Config fPrefFor_8bpc_NoAlpha_src;
+ SkBitmap::Config fPrefFor_8bpc_YesAlpha_src;
+ };
+
+ /**
+ * Set an optional table for specifying the caller's preferred config
+ * based on information about the src data.
+ *
+ * The default is no preference, which will assume the config set by
+ * decode is preferred.
+ */
+ void setPrefConfigTable(const PrefConfigTable&);
+
+ /**
+ * Do not use a PrefConfigTable to determine the output config. This
+ * is the default, so there is no need to call unless a PrefConfigTable
+ * was previously set.
+ */
+ void resetPrefConfigTable() { fUsePrefTable = false; }
+
SkBitmap::Allocator* getAllocator() const { return fAllocator; }
SkBitmap::Allocator* setAllocator(SkBitmap::Allocator*);
@@ -269,12 +316,8 @@ public:
/** Decode the image stored in the specified file, and store the result
in bitmap. Return true for success or false on failure.
- If pref is kNo_Config, then the decoder is free to choose the most natural
- config given the image data. If pref something other than kNo_Config,
- the decoder will attempt to decode the image into that format, unless
- there is a conflict (e.g. the image has per-pixel alpha and the bitmap's
- config does not support that), in which case the decoder will choose a
- closest match configuration.
+ @param prefConfig If the PrefConfigTable is not set, prefer this config.
+ See NOTE ABOUT PREFERRED CONFIGS.
@param format On success, if format is non-null, it is set to the format
of the decoded file. On failure it is ignored.
@@ -289,12 +332,8 @@ public:
/** Decode the image stored in the specified memory buffer, and store the
result in bitmap. Return true for success or false on failure.
- If pref is kNo_Config, then the decoder is free to choose the most natural
- config given the image data. If pref something other than kNo_Config,
- the decoder will attempt to decode the image into that format, unless
- there is a conflict (e.g. the image has per-pixel alpha and the bitmap's
- config does not support that), in which case the decoder will choose a
- closest match configuration.
+ @param prefConfig If the PrefConfigTable is not set, prefer this config.
+ See NOTE ABOUT PREFERRED CONFIGS.
@param format On success, if format is non-null, it is set to the format
of the decoded buffer. On failure it is ignored.
@@ -337,12 +376,8 @@ public:
/** Decode the image stored in the specified SkStream, and store the result
in bitmap. Return true for success or false on failure.
- If pref is kNo_Config, then the decoder is free to choose the most
- natural config given the image data. If pref something other than
- kNo_Config, the decoder will attempt to decode the image into that
- format, unless there is a conflict (e.g. the image has per-pixel alpha
- and the bitmap's config does not support that), in which case the
- decoder will choose a closest match configuration.
+ @param prefConfig If the PrefConfigTable is not set, prefer this config.
+ See NOTE ABOUT PREFERRED CONFIGS.
@param format On success, if format is non-null, it is set to the format
of the decoded stream. On failure it is ignored.
@@ -441,10 +476,16 @@ protected:
*/
bool allocPixelRef(SkBitmap*, SkColorTable*) const;
+ /**
+ * The raw data of the src image.
+ */
enum SrcDepth {
+ // Color-indexed.
kIndex_SrcDepth,
- k16Bit_SrcDepth,
- k32Bit_SrcDepth
+ // Grayscale in 8 bits.
+ k8BitGray_SrcDepth,
+ // 8 bits per component. Used for 24 bit if there is no alpha.
+ k32Bit_SrcDepth,
};
/** The subclass, inside onDecode(), calls this to determine the config of
the returned bitmap. SrcDepth and hasAlpha reflect the raw data of the
@@ -462,7 +503,7 @@ private:
SkBitmap::Allocator* fAllocator;
int fSampleSize;
SkBitmap::Config fDefaultPref; // use if fUsePrefTable is false
- SkBitmap::Config fPrefTable[6]; // use if fUsePrefTable is true
+ PrefConfigTable fPrefTable; // use if fUsePrefTable is true
bool fDitherImage;
bool fUsePrefTable;
mutable bool fShouldCancelDecode;
diff --git a/src/images/SkImageDecoder.cpp b/src/images/SkImageDecoder.cpp
index 5d94bb1c2d..ccdc6dfcde 100644
--- a/src/images/SkImageDecoder.cpp
+++ b/src/images/SkImageDecoder.cpp
@@ -145,31 +145,37 @@ void SkImageDecoder::setPrefConfigTable(const SkBitmap::Config pref[6]) {
fUsePrefTable = false;
} else {
fUsePrefTable = true;
- memcpy(fPrefTable, pref, sizeof(fPrefTable));
+ fPrefTable.fPrefFor_8Index_NoAlpha_src = pref[0];
+ fPrefTable.fPrefFor_8Index_YesAlpha_src = pref[1];
+ fPrefTable.fPrefFor_8Gray_src = SkBitmap::kNo_Config;
+ fPrefTable.fPrefFor_8bpc_NoAlpha_src = pref[4];
+ fPrefTable.fPrefFor_8bpc_YesAlpha_src = pref[5];
}
}
+void SkImageDecoder::setPrefConfigTable(const PrefConfigTable& prefTable) {
+ fUsePrefTable = true;
+ fPrefTable = prefTable;
+}
+
SkBitmap::Config SkImageDecoder::getPrefConfig(SrcDepth srcDepth,
bool srcHasAlpha) const {
SkBitmap::Config config;
if (fUsePrefTable) {
- int index = 0;
switch (srcDepth) {
case kIndex_SrcDepth:
- index = 0;
+ config = srcHasAlpha ? fPrefTable.fPrefFor_8Index_YesAlpha_src
+ : fPrefTable.fPrefFor_8Index_NoAlpha_src;
break;
- case k16Bit_SrcDepth:
- index = 2;
+ case k8BitGray_SrcDepth:
+ config = fPrefTable.fPrefFor_8Gray_src;
break;
case k32Bit_SrcDepth:
- index = 4;
+ config = srcHasAlpha ? fPrefTable.fPrefFor_8bpc_YesAlpha_src
+ : fPrefTable.fPrefFor_8bpc_NoAlpha_src;
break;
}
- if (srcHasAlpha) {
- index += 1;
- }
- config = fPrefTable[index];
} else {
config = fDefaultPref;
}
diff --git a/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp
index bea1e98641..e4d7ace0d4 100644
--- a/src/images/SkImageDecoder_libjpeg.cpp
+++ b/src/images/SkImageDecoder_libjpeg.cpp
@@ -289,21 +289,33 @@ bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
/* this gives another few percents */
cinfo.do_block_smoothing = 0;
+ SrcDepth srcDepth = k32Bit_SrcDepth;
/* default format is RGB */
if (cinfo.jpeg_color_space == JCS_CMYK) {
// libjpeg cannot convert from CMYK to RGB - here we set up
// so libjpeg will give us CMYK samples back and we will
// later manually convert them to RGB
cinfo.out_color_space = JCS_CMYK;
+ } else if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
+ cinfo.out_color_space = JCS_GRAYSCALE;
+ srcDepth = k8BitGray_SrcDepth;
} else {
cinfo.out_color_space = JCS_RGB;
}
- SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, false);
+ SkBitmap::Config config = this->getPrefConfig(srcDepth, false);
// only these make sense for jpegs
- if (config != SkBitmap::kARGB_8888_Config &&
- config != SkBitmap::kARGB_4444_Config &&
- config != SkBitmap::kRGB_565_Config) {
+ if (SkBitmap::kA8_Config == config) {
+ if (cinfo.jpeg_color_space != JCS_GRAYSCALE) {
+ // Converting from a non grayscale image to A8 is
+ // not currently supported.
+ config = SkBitmap::kARGB_8888_Config;
+ // Change the output from jpeg back to RGB.
+ cinfo.out_color_space = JCS_RGB;
+ }
+ } else if (config != SkBitmap::kARGB_8888_Config &&
+ config != SkBitmap::kARGB_4444_Config &&
+ config != SkBitmap::kRGB_565_Config) {
config = SkBitmap::kARGB_8888_Config;
}
diff --git a/src/images/SkScaledBitmapSampler.cpp b/src/images/SkScaledBitmapSampler.cpp
index ca41de9396..021f86ba86 100644
--- a/src/images/SkScaledBitmapSampler.cpp
+++ b/src/images/SkScaledBitmapSampler.cpp
@@ -289,6 +289,15 @@ static bool Sample_Index_DI(void* SK_RESTRICT dstRow,
return false;
}
+// A8
+static bool Sample_Gray_DA8(void* SK_RESTRICT dstRow,
+ const uint8_t* SK_RESTRICT src,
+ int width, int deltaSrc, int,
+ const SkPMColor[]) {
+ memcpy(dstRow, src, width);
+ return true;
+}
+
// 8888 Unpremul
static bool Sample_Gray_D8888_Unpremul(void* SK_RESTRICT dstRow,
@@ -396,6 +405,12 @@ bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc, bool dither,
NULL, NULL,
Sample_Index_DI, Sample_Index_DI,
NULL, NULL,
+ // A8
+ Sample_Gray_DA8, Sample_Gray_DA8,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
// 8888 Unpremul (no dither distinction)
Sample_Gray_D8888_Unpremul, Sample_Gray_D8888_Unpremul,
Sample_RGBx_D8888, Sample_RGBx_D8888,
@@ -405,7 +420,7 @@ bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc, bool dither,
};
// The jump between dst configs in the table
static const int gProcDstConfigSpan = 10;
- SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gProcs) == 5 * gProcDstConfigSpan,
+ SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gProcs) == 6 * gProcDstConfigSpan,
gProcs_has_the_wrong_number_of_entries);
fCTable = ctable;
@@ -456,6 +471,9 @@ bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc, bool dither,
case SkBitmap::kIndex8_Config:
index += 3 * gProcDstConfigSpan;
break;
+ case SkBitmap::kA8_Config:
+ index += 4 * gProcDstConfigSpan;
+ break;
default:
return false;
}
@@ -464,7 +482,7 @@ bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc, bool dither,
if (dst->config() != SkBitmap::kARGB_8888_Config) {
return false;
}
- index += 4 * gProcDstConfigSpan;
+ index += 5 * gProcDstConfigSpan;
}
fRowProc = gProcs[index];
diff --git a/tests/ImageDecodingTest.cpp b/tests/ImageDecodingTest.cpp
index 9a5d45bcfa..76308c9d97 100644
--- a/tests/ImageDecodingTest.cpp
+++ b/tests/ImageDecodingTest.cpp
@@ -132,7 +132,41 @@ static void compare_unpremul(skiatest::Reporter* reporter, const SkString& filen
}
}
-static void test_imageDecodingTests(skiatest::Reporter* reporter) {
+// Create a fake ImageDecoder to test setPrefConfigTable for
+// backwards compatibility.
+class PrefConfigTestingImageDecoder : public SkImageDecoder {
+public:
+ void testPrefConfigTable(skiatest::Reporter* reporter) {
+ // Arbitrary list of Configs. The important thing about
+ // the list is that each one is different, so we can test
+ // to make sure the correct config is chosen.
+ const SkBitmap::Config configs[] = {
+ SkBitmap::kA1_Config,
+ SkBitmap::kA8_Config,
+ SkBitmap::kIndex8_Config,
+ SkBitmap::kRGB_565_Config,
+ SkBitmap::kARGB_4444_Config,
+ SkBitmap::kARGB_8888_Config,
+ };
+ this->setPrefConfigTable(configs);
+ REPORTER_ASSERT(reporter, configs[0] == this->getPrefConfig(kIndex_SrcDepth, false));
+ REPORTER_ASSERT(reporter, configs[1] == this->getPrefConfig(kIndex_SrcDepth, true));
+ REPORTER_ASSERT(reporter, configs[4] == this->getPrefConfig(k32Bit_SrcDepth, false));
+ REPORTER_ASSERT(reporter, configs[5] == this->getPrefConfig(k32Bit_SrcDepth, true));
+ }
+
+protected:
+ virtual bool onDecode(SkStream*, SkBitmap* bitmap, Mode) SK_OVERRIDE {
+ return false;
+ }
+};
+
+static void test_pref_config_table(skiatest::Reporter* reporter) {
+ PrefConfigTestingImageDecoder decoder;
+ decoder.testPrefConfigTable(reporter);
+}
+
+static void test_unpremul(skiatest::Reporter* reporter) {
// This test cannot run if there is no resource path.
SkString resourcePath = skiatest::Test::GetResourcePath();
if (resourcePath.isEmpty()) {
@@ -151,6 +185,11 @@ static void test_imageDecodingTests(skiatest::Reporter* reporter) {
}
}
+static void test_imageDecodingTests(skiatest::Reporter* reporter) {
+ test_unpremul(reporter);
+ test_pref_config_table(reporter);
+}
+
#include "TestClassDef.h"
DEFINE_TESTCLASS("ImageDecoding", ImageDecodingTestClass,
test_imageDecodingTests)