aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/codec/SkWebpCodec.cpp
diff options
context:
space:
mode:
authorGravatar Leon Scroggins III <scroggo@google.com>2017-11-17 08:07:32 -0500
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-11-17 13:28:45 +0000
commit03588412c89899fba09893e1812866f0069fc6f6 (patch)
treea333b3ed734de13f9ff8d31412a7d1bf3d0e964b /src/codec/SkWebpCodec.cpp
parent57e0828fade9270f1fd3abf32f4e27b108c4bc1a (diff)
Fix webp bug compositing alpha frames on opaque (better fix)
The old code made the wrong assumptions about premultiplication. There are three relevant steps here for decoding a webp frame: 1 tell libwebp to decode 2 colorXform the result (sometimes) 3 blend with the prior frame (sometimes) Rearrange the code to premultiply at the blend step, in a linear space. If the client wants unpremul, the blend step will unpremul after. If there is no blending, the colorXform (if any) will premultiply. If only step 1 is necessary, let libwebp premultiply. This fixes an animated image that has an opaque frame 0 followed by a frame with alpha that blends with it. Add the test image that failed (https://mathiasbynens.be/demo/animated-webp) The prior fix is in 42bae8faa4b9b6a3341b15c6ac7c6b466e95625c. It did not properly handle the colorXform when there was no blending step. Change-Id: I2b9d265ba162eaf7e55a106c8f79341826cee0d3 Reviewed-on: https://skia-review.googlesource.com/72281 Commit-Queue: Leon Scroggins <scroggo@google.com> Reviewed-by: Mike Klein <mtklein@chromium.org>
Diffstat (limited to 'src/codec/SkWebpCodec.cpp')
-rw-r--r--src/codec/SkWebpCodec.cpp84
1 files changed, 55 insertions, 29 deletions
diff --git a/src/codec/SkWebpCodec.cpp b/src/codec/SkWebpCodec.cpp
index 6287617373..d5614d6213 100644
--- a/src/codec/SkWebpCodec.cpp
+++ b/src/codec/SkWebpCodec.cpp
@@ -39,10 +39,6 @@ bool SkWebpCodec::IsWebp(const void* buf, size_t bytesRead) {
return bytesRead >= 14 && !memcmp(bytes, "RIFF", 4) && !memcmp(&bytes[8], "WEBPVP", 6);
}
-static SkAlphaType alpha_type(bool hasAlpha) {
- return hasAlpha ? kUnpremul_SkAlphaType : kOpaque_SkAlphaType;
-}
-
// Parse headers of RIFF container, and check for valid Webp (VP8) content.
// Returns an SkWebpCodec on success
std::unique_ptr<SkCodec> SkWebpCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
@@ -181,9 +177,8 @@ bool SkWebpCodec::onDimensionsSupported(const SkISize& dim) {
&& dim.height() >= 1 && dim.height() <= info.height();
}
-static WEBP_CSP_MODE webp_decode_mode(const SkImageInfo& info) {
- const bool premultiply = info.alphaType() == kPremul_SkAlphaType;
- switch (info.colorType()) {
+static WEBP_CSP_MODE webp_decode_mode(SkColorType dstCT, bool premultiply) {
+ switch (dstCT) {
case kBGRA_8888_SkColorType:
return premultiply ? MODE_bgrA : MODE_BGRA;
case kRGBA_8888_SkColorType:
@@ -351,20 +346,13 @@ static void pick_memory_stages(SkColorType ct, SkRasterPipeline::StockStage* loa
}
}
+// Requires that the src input be unpremultiplied (or opaque).
static void blend_line(SkColorType dstCT, void* dst,
SkColorType srcCT, const void* src,
- bool needsSrgbToLinear, SkAlphaType at,
+ bool needsSrgbToLinear,
+ SkAlphaType dstAt,
+ bool srcHasAlpha,
int width) {
- // Setup conversion from the source and dest, which will be the same.
- SkRasterPipeline_<256> convert_to_linear_premul;
- if (needsSrgbToLinear) {
- convert_to_linear_premul.append_from_srgb(at);
- }
- if (kUnpremul_SkAlphaType == at) {
- // srcover assumes premultiplied inputs.
- convert_to_linear_premul.append(SkRasterPipeline::premul);
- }
-
SkJumper_MemoryCtx dst_ctx = { (void*)dst, 0 },
src_ctx = { (void*)src, 0 };
@@ -374,19 +362,29 @@ static void blend_line(SkColorType dstCT, void* dst,
// Load the final dst.
p.append(load_dst, &dst_ctx);
- p.extend(convert_to_linear_premul);
+ if (needsSrgbToLinear) {
+ p.append_from_srgb(dstAt);
+ }
+ if (kUnpremul_SkAlphaType == dstAt) {
+ p.append(SkRasterPipeline::premul);
+ }
p.append(SkRasterPipeline::move_src_dst);
// Load the src.
SkRasterPipeline::StockStage load_src;
pick_memory_stages(srcCT, &load_src, nullptr);
p.append(load_src, &src_ctx);
- p.extend(convert_to_linear_premul);
+ if (needsSrgbToLinear) {
+ p.append_from_srgb(kUnpremul_SkAlphaType);
+ }
+ if (srcHasAlpha) {
+ p.append(SkRasterPipeline::premul);
+ }
p.append(SkRasterPipeline::srcover);
// Convert back to dst.
- if (kUnpremul_SkAlphaType == at) {
+ if (kUnpremul_SkAlphaType == dstAt) {
p.append(SkRasterPipeline::unpremul);
}
if (needsSrgbToLinear) {
@@ -537,7 +535,35 @@ SkCodec::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst,
webpDst.installPixels(webpInfo, dst, rowBytes);
}
- config.output.colorspace = webp_decode_mode(webpInfo);
+ // Choose the step when we will perform premultiplication.
+ enum {
+ kNone,
+ kBlendLine,
+ kColorXform,
+ kLibwebp,
+ };
+ auto choose_premul_step = [&]() {
+ if (!frame.has_alpha) {
+ // None necessary.
+ return kNone;
+ }
+ if (blendWithPrevFrame) {
+ // Premultiply in blend_line, in a linear space.
+ return kBlendLine;
+ }
+ if (dstInfo.alphaType() != kPremul_SkAlphaType) {
+ // No blending is necessary, so we only need to premultiply if the
+ // client requested it.
+ return kNone;
+ }
+ if (this->colorXform()) {
+ // Premultiply in the colorXform, in a linear space.
+ return kColorXform;
+ }
+ return kLibwebp;
+ };
+ const auto premulStep = choose_premul_step();
+ config.output.colorspace = webp_decode_mode(webpInfo.colorType(), premulStep == kLibwebp);
config.output.is_external_memory = 1;
config.output.u.RGBA.rgba = reinterpret_cast<uint8_t*>(webpDst.getAddr(dstX, dstY));
@@ -568,10 +594,6 @@ SkCodec::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst,
return kInvalidInput;
}
- // We're only transforming the new part of the frame, so no need to worry about the
- // final composited alpha.
- const auto srcAlpha = 0 == index ? srcInfo.alphaType() : alpha_type(frame.has_alpha);
- const auto xformAlphaType = select_xform_alpha(dstInfo.alphaType(), srcAlpha);
const bool needsSrgbToLinear = dstInfo.gammaCloseToSRGB() &&
options.fPremulBehavior == SkTransferFunctionBehavior::kRespect;
@@ -592,11 +614,15 @@ SkCodec::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst,
} else {
xformDst = dst;
}
+
+ const auto xformAlphaType = (premulStep == kColorXform) ? kPremul_SkAlphaType :
+ ( frame.has_alpha) ? kUnpremul_SkAlphaType :
+ kOpaque_SkAlphaType ;
for (int y = 0; y < rowsDecoded; y++) {
this->applyColorXform(xformDst, xformSrc, scaledWidth, xformAlphaType);
if (blendWithPrevFrame) {
- blend_line(dstCT, dst, dstCT, xformDst, needsSrgbToLinear, xformAlphaType,
- scaledWidth);
+ blend_line(dstCT, dst, dstCT, xformDst, needsSrgbToLinear,
+ dstInfo.alphaType(), frame.has_alpha, scaledWidth);
dst = SkTAddOffset<void>(dst, rowBytes);
} else {
xformDst = SkTAddOffset<void>(xformDst, rowBytes);
@@ -608,7 +634,7 @@ SkCodec::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst,
for (int y = 0; y < rowsDecoded; y++) {
blend_line(dstCT, dst, webpDst.colorType(), src, needsSrgbToLinear,
- xformAlphaType, scaledWidth);
+ dstInfo.alphaType(), frame.has_alpha, scaledWidth);
src = SkTAddOffset<const uint8_t>(src, srcRowBytes);
dst = SkTAddOffset<void>(dst, rowBytes);
}