Browse Source

Using color cache, first version of decoding a lossless image

pull/1552/head
Brian Popow 7 years ago
parent
commit
99e70dc61d
  1. 30
      src/ImageSharp/Formats/WebP/ColorCache.cs
  2. 110
      src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs

30
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
/// <summary>
/// Color entries.
/// </summary>
public List<uint> Colors { get; set; }
public uint[] Colors { get; private set; }
/// <summary>
/// Hash shift: 32 - hashBits.
/// </summary>
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);
}
}
}

110
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<uint>(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<TPixel> 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<uint> src = pixelData.AsSpan(decodedPixels - dist, length);
Span<uint> dest = pixelData.AsSpan(decodedPixels);
src.CopyTo(dest);
}
else
{
int copiedPixels = 0;
while (copiedPixels < length)
{
Span<uint> src = pixelData.AsSpan(decodedPixels - dist, dist);
Span<uint> dest = pixelData.AsSpan(decodedPixels + copiedPixels);
src.CopyTo(dest);
copiedPixels += dist;
}
}
}
private int GetCopyDistance(int distanceSymbol)

Loading…
Cancel
Save