Browse Source

Add webp EXIF tests

pull/1552/head
Brian Popow 5 years ago
parent
commit
1eb7307d82
  1. 6
      src/ImageSharp/Formats/WebP/BitWriter/Vp8BitWriter.cs
  2. 4
      src/ImageSharp/Formats/WebP/BitWriter/Vp8LBitWriter.cs
  3. 10
      src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs
  4. 2
      src/ImageSharp/Formats/WebP/Lossless/LosslessUtils.cs
  5. 20
      src/ImageSharp/Formats/WebP/Lossless/PredictorEncoder.cs
  6. 2
      src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs
  7. 43
      src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs
  8. 6
      src/ImageSharp/Formats/WebP/WebpDecoderCore.cs
  9. 15
      tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs
  10. 2
      tests/ImageSharp.Benchmarks/Codecs/DecodeWebp.cs
  11. 2
      tests/ImageSharp.Benchmarks/Codecs/EncodeWebp.cs
  12. 2
      tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs
  13. 46
      tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs

6
src/ImageSharp/Formats/WebP/BitWriter/Vp8BitWriter.cs

@ -369,7 +369,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
this.nbBits -= 8; this.nbBits -= 8;
if ((bits & 0xff) != 0xff) if ((bits & 0xff) != 0xff)
{ {
var pos = this.pos; uint pos = this.pos;
this.BitWriterResize(this.run + 1); this.BitWriterResize(this.run + 1);
if ((bits & 0x100) != 0) if ((bits & 0x100) != 0)
@ -509,7 +509,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
private void WriteFilterHeader(Vp8BitWriter bitWriter) private void WriteFilterHeader(Vp8BitWriter bitWriter)
{ {
Vp8FilterHeader hdr = this.enc.FilterHeader; Vp8FilterHeader hdr = this.enc.FilterHeader;
var useLfDelta = hdr.I4x4LfDelta != 0; bool useLfDelta = hdr.I4x4LfDelta != 0;
bitWriter.PutBitUniform(hdr.Simple ? 1 : 0); bitWriter.PutBitUniform(hdr.Simple ? 1 : 0);
bitWriter.PutBits((uint)hdr.FilterLevel, 6); bitWriter.PutBits((uint)hdr.FilterLevel, 6);
bitWriter.PutBits((uint)hdr.Sharpness, 3); bitWriter.PutBits((uint)hdr.Sharpness, 3);
@ -645,7 +645,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
uint profile = 0; uint profile = 0;
int width = this.enc.Width; int width = this.enc.Width;
int height = this.enc.Height; int height = this.enc.Height;
var vp8FrameHeader = new byte[WebpConstants.Vp8FrameHeaderSize]; byte[] vp8FrameHeader = new byte[WebpConstants.Vp8FrameHeaderSize];
// Paragraph 9.1. // Paragraph 9.1.
uint bits = 0 // keyframe (1b) uint bits = 0 // keyframe (1b)

4
src/ImageSharp/Formats/WebP/BitWriter/Vp8LBitWriter.cs

@ -108,7 +108,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
public Vp8LBitWriter Clone() public Vp8LBitWriter Clone()
{ {
var clonedBuffer = new byte[this.Buffer.Length]; byte[] clonedBuffer = new byte[this.Buffer.Length];
System.Buffer.BlockCopy(this.Buffer, 0, clonedBuffer, 0, this.cur); System.Buffer.BlockCopy(this.Buffer, 0, clonedBuffer, 0, this.cur);
return new Vp8LBitWriter(clonedBuffer, this.bits, this.used, this.cur); return new Vp8LBitWriter(clonedBuffer, this.bits, this.used, this.cur);
} }
@ -186,7 +186,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
// If needed, make some room by flushing some bits out. // If needed, make some room by flushing some bits out.
if (this.cur + WriterBytes > this.end) if (this.cur + WriterBytes > this.end)
{ {
var extraSize = this.end - this.cur + MinExtraSize; int extraSize = this.end - this.cur + MinExtraSize;
this.BitWriterResize(extraSize); this.BitWriterResize(extraSize);
} }

10
src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs

@ -230,7 +230,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
private static void BackwardReferencesTraceBackwards(int xSize, int ySize, Span<uint> bgra, int cacheBits, Vp8LHashChain hashChain, Vp8LBackwardRefs refsSrc, Vp8LBackwardRefs refsDst) private static void BackwardReferencesTraceBackwards(int xSize, int ySize, Span<uint> bgra, int cacheBits, Vp8LHashChain hashChain, Vp8LBackwardRefs refsSrc, Vp8LBackwardRefs refsDst)
{ {
int distArraySize = xSize * ySize; int distArraySize = xSize * ySize;
var distArray = new ushort[distArraySize]; ushort[] distArray = new ushort[distArraySize];
BackwardReferencesHashChainDistanceOnly(xSize, ySize, bgra, cacheBits, hashChain, refsSrc, distArray); BackwardReferencesHashChainDistanceOnly(xSize, ySize, bgra, cacheBits, hashChain, refsSrc, distArray);
int chosenPathSize = TraceBackwards(distArray, distArraySize); int chosenPathSize = TraceBackwards(distArray, distArraySize);
@ -242,7 +242,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{ {
int pixCount = xSize * ySize; int pixCount = xSize * ySize;
bool useColorCache = cacheBits > 0; bool useColorCache = cacheBits > 0;
var literalArraySize = WebpConstants.NumLiteralCodes + WebpConstants.NumLengthCodes + ((cacheBits > 0) ? (1 << cacheBits) : 0); int literalArraySize = WebpConstants.NumLiteralCodes + WebpConstants.NumLengthCodes + ((cacheBits > 0) ? (1 << cacheBits) : 0);
var costModel = new CostModel(literalArraySize); var costModel = new CostModel(literalArraySize);
int offsetPrev = -1; int offsetPrev = -1;
int lenPrev = -1; int lenPrev = -1;
@ -511,11 +511,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
private static void BackwardReferencesLz77Box(int xSize, int ySize, Span<uint> bgra, int cacheBits, Vp8LHashChain hashChainBest, Vp8LHashChain hashChain, Vp8LBackwardRefs refs) private static void BackwardReferencesLz77Box(int xSize, int ySize, Span<uint> bgra, int cacheBits, Vp8LHashChain hashChainBest, Vp8LHashChain hashChain, Vp8LBackwardRefs refs)
{ {
int pixelCount = xSize * ySize; int pixelCount = xSize * ySize;
var windowOffsets = new int[WindowOffsetsSizeMax]; int[] windowOffsets = new int[WindowOffsetsSizeMax];
var windowOffsetsNew = new int[WindowOffsetsSizeMax]; int[] windowOffsetsNew = new int[WindowOffsetsSizeMax];
int windowOffsetsSize = 0; int windowOffsetsSize = 0;
int windowOffsetsNewSize = 0; int windowOffsetsNewSize = 0;
var counts = new short[xSize * ySize]; short[] counts = new short[xSize * ySize];
int bestOffsetPrev = -1; int bestOffsetPrev = -1;
int bestLengthPrev = -1; int bestLengthPrev = -1;

2
src/ImageSharp/Formats/WebP/Lossless/LosslessUtils.cs

@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// <summary> /// <summary>
/// Returns the exact index where array1 and array2 are different. For an index /// Returns the exact index where array1 and array2 are different. For an index
/// inferior or equal to bestLenMatch, the return value just has to be strictly /// inferior or equal to bestLenMatch, the return value just has to be strictly
/// inferior to best_lenMatch. The current behavior is to return 0 if this index /// inferior to bestLenMatch match. The current behavior is to return 0 if this index
/// is bestLenMatch, and the index itself otherwise. /// is bestLenMatch, and the index itself otherwise.
/// If no two elements are the same, it returns maxLimit. /// If no two elements are the same, it returns maxLimit.
/// </summary> /// </summary>

20
src/ImageSharp/Formats/WebP/Lossless/PredictorEncoder.cs

@ -94,8 +94,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int maxTileSize = 1 << bits; int maxTileSize = 1 << bits;
int tileXSize = LosslessUtils.SubSampleSize(width, bits); int tileXSize = LosslessUtils.SubSampleSize(width, bits);
int tileYSize = LosslessUtils.SubSampleSize(height, bits); int tileYSize = LosslessUtils.SubSampleSize(height, bits);
var accumulatedRedHisto = new int[256]; int[] accumulatedRedHisto = new int[256];
var accumulatedBlueHisto = new int[256]; int[] accumulatedBlueHisto = new int[256];
var prevX = default(Vp8LMultipliers); var prevX = default(Vp8LMultipliers);
var prevY = default(Vp8LMultipliers); var prevY = default(Vp8LMultipliers);
for (int tileY = 0; tileY < tileYSize; tileY++) for (int tileY = 0; tileY < tileYSize; tileY++)
@ -204,9 +204,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
Span<byte> maxDiffs = MemoryMarshal.Cast<uint, byte>(currentRow.Slice(width + 1)); Span<byte> maxDiffs = MemoryMarshal.Cast<uint, byte>(currentRow.Slice(width + 1));
float bestDiff = MaxDiffCost; float bestDiff = MaxDiffCost;
int bestMode = 0; int bestMode = 0;
var residuals = new uint[1 << WebpConstants.MaxTransformBits]; uint[] residuals = new uint[1 << WebpConstants.MaxTransformBits];
var histoArgb = new int[4][]; int[][] histoArgb = new int[4][];
var bestHisto = new int[4][]; int[][] bestHisto = new int[4][];
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
{ {
histoArgb[i] = new int[256]; histoArgb[i] = new int[256];
@ -260,7 +260,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
} }
} }
var curDiff = PredictionCostSpatialHistogram(accumulated, histoArgb); float curDiff = PredictionCostSpatialHistogram(accumulated, histoArgb);
// Favor keeping the areas locally similar. // Favor keeping the areas locally similar.
if (mode == leftMode) if (mode == leftMode)
@ -448,7 +448,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
return LosslessUtils.SubPixels(value, predict); return LosslessUtils.SubPixels(value, predict);
} }
var quantization = maxQuantization; int quantization = maxQuantization;
while (quantization >= maxDiff) while (quantization >= maxDiff)
{ {
quantization >>= 1; quantization >>= 1;
@ -464,7 +464,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
a = NearLosslessComponent((byte)(value >> 24), (byte)(predict >> 24), 0xff, quantization); a = NearLosslessComponent((byte)(value >> 24), (byte)(predict >> 24), 0xff, quantization);
} }
var g = NearLosslessComponent((byte)((value >> 8) & 0xff), (byte)((predict >> 8) & 0xff), 0xff, quantization); byte g = NearLosslessComponent((byte)((value >> 8) & 0xff), (byte)((predict >> 8) & 0xff), 0xff, quantization);
if (usedSubtractGreen) if (usedSubtractGreen)
{ {
@ -478,8 +478,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
greenDiff = NearLosslessDiff(newGreen, (byte)((value >> 8) & 0xff)); greenDiff = NearLosslessDiff(newGreen, (byte)((value >> 8) & 0xff));
} }
var r = NearLosslessComponent(NearLosslessDiff((byte)((value >> 16) & 0xff), greenDiff), (byte)((predict >> 16) & 0xff), (byte)(0xff - newGreen), quantization); byte r = NearLosslessComponent(NearLosslessDiff((byte)((value >> 16) & 0xff), greenDiff), (byte)((predict >> 16) & 0xff), (byte)(0xff - newGreen), quantization);
var b = NearLosslessComponent(NearLosslessDiff((byte)(value & 0xff), greenDiff), (byte)(predict & 0xff), (byte)(0xff - newGreen), quantization); byte b = NearLosslessComponent(NearLosslessDiff((byte)(value & 0xff), greenDiff), (byte)(predict & 0xff), (byte)(0xff - newGreen), quantization);
return ((uint)a << 24) | ((uint)r << 16) | ((uint)g << 8) | b; return ((uint)a << 24) | ((uint)r << 16) | ((uint)g << 8) | b;
} }

2
src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs

@ -172,6 +172,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
public void Encode<TPixel>(Image<TPixel> image, Stream stream) public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
image.Metadata.SyncProfiles();
// Write the image size. // Write the image size.
int width = image.Width; int width = image.Width;
int height = image.Height; int height = image.Height;

43
src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs

@ -98,10 +98,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
: (method >= 3) ? Vp8RdLevel.RdOptBasic : (method >= 3) ? Vp8RdLevel.RdOptBasic
: Vp8RdLevel.RdOptNone; : Vp8RdLevel.RdOptNone;
var pixelCount = width * height; int pixelCount = width * height;
this.Mbw = (width + 15) >> 4; this.Mbw = (width + 15) >> 4;
this.Mbh = (height + 15) >> 4; this.Mbh = (height + 15) >> 4;
var uvSize = ((width + 1) >> 1) * ((height + 1) >> 1); int uvSize = ((width + 1) >> 1) * ((height + 1) >> 1);
this.Y = this.memoryAllocator.Allocate<byte>(pixelCount); this.Y = this.memoryAllocator.Allocate<byte>(pixelCount);
this.U = this.memoryAllocator.Allocate<byte>(uvSize); this.U = this.memoryAllocator.Allocate<byte>(uvSize);
this.V = this.memoryAllocator.Allocate<byte>(uvSize); this.V = this.memoryAllocator.Allocate<byte>(uvSize);
@ -274,7 +274,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int uvStride = (yStride + 1) >> 1; int uvStride = (yStride + 1) >> 1;
var it = new Vp8EncIterator(this.YTop, this.UvTop, this.Nz, this.MbInfo, this.Preds, this.TopDerr, this.Mbw, this.Mbh); var it = new Vp8EncIterator(this.YTop, this.UvTop, this.Nz, this.MbInfo, this.Preds, this.TopDerr, this.Mbw, this.Mbh);
var alphas = new int[WebpConstants.MaxAlpha + 1]; int[] alphas = new int[WebpConstants.MaxAlpha + 1];
this.alpha = this.MacroBlockAnalysis(width, height, it, y, u, v, yStride, uvStride, alphas, out this.uvAlpha); this.alpha = this.MacroBlockAnalysis(width, height, it, y, u, v, yStride, uvStride, alphas, out this.uvAlpha);
int totalMb = this.Mbw * this.Mbw; int totalMb = this.Mbw * this.Mbw;
this.alpha /= totalMb; this.alpha /= totalMb;
@ -321,6 +321,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.AdjustFilterStrength(); this.AdjustFilterStrength();
// Write bytes from the bitwriter buffer to the stream. // Write bytes from the bitwriter buffer to the stream.
image.Metadata.SyncProfiles();
this.bitWriter.WriteEncodedImageToStream(stream, image.Metadata.ExifProfile, (uint)width, (uint)height); this.bitWriter.WriteEncodedImageToStream(stream, image.Metadata.ExifProfile, (uint)width, (uint)height);
} }
@ -367,7 +368,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
while (numPassLeft-- > 0) while (numPassLeft-- > 0)
{ {
bool isLastPass = (MathF.Abs(stats.Dq) <= DqLimit) || (numPassLeft == 0) || (this.maxI4HeaderBits == 0); bool isLastPass = (MathF.Abs(stats.Dq) <= DqLimit) || (numPassLeft == 0) || (this.maxI4HeaderBits == 0);
var sizeP0 = this.OneStatPass(width, height, yStride, uvStride, rdOpt, nbMbs, stats); long sizeP0 = this.OneStatPass(width, height, yStride, uvStride, rdOpt, nbMbs, stats);
if (sizeP0 == 0) if (sizeP0 == 0)
{ {
return; return;
@ -517,25 +518,25 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private void AssignSegments(int[] alphas) private void AssignSegments(int[] alphas)
{ {
int nb = (this.SegmentHeader.NumSegments < NumMbSegments) ? this.SegmentHeader.NumSegments : NumMbSegments; int nb = (this.SegmentHeader.NumSegments < NumMbSegments) ? this.SegmentHeader.NumSegments : NumMbSegments;
var centers = new int[NumMbSegments]; int[] centers = new int[NumMbSegments];
int weightedAverage = 0; int weightedAverage = 0;
var map = new int[WebpConstants.MaxAlpha + 1]; int[] map = new int[WebpConstants.MaxAlpha + 1];
int n, k; int n, k;
var accum = new int[NumMbSegments]; int[] accum = new int[NumMbSegments];
var distAccum = new int[NumMbSegments]; int[] distAccum = new int[NumMbSegments];
// Bracket the input. // Bracket the input.
for (n = 0; n <= WebpConstants.MaxAlpha && alphas[n] == 0; ++n) for (n = 0; n <= WebpConstants.MaxAlpha && alphas[n] == 0; ++n)
{ {
} }
var minA = n; int minA = n;
for (n = WebpConstants.MaxAlpha; n > minA && alphas[n] == 0; --n) for (n = WebpConstants.MaxAlpha; n > minA && alphas[n] == 0; --n)
{ {
} }
var maxA = n; int maxA = n;
var rangeA = maxA - minA; int rangeA = maxA - minA;
// Spread initial centers evenly. // Spread initial centers evenly.
for (k = 0, n = 1; k < nb; ++k, n += 2) for (k = 0, n = 1; k < nb; ++k, n += 2)
@ -573,9 +574,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
} }
// All point are classified. Move the centroids to the center of their respective cloud. // All point are classified. Move the centroids to the center of their respective cloud.
var displaced = 0; int displaced = 0;
weightedAverage = 0; weightedAverage = 0;
var totalWeight = 0; int totalWeight = 0;
for (n = 0; n < nb; ++n) for (n = 0; n < nb; ++n)
{ {
if (accum[n] != 0) if (accum[n] != 0)
@ -694,7 +695,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private void SetupFilterStrength() private void SetupFilterStrength()
{ {
var filterSharpness = 0; // TODO: filterSharpness is hardcoded int filterSharpness = 0; // TODO: filterSharpness is hardcoded
var filterType = 1; // TODO: filterType is hardcoded var filterType = 1; // TODO: filterType is hardcoded
// level0 is in [0..500]. Using '-f 50' as filter_strength is mid-filtering. // level0 is in [0..500]. Using '-f 50' as filter_strength is mid-filtering.
@ -720,7 +721,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private void SetSegmentProbas() private void SetSegmentProbas()
{ {
var p = new int[NumMbSegments]; int[] p = new int[NumMbSegments];
int n; int n;
for (n = 0; n < this.Mbw * this.Mbh; ++n) for (n = 0; n < this.Mbw * this.Mbh; ++n)
@ -1078,7 +1079,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// i16x16 // i16x16
residual.Init(0, 1, this.Proba); residual.Init(0, 1, this.Proba);
residual.SetCoeffs(rd.YDcLevels); residual.SetCoeffs(rd.YDcLevels);
var res = residual.RecordCoeffs(it.TopNz[8] + it.LeftNz[8]); int res = residual.RecordCoeffs(it.TopNz[8] + it.LeftNz[8]);
it.TopNz[8] = res; it.TopNz[8] = res;
it.LeftNz[8] = res; it.LeftNz[8] = res;
residual.Init(1, 0, this.Proba); residual.Init(1, 0, this.Proba);
@ -1128,8 +1129,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Span<byte> src = it.YuvIn.AsSpan(Vp8EncIterator.YOffEnc); Span<byte> src = it.YuvIn.AsSpan(Vp8EncIterator.YOffEnc);
int nz = 0; int nz = 0;
int n; int n;
var dcTmp = new short[16]; short[] dcTmp = new short[16];
var tmp = new short[16 * 16]; short[] tmp = new short[16 * 16];
Span<short> tmpSpan = tmp.AsSpan(); Span<short> tmpSpan = tmp.AsSpan();
for (n = 0; n < 16; n += 2) for (n = 0; n < 16; n += 2)
@ -1161,9 +1162,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private int ReconstructIntra4(Vp8EncIterator it, Vp8SegmentInfo dqm, Span<short> levels, Span<byte> src, Span<byte> yuvOut, int mode) private int ReconstructIntra4(Vp8EncIterator it, Vp8SegmentInfo dqm, Span<short> levels, Span<byte> src, Span<byte> yuvOut, int mode)
{ {
Span<byte> reference = it.YuvP.AsSpan(Vp8Encoding.Vp8I4ModeOffsets[mode]); Span<byte> reference = it.YuvP.AsSpan(Vp8Encoding.Vp8I4ModeOffsets[mode]);
var tmp = new short[16]; short[] tmp = new short[16];
Vp8Encoding.FTransform(src, reference, tmp); Vp8Encoding.FTransform(src, reference, tmp);
var nz = QuantEnc.QuantizeBlock(tmp, levels, dqm.Y1); int nz = QuantEnc.QuantizeBlock(tmp, levels, dqm.Y1);
Vp8Encoding.ITransform(reference, tmp, yuvOut, false); Vp8Encoding.ITransform(reference, tmp, yuvOut, false);
return nz; return nz;
@ -1175,7 +1176,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Span<byte> src = it.YuvIn.AsSpan(Vp8EncIterator.UOffEnc); Span<byte> src = it.YuvIn.AsSpan(Vp8EncIterator.UOffEnc);
int nz = 0; int nz = 0;
int n; int n;
var tmp = new short[8 * 16]; short[] tmp = new short[8 * 16];
for (n = 0; n < 8; n += 2) for (n = 0; n < 8; n += 2)
{ {

6
src/ImageSharp/Formats/WebP/WebpDecoderCore.cs

@ -84,11 +84,11 @@ namespace SixLabors.ImageSharp.Formats.Webp
this.Metadata = new ImageMetadata(); this.Metadata = new ImageMetadata();
this.currentStream = stream; this.currentStream = stream;
var fileSize = this.ReadImageHeader(); uint fileSize = this.ReadImageHeader();
using (this.webImageInfo = this.ReadVp8Info()) using (this.webImageInfo = this.ReadVp8Info())
{ {
if (this.webImageInfo.Features != null && this.webImageInfo.Features.Animation) if (this.webImageInfo.Features is { Animation: true })
{ {
WebpThrowHelper.ThrowNotSupportedException("Animations are not supported"); WebpThrowHelper.ThrowNotSupportedException("Animations are not supported");
} }
@ -384,7 +384,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
// The first 28 bits of the bitstream specify the width and height of the image. // The first 28 bits of the bitstream specify the width and height of the image.
uint width = bitReader.ReadValue(WebpConstants.Vp8LImageSizeBits) + 1; uint width = bitReader.ReadValue(WebpConstants.Vp8LImageSizeBits) + 1;
uint height = bitReader.ReadValue(WebpConstants.Vp8LImageSizeBits) + 1; uint height = bitReader.ReadValue(WebpConstants.Vp8LImageSizeBits) + 1;
if (width == 1 || height == 1) if (width == 0 || height == 0)
{ {
WebpThrowHelper.ThrowImageFormatException("invalid width or height read"); WebpThrowHelper.ThrowImageFormatException("invalid width or height read");
} }

15
tests/ImageSharp.Benchmarks/Codecs/DecodeTiff.cs

@ -8,7 +8,6 @@
using System.IO; using System.IO;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests; using SixLabors.ImageSharp.Tests;
@ -26,8 +25,6 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
private byte[] data; private byte[] data;
private Configuration configuration;
#if BIG_TESTS #if BIG_TESTS
private static readonly int BufferSize = 1024 * 68; private static readonly int BufferSize = 1024 * 68;
@ -61,16 +58,6 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
public string TestImage { get; set; } public string TestImage { get; set; }
#endif #endif
[GlobalSetup]
public void Config()
{
if (this.configuration == null)
{
this.configuration = new Configuration();
this.configuration.StreamProcessingBufferSize = BufferSize;
}
}
[IterationSetup] [IterationSetup]
public void ReadImages() public void ReadImages()
{ {
@ -95,7 +82,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
public Size TiffCore() public Size TiffCore()
{ {
using (var ms = new MemoryStream(this.data)) using (var ms = new MemoryStream(this.data))
using (var image = Image.Load<Rgba32>(this.configuration, ms)) using (var image = Image.Load<Rgba32>(ms))
{ {
return image.Size(); return image.Size();
} }

2
tests/ImageSharp.Benchmarks/Codecs/DecodeWebp.cs

@ -11,6 +11,8 @@ using SixLabors.ImageSharp.Tests;
namespace SixLabors.ImageSharp.Benchmarks.Codecs namespace SixLabors.ImageSharp.Benchmarks.Codecs
{ {
[MarkdownExporter]
[HtmlExporter]
[Config(typeof(Config.ShortMultiFramework))] [Config(typeof(Config.ShortMultiFramework))]
public class DecodeWebp public class DecodeWebp
{ {

2
tests/ImageSharp.Benchmarks/Codecs/EncodeWebp.cs

@ -10,6 +10,8 @@ using SixLabors.ImageSharp.Tests;
namespace SixLabors.ImageSharp.Benchmarks.Codecs namespace SixLabors.ImageSharp.Benchmarks.Codecs
{ {
[MarkdownExporter]
[HtmlExporter]
[Config(typeof(Config.ShortMultiFramework))] [Config(typeof(Config.ShortMultiFramework))]
public class EncodeWebp public class EncodeWebp
{ {

2
tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs

@ -13,7 +13,7 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.WebP namespace SixLabors.ImageSharp.Tests.Formats.WebP
{ {
[Trait("Format", "Webp")] [Trait("Format", "WebpLossless")]
public class PredictorEncoderTests public class PredictorEncoderTests
{ {
[Fact] [Fact]

46
tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -27,7 +28,17 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif
/// <summary> /// <summary>
/// Writes a png file. /// Writes a png file.
/// </summary> /// </summary>
Png Png,
/// <summary>
/// Writes a lossless webp file.
/// </summary>
WebpLossless,
/// <summary>
/// Writes a lossy webp file.
/// </summary>
WebpLossy
} }
private static readonly Dictionary<ExifTag, object> TestProfileValues = new Dictionary<ExifTag, object> private static readonly Dictionary<ExifTag, object> TestProfileValues = new Dictionary<ExifTag, object>
@ -44,6 +55,8 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif
[Theory] [Theory]
[InlineData(TestImageWriteFormat.Jpeg)] [InlineData(TestImageWriteFormat.Jpeg)]
[InlineData(TestImageWriteFormat.Png)] [InlineData(TestImageWriteFormat.Png)]
[InlineData(TestImageWriteFormat.WebpLossless)]
[InlineData(TestImageWriteFormat.WebpLossy)]
public void Constructor(TestImageWriteFormat imageFormat) public void Constructor(TestImageWriteFormat imageFormat)
{ {
Image<Rgba32> image = TestFile.Create(TestImages.Jpeg.Baseline.Calliphora).CreateRgba32Image(); Image<Rgba32> image = TestFile.Create(TestImages.Jpeg.Baseline.Calliphora).CreateRgba32Image();
@ -92,6 +105,8 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif
[Theory] [Theory]
[InlineData(TestImageWriteFormat.Jpeg)] [InlineData(TestImageWriteFormat.Jpeg)]
[InlineData(TestImageWriteFormat.Png)] [InlineData(TestImageWriteFormat.Png)]
[InlineData(TestImageWriteFormat.WebpLossless)]
[InlineData(TestImageWriteFormat.WebpLossy)]
public void WriteFraction(TestImageWriteFormat imageFormat) public void WriteFraction(TestImageWriteFormat imageFormat)
{ {
using (var memStream = new MemoryStream()) using (var memStream = new MemoryStream())
@ -135,6 +150,8 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif
[Theory] [Theory]
[InlineData(TestImageWriteFormat.Jpeg)] [InlineData(TestImageWriteFormat.Jpeg)]
[InlineData(TestImageWriteFormat.Png)] [InlineData(TestImageWriteFormat.Png)]
[InlineData(TestImageWriteFormat.WebpLossless)]
[InlineData(TestImageWriteFormat.WebpLossy)]
public void ReadWriteInfinity(TestImageWriteFormat imageFormat) public void ReadWriteInfinity(TestImageWriteFormat imageFormat)
{ {
Image<Rgba32> image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image(); Image<Rgba32> image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image();
@ -170,6 +187,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif
https://exiftool.org/TagNames/EXIF.html */ https://exiftool.org/TagNames/EXIF.html */
[InlineData(TestImageWriteFormat.Jpeg, 16)] [InlineData(TestImageWriteFormat.Jpeg, 16)]
[InlineData(TestImageWriteFormat.Png, 16)] [InlineData(TestImageWriteFormat.Png, 16)]
[InlineData(TestImageWriteFormat.WebpLossless, 16)]
public void SetValue(TestImageWriteFormat imageFormat, int expectedProfileValueCount) public void SetValue(TestImageWriteFormat imageFormat, int expectedProfileValueCount)
{ {
Image<Rgba32> image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image(); Image<Rgba32> image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image();
@ -241,6 +259,8 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif
[Theory] [Theory]
[InlineData(TestImageWriteFormat.Jpeg)] [InlineData(TestImageWriteFormat.Jpeg)]
[InlineData(TestImageWriteFormat.Png)] [InlineData(TestImageWriteFormat.Png)]
[InlineData(TestImageWriteFormat.WebpLossless)]
[InlineData(TestImageWriteFormat.WebpLossy)]
public void WriteOnlyExifTags_Works(TestImageWriteFormat imageFormat) public void WriteOnlyExifTags_Works(TestImageWriteFormat imageFormat)
{ {
// Arrange // Arrange
@ -327,7 +347,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif
[Fact] [Fact]
public void ReadWriteLargeProfileJpg() public void ReadWriteLargeProfileJpg()
{ {
ExifTag<string>[] tags = new[] { ExifTag.Software, ExifTag.Copyright, ExifTag.Model, ExifTag.ImageDescription }; ExifTag<string>[] tags = { ExifTag.Software, ExifTag.Copyright, ExifTag.Model, ExifTag.ImageDescription };
foreach (ExifTag<string> tag in tags) foreach (ExifTag<string> tag in tags)
{ {
// Arrange // Arrange
@ -344,7 +364,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif
image.Metadata.ExifProfile = expectedProfile; image.Metadata.ExifProfile = expectedProfile;
// Act // Act
using Image<Rgba32> reloadedImage = WriteAndRead(image, TestImageWriteFormat.Jpeg); Image<Rgba32> reloadedImage = WriteAndRead(image, TestImageWriteFormat.Jpeg);
// Assert // Assert
ExifProfile actualProfile = reloadedImage.Metadata.ExifProfile; ExifProfile actualProfile = reloadedImage.Metadata.ExifProfile;
@ -366,7 +386,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif
[Fact] [Fact]
public void ExifTypeUndefined() public void ExifTypeUndefined()
{ {
// This image contains an 802 byte EXIF profile // This image contains an 802 byte EXIF profile.
// It has a tag with an index offset of 18,481,152 bytes (overrunning the data) // It has a tag with an index offset of 18,481,152 bytes (overrunning the data)
using Image<Rgba32> image = TestFile.Create(TestImages.Jpeg.Progressive.Bad.ExifUndefType).CreateRgba32Image(); using Image<Rgba32> image = TestFile.Create(TestImages.Jpeg.Progressive.Bad.ExifUndefType).CreateRgba32Image();
Assert.NotNull(image); Assert.NotNull(image);
@ -409,6 +429,8 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif
[Theory] [Theory]
[InlineData(TestImageWriteFormat.Jpeg)] [InlineData(TestImageWriteFormat.Jpeg)]
[InlineData(TestImageWriteFormat.Png)] [InlineData(TestImageWriteFormat.Png)]
[InlineData(TestImageWriteFormat.WebpLossless)]
[InlineData(TestImageWriteFormat.WebpLossy)]
public void WritingImagePreservesExifProfile(TestImageWriteFormat imageFormat) public void WritingImagePreservesExifProfile(TestImageWriteFormat imageFormat)
{ {
// Arrange // Arrange
@ -483,6 +505,10 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif
return WriteAndReadJpeg(image); return WriteAndReadJpeg(image);
case TestImageWriteFormat.Png: case TestImageWriteFormat.Png:
return WriteAndReadPng(image); return WriteAndReadPng(image);
case TestImageWriteFormat.WebpLossless:
return WriteAndReadWebp(image, lossy: false);
case TestImageWriteFormat.WebpLossy:
return WriteAndReadWebp(image, lossy: true);
default: default:
throw new ArgumentException("Unexpected test image format, only Jpeg and Png are allowed"); throw new ArgumentException("Unexpected test image format, only Jpeg and Png are allowed");
} }
@ -512,6 +538,18 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif
} }
} }
private static Image<Rgba32> WriteAndReadWebp(Image<Rgba32> image, bool lossy)
{
using (var memStream = new MemoryStream())
{
image.SaveAsWebp(memStream, new WebpEncoder() { Lossy = lossy });
image.Dispose();
memStream.Position = 0;
return Image.Load<Rgba32>(memStream, new WebpDecoder());
}
}
private static void TestProfile(ExifProfile profile) private static void TestProfile(ExifProfile profile)
{ {
Assert.NotNull(profile); Assert.NotNull(profile);

Loading…
Cancel
Save