Browse Source

Implementing FinishRow, EmitRgb

pull/1552/head
Brian Popow 6 years ago
parent
commit
c3be7a6604
  1. 39
      src/ImageSharp/Formats/WebP/LossyUtils.cs
  2. 71
      src/ImageSharp/Formats/WebP/Vp8Decoder.cs
  3. 7
      src/ImageSharp/Formats/WebP/Vp8Io.cs
  4. 10
      src/ImageSharp/Formats/WebP/WebPConstants.cs
  5. 285
      src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs

39
src/ImageSharp/Formats/WebP/LossyUtils.cs

@ -319,6 +319,39 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
}
// We process u and v together stashed into 32bit(16bit each).
public static uint LoadUv(byte u, byte v)
{
return (uint)(u | (v << 16));
}
public static void YuvToBgr(int y, int u, int v, Span<byte> bgr)
{
bgr[0] = (byte)YuvToB(y, u);
bgr[1] = (byte)YuvToG(y, u, v);
bgr[2] = (byte)YuvToR(y, v);
}
public static int YuvToR(int y, int v)
{
return Clip8(MultHi(y, 19077) + MultHi(v, 26149) - 14234);
}
public static int YuvToG(int y, int u, int v)
{
return Clip8(MultHi(y, 19077) - MultHi(u, 6419) - MultHi(v, 13320) + 8708);
}
public static int YuvToB(int y, int u)
{
return Clip8(MultHi(y, 19077) + MultHi(u, 33050) - 17685);
}
private static int MultHi(int v, int coeff)
{
return (v * coeff) >> 8;
}
private static void Store(Span<byte> dst, int x, int y, int v)
{
dst[x + (y * WebPConstants.Bps)] = Clip8B(dst[x + (y * WebPConstants.Bps)] + (v >> 3));
@ -347,6 +380,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
return (byte)((v & ~0xff) is 0 ? v : (v < 0) ? 0 : 255);
}
private static byte Clip8(int v)
{
int yuvMask = (256 << 6) - 1;
return (byte)(((v & ~yuvMask) is 0) ? (v >> 6) : (v < 0) ? 0 : 255);
}
private static void Put8x8uv(byte value, Span<byte> dst)
{
for (int j = 0; j < 8; ++j)

71
src/ImageSharp/Formats/WebP/Vp8Decoder.cs

@ -8,17 +8,19 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// </summary>
internal class Vp8Decoder
{
public Vp8Decoder(Vp8FrameHeader frameHeader, Vp8PictureHeader pictureHeader, Vp8FilterHeader filterHeader, Vp8SegmentHeader segmentHeader, Vp8Proba probabilities, Vp8Io io)
public Vp8Decoder(Vp8FrameHeader frameHeader, Vp8PictureHeader pictureHeader, Vp8SegmentHeader segmentHeader, Vp8Proba probabilities, Vp8Io io)
{
this.FilterHeader = new Vp8FilterHeader();
this.FrameHeader = frameHeader;
this.PictureHeader = pictureHeader;
this.FilterHeader = filterHeader;
this.SegmentHeader = segmentHeader;
this.Probabilities = probabilities;
this.IntraL = new byte[4];
this.YuvBuffer = new byte[(WebPConstants.Bps * 17) + (WebPConstants.Bps * 9)];
this.MbWidth = (int)((this.PictureHeader.Width + 15) >> 4);
this.MbHeight = (int)((this.PictureHeader.Height + 15) >> 4);
this.CacheYStride = 16 * this.MbWidth;
this.CacheUvStride = 8 * this.MbWidth;
this.MacroBlockInfo = new Vp8MacroBlock[this.MbWidth + 1];
this.MacroBlockData = new Vp8MacroBlockData[this.MbWidth];
this.YuvTopSamples = new Vp8TopSamples[this.MbWidth];
@ -48,9 +50,13 @@ namespace SixLabors.ImageSharp.Formats.WebP
uint height = pictureHeader.Height;
// TODO: use memory allocator
this.Y = new byte[width * height];
this.U = new byte[width * height];
this.V = new byte[width * height];
this.CacheY = new byte[width * height]; // TODO: this is way too much mem, figure out what the min req is.
this.CacheU = new byte[width * height];
this.CacheV = new byte[width * height];
this.TmpYBuffer = new byte[width * height]; // TODO: figure out min buffer length
this.TmpUBuffer = new byte[width * height]; // TODO: figure out min buffer length
this.TmpVBuffer = new byte[width * height]; // TODO: figure out min buffer length
this.Bgr = new byte[width * height * 4];
this.Vp8BitReaders = new Vp8BitReader[WebPConstants.MaxNumPartitions];
this.Init(io);
@ -147,39 +153,58 @@ namespace SixLabors.ImageSharp.Formats.WebP
public Vp8TopSamples[] YuvTopSamples { get; }
public byte[] Y { get; }
public byte[] CacheY { get; }
public byte[] U { get; }
public byte[] CacheU { get; }
public byte[] V { get; }
public byte[] CacheV { get; }
public int YStride { get; }
public int CacheYStride { get; }
public int UvStride { get; }
public int CacheUvStride { get; }
public byte[] TmpYBuffer { get; }
public byte[] TmpUBuffer { get; }
public byte[] TmpVBuffer { get; }
public byte[] Bgr { get; }
/// <summary>
/// Gets or sets filter strength info.
/// </summary>
public Vp8FilterInfo[] FilterInfo { get; set; }
public Vp8MacroBlock CurrentMacroBlock
{
get
{
return this.MacroBlockInfo[this.MbX + 1];
}
}
public Vp8MacroBlock LeftMacroBlock
{
get
{
return this.MacroBlockInfo[this.MbX];
}
}
public Vp8MacroBlockData CurrentBlockData
{
get
{
return this.MacroBlockData[this.MbX];
}
}
public void Init(Vp8Io io)
{
int intraPredModeSize = 4 * this.MbWidth;
this.IntraT = new byte[intraPredModeSize];
io.Width = (int)this.PictureHeader.Width;
io.Height = (int)this.PictureHeader.Height;
io.UseCropping = false;
io.CropTop = 0;
io.CropLeft = 0;
io.CropRight = io.Width;
io.CropBottom = io.Height;
io.UseScaling = false;
io.ScaledWidth = io.Width;
io.ScaledHeight = io.ScaledHeight;
io.MbW = io.Width;
io.MbH = io.Height;
int extraPixels = WebPConstants.FilterExtraRows[(int)this.Filter];
if (this.Filter is LoopFilter.Complex)
{

7
src/ImageSharp/Formats/WebP/Vp8Io.cs

@ -5,7 +5,6 @@ using System;
namespace SixLabors.ImageSharp.Formats.WebP
{
// from
public ref struct Vp8Io
{
/// <summary>
@ -40,17 +39,17 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <summary>
/// Rows to copy (in YUV format)
/// </summary>
private Span<byte> Y { get; set; }
public Span<byte> Y { get; set; }
/// <summary>
/// Rows to copy (in YUV format)
/// </summary>
private Span<byte> U { get; set; }
public Span<byte> U { get; set; }
/// <summary>
/// Rows to copy (in YUV format)
/// </summary>
private Span<byte> V { get; set; }
public Span<byte> V { get; set; }
/// <summary>
/// Row stride for luma

10
src/ImageSharp/Formats/WebP/WebPConstants.cs

@ -143,12 +143,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 0
};
public static readonly short[] KScan =
public static readonly short[] Scan =
{
0 + 0 * Bps, 4 + 0 * Bps, 8 + 0 * Bps, 12 + 0 * Bps,
0 + 4 * Bps, 4 + 4 * Bps, 8 + 4 * Bps, 12 + 4 * Bps,
0 + 8 * Bps, 4 + 8 * Bps, 8 + 8 * Bps, 12 + 8 * Bps,
0 + 12 * Bps, 4 + 12 * Bps, 8 + 12 * Bps, 12 + 12 * Bps
0 + (0 * Bps), 4 + (0 * Bps), 8 + (0 * Bps), 12 + (0 * Bps),
0 + (4 * Bps), 4 + (4 * Bps), 8 + (4 * Bps), 12 + (4 * Bps),
0 + (8 * Bps), 4 + (8 * Bps), 8 + (8 * Bps), 12 + (8 * Bps),
0 + (12 * Bps), 4 + (12 * Bps), 8 + (12 * Bps), 12 + (12 * Bps)
};
// 31 ^ clz(i)

285
src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
@ -39,7 +40,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
// Paragraph 9.2: color space and clamp type follow.
sbyte colorSpace = (sbyte)this.bitReader.ReadValue(1);
sbyte clampType = (sbyte)this.bitReader.ReadValue(1);
var vp8PictureHeader = new Vp8PictureHeader()
var pictureHeader = new Vp8PictureHeader()
{
Width = (uint)width,
Height = (uint)height,
@ -53,11 +54,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
var proba = new Vp8Proba();
Vp8SegmentHeader vp8SegmentHeader = this.ParseSegmentHeader(proba);
// Paragraph 9.4: Parse the filter specs.
Vp8FilterHeader vp8FilterHeader = this.ParseFilterHeader();
Vp8Io io = InitializeVp8Io(pictureHeader);
var decoder = new Vp8Decoder(info.Vp8FrameHeader, pictureHeader, vp8SegmentHeader, proba, io);
var vp8Io = default(Vp8Io);
var decoder = new Vp8Decoder(info.Vp8FrameHeader, vp8PictureHeader, vp8FilterHeader, vp8SegmentHeader, proba, vp8Io);
// Paragraph 9.4: Parse the filter specs.
this.ParseFilterHeader(decoder);
// Paragraph 9.5: Parse partitions.
this.ParsePartitions(decoder);
@ -72,7 +73,28 @@ namespace SixLabors.ImageSharp.Formats.WebP
this.ParseProbabilities(decoder);
// Decode image data.
this.ParseFrame(decoder, vp8Io);
this.ParseFrame(decoder, io);
this.DecodePixelValues(width, height, decoder.Bgr, pixels);
}
private void DecodePixelValues<TPixel>(int width, int height, Span<byte> pixelData, Buffer2D<TPixel> pixels)
where TPixel : struct, IPixel<TPixel>
{
TPixel color = default;
for (int y = 0; y < height; y++)
{
Span<TPixel> pixelRow = pixels.GetRowSpan(y);
for (int x = 0; x < width; x++)
{
int idx = ((y * width) + x) * 3;
byte b = pixelData[idx];
byte g = pixelData[idx + 1];
byte r = pixelData[idx + 2];
color.FromRgba32(new Rgba32(r, g, b, 255));
pixelRow[x] = color;
}
}
}
private void ParseFrame(Vp8Decoder dec, Vp8Io io)
@ -98,7 +120,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
this.InitScanline(dec);
// Reconstruct, filter and emit the row.
this.ProcessRow(dec);
this.ProcessRow(dec, io);
}
}
@ -184,12 +206,13 @@ namespace SixLabors.ImageSharp.Formats.WebP
dec.MbX = 0;
}
private void ProcessRow(Vp8Decoder dec)
private void ProcessRow(Vp8Decoder dec, Vp8Io io)
{
bool filterRow = (dec.Filter != LoopFilter.None) &&
(dec.MbY >= dec.TopLeftMbY) && (dec.MbY <= dec.BottomRightMbY);
this.ReconstructRow(dec, filterRow);
this.FinishRow(dec, io);
}
private void ReconstructRow(Vp8Decoder dec, bool filterRow)
@ -307,7 +330,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
for (int n = 0; n < 16; ++n, bits <<= 2)
{
// uint8_t * const dst = y_dst + kScan[n];
Span<byte> dst = yDst.Slice(WebPConstants.KScan[n]);
Span<byte> dst = yDst.Slice(WebPConstants.Scan[n]);
byte lumaMode = block.Modes[n];
switch (lumaMode)
{
@ -379,7 +402,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
{
for (int n = 0; n < 16; ++n, bits <<= 2)
{
this.DoTransform(bits, coeffs.AsSpan(n * 16), yDst.Slice(WebPConstants.KScan[n]));
this.DoTransform(bits, coeffs.AsSpan(n * 16), yDst.Slice(WebPConstants.Scan[n]));
}
}
}
@ -431,25 +454,209 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
// Transfer reconstructed samples from yuv_b_ cache to final destination.
int cacheId = 0; // TODO: what should be cacheId?
int yOffset = cacheId * 16 * dec.YStride;
int uvOffset = cacheId * 8 * dec.UvStride;
Span<byte> yOut = dec.Y.AsSpan((mbx * 16) + yOffset);
Span<byte> uOut = dec.U.AsSpan((mbx * 8) + uvOffset);
Span<byte> vOut = dec.V.AsSpan((mbx * 8) + uvOffset);
int cacheId = 0; // TODO: what should be cacheId, always 0?
int yOffset = cacheId * 16 * dec.CacheYStride;
int uvOffset = cacheId * 8 * dec.CacheUvStride;
Span<byte> yOut = dec.CacheY.AsSpan((mbx * 16) + yOffset);
Span<byte> uOut = dec.CacheU.AsSpan((mbx * 8) + uvOffset);
Span<byte> vOut = dec.CacheV.AsSpan((mbx * 8) + uvOffset);
for (int j = 0; j < 16; ++j)
{
yDst.Slice(j * WebPConstants.Bps, 16).CopyTo(yOut.Slice(j * dec.YStride));
yDst.Slice(j * WebPConstants.Bps, 16).CopyTo(yOut.Slice(j * dec.CacheYStride));
}
for (int j = 0; j < 8; ++j)
{
uDst.Slice(j * WebPConstants.Bps, 8).CopyTo(uOut);
vDst.Slice(j * WebPConstants.Bps, 8).CopyTo(vOut);
uDst.Slice(j * WebPConstants.Bps, 8).CopyTo(uOut.Slice(j * dec.CacheUvStride));
vDst.Slice(j * WebPConstants.Bps, 8).CopyTo(vOut.Slice(j * dec.CacheUvStride));
}
}
}
private void FinishRow(Vp8Decoder dec, Vp8Io io)
{
int cacheId = 0;
int yBps = dec.CacheYStride;
int extraYRows = WebPConstants.FilterExtraRows[(int)dec.Filter];
int ySize = extraYRows * dec.CacheYStride;
int uvSize = (extraYRows / 2) * dec.CacheUvStride;
int yOffset = cacheId * 16 * dec.CacheYStride;
int uvOffset = cacheId * 8 * dec.CacheUvStride;
Span<byte> yDst = dec.CacheY.AsSpan();
Span<byte> uDst = dec.CacheU.AsSpan();
Span<byte> vDst = dec.CacheV.AsSpan();
int mby = dec.MbY;
bool isFirstRow = mby is 0;
bool isLastRow = mby >= dec.BottomRightMbY - 1;
// TODO: Filter row
//FilterRow(dec);
int yStart = mby * 16;
int yEnd = (mby + 1) * 16;
if (!isFirstRow)
{
yStart -= extraYRows;
io.Y = yDst;
io.U = uDst;
io.V = vDst;
}
else
{
io.Y = dec.CacheY.AsSpan(yOffset);
io.U = dec.CacheU.AsSpan(uvOffset);
io.V = dec.CacheV.AsSpan(uvOffset);
}
if (!isLastRow)
{
yEnd -= extraYRows;
}
if (yStart < yEnd)
{
io.Y = io.Y.Slice(io.CropLeft);
io.U = io.U.Slice(io.CropLeft);
io.V = io.V.Slice(io.CropLeft);
io.MbY = yStart - io.CropTop;
io.MbW = io.CropRight - io.CropLeft;
io.MbH = yEnd - yStart;
this.EmitRgb(dec, io);
}
// Rotate top samples if needed.
if (!isLastRow)
{
// TODO: double check this.
yDst.Slice(16 * dec.CacheYStride, ySize).CopyTo(dec.CacheY);
uDst.Slice(8 * dec.CacheUvStride, uvSize).CopyTo(dec.CacheU);
vDst.Slice(8 * dec.CacheUvStride, uvSize).CopyTo(dec.CacheV);
}
}
private int EmitRgb(Vp8Decoder dec, Vp8Io io)
{
byte[] buf = dec.Bgr;
int numLinesOut = io.MbH; // a priori guess.
Span<byte> curY = io.Y;
Span<byte> curU = io.U;
Span<byte> curV = io.V;
byte[] tmpYBuffer = dec.TmpYBuffer;
byte[] tmpUBuffer = dec.TmpUBuffer;
byte[] tmpVBuffer = dec.TmpVBuffer;
Span<byte> topU = tmpUBuffer.AsSpan();
Span<byte> topV = tmpVBuffer.AsSpan();
int bpp = 3;
int bufferStride = bpp * io.Width;
int dstStartIdx = io.MbY * bufferStride;
Span<byte> dst = buf.AsSpan(dstStartIdx);
int yEnd = io.MbY + io.MbH;
int mbw = io.MbW;
int uvw = (mbw + 1) / 2;
int y = io.MbY;
if (y is 0)
{
// First line is special cased. We mirror the u/v samples at boundary.
this.UpSample(curY, null, curU, curV, curU, curV, dst, null, mbw);
}
else
{
// We can finish the left-over line from previous call.
this.UpSample(tmpYBuffer.AsSpan(), curY, topU, topV, curU, curV, buf.AsSpan(dstStartIdx - bufferStride), dst, mbw);
numLinesOut++;
}
// Loop over each output pairs of row.
for (; y + 2 < yEnd; y += 2)
{
topU = curU;
topV = curV;
curU = curU.Slice(io.UvStride);
curV = curV.Slice(io.UvStride);
this.UpSample(curY.Slice(io.YStride), curY, topU, topV, curU, curV, dst.Slice(bufferStride), dst.Slice(2 * bufferStride), mbw);
curY = curY.Slice(2 * io.YStride);
dst = dst.Slice(2 * bufferStride);
}
// Move to last row.
curY = curY.Slice(io.YStride);
if (io.CropTop + yEnd < io.CropBottom)
{
// Save the unfinished samples for next call (as we're not done yet).
curY.Slice(0, mbw).CopyTo(tmpYBuffer);
curU.Slice(0, uvw).CopyTo(tmpUBuffer);
curV.Slice(0, uvw).CopyTo(tmpVBuffer);
// The upsampler leaves a row unfinished behind (except for the very last row).
numLinesOut--;
}
else
{
// Process the very last row of even-sized picture.
if ((yEnd & 1) is 0)
{
this.UpSample(curY, null, curU, curV, curU, curV, dst.Slice(bufferStride), null, mbw);
}
}
return numLinesOut;
}
private void UpSample(Span<byte> topY, Span<byte> bottomY, Span<byte> topU, Span<byte> topV, Span<byte> curU, Span<byte> curV, Span<byte> topDst, Span<byte> bottomDst, int len)
{
int xStep = 3;
int lastPixelPair = (len - 1) >> 1;
uint tluv = LossyUtils.LoadUv(topU[0], topV[0]); // top-left sample
uint luv = LossyUtils.LoadUv(curU[0], curV[0]); // left-sample
uint uv0 = ((3 * tluv) + luv + 0x00020002u) >> 2;
LossyUtils.YuvToBgr(topY[0], (int)(uv0 & 0xff), (int)(uv0 >> 16), topDst);
if (bottomY != null)
{
uv0 = ((3 * luv) + tluv + 0x00020002u) >> 2;
LossyUtils.YuvToBgr(bottomY[0], (int)uv0 & 0xff, (int)(uv0 >> 16), bottomDst);
}
for (int x = 1; x <= lastPixelPair; ++x)
{
uint tuv = LossyUtils.LoadUv(topU[x], topV[x]); // top sample
uint uv = LossyUtils.LoadUv(curU[x], curV[x]); // sample
// Precompute invariant values associated with first and second diagonals.
uint avg = tluv + tuv + luv + uv + 0x00080008u;
uint diag12 = (avg + (2 * (tuv + luv))) >> 3;
uint diag03 = (avg + (2 * (tluv + uv))) >> 3;
uv0 = (diag12 + tluv) >> 1;
uint uv1 = (diag03 + tuv) >> 1;
LossyUtils.YuvToBgr(topY[(2 * x) - 1], (int)(uv0 & 0xff), (int)(uv0 >> 16), topDst.Slice(((2 * x) - 1) * xStep));
LossyUtils.YuvToBgr(topY[(2 * x) - 0], (int)(uv1 & 0xff), (int)(uv1 >> 16), topDst.Slice(((2 * x) - 0) * xStep));
if (bottomY != null)
{
uv0 = (diag03 + luv) >> 1;
uv1 = (diag12 + uv) >> 1;
LossyUtils.YuvToBgr(bottomY[(2 * x) - 1], (int)(uv0 & 0xff), (int)(uv0 >> 16), bottomDst.Slice(((2 * x) - 1) * xStep));
LossyUtils.YuvToBgr(bottomY[(2 * x) + 0], (int)(uv1 & 0xff), (int)(uv1 >> 16), bottomDst.Slice(((2 * x) + 0) * xStep));
}
tluv = tuv;
luv = uv;
}
/*if ((len & 1) is 0)
{
uv0 = ((3 * tluv) + luv + 0x00020002u) >> 2;
LossyUtils.YuvToBgr(topY[len - 1], (int)(uv0 & 0xff), (int)(uv0 >> 16), topDst.Slice((len - 1) * xStep));
if (bottomY != null)
{
uv0 = ((3 * luv) + tluv + 0x00020002u) >> 2;
LossyUtils.YuvToBgr(bottomY[len - 1], (int)(uv0 & 0xff), (int)(uv0 >> 16), bottomDst.Slice((len - 1) * xStep));
}
}*/
}
private void DoTransform(uint bits, Span<short> src, Span<byte> dst)
{
switch (bits >> 30)
@ -507,9 +714,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
private void DecodeMacroBlock(Vp8Decoder dec, Vp8BitReader bitreader)
{
Vp8MacroBlock left = dec.MacroBlockInfo[0];
Vp8MacroBlock macroBlock = dec.MacroBlockInfo[1 + dec.MbX];
Vp8MacroBlockData blockData = dec.MacroBlockData[dec.MbX];
Vp8MacroBlock left = dec.LeftMacroBlock;
Vp8MacroBlock macroBlock = dec.CurrentMacroBlock;
Vp8MacroBlockData blockData = dec.CurrentBlockData;
int skip = dec.UseSkipProbability ? blockData.Skip : 0;
if (skip is 0)
@ -543,11 +750,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
uint nonZeroUv = 0;
int first;
int dstOffset = 0;
Vp8MacroBlockData block = dec.MacroBlockData[dec.MbX];
Vp8MacroBlockData block = dec.CurrentBlockData;
Vp8QuantMatrix q = dec.DeQuantMatrices[block.Segment];
Vp8BandProbas[,] bands = dec.Probabilities.BandsPtr;
Vp8BandProbas[] acProba;
Vp8MacroBlock leftMb = dec.MacroBlockInfo[0];
Vp8MacroBlock leftMb = dec.LeftMacroBlock;
short[] dst = block.Coeffs;
if (!block.IsI4x4)
@ -593,7 +800,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
int nz = this.GetCoeffs(br, acProba, ctx, q.Y1Mat, first, dst.AsSpan(dstOffset));
l = (nz > first) ? 1 : 0;
tnz = (byte)((tnz >> 1) | (l << 7));
nzCoeffs = NzCodeBits(nzCoeffs, nz, dst[0] != 0 ? 1 : 0);
nzCoeffs = NzCodeBits(nzCoeffs, nz, dst[dstOffset] != 0 ? 1 : 0);
dstOffset += 16;
}
@ -619,7 +826,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
int nz = this.GetCoeffs(br, GetBandsRow(bands, 2), ctx, q.UvMat, 0, dst.AsSpan(dstOffset));
l = (nz > 0) ? 1 : 0;
tnz = (byte)((tnz >> 1) | (l << 3));
nzCoeffs = NzCodeBits(nzCoeffs, nz, dst[0] != 0 ? 1 : 0);
nzCoeffs = NzCodeBits(nzCoeffs, nz, dst[dstOffset] != 0 ? 1 : 0);
dstOffset += 16;
}
@ -836,17 +1043,15 @@ namespace SixLabors.ImageSharp.Formats.WebP
return vp8SegmentHeader;
}
private Vp8FilterHeader ParseFilterHeader()
private Vp8FilterHeader ParseFilterHeader(Vp8Decoder dec)
{
var vp8FilterHeader = new Vp8FilterHeader();
Vp8FilterHeader vp8FilterHeader = dec.FilterHeader;
vp8FilterHeader.LoopFilter = this.bitReader.ReadBool() ? LoopFilter.Simple : LoopFilter.Complex;
vp8FilterHeader.Level = (int)this.bitReader.ReadValue(6);
vp8FilterHeader.Sharpness = (int)this.bitReader.ReadValue(3);
vp8FilterHeader.UseLfDelta = this.bitReader.ReadBool();
// TODO: use enum here?
// 0 = 0ff, 1 = simple, 2 = complex
int filterType = (vp8FilterHeader.Level is 0) ? 0 : vp8FilterHeader.LoopFilter is LoopFilter.Simple ? 1 : 2;
dec.Filter = (vp8FilterHeader.Level is 0) ? LoopFilter.None : vp8FilterHeader.LoopFilter;
if (vp8FilterHeader.UseLfDelta)
{
// Update lf-delta?
@ -998,6 +1203,24 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
}
private static Vp8Io InitializeVp8Io(Vp8PictureHeader pictureHeader)
{
var io = default(Vp8Io);
io.Width = (int)pictureHeader.Width;
io.Height = (int)pictureHeader.Height;
io.UseCropping = false;
io.CropTop = 0;
io.CropLeft = 0;
io.CropRight = io.Width;
io.CropBottom = io.Height;
io.UseScaling = false;
io.ScaledWidth = io.Width;
io.ScaledHeight = io.ScaledHeight;
io.MbW = io.Width;
io.MbH = io.Height;
return io;
}
static bool Is8bOptimizable(Vp8LMetadata hdr)
{
int i;

Loading…
Cancel
Save