From 00e91cb6ab7c4e9f2acf11674af26a6a2ea8b468 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 9 Feb 2022 11:42:01 +0100 Subject: [PATCH] Add alpha blending for animated webp --- .../Formats/Webp/WebpAnimationDecoder.cs | 49 +++++++++++-------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs index 15d2d0145..b3cf4b00f 100644 --- a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs +++ b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs @@ -146,23 +146,23 @@ namespace SixLabors.ImageSharp.Formats.Webp imageFrame = currentFrame; } + int frameX = (int)(frameData.X * 2); + int frameY = (int)(frameData.Y * 2); + int frameWidth = (int)frameData.Width; + int frameHeight = (int)frameData.Height; + var regionRectangle = Rectangle.FromLTRB(frameX, frameY, frameX + frameWidth, frameY + frameHeight); + if (frameData.DisposalMethod is AnimationDisposalMethod.Dispose) { this.RestoreToBackground(imageFrame, backgroundColor); } - uint frameX = frameData.X * 2; - uint frameY = frameData.Y * 2; - uint frameWidth = frameData.Width; - uint frameHeight = frameData.Height; - var regionRectangle = Rectangle.FromLTRB((int)frameX, (int)frameY, (int)(frameX + frameWidth), (int)(frameY + frameHeight)); - using Image decodedImage = this.DecodeImageData(frameData, webpInfo); this.DrawDecodedImageOnCanvas(decodedImage, imageFrame, frameX, frameY, frameWidth, frameHeight); if (previousFrame != null && frameData.BlendingMethod is AnimationBlendingMethod.AlphaBlending) { - this.AlphaBlend(previousFrame, imageFrame); + this.AlphaBlend(previousFrame, imageFrame, frameX, frameY, frameWidth, frameHeight); } previousFrame = currentFrame ?? image.Frames.RootFrame; @@ -207,17 +207,17 @@ namespace SixLabors.ImageSharp.Formats.Webp /// The frame y coordinate. /// The width of the frame. /// The height of the frame. - private void DrawDecodedImageOnCanvas(Image decodedImage, ImageFrame imageFrame, uint frameX, uint frameY, uint frameWidth, uint frameHeight) + private void DrawDecodedImageOnCanvas(Image decodedImage, ImageFrame imageFrame, int frameX, int frameY, int frameWidth, int frameHeight) where TPixel : unmanaged, IPixel { Buffer2D decodedImagePixels = decodedImage.Frames.RootFrame.PixelBuffer; Buffer2D imageFramePixels = imageFrame.PixelBuffer; int decodedRowIdx = 0; - for (uint y = frameY; y < frameHeight; y++) + for (int y = frameY; y < frameY + frameHeight; y++) { - Span framePixelRow = imageFramePixels.DangerousGetRowSpan((int)y); - Span decodedPixelRow = decodedImagePixels.DangerousGetRowSpan(decodedRowIdx++).Slice(0, (int)frameWidth); - decodedPixelRow.TryCopyTo(framePixelRow.Slice((int)frameX)); + Span framePixelRow = imageFramePixels.DangerousGetRowSpan(y); + Span decodedPixelRow = decodedImagePixels.DangerousGetRowSpan(decodedRowIdx++).Slice(0, frameWidth); + decodedPixelRow.TryCopyTo(framePixelRow.Slice(frameX)); } } @@ -228,30 +228,37 @@ namespace SixLabors.ImageSharp.Formats.Webp /// The pixel format. /// The source image. /// The destination image. - private void AlphaBlend(ImageFrame src, ImageFrame dst) + /// The frame x coordinate. + /// The frame y coordinate. + /// The width of the frame. + /// The height of the frame. + private void AlphaBlend(ImageFrame src, ImageFrame dst, int frameX, int frameY, int frameWidth, int frameHeight) where TPixel : unmanaged, IPixel { - int width = src.Width; - int height = src.Height; - - PixelBlender blender = PixelOperations.Instance.GetPixelBlender(PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.SrcOver); Buffer2D srcPixels = src.PixelBuffer; Buffer2D dstPixels = dst.PixelBuffer; Rgba32 srcRgba = default; Rgba32 dstRgba = default; - for (int y = 0; y < height; y++) + PixelBlender blender = PixelOperations.Instance.GetPixelBlender(PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.SrcOver); + for (int y = frameY; y < frameY + frameHeight; y++) { Span srcPixelRow = srcPixels.DangerousGetRowSpan(y); Span dstPixelRow = dstPixels.DangerousGetRowSpan(y); - for (int x = 0; x < width; x++) + for (int x = frameX; x < frameX + frameWidth; x++) { ref TPixel srcPixel = ref srcPixelRow[x]; ref TPixel dstPixel = ref dstPixelRow[x]; srcPixel.ToRgba32(ref srcRgba); dstPixel.ToRgba32(ref dstRgba); - if (dstRgba.A == 0) + + if (srcRgba.A is 0) + { + dstPixel.FromRgba32(dstRgba); + } + else { - Rgba32 blendResult = blender.Blend(srcRgba, dstRgba, 1.0f); + int dstFactorA = dstRgba.A * (255 - srcRgba.A) / 255; + Rgba32 blendResult = blender.Blend(srcRgba, dstRgba, 1.0f / dstFactorA); dstPixel.FromRgba32(blendResult); } }