Browse Source

Rename WebP to Webp

pull/1552/head
Brian Popow 6 years ago
parent
commit
d74463d5a6
  1. 2
      src/ImageSharp/Advanced/AotCompilerTools.cs
  2. 52
      src/ImageSharp/Formats/ImageExtensions.Save.cs
  3. 8
      src/ImageSharp/Formats/ImageExtensions.Save.tt
  4. 2
      src/ImageSharp/Formats/WebP/AlphaCompressionMethod.cs
  5. 38
      src/ImageSharp/Formats/WebP/AlphaDecoder.cs
  6. 2
      src/ImageSharp/Formats/WebP/BitReader/BitReaderBase.cs
  7. 4
      src/ImageSharp/Formats/WebP/BitReader/Vp8BitReader.cs
  8. 2
      src/ImageSharp/Formats/WebP/BitReader/Vp8LBitReader.cs
  9. 6
      src/ImageSharp/Formats/WebP/BitWriter/BitWriterBase.cs
  10. 58
      src/ImageSharp/Formats/WebP/BitWriter/Vp8BitWriter.cs
  11. 10
      src/ImageSharp/Formats/WebP/BitWriter/Vp8LBitWriter.cs
  12. 2
      src/ImageSharp/Formats/WebP/EntropyIx.cs
  13. 2
      src/ImageSharp/Formats/WebP/HistoIx.cs
  14. 4
      src/ImageSharp/Formats/WebP/IWebPDecoderOptions.cs
  15. 2
      src/ImageSharp/Formats/WebP/IWebPEncoderOptions.cs
  16. 36
      src/ImageSharp/Formats/WebP/ImageExtensions.cs
  17. 16
      src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs
  18. 2
      src/ImageSharp/Formats/WebP/Lossless/ColorCache.cs
  19. 2
      src/ImageSharp/Formats/WebP/Lossless/CostCacheInterval.cs
  20. 2
      src/ImageSharp/Formats/WebP/Lossless/CostInterval.cs
  21. 2
      src/ImageSharp/Formats/WebP/Lossless/CostManager.cs
  22. 8
      src/ImageSharp/Formats/WebP/Lossless/CostModel.cs
  23. 2
      src/ImageSharp/Formats/WebP/Lossless/CrunchConfig.cs
  24. 2
      src/ImageSharp/Formats/WebP/Lossless/CrunchSubConfig.cs
  25. 2
      src/ImageSharp/Formats/WebP/Lossless/DominantCostRange.cs
  26. 6
      src/ImageSharp/Formats/WebP/Lossless/HTreeGroup.cs
  27. 2
      src/ImageSharp/Formats/WebP/Lossless/HistogramBinInfo.cs
  28. 2
      src/ImageSharp/Formats/WebP/Lossless/HistogramEncoder.cs
  29. 2
      src/ImageSharp/Formats/WebP/Lossless/HistogramPair.cs
  30. 2
      src/ImageSharp/Formats/WebP/Lossless/HuffIndex.cs
  31. 2
      src/ImageSharp/Formats/WebP/Lossless/HuffmanCode.cs
  32. 2
      src/ImageSharp/Formats/WebP/Lossless/HuffmanTree.cs
  33. 2
      src/ImageSharp/Formats/WebP/Lossless/HuffmanTreeCode.cs
  34. 2
      src/ImageSharp/Formats/WebP/Lossless/HuffmanTreeToken.cs
  35. 26
      src/ImageSharp/Formats/WebP/Lossless/HuffmanUtils.cs
  36. 26
      src/ImageSharp/Formats/WebP/Lossless/LosslessUtils.cs
  37. 2
      src/ImageSharp/Formats/WebP/Lossless/PixOrCopy.cs
  38. 2
      src/ImageSharp/Formats/WebP/Lossless/PixOrCopyMode.cs
  39. 10
      src/ImageSharp/Formats/WebP/Lossless/PredictorEncoder.cs
  40. 2
      src/ImageSharp/Formats/WebP/Lossless/Vp8LBackwardRefs.cs
  41. 2
      src/ImageSharp/Formats/WebP/Lossless/Vp8LBitEntropy.cs
  42. 2
      src/ImageSharp/Formats/WebP/Lossless/Vp8LDecoder.cs
  43. 66
      src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs
  44. 2
      src/ImageSharp/Formats/WebP/Lossless/Vp8LHashChain.cs
  45. 68
      src/ImageSharp/Formats/WebP/Lossless/Vp8LHistogram.cs
  46. 2
      src/ImageSharp/Formats/WebP/Lossless/Vp8LLz77Type.cs
  47. 2
      src/ImageSharp/Formats/WebP/Lossless/Vp8LMetadata.cs
  48. 2
      src/ImageSharp/Formats/WebP/Lossless/Vp8LMultipliers.cs
  49. 4
      src/ImageSharp/Formats/WebP/Lossless/Vp8LStreaks.cs
  50. 2
      src/ImageSharp/Formats/WebP/Lossless/Vp8LTransform.cs
  51. 2
      src/ImageSharp/Formats/WebP/Lossless/Vp8LTransformType.cs
  52. 76
      src/ImageSharp/Formats/WebP/Lossless/WebPLosslessDecoder.cs
  53. 2
      src/ImageSharp/Formats/WebP/Lossy/IntraPredictionMode.cs
  54. 2
      src/ImageSharp/Formats/WebP/Lossy/LoopFilter.cs
  55. 222
      src/ImageSharp/Formats/WebP/Lossy/LossyUtils.cs
  56. 2
      src/ImageSharp/Formats/WebP/Lossy/PassStats.cs
  57. 4
      src/ImageSharp/Formats/WebP/Lossy/QuantEnc.cs
  58. 6
      src/ImageSharp/Formats/WebP/Lossy/VP8BandProbas.cs
  59. 4
      src/ImageSharp/Formats/WebP/Lossy/Vp8CostArray.cs
  60. 18
      src/ImageSharp/Formats/WebP/Lossy/Vp8Decoder.cs
  61. 40
      src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs
  62. 60
      src/ImageSharp/Formats/WebP/Lossy/Vp8EncProba.cs
  63. 2
      src/ImageSharp/Formats/WebP/Lossy/Vp8EncSegmentHeader.cs
  64. 78
      src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs
  65. 38
      src/ImageSharp/Formats/WebP/Lossy/Vp8Encoding.cs
  66. 2
      src/ImageSharp/Formats/WebP/Lossy/Vp8FilterHeader.cs
  67. 2
      src/ImageSharp/Formats/WebP/Lossy/Vp8FilterInfo.cs
  68. 2
      src/ImageSharp/Formats/WebP/Lossy/Vp8FrameHeader.cs
  69. 2
      src/ImageSharp/Formats/WebP/Lossy/Vp8Io.cs
  70. 2
      src/ImageSharp/Formats/WebP/Lossy/Vp8MacroBlock.cs
  71. 2
      src/ImageSharp/Formats/WebP/Lossy/Vp8MacroBlockData.cs
  72. 2
      src/ImageSharp/Formats/WebP/Lossy/Vp8MacroBlockInfo.cs
  73. 2
      src/ImageSharp/Formats/WebP/Lossy/Vp8MacroBlockType.cs
  74. 8
      src/ImageSharp/Formats/WebP/Lossy/Vp8Matrix.cs
  75. 2
      src/ImageSharp/Formats/WebP/Lossy/Vp8ModeScore.cs
  76. 2
      src/ImageSharp/Formats/WebP/Lossy/Vp8PictureHeader.cs
  77. 12
      src/ImageSharp/Formats/WebP/Lossy/Vp8Proba.cs
  78. 4
      src/ImageSharp/Formats/WebP/Lossy/Vp8ProbaArray.cs
  79. 2
      src/ImageSharp/Formats/WebP/Lossy/Vp8QuantMatrix.cs
  80. 2
      src/ImageSharp/Formats/WebP/Lossy/Vp8RDLevel.cs
  81. 12
      src/ImageSharp/Formats/WebP/Lossy/Vp8Residual.cs
  82. 2
      src/ImageSharp/Formats/WebP/Lossy/Vp8SegmentHeader.cs
  83. 2
      src/ImageSharp/Formats/WebP/Lossy/Vp8SegmentInfo.cs
  84. 6
      src/ImageSharp/Formats/WebP/Lossy/Vp8Stats.cs
  85. 4
      src/ImageSharp/Formats/WebP/Lossy/Vp8StatsArray.cs
  86. 2
      src/ImageSharp/Formats/WebP/Lossy/Vp8TopSamples.cs
  87. 118
      src/ImageSharp/Formats/WebP/Lossy/WebPLossyDecoder.cs
  88. 18
      src/ImageSharp/Formats/WebP/Lossy/YuvConversion.cs
  89. 6
      src/ImageSharp/Formats/WebP/MetadataExtensions.cs
  90. 2
      src/ImageSharp/Formats/WebP/Vp8HeaderType.cs
  91. 4
      src/ImageSharp/Formats/WebP/WebPAlphaFilterType.cs
  92. 4
      src/ImageSharp/Formats/WebP/WebPBitsPerPixel.cs
  93. 4
      src/ImageSharp/Formats/WebP/WebPChunkType.cs
  94. 6
      src/ImageSharp/Formats/WebP/WebPCommonUtils.cs
  95. 4
      src/ImageSharp/Formats/WebP/WebPConstants.cs
  96. 12
      src/ImageSharp/Formats/WebP/WebPDecoder.cs
  97. 128
      src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
  98. 8
      src/ImageSharp/Formats/WebP/WebPEncoder.cs
  99. 12
      src/ImageSharp/Formats/WebP/WebPEncoderCore.cs
  100. 4
      src/ImageSharp/Formats/WebP/WebPFeatures.cs

2
src/ImageSharp/Advanced/AotCompilerTools.cs

@ -95,7 +95,7 @@ namespace SixLabors.ImageSharp.Advanced
AotCodec<TPixel>(new Formats.Gif.GifDecoder(), new Formats.Gif.GifEncoder()); AotCodec<TPixel>(new Formats.Gif.GifDecoder(), new Formats.Gif.GifEncoder());
AotCodec<TPixel>(new Formats.Jpeg.JpegDecoder(), new Formats.Jpeg.JpegEncoder()); AotCodec<TPixel>(new Formats.Jpeg.JpegDecoder(), new Formats.Jpeg.JpegEncoder());
AotCodec<TPixel>(new Formats.Tga.TgaDecoder(), new Formats.Tga.TgaEncoder()); AotCodec<TPixel>(new Formats.Tga.TgaDecoder(), new Formats.Tga.TgaEncoder());
AotCodec<TPixel>(new Formats.Experimental.WebP.WebPDecoder(), new Formats.Experimental.WebP.WebPEncoder()); AotCodec<TPixel>(new Formats.Experimental.Webp.WebpDecoder(), new Formats.Experimental.Webp.WebpEncoder());
// TODO: Do the discovery work to figure out what works and what doesn't. // TODO: Do the discovery work to figure out what works and what doesn't.
} }

52
src/ImageSharp/Formats/ImageExtensions.Save.cs

@ -7,7 +7,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
// using SixLabors.ImageSharp.Formats.Experimental.Tiff; // using SixLabors.ImageSharp.Formats.Experimental.Tiff;
using SixLabors.ImageSharp.Formats.Experimental.WebP; using SixLabors.ImageSharp.Formats.Experimental.Webp;
using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Gif;
@ -538,47 +538,47 @@ namespace SixLabors.ImageSharp
cancellationToken); cancellationToken);
/// <summary> /// <summary>
/// EXPERIMENTAL! Saves the image to the given stream with the WebP format. /// EXPERIMENTAL! Saves the image to the given stream with the Webp format.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param> /// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception> /// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
public static void SaveAsWebP(this Image source, string path) => SaveAsWebP(source, path, null); public static void SaveAsWebp(this Image source, string path) => SaveAsWebp(source, path, null);
/// <summary> /// <summary>
/// EXPERIMENTAL! Saves the image to the given stream with the WebP format. /// EXPERIMENTAL! Saves the image to the given stream with the Webp format.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param> /// <param name="path">The file path to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception> /// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsWebPAsync(this Image source, string path) => SaveAsWebPAsync(source, path, null); public static Task SaveAsWebpAsync(this Image source, string path) => SaveAsWebpAsync(source, path, null);
/// <summary> /// <summary>
/// EXPERIMENTAL! Saves the image to the given stream with the WebP format. /// EXPERIMENTAL! Saves the image to the given stream with the Webp format.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param> /// <param name="path">The file path to save the image to.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception> /// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsWebPAsync(this Image source, string path, CancellationToken cancellationToken) public static Task SaveAsWebpAsync(this Image source, string path, CancellationToken cancellationToken)
=> SaveAsWebPAsync(source, path, null, cancellationToken); => SaveAsWebpAsync(source, path, null, cancellationToken);
/// <summary> /// <summary>
/// EXPERIMENTAL! Saves the image to the given stream with the WebP format. /// EXPERIMENTAL! Saves the image to the given stream with the Webp format.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param> /// <param name="path">The file path to save the image to.</param>
/// <param name="encoder">The encoder to save the image with.</param> /// <param name="encoder">The encoder to save the image with.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception> /// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
public static void SaveAsWebP(this Image source, string path, WebPEncoder encoder) => public static void SaveAsWebp(this Image source, string path, WebpEncoder encoder) =>
source.Save( source.Save(
path, path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebPFormat.Instance)); encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebpFormat.Instance));
/// <summary> /// <summary>
/// EXPERIMENTAL! Saves the image to the given stream with the WebP format. /// EXPERIMENTAL! Saves the image to the given stream with the Webp format.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="path">The file path to save the image to.</param> /// <param name="path">The file path to save the image to.</param>
@ -586,47 +586,47 @@ namespace SixLabors.ImageSharp
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception> /// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsWebPAsync(this Image source, string path, WebPEncoder encoder, CancellationToken cancellationToken = default) => public static Task SaveAsWebpAsync(this Image source, string path, WebpEncoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync( source.SaveAsync(
path, path,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebPFormat.Instance), encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebpFormat.Instance),
cancellationToken); cancellationToken);
/// <summary> /// <summary>
/// EXPERIMENTAL! Saves the image to the given stream with the WebP format. /// EXPERIMENTAL! Saves the image to the given stream with the Webp format.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param> /// <param name="stream">The stream to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception> /// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsWebP(this Image source, Stream stream) public static void SaveAsWebp(this Image source, Stream stream)
=> SaveAsWebP(source, stream, null); => SaveAsWebp(source, stream, null);
/// <summary> /// <summary>
/// EXPERIMENTAL! Saves the image to the given stream with the WebP format. /// EXPERIMENTAL! Saves the image to the given stream with the Webp format.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param> /// <param name="stream">The stream to save the image to.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception> /// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsWebPAsync(this Image source, Stream stream, CancellationToken cancellationToken = default) public static Task SaveAsWebpAsync(this Image source, Stream stream, CancellationToken cancellationToken = default)
=> SaveAsWebPAsync(source, stream, null, cancellationToken); => SaveAsWebpAsync(source, stream, null, cancellationToken);
/// <summary> /// <summary>
/// EXPERIMENTAL! Saves the image to the given stream with the WebP format. /// EXPERIMENTAL! Saves the image to the given stream with the Webp format.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param> /// <param name="stream">The stream to save the image to.</param>
/// <param name="encoder">The encoder to save the image with.</param> /// <param name="encoder">The encoder to save the image with.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception> /// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static void SaveAsWebP(this Image source, Stream stream, WebPEncoder encoder) public static void SaveAsWebp(this Image source, Stream stream, WebpEncoder encoder)
=> source.Save( => source.Save(
stream, stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebPFormat.Instance)); encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebpFormat.Instance));
/// <summary> /// <summary>
/// EXPERIMENTAL! Saves the image to the given stream with the WebP format. /// EXPERIMENTAL! Saves the image to the given stream with the Webp format.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param> /// <param name="stream">The stream to save the image to.</param>
@ -634,10 +634,10 @@ namespace SixLabors.ImageSharp
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception> /// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static Task SaveAsWebPAsync(this Image source, Stream stream, WebPEncoder encoder, CancellationToken cancellationToken = default) => public static Task SaveAsWebpAsync(this Image source, Stream stream, WebpEncoder encoder, CancellationToken cancellationToken = default) =>
source.SaveAsync( source.SaveAsync(
stream, stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebPFormat.Instance), encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebpFormat.Instance),
cancellationToken); cancellationToken);
} }

8
src/ImageSharp/Formats/ImageExtensions.Save.tt

@ -10,7 +10,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
// using SixLabors.ImageSharp.Formats.Experimental.Tiff; // using SixLabors.ImageSharp.Formats.Experimental.Tiff;
using SixLabors.ImageSharp.Formats.Experimental.WebP; using SixLabors.ImageSharp.Formats.Experimental.Webp;
<# <#
var formats = new []{ var formats = new []{
@ -19,12 +19,12 @@ using SixLabors.ImageSharp.Formats.Experimental.WebP;
"Jpeg", "Jpeg",
"Png", "Png",
"Tga", "Tga",
"WebP" "Webp"
}; };
foreach (string fmt in formats) foreach (string fmt in formats)
{ {
if (fmt == "Tiff" || fmt == "WebP") if (fmt == "Tiff" || fmt == "Webp")
{ {
continue; continue;
} }
@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp
<# <#
foreach (string fmt in formats) foreach (string fmt in formats)
{ {
string experimentalString = fmt == "Tiff" || fmt == "WebP" ? @"EXPERIMENTAL! " : ""; string experimentalString = fmt == "Tiff" || fmt == "Webp" ? @"EXPERIMENTAL! " : "";
#> #>
/// <summary> /// <summary>
/// <#= experimentalString #>Saves the image to the given stream with the <#= fmt #> format. /// <#= experimentalString #>Saves the image to the given stream with the <#= fmt #> format.

2
src/ImageSharp/Formats/WebP/AlphaCompressionMethod.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP namespace SixLabors.ImageSharp.Formats.Experimental.Webp
{ {
internal enum AlphaCompressionMethod internal enum AlphaCompressionMethod
{ {

38
src/ImageSharp/Formats/WebP/AlphaDecoder.cs

@ -6,11 +6,11 @@ using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Experimental.WebP.BitReader; using SixLabors.ImageSharp.Formats.Experimental.Webp.BitReader;
using SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless; using SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP namespace SixLabors.ImageSharp.Formats.Experimental.Webp
{ {
/// <summary> /// <summary>
/// Implements decoding for lossy alpha chunks which may be compressed. /// Implements decoding for lossy alpha chunks which may be compressed.
@ -40,20 +40,20 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
var compression = (AlphaCompressionMethod)(alphaChunkHeader & 0x03); var compression = (AlphaCompressionMethod)(alphaChunkHeader & 0x03);
if (compression != AlphaCompressionMethod.NoCompression && compression != AlphaCompressionMethod.WebPLosslessCompression) if (compression != AlphaCompressionMethod.NoCompression && compression != AlphaCompressionMethod.WebPLosslessCompression)
{ {
WebPThrowHelper.ThrowImageFormatException($"unexpected alpha compression method {compression} found"); WebpThrowHelper.ThrowImageFormatException($"unexpected alpha compression method {compression} found");
} }
this.Compressed = compression == AlphaCompressionMethod.WebPLosslessCompression; this.Compressed = compression == AlphaCompressionMethod.WebPLosslessCompression;
// The filtering method used. Only values between 0 and 3 are valid. // The filtering method used. Only values between 0 and 3 are valid.
int filter = (alphaChunkHeader >> 2) & 0x03; int filter = (alphaChunkHeader >> 2) & 0x03;
if (filter < (int)WebPAlphaFilterType.None || filter > (int)WebPAlphaFilterType.Gradient) if (filter < (int)WebpAlphaFilterType.None || filter > (int)WebpAlphaFilterType.Gradient)
{ {
WebPThrowHelper.ThrowImageFormatException($"unexpected alpha filter method {filter} found"); WebpThrowHelper.ThrowImageFormatException($"unexpected alpha filter method {filter} found");
} }
this.Alpha = memoryAllocator.Allocate<byte>(totalPixels); this.Alpha = memoryAllocator.Allocate<byte>(totalPixels);
this.AlphaFilterType = (WebPAlphaFilterType)filter; this.AlphaFilterType = (WebpAlphaFilterType)filter;
this.Vp8LDec = new Vp8LDecoder(width, height, memoryAllocator); this.Vp8LDec = new Vp8LDecoder(width, height, memoryAllocator);
if (this.Compressed) if (this.Compressed)
@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
/// <summary> /// <summary>
/// Gets the used filter type. /// Gets the used filter type.
/// </summary> /// </summary>
public WebPAlphaFilterType AlphaFilterType { get; } public WebpAlphaFilterType AlphaFilterType { get; }
/// <summary> /// <summary>
/// Gets or sets the last decoded row. /// Gets or sets the last decoded row.
@ -133,11 +133,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
var pixelCount = this.Width * this.Height; var pixelCount = this.Width * this.Height;
if (dataSpan.Length < pixelCount) if (dataSpan.Length < pixelCount)
{ {
WebPThrowHelper.ThrowImageFormatException("not enough data in the ALPH chunk"); WebpThrowHelper.ThrowImageFormatException("not enough data in the ALPH chunk");
} }
Span<byte> alphaSpan = this.Alpha.Memory.Span; Span<byte> alphaSpan = this.Alpha.Memory.Span;
if (this.AlphaFilterType == WebPAlphaFilterType.None) if (this.AlphaFilterType == WebpAlphaFilterType.None)
{ {
dataSpan.Slice(0, pixelCount).CopyTo(alphaSpan); dataSpan.Slice(0, pixelCount).CopyTo(alphaSpan);
return; return;
@ -150,13 +150,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
{ {
switch (this.AlphaFilterType) switch (this.AlphaFilterType)
{ {
case WebPAlphaFilterType.Horizontal: case WebpAlphaFilterType.Horizontal:
HorizontalUnfilter(prev, deltas, dst, this.Width); HorizontalUnfilter(prev, deltas, dst, this.Width);
break; break;
case WebPAlphaFilterType.Vertical: case WebpAlphaFilterType.Vertical:
VerticalUnfilter(prev, deltas, dst, this.Width); VerticalUnfilter(prev, deltas, dst, this.Width);
break; break;
case WebPAlphaFilterType.Gradient: case WebpAlphaFilterType.Gradient:
GradientUnfilter(prev, deltas, dst, this.Width); GradientUnfilter(prev, deltas, dst, this.Width);
break; break;
} }
@ -189,7 +189,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
/// <param name="stride">The stride to use.</param> /// <param name="stride">The stride to use.</param>
public void AlphaApplyFilter(int firstRow, int lastRow, Span<byte> dst, int stride) public void AlphaApplyFilter(int firstRow, int lastRow, Span<byte> dst, int stride)
{ {
if (this.AlphaFilterType == WebPAlphaFilterType.None) if (this.AlphaFilterType == WebpAlphaFilterType.None)
{ {
return; return;
} }
@ -200,13 +200,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
{ {
switch (this.AlphaFilterType) switch (this.AlphaFilterType)
{ {
case WebPAlphaFilterType.Horizontal: case WebpAlphaFilterType.Horizontal:
HorizontalUnfilter(prev, dst, dst, this.Width); HorizontalUnfilter(prev, dst, dst, this.Width);
break; break;
case WebPAlphaFilterType.Vertical: case WebpAlphaFilterType.Vertical:
VerticalUnfilter(prev, dst, dst, this.Width); VerticalUnfilter(prev, dst, dst, this.Width);
break; break;
case WebPAlphaFilterType.Gradient: case WebpAlphaFilterType.Gradient:
GradientUnfilter(prev, dst, dst, this.Width); GradientUnfilter(prev, dst, dst, this.Width);
break; break;
} }
@ -222,7 +222,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
{ {
// For vertical and gradient filtering, we need to decode the part above the // For vertical and gradient filtering, we need to decode the part above the
// cropTop row, in order to have the correct spatial predictors. // cropTop row, in order to have the correct spatial predictors.
int topRow = (this.AlphaFilterType == WebPAlphaFilterType.None || this.AlphaFilterType == WebPAlphaFilterType.Horizontal) ? 0 : this.LastRow; int topRow = (this.AlphaFilterType == WebpAlphaFilterType.None || this.AlphaFilterType == WebpAlphaFilterType.Horizontal) ? 0 : this.LastRow;
int firstRow = (this.LastRow < topRow) ? topRow : this.LastRow; int firstRow = (this.LastRow < topRow) ? topRow : this.LastRow;
if (lastRow > firstRow) if (lastRow > firstRow)
{ {
@ -235,7 +235,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
if (this.Vp8LDec.Transforms.Count == 0 || this.Vp8LDec.Transforms[0].TransformType != Vp8LTransformType.ColorIndexingTransform) if (this.Vp8LDec.Transforms.Count == 0 || this.Vp8LDec.Transforms[0].TransformType != Vp8LTransformType.ColorIndexingTransform)
{ {
WebPThrowHelper.ThrowImageFormatException("error while decoding alpha channel, expected color index transform data is missing"); WebpThrowHelper.ThrowImageFormatException("error while decoding alpha channel, expected color index transform data is missing");
} }
Vp8LTransform transform = this.Vp8LDec.Transforms[0]; Vp8LTransform transform = this.Vp8LDec.Transforms[0];

2
src/ImageSharp/Formats/WebP/BitReader/BitReaderBase.cs

@ -7,7 +7,7 @@ using System.IO;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.BitReader namespace SixLabors.ImageSharp.Formats.Experimental.Webp.BitReader
{ {
/// <summary> /// <summary>
/// Base class for VP8 and VP8L bitreader. /// Base class for VP8 and VP8L bitreader.

4
src/ImageSharp/Formats/WebP/BitReader/Vp8BitReader.cs

@ -7,7 +7,7 @@ using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.BitReader namespace SixLabors.ImageSharp.Formats.Experimental.Webp.BitReader
{ {
/// <summary> /// <summary>
/// A bit reader for VP8 streams. /// A bit reader for VP8 streams.
@ -114,7 +114,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.BitReader
range = split + 1; range = split + 1;
} }
int shift = 7 ^ WebPCommonUtils.BitsLog2Floor(range); int shift = 7 ^ WebpCommonUtils.BitsLog2Floor(range);
range <<= shift; range <<= shift;
this.bits -= shift; this.bits -= shift;

2
src/ImageSharp/Formats/WebP/BitReader/Vp8LBitReader.cs

@ -6,7 +6,7 @@ using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.BitReader namespace SixLabors.ImageSharp.Formats.Experimental.Webp.BitReader
{ {
/// <summary> /// <summary>
/// A bit reader for reading lossless webp streams. /// A bit reader for reading lossless webp streams.

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

@ -5,7 +5,7 @@ using System;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.IO; using System.IO;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.BitWriter namespace SixLabors.ImageSharp.Formats.Experimental.Webp.BitWriter
{ {
internal abstract class BitWriterBase internal abstract class BitWriterBase
{ {
@ -98,10 +98,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.BitWriter
{ {
Span<byte> buffer = stackalloc byte[4]; Span<byte> buffer = stackalloc byte[4];
stream.Write(WebPConstants.RiffFourCc); stream.Write(WebpConstants.RiffFourCc);
BinaryPrimitives.WriteUInt32LittleEndian(buffer, riffSize); BinaryPrimitives.WriteUInt32LittleEndian(buffer, riffSize);
stream.Write(buffer); stream.Write(buffer);
stream.Write(WebPConstants.WebPHeader); stream.Write(WebpConstants.WebPHeader);
} }
} }
} }

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

@ -4,9 +4,9 @@
using System; using System;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.IO; using System.IO;
using SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy; using SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.BitWriter namespace SixLabors.ImageSharp.Formats.Experimental.Webp.BitWriter
{ {
/// <summary> /// <summary>
/// A bit writer for writing lossy webp streams. /// A bit writer for writing lossy webp streams.
@ -100,13 +100,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.BitWriter
int v = sign ? -c : c; int v = sign ? -c : c;
if (!this.PutBit(v != 0, p.Probabilities[1])) if (!this.PutBit(v != 0, p.Probabilities[1]))
{ {
p = residual.Prob[WebPConstants.Vp8EncBands[n]].Probabilities[0]; p = residual.Prob[WebpConstants.Vp8EncBands[n]].Probabilities[0];
continue; continue;
} }
if (!this.PutBit(v > 1, p.Probabilities[2])) if (!this.PutBit(v > 1, p.Probabilities[2]))
{ {
p = residual.Prob[WebPConstants.Vp8EncBands[n]].Probabilities[1]; p = residual.Prob[WebpConstants.Vp8EncBands[n]].Probabilities[1];
} }
else else
{ {
@ -140,7 +140,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.BitWriter
this.PutBit(0, p.Probabilities[9]); this.PutBit(0, p.Probabilities[9]);
v -= 3 + (8 << 0); v -= 3 + (8 << 0);
mask = 1 << 2; mask = 1 << 2;
tab = WebPConstants.Cat3; tab = WebpConstants.Cat3;
} }
else if (v < 3 + (8 << 2)) else if (v < 3 + (8 << 2))
{ {
@ -149,7 +149,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.BitWriter
this.PutBit(1, p.Probabilities[9]); this.PutBit(1, p.Probabilities[9]);
v -= 3 + (8 << 1); v -= 3 + (8 << 1);
mask = 1 << 3; mask = 1 << 3;
tab = WebPConstants.Cat4; tab = WebpConstants.Cat4;
} }
else if (v < 3 + (8 << 3)) else if (v < 3 + (8 << 3))
{ {
@ -158,7 +158,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.BitWriter
this.PutBit(0, p.Probabilities[10]); this.PutBit(0, p.Probabilities[10]);
v -= 3 + (8 << 2); v -= 3 + (8 << 2);
mask = 1 << 4; mask = 1 << 4;
tab = WebPConstants.Cat5; tab = WebpConstants.Cat5;
} }
else else
{ {
@ -167,7 +167,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.BitWriter
this.PutBit(1, p.Probabilities[10]); this.PutBit(1, p.Probabilities[10]);
v -= 3 + (8 << 3); v -= 3 + (8 << 3);
mask = 1 << 10; mask = 1 << 10;
tab = WebPConstants.Cat6; tab = WebpConstants.Cat6;
} }
var tabIdx = 0; var tabIdx = 0;
@ -178,7 +178,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.BitWriter
} }
} }
p = residual.Prob[WebPConstants.Vp8EncBands[n]].Probabilities[2]; p = residual.Prob[WebpConstants.Vp8EncBands[n]].Probabilities[2];
} }
this.PutBitUniform(sign ? 1 : 0); this.PutBitUniform(sign ? 1 : 0);
@ -311,8 +311,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.BitWriter
if (this.range < 127) if (this.range < 127)
{ {
// emit 'shift' bits out and renormalize. // emit 'shift' bits out and renormalize.
int shift = WebPLookupTables.Norm[this.range]; int shift = WebpLookupTables.Norm[this.range];
this.range = WebPLookupTables.NewRange[this.range]; this.range = WebpLookupTables.NewRange[this.range];
this.value <<= shift; this.value <<= shift;
this.nbBits += shift; this.nbBits += shift;
if (this.nbBits > 0) if (this.nbBits > 0)
@ -339,7 +339,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.BitWriter
if (this.range < 127) if (this.range < 127)
{ {
this.range = WebPLookupTables.NewRange[this.range]; this.range = WebpLookupTables.NewRange[this.range];
this.value <<= 1; this.value <<= 1;
this.nbBits += 1; this.nbBits += 1;
if (this.nbBits > 0) if (this.nbBits > 0)
@ -420,14 +420,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.BitWriter
// Partition #0 with header and partition sizes // Partition #0 with header and partition sizes
uint size0 = this.GeneratePartition0(bitWriterPartZero); uint size0 = this.GeneratePartition0(bitWriterPartZero);
uint vp8Size = WebPConstants.Vp8FrameHeaderSize + size0; uint vp8Size = WebpConstants.Vp8FrameHeaderSize + size0;
vp8Size += numBytes; vp8Size += numBytes;
uint pad = vp8Size & 1; uint pad = vp8Size & 1;
vp8Size += pad; vp8Size += pad;
// Compute RIFF size // Compute RIFF size
// At the minimum it is: "WEBPVP8 nnnn" + VP8 data size. // At the minimum it is: "WEBPVP8 nnnn" + VP8 data size.
var riffSize = WebPConstants.TagSize + WebPConstants.ChunkHeaderSize + vp8Size; var riffSize = WebpConstants.TagSize + WebpConstants.ChunkHeaderSize + vp8Size;
// Emit headers and partition #0 // Emit headers and partition #0
this.WriteWebPHeaders(stream, size0, vp8Size, riffSize); this.WriteWebPHeaders(stream, size0, vp8Size, riffSize);
@ -474,12 +474,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.BitWriter
{ {
// We always use absolute values, not relative ones. // We always use absolute values, not relative ones.
bitWriter.PutBitUniform(1); // (segment_feature_mode = 1. Paragraph 9.3.) bitWriter.PutBitUniform(1); // (segment_feature_mode = 1. Paragraph 9.3.)
for (int s = 0; s < WebPConstants.NumMbSegments; ++s) for (int s = 0; s < WebpConstants.NumMbSegments; ++s)
{ {
bitWriter.PutSignedBits(this.enc.SegmentInfos[s].Quant, 7); bitWriter.PutSignedBits(this.enc.SegmentInfos[s].Quant, 7);
} }
for (int s = 0; s < WebPConstants.NumMbSegments; ++s) for (int s = 0; s < WebpConstants.NumMbSegments; ++s)
{ {
bitWriter.PutSignedBits(this.enc.SegmentInfos[s].FStrength, 6); bitWriter.PutSignedBits(this.enc.SegmentInfos[s].FStrength, 6);
} }
@ -535,17 +535,17 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.BitWriter
private void WriteProbas(Vp8BitWriter bitWriter) private void WriteProbas(Vp8BitWriter bitWriter)
{ {
Vp8EncProba probas = this.enc.Proba; Vp8EncProba probas = this.enc.Proba;
for (int t = 0; t < WebPConstants.NumTypes; ++t) for (int t = 0; t < WebpConstants.NumTypes; ++t)
{ {
for (int b = 0; b < WebPConstants.NumBands; ++b) for (int b = 0; b < WebpConstants.NumBands; ++b)
{ {
for (int c = 0; c < WebPConstants.NumCtx; ++c) for (int c = 0; c < WebpConstants.NumCtx; ++c)
{ {
for (int p = 0; p < WebPConstants.NumProbas; ++p) for (int p = 0; p < WebpConstants.NumProbas; ++p)
{ {
byte p0 = probas.Coeffs[t][b].Probabilities[c].Probabilities[p]; byte p0 = probas.Coeffs[t][b].Probabilities[c].Probabilities[p];
bool update = p0 != WebPLookupTables.DefaultCoeffsProba[t, b, c, p]; bool update = p0 != WebpLookupTables.DefaultCoeffsProba[t, b, c, p];
if (bitWriter.PutBit(update, WebPLookupTables.CoeffsUpdateProba[t, b, c, p])) if (bitWriter.PutBit(update, WebpLookupTables.CoeffsUpdateProba[t, b, c, p]))
{ {
bitWriter.PutBits(p0, 8); bitWriter.PutBits(p0, 8);
} }
@ -594,7 +594,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.BitWriter
int left = it.Preds[predIdx - 1]; int left = it.Preds[predIdx - 1];
for (int x = 0; x < 4; ++x) for (int x = 0; x < 4; ++x)
{ {
byte[] probas = WebPLookupTables.ModesProba[topPred[x], left]; byte[] probas = WebpLookupTables.ModesProba[topPred[x], left];
left = bitWriter.PutI4Mode(it.Preds[predIdx + x], probas); left = bitWriter.PutI4Mode(it.Preds[predIdx + x], probas);
} }
@ -617,9 +617,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.BitWriter
private void WriteVp8Header(Stream stream, uint size) private void WriteVp8Header(Stream stream, uint size)
{ {
Span<byte> vp8ChunkHeader = stackalloc byte[WebPConstants.ChunkHeaderSize]; Span<byte> vp8ChunkHeader = stackalloc byte[WebpConstants.ChunkHeaderSize];
WebPConstants.Vp8MagicBytes.AsSpan().CopyTo(vp8ChunkHeader); WebpConstants.Vp8MagicBytes.AsSpan().CopyTo(vp8ChunkHeader);
BinaryPrimitives.WriteUInt32LittleEndian(vp8ChunkHeader.Slice(4), size); BinaryPrimitives.WriteUInt32LittleEndian(vp8ChunkHeader.Slice(4), size);
stream.Write(vp8ChunkHeader); stream.Write(vp8ChunkHeader);
@ -630,7 +630,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.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]; var vp8FrameHeader = new byte[WebpConstants.Vp8FrameHeaderSize];
// Paragraph 9.1. // Paragraph 9.1.
uint bits = 0 // keyframe (1b) uint bits = 0 // keyframe (1b)
@ -643,9 +643,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.BitWriter
vp8FrameHeader[2] = (byte)((bits >> 16) & 0xff); vp8FrameHeader[2] = (byte)((bits >> 16) & 0xff);
// signature // signature
vp8FrameHeader[3] = WebPConstants.Vp8HeaderMagicBytes[0]; vp8FrameHeader[3] = WebpConstants.Vp8HeaderMagicBytes[0];
vp8FrameHeader[4] = WebPConstants.Vp8HeaderMagicBytes[1]; vp8FrameHeader[4] = WebpConstants.Vp8HeaderMagicBytes[1];
vp8FrameHeader[5] = WebPConstants.Vp8HeaderMagicBytes[2]; vp8FrameHeader[5] = WebpConstants.Vp8HeaderMagicBytes[2];
// dimensions // dimensions
vp8FrameHeader[6] = (byte)(width & 0xff); vp8FrameHeader[6] = (byte)(width & 0xff);

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

@ -4,9 +4,9 @@
using System; using System;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.IO; using System.IO;
using SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless; using SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.BitWriter namespace SixLabors.ImageSharp.Formats.Experimental.Webp.BitWriter
{ {
/// <summary> /// <summary>
/// A bit writer for writing lossless webp streams. /// A bit writer for writing lossless webp streams.
@ -143,14 +143,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.BitWriter
// Write RIFF header. // Write RIFF header.
uint pad = size & 1; uint pad = size & 1;
uint riffSize = WebPConstants.TagSize + WebPConstants.ChunkHeaderSize + size + pad; uint riffSize = WebpConstants.TagSize + WebpConstants.ChunkHeaderSize + size + pad;
this.WriteRiffHeader(stream, riffSize); this.WriteRiffHeader(stream, riffSize);
stream.Write(WebPConstants.Vp8LMagicBytes); stream.Write(WebpConstants.Vp8LMagicBytes);
// Write Vp8 Header. // Write Vp8 Header.
BinaryPrimitives.WriteUInt32LittleEndian(buffer, size); BinaryPrimitives.WriteUInt32LittleEndian(buffer, size);
stream.Write(buffer); stream.Write(buffer);
stream.WriteByte(WebPConstants.Vp8LHeaderMagicByte); stream.WriteByte(WebpConstants.Vp8LHeaderMagicByte);
this.WriteToStream(stream); this.WriteToStream(stream);
if (pad == 1) if (pad == 1)

2
src/ImageSharp/Formats/WebP/EntropyIx.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP namespace SixLabors.ImageSharp.Formats.Experimental.Webp
{ {
/// <summary> /// <summary>
/// These five modes are evaluated and their respective entropy is computed. /// These five modes are evaluated and their respective entropy is computed.

2
src/ImageSharp/Formats/WebP/HistoIx.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP namespace SixLabors.ImageSharp.Formats.Experimental.Webp
{ {
internal enum HistoIx internal enum HistoIx
{ {

4
src/ImageSharp/Formats/WebP/IWebPDecoderOptions.cs

@ -1,12 +1,12 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP namespace SixLabors.ImageSharp.Formats.Experimental.Webp
{ {
/// <summary> /// <summary>
/// Image decoder options for generating an image out of a webp stream. /// Image decoder options for generating an image out of a webp stream.
/// </summary> /// </summary>
internal interface IWebPDecoderOptions internal interface IWebpDecoderOptions
{ {
/// <summary> /// <summary>
/// Gets a value indicating whether the metadata should be ignored when the image is being decoded. /// Gets a value indicating whether the metadata should be ignored when the image is being decoded.

2
src/ImageSharp/Formats/WebP/IWebPEncoderOptions.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP namespace SixLabors.ImageSharp.Formats.Experimental.Webp
{ {
/// <summary> /// <summary>
/// Configuration options for use during webp encoding. /// Configuration options for use during webp encoding.

36
src/ImageSharp/Formats/WebP/ImageExtensions.cs

@ -1,36 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Experimental.WebP;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Saves the image to the given stream with the webp format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsWebp(this Image source, Stream stream) => SaveAsWebp(source, stream, null);
/// <summary>
/// Saves the image to the given stream with the webp format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="encoder">The options for the encoder.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsWebp(this Image source, Stream stream, WebPEncoder encoder) =>
source.Save(
stream,
encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(WebPFormat.Instance));
}
}

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

@ -4,7 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
internal class BackwardReferenceEncoder internal class BackwardReferenceEncoder
{ {
@ -129,9 +129,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
double entropyMin = MaxEntropy; double entropyMin = MaxEntropy;
int pos = 0; int pos = 0;
var colorCache = new ColorCache[WebPConstants.MaxColorCacheBits + 1]; var colorCache = new ColorCache[WebpConstants.MaxColorCacheBits + 1];
var histos = new Vp8LHistogram[WebPConstants.MaxColorCacheBits + 1]; var histos = new Vp8LHistogram[WebpConstants.MaxColorCacheBits + 1];
for (int i = 0; i <= WebPConstants.MaxColorCacheBits; i++) for (int i = 0; i <= WebpConstants.MaxColorCacheBits; i++)
{ {
histos[i] = new Vp8LHistogram(paletteCodeBits: i); histos[i] = new Vp8LHistogram(paletteCodeBits: i);
colorCache[i] = new ColorCache(); colorCache[i] = new ColorCache();
@ -166,7 +166,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
{ {
if (colorCache[i].Lookup(key) == pix) if (colorCache[i].Lookup(key) == pix)
{ {
++histos[i].Literal[WebPConstants.NumLiteralCodes + WebPConstants.NumLengthCodes + key]; ++histos[i].Literal[WebpConstants.NumLiteralCodes + WebpConstants.NumLengthCodes + key];
} }
else else
{ {
@ -244,7 +244,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.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); var 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;
@ -824,11 +824,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
int xOffset = dist - (yOffset * xSize); int xOffset = dist - (yOffset * xSize);
if (xOffset <= 8 && yOffset < 8) if (xOffset <= 8 && yOffset < 8)
{ {
return (int)WebPLookupTables.PlaneToCodeLut[(yOffset * 16) + 8 - xOffset] + 1; return (int)WebpLookupTables.PlaneToCodeLut[(yOffset * 16) + 8 - xOffset] + 1;
} }
else if (xOffset > xSize - 8 && yOffset < 7) else if (xOffset > xSize - 8 && yOffset < 7)
{ {
return (int)WebPLookupTables.PlaneToCodeLut[((yOffset + 1) * 16) + 8 + (xSize - xOffset)] + 1; return (int)WebpLookupTables.PlaneToCodeLut[((yOffset + 1) * 16) + 8 + (xSize - xOffset)] + 1;
} }
return dist + 120; return dist + 120;

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

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
/// <summary> /// <summary>
/// A small hash-addressed array to store recently used colors, to be able to recall them with shorter codes. /// A small hash-addressed array to store recently used colors, to be able to recall them with shorter codes.

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

@ -3,7 +3,7 @@
using System.Diagnostics; using System.Diagnostics;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
/// <summary> /// <summary>
/// The GetLengthCost(costModel, k) are cached in a CostCacheInterval. /// The GetLengthCost(costModel, k) are cached in a CostCacheInterval.

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

@ -3,7 +3,7 @@
using System.Diagnostics; using System.Diagnostics;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
/// <summary> /// <summary>
/// To perform backward reference every pixel at index index_ is considered and /// To perform backward reference every pixel at index index_ is considered and

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

@ -3,7 +3,7 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
/// <summary> /// <summary>
/// The CostManager is in charge of managing intervals and costs. /// The CostManager is in charge of managing intervals and costs.

8
src/ImageSharp/Formats/WebP/Lossless/CostModel.cs

@ -3,7 +3,7 @@
using System; using System;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
internal class CostModel internal class CostModel
{ {
@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
this.Alpha = new double[ValuesInBytes]; this.Alpha = new double[ValuesInBytes];
this.Red = new double[ValuesInBytes]; this.Red = new double[ValuesInBytes];
this.Blue = new double[ValuesInBytes]; this.Blue = new double[ValuesInBytes];
this.Distance = new double[WebPConstants.NumDistanceCodes]; this.Distance = new double[WebpConstants.NumDistanceCodes];
this.Literal = new double[literalArraySize]; this.Literal = new double[literalArraySize];
} }
@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
ConvertPopulationCountTableToBitEstimates(ValuesInBytes, histogram.Red, this.Red); ConvertPopulationCountTableToBitEstimates(ValuesInBytes, histogram.Red, this.Red);
ConvertPopulationCountTableToBitEstimates(ValuesInBytes, histogram.Blue, this.Blue); ConvertPopulationCountTableToBitEstimates(ValuesInBytes, histogram.Blue, this.Blue);
ConvertPopulationCountTableToBitEstimates(ValuesInBytes, histogram.Alpha, this.Alpha); ConvertPopulationCountTableToBitEstimates(ValuesInBytes, histogram.Alpha, this.Alpha);
ConvertPopulationCountTableToBitEstimates(WebPConstants.NumDistanceCodes, histogram.Distance, this.Distance); ConvertPopulationCountTableToBitEstimates(WebpConstants.NumDistanceCodes, histogram.Distance, this.Distance);
} }
public double GetLengthCost(int length) public double GetLengthCost(int length)
@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
public double GetCacheCost(uint idx) public double GetCacheCost(uint idx)
{ {
int literalIdx = (int)(ValuesInBytes + WebPConstants.NumLengthCodes + idx); int literalIdx = (int)(ValuesInBytes + WebpConstants.NumLengthCodes + idx);
return this.Literal[literalIdx]; return this.Literal[literalIdx];
} }

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

@ -3,7 +3,7 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
internal class CrunchConfig internal class CrunchConfig
{ {

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

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
internal class CrunchSubConfig internal class CrunchSubConfig
{ {

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

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
/// <summary> /// <summary>
/// Data container to keep track of cost range for the three dominant entropy symbols. /// Data container to keep track of cost range for the three dominant entropy symbols.

6
src/ImageSharp/Formats/WebP/Lossless/HTreeGroup.cs

@ -3,7 +3,7 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
/// <summary> /// <summary>
/// Huffman table group. /// Huffman table group.
@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
{ {
public HTreeGroup(uint packedTableSize) public HTreeGroup(uint packedTableSize)
{ {
this.HTrees = new List<HuffmanCode[]>(WebPConstants.HuffmanCodesPerMetaCode); this.HTrees = new List<HuffmanCode[]>(WebpConstants.HuffmanCodesPerMetaCode);
this.PackedTable = new HuffmanCode[packedTableSize]; this.PackedTable = new HuffmanCode[packedTableSize];
for (int i = 0; i < packedTableSize; i++) for (int i = 0; i < packedTableSize; i++)
{ {
@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
} }
/// <summary> /// <summary>
/// Gets the Huffman trees. This has a maximum of <see cref="WebPConstants.HuffmanCodesPerMetaCode" /> (5) entry's. /// Gets the Huffman trees. This has a maximum of <see cref="WebpConstants.HuffmanCodesPerMetaCode" /> (5) entry's.
/// </summary> /// </summary>
public List<HuffmanCode[]> HTrees { get; } public List<HuffmanCode[]> HTrees { get; }

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

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
internal struct HistogramBinInfo internal struct HistogramBinInfo
{ {

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

@ -6,7 +6,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
internal class HistogramEncoder internal class HistogramEncoder
{ {

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

@ -3,7 +3,7 @@
using System.Diagnostics; using System.Diagnostics;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
/// <summary> /// <summary>
/// Pair of histograms. Negative Idx1 value means that pair is out-of-date. /// Pair of histograms. Negative Idx1 value means that pair is out-of-date.

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

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
/// <summary> /// <summary>
/// Five Huffman codes are used at each meta code. /// Five Huffman codes are used at each meta code.

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

@ -3,7 +3,7 @@
using System.Diagnostics; using System.Diagnostics;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
/// <summary> /// <summary>
/// A classic way to do entropy coding where a smaller number of bits are used for more frequent codes. /// A classic way to do entropy coding where a smaller number of bits are used for more frequent codes.

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

@ -3,7 +3,7 @@
using System.Diagnostics; using System.Diagnostics;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
/// <summary> /// <summary>
/// Represents the Huffman tree. /// Represents the Huffman tree.

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

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
/// <summary> /// <summary>
/// Represents the tree codes (depth and bits array). /// Represents the tree codes (depth and bits array).

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

@ -3,7 +3,7 @@
using System.Diagnostics; using System.Diagnostics;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
/// <summary> /// <summary>
/// Holds the tree header in coded form. /// Holds the tree header in coded form.

26
src/ImageSharp/Formats/WebP/Lossless/HuffmanUtils.cs

@ -3,7 +3,7 @@
using System; using System;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
/// <summary> /// <summary>
/// Utility functions related to creating the huffman tables. /// Utility functions related to creating the huffman tables.
@ -312,14 +312,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
int totalSize = 1 << rootBits; // total size root table + 2nd level table. int totalSize = 1 << rootBits; // total size root table + 2nd level table.
int len; // current code length. int len; // current code length.
int symbol; // symbol index in original or sorted table. int symbol; // symbol index in original or sorted table.
var counts = new int[WebPConstants.MaxAllowedCodeLength + 1]; // number of codes of each length. var counts = new int[WebpConstants.MaxAllowedCodeLength + 1]; // number of codes of each length.
var offsets = new int[WebPConstants.MaxAllowedCodeLength + 1]; // offsets in sorted table for each length. var offsets = new int[WebpConstants.MaxAllowedCodeLength + 1]; // offsets in sorted table for each length.
// Build histogram of code lengths. // Build histogram of code lengths.
for (symbol = 0; symbol < codeLengthsSize; ++symbol) for (symbol = 0; symbol < codeLengthsSize; ++symbol)
{ {
var codeLengthOfSymbol = codeLengths[symbol]; var codeLengthOfSymbol = codeLengths[symbol];
if (codeLengthOfSymbol > WebPConstants.MaxAllowedCodeLength) if (codeLengthOfSymbol > WebpConstants.MaxAllowedCodeLength)
{ {
return 0; return 0;
} }
@ -335,7 +335,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
// Generate offsets into sorted symbol table by code length. // Generate offsets into sorted symbol table by code length.
offsets[1] = 0; offsets[1] = 0;
for (len = 1; len < WebPConstants.MaxAllowedCodeLength; ++len) for (len = 1; len < WebpConstants.MaxAllowedCodeLength; ++len)
{ {
int codesOfLength = counts[len]; int codesOfLength = counts[len];
if (codesOfLength > (1 << len)) if (codesOfLength > (1 << len))
@ -357,7 +357,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
} }
// Special case code with only one value. // Special case code with only one value.
if (offsets[WebPConstants.MaxAllowedCodeLength] == 1) if (offsets[WebpConstants.MaxAllowedCodeLength] == 1)
{ {
var huffmanCode = new HuffmanCode() var huffmanCode = new HuffmanCode()
{ {
@ -407,7 +407,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
// Fill in 2nd level tables and add pointers to root table. // Fill in 2nd level tables and add pointers to root table.
Span<HuffmanCode> tableSpan = table; Span<HuffmanCode> tableSpan = table;
int tablePos = 0; int tablePos = 0;
for (len = rootBits + 1, step = 2; len <= WebPConstants.MaxAllowedCodeLength; ++len, step <<= 1) for (len = rootBits + 1, step = 2; len <= WebpConstants.MaxAllowedCodeLength; ++len, step <<= 1)
{ {
numOpen <<= 1; numOpen <<= 1;
numNodes += numOpen; numNodes += numOpen;
@ -542,8 +542,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
private static void ConvertBitDepthsToSymbols(HuffmanTreeCode tree) private static void ConvertBitDepthsToSymbols(HuffmanTreeCode tree)
{ {
// 0 bit-depth means that the symbol does not exist. // 0 bit-depth means that the symbol does not exist.
uint[] nextCode = new uint[WebPConstants.MaxAllowedCodeLength + 1]; uint[] nextCode = new uint[WebpConstants.MaxAllowedCodeLength + 1];
int[] depthCount = new int[WebPConstants.MaxAllowedCodeLength + 1]; int[] depthCount = new int[WebpConstants.MaxAllowedCodeLength + 1];
int len = tree.NumSymbols; int len = tree.NumSymbols;
for (int i = 0; i < len; i++) for (int i = 0; i < len; i++)
@ -556,7 +556,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
nextCode[0] = 0; nextCode[0] = 0;
uint code = 0; uint code = 0;
for (int i = 1; i <= WebPConstants.MaxAllowedCodeLength; i++) for (int i = 1; i <= WebpConstants.MaxAllowedCodeLength; i++)
{ {
code = (uint)((code + depthCount[i - 1]) << 1); code = (uint)((code + depthCount[i - 1]) << 1);
nextCode[i] = code; nextCode[i] = code;
@ -589,11 +589,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
while (i < numBits) while (i < numBits)
{ {
i += 4; i += 4;
retval |= (uint)(reversedBits[bits & 0xf] << (WebPConstants.MaxAllowedCodeLength + 1 - i)); retval |= (uint)(reversedBits[bits & 0xf] << (WebpConstants.MaxAllowedCodeLength + 1 - i));
bits >>= 4; bits >>= 4;
} }
retval >>= WebPConstants.MaxAllowedCodeLength + 1 - numBits; retval >>= WebpConstants.MaxAllowedCodeLength + 1 - numBits;
return retval; return retval;
} }
@ -604,7 +604,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
private static int NextTableBitSize(int[] count, int len, int rootBits) private static int NextTableBitSize(int[] count, int len, int rootBits)
{ {
int left = 1 << (len - rootBits); int left = 1 << (len - rootBits);
while (len < WebPConstants.MaxAllowedCodeLength) while (len < WebpConstants.MaxAllowedCodeLength)
{ {
left -= count[len]; left -= count[len];
if (left <= 0) if (left <= 0)

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

@ -7,14 +7,14 @@ using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
/// <summary> /// <summary>
/// Utility functions for the lossless decoder. /// Utility functions for the lossless decoder.
/// </summary> /// </summary>
internal static unsafe class LosslessUtils internal static unsafe class LosslessUtils
{ {
private const uint Predictor0 = WebPConstants.ArgbBlack; private const uint Predictor0 = WebpConstants.ArgbBlack;
private const int PrefixLookupIdxMax = 512; private const int PrefixLookupIdxMax = 512;
@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
{ {
if (distance < PrefixLookupIdxMax) if (distance < PrefixLookupIdxMax)
{ {
(int code, int extraBits) prefixCode = WebPLookupTables.PrefixEncodeCode[distance]; (int code, int extraBits) prefixCode = WebpLookupTables.PrefixEncodeCode[distance];
extraBits = prefixCode.extraBits; extraBits = prefixCode.extraBits;
return prefixCode.code; return prefixCode.code;
} }
@ -82,9 +82,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
{ {
if (distance < PrefixLookupIdxMax) if (distance < PrefixLookupIdxMax)
{ {
(int code, int extraBits) prefixCode = WebPLookupTables.PrefixEncodeCode[distance]; (int code, int extraBits) prefixCode = WebpLookupTables.PrefixEncodeCode[distance];
extraBits = prefixCode.extraBits; extraBits = prefixCode.extraBits;
extraBitsValue = WebPLookupTables.PrefixEncodeExtraBitsValue[distance]; extraBitsValue = WebpLookupTables.PrefixEncodeExtraBitsValue[distance];
return prefixCode.code; return prefixCode.code;
} }
@ -510,7 +510,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
/// </summary> /// </summary>
public static float FastLog2(uint v) public static float FastLog2(uint v)
{ {
return (v < LogLookupIdxMax) ? WebPLookupTables.Log2Table[v] : FastLog2Slow(v); return (v < LogLookupIdxMax) ? WebpLookupTables.Log2Table[v] : FastLog2Slow(v);
} }
/// <summary> /// <summary>
@ -519,7 +519,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static float FastSLog2(uint v) public static float FastSLog2(uint v)
{ {
return (v < LogLookupIdxMax) ? WebPLookupTables.SLog2Table[v] : FastSLog2Slow(v); return (v < LogLookupIdxMax) ? WebpLookupTables.SLog2Table[v] : FastSLog2Slow(v);
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
@ -567,7 +567,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
// log2(1 + (v % y) / v) ~ LOG_2_RECIPROCAL * (v % y)/v // log2(1 + (v % y) / v) ~ LOG_2_RECIPROCAL * (v % y)/v
// LOG_2_RECIPROCAL ~ 23/16 // LOG_2_RECIPROCAL ~ 23/16
correction = (int)((23 * (origV & (y - 1))) >> 4); correction = (int)((23 * (origV & (y - 1))) >> 4);
return (vF * (WebPLookupTables.Log2Table[v] + logCnt)) + correction; return (vF * (WebpLookupTables.Log2Table[v] + logCnt)) + correction;
} }
else else
{ {
@ -591,7 +591,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
} }
while (v >= LogLookupIdxMax); while (v >= LogLookupIdxMax);
double log2 = WebPLookupTables.Log2Table[v] + logCnt; double log2 = WebpLookupTables.Log2Table[v] + logCnt;
if (origV >= ApproxLogMax) if (origV >= ApproxLogMax)
{ {
// Since the division is still expensive, add this correction factor only // Since the division is still expensive, add this correction factor only
@ -615,7 +615,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
/// </summary> /// </summary>
private static int PrefixEncodeBitsNoLut(int distance, ref int extraBits) private static int PrefixEncodeBitsNoLut(int distance, ref int extraBits)
{ {
int highestBit = WebPCommonUtils.BitsLog2Floor((uint)--distance); int highestBit = WebpCommonUtils.BitsLog2Floor((uint)--distance);
int secondHighestBit = (distance >> (highestBit - 1)) & 1; int secondHighestBit = (distance >> (highestBit - 1)) & 1;
extraBits = highestBit - 1; extraBits = highestBit - 1;
var code = (2 * highestBit) + secondHighestBit; var code = (2 * highestBit) + secondHighestBit;
@ -624,7 +624,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
private static int PrefixEncodeNoLUT(int distance, ref int extraBits, ref int extraBitsValue) private static int PrefixEncodeNoLUT(int distance, ref int extraBits, ref int extraBitsValue)
{ {
int highestBit = WebPCommonUtils.BitsLog2Floor((uint)--distance); int highestBit = WebpCommonUtils.BitsLog2Floor((uint)--distance);
int secondHighestBit = (distance >> (highestBit - 1)) & 1; int secondHighestBit = (distance >> (highestBit - 1)) & 1;
extraBits = highestBit - 1; extraBits = highestBit - 1;
extraBitsValue = distance & ((1 << extraBits) - 1); extraBitsValue = distance & ((1 << extraBits) - 1);
@ -637,7 +637,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
{ {
for (int x = 0; x < numberOfPixels; ++x) for (int x = 0; x < numberOfPixels; ++x)
{ {
output[x] = AddPixels(input[x], WebPConstants.ArgbBlack); output[x] = AddPixels(input[x], WebpConstants.ArgbBlack);
} }
} }
@ -848,7 +848,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
{ {
for (int i = 0; i < numPixels; ++i) for (int i = 0; i < numPixels; ++i)
{ {
output[i] = SubPixels(input[i], WebPConstants.ArgbBlack); output[i] = SubPixels(input[i], WebpConstants.ArgbBlack);
} }
} }

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

@ -3,7 +3,7 @@
using System.Diagnostics; using System.Diagnostics;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
[DebuggerDisplay("Mode: {Mode}, Len: {Len}, BgraOrDistance: {BgraOrDistance}")] [DebuggerDisplay("Mode: {Mode}, Len: {Len}, BgraOrDistance: {BgraOrDistance}")]
internal class PixOrCopy internal class PixOrCopy

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

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
internal enum PixOrCopyMode internal enum PixOrCopyMode
{ {

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

@ -5,7 +5,7 @@ using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
/// <summary> /// <summary>
/// Image transform methods for the lossless webp encoder. /// Image transform methods for the lossless webp encoder.
@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
usedSubtractGreen, usedSubtractGreen,
image); image);
image[(tileY * tilesPerRow) + tileX] = (uint)(WebPConstants.ArgbBlack | (pred << 8)); image[(tileY * tilesPerRow) + tileX] = (uint)(WebpConstants.ArgbBlack | (pred << 8));
} }
} }
@ -199,7 +199,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.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]; var residuals = new uint[1 << WebpConstants.MaxTransformBits];
var histoArgb = new int[4][]; var histoArgb = new int[4][];
var bestHisto = new int[4][]; var bestHisto = new int[4][];
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
@ -329,7 +329,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
uint residual; uint residual;
if (y == 0) if (y == 0)
{ {
predict = (x == 0) ? WebPConstants.ArgbBlack : currentRow[x - 1]; // Left. predict = (x == 0) ? WebpConstants.ArgbBlack : currentRow[x - 1]; // Left.
} }
else if (x == 0) else if (x == 0)
{ {
@ -340,7 +340,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
switch (mode) switch (mode)
{ {
case 0: case 0:
predict = WebPConstants.ArgbBlack; predict = WebpConstants.ArgbBlack;
break; break;
case 1: case 1:
predict = currentRow[x - 1]; predict = currentRow[x - 1];

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

@ -3,7 +3,7 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
internal class Vp8LBackwardRefs internal class Vp8LBackwardRefs
{ {

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

@ -3,7 +3,7 @@
using System; using System;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
/// <summary> /// <summary>
/// Holds bit entropy results and entropy-related functions. /// Holds bit entropy results and entropy-related functions.

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

@ -6,7 +6,7 @@ using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
/// <summary> /// <summary>
/// Holds information for decoding a lossless webp image. /// Holds information for decoding a lossless webp image.

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

@ -8,11 +8,11 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Experimental.WebP.BitWriter; using SixLabors.ImageSharp.Formats.Experimental.Webp.BitWriter;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
/// <summary> /// <summary>
/// Encoder for lossless webp images. /// Encoder for lossless webp images.
@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
this.method = Numerics.Clamp(method, 0, 6); this.method = Numerics.Clamp(method, 0, 6);
this.bitWriter = new Vp8LBitWriter(initialSize); this.bitWriter = new Vp8LBitWriter(initialSize);
this.Bgra = memoryAllocator.Allocate<uint>(pixelCount); this.Bgra = memoryAllocator.Allocate<uint>(pixelCount);
this.Palette = memoryAllocator.Allocate<uint>(WebPConstants.MaxPaletteSize); this.Palette = memoryAllocator.Allocate<uint>(WebpConstants.MaxPaletteSize);
this.Refs = new Vp8LBackwardRefs[3]; this.Refs = new Vp8LBackwardRefs[3];
this.HashChain = new Vp8LHashChain(pixelCount); this.HashChain = new Vp8LHashChain(pixelCount);
this.memoryAllocator = memoryAllocator; this.memoryAllocator = memoryAllocator;
@ -195,14 +195,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
/// <param name="inputImgHeight">The input image height.</param> /// <param name="inputImgHeight">The input image height.</param>
private void WriteImageSize(int inputImgWidth, int inputImgHeight) private void WriteImageSize(int inputImgWidth, int inputImgHeight)
{ {
Guard.MustBeLessThan(inputImgWidth, WebPConstants.MaxDimension, nameof(inputImgWidth)); Guard.MustBeLessThan(inputImgWidth, WebpConstants.MaxDimension, nameof(inputImgWidth));
Guard.MustBeLessThan(inputImgHeight, WebPConstants.MaxDimension, nameof(inputImgHeight)); Guard.MustBeLessThan(inputImgHeight, WebpConstants.MaxDimension, nameof(inputImgHeight));
uint width = (uint)inputImgWidth - 1; uint width = (uint)inputImgWidth - 1;
uint height = (uint)inputImgHeight - 1; uint height = (uint)inputImgHeight - 1;
this.bitWriter.PutBits(width, WebPConstants.Vp8LImageSizeBits); this.bitWriter.PutBits(width, WebpConstants.Vp8LImageSizeBits);
this.bitWriter.PutBits(height, WebPConstants.Vp8LImageSizeBits); this.bitWriter.PutBits(height, WebpConstants.Vp8LImageSizeBits);
} }
/// <summary> /// <summary>
@ -212,7 +212,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
private void WriteAlphaAndVersion(bool hasAlpha) private void WriteAlphaAndVersion(bool hasAlpha)
{ {
this.bitWriter.PutBits(hasAlpha ? 1U : 0, 1); this.bitWriter.PutBits(hasAlpha ? 1U : 0, 1);
this.bitWriter.PutBits(WebPConstants.Vp8LVersion, WebPConstants.Vp8LVersionBits); this.bitWriter.PutBits(WebpConstants.Vp8LVersion, WebpConstants.Vp8LVersionBits);
} }
/// <summary> /// <summary>
@ -270,9 +270,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
this.MapImageFromPalette(width, height); this.MapImageFromPalette(width, height);
// If using a color cache, do not have it bigger than the number of colors. // If using a color cache, do not have it bigger than the number of colors.
if (useCache && this.PaletteSize < (1 << WebPConstants.MaxColorCacheBits)) if (useCache && this.PaletteSize < (1 << WebpConstants.MaxColorCacheBits))
{ {
this.CacheBits = WebPCommonUtils.BitsLog2Floor((uint)this.PaletteSize) + 1; this.CacheBits = WebpCommonUtils.BitsLog2Floor((uint)this.PaletteSize) + 1;
} }
} }
@ -399,7 +399,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
{ {
int histogramImageXySize = LosslessUtils.SubSampleSize(width, histogramBits) * LosslessUtils.SubSampleSize(height, histogramBits); int histogramImageXySize = LosslessUtils.SubSampleSize(width, histogramBits) * LosslessUtils.SubSampleSize(height, histogramBits);
var histogramSymbols = new ushort[histogramImageXySize]; var histogramSymbols = new ushort[histogramImageXySize];
var huffTree = new HuffmanTree[3 * WebPConstants.CodeLengthCodes]; var huffTree = new HuffmanTree[3 * WebpConstants.CodeLengthCodes];
for (int i = 0; i < huffTree.Length; i++) for (int i = 0; i < huffTree.Length; i++)
{ {
huffTree[i] = default; huffTree[i] = default;
@ -410,7 +410,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
if (cacheBits == 0) if (cacheBits == 0)
{ {
// TODO: not sure if this should be 10 or 11. Original code comment says "The maximum allowed limit is 11.", but the value itself is 10. // TODO: not sure if this should be 10 or 11. Original code comment says "The maximum allowed limit is 11.", but the value itself is 10.
cacheBits = WebPConstants.MaxColorCacheBits; cacheBits = WebpConstants.MaxColorCacheBits;
} }
} }
else else
@ -543,10 +543,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
/// </summary> /// </summary>
private void EncodePalette() private void EncodePalette()
{ {
Span<uint> tmpPalette = new uint[WebPConstants.MaxPaletteSize]; Span<uint> tmpPalette = new uint[WebpConstants.MaxPaletteSize];
int paletteSize = this.PaletteSize; int paletteSize = this.PaletteSize;
Span<uint> palette = this.Palette.Memory.Span; Span<uint> palette = this.Palette.Memory.Span;
this.bitWriter.PutBits(WebPConstants.TransformPresent, 1); this.bitWriter.PutBits(WebpConstants.TransformPresent, 1);
this.bitWriter.PutBits((uint)Vp8LTransformType.ColorIndexingTransform, 2); this.bitWriter.PutBits((uint)Vp8LTransformType.ColorIndexingTransform, 2);
this.bitWriter.PutBits((uint)paletteSize - 1, 8); this.bitWriter.PutBits((uint)paletteSize - 1, 8);
for (int i = paletteSize - 1; i >= 1; i--) for (int i = paletteSize - 1; i >= 1; i--)
@ -565,7 +565,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
/// <param name="height">The height of the image.</param> /// <param name="height">The height of the image.</param>
private void ApplySubtractGreen(int width, int height) private void ApplySubtractGreen(int width, int height)
{ {
this.bitWriter.PutBits(WebPConstants.TransformPresent, 1); this.bitWriter.PutBits(WebpConstants.TransformPresent, 1);
this.bitWriter.PutBits((uint)Vp8LTransformType.SubtractGreen, 2); this.bitWriter.PutBits((uint)Vp8LTransformType.SubtractGreen, 2);
LosslessUtils.SubtractGreenFromBlueAndRed(this.Bgra.GetSpan(), width * height); LosslessUtils.SubtractGreenFromBlueAndRed(this.Bgra.GetSpan(), width * height);
} }
@ -580,7 +580,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
PredictorEncoder.ResidualImage(width, height, predBits, this.Bgra.GetSpan(), this.BgraScratch.GetSpan(), this.TransformData.GetSpan(), nearLosslessStrength, exact, usedSubtractGreen); PredictorEncoder.ResidualImage(width, height, predBits, this.Bgra.GetSpan(), this.BgraScratch.GetSpan(), this.TransformData.GetSpan(), nearLosslessStrength, exact, usedSubtractGreen);
this.bitWriter.PutBits(WebPConstants.TransformPresent, 1); this.bitWriter.PutBits(WebpConstants.TransformPresent, 1);
this.bitWriter.PutBits((uint)Vp8LTransformType.PredictorTransform, 2); this.bitWriter.PutBits((uint)Vp8LTransformType.PredictorTransform, 2);
this.bitWriter.PutBits((uint)(predBits - 2), 3); this.bitWriter.PutBits((uint)(predBits - 2), 3);
@ -595,7 +595,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
PredictorEncoder.ColorSpaceTransform(width, height, colorTransformBits, this.quality, this.Bgra.GetSpan(), this.TransformData.GetSpan()); PredictorEncoder.ColorSpaceTransform(width, height, colorTransformBits, this.quality, this.Bgra.GetSpan(), this.TransformData.GetSpan());
this.bitWriter.PutBits(WebPConstants.TransformPresent, 1); this.bitWriter.PutBits(WebpConstants.TransformPresent, 1);
this.bitWriter.PutBits((uint)Vp8LTransformType.CrossColorTransform, 2); this.bitWriter.PutBits((uint)Vp8LTransformType.CrossColorTransform, 2);
this.bitWriter.PutBits((uint)(colorTransformBits - 2), 3); this.bitWriter.PutBits((uint)(colorTransformBits - 2), 3);
@ -613,7 +613,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
huffmanCodes[i] = default; huffmanCodes[i] = default;
} }
var huffTree = new HuffmanTree[3UL * WebPConstants.CodeLengthCodes]; var huffTree = new HuffmanTree[3UL * WebpConstants.CodeLengthCodes];
for (int i = 0; i < huffTree.Length; i++) for (int i = 0; i < huffTree.Length; i++)
{ {
huffTree[i] = default; huffTree[i] = default;
@ -732,19 +732,19 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
private void StoreFullHuffmanCode(HuffmanTree[] huffTree, HuffmanTreeToken[] tokens, HuffmanTreeCode tree) private void StoreFullHuffmanCode(HuffmanTree[] huffTree, HuffmanTreeToken[] tokens, HuffmanTreeCode tree)
{ {
int i; int i;
var codeLengthBitDepth = new byte[WebPConstants.CodeLengthCodes]; var codeLengthBitDepth = new byte[WebpConstants.CodeLengthCodes];
var codeLengthBitDepthSymbols = new short[WebPConstants.CodeLengthCodes]; var codeLengthBitDepthSymbols = new short[WebpConstants.CodeLengthCodes];
var huffmanCode = new HuffmanTreeCode var huffmanCode = new HuffmanTreeCode
{ {
NumSymbols = WebPConstants.CodeLengthCodes, NumSymbols = WebpConstants.CodeLengthCodes,
CodeLengths = codeLengthBitDepth, CodeLengths = codeLengthBitDepth,
Codes = codeLengthBitDepthSymbols Codes = codeLengthBitDepthSymbols
}; };
this.bitWriter.PutBits(0, 1); this.bitWriter.PutBits(0, 1);
var numTokens = HuffmanUtils.CreateCompressedHuffmanTree(tree, tokens); var numTokens = HuffmanUtils.CreateCompressedHuffmanTree(tree, tokens);
var histogram = new uint[WebPConstants.CodeLengthCodes + 1]; var histogram = new uint[WebpConstants.CodeLengthCodes + 1];
var bufRle = new bool[WebPConstants.CodeLengthCodes + 1]; var bufRle = new bool[WebpConstants.CodeLengthCodes + 1];
for (i = 0; i < numTokens; i++) for (i = 0; i < numTokens; i++)
{ {
histogram[tokens[i].Code]++; histogram[tokens[i].Code]++;
@ -790,7 +790,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
} }
else else
{ {
int nBits = WebPCommonUtils.BitsLog2Floor((uint)trimmedLength - 2); int nBits = WebpCommonUtils.BitsLog2Floor((uint)trimmedLength - 2);
int nBitPairs = (nBits / 2) + 1; int nBitPairs = (nBits / 2) + 1;
this.bitWriter.PutBits((uint)nBitPairs - 1, 3); this.bitWriter.PutBits((uint)nBitPairs - 1, 3);
this.bitWriter.PutBits((uint)trimmedLength - 2, nBitPairs * 2); this.bitWriter.PutBits((uint)trimmedLength - 2, nBitPairs * 2);
@ -830,7 +830,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
byte[] storageOrder = { 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; byte[] storageOrder = { 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
// Throw away trailing zeros: // Throw away trailing zeros:
int codesToStore = WebPConstants.CodeLengthCodes; int codesToStore = WebpConstants.CodeLengthCodes;
for (; codesToStore > 4; codesToStore--) for (; codesToStore > 4; codesToStore--)
{ {
if (codeLengthBitDepth[storageOrder[codesToStore - 1]] != 0) if (codeLengthBitDepth[storageOrder[codesToStore - 1]] != 0)
@ -882,7 +882,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
else if (v.IsCacheIdx()) else if (v.IsCacheIdx())
{ {
int code = (int)v.CacheIdx(); int code = (int)v.CacheIdx();
int literalIx = 256 + WebPConstants.NumLengthCodes + code; int literalIx = 256 + WebpConstants.NumLengthCodes + code;
this.bitWriter.WriteHuffmanCode(codes[0], literalIx); this.bitWriter.WriteHuffmanCode(codes[0], literalIx);
} }
else else
@ -1084,7 +1084,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
{ {
Span<uint> palette = this.Palette.Memory.Span; Span<uint> palette = this.Palette.Memory.Span;
this.PaletteSize = this.GetColorPalette(image, palette); this.PaletteSize = this.GetColorPalette(image, palette);
if (this.PaletteSize > WebPConstants.MaxPaletteSize) if (this.PaletteSize > WebpConstants.MaxPaletteSize)
{ {
this.PaletteSize = 0; this.PaletteSize = 0;
return false; return false;
@ -1119,10 +1119,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
for (int x = 0; x < rowSpan.Length; x++) for (int x = 0; x < rowSpan.Length; x++)
{ {
colors.Add(rowSpan[x]); colors.Add(rowSpan[x]);
if (colors.Count > WebPConstants.MaxPaletteSize) if (colors.Count > WebpConstants.MaxPaletteSize)
{ {
// Exact count is not needed, because a palette will not be used then anyway. // Exact count is not needed, because a palette will not be used then anyway.
return WebPConstants.MaxPaletteSize + 1; return WebpConstants.MaxPaletteSize + 1;
} }
} }
} }
@ -1464,7 +1464,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
{ {
int numSymbols = int numSymbols =
(k == 0) ? histo.NumCodes() : (k == 0) ? histo.NumCodes() :
(k == 4) ? WebPConstants.NumDistanceCodes : 256; (k == 4) ? WebpConstants.NumDistanceCodes : 256;
huffmanCodes[startIdx + k].NumSymbols = numSymbols; huffmanCodes[startIdx + k].NumSymbols = numSymbols;
totalLengthSize += numSymbols; totalLengthSize += numSymbols;
} }
@ -1532,7 +1532,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
while (true) while (true)
{ {
int huffImageSize = LosslessUtils.SubSampleSize(width, histoBits) * LosslessUtils.SubSampleSize(height, histoBits); int huffImageSize = LosslessUtils.SubSampleSize(width, histoBits) * LosslessUtils.SubSampleSize(height, histoBits);
if (huffImageSize <= WebPConstants.MaxHuffImageSize) if (huffImageSize <= WebpConstants.MaxHuffImageSize)
{ {
break; break;
} }
@ -1540,8 +1540,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
histoBits++; histoBits++;
} }
return (histoBits < WebPConstants.MinHuffmanBits) ? WebPConstants.MinHuffmanBits : return (histoBits < WebpConstants.MinHuffmanBits) ? WebpConstants.MinHuffmanBits :
(histoBits > WebPConstants.MaxHuffmanBits) ? WebPConstants.MaxHuffmanBits : histoBits; (histoBits > WebpConstants.MaxHuffmanBits) ? WebpConstants.MaxHuffmanBits : histoBits;
} }
/// <summary> /// <summary>

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

@ -6,7 +6,7 @@ using System.Buffers;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
internal class Vp8LHashChain internal class Vp8LHashChain
{ {

68
src/ImageSharp/Formats/WebP/Lossless/Vp8LHistogram.cs

@ -5,7 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
internal class Vp8LHistogram : IDeepCloneable internal class Vp8LHistogram : IDeepCloneable
{ {
@ -66,12 +66,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
public Vp8LHistogram(int paletteCodeBits) public Vp8LHistogram(int paletteCodeBits)
{ {
this.PaletteCodeBits = paletteCodeBits; this.PaletteCodeBits = paletteCodeBits;
this.Red = new uint[WebPConstants.NumLiteralCodes + 1]; this.Red = new uint[WebpConstants.NumLiteralCodes + 1];
this.Blue = new uint[WebPConstants.NumLiteralCodes + 1]; this.Blue = new uint[WebpConstants.NumLiteralCodes + 1];
this.Alpha = new uint[WebPConstants.NumLiteralCodes + 1]; this.Alpha = new uint[WebpConstants.NumLiteralCodes + 1];
this.Distance = new uint[WebPConstants.NumDistanceCodes]; this.Distance = new uint[WebpConstants.NumDistanceCodes];
var literalSize = WebPConstants.NumLiteralCodes + WebPConstants.NumLengthCodes + (1 << WebPConstants.MaxColorCacheBits); var literalSize = WebpConstants.NumLiteralCodes + WebpConstants.NumLengthCodes + (1 << WebpConstants.MaxColorCacheBits);
this.Literal = new uint[literalSize + 1]; this.Literal = new uint[literalSize + 1];
// 5 for literal, red, blue, alpha, distance. // 5 for literal, red, blue, alpha, distance.
@ -150,14 +150,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
} }
else if (v.IsCacheIdx()) else if (v.IsCacheIdx())
{ {
int literalIx = (int)(WebPConstants.NumLiteralCodes + WebPConstants.NumLengthCodes + v.CacheIdx()); int literalIx = (int)(WebpConstants.NumLiteralCodes + WebpConstants.NumLengthCodes + v.CacheIdx());
this.Literal[literalIx]++; this.Literal[literalIx]++;
} }
else else
{ {
int extraBits = 0; int extraBits = 0;
int code = LosslessUtils.PrefixEncodeBits(v.Length(), ref extraBits); int code = LosslessUtils.PrefixEncodeBits(v.Length(), ref extraBits);
this.Literal[WebPConstants.NumLiteralCodes + code]++; this.Literal[WebpConstants.NumLiteralCodes + code]++;
if (!useDistanceModifier) if (!useDistanceModifier)
{ {
code = LosslessUtils.PrefixEncodeBits((int)v.Distance(), ref extraBits); code = LosslessUtils.PrefixEncodeBits((int)v.Distance(), ref extraBits);
@ -173,7 +173,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
public int NumCodes() public int NumCodes()
{ {
return WebPConstants.NumLiteralCodes + WebPConstants.NumLengthCodes + ((this.PaletteCodeBits > 0) ? (1 << this.PaletteCodeBits) : 0); return WebpConstants.NumLiteralCodes + WebpConstants.NumLengthCodes + ((this.PaletteCodeBits > 0) ? (1 << this.PaletteCodeBits) : 0);
} }
/// <summary> /// <summary>
@ -185,24 +185,24 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
uint notUsed = 0; uint notUsed = 0;
return return
PopulationCost(this.Literal, this.NumCodes(), ref notUsed, ref this.IsUsed[0]) PopulationCost(this.Literal, this.NumCodes(), ref notUsed, ref this.IsUsed[0])
+ PopulationCost(this.Red, WebPConstants.NumLiteralCodes, ref notUsed, ref this.IsUsed[1]) + PopulationCost(this.Red, WebpConstants.NumLiteralCodes, ref notUsed, ref this.IsUsed[1])
+ PopulationCost(this.Blue, WebPConstants.NumLiteralCodes, ref notUsed, ref this.IsUsed[2]) + PopulationCost(this.Blue, WebpConstants.NumLiteralCodes, ref notUsed, ref this.IsUsed[2])
+ PopulationCost(this.Alpha, WebPConstants.NumLiteralCodes, ref notUsed, ref this.IsUsed[3]) + PopulationCost(this.Alpha, WebpConstants.NumLiteralCodes, ref notUsed, ref this.IsUsed[3])
+ PopulationCost(this.Distance, WebPConstants.NumDistanceCodes, ref notUsed, ref this.IsUsed[4]) + PopulationCost(this.Distance, WebpConstants.NumDistanceCodes, ref notUsed, ref this.IsUsed[4])
+ ExtraCost(this.Literal.AsSpan(WebPConstants.NumLiteralCodes), WebPConstants.NumLengthCodes) + ExtraCost(this.Literal.AsSpan(WebpConstants.NumLiteralCodes), WebpConstants.NumLengthCodes)
+ ExtraCost(this.Distance, WebPConstants.NumDistanceCodes); + ExtraCost(this.Distance, WebpConstants.NumDistanceCodes);
} }
public void UpdateHistogramCost() public void UpdateHistogramCost()
{ {
uint alphaSym = 0, redSym = 0, blueSym = 0; uint alphaSym = 0, redSym = 0, blueSym = 0;
uint notUsed = 0; uint notUsed = 0;
double alphaCost = PopulationCost(this.Alpha, WebPConstants.NumLiteralCodes, ref alphaSym, ref this.IsUsed[3]); double alphaCost = PopulationCost(this.Alpha, WebpConstants.NumLiteralCodes, ref alphaSym, ref this.IsUsed[3]);
double distanceCost = PopulationCost(this.Distance, WebPConstants.NumDistanceCodes, ref notUsed, ref this.IsUsed[4]) + ExtraCost(this.Distance, WebPConstants.NumDistanceCodes); double distanceCost = PopulationCost(this.Distance, WebpConstants.NumDistanceCodes, ref notUsed, ref this.IsUsed[4]) + ExtraCost(this.Distance, WebpConstants.NumDistanceCodes);
int numCodes = this.NumCodes(); int numCodes = this.NumCodes();
this.LiteralCost = PopulationCost(this.Literal, numCodes, ref notUsed, ref this.IsUsed[0]) + ExtraCost(this.Literal.AsSpan(WebPConstants.NumLiteralCodes), WebPConstants.NumLengthCodes); this.LiteralCost = PopulationCost(this.Literal, numCodes, ref notUsed, ref this.IsUsed[0]) + ExtraCost(this.Literal.AsSpan(WebpConstants.NumLiteralCodes), WebpConstants.NumLengthCodes);
this.RedCost = PopulationCost(this.Red, WebPConstants.NumLiteralCodes, ref redSym, ref this.IsUsed[1]); this.RedCost = PopulationCost(this.Red, WebpConstants.NumLiteralCodes, ref redSym, ref this.IsUsed[1]);
this.BlueCost = PopulationCost(this.Blue, WebPConstants.NumLiteralCodes, ref blueSym, ref this.IsUsed[2]); this.BlueCost = PopulationCost(this.Blue, WebpConstants.NumLiteralCodes, ref blueSym, ref this.IsUsed[2]);
this.BitCost = this.LiteralCost + this.RedCost + this.BlueCost + alphaCost + distanceCost; this.BitCost = this.LiteralCost + this.RedCost + this.BlueCost + alphaCost + distanceCost;
if ((alphaSym | redSym | blueSym) == NonTrivialSym) if ((alphaSym | redSym | blueSym) == NonTrivialSym)
{ {
@ -247,10 +247,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
int literalSize = this.NumCodes(); int literalSize = this.NumCodes();
this.AddLiteral(b, output, literalSize); this.AddLiteral(b, output, literalSize);
this.AddRed(b, output, WebPConstants.NumLiteralCodes); this.AddRed(b, output, WebpConstants.NumLiteralCodes);
this.AddBlue(b, output, WebPConstants.NumLiteralCodes); this.AddBlue(b, output, WebpConstants.NumLiteralCodes);
this.AddAlpha(b, output, WebPConstants.NumLiteralCodes); this.AddAlpha(b, output, WebpConstants.NumLiteralCodes);
this.AddDistance(b, output, WebPConstants.NumDistanceCodes); this.AddDistance(b, output, WebpConstants.NumDistanceCodes);
for (int i = 0; i < 5; i++) for (int i = 0; i < 5; i++)
{ {
@ -269,7 +269,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
cost += GetCombinedEntropy(this.Literal, b.Literal, this.NumCodes(), this.IsUsed[0], b.IsUsed[0], false); cost += GetCombinedEntropy(this.Literal, b.Literal, this.NumCodes(), this.IsUsed[0], b.IsUsed[0], false);
cost += ExtraCostCombined(this.Literal.AsSpan(WebPConstants.NumLiteralCodes), b.Literal.AsSpan(WebPConstants.NumLiteralCodes), WebPConstants.NumLengthCodes); cost += ExtraCostCombined(this.Literal.AsSpan(WebpConstants.NumLiteralCodes), b.Literal.AsSpan(WebpConstants.NumLiteralCodes), WebpConstants.NumLengthCodes);
if (cost > costThreshold) if (cost > costThreshold)
{ {
@ -290,31 +290,31 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
} }
} }
cost += GetCombinedEntropy(this.Red, b.Red, WebPConstants.NumLiteralCodes, this.IsUsed[1], b.IsUsed[1], trivialAtEnd); cost += GetCombinedEntropy(this.Red, b.Red, WebpConstants.NumLiteralCodes, this.IsUsed[1], b.IsUsed[1], trivialAtEnd);
if (cost > costThreshold) if (cost > costThreshold)
{ {
return false; return false;
} }
cost += GetCombinedEntropy(this.Blue, b.Blue, WebPConstants.NumLiteralCodes, this.IsUsed[2], b.IsUsed[2], trivialAtEnd); cost += GetCombinedEntropy(this.Blue, b.Blue, WebpConstants.NumLiteralCodes, this.IsUsed[2], b.IsUsed[2], trivialAtEnd);
if (cost > costThreshold) if (cost > costThreshold)
{ {
return false; return false;
} }
cost += GetCombinedEntropy(this.Alpha, b.Alpha, WebPConstants.NumLiteralCodes, this.IsUsed[3], b.IsUsed[3], trivialAtEnd); cost += GetCombinedEntropy(this.Alpha, b.Alpha, WebpConstants.NumLiteralCodes, this.IsUsed[3], b.IsUsed[3], trivialAtEnd);
if (cost > costThreshold) if (cost > costThreshold)
{ {
return false; return false;
} }
cost += GetCombinedEntropy(this.Distance, b.Distance, WebPConstants.NumDistanceCodes, this.IsUsed[4], b.IsUsed[4], false); cost += GetCombinedEntropy(this.Distance, b.Distance, WebpConstants.NumDistanceCodes, this.IsUsed[4], b.IsUsed[4], false);
if (cost > costThreshold) if (cost > costThreshold)
{ {
return false; return false;
} }
cost += ExtraCostCombined(this.Distance, b.Distance, WebPConstants.NumDistanceCodes); cost += ExtraCostCombined(this.Distance, b.Distance, WebpConstants.NumDistanceCodes);
if (cost > costThreshold) if (cost > costThreshold)
{ {
return false; return false;
@ -331,7 +331,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
{ {
var output = new short[16]; var output = new short[16];
this.Vp8FTransform(reference.Slice(WebPLookupTables.Vp8DspScan[j]), pred.Slice(WebPLookupTables.Vp8DspScan[j]), output); this.Vp8FTransform(reference.Slice(WebpLookupTables.Vp8DspScan[j]), pred.Slice(WebpLookupTables.Vp8DspScan[j]), output);
// Convert coefficients to bin. // Convert coefficients to bin.
for (int k = 0; k < 16; ++k) for (int k = 0; k < 16; ++k)
@ -352,7 +352,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
// for handling the useful small values which contribute most. // for handling the useful small values which contribute most.
int maxValue = this.maxValue; int maxValue = this.maxValue;
int lastNonZero = this.lastNonZero; int lastNonZero = this.lastNonZero;
int alpha = (maxValue > 1) ? WebPConstants.AlphaScale * lastNonZero / maxValue : 0; int alpha = (maxValue > 1) ? WebpConstants.AlphaScale * lastNonZero / maxValue : 0;
return alpha; return alpha;
} }
@ -400,8 +400,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
// Do not change the span in the last iteration. // Do not change the span in the last iteration.
if (i < 3) if (i < 3)
{ {
src = src.Slice(WebPConstants.Bps); src = src.Slice(WebpConstants.Bps);
reference = reference.Slice(WebPConstants.Bps); reference = reference.Slice(WebpConstants.Bps);
} }
} }

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

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
internal enum Vp8LLz77Type internal enum Vp8LLz77Type
{ {

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

@ -3,7 +3,7 @@
using System.Buffers; using System.Buffers;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
internal class Vp8LMetadata internal class Vp8LMetadata
{ {

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

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
internal struct Vp8LMultipliers internal struct Vp8LMultipliers
{ {

4
src/ImageSharp/Formats/WebP/Lossless/Vp8LStreaks.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
internal class Vp8LStreaks internal class Vp8LStreaks
{ {
@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
private static double InitialHuffmanCost() private static double InitialHuffmanCost()
{ {
// Small bias because Huffman code length is typically not stored in full length. // Small bias because Huffman code length is typically not stored in full length.
int huffmanCodeOfHuffmanCodeSize = WebPConstants.CodeLengthCodes * 3; int huffmanCodeOfHuffmanCodeSize = WebpConstants.CodeLengthCodes * 3;
double smallBias = 9.1; double smallBias = 9.1;
return huffmanCodeOfHuffmanCodeSize - smallBias; return huffmanCodeOfHuffmanCodeSize - smallBias;
} }

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

@ -4,7 +4,7 @@
using System.Buffers; using System.Buffers;
using System.Diagnostics; using System.Diagnostics;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
/// <summary> /// <summary>
/// Data associated with a VP8L transformation to reduce the entropy. /// Data associated with a VP8L transformation to reduce the entropy.

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

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
/// <summary> /// <summary>
/// Enum for the different transform types. Transformations are reversible manipulations of the image data /// Enum for the different transform types. Transformations are reversible manipulations of the image data

76
src/ImageSharp/Formats/WebP/Lossless/WebPLosslessDecoder.cs

@ -8,11 +8,11 @@ using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Experimental.WebP.BitReader; using SixLabors.ImageSharp.Formats.Experimental.Webp.BitReader;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless
{ {
/// <summary> /// <summary>
/// Decoder for lossless webp images. This code is a port of libwebp, which can be found here: https://chromium.googlesource.com/webm/libwebp /// Decoder for lossless webp images. This code is a port of libwebp, which can be found here: https://chromium.googlesource.com/webm/libwebp
@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
private const uint PackedNonLiteralCode = 0; private const uint PackedNonLiteralCode = 0;
private static readonly int CodeToPlaneCodes = WebPLookupTables.CodeToPlane.Length; private static readonly int CodeToPlaneCodes = WebpLookupTables.CodeToPlane.Length;
// Memory needed for lookup tables of one Huffman tree group. Red, blue, alpha and distance alphabets are constant (256 for red, blue and alpha, 40 for // Memory needed for lookup tables of one Huffman tree group. Red, blue, alpha and distance alphabets are constant (256 for red, blue and alpha, 40 for
// distance) and lookup table sizes for them in worst case are 630 and 410 respectively. Size of green alphabet depends on color cache size and is equal // distance) and lookup table sizes for them in worst case are 630 and 410 respectively. Size of green alphabet depends on color cache size and is equal
@ -114,14 +114,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
int numberOfTransformsPresent = 0; int numberOfTransformsPresent = 0;
if (isLevel0) if (isLevel0)
{ {
decoder.Transforms = new List<Vp8LTransform>(WebPConstants.MaxNumberOfTransforms); decoder.Transforms = new List<Vp8LTransform>(WebpConstants.MaxNumberOfTransforms);
// Next bit indicates, if a transformation is present. // Next bit indicates, if a transformation is present.
while (this.bitReader.ReadBit()) while (this.bitReader.ReadBit())
{ {
if (numberOfTransformsPresent > WebPConstants.MaxNumberOfTransforms) if (numberOfTransformsPresent > WebpConstants.MaxNumberOfTransforms)
{ {
WebPThrowHelper.ThrowImageFormatException($"The maximum number of transforms of {WebPConstants.MaxNumberOfTransforms} was exceeded"); WebpThrowHelper.ThrowImageFormatException($"The maximum number of transforms of {WebpConstants.MaxNumberOfTransforms} was exceeded");
} }
this.ReadTransformation(transformXSize, transformYSize, decoder); this.ReadTransformation(transformXSize, transformYSize, decoder);
@ -148,10 +148,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
// Note: According to webpinfo color cache bits of 11 are valid, even though 10 is defined in the source code as maximum. // Note: According to webpinfo color cache bits of 11 are valid, even though 10 is defined in the source code as maximum.
// That is why 11 bits is also considered valid here. // That is why 11 bits is also considered valid here.
bool colorCacheBitsIsValid = colorCacheBits >= 1 && colorCacheBits <= (WebPConstants.MaxColorCacheBits + 1); bool colorCacheBitsIsValid = colorCacheBits >= 1 && colorCacheBits <= (WebpConstants.MaxColorCacheBits + 1);
if (!colorCacheBitsIsValid) if (!colorCacheBitsIsValid)
{ {
WebPThrowHelper.ThrowImageFormatException("Invalid color cache bits found"); WebpThrowHelper.ThrowImageFormatException("Invalid color cache bits found");
} }
} }
@ -216,7 +216,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
int height = decoder.Height; int height = decoder.Height;
int row = lastPixel / width; int row = lastPixel / width;
int col = lastPixel % width; int col = lastPixel % width;
const int lenCodeLimit = WebPConstants.NumLiteralCodes + WebPConstants.NumLengthCodes; const int lenCodeLimit = WebpConstants.NumLiteralCodes + WebpConstants.NumLengthCodes;
int colorCacheSize = decoder.Metadata.ColorCacheSize; int colorCacheSize = decoder.Metadata.ColorCacheSize;
ColorCache colorCache = decoder.Metadata.ColorCache; ColorCache colorCache = decoder.Metadata.ColorCache;
int colorCacheLimit = lenCodeLimit + colorCacheSize; int colorCacheLimit = lenCodeLimit + colorCacheSize;
@ -267,7 +267,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
} }
// Literal // Literal
if (code < WebPConstants.NumLiteralCodes) if (code < WebpConstants.NumLiteralCodes)
{ {
if (hTreeGroup[0].IsTrivialLiteral) if (hTreeGroup[0].IsTrivialLiteral)
{ {
@ -292,7 +292,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
else if (code < lenCodeLimit) else if (code < lenCodeLimit)
{ {
// Backward reference is used. // Backward reference is used.
int lengthSym = code - WebPConstants.NumLiteralCodes; int lengthSym = code - WebpConstants.NumLiteralCodes;
int length = this.GetCopyLength(lengthSym); int length = this.GetCopyLength(lengthSym);
uint distSymbol = this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Dist]); uint distSymbol = this.ReadSymbol(hTreeGroup[0].HTrees[HuffIndex.Dist]);
this.bitReader.FillBitWindow(); this.bitReader.FillBitWindow();
@ -341,7 +341,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
} }
else else
{ {
WebPThrowHelper.ThrowImageFormatException("Webp parsing error"); WebpThrowHelper.ThrowImageFormatException("Webp parsing error");
} }
} }
} }
@ -403,9 +403,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
} }
// Find maximum alphabet size for the hTree group. // Find maximum alphabet size for the hTree group.
for (int j = 0; j < WebPConstants.HuffmanCodesPerMetaCode; j++) for (int j = 0; j < WebpConstants.HuffmanCodesPerMetaCode; j++)
{ {
int alphabetSize = WebPConstants.AlphabetSize[j]; int alphabetSize = WebpConstants.AlphabetSize[j];
if (j == 0 && colorCacheBits > 0) if (j == 0 && colorCacheBits > 0)
{ {
alphabetSize += 1 << colorCacheBits; alphabetSize += 1 << colorCacheBits;
@ -429,9 +429,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
bool isTrivialLiteral = true; bool isTrivialLiteral = true;
int maxBits = 0; int maxBits = 0;
var codeLengths = new int[maxAlphabetSize]; var codeLengths = new int[maxAlphabetSize];
for (int j = 0; j < WebPConstants.HuffmanCodesPerMetaCode; j++) for (int j = 0; j < WebpConstants.HuffmanCodesPerMetaCode; j++)
{ {
int alphabetSize = WebPConstants.AlphabetSize[j]; int alphabetSize = WebpConstants.AlphabetSize[j];
if (j == 0 && colorCacheBits > 0) if (j == 0 && colorCacheBits > 0)
{ {
alphabetSize += 1 << colorCacheBits; alphabetSize += 1 << colorCacheBits;
@ -440,7 +440,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
int size = this.ReadHuffmanCode(alphabetSize, codeLengths, huffmanTable); int size = this.ReadHuffmanCode(alphabetSize, codeLengths, huffmanTable);
if (size == 0) if (size == 0)
{ {
WebPThrowHelper.ThrowImageFormatException("Huffman table size is zero"); WebpThrowHelper.ThrowImageFormatException("Huffman table size is zero");
} }
// TODO: Avoid allocation. // TODO: Avoid allocation.
@ -481,7 +481,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
uint green = hTreeGroup.HTrees[HuffIndex.Green][0].Value; uint green = hTreeGroup.HTrees[HuffIndex.Green][0].Value;
uint alpha = hTreeGroup.HTrees[HuffIndex.Alpha][0].Value; uint alpha = hTreeGroup.HTrees[HuffIndex.Alpha][0].Value;
hTreeGroup.LiteralArb = (alpha << 24) | (red << 16) | blue; hTreeGroup.LiteralArb = (alpha << 24) | (red << 16) | blue;
if (totalSize == 0 && green < WebPConstants.NumLiteralCodes) if (totalSize == 0 && green < WebpConstants.NumLiteralCodes)
{ {
hTreeGroup.IsTrivialCode = true; hTreeGroup.IsTrivialCode = true;
hTreeGroup.LiteralArb |= green << 8; hTreeGroup.LiteralArb |= green << 8;
@ -538,7 +538,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
uint numCodes = this.bitReader.ReadValue(4) + 4; uint numCodes = this.bitReader.ReadValue(4) + 4;
if (numCodes > NumCodeLengthCodes) if (numCodes > NumCodeLengthCodes)
{ {
WebPThrowHelper.ThrowImageFormatException("Bitstream error, numCodes has an invalid value"); WebpThrowHelper.ThrowImageFormatException("Bitstream error, numCodes has an invalid value");
} }
for (int i = 0; i < numCodes; i++) for (int i = 0; i < numCodes; i++)
@ -558,11 +558,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
{ {
int maxSymbol; int maxSymbol;
int symbol = 0; int symbol = 0;
int prevCodeLen = WebPConstants.DefaultCodeLength; int prevCodeLen = WebpConstants.DefaultCodeLength;
int size = HuffmanUtils.BuildHuffmanTable(table, WebPConstants.LengthTableBits, codeLengthCodeLengths, NumCodeLengthCodes); int size = HuffmanUtils.BuildHuffmanTable(table, WebpConstants.LengthTableBits, codeLengthCodeLengths, NumCodeLengthCodes);
if (size == 0) if (size == 0)
{ {
WebPThrowHelper.ThrowImageFormatException("Error building huffman table"); WebpThrowHelper.ThrowImageFormatException("Error building huffman table");
} }
if (this.bitReader.ReadBit()) if (this.bitReader.ReadBit())
@ -588,7 +588,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
HuffmanCode huffmanCode = table[idx]; HuffmanCode huffmanCode = table[idx];
this.bitReader.AdvanceBitPosition(huffmanCode.BitsUsed); this.bitReader.AdvanceBitPosition(huffmanCode.BitsUsed);
uint codeLen = huffmanCode.Value; uint codeLen = huffmanCode.Value;
if (codeLen < WebPConstants.CodeLengthLiterals) if (codeLen < WebpConstants.CodeLengthLiterals)
{ {
codeLengths[symbol++] = (int)codeLen; codeLengths[symbol++] = (int)codeLen;
if (codeLen != 0) if (codeLen != 0)
@ -598,10 +598,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
} }
else else
{ {
bool usePrev = codeLen == WebPConstants.CodeLengthRepeatCode; bool usePrev = codeLen == WebpConstants.CodeLengthRepeatCode;
uint slot = codeLen - WebPConstants.CodeLengthLiterals; uint slot = codeLen - WebpConstants.CodeLengthLiterals;
int extraBits = WebPConstants.CodeLengthExtraBits[slot]; int extraBits = WebpConstants.CodeLengthExtraBits[slot];
int repeatOffset = WebPConstants.CodeLengthRepeatOffsets[slot]; int repeatOffset = WebpConstants.CodeLengthRepeatOffsets[slot];
int repeat = (int)(this.bitReader.ReadValue(extraBits) + repeatOffset); int repeat = (int)(this.bitReader.ReadValue(extraBits) + repeatOffset);
if (symbol + repeat > numSymbols) if (symbol + repeat > numSymbols)
{ {
@ -633,7 +633,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
{ {
if (decoderTransform.TransformType == transform.TransformType) if (decoderTransform.TransformType == transform.TransformType)
{ {
WebPThrowHelper.ThrowImageFormatException("Each transform can only be present once"); WebpThrowHelper.ThrowImageFormatException("Each transform can only be present once");
} }
} }
@ -732,7 +732,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
int end = width * height; // End of data. int end = width * height; // End of data.
int last = end; // Last pixel to decode. int last = end; // Last pixel to decode.
int lastRow = height; int lastRow = height;
const int lenCodeLimit = WebPConstants.NumLiteralCodes + WebPConstants.NumLengthCodes; const int lenCodeLimit = WebpConstants.NumLiteralCodes + WebpConstants.NumLengthCodes;
int mask = hdr.HuffmanMask; int mask = hdr.HuffmanMask;
HTreeGroup[] htreeGroup = (pos < last) ? GetHTreeGroupForPos(hdr, col, row) : null; HTreeGroup[] htreeGroup = (pos < last) ? GetHTreeGroupForPos(hdr, col, row) : null;
while (!this.bitReader.Eos && pos < last) while (!this.bitReader.Eos && pos < last)
@ -745,7 +745,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
this.bitReader.FillBitWindow(); this.bitReader.FillBitWindow();
int code = (int)this.ReadSymbol(htreeGroup[0].HTrees[HuffIndex.Green]); int code = (int)this.ReadSymbol(htreeGroup[0].HTrees[HuffIndex.Green]);
if (code < WebPConstants.NumLiteralCodes) if (code < WebpConstants.NumLiteralCodes)
{ {
// Literal // Literal
data[pos] = (byte)code; data[pos] = (byte)code;
@ -756,7 +756,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
{ {
col = 0; col = 0;
++row; ++row;
if (row <= lastRow && (row % WebPConstants.NumArgbCacheRows == 0)) if (row <= lastRow && (row % WebpConstants.NumArgbCacheRows == 0))
{ {
dec.ExtractPalettedAlphaRows(row); dec.ExtractPalettedAlphaRows(row);
} }
@ -765,7 +765,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
else if (code < lenCodeLimit) else if (code < lenCodeLimit)
{ {
// Backward reference // Backward reference
int lengthSym = code - WebPConstants.NumLiteralCodes; int lengthSym = code - WebpConstants.NumLiteralCodes;
int length = this.GetCopyLength(lengthSym); int length = this.GetCopyLength(lengthSym);
int distSymbol = (int)this.ReadSymbol(htreeGroup[0].HTrees[HuffIndex.Dist]); int distSymbol = (int)this.ReadSymbol(htreeGroup[0].HTrees[HuffIndex.Dist]);
this.bitReader.FillBitWindow(); this.bitReader.FillBitWindow();
@ -777,7 +777,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
} }
else else
{ {
WebPThrowHelper.ThrowImageFormatException("error while decoding alpha data"); WebpThrowHelper.ThrowImageFormatException("error while decoding alpha data");
} }
pos += length; pos += length;
@ -786,7 +786,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
{ {
col -= width; col -= width;
++row; ++row;
if (row <= lastRow && (row % WebPConstants.NumArgbCacheRows == 0)) if (row <= lastRow && (row % WebpConstants.NumArgbCacheRows == 0))
{ {
dec.ExtractPalettedAlphaRows(row); dec.ExtractPalettedAlphaRows(row);
} }
@ -799,7 +799,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
} }
else else
{ {
WebPThrowHelper.ThrowImageFormatException("bitstream error while parsing alpha data"); WebpThrowHelper.ThrowImageFormatException("bitstream error while parsing alpha data");
} }
this.bitReader.Eos = this.bitReader.IsEndOfStream(); this.bitReader.Eos = this.bitReader.IsEndOfStream();
@ -841,7 +841,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
uint bits = code; uint bits = code;
HuffmanCode huff = hTreeGroup.PackedTable[bits]; HuffmanCode huff = hTreeGroup.PackedTable[bits];
HuffmanCode hCode = hTreeGroup.HTrees[HuffIndex.Green][bits]; HuffmanCode hCode = hTreeGroup.HTrees[HuffIndex.Green][bits];
if (hCode.Value >= WebPConstants.NumLiteralCodes) if (hCode.Value >= WebpConstants.NumLiteralCodes)
{ {
huff.BitsUsed = hCode.BitsUsed + BitsSpecialMarker; huff.BitsUsed = hCode.BitsUsed + BitsSpecialMarker;
huff.Value = hCode.Value; huff.Value = hCode.Value;
@ -926,7 +926,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
return planeCode - CodeToPlaneCodes; return planeCode - CodeToPlaneCodes;
} }
int distCode = WebPLookupTables.CodeToPlane[planeCode - 1]; int distCode = WebpLookupTables.CodeToPlane[planeCode - 1];
int yOffset = distCode >> 4; int yOffset = distCode >> 4;
int xOffset = 8 - (distCode & 0xf); int xOffset = 8 - (distCode & 0xf);
int dist = (yOffset * xSize) + xOffset; int dist = (yOffset * xSize) + xOffset;
@ -948,7 +948,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless
int start = decodedPixels - dist; int start = decodedPixels - dist;
if (start < 0) if (start < 0)
{ {
WebPThrowHelper.ThrowImageFormatException("webp image data seems to be invalid"); WebpThrowHelper.ThrowImageFormatException("webp image data seems to be invalid");
} }
if (dist >= length) if (dist >= length)

2
src/ImageSharp/Formats/WebP/Lossy/IntraPredictionMode.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
internal enum IntraPredictionMode internal enum IntraPredictionMode
{ {

2
src/ImageSharp/Formats/WebP/Lossy/LoopFilter.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
/// <summary> /// <summary>
/// Enum for the different loop filters used. VP8 supports two types of loop filters. /// Enum for the different loop filters used. VP8 supports two types of loop filters.

222
src/ImageSharp/Formats/WebP/Lossy/LossyUtils.cs

@ -6,19 +6,19 @@ using System.Buffers.Binary;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
internal static class LossyUtils internal static class LossyUtils
{ {
public static void DC16(Span<byte> dst, Span<byte> yuv, int offset) public static void DC16(Span<byte> dst, Span<byte> yuv, int offset)
{ {
int offsetMinus1 = offset - 1; int offsetMinus1 = offset - 1;
int offsetMinusBps = offset - WebPConstants.Bps; int offsetMinusBps = offset - WebpConstants.Bps;
int dc = 16; int dc = 16;
for (int j = 0; j < 16; ++j) for (int j = 0; j < 16; ++j)
{ {
// DC += dst[-1 + j * BPS] + dst[j - BPS]; // DC += dst[-1 + j * BPS] + dst[j - BPS];
dc += yuv[offsetMinus1 + (j * WebPConstants.Bps)] + yuv[offsetMinusBps + j]; dc += yuv[offsetMinus1 + (j * WebpConstants.Bps)] + yuv[offsetMinusBps + j];
} }
Put16(dc >> 5, dst); Put16(dc >> 5, dst);
@ -33,11 +33,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
public static void VE16(Span<byte> dst, Span<byte> yuv, int offset) public static void VE16(Span<byte> dst, Span<byte> yuv, int offset)
{ {
// vertical // vertical
Span<byte> src = yuv.Slice(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);
src.CopyTo(dst.Slice(j * WebPConstants.Bps)); src.CopyTo(dst.Slice(j * WebpConstants.Bps));
} }
} }
@ -50,8 +50,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
// memset(dst, dst[-1], 16); // memset(dst, dst[-1], 16);
byte v = yuv[offset]; byte v = yuv[offset];
Memset(dst, v, 0, 16); Memset(dst, v, 0, 16);
offset += WebPConstants.Bps; offset += WebpConstants.Bps;
dst = dst.Slice(WebPConstants.Bps); dst = dst.Slice(WebpConstants.Bps);
} }
} }
@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
for (int j = 0; j < 16; ++j) for (int j = 0; j < 16; ++j)
{ {
// DC += dst[-1 + j * BPS]; // DC += dst[-1 + j * BPS];
dc += yuv[-1 + (j * WebPConstants.Bps) + offset]; dc += yuv[-1 + (j * WebpConstants.Bps) + offset];
} }
Put16(dc >> 4, dst); Put16(dc >> 4, dst);
@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
for (int i = 0; i < 16; ++i) for (int i = 0; i < 16; ++i)
{ {
// DC += dst[i - BPS]; // DC += dst[i - BPS];
dc += yuv[i - WebPConstants.Bps + offset]; dc += yuv[i - WebpConstants.Bps + offset];
} }
Put16(dc >> 4, dst); Put16(dc >> 4, dst);
@ -92,11 +92,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
{ {
int dc0 = 8; int dc0 = 8;
int offsetMinus1 = offset - 1; int offsetMinus1 = offset - 1;
int offsetMinusBps = offset - WebPConstants.Bps; int offsetMinusBps = offset - WebpConstants.Bps;
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
{ {
// dc0 += dst[i - BPS] + dst[-1 + i * BPS]; // dc0 += dst[i - BPS] + dst[-1 + i * BPS];
dc0 += yuv[offsetMinusBps + i] + yuv[offsetMinus1 + (i * WebPConstants.Bps)]; dc0 += yuv[offsetMinusBps + i] + yuv[offsetMinus1 + (i * WebpConstants.Bps)];
} }
Put8x8uv((byte)(dc0 >> 4), dst); Put8x8uv((byte)(dc0 >> 4), dst);
@ -112,10 +112,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
public static void VE8uv(Span<byte> dst, Span<byte> yuv, int offset) public static void VE8uv(Span<byte> dst, Span<byte> yuv, int offset)
{ {
// vertical // vertical
Span<byte> src = yuv.Slice(offset - WebPConstants.Bps, 8); Span<byte> src = yuv.Slice(offset - WebpConstants.Bps, 8);
int endIdx = 8 * WebPConstants.Bps; int endIdx = 8 * WebpConstants.Bps;
for (int j = 0; j < endIdx; j += WebPConstants.Bps) for (int j = 0; j < endIdx; j += WebpConstants.Bps)
{ {
// memcpy(dst + j * BPS, dst - BPS, 8); // memcpy(dst + j * BPS, dst - BPS, 8);
src.CopyTo(dst.Slice(j)); src.CopyTo(dst.Slice(j));
@ -132,8 +132,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
// dst += BPS; // dst += BPS;
byte v = yuv[offset]; byte v = yuv[offset];
Memset(dst, v, 0, 8); Memset(dst, v, 0, 8);
dst = dst.Slice(WebPConstants.Bps); dst = dst.Slice(WebpConstants.Bps);
offset += WebPConstants.Bps; offset += WebpConstants.Bps;
} }
} }
@ -142,8 +142,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
// DC with no top samples. // DC with no top samples.
int dc0 = 4; int dc0 = 4;
int offsetMinusOne = offset - 1; int offsetMinusOne = offset - 1;
int endIdx = 8 * WebPConstants.Bps; int endIdx = 8 * WebpConstants.Bps;
for (int i = 0; i < endIdx; i += WebPConstants.Bps) for (int i = 0; i < endIdx; i += WebpConstants.Bps)
{ {
// dc0 += dst[-1 + i * BPS]; // dc0 += dst[-1 + i * BPS];
dc0 += yuv[offsetMinusOne + i]; dc0 += yuv[offsetMinusOne + i];
@ -155,7 +155,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
public static void DC8uvNoLeft(Span<byte> dst, Span<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 offsetMinusBps = offset - WebPConstants.Bps; int offsetMinusBps = offset - WebpConstants.Bps;
int dc0 = 4; int dc0 = 4;
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
{ {
@ -176,16 +176,16 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
public static void DC4(Span<byte> dst, Span<byte> yuv, int offset) public static void DC4(Span<byte> dst, Span<byte> yuv, int offset)
{ {
int dc = 4; int dc = 4;
int offsetMinusBps = offset - WebPConstants.Bps; int offsetMinusBps = offset - WebpConstants.Bps;
int offsetMinusOne = offset - 1; int offsetMinusOne = offset - 1;
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
{ {
dc += yuv[offsetMinusBps + i] + yuv[offsetMinusOne + (i * WebPConstants.Bps)]; dc += yuv[offsetMinusBps + i] + yuv[offsetMinusOne + (i * WebpConstants.Bps)];
} }
dc >>= 3; dc >>= 3;
int endIndx = 4 * WebPConstants.Bps; int endIndx = 4 * WebpConstants.Bps;
for (int i = 0; i < endIndx; i += WebPConstants.Bps) for (int i = 0; i < endIndx; i += WebpConstants.Bps)
{ {
Memset(dst, (byte)dc, i, 4); Memset(dst, (byte)dc, i, 4);
} }
@ -200,7 +200,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
public static void VE4(Span<byte> dst, Span<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;
byte[] vals = byte[] vals =
{ {
Avg3(yuv[topOffset - 1], yuv[topOffset], yuv[topOffset + 1]), Avg3(yuv[topOffset - 1], yuv[topOffset], yuv[topOffset + 1]),
@ -209,8 +209,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
Avg3(yuv[topOffset + 2], yuv[topOffset + 3], yuv[topOffset + 4]) Avg3(yuv[topOffset + 2], yuv[topOffset + 3], yuv[topOffset + 4])
}; };
int endIdx = 4 * WebPConstants.Bps; int endIdx = 4 * WebpConstants.Bps;
for (int i = 0; i < endIdx; i += WebPConstants.Bps) for (int i = 0; i < endIdx; i += WebpConstants.Bps)
{ {
vals.CopyTo(dst.Slice(i)); vals.CopyTo(dst.Slice(i));
} }
@ -220,19 +220,19 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
{ {
// horizontal // horizontal
int offsetMinusOne = offset - 1; int offsetMinusOne = offset - 1;
byte a = yuv[offsetMinusOne - WebPConstants.Bps]; byte a = yuv[offsetMinusOne - WebpConstants.Bps];
byte b = yuv[offsetMinusOne]; byte b = yuv[offsetMinusOne];
byte c = yuv[offsetMinusOne + WebPConstants.Bps]; byte c = yuv[offsetMinusOne + WebpConstants.Bps];
byte d = yuv[offsetMinusOne + (2 * WebPConstants.Bps)]; byte d = yuv[offsetMinusOne + (2 * WebpConstants.Bps)];
byte e = yuv[offsetMinusOne + (3 * WebPConstants.Bps)]; byte e = yuv[offsetMinusOne + (3 * WebpConstants.Bps)];
uint val = 0x01010101U * Avg3(a, b, c); uint val = 0x01010101U * Avg3(a, b, c);
BinaryPrimitives.WriteUInt32BigEndian(dst, val); BinaryPrimitives.WriteUInt32BigEndian(dst, val);
val = 0x01010101U * Avg3(b, c, d); val = 0x01010101U * Avg3(b, c, d);
BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(WebPConstants.Bps), val); BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(WebpConstants.Bps), val);
val = 0x01010101U * Avg3(c, d, e); val = 0x01010101U * Avg3(c, d, e);
BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(2 * WebPConstants.Bps), val); BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(2 * WebpConstants.Bps), val);
val = 0x01010101U * Avg3(d, e, e); val = 0x01010101U * Avg3(d, e, e);
BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(3 * WebPConstants.Bps), val); BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(3 * WebpConstants.Bps), val);
} }
public static void RD4(Span<byte> dst, Span<byte> yuv, int offset) public static void RD4(Span<byte> dst, Span<byte> yuv, int offset)
@ -240,14 +240,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
// Down-right // Down-right
int offsetMinusOne = offset - 1; int offsetMinusOne = offset - 1;
byte i = yuv[offsetMinusOne]; byte i = yuv[offsetMinusOne];
byte j = yuv[offsetMinusOne + (1 * WebPConstants.Bps)]; byte j = yuv[offsetMinusOne + (1 * WebpConstants.Bps)];
byte k = yuv[offsetMinusOne + (2 * WebPConstants.Bps)]; byte k = yuv[offsetMinusOne + (2 * WebpConstants.Bps)];
byte l = yuv[offsetMinusOne + (3 * WebPConstants.Bps)]; byte l = yuv[offsetMinusOne + (3 * WebpConstants.Bps)];
byte x = yuv[offsetMinusOne - WebPConstants.Bps]; byte x = yuv[offsetMinusOne - WebpConstants.Bps];
byte a = yuv[offset - WebPConstants.Bps]; byte a = yuv[offset - WebpConstants.Bps];
byte b = yuv[offset + 1 - WebPConstants.Bps]; byte b = yuv[offset + 1 - WebpConstants.Bps];
byte c = yuv[offset + 2 - WebPConstants.Bps]; byte c = yuv[offset + 2 - WebpConstants.Bps];
byte d = yuv[offset + 3 - WebPConstants.Bps]; byte d = yuv[offset + 3 - WebpConstants.Bps];
Dst(dst, 0, 3, Avg3(j, k, l)); Dst(dst, 0, 3, Avg3(j, k, l));
byte ijk = Avg3(i, j, k); byte ijk = Avg3(i, j, k);
@ -277,13 +277,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
// Vertical-Right // Vertical-Right
int offsetMinusOne = offset - 1; int offsetMinusOne = offset - 1;
byte i = yuv[offsetMinusOne]; byte i = yuv[offsetMinusOne];
byte j = yuv[offsetMinusOne + (1 * WebPConstants.Bps)]; byte j = yuv[offsetMinusOne + (1 * WebpConstants.Bps)];
byte k = yuv[offsetMinusOne + (2 * WebPConstants.Bps)]; byte k = yuv[offsetMinusOne + (2 * WebpConstants.Bps)];
byte x = yuv[offsetMinusOne - WebPConstants.Bps]; byte x = yuv[offsetMinusOne - WebpConstants.Bps];
byte a = yuv[offset - WebPConstants.Bps]; byte a = yuv[offset - WebpConstants.Bps];
byte b = yuv[offset + 1 - WebPConstants.Bps]; byte b = yuv[offset + 1 - WebpConstants.Bps];
byte c = yuv[offset + 2 - WebPConstants.Bps]; byte c = yuv[offset + 2 - WebpConstants.Bps];
byte d = yuv[offset + 3 - WebPConstants.Bps]; byte d = yuv[offset + 3 - WebpConstants.Bps];
byte xa = Avg2(x, a); byte xa = Avg2(x, a);
Dst(dst, 0, 0, xa); Dst(dst, 0, 0, xa);
@ -312,14 +312,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
public static void LD4(Span<byte> dst, Span<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];
byte b = yuv[offset + 1 - WebPConstants.Bps]; byte b = yuv[offset + 1 - WebpConstants.Bps];
byte c = yuv[offset + 2 - WebPConstants.Bps]; byte c = yuv[offset + 2 - WebpConstants.Bps];
byte d = yuv[offset + 3 - WebPConstants.Bps]; byte d = yuv[offset + 3 - WebpConstants.Bps];
byte e = yuv[offset + 4 - WebPConstants.Bps]; byte e = yuv[offset + 4 - WebpConstants.Bps];
byte f = yuv[offset + 5 - WebPConstants.Bps]; byte f = yuv[offset + 5 - WebpConstants.Bps];
byte g = yuv[offset + 6 - WebPConstants.Bps]; byte g = yuv[offset + 6 - WebpConstants.Bps];
byte h = yuv[offset + 7 - WebPConstants.Bps]; byte h = yuv[offset + 7 - WebpConstants.Bps];
Dst(dst, 0, 0, Avg3(a, b, c)); Dst(dst, 0, 0, Avg3(a, b, c));
byte bcd = Avg3(b, c, d); byte bcd = Avg3(b, c, d);
@ -347,14 +347,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
public static void VL4(Span<byte> dst, Span<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];
byte b = yuv[offset + 1 - WebPConstants.Bps]; byte b = yuv[offset + 1 - WebpConstants.Bps];
byte c = yuv[offset + 2 - WebPConstants.Bps]; byte c = yuv[offset + 2 - WebpConstants.Bps];
byte d = yuv[offset + 3 - WebPConstants.Bps]; byte d = yuv[offset + 3 - WebpConstants.Bps];
byte e = yuv[offset + 4 - WebPConstants.Bps]; byte e = yuv[offset + 4 - WebpConstants.Bps];
byte f = yuv[offset + 5 - WebPConstants.Bps]; byte f = yuv[offset + 5 - WebpConstants.Bps];
byte g = yuv[offset + 6 - WebPConstants.Bps]; byte g = yuv[offset + 6 - WebpConstants.Bps];
byte h = yuv[offset + 7 - WebPConstants.Bps]; byte h = yuv[offset + 7 - WebpConstants.Bps];
Dst(dst, 0, 0, Avg2(a, b)); Dst(dst, 0, 0, Avg2(a, b));
byte bc = Avg2(b, c); byte bc = Avg2(b, c);
@ -384,13 +384,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
{ {
// Horizontal-Down // Horizontal-Down
byte i = yuv[offset - 1]; byte i = yuv[offset - 1];
byte j = yuv[offset - 1 + (1 * WebPConstants.Bps)]; byte j = yuv[offset - 1 + (1 * WebpConstants.Bps)];
byte k = yuv[offset - 1 + (2 * WebPConstants.Bps)]; byte k = yuv[offset - 1 + (2 * WebpConstants.Bps)];
byte l = yuv[offset - 1 + (3 * WebPConstants.Bps)]; byte l = yuv[offset - 1 + (3 * WebpConstants.Bps)];
byte x = yuv[offset - 1 - WebPConstants.Bps]; byte x = yuv[offset - 1 - WebpConstants.Bps];
byte a = yuv[offset - WebPConstants.Bps]; byte a = yuv[offset - WebpConstants.Bps];
byte b = yuv[offset + 1 - WebPConstants.Bps]; byte b = yuv[offset + 1 - WebpConstants.Bps];
byte c = yuv[offset + 2 - WebPConstants.Bps]; byte c = yuv[offset + 2 - WebpConstants.Bps];
byte ix = Avg2(i, x); byte ix = Avg2(i, x);
Dst(dst, 0, 0, ix); Dst(dst, 0, 0, ix);
@ -420,9 +420,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
{ {
// Horizontal-Up // Horizontal-Up
byte i = yuv[offset - 1]; byte i = yuv[offset - 1];
byte j = yuv[offset - 1 + (1 * WebPConstants.Bps)]; byte j = yuv[offset - 1 + (1 * WebpConstants.Bps)];
byte k = yuv[offset - 1 + (2 * WebPConstants.Bps)]; byte k = yuv[offset - 1 + (2 * WebpConstants.Bps)];
byte l = yuv[offset - 1 + (3 * WebPConstants.Bps)]; byte l = yuv[offset - 1 + (3 * WebpConstants.Bps)];
Dst(dst, 0, 0, Avg2(i, j)); Dst(dst, 0, 0, Avg2(i, j));
byte jk = Avg2(j, k); byte jk = Avg2(j, k);
@ -532,7 +532,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
Store(dst, 2, 0, b - c); Store(dst, 2, 0, b - c);
Store(dst, 3, 0, a - d); Store(dst, 3, 0, a - d);
tmpOffset++; tmpOffset++;
dst = dst.Slice(WebPConstants.Bps); dst = dst.Slice(WebpConstants.Bps);
} }
} }
@ -565,7 +565,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
public static void TransformUv(Span<short> src, Span<byte> dst) public static void TransformUv(Span<short> src, Span<byte> dst)
{ {
TransformTwo(src.Slice(0 * 16), dst); TransformTwo(src.Slice(0 * 16), dst);
TransformTwo(src.Slice(2 * 16), dst.Slice(4 * WebPConstants.Bps)); TransformTwo(src.Slice(2 * 16), dst.Slice(4 * WebpConstants.Bps));
} }
public static void TransformDcuv(Span<short> src, Span<byte> dst) public static void TransformDcuv(Span<short> src, Span<byte> dst)
@ -582,12 +582,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
if (src[2 * 16] != 0) if (src[2 * 16] != 0)
{ {
TransformDc(src.Slice(2 * 16), dst.Slice(4 * WebPConstants.Bps)); TransformDc(src.Slice(2 * 16), dst.Slice(4 * WebpConstants.Bps));
} }
if (src[3 * 16] != 0) if (src[3 * 16] != 0)
{ {
TransformDc(src.Slice(3 * 16), dst.Slice((4 * WebPConstants.Bps) + 4)); TransformDc(src.Slice(3 * 16), dst.Slice((4 * WebpConstants.Bps) + 4));
} }
} }
@ -745,7 +745,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void Dst(Span<byte> dst, int x, int y, byte v) public static void Dst(Span<byte> dst, int x, int y, byte v)
{ {
dst[x + (y * WebPConstants.Bps)] = v; dst[x + (y * WebpConstants.Bps)] = v;
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
@ -757,7 +757,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
// Cost of coding one event with probability 'proba'. // Cost of coding one event with probability 'proba'.
public static int Vp8BitCost(int bit, byte proba) public static int Vp8BitCost(int bit, byte proba)
{ {
return bit == 0 ? WebPLookupTables.Vp8EntropyCost[proba] : WebPLookupTables.Vp8EntropyCost[255 - proba]; return bit == 0 ? WebpLookupTables.Vp8EntropyCost[proba] : WebpLookupTables.Vp8EntropyCost[255 - proba];
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
@ -765,14 +765,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
{ {
for (int j = 0; j < 16; ++j) for (int j = 0; j < 16; ++j)
{ {
Memset(dst.Slice(j * WebPConstants.Bps), (byte)v, 0, 16); Memset(dst.Slice(j * WebpConstants.Bps), (byte)v, 0, 16);
} }
} }
private static void TrueMotion(Span<byte> dst, Span<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.Slice(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;
@ -784,9 +784,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
dst[x] = (byte)Clamp255(left + top[x] - p); dst[x] = (byte)Clamp255(left + top[x] - p);
} }
leftOffset += WebPConstants.Bps; leftOffset += WebpConstants.Bps;
left = yuv[leftOffset]; left = yuv[leftOffset];
dst = dst.Slice(WebPConstants.Bps); dst = dst.Slice(WebpConstants.Bps);
} }
} }
@ -856,11 +856,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
int p0 = p[offset - step]; int p0 = p[offset - step];
int q0 = p[offset]; int q0 = p[offset];
int q1 = p[offset + step]; int q1 = p[offset + step];
int a = (3 * (q0 - p0)) + WebPLookupTables.Sclip1[p1 - q1]; int a = (3 * (q0 - p0)) + WebpLookupTables.Sclip1[p1 - q1];
int a1 = WebPLookupTables.Sclip2[(a + 4) >> 3]; int a1 = WebpLookupTables.Sclip2[(a + 4) >> 3];
int a2 = WebPLookupTables.Sclip2[(a + 3) >> 3]; int a2 = WebpLookupTables.Sclip2[(a + 3) >> 3];
p[offset - step] = WebPLookupTables.Clip1[p0 + a2]; p[offset - step] = WebpLookupTables.Clip1[p0 + a2];
p[offset] = WebPLookupTables.Clip1[q0 - a1]; p[offset] = WebpLookupTables.Clip1[q0 - a1];
} }
private static void DoFilter4(Span<byte> p, int offset, int step) private static void DoFilter4(Span<byte> p, int offset, int step)
@ -872,13 +872,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
int q0 = p[offset]; int q0 = p[offset];
int q1 = p[offset + step]; int q1 = p[offset + step];
int a = 3 * (q0 - p0); int a = 3 * (q0 - p0);
int a1 = WebPLookupTables.Sclip2[(a + 4) >> 3]; int a1 = WebpLookupTables.Sclip2[(a + 4) >> 3];
int a2 = WebPLookupTables.Sclip2[(a + 3) >> 3]; int a2 = WebpLookupTables.Sclip2[(a + 3) >> 3];
int a3 = (a1 + 1) >> 1; int a3 = (a1 + 1) >> 1;
p[offsetMinus2Step] = WebPLookupTables.Clip1[p1 + a3]; p[offsetMinus2Step] = WebpLookupTables.Clip1[p1 + a3];
p[offset - step] = WebPLookupTables.Clip1[p0 + a2]; p[offset - step] = WebpLookupTables.Clip1[p0 + a2];
p[offset] = WebPLookupTables.Clip1[q0 - a1]; p[offset] = WebpLookupTables.Clip1[q0 - a1];
p[offset + step] = WebPLookupTables.Clip1[q1 - a3]; p[offset + step] = WebpLookupTables.Clip1[q1 - a3];
} }
private static void DoFilter6(Span<byte> p, int offset, int step) private static void DoFilter6(Span<byte> p, int offset, int step)
@ -893,18 +893,18 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
int q0 = p[offset]; int q0 = p[offset];
int q1 = p[offset + step]; int q1 = p[offset + step];
int q2 = p[offset + step2]; int q2 = p[offset + step2];
int a = WebPLookupTables.Sclip1[(3 * (q0 - p0)) + WebPLookupTables.Sclip1[p1 - q1]]; int a = WebpLookupTables.Sclip1[(3 * (q0 - p0)) + WebpLookupTables.Sclip1[p1 - q1]];
// a is in [-128,127], a1 in [-27,27], a2 in [-18,18] and a3 in [-9,9] // a is in [-128,127], a1 in [-27,27], a2 in [-18,18] and a3 in [-9,9]
int a1 = ((27 * a) + 63) >> 7; // eq. to ((3 * a + 7) * 9) >> 7 int a1 = ((27 * a) + 63) >> 7; // eq. to ((3 * a + 7) * 9) >> 7
int a2 = ((18 * a) + 63) >> 7; // eq. to ((2 * a + 7) * 9) >> 7 int a2 = ((18 * a) + 63) >> 7; // eq. to ((2 * a + 7) * 9) >> 7
int a3 = ((9 * a) + 63) >> 7; // eq. to ((1 * a + 7) * 9) >> 7 int a3 = ((9 * a) + 63) >> 7; // eq. to ((1 * a + 7) * 9) >> 7
p[offset - step3] = WebPLookupTables.Clip1[p2 + a3]; p[offset - step3] = WebpLookupTables.Clip1[p2 + a3];
p[offset - step2] = WebPLookupTables.Clip1[p1 + a2]; p[offset - step2] = WebpLookupTables.Clip1[p1 + a2];
p[offsetMinusStep] = WebPLookupTables.Clip1[p0 + a1]; p[offsetMinusStep] = WebpLookupTables.Clip1[p0 + a1];
p[offset] = WebPLookupTables.Clip1[q0 - a1]; p[offset] = WebpLookupTables.Clip1[q0 - a1];
p[offset + step] = WebPLookupTables.Clip1[q1 - a2]; p[offset + step] = WebpLookupTables.Clip1[q1 - a2];
p[offset + step2] = WebPLookupTables.Clip1[q2 - a3]; p[offset + step2] = WebpLookupTables.Clip1[q2 - a3];
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
@ -914,7 +914,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
int p0 = p[offset - step]; int p0 = p[offset - step];
int q0 = p[offset]; int q0 = p[offset];
int q1 = p[offset + step]; int q1 = p[offset + step];
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(Span<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)
@ -929,14 +929,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
int q1 = p[offset + step]; int q1 = p[offset + step];
int q2 = p[offset + step2]; int q2 = p[offset + step2];
int q3 = p[offset + step3]; int q3 = p[offset + step3];
if (((4 * WebPLookupTables.Abs0[p0 - q0]) + WebPLookupTables.Abs0[p1 - q1]) > t) if (((4 * WebpLookupTables.Abs0[p0 - q0]) + WebpLookupTables.Abs0[p1 - q1]) > t)
{ {
return false; return false;
} }
return WebPLookupTables.Abs0[p3 - p2] <= it && WebPLookupTables.Abs0[p2 - p1] <= it && return WebpLookupTables.Abs0[p3 - p2] <= it && WebpLookupTables.Abs0[p2 - p1] <= it &&
WebPLookupTables.Abs0[p1 - p0] <= it && WebPLookupTables.Abs0[q3 - q2] <= it && WebpLookupTables.Abs0[p1 - p0] <= it && WebpLookupTables.Abs0[q3 - q2] <= it &&
WebPLookupTables.Abs0[q2 - q1] <= it && WebPLookupTables.Abs0[q1 - q0] <= it; WebpLookupTables.Abs0[q2 - q1] <= it && WebpLookupTables.Abs0[q1 - q0] <= it;
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
@ -946,7 +946,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
int p0 = p[offset - step]; int p0 = p[offset - step];
int q0 = p[offset]; int q0 = p[offset];
int q1 = p[offset + step]; int q1 = p[offset + step];
return (WebPLookupTables.Abs0[p1 - p0] > thresh) || (WebPLookupTables.Abs0[q1 - q0] > thresh); return (WebpLookupTables.Abs0[p1 - p0] > thresh) || (WebpLookupTables.Abs0[q1 - q0] > thresh);
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
@ -958,7 +958,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static void Store(Span<byte> dst, int x, int y, int v) private static void Store(Span<byte> dst, int x, int y, int v)
{ {
var index = x + (y * WebPConstants.Bps); var index = x + (y * WebpConstants.Bps);
dst[index] = Clip8B(dst[index] + (v >> 3)); dst[index] = Clip8B(dst[index] + (v >> 3));
} }
@ -993,8 +993,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static void Put8x8uv(byte value, Span<byte> dst) private static void Put8x8uv(byte value, Span<byte> dst)
{ {
int end = 8 * WebPConstants.Bps; int end = 8 * WebpConstants.Bps;
for (int j = 0; j < end; j += WebPConstants.Bps) for (int j = 0; j < end; j += WebpConstants.Bps)
{ {
// memset(dst + j * BPS, value, 8); // memset(dst + j * BPS, value, 8);
Memset(dst, value, j, 8); Memset(dst, value, j, 8);

2
src/ImageSharp/Formats/WebP/Lossy/PassStats.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
/// <summary> /// <summary>
/// Class for organizing convergence in either size or PSNR. /// Class for organizing convergence in either size or PSNR.

4
src/ImageSharp/Formats/WebP/Lossy/QuantEnc.cs

@ -4,7 +4,7 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
/// <summary> /// <summary>
/// Quantization methods. /// Quantization methods.
@ -131,7 +131,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static int QuantDiv(uint n, uint iQ, uint b) private static int QuantDiv(uint n, uint iQ, uint b)
{ {
return (int)(((n * iQ) + b) >> WebPConstants.QFix); return (int)(((n * iQ) + b) >> WebpConstants.QFix);
} }
} }
} }

6
src/ImageSharp/Formats/WebP/Lossy/VP8BandProbas.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
/// <summary> /// <summary>
/// All the probabilities associated to one band. /// All the probabilities associated to one band.
@ -13,8 +13,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
/// </summary> /// </summary>
public Vp8BandProbas() public Vp8BandProbas()
{ {
this.Probabilities = new Vp8ProbaArray[WebPConstants.NumCtx]; this.Probabilities = new Vp8ProbaArray[WebpConstants.NumCtx];
for (int i = 0; i < WebPConstants.NumCtx; i++) for (int i = 0; i < WebpConstants.NumCtx; i++)
{ {
this.Probabilities[i] = new Vp8ProbaArray(); this.Probabilities[i] = new Vp8ProbaArray();
} }

4
src/ImageSharp/Formats/WebP/Lossy/Vp8CostArray.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
internal class Vp8CostArray internal class Vp8CostArray
{ {
@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
/// </summary> /// </summary>
public Vp8CostArray() public Vp8CostArray()
{ {
this.Costs = new ushort[WebPConstants.NumCtx * (67 + 1)]; this.Costs = new ushort[WebpConstants.NumCtx * (67 + 1)];
} }
public ushort[] Costs { get; } public ushort[] Costs { get; }

18
src/ImageSharp/Formats/WebP/Lossy/Vp8Decoder.cs

@ -3,10 +3,10 @@
using System; using System;
using System.Buffers; using System.Buffers;
using SixLabors.ImageSharp.Formats.Experimental.WebP.BitReader; using SixLabors.ImageSharp.Formats.Experimental.Webp.BitReader;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
/// <summary> /// <summary>
/// Holds information for decoding a lossy webp image. /// Holds information for decoding a lossy webp image.
@ -49,9 +49,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
this.MacroBlockInfo[this.MbWidth] = new Vp8MacroBlock(); this.MacroBlockInfo[this.MbWidth] = new Vp8MacroBlock();
this.DeQuantMatrices = new Vp8QuantMatrix[WebPConstants.NumMbSegments]; this.DeQuantMatrices = new Vp8QuantMatrix[WebpConstants.NumMbSegments];
this.FilterStrength = new Vp8FilterInfo[WebPConstants.NumMbSegments, 2]; this.FilterStrength = new Vp8FilterInfo[WebpConstants.NumMbSegments, 2];
for (int i = 0; i < WebPConstants.NumMbSegments; i++) for (int i = 0; i < WebpConstants.NumMbSegments; i++)
{ {
this.DeQuantMatrices[i] = new Vp8QuantMatrix(); this.DeQuantMatrices[i] = new Vp8QuantMatrix();
for (int j = 0; j < 2; j++) for (int j = 0; j < 2; j++)
@ -63,10 +63,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
uint width = pictureHeader.Width; uint width = pictureHeader.Width;
uint height = pictureHeader.Height; uint height = pictureHeader.Height;
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 = memoryAllocator.Allocate<byte>((WebPConstants.Bps * 17) + (WebPConstants.Bps * 9) + extraY); this.YuvBuffer = memoryAllocator.Allocate<byte>((WebpConstants.Bps * 17) + (WebpConstants.Bps * 9) + extraY);
this.CacheY = memoryAllocator.Allocate<byte>((16 * this.CacheYStride) + extraY); this.CacheY = memoryAllocator.Allocate<byte>((16 * this.CacheYStride) + extraY);
int cacheUvSize = (16 * this.CacheUvStride) + extraUv; int cacheUvSize = (16 * this.CacheUvStride) + extraUv;
this.CacheU = memoryAllocator.Allocate<byte>(cacheUvSize); this.CacheU = memoryAllocator.Allocate<byte>(cacheUvSize);
@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
this.CacheU.Memory.Span.Fill(205); this.CacheU.Memory.Span.Fill(205);
this.CacheV.Memory.Span.Fill(205); this.CacheV.Memory.Span.Fill(205);
this.Vp8BitReaders = new Vp8BitReader[WebPConstants.MaxNumPartitions]; this.Vp8BitReaders = new Vp8BitReader[WebpConstants.MaxNumPartitions];
} }
/// <summary> /// <summary>
@ -276,7 +276,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
} }
Vp8FilterHeader hdr = this.FilterHeader; Vp8FilterHeader hdr = this.FilterHeader;
for (int s = 0; s < WebPConstants.NumMbSegments; ++s) for (int s = 0; s < WebpConstants.NumMbSegments; ++s)
{ {
int baseLevel; int baseLevel;

40
src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs

@ -2,9 +2,9 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless; using SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
/// <summary> /// <summary>
/// Iterator structure to iterate through macroblocks, pointing to the /// Iterator structure to iterate through macroblocks, pointing to the
@ -67,10 +67,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
this.uvTopIdx = 0; this.uvTopIdx = 0;
this.predsWidth = (4 * mbw) + 1; this.predsWidth = (4 * mbw) + 1;
this.predIdx = this.predsWidth; this.predIdx = this.predsWidth;
this.YuvIn = new byte[WebPConstants.Bps * 16]; this.YuvIn = new byte[WebpConstants.Bps * 16];
this.YuvOut = new byte[WebPConstants.Bps * 16]; this.YuvOut = new byte[WebpConstants.Bps * 16];
this.YuvOut2 = new byte[WebPConstants.Bps * 16]; this.YuvOut2 = new byte[WebpConstants.Bps * 16];
this.YuvP = new byte[(32 * WebPConstants.Bps) + (16 * WebPConstants.Bps) + (8 * WebPConstants.Bps)]; // I16+Chroma+I4 preds this.YuvP = new byte[(32 * WebpConstants.Bps) + (16 * WebpConstants.Bps) + (8 * WebpConstants.Bps)]; // I16+Chroma+I4 preds
this.YLeft = new byte[32]; this.YLeft = new byte[32];
this.UvLeft = new byte[32]; this.UvLeft = new byte[32];
this.TopNz = new int[9]; this.TopNz = new int[9];
@ -355,7 +355,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
uint m2; uint m2;
for (k = 0; k < 16; k += 4) for (k = 0; k < 16; k += 4)
{ {
this.Mean16x4(this.YuvIn.AsSpan(YOffEnc + (k * WebPConstants.Bps)), dc.AsSpan(k)); this.Mean16x4(this.YuvIn.AsSpan(YOffEnc + (k * WebpConstants.Bps)), dc.AsSpan(k));
} }
for (m = 0, m2 = 0, k = 0; k < 16; ++k) for (m = 0, m2 = 0, k = 0; k < 16; ++k)
@ -466,7 +466,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
int y = this.I4 >> 2; int y = this.I4 >> 2;
int left = (x == 0) ? this.Preds[predIdx + (y * predsWidth) - 1] : modes[this.I4 - 1]; int left = (x == 0) ? this.Preds[predIdx + (y * predsWidth) - 1] : modes[this.I4 - 1];
int top = (y == 0) ? this.Preds[predIdx - predsWidth + x] : modes[this.I4 - 4]; int top = (y == 0) ? this.Preds[predIdx - predsWidth + x] : modes[this.I4 - 4];
return WebPLookupTables.Vp8FixedCostsI4[top, left]; return WebpLookupTables.Vp8FixedCostsI4[top, left];
} }
public void SetIntraUvMode(int mode) public void SetIntraUvMode(int mode)
@ -526,13 +526,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
// left // left
for (int i = 0; i < 16; ++i) for (int i = 0; i < 16; ++i)
{ {
this.YLeft[i + 1] = ySrc[15 + (i * WebPConstants.Bps)]; this.YLeft[i + 1] = ySrc[15 + (i * WebpConstants.Bps)];
} }
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
{ {
this.UvLeft[i + 1] = uvSrc[7 + (i * WebPConstants.Bps)]; this.UvLeft[i + 1] = uvSrc[7 + (i * WebpConstants.Bps)];
this.UvLeft[i + 16 + 1] = uvSrc[15 + (i * WebPConstants.Bps)]; this.UvLeft[i + 16 + 1] = uvSrc[15 + (i * WebpConstants.Bps)];
} }
// top-left (before 'top'!) // top-left (before 'top'!)
@ -544,14 +544,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
if (y < this.mbh - 1) if (y < this.mbh - 1)
{ {
// top // top
ySrc.Slice(15 * WebPConstants.Bps, 16).CopyTo(this.YTop.AsSpan(this.yTopIdx)); ySrc.Slice(15 * WebpConstants.Bps, 16).CopyTo(this.YTop.AsSpan(this.yTopIdx));
uvSrc.Slice(7 * WebPConstants.Bps, 8 + 8).CopyTo(this.UvTop.AsSpan(this.uvTopIdx)); uvSrc.Slice(7 * WebpConstants.Bps, 8 + 8).CopyTo(this.UvTop.AsSpan(this.uvTopIdx));
} }
} }
public bool RotateI4(Span<byte> yuvOut) public bool RotateI4(Span<byte> yuvOut)
{ {
Span<byte> blk = yuvOut.Slice(WebPLookupTables.Vp8Scan[this.I4]); Span<byte> blk = yuvOut.Slice(WebpLookupTables.Vp8Scan[this.I4]);
Span<byte> top = this.I4Boundary.AsSpan(); Span<byte> top = this.I4Boundary.AsSpan();
int topOffset = this.I4BoundaryIdx; int topOffset = this.I4BoundaryIdx;
int i; int i;
@ -559,7 +559,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
// Update the cache with 7 fresh samples. // Update the cache with 7 fresh samples.
for (i = 0; i <= 3; ++i) for (i = 0; i <= 3; ++i)
{ {
top[topOffset - 4 + i] = blk[i + (3 * WebPConstants.Bps)]; // Store future top samples. top[topOffset - 4 + i] = blk[i + (3 * WebpConstants.Bps)]; // Store future top samples.
} }
if ((this.I4 & 3) != 3) if ((this.I4 & 3) != 3)
@ -568,7 +568,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
for (i = 0; i <= 2; ++i) for (i = 0; i <= 2; ++i)
{ {
// store future left samples // store future left samples
top[topOffset + i] = blk[3 + ((2 - i) * WebPConstants.Bps)]; top[topOffset + i] = blk[3 + ((2 - i) * WebpConstants.Bps)];
} }
} }
else else
@ -706,7 +706,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
{ {
for (int x = 0; x < 4; ++x) for (int x = 0; x < 4; ++x)
{ {
avg += input[x + (y * WebPConstants.Bps)]; avg += input[x + (y * WebpConstants.Bps)];
} }
} }
@ -727,14 +727,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
dst.Slice(dstIdx, size - w).Fill(dst[dstIdx + w - 1]); dst.Slice(dstIdx, size - w).Fill(dst[dstIdx + w - 1]);
} }
dstIdx += WebPConstants.Bps; dstIdx += WebpConstants.Bps;
srcIdx += srcStride; srcIdx += srcStride;
} }
for (int i = h; i < size; ++i) for (int i = h; i < size; ++i)
{ {
dst.Slice(dstIdx - WebPConstants.Bps, size).CopyTo(dst); dst.Slice(dstIdx - WebpConstants.Bps, size).CopyTo(dst);
dstIdx += WebPConstants.Bps; dstIdx += WebpConstants.Bps;
} }
} }

60
src/ImageSharp/Formats/WebP/Lossy/Vp8EncProba.cs

@ -3,7 +3,7 @@
using System; using System;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
internal class Vp8EncProba internal class Vp8EncProba
{ {
@ -25,37 +25,37 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
this.Dirty = true; this.Dirty = true;
this.UseSkipProba = false; this.UseSkipProba = false;
this.Segments = new byte[3]; this.Segments = new byte[3];
this.Coeffs = new Vp8BandProbas[WebPConstants.NumTypes][]; this.Coeffs = new Vp8BandProbas[WebpConstants.NumTypes][];
for (int i = 0; i < this.Coeffs.Length; i++) for (int i = 0; i < this.Coeffs.Length; i++)
{ {
this.Coeffs[i] = new Vp8BandProbas[WebPConstants.NumBands]; this.Coeffs[i] = new Vp8BandProbas[WebpConstants.NumBands];
for (int j = 0; j < this.Coeffs[i].Length; j++) for (int j = 0; j < this.Coeffs[i].Length; j++)
{ {
this.Coeffs[i][j] = new Vp8BandProbas(); this.Coeffs[i][j] = new Vp8BandProbas();
} }
} }
this.Stats = new Vp8Stats[WebPConstants.NumTypes][]; this.Stats = new Vp8Stats[WebpConstants.NumTypes][];
for (int i = 0; i < this.Coeffs.Length; i++) for (int i = 0; i < this.Coeffs.Length; i++)
{ {
this.Stats[i] = new Vp8Stats[WebPConstants.NumBands]; this.Stats[i] = new Vp8Stats[WebpConstants.NumBands];
for (int j = 0; j < this.Stats[i].Length; j++) for (int j = 0; j < this.Stats[i].Length; j++)
{ {
this.Stats[i][j] = new Vp8Stats(); this.Stats[i][j] = new Vp8Stats();
} }
} }
this.LevelCost = new Vp8CostArray[WebPConstants.NumTypes][]; this.LevelCost = new Vp8CostArray[WebpConstants.NumTypes][];
for (int i = 0; i < this.LevelCost.Length; i++) for (int i = 0; i < this.LevelCost.Length; i++)
{ {
this.LevelCost[i] = new Vp8CostArray[WebPConstants.NumBands]; this.LevelCost[i] = new Vp8CostArray[WebpConstants.NumBands];
for (int j = 0; j < this.LevelCost[i].Length; j++) for (int j = 0; j < this.LevelCost[i].Length; j++)
{ {
this.LevelCost[i][j] = new Vp8CostArray(); this.LevelCost[i][j] = new Vp8CostArray();
} }
} }
this.RemappedCosts = new Vp8CostArray[WebPConstants.NumTypes][]; this.RemappedCosts = new Vp8CostArray[WebpConstants.NumTypes][];
for (int i = 0; i < this.RemappedCosts.Length; i++) for (int i = 0; i < this.RemappedCosts.Length; i++)
{ {
this.RemappedCosts[i] = new Vp8CostArray[16]; this.RemappedCosts[i] = new Vp8CostArray[16];
@ -67,16 +67,16 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
// Initialize with default probabilities. // Initialize with default probabilities.
this.Segments.AsSpan().Fill(255); this.Segments.AsSpan().Fill(255);
for (int t = 0; t < WebPConstants.NumTypes; ++t) for (int t = 0; t < WebpConstants.NumTypes; ++t)
{ {
for (int b = 0; b < WebPConstants.NumBands; ++b) for (int b = 0; b < WebpConstants.NumBands; ++b)
{ {
for (int c = 0; c < WebPConstants.NumCtx; ++c) for (int c = 0; c < WebpConstants.NumCtx; ++c)
{ {
Vp8ProbaArray dst = this.Coeffs[t][b].Probabilities[c]; Vp8ProbaArray dst = this.Coeffs[t][b].Probabilities[c];
for (int p = 0; p < WebPConstants.NumProbas; ++p) for (int p = 0; p < WebpConstants.NumProbas; ++p)
{ {
dst.Probabilities[p] = WebPLookupTables.DefaultCoeffsProba[t, b, c, p]; dst.Probabilities[p] = WebpLookupTables.DefaultCoeffsProba[t, b, c, p];
} }
} }
} }
@ -123,11 +123,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
return; // nothing to do. return; // nothing to do.
} }
for (int ctype = 0; ctype < WebPConstants.NumTypes; ++ctype) for (int ctype = 0; ctype < WebpConstants.NumTypes; ++ctype)
{ {
for (int band = 0; band < WebPConstants.NumBands; ++band) for (int band = 0; band < WebpConstants.NumBands; ++band)
{ {
for (int ctx = 0; ctx < WebPConstants.NumCtx; ++ctx) for (int ctx = 0; ctx < WebpConstants.NumCtx; ++ctx)
{ {
Vp8ProbaArray p = this.Coeffs[ctype][band].Probabilities[ctx]; Vp8ProbaArray p = this.Coeffs[ctype][band].Probabilities[ctx];
Span<ushort> table = this.LevelCost[ctype][band].Costs.AsSpan(ctx * MaxVariableLevel); Span<ushort> table = this.LevelCost[ctype][band].Costs.AsSpan(ctx * MaxVariableLevel);
@ -146,10 +146,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
for (int n = 0; n < 16; ++n) for (int n = 0; n < 16; ++n)
{ {
for (int ctx = 0; ctx < WebPConstants.NumCtx; ++ctx) for (int ctx = 0; ctx < WebpConstants.NumCtx; ++ctx)
{ {
Span<ushort> dst = this.RemappedCosts[ctype][n].Costs.AsSpan(ctx * MaxVariableLevel, MaxVariableLevel); Span<ushort> dst = this.RemappedCosts[ctype][n].Costs.AsSpan(ctx * MaxVariableLevel, MaxVariableLevel);
Span<ushort> src = this.LevelCost[ctype][WebPConstants.Vp8EncBands[n]].Costs.AsSpan(ctx * MaxVariableLevel, MaxVariableLevel); Span<ushort> src = this.LevelCost[ctype][WebpConstants.Vp8EncBands[n]].Costs.AsSpan(ctx * MaxVariableLevel, MaxVariableLevel);
src.CopyTo(dst); src.CopyTo(dst);
} }
} }
@ -162,19 +162,19 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
{ {
bool hasChanged = false; bool hasChanged = false;
int size = 0; int size = 0;
for (int t = 0; t < WebPConstants.NumTypes; ++t) for (int t = 0; t < WebpConstants.NumTypes; ++t)
{ {
for (int b = 0; b < WebPConstants.NumBands; ++b) for (int b = 0; b < WebpConstants.NumBands; ++b)
{ {
for (int c = 0; c < WebPConstants.NumCtx; ++c) for (int c = 0; c < WebpConstants.NumCtx; ++c)
{ {
for (int p = 0; p < WebPConstants.NumProbas; ++p) for (int p = 0; p < WebpConstants.NumProbas; ++p)
{ {
var stats = this.Stats[t][b].Stats[c].Stats[p]; var stats = this.Stats[t][b].Stats[c].Stats[p];
int nb = (int)((stats >> 0) & 0xffff); int nb = (int)((stats >> 0) & 0xffff);
int total = (int)((stats >> 16) & 0xffff); int total = (int)((stats >> 16) & 0xffff);
int updateProba = WebPLookupTables.CoeffsUpdateProba[t, b, c, p]; int updateProba = WebpLookupTables.CoeffsUpdateProba[t, b, c, p];
int oldP = WebPLookupTables.DefaultCoeffsProba[t, b, c, p]; int oldP = WebpLookupTables.DefaultCoeffsProba[t, b, c, p];
int newP = CalcTokenProba(nb, total); int newP = CalcTokenProba(nb, total);
int oldCost = BranchCost(nb, total, oldP) + LossyUtils.Vp8BitCost(0, (byte)updateProba); int oldCost = BranchCost(nb, total, oldP) + LossyUtils.Vp8BitCost(0, (byte)updateProba);
int newCost = BranchCost(nb, total, newP) + LossyUtils.Vp8BitCost(1, (byte)updateProba) + (8 * 256); int newCost = BranchCost(nb, total, newP) + LossyUtils.Vp8BitCost(1, (byte)updateProba) + (8 * 256);
@ -219,13 +219,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
public void ResetTokenStats() public void ResetTokenStats()
{ {
for (int t = 0; t < WebPConstants.NumTypes; ++t) for (int t = 0; t < WebpConstants.NumTypes; ++t)
{ {
for (int b = 0; b < WebPConstants.NumBands; ++b) for (int b = 0; b < WebpConstants.NumBands; ++b)
{ {
for (int c = 0; c < WebPConstants.NumCtx; ++c) for (int c = 0; c < WebpConstants.NumCtx; ++c)
{ {
for (int p = 0; p < WebPConstants.NumProbas; ++p) for (int p = 0; p < WebpConstants.NumProbas; ++p)
{ {
this.Stats[t][b].Stats[c].Stats[p] = 0; this.Stats[t][b].Stats[c].Stats[p] = 0;
} }
@ -241,8 +241,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
private static int VariableLevelCost(int level, Span<byte> probas) private static int VariableLevelCost(int level, Span<byte> probas)
{ {
int pattern = WebPLookupTables.Vp8LevelCodes[level - 1][0]; int pattern = WebpLookupTables.Vp8LevelCodes[level - 1][0];
int bits = WebPLookupTables.Vp8LevelCodes[level - 1][1]; int bits = WebpLookupTables.Vp8LevelCodes[level - 1][1];
int cost = 0; int cost = 0;
for (int i = 2; pattern != 0; ++i) for (int i = 2; pattern != 0; ++i)
{ {

2
src/ImageSharp/Formats/WebP/Lossy/Vp8EncSegmentHeader.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
internal class Vp8EncSegmentHeader internal class Vp8EncSegmentHeader
{ {

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

@ -6,11 +6,11 @@ using System.Buffers;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Experimental.WebP.BitWriter; using SixLabors.ImageSharp.Formats.Experimental.Webp.BitWriter;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
/// <summary> /// <summary>
/// Encoder for lossy webp images. /// Encoder for lossy webp images.
@ -115,9 +115,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
// Convergence is considered reached if dq < DqLimit // Convergence is considered reached if dq < DqLimit
private const float DqLimit = 0.4f; private const float DqLimit = 0.4f;
private const ulong Partition0SizeLimit = (WebPConstants.Vp8MaxPartition0Size - 2048UL) << 11; private const ulong Partition0SizeLimit = (WebpConstants.Vp8MaxPartition0Size - 2048UL) << 11;
private const long HeaderSizeEstimate = WebPConstants.RiffHeaderSize + WebPConstants.ChunkHeaderSize + WebPConstants.Vp8FrameHeaderSize; private const long HeaderSizeEstimate = WebpConstants.RiffHeaderSize + WebpConstants.ChunkHeaderSize + WebpConstants.Vp8FrameHeaderSize;
private const int QMin = 0; private const int QMin = 0;
@ -355,7 +355,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.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]; var 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 = this.alpha / totalMb; this.alpha = this.alpha / totalMb;
@ -550,7 +550,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
if (FilterStrength > 0) if (FilterStrength > 0)
{ {
int maxLevel = 0; int maxLevel = 0;
for (int s = 0; s < WebPConstants.NumMbSegments; s++) for (int s = 0; s < WebpConstants.NumMbSegments; s++)
{ {
Vp8SegmentInfo dqm = this.SegmentInfos[s]; Vp8SegmentInfo dqm = this.SegmentInfos[s];
@ -600,18 +600,18 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
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]; var centers = new int[NumMbSegments];
int weightedAverage = 0; int weightedAverage = 0;
var map = new int[WebPConstants.MaxAlpha + 1]; var map = new int[WebpConstants.MaxAlpha + 1];
int a, n, k; int a, n, k;
var accum = new int[NumMbSegments]; var accum = new int[NumMbSegments];
var distAccum = new int[NumMbSegments]; var 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; var minA = n;
for (n = WebPConstants.MaxAlpha; n > minA && alphas[n] == 0; --n) for (n = WebpConstants.MaxAlpha; n > minA && alphas[n] == 0; --n)
{ {
} }
@ -730,7 +730,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
int nb = this.segmentHeader.NumSegments; int nb = this.segmentHeader.NumSegments;
Vp8SegmentInfo[] dqm = this.SegmentInfos; Vp8SegmentInfo[] dqm = this.SegmentInfos;
int snsStrength = 50; // TODO: Spatial Noise Shaping, hardcoded for now. int snsStrength = 50; // TODO: Spatial Noise Shaping, hardcoded for now.
double amp = WebPConstants.SnsToDq * snsStrength / 100.0d / 128.0d; double amp = WebpConstants.SnsToDq * snsStrength / 100.0d / 128.0d;
double cBase = QualityToCompression(quality / 100.0d); double cBase = QualityToCompression(quality / 100.0d);
for (int i = 0; i < nb; ++i) for (int i = 0; i < nb; ++i)
{ {
@ -748,13 +748,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
// uvAlpha is normally spread around ~60. The useful range is // uvAlpha is normally spread around ~60. The useful range is
// typically ~30 (quite bad) to ~100 (ok to decimate UV more). // typically ~30 (quite bad) to ~100 (ok to decimate UV more).
// We map it to the safe maximal range of MAX/MIN_DQ_UV for dq_uv. // We map it to the safe maximal range of MAX/MIN_DQ_UV for dq_uv.
this.dqUvAc = (this.uvAlpha - WebPConstants.QuantEncMidAlpha) * (WebPConstants.QuantEncMaxDqUv - WebPConstants.QuantEncMinDqUv) / (WebPConstants.QuantEncMaxAlpha - WebPConstants.QuantEncMinAlpha); this.dqUvAc = (this.uvAlpha - WebpConstants.QuantEncMidAlpha) * (WebpConstants.QuantEncMaxDqUv - WebpConstants.QuantEncMinDqUv) / (WebpConstants.QuantEncMaxAlpha - WebpConstants.QuantEncMinAlpha);
// We rescale by the user-defined strength of adaptation. // We rescale by the user-defined strength of adaptation.
this.dqUvAc = this.dqUvAc * snsStrength / 100; this.dqUvAc = this.dqUvAc * snsStrength / 100;
// and make it safe. // and make it safe.
this.dqUvAc = Clip(this.dqUvAc, WebPConstants.QuantEncMinDqUv, WebPConstants.QuantEncMaxDqUv); this.dqUvAc = Clip(this.dqUvAc, WebpConstants.QuantEncMinDqUv, WebpConstants.QuantEncMaxDqUv);
// We also boost the dc-uv-quant a little, based on sns-strength, since // We also boost the dc-uv-quant a little, based on sns-strength, since
// U/V channels are quite more reactive to high quants (flat DC-blocks // U/V channels are quite more reactive to high quants (flat DC-blocks
@ -779,17 +779,17 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
// 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.
int level0 = 5 * FilterStrength; int level0 = 5 * FilterStrength;
for (int i = 0; i < WebPConstants.NumMbSegments; ++i) for (int i = 0; i < WebpConstants.NumMbSegments; ++i)
{ {
Vp8SegmentInfo m = this.SegmentInfos[i]; Vp8SegmentInfo m = this.SegmentInfos[i];
// We focus on the quantization of AC coeffs. // We focus on the quantization of AC coeffs.
int qstep = WebPLookupTables.AcTable[Clip(m.Quant, 0, 127)] >> 2; int qstep = WebpLookupTables.AcTable[Clip(m.Quant, 0, 127)] >> 2;
int baseStrength = this.FilterStrengthFromDelta(this.filterHeader.Sharpness, qstep); int baseStrength = this.FilterStrengthFromDelta(this.filterHeader.Sharpness, qstep);
// Segments with lower complexity ('beta') will be less filtered. // Segments with lower complexity ('beta') will be less filtered.
int f = baseStrength * level0 / (256 + m.Beta); int f = baseStrength * level0 / (256 + m.Beta);
m.FStrength = (f < WebPConstants.FilterStrengthCutoff) ? 0 : (f > 63) ? 63 : f; m.FStrength = (f < WebpConstants.FilterStrengthCutoff) ? 0 : (f > 63) ? 63 : f;
} }
// We record the initial strength (mainly for the case of 1-segment only). // We record the initial strength (mainly for the case of 1-segment only).
@ -861,14 +861,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
m.Y2 = new Vp8Matrix(); m.Y2 = new Vp8Matrix();
m.Uv = new Vp8Matrix(); m.Uv = new Vp8Matrix();
m.Y1.Q[0] = WebPLookupTables.DcTable[Clip(q, 0, 127)]; m.Y1.Q[0] = WebpLookupTables.DcTable[Clip(q, 0, 127)];
m.Y1.Q[1] = WebPLookupTables.AcTable[Clip(q, 0, 127)]; m.Y1.Q[1] = WebpLookupTables.AcTable[Clip(q, 0, 127)];
m.Y2.Q[0] = (ushort)(WebPLookupTables.DcTable[Clip(q, 0, 127)] * 2); m.Y2.Q[0] = (ushort)(WebpLookupTables.DcTable[Clip(q, 0, 127)] * 2);
m.Y2.Q[1] = WebPLookupTables.AcTable2[Clip(q, 0, 127)]; m.Y2.Q[1] = WebpLookupTables.AcTable2[Clip(q, 0, 127)];
m.Uv.Q[0] = WebPLookupTables.DcTable[Clip(q + this.dqUvDc, 0, 117)]; m.Uv.Q[0] = WebpLookupTables.DcTable[Clip(q + this.dqUvDc, 0, 117)];
m.Uv.Q[1] = WebPLookupTables.AcTable[Clip(q + this.dqUvAc, 0, 127)]; m.Uv.Q[1] = WebpLookupTables.AcTable[Clip(q + this.dqUvAc, 0, 127)];
var qi4 = m.Y1.Expand(0); var qi4 = m.Y1.Expand(0);
var qi16 = m.Y2.Expand(1); var qi16 = m.Y2.Expand(1);
@ -974,9 +974,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
for (mode = 0; mode < numPredModes; ++mode) for (mode = 0; mode < numPredModes; ++mode)
{ {
Span<byte> reference = it.YuvP.AsSpan(Vp8Encoding.Vp8I16ModeOffsets[mode]); Span<byte> reference = it.YuvP.AsSpan(Vp8Encoding.Vp8I16ModeOffsets[mode]);
long score = (Vp8Sse16X16(src, reference) * WebPConstants.RdDistoMult) + (WebPConstants.Vp8FixedCostsI16[mode] * lambdaDi16); long score = (Vp8Sse16X16(src, reference) * WebpConstants.RdDistoMult) + (WebpConstants.Vp8FixedCostsI16[mode] * lambdaDi16);
if (mode > 0 && WebPConstants.Vp8FixedCostsI16[mode] > bitLimit) if (mode > 0 && WebpConstants.Vp8FixedCostsI16[mode] > bitLimit)
{ {
continue; continue;
} }
@ -1014,14 +1014,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
{ {
int bestI4Mode = -1; int bestI4Mode = -1;
long bestI4Score = Vp8ModeScore.MaxCost; long bestI4Score = Vp8ModeScore.MaxCost;
Span<byte> src = it.YuvIn.AsSpan(Vp8EncIterator.YOffEnc + WebPLookupTables.Vp8Scan[it.I4]); Span<byte> src = it.YuvIn.AsSpan(Vp8EncIterator.YOffEnc + WebpLookupTables.Vp8Scan[it.I4]);
short[] modeCosts = it.GetCostModeI4(rd.ModesI4); short[] modeCosts = it.GetCostModeI4(rd.ModesI4);
it.MakeIntra4Preds(); it.MakeIntra4Preds();
for (mode = 0; mode < numBModes; ++mode) for (mode = 0; mode < numBModes; ++mode)
{ {
Span<byte> reference = it.YuvP.AsSpan(Vp8Encoding.Vp8I4ModeOffsets[mode]); Span<byte> reference = it.YuvP.AsSpan(Vp8Encoding.Vp8I4ModeOffsets[mode]);
long score = (Vp8Sse4X4(src, reference) * WebPConstants.RdDistoMult) + (modeCosts[mode] * lambdaDi4); long score = (Vp8Sse4X4(src, reference) * WebpConstants.RdDistoMult) + (modeCosts[mode] * lambdaDi4);
if (score < bestI4Score) if (score < bestI4Score)
{ {
bestI4Mode = mode; bestI4Mode = mode;
@ -1041,7 +1041,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
else else
{ {
// Reconstruct partial block inside YuvOut2 buffer // Reconstruct partial block inside YuvOut2 buffer
Span<byte> tmpDst = it.YuvOut2.AsSpan(Vp8EncIterator.YOffEnc + WebPLookupTables.Vp8Scan[it.I4]); Span<byte> tmpDst = it.YuvOut2.AsSpan(Vp8EncIterator.YOffEnc + WebpLookupTables.Vp8Scan[it.I4]);
nz |= this.ReconstructIntra4(it, dqm, rd.YAcLevels.AsSpan(it.I4 * 16, 16), src, tmpDst, bestI4Mode) << it.I4; nz |= this.ReconstructIntra4(it, dqm, rd.YAcLevels.AsSpan(it.I4 * 16, 16), src, tmpDst, bestI4Mode) << it.I4;
} }
} }
@ -1070,7 +1070,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
for (mode = 0; mode < numPredModes; ++mode) for (mode = 0; mode < numPredModes; ++mode)
{ {
Span<byte> reference = it.YuvP.AsSpan(Vp8Encoding.Vp8UvModeOffsets[mode]); Span<byte> reference = it.YuvP.AsSpan(Vp8Encoding.Vp8UvModeOffsets[mode]);
long score = (Vp8Sse16X8(src, reference) * WebPConstants.RdDistoMult) + (WebPConstants.Vp8FixedCostsUv[mode] * lambdaDuv); long score = (Vp8Sse16X8(src, reference) * WebpConstants.RdDistoMult) + (WebpConstants.Vp8FixedCostsUv[mode] * lambdaDuv);
if (score < bestUvScore) if (score < bestUvScore)
{ {
bestMode = mode; bestMode = mode;
@ -1222,7 +1222,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
for (n = 0; n < 16; n += 2) for (n = 0; n < 16; n += 2)
{ {
Vp8Encoding.FTransform2(src.Slice(WebPLookupTables.Vp8Scan[n]), reference.Slice(WebPLookupTables.Vp8Scan[n]), tmpSpan.Slice(n * 16, 16), tmpSpan.Slice((n + 1) * 16, 16)); Vp8Encoding.FTransform2(src.Slice(WebpLookupTables.Vp8Scan[n]), reference.Slice(WebpLookupTables.Vp8Scan[n]), tmpSpan.Slice(n * 16, 16), tmpSpan.Slice((n + 1) * 16, 16));
} }
Vp8Encoding.FTransformWht(tmp, dcTmp); Vp8Encoding.FTransformWht(tmp, dcTmp);
@ -1240,7 +1240,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
LossyUtils.TransformWht(dcTmp, tmpSpan); LossyUtils.TransformWht(dcTmp, tmpSpan);
for (n = 0; n < 16; n += 2) for (n = 0; n < 16; n += 2)
{ {
Vp8Encoding.ITransform(reference.Slice(WebPLookupTables.Vp8Scan[n]), tmpSpan.Slice(n * 16, 32), yuvOut.Slice(WebPLookupTables.Vp8Scan[n]), true); Vp8Encoding.ITransform(reference.Slice(WebpLookupTables.Vp8Scan[n]), tmpSpan.Slice(n * 16, 32), yuvOut.Slice(WebpLookupTables.Vp8Scan[n]), true);
} }
return nz; return nz;
@ -1268,8 +1268,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
for (n = 0; n < 8; n += 2) for (n = 0; n < 8; n += 2)
{ {
Vp8Encoding.FTransform2( Vp8Encoding.FTransform2(
src.Slice(WebPLookupTables.Vp8ScanUv[n]), src.Slice(WebpLookupTables.Vp8ScanUv[n]),
reference.Slice(WebPLookupTables.Vp8ScanUv[n]), reference.Slice(WebpLookupTables.Vp8ScanUv[n]),
tmp.AsSpan(n * 16, 16), tmp.AsSpan(n * 16, 16),
tmp.AsSpan((n + 1) * 16, 16)); tmp.AsSpan((n + 1) * 16, 16));
} }
@ -1283,7 +1283,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
for (n = 0; n < 8; n += 2) for (n = 0; n < 8; n += 2)
{ {
Vp8Encoding.ITransform(reference.Slice(WebPLookupTables.Vp8ScanUv[n]), tmp.AsSpan(n * 16, 32), yuvOut.Slice(WebPLookupTables.Vp8ScanUv[n]), true); Vp8Encoding.ITransform(reference.Slice(WebpLookupTables.Vp8ScanUv[n]), tmp.AsSpan(n * 16, 32), yuvOut.Slice(WebpLookupTables.Vp8ScanUv[n]), true);
} }
return nz << 16; return nz << 16;
@ -1346,8 +1346,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static int FinalAlphaValue(int alpha) private static int FinalAlphaValue(int alpha)
{ {
alpha = WebPConstants.MaxAlpha - alpha; alpha = WebpConstants.MaxAlpha - alpha;
return Clip(alpha, 0, WebPConstants.MaxAlpha); return Clip(alpha, 0, WebpConstants.MaxAlpha);
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
@ -1387,8 +1387,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
count += diff * diff; count += diff * diff;
} }
aOffset += WebPConstants.Bps; aOffset += WebpConstants.Bps;
bOffset += WebPConstants.Bps; bOffset += WebpConstants.Bps;
} }
return count; return count;
@ -1407,7 +1407,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
return false; return false;
} }
src = src.Slice(WebPConstants.Bps); src = src.Slice(WebpConstants.Bps);
} }
return true; return true;
@ -1435,8 +1435,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
private int FilterStrengthFromDelta(int sharpness, int delta) private int FilterStrengthFromDelta(int sharpness, int delta)
{ {
int pos = (delta < WebPConstants.MaxDelzaSize) ? delta : WebPConstants.MaxDelzaSize - 1; int pos = (delta < WebpConstants.MaxDelzaSize) ? delta : WebpConstants.MaxDelzaSize - 1;
return WebPLookupTables.LevelsFromDelta[sharpness, pos]; return WebpLookupTables.LevelsFromDelta[sharpness, pos];
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

38
src/ImageSharp/Formats/WebP/Lossy/Vp8Encoding.cs

@ -5,7 +5,7 @@ using System;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
/// <summary> /// <summary>
/// Methods for encoding a VP8 frame. /// Methods for encoding a VP8 frame.
@ -18,19 +18,19 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
private static readonly byte[] Clip1 = new byte[255 + 510 + 1]; // clips [-255,510] to [0,255] private static readonly byte[] Clip1 = new byte[255 + 510 + 1]; // clips [-255,510] to [0,255]
private const int I16DC16 = 0 * 16 * WebPConstants.Bps; private const int I16DC16 = 0 * 16 * WebpConstants.Bps;
private const int I16TM16 = I16DC16 + 16; private const int I16TM16 = I16DC16 + 16;
private const int I16VE16 = 1 * 16 * WebPConstants.Bps; private const int I16VE16 = 1 * 16 * WebpConstants.Bps;
private const int I16HE16 = I16VE16 + 16; private const int I16HE16 = I16VE16 + 16;
private const int C8DC8 = 2 * 16 * WebPConstants.Bps; private const int C8DC8 = 2 * 16 * WebpConstants.Bps;
private const int C8TM8 = C8DC8 + (1 * 16); private const int C8TM8 = C8DC8 + (1 * 16);
private const int C8VE8 = (2 * 16 * WebPConstants.Bps) + (8 * WebPConstants.Bps); private const int C8VE8 = (2 * 16 * WebpConstants.Bps) + (8 * WebpConstants.Bps);
private const int C8HE8 = C8VE8 + (1 * 16); private const int C8HE8 = C8VE8 + (1 * 16);
@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
public static readonly int[] Vp8UvModeOffsets = { C8DC8, C8TM8, C8VE8, C8HE8 }; public static readonly int[] Vp8UvModeOffsets = { C8DC8, C8TM8, C8VE8, C8HE8 };
private const int I4DC4 = (3 * 16 * WebPConstants.Bps) + 0; private const int I4DC4 = (3 * 16 * WebpConstants.Bps) + 0;
private const int I4TM4 = I4DC4 + 4; private const int I4TM4 = I4DC4 + 4;
@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
private const int I4VL4 = I4DC4 + 28; private const int I4VL4 = I4DC4 + 28;
private const int I4HD4 = (3 * 16 * WebPConstants.Bps) + (4 * WebPConstants.Bps); private const int I4HD4 = (3 * 16 * WebpConstants.Bps) + (4 * WebpConstants.Bps);
private const int I4HU4 = I4HD4 + 4; private const int I4HU4 = I4HD4 + 4;
@ -143,8 +143,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
tmp[2 + (i * 4)] = (a0 - a1) * 8; tmp[2 + (i * 4)] = (a0 - a1) * 8;
tmp[3 + (i * 4)] = ((a3 * 2217) - (a2 * 5352) + 937) >> 9; tmp[3 + (i * 4)] = ((a3 * 2217) - (a2 * 5352) + 937) >> 9;
srcIdx += WebPConstants.Bps; srcIdx += WebpConstants.Bps;
refIdx += WebPConstants.Bps; refIdx += WebpConstants.Bps;
} }
for (i = 0; i < 4; ++i) for (i = 0; i < 4; ++i)
@ -254,7 +254,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
{ {
for (int j = 0; j < size; ++j) for (int j = 0; j < size; ++j)
{ {
top.Slice(0, size).CopyTo(dst.Slice(j * WebPConstants.Bps)); top.Slice(0, size).CopyTo(dst.Slice(j * WebpConstants.Bps));
} }
} }
else else
@ -270,7 +270,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
left = left.Slice(1); // in the reference implementation, left starts at - 1. left = left.Slice(1); // in the reference implementation, left starts at - 1.
for (int j = 0; j < size; ++j) for (int j = 0; j < size; ++j)
{ {
dst.Slice(j * WebPConstants.Bps, size).Fill(left[j]); dst.Slice(j * WebpConstants.Bps, size).Fill(left[j]);
} }
} }
else else
@ -294,7 +294,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
dst[x] = clipTable[top[x]]; dst[x] = clipTable[top[x]];
} }
dst = dst.Slice(WebPConstants.Bps); dst = dst.Slice(WebpConstants.Bps);
} }
} }
else else
@ -391,7 +391,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
dst[x] = clipTable[top[topOffset + x]]; dst[x] = clipTable[top[topOffset + x]];
} }
dst = dst.Slice(WebPConstants.Bps); dst = dst.Slice(WebpConstants.Bps);
} }
} }
@ -408,7 +408,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
{ {
vals.AsSpan().CopyTo(dst.Slice(i * WebPConstants.Bps)); vals.AsSpan().CopyTo(dst.Slice(i * WebpConstants.Bps));
} }
} }
@ -424,11 +424,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
uint val = 0x01010101U * LossyUtils.Avg3(x, i, j); uint val = 0x01010101U * LossyUtils.Avg3(x, i, j);
BinaryPrimitives.WriteUInt32BigEndian(dst, val); BinaryPrimitives.WriteUInt32BigEndian(dst, val);
val = 0x01010101U * LossyUtils.Avg3(i, j, k); val = 0x01010101U * LossyUtils.Avg3(i, j, k);
BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(1 * WebPConstants.Bps), val); BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(1 * WebpConstants.Bps), val);
val = 0x01010101U * LossyUtils.Avg3(j, k, l); val = 0x01010101U * LossyUtils.Avg3(j, k, l);
BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(2 * WebPConstants.Bps), val); BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(2 * WebpConstants.Bps), val);
val = 0x01010101U * LossyUtils.Avg3(k, l, l); val = 0x01010101U * LossyUtils.Avg3(k, l, l);
BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(3 * WebPConstants.Bps), val); BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(3 * WebpConstants.Bps), val);
} }
private static void Rd4(Span<byte> dst, Span<byte> top, int topOffset) private static void Rd4(Span<byte> dst, Span<byte> top, int topOffset)
@ -639,7 +639,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
{ {
for (int j = 0; j < size; ++j) for (int j = 0; j < size; ++j)
{ {
dst.Slice(j * WebPConstants.Bps, size).Fill((byte)value); dst.Slice(j * WebpConstants.Bps, size).Fill((byte)value);
} }
} }
@ -652,7 +652,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static void Store(Span<byte> dst, Span<byte> reference, int x, int y, int v) private static void Store(Span<byte> dst, Span<byte> reference, int x, int y, int v)
{ {
dst[x + (y * WebPConstants.Bps)] = LossyUtils.Clip8B(reference[x + (y * WebPConstants.Bps)] + (v >> 3)); dst[x + (y * WebpConstants.Bps)] = LossyUtils.Clip8B(reference[x + (y * WebpConstants.Bps)] + (v >> 3));
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]

2
src/ImageSharp/Formats/WebP/Lossy/Vp8FilterHeader.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
internal class Vp8FilterHeader internal class Vp8FilterHeader
{ {

2
src/ImageSharp/Formats/WebP/Lossy/Vp8FilterInfo.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
/// <summary> /// <summary>
/// Filter information. /// Filter information.

2
src/ImageSharp/Formats/WebP/Lossy/Vp8FrameHeader.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
/// <summary> /// <summary>
/// Vp8 frame header information. /// Vp8 frame header information.

2
src/ImageSharp/Formats/WebP/Lossy/Vp8Io.cs

@ -3,7 +3,7 @@
using System; using System;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
internal ref struct Vp8Io internal ref struct Vp8Io
{ {

2
src/ImageSharp/Formats/WebP/Lossy/Vp8MacroBlock.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
/// <summary> /// <summary>
/// Contextual macroblock information. /// Contextual macroblock information.

2
src/ImageSharp/Formats/WebP/Lossy/Vp8MacroBlockData.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
/// <summary> /// <summary>
/// Data needed to reconstruct a macroblock. /// Data needed to reconstruct a macroblock.

2
src/ImageSharp/Formats/WebP/Lossy/Vp8MacroBlockInfo.cs

@ -3,7 +3,7 @@
using System.Diagnostics; using System.Diagnostics;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
[DebuggerDisplay("Type: {MacroBlockType}, Alpha: {Alpha}, UvMode: {UvMode}")] [DebuggerDisplay("Type: {MacroBlockType}, Alpha: {Alpha}, UvMode: {UvMode}")]
internal class Vp8MacroBlockInfo internal class Vp8MacroBlockInfo

2
src/ImageSharp/Formats/WebP/Lossy/Vp8MacroBlockType.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
internal enum Vp8MacroBlockType internal enum Vp8MacroBlockType
{ {

8
src/ImageSharp/Formats/WebP/Lossy/Vp8Matrix.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
internal class Vp8Matrix internal class Vp8Matrix
{ {
@ -71,13 +71,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
{ {
int isAcCoeff = (i > 0) ? 1 : 0; int isAcCoeff = (i > 0) ? 1 : 0;
int bias = BiasMatrices[type][isAcCoeff]; int bias = BiasMatrices[type][isAcCoeff];
this.IQ[i] = (ushort)((1 << WebPConstants.QFix) / this.Q[i]); this.IQ[i] = (ushort)((1 << WebpConstants.QFix) / this.Q[i]);
this.Bias[i] = (uint)this.BIAS(bias); this.Bias[i] = (uint)this.BIAS(bias);
// zthresh is the exact value such that QUANTDIV(coeff, iQ, B) is: // zthresh is the exact value such that QUANTDIV(coeff, iQ, B) is:
// * zero if coeff <= zthresh // * zero if coeff <= zthresh
// * non-zero if coeff > zthresh // * non-zero if coeff > zthresh
this.ZThresh[i] = ((1 << WebPConstants.QFix) - 1 - this.Bias[i]) / this.IQ[i]; this.ZThresh[i] = ((1 << WebpConstants.QFix) - 1 - this.Bias[i]) / this.IQ[i];
} }
for (i = 2; i < 16; ++i) for (i = 2; i < 16; ++i)
@ -108,7 +108,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
private int BIAS(int b) private int BIAS(int b)
{ {
return b << (WebPConstants.QFix - 8); return b << (WebpConstants.QFix - 8);
} }
} }
} }

2
src/ImageSharp/Formats/WebP/Lossy/Vp8ModeScore.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
/// <summary> /// <summary>
/// Class to accumulate score and info during RD-optimization and mode evaluation. /// Class to accumulate score and info during RD-optimization and mode evaluation.

2
src/ImageSharp/Formats/WebP/Lossy/Vp8PictureHeader.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
internal class Vp8PictureHeader internal class Vp8PictureHeader
{ {

12
src/ImageSharp/Formats/WebP/Lossy/Vp8Proba.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
/// <summary> /// <summary>
/// Data for all frame-persistent probabilities. /// Data for all frame-persistent probabilities.
@ -16,18 +16,18 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
public Vp8Proba() public Vp8Proba()
{ {
this.Segments = new uint[MbFeatureTreeProbs]; this.Segments = new uint[MbFeatureTreeProbs];
this.Bands = new Vp8BandProbas[WebPConstants.NumTypes, WebPConstants.NumBands]; this.Bands = new Vp8BandProbas[WebpConstants.NumTypes, WebpConstants.NumBands];
this.BandsPtr = new Vp8BandProbas[WebPConstants.NumTypes][]; this.BandsPtr = new Vp8BandProbas[WebpConstants.NumTypes][];
for (int i = 0; i < WebPConstants.NumTypes; i++) for (int i = 0; i < WebpConstants.NumTypes; i++)
{ {
for (int j = 0; j < WebPConstants.NumBands; j++) for (int j = 0; j < WebpConstants.NumBands; j++)
{ {
this.Bands[i, j] = new Vp8BandProbas(); this.Bands[i, j] = new Vp8BandProbas();
} }
} }
for (int i = 0; i < WebPConstants.NumTypes; i++) for (int i = 0; i < WebpConstants.NumTypes; i++)
{ {
this.BandsPtr[i] = new Vp8BandProbas[16 + 1]; this.BandsPtr[i] = new Vp8BandProbas[16 + 1];
} }

4
src/ImageSharp/Formats/WebP/Lossy/Vp8ProbaArray.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
/// <summary> /// <summary>
/// Probabilities associated to one of the contexts. /// Probabilities associated to one of the contexts.
@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
/// </summary> /// </summary>
public Vp8ProbaArray() public Vp8ProbaArray()
{ {
this.Probabilities = new byte[WebPConstants.NumProbas]; this.Probabilities = new byte[WebpConstants.NumProbas];
} }
/// <summary> /// <summary>

2
src/ImageSharp/Formats/WebP/Lossy/Vp8QuantMatrix.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
internal class Vp8QuantMatrix internal class Vp8QuantMatrix
{ {

2
src/ImageSharp/Formats/WebP/Lossy/Vp8RDLevel.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
/// <summary> /// <summary>
/// Rate-distortion optimization levels /// Rate-distortion optimization levels

12
src/ImageSharp/Formats/WebP/Lossy/Vp8Residual.cs

@ -3,7 +3,7 @@
using System; using System;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
/// <summary> /// <summary>
/// On-the-fly info about the current set of residuals. /// On-the-fly info about the current set of residuals.
@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
while ((v = this.Coeffs[n++]) == 0) while ((v = this.Coeffs[n++]) == 0)
{ {
this.RecordStats(0, s, 1); this.RecordStats(0, s, 1);
s = this.Stats[WebPConstants.Vp8EncBands[n]].Stats[0]; s = this.Stats[WebpConstants.Vp8EncBands[n]].Stats[0];
} }
this.RecordStats(1, s, 1); this.RecordStats(1, s, 1);
@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
if (this.RecordStats(bit ? 1 : 0, s, 2) == 0) if (this.RecordStats(bit ? 1 : 0, s, 2) == 0)
{ {
// v = -1 or 1 // v = -1 or 1
s = this.Stats[WebPConstants.Vp8EncBands[n]].Stats[1]; s = this.Stats[WebpConstants.Vp8EncBands[n]].Stats[1];
} }
else else
{ {
@ -88,8 +88,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
v = MaxVariableLevel; v = MaxVariableLevel;
} }
int bits = WebPLookupTables.Vp8LevelCodes[v - 1][1]; int bits = WebpLookupTables.Vp8LevelCodes[v - 1][1];
int pattern = WebPLookupTables.Vp8LevelCodes[v - 1][0]; int pattern = WebpLookupTables.Vp8LevelCodes[v - 1][0];
int i; int i;
for (i = 0; (pattern >>= 1) != 0; ++i) for (i = 0; (pattern >>= 1) != 0; ++i)
{ {
@ -100,7 +100,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
} }
} }
s = this.Stats[WebPConstants.Vp8EncBands[n]].Stats[2]; s = this.Stats[WebpConstants.Vp8EncBands[n]].Stats[2];
} }
} }

2
src/ImageSharp/Formats/WebP/Lossy/Vp8SegmentHeader.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
/// <summary> /// <summary>
/// Segment features. /// Segment features.

2
src/ImageSharp/Formats/WebP/Lossy/Vp8SegmentInfo.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
internal class Vp8SegmentInfo internal class Vp8SegmentInfo
{ {

6
src/ImageSharp/Formats/WebP/Lossy/Vp8Stats.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
internal class Vp8Stats internal class Vp8Stats
{ {
@ -10,8 +10,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
/// </summary> /// </summary>
public Vp8Stats() public Vp8Stats()
{ {
this.Stats = new Vp8StatsArray[WebPConstants.NumCtx]; this.Stats = new Vp8StatsArray[WebpConstants.NumCtx];
for (int i = 0; i < WebPConstants.NumCtx; i++) for (int i = 0; i < WebpConstants.NumCtx; i++)
{ {
this.Stats[i] = new Vp8StatsArray(); this.Stats[i] = new Vp8StatsArray();
} }

4
src/ImageSharp/Formats/WebP/Lossy/Vp8StatsArray.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
internal class Vp8StatsArray internal class Vp8StatsArray
{ {
@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
/// </summary> /// </summary>
public Vp8StatsArray() public Vp8StatsArray()
{ {
this.Stats = new uint[WebPConstants.NumProbas]; this.Stats = new uint[WebpConstants.NumProbas];
} }
public uint[] Stats { get; } public uint[] Stats { get; }

2
src/ImageSharp/Formats/WebP/Lossy/Vp8TopSamples.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
internal class Vp8TopSamples internal class Vp8TopSamples
{ {

118
src/ImageSharp/Formats/WebP/Lossy/WebPLossyDecoder.cs

@ -6,11 +6,11 @@ using System.Buffers;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Experimental.WebP.BitReader; using SixLabors.ImageSharp.Formats.Experimental.Webp.BitReader;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
/// <summary> /// <summary>
/// Decoder for lossy webp images. This code is a port of libwebp, which can be found here: https://chromium.googlesource.com/webm/libwebp /// Decoder for lossy webp images. This code is a port of libwebp, which can be found here: https://chromium.googlesource.com/webm/libwebp
@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
this.configuration = configuration; this.configuration = configuration;
} }
public void Decode<TPixel>(Buffer2D<TPixel> pixels, int width, int height, WebPImageInfo info) public void Decode<TPixel>(Buffer2D<TPixel> pixels, int width, int height, WebpImageInfo info)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
// Paragraph 9.2: color space and clamp type follow. // Paragraph 9.2: color space and clamp type follow.
@ -227,11 +227,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
int yMode = left[y]; int yMode = left[y];
for (int x = 0; x < 4; ++x) for (int x = 0; x < 4; ++x)
{ {
byte[] prob = WebPLookupTables.ModesProba[top[x], yMode]; byte[] prob = WebpLookupTables.ModesProba[top[x], yMode];
int i = WebPConstants.YModesIntra4[this.bitReader.GetBit(prob[0])]; int i = WebpConstants.YModesIntra4[this.bitReader.GetBit(prob[0])];
while (i > 0) while (i > 0)
{ {
i = WebPConstants.YModesIntra4[(2 * i) + this.bitReader.GetBit(prob[i])]; i = WebpConstants.YModesIntra4[(2 * i) + this.bitReader.GetBit(prob[i])];
} }
yMode = -i; yMode = -i;
@ -272,8 +272,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
private void ReconstructRow(Vp8Decoder dec) private void ReconstructRow(Vp8Decoder dec)
{ {
int mby = dec.MbY; int mby = dec.MbY;
const int yOff = (WebPConstants.Bps * 1) + 8; const int yOff = (WebpConstants.Bps * 1) + 8;
const int uOff = yOff + (WebPConstants.Bps * 16) + WebPConstants.Bps; const int uOff = yOff + (WebpConstants.Bps * 16) + WebpConstants.Bps;
const int vOff = uOff + 16; const int vOff = uOff + 16;
Span<byte> yuv = dec.YuvBuffer.Memory.Span; Span<byte> yuv = dec.YuvBuffer.Memory.Span;
@ -282,14 +282,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
Span<byte> vDst = yuv.Slice(vOff); Span<byte> vDst = yuv.Slice(vOff);
// Initialize left-most block. // Initialize left-most block.
var end = 16 * WebPConstants.Bps; var end = 16 * WebpConstants.Bps;
for (int i = 0; i < end; i += WebPConstants.Bps) for (int i = 0; i < end; i += WebpConstants.Bps)
{ {
yuv[i - 1 + yOff] = 129; yuv[i - 1 + yOff] = 129;
} }
end = 8 * WebPConstants.Bps; end = 8 * WebpConstants.Bps;
for (int i = 0; i < end; i += WebPConstants.Bps) for (int i = 0; i < end; i += WebpConstants.Bps)
{ {
yuv[i - 1 + uOff] = 129; yuv[i - 1 + uOff] = 129;
yuv[i - 1 + vOff] = 129; yuv[i - 1 + vOff] = 129;
@ -298,25 +298,25 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
// Init top-left sample on left column too. // Init top-left sample on left column too.
if (mby > 0) if (mby > 0)
{ {
yuv[yOff - 1 - WebPConstants.Bps] = yuv[uOff - 1 - WebPConstants.Bps] = yuv[vOff - 1 - WebPConstants.Bps] = 129; yuv[yOff - 1 - WebpConstants.Bps] = yuv[uOff - 1 - WebpConstants.Bps] = yuv[vOff - 1 - WebpConstants.Bps] = 129;
} }
else else
{ {
// 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 = yuv.Slice(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 = yuv.Slice(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 = yuv.Slice(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;
@ -334,18 +334,18 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
{ {
for (int i = -1; i < 16; ++i) for (int i = -1; i < 16; ++i)
{ {
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.Slice(srcIdx, 4).CopyTo(yuv.Slice(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.Slice(srcIdx, 4).CopyTo(yuv.Slice(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.Slice(srcIdx, 4).CopyTo(yuv.Slice(dstIdx)); yuv.Slice(srcIdx, 4).CopyTo(yuv.Slice(dstIdx));
} }
} }
@ -356,15 +356,15 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
uint bits = block.NonZeroY; uint bits = block.NonZeroY;
if (mby > 0) if (mby > 0)
{ {
topYuv.Y.CopyTo(yuv.Slice(yOff - WebPConstants.Bps)); topYuv.Y.CopyTo(yuv.Slice(yOff - WebpConstants.Bps));
topYuv.U.CopyTo(yuv.Slice(uOff - WebPConstants.Bps)); topYuv.U.CopyTo(yuv.Slice(uOff - WebpConstants.Bps));
topYuv.V.CopyTo(yuv.Slice(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.Slice(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)
@ -383,13 +383,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
} }
// Replicate the top-right pixels below. // Replicate the top-right pixels below.
Span<uint> topRightUint = MemoryMarshal.Cast<byte, uint>(yuv.Slice(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.Slice(offset); Span<byte> dst = yuv.Slice(offset);
byte lumaMode = block.Modes[n]; byte lumaMode = block.Modes[n];
switch (lumaMode) switch (lumaMode)
@ -462,7 +462,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
{ {
for (int n = 0; n < 16; ++n, bits <<= 2) for (int n = 0; n < 16; ++n, bits <<= 2)
{ {
this.DoTransform(bits, coeffs.AsSpan(n * 16), yDst.Slice(WebPConstants.Scan[n])); this.DoTransform(bits, coeffs.AsSpan(n * 16), yDst.Slice(WebpConstants.Scan[n]));
} }
} }
} }
@ -508,9 +508,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
// Stash away top samples for next block. // Stash away top samples for next block.
if (mby < dec.MbHeight - 1) if (mby < dec.MbHeight - 1)
{ {
yDst.Slice(15 * WebPConstants.Bps, 16).CopyTo(topYuv.Y); yDst.Slice(15 * WebpConstants.Bps, 16).CopyTo(topYuv.Y);
uDst.Slice(7 * WebPConstants.Bps, 8).CopyTo(topYuv.U); uDst.Slice(7 * WebpConstants.Bps, 8).CopyTo(topYuv.U);
vDst.Slice(7 * WebPConstants.Bps, 8).CopyTo(topYuv.V); vDst.Slice(7 * WebpConstants.Bps, 8).CopyTo(topYuv.V);
} }
// Transfer reconstructed samples from yuv_buffer cache to final destination. // Transfer reconstructed samples from yuv_buffer cache to final destination.
@ -519,14 +519,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
Span<byte> vOut = dec.CacheV.Memory.Span.Slice(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));
} }
for (int j = 0; j < 8; ++j) for (int j = 0; j < 8; ++j)
{ {
int jUvStride = j * dec.CacheUvStride; int jUvStride = j * dec.CacheUvStride;
uDst.Slice(j * WebPConstants.Bps, Math.Min(8, uOut.Length)).CopyTo(uOut.Slice(jUvStride)); uDst.Slice(j * WebpConstants.Bps, Math.Min(8, uOut.Length)).CopyTo(uOut.Slice(jUvStride));
vDst.Slice(j * WebPConstants.Bps, Math.Min(8, vOut.Length)).CopyTo(vOut.Slice(jUvStride)); vDst.Slice(j * WebpConstants.Bps, Math.Min(8, vOut.Length)).CopyTo(vOut.Slice(jUvStride));
} }
} }
} }
@ -609,7 +609,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
private void FinishRow(Vp8Decoder dec, Vp8Io io) private void FinishRow(Vp8Decoder dec, Vp8Io io)
{ {
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.Memory.Span; Span<byte> yDst = dec.CacheY.Memory.Span;
@ -1008,7 +1008,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
} }
int idx = n > 0 ? 1 : 0; int idx = n > 0 ? 1 : 0;
coeffs[WebPConstants.Zigzag[n]] = (short)(br.GetSigned(v) * dq[idx]); coeffs[WebpConstants.Zigzag[n]] = (short)(br.GetSigned(v) * dq[idx]);
} }
return 16; return 16;
@ -1053,19 +1053,19 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
switch (cat) switch (cat)
{ {
case 0: case 0:
tab = WebPConstants.Cat3; tab = WebpConstants.Cat3;
break; break;
case 1: case 1:
tab = WebPConstants.Cat4; tab = WebpConstants.Cat4;
break; break;
case 2: case 2:
tab = WebPConstants.Cat5; tab = WebpConstants.Cat5;
break; break;
case 3: case 3:
tab = WebPConstants.Cat6; tab = WebpConstants.Cat6;
break; break;
default: default:
WebPThrowHelper.ThrowImageFormatException("VP8 parsing error"); WebpThrowHelper.ThrowImageFormatException("VP8 parsing error");
break; break;
} }
@ -1162,7 +1162,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
} }
} }
int extraRows = WebPConstants.FilterExtraRows[(int)dec.Filter]; int extraRows = WebpConstants.FilterExtraRows[(int)dec.Filter];
int extraY = extraRows * dec.CacheYStride; int extraY = extraRows * dec.CacheYStride;
int extraUv = (extraRows / 2) * dec.CacheUvStride; int extraUv = (extraRows / 2) * dec.CacheUvStride;
dec.CacheYOffset = extraY; dec.CacheYOffset = extraY;
@ -1213,7 +1213,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
int dquvDc = hasValue ? this.bitReader.ReadSignedValue(4) : 0; int dquvDc = hasValue ? this.bitReader.ReadSignedValue(4) : 0;
hasValue = this.bitReader.ReadBool(); hasValue = this.bitReader.ReadBool();
int dquvAc = hasValue ? this.bitReader.ReadSignedValue(4) : 0; int dquvAc = hasValue ? this.bitReader.ReadSignedValue(4) : 0;
for (int i = 0; i < WebPConstants.NumMbSegments; ++i) for (int i = 0; i < WebpConstants.NumMbSegments; ++i)
{ {
int q; int q;
if (vp8SegmentHeader.UseSegment) if (vp8SegmentHeader.UseSegment)
@ -1238,20 +1238,20 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
} }
Vp8QuantMatrix m = decoder.DeQuantMatrices[i]; Vp8QuantMatrix m = decoder.DeQuantMatrices[i];
m.Y1Mat[0] = WebPLookupTables.DcTable[Clip(q + dqy1Dc, 127)]; m.Y1Mat[0] = WebpLookupTables.DcTable[Clip(q + dqy1Dc, 127)];
m.Y1Mat[1] = WebPLookupTables.AcTable[Clip(q + 0, 127)]; m.Y1Mat[1] = WebpLookupTables.AcTable[Clip(q + 0, 127)];
m.Y2Mat[0] = WebPLookupTables.DcTable[Clip(q + dqy2Dc, 127)] * 2; m.Y2Mat[0] = WebpLookupTables.DcTable[Clip(q + dqy2Dc, 127)] * 2;
// For all x in [0..284], x*155/100 is bitwise equal to (x*101581) >> 16. // For all x in [0..284], x*155/100 is bitwise equal to (x*101581) >> 16.
// The smallest precision for that is '(x*6349) >> 12' but 16 is a good word size. // The smallest precision for that is '(x*6349) >> 12' but 16 is a good word size.
m.Y2Mat[1] = (WebPLookupTables.AcTable[Clip(q + dqy2Ac, 127)] * 101581) >> 16; m.Y2Mat[1] = (WebpLookupTables.AcTable[Clip(q + dqy2Ac, 127)] * 101581) >> 16;
if (m.Y2Mat[1] < 8) if (m.Y2Mat[1] < 8)
{ {
m.Y2Mat[1] = 8; m.Y2Mat[1] = 8;
} }
m.UvMat[0] = WebPLookupTables.DcTable[Clip(q + dquvDc, 117)]; m.UvMat[0] = WebpLookupTables.DcTable[Clip(q + dquvDc, 117)];
m.UvMat[1] = WebPLookupTables.AcTable[Clip(q + dquvAc, 127)]; m.UvMat[1] = WebpLookupTables.AcTable[Clip(q + dquvAc, 127)];
// For dithering strength evaluation. // For dithering strength evaluation.
m.UvQuant = q + dquvAc; m.UvQuant = q + dquvAc;
@ -1262,18 +1262,18 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
{ {
Vp8Proba proba = dec.Probabilities; Vp8Proba proba = dec.Probabilities;
for (int t = 0; t < WebPConstants.NumTypes; ++t) for (int t = 0; t < WebpConstants.NumTypes; ++t)
{ {
for (int b = 0; b < WebPConstants.NumBands; ++b) for (int b = 0; b < WebpConstants.NumBands; ++b)
{ {
for (int c = 0; c < WebPConstants.NumCtx; ++c) for (int c = 0; c < WebpConstants.NumCtx; ++c)
{ {
for (int p = 0; p < WebPConstants.NumProbas; ++p) for (int p = 0; p < WebpConstants.NumProbas; ++p)
{ {
byte prob = WebPLookupTables.CoeffsUpdateProba[t, b, c, p]; byte prob = WebpLookupTables.CoeffsUpdateProba[t, b, c, p];
byte v = (byte)(this.bitReader.GetBit(prob) != 0 byte v = (byte)(this.bitReader.GetBit(prob) != 0
? this.bitReader.ReadValue(8) ? this.bitReader.ReadValue(8)
: WebPLookupTables.DefaultCoeffsProba[t, b, c, p]); : WebpLookupTables.DefaultCoeffsProba[t, b, c, p]);
proba.Bands[t, b].Probabilities[c].Probabilities[p] = v; proba.Bands[t, b].Probabilities[c].Probabilities[p] = v;
} }
} }
@ -1281,7 +1281,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
for (int b = 0; b < 16 + 1; ++b) for (int b = 0; b < 16 + 1; ++b)
{ {
proba.BandsPtr[t][b] = proba.Bands[t, WebPConstants.Vp8EncBands[b]]; proba.BandsPtr[t][b] = proba.Bands[t, WebpConstants.Vp8EncBands[b]];
} }
} }
@ -1309,7 +1309,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
int intraPredModeSize = 4 * dec.MbWidth; int intraPredModeSize = 4 * dec.MbWidth;
dec.IntraT = new byte[intraPredModeSize]; dec.IntraT = new byte[intraPredModeSize];
int extraPixels = WebPConstants.FilterExtraRows[(int)dec.Filter]; int extraPixels = WebpConstants.FilterExtraRows[(int)dec.Filter];
if (dec.Filter == LoopFilter.Complex) if (dec.Filter == LoopFilter.Complex)
{ {
// For complex filter, we need to preserve the dependency chain. // For complex filter, we need to preserve the dependency chain.

18
src/ImageSharp/Formats/WebP/Lossy/YuvConversion.cs

@ -5,7 +5,7 @@ using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy namespace SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy
{ {
internal static class YuvConversion internal static class YuvConversion
{ {
@ -199,7 +199,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
private static int LinearToGammaWeighted(byte rgb0, byte rgb1, byte rgb2, byte rgb3, byte a0, byte a1, byte a2, byte a3, uint totalA) private static int LinearToGammaWeighted(byte rgb0, byte rgb1, byte rgb2, byte rgb3, byte a0, byte a1, byte a2, byte a3, uint totalA)
{ {
uint sum = (a0 * GammaToLinear(rgb0)) + (a1 * GammaToLinear(rgb1)) + (a2 * GammaToLinear(rgb2)) + (a3 * GammaToLinear(rgb3)); uint sum = (a0 * GammaToLinear(rgb0)) + (a1 * GammaToLinear(rgb1)) + (a2 * GammaToLinear(rgb2)) + (a3 * GammaToLinear(rgb3));
return LinearToGamma((sum * WebPLookupTables.InvAlpha[totalA]) >> (WebPConstants.AlphaFix - 2), 0); return LinearToGamma((sum * WebpLookupTables.InvAlpha[totalA]) >> (WebpConstants.AlphaFix - 2), 0);
} }
// Convert a linear value 'v' to YUV_FIX+2 fixed-point precision // Convert a linear value 'v' to YUV_FIX+2 fixed-point precision
@ -208,23 +208,23 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy
private static int LinearToGamma(uint baseValue, int shift) private static int LinearToGamma(uint baseValue, int shift)
{ {
int y = Interpolate((int)(baseValue << shift)); // Final uplifted value. int y = Interpolate((int)(baseValue << shift)); // Final uplifted value.
return (y + WebPConstants.GammaTabRounder) >> WebPConstants.GammaTabFix; // Descale. return (y + WebpConstants.GammaTabRounder) >> WebpConstants.GammaTabFix; // Descale.
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static uint GammaToLinear(byte v) private static uint GammaToLinear(byte v)
{ {
return WebPLookupTables.GammaToLinearTab[v]; return WebpLookupTables.GammaToLinearTab[v];
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static int Interpolate(int v) private static int Interpolate(int v)
{ {
int tabPos = v >> (WebPConstants.GammaTabFix + 2); // integer part. int tabPos = v >> (WebpConstants.GammaTabFix + 2); // integer part.
int x = v & ((WebPConstants.GammaTabScale << 2) - 1); // fractional part. int x = v & ((WebpConstants.GammaTabScale << 2) - 1); // fractional part.
int v0 = WebPLookupTables.LinearToGammaTab[tabPos]; int v0 = WebpLookupTables.LinearToGammaTab[tabPos];
int v1 = WebPLookupTables.LinearToGammaTab[tabPos + 1]; int v1 = WebpLookupTables.LinearToGammaTab[tabPos + 1];
int y = (v1 * x) + (v0 * ((WebPConstants.GammaTabScale << 2) - x)); // interpolate int y = (v1 * x) + (v0 * ((WebpConstants.GammaTabScale << 2) - x)); // interpolate
return y; return y;
} }

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

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats.Experimental.WebP; using SixLabors.ImageSharp.Formats.Experimental.Webp;
using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata;
namespace SixLabors.ImageSharp namespace SixLabors.ImageSharp
@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp
/// Gets the webp format specific metadata for the image. /// Gets the webp format specific metadata for the image.
/// </summary> /// </summary>
/// <param name="metadata">The metadata this method extends.</param> /// <param name="metadata">The metadata this method extends.</param>
/// <returns>The <see cref="WebPMetadata"/>.</returns> /// <returns>The <see cref="WebpMetadata"/>.</returns>
public static WebPMetadata GetWebpMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(WebPFormat.Instance); public static WebpMetadata GetWebpMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(WebpFormat.Instance);
} }
} }

2
src/ImageSharp/Formats/WebP/Vp8HeaderType.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP namespace SixLabors.ImageSharp.Formats.Experimental.Webp
{ {
/// <summary> /// <summary>
/// Enum for the different VP8 chunk header types. /// Enum for the different VP8 chunk header types.

4
src/ImageSharp/Formats/WebP/WebPAlphaFilterType.cs

@ -1,12 +1,12 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP namespace SixLabors.ImageSharp.Formats.Experimental.Webp
{ {
/// <summary> /// <summary>
/// Enum for the different alpha filter types. /// Enum for the different alpha filter types.
/// </summary> /// </summary>
internal enum WebPAlphaFilterType : int internal enum WebpAlphaFilterType : int
{ {
/// <summary> /// <summary>
/// No filtering. /// No filtering.

4
src/ImageSharp/Formats/WebP/WebPBitsPerPixel.cs

@ -1,12 +1,12 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP namespace SixLabors.ImageSharp.Formats.Experimental.Webp
{ {
/// <summary> /// <summary>
/// Enumerates the available bits per pixel the webp image uses. /// Enumerates the available bits per pixel the webp image uses.
/// </summary> /// </summary>
public enum WebPBitsPerPixel : short public enum WebpBitsPerPixel : short
{ {
/// <summary> /// <summary>
/// 24 bits per pixel. Each pixel consists of 3 bytes. /// 24 bits per pixel. Each pixel consists of 3 bytes.

4
src/ImageSharp/Formats/WebP/WebPChunkType.cs

@ -1,13 +1,13 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Experimental.WebP namespace SixLabors.ImageSharp.Formats.Experimental.Webp
{ {
/// <summary> /// <summary>
/// Contains a list of different webp chunk types. /// Contains a list of different webp chunk types.
/// </summary> /// </summary>
/// <remarks>See WebP Container Specification for more details: https://developers.google.com/speed/webp/docs/riff_container </remarks> /// <remarks>See WebP Container Specification for more details: https://developers.google.com/speed/webp/docs/riff_container </remarks>
public enum WebPChunkType : uint public enum WebpChunkType : uint
{ {
/// <summary> /// <summary>
/// Header signaling the use of the VP8 format. /// Header signaling the use of the VP8 format.

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

@ -4,12 +4,12 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP namespace SixLabors.ImageSharp.Formats.Experimental.Webp
{ {
/// <summary> /// <summary>
/// Utility methods for lossy and lossless webp format. /// Utility methods for lossy and lossless webp format.
/// </summary> /// </summary>
internal static class WebPCommonUtils internal static class WebpCommonUtils
{ {
/// <summary> /// <summary>
/// Returns 31 ^ clz(n) = log2(n).Returns 31 ^ clz(n) = log2(n). /// Returns 31 ^ clz(n) = log2(n).Returns 31 ^ clz(n) = log2(n).
@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
n >>= 8; n >>= 8;
} }
return logValue + Unsafe.Add(ref MemoryMarshal.GetReference(WebPLookupTables.LogTable8Bit), (int)n); return logValue + Unsafe.Add(ref MemoryMarshal.GetReference(WebpLookupTables.LogTable8Bit), (int)n);
} }
} }
} }

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

@ -3,12 +3,12 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP namespace SixLabors.ImageSharp.Formats.Experimental.Webp
{ {
/// <summary> /// <summary>
/// Constants used for encoding and decoding VP8 and VP8L bitstreams. /// Constants used for encoding and decoding VP8 and VP8L bitstreams.
/// </summary> /// </summary>
internal static class WebPConstants internal static class WebpConstants
{ {
/// <summary> /// <summary>
/// The list of file extensions that equate to WebP. /// The list of file extensions that equate to WebP.

12
src/ImageSharp/Formats/WebP/WebPDecoder.cs

@ -9,13 +9,13 @@ using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP namespace SixLabors.ImageSharp.Formats.Experimental.Webp
{ {
/// <summary> /// <summary>
/// EXPERIMENTAL: /// EXPERIMENTAL:
/// Image decoder for generating an image out of a webp stream. /// Image decoder for generating an image out of a webp stream.
/// </summary> /// </summary>
public sealed class WebPDecoder : IImageDecoder, IWebPDecoderOptions, IImageInfoDetector public sealed class WebpDecoder : IImageDecoder, IWebpDecoderOptions, IImageInfoDetector
{ {
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
{ {
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
var decoder = new WebPDecoderCore(configuration, this); var decoder = new WebpDecoderCore(configuration, this);
try try
{ {
@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
{ {
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
return new WebPDecoderCore(configuration, this).Identify(configuration, stream); return new WebpDecoderCore(configuration, this).Identify(configuration, stream);
} }
/// <inheritdoc /> /// <inheritdoc />
@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
{ {
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
var decoder = new WebPDecoderCore(configuration, this); var decoder = new WebpDecoderCore(configuration, this);
try try
{ {
@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
using var bufferedStream = new BufferedReadStream(configuration, stream); using var bufferedStream = new BufferedReadStream(configuration, stream);
return new WebPDecoderCore(configuration, this).IdentifyAsync(configuration, bufferedStream, cancellationToken); return new WebpDecoderCore(configuration, this).IdentifyAsync(configuration, bufferedStream, cancellationToken);
} }
} }
} }

128
src/ImageSharp/Formats/WebP/WebPDecoderCore.cs

@ -5,9 +5,9 @@ using System;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using SixLabors.ImageSharp.Formats.Experimental.WebP.BitReader; using SixLabors.ImageSharp.Formats.Experimental.Webp.BitReader;
using SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless; using SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless;
using SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy; using SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy;
using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata;
@ -15,12 +15,12 @@ using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.Metadata.Profiles.Icc;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP namespace SixLabors.ImageSharp.Formats.Experimental.Webp
{ {
/// <summary> /// <summary>
/// Performs the webp decoding operation. /// Performs the webp decoding operation.
/// </summary> /// </summary>
internal sealed class WebPDecoderCore : IImageDecoderInternals internal sealed class WebpDecoderCore : IImageDecoderInternals
{ {
/// <summary> /// <summary>
/// Reusable buffer. /// Reusable buffer.
@ -40,19 +40,19 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
/// <summary> /// <summary>
/// The webp specific metadata. /// The webp specific metadata.
/// </summary> /// </summary>
private WebPMetadata webpMetadata; private WebpMetadata webpMetadata;
/// <summary> /// <summary>
/// Information about the webp image. /// Information about the webp image.
/// </summary> /// </summary>
private WebPImageInfo webImageInfo; private WebpImageInfo webImageInfo;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="WebPDecoderCore"/> class. /// Initializes a new instance of the <see cref="WebpDecoderCore"/> class.
/// </summary> /// </summary>
/// <param name="configuration">The configuration.</param> /// <param name="configuration">The configuration.</param>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
public WebPDecoderCore(Configuration configuration, IWebPDecoderOptions options) public WebpDecoderCore(Configuration configuration, IWebpDecoderOptions options)
{ {
this.Configuration = configuration; this.Configuration = configuration;
this.memoryAllocator = configuration.MemoryAllocator; this.memoryAllocator = configuration.MemoryAllocator;
@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
{ {
if (this.webImageInfo.Features != null && this.webImageInfo.Features.Animation) if (this.webImageInfo.Features != null && this.webImageInfo.Features.Animation)
{ {
WebPThrowHelper.ThrowNotSupportedException("Animations are not supported"); WebpThrowHelper.ThrowNotSupportedException("Animations are not supported");
} }
var image = new Image<TPixel>(this.Configuration, (int)this.webImageInfo.Width, (int)this.webImageInfo.Height, this.Metadata); var image = new Image<TPixel>(this.Configuration, (int)this.webImageInfo.Width, (int)this.webImageInfo.Height, this.Metadata);
@ -152,24 +152,24 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
/// Reads information present in the image header, about the image content and how to decode the image. /// Reads information present in the image header, about the image content and how to decode the image.
/// </summary> /// </summary>
/// <returns>Information about the webp image.</returns> /// <returns>Information about the webp image.</returns>
private WebPImageInfo ReadVp8Info() private WebpImageInfo ReadVp8Info()
{ {
this.Metadata = new ImageMetadata(); this.Metadata = new ImageMetadata();
this.webpMetadata = this.Metadata.GetFormatMetadata(WebPFormat.Instance); this.webpMetadata = this.Metadata.GetFormatMetadata(WebpFormat.Instance);
WebPChunkType chunkType = this.ReadChunkType(); WebpChunkType chunkType = this.ReadChunkType();
switch (chunkType) switch (chunkType)
{ {
case WebPChunkType.Vp8: case WebpChunkType.Vp8:
return this.ReadVp8Header(); return this.ReadVp8Header();
case WebPChunkType.Vp8L: case WebpChunkType.Vp8L:
return this.ReadVp8LHeader(); return this.ReadVp8LHeader();
case WebPChunkType.Vp8X: case WebpChunkType.Vp8X:
return this.ReadVp8XHeader(); return this.ReadVp8XHeader();
default: default:
WebPThrowHelper.ThrowImageFormatException("Unrecognized VP8 header"); WebpThrowHelper.ThrowImageFormatException("Unrecognized VP8 header");
return new WebPImageInfo(); // this return will never be reached, because throw helper will throw an exception. return new WebpImageInfo(); // this return will never be reached, because throw helper will throw an exception.
} }
} }
@ -182,9 +182,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
/// After the image header, image data will follow. After that optional image metadata chunks (EXIF and XMP) can follow. /// After the image header, image data will follow. After that optional image metadata chunks (EXIF and XMP) can follow.
/// </summary> /// </summary>
/// <returns>Information about this webp image.</returns> /// <returns>Information about this webp image.</returns>
private WebPImageInfo ReadVp8XHeader() private WebpImageInfo ReadVp8XHeader()
{ {
var features = new WebPFeatures(); var features = new WebpFeatures();
uint chunkSize = this.ReadChunkSize(); uint chunkSize = this.ReadChunkSize();
// The first byte contains information about the image features used. // The first byte contains information about the image features used.
@ -193,7 +193,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
// The first two bit of it are reserved and should be 0. // The first two bit of it are reserved and should be 0.
if (imageFeatures >> 6 != 0) if (imageFeatures >> 6 != 0)
{ {
WebPThrowHelper.ThrowImageFormatException( WebpThrowHelper.ThrowImageFormatException(
"first two bits of the VP8X header are expected to be zero"); "first two bits of the VP8X header are expected to be zero");
} }
@ -216,7 +216,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
this.currentStream.Read(this.buffer, 0, 3); this.currentStream.Read(this.buffer, 0, 3);
if (this.buffer[0] != 0 || this.buffer[1] != 0 | this.buffer[2] != 0) if (this.buffer[0] != 0 || this.buffer[1] != 0 | this.buffer[2] != 0)
{ {
WebPThrowHelper.ThrowImageFormatException("reserved bytes should be zero"); WebpThrowHelper.ThrowImageFormatException("reserved bytes should be zero");
} }
// 3 bytes for the width. // 3 bytes for the width.
@ -230,7 +230,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
uint height = (uint)BinaryPrimitives.ReadInt32LittleEndian(this.buffer) + 1; uint height = (uint)BinaryPrimitives.ReadInt32LittleEndian(this.buffer) + 1;
// Optional chunks ICCP, ALPH and ANIM can follow here. // Optional chunks ICCP, ALPH and ANIM can follow here.
WebPChunkType chunkType = this.ReadChunkType(); WebpChunkType chunkType = this.ReadChunkType();
while (IsOptionalVp8XChunk(chunkType)) while (IsOptionalVp8XChunk(chunkType))
{ {
this.ParseOptionalExtendedChunks(chunkType, features); this.ParseOptionalExtendedChunks(chunkType, features);
@ -240,20 +240,20 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
if (features.Animation) if (features.Animation)
{ {
// TODO: Animations are not yet supported. // TODO: Animations are not yet supported.
return new WebPImageInfo() { Width = width, Height = height, Features = features }; return new WebpImageInfo() { Width = width, Height = height, Features = features };
} }
switch (chunkType) switch (chunkType)
{ {
case WebPChunkType.Vp8: case WebpChunkType.Vp8:
return this.ReadVp8Header(features); return this.ReadVp8Header(features);
case WebPChunkType.Vp8L: case WebpChunkType.Vp8L:
return this.ReadVp8LHeader(features); return this.ReadVp8LHeader(features);
} }
WebPThrowHelper.ThrowImageFormatException("Unexpected chunk followed VP8X header"); WebpThrowHelper.ThrowImageFormatException("Unexpected chunk followed VP8X header");
return new WebPImageInfo(); return new WebpImageInfo();
} }
/// <summary> /// <summary>
@ -261,9 +261,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
/// </summary> /// </summary>
/// <param name="features">Webp features.</param> /// <param name="features">Webp features.</param>
/// <returns>Information about this webp image.</returns> /// <returns>Information about this webp image.</returns>
private WebPImageInfo ReadVp8Header(WebPFeatures features = null) private WebpImageInfo ReadVp8Header(WebpFeatures features = null)
{ {
this.webpMetadata.Format = WebPFormatType.Lossy; this.webpMetadata.Format = WebpFormatType.Lossy;
// VP8 data size (not including this 4 bytes). // VP8 data size (not including this 4 bytes).
this.currentStream.Read(this.buffer, 0, 4); this.currentStream.Read(this.buffer, 0, 4);
@ -284,32 +284,32 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
bool isNoKeyFrame = (frameTag & 0x1) == 1; bool isNoKeyFrame = (frameTag & 0x1) == 1;
if (isNoKeyFrame) if (isNoKeyFrame)
{ {
WebPThrowHelper.ThrowImageFormatException("VP8 header indicates the image is not a key frame"); WebpThrowHelper.ThrowImageFormatException("VP8 header indicates the image is not a key frame");
} }
uint version = (frameTag >> 1) & 0x7; uint version = (frameTag >> 1) & 0x7;
if (version > 3) if (version > 3)
{ {
WebPThrowHelper.ThrowImageFormatException($"VP8 header indicates unknown profile {version}"); WebpThrowHelper.ThrowImageFormatException($"VP8 header indicates unknown profile {version}");
} }
bool invisibleFrame = ((frameTag >> 4) & 0x1) == 0; bool invisibleFrame = ((frameTag >> 4) & 0x1) == 0;
if (invisibleFrame) if (invisibleFrame)
{ {
WebPThrowHelper.ThrowImageFormatException("VP8 header indicates that the first frame is invisible"); WebpThrowHelper.ThrowImageFormatException("VP8 header indicates that the first frame is invisible");
} }
uint partitionLength = frameTag >> 5; uint partitionLength = frameTag >> 5;
if (partitionLength > dataSize) if (partitionLength > dataSize)
{ {
WebPThrowHelper.ThrowImageFormatException("VP8 header contains inconsistent size information"); WebpThrowHelper.ThrowImageFormatException("VP8 header contains inconsistent size information");
} }
// Check for VP8 magic bytes. // Check for VP8 magic bytes.
this.currentStream.Read(this.buffer, 0, 3); this.currentStream.Read(this.buffer, 0, 3);
if (!this.buffer.AsSpan().Slice(0, 3).SequenceEqual(WebPConstants.Vp8HeaderMagicBytes)) if (!this.buffer.AsSpan().Slice(0, 3).SequenceEqual(WebpConstants.Vp8HeaderMagicBytes))
{ {
WebPThrowHelper.ThrowImageFormatException("VP8 magic bytes not found"); WebpThrowHelper.ThrowImageFormatException("VP8 magic bytes not found");
} }
this.currentStream.Read(this.buffer, 0, 4); this.currentStream.Read(this.buffer, 0, 4);
@ -322,12 +322,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
remaining -= 7; remaining -= 7;
if (width == 0 || height == 0) if (width == 0 || height == 0)
{ {
WebPThrowHelper.ThrowImageFormatException("width or height can not be zero"); WebpThrowHelper.ThrowImageFormatException("width or height can not be zero");
} }
if (partitionLength > remaining) if (partitionLength > remaining)
{ {
WebPThrowHelper.ThrowImageFormatException("bad partition length"); WebpThrowHelper.ThrowImageFormatException("bad partition length");
} }
var vp8FrameHeader = new Vp8FrameHeader() var vp8FrameHeader = new Vp8FrameHeader()
@ -346,13 +346,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
Remaining = remaining Remaining = remaining
}; };
return new WebPImageInfo() return new WebpImageInfo()
{ {
Width = width, Width = width,
Height = height, Height = height,
XScale = xScale, XScale = xScale,
YScale = yScale, YScale = yScale,
BitsPerPixel = features?.Alpha == true ? WebPBitsPerPixel.Pixel32 : WebPBitsPerPixel.Pixel24, BitsPerPixel = features?.Alpha == true ? WebpBitsPerPixel.Pixel32 : WebpBitsPerPixel.Pixel24,
IsLossless = false, IsLossless = false,
Features = features, Features = features,
Vp8Profile = (sbyte)version, Vp8Profile = (sbyte)version,
@ -366,9 +366,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
/// </summary> /// </summary>
/// <param name="features">Webp image features.</param> /// <param name="features">Webp image features.</param>
/// <returns>Information about this image.</returns> /// <returns>Information about this image.</returns>
private WebPImageInfo ReadVp8LHeader(WebPFeatures features = null) private WebpImageInfo ReadVp8LHeader(WebpFeatures features = null)
{ {
this.webpMetadata.Format = WebPFormatType.Lossless; this.webpMetadata.Format = WebpFormatType.Lossless;
// VP8 data size. // VP8 data size.
uint imageDataSize = this.ReadChunkSize(); uint imageDataSize = this.ReadChunkSize();
@ -377,17 +377,17 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
// One byte signature, should be 0x2f. // One byte signature, should be 0x2f.
uint signature = bitReader.ReadValue(8); uint signature = bitReader.ReadValue(8);
if (signature != WebPConstants.Vp8LHeaderMagicByte) if (signature != WebpConstants.Vp8LHeaderMagicByte)
{ {
WebPThrowHelper.ThrowImageFormatException("Invalid VP8L signature"); WebpThrowHelper.ThrowImageFormatException("Invalid VP8L signature");
} }
// 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 == 1 || height == 1)
{ {
WebPThrowHelper.ThrowImageFormatException("invalid width or height read"); WebpThrowHelper.ThrowImageFormatException("invalid width or height read");
} }
// The alphaIsUsed flag should be set to 0 when all alpha values are 255 in the picture, and 1 otherwise. // The alphaIsUsed flag should be set to 0 when all alpha values are 255 in the picture, and 1 otherwise.
@ -396,17 +396,17 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
// The next 3 bits are the version. The version number is a 3 bit code that must be set to 0. // The next 3 bits are the version. The version number is a 3 bit code that must be set to 0.
// Any other value should be treated as an error. // Any other value should be treated as an error.
uint version = bitReader.ReadValue(WebPConstants.Vp8LVersionBits); uint version = bitReader.ReadValue(WebpConstants.Vp8LVersionBits);
if (version != 0) if (version != 0)
{ {
WebPThrowHelper.ThrowNotSupportedException($"Unexpected version number {version} found in VP8L header"); WebpThrowHelper.ThrowNotSupportedException($"Unexpected version number {version} found in VP8L header");
} }
return new WebPImageInfo() return new WebpImageInfo()
{ {
Width = width, Width = width,
Height = height, Height = height,
BitsPerPixel = WebPBitsPerPixel.Pixel32, BitsPerPixel = WebpBitsPerPixel.Pixel32,
IsLossless = true, IsLossless = true,
Features = features, Features = features,
Vp8LBitReader = bitReader Vp8LBitReader = bitReader
@ -418,11 +418,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
/// </summary> /// </summary>
/// <param name="chunkType">The chunk type.</param> /// <param name="chunkType">The chunk type.</param>
/// <param name="features">The webp image features.</param> /// <param name="features">The webp image features.</param>
private void ParseOptionalExtendedChunks(WebPChunkType chunkType, WebPFeatures features) private void ParseOptionalExtendedChunks(WebpChunkType chunkType, WebpFeatures features)
{ {
switch (chunkType) switch (chunkType)
{ {
case WebPChunkType.Iccp: case WebpChunkType.Iccp:
uint iccpChunkSize = this.ReadChunkSize(); uint iccpChunkSize = this.ReadChunkSize();
if (this.IgnoreMetadata) if (this.IgnoreMetadata)
{ {
@ -441,11 +441,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
break; break;
case WebPChunkType.Animation: case WebpChunkType.Animation:
this.webpMetadata.Animated = true; this.webpMetadata.Animated = true;
break; break;
case WebPChunkType.Alpha: case WebpChunkType.Alpha:
uint alphaChunkSize = this.ReadChunkSize(); uint alphaChunkSize = this.ReadChunkSize();
features.AlphaChunkHeader = (byte)this.currentStream.ReadByte(); features.AlphaChunkHeader = (byte)this.currentStream.ReadByte();
var alphaDataSize = (int)(alphaChunkSize - 1); var alphaDataSize = (int)(alphaChunkSize - 1);
@ -461,7 +461,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
/// Also, a file may possibly contain both 'EXIF' and 'XMP ' chunks. /// Also, a file may possibly contain both 'EXIF' and 'XMP ' chunks.
/// </summary> /// </summary>
/// <param name="features">The webp features.</param> /// <param name="features">The webp features.</param>
private void ParseOptionalChunks(WebPFeatures features) private void ParseOptionalChunks(WebpFeatures features)
{ {
if (this.IgnoreMetadata || (features.ExifProfile == false && features.XmpMetaData == false)) if (this.IgnoreMetadata || (features.ExifProfile == false && features.XmpMetaData == false))
{ {
@ -472,10 +472,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
while (this.currentStream.Position < streamLength) while (this.currentStream.Position < streamLength)
{ {
// Read chunk header. // Read chunk header.
WebPChunkType chunkType = this.ReadChunkType(); WebpChunkType chunkType = this.ReadChunkType();
uint chunkLength = this.ReadChunkSize(); uint chunkLength = this.ReadChunkSize();
if (chunkType == WebPChunkType.Exif) if (chunkType == WebpChunkType.Exif)
{ {
var exifData = new byte[chunkLength]; var exifData = new byte[chunkLength];
this.currentStream.Read(exifData, 0, (int)chunkLength); this.currentStream.Read(exifData, 0, (int)chunkLength);
@ -495,11 +495,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
/// <exception cref="ImageFormatException"> /// <exception cref="ImageFormatException">
/// Thrown if the input stream is not valid. /// Thrown if the input stream is not valid.
/// </exception> /// </exception>
private WebPChunkType ReadChunkType() private WebpChunkType ReadChunkType()
{ {
if (this.currentStream.Read(this.buffer, 0, 4) == 4) if (this.currentStream.Read(this.buffer, 0, 4) == 4)
{ {
var chunkType = (WebPChunkType)BinaryPrimitives.ReadUInt32BigEndian(this.buffer); var chunkType = (WebpChunkType)BinaryPrimitives.ReadUInt32BigEndian(this.buffer);
this.webpMetadata.ChunkTypes.Enqueue(chunkType); this.webpMetadata.ChunkTypes.Enqueue(chunkType);
return chunkType; return chunkType;
} }
@ -528,13 +528,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
/// </summary> /// </summary>
/// <param name="chunkType">The chunk type.</param> /// <param name="chunkType">The chunk type.</param>
/// <returns>True, if its an optional chunk type.</returns> /// <returns>True, if its an optional chunk type.</returns>
private static bool IsOptionalVp8XChunk(WebPChunkType chunkType) private static bool IsOptionalVp8XChunk(WebpChunkType chunkType)
{ {
return chunkType switch return chunkType switch
{ {
WebPChunkType.Alpha => true, WebpChunkType.Alpha => true,
WebPChunkType.Animation => true, WebpChunkType.Animation => true,
WebPChunkType.Iccp => true, WebpChunkType.Iccp => true,
_ => false _ => false
}; };
} }

8
src/ImageSharp/Formats/WebP/WebPEncoder.cs

@ -7,13 +7,13 @@ using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP namespace SixLabors.ImageSharp.Formats.Experimental.Webp
{ {
/// <summary> /// <summary>
/// EXPERIMENTAL: /// EXPERIMENTAL:
/// Image encoder for writing an image to a stream in the WebP format. /// Image encoder for writing an image to a stream in the WebP format.
/// </summary> /// </summary>
public sealed class WebPEncoder : IImageEncoder, IWebPEncoderOptions public sealed class WebpEncoder : IImageEncoder, IWebPEncoderOptions
{ {
/// <inheritdoc/> /// <inheritdoc/>
public bool Lossy { get; set; } public bool Lossy { get; set; }
@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
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>
{ {
var encoder = new WebPEncoderCore(this, image.GetMemoryAllocator()); var encoder = new WebpEncoderCore(this, image.GetMemoryAllocator());
encoder.Encode(image, stream); encoder.Encode(image, stream);
} }
@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
public Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken) public Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
var encoder = new WebPEncoderCore(this, image.GetMemoryAllocator()); var encoder = new WebpEncoderCore(this, image.GetMemoryAllocator());
return encoder.EncodeAsync(image, stream); return encoder.EncodeAsync(image, stream);
} }
} }

12
src/ImageSharp/Formats/WebP/WebPEncoderCore.cs

@ -5,18 +5,18 @@ using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Experimental.WebP.Lossless; using SixLabors.ImageSharp.Formats.Experimental.Webp.Lossless;
using SixLabors.ImageSharp.Formats.Experimental.WebP.Lossy; using SixLabors.ImageSharp.Formats.Experimental.Webp.Lossy;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP namespace SixLabors.ImageSharp.Formats.Experimental.Webp
{ {
/// <summary> /// <summary>
/// Image encoder for writing an image to a stream in the WebP format. /// Image encoder for writing an image to a stream in the WebP format.
/// </summary> /// </summary>
internal sealed class WebPEncoderCore internal sealed class WebpEncoderCore
{ {
/// <summary> /// <summary>
/// Used for allocating memory during processing operations. /// Used for allocating memory during processing operations.
@ -54,11 +54,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.WebP
private readonly int entropyPasses; private readonly int entropyPasses;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="WebPEncoderCore"/> class. /// Initializes a new instance of the <see cref="WebpEncoderCore"/> class.
/// </summary> /// </summary>
/// <param name="options">The encoder options.</param> /// <param name="options">The encoder options.</param>
/// <param name="memoryAllocator">The memory manager.</param> /// <param name="memoryAllocator">The memory manager.</param>
public WebPEncoderCore(IWebPEncoderOptions options, MemoryAllocator memoryAllocator) public WebpEncoderCore(IWebPEncoderOptions options, MemoryAllocator memoryAllocator)
{ {
this.memoryAllocator = memoryAllocator; this.memoryAllocator = memoryAllocator;
this.alphaCompression = options.AlphaCompression; this.alphaCompression = options.AlphaCompression;

4
src/ImageSharp/Formats/WebP/WebPFeatures.cs

@ -4,12 +4,12 @@
using System; using System;
using System.Buffers; using System.Buffers;
namespace SixLabors.ImageSharp.Formats.Experimental.WebP namespace SixLabors.ImageSharp.Formats.Experimental.Webp
{ {
/// <summary> /// <summary>
/// Image features of a VP8X image. /// Image features of a VP8X image.
/// </summary> /// </summary>
internal class WebPFeatures : IDisposable internal class WebpFeatures : IDisposable
{ {
/// <summary> /// <summary>
/// Gets or sets a value indicating whether this image has an ICC Profile. /// Gets or sets a value indicating whether this image has an ICC Profile.

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save