Browse Source

Vp8Decoder now uses memory allocator

pull/1552/head
Brian Popow 6 years ago
parent
commit
c0bbd0631e
  1. 92
      src/ImageSharp/Formats/WebP/LossyUtils.cs
  2. 82
      src/ImageSharp/Formats/WebP/Vp8Decoder.cs
  3. 151
      src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs

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

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
public static void DC16(Span<byte> dst, byte[] yuv, int offset) public static void DC16(Span<byte> dst, Span<byte> yuv, int offset)
{ {
int dc = 16; int dc = 16;
for (int j = 0; j < 16; ++j) for (int j = 0; j < 16; ++j)
@ -28,15 +28,15 @@ namespace SixLabors.ImageSharp.Formats.WebP
Put16(dc >> 5, dst); Put16(dc >> 5, dst);
} }
public static void TM16(Span<byte> dst, byte[] yuv, int offset) public static void TM16(Span<byte> dst, Span<byte> yuv, int offset)
{ {
TrueMotion(dst, yuv, offset, 16); TrueMotion(dst, yuv, offset, 16);
} }
public static void VE16(Span<byte> dst, byte[] yuv, int offset) public static void VE16(Span<byte> dst, Span<byte> yuv, int offset)
{ {
// vertical // vertical
Span<byte> src = yuv.AsSpan(offset - WebPConstants.Bps, 16); Span<byte> src = yuv.Slice(offset - WebPConstants.Bps, 16);
for (int j = 0; j < 16; ++j) for (int j = 0; j < 16; ++j)
{ {
// memcpy(dst + j * BPS, dst - BPS, 16); // memcpy(dst + j * BPS, dst - BPS, 16);
@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
public static void HE16(Span<byte> dst, byte[] yuv, int offset) public static void HE16(Span<byte> dst, Span<byte> yuv, int offset)
{ {
// horizontal // horizontal
for (int j = 16; j > 0; --j) for (int j = 16; j > 0; --j)
@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
public static void DC16NoTop(Span<byte> dst, byte[] yuv, int offset) public static void DC16NoTop(Span<byte> dst, Span<byte> yuv, int offset)
{ {
// DC with top samples not available. // DC with top samples not available.
int dc = 8; int dc = 8;
@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
Put16(dc >> 4, dst); Put16(dc >> 4, dst);
} }
public static void DC16NoLeft(Span<byte> dst, byte[] yuv, int offset) public static void DC16NoLeft(Span<byte> dst, Span<byte> yuv, int offset)
{ {
// DC with left samples not available. // DC with left samples not available.
int dc = 8; int dc = 8;
@ -89,7 +89,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
Put16(0x80, dst); Put16(0x80, dst);
} }
public static void DC8uv(Span<byte> dst, byte[] yuv, int offset) public static void DC8uv(Span<byte> dst, Span<byte> yuv, int offset)
{ {
int dc0 = 8; int dc0 = 8;
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
@ -101,16 +101,16 @@ namespace SixLabors.ImageSharp.Formats.WebP
Put8x8uv((byte)(dc0 >> 4), dst); Put8x8uv((byte)(dc0 >> 4), dst);
} }
public static void TM8uv(Span<byte> dst, byte[] yuv, int offset) public static void TM8uv(Span<byte> dst, Span<byte> yuv, int offset)
{ {
// TrueMotion // TrueMotion
TrueMotion(dst, yuv, offset, 8); TrueMotion(dst, yuv, offset, 8);
} }
public static void VE8uv(Span<byte> dst, byte[] yuv, int offset) public static void VE8uv(Span<byte> dst, Span<byte> yuv, int offset)
{ {
// vertical // vertical
Span<byte> src = yuv.AsSpan(offset - WebPConstants.Bps, 8); Span<byte> src = yuv.Slice(offset - WebPConstants.Bps, 8);
for (int j = 0; j < 8; ++j) for (int j = 0; j < 8; ++j)
{ {
@ -119,7 +119,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
public static void HE8uv(Span<byte> dst, byte[] yuv, int offset) public static void HE8uv(Span<byte> dst, Span<byte> yuv, int offset)
{ {
// horizontal // horizontal
for (int j = 0; j < 8; ++j) for (int j = 0; j < 8; ++j)
@ -133,7 +133,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
public static void DC8uvNoTop(Span<byte> dst, byte[] yuv, int offset) public static void DC8uvNoTop(Span<byte> dst, Span<byte> yuv, int offset)
{ {
// DC with no top samples. // DC with no top samples.
int dc0 = 4; int dc0 = 4;
@ -146,7 +146,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
Put8x8uv((byte)(dc0 >> 3), dst); Put8x8uv((byte)(dc0 >> 3), dst);
} }
public static void DC8uvNoLeft(Span<byte> dst, byte[] yuv, int offset) public static void DC8uvNoLeft(Span<byte> dst, Span<byte> yuv, int offset)
{ {
// DC with no left samples. // DC with no left samples.
int dc0 = 4; int dc0 = 4;
@ -165,7 +165,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
Put8x8uv(0x80, dst); Put8x8uv(0x80, dst);
} }
public static void DC4(Span<byte> dst, byte[] yuv, int offset) public static void DC4(Span<byte> dst, Span<byte> yuv, int offset)
{ {
int dc = 4; int dc = 4;
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
@ -180,12 +180,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
public static void TM4(Span<byte> dst, byte[] yuv, int offset) public static void TM4(Span<byte> dst, Span<byte> yuv, int offset)
{ {
TrueMotion(dst, yuv, offset, 4); TrueMotion(dst, yuv, offset, 4);
} }
public static void VE4(Span<byte> dst, byte[] yuv, int offset) public static void VE4(Span<byte> dst, Span<byte> yuv, int offset)
{ {
// vertical // vertical
int topOffset = offset - WebPConstants.Bps; int topOffset = offset - WebPConstants.Bps;
@ -203,7 +203,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
public static void HE4(Span<byte> dst, byte[] yuv, int offset) public static void HE4(Span<byte> dst, Span<byte> yuv, int offset)
{ {
// horizontal // horizontal
byte a = yuv[offset - 1 - WebPConstants.Bps]; byte a = yuv[offset - 1 - WebPConstants.Bps];
@ -221,7 +221,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(3 * WebPConstants.Bps), val); BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(3 * WebPConstants.Bps), val);
} }
public static void RD4(Span<byte> dst, byte[] yuv, int offset) public static void RD4(Span<byte> dst, Span<byte> yuv, int offset)
{ {
// Down-right // Down-right
byte i = yuv[offset - 1]; byte i = yuv[offset - 1];
@ -257,7 +257,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
Dst(dst, 3, 0, Avg3(d, c, b)); Dst(dst, 3, 0, Avg3(d, c, b));
} }
public static void VR4(Span<byte> dst, byte[] yuv, int offset) public static void VR4(Span<byte> dst, Span<byte> yuv, int offset)
{ {
// Vertical-Right // Vertical-Right
byte i = yuv[offset - 1]; byte i = yuv[offset - 1];
@ -293,7 +293,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
Dst(dst, 3, 1, Avg3(b, c, d)); Dst(dst, 3, 1, Avg3(b, c, d));
} }
public static void LD4(Span<byte> dst, byte[] yuv, int offset) public static void LD4(Span<byte> dst, Span<byte> yuv, int offset)
{ {
// Down-Left // Down-Left
byte a = yuv[offset - WebPConstants.Bps]; byte a = yuv[offset - WebPConstants.Bps];
@ -328,7 +328,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
Dst(dst, 3, 3, Avg3(g, h, h)); Dst(dst, 3, 3, Avg3(g, h, h));
} }
public static void VL4(Span<byte> dst, byte[] yuv, int offset) public static void VL4(Span<byte> dst, Span<byte> yuv, int offset)
{ {
// Vertical-Left // Vertical-Left
byte a = yuv[offset - WebPConstants.Bps]; byte a = yuv[offset - WebPConstants.Bps];
@ -364,7 +364,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
Dst(dst, 3, 3, Avg3(f, g, h)); Dst(dst, 3, 3, Avg3(f, g, h));
} }
public static void HD4(Span<byte> dst, byte[] yuv, int offset) public static void HD4(Span<byte> dst, Span<byte> yuv, int offset)
{ {
// Horizontal-Down // Horizontal-Down
byte i = yuv[offset - 1]; byte i = yuv[offset - 1];
@ -400,7 +400,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
Dst(dst, 1, 3, Avg3(l, k, j)); Dst(dst, 1, 3, Avg3(l, k, j));
} }
public static void HU4(Span<byte> dst, byte[] yuv, int offset) public static void HU4(Span<byte> dst, Span<byte> yuv, int offset)
{ {
// Horizontal-Up // Horizontal-Up
byte i = yuv[offset - 1]; byte i = yuv[offset - 1];
@ -537,11 +537,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
private static void TrueMotion(Span<byte> dst, byte[] yuv, int offset, int size) private static void TrueMotion(Span<byte> dst, Span<byte> yuv, int offset, int size)
{ {
// For information about how true motion works, see rfc6386, page 52. ff and section 20.14. // For information about how true motion works, see rfc6386, page 52. ff and section 20.14.
int topOffset = offset - WebPConstants.Bps; int topOffset = offset - WebPConstants.Bps;
Span<byte> top = yuv.AsSpan(topOffset); Span<byte> top = yuv.Slice(topOffset);
byte p = yuv[topOffset - 1]; byte p = yuv[topOffset - 1];
int leftOffset = offset - 1; int leftOffset = offset - 1;
byte left = yuv[leftOffset]; byte left = yuv[leftOffset];
@ -559,7 +559,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
// Simple In-loop filtering (Paragraph 15.2) // Simple In-loop filtering (Paragraph 15.2)
public static void SimpleVFilter16(byte[] p, int offset, int stride, int thresh) public static void SimpleVFilter16(Span<byte> p, int offset, int stride, int thresh)
{ {
int thresh2 = (2 * thresh) + 1; int thresh2 = (2 * thresh) + 1;
for (int i = 0; i < 16; ++i) for (int i = 0; i < 16; ++i)
@ -571,7 +571,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
public static void SimpleHFilter16(byte[] p, int offset, int stride, int thresh) public static void SimpleHFilter16(Span<byte> p, int offset, int stride, int thresh)
{ {
int thresh2 = (2 * thresh) + 1; int thresh2 = (2 * thresh) + 1;
for (int i = 0; i < 16; ++i) for (int i = 0; i < 16; ++i)
@ -583,7 +583,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
public static void SimpleVFilter16i(byte[] p, int offset, int stride, int thresh) public static void SimpleVFilter16i(Span<byte> p, int offset, int stride, int thresh)
{ {
for (int k = 3; k > 0; --k) for (int k = 3; k > 0; --k)
{ {
@ -592,7 +592,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
public static void SimpleHFilter16i(byte[] p, int offset, int stride, int thresh) public static void SimpleHFilter16i(Span<byte> p, int offset, int stride, int thresh)
{ {
for (int k = 3; k > 0; --k) for (int k = 3; k > 0; --k)
{ {
@ -601,17 +601,17 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
public static void VFilter16(byte[] p, int offset, int stride, int thresh, int ithresh, int hevThresh) public static void VFilter16(Span<byte> p, int offset, int stride, int thresh, int ithresh, int hevThresh)
{ {
FilterLoop26(p, offset, stride, 1, 16, thresh, ithresh, hevThresh); FilterLoop26(p, offset, stride, 1, 16, thresh, ithresh, hevThresh);
} }
public static void HFilter16(byte[] p, int offset, int stride, int thresh, int ithresh, int hevThresh) public static void HFilter16(Span<byte> p, int offset, int stride, int thresh, int ithresh, int hevThresh)
{ {
FilterLoop26(p, offset, 1, stride, 16, thresh, ithresh, hevThresh); FilterLoop26(p, offset, 1, stride, 16, thresh, ithresh, hevThresh);
} }
public static void VFilter16i(byte[] p, int offset, int stride, int thresh, int ithresh, int hevThresh) public static void VFilter16i(Span<byte> p, int offset, int stride, int thresh, int ithresh, int hevThresh)
{ {
for (int k = 3; k > 0; --k) for (int k = 3; k > 0; --k)
{ {
@ -620,7 +620,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
public static void HFilter16i(byte[] p, int offset, int stride, int thresh, int ithresh, int hevThresh) public static void HFilter16i(Span<byte> p, int offset, int stride, int thresh, int ithresh, int hevThresh)
{ {
for (int k = 3; k > 0; --k) for (int k = 3; k > 0; --k)
{ {
@ -630,25 +630,25 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
// 8-pixels wide variant, for chroma filtering. // 8-pixels wide variant, for chroma filtering.
public static void VFilter8(byte[] u, byte[] v, int offset, int stride, int thresh, int ithresh, int hevThresh) public static void VFilter8(Span<byte> u, Span<byte> v, int offset, int stride, int thresh, int ithresh, int hevThresh)
{ {
FilterLoop26(u, offset, stride, 1, 8, thresh, ithresh, hevThresh); FilterLoop26(u, offset, stride, 1, 8, thresh, ithresh, hevThresh);
FilterLoop26(v, offset, stride, 1, 8, thresh, ithresh, hevThresh); FilterLoop26(v, offset, stride, 1, 8, thresh, ithresh, hevThresh);
} }
public static void HFilter8(byte[] u, byte[] v, int offset, int stride, int thresh, int ithresh, int hevThresh) public static void HFilter8(Span<byte> u, Span<byte> v, int offset, int stride, int thresh, int ithresh, int hevThresh)
{ {
FilterLoop26(u, offset, 1, stride, 8, thresh, ithresh, hevThresh); FilterLoop26(u, offset, 1, stride, 8, thresh, ithresh, hevThresh);
FilterLoop26(v, offset, 1, stride, 8, thresh, ithresh, hevThresh); FilterLoop26(v, offset, 1, stride, 8, thresh, ithresh, hevThresh);
} }
public static void VFilter8i(byte[] u, byte[] v, int offset, int stride, int thresh, int ithresh, int hevThresh) public static void VFilter8i(Span<byte> u, Span<byte> v, int offset, int stride, int thresh, int ithresh, int hevThresh)
{ {
FilterLoop24(u, offset + (4 * stride), stride, 1, 8, thresh, ithresh, hevThresh); FilterLoop24(u, offset + (4 * stride), stride, 1, 8, thresh, ithresh, hevThresh);
FilterLoop24(v, offset + (4 * stride), stride, 1, 8, thresh, ithresh, hevThresh); FilterLoop24(v, offset + (4 * stride), stride, 1, 8, thresh, ithresh, hevThresh);
} }
public static void HFilter8i(byte[] u, byte[] v, int offset, int stride, int thresh, int ithresh, int hevThresh) public static void HFilter8i(Span<byte> u, Span<byte> v, int offset, int stride, int thresh, int ithresh, int hevThresh)
{ {
FilterLoop24(u, offset + 4, 1, stride, 8, thresh, ithresh, hevThresh); FilterLoop24(u, offset + 4, 1, stride, 8, thresh, ithresh, hevThresh);
FilterLoop24(v, offset + 4, 1, stride, 8, thresh, ithresh, hevThresh); FilterLoop24(v, offset + 4, 1, stride, 8, thresh, ithresh, hevThresh);
@ -684,7 +684,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
// Complex In-loop filtering (Paragraph 15.3) // Complex In-loop filtering (Paragraph 15.3)
private static void FilterLoop24( private static void FilterLoop24(
byte[] p, Span<byte> p,
int offset, int offset,
int hStride, int hStride,
int vStride, int vStride,
@ -713,7 +713,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
private static void FilterLoop26( private static void FilterLoop26(
byte[] p, Span<byte> p,
int offset, int offset,
int hStride, int hStride,
int vStride, int vStride,
@ -741,7 +741,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
private static void DoFilter2(byte[] p, int offset, int step) private static void DoFilter2(Span<byte> p, int offset, int step)
{ {
// 4 pixels in, 2 pixels out. // 4 pixels in, 2 pixels out.
int p1 = p[offset - (2 * step)]; int p1 = p[offset - (2 * step)];
@ -755,7 +755,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
p[offset] = WebPLookupTables.Clip1[q0 - a1]; p[offset] = WebPLookupTables.Clip1[q0 - a1];
} }
private static void DoFilter4(byte[] p, int offset, int step) private static void DoFilter4(Span<byte> p, int offset, int step)
{ {
// 4 pixels in, 4 pixels out. // 4 pixels in, 4 pixels out.
int p1 = p[offset - (2 * step)]; int p1 = p[offset - (2 * step)];
@ -772,7 +772,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
p[offset + step] = WebPLookupTables.Clip1[q1 - a3]; p[offset + step] = WebPLookupTables.Clip1[q1 - a3];
} }
private static void DoFilter6(byte[] p, int offset, int step) private static void DoFilter6(Span<byte> p, int offset, int step)
{ {
// 6 pixels in, 6 pixels out. // 6 pixels in, 6 pixels out.
int p2 = p[offset - (3 * step)]; int p2 = p[offset - (3 * step)];
@ -795,7 +795,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
p[offset + (2 * step)] = WebPLookupTables.Clip1[q2 - a3]; p[offset + (2 * step)] = WebPLookupTables.Clip1[q2 - a3];
} }
private static bool NeedsFilter(byte[] p, int offset, int step, int t) private static bool NeedsFilter(Span<byte> p, int offset, int step, int t)
{ {
int p1 = p[offset + (-2 * step)]; int p1 = p[offset + (-2 * step)];
int p0 = p[offset - step]; int p0 = p[offset - step];
@ -804,7 +804,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
return ((4 * WebPLookupTables.Abs0[p0 - q0]) + WebPLookupTables.Abs0[p1 - q1]) <= t; return ((4 * WebPLookupTables.Abs0[p0 - q0]) + WebPLookupTables.Abs0[p1 - q1]) <= t;
} }
private static bool NeedsFilter2(byte[] p, int offset, int step, int t, int it) private static bool NeedsFilter2(Span<byte> p, int offset, int step, int t, int it)
{ {
int p3 = p[offset - (4 * step)]; int p3 = p[offset - (4 * step)];
int p2 = p[offset - (3 * step)]; int p2 = p[offset - (3 * step)];
@ -824,7 +824,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
WebPLookupTables.Abs0[q2 - q1] <= it && WebPLookupTables.Abs0[q1 - q0] <= it; WebPLookupTables.Abs0[q2 - q1] <= it && WebPLookupTables.Abs0[q1 - q0] <= it;
} }
private static bool Hev(byte[] p, int offset, int step, int thresh) private static bool Hev(Span<byte> p, int offset, int step, int thresh)
{ {
int p1 = p[offset - (2 * step)]; int p1 = p[offset - (2 * step)];
int p0 = p[offset - step]; int p0 = p[offset - step];

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

@ -1,12 +1,17 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.WebP namespace SixLabors.ImageSharp.Formats.WebP
{ {
/// <summary> /// <summary>
/// Holds information for decoding a lossy webp image. /// Holds information for decoding a lossy webp image.
/// </summary> /// </summary>
internal class Vp8Decoder internal class Vp8Decoder : IDisposable
{ {
private Vp8MacroBlock leftMacroBlock; private Vp8MacroBlock leftMacroBlock;
@ -17,7 +22,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <param name="pictureHeader">The picture header.</param> /// <param name="pictureHeader">The picture header.</param>
/// <param name="segmentHeader">The segment header.</param> /// <param name="segmentHeader">The segment header.</param>
/// <param name="probabilities">The probabilities.</param> /// <param name="probabilities">The probabilities.</param>
public Vp8Decoder(Vp8FrameHeader frameHeader, Vp8PictureHeader pictureHeader, Vp8SegmentHeader segmentHeader, Vp8Proba probabilities) /// <param name="memoryAllocator">Used for allocating memory for the pixel data output and the temporary buffers.</param>
public Vp8Decoder(Vp8FrameHeader frameHeader, Vp8PictureHeader pictureHeader, Vp8SegmentHeader segmentHeader, Vp8Proba probabilities, MemoryAllocator memoryAllocator)
{ {
this.FilterHeader = new Vp8FilterHeader(); this.FilterHeader = new Vp8FilterHeader();
this.FrameHeader = frameHeader; this.FrameHeader = frameHeader;
@ -57,34 +63,22 @@ namespace SixLabors.ImageSharp.Formats.WebP
uint width = pictureHeader.Width; uint width = pictureHeader.Width;
uint height = pictureHeader.Height; uint height = pictureHeader.Height;
// TODO: use memory allocator
int extraRows = WebPConstants.FilterExtraRows[(int)LoopFilter.Complex]; // assuming worst case: complex filter int extraRows = WebPConstants.FilterExtraRows[(int)LoopFilter.Complex]; // assuming worst case: complex filter
int extraY = extraRows * this.CacheYStride; int extraY = extraRows * this.CacheYStride;
int extraUv = (extraRows / 2) * this.CacheUvStride; int extraUv = (extraRows / 2) * this.CacheUvStride;
this.YuvBuffer = new byte[(WebPConstants.Bps * 17) + (WebPConstants.Bps * 9) + extraY]; this.YuvBuffer = memoryAllocator.Allocate<byte>((WebPConstants.Bps * 17) + (WebPConstants.Bps * 9) + extraY);
this.CacheY = new byte[(16 * this.CacheYStride) + extraY]; this.CacheY = memoryAllocator.Allocate<byte>((16 * this.CacheYStride) + extraY);
this.CacheU = new byte[(16 * this.CacheUvStride) + extraUv]; this.CacheU = memoryAllocator.Allocate<byte>((16 * this.CacheUvStride) + extraUv);
this.CacheV = new byte[(16 * this.CacheUvStride) + extraUv]; this.CacheV = memoryAllocator.Allocate<byte>((16 * this.CacheUvStride) + extraUv);
this.TmpYBuffer = new byte[width]; this.TmpYBuffer = memoryAllocator.Allocate<byte>((int)width);
this.TmpUBuffer = new byte[width]; this.TmpUBuffer = memoryAllocator.Allocate<byte>((int)width);
this.TmpVBuffer = new byte[width]; this.TmpVBuffer = memoryAllocator.Allocate<byte>((int)width);
this.Pixels = new byte[width * height * 4]; this.Pixels = memoryAllocator.Allocate<byte>((int)(width * height * 4));
for (int i = 0; i < this.YuvBuffer.Length; i++) this.YuvBuffer.Memory.Span.Fill(205);
{ this.CacheY.Memory.Span.Fill(205);
this.YuvBuffer[i] = 205; this.CacheU.Memory.Span.Fill(205);
} this.CacheV.Memory.Span.Fill(205);
for (int i = 0; i < this.CacheY.Length; i++)
{
this.CacheY[i] = 205;
}
for (int i = 0; i < this.CacheU.Length; i++)
{
this.CacheU[i] = 205;
this.CacheV[i] = 205;
}
this.Vp8BitReaders = new Vp8BitReader[WebPConstants.MaxNumPartitions]; this.Vp8BitReaders = new Vp8BitReader[WebPConstants.MaxNumPartitions];
} }
@ -206,19 +200,19 @@ namespace SixLabors.ImageSharp.Formats.WebP
public LoopFilter Filter { get; set; } public LoopFilter Filter { get; set; }
/// <summary> /// <summary>
/// Gets or sets the filter strengths. /// Gets the filter strengths.
/// </summary> /// </summary>
public Vp8FilterInfo[,] FilterStrength { get; } public Vp8FilterInfo[,] FilterStrength { get; }
public byte[] YuvBuffer { get; } public IMemoryOwner<byte> YuvBuffer { get; }
public Vp8TopSamples[] YuvTopSamples { get; } public Vp8TopSamples[] YuvTopSamples { get; }
public byte[] CacheY { get; } public IMemoryOwner<byte> CacheY { get; }
public byte[] CacheU { get; } public IMemoryOwner<byte> CacheU { get; }
public byte[] CacheV { get; } public IMemoryOwner<byte> CacheV { get; }
public int CacheYOffset { get; set; } public int CacheYOffset { get; set; }
@ -228,13 +222,16 @@ namespace SixLabors.ImageSharp.Formats.WebP
public int CacheUvStride { get; } public int CacheUvStride { get; }
public byte[] TmpYBuffer { get; } public IMemoryOwner<byte> TmpYBuffer { get; }
public byte[] TmpUBuffer { get; } public IMemoryOwner<byte> TmpUBuffer { get; }
public byte[] TmpVBuffer { get; } public IMemoryOwner<byte> TmpVBuffer { get; }
public byte[] Pixels { get; } /// <summary>
/// Gets the pixel buffer where the decoded pixel data will be stored.
/// </summary>
public IMemoryOwner<byte> Pixels { get; }
/// <summary> /// <summary>
/// Gets or sets filter strength info. /// Gets or sets filter strength info.
@ -348,5 +345,18 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
} }
/// <inheritdoc/>
public void Dispose()
{
this.YuvBuffer.Dispose();
this.CacheY.Dispose();
this.CacheU.Dispose();
this.CacheV.Dispose();
this.TmpYBuffer.Dispose();
this.TmpUBuffer.Dispose();
this.TmpVBuffer.Dispose();
this.Pixels.Dispose();
}
} }
} }

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

@ -55,39 +55,46 @@ namespace SixLabors.ImageSharp.Formats.WebP
var proba = new Vp8Proba(); var proba = new Vp8Proba();
Vp8SegmentHeader vp8SegmentHeader = this.ParseSegmentHeader(proba); Vp8SegmentHeader vp8SegmentHeader = this.ParseSegmentHeader(proba);
var decoder = new Vp8Decoder(info.Vp8FrameHeader, pictureHeader, vp8SegmentHeader, proba); using (var decoder = new Vp8Decoder(info.Vp8FrameHeader, pictureHeader, vp8SegmentHeader, proba, this.memoryAllocator))
Vp8Io io = InitializeVp8Io(decoder, pictureHeader); {
Vp8Io io = InitializeVp8Io(decoder, pictureHeader);
// Paragraph 9.4: Parse the filter specs. // Paragraph 9.4: Parse the filter specs.
this.ParseFilterHeader(decoder); this.ParseFilterHeader(decoder);
decoder.PrecomputeFilterStrengths(); decoder.PrecomputeFilterStrengths();
// Paragraph 9.5: Parse partitions. // Paragraph 9.5: Parse partitions.
this.ParsePartitions(decoder); this.ParsePartitions(decoder);
// Paragraph 9.6: Dequantization Indices. // Paragraph 9.6: Dequantization Indices.
this.ParseDequantizationIndices(decoder); this.ParseDequantizationIndices(decoder);
// Ignore the value of update probabilities. // Ignore the value of update probabilities.
this.bitReader.ReadBool(); this.bitReader.ReadBool();
// Paragraph 13.4: Parse probabilities. // Paragraph 13.4: Parse probabilities.
this.ParseProbabilities(decoder); this.ParseProbabilities(decoder);
// Decode image data. // Decode image data.
this.ParseFrame(decoder, io); this.ParseFrame(decoder, io);
if (info.Features?.Alpha is true) if (info.Features?.Alpha is true)
{
using (var alphaDecoder = new AlphaDecoder(width, height, info.Features.AlphaData, info.Features.AlphaChunkHeader, this.memoryAllocator))
{ {
alphaDecoder.Decode(); using (var alphaDecoder = new AlphaDecoder(
this.DecodePixelValues(width, height, decoder.Pixels, pixels, alphaDecoder.Alpha); width,
height,
info.Features.AlphaData,
info.Features.AlphaChunkHeader,
this.memoryAllocator))
{
alphaDecoder.Decode();
this.DecodePixelValues(width, height, decoder.Pixels.Memory.Span, pixels, alphaDecoder.Alpha);
}
}
else
{
this.DecodePixelValues(width, height, decoder.Pixels.Memory.Span, pixels);
} }
}
else
{
this.DecodePixelValues(width, height, decoder.Pixels, pixels);
} }
} }
@ -252,10 +259,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
int uOff = yOff + (WebPConstants.Bps * 16) + WebPConstants.Bps; int uOff = yOff + (WebPConstants.Bps * 16) + WebPConstants.Bps;
int vOff = uOff + 16; int vOff = uOff + 16;
byte[] yuv = dec.YuvBuffer; Span<byte> yuv = dec.YuvBuffer.Memory.Span;
Span<byte> yDst = dec.YuvBuffer.AsSpan(yOff); Span<byte> yDst = yuv.Slice(yOff);
Span<byte> uDst = dec.YuvBuffer.AsSpan(uOff); Span<byte> uDst = yuv.Slice(uOff);
Span<byte> vDst = dec.YuvBuffer.AsSpan(vOff); Span<byte> vDst = yuv.Slice(vOff);
// Initialize left-most block. // Initialize left-most block.
for (int i = 0; i < 16; ++i) for (int i = 0; i < 16; ++i)
@ -278,19 +285,19 @@ namespace SixLabors.ImageSharp.Formats.WebP
{ {
// We only need to do this init once at block (0,0). // We only need to do this init once at block (0,0).
// Afterward, it remains valid for the whole topmost row. // Afterward, it remains valid for the whole topmost row.
Span<byte> tmp = dec.YuvBuffer.AsSpan(yOff - WebPConstants.Bps - 1, 16 + 4 + 1); Span<byte> tmp = yuv.Slice(yOff - WebPConstants.Bps - 1, 16 + 4 + 1);
for (int i = 0; i < tmp.Length; ++i) for (int i = 0; i < tmp.Length; ++i)
{ {
tmp[i] = 127; tmp[i] = 127;
} }
tmp = dec.YuvBuffer.AsSpan(uOff - WebPConstants.Bps - 1, 8 + 1); tmp = yuv.Slice(uOff - WebPConstants.Bps - 1, 8 + 1);
for (int i = 0; i < tmp.Length; ++i) for (int i = 0; i < tmp.Length; ++i)
{ {
tmp[i] = 127; tmp[i] = 127;
} }
tmp = dec.YuvBuffer.AsSpan(vOff - WebPConstants.Bps - 1, 8 + 1); tmp = yuv.Slice(vOff - WebPConstants.Bps - 1, 8 + 1);
for (int i = 0; i < tmp.Length; ++i) for (int i = 0; i < tmp.Length; ++i)
{ {
tmp[i] = 127; tmp[i] = 127;
@ -310,17 +317,17 @@ namespace SixLabors.ImageSharp.Formats.WebP
{ {
int srcIdx = (i * WebPConstants.Bps) + 12 + yOff; int srcIdx = (i * WebPConstants.Bps) + 12 + yOff;
int dstIdx = (i * WebPConstants.Bps) - 4 + yOff; int dstIdx = (i * WebPConstants.Bps) - 4 + yOff;
yuv.AsSpan(srcIdx, 4).CopyTo(yuv.AsSpan(dstIdx)); yuv.Slice(srcIdx, 4).CopyTo(yuv.Slice(dstIdx));
} }
for (int i = -1; i < 8; ++i) for (int i = -1; i < 8; ++i)
{ {
int srcIdx = (i * WebPConstants.Bps) + 4 + uOff; int srcIdx = (i * WebPConstants.Bps) + 4 + uOff;
int dstIdx = (i * WebPConstants.Bps) - 4 + uOff; int dstIdx = (i * WebPConstants.Bps) - 4 + uOff;
yuv.AsSpan(srcIdx, 4).CopyTo(yuv.AsSpan(dstIdx)); yuv.Slice(srcIdx, 4).CopyTo(yuv.Slice(dstIdx));
srcIdx = (i * WebPConstants.Bps) + 4 + vOff; srcIdx = (i * WebPConstants.Bps) + 4 + vOff;
dstIdx = (i * WebPConstants.Bps) - 4 + vOff; dstIdx = (i * WebPConstants.Bps) - 4 + vOff;
yuv.AsSpan(srcIdx, 4).CopyTo(yuv.AsSpan(dstIdx)); yuv.Slice(srcIdx, 4).CopyTo(yuv.Slice(dstIdx));
} }
} }
@ -330,15 +337,15 @@ namespace SixLabors.ImageSharp.Formats.WebP
uint bits = block.NonZeroY; uint bits = block.NonZeroY;
if (mby > 0) if (mby > 0)
{ {
topYuv.Y.CopyTo(yuv.AsSpan(yOff - WebPConstants.Bps)); topYuv.Y.CopyTo(yuv.Slice(yOff - WebPConstants.Bps));
topYuv.U.CopyTo(yuv.AsSpan(uOff - WebPConstants.Bps)); topYuv.U.CopyTo(yuv.Slice(uOff - WebPConstants.Bps));
topYuv.V.CopyTo(yuv.AsSpan(vOff - WebPConstants.Bps)); topYuv.V.CopyTo(yuv.Slice(vOff - WebPConstants.Bps));
} }
// Predict and add residuals. // Predict and add residuals.
if (block.IsI4x4) if (block.IsI4x4)
{ {
Span<byte> topRight = yuv.AsSpan(yOff - WebPConstants.Bps + 16); Span<byte> topRight = yuv.Slice(yOff - WebPConstants.Bps + 16);
if (mby > 0) if (mby > 0)
{ {
if (mbx >= dec.MbWidth - 1) if (mbx >= dec.MbWidth - 1)
@ -356,14 +363,14 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
// Replicate the top-right pixels below. // Replicate the top-right pixels below.
Span<uint> topRightUint = MemoryMarshal.Cast<byte, uint>(yuv.AsSpan(yOff - WebPConstants.Bps + 16)); Span<uint> topRightUint = MemoryMarshal.Cast<byte, uint>(yuv.Slice(yOff - WebPConstants.Bps + 16));
topRightUint[WebPConstants.Bps] = topRightUint[2 * WebPConstants.Bps] = topRightUint[3 * WebPConstants.Bps] = topRightUint[0]; topRightUint[WebPConstants.Bps] = topRightUint[2 * WebPConstants.Bps] = topRightUint[3 * WebPConstants.Bps] = topRightUint[0];
// Predict and add residuals for all 4x4 blocks in turn. // Predict and add residuals for all 4x4 blocks in turn.
for (int n = 0; n < 16; ++n, bits <<= 2) for (int n = 0; n < 16; ++n, bits <<= 2)
{ {
int offset = yOff + WebPConstants.Scan[n]; int offset = yOff + WebPConstants.Scan[n];
Span<byte> dst = yuv.AsSpan(offset); Span<byte> dst = yuv.Slice(offset);
byte lumaMode = block.Modes[n]; byte lumaMode = block.Modes[n];
switch (lumaMode) switch (lumaMode)
{ {
@ -487,9 +494,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
// Transfer reconstructed samples from yuv_buffer cache to final destination. // Transfer reconstructed samples from yuv_buffer cache to final destination.
Span<byte> yOut = dec.CacheY.AsSpan(dec.CacheYOffset + (mbx * 16)); Span<byte> yOut = dec.CacheY.Memory.Span.Slice(dec.CacheYOffset + (mbx * 16));
Span<byte> uOut = dec.CacheU.AsSpan(dec.CacheUvOffset + (mbx * 8)); Span<byte> uOut = dec.CacheU.Memory.Span.Slice(dec.CacheUvOffset + (mbx * 8));
Span<byte> vOut = dec.CacheV.AsSpan(dec.CacheUvOffset + (mbx * 8)); Span<byte> vOut = dec.CacheV.Memory.Span.Slice(dec.CacheUvOffset + (mbx * 8));
for (int j = 0; j < 16; ++j) for (int j = 0; j < 16; ++j)
{ {
yDst.Slice(j * WebPConstants.Bps, Math.Min(16, yOut.Length)).CopyTo(yOut.Slice(j * dec.CacheYStride)); yDst.Slice(j * WebPConstants.Bps, Math.Min(16, yOut.Length)).CopyTo(yOut.Slice(j * dec.CacheYStride));
@ -529,22 +536,22 @@ namespace SixLabors.ImageSharp.Formats.WebP
int offset = dec.CacheYOffset + (mbx * 16); int offset = dec.CacheYOffset + (mbx * 16);
if (mbx > 0) if (mbx > 0)
{ {
LossyUtils.SimpleHFilter16(dec.CacheY, offset, yBps, limit + 4); LossyUtils.SimpleHFilter16(dec.CacheY.Memory.Span, offset, yBps, limit + 4);
} }
if (filterInfo.UseInnerFiltering > 0) if (filterInfo.UseInnerFiltering > 0)
{ {
LossyUtils.SimpleHFilter16i(dec.CacheY, offset, yBps, limit); LossyUtils.SimpleHFilter16i(dec.CacheY.Memory.Span, offset, yBps, limit);
} }
if (mby > 0) if (mby > 0)
{ {
LossyUtils.SimpleVFilter16(dec.CacheY, offset, yBps, limit + 4); LossyUtils.SimpleVFilter16(dec.CacheY.Memory.Span, offset, yBps, limit + 4);
} }
if (filterInfo.UseInnerFiltering > 0) if (filterInfo.UseInnerFiltering > 0)
{ {
LossyUtils.SimpleVFilter16i(dec.CacheY, offset, yBps, limit); LossyUtils.SimpleVFilter16i(dec.CacheY.Memory.Span, offset, yBps, limit);
} }
} }
else if (dec.Filter is LoopFilter.Complex) else if (dec.Filter is LoopFilter.Complex)
@ -555,26 +562,26 @@ namespace SixLabors.ImageSharp.Formats.WebP
int hevThresh = filterInfo.HighEdgeVarianceThreshold; int hevThresh = filterInfo.HighEdgeVarianceThreshold;
if (mbx > 0) if (mbx > 0)
{ {
LossyUtils.HFilter16(dec.CacheY, yOffset, yBps, limit + 4, iLevel, hevThresh); LossyUtils.HFilter16(dec.CacheY.Memory.Span, yOffset, yBps, limit + 4, iLevel, hevThresh);
LossyUtils.HFilter8(dec.CacheU, dec.CacheV, uvOffset, uvBps, limit + 4, iLevel, hevThresh); LossyUtils.HFilter8(dec.CacheU.Memory.Span, dec.CacheV.Memory.Span, uvOffset, uvBps, limit + 4, iLevel, hevThresh);
} }
if (filterInfo.UseInnerFiltering > 0) if (filterInfo.UseInnerFiltering > 0)
{ {
LossyUtils.HFilter16i(dec.CacheY, yOffset, yBps, limit, iLevel, hevThresh); LossyUtils.HFilter16i(dec.CacheY.Memory.Span, yOffset, yBps, limit, iLevel, hevThresh);
LossyUtils.HFilter8i(dec.CacheU, dec.CacheV, uvOffset, uvBps, limit, iLevel, hevThresh); LossyUtils.HFilter8i(dec.CacheU.Memory.Span, dec.CacheV.Memory.Span, uvOffset, uvBps, limit, iLevel, hevThresh);
} }
if (mby > 0) if (mby > 0)
{ {
LossyUtils.VFilter16(dec.CacheY, yOffset, yBps, limit + 4, iLevel, hevThresh); LossyUtils.VFilter16(dec.CacheY.Memory.Span, yOffset, yBps, limit + 4, iLevel, hevThresh);
LossyUtils.VFilter8(dec.CacheU, dec.CacheV, uvOffset, uvBps, limit + 4, iLevel, hevThresh); LossyUtils.VFilter8(dec.CacheU.Memory.Span, dec.CacheV.Memory.Span, uvOffset, uvBps, limit + 4, iLevel, hevThresh);
} }
if (filterInfo.UseInnerFiltering > 0) if (filterInfo.UseInnerFiltering > 0)
{ {
LossyUtils.VFilter16i(dec.CacheY, yOffset, yBps, limit, iLevel, hevThresh); LossyUtils.VFilter16i(dec.CacheY.Memory.Span, yOffset, yBps, limit, iLevel, hevThresh);
LossyUtils.VFilter8i(dec.CacheU, dec.CacheV, uvOffset, uvBps, limit, iLevel, hevThresh); LossyUtils.VFilter8i(dec.CacheU.Memory.Span, dec.CacheV.Memory.Span, uvOffset, uvBps, limit, iLevel, hevThresh);
} }
} }
} }
@ -584,9 +591,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
int extraYRows = WebPConstants.FilterExtraRows[(int)dec.Filter]; int extraYRows = WebPConstants.FilterExtraRows[(int)dec.Filter];
int ySize = extraYRows * dec.CacheYStride; int ySize = extraYRows * dec.CacheYStride;
int uvSize = (extraYRows / 2) * dec.CacheUvStride; int uvSize = (extraYRows / 2) * dec.CacheUvStride;
Span<byte> yDst = dec.CacheY.AsSpan(); Span<byte> yDst = dec.CacheY.Memory.Span;
Span<byte> uDst = dec.CacheU.AsSpan(); Span<byte> uDst = dec.CacheU.Memory.Span;
Span<byte> vDst = dec.CacheV.AsSpan(); Span<byte> vDst = dec.CacheV.Memory.Span;
int mby = dec.MbY; int mby = dec.MbY;
bool isFirstRow = mby is 0; bool isFirstRow = mby is 0;
bool isLastRow = mby >= dec.BottomRightMbY - 1; bool isLastRow = mby >= dec.BottomRightMbY - 1;
@ -609,9 +616,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
else else
{ {
io.Y = dec.CacheY.AsSpan(dec.CacheYOffset); io.Y = dec.CacheY.Memory.Span.Slice(dec.CacheYOffset);
io.U = dec.CacheU.AsSpan(dec.CacheUvOffset); io.U = dec.CacheU.Memory.Span.Slice(dec.CacheUvOffset);
io.V = dec.CacheV.AsSpan(dec.CacheUvOffset); io.V = dec.CacheV.Memory.Span.Slice(dec.CacheUvOffset);
} }
if (!isLastRow) if (!isLastRow)
@ -639,28 +646,28 @@ namespace SixLabors.ImageSharp.Formats.WebP
// Rotate top samples if needed. // Rotate top samples if needed.
if (!isLastRow) if (!isLastRow)
{ {
yDst.Slice(16 * dec.CacheYStride, ySize).CopyTo(dec.CacheY.AsSpan()); yDst.Slice(16 * dec.CacheYStride, ySize).CopyTo(dec.CacheY.Memory.Span);
uDst.Slice(8 * dec.CacheUvStride, uvSize).CopyTo(dec.CacheU.AsSpan()); uDst.Slice(8 * dec.CacheUvStride, uvSize).CopyTo(dec.CacheU.Memory.Span);
vDst.Slice(8 * dec.CacheUvStride, uvSize).CopyTo(dec.CacheV.AsSpan()); vDst.Slice(8 * dec.CacheUvStride, uvSize).CopyTo(dec.CacheV.Memory.Span);
} }
} }
private int EmitRgb(Vp8Decoder dec, Vp8Io io) private int EmitRgb(Vp8Decoder dec, Vp8Io io)
{ {
byte[] buf = dec.Pixels; Span<byte> buf = dec.Pixels.Memory.Span;
int numLinesOut = io.MbH; // a priori guess. int numLinesOut = io.MbH; // a priori guess.
Span<byte> curY = io.Y; Span<byte> curY = io.Y;
Span<byte> curU = io.U; Span<byte> curU = io.U;
Span<byte> curV = io.V; Span<byte> curV = io.V;
byte[] tmpYBuffer = dec.TmpYBuffer; Span<byte> tmpYBuffer = dec.TmpYBuffer.Memory.Span;
byte[] tmpUBuffer = dec.TmpUBuffer; Span<byte> tmpUBuffer = dec.TmpUBuffer.Memory.Span;
byte[] tmpVBuffer = dec.TmpVBuffer; Span<byte> tmpVBuffer = dec.TmpVBuffer.Memory.Span;
Span<byte> topU = tmpUBuffer.AsSpan(); Span<byte> topU = tmpUBuffer;
Span<byte> topV = tmpVBuffer.AsSpan(); Span<byte> topV = tmpVBuffer;
int bpp = 3; int bpp = 3;
int bufferStride = bpp * io.Width; int bufferStride = bpp * io.Width;
int dstStartIdx = io.MbY * bufferStride; int dstStartIdx = io.MbY * bufferStride;
Span<byte> dst = buf.AsSpan(dstStartIdx); Span<byte> dst = buf.Slice(dstStartIdx);
int yEnd = io.MbY + io.MbH; int yEnd = io.MbY + io.MbH;
int mbw = io.MbW; int mbw = io.MbW;
int uvw = (mbw + 1) / 2; int uvw = (mbw + 1) / 2;
@ -674,7 +681,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
else else
{ {
// We can finish the left-over line from previous call. // 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); this.UpSample(tmpYBuffer, curY, topU, topV, curU, curV, buf.Slice(dstStartIdx - bufferStride), dst, mbw);
numLinesOut++; numLinesOut++;
} }

Loading…
Cancel
Save