SkPngCodec: Add support for 16-bit pngs (step 1)
Android plans to use 16-bit png to encode higher precision assets. This CL should not change any behavior or cause diffs on Gold. It simply moves the 16-bit -> 8-bit strip from libpng to SkSwizzler. As a follow-up, I plan to add support for 16-bit input to SkColorSpaceXform. This will require a new swizzler function that just samples or subsets 16-bit values (but does not strip to 8-bit). An alternative implementation could avoid the additional swizzler functions by deciding whether or not to call png_set_strip() at decode time (we would still need the swizzler fn to sample/subset 16-bit values). I find this strategy to be cleaner than that. I would rather handle 16-bit rgb(a) all the time than *some* of the time. And this is implementation is also more efficient than libpng. Though it is also more skia code. Gray and gray alpha are left alone until I know whether anyone wants high precision gray support. b/32984164 Change-Id: I44e307473526de3f4bba06879c5fffa25d480f56 Reviewed-on: https://skia-review.googlesource.com/6020 Reviewed-by: Leon Scroggins <scroggo@google.com> Commit-Queue: Matt Sarett <msarett@google.com>
This commit is contained in:
parent
ff11428526
commit
7a1cc6766d
@ -874,17 +874,17 @@ static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader, SkCodec
|
|||||||
// FIXME (scroggo): Once SK_GOOGLE3_PNG_HACK is no more, this method can be inline in
|
// FIXME (scroggo): Once SK_GOOGLE3_PNG_HACK is no more, this method can be inline in
|
||||||
// AutoCleanPng::infoCallback
|
// AutoCleanPng::infoCallback
|
||||||
static void general_info_callback(png_structp png_ptr, png_infop info_ptr,
|
static void general_info_callback(png_structp png_ptr, png_infop info_ptr,
|
||||||
SkEncodedInfo::Color* outColor, SkEncodedInfo::Alpha* outAlpha) {
|
SkEncodedInfo::Color* outColor, SkEncodedInfo::Alpha* outAlpha,
|
||||||
|
int* outBitDepth) {
|
||||||
png_uint_32 origWidth, origHeight;
|
png_uint_32 origWidth, origHeight;
|
||||||
int bitDepth, encodedColorType;
|
int bitDepth, encodedColorType;
|
||||||
png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
|
png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,
|
||||||
&encodedColorType, nullptr, nullptr, nullptr);
|
&encodedColorType, nullptr, nullptr, nullptr);
|
||||||
|
|
||||||
// Tell libpng to strip 16 bit/color files down to 8 bits/color.
|
// TODO: Should we support 16-bits of precision for gray images?
|
||||||
// TODO: Should we handle this in SkSwizzler? Could this also benefit
|
if (bitDepth == 16 && (PNG_COLOR_TYPE_GRAY == encodedColorType ||
|
||||||
// RAW decodes?
|
PNG_COLOR_TYPE_GRAY_ALPHA == encodedColorType)) {
|
||||||
if (bitDepth == 16) {
|
bitDepth = 8;
|
||||||
SkASSERT(PNG_COLOR_TYPE_PALETTE != encodedColorType);
|
|
||||||
png_set_strip_16(png_ptr);
|
png_set_strip_16(png_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -899,6 +899,7 @@ static void general_info_callback(png_structp png_ptr, png_infop info_ptr,
|
|||||||
// byte into separate bytes (useful for paletted and grayscale images).
|
// byte into separate bytes (useful for paletted and grayscale images).
|
||||||
if (bitDepth < 8) {
|
if (bitDepth < 8) {
|
||||||
// TODO: Should we use SkSwizzler here?
|
// TODO: Should we use SkSwizzler here?
|
||||||
|
bitDepth = 8;
|
||||||
png_set_packing(png_ptr);
|
png_set_packing(png_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -922,6 +923,7 @@ static void general_info_callback(png_structp png_ptr, png_infop info_ptr,
|
|||||||
// Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel.
|
// Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel.
|
||||||
if (bitDepth < 8) {
|
if (bitDepth < 8) {
|
||||||
// TODO: Should we use SkSwizzler here?
|
// TODO: Should we use SkSwizzler here?
|
||||||
|
bitDepth = 8;
|
||||||
png_set_expand_gray_1_2_4_to_8(png_ptr);
|
png_set_expand_gray_1_2_4_to_8(png_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -954,11 +956,14 @@ static void general_info_callback(png_structp png_ptr, png_infop info_ptr,
|
|||||||
if (outAlpha) {
|
if (outAlpha) {
|
||||||
*outAlpha = alpha;
|
*outAlpha = alpha;
|
||||||
}
|
}
|
||||||
|
if (outBitDepth) {
|
||||||
|
*outBitDepth = bitDepth;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SK_GOOGLE3_PNG_HACK
|
#ifdef SK_GOOGLE3_PNG_HACK
|
||||||
void SkPngCodec::rereadInfoCallback() {
|
void SkPngCodec::rereadInfoCallback() {
|
||||||
general_info_callback(fPng_ptr, fInfo_ptr, nullptr, nullptr);
|
general_info_callback(fPng_ptr, fInfo_ptr, nullptr, nullptr, nullptr);
|
||||||
png_set_interlace_handling(fPng_ptr);
|
png_set_interlace_handling(fPng_ptr);
|
||||||
png_read_update_info(fPng_ptr, fInfo_ptr);
|
png_read_update_info(fPng_ptr, fInfo_ptr);
|
||||||
}
|
}
|
||||||
@ -967,7 +972,8 @@ void SkPngCodec::rereadInfoCallback() {
|
|||||||
void AutoCleanPng::infoCallback() {
|
void AutoCleanPng::infoCallback() {
|
||||||
SkEncodedInfo::Color color;
|
SkEncodedInfo::Color color;
|
||||||
SkEncodedInfo::Alpha alpha;
|
SkEncodedInfo::Alpha alpha;
|
||||||
general_info_callback(fPng_ptr, fInfo_ptr, &color, &alpha);
|
int bitDepth;
|
||||||
|
general_info_callback(fPng_ptr, fInfo_ptr, &color, &alpha, &bitDepth);
|
||||||
|
|
||||||
const int numberPasses = png_set_interlace_handling(fPng_ptr);
|
const int numberPasses = png_set_interlace_handling(fPng_ptr);
|
||||||
|
|
||||||
@ -990,7 +996,7 @@ void AutoCleanPng::infoCallback() {
|
|||||||
colorSpace = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
|
colorSpace = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
|
||||||
}
|
}
|
||||||
|
|
||||||
SkEncodedInfo encodedInfo = SkEncodedInfo::Make(color, alpha, 8);
|
SkEncodedInfo encodedInfo = SkEncodedInfo::Make(color, alpha, bitDepth);
|
||||||
// FIXME (scroggo): Once we get rid of SK_GOOGLE3_PNG_HACK, general_info_callback can
|
// FIXME (scroggo): Once we get rid of SK_GOOGLE3_PNG_HACK, general_info_callback can
|
||||||
// be inlined, so these values will already be set.
|
// be inlined, so these values will already be set.
|
||||||
png_uint_32 origWidth = png_get_image_width(fPng_ptr, fInfo_ptr);
|
png_uint_32 origWidth = png_get_image_width(fPng_ptr, fInfo_ptr);
|
||||||
@ -1072,9 +1078,9 @@ bool SkPngCodec::initializeXforms(const SkImageInfo& dstInfo, const Options& opt
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the image is RGBA and we have a color xform, we can skip the swizzler.
|
// If the image is 32-bit RGBA and we have a color xform, we can skip the swizzler.
|
||||||
if (this->colorXform() && SkEncodedInfo::kRGBA_Color == this->getEncodedInfo().color() &&
|
if (this->colorXform() && SkEncodedInfo::kRGBA_Color == this->getEncodedInfo().color() &&
|
||||||
!options.fSubset)
|
8 == this->getEncodedInfo().bitsPerComponent() && !options.fSubset)
|
||||||
{
|
{
|
||||||
fXformMode = kColorOnly_XformMode;
|
fXformMode = kColorOnly_XformMode;
|
||||||
return true;
|
return true;
|
||||||
|
@ -524,6 +524,113 @@ static void fast_swizzle_rgba_to_bgra_unpremul(
|
|||||||
SkOpts::RGBA_to_BGRA((uint32_t*) dst, src + offset, width);
|
SkOpts::RGBA_to_BGRA((uint32_t*) dst, src + offset, width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 16-bits per component kRGB and kRGBA
|
||||||
|
|
||||||
|
static void swizzle_rgb16_to_rgba(
|
||||||
|
void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset,
|
||||||
|
const SkPMColor ctable[]) {
|
||||||
|
auto strip16to8 = [](const uint8_t* ptr) {
|
||||||
|
return 0xFF000000 | (ptr[4] << 16) | (ptr[2] << 8) | ptr[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
src += offset;
|
||||||
|
uint32_t* dst32 = (uint32_t*) dst;
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
dst32[x] = strip16to8(src);
|
||||||
|
src += deltaSrc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void swizzle_rgb16_to_bgra(
|
||||||
|
void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset,
|
||||||
|
const SkPMColor ctable[]) {
|
||||||
|
auto strip16to8 = [](const uint8_t* ptr) {
|
||||||
|
return 0xFF000000 | (ptr[0] << 16) | (ptr[2] << 8) | ptr[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
src += offset;
|
||||||
|
uint32_t* dst32 = (uint32_t*) dst;
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
dst32[x] = strip16to8(src);
|
||||||
|
src += deltaSrc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void swizzle_rgb16_to_565(
|
||||||
|
void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset,
|
||||||
|
const SkPMColor ctable[]) {
|
||||||
|
auto strip16to565 = [](const uint8_t* ptr) {
|
||||||
|
return SkPack888ToRGB16(ptr[0], ptr[2], ptr[4]);
|
||||||
|
};
|
||||||
|
|
||||||
|
src += offset;
|
||||||
|
uint16_t* dst16 = (uint16_t*) dst;
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
dst16[x] = strip16to565(src);
|
||||||
|
src += deltaSrc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void swizzle_rgba16_to_rgba_unpremul(
|
||||||
|
void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset,
|
||||||
|
const SkPMColor ctable[]) {
|
||||||
|
auto strip16to8 = [](const uint8_t* ptr) {
|
||||||
|
return (ptr[6] << 24) | (ptr[4] << 16) | (ptr[2] << 8) | ptr[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
src += offset;
|
||||||
|
uint32_t* dst32 = (uint32_t*) dst;
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
dst32[x] = strip16to8(src);
|
||||||
|
src += deltaSrc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void swizzle_rgba16_to_rgba_premul(
|
||||||
|
void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset,
|
||||||
|
const SkPMColor ctable[]) {
|
||||||
|
auto stripAndPremul16to8 = [](const uint8_t* ptr) {
|
||||||
|
return premultiply_argb_as_rgba(ptr[6], ptr[0], ptr[2], ptr[4]);
|
||||||
|
};
|
||||||
|
|
||||||
|
src += offset;
|
||||||
|
uint32_t* dst32 = (uint32_t*) dst;
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
dst32[x] = stripAndPremul16to8(src);
|
||||||
|
src += deltaSrc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void swizzle_rgba16_to_bgra_unpremul(
|
||||||
|
void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset,
|
||||||
|
const SkPMColor ctable[]) {
|
||||||
|
auto strip16to8 = [](const uint8_t* ptr) {
|
||||||
|
return (ptr[6] << 24) | (ptr[0] << 16) | (ptr[2] << 8) | ptr[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
src += offset;
|
||||||
|
uint32_t* dst32 = (uint32_t*) dst;
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
dst32[x] = strip16to8(src);
|
||||||
|
src += deltaSrc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void swizzle_rgba16_to_bgra_premul(
|
||||||
|
void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset,
|
||||||
|
const SkPMColor ctable[]) {
|
||||||
|
auto stripAndPremul16to8 = [](const uint8_t* ptr) {
|
||||||
|
return premultiply_argb_as_bgra(ptr[6], ptr[0], ptr[2], ptr[4]);
|
||||||
|
};
|
||||||
|
|
||||||
|
src += offset;
|
||||||
|
uint32_t* dst32 = (uint32_t*) dst;
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
dst32[x] = stripAndPremul16to8(src);
|
||||||
|
src += deltaSrc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// kCMYK
|
// kCMYK
|
||||||
//
|
//
|
||||||
// CMYK is stored as four bytes per pixel.
|
// CMYK is stored as four bytes per pixel.
|
||||||
@ -839,14 +946,31 @@ SkSwizzler* SkSwizzler::CreateSwizzler(const SkEncodedInfo& encodedInfo,
|
|||||||
case SkEncodedInfo::kRGB_Color:
|
case SkEncodedInfo::kRGB_Color:
|
||||||
switch (dstInfo.colorType()) {
|
switch (dstInfo.colorType()) {
|
||||||
case kRGBA_8888_SkColorType:
|
case kRGBA_8888_SkColorType:
|
||||||
|
if (16 == encodedInfo.bitsPerComponent()) {
|
||||||
|
proc = &swizzle_rgb16_to_rgba;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
SkASSERT(8 == encodedInfo.bitsPerComponent());
|
||||||
proc = &swizzle_rgb_to_rgba;
|
proc = &swizzle_rgb_to_rgba;
|
||||||
fastProc = &fast_swizzle_rgb_to_rgba;
|
fastProc = &fast_swizzle_rgb_to_rgba;
|
||||||
break;
|
break;
|
||||||
case kBGRA_8888_SkColorType:
|
case kBGRA_8888_SkColorType:
|
||||||
|
if (16 == encodedInfo.bitsPerComponent()) {
|
||||||
|
proc = &swizzle_rgb16_to_bgra;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
SkASSERT(8 == encodedInfo.bitsPerComponent());
|
||||||
proc = &swizzle_rgb_to_bgra;
|
proc = &swizzle_rgb_to_bgra;
|
||||||
fastProc = &fast_swizzle_rgb_to_bgra;
|
fastProc = &fast_swizzle_rgb_to_bgra;
|
||||||
break;
|
break;
|
||||||
case kRGB_565_SkColorType:
|
case kRGB_565_SkColorType:
|
||||||
|
if (16 == encodedInfo.bitsPerComponent()) {
|
||||||
|
proc = &swizzle_rgb16_to_565;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
proc = &swizzle_rgb_to_565;
|
proc = &swizzle_rgb_to_565;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -856,6 +980,13 @@ SkSwizzler* SkSwizzler::CreateSwizzler(const SkEncodedInfo& encodedInfo,
|
|||||||
case SkEncodedInfo::kRGBA_Color:
|
case SkEncodedInfo::kRGBA_Color:
|
||||||
switch (dstInfo.colorType()) {
|
switch (dstInfo.colorType()) {
|
||||||
case kRGBA_8888_SkColorType:
|
case kRGBA_8888_SkColorType:
|
||||||
|
if (16 == encodedInfo.bitsPerComponent()) {
|
||||||
|
proc = premultiply ? &swizzle_rgba16_to_rgba_premul :
|
||||||
|
&swizzle_rgba16_to_rgba_unpremul;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
SkASSERT(8 == encodedInfo.bitsPerComponent());
|
||||||
if (premultiply) {
|
if (premultiply) {
|
||||||
if (SkCodec::kYes_ZeroInitialized == zeroInit) {
|
if (SkCodec::kYes_ZeroInitialized == zeroInit) {
|
||||||
proc = &SkipLeading8888ZerosThen<swizzle_rgba_to_rgba_premul>;
|
proc = &SkipLeading8888ZerosThen<swizzle_rgba_to_rgba_premul>;
|
||||||
@ -876,6 +1007,13 @@ SkSwizzler* SkSwizzler::CreateSwizzler(const SkEncodedInfo& encodedInfo,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case kBGRA_8888_SkColorType:
|
case kBGRA_8888_SkColorType:
|
||||||
|
if (16 == encodedInfo.bitsPerComponent()) {
|
||||||
|
proc = premultiply ? &swizzle_rgba16_to_bgra_premul :
|
||||||
|
&swizzle_rgba16_to_bgra_unpremul;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
SkASSERT(8 == encodedInfo.bitsPerComponent());
|
||||||
if (premultiply) {
|
if (premultiply) {
|
||||||
if (SkCodec::kYes_ZeroInitialized == zeroInit) {
|
if (SkCodec::kYes_ZeroInitialized == zeroInit) {
|
||||||
proc = &SkipLeading8888ZerosThen<swizzle_rgba_to_bgra_premul>;
|
proc = &SkipLeading8888ZerosThen<swizzle_rgba_to_bgra_premul>;
|
||||||
|
Loading…
Reference in New Issue
Block a user