Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
cbfdaa7
Update ErrorDiffuser.cs
JimBobSquarePants Feb 11, 2020
3a96474
Merge branch 'master' into js/quantization-tweaks
JimBobSquarePants Feb 13, 2020
a75d833
Refactor dithering and quantizers.
JimBobSquarePants Feb 14, 2020
0509886
Cleanup and fix tests.
JimBobSquarePants Feb 14, 2020
98b98ca
Update EuclideanPixelMap{TPixel}.cs
JimBobSquarePants Feb 14, 2020
305a1c9
Make dither parallel and add benchmarks.
JimBobSquarePants Feb 15, 2020
c98ea13
Add bounded quantization and update namings.
JimBobSquarePants Feb 15, 2020
54450a3
Fix Color.Transparent to match spec.
JimBobSquarePants Feb 15, 2020
f921df1
Handle transparent pixels with Octree
JimBobSquarePants Feb 15, 2020
1a8005a
Merge branch 'master' into js/quantization-tweaks
JimBobSquarePants Feb 15, 2020
66a735e
Update WuFrameQuantizer{TPixel}.cs
JimBobSquarePants Feb 15, 2020
e535d1d
Add dither scaling and simplify API.
JimBobSquarePants Feb 16, 2020
860aebf
Add ditherscale to palette API
JimBobSquarePants Feb 16, 2020
259f7c4
Fix tests
JimBobSquarePants Feb 16, 2020
8af7620
Update benchmarks results
JimBobSquarePants Feb 16, 2020
cce7a53
Use different comparer on CI NETFX
JimBobSquarePants Feb 16, 2020
9ee7530
Add some environment output confirmation.
JimBobSquarePants Feb 16, 2020
2484a0c
Add more detail to threshold exceptions.
JimBobSquarePants Feb 16, 2020
c6e6a4c
Skip tests on CI NETFX
JimBobSquarePants Feb 16, 2020
5c46dd8
Fix localization issue with DitheringScale
brianpopow Feb 16, 2020
b9069de
Merge branch 'master' into js/quantization-tweaks
JimBobSquarePants Feb 18, 2020
4dffc17
Refactor to inline based on feedback.
JimBobSquarePants Feb 19, 2020
1bd4704
Update DitherExtensions.cs
JimBobSquarePants Feb 19, 2020
429872d
Update equality check and add ests. Fix #1116
JimBobSquarePants Feb 20, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 27 additions & 7 deletions src/ImageSharp/Advanced/AotCompilerTools.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.

using System;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Dithering;
using SixLabors.ImageSharp.Processing.Processors.Quantization;

Expand Down Expand Up @@ -82,6 +84,7 @@ private static void Seed<TPixel>()
// This is we actually call all the individual methods you need to seed.
AotCompileOctreeQuantizer<TPixel>();
AotCompileWuQuantizer<TPixel>();
AotCompilePaletteQuantizer<TPixel>();
AotCompileDithering<TPixel>();
AotCompilePixelOperations<TPixel>();

Expand Down Expand Up @@ -109,9 +112,10 @@ private static void Seed<TPixel>()
private static void AotCompileOctreeQuantizer<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
using (var test = new OctreeFrameQuantizer<TPixel>(Configuration.Default, new OctreeQuantizer(false)))
using (var test = new OctreeFrameQuantizer<TPixel>(Configuration.Default, new OctreeQuantizer().Options))
{
test.AotGetPalette();
var frame = new ImageFrame<TPixel>(Configuration.Default, 1, 1);
test.QuantizeFrame(frame, frame.Bounds());
}
}

Expand All @@ -122,10 +126,24 @@ private static void AotCompileOctreeQuantizer<TPixel>()
private static void AotCompileWuQuantizer<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
using (var test = new WuFrameQuantizer<TPixel>(Configuration.Default, new WuQuantizer(false)))
using (var test = new WuFrameQuantizer<TPixel>(Configuration.Default, new WuQuantizer().Options))
{
test.QuantizeFrame(new ImageFrame<TPixel>(Configuration.Default, 1, 1));
test.AotGetPalette();
var frame = new ImageFrame<TPixel>(Configuration.Default, 1, 1);
test.QuantizeFrame(frame, frame.Bounds());
}
}

/// <summary>
/// This method pre-seeds the PaletteQuantizer in the AoT compiler for iOS.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
private static void AotCompilePaletteQuantizer<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
using (var test = (PaletteFrameQuantizer<TPixel>)new PaletteQuantizer(Array.Empty<Color>()).CreateFrameQuantizer<TPixel>(Configuration.Default))
{
var frame = new ImageFrame<TPixel>(Configuration.Default, 1, 1);
test.QuantizeFrame(frame, frame.Bounds());
}
}

Expand All @@ -136,11 +154,13 @@ private static void AotCompileWuQuantizer<TPixel>()
private static void AotCompileDithering<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
var test = new FloydSteinbergDiffuser();
ErrorDither errorDither = ErrorDither.FloydSteinberg;
OrderedDither orderedDither = OrderedDither.Bayer2x2;
TPixel pixel = default;
using (var image = new ImageFrame<TPixel>(Configuration.Default, 1, 1))
{
test.Dither(image, pixel, pixel, 0, 0, 0, 0, 0);
errorDither.Dither(image, image.Bounds(), pixel, pixel, 0, 0, 0);
orderedDither.Dither(pixel, 0, 0, 0, 0);
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/ImageSharp/Color/Color.NamedColors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace SixLabors.ImageSharp
{
/// <content>
/// Contains static named color values.
/// <see href="https://www.w3.org/TR/css-color-3/"/>
/// </content>
public readonly partial struct Color
{
Expand Down Expand Up @@ -719,9 +720,9 @@ public readonly partial struct Color
public static readonly Color Tomato = FromRgba(255, 99, 71, 255);

/// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #FFFFFF.
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #00000000.
/// </summary>
public static readonly Color Transparent = FromRgba(255, 255, 255, 0);
public static readonly Color Transparent = FromRgba(0, 0, 0, 0);

/// <summary>
/// Represents a <see paramref="Color"/> matching the W3C definition that has an hex value of #40E0D0.
Expand Down
53 changes: 27 additions & 26 deletions src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Quantization;

namespace SixLabors.ImageSharp.Formats.Bmp
Expand Down Expand Up @@ -87,7 +88,7 @@ public BmpEncoderCore(IBmpEncoderOptions options, MemoryAllocator memoryAllocato
this.memoryAllocator = memoryAllocator;
this.bitsPerPixel = options.BitsPerPixel;
this.writeV4Header = options.SupportTransparency;
this.quantizer = options.Quantizer ?? new OctreeQuantizer(dither: true, maxColors: 256);
this.quantizer = options.Quantizer ?? KnownQuantizers.Octree;
}

/// <summary>
Expand Down Expand Up @@ -335,36 +336,36 @@ private void Write8Bit<TPixel>(Stream stream, ImageFrame<TPixel> image)
private void Write8BitColor<TPixel>(Stream stream, ImageFrame<TPixel> image, Span<byte> colorPalette)
where TPixel : struct, IPixel<TPixel>
{
using (IQuantizedFrame<TPixel> quantized = this.quantizer.CreateFrameQuantizer<TPixel>(this.configuration, 256).QuantizeFrame(image))
using IFrameQuantizer<TPixel> quantizer = this.quantizer.CreateFrameQuantizer<TPixel>(this.configuration);
using QuantizedFrame<TPixel> quantized = quantizer.QuantizeFrame(image, image.Bounds());

ReadOnlySpan<TPixel> quantizedColors = quantized.Palette.Span;
var color = default(Rgba32);

// TODO: Use bulk conversion here for better perf
int idx = 0;
foreach (TPixel quantizedColor in quantizedColors)
{
ReadOnlySpan<TPixel> quantizedColors = quantized.Palette.Span;
var color = default(Rgba32);
quantizedColor.ToRgba32(ref color);
colorPalette[idx] = color.B;
colorPalette[idx + 1] = color.G;
colorPalette[idx + 2] = color.R;

// TODO: Use bulk conversion here for better perf
int idx = 0;
foreach (TPixel quantizedColor in quantizedColors)
{
quantizedColor.ToRgba32(ref color);
colorPalette[idx] = color.B;
colorPalette[idx + 1] = color.G;
colorPalette[idx + 2] = color.R;

// Padding byte, always 0.
colorPalette[idx + 3] = 0;
idx += 4;
}
// Padding byte, always 0.
colorPalette[idx + 3] = 0;
idx += 4;
}

stream.Write(colorPalette);

stream.Write(colorPalette);
for (int y = image.Height - 1; y >= 0; y--)
{
ReadOnlySpan<byte> pixelSpan = quantized.GetRowSpan(y);
stream.Write(pixelSpan);

for (int y = image.Height - 1; y >= 0; y--)
for (int i = 0; i < this.padding; i++)
{
ReadOnlySpan<byte> pixelSpan = quantized.GetRowSpan(y);
stream.Write(pixelSpan);

for (int i = 0; i < this.padding; i++)
{
stream.WriteByte(0);
}
stream.WriteByte(0);
}
}
}
Expand Down
42 changes: 22 additions & 20 deletions src/ImageSharp/Formats/Gif/GifEncoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
Expand All @@ -28,7 +26,7 @@ internal sealed class GifEncoderCore
/// <summary>
/// Configuration bound to the encoding operation.
/// </summary>
private Configuration configuration;
private readonly Configuration configuration;

/// <summary>
/// A reusable buffer used to reduce allocations.
Expand Down Expand Up @@ -81,10 +79,10 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream)
bool useGlobalTable = this.colorTableMode == GifColorTableMode.Global;

// Quantize the image returning a palette.
IQuantizedFrame<TPixel> quantized;
QuantizedFrame<TPixel> quantized;
using (IFrameQuantizer<TPixel> frameQuantizer = this.quantizer.CreateFrameQuantizer<TPixel>(this.configuration))
{
quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame);
quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds());
}

// Get the number of bits.
Expand Down Expand Up @@ -127,7 +125,7 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream)
stream.WriteByte(GifConstants.EndIntroducer);
}

private void EncodeGlobal<TPixel>(Image<TPixel> image, IQuantizedFrame<TPixel> quantized, int transparencyIndex, Stream stream)
private void EncodeGlobal<TPixel>(Image<TPixel> image, QuantizedFrame<TPixel> quantized, int transparencyIndex, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
for (int i = 0; i < image.Frames.Count; i++)
Expand All @@ -144,19 +142,16 @@ private void EncodeGlobal<TPixel>(Image<TPixel> image, IQuantizedFrame<TPixel> q
}
else
{
using (IFrameQuantizer<TPixel> paletteFrameQuantizer =
new PaletteFrameQuantizer<TPixel>(this.configuration, this.quantizer.Diffuser, quantized.Palette))
using (var paletteFrameQuantizer = new PaletteFrameQuantizer<TPixel>(this.configuration, this.quantizer.Options, quantized.Palette))
using (QuantizedFrame<TPixel> paletteQuantized = paletteFrameQuantizer.QuantizeFrame(frame, frame.Bounds()))
{
using (IQuantizedFrame<TPixel> paletteQuantized = paletteFrameQuantizer.QuantizeFrame(frame))
{
this.WriteImageData(paletteQuantized, stream);
}
this.WriteImageData(paletteQuantized, stream);
}
}
}
}

private void EncodeLocal<TPixel>(Image<TPixel> image, IQuantizedFrame<TPixel> quantized, Stream stream)
private void EncodeLocal<TPixel>(Image<TPixel> image, QuantizedFrame<TPixel> quantized, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
ImageFrame<TPixel> previousFrame = null;
Expand All @@ -171,16 +166,23 @@ private void EncodeLocal<TPixel>(Image<TPixel> image, IQuantizedFrame<TPixel> qu
if (previousFrame != null && previousMeta.ColorTableLength != frameMetadata.ColorTableLength
&& frameMetadata.ColorTableLength > 0)
{
using (IFrameQuantizer<TPixel> frameQuantizer = this.quantizer.CreateFrameQuantizer<TPixel>(this.configuration, frameMetadata.ColorTableLength))
var options = new QuantizerOptions
{
Dither = this.quantizer.Options.Dither,
DitherScale = this.quantizer.Options.DitherScale,
MaxColors = frameMetadata.ColorTableLength
};

using (IFrameQuantizer<TPixel> frameQuantizer = this.quantizer.CreateFrameQuantizer<TPixel>(this.configuration, options))
{
quantized = frameQuantizer.QuantizeFrame(frame);
quantized = frameQuantizer.QuantizeFrame(frame, frame.Bounds());
}
}
else
{
using (IFrameQuantizer<TPixel> frameQuantizer = this.quantizer.CreateFrameQuantizer<TPixel>(this.configuration))
{
quantized = frameQuantizer.QuantizeFrame(frame);
quantized = frameQuantizer.QuantizeFrame(frame, frame.Bounds());
}
}
}
Expand All @@ -206,7 +208,7 @@ private void EncodeLocal<TPixel>(Image<TPixel> image, IQuantizedFrame<TPixel> qu
/// <returns>
/// The <see cref="int"/>.
/// </returns>
private int GetTransparentIndex<TPixel>(IQuantizedFrame<TPixel> quantized)
private int GetTransparentIndex<TPixel>(QuantizedFrame<TPixel> quantized)
where TPixel : struct, IPixel<TPixel>
{
// Transparent pixels are much more likely to be found at the end of a palette
Expand Down Expand Up @@ -435,7 +437,7 @@ private void WriteImageDescriptor<TPixel>(ImageFrame<TPixel> image, bool hasColo
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode.</param>
/// <param name="stream">The stream to write to.</param>
private void WriteColorTable<TPixel>(IQuantizedFrame<TPixel> image, Stream stream)
private void WriteColorTable<TPixel>(QuantizedFrame<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
// The maximum number of colors for the bit depth
Expand All @@ -457,9 +459,9 @@ private void WriteColorTable<TPixel>(IQuantizedFrame<TPixel> image, Stream strea
/// Writes the image pixel data to the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="IQuantizedFrame{TPixel}"/> containing indexed pixels.</param>
/// <param name="image">The <see cref="QuantizedFrame{TPixel}"/> containing indexed pixels.</param>
/// <param name="stream">The stream to write to.</param>
private void WriteImageData<TPixel>(IQuantizedFrame<TPixel> image, Stream stream)
private void WriteImageData<TPixel>(QuantizedFrame<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
using (var encoder = new LzwEncoder(this.memoryAllocator, (byte)this.bitDepth))
Expand Down
14 changes: 7 additions & 7 deletions src/ImageSharp/Formats/Png/PngEncoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream)
ImageMetadata metadata = image.Metadata;
PngMetadata pngMetadata = metadata.GetPngMetadata();
PngEncoderOptionsHelpers.AdjustOptions<TPixel>(this.options, pngMetadata, out this.use16Bit, out this.bytesPerPixel);
IQuantizedFrame<TPixel> quantized = PngEncoderOptionsHelpers.CreateQuantizedFrame(this.options, image);
QuantizedFrame<TPixel> quantized = PngEncoderOptionsHelpers.CreateQuantizedFrame(this.options, image);
this.bitDepth = PngEncoderOptionsHelpers.CalculateBitDepth(this.options, image, quantized);

stream.Write(PngConstants.HeaderBytes, 0, PngConstants.HeaderBytes.Length);
Expand Down Expand Up @@ -371,7 +371,7 @@ private void CollectTPixelBytes<TPixel>(ReadOnlySpan<TPixel> rowSpan)
/// <param name="rowSpan">The row span.</param>
/// <param name="quantized">The quantized pixels. Can be null.</param>
/// <param name="row">The row.</param>
private void CollectPixelBytes<TPixel>(ReadOnlySpan<TPixel> rowSpan, IQuantizedFrame<TPixel> quantized, int row)
private void CollectPixelBytes<TPixel>(ReadOnlySpan<TPixel> rowSpan, QuantizedFrame<TPixel> quantized, int row)
where TPixel : struct, IPixel<TPixel>
{
switch (this.options.ColorType)
Expand Down Expand Up @@ -440,7 +440,7 @@ private IManagedByteBuffer FilterPixelBytes()
/// <param name="quantized">The quantized pixels. Can be null.</param>
/// <param name="row">The row.</param>
/// <returns>The <see cref="IManagedByteBuffer"/></returns>
private IManagedByteBuffer EncodePixelRow<TPixel>(ReadOnlySpan<TPixel> rowSpan, IQuantizedFrame<TPixel> quantized, int row)
private IManagedByteBuffer EncodePixelRow<TPixel>(ReadOnlySpan<TPixel> rowSpan, QuantizedFrame<TPixel> quantized, int row)
where TPixel : struct, IPixel<TPixel>
{
this.CollectPixelBytes(rowSpan, quantized, row);
Expand Down Expand Up @@ -546,7 +546,7 @@ private void WriteHeaderChunk(Stream stream)
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="quantized">The quantized frame.</param>
private void WritePaletteChunk<TPixel>(Stream stream, IQuantizedFrame<TPixel> quantized)
private void WritePaletteChunk<TPixel>(Stream stream, QuantizedFrame<TPixel> quantized)
where TPixel : struct, IPixel<TPixel>
{
if (quantized == null)
Expand Down Expand Up @@ -783,7 +783,7 @@ private void WriteTransparencyChunk(Stream stream, PngMetadata pngMetadata)
/// <param name="pixels">The image.</param>
/// <param name="quantized">The quantized pixel data. Can be null.</param>
/// <param name="stream">The stream.</param>
private void WriteDataChunks<TPixel>(ImageFrame<TPixel> pixels, IQuantizedFrame<TPixel> quantized, Stream stream)
private void WriteDataChunks<TPixel>(ImageFrame<TPixel> pixels, QuantizedFrame<TPixel> quantized, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
byte[] buffer;
Expand Down Expand Up @@ -881,7 +881,7 @@ private void AllocateExtBuffers()
/// <param name="pixels">The pixels.</param>
/// <param name="quantized">The quantized pixels span.</param>
/// <param name="deflateStream">The deflate stream.</param>
private void EncodePixels<TPixel>(ImageFrame<TPixel> pixels, IQuantizedFrame<TPixel> quantized, ZlibDeflateStream deflateStream)
private void EncodePixels<TPixel>(ImageFrame<TPixel> pixels, QuantizedFrame<TPixel> quantized, ZlibDeflateStream deflateStream)
where TPixel : struct, IPixel<TPixel>
{
int bytesPerScanline = this.CalculateScanlineLength(this.width);
Expand Down Expand Up @@ -960,7 +960,7 @@ private void EncodeAdam7Pixels<TPixel>(ImageFrame<TPixel> pixels, ZlibDeflateStr
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="quantized">The quantized.</param>
/// <param name="deflateStream">The deflate stream.</param>
private void EncodeAdam7IndexedPixels<TPixel>(IQuantizedFrame<TPixel> quantized, ZlibDeflateStream deflateStream)
private void EncodeAdam7IndexedPixels<TPixel>(QuantizedFrame<TPixel> quantized, ZlibDeflateStream deflateStream)
where TPixel : struct, IPixel<TPixel>
{
int width = quantized.Width;
Expand Down
Loading