|
|
|
@ -1,3 +1,6 @@ |
|
|
|
using System; |
|
|
|
using System.Runtime.InteropServices; |
|
|
|
|
|
|
|
namespace SixLabors.ImageSharp.Formats.WebP |
|
|
|
{ |
|
|
|
/// <summary>
|
|
|
|
@ -22,6 +25,57 @@ namespace SixLabors.ImageSharp.Formats.WebP |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public static void ColorIndexInverseTransform(Vp8LTransform transform, uint[] pixelData) |
|
|
|
{ |
|
|
|
int bitsPerPixel = 8 >> transform.Bits; |
|
|
|
int width = transform.XSize; |
|
|
|
int height = transform.YSize; |
|
|
|
uint[] colorMap = transform.Data; |
|
|
|
int decodedPixels = 0; |
|
|
|
if (bitsPerPixel < 8) |
|
|
|
{ |
|
|
|
int pixelsPerByte = 1 << transform.Bits; |
|
|
|
int countMask = pixelsPerByte - 1; |
|
|
|
int bitMask = (1 << bitsPerPixel) - 1; |
|
|
|
|
|
|
|
// TODO: use memoryAllocator here
|
|
|
|
var decodedPixelData = new uint[width * height]; |
|
|
|
int pixelDataPos = 0; |
|
|
|
for (int y = 0; y < height; ++y) |
|
|
|
{ |
|
|
|
uint packedPixels = 0; |
|
|
|
for (int x = 0; x < width; ++x) |
|
|
|
{ |
|
|
|
// We need to load fresh 'packed_pixels' once every
|
|
|
|
// 'pixelsPerByte' increments of x. Fortunately, pixelsPerByte
|
|
|
|
// is a power of 2, so can just use a mask for that, instead of
|
|
|
|
// decrementing a counter.
|
|
|
|
if ((x & countMask) is 0) |
|
|
|
{ |
|
|
|
packedPixels = GetARGBIndex(pixelData[pixelDataPos++]); |
|
|
|
} |
|
|
|
|
|
|
|
decodedPixelData[decodedPixels++] = colorMap[packedPixels & bitMask]; |
|
|
|
packedPixels >>= bitsPerPixel; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
decodedPixelData.AsSpan().CopyTo(pixelData); |
|
|
|
|
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
for (int y = 0; y < height; ++y) |
|
|
|
{ |
|
|
|
for (int x = 0; x < width; ++x) |
|
|
|
{ |
|
|
|
uint colorMapIndex = GetARGBIndex(pixelData[decodedPixels]); |
|
|
|
pixelData[decodedPixels] = colorMap[colorMapIndex]; |
|
|
|
decodedPixels++; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public static void ColorSpaceInverseTransform(Vp8LTransform transform, uint[] pixelData) |
|
|
|
{ |
|
|
|
int width = transform.XSize; |
|
|
|
@ -78,11 +132,36 @@ namespace SixLabors.ImageSharp.Formats.WebP |
|
|
|
newBlue += ColorTransformDelta(m.GreenToBlue, (sbyte)green); |
|
|
|
newBlue += ColorTransformDelta(m.RedToBlue, (sbyte)newRed); |
|
|
|
newBlue &= 0xff; |
|
|
|
var pixelValue = (uint)((argb & 0xff00ff00u) | (newRed << 16) | newBlue); |
|
|
|
// uint pixelValue = (uint)((argb & 0xff00ff00u) | (newRed << 16) | newBlue);
|
|
|
|
pixelData[i] = (uint)((argb & 0xff00ff00u) | (newRed << 16) | newBlue); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public static uint[] ExpandColorMap(int numColors, Vp8LTransform transform, uint[] transformData) |
|
|
|
{ |
|
|
|
int finalNumColors = 1 << (8 >> transform.Bits); |
|
|
|
|
|
|
|
// TODO: use memoryAllocator here
|
|
|
|
var newColorMap = new uint[finalNumColors]; |
|
|
|
newColorMap[0] = transformData[0]; |
|
|
|
|
|
|
|
Span<byte> data = MemoryMarshal.Cast<uint, byte>(transformData); |
|
|
|
Span<byte> newData = MemoryMarshal.Cast<uint, byte>(newColorMap); |
|
|
|
int i; |
|
|
|
for (i = 4; i < 4 * numColors; ++i) |
|
|
|
{ |
|
|
|
// Equivalent to AddPixelEq(), on a byte-basis.
|
|
|
|
newData[i] = (byte)((data[i] + newData[i - 4]) & 0xff); |
|
|
|
} |
|
|
|
|
|
|
|
for (; i < 4 * finalNumColors; ++i) |
|
|
|
{ |
|
|
|
newData[i] = 0; // black tail.
|
|
|
|
} |
|
|
|
|
|
|
|
return newColorMap; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Computes sampled size of 'size' when sampling using 'sampling bits'.
|
|
|
|
/// </summary>
|
|
|
|
@ -91,6 +170,39 @@ namespace SixLabors.ImageSharp.Formats.WebP |
|
|
|
return (size + (1 << samplingBits) - 1) >> samplingBits; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Sum of each component, mod 256.
|
|
|
|
/// </summary>
|
|
|
|
private static uint AddPixels(uint a, uint b) |
|
|
|
{ |
|
|
|
uint alphaAndGreen = (a & 0xff00ff00u) + (b & 0xff00ff00u); |
|
|
|
uint redAndBlue = (a & 0x00ff00ffu) + (b & 0x00ff00ffu); |
|
|
|
return (alphaAndGreen & 0xff00ff00u) | (redAndBlue & 0x00ff00ffu); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Difference of each component, mod 256.
|
|
|
|
/// </summary>
|
|
|
|
private static uint SubPixels(uint a, uint b) |
|
|
|
{ |
|
|
|
uint alphaAndGreen = 0x00ff00ffu + (a & 0xff00ff00u) - (b & 0xff00ff00u); |
|
|
|
uint redAndBlue = 0xff00ff00u + (a & 0x00ff00ffu) - (b & 0x00ff00ffu); |
|
|
|
return (alphaAndGreen & 0xff00ff00u) | (redAndBlue & 0x00ff00ffu); |
|
|
|
} |
|
|
|
|
|
|
|
private static void PredictorAdd1(uint[] pixelData, int numPixels) |
|
|
|
{ |
|
|
|
/*for (int i = 0; i < num_pixels; ++i) |
|
|
|
{ |
|
|
|
pixelData[i] = VP8LAddPixels(in[i], left); |
|
|
|
}*/ |
|
|
|
} |
|
|
|
|
|
|
|
private static uint GetARGBIndex(uint idx) |
|
|
|
{ |
|
|
|
return (idx >> 8) & 0xff; |
|
|
|
} |
|
|
|
|
|
|
|
private static int ColorTransformDelta(sbyte colorPred, sbyte color) |
|
|
|
{ |
|
|
|
return ((int)colorPred * color) >> 5; |
|
|
|
|