diff --git a/src/ImageSharp/Formats/WebP/ColorCache.cs b/src/ImageSharp/Formats/WebP/ColorCache.cs
index e8e34e878b..e9b4bc7482 100644
--- a/src/ImageSharp/Formats/WebP/ColorCache.cs
+++ b/src/ImageSharp/Formats/WebP/ColorCache.cs
@@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System.Collections.Generic;
-
namespace SixLabors.ImageSharp.Formats.WebP
{
internal class ColorCache
@@ -10,29 +8,39 @@ namespace SixLabors.ImageSharp.Formats.WebP
///
/// Color entries.
///
- public List Colors { get; set; }
+ public uint[] Colors { get; private set; }
///
/// Hash shift: 32 - hashBits.
///
- public uint HashShift { get; set; }
+ public int HashShift { get; private set; }
+
+ public int HashBits { get; private set; }
- public uint HashBits { get; set; }
+ private const uint KHashMul = 0x1e35a7bdu;
- public void Init(int colorCacheBits)
+ public void Init(int hashBits)
{
+ int hashSize = 1 << hashBits;
+ this.Colors = new uint[hashSize];
+ this.HashBits = hashBits;
+ this.HashShift = 32 - hashBits;
+ }
+ public void Insert(uint argb)
+ {
+ int key = this.HashPix(argb, this.HashShift);
+ this.Colors[key] = argb;
}
- public void Insert()
+ public uint Lookup(int key)
{
- // TODO: implement VP8LColorCacheInsert
+ return this.Colors[key];
}
- public int ColorCacheLookup()
+ private int HashPix(uint argb, int shift)
{
- // TODO: implement VP8LColorCacheLookup
- return 0;
+ return (int)((argb * KHashMul) >> shift);
}
}
}
diff --git a/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs b/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs
index 37c6aba557..1cb550950a 100644
--- a/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs
+++ b/src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Numerics;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@@ -98,10 +99,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
WebPThrowHelper.ThrowImageFormatException("Invalid color cache bits found");
}
- int hashSize = 1 << colorCacheBits;
- colorCache.Colors = new List(hashSize);
- colorCache.HashBits = (uint)colorCacheBits;
- colorCache.HashShift = (uint)(32 - colorCacheBits);
colorCache.Init(colorCacheBits);
}
@@ -109,7 +106,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
var numBits = 0; // TODO: use huffmanSubsampleBits.
metadata.HuffmanMask = (numBits == 0) ? ~0 : (1 << numBits) - 1;
metadata.ColorCacheSize = colorCacheSize;
-
+
int lastPixel = 0;
int row = lastPixel / width;
int col = lastPixel % width;
@@ -119,10 +116,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
int nextSyncRow = decIsIncremental ? row : 1 << 24;
int mask = metadata.HuffmanMask;
HTreeGroup[] hTreeGroup = this.GetHtreeGroupForPos(metadata, col, row);
- var pixelData = new byte[width * height * 4];
+ var pixelData = new uint[width * height];
int totalPixels = width * height;
int decodedPixels = 0;
+ int lastCached = decodedPixels;
while (decodedPixels < totalPixels)
{
int code = 0;
@@ -131,10 +129,17 @@ namespace SixLabors.ImageSharp.Formats.WebP
hTreeGroup = this.GetHtreeGroupForPos(metadata, col, row);
}
+ if (hTreeGroup[0].IsTrivialCode)
+ {
+ pixelData[decodedPixels] = hTreeGroup[0].LiteralArb;
+ this.AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref lastCached);
+ continue;
+ }
+
this.bitReader.FillBitWindow();
if (hTreeGroup[0].UsePackedTable)
{
- code = (int)this.ReadPackedSymbols(hTreeGroup);
+ code = (int)this.ReadPackedSymbols(hTreeGroup, pixelData, decodedPixels);
if (this.bitReader.IsEndOfStream())
{
break;
@@ -142,7 +147,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
if (code == PackedNonLiteralCode)
{
- this.AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels);
+ this.AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref lastCached);
continue;
}
}
@@ -161,7 +166,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
{
if (hTreeGroup[0].IsTrivialLiteral)
{
- long pixel = hTreeGroup[0].LiteralArb | (code << 8);
+ pixelData[decodedPixels] = (uint)(hTreeGroup[0].LiteralArb | (code << 8));
}
else
{
@@ -175,13 +180,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
int pixelIdx = decodedPixels * 4;
- pixelData[pixelIdx] = (byte)alpha;
- pixelData[pixelIdx + 1] = (byte)red;
- pixelData[pixelIdx + 2] = (byte)code;
- pixelData[pixelIdx + 3] = (byte)blue;
+ pixelData[pixelIdx] =
+ (uint)(((byte)alpha << 24) | ((byte)red << 16) | ((byte)code << 8) | (byte)blue);
}
- this.AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels);
+ this.AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref lastCached);
}
else if (code < lenCodeLimit)
{
@@ -197,7 +200,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
break;
}
- this.CopyBlock32b(pixelData, dist, length);
+ this.CopyBlock(pixelData, decodedPixels, dist, length);
decodedPixels += length;
col += length;
while (col >= width)
@@ -213,31 +216,51 @@ namespace SixLabors.ImageSharp.Formats.WebP
if (colorCache != null)
{
- //while (lastCached < src)
- //{
- // colorCache.Insert(lastCached);
- //}
+ while (lastCached < decodedPixels)
+ {
+ colorCache.Insert(pixelData[lastCached]);
+ lastCached++;
+ }
}
}
else if (code < colorCacheLimit)
{
// Color cache should be used.
int key = code - lenCodeLimit;
- /*while (lastCached < src)
+ while (lastCached < decodedPixels)
{
- colorCache.Insert(lastCached);
- }*/
- //pixelData = colorCache.Lookup(key);
- this.AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels);
+ colorCache.Insert(pixelData[lastCached]);
+ lastCached++;
+ }
+
+ pixelData[decodedPixels] = colorCache.Lookup(key);
+ this.AdvanceByOne(ref col, ref row, width, colorCache, ref decodedPixels, pixelData, ref lastCached);
}
else
{
// Error
}
}
+
+ TPixel color = default;
+ for (int y = 0; y < height; y++)
+ {
+ Span pixelRow = pixels.GetRowSpan(y);
+ for (int x = 0; x < width; x++)
+ {
+ int idx = (y * width) + x;
+ uint pixel = pixelData[idx];
+ uint a = (pixel & 0xFF000000) >> 24;
+ uint r = (pixel & 0xFF0000) >> 16;
+ uint g = (pixel & 0xFF00) >> 8;
+ uint b = pixel & 0xFF;
+ color.FromRgba32(new Rgba32(r, g, b, a));
+ pixelRow[x] = color;
+ }
+ }
}
- private void AdvanceByOne(ref int col, ref int row, int width, ColorCache colorCache, ref int decodedPixels)
+ private void AdvanceByOne(ref int col, ref int row, int width, ColorCache colorCache, ref int decodedPixels, uint[] pixelData, ref int lastCached)
{
++col;
decodedPixels++;
@@ -252,15 +275,16 @@ namespace SixLabors.ImageSharp.Formats.WebP
if (colorCache != null)
{
- /*while (lastCached < src)
+ while (lastCached < decodedPixels)
{
- VP8LColorCacheInsert(color_cache, *last_cached++);
- }*/
+ colorCache.Insert(pixelData[lastCached]);
+ lastCached++;
+ }
}
}
}
- private Vp8LMetadata ReadHuffmanCodes(int xsize, int ysize, int colorCacheBits, bool allowRecursion = true)
+ private Vp8LMetadata ReadHuffmanCodes(int xSize, int ySize, int colorCacheBits, bool allowRecursion = true)
{
int maxAlphabetSize = 0;
int numHtreeGroups = 1;
@@ -273,8 +297,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
if (isEntropyImage)
{
uint huffmanPrecision = this.bitReader.ReadBits(3) + 2;
- int huffmanXSize = SubSampleSize(xsize, (int)huffmanPrecision);
- int huffmanYSize = SubSampleSize(ysize, (int)huffmanPrecision);
+ int huffmanXSize = SubSampleSize(xSize, (int)huffmanPrecision);
+ int huffmanYSize = SubSampleSize(ySize, (int)huffmanPrecision);
// TODO: decode entropy image
return new Vp8LMetadata();
@@ -591,14 +615,14 @@ namespace SixLabors.ImageSharp.Formats.WebP
return tableSpan[0].Value;
}
- private uint ReadPackedSymbols(HTreeGroup[] group)
+ private uint ReadPackedSymbols(HTreeGroup[] group, uint[] pixelData, int decodedPixels)
{
uint val = (uint)(this.bitReader.PrefetchBits() & (HuffmanUtils.HuffmanPackedTableSize - 1));
HuffmanCode code = group[0].PackedTable[val];
if (code.BitsUsed < BitsSpecialMarker)
{
this.bitReader.AdvanceBitPosition(code.BitsUsed);
- // dest = (uint)code.Value;
+ pixelData[decodedPixels] = code.Value;
return PackedNonLiteralCode;
}
@@ -607,9 +631,25 @@ namespace SixLabors.ImageSharp.Formats.WebP
return code.Value;
}
- private void CopyBlock32b(byte[] dest, int dist, int length)
+ private void CopyBlock(uint[] pixelData, int decodedPixels, int dist, int length)
{
-
+ if (dist > length)
+ {
+ Span src = pixelData.AsSpan(decodedPixels - dist, length);
+ Span dest = pixelData.AsSpan(decodedPixels);
+ src.CopyTo(dest);
+ }
+ else
+ {
+ int copiedPixels = 0;
+ while (copiedPixels < length)
+ {
+ Span src = pixelData.AsSpan(decodedPixels - dist, dist);
+ Span dest = pixelData.AsSpan(decodedPixels + copiedPixels);
+ src.CopyTo(dest);
+ copiedPixels += dist;
+ }
+ }
}
private int GetCopyDistance(int distanceSymbol)