From 84014f03a90d137c0f5c95e15c1e5f8503df7101 Mon Sep 17 00:00:00 2001 From: Matt Sarett Date: Tue, 10 Jan 2017 11:28:54 -0500 Subject: Respect SkColorSpace in SkPNGImageEncoder This only changes behavior when the input SkBitmap/SkPixmap is tagged with a non-null SkColorSpace. Android tags their bitmaps as sRGB when linear blending is enabled. So this only changes behavior in Android when linear blending is turned on. *If linear blending is turned on, this will do a color correct encode (which is the desired behavior). *If linear blending is turned off, this will do a legacy encode. TODO: Add support for F16. TODO: Add color space support to WEBP. TODO: Tag encoded images with ICC profiles (when it makes sense). BUG=skia: Change-Id: Idd8a2836371d24a453d953e6fe2e76a87751be96 Reviewed-on: https://skia-review.googlesource.com/6498 Reviewed-by: Brian Osman Reviewed-by: Leon Scroggins Reviewed-by: Mike Klein Commit-Queue: Matt Sarett --- gm/encode-platform.cpp | 3 +- gm/encode-srgb.cpp | 141 ++++++++++++++++++++++++++++++++++ gn/gm.gni | 1 + src/image/SkImage.cpp | 6 -- src/images/SkImageEncoder.cpp | 12 ++- src/images/SkImageEncoderPriv.h | 14 +++- src/images/SkPNGImageEncoder.cpp | 159 +++++++++++++++++++++++---------------- src/images/transform_scanline.h | 45 +++++++++-- 8 files changed, 298 insertions(+), 83 deletions(-) create mode 100644 gm/encode-srgb.cpp diff --git a/gm/encode-platform.cpp b/gm/encode-platform.cpp index e8f420fb15..ca3cda1d73 100644 --- a/gm/encode-platform.cpp +++ b/gm/encode-platform.cpp @@ -70,7 +70,8 @@ static sk_sp encode_data(SkEncodedImageFormat type, const SkBitmap& bitm #else switch (type) { case SkEncodedImageFormat::kPNG: - return SkEncodeImageAsPNG(&buf, src) ? buf.detachAsData() : nullptr; + return SkEncodeImageAsPNG(&buf, src, SkEncodeOptions()) ? buf.detachAsData() + : nullptr; case SkEncodedImageFormat::kJPEG: return SkEncodeImageAsJPEG(&buf, src, 100) ? buf.detachAsData() : nullptr; case SkEncodedImageFormat::kWEBP: diff --git a/gm/encode-srgb.cpp b/gm/encode-srgb.cpp new file mode 100644 index 0000000000..b9fe65bf6a --- /dev/null +++ b/gm/encode-srgb.cpp @@ -0,0 +1,141 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "gm.h" + +#include "Resources.h" +#include "SkCanvas.h" +#include "SkCodec.h" +#include "SkData.h" +#include "SkImageEncoderPriv.h" +#include "SkPM4f.h" +#include "SkSRGB.h" + +namespace skiagm { + +static const int imageWidth = 128; +static const int imageHeight = 128; + +static inline int div_round_up(int a, int b) { + return (a + b - 1) / b; +} + +static void make_index8(SkBitmap* bitmap, SkAlphaType alphaType, sk_sp colorSpace) { + const SkColor colors[] = { + 0x800000FF, 0x8000FF00, 0x80FF0000, 0x80FFFF00, + }; + + auto toPMColor = [alphaType, colorSpace](SkColor color) { + // In the unpremul case, just convert to SkPMColor ordering. + if (kUnpremul_SkAlphaType == alphaType) { + return SkSwizzle_BGRA_to_PMColor(color); + } + + // Linear premultiply. + if (colorSpace) { + uint32_t result; + Sk4f pmFloat = SkColor4f::FromColor(color).premul().to4f_pmorder(); + SkNx_cast(sk_linear_to_srgb_needs_trunc(pmFloat)).store(&result); + result = (result & 0x00FFFFFF) | (color & 0xFF000000); + return result; + } + + // Legacy premultiply. + return SkPreMultiplyColor(color); + }; + + // Note that these are not necessarily premultiplied, but they are platform byte ordering. + SkPMColor pmColors[SK_ARRAY_COUNT(colors)]; + for (int i = 0; i < (int) SK_ARRAY_COUNT(colors); i++) { + pmColors[i] = toPMColor(colors[i]); + } + + sk_sp colorTable(new SkColorTable(pmColors, SK_ARRAY_COUNT(pmColors))); + SkImageInfo info = SkImageInfo::Make(imageWidth, imageHeight, kIndex_8_SkColorType, + alphaType, colorSpace); + bitmap->allocPixels(info, nullptr, colorTable.get()); + for (int y = 0; y < imageHeight; y++) { + for (int x = 0; x < imageWidth; x++) { + *bitmap->getAddr8(x, y) = (x / div_round_up(imageWidth, 2)) + + (y / div_round_up(imageHeight, 3)); + } + } +} + +static void make(SkBitmap* bitmap, SkColorType colorType, SkAlphaType alphaType, + sk_sp colorSpace) { + if (kIndex_8_SkColorType == colorType) { + make_index8(bitmap, alphaType, colorSpace); + return; + } + + sk_sp data = GetResourceAsData("color_wheel.png"); + std::unique_ptr codec(SkCodec::NewFromData(data)); + SkImageInfo dstInfo = codec->getInfo().makeColorType(colorType) + .makeAlphaType(alphaType) + .makeColorSpace(colorSpace); + bitmap->allocPixels(dstInfo); + codec->getPixels(dstInfo, bitmap->getPixels(), bitmap->rowBytes()); +} + +static sk_sp encode_data(const SkBitmap& bitmap) { + SkAutoLockPixels autoLockPixels(bitmap); + SkPixmap src; + if (!bitmap.peekPixels(&src)) { + return nullptr; + } + SkDynamicMemoryWStream buf; + SkEncodeOptions options; + if (bitmap.colorSpace()) { + options.fPremulBehavior = SkEncodeOptions::PremulBehavior::kGammaCorrect; + } + SkAssertResult(SkEncodeImageAsPNG(&buf, src, options)); + return buf.detachAsData(); +} + +class EncodeSRGBGM : public GM { +public: + EncodeSRGBGM() {} + +protected: + SkString onShortName() override { + return SkString("encode-srgb"); + } + + SkISize onISize() override { + return SkISize::Make(imageWidth * 2, imageHeight * 4); + } + + void onDraw(SkCanvas* canvas) override { + const SkColorType colorTypes[] = { kN32_SkColorType, kIndex_8_SkColorType, }; + const SkAlphaType alphaTypes[] = { kUnpremul_SkAlphaType, kPremul_SkAlphaType, }; + const sk_sp colorSpaces[] = { + nullptr, SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named), + }; + + SkBitmap bitmap; + for (SkColorType colorType : colorTypes) { + for (SkAlphaType alphaType : alphaTypes) { + canvas->save(); + for (sk_sp colorSpace : colorSpaces) { + make(&bitmap, colorType, alphaType, colorSpace); + auto image = SkImage::MakeFromEncoded(encode_data(bitmap)); + canvas->drawImage(image.get(), 0.0f, 0.0f); + canvas->translate((float) imageWidth, 0.0f); + } + canvas->restore(); + canvas->translate(0.0f, (float) imageHeight); + } + } + } + +private: + typedef GM INHERITED; +}; + +DEF_GM( return new EncodeSRGBGM; ) +} diff --git a/gn/gm.gni b/gn/gm.gni index 327416c611..2a90b14ec5 100644 --- a/gn/gm.gni +++ b/gn/gm.gni @@ -113,6 +113,7 @@ gm_sources = [ "$_gm/emptypath.cpp", "$_gm/encode.cpp", "$_gm/encode-platform.cpp", + "$_gm/encode-srgb.cpp", "$_gm/extractbitmap.cpp", "$_gm/fadefilter.cpp", "$_gm/fatpathfill.cpp", diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp index 417a29a1ed..d2fc8456f3 100644 --- a/src/image/SkImage.cpp +++ b/src/image/SkImage.cpp @@ -92,9 +92,6 @@ sk_sp SkImage::makeShader(SkShader::TileMode tileX, SkShader::TileMode SkData* SkImage::encode(SkEncodedImageFormat type, int quality) const { SkBitmap bm; - // TODO: Right now, the encoders don't handle F16 or linearly premultiplied data. Once they do, - // we should decode in "color space aware" mode, then re-encode that. For now, work around this - // by asking for a legacy decode (which gives us the raw data in N32). SkColorSpace* legacyColorSpace = nullptr; if (as_IB(this)->getROPixels(&bm, legacyColorSpace)) { SkDynamicMemoryWStream buf; @@ -112,9 +109,6 @@ SkData* SkImage::encode(SkPixelSerializer* serializer) const { SkBitmap bm; SkAutoPixmapUnlock apu; - // TODO: Right now, the encoders don't handle F16 or linearly premultiplied data. Once they do, - // we should decode in "color space aware" mode, then re-encode that. For now, work around this - // by asking for a legacy decode (which gives us the raw data in N32). SkColorSpace* legacyColorSpace = nullptr; if (as_IB(this)->getROPixels(&bm, legacyColorSpace) && bm.requestLock(&apu)) { diff --git a/src/images/SkImageEncoder.cpp b/src/images/SkImageEncoder.cpp index f135a02063..fecadbf82a 100644 --- a/src/images/SkImageEncoder.cpp +++ b/src/images/SkImageEncoder.cpp @@ -16,10 +16,14 @@ bool SkEncodeImage(SkWStream* dst, const SkPixmap& src, return SkEncodeImageWithWIC(dst, src, format, quality); #else switch(format) { - case SkEncodedImageFormat::kJPEG: return SkEncodeImageAsJPEG(dst, src, quality); - case SkEncodedImageFormat::kPNG: return SkEncodeImageAsPNG(dst, src); - case SkEncodedImageFormat::kWEBP: return SkEncodeImageAsWEBP(dst, src, quality); - default: return false; + case SkEncodedImageFormat::kJPEG: + return SkEncodeImageAsJPEG(dst, src, quality); + case SkEncodedImageFormat::kPNG: + return SkEncodeImageAsPNG(dst, src, SkEncodeOptions()); + case SkEncodedImageFormat::kWEBP: + return SkEncodeImageAsWEBP(dst, src, quality); + default: + return false; } #endif } diff --git a/src/images/SkImageEncoderPriv.h b/src/images/SkImageEncoderPriv.h index 22f8f4b0ef..7748204fed 100644 --- a/src/images/SkImageEncoderPriv.h +++ b/src/images/SkImageEncoderPriv.h @@ -10,6 +10,18 @@ #include "SkImageEncoder.h" +struct SkEncodeOptions { + enum class PremulBehavior { + // Convert to a linear space before premultiplying or unpremultiplying. + kGammaCorrect, + + // Ignore the transfer function when premultiplying or unpremultiplying. + kLegacy, + }; + + PremulBehavior fPremulBehavior = PremulBehavior::kLegacy; +}; + #ifdef SK_HAS_JPEG_LIBRARY bool SkEncodeImageAsJPEG(SkWStream*, const SkPixmap&, int quality); #else @@ -17,7 +29,7 @@ #endif #ifdef SK_HAS_PNG_LIBRARY - bool SkEncodeImageAsPNG(SkWStream*, const SkPixmap&); + bool SkEncodeImageAsPNG(SkWStream*, const SkPixmap&, const SkEncodeOptions&); #else #define SkEncodeImageAsPNG(...) false #endif diff --git a/src/images/SkPNGImageEncoder.cpp b/src/images/SkPNGImageEncoder.cpp index d752bf86ba..55aead2c04 100644 --- a/src/images/SkPNGImageEncoder.cpp +++ b/src/images/SkPNGImageEncoder.cpp @@ -38,34 +38,55 @@ static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) { } } -static transform_scanline_proc choose_proc(SkColorType ct, SkAlphaType alphaType) { - static const struct { - SkColorType fColorType; - SkAlphaType fAlphaType; - transform_scanline_proc fProc; - } gMap[] = { - { kRGB_565_SkColorType, kOpaque_SkAlphaType, transform_scanline_565 }, - { kRGBA_8888_SkColorType, kOpaque_SkAlphaType, transform_scanline_RGBX }, - { kBGRA_8888_SkColorType, kOpaque_SkAlphaType, transform_scanline_BGRX }, - { kRGBA_8888_SkColorType, kPremul_SkAlphaType, transform_scanline_rgbA }, - { kBGRA_8888_SkColorType, kPremul_SkAlphaType, transform_scanline_bgrA }, - { kRGBA_8888_SkColorType, kUnpremul_SkAlphaType, transform_scanline_memcpy }, - { kBGRA_8888_SkColorType, kUnpremul_SkAlphaType, transform_scanline_BGRA }, - { kARGB_4444_SkColorType, kOpaque_SkAlphaType, transform_scanline_444 }, - { kARGB_4444_SkColorType, kPremul_SkAlphaType, transform_scanline_4444 }, - { kIndex_8_SkColorType, kOpaque_SkAlphaType, transform_scanline_memcpy }, - { kIndex_8_SkColorType, kPremul_SkAlphaType, transform_scanline_memcpy }, - { kIndex_8_SkColorType, kUnpremul_SkAlphaType, transform_scanline_memcpy }, - { kGray_8_SkColorType, kOpaque_SkAlphaType, transform_scanline_memcpy }, - }; - - for (auto entry : gMap) { - if (entry.fColorType == ct && entry.fAlphaType == alphaType) { - return entry.fProc; - } +static transform_scanline_proc choose_proc(const SkImageInfo& info) { + const bool isGammaEncoded = info.gammaCloseToSRGB(); + switch (info.colorType()) { + case kRGBA_8888_SkColorType: + switch (info.alphaType()) { + case kOpaque_SkAlphaType: + return transform_scanline_RGBX; + case kUnpremul_SkAlphaType: + return transform_scanline_memcpy; + case kPremul_SkAlphaType: + return isGammaEncoded ? transform_scanline_srgbA : + transform_scanline_rgbA; + default: + SkASSERT(false); + return nullptr; + } + case kBGRA_8888_SkColorType: + switch (info.alphaType()) { + case kOpaque_SkAlphaType: + return transform_scanline_BGRX; + case kUnpremul_SkAlphaType: + return transform_scanline_BGRA; + case kPremul_SkAlphaType: + return isGammaEncoded ? transform_scanline_sbgrA : + transform_scanline_bgrA; + default: + SkASSERT(false); + return nullptr; + } + case kRGB_565_SkColorType: + return transform_scanline_565; + case kARGB_4444_SkColorType: + switch (info.alphaType()) { + case kOpaque_SkAlphaType: + return transform_scanline_444; + case kPremul_SkAlphaType: + // 4444 is assumed to be legacy premul. + return transform_scanline_4444; + default: + SkASSERT(false); + return nullptr; + } + case kIndex_8_SkColorType: + case kGray_8_SkColorType: + return transform_scanline_memcpy; + default: + SkASSERT(false); + return nullptr; } - sk_throw(); - return nullptr; } /* Pack palette[] with the corresponding colors, and if the image has alpha, also @@ -73,29 +94,28 @@ static transform_scanline_proc choose_proc(SkColorType ct, SkAlphaType alphaType opaque, the return value will always be 0. */ static inline int pack_palette(SkColorTable* ctable, png_color* SK_RESTRICT palette, - png_byte* SK_RESTRICT alphas, SkAlphaType alphaType) { - const SkPMColor* SK_RESTRICT colors = ctable->readColors(); + png_byte* SK_RESTRICT alphas, const SkImageInfo& info) { + const SkPMColor* colors = ctable->readColors(); const int count = ctable->count(); - int numWithAlpha = 0; - if (kOpaque_SkAlphaType != alphaType) { - auto getUnpremulColor = [alphaType](uint8_t color, uint8_t alpha) { - if (kPremul_SkAlphaType == alphaType) { - const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable(); - const SkUnPreMultiply::Scale scale = table[alpha]; - return (uint8_t) SkUnPreMultiply::ApplyScale(scale, color); - } else { - return color; - } - }; + SkPMColor storage[256]; + if (kPremul_SkAlphaType == info.alphaType()) { + // Unpremultiply the colors. + const SkImageInfo rgbaInfo = info.makeColorType(kRGBA_8888_SkColorType); + transform_scanline_proc proc = choose_proc(rgbaInfo); + proc((char*) storage, (const char*) colors, ctable->count(), 4); + colors = storage; + } + int numWithAlpha = 0; + if (kOpaque_SkAlphaType != info.alphaType()) { // PNG requires that all non-opaque colors come first in the palette. Write these first. for (int i = 0; i < count; i++) { uint8_t alpha = SkGetPackedA32(colors[i]); if (0xFF != alpha) { alphas[numWithAlpha] = alpha; - palette[numWithAlpha].red = getUnpremulColor(SkGetPackedR32(colors[i]), alpha); - palette[numWithAlpha].green = getUnpremulColor(SkGetPackedG32(colors[i]), alpha); - palette[numWithAlpha].blue = getUnpremulColor(SkGetPackedB32(colors[i]), alpha); + palette[numWithAlpha].red = SkGetPackedR32(colors[i]); + palette[numWithAlpha].green = SkGetPackedG32(colors[i]); + palette[numWithAlpha].blue = SkGetPackedB32(colors[i]); numWithAlpha++; } } @@ -132,12 +152,24 @@ static inline int pack_palette(SkColorTable* ctable, png_color* SK_RESTRICT pale static bool do_encode(SkWStream*, const SkPixmap&, int, int, png_color_8&); -bool SkEncodeImageAsPNG(SkWStream* stream, const SkPixmap& pixmap) { +bool SkEncodeImageAsPNG(SkWStream* stream, const SkPixmap& src, const SkEncodeOptions& opts) { + SkASSERT(!src.colorSpace() || src.colorSpace()->gammaCloseToSRGB() || + src.colorSpace()->gammaIsLinear()); + + SkPixmap pixmap = src; + if (SkEncodeOptions::PremulBehavior::kLegacy == opts.fPremulBehavior) { + pixmap.setColorSpace(nullptr); + } else { + if (!pixmap.colorSpace()) { + return false; + } + } + if (!pixmap.addr() || pixmap.info().isEmpty()) { return false; } - const SkColorType ct = pixmap.colorType(); - switch (ct) { + const SkColorType colorType = pixmap.colorType(); + switch (colorType) { case kIndex_8_SkColorType: case kGray_8_SkColorType: case kRGBA_8888_SkColorType: @@ -152,7 +184,7 @@ bool SkEncodeImageAsPNG(SkWStream* stream, const SkPixmap& pixmap) { const SkAlphaType alphaType = pixmap.alphaType(); switch (alphaType) { case kUnpremul_SkAlphaType: - if (kARGB_4444_SkColorType == ct) { + if (kARGB_4444_SkColorType == colorType) { return false; } @@ -169,18 +201,18 @@ bool SkEncodeImageAsPNG(SkWStream* stream, const SkPixmap& pixmap) { png_color_8 sig_bit; sk_bzero(&sig_bit, sizeof(png_color_8)); - int colorType; - switch (ct) { + int pngColorType; + switch (colorType) { case kIndex_8_SkColorType: sig_bit.red = 8; sig_bit.green = 8; sig_bit.blue = 8; sig_bit.alpha = 8; - colorType = PNG_COLOR_TYPE_PALETTE; + pngColorType = PNG_COLOR_TYPE_PALETTE; break; case kGray_8_SkColorType: sig_bit.gray = 8; - colorType = PNG_COLOR_TYPE_GRAY; + pngColorType = PNG_COLOR_TYPE_GRAY; SkASSERT(isOpaque); break; case kRGBA_8888_SkColorType: @@ -189,26 +221,26 @@ bool SkEncodeImageAsPNG(SkWStream* stream, const SkPixmap& pixmap) { sig_bit.green = 8; sig_bit.blue = 8; sig_bit.alpha = 8; - colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA; + pngColorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA; break; case kARGB_4444_SkColorType: sig_bit.red = 4; sig_bit.green = 4; sig_bit.blue = 4; sig_bit.alpha = 4; - colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA; + pngColorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA; break; case kRGB_565_SkColorType: sig_bit.red = 5; sig_bit.green = 6; sig_bit.blue = 5; - colorType = PNG_COLOR_TYPE_RGB; + pngColorType = PNG_COLOR_TYPE_RGB; SkASSERT(isOpaque); break; default: return false; } - if (kIndex_8_SkColorType == ct) { + if (kIndex_8_SkColorType == colorType) { SkColorTable* ctable = pixmap.ctable(); if (!ctable || ctable->count() == 0) { return false; @@ -218,14 +250,11 @@ bool SkEncodeImageAsPNG(SkWStream* stream, const SkPixmap& pixmap) { // When ctable->count() <= 16, we could potentially use 1, 2, // or 4 bit indices. } - return do_encode(stream, pixmap, colorType, bitDepth, sig_bit); + return do_encode(stream, pixmap, pngColorType, bitDepth, sig_bit); } static bool do_encode(SkWStream* stream, const SkPixmap& pixmap, - int colorType, int bitDepth, png_color_8& sig_bit) { - SkAlphaType alphaType = pixmap.alphaType(); - SkColorType ct = pixmap.colorType(); - + int pngColorType, int bitDepth, png_color_8& sig_bit) { png_structp png_ptr; png_infop info_ptr; @@ -260,17 +289,17 @@ static bool do_encode(SkWStream* stream, const SkPixmap& pixmap, */ png_set_IHDR(png_ptr, info_ptr, pixmap.width(), pixmap.height(), - bitDepth, colorType, + bitDepth, pngColorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); // set our colortable/trans arrays if needed png_color paletteColors[256]; png_byte trans[256]; - if (kIndex_8_SkColorType == ct) { + if (kIndex_8_SkColorType == pixmap.colorType()) { SkColorTable* colorTable = pixmap.ctable(); SkASSERT(colorTable); - int numTrans = pack_palette(colorTable, paletteColors, trans, alphaType); + int numTrans = pack_palette(colorTable, paletteColors, trans, pixmap.info()); png_set_PLTE(png_ptr, info_ptr, paletteColors, colorTable->count()); if (numTrans > 0) { png_set_tRNS(png_ptr, info_ptr, trans, numTrans, nullptr); @@ -283,11 +312,11 @@ static bool do_encode(SkWStream* stream, const SkPixmap& pixmap, const char* srcImage = (const char*)pixmap.addr(); SkAutoSTMalloc<1024, char> rowStorage(pixmap.width() << 2); char* storage = rowStorage.get(); - transform_scanline_proc proc = choose_proc(ct, alphaType); + transform_scanline_proc proc = choose_proc(pixmap.info()); for (int y = 0; y < pixmap.height(); y++) { png_bytep row_ptr = (png_bytep)storage; - proc(storage, srcImage, pixmap.width(), SkColorTypeBytesPerPixel(ct)); + proc(storage, srcImage, pixmap.width(), SkColorTypeBytesPerPixel(pixmap.colorType())); png_write_rows(png_ptr, &row_ptr, 1); srcImage += pixmap.rowBytes(); } diff --git a/src/images/transform_scanline.h b/src/images/transform_scanline.h index a02e98ab07..1ce67cd3a2 100644 --- a/src/images/transform_scanline.h +++ b/src/images/transform_scanline.h @@ -13,6 +13,7 @@ #include "SkColor.h" #include "SkColorPriv.h" #include "SkPreConfig.h" +#include "SkRasterPipeline.h" #include "SkUnPreMultiply.h" /** @@ -128,21 +129,53 @@ static inline void transform_scanline_unpremultiply(char* SK_RESTRICT dst, } /** - * Transform from kPremul, kRGBA_8888_SkColorType to 4-bytes-per-pixel unpremultiplied RGBA. + * Transform from legacy kPremul, kRGBA_8888_SkColorType to 4-bytes-per-pixel unpremultiplied RGBA. */ -static void transform_scanline_rgbA(char* SK_RESTRICT dst, const char* SK_RESTRICT src, - int width, int bpp) { +static void transform_scanline_rgbA(char* SK_RESTRICT dst, const char* SK_RESTRICT src, int width, + int bpp) { transform_scanline_unpremultiply(dst, src, width, bpp); } /** - * Transform from kPremul, kBGRA_8888_SkColorType to 4-bytes-per-pixel unpremultiplied RGBA. + * Transform from legacy kPremul, kBGRA_8888_SkColorType to 4-bytes-per-pixel unpremultiplied RGBA. */ -static void transform_scanline_bgrA(char* SK_RESTRICT dst, const char* SK_RESTRICT src, - int width, int bpp) { +static void transform_scanline_bgrA(char* SK_RESTRICT dst, const char* SK_RESTRICT src, int width, + int bpp) { transform_scanline_unpremultiply(dst, src, width, bpp); } +template +static inline void transform_scanline_unpremultiply_sRGB(void* dst, const void* src, int width, int) +{ + SkRasterPipeline p; + p.append(SkRasterPipeline::load_8888, &src); + if (!kIsRGBA) { + p.append(SkRasterPipeline::swap_rb); + } + + p.append_from_srgb(kPremul_SkAlphaType); + p.append(SkRasterPipeline::unpremul); + p.append(SkRasterPipeline::to_srgb); + p.append(SkRasterPipeline::store_8888, &dst); + p.run(0, 0, width); +} + +/** + * Transform from kPremul, kRGBA_8888_SkColorType to 4-bytes-per-pixel unpremultiplied RGBA. + */ +static void transform_scanline_srgbA(char* SK_RESTRICT dst, const char* SK_RESTRICT src, + int width, int bpp) { + transform_scanline_unpremultiply_sRGB(dst, src, width, bpp); +} + +/** + * Transform from kPremul, kBGRA_8888_SkColorType to 4-bytes-per-pixel unpremultiplied RGBA. + */ +static void transform_scanline_sbgrA(char* SK_RESTRICT dst, const char* SK_RESTRICT src, + int width, int bpp) { + transform_scanline_unpremultiply_sRGB(dst, src, width, bpp); +} + /** * Transform from kUnpremul, kBGRA_8888_SkColorType to 4-bytes-per-pixel unpremultiplied RGBA. */ -- cgit v1.2.3