From 50aefa6ead69d688519a9d852b45e78973c4fa57 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 28 Jan 2017 17:37:49 +0100 Subject: [PATCH 001/142] DecodedBlock cleanup & better comments --- .../Components/Decoder/DecodedBlock.cs | 58 ++++++++++ .../Components/Decoder/DecodedBlockArray.cs | 55 ++++++++++ .../Components/Decoder/DecodedBlockMemento.cs | 101 ------------------ .../Components/Decoder/JpegScanDecoder.cs | 10 +- .../JpegDecoderCore.cs | 18 ++-- 5 files changed, 128 insertions(+), 114 deletions(-) create mode 100644 src/ImageSharp.Formats.Jpeg/Components/Decoder/DecodedBlock.cs create mode 100644 src/ImageSharp.Formats.Jpeg/Components/Decoder/DecodedBlockArray.cs delete mode 100644 src/ImageSharp.Formats.Jpeg/Components/Decoder/DecodedBlockMemento.cs diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecodedBlock.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecodedBlock.cs new file mode 100644 index 000000000..bc93a18f7 --- /dev/null +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecodedBlock.cs @@ -0,0 +1,58 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Jpg +{ + using System; + + /// + /// A structure to store unprocessed instances and their coordinates while scanning the image. + /// The is present in a "raw" decoded frequency-domain form. + /// We need to apply IDCT and unzigging to transform them into color-space blocks. + /// + internal struct DecodedBlock + { + /// + /// A value indicating whether the instance is initialized. + /// + public bool Initialized; + + /// + /// X coordinate of the current block, in units of 8x8. (The third block in the first row has (bx, by) = (2, 0)) + /// + public int Bx; + + /// + /// Y coordinate of the current block, in units of 8x8. (The third block in the first row has (bx, by) = (2, 0)) + /// + public int By; + + /// + /// The + /// + public Block8x8F Block; + + /// + /// Store the block data into a at the given index of an . + /// + /// The array + /// The index in the array + /// X coordinate of the block + /// Y coordinate of the block + /// The + public static void Store(ref DecodedBlockArray blockArray, int index, int bx, int by, ref Block8x8F block) + { + if (index >= blockArray.Count) + { + throw new IndexOutOfRangeException("Block index is out of range in DecodedBlock.Store()!"); + } + + blockArray.Buffer[index].Initialized = true; + blockArray.Buffer[index].Bx = bx; + blockArray.Buffer[index].By = by; + blockArray.Buffer[index].Block = block; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecodedBlockArray.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecodedBlockArray.cs new file mode 100644 index 000000000..97a79dd61 --- /dev/null +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecodedBlockArray.cs @@ -0,0 +1,55 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Jpg +{ + using System; + using System.Buffers; + + /// + /// Because has no information for rented arrays, + /// we need to store the count and the buffer separately when storing pooled arrays. + /// + internal struct DecodedBlockArray : IDisposable + { + /// + /// The used to pool data in . + /// Should always clean arrays when returning! + /// + private static readonly ArrayPool ArrayPool = ArrayPool.Create(); + + /// + /// Initializes a new instance of the struct. Rents a buffer. + /// + /// The number of valid -s + public DecodedBlockArray(int count) + { + this.Count = count; + this.Buffer = ArrayPool.Rent(count); + } + + /// + /// Gets the number of actual -s inside + /// + public int Count { get; } + + /// + /// Gets the rented buffer. + /// + public DecodedBlock[] Buffer { get; private set; } + + /// + /// Returns the rented buffer to the pool. + /// + public void Dispose() + { + if (this.Buffer != null) + { + ArrayPool.Return(this.Buffer, true); + this.Buffer = null; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecodedBlockMemento.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecodedBlockMemento.cs deleted file mode 100644 index 04ece04ee..000000000 --- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecodedBlockMemento.cs +++ /dev/null @@ -1,101 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats.Jpg -{ - using System; - using System.Buffers; - - /// - /// A structure to store unprocessed instances and their coordinates while scanning the image. - /// - internal struct DecodedBlockMemento - { - /// - /// A value indicating whether the instance is initialized. - /// - public bool Initialized; - - /// - /// X coordinate of the current block, in units of 8x8. (The third block in the first row has (bx, by) = (2, 0)) - /// - public int Bx; - - /// - /// Y coordinate of the current block, in units of 8x8. (The third block in the first row has (bx, by) = (2, 0)) - /// - public int By; - - /// - /// The - /// - public Block8x8F Block; - - /// - /// Store the block data into a at the given index of an . - /// - /// The array - /// The index in the array - /// X coordinate of the block - /// Y coordinate of the block - /// The - public static void Store(ref DecodedBlockMemento.Array blockArray, int index, int bx, int by, ref Block8x8F block) - { - if (index >= blockArray.Count) - { - throw new IndexOutOfRangeException("Block index is out of range in DecodedBlockMemento.Store()!"); - } - - blockArray.Buffer[index].Initialized = true; - blockArray.Buffer[index].Bx = bx; - blockArray.Buffer[index].By = by; - blockArray.Buffer[index].Block = block; - } - - /// - /// Because has no information for rented arrays, we need to store the count and the buffer separately. - /// - public struct Array : IDisposable - { - /// - /// The used to pool data in . - /// Should always clean arrays when returning! - /// - private static readonly ArrayPool ArrayPool = ArrayPool.Create(); - - /// - /// Initializes a new instance of the struct. Rents a buffer. - /// - /// The number of valid -s - public Array(int count) - { - this.Count = count; - this.Buffer = ArrayPool.Rent(count); - } - - /// - /// Gets the number of actual -s inside - /// - public int Count { get; } - - /// - /// Gets the rented buffer. - /// - public DecodedBlockMemento[] Buffer { get; private set; } - - /// - /// Returns the rented buffer to the pool. - /// - public void Dispose() - { - if (this.Buffer != null) - { - ArrayPool.Return(this.Buffer, true); - this.Buffer = null; - } - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs index 0e389771c..103ee60c0 100644 --- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs @@ -121,10 +121,10 @@ namespace ImageSharp.Formats.Jpg } /// - /// Loads the data from the given into the block. + /// Loads the data from the given into the block. /// - /// The - public void LoadMemento(ref DecodedBlockMemento memento) + /// The + public void LoadMemento(ref DecodedBlock memento) { this.bx = memento.Bx; this.by = memento.By; @@ -204,8 +204,8 @@ namespace ImageSharp.Formats.Jpg } // Store the decoded block - DecodedBlockMemento.Array blocks = decoder.DecodedBlocks[this.ComponentIndex]; - DecodedBlockMemento.Store(ref blocks, blockIndex, this.bx, this.by, ref this.data.Block); + DecodedBlockArray blocks = decoder.DecodedBlocks[this.ComponentIndex]; + DecodedBlock.Store(ref blocks, blockIndex, this.bx, this.by, ref this.data.Block); } // for j diff --git a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs index eca4d4622..fd06018a2 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs @@ -87,7 +87,7 @@ namespace ImageSharp.Formats this.QuantizationTables = new Block8x8F[MaxTq + 1]; this.Temp = new byte[2 * Block8x8F.ScalarCount]; this.ComponentArray = new Component[MaxComponents]; - this.DecodedBlocks = new DecodedBlockMemento.Array[MaxComponents]; + this.DecodedBlocks = new DecodedBlockArray[MaxComponents]; } /// @@ -101,10 +101,12 @@ namespace ImageSharp.Formats public HuffmanTree[] HuffmanTrees { get; } /// - /// Gets the saved state between progressive-mode scans. - /// TODO: Also save non-progressive data here. (Helps splitting and parallelizing JpegScanDecoder-s loop) + /// Gets the array of -s storing the "raw" frequency-domain decoded blocks. + /// We need to apply IDCT and unzigging to transform them into color-space blocks. + /// This is done by . + /// When ==true, we are touching these blocks each time we process a Scan. /// - public DecodedBlockMemento.Array[] DecodedBlocks { get; } + public DecodedBlockArray[] DecodedBlocks { get; } /// /// Gets the quantization tables, in zigzag order. @@ -191,7 +193,7 @@ namespace ImageSharp.Formats this.HuffmanTrees[i].Dispose(); } - foreach (DecodedBlockMemento.Array blockArray in this.DecodedBlocks) + foreach (DecodedBlockArray blockArray in this.DecodedBlocks) { blockArray.Dispose(); } @@ -461,7 +463,7 @@ namespace ImageSharp.Formats /// /// Process the blocks in into Jpeg image channels ( and ) - /// The blocks are expected in a "raw" frequency-domain decoded format. We need to apply IDCT and unzigging to transform them into color-space blocks. + /// are in a "raw" frequency-domain form. We need to apply IDCT and unzigging to transform them into color-space blocks. /// We can copy these blocks into -s afterwards. /// /// The pixel type @@ -477,7 +479,7 @@ namespace ImageSharp.Formats JpegScanDecoder.Init(&scanDecoder); scanDecoder.ComponentIndex = componentIndex; - DecodedBlockMemento.Array blockArray = this.DecodedBlocks[componentIndex]; + DecodedBlockArray blockArray = this.DecodedBlocks[componentIndex]; for (int i = 0; i < blockArray.Count; i++) { scanDecoder.LoadMemento(ref blockArray.Buffer[i]); @@ -1316,7 +1318,7 @@ namespace ImageSharp.Formats { int count = this.TotalMCUCount * this.ComponentArray[i].HorizontalFactor * this.ComponentArray[i].VerticalFactor; - this.DecodedBlocks[i] = new DecodedBlockMemento.Array(count); + this.DecodedBlocks[i] = new DecodedBlockArray(count); } } } From 907576b1f714bba8b782615bfceb17b0607c70b4 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 29 Jan 2017 18:15:33 +0100 Subject: [PATCH 002/142] cleanup + stylecop --- .../Components/Decoder/HuffmanTree.cs | 10 ++ .../Components/Decoder/JpegBlockProcessor.cs | 166 ++++++++++++++++++ .../JpegScanDecoder.ComputationData.cs | 15 -- .../Decoder/JpegScanDecoder.DataPointers.cs | 18 -- .../Components/Decoder/JpegScanDecoder.cs | 75 ++------ .../JpegDecoderCore.cs | 19 +- 6 files changed, 197 insertions(+), 106 deletions(-) create mode 100644 src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegBlockProcessor.cs diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/HuffmanTree.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/HuffmanTree.cs index 03013219c..390e5dd15 100644 --- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/HuffmanTree.cs +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/HuffmanTree.cs @@ -12,6 +12,16 @@ namespace ImageSharp.Formats.Jpg /// internal struct HuffmanTree : IDisposable { + /// + /// The index of the AC table row + /// + public const int AcTableIndex = 1; + + /// + /// The index of the DC table row + /// + public const int DcTableIndex = 0; + /// /// The maximum (inclusive) number of codes in a Huffman tree. /// diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegBlockProcessor.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegBlockProcessor.cs new file mode 100644 index 000000000..85018a06f --- /dev/null +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegBlockProcessor.cs @@ -0,0 +1,166 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Jpg +{ + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + + /// + /// Encapsulates the implementation of processing "raw" -s into Jpeg image channels. + /// + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct JpegBlockProcessor + { + /// + /// The + /// + private ComputationData data; + + /// + /// Pointers to elements of + /// + private DataPointers pointers; + + /// + /// The component index. + /// + private int componentIndex; + + /// + /// Initialize the instance on the stack. + /// + /// The instance + /// The current component index + public static void Init(JpegBlockProcessor* processor, int componentIndex) + { + processor->componentIndex = componentIndex; + processor->data = ComputationData.Create(); + processor->pointers = new DataPointers(&processor->data); + } + + /// + /// Dequantize, perform the inverse DCT and store the blocks to the into the corresponding instances. + /// + /// The instance + public void ProcessAllBlocks(JpegDecoderCore decoder) + { + DecodedBlockArray blockArray = decoder.DecodedBlocks[this.componentIndex]; + for (int i = 0; i < blockArray.Count; i++) + { + this.ProcessBlockColors(decoder, ref blockArray.Buffer[i]); + } + } + + /// + /// Dequantize, perform the inverse DCT and store decodedBlock.Block to the into the corresponding instance. + /// + /// The + /// The + private void ProcessBlockColors(JpegDecoderCore decoder, ref DecodedBlock decodedBlock) + { + this.data.Block = decodedBlock.Block; + int qtIndex = decoder.ComponentArray[this.componentIndex].Selector; + this.data.QuantiazationTable = decoder.QuantizationTables[qtIndex]; + + Block8x8F* b = this.pointers.Block; + + Block8x8F.UnZig(b, this.pointers.QuantiazationTable, this.pointers.Unzig); + + DCT.TransformIDCT(ref *b, ref *this.pointers.Temp1, ref *this.pointers.Temp2); + + var destChannel = decoder.GetDestinationChannel(this.componentIndex); + var destArea = destChannel.GetOffsetedSubAreaForBlock(decodedBlock.Bx, decodedBlock.By); + destArea.LoadColorsFrom(this.pointers.Temp1, this.pointers.Temp2); + } + + /// + /// Holds the "large" data blocks needed for computations. + /// + [StructLayout(LayoutKind.Sequential)] + public struct ComputationData + { + /// + /// Temporal block 1 to store intermediate and/or final computation results + /// + public Block8x8F Block; + + /// + /// Temporal block 1 to store intermediate and/or final computation results + /// + public Block8x8F Temp1; + + /// + /// Temporal block 2 to store intermediate and/or final computation results + /// + public Block8x8F Temp2; + + /// + /// The quantization table as + /// + public Block8x8F QuantiazationTable; + + /// + /// The jpeg unzig data + /// + public UnzigData Unzig; + + /// + /// Creates and initializes a new instance + /// + /// The + public static ComputationData Create() + { + ComputationData data = default(ComputationData); + data.Unzig = UnzigData.Create(); + return data; + } + } + + /// + /// Contains pointers to the memory regions of so they can be easily passed around to pointer based utility methods of + /// + public struct DataPointers + { + /// + /// Pointer to + /// + public Block8x8F* Block; + + /// + /// Pointer to + /// + public Block8x8F* Temp1; + + /// + /// Pointer to + /// + public Block8x8F* Temp2; + + /// + /// Pointer to + /// + public Block8x8F* QuantiazationTable; + + /// + /// Pointer to as int* + /// + public int* Unzig; + + /// + /// Initializes a new instance of the struct. + /// + /// Pointer to + internal DataPointers(ComputationData* dataPtr) + { + this.Block = &dataPtr->Block; + this.Temp1 = &dataPtr->Temp1; + this.Temp2 = &dataPtr->Temp2; + this.QuantiazationTable = &dataPtr->QuantiazationTable; + this.Unzig = dataPtr->Unzig.Data; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.ComputationData.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.ComputationData.cs index 06f170be5..7b910cdd2 100644 --- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.ComputationData.cs +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.ComputationData.cs @@ -23,21 +23,6 @@ namespace ImageSharp.Formats.Jpg /// public Block8x8F Block; - /// - /// Temporal block 1 to store intermediate and/or final computation results - /// - public Block8x8F Temp1; - - /// - /// Temporal block 2 to store intermediate and/or final computation results - /// - public Block8x8F Temp2; - - /// - /// The quantization table as - /// - public Block8x8F QuantiazationTable; - /// /// The jpeg unzig data /// diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.DataPointers.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.DataPointers.cs index b76ad59bb..52e25f3a8 100644 --- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.DataPointers.cs +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.DataPointers.cs @@ -20,21 +20,6 @@ namespace ImageSharp.Formats.Jpg /// public Block8x8F* Block; - /// - /// Pointer to - /// - public Block8x8F* Temp1; - - /// - /// Pointer to - /// - public Block8x8F* Temp2; - - /// - /// Pointer to - /// - public Block8x8F* QuantiazationTable; - /// /// Pointer to as int* /// @@ -57,9 +42,6 @@ namespace ImageSharp.Formats.Jpg public DataPointers(ComputationData* basePtr) { this.Block = &basePtr->Block; - this.Temp1 = &basePtr->Temp1; - this.Temp2 = &basePtr->Temp2; - this.QuantiazationTable = &basePtr->QuantiazationTable; this.Unzig = basePtr->Unzig.Data; this.ComponentScan = (ComponentScan*)basePtr->ScanData; this.Dc = basePtr->Dc; diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs index 103ee60c0..a43c545cd 100644 --- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs @@ -7,10 +7,11 @@ namespace ImageSharp.Formats.Jpg { using System; using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; /// - /// Encapsulates the impementation of Jpeg SOS decoder. See JpegScanDecoder.md! - /// TODO: Split JpegScanDecoder: 1. JpegScanDecoder for Huffman-decoding () 2. JpegBlockProcessor for processing () + /// Encapsulates the impementation of Jpeg SOS Huffman decoding. See JpegScanDecoder.md! + /// /// and are the spectral selection bounds. /// and are the successive approximation high and low values. /// The spec calls these values Ss, Se, Ah and Al. @@ -26,17 +27,21 @@ namespace ImageSharp.Formats.Jpg /// significant bit. /// For baseline JPEGs, these parameters are hard-coded to 0/63/0/0. /// + [StructLayout(LayoutKind.Sequential)] internal unsafe partial struct JpegScanDecoder { + // The JpegScanDecoder members should be ordered in a way that results in optimal memory layout. +#pragma warning disable SA1202 // ElementsMustBeOrderedByAccess + /// - /// The AC table index + /// The buffer /// - public const int AcTableIndex = 1; + private ComputationData data; /// - /// The DC table index + /// Pointers to elements of /// - public const int DcTableIndex = 0; + private DataPointers pointers; /// /// The current component index @@ -88,16 +93,6 @@ namespace ImageSharp.Formats.Jpg /// private int eobRun; - /// - /// Pointers to elements of - /// - private DataPointers pointers; - - /// - /// The buffer - /// - private ComputationData data; - /// /// Initializes a default-constructed instance for reading data from -s stream. /// @@ -105,30 +100,10 @@ namespace ImageSharp.Formats.Jpg /// The instance /// The remaining bytes in the segment block. public static void InitStreamReading(JpegScanDecoder* p, JpegDecoderCore decoder, int remaining) - { - Init(p); - p->InitStreamReadingImpl(decoder, remaining); - } - - /// - /// Initializes a default-constructed instance, filling the data and setting the pointers. - /// - /// Pointer to on the stack - public static void Init(JpegScanDecoder* p) { p->data = ComputationData.Create(); p->pointers = new DataPointers(&p->data); - } - - /// - /// Loads the data from the given into the block. - /// - /// The - public void LoadMemento(ref DecodedBlock memento) - { - this.bx = memento.Bx; - this.by = memento.By; - this.data.Block = memento.Block; + p->InitStreamReadingImpl(decoder, remaining); } /// @@ -251,26 +226,6 @@ namespace ImageSharp.Formats.Jpg } } - /// - /// Dequantize, perform the inverse DCT and store the block to the into the corresponding instances. - /// - /// The instance - public void ProcessBlockColors(JpegDecoderCore decoder) - { - int qtIndex = decoder.ComponentArray[this.ComponentIndex].Selector; - this.data.QuantiazationTable = decoder.QuantizationTables[qtIndex]; - - Block8x8F* b = this.pointers.Block; - - Block8x8F.UnZig(b, this.pointers.QuantiazationTable, this.pointers.Unzig); - - DCT.TransformIDCT(ref *b, ref *this.pointers.Temp1, ref *this.pointers.Temp2); - - var destChannel = decoder.GetDestinationChannel(this.ComponentIndex); - var destArea = destChannel.GetOffsetedSubAreaForBlock(this.bx, this.by); - destArea.LoadColorsFrom(this.pointers.Temp1, this.pointers.Temp2); - } - private void ResetDc() { Unsafe.InitBlock(this.pointers.Dc, default(byte), sizeof(int) * JpegDecoderCore.MaxComponents); @@ -351,8 +306,7 @@ namespace ImageSharp.Formats.Jpg private void DecodeBlock(JpegDecoderCore decoder, int scanIndex) { var b = this.pointers.Block; - DecoderErrorCode errorCode; - int huffmannIdx = (AcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].AcTableSelector; + int huffmannIdx = (HuffmanTree.AcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].AcTableSelector; if (this.ah != 0) { this.Refine(ref decoder.InputProcessor, ref decoder.HuffmanTrees[huffmannIdx], 1 << this.al); @@ -360,13 +314,14 @@ namespace ImageSharp.Formats.Jpg else { int zig = this.zigStart; + DecoderErrorCode errorCode; if (zig == 0) { zig++; // Decode the DC coefficient, as specified in section F.2.2.1. int value; - int huffmanIndex = (DcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].DcTableSelector; + int huffmanIndex = (HuffmanTree.DcTableIndex * HuffmanTree.ThRowSize) + this.pointers.ComponentScan[scanIndex].DcTableSelector; errorCode = decoder.InputProcessor.DecodeHuffmanUnsafe( ref decoder.HuffmanTrees[huffmanIndex], out value); diff --git a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs index fd06018a2..72655a500 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs @@ -102,9 +102,9 @@ namespace ImageSharp.Formats /// /// Gets the array of -s storing the "raw" frequency-domain decoded blocks. - /// We need to apply IDCT and unzigging to transform them into color-space blocks. + /// We need to apply IDCT, dequantiazition and unzigging to transform them into color-space blocks. /// This is done by . - /// When ==true, we are touching these blocks each time we process a Scan. + /// When ==true, we are touching these blocks multiple times - each time we process a Scan. /// public DecodedBlockArray[] DecodedBlocks { get; } @@ -463,7 +463,7 @@ namespace ImageSharp.Formats /// /// Process the blocks in into Jpeg image channels ( and ) - /// are in a "raw" frequency-domain form. We need to apply IDCT and unzigging to transform them into color-space blocks. + /// are in a "raw" frequency-domain form. We need to apply IDCT, dequantization and unzigging to transform them into color-space blocks. /// We can copy these blocks into -s afterwards. /// /// The pixel type @@ -475,16 +475,9 @@ namespace ImageSharp.Formats this.ComponentCount, componentIndex => { - JpegScanDecoder scanDecoder = default(JpegScanDecoder); - JpegScanDecoder.Init(&scanDecoder); - - scanDecoder.ComponentIndex = componentIndex; - DecodedBlockArray blockArray = this.DecodedBlocks[componentIndex]; - for (int i = 0; i < blockArray.Count; i++) - { - scanDecoder.LoadMemento(ref blockArray.Buffer[i]); - scanDecoder.ProcessBlockColors(this); - } + JpegBlockProcessor processor = default(JpegBlockProcessor); + JpegBlockProcessor.Init(&processor, componentIndex); + processor.ProcessAllBlocks(this); }); } From 7be47fb81fdb5dfce1f37d2ffa4536a3a1f53982 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 29 Jan 2017 19:16:36 +0100 Subject: [PATCH 003/142] finished JpegBlockProcessor refactor --- .../Components/Decoder/DecodedBlock.cs | 18 +++++------------- .../Components/Decoder/JpegScanDecoder.cs | 2 +- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecodedBlock.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecodedBlock.cs index bc93a18f7..900d77ec4 100644 --- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecodedBlock.cs +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/DecodedBlock.cs @@ -35,24 +35,16 @@ namespace ImageSharp.Formats.Jpg public Block8x8F Block; /// - /// Store the block data into a at the given index of an . + /// Store the block data into a /// - /// The array - /// The index in the array /// X coordinate of the block /// Y coordinate of the block /// The - public static void Store(ref DecodedBlockArray blockArray, int index, int bx, int by, ref Block8x8F block) + public void SaveBlock(int bx, int by, ref Block8x8F block) { - if (index >= blockArray.Count) - { - throw new IndexOutOfRangeException("Block index is out of range in DecodedBlock.Store()!"); - } - - blockArray.Buffer[index].Initialized = true; - blockArray.Buffer[index].Bx = bx; - blockArray.Buffer[index].By = by; - blockArray.Buffer[index].Block = block; + this.Bx = bx; + this.By = by; + this.Block = block; } } } \ No newline at end of file diff --git a/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs index a43c545cd..10f859e42 100644 --- a/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs +++ b/src/ImageSharp.Formats.Jpeg/Components/Decoder/JpegScanDecoder.cs @@ -180,7 +180,7 @@ namespace ImageSharp.Formats.Jpg // Store the decoded block DecodedBlockArray blocks = decoder.DecodedBlocks[this.ComponentIndex]; - DecodedBlock.Store(ref blocks, blockIndex, this.bx, this.by, ref this.data.Block); + blocks.Buffer[blockIndex].SaveBlock(this.bx, this.by, ref this.data.Block); } // for j From 1339ad3075fd39301e377f40a843f5e29dd89359 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 1 Feb 2017 19:29:38 +1100 Subject: [PATCH 004/142] Make all images implement IDisposable --- .../Convolution/Convolution2DProcessor.cs | 4 +- .../Convolution/Convolution2PassProcessor.cs | 21 +- .../Effects/OilPaintingProcessor.cs | 4 +- .../Processors/Effects/PixelateProcessor.cs | 4 +- .../Transforms/CompandingResizeProcessor.cs | 159 ++++++------ .../Processors/Transforms/CropProcessor.cs | 4 +- .../Processors/Transforms/FlipProcessor.cs | 8 +- .../Processors/Transforms/ResizeProcessor.cs | 159 ++++++------ .../Processors/Transforms/RotateProcessor.cs | 16 +- .../Processors/Transforms/SkewProcessor.cs | 4 +- src/ImageSharp/Image/IImageBase{TColor}.cs | 4 +- src/ImageSharp/Image/ImageBase{TColor}.cs | 111 ++++++++- src/ImageSharp/Image/Image{TColor}.cs | 16 +- src/ImageSharp/Image/PixelAccessor{TColor}.cs | 4 +- src/ImageSharp/Image/PixelPool{TColor}.cs | 50 ++++ .../Drawing/DrawBeziers.cs | 18 +- .../Drawing/DrawLines.cs | 24 +- .../Drawing/DrawPolygon.cs | 24 +- .../Drawing/FillPolygon.cs | 23 +- .../Drawing/FillRectangle.cs | 29 ++- .../Drawing/FillWithPattern.cs | 12 +- .../ImageSharp.Benchmarks/Image/CopyPixels.cs | 34 +-- .../ImageSharp.Benchmarks/Image/DecodeBmp.cs | 6 +- .../Image/DecodeFilteredPng.cs | 17 +- .../ImageSharp.Benchmarks/Image/DecodeGif.cs | 6 +- .../ImageSharp.Benchmarks/Image/DecodeJpeg.cs | 6 +- .../ImageSharp.Benchmarks/Image/DecodePng.cs | 6 +- .../ImageSharp.Benchmarks/Image/EncodeBmp.cs | 8 + .../Image/EncodeBmpMultiple.cs | 4 +- .../ImageSharp.Benchmarks/Image/EncodeGif.cs | 8 + .../ImageSharp.Benchmarks/Image/EncodeJpeg.cs | 8 + .../ImageSharp.Benchmarks/Image/EncodePng.cs | 8 + .../Image/GetSetPixel.cs | 10 +- tests/ImageSharp.Benchmarks/Samplers/Crop.cs | 8 +- .../Samplers/DetectEdges.cs | 6 + .../ImageSharp.Benchmarks/Samplers/Resize.cs | 16 +- .../ImageSharp.Tests/Drawing/BeziersTests.cs | 137 ++++++----- .../ImageSharp.Tests/Drawing/DrawImageTest.cs | 21 +- .../ImageSharp.Tests/Drawing/DrawPathTests.cs | 101 ++++---- .../Drawing/FillPatternTests.cs | 71 +++--- .../Drawing/FillSolidBrushTests.cs | 100 ++++---- .../Drawing/LineComplexPolygonTests.cs | 226 +++++++++--------- tests/ImageSharp.Tests/Drawing/LineTests.cs | 155 ++++++------ .../ImageSharp.Tests/Drawing/PolygonTests.cs | 112 ++++----- .../Drawing/RecolorImageTest.cs | 32 +-- .../Drawing/SolidBezierTests.cs | 102 ++++---- .../Drawing/SolidComplexPolygonTests.cs | 138 +++++------ .../Drawing/SolidPolygonTests.cs | 156 ++++++------ .../Formats/Bmp/BitmapTests.cs | 15 +- .../Formats/GeneralFormatTests.cs | 101 ++++---- .../Formats/Jpg/BadEofJpegTests.cs | 16 +- .../Formats/Jpg/JpegDecoderTests.cs | 61 ++--- .../Formats/Jpg/JpegEncoderTests.cs | 44 ++-- .../Formats/Jpg/JpegUtilsTests.cs | 85 +++---- .../Jpg/ReferenceImplementationsTests.cs | 4 +- .../ImageSharp.Tests/Formats/Png/PngTests.cs | 20 +- tests/ImageSharp.Tests/Image/ImageTests.cs | 9 +- .../Image/PixelAccessorTests.cs | 118 +++++---- .../Processors/Filters/AlphaTest.cs | 20 +- .../Processors/Filters/AutoOrientTests.cs | 19 +- .../Processors/Filters/BackgroundColorTest.cs | 8 +- .../Processors/Filters/BinaryThreshold.cs | 10 +- .../Processors/Filters/BlackWhiteTest.cs | 8 +- .../Processors/Filters/BoxBlurTest.cs | 10 +- .../Processors/Filters/BrightnessTest.cs | 10 +- .../Processors/Filters/ColorBlindnessTest.cs | 10 +- .../Processors/Filters/ContrastTest.cs | 10 +- .../Processors/Filters/CropTest.cs | 8 +- .../Processors/Filters/DetectEdgesTest.cs | 15 +- .../Processors/Filters/EntropyCropTest.cs | 10 +- .../Processors/Filters/FlipTests.cs | 10 +- .../Processors/Filters/GaussianBlurTest.cs | 10 +- .../Processors/Filters/GaussianSharpenTest.cs | 10 +- .../Processors/Filters/GlowTest.cs | 31 +-- .../Processors/Filters/GrayscaleTest.cs | 10 +- .../Processors/Filters/HueTest.cs | 10 +- .../Processors/Filters/InvertTest.cs | 16 +- .../Processors/Filters/KodachromeTest.cs | 8 +- .../Processors/Filters/LomographTest.cs | 15 +- .../Processors/Filters/OilPaintTest.cs | 21 +- .../Processors/Filters/PadTest.cs | 8 +- .../Processors/Filters/PixelateTest.cs | 8 +- .../Processors/Filters/PolaroidTest.cs | 8 +- .../Processors/Filters/ResizeTests.cs | 76 +++--- .../Processors/Filters/RotateFlipTest.cs | 10 +- .../Processors/Filters/RotateTest.cs | 20 +- .../Processors/Filters/SaturationTest.cs | 8 +- .../Processors/Filters/SepiaTest.cs | 6 +- .../Processors/Filters/SkewTest.cs | 12 +- .../Processors/Filters/VignetteTest.cs | 31 +-- .../Profiles/Exif/ExifProfileTests.cs | 3 + .../Exif/ExifTagDescriptionAttributeTests.cs | 2 +- .../Profiles/Exif/ExifValueTests.cs | 7 +- 93 files changed, 1671 insertions(+), 1480 deletions(-) create mode 100644 src/ImageSharp/Image/PixelPool{TColor}.cs diff --git a/src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs index d6ea42f0c..f77f1f439 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs @@ -54,9 +54,9 @@ namespace ImageSharp.Processing.Processors int maxY = endY - 1; int maxX = endX - 1; - TColor[] target = new TColor[source.Width * source.Height]; + TColor[] target = PixelPool.RentPixels(source.Width * source.Height); using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(source.Width, source.Height)) + using (PixelAccessor targetPixels = target.Lock(source.Width, source.Height)) { Parallel.For( startY, diff --git a/src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs index ad7ed83ed..ca343b868 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs @@ -45,13 +45,20 @@ namespace ImageSharp.Processing.Processors int width = source.Width; int height = source.Height; - TColor[] target = new TColor[width * height]; - TColor[] firstPass = new TColor[width * height]; + TColor[] target = PixelPool.RentPixels(width * height); + TColor[] firstPass = PixelPool.RentPixels(width * height); - this.ApplyConvolution(width, height, firstPass, source.Pixels, sourceRectangle, kernelX); - this.ApplyConvolution(width, height, target, firstPass, sourceRectangle, kernelY); + try + { + this.ApplyConvolution(width, height, firstPass, source.Pixels, sourceRectangle, kernelX); + this.ApplyConvolution(width, height, target, firstPass, sourceRectangle, kernelY); - source.SetPixels(width, height, target); + source.SetPixels(width, height, target); + } + finally + { + PixelPool.ReturnPixels(firstPass); + } } /// @@ -80,8 +87,8 @@ namespace ImageSharp.Processing.Processors int maxY = endY - 1; int maxX = endX - 1; - using (PixelAccessor sourcePixels = source.Lock(width, height)) - using (PixelAccessor targetPixels = target.Lock(width, height)) + using (PixelAccessor sourcePixels = source.Lock(width, height)) + using (PixelAccessor targetPixels = target.Lock(width, height)) { Parallel.For( startY, diff --git a/src/ImageSharp.Processing/Processors/Effects/OilPaintingProcessor.cs b/src/ImageSharp.Processing/Processors/Effects/OilPaintingProcessor.cs index 9e12a2a91..8d2340256 100644 --- a/src/ImageSharp.Processing/Processors/Effects/OilPaintingProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Effects/OilPaintingProcessor.cs @@ -67,9 +67,9 @@ namespace ImageSharp.Processing.Processors startX = 0; } - TColor[] target = new TColor[source.Width * source.Height]; + TColor[] target = PixelPool.RentPixels(source.Width * source.Height); using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(source.Width, source.Height)) + using (PixelAccessor targetPixels = target.Lock(source.Width, source.Height)) { Parallel.For( minY, diff --git a/src/ImageSharp.Processing/Processors/Effects/PixelateProcessor.cs b/src/ImageSharp.Processing/Processors/Effects/PixelateProcessor.cs index 9c9cf92fe..d44858061 100644 --- a/src/ImageSharp.Processing/Processors/Effects/PixelateProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Effects/PixelateProcessor.cs @@ -63,10 +63,10 @@ namespace ImageSharp.Processing.Processors // Get the range on the y-plane to choose from. IEnumerable range = EnumerableExtensions.SteppedRange(minY, i => i < maxY, size); - TColor[] target = new TColor[source.Width * source.Height]; + TColor[] target = PixelPool.RentPixels(source.Width * source.Height); using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(source.Width, source.Height)) + using (PixelAccessor targetPixels = target.Lock(source.Width, source.Height)) { Parallel.ForEach( range, diff --git a/src/ImageSharp.Processing/Processors/Transforms/CompandingResizeProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/CompandingResizeProcessor.cs index a392de051..d9e4f6675 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/CompandingResizeProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Transforms/CompandingResizeProcessor.cs @@ -66,103 +66,112 @@ namespace ImageSharp.Processing.Processors int minY = Math.Max(0, startY); int maxY = Math.Min(height, endY); - TColor[] target = new TColor[width * height]; + TColor[] firstPass = null; - if (this.Sampler is NearestNeighborResampler) + try { - // Scaling factors - float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width; - float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height; + TColor[] target = PixelPool.RentPixels(width * height); + if (this.Sampler is NearestNeighborResampler) + { + // Scaling factors + float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width; + float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height; + + using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor targetPixels = target.Lock(width, height)) + { + Parallel.For( + minY, + maxY, + this.ParallelOptions, + y => + { + // Y coordinates of source points + int originY = (int)((y - startY) * heightFactor); + + for (int x = minX; x < maxX; x++) + { + // X coordinates of source points + targetPixels[x, y] = sourcePixels[(int)((x - startX) * widthFactor), originY]; + } + }); + } + + // Break out now. + source.SetPixels(width, height, target); + return; + } + // Interpolate the image using the calculated weights. + // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm + // First process the columns. Since we are not using multiple threads startY and endY + // are the upper and lower bounds of the source rectangle. + firstPass = PixelPool.RentPixels(width * source.Height); using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(width, height)) + using (PixelAccessor firstPassPixels = firstPass.Lock(width, source.Height)) + using (PixelAccessor targetPixels = target.Lock(width, height)) { Parallel.For( - minY, - maxY, + 0, + sourceRectangle.Height, this.ParallelOptions, y => { - // Y coordinates of source points - int originY = (int)((y - startY) * heightFactor); - for (int x = minX; x < maxX; x++) { - // X coordinates of source points - targetPixels[x, y] = sourcePixels[(int)((x - startX) * widthFactor), originY]; - } - }); - } + // Ensure offsets are normalised for cropping and padding. + Weight[] horizontalValues = this.HorizontalWeights[x - startX].Values; - // Break out now. - source.SetPixels(width, height, target); - return; - } + // Destination color components + Vector4 destination = Vector4.Zero; - // Interpolate the image using the calculated weights. - // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm - // First process the columns. Since we are not using multiple threads startY and endY - // are the upper and lower bounds of the source rectangle. - TColor[] firstPass = new TColor[width * source.Height]; - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor firstPassPixels = firstPass.Lock(width, source.Height)) - using (PixelAccessor targetPixels = target.Lock(width, height)) - { - Parallel.For( - 0, - sourceRectangle.Height, - this.ParallelOptions, - y => - { - for (int x = minX; x < maxX; x++) - { - // Ensure offsets are normalised for cropping and padding. - Weight[] horizontalValues = this.HorizontalWeights[x - startX].Values; + for (int i = 0; i < horizontalValues.Length; i++) + { + Weight xw = horizontalValues[i]; + destination += sourcePixels[xw.Index, y].ToVector4().Expand() * xw.Value; + } - // Destination color components - Vector4 destination = Vector4.Zero; - - for (int i = 0; i < horizontalValues.Length; i++) - { - Weight xw = horizontalValues[i]; - destination += sourcePixels[xw.Index, y].ToVector4().Expand() * xw.Value; + TColor d = default(TColor); + d.PackFromVector4(destination.Compress()); + firstPassPixels[x, y] = d; } + }); - TColor d = default(TColor); - d.PackFromVector4(destination.Compress()); - firstPassPixels[x, y] = d; - } - }); - - // Now process the rows. - Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => - { - // Ensure offsets are normalised for cropping and padding. - Weight[] verticalValues = this.VerticalWeights[y - startY].Values; - - for (int x = 0; x < width; x++) + // Now process the rows. + Parallel.For( + minY, + maxY, + this.ParallelOptions, + y => { - // Destination color components - Vector4 destination = Vector4.Zero; + // Ensure offsets are normalised for cropping and padding. + Weight[] verticalValues = this.VerticalWeights[y - startY].Values; - for (int i = 0; i < verticalValues.Length; i++) + for (int x = 0; x < width; x++) { - Weight yw = verticalValues[i]; - destination += firstPassPixels[x, yw.Index].ToVector4().Expand() * yw.Value; + // Destination color components + Vector4 destination = Vector4.Zero; + + for (int i = 0; i < verticalValues.Length; i++) + { + Weight yw = verticalValues[i]; + destination += firstPassPixels[x, yw.Index].ToVector4().Expand() * yw.Value; + } + + TColor d = default(TColor); + d.PackFromVector4(destination.Compress()); + targetPixels[x, y] = d; } + }); + } - TColor d = default(TColor); - d.PackFromVector4(destination.Compress()); - targetPixels[x, y] = d; - } - }); + source.SetPixels(width, height, target); + } + finally + { + // We don't return target or source pixels as they are handled in the image itself. + PixelPool.ReturnPixels(firstPass); } - - source.SetPixels(width, height, target); } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Transforms/CropProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/CropProcessor.cs index 27b5bef0c..31bd08090 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/CropProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Transforms/CropProcessor.cs @@ -42,10 +42,10 @@ namespace ImageSharp.Processing.Processors int minX = Math.Max(this.CropRectangle.X, sourceRectangle.X); int maxX = Math.Min(this.CropRectangle.Right, sourceRectangle.Right); - TColor[] target = new TColor[this.CropRectangle.Width * this.CropRectangle.Height]; + TColor[] target = PixelPool.RentPixels(this.CropRectangle.Width * this.CropRectangle.Height); using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(this.CropRectangle.Width, this.CropRectangle.Height)) + using (PixelAccessor targetPixels = target.Lock(this.CropRectangle.Width, this.CropRectangle.Height)) { Parallel.For( minY, diff --git a/src/ImageSharp.Processing/Processors/Transforms/FlipProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/FlipProcessor.cs index ba21dced7..374d54fa2 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/FlipProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Transforms/FlipProcessor.cs @@ -55,10 +55,10 @@ namespace ImageSharp.Processing.Processors int height = source.Height; int halfHeight = (int)Math.Ceiling(source.Height * .5F); - TColor[] target = new TColor[width * height]; + TColor[] target = PixelPool.RentPixels(width * height); using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(width, height)) + using (PixelAccessor targetPixels = target.Lock(width, height)) { Parallel.For( 0, @@ -89,10 +89,10 @@ namespace ImageSharp.Processing.Processors int height = source.Height; int halfWidth = (int)Math.Ceiling(width * .5F); - TColor[] target = new TColor[width * height]; + TColor[] target = PixelPool.RentPixels(width * height); using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(width, height)) + using (PixelAccessor targetPixels = target.Lock(width, height)) { Parallel.For( 0, diff --git a/src/ImageSharp.Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/ResizeProcessor.cs index 687e452e6..108391713 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Transforms/ResizeProcessor.cs @@ -65,103 +65,112 @@ namespace ImageSharp.Processing.Processors int minY = Math.Max(0, startY); int maxY = Math.Min(height, endY); - TColor[] target = new TColor[width * height]; + TColor[] firstPass = null; - if (this.Sampler is NearestNeighborResampler) + try { - // Scaling factors - float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width; - float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height; + TColor[] target = PixelPool.RentPixels(width * height); + if (this.Sampler is NearestNeighborResampler) + { + // Scaling factors + float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width; + float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height; + + using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor targetPixels = target.Lock(width, height)) + { + Parallel.For( + minY, + maxY, + this.ParallelOptions, + y => + { + // Y coordinates of source points + int originY = (int)((y - startY) * heightFactor); + + for (int x = minX; x < maxX; x++) + { + // X coordinates of source points + targetPixels[x, y] = sourcePixels[(int)((x - startX) * widthFactor), originY]; + } + }); + } + + // Break out now. + source.SetPixels(width, height, target); + return; + } + // Interpolate the image using the calculated weights. + // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm + // First process the columns. Since we are not using multiple threads startY and endY + // are the upper and lower bounds of the source rectangle. + firstPass = PixelPool.RentPixels(width * source.Height); using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(width, height)) + using (PixelAccessor firstPassPixels = firstPass.Lock(width, source.Height)) + using (PixelAccessor targetPixels = target.Lock(width, height)) { Parallel.For( - minY, - maxY, + 0, + sourceRectangle.Height, this.ParallelOptions, y => { - // Y coordinates of source points - int originY = (int)((y - startY) * heightFactor); - for (int x = minX; x < maxX; x++) { - // X coordinates of source points - targetPixels[x, y] = sourcePixels[(int)((x - startX) * widthFactor), originY]; - } - }); - } + // Ensure offsets are normalised for cropping and padding. + Weight[] horizontalValues = this.HorizontalWeights[x - startX].Values; - // Break out now. - source.SetPixels(width, height, target); - return; - } + // Destination color components + Vector4 destination = Vector4.Zero; - // Interpolate the image using the calculated weights. - // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm - // First process the columns. Since we are not using multiple threads startY and endY - // are the upper and lower bounds of the source rectangle. - TColor[] firstPass = new TColor[width * source.Height]; - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor firstPassPixels = firstPass.Lock(width, source.Height)) - using (PixelAccessor targetPixels = target.Lock(width, height)) - { - Parallel.For( - 0, - sourceRectangle.Height, - this.ParallelOptions, - y => - { - for (int x = minX; x < maxX; x++) - { - // Ensure offsets are normalised for cropping and padding. - Weight[] horizontalValues = this.HorizontalWeights[x - startX].Values; + for (int i = 0; i < horizontalValues.Length; i++) + { + Weight xw = horizontalValues[i]; + destination += sourcePixels[xw.Index, y].ToVector4() * xw.Value; + } - // Destination color components - Vector4 destination = Vector4.Zero; - - for (int i = 0; i < horizontalValues.Length; i++) - { - Weight xw = horizontalValues[i]; - destination += sourcePixels[xw.Index, y].ToVector4() * xw.Value; + TColor d = default(TColor); + d.PackFromVector4(destination); + firstPassPixels[x, y] = d; } + }); - TColor d = default(TColor); - d.PackFromVector4(destination); - firstPassPixels[x, y] = d; - } - }); - - // Now process the rows. - Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => - { - // Ensure offsets are normalised for cropping and padding. - Weight[] verticalValues = this.VerticalWeights[y - startY].Values; - - for (int x = 0; x < width; x++) + // Now process the rows. + Parallel.For( + minY, + maxY, + this.ParallelOptions, + y => { - // Destination color components - Vector4 destination = Vector4.Zero; + // Ensure offsets are normalised for cropping and padding. + Weight[] verticalValues = this.VerticalWeights[y - startY].Values; - for (int i = 0; i < verticalValues.Length; i++) + for (int x = 0; x < width; x++) { - Weight yw = verticalValues[i]; - destination += firstPassPixels[x, yw.Index].ToVector4() * yw.Value; + // Destination color components + Vector4 destination = Vector4.Zero; + + for (int i = 0; i < verticalValues.Length; i++) + { + Weight yw = verticalValues[i]; + destination += firstPassPixels[x, yw.Index].ToVector4() * yw.Value; + } + + TColor d = default(TColor); + d.PackFromVector4(destination); + targetPixels[x, y] = d; } + }); + } - TColor d = default(TColor); - d.PackFromVector4(destination); - targetPixels[x, y] = d; - } - }); + source.SetPixels(width, height, target); + } + finally + { + // We don't return target or source pixels as they are handled in the image itself. + PixelPool.ReturnPixels(firstPass); } - - source.SetPixels(width, height, target); } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/RotateProcessor.cs index 9b9534b39..313542adc 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Transforms/RotateProcessor.cs @@ -42,10 +42,10 @@ namespace ImageSharp.Processing.Processors int height = this.CanvasRectangle.Height; int width = this.CanvasRectangle.Width; Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix); - TColor[] target = new TColor[width * height]; + TColor[] target = PixelPool.RentPixels(width * height); using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(width, height)) + using (PixelAccessor targetPixels = target.Lock(width, height)) { Parallel.For( 0, @@ -124,10 +124,10 @@ namespace ImageSharp.Processing.Processors { int width = source.Width; int height = source.Height; - TColor[] target = new TColor[width * height]; + TColor[] target = PixelPool.RentPixels(width * height); using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(height, width)) + using (PixelAccessor targetPixels = target.Lock(height, width)) { Parallel.For( 0, @@ -156,10 +156,10 @@ namespace ImageSharp.Processing.Processors { int width = source.Width; int height = source.Height; - TColor[] target = new TColor[width * height]; + TColor[] target = PixelPool.RentPixels(width * height); using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(width, height)) + using (PixelAccessor targetPixels = target.Lock(width, height)) { Parallel.For( 0, @@ -187,10 +187,10 @@ namespace ImageSharp.Processing.Processors { int width = source.Width; int height = source.Height; - TColor[] target = new TColor[width * height]; + TColor[] target = PixelPool.RentPixels(width * height); using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(height, width)) + using (PixelAccessor targetPixels = target.Lock(height, width)) { Parallel.For( 0, diff --git a/src/ImageSharp.Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/SkewProcessor.cs index c94f69358..d2d2a129d 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/SkewProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Transforms/SkewProcessor.cs @@ -42,10 +42,10 @@ namespace ImageSharp.Processing.Processors int height = this.CanvasRectangle.Height; int width = this.CanvasRectangle.Width; Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix); - TColor[] target = new TColor[width * height]; + TColor[] target = PixelPool.RentPixels(width * height); using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(width, height)) + using (PixelAccessor targetPixels = target.Lock(width, height)) { Parallel.For( 0, diff --git a/src/ImageSharp/Image/IImageBase{TColor}.cs b/src/ImageSharp/Image/IImageBase{TColor}.cs index bd5b31712..39f3fba67 100644 --- a/src/ImageSharp/Image/IImageBase{TColor}.cs +++ b/src/ImageSharp/Image/IImageBase{TColor}.cs @@ -11,11 +11,13 @@ namespace ImageSharp /// Encapsulates the basic properties and methods required to manipulate images in varying formats. /// /// The pixel format. - public interface IImageBase : IImageBase + public interface IImageBase : IImageBase, IDisposable where TColor : struct, IPackedPixel, IEquatable { /// /// Gets the pixels as an array of the given packed pixel format. + /// Important. Due to the nature in the way this is constructed do not rely on the length + /// of the array for calculations. Use Width * Height. /// TColor[] Pixels { get; } diff --git a/src/ImageSharp/Image/ImageBase{TColor}.cs b/src/ImageSharp/Image/ImageBase{TColor}.cs index 8feaabdab..c58f9412d 100644 --- a/src/ImageSharp/Image/ImageBase{TColor}.cs +++ b/src/ImageSharp/Image/ImageBase{TColor}.cs @@ -6,6 +6,7 @@ namespace ImageSharp { using System; + using System.Buffers; using System.Diagnostics; /// @@ -14,14 +15,30 @@ namespace ImageSharp /// /// The pixel format. [DebuggerDisplay("Image: {Width}x{Height}")] - public abstract class ImageBase : IImageBase + public abstract class ImageBase : IImageBase // IImageBase implements IDisposable where TColor : struct, IPackedPixel, IEquatable { + /// + /// The used to pool data. TODO: Choose sensible default size and count + /// + private static readonly ArrayPool ArrayPool = ArrayPool.Create(int.MaxValue, 50); + /// /// The image pixels /// private TColor[] pixelBuffer; + /// + /// A value indicating whether this instance of the given entity has been disposed. + /// + /// if this instance has been disposed; otherwise, . + /// + /// If the entity is disposed, it must not be disposed a second time. The isDisposed field is set the first time the entity + /// is disposed. If the isDisposed field is true, then the Dispose() method will not dispose again. This help not to prolong the entity's + /// life in the Garbage Collector. + /// + private bool isDisposed; + /// /// Initializes a new instance of the class. /// @@ -41,7 +58,7 @@ namespace ImageSharp /// /// The configuration providing initialization code which allows extending the library. /// - /// + /// /// Thrown if either or are less than or equal to 0. /// protected ImageBase(int width, int height, Configuration configuration = null) @@ -67,15 +84,24 @@ namespace ImageSharp this.Height = other.Height; this.CopyProperties(other); - // Copy the pixels. Unsafe.CopyBlock gives us a nice speed boost here. - this.pixelBuffer = new TColor[this.Width * this.Height]; + // Rent then copy the pixels. Unsafe.CopyBlock gives us a nice speed boost here. + this.RentPixels(); using (PixelAccessor sourcePixels = other.Lock()) using (PixelAccessor target = this.Lock()) { + // Check we can do this without crashing sourcePixels.CopyTo(target); } } + /// + /// Finalizes an instance of the class. + /// + ~ImageBase() + { + this.Dispose(false); + } + /// public int MaxWidth { get; set; } = int.MaxValue; @@ -108,6 +134,19 @@ namespace ImageSharp /// public Configuration Configuration { get; private set; } + /// + public void Dispose() + { + this.Dispose(true); + + // This object will be cleaned up by the Dispose method. + // Therefore, you should call GC.SuppressFinalize to + // take this object off the finalization queue + // and prevent finalization code for this object + // from executing a second time. + GC.SuppressFinalize(this); + } + /// public void InitPixels(int width, int height) { @@ -116,7 +155,7 @@ namespace ImageSharp this.Width = width; this.Height = height; - this.pixelBuffer = new TColor[width * height]; + this.RentPixels(); } /// @@ -126,13 +165,15 @@ namespace ImageSharp Guard.MustBeGreaterThan(height, 0, nameof(height)); Guard.NotNull(pixels, nameof(pixels)); - if (pixels.Length != width * height) + if (!(pixels.Length >= width * height)) { - throw new ArgumentException("Pixel array must have the length of Width * Height."); + throw new ArgumentException($"Pixel array must have the length of at least {width * height}."); } this.Width = width; this.Height = height; + + this.ReturnPixels(); this.pixelBuffer = pixels; } @@ -143,17 +184,18 @@ namespace ImageSharp Guard.MustBeGreaterThan(height, 0, nameof(height)); Guard.NotNull(pixels, nameof(pixels)); - if (pixels.Length != width * height) + if (!(pixels.Length >= width * height)) { - throw new ArgumentException("Pixel array must have the length of Width * Height."); + throw new ArgumentException($"Pixel array must have the length of at least {width * height}."); } this.Width = width; this.Height = height; // Copy the pixels. TODO: use Unsafe.Copy. - this.pixelBuffer = new TColor[pixels.Length]; - Array.Copy(pixels, this.pixelBuffer, pixels.Length); + this.ReturnPixels(); + this.RentPixels(); + Array.Copy(pixels, this.pixelBuffer, width * height); } /// @@ -174,5 +216,52 @@ namespace ImageSharp this.Quality = other.Quality; this.FrameDelay = other.FrameDelay; } + + /// + /// Releases any unmanaged resources from the inheriting class. + /// + protected virtual void ReleaseUnmanagedResources() + { + // TODO release unmanaged resources here + } + + /// + /// Disposes the object and frees resources for the Garbage Collector. + /// + /// If true, the object gets disposed. + protected virtual void Dispose(bool disposing) + { + if (this.isDisposed) + { + return; + } + + this.ReleaseUnmanagedResources(); + + if (disposing) + { + this.ReturnPixels(); + } + + // Note disposing is done. + this.isDisposed = true; + } + + /// + /// Rents the pixel array from the pool. + /// + private void RentPixels() + { + this.pixelBuffer = PixelPool.RentPixels(this.Width * this.Height); + } + + /// + /// Returns the rented pixel array back to the pool. + /// + private void ReturnPixels() + { + PixelPool.ReturnPixels(this.pixelBuffer); + this.pixelBuffer = null; + } } } \ No newline at end of file diff --git a/src/ImageSharp/Image/Image{TColor}.cs b/src/ImageSharp/Image/Image{TColor}.cs index 6bde8c3a5..5c83ef9bb 100644 --- a/src/ImageSharp/Image/Image{TColor}.cs +++ b/src/ImageSharp/Image/Image{TColor}.cs @@ -334,9 +334,9 @@ namespace ImageSharp target.ExifProfile = new ExifProfile(this.ExifProfile); } - foreach (ImageFrame frame in this.Frames) + for (int i = 0; i < this.Frames.Count; i++) { - target.Frames.Add(frame.To()); + target.Frames.Add(this.Frames[i].To()); } return target; @@ -372,6 +372,18 @@ namespace ImageSharp return new ImageFrame(this); } + /// + protected override void Dispose(bool disposing) + { + // ReSharper disable once ForCanBeConvertedToForeach + for (int i = 0; i < this.Frames.Count; i++) + { + this.Frames[i].Dispose(); + } + + base.Dispose(disposing); + } + /// /// Loads the image from the given stream. /// diff --git a/src/ImageSharp/Image/PixelAccessor{TColor}.cs b/src/ImageSharp/Image/PixelAccessor{TColor}.cs index 162891442..60bf8de78 100644 --- a/src/ImageSharp/Image/PixelAccessor{TColor}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TColor}.cs @@ -76,9 +76,9 @@ namespace ImageSharp Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(height, 0, nameof(height)); - if (pixels.Length != width * height) + if (!(pixels.Length >= width * height)) { - throw new ArgumentException("Pixel array must have the length of Width * Height."); + throw new ArgumentException($"Pixel array must have the length of at least {width * height}."); } this.Width = width; diff --git a/src/ImageSharp/Image/PixelPool{TColor}.cs b/src/ImageSharp/Image/PixelPool{TColor}.cs new file mode 100644 index 000000000..8673499a8 --- /dev/null +++ b/src/ImageSharp/Image/PixelPool{TColor}.cs @@ -0,0 +1,50 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Buffers; + + /// + /// Provides a resource pool that enables reusing instances of type . + /// + /// The pixel format. + public static class PixelPool + where TColor : struct, IPackedPixel, IEquatable + { + /// + /// The used to pool data. TODO: Choose sensible default size and count + /// + private static readonly ArrayPool ArrayPool = ArrayPool.Create(int.MaxValue, 50); + + /// + /// Rents the pixel array from the pool. + /// + /// The minimum length of the array to return. + /// The + public static TColor[] RentPixels(int minimumLength) + { + return ArrayPool.Rent(minimumLength); + } + + /// + /// Returns the rented pixel array back to the pool. + /// + /// The array to return to the buffer pool. + public static void ReturnPixels(TColor[] array) + { + try + { + ArrayPool.Return(array, true); + } + catch + { + // Do nothing. + // Hacky but it allows us to attempt to return non-pooled arrays and arrays that have already been returned + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs index 2dd3acb0a..c066ac18c 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs @@ -47,18 +47,22 @@ namespace ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Draw Beziers")] public void DrawLinesCore() { - CoreImage image = new CoreImage(800, 800); - - image.DrawBeziers(CoreColor.HotPink, 10, new[] { + using (CoreImage image = new CoreImage(800, 800)) + { + image.DrawBeziers( + CoreColor.HotPink, + 10, + new[] { new Vector2(10, 500), new Vector2(30, 10), new Vector2(240, 30), new Vector2(300, 500) - }); + }); - using (MemoryStream ms = new MemoryStream()) - { - image.SaveAsBmp(ms); + using (MemoryStream ms = new MemoryStream()) + { + image.SaveAsBmp(ms); + } } } } diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs index b418feb5b..78f71b660 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs @@ -46,17 +46,21 @@ namespace ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Draw Lines")] public void DrawLinesCore() { - CoreImage image = new CoreImage(800, 800); - - image.DrawLines(CoreColor.HotPink, 10, new[] { - new Vector2(10, 10), - new Vector2(550, 50), - new Vector2(200, 400) - }); - - using (MemoryStream ms = new MemoryStream()) + using (CoreImage image = new CoreImage(800, 800)) { - image.SaveAsBmp(ms); + image.DrawLines( + CoreColor.HotPink, + 10, + new[] { + new Vector2(10, 10), + new Vector2(550, 50), + new Vector2(200, 400) + }); + + using (MemoryStream ms = new MemoryStream()) + { + image.SaveAsBmp(ms); + } } } } diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs index 46526330f..88618b912 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs @@ -45,17 +45,21 @@ namespace ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Draw Polygon")] public void DrawPolygonCore() { - CoreImage image = new CoreImage(800, 800); - - image.DrawPolygon(CoreColor.HotPink, 10, new[] { - new Vector2(10, 10), - new Vector2(550, 50), - new Vector2(200, 400) - }); - - using (MemoryStream ms = new MemoryStream()) + using (CoreImage image = new CoreImage(800, 800)) { - image.SaveAsBmp(ms); + image.DrawPolygon( + CoreColor.HotPink, + 10, + new[] { + new Vector2(10, 10), + new Vector2(550, 50), + new Vector2(200, 400) + }); + + using (MemoryStream ms = new MemoryStream()) + { + image.SaveAsBmp(ms); + } } } } diff --git a/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs b/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs index b774d73cb..1eafbe077 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs @@ -44,17 +44,20 @@ namespace ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Fill Polygon")] public void DrawSolidPolygonCore() { - CoreImage image = new CoreImage(800, 800); - image.FillPolygon(CoreColor.HotPink, - new[] { - new Vector2(10, 10), - new Vector2(550, 50), - new Vector2(200, 400) - }); - - using (MemoryStream ms = new MemoryStream()) + using (CoreImage image = new CoreImage(800, 800)) { - image.SaveAsBmp(ms); + image.FillPolygon( + CoreColor.HotPink, + new[] { + new Vector2(10, 10), + new Vector2(550, 50), + new Vector2(200, 400) + }); + + using (MemoryStream ms = new MemoryStream()) + { + image.SaveAsBmp(ms); + } } } } diff --git a/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs b/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs index d9782249a..8e5c18d27 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs @@ -28,7 +28,6 @@ namespace ImageSharp.Benchmarks { graphics.InterpolationMode = InterpolationMode.Default; graphics.SmoothingMode = SmoothingMode.AntiAlias; - var pen = new Pen(Color.HotPink, 10); graphics.FillRectangle(Brushes.HotPink, new Rectangle(10, 10, 190, 140)); } return destination.Size; @@ -38,25 +37,29 @@ namespace ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Fill Rectangle")] public CoreSize FillRactangleCore() { - CoreImage image = new CoreImage(800, 800); - - image.Fill(CoreColor.HotPink, new ImageSharp.Drawing.Shapes.RectangularPolygon(new CoreRectangle(10, 10, 190, 140))); + using (CoreImage image = new CoreImage(800, 800)) + { + image.Fill(CoreColor.HotPink, new ImageSharp.Drawing.Shapes.RectangularPolygon(new CoreRectangle(10, 10, 190, 140))); - return new CoreSize(image.Width, image.Height); + return new CoreSize(image.Width, image.Height); + } } [Benchmark(Description = "ImageSharp Fill Rectangle - As Polygon")] public CoreSize FillPolygonCore() { - CoreImage image = new CoreImage(800, 800); - - image.FillPolygon(CoreColor.HotPink, new[] { - new Vector2(10, 10), - new Vector2(200, 10), - new Vector2(200, 150), - new Vector2(10, 150) }); + using (CoreImage image = new CoreImage(800, 800)) + { + image.FillPolygon( + CoreColor.HotPink, + new[] { + new Vector2(10, 10), + new Vector2(200, 10), + new Vector2(200, 150), + new Vector2(10, 150) }); - return new CoreSize(image.Width, image.Height); + return new CoreSize(image.Width, image.Height); + } } } } \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs b/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs index e5676cd13..718474f1f 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs @@ -38,12 +38,14 @@ namespace ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Fill with Pattern")] public void DrawPatternPolygon3Core() { - CoreImage image = new CoreImage(800, 800); - image.Fill(CoreBrushes.BackwardDiagonal(CoreColor.HotPink)); - - using (MemoryStream ms = new MemoryStream()) + using (CoreImage image = new CoreImage(800, 800)) { - image.SaveAsBmp(ms); + image.Fill(CoreBrushes.BackwardDiagonal(CoreColor.HotPink)); + + using (MemoryStream ms = new MemoryStream()) + { + image.SaveAsBmp(ms); + } } } } diff --git a/tests/ImageSharp.Benchmarks/Image/CopyPixels.cs b/tests/ImageSharp.Benchmarks/Image/CopyPixels.cs index ae732d053..335d8247d 100644 --- a/tests/ImageSharp.Benchmarks/Image/CopyPixels.cs +++ b/tests/ImageSharp.Benchmarks/Image/CopyPixels.cs @@ -17,24 +17,26 @@ namespace ImageSharp.Benchmarks.Image [Benchmark(Description = "Copy by Pixel")] public CoreColor CopyByPixel() { - CoreImage source = new CoreImage(1024, 768); - CoreImage target = new CoreImage(1024, 768); - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock()) + using (CoreImage source = new CoreImage(1024, 768)) + using (CoreImage target = new CoreImage(1024, 768)) { - Parallel.For( - 0, - source.Height, - Configuration.Default.ParallelOptions, - y => - { - for (int x = 0; x < source.Width; x++) - { - targetPixels[x, y] = sourcePixels[x, y]; - } - }); + using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor targetPixels = target.Lock()) + { + Parallel.For( + 0, + source.Height, + Configuration.Default.ParallelOptions, + y => + { + for (int x = 0; x < source.Width; x++) + { + targetPixels[x, y] = sourcePixels[x, y]; + } + }); - return targetPixels[0, 0]; + return targetPixels[0, 0]; + } } } } diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs b/tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs index b8f433a14..431bbeb07 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs @@ -43,8 +43,10 @@ namespace ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream(this.bmpBytes)) { - CoreImage image = new CoreImage(memoryStream); - return new CoreSize(image.Width, image.Height); + using (CoreImage image = new CoreImage(memoryStream)) + { + return new CoreSize(image.Width, image.Height); + } } } } diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs b/tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs index 5f7050fb0..517915bac 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs @@ -29,37 +29,40 @@ namespace ImageSharp.Benchmarks.Image this.filter4 = new MemoryStream(File.ReadAllBytes("../ImageSharp.Tests/TestImages/Formats/Png/filter4.png")); } - private Image LoadPng(MemoryStream stream) + private Size LoadPng(MemoryStream stream) { - return new Image(stream); + using (Image image = new Image(stream)) + { + return new Size(image.Width, image.Height); + } } [Benchmark(Baseline = true, Description = "None-filtered PNG file")] - public Image PngFilter0() + public Size PngFilter0() { return LoadPng(filter0); } [Benchmark(Description = "Sub-filtered PNG file")] - public Image PngFilter1() + public Size PngFilter1() { return LoadPng(filter1); } [Benchmark(Description = "Up-filtered PNG file")] - public Image PngFilter2() + public Size PngFilter2() { return LoadPng(filter2); } [Benchmark(Description = "Average-filtered PNG file")] - public Image PngFilter3() + public Size PngFilter3() { return LoadPng(filter3); } [Benchmark(Description = "Paeth-filtered PNG file")] - public Image PngFilter4() + public Size PngFilter4() { return LoadPng(filter4); } diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeGif.cs b/tests/ImageSharp.Benchmarks/Image/DecodeGif.cs index 3da6eb362..cb70213da 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeGif.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeGif.cs @@ -43,8 +43,10 @@ namespace ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream(this.gifBytes)) { - CoreImage image = new CoreImage(memoryStream); - return new CoreSize(image.Width, image.Height); + using (CoreImage image = new CoreImage(memoryStream)) + { + return new CoreSize(image.Width, image.Height); + } } } } diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs b/tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs index b5a44d919..cbbe9c9f2 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs @@ -43,8 +43,10 @@ namespace ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream(this.jpegBytes)) { - CoreImage image = new CoreImage(memoryStream); - return new CoreSize(image.Width, image.Height); + using (CoreImage image = new CoreImage(memoryStream)) + { + return new CoreSize(image.Width, image.Height); + } } } } diff --git a/tests/ImageSharp.Benchmarks/Image/DecodePng.cs b/tests/ImageSharp.Benchmarks/Image/DecodePng.cs index f3bda758d..79c8dbc23 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodePng.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodePng.cs @@ -43,8 +43,10 @@ namespace ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream(this.pngBytes)) { - CoreImage image = new CoreImage(memoryStream); - return new CoreSize(image.Width, image.Height); + using (CoreImage image = new CoreImage(memoryStream)) + { + return new CoreSize(image.Width, image.Height); + } } } } diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs b/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs index 5a77fbca0..b0a3b4499 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs @@ -31,6 +31,14 @@ namespace ImageSharp.Benchmarks.Image } } + [Cleanup] + public void Cleanup() + { + this.bmpStream.Dispose(); + this.bmpCore.Dispose(); + this.bmpDrawing.Dispose(); + } + [Benchmark(Baseline = true, Description = "System.Drawing Bmp")] public void BmpSystemDrawing() { diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeBmpMultiple.cs b/tests/ImageSharp.Benchmarks/Image/EncodeBmpMultiple.cs index 1775f144d..852e17572 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeBmpMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeBmpMultiple.cs @@ -18,7 +18,7 @@ namespace ImageSharp.Benchmarks.Image protected override IEnumerable InputImageSubfoldersOrFiles => new[] { "Bmp/", "Jpg/baseline" }; [Benchmark(Description = "EncodeBmpMultiple - ImageSharp")] - public void EncodeGifImageSharp() + public void EncodeBmpImageSharp() { this.ForEachImageSharpImage( (img, ms) => @@ -29,7 +29,7 @@ namespace ImageSharp.Benchmarks.Image } [Benchmark(Baseline = true, Description = "EncodeBmpMultiple - System.Drawing")] - public void EncodeGifSystemDrawing() + public void EncodeBmpSystemDrawing() { this.ForEachSystemDrawingImage( (img, ms) => diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs b/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs index 42feb085f..0810f3fe1 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs @@ -31,6 +31,14 @@ namespace ImageSharp.Benchmarks.Image } } + [Cleanup] + public void Cleanup() + { + this.bmpStream.Dispose(); + this.bmpCore.Dispose(); + this.bmpDrawing.Dispose(); + } + [Benchmark(Baseline = true, Description = "System.Drawing Gif")] public void GifSystemDrawing() { diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs b/tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs index f1e3b2114..f835f9666 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs @@ -31,6 +31,14 @@ namespace ImageSharp.Benchmarks.Image } } + [Cleanup] + public void Cleanup() + { + this.bmpStream.Dispose(); + this.bmpCore.Dispose(); + this.bmpDrawing.Dispose(); + } + [Benchmark(Baseline = true, Description = "System.Drawing Jpeg")] public void JpegSystemDrawing() { diff --git a/tests/ImageSharp.Benchmarks/Image/EncodePng.cs b/tests/ImageSharp.Benchmarks/Image/EncodePng.cs index 44ffba679..dd1882c80 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodePng.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodePng.cs @@ -31,6 +31,14 @@ namespace ImageSharp.Benchmarks.Image } } + [Cleanup] + public void Cleanup() + { + this.bmpStream.Dispose(); + this.bmpCore.Dispose(); + this.bmpDrawing.Dispose(); + } + [Benchmark(Baseline = true, Description = "System.Drawing Png")] public void PngSystemDrawing() { diff --git a/tests/ImageSharp.Benchmarks/Image/GetSetPixel.cs b/tests/ImageSharp.Benchmarks/Image/GetSetPixel.cs index ffd476007..78295e27d 100644 --- a/tests/ImageSharp.Benchmarks/Image/GetSetPixel.cs +++ b/tests/ImageSharp.Benchmarks/Image/GetSetPixel.cs @@ -28,11 +28,13 @@ namespace ImageSharp.Benchmarks.Image [Benchmark(Description = "ImageSharp GetSet pixel")] public CoreColor ResizeCore() { - CoreImage image = new CoreImage(400, 400); - using (PixelAccessor imagePixels = image.Lock()) + using (CoreImage image = new CoreImage(400, 400)) { - imagePixels[200, 200] = CoreColor.White; - return imagePixels[200, 200]; + using (PixelAccessor imagePixels = image.Lock()) + { + imagePixels[200, 200] = CoreColor.White; + return imagePixels[200, 200]; + } } } } diff --git a/tests/ImageSharp.Benchmarks/Samplers/Crop.cs b/tests/ImageSharp.Benchmarks/Samplers/Crop.cs index 3a98569e7..a3cdef92e 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Crop.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Crop.cs @@ -38,9 +38,11 @@ namespace ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Crop")] public CoreSize CropResizeCore() { - CoreImage image = new CoreImage(800, 800); - image.Crop(100, 100); - return new CoreSize(image.Width, image.Height); + using (CoreImage image = new CoreImage(800, 800)) + { + image.Crop(100, 100); + return new CoreSize(image.Width, image.Height); + } } } } diff --git a/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs b/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs index be76c13fd..28bec5124 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs @@ -28,6 +28,12 @@ namespace ImageSharp.Benchmarks } } + [Cleanup] + public void Cleanup() + { + this.image.Dispose(); + } + [Benchmark(Description = "ImageSharp DetectEdges")] public void ImageProcessorCoreDetectEdges() { diff --git a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs index 10fe1fc75..04570ce8f 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs @@ -37,17 +37,21 @@ namespace ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Resize")] public CoreSize ResizeCore() { - CoreImage image = new CoreImage(2000, 2000); - image.Resize(400, 400); - return new CoreSize(image.Width, image.Height); + using (CoreImage image = new CoreImage(2000, 2000)) + { + image.Resize(400, 400); + return new CoreSize(image.Width, image.Height); + } } [Benchmark(Description = "ImageSharp Compand Resize")] public CoreSize ResizeCoreCompand() { - CoreImage image = new CoreImage(2000, 2000); - image.Resize(400, 400, true); - return new CoreSize(image.Width, image.Height); + using (CoreImage image = new CoreImage(2000, 2000)) + { + image.Resize(400, 400, true); + return new CoreSize(image.Width, image.Height); + } } } } diff --git a/tests/ImageSharp.Tests/Drawing/BeziersTests.cs b/tests/ImageSharp.Tests/Drawing/BeziersTests.cs index c219f91aa..a1d4d3fd5 100644 --- a/tests/ImageSharp.Tests/Drawing/BeziersTests.cs +++ b/tests/ImageSharp.Tests/Drawing/BeziersTests.cs @@ -15,89 +15,88 @@ namespace ImageSharp.Tests.Drawing public class Beziers : FileTestBase { - - [Fact] public void ImageShouldBeOverlayedByBezierLine() { - string path = CreateOutputDirectory("Drawing","BezierLine"); -var image = new Image(500, 500); - -using (FileStream output = File.OpenWrite($"{path}/Simple.png")) -{ - image - .BackgroundColor(Color.Blue) - .DrawBeziers(Color.HotPink, 5, new[] { - new Vector2(10, 400), - new Vector2(30, 10), - new Vector2(240, 30), - new Vector2(300, 400) - }) - .Save(output); -} - - using (var sourcePixels = image.Lock()) + string path = this.CreateOutputDirectory("Drawing", "BezierLine"); + using (Image image = new Image(500, 500)) { - //top of curve - Assert.Equal(Color.HotPink, sourcePixels[138,115]); - - //start points - Assert.Equal(Color.HotPink, sourcePixels[10, 400]); - Assert.Equal(Color.HotPink, sourcePixels[300, 400]); - - //curve points should not be never be set - Assert.Equal(Color.Blue, sourcePixels[30, 10]); - Assert.Equal(Color.Blue, sourcePixels[240, 30]); - - // inside shape should be empty - Assert.Equal(Color.Blue, sourcePixels[200, 250]); + using (FileStream output = File.OpenWrite($"{path}/Simple.png")) + { + image.BackgroundColor(Color.Blue) + .DrawBeziers(Color.HotPink, 5, + new[] { + new Vector2(10, 400), + new Vector2(30, 10), + new Vector2(240, 30), + new Vector2(300, 400) + }) + .Save(output); + } + + using (PixelAccessor sourcePixels = image.Lock()) + { + //top of curve + Assert.Equal(Color.HotPink, sourcePixels[138, 115]); + + //start points + Assert.Equal(Color.HotPink, sourcePixels[10, 400]); + Assert.Equal(Color.HotPink, sourcePixels[300, 400]); + + //curve points should not be never be set + Assert.Equal(Color.Blue, sourcePixels[30, 10]); + Assert.Equal(Color.Blue, sourcePixels[240, 30]); + + // inside shape should be empty + Assert.Equal(Color.Blue, sourcePixels[200, 250]); + } } - } [Fact] public void ImageShouldBeOverlayedBezierLineWithOpacity() { - string path = CreateOutputDirectory("Drawing", "BezierLine"); + string path = this.CreateOutputDirectory("Drawing", "BezierLine"); - var color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); + Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); - var image = new Image(500, 500); - - using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) + using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Color.Blue) - .DrawBeziers(color, 10, new[] { - new Vector2(10, 400), - new Vector2(30, 10), - new Vector2(240, 30), - new Vector2(300, 400) - }) - .Save(output); + using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) + { + image.BackgroundColor(Color.Blue) + .DrawBeziers(color, + 10, + new[] { + new Vector2(10, 400), + new Vector2(30, 10), + new Vector2(240, 30), + new Vector2(300, 400) + }) + .Save(output); + } + + //shift background color towards forground color by the opacity amount + Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); + + using (PixelAccessor sourcePixels = image.Lock()) + { + //top of curve + Assert.Equal(mergedColor, sourcePixels[138, 115]); + + //start points + Assert.Equal(mergedColor, sourcePixels[10, 400]); + Assert.Equal(mergedColor, sourcePixels[300, 400]); + + //curve points should not be never be set + Assert.Equal(Color.Blue, sourcePixels[30, 10]); + Assert.Equal(Color.Blue, sourcePixels[240, 30]); + + // inside shape should be empty + Assert.Equal(Color.Blue, sourcePixels[200, 250]); + } } - - //shift background color towards forground color by the opacity amount - var mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f/255f)); - - using (var sourcePixels = image.Lock()) - { - //top of curve - Assert.Equal(mergedColor, sourcePixels[138, 115]); - - //start points - Assert.Equal(mergedColor, sourcePixels[10, 400]); - Assert.Equal(mergedColor, sourcePixels[300, 400]); - - //curve points should not be never be set - Assert.Equal(Color.Blue, sourcePixels[30, 10]); - Assert.Equal(Color.Blue, sourcePixels[240, 30]); - - // inside shape should be empty - Assert.Equal(Color.Blue, sourcePixels[200, 250]); - } - } - + } } } diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs index 5e5a6897f..3a59de624 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs @@ -6,7 +6,6 @@ namespace ImageSharp.Tests { using System.IO; - using System.Linq; using Xunit; @@ -15,18 +14,20 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldApplyDrawImageFilter() { - string path = CreateOutputDirectory("Drawing", "DrawImage"); + string path = this.CreateOutputDirectory("Drawing", "DrawImage"); - Image blend = TestFile.Create(TestImages.Bmp.Car).CreateImage(); - - foreach (TestFile file in Files) + using (Image blend = TestFile.Create(TestImages.Bmp.Car).CreateImage()) { - Image image = file.CreateImage(); - - using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) + foreach (TestFile file in Files) { - image.DrawImage(blend, 75, new Size(image.Width / 2, image.Height / 2), new Point(image.Width / 4, image.Height / 4)) - .Save(output); + using (Image image = file.CreateImage()) + { + using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) + { + image.DrawImage(blend, 75, new Size(image.Width / 2, image.Height / 2), new Point(image.Width / 4, image.Height / 4)) + .Save(output); + } + } } } } diff --git a/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs b/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs index 61080b815..b61948316 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs @@ -20,81 +20,82 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByPath() { - string path = CreateOutputDirectory("Drawing", "Path"); - var image = new Image(500, 500); - - var linerSegemnt = new LinearLineSegment( - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300)); - var bazierSegment = new BezierLineSegment(new Vector2(50, 300), - new Vector2(500, 500), - new Vector2(60, 10), - new Vector2(10, 400)); - - var p = new CorePath(linerSegemnt, bazierSegment); - - using (FileStream output = File.OpenWrite($"{path}/Simple.png")) - { - image - .BackgroundColor(Color.Blue) - .DrawPath(Color.HotPink, 5, p) - .Save(output); - } - - using (var sourcePixels = image.Lock()) + string path = this.CreateOutputDirectory("Drawing", "Path"); + using (Image image = new Image(500, 500)) { - Assert.Equal(Color.HotPink, sourcePixels[9, 9]); - - Assert.Equal(Color.HotPink, sourcePixels[199, 149]); - - Assert.Equal(Color.Blue, sourcePixels[50, 50]); + LinearLineSegment linerSegemnt = new LinearLineSegment( + new Vector2(10, 10), + new Vector2(200, 150), + new Vector2(50, 300)); + BezierLineSegment bazierSegment = new BezierLineSegment(new Vector2(50, 300), + new Vector2(500, 500), + new Vector2(60, 10), + new Vector2(10, 400)); + + CorePath p = new CorePath(linerSegemnt, bazierSegment); + + using (FileStream output = File.OpenWrite($"{path}/Simple.png")) + { + image + .BackgroundColor(Color.Blue) + .DrawPath(Color.HotPink, 5, p) + .Save(output); + } + + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(Color.HotPink, sourcePixels[9, 9]); + + Assert.Equal(Color.HotPink, sourcePixels[199, 149]); + + Assert.Equal(Color.Blue, sourcePixels[50, 50]); + } } - } [Fact] public void ImageShouldBeOverlayedPathWithOpacity() { - string path = CreateOutputDirectory("Drawing", "Path"); + string path = this.CreateOutputDirectory("Drawing", "Path"); - var color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); + Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); - var linerSegemnt = new LinearLineSegment( + LinearLineSegment linerSegemnt = new LinearLineSegment( new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) ); - var bazierSegment = new BezierLineSegment(new Vector2(50, 300), + + BezierLineSegment bazierSegment = new BezierLineSegment(new Vector2(50, 300), new Vector2(500, 500), new Vector2(60, 10), new Vector2(10, 400)); - var p = new CorePath(linerSegemnt, bazierSegment); + CorePath p = new CorePath(linerSegemnt, bazierSegment); - var image = new Image(500, 500); - - - using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) + using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Color.Blue) - .DrawPath(color, 10, p) - .Save(output); - } + using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) + { + image + .BackgroundColor(Color.Blue) + .DrawPath(color, 10, p) + .Save(output); + } - //shift background color towards forground color by the opacity amount - var mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); + //shift background color towards forground color by the opacity amount + Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); - using (var sourcePixels = image.Lock()) - { - Assert.Equal(mergedColor, sourcePixels[9, 9]); + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(mergedColor, sourcePixels[9, 9]); - Assert.Equal(mergedColor, sourcePixels[199, 149]); + Assert.Equal(mergedColor, sourcePixels[199, 149]); - Assert.Equal(Color.Blue, sourcePixels[50, 50]); + Assert.Equal(Color.Blue, sourcePixels[50, 50]); + } } } diff --git a/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs b/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs index 5bb93a55c..d3c1877ab 100644 --- a/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs @@ -15,55 +15,56 @@ namespace ImageSharp.Tests.Drawing public class FillPatternBrushTests : FileTestBase { - private Image Test(string name, Color background, IBrush brush, Color[,] expectedPattern) + private void Test(string name, Color background, IBrush brush, Color[,] expectedPattern) { - string path = CreateOutputDirectory("Fill", "PatternBrush"); - Image image = new Image(20, 20); - image - .Fill(background) - .Fill(brush); - - using (FileStream output = File.OpenWrite($"{path}/{name}.png")) - { - image.Save(output); - } - using (var sourcePixels = image.Lock()) + string path = this.CreateOutputDirectory("Fill", "PatternBrush"); + using (Image image = new Image(20, 20)) { - // lets pick random spots to start checking - var r = new Random(); - var xStride = expectedPattern.GetLength(1); - var yStride = expectedPattern.GetLength(0); - var offsetX = r.Next(image.Width / xStride) * xStride; - var offsetY = r.Next(image.Height / yStride) * yStride; - for (var x = 0; x < xStride; x++) + image + .Fill(background) + .Fill(brush); + + using (FileStream output = File.OpenWrite($"{path}/{name}.png")) { - for (var y = 0; y < yStride; y++) + image.Save(output); + } + + using (PixelAccessor sourcePixels = image.Lock()) + { + // lets pick random spots to start checking + Random r = new Random(); + int xStride = expectedPattern.GetLength(1); + int yStride = expectedPattern.GetLength(0); + int offsetX = r.Next(image.Width / xStride) * xStride; + int offsetY = r.Next(image.Height / yStride) * yStride; + for (int x = 0; x < xStride; x++) { - var actualX = x + offsetX; - var actualY = y + offsetY; - var expected = expectedPattern[y, x]; // inverted pattern - var actual = sourcePixels[actualX, actualY]; - if (expected != actual) + for (int y = 0; y < yStride; y++) { - Assert.True(false, $"Expected {expected} but found {actual} at ({actualX},{actualY})"); + int actualX = x + offsetX; + int actualY = y + offsetY; + Color expected = expectedPattern[y, x]; // inverted pattern + Color actual = sourcePixels[actualX, actualY]; + if (expected != actual) + { + Assert.True(false, $"Expected {expected} but found {actual} at ({actualX},{actualY})"); + } } } } + using (FileStream output = File.OpenWrite($"{path}/{name}x4.png")) + { + image.Resize(80, 80).Save(output); + } } - using (FileStream output = File.OpenWrite($"{path}/{name}x4.png")) - { - image.Resize(80, 80).Save(output); - } - - - - return image; } [Fact] public void ImageShouldBeFloodFilledWithPercent10() { - Test("Percent10", Color.Blue, Brushes.Percent10(Color.HotPink, Color.LimeGreen), new Color[,] { + this.Test("Percent10", Color.Blue, Brushes.Percent10(Color.HotPink, Color.LimeGreen), + new[,] + { { Color.HotPink , Color.LimeGreen, Color.LimeGreen, Color.LimeGreen}, { Color.LimeGreen, Color.LimeGreen, Color.LimeGreen, Color.LimeGreen}, { Color.LimeGreen, Color.LimeGreen, Color.HotPink , Color.LimeGreen}, diff --git a/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs index b7551d63a..bafc84b69 100644 --- a/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs @@ -18,71 +18,73 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeFloodFilledWithColorOnDefaultBackground() { - string path = CreateOutputDirectory("Fill", "SolidBrush"); - var image = new Image(500, 500); - - using (FileStream output = File.OpenWrite($"{path}/DefaultBack.png")) + string path = this.CreateOutputDirectory("Fill", "SolidBrush"); + using (Image image = new Image(500, 500)) { - image - .Fill(Color.HotPink) - .Save(output); - } - - using (var sourcePixels = image.Lock()) - { - Assert.Equal(Color.HotPink, sourcePixels[9, 9]); - - Assert.Equal(Color.HotPink, sourcePixels[199, 149]); + using (FileStream output = File.OpenWrite($"{path}/DefaultBack.png")) + { + image + .Fill(Color.HotPink) + .Save(output); + } + + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(Color.HotPink, sourcePixels[9, 9]); + + Assert.Equal(Color.HotPink, sourcePixels[199, 149]); + } } } [Fact] public void ImageShouldBeFloodFilledWithColor() { - string path = CreateOutputDirectory("Fill", "SolidBrush"); - var image = new Image(500, 500); - - using (FileStream output = File.OpenWrite($"{path}/Simple.png")) + string path = this.CreateOutputDirectory("Fill", "SolidBrush"); + using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Color.Blue) - .Fill(Color.HotPink) - .Save(output); - } - - using (var sourcePixels = image.Lock()) - { - Assert.Equal(Color.HotPink, sourcePixels[9, 9]); - - Assert.Equal(Color.HotPink, sourcePixels[199, 149]); + using (FileStream output = File.OpenWrite($"{path}/Simple.png")) + { + image + .BackgroundColor(Color.Blue) + .Fill(Color.HotPink) + .Save(output); + } + + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(Color.HotPink, sourcePixels[9, 9]); + + Assert.Equal(Color.HotPink, sourcePixels[199, 149]); + } } } [Fact] public void ImageShouldBeFloodFilledWithColorOpacity() { - string path = CreateOutputDirectory("Fill", "SolidBrush"); - var image = new Image(500, 500); - - var color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); - - using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) - { - image - .BackgroundColor(Color.Blue) - .Fill(color) - .Save(output); - } - //shift background color towards forground color by the opacity amount - var mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); - - - using (var sourcePixels = image.Lock()) + string path = this.CreateOutputDirectory("Fill", "SolidBrush"); + using (Image image = new Image(500, 500)) { - Assert.Equal(mergedColor, sourcePixels[9, 9]); - Assert.Equal(mergedColor, sourcePixels[199, 149]); + Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); + + using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) + { + image + .BackgroundColor(Color.Blue) + .Fill(color) + .Save(output); + } + //shift background color towards forground color by the opacity amount + Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); + + + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(mergedColor, sourcePixels[9, 9]); + Assert.Equal(mergedColor, sourcePixels[199, 149]); + } } - } } diff --git a/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs index cdcb5ebe4..9ce93ee89 100644 --- a/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs @@ -5,12 +5,9 @@ namespace ImageSharp.Tests.Drawing { - using System; - using System.Diagnostics.CodeAnalysis; using System.IO; using Xunit; - using Drawing; - using ImageSharp.Drawing; + using System.Numerics; using ImageSharp.Drawing.Shapes; using ImageSharp.Drawing.Pens; @@ -20,98 +17,100 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByPolygonOutline() { - string path = CreateOutputDirectory("Drawing", "LineComplexPolygon"); - var simplePath = new LinearPolygon( + string path = this.CreateOutputDirectory("Drawing", "LineComplexPolygon"); + LinearPolygon simplePath = new LinearPolygon( new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300)); - var hole1 = new LinearPolygon( + LinearPolygon hole1 = new LinearPolygon( new Vector2(37, 85), new Vector2(93, 85), new Vector2(65, 137)); - var image = new Image(500, 500); - - using (FileStream output = File.OpenWrite($"{path}/Simple.png")) + using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Color.Blue) - .DrawPolygon(Color.HotPink, 5, new ComplexPolygon(simplePath, hole1)) - .Save(output); - } + using (FileStream output = File.OpenWrite($"{path}/Simple.png")) + { + image + .BackgroundColor(Color.Blue) + .DrawPolygon(Color.HotPink, 5, new ComplexPolygon(simplePath, hole1)) + .Save(output); + } - using (var sourcePixels = image.Lock()) - { - Assert.Equal(Color.HotPink, sourcePixels[10, 10]); + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(Color.HotPink, sourcePixels[10, 10]); - Assert.Equal(Color.HotPink, sourcePixels[200, 150]); + Assert.Equal(Color.HotPink, sourcePixels[200, 150]); - Assert.Equal(Color.HotPink, sourcePixels[50, 300]); + Assert.Equal(Color.HotPink, sourcePixels[50, 300]); - Assert.Equal(Color.HotPink, sourcePixels[37, 85]); + Assert.Equal(Color.HotPink, sourcePixels[37, 85]); - Assert.Equal(Color.HotPink, sourcePixels[93, 85]); + Assert.Equal(Color.HotPink, sourcePixels[93, 85]); - Assert.Equal(Color.HotPink, sourcePixels[65, 137]); + Assert.Equal(Color.HotPink, sourcePixels[65, 137]); - Assert.Equal(Color.Blue, sourcePixels[2, 2]); + Assert.Equal(Color.Blue, sourcePixels[2, 2]); - //inside hole - Assert.Equal(Color.Blue, sourcePixels[57, 99]); + //inside hole + Assert.Equal(Color.Blue, sourcePixels[57, 99]); - //inside shape - Assert.Equal(Color.Blue, sourcePixels[100, 192]); + //inside shape + Assert.Equal(Color.Blue, sourcePixels[100, 192]); + } } } [Fact] public void ImageShouldBeOverlayedByPolygonOutlineNoOverlapping() { - string path = CreateOutputDirectory("Drawing", "LineComplexPolygon"); - var simplePath = new LinearPolygon( + string path = this.CreateOutputDirectory("Drawing", "LineComplexPolygon"); + LinearPolygon simplePath = new LinearPolygon( new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300)); - var hole1 = new LinearPolygon( + LinearPolygon hole1 = new LinearPolygon( new Vector2(207, 25), new Vector2(263, 25), new Vector2(235, 57)); - var image = new Image(500, 500); - - using (FileStream output = File.OpenWrite($"{path}/SimpleVanishHole.png")) + using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Color.Blue) - .DrawPolygon(Color.HotPink, 5, new ComplexPolygon(simplePath, hole1)) - .Save(output); - } + using (FileStream output = File.OpenWrite($"{path}/SimpleVanishHole.png")) + { + image + .BackgroundColor(Color.Blue) + .DrawPolygon(Color.HotPink, 5, new ComplexPolygon(simplePath, hole1)) + .Save(output); + } - using (var sourcePixels = image.Lock()) - { - Assert.Equal(Color.HotPink, sourcePixels[10, 10]); + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(Color.HotPink, sourcePixels[10, 10]); - Assert.Equal(Color.HotPink, sourcePixels[200, 150]); + Assert.Equal(Color.HotPink, sourcePixels[200, 150]); - Assert.Equal(Color.HotPink, sourcePixels[50, 300]); + Assert.Equal(Color.HotPink, sourcePixels[50, 300]); - //Assert.Equal(Color.HotPink, sourcePixels[37, 85]); + //Assert.Equal(Color.HotPink, sourcePixels[37, 85]); - //Assert.Equal(Color.HotPink, sourcePixels[93, 85]); + //Assert.Equal(Color.HotPink, sourcePixels[93, 85]); - //Assert.Equal(Color.HotPink, sourcePixels[65, 137]); + //Assert.Equal(Color.HotPink, sourcePixels[65, 137]); - Assert.Equal(Color.Blue, sourcePixels[2, 2]); + Assert.Equal(Color.Blue, sourcePixels[2, 2]); - //inside hole - Assert.Equal(Color.Blue, sourcePixels[57, 99]); + //inside hole + Assert.Equal(Color.Blue, sourcePixels[57, 99]); - //inside shape - Assert.Equal(Color.Blue, sourcePixels[100, 192]); + //inside shape + Assert.Equal(Color.Blue, sourcePixels[100, 192]); + } } } @@ -119,44 +118,45 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByPolygonOutlineOverlapping() { - string path = CreateOutputDirectory("Drawing", "LineComplexPolygon"); - var simplePath = new LinearPolygon( + string path = this.CreateOutputDirectory("Drawing", "LineComplexPolygon"); + LinearPolygon simplePath = new LinearPolygon( new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300)); - var hole1 = new LinearPolygon( + LinearPolygon hole1 = new LinearPolygon( new Vector2(37, 85), new Vector2(130, 40), new Vector2(65, 137)); - var image = new Image(500, 500); - - using (FileStream output = File.OpenWrite($"{path}/SimpleOverlapping.png")) + using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Color.Blue) - .DrawPolygon(Color.HotPink, 5, new ComplexPolygon(simplePath, hole1)) - .Save(output); - } + using (FileStream output = File.OpenWrite($"{path}/SimpleOverlapping.png")) + { + image + .BackgroundColor(Color.Blue) + .DrawPolygon(Color.HotPink, 5, new ComplexPolygon(simplePath, hole1)) + .Save(output); + } - using (var sourcePixels = image.Lock()) - { - Assert.Equal(Color.HotPink, sourcePixels[10, 10]); + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(Color.HotPink, sourcePixels[10, 10]); - Assert.Equal(Color.HotPink, sourcePixels[200, 150]); + Assert.Equal(Color.HotPink, sourcePixels[200, 150]); - Assert.Equal(Color.HotPink, sourcePixels[50, 300]); + Assert.Equal(Color.HotPink, sourcePixels[50, 300]); - Assert.Equal(Color.Blue, sourcePixels[130, 41]); - - Assert.Equal(Color.Blue, sourcePixels[2, 2]); + Assert.Equal(Color.Blue, sourcePixels[130, 41]); - //inside hole - Assert.Equal(Color.Blue, sourcePixels[57, 99]); + Assert.Equal(Color.Blue, sourcePixels[2, 2]); - //inside shape - Assert.Equal(Color.Blue, sourcePixels[100, 192]); + //inside hole + Assert.Equal(Color.Blue, sourcePixels[57, 99]); + + //inside shape + Assert.Equal(Color.Blue, sourcePixels[100, 192]); + } } } @@ -164,25 +164,26 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByPolygonOutlineDashed() { - string path = CreateOutputDirectory("Drawing", "LineComplexPolygon"); - var simplePath = new LinearPolygon( + string path = this.CreateOutputDirectory("Drawing", "LineComplexPolygon"); + LinearPolygon simplePath = new LinearPolygon( new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300)); - var hole1 = new LinearPolygon( + LinearPolygon hole1 = new LinearPolygon( new Vector2(37, 85), new Vector2(93, 85), new Vector2(65, 137)); - var image = new Image(500, 500); - - using (FileStream output = File.OpenWrite($"{path}/Dashed.png")) + using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Color.Blue) - .DrawPolygon(Pens.Dash(Color.HotPink, 5), new ComplexPolygon(simplePath, hole1)) - .Save(output); + using (FileStream output = File.OpenWrite($"{path}/Dashed.png")) + { + image + .BackgroundColor(Color.Blue) + .DrawPolygon(Pens.Dash(Color.HotPink, 5), new ComplexPolygon(simplePath, hole1)) + .Save(output); + } } } @@ -190,54 +191,55 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedPolygonOutlineWithOpacity() { - string path = CreateOutputDirectory("Drawing", "LineComplexPolygon"); - var simplePath = new LinearPolygon( + string path = this.CreateOutputDirectory("Drawing", "LineComplexPolygon"); + LinearPolygon simplePath = new LinearPolygon( new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300)); - var hole1 = new LinearPolygon( + LinearPolygon hole1 = new LinearPolygon( new Vector2(37, 85), new Vector2(93, 85), new Vector2(65, 137)); - var color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); + Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); - var image = new Image(500, 500); - - using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) + using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Color.Blue) - .DrawPolygon(color, 5, new ComplexPolygon(simplePath, hole1)) - .Save(output); - } + using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) + { + image + .BackgroundColor(Color.Blue) + .DrawPolygon(color, 5, new ComplexPolygon(simplePath, hole1)) + .Save(output); + } - //shift background color towards forground color by the opacity amount - var mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); + //shift background color towards forground color by the opacity amount + Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); - using (var sourcePixels = image.Lock()) - { - Assert.Equal(mergedColor, sourcePixels[10, 10]); + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(mergedColor, sourcePixels[10, 10]); - Assert.Equal(mergedColor, sourcePixels[200, 150]); + Assert.Equal(mergedColor, sourcePixels[200, 150]); - Assert.Equal(mergedColor, sourcePixels[50, 300]); + Assert.Equal(mergedColor, sourcePixels[50, 300]); - Assert.Equal(mergedColor, sourcePixels[37, 85]); + Assert.Equal(mergedColor, sourcePixels[37, 85]); - Assert.Equal(mergedColor, sourcePixels[93, 85]); + Assert.Equal(mergedColor, sourcePixels[93, 85]); - Assert.Equal(mergedColor, sourcePixels[65, 137]); + Assert.Equal(mergedColor, sourcePixels[65, 137]); - Assert.Equal(Color.Blue, sourcePixels[2, 2]); + Assert.Equal(Color.Blue, sourcePixels[2, 2]); - //inside hole - Assert.Equal(Color.Blue, sourcePixels[57, 99]); + //inside hole + Assert.Equal(Color.Blue, sourcePixels[57, 99]); - //inside shape - Assert.Equal(Color.Blue, sourcePixels[100, 192]); + //inside shape + Assert.Equal(Color.Blue, sourcePixels[100, 192]); + } } } } diff --git a/tests/ImageSharp.Tests/Drawing/LineTests.cs b/tests/ImageSharp.Tests/Drawing/LineTests.cs index 46b315164..81efd933b 100644 --- a/tests/ImageSharp.Tests/Drawing/LineTests.cs +++ b/tests/ImageSharp.Tests/Drawing/LineTests.cs @@ -5,11 +5,9 @@ namespace ImageSharp.Tests.Drawing { - using Drawing; using ImageSharp.Drawing; using ImageSharp.Drawing.Pens; - using System; - using System.Diagnostics.CodeAnalysis; + using System.IO; using System.Numerics; using Xunit; @@ -19,123 +17,132 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByPath() { - string path = CreateOutputDirectory("Drawing", "Lines"); - var image = new Image(500, 500); - - using (FileStream output = File.OpenWrite($"{path}/Simple.png")) + string path = this.CreateOutputDirectory("Drawing", "Lines"); + using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Color.Blue) - .DrawLines(Color.HotPink, 5, new[] { + using (FileStream output = File.OpenWrite($"{path}/Simple.png")) + { + image + .BackgroundColor(Color.Blue) + .DrawLines(Color.HotPink, 5, + new[] { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) - }) - .Save(output); - } + }) + .Save(output); + } - using (var sourcePixels = image.Lock()) - { - Assert.Equal(Color.HotPink, sourcePixels[9, 9]); + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(Color.HotPink, sourcePixels[9, 9]); - Assert.Equal(Color.HotPink, sourcePixels[199, 149]); + Assert.Equal(Color.HotPink, sourcePixels[199, 149]); - Assert.Equal(Color.Blue, sourcePixels[50, 50]); + Assert.Equal(Color.Blue, sourcePixels[50, 50]); + } } } [Fact] public void ImageShouldBeOverlayedByPath_NoAntialias() { - string path = CreateOutputDirectory("Drawing", "Lines"); - var image = new Image(500, 500); - - using (FileStream output = File.OpenWrite($"{path}/Simple_noantialias.png")) + string path = this.CreateOutputDirectory("Drawing", "Lines"); + using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Color.Blue) - .DrawLines(Color.HotPink, 5, new[] { + using (FileStream output = File.OpenWrite($"{path}/Simple_noantialias.png")) + { + image + .BackgroundColor(Color.Blue) + .DrawLines(Color.HotPink, 5, + new[] { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) - }, new GraphicsOptions(false)) - .Save(output); - } + }, + new GraphicsOptions(false)) + .Save(output); + } - using (var sourcePixels = image.Lock()) - { - Assert.Equal(Color.HotPink, sourcePixels[9, 9]); + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(Color.HotPink, sourcePixels[9, 9]); - Assert.Equal(Color.HotPink, sourcePixels[199, 149]); + Assert.Equal(Color.HotPink, sourcePixels[199, 149]); - Assert.Equal(Color.Blue, sourcePixels[50, 50]); + Assert.Equal(Color.Blue, sourcePixels[50, 50]); + } } } [Fact] public void ImageShouldBeOverlayedByPathDashed() { - string path = CreateOutputDirectory("Drawing", "Lines"); - var image = new Image(500, 500); - - using (FileStream output = File.OpenWrite($"{path}/Dashed.png")) + string path = this.CreateOutputDirectory("Drawing", "Lines"); + using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Color.Blue) - .DrawLines(Pens.Dash(Color.HotPink, 5), new[] { + using (FileStream output = File.OpenWrite($"{path}/Dashed.png")) + { + image + .BackgroundColor(Color.Blue) + .DrawLines(Pens.Dash(Color.HotPink, 5), + new[] { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) - }) - .Save(output); + }) + .Save(output); + } } - } [Fact] public void ImageShouldBeOverlayedByPathDotted() { - string path = CreateOutputDirectory("Drawing", "Lines"); - var image = new Image(500, 500); - - using (FileStream output = File.OpenWrite($"{path}/Dot.png")) + string path = this.CreateOutputDirectory("Drawing", "Lines"); + using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Color.Blue) - .DrawLines(Pens.Dot(Color.HotPink, 5), new[] { + using (FileStream output = File.OpenWrite($"{path}/Dot.png")) + { + image + .BackgroundColor(Color.Blue) + .DrawLines(Pens.Dot(Color.HotPink, 5), + new[] { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) - }) - .Save(output); + }) + .Save(output); + } } } [Fact] public void ImageShouldBeOverlayedByPathDashDot() { - string path = CreateOutputDirectory("Drawing", "Lines"); - var image = new Image(500, 500); - - using (FileStream output = File.OpenWrite($"{path}/DashDot.png")) + string path = this.CreateOutputDirectory("Drawing", "Lines"); + using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Color.Blue) - .DrawLines(Pens.DashDot(Color.HotPink, 5), new[] { + using (FileStream output = File.OpenWrite($"{path}/DashDot.png")) + { + image + .BackgroundColor(Color.Blue) + .DrawLines(Pens.DashDot(Color.HotPink, 5), + new[] { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) - }) - .Save(output); + }) + .Save(output); + } } - } [Fact] public void ImageShouldBeOverlayedByPathDashDotDot() { - string path = CreateOutputDirectory("Drawing", "Lines"); - var image = new Image(500, 500); + string path = this.CreateOutputDirectory("Drawing", "Lines"); + Image image = new Image(500, 500); using (FileStream output = File.OpenWrite($"{path}/DashDotDot.png")) { @@ -153,12 +160,12 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedPathWithOpacity() { - string path = CreateOutputDirectory("Drawing", "Lines"); + string path = this.CreateOutputDirectory("Drawing", "Lines"); - var color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); + Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); + + Image image = new Image(500, 500); - var image = new Image(500, 500); - using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) { @@ -173,9 +180,9 @@ namespace ImageSharp.Tests.Drawing } //shift background color towards forground color by the opacity amount - var mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f/255f)); + Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f/255f)); - using (var sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { Assert.Equal(mergedColor, sourcePixels[9, 9]); @@ -188,9 +195,9 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByPathOutline() { - string path = CreateOutputDirectory("Drawing", "Lines"); + string path = this.CreateOutputDirectory("Drawing", "Lines"); - var image = new Image(500, 500); + Image image = new Image(500, 500); using (FileStream output = File.OpenWrite($"{path}/Rectangle.png")) { @@ -205,7 +212,7 @@ namespace ImageSharp.Tests.Drawing .Save(output); } - using (var sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { Assert.Equal(Color.HotPink, sourcePixels[8, 8]); @@ -216,6 +223,6 @@ namespace ImageSharp.Tests.Drawing Assert.Equal(Color.Blue, sourcePixels[50, 50]); } } - + } } diff --git a/tests/ImageSharp.Tests/Drawing/PolygonTests.cs b/tests/ImageSharp.Tests/Drawing/PolygonTests.cs index 68027d0bf..f98744075 100644 --- a/tests/ImageSharp.Tests/Drawing/PolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/PolygonTests.cs @@ -18,97 +18,101 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByPolygonOutline() { - string path = CreateOutputDirectory("Drawing", "Polygons"); + string path = this.CreateOutputDirectory("Drawing", "Polygons"); - var image = new Image(500, 500); - - using (FileStream output = File.OpenWrite($"{path}/Simple.png")) + using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Color.Blue) - .DrawPolygon(Color.HotPink, 5, new[] { - new Vector2(10, 10), - new Vector2(200, 150), - new Vector2(50, 300) - }) - .Save(output); - } + using (FileStream output = File.OpenWrite($"{path}/Simple.png")) + { + image + .BackgroundColor(Color.Blue) + .DrawPolygon(Color.HotPink, 5, + new[] { + new Vector2(10, 10), + new Vector2(200, 150), + new Vector2(50, 300) + }) + .Save(output); + } - using (var sourcePixels = image.Lock()) - { - Assert.Equal(Color.HotPink, sourcePixels[9, 9]); + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(Color.HotPink, sourcePixels[9, 9]); - Assert.Equal(Color.HotPink, sourcePixels[199, 149]); + Assert.Equal(Color.HotPink, sourcePixels[199, 149]); - Assert.Equal(Color.Blue, sourcePixels[50, 50]); + Assert.Equal(Color.Blue, sourcePixels[50, 50]); - Assert.Equal(Color.Blue, sourcePixels[2, 2]); + Assert.Equal(Color.Blue, sourcePixels[2, 2]); + } } } [Fact] public void ImageShouldBeOverlayedPolygonOutlineWithOpacity() { - string path = CreateOutputDirectory("Drawing", "Polygons"); - var simplePath = new[] { + string path = this.CreateOutputDirectory("Drawing", "Polygons"); + Vector2[] simplePath = new[] { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) }; - var color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); + Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); - var image = new Image(500, 500); - - using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) + using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Color.Blue) - .DrawPolygon(color, 10, simplePath) - .Save(output); - } + using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) + { + image + .BackgroundColor(Color.Blue) + .DrawPolygon(color, 10, simplePath) + .Save(output); + } - //shift background color towards forground color by the opacity amount - var mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); + //shift background color towards forground color by the opacity amount + Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); - using (var sourcePixels = image.Lock()) - { - Assert.Equal(mergedColor, sourcePixels[9, 9]); + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(mergedColor, sourcePixels[9, 9]); - Assert.Equal(mergedColor, sourcePixels[199, 149]); + Assert.Equal(mergedColor, sourcePixels[199, 149]); - Assert.Equal(Color.Blue, sourcePixels[50, 50]); + Assert.Equal(Color.Blue, sourcePixels[50, 50]); - Assert.Equal(Color.Blue, sourcePixels[2, 2]); + Assert.Equal(Color.Blue, sourcePixels[2, 2]); + } } } [Fact] public void ImageShouldBeOverlayedByRectangleOutline() { - string path = CreateOutputDirectory("Drawing", "Polygons"); + string path = this.CreateOutputDirectory("Drawing", "Polygons"); - var image = new Image(500, 500); - - using (FileStream output = File.OpenWrite($"{path}/Rectangle.png")) + using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Color.Blue) - .DrawPolygon(Color.HotPink, 10, new Rectangle(10, 10, 190, 140)) - .Save(output); - } + using (FileStream output = File.OpenWrite($"{path}/Rectangle.png")) + { + image + .BackgroundColor(Color.Blue) + .DrawPolygon(Color.HotPink, 10, new Rectangle(10, 10, 190, 140)) + .Save(output); + } - using (var sourcePixels = image.Lock()) - { - Assert.Equal(Color.HotPink, sourcePixels[8, 8]); + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(Color.HotPink, sourcePixels[8, 8]); - Assert.Equal(Color.HotPink, sourcePixels[198, 10]); + Assert.Equal(Color.HotPink, sourcePixels[198, 10]); - Assert.Equal(Color.HotPink, sourcePixels[10, 50]); + Assert.Equal(Color.HotPink, sourcePixels[10, 50]); - Assert.Equal(Color.Blue, sourcePixels[50, 50]); + Assert.Equal(Color.Blue, sourcePixels[50, 50]); - Assert.Equal(Color.Blue, sourcePixels[2, 2]); + Assert.Equal(Color.Blue, sourcePixels[2, 2]); + } } } } diff --git a/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs b/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs index 2edd05be1..0b450d166 100644 --- a/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs +++ b/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs @@ -16,18 +16,19 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldRecolorYellowToHotPink() { - string path = CreateOutputDirectory("Drawing", "RecolorImage"); + string path = this.CreateOutputDirectory("Drawing", "RecolorImage"); - var brush = new RecolorBrush(Color.Yellow, Color.HotPink, 0.2f); + RecolorBrush brush = new RecolorBrush(Color.Yellow, Color.HotPink, 0.2f); foreach (TestFile file in Files) { - Image image = file.CreateImage(); - - using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) + using (Image image = file.CreateImage()) { - image.Fill(brush) - .Save(output); + using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) + { + image.Fill(brush) + .Save(output); + } } } } @@ -35,19 +36,20 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldRecolorYellowToHotPinkInARectangle() { - string path = CreateOutputDirectory("Drawing", "RecolorImage"); + string path = this.CreateOutputDirectory("Drawing", "RecolorImage"); - var brush = new RecolorBrush(Color.Yellow, Color.HotPink, 0.2f); + RecolorBrush brush = new RecolorBrush(Color.Yellow, Color.HotPink, 0.2f); foreach (TestFile file in Files) { - Image image = file.CreateImage(); - - using (FileStream output = File.OpenWrite($"{path}/Shaped_{file.FileName}")) + using (Image image = file.CreateImage()) { - var imageHeight = image.Height; - image.Fill(brush, new Rectangle(0, imageHeight/2 - imageHeight/4, image.Width, imageHeight/2)) - .Save(output); + using (FileStream output = File.OpenWrite($"{path}/Shaped_{file.FileName}")) + { + int imageHeight = image.Height; + image.Fill(brush, new Rectangle(0, imageHeight/2 - imageHeight/4, image.Width, imageHeight/2)) + .Save(output); + } } } } diff --git a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs index f6bcf4906..18275ef38 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs @@ -5,11 +5,8 @@ namespace ImageSharp.Tests.Drawing { - using Drawing; - using ImageSharp.Drawing; using ImageSharp.Drawing.Shapes; - using System; - using System.Diagnostics.CodeAnalysis; + using System.IO; using System.Numerics; using Xunit; @@ -19,83 +16,84 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByFilledPolygon() { - string path = CreateOutputDirectory("Drawing", "FilledBezier"); - var simplePath = new[] { + string path = this.CreateOutputDirectory("Drawing", "FilledBezier"); + Vector2[] simplePath = new[] { new Vector2(10, 400), new Vector2(30, 10), new Vector2(240, 30), new Vector2(300, 400) }; - var image = new Image(500, 500); - - using (FileStream output = File.OpenWrite($"{path}/Simple.png")) + using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Color.Blue) - .Fill(Color.HotPink,new BezierPolygon(simplePath)) - .Save(output); - } + using (FileStream output = File.OpenWrite($"{path}/Simple.png")) + { + image + .BackgroundColor(Color.Blue) + .Fill(Color.HotPink,new BezierPolygon(simplePath)) + .Save(output); + } - using (var sourcePixels = image.Lock()) - { - //top of curve - Assert.Equal(Color.HotPink, sourcePixels[138, 116]); + using (PixelAccessor sourcePixels = image.Lock()) + { + //top of curve + Assert.Equal(Color.HotPink, sourcePixels[138, 116]); - //start points - Assert.Equal(Color.HotPink, sourcePixels[10, 400]); - Assert.Equal(Color.HotPink, sourcePixels[300, 400]); + //start points + Assert.Equal(Color.HotPink, sourcePixels[10, 400]); + Assert.Equal(Color.HotPink, sourcePixels[300, 400]); - //curve points should not be never be set - Assert.Equal(Color.Blue, sourcePixels[30, 10]); - Assert.Equal(Color.Blue, sourcePixels[240, 30]); + //curve points should not be never be set + Assert.Equal(Color.Blue, sourcePixels[30, 10]); + Assert.Equal(Color.Blue, sourcePixels[240, 30]); - // inside shape should not be empty - Assert.Equal(Color.HotPink, sourcePixels[200, 250]); + // inside shape should not be empty + Assert.Equal(Color.HotPink, sourcePixels[200, 250]); + } } } [Fact] public void ImageShouldBeOverlayedByFilledPolygonOpacity() { - string path = CreateOutputDirectory("Drawing", "FilledBezier"); - var simplePath = new[] { + string path = this.CreateOutputDirectory("Drawing", "FilledBezier"); + Vector2[] simplePath = new[] { new Vector2(10, 400), new Vector2(30, 10), new Vector2(240, 30), new Vector2(300, 400) }; - var color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); + Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); - var image = new Image(500, 500); - - using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) + using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Color.Blue) - .Fill(color, new BezierPolygon(simplePath)) - .Save(output); - } + using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) + { + image + .BackgroundColor(Color.Blue) + .Fill(color, new BezierPolygon(simplePath)) + .Save(output); + } - //shift background color towards forground color by the opacity amount - var mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); + //shift background color towards forground color by the opacity amount + Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); - using (var sourcePixels = image.Lock()) - { - //top of curve - Assert.Equal(mergedColor, sourcePixels[138, 116]); + using (PixelAccessor sourcePixels = image.Lock()) + { + //top of curve + Assert.Equal(mergedColor, sourcePixels[138, 116]); - //start points - Assert.Equal(mergedColor, sourcePixels[10, 400]); - Assert.Equal(mergedColor, sourcePixels[300, 400]); + //start points + Assert.Equal(mergedColor, sourcePixels[10, 400]); + Assert.Equal(mergedColor, sourcePixels[300, 400]); - //curve points should not be never be set - Assert.Equal(Color.Blue, sourcePixels[30, 10]); - Assert.Equal(Color.Blue, sourcePixels[240, 30]); + //curve points should not be never be set + Assert.Equal(Color.Blue, sourcePixels[30, 10]); + Assert.Equal(Color.Blue, sourcePixels[240, 30]); - // inside shape should not be empty - Assert.Equal(mergedColor, sourcePixels[200, 250]); + // inside shape should not be empty + Assert.Equal(mergedColor, sourcePixels[200, 250]); + } } } - } } diff --git a/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs index cbd44d7d1..144a1398d 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs @@ -5,12 +5,9 @@ namespace ImageSharp.Tests.Drawing { - using System; - using System.Diagnostics.CodeAnalysis; using System.IO; using Xunit; - using Drawing; - using ImageSharp.Drawing; + using System.Numerics; using ImageSharp.Drawing.Shapes; @@ -19,41 +16,42 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByPolygonOutline() { - string path = CreateOutputDirectory("Drawing", "ComplexPolygon"); - var simplePath = new LinearPolygon( + string path = this.CreateOutputDirectory("Drawing", "ComplexPolygon"); + LinearPolygon simplePath = new LinearPolygon( new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300)); - var hole1 = new LinearPolygon( + LinearPolygon hole1 = new LinearPolygon( new Vector2(37, 85), new Vector2(93, 85), new Vector2(65, 137)); - var image = new Image(500, 500); - - using (FileStream output = File.OpenWrite($"{path}/Simple.png")) + using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Color.Blue) - .Fill(Color.HotPink, new ComplexPolygon(simplePath, hole1)) - .Save(output); - } + using (FileStream output = File.OpenWrite($"{path}/Simple.png")) + { + image + .BackgroundColor(Color.Blue) + .Fill(Color.HotPink, new ComplexPolygon(simplePath, hole1)) + .Save(output); + } - using (var sourcePixels = image.Lock()) - { - Assert.Equal(Color.HotPink, sourcePixels[11, 11]); + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(Color.HotPink, sourcePixels[11, 11]); - Assert.Equal(Color.HotPink, sourcePixels[200, 150]); + Assert.Equal(Color.HotPink, sourcePixels[200, 150]); - Assert.Equal(Color.HotPink, sourcePixels[50, 50]); + Assert.Equal(Color.HotPink, sourcePixels[50, 50]); - Assert.Equal(Color.HotPink, sourcePixels[35, 100]); + Assert.Equal(Color.HotPink, sourcePixels[35, 100]); - Assert.Equal(Color.Blue, sourcePixels[2, 2]); + Assert.Equal(Color.Blue, sourcePixels[2, 2]); - //inside hole - Assert.Equal(Color.Blue, sourcePixels[57, 99]); + //inside hole + Assert.Equal(Color.Blue, sourcePixels[57, 99]); + } } } @@ -61,87 +59,89 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedPolygonOutlineWithOverlap() { - string path = CreateOutputDirectory("Drawing", "ComplexPolygon"); - var simplePath = new LinearPolygon( + string path = this.CreateOutputDirectory("Drawing", "ComplexPolygon"); + LinearPolygon simplePath = new LinearPolygon( new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300)); - var hole1 = new LinearPolygon( + LinearPolygon hole1 = new LinearPolygon( new Vector2(37, 85), new Vector2(130, 40), new Vector2(65, 137)); - var image = new Image(500, 500); - - using (FileStream output = File.OpenWrite($"{path}/SimpleOverlapping.png")) + using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Color.Blue) - .Fill(Color.HotPink, new ComplexPolygon(simplePath, hole1)) - .Save(output); - } - - using (var sourcePixels = image.Lock()) - { - Assert.Equal(Color.HotPink, sourcePixels[11, 11]); + using (FileStream output = File.OpenWrite($"{path}/SimpleOverlapping.png")) + { + image + .BackgroundColor(Color.Blue) + .Fill(Color.HotPink, new ComplexPolygon(simplePath, hole1)) + .Save(output); + } + + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(Color.HotPink, sourcePixels[11, 11]); - Assert.Equal(Color.HotPink, sourcePixels[200, 150]); + Assert.Equal(Color.HotPink, sourcePixels[200, 150]); - Assert.Equal(Color.HotPink, sourcePixels[50, 50]); + Assert.Equal(Color.HotPink, sourcePixels[50, 50]); - Assert.Equal(Color.HotPink, sourcePixels[35, 100]); + Assert.Equal(Color.HotPink, sourcePixels[35, 100]); - Assert.Equal(Color.Blue, sourcePixels[2, 2]); + Assert.Equal(Color.Blue, sourcePixels[2, 2]); - //inside hole - Assert.Equal(Color.Blue, sourcePixels[57, 99]); + //inside hole + Assert.Equal(Color.Blue, sourcePixels[57, 99]); + } } } [Fact] public void ImageShouldBeOverlayedPolygonOutlineWithOpacity() { - string path = CreateOutputDirectory("Drawing", "ComplexPolygon"); - var simplePath = new LinearPolygon( + string path = this.CreateOutputDirectory("Drawing", "ComplexPolygon"); + LinearPolygon simplePath = new LinearPolygon( new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300)); - var hole1 = new LinearPolygon( + LinearPolygon hole1 = new LinearPolygon( new Vector2(37, 85), new Vector2(93, 85), new Vector2(65, 137)); - var color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); - - var image = new Image(500, 500); + Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); - using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) + using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Color.Blue) - .Fill(color, new ComplexPolygon(simplePath, hole1)) - .Save(output); - } + using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) + { + image + .BackgroundColor(Color.Blue) + .Fill(color, new ComplexPolygon(simplePath, hole1)) + .Save(output); + } - //shift background color towards forground color by the opacity amount - var mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); + //shift background color towards forground color by the opacity amount + Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); - using (var sourcePixels = image.Lock()) - { - Assert.Equal(mergedColor, sourcePixels[11, 11]); + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(mergedColor, sourcePixels[11, 11]); - Assert.Equal(mergedColor, sourcePixels[200, 150]); + Assert.Equal(mergedColor, sourcePixels[200, 150]); - Assert.Equal(mergedColor, sourcePixels[50, 50]); + Assert.Equal(mergedColor, sourcePixels[50, 50]); - Assert.Equal(mergedColor, sourcePixels[35, 100]); + Assert.Equal(mergedColor, sourcePixels[35, 100]); - Assert.Equal(Color.Blue, sourcePixels[2, 2]); + Assert.Equal(Color.Blue, sourcePixels[2, 2]); - //inside hole - Assert.Equal(Color.Blue, sourcePixels[57, 99]); + //inside hole + Assert.Equal(Color.Blue, sourcePixels[57, 99]); + } } } } -} +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs index 0d3b721d5..b4cf8e090 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs @@ -5,10 +5,8 @@ namespace ImageSharp.Tests.Drawing { - using Drawing; using ImageSharp.Drawing; - using System; - using System.Diagnostics.CodeAnalysis; + using System.IO; using System.Numerics; using Xunit; @@ -19,155 +17,153 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeOverlayedByFilledPolygon() { - string path = CreateOutputDirectory("Drawing", "FilledPolygons"); - var simplePath = new[] { + string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + Vector2[] simplePath = new[] { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) }; - var image = new Image(500, 500); - using (FileStream output = File.OpenWrite($"{path}/Simple.png")) + using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Color.Blue) - .FillPolygon(Color.HotPink, simplePath, new GraphicsOptions(true)) - .Save(output); - } + using (FileStream output = File.OpenWrite($"{path}/Simple.png")) + { + image + .BackgroundColor(Color.Blue) + .FillPolygon(Color.HotPink, simplePath, new GraphicsOptions(true)) + .Save(output); + } - using (var sourcePixels = image.Lock()) - { - Assert.Equal(Color.HotPink, sourcePixels[11, 11]); + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(Color.HotPink, sourcePixels[11, 11]); - Assert.Equal(Color.HotPink, sourcePixels[200, 150]); + Assert.Equal(Color.HotPink, sourcePixels[200, 150]); - Assert.Equal(Color.HotPink, sourcePixels[50, 50]); + Assert.Equal(Color.HotPink, sourcePixels[50, 50]); - Assert.Equal(Color.Blue, sourcePixels[2, 2]); + Assert.Equal(Color.Blue, sourcePixels[2, 2]); + } } } [Fact] - public void ImageShouldBeOverlayedByFilledPolygon_NoAntialias() + public void ImageShouldBeOverlayedByFilledPolygonNoAntialias() { - string path = CreateOutputDirectory("Drawing", "FilledPolygons"); - var simplePath = new[] { + string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + Vector2[] simplePath = new[] { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) }; - var image = new Image(500, 500); + using (Image image = new Image(500, 500)) using (FileStream output = File.OpenWrite($"{path}/Simple_NoAntialias.png")) { image .BackgroundColor(Color.Blue) .FillPolygon(Color.HotPink, simplePath, new GraphicsOptions(false)) .Save(output); - } - using (var sourcePixels = image.Lock()) - { - Assert.Equal(Color.HotPink, sourcePixels[11, 11]); + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(Color.HotPink, sourcePixels[11, 11]); - Assert.Equal(Color.HotPink, sourcePixels[200, 150]); + Assert.Equal(Color.HotPink, sourcePixels[200, 150]); - Assert.Equal(Color.HotPink, sourcePixels[50, 50]); + Assert.Equal(Color.HotPink, sourcePixels[50, 50]); - Assert.Equal(Color.Blue, sourcePixels[2, 2]); + Assert.Equal(Color.Blue, sourcePixels[2, 2]); + } } } [Fact] - public void ImageShouldBeOverlayedByFilledPolygon_Image() + public void ImageShouldBeOverlayedByFilledPolygonImage() { - string path = CreateOutputDirectory("Drawing", "FilledPolygons"); - var simplePath = new[] { + string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + Vector2[] simplePath = new[] { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) }; - var brush = new ImageBrush(TestFile.Create(TestImages.Bmp.Car).CreateImage()); - var image = new Image(500, 500); - + using (Image brushImage = TestFile.Create(TestImages.Bmp.Car).CreateImage()) + using (Image image = new Image(500, 500)) using (FileStream output = File.OpenWrite($"{path}/Image.png")) { + ImageBrush brush = new ImageBrush(brushImage); + image - .BackgroundColor(Color.Blue) - .FillPolygon(brush, simplePath) - .Save(output); + .BackgroundColor(Color.Blue) + .FillPolygon(brush, simplePath) + .Save(output); } } [Fact] public void ImageShouldBeOverlayedByFilledPolygonOpacity() { - string path = CreateOutputDirectory("Drawing", "FilledPolygons"); - var simplePath = new[] { + string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + Vector2[] simplePath = new[] { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) }; - var color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); - - var image = new Image(500, 500); + Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); - using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) + using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Color.Blue) - .FillPolygon(color, simplePath) - .Save(output); - } + using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) + { + image + .BackgroundColor(Color.Blue) + .FillPolygon(color, simplePath) + .Save(output); + } - //shift background color towards forground color by the opacity amount - var mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); + //shift background color towards forground color by the opacity amount + Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); - using (var sourcePixels = image.Lock()) - { - Assert.Equal(mergedColor, sourcePixels[11, 11]); + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(mergedColor, sourcePixels[11, 11]); - Assert.Equal(mergedColor, sourcePixels[200, 150]); + Assert.Equal(mergedColor, sourcePixels[200, 150]); - Assert.Equal(mergedColor, sourcePixels[50, 50]); + Assert.Equal(mergedColor, sourcePixels[50, 50]); - Assert.Equal(Color.Blue, sourcePixels[2, 2]); + Assert.Equal(Color.Blue, sourcePixels[2, 2]); + } } } [Fact] public void ImageShouldBeOverlayedByFilledRectangle() { - string path = CreateOutputDirectory("Drawing", "FilledPolygons"); - var simplePath = new[] { - new Vector2(10, 10), - new Vector2(200, 10), - new Vector2(200, 150), - new Vector2(10, 150) - }; - - var image = new Image(500, 500); - - using (FileStream output = File.OpenWrite($"{path}/Rectangle.png")) + string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + using (Image image = new Image(500, 500)) { - image - .BackgroundColor(Color.Blue) - .Fill(Color.HotPink, new ImageSharp.Drawing.Shapes.RectangularPolygon(new Rectangle(10,10, 190, 140))) - .Save(output); - } + using (FileStream output = File.OpenWrite($"{path}/Rectangle.png")) + { + image + .BackgroundColor(Color.Blue) + .Fill(Color.HotPink, new ImageSharp.Drawing.Shapes.RectangularPolygon(new Rectangle(10, 10, 190, 140))) + .Save(output); + } - using (var sourcePixels = image.Lock()) - { - Assert.Equal(Color.HotPink, sourcePixels[11, 11]); + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(Color.HotPink, sourcePixels[11, 11]); - Assert.Equal(Color.HotPink, sourcePixels[198, 10]); + Assert.Equal(Color.HotPink, sourcePixels[198, 10]); - Assert.Equal(Color.HotPink, sourcePixels[10, 50]); + Assert.Equal(Color.HotPink, sourcePixels[10, 50]); - Assert.Equal(Color.HotPink, sourcePixels[50, 50]); + Assert.Equal(Color.HotPink, sourcePixels[50, 50]); - Assert.Equal(Color.Blue, sourcePixels[2, 2]); + Assert.Equal(Color.Blue, sourcePixels[2, 2]); + } } } } diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BitmapTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BitmapTests.cs index c91b0ad1b..2eb81a623 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BitmapTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BitmapTests.cs @@ -9,8 +9,6 @@ namespace ImageSharp.Tests { using System.IO; - using Formats; - using Xunit; public class BitmapTests : FileTestBase @@ -23,19 +21,20 @@ namespace ImageSharp.Tests }; [Theory] - [MemberData("BitsPerPixel")] + [MemberData(nameof(BitsPerPixel))] public void BitmapCanEncodeDifferentBitRates(BmpBitsPerPixel bitsPerPixel) { - string path = CreateOutputDirectory("Bmp"); + string path = this.CreateOutputDirectory("Bmp"); foreach (TestFile file in Files) { string filename = file.GetFileNameWithoutExtension(bitsPerPixel); - Image image = file.CreateImage(); - - using (FileStream output = File.OpenWrite($"{path}/{filename}.bmp")) + using (Image image = file.CreateImage()) { - image.Save(output, new BmpEncoder { BitsPerPixel = bitsPerPixel }); + using (FileStream output = File.OpenWrite($"{path}/{filename}.bmp")) + { + image.Save(output, new BmpEncoder { BitsPerPixel = bitsPerPixel }); + } } } } diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index 9ead4cf56..fc74fe928 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -16,17 +16,18 @@ namespace ImageSharp.Tests [Fact] public void ResolutionShouldChange() { - string path = CreateOutputDirectory("Resolution"); + string path = this.CreateOutputDirectory("Resolution"); foreach (TestFile file in Files) { - Image image = file.CreateImage(); - - using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) + using (Image image = file.CreateImage()) { - image.VerticalResolution = 150; - image.HorizontalResolution = 150; - image.Save(output); + using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) + { + image.VerticalResolution = 150; + image.HorizontalResolution = 150; + image.Save(output); + } } } } @@ -34,45 +35,31 @@ namespace ImageSharp.Tests [Fact] public void ImageCanEncodeToString() { - string path = CreateOutputDirectory("ToString"); + string path = this.CreateOutputDirectory("ToString"); foreach (TestFile file in Files) { - Image image = file.CreateImage(); - - string filename = path + "/" + file.FileNameWithoutExtension + ".txt"; - File.WriteAllText(filename, image.ToBase64String()); + using (Image image = file.CreateImage()) + { + string filename = path + "/" + file.FileNameWithoutExtension + ".txt"; + File.WriteAllText(filename, image.ToBase64String()); + } } } [Fact] public void DecodeThenEncodeImageFromStreamShouldSucceed() { - string path = CreateOutputDirectory("Encode"); + string path = this.CreateOutputDirectory("Encode"); foreach (TestFile file in Files) { - Image image = file.CreateImage(); - - //Image image = file.CreateImage().To(); - //Image image = file.CreateImage().To(); - //Image image = file.CreateImage().To(); - //Image image = file.CreateImage().To(); - //Image image = file.CreateImage().To(); - //Image image = file.CreateImage().To(); - //Image image = file.CreateImage().To(); - //Image image = file.CreateImage().To(); - //Image image = file.CreateImage().To(); - //Image image = file.CreateImage().To(); - //Image image = file.CreateImage().To(); - //Image image = file.CreateImage().To(); - //Image image = file.CreateImage().To(); - //Image image = file.CreateImage().To(); - //Image image = file.CreateImage().To(); - //Image image = file.CreateImage().To(); - using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) + using (Image image = file.CreateImage()) { - image.Save(output); + using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) + { + image.Save(output); + } } } } @@ -80,35 +67,35 @@ namespace ImageSharp.Tests [Fact] public void QuantizeImageShouldPreserveMaximumColorPrecision() { - string path = CreateOutputDirectory("Quantize"); + string path = this.CreateOutputDirectory("Quantize"); foreach (TestFile file in Files) { - Image image = file.CreateImage(); - - // Copy the original pixels to save decoding time. - Color[] pixels = new Color[image.Width * image.Height]; - Array.Copy(image.Pixels, pixels, image.Pixels.Length); - - using (FileStream output = File.OpenWrite($"{path}/Octree-{file.FileName}")) + using (Image image = file.CreateImage()) { - image.Quantize(Quantization.Octree) - .Save(output, image.CurrentImageFormat); + Color[] pixels = new Color[image.Width * image.Height]; + Array.Copy(image.Pixels, pixels, image.Width * image.Height); - } + using (FileStream output = File.OpenWrite($"{path}/Octree-{file.FileName}")) + { + image.Quantize(Quantization.Octree) + .Save(output, image.CurrentImageFormat); - image.SetPixels(image.Width, image.Height, pixels); - using (FileStream output = File.OpenWrite($"{path}/Wu-{file.FileName}")) - { - image.Quantize(Quantization.Wu) - .Save(output, image.CurrentImageFormat); - } + } - image.SetPixels(image.Width, image.Height, pixels); - using (FileStream output = File.OpenWrite($"{path}/Palette-{file.FileName}")) - { - image.Quantize(Quantization.Palette) - .Save(output, image.CurrentImageFormat); + image.SetPixels(image.Width, image.Height, pixels); + using (FileStream output = File.OpenWrite($"{path}/Wu-{file.FileName}")) + { + image.Quantize(Quantization.Wu) + .Save(output, image.CurrentImageFormat); + } + + image.SetPixels(image.Width, image.Height, pixels); + using (FileStream output = File.OpenWrite($"{path}/Palette-{file.FileName}")) + { + image.Quantize(Quantization.Palette) + .Save(output, image.CurrentImageFormat); + } } } } @@ -116,7 +103,7 @@ namespace ImageSharp.Tests [Fact] public void ImageCanConvertFormat() { - string path = CreateOutputDirectory("Format"); + string path = this.CreateOutputDirectory("Format"); foreach (TestFile file in Files) { @@ -147,7 +134,7 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldPreservePixelByteOrderWhenSerialized() { - string path = CreateOutputDirectory("Serialized"); + string path = this.CreateOutputDirectory("Serialized"); foreach (TestFile file in Files) { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/BadEofJpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/BadEofJpegTests.cs index 628bc4ea9..adc568c82 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/BadEofJpegTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/BadEofJpegTests.cs @@ -31,9 +31,11 @@ namespace ImageSharp.Tests public void LoadBaselineImage(TestImageProvider provider) where TColor : struct, IPackedPixel, IEquatable { - var image = provider.GetImage(); - Assert.NotNull(image); - provider.Utility.SaveTestOutputFile(image, "bmp"); + using (Image image = provider.GetImage()) + { + Assert.NotNull(image); + provider.Utility.SaveTestOutputFile(image, "bmp"); + } } [Theory] // TODO: #18 @@ -41,9 +43,11 @@ namespace ImageSharp.Tests public void LoadProgressiveImage(TestImageProvider provider) where TColor : struct, IPackedPixel, IEquatable { - var image = provider.GetImage(); - Assert.NotNull(image); - provider.Utility.SaveTestOutputFile(image, "bmp"); + using (Image image = provider.GetImage()) + { + Assert.NotNull(image); + provider.Utility.SaveTestOutputFile(image, "bmp"); + } } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 01ffa2eb0..25329d93a 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -29,19 +29,21 @@ namespace ImageSharp.Tests public void OpenBaselineJpeg_SaveBmp(TestImageProvider provider) where TColor : struct, IPackedPixel, IEquatable { - Image image = provider.GetImage(); - - provider.Utility.SaveTestOutputFile(image, "bmp"); + using (Image image = provider.GetImage()) + { + provider.Utility.SaveTestOutputFile(image, "bmp"); + } } - + [Theory] [WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb)] public void OpenProgressiveJpeg_SaveBmp(TestImageProvider provider) where TColor : struct, IPackedPixel, IEquatable { - Image image = provider.GetImage(); - - provider.Utility.SaveTestOutputFile(image, "bmp"); + using (Image image = provider.GetImage()) + { + provider.Utility.SaveTestOutputFile(image, "bmp"); + } } [Theory] @@ -53,17 +55,19 @@ namespace ImageSharp.Tests public void DecodeGenerated_SaveBmp( TestImageProvider provider, JpegSubsample subsample, - int quality) + int quality) where TColor : struct, IPackedPixel, IEquatable { - Image image = provider.GetImage(); - - JpegEncoder encoder = new JpegEncoder() { Subsample = subsample, Quality = quality }; - - byte[] data = new byte[65536]; - using (MemoryStream ms = new MemoryStream(data)) + byte[] data; + using (Image image = provider.GetImage()) { - image.Save(ms, encoder); + JpegEncoder encoder = new JpegEncoder() { Subsample = subsample, Quality = quality }; + + data = new byte[65536]; + using (MemoryStream ms = new MemoryStream(data)) + { + image.Save(ms, encoder); + } } // TODO: Automatic image comparers could help here a lot :P @@ -75,23 +79,24 @@ namespace ImageSharp.Tests [Theory] [WithSolidFilledImages(42, 88, 255, 0, 0, PixelTypes.StandardImageClass)] public void DecodeGenerated_MetadataOnly( - TestImageProvider provider) + TestImageProvider provider) where TColor : struct, IPackedPixel, IEquatable { - Image image = provider.GetImage(); - - using (MemoryStream ms = new MemoryStream()) + using (Image image = provider.GetImage()) { - image.Save(ms, new JpegEncoder()); - ms.Seek(0, SeekOrigin.Begin); - - Image mirror = provider.Factory.CreateImage(1, 1); - using (JpegDecoderCore decoder = new JpegDecoderCore()) + using (MemoryStream ms = new MemoryStream()) { - decoder.Decode(mirror, ms, true); - - Assert.Equal(decoder.ImageWidth, image.Width); - Assert.Equal(decoder.ImageHeight, image.Height); + image.Save(ms, new JpegEncoder()); + ms.Seek(0, SeekOrigin.Begin); + + Image mirror = provider.Factory.CreateImage(1, 1); + using (JpegDecoderCore decoder = new JpegDecoderCore()) + { + decoder.Decode(mirror, ms, true); + + Assert.Equal(decoder.ImageWidth, image.Width); + Assert.Equal(decoder.ImageHeight, image.Height); + } } } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index d1a5e185f..59e5eba5c 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -34,19 +34,16 @@ namespace ImageSharp.Tests public void LoadResizeSave(TestImageProvider provider, int quality, JpegSubsample subsample) where TColor : struct, IPackedPixel, IEquatable { - Image image = provider.GetImage() - .Resize(new ResizeOptions - { - Size = new Size(150, 100), - Mode = ResizeMode.Max - }); - image.Quality = quality; - image.ExifProfile = null; // Reduce the size of the file - JpegEncoder encoder = new JpegEncoder { Subsample = subsample, Quality = quality }; + using (Image image = provider.GetImage().Resize(new ResizeOptions { Size = new Size(150, 100), Mode = ResizeMode.Max })) + { + image.Quality = quality; + image.ExifProfile = null; // Reduce the size of the file + JpegEncoder encoder = new JpegEncoder { Subsample = subsample, Quality = quality }; - provider.Utility.TestName += $"{subsample}_Q{quality}"; - provider.Utility.SaveTestOutputFile(image, "png"); - provider.Utility.SaveTestOutputFile(image, "jpg", encoder); + provider.Utility.TestName += $"{subsample}_Q{quality}"; + provider.Utility.SaveTestOutputFile(image, "png"); + provider.Utility.SaveTestOutputFile(image, "jpg", encoder); + } } [Theory] @@ -55,20 +52,21 @@ namespace ImageSharp.Tests public void OpenBmp_SaveJpeg(TestImageProvider provider, JpegSubsample subSample, int quality) where TColor : struct, IPackedPixel, IEquatable { - Image image = provider.GetImage(); - - ImagingTestCaseUtility utility = provider.Utility; - utility.TestName += "_" + subSample + "_Q" + quality; - - using (var outputStream = File.OpenWrite(utility.GetTestOutputFileName("jpg"))) + using (Image image = provider.GetImage()) { - var encoder = new JpegEncoder() + ImagingTestCaseUtility utility = provider.Utility; + utility.TestName += "_" + subSample + "_Q" + quality; + + using (FileStream outputStream = File.OpenWrite(utility.GetTestOutputFileName("jpg"))) { - Subsample = subSample, - Quality = quality - }; + JpegEncoder encoder = new JpegEncoder() + { + Subsample = subSample, + Quality = quality + }; - image.Save(outputStream, encoder); + image.Save(outputStream, encoder); + } } } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs index cc7df178b..0956131b1 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs @@ -19,7 +19,6 @@ namespace ImageSharp.Tests where TColor : struct, IPackedPixel, IEquatable { Image image = factory.CreateImage(10, 10); - using (PixelAccessor pixels = image.Lock()) { for (int i = 0; i < 10; i++) @@ -35,6 +34,7 @@ namespace ImageSharp.Tests } } } + return image; } @@ -43,24 +43,21 @@ namespace ImageSharp.Tests public void CopyStretchedRGBTo_FromOrigo(TestImageProvider provider) where TColor : struct, IPackedPixel, IEquatable { - Image src = provider.GetImage(); - - PixelArea area = new PixelArea(8, 8, ComponentOrder.Xyz); - Image dest = provider.Factory.CreateImage(8, 8); - - using (var s = src.Lock()) + using (Image src = provider.GetImage()) + using (Image dest = provider.Factory.CreateImage(8, 8)) + using (PixelArea area = new PixelArea(8, 8, ComponentOrder.Xyz)) + using (PixelAccessor s = src.Lock()) + using (PixelAccessor d = dest.Lock()) { - using (var d = dest.Lock()) - { - s.CopyRGBBytesStretchedTo(area, 0, 0); - d.CopyFrom(area, 0, 0); + s.CopyRGBBytesStretchedTo(area, 0, 0); + d.CopyFrom(area, 0, 0); - Assert.Equal(s[0, 0], d[0, 0]); - Assert.Equal(s[7, 0], d[7, 0]); - Assert.Equal(s[0, 7], d[0, 7]); - Assert.Equal(s[7, 7], d[7, 7]); - } + Assert.Equal(s[0, 0], d[0, 0]); + Assert.Equal(s[7, 0], d[7, 0]); + Assert.Equal(s[0, 7], d[0, 7]); + Assert.Equal(s[7, 7], d[7, 7]); } + } [Theory] @@ -68,45 +65,41 @@ namespace ImageSharp.Tests public void CopyStretchedRGBTo_WithOffset(TestImageProvider provider) where TColor : struct, IPackedPixel, IEquatable { - Image src = provider.GetImage(); - - PixelArea area = new PixelArea(8, 8, ComponentOrder.Xyz); - Image dest = provider.Factory.CreateImage(8, 8); - + using (Image src = provider.GetImage()) + using (PixelArea area = new PixelArea(8, 8, ComponentOrder.Xyz)) + using (Image dest = provider.Factory.CreateImage(8, 8)) using (PixelAccessor s = src.Lock()) + using (PixelAccessor d = dest.Lock()) { - using (var d = dest.Lock()) - { - s.CopyRGBBytesStretchedTo(area, 7, 6); - d.CopyFrom(area, 0, 0); + s.CopyRGBBytesStretchedTo(area, 7, 6); + d.CopyFrom(area, 0, 0); - Assert.Equal(s[6, 7], d[0, 0]); - Assert.Equal(s[6, 8], d[0, 1]); - Assert.Equal(s[7, 8], d[1, 1]); + Assert.Equal(s[6, 7], d[0, 0]); + Assert.Equal(s[6, 8], d[0, 1]); + Assert.Equal(s[7, 8], d[1, 1]); - Assert.Equal(s[6, 9], d[0, 2]); - Assert.Equal(s[6, 9], d[0, 3]); - Assert.Equal(s[6, 9], d[0, 7]); + Assert.Equal(s[6, 9], d[0, 2]); + Assert.Equal(s[6, 9], d[0, 3]); + Assert.Equal(s[6, 9], d[0, 7]); - Assert.Equal(s[7, 9], d[1, 2]); - Assert.Equal(s[7, 9], d[1, 3]); - Assert.Equal(s[7, 9], d[1, 7]); + Assert.Equal(s[7, 9], d[1, 2]); + Assert.Equal(s[7, 9], d[1, 3]); + Assert.Equal(s[7, 9], d[1, 7]); - Assert.Equal(s[9, 9], d[3, 2]); - Assert.Equal(s[9, 9], d[3, 3]); - Assert.Equal(s[9, 9], d[3, 7]); + Assert.Equal(s[9, 9], d[3, 2]); + Assert.Equal(s[9, 9], d[3, 3]); + Assert.Equal(s[9, 9], d[3, 7]); - Assert.Equal(s[9, 7], d[3, 0]); - Assert.Equal(s[9, 7], d[4, 0]); - Assert.Equal(s[9, 7], d[7, 0]); + Assert.Equal(s[9, 7], d[3, 0]); + Assert.Equal(s[9, 7], d[4, 0]); + Assert.Equal(s[9, 7], d[7, 0]); - Assert.Equal(s[9, 9], d[3, 2]); - Assert.Equal(s[9, 9], d[4, 2]); - Assert.Equal(s[9, 9], d[7, 2]); + Assert.Equal(s[9, 9], d[3, 2]); + Assert.Equal(s[9, 9], d[4, 2]); + Assert.Equal(s[9, 9], d[7, 2]); - Assert.Equal(s[9, 9], d[4, 3]); - Assert.Equal(s[9, 9], d[7, 7]); - } + Assert.Equal(s[9, 9], d[4, 3]); + Assert.Equal(s[9, 9], d[7, 7]); } } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs index 10106ae6f..589317a36 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs @@ -6,8 +6,6 @@ // ReSharper disable InconsistentNaming namespace ImageSharp.Tests.Formats.Jpg { - using System.Numerics; - using ImageSharp.Formats; using ImageSharp.Formats.Jpg; using Xunit; @@ -97,7 +95,7 @@ namespace ImageSharp.Tests.Formats.Jpg Assert.Equal(expected, actual, new ApproximateFloatComparer(2f)); } } - + [Theory] [InlineData(42)] [InlineData(1)] diff --git a/tests/ImageSharp.Tests/Formats/Png/PngTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngTests.cs index a76cbc615..ae6487ea9 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngTests.cs @@ -23,12 +23,13 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { - Image image = file.CreateImage(); - - using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.png")) + using (Image image = file.CreateImage()) { - image.Quality = 256; - image.Save(output, new PngFormat()); + using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.png")) + { + image.Quality = 256; + image.Save(output, new PngFormat()); + } } } } @@ -42,11 +43,12 @@ namespace ImageSharp.Tests Files, file => { - Image image = file.CreateImage(); - - using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.png")) + using (Image image = file.CreateImage()) { - image.SaveAsPng(output); + using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.png")) + { + image.SaveAsPng(output); + } } }); } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.cs b/tests/ImageSharp.Tests/Image/ImageTests.cs index 829dce748..c3c092e8e 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.cs @@ -23,10 +23,11 @@ namespace ImageSharp.Tests }); TestFile file = TestFile.Create(TestImages.Bmp.Car); - Image image = new Image(file.Bytes); - - Assert.Equal(600, image.Width); - Assert.Equal(450, image.Height); + using (Image image = new Image(file.Bytes)) + { + Assert.Equal(600, image.Width); + Assert.Equal(450, image.Height); + } } } } diff --git a/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs b/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs index ce28518d7..a0bf2cf7a 100644 --- a/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs +++ b/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs @@ -19,8 +19,7 @@ namespace ImageSharp.Tests where TColor : struct, IPackedPixel, IEquatable { Image image = factory.CreateImage(10, 10); - - using (var pixels = image.Lock()) + using (PixelAccessor pixels = image.Lock()) { for (int i = 0; i < 10; i++) { @@ -47,31 +46,33 @@ namespace ImageSharp.Tests public void CopyTo_Then_CopyFrom_OnFullImageRect(TestImageProvider provider, ComponentOrder order) where TColor : struct, IPackedPixel, IEquatable { - var src = provider.GetImage(); - - var dest = new Image(src.Width, src.Height); - - using (PixelArea area = new PixelArea(src.Width, src.Height, order)) + using (Image src = provider.GetImage()) { - using (var srcPixels = src.Lock()) + using (Image dest = new Image(src.Width, src.Height)) { - srcPixels.CopyTo(area, 0, 0); - } + using (PixelArea area = new PixelArea(src.Width, src.Height, order)) + { + using (PixelAccessor srcPixels = src.Lock()) + { + srcPixels.CopyTo(area, 0, 0); + } + + using (PixelAccessor destPixels = dest.Lock()) + { + destPixels.CopyFrom(area, 0, 0); + } + } - using (var destPixels = dest.Lock()) - { - destPixels.CopyFrom(area, 0, 0); + Assert.True(src.IsEquivalentTo(dest, false)); } } - - Assert.True(src.IsEquivalentTo(dest, false)); } // TODO: Need a processor in the library with this signature private static void Fill(Image image, Rectangle region, TColor color) where TColor : struct, IPackedPixel, IEquatable { - using (var pixels = image.Lock()) + using (PixelAccessor pixels = image.Lock()) { for (int y = region.Top; y < region.Bottom; y++) { @@ -88,87 +89,114 @@ namespace ImageSharp.Tests [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyx)] [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Xyzw)] [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyxw)] - public void CopyTo_Then_CopyFrom_WithOffset(TestImageProvider provider, ComponentOrder order) + public void CopyToThenCopyFromWithOffset(TestImageProvider provider, ComponentOrder order) where TColor : struct, IPackedPixel, IEquatable { - var srcImage = provider.GetImage(); - - var color = default(TColor); - color.PackFromBytes(255, 0, 0, 255); - - Fill(srcImage, new Rectangle(4, 4, 8, 8), color); - - var destImage = new Image(8, 8); - - using (var srcPixels = srcImage.Lock()) + using (Image destImage = new Image(8, 8)) { - using (var area = new PixelArea(8, 8, order)) + TColor color; + using (Image srcImage = provider.GetImage()) { - srcPixels.CopyTo(area, 4, 4); + color = default(TColor); + color.PackFromBytes(255, 0, 0, 255); - using (var destPixels = destImage.Lock()) + Fill(srcImage, new Rectangle(4, 4, 8, 8), color); + using (PixelAccessor srcPixels = srcImage.Lock()) { - destPixels.CopyFrom(area, 0, 0); + using (PixelArea area = new PixelArea(8, 8, order)) + { + srcPixels.CopyTo(area, 4, 4); + + using (PixelAccessor destPixels = destImage.Lock()) + { + destPixels.CopyFrom(area, 0, 0); + } + } } } - } - - provider.Utility.SourceFileOrDescription = order.ToString(); - provider.Utility.SaveTestOutputFile(destImage, "bmp"); - var expectedImage = new Image(8, 8).Fill(color); + provider.Utility.SourceFileOrDescription = order.ToString(); + provider.Utility.SaveTestOutputFile(destImage, "bmp"); - Assert.True(destImage.IsEquivalentTo(expectedImage)); + using (Image expectedImage = new Image(8, 8).Fill(color)) + { + Assert.True(destImage.IsEquivalentTo(expectedImage)); + } + } } [Fact] public void CopyFromZYX() { - CopyFromZYX(new Image(1, 1)); + using (Image image = new Image(1, 1)) + { + CopyFromZYX(image); + } } [Fact] public void CopyFromZYXOptimized() { - CopyFromZYX(new Image(1, 1)); + using (Image image = new Image(1, 1)) + { + CopyFromZYX(image); + } } [Fact] public void CopyFromZYXW() { - CopyFromZYXW(new Image(1, 1)); + using (Image image = new Image(1, 1)) + { + CopyFromZYXW(image); + } } [Fact] public void CopyFromZYXWOptimized() { - CopyFromZYXW(new Image(1, 1)); + using (Image image = new Image(1, 1)) + { + CopyFromZYXW(image); + } } [Fact] public void CopyToZYX() { - CopyToZYX(new Image(1, 1)); + using (Image image = new Image(1, 1)) + { + CopyToZYX(image); + } } [Fact] public void CopyToZYXOptimized() { - CopyToZYX(new Image(1, 1)); + using (Image image = new Image(1, 1)) + { + CopyToZYX(image); + } } [Fact] public void CopyToZYXW() { - CopyToZYXW(new Image(1, 1)); + using (Image image = new Image(1, 1)) + { + CopyToZYXW(image); + } } [Fact] public void CopyToZYXWOptimized() { - CopyToZYXW(new Image(1, 1)); + using (Image image = new Image(1, 1)) + { + CopyToZYXW(image); + } } private static void CopyFromZYX(Image image) diff --git a/tests/ImageSharp.Tests/Processors/Filters/AlphaTest.cs b/tests/ImageSharp.Tests/Processors/Filters/AlphaTest.cs index efbfe75a8..a8aeb3341 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/AlphaTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/AlphaTest.cs @@ -19,39 +19,35 @@ namespace ImageSharp.Tests }; [Theory] - [MemberData("AlphaValues")] + [MemberData(nameof(AlphaValues))] public void ImageShouldApplyAlphaFilter(int value) { - string path = CreateOutputDirectory("Alpha"); + string path = this.CreateOutputDirectory("Alpha"); foreach (TestFile file in Files) { string filename = file.GetFileName(value); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Alpha(value) - .Save(output); + image.Alpha(value).Save(output); } } } [Theory] - [MemberData("AlphaValues")] + [MemberData(nameof(AlphaValues))] public void ImageShouldApplyAlphaFilterInBox(int value) { - string path = CreateOutputDirectory("Alpha"); + string path = this.CreateOutputDirectory("Alpha"); foreach (TestFile file in Files) { string filename = file.GetFileName(value); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Alpha(value, new Rectangle(10, 10, image.Width / 2, image.Height / 2)) - .Save(output); + image.Alpha(value, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/AutoOrientTests.cs b/tests/ImageSharp.Tests/Processors/Filters/AutoOrientTests.cs index 10d6253fd..499bdff82 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/AutoOrientTests.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/AutoOrientTests.cs @@ -26,25 +26,22 @@ namespace ImageSharp.Tests }; [Theory] - [MemberData("OrientationValues")] + [MemberData(nameof(OrientationValues))] public void ImageShouldFlip(RotateType rotateType, FlipType flipType, ushort orientation) { - string path = CreateOutputDirectory("AutoOrient"); + string path = this.CreateOutputDirectory("AutoOrient"); TestFile file = TestFile.Create(TestImages.Bmp.F); - Image image = file.CreateImage(); - image.ExifProfile = new ExifProfile(); - image.ExifProfile.SetValue(ExifTag.Orientation, orientation); - - using (FileStream before = File.OpenWrite($"{path}/before-{file.FileName}")) + using (Image image = file.CreateImage()) { + image.ExifProfile = new ExifProfile(); + image.ExifProfile.SetValue(ExifTag.Orientation, orientation); + + using (FileStream before = File.OpenWrite($"{path}/before-{file.FileName}")) using (FileStream after = File.OpenWrite($"{path}/after-{file.FileName}")) { - image.RotateFlip(rotateType, flipType) - .Save(before) - .AutoOrient() - .Save(after); + image.RotateFlip(rotateType, flipType).Save(before).AutoOrient().Save(after); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/BackgroundColorTest.cs b/tests/ImageSharp.Tests/Processors/Filters/BackgroundColorTest.cs index a7ecf6c08..fd08b87a4 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/BackgroundColorTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/BackgroundColorTest.cs @@ -14,16 +14,14 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldApplyBackgroundColorFilter() { - string path = CreateOutputDirectory("BackgroundColor"); + string path = this.CreateOutputDirectory("BackgroundColor"); foreach (TestFile file in Files) { - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { - image.BackgroundColor(Color.HotPink) - .Save(output); + image.BackgroundColor(Color.HotPink).Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/BinaryThreshold.cs b/tests/ImageSharp.Tests/Processors/Filters/BinaryThreshold.cs index 10b174cd5..d7d4eac05 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/BinaryThreshold.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/BinaryThreshold.cs @@ -19,20 +19,18 @@ namespace ImageSharp.Tests }; [Theory] - [MemberData("BinaryThresholdValues")] + [MemberData(nameof(BinaryThresholdValues))] public void ImageShouldApplyBinaryThresholdFilter(float value) { - string path = CreateOutputDirectory("BinaryThreshold"); + string path = this.CreateOutputDirectory("BinaryThreshold"); foreach (TestFile file in Files) { string filename = file.GetFileName(value); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.BinaryThreshold(value) - .Save(output); + image.BinaryThreshold(value).Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/BlackWhiteTest.cs b/tests/ImageSharp.Tests/Processors/Filters/BlackWhiteTest.cs index d4af4ad38..6b9a48f72 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/BlackWhiteTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/BlackWhiteTest.cs @@ -14,16 +14,14 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldApplyBlackWhiteFilter() { - string path = CreateOutputDirectory("BlackWhite"); + string path = this.CreateOutputDirectory("BlackWhite"); foreach (TestFile file in Files) { - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { - image.BlackWhite() - .Save(output); + image.BlackWhite().Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/BoxBlurTest.cs b/tests/ImageSharp.Tests/Processors/Filters/BoxBlurTest.cs index 4755acb1e..5d4f628ee 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/BoxBlurTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/BoxBlurTest.cs @@ -19,20 +19,18 @@ namespace ImageSharp.Tests }; [Theory] - [MemberData("BoxBlurValues")] + [MemberData(nameof(BoxBlurValues))] public void ImageShouldApplyBoxBlurFilter(int value) { - string path = CreateOutputDirectory("BoxBlur"); + string path = this.CreateOutputDirectory("BoxBlur"); foreach (TestFile file in Files) { string filename = file.GetFileName(value); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.BoxBlur(value) - .Save(output); + image.BoxBlur(value).Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/BrightnessTest.cs b/tests/ImageSharp.Tests/Processors/Filters/BrightnessTest.cs index e32c7d35e..e274ef041 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/BrightnessTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/BrightnessTest.cs @@ -19,20 +19,18 @@ namespace ImageSharp.Tests }; [Theory] - [MemberData("BrightnessValues")] + [MemberData(nameof(BrightnessValues))] public void ImageShouldApplyBrightnessFilter(int value) { - string path = CreateOutputDirectory("Brightness"); + string path = this.CreateOutputDirectory("Brightness"); foreach (TestFile file in Files) { string filename = file.GetFileName(value); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Brightness(value) - .Save(output); + image.Brightness(value).Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/ColorBlindnessTest.cs b/tests/ImageSharp.Tests/Processors/Filters/ColorBlindnessTest.cs index 63005733a..d18f32caf 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/ColorBlindnessTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/ColorBlindnessTest.cs @@ -26,20 +26,18 @@ namespace ImageSharp.Tests }; [Theory] - [MemberData("ColorBlindnessFilters")] + [MemberData(nameof(ColorBlindnessFilters))] public void ImageShouldApplyColorBlindnessFilter(ColorBlindness colorBlindness) { - string path = CreateOutputDirectory("ColorBlindness"); + string path = this.CreateOutputDirectory("ColorBlindness"); foreach (TestFile file in Files) { string filename = file.GetFileName(colorBlindness); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.ColorBlindness(colorBlindness) - .Save(output); + image.ColorBlindness(colorBlindness).Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/ContrastTest.cs b/tests/ImageSharp.Tests/Processors/Filters/ContrastTest.cs index 3c83fd892..09376f2c0 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/ContrastTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/ContrastTest.cs @@ -19,19 +19,17 @@ namespace ImageSharp.Tests }; [Theory] - [MemberData("ContrastValues")] + [MemberData(nameof(ContrastValues))] public void ImageShouldApplyContrastFilter(int value) { - string path = CreateOutputDirectory("Contrast"); + string path = this.CreateOutputDirectory("Contrast"); foreach (TestFile file in Files) { - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { - image.Contrast(value) - .Save(output); + image.Contrast(value).Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/CropTest.cs b/tests/ImageSharp.Tests/Processors/Filters/CropTest.cs index 9e9dd34db..69c9d9372 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/CropTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/CropTest.cs @@ -14,16 +14,14 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldApplyCropSampler() { - string path = CreateOutputDirectory("Crop"); + string path = this.CreateOutputDirectory("Crop"); foreach (TestFile file in Files) { - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { - image.Crop(image.Width / 2, image.Height / 2) - .Save(output); + image.Crop(image.Width / 2, image.Height / 2).Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processors/Filters/DetectEdgesTest.cs index 1c3815b9b..e12440106 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/DetectEdgesTest.cs @@ -31,32 +31,29 @@ namespace ImageSharp.Tests [MemberData(nameof(DetectEdgesFilters))] public void ImageShouldApplyDetectEdgesFilter(EdgeDetection detector) { - string path = CreateOutputDirectory("DetectEdges"); + string path = this.CreateOutputDirectory("DetectEdges"); foreach (TestFile file in Files) { string filename = file.GetFileName(detector); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.DetectEdges(detector) - .Save(output); + image.DetectEdges(detector).Save(output); } } } [Theory] - [MemberData("DetectEdgesFilters")] + [MemberData(nameof(DetectEdgesFilters))] public void ImageShouldApplyDetectEdgesFilterInBox(EdgeDetection detector) { - string path = CreateOutputDirectory("DetectEdges"); + string path = this.CreateOutputDirectory("DetectEdges"); foreach (TestFile file in Files) { string filename = file.GetFileName(detector + "-InBox"); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.DetectEdges(detector, new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2)) diff --git a/tests/ImageSharp.Tests/Processors/Filters/EntropyCropTest.cs b/tests/ImageSharp.Tests/Processors/Filters/EntropyCropTest.cs index fdbbc5cde..1299d9814 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/EntropyCropTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/EntropyCropTest.cs @@ -19,20 +19,18 @@ namespace ImageSharp.Tests }; [Theory] - [MemberData("EntropyCropValues")] + [MemberData(nameof(EntropyCropValues))] public void ImageShouldApplyEntropyCropSampler(float value) { - string path = CreateOutputDirectory("EntropyCrop"); + string path = this.CreateOutputDirectory("EntropyCrop"); foreach (TestFile file in Files) { string filename = file.GetFileName(value); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.EntropyCrop(value) - .Save(output); + image.EntropyCrop(value).Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/FlipTests.cs b/tests/ImageSharp.Tests/Processors/Filters/FlipTests.cs index 26f964676..26bc240d5 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/FlipTests.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/FlipTests.cs @@ -20,20 +20,18 @@ namespace ImageSharp.Tests }; [Theory] - [MemberData("FlipValues")] + [MemberData(nameof(FlipValues))] public void ImageShouldFlip(FlipType flipType) { - string path = CreateOutputDirectory("Flip"); + string path = this.CreateOutputDirectory("Flip"); foreach (TestFile file in Files) { string filename = file.GetFileName(flipType); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Flip(flipType) - .Save(output); + image.Flip(flipType).Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/GaussianBlurTest.cs b/tests/ImageSharp.Tests/Processors/Filters/GaussianBlurTest.cs index 6a8279689..809ffa2f5 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/GaussianBlurTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/GaussianBlurTest.cs @@ -19,20 +19,18 @@ namespace ImageSharp.Tests }; [Theory] - [MemberData("GaussianBlurValues")] + [MemberData(nameof(GaussianBlurValues))] public void ImageShouldApplyGaussianBlurFilter(int value) { - string path = CreateOutputDirectory("GaussianBlur"); + string path = this.CreateOutputDirectory("GaussianBlur"); foreach (TestFile file in Files) { string filename = file.GetFileName(value); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.GaussianBlur(value) - .Save(output); + image.GaussianBlur(value).Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/GaussianSharpenTest.cs b/tests/ImageSharp.Tests/Processors/Filters/GaussianSharpenTest.cs index 69c24ab3b..c1aa06941 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/GaussianSharpenTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/GaussianSharpenTest.cs @@ -19,20 +19,18 @@ namespace ImageSharp.Tests }; [Theory] - [MemberData("GaussianSharpenValues")] + [MemberData(nameof(GaussianSharpenValues))] public void ImageShouldApplyGaussianSharpenFilter(int value) { - string path = CreateOutputDirectory("GaussianSharpen"); + string path = this.CreateOutputDirectory("GaussianSharpen"); foreach (TestFile file in Files) { string filename = file.GetFileName(value); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.GaussianSharpen(value) - .Save(output); + image.GaussianSharpen(value).Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/GlowTest.cs b/tests/ImageSharp.Tests/Processors/Filters/GlowTest.cs index 1d317795d..1afb1300a 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/GlowTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/GlowTest.cs @@ -14,16 +14,14 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldApplyGlowFilter() { - string path = CreateOutputDirectory("Glow"); + string path = this.CreateOutputDirectory("Glow"); foreach (TestFile file in Files) { - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { - image.Glow() - .Save(output); + image.Glow().Save(output); } } } @@ -31,17 +29,15 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldApplyGlowFilterColor() { - string path = CreateOutputDirectory("Glow"); + string path = this.CreateOutputDirectory("Glow"); foreach (TestFile file in Files) { string filename = file.GetFileName("Color"); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Glow(Color.HotPink) - .Save(output); + image.Glow(Color.HotPink).Save(output); } } } @@ -49,17 +45,15 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldApplyGlowFilterRadius() { - string path = CreateOutputDirectory("Glow"); + string path = this.CreateOutputDirectory("Glow"); foreach (TestFile file in Files) { string filename = file.GetFileName("Radius"); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Glow(image.Width / 4) - .Save(output); + image.Glow(image.Width / 4F).Save(output); } } } @@ -67,17 +61,16 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldApplyGlowFilterInBox() { - string path = CreateOutputDirectory("Glow"); + string path = this.CreateOutputDirectory("Glow"); foreach (TestFile file in Files) { string filename = file.GetFileName("InBox"); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.Glow(new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2)) - .Save(output); + .Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/GrayscaleTest.cs b/tests/ImageSharp.Tests/Processors/Filters/GrayscaleTest.cs index ee4d0b027..91f383dd2 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/GrayscaleTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/GrayscaleTest.cs @@ -20,20 +20,18 @@ namespace ImageSharp.Tests }; [Theory] - [MemberData("GrayscaleValues")] + [MemberData(nameof(GrayscaleValues))] public void ImageShouldApplyGrayscaleFilter(GrayscaleMode value) { - string path = CreateOutputDirectory("Grayscale"); + string path = this.CreateOutputDirectory("Grayscale"); foreach (TestFile file in Files) { string filename = file.GetFileName(value); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Grayscale(value) - .Save(output); + image.Grayscale(value).Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/HueTest.cs b/tests/ImageSharp.Tests/Processors/Filters/HueTest.cs index a56aec9ec..4241dc833 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/HueTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/HueTest.cs @@ -19,20 +19,18 @@ namespace ImageSharp.Tests }; [Theory] - [MemberData("HueValues")] + [MemberData(nameof(HueValues))] public void ImageShouldApplyHueFilter(int value) { - string path = CreateOutputDirectory("Hue"); + string path = this.CreateOutputDirectory("Hue"); foreach (TestFile file in Files) { string filename = file.GetFileName(value); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Hue(value) - .Save(output); + image.Hue(value).Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/InvertTest.cs b/tests/ImageSharp.Tests/Processors/Filters/InvertTest.cs index 55bfa852b..da672f830 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/InvertTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/InvertTest.cs @@ -14,15 +14,13 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldApplyInvertFilter() { - string path = CreateOutputDirectory("Invert"); + string path = this.CreateOutputDirectory("Invert"); foreach (TestFile file in Files) { - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { - image.Invert() - .Save(output); + image.Invert().Save(output); } } } @@ -30,17 +28,15 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldApplyInvertFilterInBox() { - string path = CreateOutputDirectory("Invert"); + string path = this.CreateOutputDirectory("Invert"); foreach (TestFile file in Files) { string filename = file.GetFileName("InBox"); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Invert(new Rectangle(10, 10, image.Width / 2, image.Height / 2)) - .Save(output); + image.Invert(new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/KodachromeTest.cs b/tests/ImageSharp.Tests/Processors/Filters/KodachromeTest.cs index adb7cb36d..40734e02a 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/KodachromeTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/KodachromeTest.cs @@ -14,16 +14,14 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldApplyKodachromeFilter() { - string path = CreateOutputDirectory("Kodachrome"); + string path = this.CreateOutputDirectory("Kodachrome"); foreach (TestFile file in Files) { - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { - image.Kodachrome() - .Save(output); + image.Kodachrome().Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/LomographTest.cs b/tests/ImageSharp.Tests/Processors/Filters/LomographTest.cs index 79a7aa3ba..57ca72d39 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/LomographTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/LomographTest.cs @@ -14,16 +14,14 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldApplyLomographFilter() { - string path = CreateOutputDirectory("Lomograph"); + string path = this.CreateOutputDirectory("Lomograph"); foreach (TestFile file in Files) { - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { - image.Lomograph() - .Save(output); + image.Lomograph().Save(output); } } } @@ -31,17 +29,16 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldApplyLomographFilterInBox() { - string path = CreateOutputDirectory("Lomograph"); + string path = this.CreateOutputDirectory("Lomograph"); foreach (TestFile file in Files) { string filename = file.GetFileName("InBox"); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.Lomograph(new Rectangle(image.Width / 4, image.Width / 4, image.Width / 2, image.Height / 2)) - .Save(output); + .Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/OilPaintTest.cs b/tests/ImageSharp.Tests/Processors/Filters/OilPaintTest.cs index c177e9423..a9b552e21 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/OilPaintTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/OilPaintTest.cs @@ -23,19 +23,17 @@ namespace ImageSharp.Tests [MemberData(nameof(OilPaintValues))] public void ImageShouldApplyOilPaintFilter(Tuple value) { - string path = CreateOutputDirectory("OilPaint"); + string path = this.CreateOutputDirectory("OilPaint"); foreach (TestFile file in Files) { string filename = file.GetFileName(value); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { if (image.Width > value.Item2 && image.Height > value.Item2) { - image.OilPaint(value.Item1, value.Item2) - .Save(output); + image.OilPaint(value.Item1, value.Item2).Save(output); } } } @@ -45,18 +43,19 @@ namespace ImageSharp.Tests [MemberData(nameof(OilPaintValues))] public void ImageShouldApplyOilPaintFilterInBox(Tuple value) { - string path = CreateOutputDirectory("OilPaint"); + string path = this.CreateOutputDirectory("OilPaint"); foreach (TestFile file in Files) { string filename = file.GetFileName(value + "-InBox"); - Image image = file.CreateImage(); - - if (image.Width > value.Item2 && image.Height > value.Item2) + using (Image image = file.CreateImage()) { - using (FileStream output = File.OpenWrite($"{path}/{filename}")) + if (image.Width > value.Item2 && image.Height > value.Item2) { - image.OilPaint(value.Item1, value.Item2, new Rectangle(image.Width / 4, image.Width / 4, image.Width / 2, image.Height / 2)).Save(output); + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.OilPaint(value.Item1, value.Item2, new Rectangle(image.Width / 4, image.Width / 4, image.Width / 2, image.Height / 2)).Save(output); + } } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/PadTest.cs b/tests/ImageSharp.Tests/Processors/Filters/PadTest.cs index 5a33aac2b..f00cdd4f3 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/PadTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/PadTest.cs @@ -14,16 +14,14 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldApplyPadSampler() { - string path = CreateOutputDirectory("Pad"); + string path = this.CreateOutputDirectory("Pad"); foreach (TestFile file in Files) { - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { - image.Pad(image.Width + 50, image.Height + 50) - .Save(output); + image.Pad(image.Width + 50, image.Height + 50).Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/PixelateTest.cs b/tests/ImageSharp.Tests/Processors/Filters/PixelateTest.cs index 38ec406ac..3a5fbc556 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/PixelateTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/PixelateTest.cs @@ -41,17 +41,15 @@ namespace ImageSharp.Tests [MemberData(nameof(PixelateValues))] public void ImageShouldApplyPixelateFilterInBox(int value) { - string path = CreateOutputDirectory("Pixelate"); + string path = this.CreateOutputDirectory("Pixelate"); foreach (TestFile file in Files) { string filename = file.GetFileName(value + "-InBox"); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Pixelate(value, new Rectangle(10, 10, image.Width / 2, image.Height / 2)) - .Save(output); + image.Pixelate(value, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/PolaroidTest.cs b/tests/ImageSharp.Tests/Processors/Filters/PolaroidTest.cs index dc9d3a150..040f8b4a2 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/PolaroidTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/PolaroidTest.cs @@ -14,16 +14,14 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldApplyPolaroidFilter() { - string path = CreateOutputDirectory("Polaroid"); + string path = this.CreateOutputDirectory("Polaroid"); foreach (TestFile file in Files) { - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { - image.Polaroid() - .Save(output); + image.Polaroid().Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/ResizeTests.cs b/tests/ImageSharp.Tests/Processors/Filters/ResizeTests.cs index a57d7f6c4..5acbe0f3e 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/ResizeTests.cs @@ -42,12 +42,10 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(name); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Resize(image.Width / 2, image.Height / 2, sampler, true) - .Save(output); + image.Resize(image.Width / 2, image.Height / 2, sampler, true).Save(output); } } } @@ -63,12 +61,10 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(name); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Resize(image.Width / 3, 0, sampler, false) - .Save(output); + image.Resize(image.Width / 3, 0, sampler, false).Save(output); } } } @@ -84,12 +80,10 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(name); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Resize(0, image.Height / 3, sampler, false) - .Save(output); + image.Resize(0, image.Height / 3, sampler, false).Save(output); } } } @@ -105,18 +99,16 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(name); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - ResizeOptions options = new ResizeOptions() + ResizeOptions options = new ResizeOptions { Sampler = sampler, Size = new Size(image.Width / 2, image.Height) }; - image.Resize(options) - .Save(output); + image.Resize(options).Save(output); } } } @@ -132,18 +124,16 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(name); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - ResizeOptions options = new ResizeOptions() + ResizeOptions options = new ResizeOptions { Sampler = sampler, Size = new Size(image.Width, image.Height / 2) }; - image.Resize(options) - .Save(output); + image.Resize(options).Save(output); } } } @@ -159,18 +149,16 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(name); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - ResizeOptions options = new ResizeOptions() + ResizeOptions options = new ResizeOptions { Size = new Size(image.Width + 200, image.Height), Mode = ResizeMode.Pad }; - image.Resize(options) - .Save(output); + image.Resize(options).Save(output); } } } @@ -186,19 +174,17 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(name); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - ResizeOptions options = new ResizeOptions() + ResizeOptions options = new ResizeOptions { Sampler = sampler, Size = new Size(image.Width + 200, image.Height + 200), Mode = ResizeMode.BoxPad }; - image.Resize(options) - .Save(output); + image.Resize(options).Save(output); } } } @@ -214,19 +200,17 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(name); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - ResizeOptions options = new ResizeOptions() + ResizeOptions options = new ResizeOptions { Sampler = sampler, Size = new Size(300, 300), Mode = ResizeMode.Max }; - image.Resize(options) - .Save(output); + image.Resize(options).Save(output); } } } @@ -242,19 +226,17 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(name); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - ResizeOptions options = new ResizeOptions() + ResizeOptions options = new ResizeOptions { Sampler = sampler, - Size = new Size((int)Math.Round(image.Width * .75F), (int)Math.Round(image.Height * 95F)), + Size = new Size((int)Math.Round(image.Width * .75F), (int)Math.Round(image.Height * .95F)), Mode = ResizeMode.Min }; - image.Resize(options) - .Save(output); + image.Resize(options).Save(output); } } } @@ -270,19 +252,17 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(name); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - ResizeOptions options = new ResizeOptions() + ResizeOptions options = new ResizeOptions { Sampler = sampler, Size = new Size(image.Width / 2, image.Height), Mode = ResizeMode.Stretch }; - image.Resize(options) - .Save(output); + image.Resize(options).Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/RotateFlipTest.cs b/tests/ImageSharp.Tests/Processors/Filters/RotateFlipTest.cs index 1cccef48a..e235ed229 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/RotateFlipTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/RotateFlipTest.cs @@ -22,20 +22,18 @@ namespace ImageSharp.Tests }; [Theory] - [MemberData("RotateFlipValues")] + [MemberData(nameof(RotateFlipValues))] public void ImageShouldRotateFlip(RotateType rotateType, FlipType flipType) { - string path = CreateOutputDirectory("RotateFlip"); + string path = this.CreateOutputDirectory("RotateFlip"); foreach (TestFile file in Files) { string filename = file.GetFileName(rotateType + "-" + flipType); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.RotateFlip(rotateType, flipType) - .Save(output); + image.RotateFlip(rotateType, flipType).Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/RotateTest.cs b/tests/ImageSharp.Tests/Processors/Filters/RotateTest.cs index 406edda37..a504fd989 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/RotateTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/RotateTest.cs @@ -28,39 +28,35 @@ namespace ImageSharp.Tests }; [Theory] - [MemberData("RotateFloatValues")] + [MemberData(nameof(RotateFloatValues))] public void ImageShouldApplyRotateSampler(float value) { - string path = CreateOutputDirectory("Rotate"); + string path = this.CreateOutputDirectory("Rotate"); foreach (TestFile file in Files) { string filename = file.GetFileName(value); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Rotate(value) - .Save(output); + image.Rotate(value).Save(output); } } } [Theory] - [MemberData("RotateEnumValues")] + [MemberData(nameof(RotateEnumValues))] public void ImageShouldApplyRotateSampler(RotateType value) { - string path = CreateOutputDirectory("Rotate"); + string path = this.CreateOutputDirectory("Rotate"); foreach (TestFile file in Files) { string filename = file.GetFileName(value); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Rotate(value) - .Save(output); + image.Rotate(value).Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/SaturationTest.cs b/tests/ImageSharp.Tests/Processors/Filters/SaturationTest.cs index 5fe4c3e00..abd596d70 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/SaturationTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/SaturationTest.cs @@ -19,7 +19,7 @@ namespace ImageSharp.Tests }; [Theory] - [MemberData("SaturationValues")] + [MemberData(nameof(SaturationValues))] public void ImageShouldApplySaturationFilter(int value) { string path = CreateOutputDirectory("Saturation"); @@ -27,12 +27,10 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(value); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Saturation(value) - .Save(output); + image.Saturation(value).Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/SepiaTest.cs b/tests/ImageSharp.Tests/Processors/Filters/SepiaTest.cs index b5e4d3105..fbae10fa5 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/SepiaTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/SepiaTest.cs @@ -18,12 +18,10 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { - image.Sepia() - .Save(output); + image.Sepia().Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/SkewTest.cs b/tests/ImageSharp.Tests/Processors/Filters/SkewTest.cs index 11db7e691..231f5dae8 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/SkewTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/SkewTest.cs @@ -19,22 +19,20 @@ namespace ImageSharp.Tests }; [Theory] - [MemberData("SkewValues")] + [MemberData(nameof(SkewValues))] public void ImageShouldApplySkewSampler(float x, float y) { - string path = CreateOutputDirectory("Skew"); + string path = this.CreateOutputDirectory("Skew"); - // Matches live example + // Matches live example // http://www.w3schools.com/css/tryit.asp?filename=trycss3_transform_skew foreach (TestFile file in Files) { string filename = file.GetFileName(x + "-" + y); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Skew(x, y) - .Save(output); + image.Skew(x, y).Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/VignetteTest.cs b/tests/ImageSharp.Tests/Processors/Filters/VignetteTest.cs index 3fddad1da..7f40ef1d2 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/VignetteTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/VignetteTest.cs @@ -14,16 +14,14 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldApplyVignetteFilter() { - string path = CreateOutputDirectory("Vignette"); + string path = this.CreateOutputDirectory("Vignette"); foreach (TestFile file in Files) { - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { - image.Vignette() - .Save(output); + image.Vignette().Save(output); } } } @@ -31,17 +29,15 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldApplyVignetteFilterColor() { - string path = CreateOutputDirectory("Vignette"); + string path = this.CreateOutputDirectory("Vignette"); foreach (TestFile file in Files) { string filename = file.GetFileName("Color"); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Vignette(Color.HotPink) - .Save(output); + image.Vignette(Color.HotPink).Save(output); } } } @@ -49,17 +45,15 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldApplyVignetteFilterRadius() { - string path = CreateOutputDirectory("Vignette"); + string path = this.CreateOutputDirectory("Vignette"); foreach (TestFile file in Files) { string filename = file.GetFileName("Radius"); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Vignette(image.Width / 4, image.Height / 4) - .Save(output); + image.Vignette(image.Width / 4F, image.Height / 4F).Save(output); } } } @@ -67,17 +61,16 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldApplyVignetteFilterInBox() { - string path = CreateOutputDirectory("Vignette"); + string path = this.CreateOutputDirectory("Vignette"); foreach (TestFile file in Files) { string filename = file.GetFileName("InBox"); - Image image = file.CreateImage(); - + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.Vignette(new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2)) - .Save(output); + .Save(output); } } } diff --git a/tests/ImageSharp.Tests/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/Profiles/Exif/ExifProfileTests.cs index 13fe24f50..1900b58c6 100644 --- a/tests/ImageSharp.Tests/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/Profiles/Exif/ExifProfileTests.cs @@ -31,6 +31,8 @@ namespace ImageSharp.Tests ExifValue value = image.ExifProfile.Values.FirstOrDefault(val => val.Tag == ExifTag.Copyright); TestValue(value, "Dirk Lemstra"); + + } [Fact] @@ -247,6 +249,7 @@ namespace ImageSharp.Tests using (MemoryStream memStream = new MemoryStream()) { image.SaveAsJpeg(memStream); + image.Dispose(); memStream.Position = 0; return new Image(memStream); diff --git a/tests/ImageSharp.Tests/Profiles/Exif/ExifTagDescriptionAttributeTests.cs b/tests/ImageSharp.Tests/Profiles/Exif/ExifTagDescriptionAttributeTests.cs index 4f77dc11a..daad49b2c 100644 --- a/tests/ImageSharp.Tests/Profiles/Exif/ExifTagDescriptionAttributeTests.cs +++ b/tests/ImageSharp.Tests/Profiles/Exif/ExifTagDescriptionAttributeTests.cs @@ -10,7 +10,7 @@ namespace ImageSharp.Tests public class ExifDescriptionAttributeTests { [Fact] - public void Test_ExifTag() + public void TestExifTag() { var exifProfile = new ExifProfile(); diff --git a/tests/ImageSharp.Tests/Profiles/Exif/ExifValueTests.cs b/tests/ImageSharp.Tests/Profiles/Exif/ExifValueTests.cs index 5993d1720..e777d9e3b 100644 --- a/tests/ImageSharp.Tests/Profiles/Exif/ExifValueTests.cs +++ b/tests/ImageSharp.Tests/Profiles/Exif/ExifValueTests.cs @@ -12,9 +12,12 @@ namespace ImageSharp.Tests { private static ExifValue GetExifValue() { - Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage(); + ExifProfile profile; + using (Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage()) + { + profile = image.ExifProfile; + } - ExifProfile profile = image.ExifProfile; Assert.NotNull(profile); return profile.Values.First(); From 1ebdfb9e61cb59e3edec546b6752852d812b3c46 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 1 Feb 2017 20:42:31 +1100 Subject: [PATCH 005/142] Remove finalizer --- src/ImageSharp/Image/ImageBase{TColor}.cs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/ImageSharp/Image/ImageBase{TColor}.cs b/src/ImageSharp/Image/ImageBase{TColor}.cs index c58f9412d..02bab249b 100644 --- a/src/ImageSharp/Image/ImageBase{TColor}.cs +++ b/src/ImageSharp/Image/ImageBase{TColor}.cs @@ -15,14 +15,9 @@ namespace ImageSharp /// /// The pixel format. [DebuggerDisplay("Image: {Width}x{Height}")] - public abstract class ImageBase : IImageBase // IImageBase implements IDisposable + public abstract class ImageBase : IImageBase where TColor : struct, IPackedPixel, IEquatable { - /// - /// The used to pool data. TODO: Choose sensible default size and count - /// - private static readonly ArrayPool ArrayPool = ArrayPool.Create(int.MaxValue, 50); - /// /// The image pixels /// @@ -94,14 +89,6 @@ namespace ImageSharp } } - /// - /// Finalizes an instance of the class. - /// - ~ImageBase() - { - this.Dispose(false); - } - /// public int MaxWidth { get; set; } = int.MaxValue; From 1a2f2c9636502b4dd608b7e00fa6ce596765b0f0 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 1 Feb 2017 23:38:32 +1100 Subject: [PATCH 006/142] Add PixelPool tests --- src/ImageSharp/Image/PixelPool{TColor}.cs | 10 +-- .../ImageSharp.Tests/Image/PixelPoolTests.cs | 74 +++++++++++++++++++ 2 files changed, 75 insertions(+), 9 deletions(-) create mode 100644 tests/ImageSharp.Tests/Image/PixelPoolTests.cs diff --git a/src/ImageSharp/Image/PixelPool{TColor}.cs b/src/ImageSharp/Image/PixelPool{TColor}.cs index 8673499a8..1f3392621 100644 --- a/src/ImageSharp/Image/PixelPool{TColor}.cs +++ b/src/ImageSharp/Image/PixelPool{TColor}.cs @@ -36,15 +36,7 @@ namespace ImageSharp /// The array to return to the buffer pool. public static void ReturnPixels(TColor[] array) { - try - { - ArrayPool.Return(array, true); - } - catch - { - // Do nothing. - // Hacky but it allows us to attempt to return non-pooled arrays and arrays that have already been returned - } + ArrayPool.Return(array, true); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Image/PixelPoolTests.cs b/tests/ImageSharp.Tests/Image/PixelPoolTests.cs new file mode 100644 index 000000000..0b762cf7c --- /dev/null +++ b/tests/ImageSharp.Tests/Image/PixelPoolTests.cs @@ -0,0 +1,74 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.Linq; + + using Xunit; + + /// + /// Tests the class. + /// + public class PixelPoolTests + { + [Fact] + public void PixelPoolRentsMinimumSize() + { + Color[] pixels = PixelPool.RentPixels(1024); + + Assert.True(pixels.Length >= 1024); + } + + [Fact] + public void PixelPoolRentsEmptyArray() + { + for (int i = 16; i < 1024; i += 16) + { + Color[] pixels = PixelPool.RentPixels(i); + + Assert.True(pixels.All(p => p == default(Color))); + + PixelPool.ReturnPixels(pixels); + } + + for (int i = 16; i < 1024; i += 16) + { + Color[] pixels = PixelPool.RentPixels(i); + + Assert.True(pixels.All(p => p == default(Color))); + + PixelPool.ReturnPixels(pixels); + } + } + + [Fact] + public void PixelPoolDoesNotThrowWhenReturningNonPooled() + { + Color[] pixels = new Color[1024]; + + PixelPool.ReturnPixels(pixels); + + Assert.True(pixels.Length >= 1024); + } + + [Fact] + public void PixelPoolCleansRentedArray() + { + Color[] pixels = PixelPool.RentPixels(256); + + for (int i = 0; i < pixels.Length; i++) + { + pixels[i] = Color.Azure; + } + + Assert.True(pixels.All(p => p == Color.Azure)); + + PixelPool.ReturnPixels(pixels); + + Assert.True(pixels.All(p => p == default(Color))); + } + } +} \ No newline at end of file From f17831a4d665bc13dd7a9a9e79c01224e7ad6ab5 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Wed, 1 Feb 2017 12:54:19 +0000 Subject: [PATCH 007/142] Have PixelAccessor manage PixelPool data. --- .../Convolution/Convolution2DProcessor.cs | 109 +++++++------- .../Convolution/Convolution2PassProcessor.cs | 81 +++++----- .../Convolution/ConvolutionProcessor.cs | 83 ++++++----- .../EdgeDetectorCompassProcessor.cs | 94 ++++++------ .../Effects/OilPaintingProcessor.cs | 127 ++++++++-------- .../Processors/Effects/PixelateProcessor.cs | 67 ++++----- .../Transforms/CompandingResizeProcessor.cs | 40 ++--- .../Processors/Transforms/CropProcessor.cs | 32 ++-- .../Transforms/EntropyCropProcessor.cs | 26 ++-- .../Processors/Transforms/FlipProcessor.cs | 72 ++++----- .../Processors/Transforms/ResizeProcessor.cs | 40 ++--- .../Processors/Transforms/RotateProcessor.cs | 140 +++++++++--------- .../Processors/Transforms/SkewProcessor.cs | 35 ++--- src/ImageSharp.Processing/project.json | 3 +- src/ImageSharp/Image/IImageBase{TColor}.cs | 29 ---- src/ImageSharp/Image/ImageBase{TColor}.cs | 53 ++----- src/ImageSharp/Image/PixelAccessor{TColor}.cs | 138 +++++++++++++---- src/ImageSharp/Quantizers/Quantize.cs | 30 ++-- .../Formats/GeneralFormatTests.cs | 34 +++-- 19 files changed, 632 insertions(+), 601 deletions(-) diff --git a/src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs index f77f1f439..cdea43e85 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs @@ -54,73 +54,74 @@ namespace ImageSharp.Processing.Processors int maxY = endY - 1; int maxX = endX - 1; - TColor[] target = PixelPool.RentPixels(source.Width * source.Height); - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(source.Width, source.Height)) + using (PixelAccessor targetPixels = new PixelAccessor(source.Width, source.Height)) { - Parallel.For( - startY, - endY, - this.ParallelOptions, - y => + using (PixelAccessor sourcePixels = source.Lock()) { - for (int x = startX; x < endX; x++) + Parallel.For( + startY, + endY, + this.ParallelOptions, + y => { - float rX = 0; - float gX = 0; - float bX = 0; - float rY = 0; - float gY = 0; - float bY = 0; - - // Apply each matrix multiplier to the color components for each pixel. - for (int fy = 0; fy < kernelYHeight; fy++) + for (int x = startX; x < endX; x++) { - int fyr = fy - radiusY; - int offsetY = y + fyr; - - offsetY = offsetY.Clamp(0, maxY); - - for (int fx = 0; fx < kernelXWidth; fx++) + float rX = 0; + float gX = 0; + float bX = 0; + float rY = 0; + float gY = 0; + float bY = 0; + + // Apply each matrix multiplier to the color components for each pixel. + for (int fy = 0; fy < kernelYHeight; fy++) { - int fxr = fx - radiusX; - int offsetX = x + fxr; - - offsetX = offsetX.Clamp(0, maxX); + int fyr = fy - radiusY; + int offsetY = y + fyr; - Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); - float r = currentColor.X; - float g = currentColor.Y; - float b = currentColor.Z; - - if (fy < kernelXHeight) - { - rX += this.KernelX[fy][fx] * r; - gX += this.KernelX[fy][fx] * g; - bX += this.KernelX[fy][fx] * b; - } + offsetY = offsetY.Clamp(0, maxY); - if (fx < kernelYWidth) + for (int fx = 0; fx < kernelXWidth; fx++) { - rY += this.KernelY[fy][fx] * r; - gY += this.KernelY[fy][fx] * g; - bY += this.KernelY[fy][fx] * b; + int fxr = fx - radiusX; + int offsetX = x + fxr; + + offsetX = offsetX.Clamp(0, maxX); + + Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); + float r = currentColor.X; + float g = currentColor.Y; + float b = currentColor.Z; + + if (fy < kernelXHeight) + { + rX += this.KernelX[fy][fx] * r; + gX += this.KernelX[fy][fx] * g; + bX += this.KernelX[fy][fx] * b; + } + + if (fx < kernelYWidth) + { + rY += this.KernelY[fy][fx] * r; + gY += this.KernelY[fy][fx] * g; + bY += this.KernelY[fy][fx] * b; + } } } - } - float red = (float)Math.Sqrt((rX * rX) + (rY * rY)); - float green = (float)Math.Sqrt((gX * gX) + (gY * gY)); - float blue = (float)Math.Sqrt((bX * bX) + (bY * bY)); + float red = (float)Math.Sqrt((rX * rX) + (rY * rY)); + float green = (float)Math.Sqrt((gX * gX) + (gY * gY)); + float blue = (float)Math.Sqrt((bX * bX) + (bY * bY)); - TColor packed = default(TColor); - packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W)); - targetPixels[x, y] = packed; - } - }); - } + TColor packed = default(TColor); + packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W)); + targetPixels[x, y] = packed; + } + }); + } - source.SetPixels(source.Width, source.Height, target); + source.SwapPixelsBuffers(targetPixels); + } } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs index ca343b868..71b806261 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs @@ -45,19 +45,16 @@ namespace ImageSharp.Processing.Processors int width = source.Width; int height = source.Height; - TColor[] target = PixelPool.RentPixels(width * height); - TColor[] firstPass = PixelPool.RentPixels(width * height); - - try + using (PixelAccessor targetPixels = new PixelAccessor(width, height)) { - this.ApplyConvolution(width, height, firstPass, source.Pixels, sourceRectangle, kernelX); - this.ApplyConvolution(width, height, target, firstPass, sourceRectangle, kernelY); + using (PixelAccessor firstPassPixels = new PixelAccessor(width, height)) + using (PixelAccessor sourcePixels = source.Lock()) + { + this.ApplyConvolution(width, height, firstPassPixels, sourcePixels, sourceRectangle, kernelX); + this.ApplyConvolution(width, height, targetPixels, firstPassPixels, sourceRectangle, kernelY); + } - source.SetPixels(width, height, target); - } - finally - { - PixelPool.ReturnPixels(firstPass); + source.SwapPixelsBuffers(targetPixels); } } @@ -67,13 +64,13 @@ namespace ImageSharp.Processing.Processors /// /// The image width. /// The image height. - /// The target pixels to apply the process to. - /// The source pixels. Cannot be null. + /// The target pixels to apply the process to. + /// The source pixels. Cannot be null. /// /// The structure that specifies the portion of the image object to draw. /// /// The kernel operator. - private void ApplyConvolution(int width, int height, TColor[] target, TColor[] source, Rectangle sourceRectangle, float[][] kernel) + private void ApplyConvolution(int width, int height, PixelAccessor targetPixels, PixelAccessor sourcePixels, Rectangle sourceRectangle, float[][] kernel) { int kernelHeight = kernel.Length; int kernelWidth = kernel[0].Length; @@ -87,45 +84,41 @@ namespace ImageSharp.Processing.Processors int maxY = endY - 1; int maxX = endX - 1; - using (PixelAccessor sourcePixels = source.Lock(width, height)) - using (PixelAccessor targetPixels = target.Lock(width, height)) + Parallel.For( + startY, + endY, + this.ParallelOptions, + y => { - Parallel.For( - startY, - endY, - this.ParallelOptions, - y => + for (int x = startX; x < endX; x++) { - for (int x = startX; x < endX; x++) - { - Vector4 destination = default(Vector4); + Vector4 destination = default(Vector4); - // Apply each matrix multiplier to the color components for each pixel. - for (int fy = 0; fy < kernelHeight; fy++) - { - int fyr = fy - radiusY; - int offsetY = y + fyr; + // Apply each matrix multiplier to the color components for each pixel. + for (int fy = 0; fy < kernelHeight; fy++) + { + int fyr = fy - radiusY; + int offsetY = y + fyr; - offsetY = offsetY.Clamp(0, maxY); + offsetY = offsetY.Clamp(0, maxY); - for (int fx = 0; fx < kernelWidth; fx++) - { - int fxr = fx - radiusX; - int offsetX = x + fxr; + for (int fx = 0; fx < kernelWidth; fx++) + { + int fxr = fx - radiusX; + int offsetX = x + fxr; - offsetX = offsetX.Clamp(0, maxX); + offsetX = offsetX.Clamp(0, maxX); - Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); - destination += kernel[fy][fx] * currentColor; - } + Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); + destination += kernel[fy][fx] * currentColor; } - - TColor packed = default(TColor); - packed.PackFromVector4(destination); - targetPixels[x, y] = packed; } - }); - } + + TColor packed = default(TColor); + packed.PackFromVector4(destination); + targetPixels[x, y] = packed; + } + }); } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs index 17d7e2918..aa4940192 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs @@ -44,60 +44,61 @@ namespace ImageSharp.Processing.Processors int maxY = endY - 1; int maxX = endX - 1; - TColor[] target = new TColor[source.Width * source.Height]; - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(source.Width, source.Height)) + using (PixelAccessor targetPixels = new PixelAccessor(source.Width, source.Height)) { - Parallel.For( - startY, - endY, - this.ParallelOptions, - y => + using (PixelAccessor sourcePixels = source.Lock()) { - for (int x = startX; x < endX; x++) + Parallel.For( + startY, + endY, + this.ParallelOptions, + y => { - float rX = 0; - float gX = 0; - float bX = 0; - - // Apply each matrix multiplier to the color components for each pixel. - for (int fy = 0; fy < kernelLength; fy++) + for (int x = startX; x < endX; x++) { - int fyr = fy - radius; - int offsetY = y + fyr; - - offsetY = offsetY.Clamp(0, maxY); + float rX = 0; + float gX = 0; + float bX = 0; - for (int fx = 0; fx < kernelLength; fx++) + // Apply each matrix multiplier to the color components for each pixel. + for (int fy = 0; fy < kernelLength; fy++) { - int fxr = fx - radius; - int offsetX = x + fxr; + int fyr = fy - radius; + int offsetY = y + fyr; + + offsetY = offsetY.Clamp(0, maxY); + + for (int fx = 0; fx < kernelLength; fx++) + { + int fxr = fx - radius; + int offsetX = x + fxr; - offsetX = offsetX.Clamp(0, maxX); + offsetX = offsetX.Clamp(0, maxX); - Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); - float r = currentColor.X; - float g = currentColor.Y; - float b = currentColor.Z; + Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); + float r = currentColor.X; + float g = currentColor.Y; + float b = currentColor.Z; - rX += kernelX[fy][fx] * r; - gX += kernelX[fy][fx] * g; - bX += kernelX[fy][fx] * b; + rX += kernelX[fy][fx] * r; + gX += kernelX[fy][fx] * g; + bX += kernelX[fy][fx] * b; + } } - } - float red = rX; - float green = gX; - float blue = bX; + float red = rX; + float green = gX; + float blue = bX; - TColor packed = default(TColor); - packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W)); - targetPixels[x, y] = packed; - } - }); - } + TColor packed = default(TColor); + packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W)); + targetPixels[x, y] = packed; + } + }); + } - source.SetPixels(source.Width, source.Height, target); + source.SwapPixelsBuffers(targetPixels); + } } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs index 5a1487761..1a88dbe34 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs @@ -75,64 +75,62 @@ namespace ImageSharp.Processing.Processors int minY = Math.Max(0, startY); int maxY = Math.Min(source.Height, endY); - // First run. - ImageBase target = new Image(source.Width, source.Height); - target.ClonePixels(source.Width, source.Height, source.Pixels); - new ConvolutionProcessor(kernels[0]).Apply(target, sourceRectangle); - - if (kernels.Length == 1) + // we need a clean copy for each pass to start from + using (ImageBase cleanCopy = new Image(source)) { - return; - } + new ConvolutionProcessor(kernels[0]).Apply(source, sourceRectangle); - int shiftY = startY; - int shiftX = startX; - - // Reset offset if necessary. - if (minX > 0) - { - shiftX = 0; - } + if (kernels.Length == 1) + { + return; + } - if (minY > 0) - { - shiftY = 0; - } + int shiftY = startY; + int shiftX = startX; - // Additional runs. - // ReSharper disable once ForCanBeConvertedToForeach - for (int i = 1; i < kernels.Length; i++) - { - // Create a clone for each pass and copy the offset pixels across. - ImageBase pass = new Image(source.Width, source.Height); - pass.ClonePixels(source.Width, source.Height, source.Pixels); + // Reset offset if necessary. + if (minX > 0) + { + shiftX = 0; + } - new ConvolutionProcessor(kernels[i]).Apply(pass, sourceRectangle); + if (minY > 0) + { + shiftY = 0; + } - using (PixelAccessor passPixels = pass.Lock()) - using (PixelAccessor targetPixels = target.Lock()) + // Additional runs. + // ReSharper disable once ForCanBeConvertedToForeach + for (int i = 1; i < kernels.Length; i++) { - Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => + using (ImageBase pass = new Image(cleanCopy)) + { + new ConvolutionProcessor(kernels[i]).Apply(pass, sourceRectangle); + + using (PixelAccessor passPixels = pass.Lock()) + using (PixelAccessor targetPixels = source.Lock()) { - int offsetY = y - shiftY; - for (int x = minX; x < maxX; x++) - { - int offsetX = x - shiftX; - - // Grab the max components of the two pixels - TColor packed = default(TColor); - packed.PackFromVector4(Vector4.Max(passPixels[offsetX, offsetY].ToVector4(), targetPixels[offsetX, offsetY].ToVector4())); - targetPixels[offsetX, offsetY] = packed; - } - }); + Parallel.For( + minY, + maxY, + this.ParallelOptions, + y => + { + int offsetY = y - shiftY; + for (int x = minX; x < maxX; x++) + { + int offsetX = x - shiftX; + + // Grab the max components of the two pixels + TColor packed = default(TColor); + packed.PackFromVector4(Vector4.Max(passPixels[offsetX, offsetY].ToVector4(), targetPixels[offsetX, offsetY].ToVector4())); + targetPixels[offsetX, offsetY] = packed; + } + }); + } + } } } - - source.SetPixels(source.Width, source.Height, target.Pixels); } /// diff --git a/src/ImageSharp.Processing/Processors/Effects/OilPaintingProcessor.cs b/src/ImageSharp.Processing/Processors/Effects/OilPaintingProcessor.cs index 8d2340256..5c16af2f7 100644 --- a/src/ImageSharp.Processing/Processors/Effects/OilPaintingProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Effects/OilPaintingProcessor.cs @@ -67,91 +67,92 @@ namespace ImageSharp.Processing.Processors startX = 0; } - TColor[] target = PixelPool.RentPixels(source.Width * source.Height); - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(source.Width, source.Height)) + using (PixelAccessor targetPixels = new PixelAccessor(source.Width, source.Height)) { - Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => - { - for (int x = startX; x < endX; x++) + using (PixelAccessor sourcePixels = source.Lock()) + { + Parallel.For( + minY, + maxY, + this.ParallelOptions, + y => { - int maxIntensity = 0; - int maxIndex = 0; - - int[] intensityBin = new int[levels]; - float[] redBin = new float[levels]; - float[] blueBin = new float[levels]; - float[] greenBin = new float[levels]; - - for (int fy = 0; fy <= radius; fy++) + for (int x = startX; x < endX; x++) { - int fyr = fy - radius; - int offsetY = y + fyr; - - // Skip the current row - if (offsetY < minY) - { - continue; - } + int maxIntensity = 0; + int maxIndex = 0; - // Outwith the current bounds so break. - if (offsetY >= maxY) - { - break; - } + int[] intensityBin = new int[levels]; + float[] redBin = new float[levels]; + float[] blueBin = new float[levels]; + float[] greenBin = new float[levels]; - for (int fx = 0; fx <= radius; fx++) + for (int fy = 0; fy <= radius; fy++) { - int fxr = fx - radius; - int offsetX = x + fxr; + int fyr = fy - radius; + int offsetY = y + fyr; - // Skip the column - if (offsetX < 0) + // Skip the current row + if (offsetY < minY) { continue; } - if (offsetX < maxX) + // Outwith the current bounds so break. + if (offsetY >= maxY) { - // ReSharper disable once AccessToDisposedClosure - Vector4 color = sourcePixels[offsetX, offsetY].ToVector4(); - - float sourceRed = color.X; - float sourceBlue = color.Z; - float sourceGreen = color.Y; + break; + } - int currentIntensity = (int)Math.Round((sourceBlue + sourceGreen + sourceRed) / 3.0 * (levels - 1)); + for (int fx = 0; fx <= radius; fx++) + { + int fxr = fx - radius; + int offsetX = x + fxr; - intensityBin[currentIntensity] += 1; - blueBin[currentIntensity] += sourceBlue; - greenBin[currentIntensity] += sourceGreen; - redBin[currentIntensity] += sourceRed; + // Skip the column + if (offsetX < 0) + { + continue; + } - if (intensityBin[currentIntensity] > maxIntensity) + if (offsetX < maxX) { - maxIntensity = intensityBin[currentIntensity]; - maxIndex = currentIntensity; + // ReSharper disable once AccessToDisposedClosure + Vector4 color = sourcePixels[offsetX, offsetY].ToVector4(); + + float sourceRed = color.X; + float sourceBlue = color.Z; + float sourceGreen = color.Y; + + int currentIntensity = (int)Math.Round((sourceBlue + sourceGreen + sourceRed) / 3.0 * (levels - 1)); + + intensityBin[currentIntensity] += 1; + blueBin[currentIntensity] += sourceBlue; + greenBin[currentIntensity] += sourceGreen; + redBin[currentIntensity] += sourceRed; + + if (intensityBin[currentIntensity] > maxIntensity) + { + maxIntensity = intensityBin[currentIntensity]; + maxIndex = currentIntensity; + } } } - } - float red = Math.Abs(redBin[maxIndex] / maxIntensity); - float green = Math.Abs(greenBin[maxIndex] / maxIntensity); - float blue = Math.Abs(blueBin[maxIndex] / maxIntensity); + float red = Math.Abs(redBin[maxIndex] / maxIntensity); + float green = Math.Abs(greenBin[maxIndex] / maxIntensity); + float blue = Math.Abs(blueBin[maxIndex] / maxIntensity); - TColor packed = default(TColor); - packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W)); - targetPixels[x, y] = packed; + TColor packed = default(TColor); + packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W)); + targetPixels[x, y] = packed; + } } - } - }); - } + }); + } - source.SetPixels(source.Width, source.Height, target); + source.SwapPixelsBuffers(targetPixels); + } } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Effects/PixelateProcessor.cs b/src/ImageSharp.Processing/Processors/Effects/PixelateProcessor.cs index d44858061..c197ce356 100644 --- a/src/ImageSharp.Processing/Processors/Effects/PixelateProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Effects/PixelateProcessor.cs @@ -63,51 +63,52 @@ namespace ImageSharp.Processing.Processors // Get the range on the y-plane to choose from. IEnumerable range = EnumerableExtensions.SteppedRange(minY, i => i < maxY, size); - TColor[] target = PixelPool.RentPixels(source.Width * source.Height); - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(source.Width, source.Height)) + using (PixelAccessor targetPixels = new PixelAccessor(source.Width, source.Height)) { - Parallel.ForEach( - range, - this.ParallelOptions, - y => - { - int offsetY = y - startY; - int offsetPy = offset; - - for (int x = minX; x < maxX; x += size) + using (PixelAccessor sourcePixels = source.Lock()) + { + Parallel.ForEach( + range, + this.ParallelOptions, + y => { - int offsetX = x - startX; - int offsetPx = offset; + int offsetY = y - startY; + int offsetPy = offset; - // Make sure that the offset is within the boundary of the image. - while (offsetY + offsetPy >= maxY) + for (int x = minX; x < maxX; x += size) { - offsetPy--; - } + int offsetX = x - startX; + int offsetPx = offset; - while (x + offsetPx >= maxX) - { - offsetPx--; - } + // Make sure that the offset is within the boundary of the image. + while (offsetY + offsetPy >= maxY) + { + offsetPy--; + } - // Get the pixel color in the centre of the soon to be pixelated area. - // ReSharper disable AccessToDisposedClosure - TColor pixel = sourcePixels[offsetX + offsetPx, offsetY + offsetPy]; + while (x + offsetPx >= maxX) + { + offsetPx--; + } - // For each pixel in the pixelate size, set it to the centre color. - for (int l = offsetY; l < offsetY + size && l < maxY; l++) - { - for (int k = offsetX; k < offsetX + size && k < maxX; k++) + // Get the pixel color in the centre of the soon to be pixelated area. + // ReSharper disable AccessToDisposedClosure + TColor pixel = sourcePixels[offsetX + offsetPx, offsetY + offsetPy]; + + // For each pixel in the pixelate size, set it to the centre color. + for (int l = offsetY; l < offsetY + size && l < maxY; l++) { - targetPixels[k, l] = pixel; + for (int k = offsetX; k < offsetX + size && k < maxX; k++) + { + targetPixels[k, l] = pixel; + } } } - } - }); + }); - source.SetPixels(source.Width, source.Height, target); + source.SwapPixelsBuffers(targetPixels); + } } } } diff --git a/src/ImageSharp.Processing/Processors/Transforms/CompandingResizeProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/CompandingResizeProcessor.cs index d9e4f6675..ac8a52321 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/CompandingResizeProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Transforms/CompandingResizeProcessor.cs @@ -66,19 +66,15 @@ namespace ImageSharp.Processing.Processors int minY = Math.Max(0, startY); int maxY = Math.Min(height, endY); - TColor[] firstPass = null; - - try + if (this.Sampler is NearestNeighborResampler) { - TColor[] target = PixelPool.RentPixels(width * height); - if (this.Sampler is NearestNeighborResampler) - { - // Scaling factors - float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width; - float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height; + // Scaling factors + float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width; + float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height; + using (PixelAccessor targetPixels = new PixelAccessor(width, height)) + { using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(width, height)) { Parallel.For( minY, @@ -98,18 +94,19 @@ namespace ImageSharp.Processing.Processors } // Break out now. - source.SetPixels(width, height, target); + source.SwapPixelsBuffers(targetPixels); return; } + } - // Interpolate the image using the calculated weights. - // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm - // First process the columns. Since we are not using multiple threads startY and endY - // are the upper and lower bounds of the source rectangle. - firstPass = PixelPool.RentPixels(width * source.Height); + // Interpolate the image using the calculated weights. + // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm + // First process the columns. Since we are not using multiple threads startY and endY + // are the upper and lower bounds of the source rectangle. + using (PixelAccessor targetPixels = new PixelAccessor(width, height)) + { using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor firstPassPixels = firstPass.Lock(width, source.Height)) - using (PixelAccessor targetPixels = target.Lock(width, height)) + using (PixelAccessor firstPassPixels = new PixelAccessor(width, source.Height)) { Parallel.For( 0, @@ -165,12 +162,7 @@ namespace ImageSharp.Processing.Processors }); } - source.SetPixels(width, height, target); - } - finally - { - // We don't return target or source pixels as they are handled in the image itself. - PixelPool.ReturnPixels(firstPass); + source.SwapPixelsBuffers(targetPixels); } } } diff --git a/src/ImageSharp.Processing/Processors/Transforms/CropProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/CropProcessor.cs index 31bd08090..bdfbc496c 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/CropProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Transforms/CropProcessor.cs @@ -42,25 +42,25 @@ namespace ImageSharp.Processing.Processors int minX = Math.Max(this.CropRectangle.X, sourceRectangle.X); int maxX = Math.Min(this.CropRectangle.Right, sourceRectangle.Right); - TColor[] target = PixelPool.RentPixels(this.CropRectangle.Width * this.CropRectangle.Height); - - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(this.CropRectangle.Width, this.CropRectangle.Height)) + using (PixelAccessor targetPixels = new PixelAccessor(this.CropRectangle.Width, this.CropRectangle.Height)) { - Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => - { - for (int x = minX; x < maxX; x++) + using (PixelAccessor sourcePixels = source.Lock()) + { + Parallel.For( + minY, + maxY, + this.ParallelOptions, + y => { - targetPixels[x - minX, y - minY] = sourcePixels[x, y]; - } - }); - } + for (int x = minX; x < maxX; x++) + { + targetPixels[x - minX, y - minY] = sourcePixels[x, y]; + } + }); + } - source.SetPixels(this.CropRectangle.Width, this.CropRectangle.Height, target); + source.SwapPixelsBuffers(targetPixels); + } } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Transforms/EntropyCropProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/EntropyCropProcessor.cs index e0c6e9b92..98297eed9 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/EntropyCropProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Transforms/EntropyCropProcessor.cs @@ -36,24 +36,24 @@ namespace ImageSharp.Processing.Processors /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - ImageBase temp = new Image(source.Width, source.Height); - temp.ClonePixels(source.Width, source.Height, source.Pixels); + using (ImageBase temp = new Image(source)) + { + // Detect the edges. + new SobelProcessor().Apply(temp, sourceRectangle); - // Detect the edges. - new SobelProcessor().Apply(temp, sourceRectangle); + // Apply threshold binarization filter. + new BinaryThresholdProcessor(this.Value).Apply(temp, sourceRectangle); - // Apply threshold binarization filter. - new BinaryThresholdProcessor(this.Value).Apply(temp, sourceRectangle); + // Search for the first white pixels + Rectangle rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0); - // Search for the first white pixels - Rectangle rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0); + if (rectangle == sourceRectangle) + { + return; + } - if (rectangle == sourceRectangle) - { - return; + new CropProcessor(rectangle).Apply(source, sourceRectangle); } - - new CropProcessor(rectangle).Apply(source, sourceRectangle); } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Transforms/FlipProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/FlipProcessor.cs index 374d54fa2..ad375ce0f 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/FlipProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Transforms/FlipProcessor.cs @@ -55,27 +55,27 @@ namespace ImageSharp.Processing.Processors int height = source.Height; int halfHeight = (int)Math.Ceiling(source.Height * .5F); - TColor[] target = PixelPool.RentPixels(width * height); - - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(width, height)) + using (PixelAccessor targetPixels = new PixelAccessor(width, height)) { - Parallel.For( - 0, - halfHeight, - this.ParallelOptions, - y => - { - for (int x = 0; x < width; x++) + using (PixelAccessor sourcePixels = source.Lock()) + { + Parallel.For( + 0, + halfHeight, + this.ParallelOptions, + y => { - int newY = height - y - 1; - targetPixels[x, y] = sourcePixels[x, newY]; - targetPixels[x, newY] = sourcePixels[x, y]; - } - }); - } + for (int x = 0; x < width; x++) + { + int newY = height - y - 1; + targetPixels[x, y] = sourcePixels[x, newY]; + targetPixels[x, newY] = sourcePixels[x, y]; + } + }); + } - source.SetPixels(width, height, target); + source.SwapPixelsBuffers(targetPixels); + } } /// @@ -89,27 +89,27 @@ namespace ImageSharp.Processing.Processors int height = source.Height; int halfWidth = (int)Math.Ceiling(width * .5F); - TColor[] target = PixelPool.RentPixels(width * height); - - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(width, height)) + using (PixelAccessor targetPixels = new PixelAccessor(width, height)) { - Parallel.For( - 0, - height, - this.ParallelOptions, - y => - { - for (int x = 0; x < halfWidth; x++) + using (PixelAccessor sourcePixels = source.Lock()) + { + Parallel.For( + 0, + height, + this.ParallelOptions, + y => { - int newX = width - x - 1; - targetPixels[x, y] = sourcePixels[newX, y]; - targetPixels[newX, y] = sourcePixels[x, y]; - } - }); - } + for (int x = 0; x < halfWidth; x++) + { + int newX = width - x - 1; + targetPixels[x, y] = sourcePixels[newX, y]; + targetPixels[newX, y] = sourcePixels[x, y]; + } + }); + } - source.SetPixels(width, height, target); + source.SwapPixelsBuffers(targetPixels); + } } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/ResizeProcessor.cs index 108391713..f5d630808 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Transforms/ResizeProcessor.cs @@ -65,19 +65,15 @@ namespace ImageSharp.Processing.Processors int minY = Math.Max(0, startY); int maxY = Math.Min(height, endY); - TColor[] firstPass = null; - - try + if (this.Sampler is NearestNeighborResampler) { - TColor[] target = PixelPool.RentPixels(width * height); - if (this.Sampler is NearestNeighborResampler) - { - // Scaling factors - float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width; - float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height; + // Scaling factors + float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width; + float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height; + using (PixelAccessor targetPixels = new PixelAccessor(width, height)) + { using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(width, height)) { Parallel.For( minY, @@ -97,18 +93,19 @@ namespace ImageSharp.Processing.Processors } // Break out now. - source.SetPixels(width, height, target); + source.SwapPixelsBuffers(targetPixels); return; } + } - // Interpolate the image using the calculated weights. - // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm - // First process the columns. Since we are not using multiple threads startY and endY - // are the upper and lower bounds of the source rectangle. - firstPass = PixelPool.RentPixels(width * source.Height); + // Interpolate the image using the calculated weights. + // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm + // First process the columns. Since we are not using multiple threads startY and endY + // are the upper and lower bounds of the source rectangle. + using (PixelAccessor targetPixels = new PixelAccessor(width, height)) + { using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor firstPassPixels = firstPass.Lock(width, source.Height)) - using (PixelAccessor targetPixels = target.Lock(width, height)) + using (PixelAccessor firstPassPixels = new PixelAccessor(width, source.Height)) { Parallel.For( 0, @@ -164,12 +161,7 @@ namespace ImageSharp.Processing.Processors }); } - source.SetPixels(width, height, target); - } - finally - { - // We don't return target or source pixels as they are handled in the image itself. - PixelPool.ReturnPixels(firstPass); + source.SwapPixelsBuffers(targetPixels); } } } diff --git a/src/ImageSharp.Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/RotateProcessor.cs index 313542adc..a5a762b91 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Transforms/RotateProcessor.cs @@ -42,29 +42,30 @@ namespace ImageSharp.Processing.Processors int height = this.CanvasRectangle.Height; int width = this.CanvasRectangle.Width; Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix); - TColor[] target = PixelPool.RentPixels(width * height); - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(width, height)) + using (PixelAccessor targetPixels = new PixelAccessor(width, height)) { - Parallel.For( - 0, - height, - this.ParallelOptions, - y => - { - for (int x = 0; x < width; x++) + using (PixelAccessor sourcePixels = source.Lock()) + { + Parallel.For( + 0, + height, + this.ParallelOptions, + y => { - Point transformedPoint = Point.Rotate(new Point(x, y), matrix); - if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y)) + for (int x = 0; x < width; x++) { - targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y]; + Point transformedPoint = Point.Rotate(new Point(x, y), matrix); + if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y)) + { + targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y]; + } } - } - }); - } + }); + } - source.SetPixels(width, height, target); + source.SwapPixelsBuffers(targetPixels); + } } /// @@ -124,28 +125,29 @@ namespace ImageSharp.Processing.Processors { int width = source.Width; int height = source.Height; - TColor[] target = PixelPool.RentPixels(width * height); - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(height, width)) + using (PixelAccessor targetPixels = new PixelAccessor(height, width)) { - Parallel.For( - 0, - height, - this.ParallelOptions, - y => - { - for (int x = 0; x < width; x++) + using (PixelAccessor sourcePixels = source.Lock()) + { + Parallel.For( + 0, + height, + this.ParallelOptions, + y => { - int newX = height - y - 1; - newX = height - newX - 1; - int newY = width - x - 1; - targetPixels[newX, newY] = sourcePixels[x, y]; - } - }); - } + for (int x = 0; x < width; x++) + { + int newX = height - y - 1; + newX = height - newX - 1; + int newY = width - x - 1; + targetPixels[newX, newY] = sourcePixels[x, y]; + } + }); + } - source.SetPixels(height, width, target); + source.SwapPixelsBuffers(targetPixels); + } } /// @@ -156,27 +158,28 @@ namespace ImageSharp.Processing.Processors { int width = source.Width; int height = source.Height; - TColor[] target = PixelPool.RentPixels(width * height); - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(width, height)) + using (PixelAccessor targetPixels = new PixelAccessor(width, height)) { - Parallel.For( - 0, - height, - this.ParallelOptions, - y => - { - for (int x = 0; x < width; x++) + using (PixelAccessor sourcePixels = source.Lock()) + { + Parallel.For( + 0, + height, + this.ParallelOptions, + y => { - int newX = width - x - 1; - int newY = height - y - 1; - targetPixels[newX, newY] = sourcePixels[x, y]; - } - }); - } + for (int x = 0; x < width; x++) + { + int newX = width - x - 1; + int newY = height - y - 1; + targetPixels[newX, newY] = sourcePixels[x, y]; + } + }); + } - source.SetPixels(width, height, target); + source.SwapPixelsBuffers(targetPixels); + } } /// @@ -187,26 +190,27 @@ namespace ImageSharp.Processing.Processors { int width = source.Width; int height = source.Height; - TColor[] target = PixelPool.RentPixels(width * height); - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(height, width)) + using (PixelAccessor targetPixels = new PixelAccessor(height, width)) { - Parallel.For( - 0, - height, - this.ParallelOptions, - y => - { - for (int x = 0; x < width; x++) + using (PixelAccessor sourcePixels = source.Lock()) + { + Parallel.For( + 0, + height, + this.ParallelOptions, + y => { - int newX = height - y - 1; - targetPixels[newX, x] = sourcePixels[x, y]; - } - }); - } + for (int x = 0; x < width; x++) + { + int newX = height - y - 1; + targetPixels[newX, x] = sourcePixels[x, y]; + } + }); + } - source.SetPixels(height, width, target); + source.SwapPixelsBuffers(targetPixels); + } } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/SkewProcessor.cs index d2d2a129d..4daa46491 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/SkewProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Transforms/SkewProcessor.cs @@ -42,29 +42,30 @@ namespace ImageSharp.Processing.Processors int height = this.CanvasRectangle.Height; int width = this.CanvasRectangle.Width; Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix); - TColor[] target = PixelPool.RentPixels(width * height); - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock(width, height)) + using (PixelAccessor targetPixels = new PixelAccessor(width, height)) { - Parallel.For( - 0, - height, - this.ParallelOptions, - y => - { - for (int x = 0; x < width; x++) + using (PixelAccessor sourcePixels = source.Lock()) + { + Parallel.For( + 0, + height, + this.ParallelOptions, + y => { - Point transformedPoint = Point.Skew(new Point(x, y), matrix); - if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y)) + for (int x = 0; x < width; x++) { - targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y]; + Point transformedPoint = Point.Skew(new Point(x, y), matrix); + if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y)) + { + targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y]; + } } - } - }); - } + }); + } - source.SetPixels(width, height, target); + source.SwapPixelsBuffers(targetPixels); + } } /// diff --git a/src/ImageSharp.Processing/project.json b/src/ImageSharp.Processing/project.json index 2ff224fa4..b375ca3a0 100644 --- a/src/ImageSharp.Processing/project.json +++ b/src/ImageSharp.Processing/project.json @@ -39,8 +39,7 @@ }, "dependencies": { "ImageSharp": { - "target": "project", - "version": "1.0.0-alpha1" + "target": "project" }, "StyleCop.Analyzers": { "version": "1.1.0-beta001", diff --git a/src/ImageSharp/Image/IImageBase{TColor}.cs b/src/ImageSharp/Image/IImageBase{TColor}.cs index 39f3fba67..66746c993 100644 --- a/src/ImageSharp/Image/IImageBase{TColor}.cs +++ b/src/ImageSharp/Image/IImageBase{TColor}.cs @@ -31,35 +31,6 @@ namespace ImageSharp /// void InitPixels(int width, int height); - /// - /// Sets the pixel array of the image to the given value. - /// - /// The new width of the image. Must be greater than zero. - /// The new height of the image. Must be greater than zero. - /// The array with pixels. Must be a multiple of the width and height. - /// - /// Thrown if either or are less than or equal to 0. - /// - /// - /// Thrown if the length is not equal to Width * Height. - /// - void SetPixels(int width, int height, TColor[] pixels); - - /// - /// Sets the pixel array of the image to the given value, creating a copy of - /// the original pixels. - /// - /// The new width of the image. Must be greater than zero. - /// The new height of the image. Must be greater than zero. - /// The array with pixels. Must be a multiple of four times the width and height. - /// - /// Thrown if either or are less than or equal to 0. - /// - /// - /// Thrown if the length is not equal to Width * Height. - /// - void ClonePixels(int width, int height, TColor[] pixels); - /// /// Locks the image providing access to the pixels. /// diff --git a/src/ImageSharp/Image/ImageBase{TColor}.cs b/src/ImageSharp/Image/ImageBase{TColor}.cs index 02bab249b..0cedb7aaa 100644 --- a/src/ImageSharp/Image/ImageBase{TColor}.cs +++ b/src/ImageSharp/Image/ImageBase{TColor}.cs @@ -146,49 +146,28 @@ namespace ImageSharp } /// - public void SetPixels(int width, int height, TColor[] pixels) + public virtual PixelAccessor Lock() { - Guard.MustBeGreaterThan(width, 0, nameof(width)); - Guard.MustBeGreaterThan(height, 0, nameof(height)); - Guard.NotNull(pixels, nameof(pixels)); - - if (!(pixels.Length >= width * height)) - { - throw new ArgumentException($"Pixel array must have the length of at least {width * height}."); - } - - this.Width = width; - this.Height = height; - - this.ReturnPixels(); - this.pixelBuffer = pixels; + return new PixelAccessor(this); } - /// - public void ClonePixels(int width, int height, TColor[] pixels) + /// + /// Switches the buffers used by the image and the PixelAccessor meaning that the Image will "own" the buffer from the PixelAccessor and the PixelAccessor will now own the Images buffer. + /// + /// The pixel source. + internal void SwapPixelsBuffers(PixelAccessor pixelSource) { - Guard.MustBeGreaterThan(width, 0, nameof(width)); - Guard.MustBeGreaterThan(height, 0, nameof(height)); - Guard.NotNull(pixels, nameof(pixels)); - - if (!(pixels.Length >= width * height)) - { - throw new ArgumentException($"Pixel array must have the length of at least {width * height}."); - } - - this.Width = width; - this.Height = height; + Guard.NotNull(pixelSource, nameof(pixelSource)); + Guard.IsTrue(pixelSource.PooledMemory, nameof(pixelSource.PooledMemory), "pixelSource must be using pooled memory"); - // Copy the pixels. TODO: use Unsafe.Copy. - this.ReturnPixels(); - this.RentPixels(); - Array.Copy(pixels, this.pixelBuffer, width * height); - } + int newWidth = pixelSource.Width; + int newHeight = pixelSource.Height; - /// - public virtual PixelAccessor Lock() - { - return new PixelAccessor(this); + // push my memory into the accessor (which in turn unpins the old puffer ready for the images use) + TColor[] newPixels = pixelSource.ReturnCurrentPixelsAndReplaceThemInternally(this.Width, this.Height, this.pixelBuffer, true); + this.Width = newWidth; + this.Height = newHeight; + this.pixelBuffer = newPixels; } /// diff --git a/src/ImageSharp/Image/PixelAccessor{TColor}.cs b/src/ImageSharp/Image/PixelAccessor{TColor}.cs index 60bf8de78..58cff55c8 100644 --- a/src/ImageSharp/Image/PixelAccessor{TColor}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TColor}.cs @@ -44,6 +44,11 @@ namespace ImageSharp /// private bool isDisposed; + /// + /// The pixels data + /// + private TColor[] pixels; + /// /// Initializes a new instance of the class. /// @@ -54,13 +59,7 @@ namespace ImageSharp Guard.MustBeGreaterThan(image.Width, 0, "image width"); Guard.MustBeGreaterThan(image.Height, 0, "image height"); - this.Width = image.Width; - this.Height = image.Height; - this.pixelsHandle = GCHandle.Alloc(image.Pixels, GCHandleType.Pinned); - this.dataPointer = this.pixelsHandle.AddrOfPinnedObject(); - this.pixelsBase = (byte*)this.dataPointer.ToPointer(); - this.PixelSize = Unsafe.SizeOf(); - this.RowStride = this.Width * this.PixelSize; + this.SetPixelBufferUnsafe(image.Width, image.Height, image.Pixels, false); this.ParallelOptions = image.Configuration.ParallelOptions; } @@ -71,6 +70,28 @@ namespace ImageSharp /// The height of the image represented by the pixel buffer. /// The pixel buffer. public PixelAccessor(int width, int height, TColor[] pixels) + : this(width, height, pixels, false) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Gets the width of the image represented by the pixel buffer. + /// The height of the image represented by the pixel buffer. + public PixelAccessor(int width, int height) + : this(width, height, PixelPool.RentPixels(width * height), true) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Gets the width of the image represented by the pixel buffer. + /// The height of the image represented by the pixel buffer. + /// The pixel buffer. + /// if set to true then the TColor[] is from the PixelPool{TColor} thus should be returned once disposed. + private PixelAccessor(int width, int height, TColor[] pixels, bool pooledMemory) { Guard.NotNull(pixels, nameof(pixels)); Guard.MustBeGreaterThan(width, 0, nameof(width)); @@ -81,13 +102,8 @@ namespace ImageSharp throw new ArgumentException($"Pixel array must have the length of at least {width * height}."); } - this.Width = width; - this.Height = height; - this.pixelsHandle = GCHandle.Alloc(pixels, GCHandleType.Pinned); - this.dataPointer = this.pixelsHandle.AddrOfPinnedObject(); - this.pixelsBase = (byte*)this.dataPointer.ToPointer(); - this.PixelSize = Unsafe.SizeOf(); - this.RowStride = this.Width * this.PixelSize; + this.SetPixelBufferUnsafe(width, height, pixels, pooledMemory); + this.ParallelOptions = Configuration.Default.ParallelOptions; } @@ -99,6 +115,14 @@ namespace ImageSharp this.Dispose(); } + /// + /// Gets a value indicating whether [pooled memory]. + /// + /// + /// true if [pooled memory]; otherwise, false. + /// + public bool PooledMemory { get; private set; } + /// /// Gets the pointer to the pixel buffer. /// @@ -107,22 +131,22 @@ namespace ImageSharp /// /// Gets the size of a single pixel in the number of bytes. /// - public int PixelSize { get; } + public int PixelSize { get; private set; } /// /// Gets the width of one row in the number of bytes. /// - public int RowStride { get; } + public int RowStride { get; private set; } /// /// Gets the width of the image. /// - public int Width { get; } + public int Width { get; private set; } /// /// Gets the height of the image. /// - public int Height { get; } + public int Height { get; private set; } /// /// Gets the global parallel options for processing tasks in parallel. @@ -221,13 +245,7 @@ namespace ImageSharp return; } - if (this.pixelsHandle.IsAllocated) - { - this.pixelsHandle.Free(); - } - - this.dataPointer = IntPtr.Zero; - this.pixelsBase = null; + this.UnPinPixels(); // Note disposing is done. this.isDisposed = true; @@ -238,6 +256,12 @@ namespace ImageSharp // and prevent finalization code for this object // from executing a second time. GC.SuppressFinalize(this); + + if (this.PooledMemory) + { + PixelPool.ReturnPixels(this.pixels); + this.pixels = null; + } } /// @@ -248,6 +272,22 @@ namespace ImageSharp Unsafe.InitBlock(this.pixelsBase, 0, (uint)(this.RowStride * this.Height)); } + /// + /// Sets the pixel buffer in an unsafe manor this should not be used unless you know what its doing!!! + /// + /// The width. + /// The height. + /// The pixels. + /// if set to true [pooled memory]. + /// Returns the old pixel data thats has gust been replaced. + /// If PixelAccessor.PooledMemory is true then caller is responsible for ensuring PixelPool.ReturnPixels() is called. + internal TColor[] ReturnCurrentPixelsAndReplaceThemInternally(int width, int height, TColor[] pixels, bool pooledMemory) + { + TColor[] oldPixels = this.pixels; + this.SetPixelBufferUnsafe(width, height, pixels, pooledMemory); + return oldPixels; + } + /// /// Copies the pixels to another of the same size. /// @@ -472,6 +512,54 @@ namespace ImageSharp return this.pixelsBase + (((y * this.Width) + x) * Unsafe.SizeOf()); } + /// + /// Sets the pixel buffer in an unsafe manor this should not be used unless you know what its doing!!! + /// + /// The width. + /// The height. + /// The pixels. + /// if set to true [pooled memory]. + private void SetPixelBufferUnsafe(int width, int height, TColor[] pixels, bool pooledMemory) + { + this.pixels = pixels; + this.PooledMemory = pooledMemory; + this.Width = width; + this.Height = height; + this.PinPixels(); + this.PixelSize = Unsafe.SizeOf(); + this.RowStride = this.Width * this.PixelSize; + } + + /// + /// Pins the pixels data. + /// + private void PinPixels() + { + // unpin any old pixels just incase + this.UnPinPixels(); + + this.pixelsHandle = GCHandle.Alloc(this.pixels, GCHandleType.Pinned); + this.dataPointer = this.pixelsHandle.AddrOfPinnedObject(); + this.pixelsBase = (byte*)this.dataPointer.ToPointer(); + } + + /// + /// Unpins pixels data. + /// + private void UnPinPixels() + { + if (this.pixelsBase != null) + { + if (this.pixelsHandle.IsAllocated) + { + this.pixelsHandle.Free(); + } + + this.dataPointer = IntPtr.Zero; + this.pixelsBase = null; + } + } + /// /// Copy an area of pixels to the image. /// diff --git a/src/ImageSharp/Quantizers/Quantize.cs b/src/ImageSharp/Quantizers/Quantize.cs index 7a42090c7..a03833b25 100644 --- a/src/ImageSharp/Quantizers/Quantize.cs +++ b/src/ImageSharp/Quantizers/Quantize.cs @@ -60,20 +60,26 @@ namespace ImageSharp int pixelCount = quantized.Pixels.Length; int palleteCount = quantized.Palette.Length - 1; - TColor[] pixels = new TColor[pixelCount]; - Parallel.For( - 0, - pixelCount, - source.Configuration.ParallelOptions, - i => - { - TColor color = quantized.Palette[Math.Min(palleteCount, quantized.Pixels[i])]; - pixels[i] = color; - }); + using (PixelAccessor pixels = new PixelAccessor(quantized.Width, quantized.Height)) + { + Parallel.For( + 0, + pixels.Height, + source.Configuration.ParallelOptions, + y => + { + for (var x = 0; x < pixels.Width; x++) + { + var i = x + (y * pixels.Width); + TColor color = quantized.Palette[Math.Min(palleteCount, quantized.Pixels[i])]; + pixels[x, y] = color; + } + }); - source.SetPixels(source.Width, source.Height, pixels); - return source; + source.SwapPixelsBuffers(pixels); + return source; + } } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index fc74fe928..70163187c 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -71,30 +71,34 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image srcImage = file.CreateImage()) { - Color[] pixels = new Color[image.Width * image.Height]; - Array.Copy(image.Pixels, pixels, image.Width * image.Height); - - using (FileStream output = File.OpenWrite($"{path}/Octree-{file.FileName}")) + using (Image image = new Image(srcImage)) { - image.Quantize(Quantization.Octree) - .Save(output, image.CurrentImageFormat); + using (FileStream output = File.OpenWrite($"{path}/Octree-{file.FileName}")) + { + image.Quantize(Quantization.Octree) + .Save(output, image.CurrentImageFormat); + } } - image.SetPixels(image.Width, image.Height, pixels); - using (FileStream output = File.OpenWrite($"{path}/Wu-{file.FileName}")) + using (Image image = new Image(srcImage)) { - image.Quantize(Quantization.Wu) - .Save(output, image.CurrentImageFormat); + using (FileStream output = File.OpenWrite($"{path}/Wu-{file.FileName}")) + { + image.Quantize(Quantization.Wu) + .Save(output, image.CurrentImageFormat); + } } - image.SetPixels(image.Width, image.Height, pixels); - using (FileStream output = File.OpenWrite($"{path}/Palette-{file.FileName}")) + using (Image image = new Image(srcImage)) { - image.Quantize(Quantization.Palette) - .Save(output, image.CurrentImageFormat); + using (FileStream output = File.OpenWrite($"{path}/Wu-{file.FileName}")) + { + image.Quantize(Quantization.Palette) + .Save(output, image.CurrentImageFormat); + } } } } From b997f79455fad0db0e4304dfb5995f264c3dc87c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 2 Feb 2017 09:47:55 +1100 Subject: [PATCH 008/142] Clean up comments --- src/ImageSharp/Image/ImageBase{TColor}.cs | 2 +- src/ImageSharp/Image/PixelAccessor{TColor}.cs | 39 +++++++++---------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/ImageSharp/Image/ImageBase{TColor}.cs b/src/ImageSharp/Image/ImageBase{TColor}.cs index 0cedb7aaa..e6c14f0e9 100644 --- a/src/ImageSharp/Image/ImageBase{TColor}.cs +++ b/src/ImageSharp/Image/ImageBase{TColor}.cs @@ -163,7 +163,7 @@ namespace ImageSharp int newWidth = pixelSource.Width; int newHeight = pixelSource.Height; - // push my memory into the accessor (which in turn unpins the old puffer ready for the images use) + // Push my memory into the accessor (which in turn unpins the old puffer ready for the images use) TColor[] newPixels = pixelSource.ReturnCurrentPixelsAndReplaceThemInternally(this.Width, this.Height, this.pixelBuffer, true); this.Width = newWidth; this.Height = newHeight; diff --git a/src/ImageSharp/Image/PixelAccessor{TColor}.cs b/src/ImageSharp/Image/PixelAccessor{TColor}.cs index 58cff55c8..f37ba7496 100644 --- a/src/ImageSharp/Image/PixelAccessor{TColor}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TColor}.cs @@ -45,9 +45,9 @@ namespace ImageSharp private bool isDisposed; /// - /// The pixels data + /// The pixel buffer /// - private TColor[] pixels; + private TColor[] pixelBuffer; /// /// Initializes a new instance of the class. @@ -66,7 +66,7 @@ namespace ImageSharp /// /// Initializes a new instance of the class. /// - /// Gets the width of the image represented by the pixel buffer. + /// The width of the image represented by the pixel buffer. /// The height of the image represented by the pixel buffer. /// The pixel buffer. public PixelAccessor(int width, int height, TColor[] pixels) @@ -77,7 +77,7 @@ namespace ImageSharp /// /// Initializes a new instance of the class. /// - /// Gets the width of the image represented by the pixel buffer. + /// The width of the image represented by the pixel buffer. /// The height of the image represented by the pixel buffer. public PixelAccessor(int width, int height) : this(width, height, PixelPool.RentPixels(width * height), true) @@ -87,10 +87,10 @@ namespace ImageSharp /// /// Initializes a new instance of the class. /// - /// Gets the width of the image represented by the pixel buffer. + /// The width of the image represented by the pixel buffer. /// The height of the image represented by the pixel buffer. /// The pixel buffer. - /// if set to true then the TColor[] is from the PixelPool{TColor} thus should be returned once disposed. + /// if set to true then the is from the thus should be returned once disposed. private PixelAccessor(int width, int height, TColor[] pixels, bool pooledMemory) { Guard.NotNull(pixels, nameof(pixels)); @@ -116,11 +116,8 @@ namespace ImageSharp } /// - /// Gets a value indicating whether [pooled memory]. + /// Gets a value indicating whether the current pixel buffer is from a pooled source. /// - /// - /// true if [pooled memory]; otherwise, false. - /// public bool PooledMemory { get; private set; } /// @@ -259,8 +256,8 @@ namespace ImageSharp if (this.PooledMemory) { - PixelPool.ReturnPixels(this.pixels); - this.pixels = null; + PixelPool.ReturnPixels(this.pixelBuffer); + this.pixelBuffer = null; } } @@ -273,17 +270,17 @@ namespace ImageSharp } /// - /// Sets the pixel buffer in an unsafe manor this should not be used unless you know what its doing!!! + /// Sets the pixel buffer in an unsafe manner. This should not be used unless you know what its doing!!! /// /// The width. /// The height. /// The pixels. - /// if set to true [pooled memory]. + /// If set to true this indicates that the pixel buffer is from a pooled source. /// Returns the old pixel data thats has gust been replaced. - /// If PixelAccessor.PooledMemory is true then caller is responsible for ensuring PixelPool.ReturnPixels() is called. + /// If is true then caller is responsible for ensuring is called. internal TColor[] ReturnCurrentPixelsAndReplaceThemInternally(int width, int height, TColor[] pixels, bool pooledMemory) { - TColor[] oldPixels = this.pixels; + TColor[] oldPixels = this.pixelBuffer; this.SetPixelBufferUnsafe(width, height, pixels, pooledMemory); return oldPixels; } @@ -518,10 +515,10 @@ namespace ImageSharp /// The width. /// The height. /// The pixels. - /// if set to true [pooled memory]. + /// If set to true this indicates that the pixel buffer is from a pooled source. private void SetPixelBufferUnsafe(int width, int height, TColor[] pixels, bool pooledMemory) { - this.pixels = pixels; + this.pixelBuffer = pixels; this.PooledMemory = pooledMemory; this.Width = width; this.Height = height; @@ -538,7 +535,7 @@ namespace ImageSharp // unpin any old pixels just incase this.UnPinPixels(); - this.pixelsHandle = GCHandle.Alloc(this.pixels, GCHandleType.Pinned); + this.pixelsHandle = GCHandle.Alloc(this.pixelBuffer, GCHandleType.Pinned); this.dataPointer = this.pixelsHandle.AddrOfPinnedObject(); this.pixelsBase = (byte*)this.dataPointer.ToPointer(); } @@ -639,13 +636,13 @@ namespace ImageSharp int width = Math.Min(area.Width, this.Width - x); if (width < 1) { - throw new ArgumentOutOfRangeException(nameof(width), width, $"Invalid area size specified."); + throw new ArgumentOutOfRangeException(nameof(width), width, "Invalid area size specified."); } int height = Math.Min(area.Height, this.Height - y); if (height < 1) { - throw new ArgumentOutOfRangeException(nameof(height), height, $"Invalid area size specified."); + throw new ArgumentOutOfRangeException(nameof(height), height, "Invalid area size specified."); } } From 8b8ea351a2b79009db437fa1333ca68a3190826c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 2 Feb 2017 09:55:05 +1100 Subject: [PATCH 009/142] A little sanitation --- src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs | 18 +++++++++--------- src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs | 8 +++----- src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs | 1 - src/ImageSharp/Image/ImageBase{TColor}.cs | 1 - 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs b/src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs index 94f2b3a38..8abe7cbfc 100644 --- a/src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs @@ -34,7 +34,7 @@ namespace ImageSharp.Formats /// The public void Encode(ImageBase image, Stream stream, BmpBitsPerPixel bitsPerPixel) where TColor : struct, IPackedPixel, IEquatable - { + { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); @@ -119,23 +119,23 @@ namespace ImageSharp.Formats /// Writes the pixel data to the binary stream. /// /// The pixel format. - /// The containing the stream to write to. + /// The containing the stream to write to. /// /// The containing pixel data. /// private void WriteImage(EndianBinaryWriter writer, ImageBase image) where TColor : struct, IPackedPixel, IEquatable - { + { using (PixelAccessor pixels = image.Lock()) { switch (this.bmpBitsPerPixel) { case BmpBitsPerPixel.Pixel32: - this.Write32Bit(writer, pixels); + this.Write32Bit(writer, pixels); break; case BmpBitsPerPixel.Pixel24: - this.Write24Bit(writer, pixels); + this.Write24Bit(writer, pixels); break; } } @@ -145,11 +145,11 @@ namespace ImageSharp.Formats /// Writes the 32bit color palette to the stream. /// /// The pixel format. - /// The containing the stream to write to. + /// The containing the stream to write to. /// The containing pixel data. private void Write32Bit(EndianBinaryWriter writer, PixelAccessor pixels) where TColor : struct, IPackedPixel, IEquatable - { + { using (PixelArea row = new PixelArea(pixels.Width, ComponentOrder.Zyxw, this.padding)) { for (int y = pixels.Height - 1; y >= 0; y--) @@ -164,11 +164,11 @@ namespace ImageSharp.Formats /// Writes the 24bit color palette to the stream. /// /// The pixel format. - /// The containing the stream to write to. + /// The containing the stream to write to. /// The containing pixel data. private void Write24Bit(EndianBinaryWriter writer, PixelAccessor pixels) where TColor : struct, IPackedPixel, IEquatable - { + { using (PixelArea row = new PixelArea(pixels.Width, ComponentOrder.Zyx, this.padding)) { for (int y = pixels.Height - 1; y >= 0; y--) diff --git a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs index 72655a500..1fd963292 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs @@ -5,10 +5,8 @@ namespace ImageSharp.Formats { using System; - using System.Buffers; using System.IO; using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; using System.Threading.Tasks; using ImageSharp.Formats.Jpg; @@ -586,7 +584,7 @@ namespace ImageSharp.Formats byte yellow = this.ycbcrImage.CrChannel.Pixels[co + (x / scale)]; TColor packed = default(TColor); - this.PackCmyk(ref packed, cyan, magenta, yellow, x, y); + this.PackCmyk(ref packed, cyan, magenta, yellow, x, y); pixels[x, y] = packed; } }); @@ -744,7 +742,7 @@ namespace ImageSharp.Formats byte cr = this.ycbcrImage.CrChannel.Pixels[co + (x / scale)]; TColor packed = default(TColor); - this.PackYcck(ref packed, yy, cb, cr, x, y); + this.PackYcck(ref packed, yy, cb, cr, x, y); pixels[x, y] = packed; } }); @@ -1037,7 +1035,7 @@ namespace ImageSharp.Formats } this.InputProcessor.ReadFull(this.Temp, 0, remaining); - this.RestartInterval = ((int)this.Temp[0] << 8) + (int)this.Temp[1]; + this.RestartInterval = (this.Temp[0] << 8) + this.Temp[1]; } /// diff --git a/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs b/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs index a97bc4bca..984418dd3 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs @@ -8,7 +8,6 @@ namespace ImageSharp.Formats using System; using System.Buffers; using System.IO; - using System.Numerics; using System.Runtime.CompilerServices; using ImageSharp.Formats.Jpg; diff --git a/src/ImageSharp/Image/ImageBase{TColor}.cs b/src/ImageSharp/Image/ImageBase{TColor}.cs index e6c14f0e9..1b3abd360 100644 --- a/src/ImageSharp/Image/ImageBase{TColor}.cs +++ b/src/ImageSharp/Image/ImageBase{TColor}.cs @@ -6,7 +6,6 @@ namespace ImageSharp { using System; - using System.Buffers; using System.Diagnostics; /// From 2992ffe17dfc26e462f441ddb519bc14d2f15e52 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 2 Feb 2017 10:51:54 +1100 Subject: [PATCH 010/142] Fix quantizer test --- tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index 70163187c..6873717ed 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -94,7 +94,7 @@ namespace ImageSharp.Tests using (Image image = new Image(srcImage)) { - using (FileStream output = File.OpenWrite($"{path}/Wu-{file.FileName}")) + using (FileStream output = File.OpenWrite($"{path}/Palette-{file.FileName}")) { image.Quantize(Quantization.Palette) .Save(output, image.CurrentImageFormat); From 9bca8649f335a649e6482c0d25b10780f1d326bc Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 2 Feb 2017 10:55:57 +1100 Subject: [PATCH 011/142] Bump alpha --- src/ImageSharp.Drawing/project.json | 2 +- src/ImageSharp.Formats.Bmp/project.json | 2 +- src/ImageSharp.Formats.Gif/project.json | 2 +- src/ImageSharp.Formats.Jpeg/project.json | 2 +- src/ImageSharp.Formats.Png/project.json | 2 +- src/ImageSharp.Processing/project.json | 2 +- src/ImageSharp/project.json | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp.Drawing/project.json b/src/ImageSharp.Drawing/project.json index 04a560170..b974684a5 100644 --- a/src/ImageSharp.Drawing/project.json +++ b/src/ImageSharp.Drawing/project.json @@ -1,5 +1,5 @@ { - "version": "1.0.0-alpha1-*", + "version": "1.0.0-alpha2-*", "title": "ImageSharp.Drawing", "description": "A cross-platform library for the processing of image files; written in C#", "authors": [ diff --git a/src/ImageSharp.Formats.Bmp/project.json b/src/ImageSharp.Formats.Bmp/project.json index 575e414aa..e66ecaf24 100644 --- a/src/ImageSharp.Formats.Bmp/project.json +++ b/src/ImageSharp.Formats.Bmp/project.json @@ -1,5 +1,5 @@ { - "version": "1.0.0-alpha1-*", + "version": "1.0.0-alpha2-*", "title": "ImageSharp.Formats.Bmp", "description": "A cross-platform library for the processing of image files; written in C#", "authors": [ diff --git a/src/ImageSharp.Formats.Gif/project.json b/src/ImageSharp.Formats.Gif/project.json index e12d3c733..a501fe1c6 100644 --- a/src/ImageSharp.Formats.Gif/project.json +++ b/src/ImageSharp.Formats.Gif/project.json @@ -1,5 +1,5 @@ { - "version": "1.0.0-alpha1-*", + "version": "1.0.0-alpha2-*", "title": "ImageSharp.Formats.Gif", "description": "A cross-platform library for the processing of image files; written in C#", "authors": [ diff --git a/src/ImageSharp.Formats.Jpeg/project.json b/src/ImageSharp.Formats.Jpeg/project.json index de16f6c1c..c7e6f7e67 100644 --- a/src/ImageSharp.Formats.Jpeg/project.json +++ b/src/ImageSharp.Formats.Jpeg/project.json @@ -1,5 +1,5 @@ { - "version": "1.0.0-alpha1-*", + "version": "1.0.0-alpha2-*", "title": "ImageSharp.Formats.Jpeg", "description": "A cross-platform library for the processing of image files; written in C#", "authors": [ diff --git a/src/ImageSharp.Formats.Png/project.json b/src/ImageSharp.Formats.Png/project.json index eac71d8c7..bd8c2a323 100644 --- a/src/ImageSharp.Formats.Png/project.json +++ b/src/ImageSharp.Formats.Png/project.json @@ -1,5 +1,5 @@ { - "version": "1.0.0-alpha1-*", + "version": "1.0.0-alpha2-*", "title": "ImageSharp.Formats.Png", "description": "A cross-platform library for the processing of image files; written in C#", "authors": [ diff --git a/src/ImageSharp.Processing/project.json b/src/ImageSharp.Processing/project.json index b375ca3a0..cf4d2fc24 100644 --- a/src/ImageSharp.Processing/project.json +++ b/src/ImageSharp.Processing/project.json @@ -1,5 +1,5 @@ { - "version": "1.0.0-alpha1-*", + "version": "1.0.0-alpha2-*", "title": "ImageSharp.Processing", "description": "A cross-platform library for the processing of image files; written in C#", "authors": [ diff --git a/src/ImageSharp/project.json b/src/ImageSharp/project.json index 117d32090..f5f0f103b 100644 --- a/src/ImageSharp/project.json +++ b/src/ImageSharp/project.json @@ -1,5 +1,5 @@ { - "version": "1.0.0-alpha1-*", + "version": "1.0.0-alpha2-*", "title": "ImageSharp", "description": "A cross-platform library for the processing of image files; written in C#", "authors": [ From e00e936d65bf98d0076d8fb26bf3724c64e83a89 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 2 Feb 2017 22:32:34 +1100 Subject: [PATCH 012/142] Update code sample [skip ci] --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4d499fa35..9d947f227 100644 --- a/README.md +++ b/README.md @@ -75,8 +75,8 @@ Here's an example of the code required to resize an image using the default Bicu ```csharp using (FileStream stream = File.OpenRead("foo.jpg")) using (FileStream output = File.OpenWrite("bar.jpg")) +using (Image image = new Image(stream)) { - Image image = new Image(stream); image.Resize(image.Width / 2, image.Height / 2) .Grayscale() .Save(output); @@ -92,7 +92,7 @@ new BrightnessProcessor(50).Apply(sourceImage, sourceImage.Bounds); Setting individual pixel values is perfomed as follows: ```csharp -Image image = new Image(400, 400); +using (image = new Image(400, 400) using (var pixels = image.Lock()) { pixels[200, 200] = Color.White; From 7d253d81ed5d087b12df2f29ae2ea6ef4e98e151 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sun, 29 Jan 2017 15:31:15 +0000 Subject: [PATCH 013/142] migrate to third party shapes library --- NuGet.config | 1 + src/ImageSharp.Drawing/Draw.cs | 3 +- src/ImageSharp.Drawing/DrawRectangle.cs | 6 +- src/ImageSharp.Drawing/Fill.cs | 4 +- src/ImageSharp.Drawing/FillRectangle.cs | 5 +- .../Paths/BezierLineSegment.cs | 119 - src/ImageSharp.Drawing/Paths/ILineSegment.cs | 21 - src/ImageSharp.Drawing/Paths/IPath.cs | 48 - src/ImageSharp.Drawing/Paths/InternalPath.cs | 516 --- .../Paths/LinearLineSegment.cs | 55 - src/ImageSharp.Drawing/Paths/Path.cs | 51 - src/ImageSharp.Drawing/Pens/Pen{TColor}.cs | 1 - .../Pens/Processors/PenApplicator.cs | 1 - .../{Paths => Pens/Processors}/PointInfo.cs | 18 +- .../Processors/DrawPathProcessor.cs | 30 +- .../Processors/FillShapeProcessor.cs | 4 +- .../Processors/PointInfoExtensions.cs | 37 + .../Processors/RectangleExtensions.cs | 32 + .../Shapes/BezierPolygon.cs | 93 - .../Shapes/ComplexPolygon.cs | 246 -- src/ImageSharp.Drawing/Shapes/IShape.cs | 56 - .../Shapes/LinearPolygon.cs | 93 - src/ImageSharp.Drawing/Shapes/Polygon.cs | 156 - .../Shapes/PolygonClipper/Clipper.cs | 3860 ----------------- .../Shapes/PolygonClipper/ClipperException.cs | 31 - .../Shapes/PolygonClipper/Direction.cs | 31 - .../Shapes/PolygonClipper/EdgeSide.cs | 31 - .../Shapes/PolygonClipper/IntersectNode.cs | 38 - .../PolygonClipper/IntersectNodeSort.cs | 48 - .../Shapes/PolygonClipper/Join.cs | 38 - .../Shapes/PolygonClipper/LocalMinima.cs | 44 - .../Shapes/PolygonClipper/Maxima.cs | 38 - .../Shapes/PolygonClipper/OutPt.cs | 43 - .../Shapes/PolygonClipper/OutRec.cs | 64 - .../Shapes/PolygonClipper/PolyNode.cs | 179 - .../Shapes/PolygonClipper/PolyTree.cs | 81 - .../Shapes/PolygonClipper/PolyType.cs | 31 - .../Shapes/PolygonClipper/README.md | 40 - .../Shapes/PolygonClipper/Scanbeam.cs | 33 - .../Shapes/PolygonClipper/TEdge.cs | 118 - .../Shapes/RectangularPolygon.cs | 281 -- src/ImageSharp.Drawing/project.json | 1 + .../Drawing/FillRectangle.cs | 10 +- 43 files changed, 108 insertions(+), 6528 deletions(-) delete mode 100644 src/ImageSharp.Drawing/Paths/BezierLineSegment.cs delete mode 100644 src/ImageSharp.Drawing/Paths/ILineSegment.cs delete mode 100644 src/ImageSharp.Drawing/Paths/IPath.cs delete mode 100644 src/ImageSharp.Drawing/Paths/InternalPath.cs delete mode 100644 src/ImageSharp.Drawing/Paths/LinearLineSegment.cs delete mode 100644 src/ImageSharp.Drawing/Paths/Path.cs rename src/ImageSharp.Drawing/{Paths => Pens/Processors}/PointInfo.cs (54%) create mode 100644 src/ImageSharp.Drawing/Processors/PointInfoExtensions.cs create mode 100644 src/ImageSharp.Drawing/Processors/RectangleExtensions.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/BezierPolygon.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/ComplexPolygon.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/IShape.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/LinearPolygon.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/Polygon.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/Clipper.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/ClipperException.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/Direction.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/EdgeSide.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/IntersectNode.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/IntersectNodeSort.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/Join.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/LocalMinima.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/Maxima.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/OutPt.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/OutRec.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyNode.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyTree.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyType.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/README.md delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/Scanbeam.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/PolygonClipper/TEdge.cs delete mode 100644 src/ImageSharp.Drawing/Shapes/RectangularPolygon.cs diff --git a/NuGet.config b/NuGet.config index b2c967cc9..88d71e7ba 100644 --- a/NuGet.config +++ b/NuGet.config @@ -1,6 +1,7 @@  + diff --git a/src/ImageSharp.Drawing/Draw.cs b/src/ImageSharp.Drawing/Draw.cs index c10665b83..8c3dbb1b5 100644 --- a/src/ImageSharp.Drawing/Draw.cs +++ b/src/ImageSharp.Drawing/Draw.cs @@ -9,10 +9,9 @@ namespace ImageSharp using System.Numerics; using Drawing; using Drawing.Brushes; - using Drawing.Paths; using Drawing.Pens; using Drawing.Processors; - using Drawing.Shapes; + using SixLabors.Shapes; /// /// Extension methods for the type. diff --git a/src/ImageSharp.Drawing/DrawRectangle.cs b/src/ImageSharp.Drawing/DrawRectangle.cs index 38ed578b6..17d6d5f36 100644 --- a/src/ImageSharp.Drawing/DrawRectangle.cs +++ b/src/ImageSharp.Drawing/DrawRectangle.cs @@ -9,10 +9,10 @@ namespace ImageSharp using Drawing; using Drawing.Brushes; - using Drawing.Paths; using Drawing.Pens; using Drawing.Processors; - using Drawing.Shapes; + + using SixLabors.Shapes; /// /// Extension methods for the type. @@ -33,7 +33,7 @@ namespace ImageSharp public static Image DrawPolygon(this Image source, IPen pen, RectangleF shape, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(new DrawPathProcessor(pen, (IPath)new RectangularPolygon(shape), options)); + return source.Apply(new DrawPathProcessor(pen, (IPath)new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height), options)); } /// diff --git a/src/ImageSharp.Drawing/Fill.cs b/src/ImageSharp.Drawing/Fill.cs index c0f43bdd1..637950e76 100644 --- a/src/ImageSharp.Drawing/Fill.cs +++ b/src/ImageSharp.Drawing/Fill.cs @@ -9,9 +9,9 @@ namespace ImageSharp using System.Numerics; using Drawing; using Drawing.Brushes; - using Drawing.Paths; using Drawing.Processors; - using Drawing.Shapes; + + using SixLabors.Shapes; /// /// Extension methods for the type. diff --git a/src/ImageSharp.Drawing/FillRectangle.cs b/src/ImageSharp.Drawing/FillRectangle.cs index d29b58e1b..7749e7c92 100644 --- a/src/ImageSharp.Drawing/FillRectangle.cs +++ b/src/ImageSharp.Drawing/FillRectangle.cs @@ -10,7 +10,6 @@ namespace ImageSharp using Drawing; using Drawing.Brushes; using Drawing.Processors; - using Drawing.Shapes; /// /// Extension methods for the type. @@ -31,7 +30,7 @@ namespace ImageSharp public static Image Fill(this Image source, IBrush brush, RectangleF shape, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(new FillShapeProcessor(brush, new RectangularPolygon(shape), options)); + return source.Apply(new FillShapeProcessor(brush, new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height), options)); } /// @@ -45,7 +44,7 @@ namespace ImageSharp public static Image Fill(this Image source, IBrush brush, RectangleF shape) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(new FillShapeProcessor(brush, new RectangularPolygon(shape), GraphicsOptions.Default)); + return source.Apply(new FillShapeProcessor(brush, new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height), GraphicsOptions.Default)); } /// diff --git a/src/ImageSharp.Drawing/Paths/BezierLineSegment.cs b/src/ImageSharp.Drawing/Paths/BezierLineSegment.cs deleted file mode 100644 index 647f97f1e..000000000 --- a/src/ImageSharp.Drawing/Paths/BezierLineSegment.cs +++ /dev/null @@ -1,119 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Paths -{ - using System.Numerics; - - /// - /// Represents a line segment that conistst of control points that will be rendered as a cubic bezier curve - /// - /// - public class BezierLineSegment : ILineSegment - { - /// - /// The segments per curve. - /// code for this taken from - /// - private const int SegmentsPerCurve = 50; - - /// - /// The line points. - /// - private readonly Vector2[] linePoints; - - /// - /// Initializes a new instance of the class. - /// - /// The points. - public BezierLineSegment(params Vector2[] points) - { - Guard.NotNull(points, nameof(points)); - Guard.MustBeGreaterThanOrEqualTo(points.Length, 4, nameof(points)); - - this.linePoints = this.GetDrawingPoints(points); - } - - /// - /// Returns the current a simple linear path. - /// - /// - /// Returns the current as simple linear path. - /// - public Vector2[] AsSimpleLinearPath() - { - return this.linePoints; - } - - /// - /// Returns the drawing points along the line. - /// - /// The control points. - /// - /// The . - /// - private Vector2[] GetDrawingPoints(Vector2[] controlPoints) - { - // TODO we need to calculate an optimal SegmentsPerCurve value - // depending on the calcualted length of this curve - int curveCount = (controlPoints.Length - 1) / 3; - int finalPointCount = (SegmentsPerCurve * curveCount) + 1; // we have SegmentsPerCurve for each curve plus the origon point; - - Vector2[] drawingPoints = new Vector2[finalPointCount]; - - int position = 0; - int targetPoint = controlPoints.Length - 3; - for (int i = 0; i < targetPoint; i += 3) - { - Vector2 p0 = controlPoints[i]; - Vector2 p1 = controlPoints[i + 1]; - Vector2 p2 = controlPoints[i + 2]; - Vector2 p3 = controlPoints[i + 3]; - - // only do this for the first end point. When i != 0, this coincides with the end point of the previous segment, - if (i == 0) - { - drawingPoints[position++] = this.CalculateBezierPoint(0, p0, p1, p2, p3); - } - - for (int j = 1; j <= SegmentsPerCurve; j++) - { - float t = j / (float)SegmentsPerCurve; - drawingPoints[position++] = this.CalculateBezierPoint(t, p0, p1, p2, p3); - } - } - - return drawingPoints; - } - - /// - /// Calculates the bezier point along the line. - /// - /// The position within the line. - /// The p 0. - /// The p 1. - /// The p 2. - /// The p 3. - /// - /// The . - /// - private Vector2 CalculateBezierPoint(float t, Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3) - { - float u = 1 - t; - float tt = t * t; - float uu = u * u; - float uuu = uu * u; - float ttt = tt * t; - - Vector2 p = uuu * p0; // first term - - p += 3 * uu * t * p1; // second term - p += 3 * u * tt * p2; // third term - p += ttt * p3; // fourth term - - return p; - } - } -} diff --git a/src/ImageSharp.Drawing/Paths/ILineSegment.cs b/src/ImageSharp.Drawing/Paths/ILineSegment.cs deleted file mode 100644 index 380f0bf40..000000000 --- a/src/ImageSharp.Drawing/Paths/ILineSegment.cs +++ /dev/null @@ -1,21 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Paths -{ - using System.Numerics; - - /// - /// Represents a simple path segment - /// - public interface ILineSegment - { - /// - /// Converts the into a simple linear path.. - /// - /// Returns the current as simple linear path. - Vector2[] AsSimpleLinearPath(); // TODO move this over to ReadonlySpan once available - } -} diff --git a/src/ImageSharp.Drawing/Paths/IPath.cs b/src/ImageSharp.Drawing/Paths/IPath.cs deleted file mode 100644 index 278d97251..000000000 --- a/src/ImageSharp.Drawing/Paths/IPath.cs +++ /dev/null @@ -1,48 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Paths -{ - using System.Numerics; - - /// - /// Represents a logic path that can be drawn - /// - public interface IPath : ILineSegment - { - /// - /// Gets the bounds enclosing the path - /// - /// - /// The bounds. - /// - RectangleF Bounds { get; } - - /// - /// Gets a value indicating whether this instance is closed. - /// - /// - /// true if this instance is closed; otherwise, false. - /// - bool IsClosed { get; } - - /// - /// Gets the length of the path - /// - /// - /// The length. - /// - float Length { get; } - - /// - /// Calculates the distance along and away from the path for a specified point. - /// - /// The point along the path. - /// - /// Returns details about the point and its distance away from the path. - /// - PointInfo Distance(Vector2 point); - } -} diff --git a/src/ImageSharp.Drawing/Paths/InternalPath.cs b/src/ImageSharp.Drawing/Paths/InternalPath.cs deleted file mode 100644 index 36a0704cb..000000000 --- a/src/ImageSharp.Drawing/Paths/InternalPath.cs +++ /dev/null @@ -1,516 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Drawing.Paths -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - - /// - /// Internal logic for integrating linear paths. - /// - internal class InternalPath - { - /// - /// The maximum vector - /// - private static readonly Vector2 MaxVector = new Vector2(float.MaxValue); - - /// - /// The locker. - /// - private static readonly object Locker = new object(); - - /// - /// The points. - /// - private readonly Vector2[] points; - - /// - /// The closed path. - /// - private readonly bool closedPath; - - /// - /// The total distance. - /// - private readonly Lazy totalDistance; - - /// - /// The constant. - /// - private float[] constant; - - /// - /// The multiples. - /// - private float[] multiple; - - /// - /// The distances. - /// - private float[] distance; - - /// - /// The calculated. - /// - private bool calculated = false; - - /// - /// Initializes a new instance of the class. - /// - /// The segments. - /// if set to true [is closed path]. - internal InternalPath(ILineSegment[] segments, bool isClosedPath) - : this(Simplify(segments), isClosedPath) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The segment. - /// if set to true [is closed path]. - internal InternalPath(ILineSegment segment, bool isClosedPath) - : this(segment.AsSimpleLinearPath(), isClosedPath) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The points. - /// if set to true [is closed path]. - internal InternalPath(Vector2[] points, bool isClosedPath) - { - this.points = points; - this.closedPath = isClosedPath; - - float minX = this.points.Min(x => x.X); - float maxX = this.points.Max(x => x.X); - float minY = this.points.Min(x => x.Y); - float maxY = this.points.Max(x => x.Y); - - this.Bounds = new RectangleF(minX, minY, maxX - minX, maxY - minY); - this.totalDistance = new Lazy(this.CalculateLength); - } - - /// - /// Gets the bounds. - /// - /// - /// The bounds. - /// - public RectangleF Bounds - { - get; - } - - /// - /// Gets the length. - /// - /// - /// The length. - /// - public float Length => this.totalDistance.Value; - - /// - /// Gets the points. - /// - /// - /// The points. - /// - internal Vector2[] Points => this.points; - - /// - /// Calculates the distance from the path. - /// - /// The point. - /// Returns the distance from the path - public PointInfo DistanceFromPath(Vector2 point) - { - this.CalculateConstants(); - - PointInfoInternal internalInfo = default(PointInfoInternal); - internalInfo.DistanceSquared = float.MaxValue; // Set it to max so that CalculateShorterDistance can reduce it back down - - int polyCorners = this.points.Length; - - if (!this.closedPath) - { - polyCorners -= 1; - } - - int closestPoint = 0; - for (int i = 0; i < polyCorners; i++) - { - int next = i + 1; - if (this.closedPath && next == polyCorners) - { - next = 0; - } - - if (this.CalculateShorterDistance(this.points[i], this.points[next], point, ref internalInfo)) - { - closestPoint = i; - } - } - - return new PointInfo - { - DistanceAlongPath = this.distance[closestPoint] + Vector2.Distance(this.points[closestPoint], point), - DistanceFromPath = (float)Math.Sqrt(internalInfo.DistanceSquared), - SearchPoint = point, - ClosestPointOnPath = internalInfo.PointOnLine - }; - } - - /// - /// Based on a line described by and - /// populate a buffer for all points on the path that the line intersects. - /// - /// The start. - /// The end. - /// The buffer. - /// The count. - /// The offset. - /// number iof intersections hit - public int FindIntersections(Vector2 start, Vector2 end, Vector2[] buffer, int count, int offset) - { - int polyCorners = this.points.Length; - - if (!this.closedPath) - { - polyCorners -= 1; - } - - int position = 0; - for (int i = 0; i < polyCorners && count > 0; i++) - { - int next = i + 1; - if (this.closedPath && next == polyCorners) - { - next = 0; - } - - Vector2 point = FindIntersection(this.points[i], this.points[next], start, end); - if (point != MaxVector) - { - buffer[position + offset] = point; - position++; - count--; - } - } - - return position; - } - - /// - /// Determines if the specified point is inside or outside the path. - /// - /// The point. - /// Returns true if the point is inside the closed path. - public bool PointInPolygon(Vector2 point) - { - // You can only be inside a path if its "closed" - if (!this.closedPath) - { - return false; - } - - if (!this.Bounds.Contains(point.X, point.Y)) - { - return false; - } - - this.CalculateConstants(); - - Vector2[] poly = this.points; - int polyCorners = poly.Length; - - int j = polyCorners - 1; - bool oddNodes = false; - - for (int i = 0; i < polyCorners; i++) - { - if ((poly[i].Y < point.Y && poly[j].Y >= point.Y) - || (poly[j].Y < point.Y && poly[i].Y >= point.Y)) - { - oddNodes ^= (point.Y * this.multiple[i]) + this.constant[i] < point.X; - } - - j = i; - } - - return oddNodes; - } - - /// - /// Determins if the bounding box for 2 lines - /// described by and - /// and and overlap. - /// - /// The line1 start. - /// The line1 end. - /// The line2 start. - /// The line2 end. - /// Returns true it the bounding box of the 2 lines intersect - private static bool BoundingBoxesIntersect(Vector2 line1Start, Vector2 line1End, Vector2 line2Start, Vector2 line2End) - { - Vector2 topLeft1 = Vector2.Min(line1Start, line1End); - Vector2 bottomRight1 = Vector2.Max(line1Start, line1End); - - Vector2 topLeft2 = Vector2.Min(line2Start, line2End); - Vector2 bottomRight2 = Vector2.Max(line2Start, line2End); - - float left1 = topLeft1.X; - float right1 = bottomRight1.X; - float top1 = topLeft1.Y; - float bottom1 = bottomRight1.Y; - - float left2 = topLeft2.X; - float right2 = bottomRight2.X; - float top2 = topLeft2.Y; - float bottom2 = bottomRight2.Y; - - return left1 <= right2 && right1 >= left2 - && - top1 <= bottom2 && bottom1 >= top2; - } - - /// - /// Finds the point on line described by and - /// that intersects with line described by and - /// - /// The line1 start. - /// The line1 end. - /// The line2 start. - /// The line2 end. - /// - /// A describing the point that the 2 lines cross or if they do not. - /// - private static Vector2 FindIntersection(Vector2 line1Start, Vector2 line1End, Vector2 line2Start, Vector2 line2End) - { - // do bounding boxes overlap, if not then the lines can't and return fast. - if (!BoundingBoxesIntersect(line1Start, line1End, line2Start, line2End)) - { - return MaxVector; - } - - Vector2 line1Diff = line1End - line1Start; - Vector2 line2Diff = line2End - line2Start; - - Vector2 point; - if (line1Diff.X == 0) - { - float slope = line2Diff.Y / line2Diff.X; - float yinter = line2Start.Y - (slope * line2Start.X); - float y = (line1Start.X * slope) + yinter; - point = new Vector2(line1Start.X, y); - - // horizontal and vertical lines - } - else if (line2Diff.X == 0) - { - float slope = line1Diff.Y / line1Diff.X; - float yinter = line1Start.Y - (slope * line1Start.X); - float y = (line2Start.X * slope) + yinter; - point = new Vector2(line2Start.X, y); - - // horizontal and vertical lines - } - else - { - float slope1 = line1Diff.Y / line1Diff.X; - float slope2 = line2Diff.Y / line2Diff.X; - - float yinter1 = line1Start.Y - (slope1 * line1Start.X); - float yinter2 = line2Start.Y - (slope2 * line2Start.X); - - if (slope1 == slope2 && yinter1 != yinter2) - { - return MaxVector; - } - - float x = (yinter2 - yinter1) / (slope1 - slope2); - float y = (slope1 * x) + yinter1; - - point = new Vector2(x, y); - } - - if (BoundingBoxesIntersect(line1Start, line1End, point, point)) - { - return point; - } - else if (BoundingBoxesIntersect(line2Start, line2End, point, point)) - { - return point; - } - - return MaxVector; - } - - /// - /// Simplifies the collection of segments. - /// - /// The segments. - /// - /// The . - /// - private static Vector2[] Simplify(ILineSegment[] segments) - { - List simplified = new List(); - foreach (ILineSegment seg in segments) - { - simplified.AddRange(seg.AsSimpleLinearPath()); - } - - return simplified.ToArray(); - } - - /// - /// Returns the length of the path. - /// - /// - /// The . - /// - private float CalculateLength() - { - float length = 0; - int polyCorners = this.points.Length; - - if (!this.closedPath) - { - polyCorners -= 1; - } - - for (int i = 0; i < polyCorners; i++) - { - int next = i + 1; - if (this.closedPath && next == polyCorners) - { - next = 0; - } - - length += Vector2.Distance(this.points[i], this.points[next]); - } - - return length; - } - - /// - /// Calculate the constants. - /// - private void CalculateConstants() - { - // http://alienryderflex.com/polygon/ source for point in polygon logic - if (this.calculated) - { - return; - } - - lock (Locker) - { - if (this.calculated) - { - return; - } - - Vector2[] poly = this.points; - int polyCorners = poly.Length; - this.constant = new float[polyCorners]; - this.multiple = new float[polyCorners]; - this.distance = new float[polyCorners]; - int i, j = polyCorners - 1; - - this.distance[0] = 0; - - for (i = 0; i < polyCorners; i++) - { - this.distance[j] = this.distance[i] + Vector2.Distance(poly[i], poly[j]); - if (poly[j].Y == poly[i].Y) - { - this.constant[i] = poly[i].X; - this.multiple[i] = 0; - } - else - { - Vector2 subtracted = poly[j] - poly[i]; - this.constant[i] = (poly[i].X - ((poly[i].Y * poly[j].X) / subtracted.Y)) + ((poly[i].Y * poly[i].X) / subtracted.Y); - this.multiple[i] = subtracted.X / subtracted.Y; - } - - j = i; - } - - this.calculated = true; - } - } - - /// - /// Calculate any shorter distances along the path. - /// - /// The start position. - /// The end position. - /// The current point. - /// The info. - /// - /// The . - /// - private bool CalculateShorterDistance(Vector2 start, Vector2 end, Vector2 point, ref PointInfoInternal info) - { - Vector2 diffEnds = end - start; - - float lengthSquared = diffEnds.LengthSquared(); - Vector2 diff = point - start; - - Vector2 multiplied = diff * diffEnds; - float u = (multiplied.X + multiplied.Y) / lengthSquared; - - if (u > 1) - { - u = 1; - } - else if (u < 0) - { - u = 0; - } - - Vector2 multipliedByU = diffEnds * u; - - Vector2 pointOnLine = start + multipliedByU; - - Vector2 d = pointOnLine - point; - - float dist = d.LengthSquared(); - - if (info.DistanceSquared > dist) - { - info.DistanceSquared = dist; - info.PointOnLine = pointOnLine; - return true; - } - - return false; - } - - /// - /// Contains information about the current point. - /// - private struct PointInfoInternal - { - /// - /// The distance squared. - /// - public float DistanceSquared; - - /// - /// The point on the current line. - /// - public Vector2 PointOnLine; - } - } -} diff --git a/src/ImageSharp.Drawing/Paths/LinearLineSegment.cs b/src/ImageSharp.Drawing/Paths/LinearLineSegment.cs deleted file mode 100644 index 6942ce5a5..000000000 --- a/src/ImageSharp.Drawing/Paths/LinearLineSegment.cs +++ /dev/null @@ -1,55 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Paths -{ - using System.Linq; - using System.Numerics; - - /// - /// Represents a series of control points that will be joined by straight lines - /// - /// - public class LinearLineSegment : ILineSegment - { - /// - /// The collection of points. - /// - private readonly Vector2[] points; - - /// - /// Initializes a new instance of the class. - /// - /// The start. - /// The end. - public LinearLineSegment(Vector2 start, Vector2 end) - : this(new[] { start, end }) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The points. - public LinearLineSegment(params Vector2[] points) - { - Guard.NotNull(points, nameof(points)); - Guard.MustBeGreaterThanOrEqualTo(points.Count(), 2, nameof(points)); - - this.points = points; - } - - /// - /// Converts the into a simple linear path.. - /// - /// - /// Returns the current as simple linear path. - /// - public Vector2[] AsSimpleLinearPath() - { - return this.points; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Paths/Path.cs b/src/ImageSharp.Drawing/Paths/Path.cs deleted file mode 100644 index eb2ab5e85..000000000 --- a/src/ImageSharp.Drawing/Paths/Path.cs +++ /dev/null @@ -1,51 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Paths -{ - using System.Numerics; - - /// - /// A aggregate of s making a single logical path - /// - /// - public class Path : IPath - { - /// - /// The inner path. - /// - private readonly InternalPath innerPath; - - /// - /// Initializes a new instance of the class. - /// - /// The segment. - public Path(params ILineSegment[] segment) - { - this.innerPath = new InternalPath(segment, false); - } - - /// - public RectangleF Bounds => this.innerPath.Bounds; - - /// - public bool IsClosed => false; - - /// - public float Length => this.innerPath.Length; - - /// - public Vector2[] AsSimpleLinearPath() - { - return this.innerPath.Points; - } - - /// - public PointInfo Distance(Vector2 point) - { - return this.innerPath.DistanceFromPath(point); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Pens/Pen{TColor}.cs b/src/ImageSharp.Drawing/Pens/Pen{TColor}.cs index a08c7a7fa..bbb3c2559 100644 --- a/src/ImageSharp.Drawing/Pens/Pen{TColor}.cs +++ b/src/ImageSharp.Drawing/Pens/Pen{TColor}.cs @@ -9,7 +9,6 @@ namespace ImageSharp.Drawing.Pens using System.Numerics; using ImageSharp.Drawing.Brushes; - using ImageSharp.Drawing.Paths; using Processors; /// diff --git a/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs b/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs index e07b96949..222598d85 100644 --- a/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs +++ b/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs @@ -6,7 +6,6 @@ namespace ImageSharp.Drawing.Processors { using System; - using Paths; /// /// primitive that converts a into a color and a distance away from the drawable part of the path. diff --git a/src/ImageSharp.Drawing/Paths/PointInfo.cs b/src/ImageSharp.Drawing/Pens/Processors/PointInfo.cs similarity index 54% rename from src/ImageSharp.Drawing/Paths/PointInfo.cs rename to src/ImageSharp.Drawing/Pens/Processors/PointInfo.cs index a2cbf1e73..6fc78b09b 100644 --- a/src/ImageSharp.Drawing/Paths/PointInfo.cs +++ b/src/ImageSharp.Drawing/Pens/Processors/PointInfo.cs @@ -3,33 +3,29 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Drawing.Paths +namespace ImageSharp.Drawing.Processors { + using System; using System.Numerics; /// - /// Returns meta data about the nearest point on a path from a vector + /// Returns details about how far away from the inside of a shape and the color the pixel could be. /// public struct PointInfo { /// - /// The search point - /// - public Vector2 SearchPoint; - - /// - /// The distance along path is away from the start of the path + /// The distance along path /// public float DistanceAlongPath; /// - /// The distance is away from . + /// The distance from path /// public float DistanceFromPath; /// - /// The closest point to that lies on the path. + /// The search point /// - public Vector2 ClosestPointOnPath; + public Vector2 SearchPoint; } } diff --git a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs index f7bdcb689..d6b2f3eb2 100644 --- a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs @@ -6,13 +6,13 @@ namespace ImageSharp.Drawing.Processors { using System; + using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Threading.Tasks; using ImageSharp.Processing; - using Paths; using Pens; - using Shapes; + using SixLabors.Shapes; using Rectangle = ImageSharp.Rectangle; /// @@ -38,7 +38,7 @@ namespace ImageSharp.Drawing.Processors /// The shape. /// The options. public DrawPathProcessor(IPen pen, IShape shape, GraphicsOptions options) - : this(pen, shape.ToArray(), options) + : this(pen, shape.Paths, options) { } @@ -59,24 +59,24 @@ namespace ImageSharp.Drawing.Processors /// The pen. /// The paths. /// The options. - public DrawPathProcessor(IPen pen, IPath[] paths, GraphicsOptions options) + public DrawPathProcessor(IPen pen, IEnumerable paths, GraphicsOptions options) { - this.paths = paths; + this.paths = paths.ToArray(); this.pen = pen; this.options = options; - if (paths.Length != 1) + if (this.paths.Length != 1) { - var maxX = paths.Max(x => x.Bounds.Right); - var minX = paths.Min(x => x.Bounds.Left); - var maxY = paths.Max(x => x.Bounds.Bottom); - var minY = paths.Min(x => x.Bounds.Top); + var maxX = this.paths.Max(x => x.Bounds.Right); + var minX = this.paths.Min(x => x.Bounds.Left); + var maxY = this.paths.Max(x => x.Bounds.Bottom); + var minY = this.paths.Min(x => x.Bounds.Top); this.region = new RectangleF(minX, minY, maxX - minX, maxY - minY); } else { - this.region = paths[0].Bounds; + this.region = this.paths[0].Bounds.Convert(); } } @@ -119,7 +119,7 @@ namespace ImageSharp.Drawing.Processors minY, maxY, this.ParallelOptions, - y => + (int y) => { int offsetY = y - polyStartY; var currentPoint = default(Vector2); @@ -131,7 +131,7 @@ namespace ImageSharp.Drawing.Processors var dist = this.Closest(currentPoint); - var color = applicator.GetColor(dist); + var color = applicator.GetColor(dist.Convert()); var opacity = this.Opacity(color.DistanceFromElement); @@ -154,9 +154,9 @@ namespace ImageSharp.Drawing.Processors } } - private PointInfo Closest(Vector2 point) + private SixLabors.Shapes.PointInfo Closest(Vector2 point) { - PointInfo result = default(PointInfo); + SixLabors.Shapes.PointInfo result = default(SixLabors.Shapes.PointInfo); float distance = float.MaxValue; for (int i = 0; i < this.paths.Length; i++) diff --git a/src/ImageSharp.Drawing/Processors/FillShapeProcessor.cs b/src/ImageSharp.Drawing/Processors/FillShapeProcessor.cs index 4696b8613..14e99cba3 100644 --- a/src/ImageSharp.Drawing/Processors/FillShapeProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillShapeProcessor.cs @@ -11,7 +11,7 @@ namespace ImageSharp.Drawing.Processors using System.Threading.Tasks; using Drawing; using ImageSharp.Processing; - using Shapes; + using SixLabors.Shapes; using Rectangle = ImageSharp.Rectangle; /// @@ -44,7 +44,7 @@ namespace ImageSharp.Drawing.Processors /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - Rectangle rect = RectangleF.Ceiling(this.poly.Bounds); // rounds the points out away from the center + Rectangle rect = RectangleF.Ceiling(this.poly.Bounds.Convert()); // rounds the points out away from the center int polyStartY = rect.Y - DrawPadding; int polyEndY = rect.Bottom + DrawPadding; diff --git a/src/ImageSharp.Drawing/Processors/PointInfoExtensions.cs b/src/ImageSharp.Drawing/Processors/PointInfoExtensions.cs new file mode 100644 index 000000000..939a71845 --- /dev/null +++ b/src/ImageSharp.Drawing/Processors/PointInfoExtensions.cs @@ -0,0 +1,37 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Drawing.Processors +{ + using System; + using System.Buffers; + using System.Numerics; + using System.Threading.Tasks; + using Drawing; + using ImageSharp.Processing; + using SixLabors.Shapes; + using Rectangle = ImageSharp.Rectangle; + + /// + /// Extension methods for helping to bridge Shaper2D and ImageSharp primitives. + /// + internal static class PointInfoExtensions + { + /// + /// Converts a to an ImageSharp . + /// + /// The source. + /// A representation of this + public static PointInfo Convert(this SixLabors.Shapes.PointInfo source) + { + return new PointInfo + { + DistanceAlongPath = source.DistanceAlongPath, + DistanceFromPath = source.DistanceFromPath, + SearchPoint = source.SearchPoint + }; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processors/RectangleExtensions.cs b/src/ImageSharp.Drawing/Processors/RectangleExtensions.cs new file mode 100644 index 000000000..327b618fa --- /dev/null +++ b/src/ImageSharp.Drawing/Processors/RectangleExtensions.cs @@ -0,0 +1,32 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Drawing.Processors +{ + using System; + using System.Buffers; + using System.Numerics; + using System.Threading.Tasks; + using Drawing; + using ImageSharp.Processing; + using SixLabors.Shapes; + using Rectangle = ImageSharp.Rectangle; + + /// + /// Extension methods for helping to bridge Shaper2D and ImageSharp primitives. + /// + internal static class RectangleExtensions + { + /// + /// Converts a Shaper2D to an ImageSharp . + /// + /// The source. + /// A representation of this + public static RectangleF Convert(this SixLabors.Shapes.Rectangle source) + { + return new RectangleF(source.Location.X, source.Location.Y, source.Size.Width, source.Size.Height); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/BezierPolygon.cs b/src/ImageSharp.Drawing/Shapes/BezierPolygon.cs deleted file mode 100644 index b69ded207..000000000 --- a/src/ImageSharp.Drawing/Shapes/BezierPolygon.cs +++ /dev/null @@ -1,93 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes -{ - using System.Collections; - using System.Collections.Generic; - using System.Numerics; - using Paths; - - /// - /// Represents a polygon made up exclusivly of a single close cubic Bezier curve. - /// - public sealed class BezierPolygon : IShape - { - private Polygon innerPolygon; - - /// - /// Initializes a new instance of the class. - /// - /// The points. - public BezierPolygon(params Vector2[] points) - { - this.innerPolygon = new Polygon(new BezierLineSegment(points)); - } - - /// - /// Gets the bounding box of this shape. - /// - /// - /// The bounds. - /// - public RectangleF Bounds => this.innerPolygon.Bounds; - - /// - /// Gets the maximum number intersections that a shape can have when testing a line. - /// - /// - /// The maximum intersections. - /// - public int MaxIntersections => this.innerPolygon.MaxIntersections; - - /// - /// the distance of the point from the outline of the shape, if the value is negative it is inside the polygon bounds - /// - /// The point. - /// - /// The distance from the shape. - /// - public float Distance(Vector2 point) => this.innerPolygon.Distance(point); - - /// - /// Based on a line described by and - /// populate a buffer for all points on the polygon that the line intersects. - /// - /// The start point of the line. - /// The end point of the line. - /// The buffer that will be populated with intersections. - /// The count. - /// The offset. - /// - /// The number of intersections populated into the buffer. - /// - public int FindIntersections(Vector2 start, Vector2 end, Vector2[] buffer, int count, int offset) - { - return this.innerPolygon.FindIntersections(start, end, buffer, count, offset); - } - - /// - /// Returns an enumerator that iterates through the collection. - /// - /// - /// An enumerator that can be used to iterate through the collection. - /// - public IEnumerator GetEnumerator() - { - return this.innerPolygon.GetEnumerator(); - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() - { - return this.innerPolygon.GetEnumerator(); - } - } -} diff --git a/src/ImageSharp.Drawing/Shapes/ComplexPolygon.cs b/src/ImageSharp.Drawing/Shapes/ComplexPolygon.cs deleted file mode 100644 index fd709719c..000000000 --- a/src/ImageSharp.Drawing/Shapes/ComplexPolygon.cs +++ /dev/null @@ -1,246 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - - using Paths; - using PolygonClipper; - - /// - /// Represents a complex polygon made up of one or more outline - /// polygons and one or more holes to punch out of them. - /// - /// - public sealed class ComplexPolygon : IShape - { - private const float ClipperScaleFactor = 100f; - private IShape[] shapes; - private IEnumerable paths; - - /// - /// Initializes a new instance of the class. - /// - /// The outline. - /// The holes. - public ComplexPolygon(IShape outline, params IShape[] holes) - : this(new[] { outline }, holes) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The outlines. - /// The holes. - public ComplexPolygon(IShape[] outlines, IShape[] holes) - { - Guard.NotNull(outlines, nameof(outlines)); - Guard.MustBeGreaterThanOrEqualTo(outlines.Length, 1, nameof(outlines)); - - this.MaxIntersections = this.FixAndSetShapes(outlines, holes); - - float minX = this.shapes.Min(x => x.Bounds.Left); - float maxX = this.shapes.Max(x => x.Bounds.Right); - float minY = this.shapes.Min(x => x.Bounds.Top); - float maxY = this.shapes.Max(x => x.Bounds.Bottom); - - this.Bounds = new RectangleF(minX, minY, maxX - minX, maxY - minY); - } - - /// - /// Gets the bounding box of this shape. - /// - /// - /// The bounds. - /// - public RectangleF Bounds { get; } - - /// - /// Gets the maximum number intersections that a shape can have when testing a line. - /// - /// - /// The maximum intersections. - /// - public int MaxIntersections { get; } - - /// - /// the distance of the point from the outline of the shape, if the value is negative it is inside the polygon bounds - /// - /// The point. - /// - /// Returns the distance from thr shape to the point - /// - /// - /// Due to the clipping we did during construction we know that out shapes do not overlap at there edges - /// therefore for apoint to be in more that one we must be in a hole of another, theoretically this could - /// then flip again to be in a outlin inside a hole inside an outline :) - /// - float IShape.Distance(Vector2 point) - { - float dist = float.MaxValue; - bool inside = false; - foreach (IShape shape in this.shapes) - { - float d = shape.Distance(point); - - if (d <= 0) - { - // we are inside a poly - d = -d; // flip the sign - inside ^= true; // flip the inside flag - } - - if (d < dist) - { - dist = d; - } - } - - if (inside) - { - return -dist; - } - - return dist; - } - - /// - /// Based on a line described by and - /// populate a buffer for all points on all the polygons, that make up this complex shape, - /// that the line intersects. - /// - /// The start point of the line. - /// The end point of the line. - /// The buffer that will be populated with intersections. - /// The count. - /// The offset. - /// - /// The number of intersections populated into the buffer. - /// - public int FindIntersections(Vector2 start, Vector2 end, Vector2[] buffer, int count, int offset) - { - int totalAdded = 0; - for (int i = 0; i < this.shapes.Length; i++) - { - int added = this.shapes[i].FindIntersections(start, end, buffer, count, offset); - count -= added; - offset += added; - totalAdded += added; - } - - return totalAdded; - } - - /// - /// Returns an enumerator that iterates through the collection. - /// - /// - /// An enumerator that can be used to iterate through the collection. - /// - public IEnumerator GetEnumerator() - { - return this.paths.GetEnumerator(); - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() - { - return this.GetEnumerator(); - } - - private void AddPoints(Clipper clipper, IShape shape, PolyType polyType) - { - // if the path is already the shape use it directly and skip the path loop. - if (shape is IPath) - { - clipper.AddPath( - (IPath)shape, - polyType); - } - else - { - foreach (IPath path in shape) - { - clipper.AddPath( - path, - polyType); - } - } - } - - private void AddPoints(Clipper clipper, IEnumerable shapes, PolyType polyType) - { - foreach (IShape shape in shapes) - { - this.AddPoints(clipper, shape, polyType); - } - } - - private void ExtractOutlines(PolyNode tree, List shapes, List paths) - { - if (tree.Contour.Any()) - { - // if the source path is set then we clipper retained the full path intact thus we can freely - // use it and get any shape optimisations that are availible. - if (tree.SourcePath != null) - { - shapes.Add((IShape)tree.SourcePath); - paths.Add(tree.SourcePath); - } - else - { - // convert the Clipper Contour from scaled ints back down to the origional size (this is going to be lossy but not significantly) - Polygon polygon = new Polygon(new Paths.LinearLineSegment(tree.Contour.ToArray())); - - shapes.Add(polygon); - paths.Add(polygon); - } - } - - foreach (PolyNode c in tree.Children) - { - this.ExtractOutlines(c, shapes, paths); - } - } - - private int FixAndSetShapes(IEnumerable outlines, IEnumerable holes) - { - Clipper clipper = new Clipper(); - - // add the outlines and the holes to clipper, scaling up from the float source to the int based system clipper uses - this.AddPoints(clipper, outlines, PolyType.Subject); - this.AddPoints(clipper, holes, PolyType.Clip); - - PolyTree tree = clipper.Execute(); - - List shapes = new List(); - List paths = new List(); - - // convert the 'tree' back to paths - this.ExtractOutlines(tree, shapes, paths); - this.shapes = shapes.ToArray(); - this.paths = paths.ToArray(); - - int intersections = 0; - foreach (IShape s in this.shapes) - { - intersections += s.MaxIntersections; - } - - return intersections; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/IShape.cs b/src/ImageSharp.Drawing/Shapes/IShape.cs deleted file mode 100644 index 242e3bd8e..000000000 --- a/src/ImageSharp.Drawing/Shapes/IShape.cs +++ /dev/null @@ -1,56 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes -{ - using System.Collections.Generic; - using System.Numerics; - using Paths; - - /// - /// Represents a closed set of paths making up a single shape. - /// - public interface IShape : IEnumerable - { - /// - /// Gets the bounding box of this shape. - /// - /// - /// The bounds. - /// - RectangleF Bounds { get; } - - /// - /// Gets the maximum number intersections that a shape can have when testing a line. - /// - /// - /// The maximum intersections. - /// - int MaxIntersections { get; } - - /// - /// the distance of the point from the outline of the shape, if the value is negative it is inside the polygon bounds - /// - /// The point. - /// - /// Returns the distance from the shape to the point - /// - float Distance(Vector2 point); - - /// - /// Based on a line described by and - /// populate a buffer for all points on the polygon that the line intersects. - /// - /// The start point of the line. - /// The end point of the line. - /// The buffer that will be populated with intersections. - /// The count. - /// The offset. - /// - /// The number of intersections populated into the buffer. - /// - int FindIntersections(Vector2 start, Vector2 end, Vector2[] buffer, int count, int offset); - } -} diff --git a/src/ImageSharp.Drawing/Shapes/LinearPolygon.cs b/src/ImageSharp.Drawing/Shapes/LinearPolygon.cs deleted file mode 100644 index 30a30c20f..000000000 --- a/src/ImageSharp.Drawing/Shapes/LinearPolygon.cs +++ /dev/null @@ -1,93 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes -{ - using System.Collections; - using System.Collections.Generic; - using System.Numerics; - using Paths; - - /// - /// Represents a polygon made up exclusivly of a single Linear path. - /// - public sealed class LinearPolygon : IShape - { - private Polygon innerPolygon; - - /// - /// Initializes a new instance of the class. - /// - /// The points. - public LinearPolygon(params Vector2[] points) - { - this.innerPolygon = new Polygon(new LinearLineSegment(points)); - } - - /// - /// Gets the bounding box of this shape. - /// - /// - /// The bounds. - /// - public RectangleF Bounds => this.innerPolygon.Bounds; - - /// - /// Gets the maximum number intersections that a shape can have when testing a line. - /// - /// - /// The maximum intersections. - /// - public int MaxIntersections - { - get - { - return this.innerPolygon.MaxIntersections; - } - } - - /// - /// the distance of the point from the outline of the shape, if the value is negative it is inside the polygon bounds - /// - /// The point. - /// - /// Returns the distance from the shape to the point - /// - public float Distance(Vector2 point) => this.innerPolygon.Distance(point); - - /// - /// Based on a line described by and - /// populate a buffer for all points on the polygon that the line intersects. - /// - /// The start point of the line. - /// The end point of the line. - /// The buffer that will be populated with intersections. - /// The count. - /// The offset. - /// - /// The number of intersections populated into the buffer. - /// - public int FindIntersections(Vector2 start, Vector2 end, Vector2[] buffer, int count, int offset) - { - return this.innerPolygon.FindIntersections(start, end, buffer, count, offset); - } - - /// - /// Returns an enumerator that iterates through the collection. - /// - /// - /// An enumerator that can be used to iterate through the collection. - /// - public IEnumerator GetEnumerator() => this.innerPolygon.GetEnumerator(); - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() => this.innerPolygon.GetEnumerator(); - } -} diff --git a/src/ImageSharp.Drawing/Shapes/Polygon.cs b/src/ImageSharp.Drawing/Shapes/Polygon.cs deleted file mode 100644 index 86c3c9ee4..000000000 --- a/src/ImageSharp.Drawing/Shapes/Polygon.cs +++ /dev/null @@ -1,156 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes -{ - using System.Collections; - using System.Collections.Generic; - using System.Numerics; - - using Paths; - - /// - /// A shape made up of a single path made up of one of more s - /// - public sealed class Polygon : IShape, IPath - { - private readonly InternalPath innerPath; - private readonly IEnumerable pathCollection; - - /// - /// Initializes a new instance of the class. - /// - /// The segments. - public Polygon(params ILineSegment[] segments) - { - this.innerPath = new InternalPath(segments, true); - this.pathCollection = new[] { this }; - } - - /// - /// Initializes a new instance of the class. - /// - /// The segment. - public Polygon(ILineSegment segment) - { - this.innerPath = new InternalPath(segment, true); - this.pathCollection = new[] { this }; - } - - /// - /// Gets the bounding box of this shape. - /// - /// - /// The bounds. - /// - public RectangleF Bounds => this.innerPath.Bounds; - - /// - /// Gets the length of the path - /// - /// - /// The length. - /// - public float Length => this.innerPath.Length; - - /// - /// Gets a value indicating whether this instance is closed. - /// - /// - /// true if this instance is closed; otherwise, false. - /// - public bool IsClosed => true; - - /// - /// Gets the maximum number intersections that a shape can have when testing a line. - /// - /// - /// The maximum intersections. - /// - public int MaxIntersections => this.innerPath.Points.Length; - - /// - /// the distance of the point from the outline of the shape, if the value is negative it is inside the polygon bounds - /// - /// The point. - /// - /// The distance of the point away from the shape - /// - public float Distance(Vector2 point) - { - bool isInside = this.innerPath.PointInPolygon(point); - - float distance = this.innerPath.DistanceFromPath(point).DistanceFromPath; - if (isInside) - { - return -distance; - } - - return distance; - } - - /// - /// Returns an enumerator that iterates through the collection. - /// - /// - /// An enumerator that can be used to iterate through the collection. - /// - public IEnumerator GetEnumerator() - { - return this.pathCollection.GetEnumerator(); - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() - { - return this.pathCollection.GetEnumerator(); - } - - /// - /// Calcualtes the distance along and away from the path for a specified point. - /// - /// The point along the path. - /// - /// distance metadata about the point. - /// - PointInfo IPath.Distance(Vector2 point) - { - return this.innerPath.DistanceFromPath(point); - } - - /// - /// Returns the current shape as a simple linear path. - /// - /// - /// Returns the current as simple linear path. - /// - public Vector2[] AsSimpleLinearPath() - { - return this.innerPath.Points; - } - - /// - /// Based on a line described by and - /// populate a buffer for all points on the polygon that the line intersects. - /// - /// The start point of the line. - /// The end point of the line. - /// The buffer that will be populated with intersections. - /// The count. - /// The offset. - /// - /// The number of intersections populated into the buffer. - /// - public int FindIntersections(Vector2 start, Vector2 end, Vector2[] buffer, int count, int offset) - { - return this.innerPath.FindIntersections(start, end, buffer, count, offset); - } - } -} diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/Clipper.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/Clipper.cs deleted file mode 100644 index c13acec8f..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/Clipper.cs +++ /dev/null @@ -1,3860 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// Library to clip polygons. - /// - internal class Clipper - { - private const double HorizontalDeltaLimit = -3.4E+38; - private const int Skip = -2; - private const int Unassigned = -1; // InitOptions that can be passed to the constructor ... - - private Maxima maxima; - private TEdge sortedEdges; - private List intersectList; - private IComparer intersectNodeComparer = new IntersectNodeSort(); - private bool executeLocked; - - private List joins; - private List ghostJoins; - private bool usingPolyTree; - private LocalMinima minimaList = null; - private LocalMinima currentLM = null; - private List> edges = new List>(); - private Scanbeam scanbeam; - private List polyOuts; - private TEdge activeEdges; - - /// - /// Initializes a new instance of the class. - /// - public Clipper() - { - this.scanbeam = null; - this.maxima = null; - this.activeEdges = null; - this.sortedEdges = null; - this.intersectList = new List(); - this.executeLocked = false; - this.usingPolyTree = false; - this.polyOuts = new List(); - this.joins = new List(); - this.ghostJoins = new List(); - } - - /// - /// Node types - /// - private enum NodeType - { - /// - /// Any - /// - Any, - - /// - /// The open - /// - Open, - - /// - /// The closed - /// - Closed - } - - /// - /// Adds the path. - /// - /// The path. - /// Type of the poly. - /// True if the path was added. - /// AddPath: Open paths have been disabled. - public bool AddPath(IPath path, PolyType polyType) - { - var pg = path.AsSimpleLinearPath(); - - int highI = pg.Length - 1; - while (highI > 0 && (pg[highI] == pg[0])) - { - --highI; - } - - while (highI > 0 && (pg[highI] == pg[highI - 1])) - { - --highI; - } - - if (highI < 2) - { - return false; - } - - // create a new edge array ... - List edges = new List(highI + 1); - for (int i = 0; i <= highI; i++) - { - edges.Add(new TEdge() { SourcePath = path }); - } - - bool isFlat = true; - - // 1. Basic (first) edge initialization ... - edges[1].Curr = pg[1]; - - InitEdge(edges[0], edges[1], edges[highI], pg[0]); - InitEdge(edges[highI], edges[0], edges[highI - 1], pg[highI]); - for (int i = highI - 1; i >= 1; --i) - { - InitEdge(edges[i], edges[i + 1], edges[i - 1], pg[i]); - } - - TEdge eStart = edges[0]; - - // 2. Remove duplicate vertices, and (when closed) collinear edges ... - TEdge edge = eStart, eLoopStop = eStart; - while (true) - { - // nb: allows matching start and end points when not Closed ... - if (edge.Curr == edge.Next.Curr) - { - if (edge == edge.Next) - { - break; - } - - if (edge == eStart) - { - eStart = edge.Next; - } - - edge = RemoveEdge(edge); - eLoopStop = edge; - continue; - } - - if (edge.Prev == edge.Next) - { - break; // only two vertices - } - else if (SlopesEqual(edge.Prev.Curr, edge.Curr, edge.Next.Curr)) - { - // Collinear edges are allowed for open paths but in closed paths - // the default is to merge adjacent collinear edges into a single edge. - // However, if the PreserveCollinear property is enabled, only overlapping - // collinear edges (ie spikes) will be removed from closed paths. - if (edge == eStart) - { - eStart = edge.Next; - } - - edge = RemoveEdge(edge); - edge = edge.Prev; - eLoopStop = edge; - continue; - } - - edge = edge.Next; - if (edge == eLoopStop) - { - break; - } - } - - if (edge.Prev == edge.Next) - { - return false; - } - - // 3. Do second stage of edge initialization ... - edge = eStart; - do - { - this.InitEdge2(edge, polyType); - edge = edge.Next; - if (isFlat && edge.Curr.Y != eStart.Curr.Y) - { - isFlat = false; - } - } - while (edge != eStart); - - // 4. Finally, add edge bounds to LocalMinima list ... - // Totally flat paths must be handled differently when adding them - // to LocalMinima list to avoid endless loops etc ... - if (isFlat) - { - return false; - } - - this.edges.Add(edges); - bool leftBoundIsForward; - TEdge emIn = null; - - // workaround to avoid an endless loop in the while loop below when - // open paths have matching start and end points ... - if (edge.Prev.Bot == edge.Prev.Top) - { - edge = edge.Next; - } - - while (true) - { - edge = FindNextLocMin(edge); - if (edge == emIn) - { - break; - } - else if (emIn == null) - { - emIn = edge; - } - - // E and E.Prev now share a local minima (left aligned if horizontal). - // Compare their slopes to find which starts which bound ... - LocalMinima locMin = new LocalMinima(); - locMin.Next = null; - locMin.Y = edge.Bot.Y; - if (edge.Dx < edge.Prev.Dx) - { - locMin.LeftBound = edge.Prev; - locMin.RightBound = edge; - leftBoundIsForward = false; // Q.nextInLML = Q.prev - } - else - { - locMin.LeftBound = edge; - locMin.RightBound = edge.Prev; - leftBoundIsForward = true; // Q.nextInLML = Q.next - } - - locMin.LeftBound.Side = EdgeSide.Left; - locMin.RightBound.Side = EdgeSide.Right; - - if (locMin.LeftBound.Next == locMin.RightBound) - { - locMin.LeftBound.WindDelta = -1; - } - else - { - locMin.LeftBound.WindDelta = 1; - } - - locMin.RightBound.WindDelta = -locMin.LeftBound.WindDelta; - - edge = this.ProcessBound(locMin.LeftBound, leftBoundIsForward); - if (edge.OutIdx == Skip) - { - edge = this.ProcessBound(edge, leftBoundIsForward); - } - - TEdge edge2 = this.ProcessBound(locMin.RightBound, !leftBoundIsForward); - if (edge2.OutIdx == Skip) - { - edge2 = this.ProcessBound(edge2, !leftBoundIsForward); - } - - if (locMin.LeftBound.OutIdx == Skip) - { - locMin.LeftBound = null; - } - else if (locMin.RightBound.OutIdx == Skip) - { - locMin.RightBound = null; - } - - this.InsertLocalMinima(locMin); - if (!leftBoundIsForward) - { - edge = edge2; - } - } - - return true; - } - - /// - /// Executes the specified clip type. - /// - /// - /// Returns the polytree containing the converted polygons. - /// - public PolyTree Execute() - { - PolyTree polytree = new PolyTree(); - - if (this.executeLocked) - { - return null; - } - - this.executeLocked = true; - this.usingPolyTree = true; - bool succeeded; - try - { - succeeded = this.ExecuteInternal(); - - // build the return polygons ... - if (succeeded) - { - this.BuildResult2(polytree); - } - } - finally - { - this.DisposeAllPolyPts(); - this.executeLocked = false; - } - - if (succeeded) - { - return polytree; - } - - return null; - } - - private static float Round(double value) - { - return value < 0 ? (float)(value - 0.5) : (float)(value + 0.5); - } - - private static float TopX(TEdge edge, float currentY) - { - if (currentY == edge.Top.Y) - { - return edge.Top.X; - } - - return edge.Bot.X + Round(edge.Dx * (currentY - edge.Bot.Y)); - } - - private static void AddPolyNodeToPaths(PolyNode polynode, NodeType nt, List> paths) - { - bool match = true; - switch (nt) - { - case NodeType.Open: return; - case NodeType.Closed: match = !polynode.IsOpen; break; - default: break; - } - - if (polynode.Polygon.Count > 0 && match) - { - paths.Add(polynode.Polygon); - } - - foreach (PolyNode pn in polynode.Children) - { - AddPolyNodeToPaths(pn, nt, paths); - } - } - - private static double DistanceFromLineSqrd(Vector2 pt, Vector2 ln1, Vector2 ln2) - { - // The equation of a line in general form (Ax + By + C = 0) - // given 2 points (x¹,y¹) & (x²,y²) is ... - // (y¹ - y²)x + (x² - x¹)y + (y² - y¹)x¹ - (x² - x¹)y¹ = 0 - // A = (y¹ - y²); B = (x² - x¹); C = (y² - y¹)x¹ - (x² - x¹)y¹ - // perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²) - // see http://en.wikipedia.org/wiki/Perpendicular_distance - double a = ln1.Y - ln2.Y; - double b = ln2.X - ln1.X; - double c = (a * ln1.X) + (b * ln1.Y); - c = (a * pt.X) + (b * pt.Y) - c; - return (c * c) / ((a * a) + (b * b)); - } - - private static bool SlopesNearCollinear(Vector2 pt1, Vector2 pt2, Vector2 pt3, double distSqrd) - { - // this function is more accurate when the point that's GEOMETRICALLY - // between the other 2 points is the one that's tested for distance. - // nb: with 'spikes', either pt1 or pt3 is geometrically between the other pts - if (Math.Abs(pt1.X - pt2.X) > Math.Abs(pt1.Y - pt2.Y)) - { - if ((pt1.X > pt2.X) == (pt1.X < pt3.X)) - { - return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; - } - else if ((pt2.X > pt1.X) == (pt2.X < pt3.X)) - { - return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; - } - else - { - return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; - } - } - else - { - if ((pt1.Y > pt2.Y) == (pt1.Y < pt3.Y)) - { - return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; - } - else if ((pt2.Y > pt1.Y) == (pt2.Y < pt3.Y)) - { - return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; - } - else - { - return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; - } - } - } - - private static bool PointsAreClose(Vector2 pt1, Vector2 pt2, double distSqrd) - { - return Vector2.DistanceSquared(pt1, pt2) <= distSqrd; - } - - private static OutPt ExcludeOp(OutPt op) - { - OutPt result = op.Prev; - result.Next = op.Next; - op.Next.Prev = result; - result.Idx = 0; - return result; - } - - private static void FixHoleLinkage(OutRec outRec) - { - // skip if an outermost polygon or - // already already points to the correct FirstLeft ... - if (outRec.FirstLeft == null || - (outRec.IsHole != outRec.FirstLeft.IsHole && - outRec.FirstLeft.Pts != null)) - { - return; - } - - OutRec orfl = outRec.FirstLeft; - while (orfl != null && ((orfl.IsHole == outRec.IsHole) || orfl.Pts == null)) - { - orfl = orfl.FirstLeft; - } - - outRec.FirstLeft = orfl; - } - - // See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos - // http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf - private static int PointInPolygon(Vector2 pt, OutPt op) - { - // returns 0 if false, +1 if true, -1 if pt ON polygon boundary - int result = 0; - OutPt startOp = op; - float ptx = pt.X, pty = pt.Y; - float poly0x = op.Pt.X, poly0y = op.Pt.Y; - do - { - op = op.Next; - float poly1x = op.Pt.X, poly1y = op.Pt.Y; - - if (poly1y == pty) - { - if ((poly1x == ptx) || (poly0y == pty && - ((poly1x > ptx) == (poly0x < ptx)))) - { - return -1; - } - } - - if ((poly0y < pty) != (poly1y < pty)) - { - if (poly0x >= ptx) - { - if (poly1x > ptx) - { - result = 1 - result; - } - else - { - double d = (double)((poly0x - ptx) * (poly1y - pty)) - - (double)((poly1x - ptx) * (poly0y - pty)); - if (d == 0) - { - return -1; - } - - if ((d > 0) == (poly1y > poly0y)) - { - result = 1 - result; - } - } - } - else - { - if (poly1x > ptx) - { - double d = (double)((poly0x - ptx) * (poly1y - pty)) - (double)((poly1x - ptx) * (poly0y - pty)); - if (d == 0) - { - return -1; - } - - if ((d > 0) == (poly1y > poly0y)) - { - result = 1 - result; - } - } - } - } - - poly0x = poly1x; - poly0y = poly1y; - } - while (startOp != op); - - return result; - } - - private static bool Poly2ContainsPoly1(OutPt outPt1, OutPt outPt2) - { - OutPt op = outPt1; - do - { - // nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon - int res = PointInPolygon(op.Pt, outPt2); - if (res >= 0) - { - return res > 0; - } - - op = op.Next; - } - while (op != outPt1); - return true; - } - - private static void SwapSides(TEdge edge1, TEdge edge2) - { - EdgeSide side = edge1.Side; - edge1.Side = edge2.Side; - edge2.Side = side; - } - - private static void SwapPolyIndexes(TEdge edge1, TEdge edge2) - { - int outIdx = edge1.OutIdx; - edge1.OutIdx = edge2.OutIdx; - edge2.OutIdx = outIdx; - } - - private static double GetDx(Vector2 pt1, Vector2 pt2) - { - if (pt1.Y == pt2.Y) - { - return HorizontalDeltaLimit; - } - else - { - return (double)(pt2.X - pt1.X) / (pt2.Y - pt1.Y); - } - } - - private static bool HorzSegmentsOverlap(float seg1a, float seg1b, float seg2a, float seg2b) - { - if (seg1a > seg1b) - { - Swap(ref seg1a, ref seg1b); - } - - if (seg2a > seg2b) - { - Swap(ref seg2a, ref seg2b); - } - - return (seg1a < seg2b) && (seg2a < seg1b); - } - - private static TEdge FindNextLocMin(TEdge edge) - { - TEdge edge2; - while (true) - { - while (edge.Bot != edge.Prev.Bot || edge.Curr == edge.Top) - { - edge = edge.Next; - } - - if (edge.Dx != HorizontalDeltaLimit && edge.Prev.Dx != HorizontalDeltaLimit) - { - break; - } - - while (edge.Prev.Dx == HorizontalDeltaLimit) - { - edge = edge.Prev; - } - - edge2 = edge; - while (edge.Dx == HorizontalDeltaLimit) - { - edge = edge.Next; - } - - if (edge.Top.Y == edge.Prev.Bot.Y) - { - continue; // ie just an intermediate horz. - } - - if (edge2.Prev.Bot.X < edge.Bot.X) - { - edge = edge2; - } - - break; - } - - return edge; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Swap(ref float val1, ref float val2) - { - float tmp = val1; - val1 = val2; - val2 = tmp; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool IsHorizontal(TEdge e) - { - return e.Delta.Y == 0; - } - - private static bool SlopesEqual(TEdge e1, TEdge e2) - { - return e1.Delta.Y * e2.Delta.X == e1.Delta.X * e2.Delta.Y; - } - - private static bool SlopesEqual(Vector2 pt1, Vector2 pt2, Vector2 pt3) - { - var dif12 = pt1 - pt2; - var dif23 = pt2 - pt3; - return (dif12.Y * dif23.X) - (dif12.X * dif23.Y) == 0; - } - - private static bool SlopesEqual(Vector2 pt1, Vector2 pt2, Vector2 pt3, Vector2 pt4) - { - var dif12 = pt1 - pt2; - var dif34 = pt3 - pt4; - - return (dif12.Y * dif34.X) - (dif12.X * dif34.Y) == 0; - } - - private static void InitEdge(TEdge e, TEdge eNext, TEdge ePrev, Vector2 pt) - { - e.Next = eNext; - e.Prev = ePrev; - e.Curr = pt; - e.OutIdx = Unassigned; - } - - private static OutRec ParseFirstLeft(OutRec firstLeft) - { - while (firstLeft != null && firstLeft.Pts == null) - { - firstLeft = firstLeft.FirstLeft; - } - - return firstLeft; - } - - private static bool Pt2IsBetweenPt1AndPt3(Vector2 pt1, Vector2 pt2, Vector2 pt3) - { - if ((pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2)) - { - return false; - } - else if (pt1.X != pt3.X) - { - return (pt2.X > pt1.X) == (pt2.X < pt3.X); - } - else - { - return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y); - } - } - - private static TEdge RemoveEdge(TEdge e) - { - // removes e from double_linked_list (but without removing from memory) - e.Prev.Next = e.Next; - e.Next.Prev = e.Prev; - TEdge result = e.Next; - e.Prev = null; // flag as removed (see ClipperBase.Clear) - return result; - } - - private static void ReverseHorizontal(TEdge e) - { - // swap horizontal edges' top and bottom x's so they follow the natural - // progression of the bounds - ie so their xbots will align with the - // adjoining lower edge. [Helpful in the ProcessHorizontal() method.] - Swap(ref e.Top.X, ref e.Bot.X); - } - - private void InsertMaxima(float x) - { - // double-linked list: sorted ascending, ignoring dups. - Maxima newMax = new Maxima(); - newMax.X = x; - if (this.maxima == null) - { - this.maxima = newMax; - this.maxima.Next = null; - this.maxima.Prev = null; - } - else if (x < this.maxima.X) - { - newMax.Next = this.maxima; - newMax.Prev = null; - this.maxima = newMax; - } - else - { - Maxima m = this.maxima; - while (m.Next != null && (x >= m.Next.X)) - { - m = m.Next; - } - - if (x == m.X) - { - return; // ie ignores duplicates (& CG to clean up newMax) - } - - // insert newMax between m and m.Next ... - newMax.Next = m.Next; - newMax.Prev = m; - if (m.Next != null) - { - m.Next.Prev = newMax; - } - - m.Next = newMax; - } - } - - private bool ExecuteInternal() - { - try - { - this.Reset(); - this.sortedEdges = null; - this.maxima = null; - - float botY, topY; - if (!this.PopScanbeam(out botY)) - { - return false; - } - - this.InsertLocalMinimaIntoAEL(botY); - while (this.PopScanbeam(out topY) || this.LocalMinimaPending()) - { - this.ProcessHorizontals(); - this.ghostJoins.Clear(); - if (!this.ProcessIntersections(topY)) - { - return false; - } - - this.ProcessEdgesAtTopOfScanbeam(topY); - botY = topY; - this.InsertLocalMinimaIntoAEL(botY); - } - - // fix orientations ... - foreach (OutRec outRec in this.polyOuts) - { - if (outRec.Pts == null || outRec.IsOpen) - { - continue; - } - } - - this.JoinCommonEdges(); - - foreach (OutRec outRec in this.polyOuts) - { - if (outRec.Pts == null) - { - continue; - } - else if (outRec.IsOpen) - { - this.FixupOutPolyline(outRec); - } - else - { - this.FixupOutPolygon(outRec); - } - } - - return true; - } - finally - { - this.joins.Clear(); - this.ghostJoins.Clear(); - } - } - - private void DisposeAllPolyPts() - { - for (int i = 0; i < this.polyOuts.Count; ++i) - { - this.DisposeOutRec(i); - } - - this.polyOuts.Clear(); - } - - private void AddJoin(OutPt op1, OutPt op2, Vector2 offPt) - { - Join j = new Join(); - j.OutPt1 = op1; - j.OutPt2 = op2; - j.OffPt = offPt; - this.joins.Add(j); - } - - private void AddGhostJoin(OutPt op, Vector2 offPt) - { - Join j = new Join(); - j.OutPt1 = op; - j.OffPt = offPt; - this.ghostJoins.Add(j); - } - - private void InsertLocalMinimaIntoAEL(float botY) - { - LocalMinima lm; - while (this.PopLocalMinima(botY, out lm)) - { - TEdge lb = lm.LeftBound; - TEdge rb = lm.RightBound; - - OutPt op1 = null; - if (lb == null) - { - this.InsertEdgeIntoAEL(rb, null); - this.SetWindingCount(rb); - if (this.IsContributing(rb)) - { - op1 = this.AddOutPt(rb, rb.Bot); - } - } - else if (rb == null) - { - this.InsertEdgeIntoAEL(lb, null); - this.SetWindingCount(lb); - if (this.IsContributing(lb)) - { - op1 = this.AddOutPt(lb, lb.Bot); - } - - this.InsertScanbeam(lb.Top.Y); - } - else - { - this.InsertEdgeIntoAEL(lb, null); - this.InsertEdgeIntoAEL(rb, lb); - this.SetWindingCount(lb); - rb.WindCnt = lb.WindCnt; - rb.WindCnt2 = lb.WindCnt2; - if (this.IsContributing(lb)) - { - op1 = this.AddLocalMinPoly(lb, rb, lb.Bot); - } - - this.InsertScanbeam(lb.Top.Y); - } - - if (rb != null) - { - if (IsHorizontal(rb)) - { - if (rb.NextInLML != null) - { - this.InsertScanbeam(rb.NextInLML.Top.Y); - } - - this.AddEdgeToSEL(rb); - } - else - { - this.InsertScanbeam(rb.Top.Y); - } - } - - if (lb == null || rb == null) - { - continue; - } - - // if output polygons share an Edge with a horizontal rb, they'll need joining later ... - if (op1 != null && IsHorizontal(rb) && - this.ghostJoins.Count > 0 && rb.WindDelta != 0) - { - for (int i = 0; i < this.ghostJoins.Count; i++) - { - // if the horizontal Rb and a 'ghost' horizontal overlap, then convert - // the 'ghost' join to a real join ready for later ... - Join j = this.ghostJoins[i]; - if (HorzSegmentsOverlap(j.OutPt1.Pt.X, j.OffPt.X, rb.Bot.X, rb.Top.X)) - { - this.AddJoin(j.OutPt1, op1, j.OffPt); - } - } - } - - if (lb.OutIdx >= 0 && lb.PrevInAEL != null && - lb.PrevInAEL.Curr.X == lb.Bot.X && - lb.PrevInAEL.OutIdx >= 0 && - SlopesEqual(lb.PrevInAEL.Curr, lb.PrevInAEL.Top, lb.Curr, lb.Top) && - lb.WindDelta != 0 && lb.PrevInAEL.WindDelta != 0) - { - OutPt op2 = this.AddOutPt(lb.PrevInAEL, lb.Bot); - this.AddJoin(op1, op2, lb.Top); - } - - if (lb.NextInAEL != rb) - { - if (rb.OutIdx >= 0 && rb.PrevInAEL.OutIdx >= 0 && - SlopesEqual(rb.PrevInAEL.Curr, rb.PrevInAEL.Top, rb.Curr, rb.Top) && - rb.WindDelta != 0 && rb.PrevInAEL.WindDelta != 0) - { - OutPt op2 = this.AddOutPt(rb.PrevInAEL, rb.Bot); - this.AddJoin(op1, op2, rb.Top); - } - - TEdge e = lb.NextInAEL; - if (e != null) - { - while (e != rb) - { - // nb: For calculating winding counts etc, IntersectEdges() assumes - // that param1 will be to the right of param2 ABOVE the intersection ... - this.IntersectEdges(rb, e, lb.Curr); // order important here - e = e.NextInAEL; - } - } - } - } - } - - private void InsertEdgeIntoAEL(TEdge edge, TEdge startEdge) - { - if (this.activeEdges == null) - { - edge.PrevInAEL = null; - edge.NextInAEL = null; - this.activeEdges = edge; - } - else if (startEdge == null && this.E2InsertsBeforeE1(this.activeEdges, edge)) - { - edge.PrevInAEL = null; - edge.NextInAEL = this.activeEdges; - this.activeEdges.PrevInAEL = edge; - this.activeEdges = edge; - } - else - { - if (startEdge == null) - { - startEdge = this.activeEdges; - } - - while (startEdge.NextInAEL != null && - !this.E2InsertsBeforeE1(startEdge.NextInAEL, edge)) - { - startEdge = startEdge.NextInAEL; - } - - edge.NextInAEL = startEdge.NextInAEL; - if (startEdge.NextInAEL != null) - { - startEdge.NextInAEL.PrevInAEL = edge; - } - - edge.PrevInAEL = startEdge; - startEdge.NextInAEL = edge; - } - } - - private bool E2InsertsBeforeE1(TEdge e1, TEdge e2) - { - if (e2.Curr.X == e1.Curr.X) - { - if (e2.Top.Y > e1.Top.Y) - { - return e2.Top.X < TopX(e1, e2.Top.Y); - } - else - { - return e1.Top.X > TopX(e2, e1.Top.Y); - } - } - else - { - return e2.Curr.X < e1.Curr.X; - } - } - - private bool IsContributing(TEdge edge) - { - // return false if a subj line has been flagged as inside a subj polygon - if (edge.WindDelta == 0 && edge.WindCnt != 1) - { - return false; - } - - if (edge.PolyTyp == PolyType.Subject) - { - return edge.WindCnt2 == 0; - } - else - { - return edge.WindCnt2 != 0; - } - } - - private void SetWindingCount(TEdge edge) - { - TEdge e = edge.PrevInAEL; - - // find the edge of the same polytype that immediately preceeds 'edge' in AEL - while (e != null && ((e.PolyTyp != edge.PolyTyp) || (e.WindDelta == 0))) - { - e = e.PrevInAEL; - } - - if (e == null) - { - if (edge.WindDelta == 0) - { - edge.WindCnt = 1; - } - else - { - edge.WindCnt = edge.WindDelta; - } - - edge.WindCnt2 = 0; - e = this.activeEdges; // ie get ready to calc WindCnt2 - } - else if (edge.WindDelta == 0) - { - edge.WindCnt = 1; - edge.WindCnt2 = e.WindCnt2; - e = e.NextInAEL; // ie get ready to calc WindCnt2 - } - else - { - // EvenOdd filling ... - if (edge.WindDelta == 0) - { - // are we inside a subj polygon ... - bool inside = true; - TEdge e2 = e.PrevInAEL; - while (e2 != null) - { - if (e2.PolyTyp == e.PolyTyp && e2.WindDelta != 0) - { - inside = !inside; - } - - e2 = e2.PrevInAEL; - } - - edge.WindCnt = inside ? 0 : 1; - } - else - { - edge.WindCnt = edge.WindDelta; - } - - edge.WindCnt2 = e.WindCnt2; - e = e.NextInAEL; // ie get ready to calc WindCnt2 - } - - // update WindCnt2 ... - // EvenOdd filling ... - while (e != edge) - { - if (e.WindDelta != 0) - { - edge.WindCnt2 = edge.WindCnt2 == 0 ? 1 : 0; - } - - e = e.NextInAEL; - } - } - - private void AddEdgeToSEL(TEdge edge) - { - // SEL pointers in PEdge are use to build transient lists of horizontal edges. - // However, since we don't need to worry about processing order, all additions - // are made to the front of the list ... - if (this.sortedEdges == null) - { - this.sortedEdges = edge; - edge.PrevInSEL = null; - edge.NextInSEL = null; - } - else - { - edge.NextInSEL = this.sortedEdges; - edge.PrevInSEL = null; - this.sortedEdges.PrevInSEL = edge; - this.sortedEdges = edge; - } - } - - private bool PopEdgeFromSEL(out TEdge e) - { - // Pop edge from front of SEL (ie SEL is a FILO list) - e = this.sortedEdges; - if (e == null) - { - return false; - } - - TEdge oldE = e; - this.sortedEdges = e.NextInSEL; - if (this.sortedEdges != null) - { - this.sortedEdges.PrevInSEL = null; - } - - oldE.NextInSEL = null; - oldE.PrevInSEL = null; - return true; - } - - private void CopyAELToSEL() - { - TEdge e = this.activeEdges; - this.sortedEdges = e; - while (e != null) - { - e.PrevInSEL = e.PrevInAEL; - e.NextInSEL = e.NextInAEL; - e = e.NextInAEL; - } - } - - private void SwapPositionsInSEL(TEdge edge1, TEdge edge2) - { - if (edge1.NextInSEL == null && edge1.PrevInSEL == null) - { - return; - } - - if (edge2.NextInSEL == null && edge2.PrevInSEL == null) - { - return; - } - - if (edge1.NextInSEL == edge2) - { - TEdge next = edge2.NextInSEL; - if (next != null) - { - next.PrevInSEL = edge1; - } - - TEdge prev = edge1.PrevInSEL; - if (prev != null) - { - prev.NextInSEL = edge2; - } - - edge2.PrevInSEL = prev; - edge2.NextInSEL = edge1; - edge1.PrevInSEL = edge2; - edge1.NextInSEL = next; - } - else if (edge2.NextInSEL == edge1) - { - TEdge next = edge1.NextInSEL; - if (next != null) - { - next.PrevInSEL = edge2; - } - - TEdge prev = edge2.PrevInSEL; - if (prev != null) - { - prev.NextInSEL = edge1; - } - - edge1.PrevInSEL = prev; - edge1.NextInSEL = edge2; - edge2.PrevInSEL = edge1; - edge2.NextInSEL = next; - } - else - { - TEdge next = edge1.NextInSEL; - TEdge prev = edge1.PrevInSEL; - edge1.NextInSEL = edge2.NextInSEL; - if (edge1.NextInSEL != null) - { - edge1.NextInSEL.PrevInSEL = edge1; - } - - edge1.PrevInSEL = edge2.PrevInSEL; - if (edge1.PrevInSEL != null) - { - edge1.PrevInSEL.NextInSEL = edge1; - } - - edge2.NextInSEL = next; - if (edge2.NextInSEL != null) - { - edge2.NextInSEL.PrevInSEL = edge2; - } - - edge2.PrevInSEL = prev; - if (edge2.PrevInSEL != null) - { - edge2.PrevInSEL.NextInSEL = edge2; - } - } - - if (edge1.PrevInSEL == null) - { - this.sortedEdges = edge1; - } - else if (edge2.PrevInSEL == null) - { - this.sortedEdges = edge2; - } - } - - private void AddLocalMaxPoly(TEdge e1, TEdge e2, Vector2 pt) - { - this.AddOutPt(e1, pt); - if (e2.WindDelta == 0) - { - this.AddOutPt(e2, pt); - } - - if (e1.OutIdx == e2.OutIdx) - { - e1.OutIdx = Unassigned; - e2.OutIdx = Unassigned; - } - else if (e1.OutIdx < e2.OutIdx) - { - this.AppendPolygon(e1, e2); - } - else - { - this.AppendPolygon(e2, e1); - } - } - - private OutPt AddLocalMinPoly(TEdge e1, TEdge e2, Vector2 pt) - { - OutPt result; - TEdge e, prevE; - if (IsHorizontal(e2) || (e1.Dx > e2.Dx)) - { - result = this.AddOutPt(e1, pt); - e2.OutIdx = e1.OutIdx; - e1.Side = EdgeSide.Left; - e2.Side = EdgeSide.Right; - e = e1; - if (e.PrevInAEL == e2) - { - prevE = e2.PrevInAEL; - } - else - { - prevE = e.PrevInAEL; - } - } - else - { - result = this.AddOutPt(e2, pt); - e1.OutIdx = e2.OutIdx; - e1.Side = EdgeSide.Right; - e2.Side = EdgeSide.Left; - e = e2; - if (e.PrevInAEL == e1) - { - prevE = e1.PrevInAEL; - } - else - { - prevE = e.PrevInAEL; - } - } - - if (prevE != null && prevE.OutIdx >= 0) - { - float xPrev = TopX(prevE, pt.Y); - float xE = TopX(e, pt.Y); - if ((xPrev == xE) && - (e.WindDelta != 0) && - (prevE.WindDelta != 0) && - SlopesEqual(new Vector2(xPrev, pt.Y), prevE.Top, new Vector2(xE, pt.Y), e.Top)) - { - OutPt outPt = this.AddOutPt(prevE, pt); - this.AddJoin(result, outPt, e.Top); - } - } - - return result; - } - - private OutPt AddOutPt(TEdge e, Vector2 pt) - { - if (e.OutIdx < 0) - { - OutRec outRec = this.CreateOutRec(); - outRec.SourcePath = e.SourcePath; // copy source from edge to outrec - outRec.IsOpen = e.WindDelta == 0; - OutPt newOp = new OutPt(); - outRec.Pts = newOp; - newOp.Idx = outRec.Idx; - newOp.Pt = pt; - newOp.Next = newOp; - newOp.Prev = newOp; - if (!outRec.IsOpen) - { - this.SetHoleState(e, outRec); - } - - e.OutIdx = outRec.Idx; // nb: do this after SetZ ! - return newOp; - } - else - { - OutRec outRec = this.polyOuts[e.OutIdx]; - - if (outRec.SourcePath != e.SourcePath) - { - // this edge was from a different/unknown source - outRec.SourcePath = null; // drop source form output - } - - // OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' - OutPt op = outRec.Pts; - bool toFront = e.Side == EdgeSide.Left; - if (toFront && pt == op.Pt) - { - return op; - } - else if (!toFront && pt == op.Prev.Pt) - { - return op.Prev; - } - - // do we need to move the source to the point??? - OutPt newOp = new OutPt(); - newOp.Idx = outRec.Idx; - newOp.Pt = pt; - newOp.Next = op; - newOp.Prev = op.Prev; - newOp.Prev.Next = newOp; - op.Prev = newOp; - if (toFront) - { - outRec.Pts = newOp; - } - - return newOp; - } - } - - private OutPt GetLastOutPt(TEdge e) - { - OutRec outRec = this.polyOuts[e.OutIdx]; - if (e.Side == EdgeSide.Left) - { - return outRec.Pts; - } - else - { - return outRec.Pts.Prev; - } - } - - private void SetHoleState(TEdge e, OutRec outRec) - { - TEdge e2 = e.PrevInAEL; - TEdge eTmp = null; - while (e2 != null) - { - if (e2.OutIdx >= 0 && e2.WindDelta != 0) - { - if (eTmp == null) - { - eTmp = e2; - } - else if (eTmp.OutIdx == e2.OutIdx) - { - eTmp = null; // paired - } - } - - e2 = e2.PrevInAEL; - } - - if (eTmp == null) - { - outRec.FirstLeft = null; - outRec.IsHole = false; - } - else - { - outRec.FirstLeft = this.polyOuts[eTmp.OutIdx]; - outRec.IsHole = !outRec.FirstLeft.IsHole; - } - } - - private bool FirstIsBottomPt(OutPt btmPt1, OutPt btmPt2) - { - OutPt p = btmPt1.Prev; - while ((p.Pt == btmPt1.Pt) && (p != btmPt1)) - { - p = p.Prev; - } - - double dx1p = Math.Abs(GetDx(btmPt1.Pt, p.Pt)); - p = btmPt1.Next; - while ((p.Pt == btmPt1.Pt) && (p != btmPt1)) - { - p = p.Next; - } - - double dx1n = Math.Abs(GetDx(btmPt1.Pt, p.Pt)); - - p = btmPt2.Prev; - while ((p.Pt == btmPt2.Pt) && (p != btmPt2)) - { - p = p.Prev; - } - - double dx2p = Math.Abs(GetDx(btmPt2.Pt, p.Pt)); - p = btmPt2.Next; - while ((p.Pt == btmPt2.Pt) && (p != btmPt2)) - { - p = p.Next; - } - - double dx2n = Math.Abs(GetDx(btmPt2.Pt, p.Pt)); - - if (Math.Max(dx1p, dx1n) == Math.Max(dx2p, dx2n) && - Math.Min(dx1p, dx1n) == Math.Min(dx2p, dx2n)) - { - return this.Area(btmPt1) > 0; // if otherwise identical use orientation - } - else - { - return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n); - } - } - - private OutPt GetBottomPt(OutPt pp) - { - OutPt dups = null; - OutPt p = pp.Next; - while (p != pp) - { - if (p.Pt.Y > pp.Pt.Y) - { - pp = p; - dups = null; - } - else if (p.Pt.Y == pp.Pt.Y && p.Pt.X <= pp.Pt.X) - { - if (p.Pt.X < pp.Pt.X) - { - dups = null; - pp = p; - } - else - { - if (p.Next != pp && p.Prev != pp) - { - dups = p; - } - } - } - - p = p.Next; - } - - if (dups != null) - { - // there appears to be at least 2 vertices at bottomPt so ... - while (dups != p) - { - if (!this.FirstIsBottomPt(p, dups)) - { - pp = dups; - } - - dups = dups.Next; - while (dups.Pt != pp.Pt) - { - dups = dups.Next; - } - } - } - - return pp; - } - - private OutRec GetLowermostRec(OutRec outRec1, OutRec outRec2) - { - // work out which polygon fragment has the correct hole state ... - if (outRec1.BottomPt == null) - { - outRec1.BottomPt = this.GetBottomPt(outRec1.Pts); - } - - if (outRec2.BottomPt == null) - { - outRec2.BottomPt = this.GetBottomPt(outRec2.Pts); - } - - OutPt bPt1 = outRec1.BottomPt; - OutPt bPt2 = outRec2.BottomPt; - if (bPt1.Pt.Y > bPt2.Pt.Y) - { - return outRec1; - } - else if (bPt1.Pt.Y < bPt2.Pt.Y) - { - return outRec2; - } - else if (bPt1.Pt.X < bPt2.Pt.X) - { - return outRec1; - } - else if (bPt1.Pt.X > bPt2.Pt.X) - { - return outRec2; - } - else if (bPt1.Next == bPt1) - { - return outRec2; - } - else if (bPt2.Next == bPt2) - { - return outRec1; - } - else if (this.FirstIsBottomPt(bPt1, bPt2)) - { - return outRec1; - } - else - { - return outRec2; - } - } - - private bool OutRec1RightOfOutRec2(OutRec outRec1, OutRec outRec2) - { - do - { - outRec1 = outRec1.FirstLeft; - if (outRec1 == outRec2) - { - return true; - } - } - while (outRec1 != null); - - return false; - } - - private OutRec GetOutRec(int idx) - { - OutRec outrec = this.polyOuts[idx]; - while (outrec != this.polyOuts[outrec.Idx]) - { - outrec = this.polyOuts[outrec.Idx]; - } - - return outrec; - } - - private void AppendPolygon(TEdge e1, TEdge e2) - { - OutRec outRec1 = this.polyOuts[e1.OutIdx]; - OutRec outRec2 = this.polyOuts[e2.OutIdx]; - - OutRec holeStateRec; - if (this.OutRec1RightOfOutRec2(outRec1, outRec2)) - { - holeStateRec = outRec2; - } - else if (this.OutRec1RightOfOutRec2(outRec2, outRec1)) - { - holeStateRec = outRec1; - } - else - { - holeStateRec = this.GetLowermostRec(outRec1, outRec2); - } - - // get the start and ends of both output polygons and - // join E2 poly onto E1 poly and delete pointers to E2 ... - OutPt p1_lft = outRec1.Pts; - OutPt p1_rt = p1_lft.Prev; - OutPt p2_lft = outRec2.Pts; - OutPt p2_rt = p2_lft.Prev; - - // join e2 poly onto e1 poly and delete pointers to e2 ... - if (e1.Side == EdgeSide.Left) - { - if (e2.Side == EdgeSide.Left) - { - // z y x a b c - this.ReversePolyPtLinks(p2_lft); - p2_lft.Next = p1_lft; - p1_lft.Prev = p2_lft; - p1_rt.Next = p2_rt; - p2_rt.Prev = p1_rt; - outRec1.Pts = p2_rt; - } - else - { - // x y z a b c - p2_rt.Next = p1_lft; - p1_lft.Prev = p2_rt; - p2_lft.Prev = p1_rt; - p1_rt.Next = p2_lft; - outRec1.Pts = p2_lft; - } - } - else - { - if (e2.Side == EdgeSide.Right) - { - // a b c z y x - this.ReversePolyPtLinks(p2_lft); - p1_rt.Next = p2_rt; - p2_rt.Prev = p1_rt; - p2_lft.Next = p1_lft; - p1_lft.Prev = p2_lft; - } - else - { - // a b c x y z - p1_rt.Next = p2_lft; - p2_lft.Prev = p1_rt; - p1_lft.Prev = p2_rt; - p2_rt.Next = p1_lft; - } - } - - outRec1.BottomPt = null; - if (holeStateRec == outRec2) - { - if (outRec2.FirstLeft != outRec1) - { - outRec1.FirstLeft = outRec2.FirstLeft; - } - - outRec1.IsHole = outRec2.IsHole; - } - - outRec2.Pts = null; - outRec2.BottomPt = null; - - outRec2.FirstLeft = outRec1; - - int okIdx = e1.OutIdx; - int obsoleteIdx = e2.OutIdx; - - e1.OutIdx = Unassigned; // nb: safe because we only get here via AddLocalMaxPoly - e2.OutIdx = Unassigned; - - TEdge e = this.activeEdges; - while (e != null) - { - if (e.OutIdx == obsoleteIdx) - { - e.OutIdx = okIdx; - e.Side = e1.Side; - break; - } - - e = e.NextInAEL; - } - - outRec2.Idx = outRec1.Idx; - } - - private void ReversePolyPtLinks(OutPt pp) - { - if (pp == null) - { - return; - } - - OutPt pp1; - OutPt pp2; - pp1 = pp; - do - { - pp2 = pp1.Next; - pp1.Next = pp1.Prev; - pp1.Prev = pp2; - pp1 = pp2; - } - while (pp1 != pp); - } - - private void IntersectEdges(TEdge e1, TEdge e2, Vector2 pt) - { - // e1 will be to the left of e2 BELOW the intersection. Therefore e1 is before - // e2 in AEL except when e1 is being inserted at the intersection point ... - bool e1Contributing = e1.OutIdx >= 0; - bool e2Contributing = e2.OutIdx >= 0; - - // update winding counts... - // assumes that e1 will be to the Right of e2 ABOVE the intersection - if (e1.PolyTyp == e2.PolyTyp) - { - int oldE1WindCnt = e1.WindCnt; - e1.WindCnt = e2.WindCnt; - e2.WindCnt = oldE1WindCnt; - } - else - { - e1.WindCnt2 = (e1.WindCnt2 == 0) ? 1 : 0; - e2.WindCnt2 = (e2.WindCnt2 == 0) ? 1 : 0; - } - - int e1Wc, e2Wc; - e1Wc = Math.Abs(e1.WindCnt); - e2Wc = Math.Abs(e2.WindCnt); - - if (e1Contributing && e2Contributing) - { - if ((e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || - (e1.PolyTyp != e2.PolyTyp)) - { - this.AddLocalMaxPoly(e1, e2, pt); - } - else - { - this.AddOutPt(e1, pt); - this.AddOutPt(e2, pt); - SwapSides(e1, e2); - SwapPolyIndexes(e1, e2); - } - } - else if (e1Contributing) - { - if (e2Wc == 0 || e2Wc == 1) - { - this.AddOutPt(e1, pt); - SwapSides(e1, e2); - SwapPolyIndexes(e1, e2); - } - } - else if (e2Contributing) - { - if (e1Wc == 0 || e1Wc == 1) - { - this.AddOutPt(e2, pt); - SwapSides(e1, e2); - SwapPolyIndexes(e1, e2); - } - } - else if ((e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1)) - { - // neither edge is currently contributing ... - float e1Wc2, e2Wc2; - - e1Wc2 = Math.Abs(e1.WindCnt2); - e2Wc2 = Math.Abs(e2.WindCnt2); - - if (e1.PolyTyp != e2.PolyTyp) - { - this.AddLocalMinPoly(e1, e2, pt); - } - else if (e1Wc == 1 && e2Wc == 1) - { - if (((e1.PolyTyp == PolyType.Clip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || - ((e1.PolyTyp == PolyType.Subject) && (e1Wc2 <= 0) && (e2Wc2 <= 0))) - { - this.AddLocalMinPoly(e1, e2, pt); - } - } - else - { - SwapSides(e1, e2); - } - } - } - - private void ProcessHorizontals() - { - TEdge horzEdge; // m_SortedEdges; - while (this.PopEdgeFromSEL(out horzEdge)) - { - this.ProcessHorizontal(horzEdge); - } - } - - private void GetHorzDirection(TEdge horzEdge, out Direction dir, out float left, out float right) - { - if (horzEdge.Bot.X < horzEdge.Top.X) - { - left = horzEdge.Bot.X; - right = horzEdge.Top.X; - dir = Direction.LeftToRight; - } - else - { - left = horzEdge.Top.X; - right = horzEdge.Bot.X; - dir = Direction.RightToLeft; - } - } - - private void ProcessHorizontal(TEdge horzEdge) - { - Direction dir; - float horzLeft, horzRight; - bool isOpen = horzEdge.WindDelta == 0; - - this.GetHorzDirection(horzEdge, out dir, out horzLeft, out horzRight); - - TEdge eLastHorz = horzEdge, eMaxPair = null; - while (eLastHorz.NextInLML != null && IsHorizontal(eLastHorz.NextInLML)) - { - eLastHorz = eLastHorz.NextInLML; - } - - if (eLastHorz.NextInLML == null) - { - eMaxPair = this.GetMaximaPair(eLastHorz); - } - - Maxima currMax = this.maxima; - if (currMax != null) - { - // get the first maxima in range (X) ... - if (dir == Direction.LeftToRight) - { - while (currMax != null && currMax.X <= horzEdge.Bot.X) - { - currMax = currMax.Next; - } - - if (currMax != null && currMax.X >= eLastHorz.Top.X) - { - currMax = null; - } - } - else - { - while (currMax.Next != null && currMax.Next.X < horzEdge.Bot.X) - { - currMax = currMax.Next; - } - - if (currMax.X <= eLastHorz.Top.X) - { - currMax = null; - } - } - } - - OutPt op1 = null; - - // loop through consec. horizontal edges - while (true) - { - bool isLastHorz = horzEdge == eLastHorz; - TEdge e = this.GetNextInAEL(horzEdge, dir); - while (e != null) - { - // this code block inserts extra coords into horizontal edges (in output - // polygons) whereever maxima touch these horizontal edges. This helps - // 'simplifying' polygons (ie if the Simplify property is set). - if (currMax != null) - { - if (dir == Direction.LeftToRight) - { - while (currMax != null && currMax.X < e.Curr.X) - { - if (horzEdge.OutIdx >= 0 && !isOpen) - { - this.AddOutPt(horzEdge, new Vector2(currMax.X, horzEdge.Bot.Y)); - } - - currMax = currMax.Next; - } - } - else - { - while (currMax != null && currMax.X > e.Curr.X) - { - if (horzEdge.OutIdx >= 0 && !isOpen) - { - this.AddOutPt(horzEdge, new Vector2(currMax.X, horzEdge.Bot.Y)); - } - - currMax = currMax.Prev; - } - } - } - - if ((dir == Direction.LeftToRight && e.Curr.X > horzRight) || - (dir == Direction.RightToLeft && e.Curr.X < horzLeft)) - { - break; - } - - // Also break if we've got to the end of an intermediate horizontal edge ... - // nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. - if (e.Curr.X == horzEdge.Top.X && horzEdge.NextInLML != null && - e.Dx < horzEdge.NextInLML.Dx) - { - break; - } - - // note: may be done multiple times - if (horzEdge.OutIdx >= 0 && !isOpen) - { - op1 = this.AddOutPt(horzEdge, e.Curr); - TEdge eNextHorz = this.sortedEdges; - while (eNextHorz != null) - { - if (eNextHorz.OutIdx >= 0 && - HorzSegmentsOverlap(horzEdge.Bot.X, horzEdge.Top.X, eNextHorz.Bot.X, eNextHorz.Top.X)) - { - OutPt op2 = this.GetLastOutPt(eNextHorz); - this.AddJoin(op2, op1, eNextHorz.Top); - } - - eNextHorz = eNextHorz.NextInSEL; - } - - this.AddGhostJoin(op1, horzEdge.Bot); - } - - // OK, so far we're still in range of the horizontal Edge but make sure - // we're at the last of consec. horizontals when matching with eMaxPair - if (e == eMaxPair && isLastHorz) - { - if (horzEdge.OutIdx >= 0) - { - this.AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge.Top); - } - - this.DeleteFromAEL(horzEdge); - this.DeleteFromAEL(eMaxPair); - return; - } - - if (dir == Direction.LeftToRight) - { - Vector2 pt = new Vector2(e.Curr.X, horzEdge.Curr.Y); - this.IntersectEdges(horzEdge, e, pt); - } - else - { - Vector2 pt = new Vector2(e.Curr.X, horzEdge.Curr.Y); - this.IntersectEdges(e, horzEdge, pt); - } - - TEdge eNext = this.GetNextInAEL(e, dir); - this.SwapPositionsInAEL(horzEdge, e); - e = eNext; - } // end while(e != null) - - // Break out of loop if HorzEdge.NextInLML is not also horizontal ... - if (horzEdge.NextInLML == null || !IsHorizontal(horzEdge.NextInLML)) - { - break; - } - - this.UpdateEdgeIntoAEL(ref horzEdge); - if (horzEdge.OutIdx >= 0) - { - this.AddOutPt(horzEdge, horzEdge.Bot); - } - - this.GetHorzDirection(horzEdge, out dir, out horzLeft, out horzRight); - } - - if (horzEdge.OutIdx >= 0 && op1 == null) - { - op1 = this.GetLastOutPt(horzEdge); - TEdge eNextHorz = this.sortedEdges; - while (eNextHorz != null) - { - if (eNextHorz.OutIdx >= 0 && - HorzSegmentsOverlap(horzEdge.Bot.X, horzEdge.Top.X, eNextHorz.Bot.X, eNextHorz.Top.X)) - { - OutPt op2 = this.GetLastOutPt(eNextHorz); - this.AddJoin(op2, op1, eNextHorz.Top); - } - - eNextHorz = eNextHorz.NextInSEL; - } - - this.AddGhostJoin(op1, horzEdge.Top); - } - - if (horzEdge.NextInLML != null) - { - if (horzEdge.OutIdx >= 0) - { - op1 = this.AddOutPt(horzEdge, horzEdge.Top); - - this.UpdateEdgeIntoAEL(ref horzEdge); - if (horzEdge.WindDelta == 0) - { - return; - } - - // nb: HorzEdge is no longer horizontal here - TEdge ePrev = horzEdge.PrevInAEL; - TEdge eNext = horzEdge.NextInAEL; - if (ePrev != null && ePrev.Curr.X == horzEdge.Bot.X && - ePrev.Curr.Y == horzEdge.Bot.Y && ePrev.WindDelta != 0 && - (ePrev.OutIdx >= 0 && ePrev.Curr.Y > ePrev.Top.Y && - SlopesEqual(horzEdge, ePrev))) - { - OutPt op2 = this.AddOutPt(ePrev, horzEdge.Bot); - this.AddJoin(op1, op2, horzEdge.Top); - } - else if (eNext != null && eNext.Curr.X == horzEdge.Bot.X && - eNext.Curr.Y == horzEdge.Bot.Y && eNext.WindDelta != 0 && - eNext.OutIdx >= 0 && eNext.Curr.Y > eNext.Top.Y && - SlopesEqual(horzEdge, eNext)) - { - OutPt op2 = this.AddOutPt(eNext, horzEdge.Bot); - this.AddJoin(op1, op2, horzEdge.Top); - } - } - else - { - this.UpdateEdgeIntoAEL(ref horzEdge); - } - } - else - { - if (horzEdge.OutIdx >= 0) - { - this.AddOutPt(horzEdge, horzEdge.Top); - } - - this.DeleteFromAEL(horzEdge); - } - } - - private TEdge GetNextInAEL(TEdge e, Direction direction) - { - return direction == Direction.LeftToRight ? e.NextInAEL : e.PrevInAEL; - } - - private bool IsMaxima(TEdge e, double y) - { - return e != null && e.Top.Y == y && e.NextInLML == null; - } - - private bool IsIntermediate(TEdge e, double y) - { - return e.Top.Y == y && e.NextInLML != null; - } - - private TEdge GetMaximaPair(TEdge e) - { - if ((e.Next.Top == e.Top) && e.Next.NextInLML == null) - { - return e.Next; - } - else if ((e.Prev.Top == e.Top) && e.Prev.NextInLML == null) - { - return e.Prev; - } - else - { - return null; - } - } - - private TEdge GetMaximaPairEx(TEdge e) - { - // as above but returns null if MaxPair isn't in AEL (unless it's horizontal) - TEdge result = this.GetMaximaPair(e); - if (result == null || result.OutIdx == Skip || - ((result.NextInAEL == result.PrevInAEL) && !IsHorizontal(result))) - { - return null; - } - - return result; - } - - private bool ProcessIntersections(float topY) - { - if (this.activeEdges == null) - { - return true; - } - - try - { - this.BuildIntersectList(topY); - if (this.intersectList.Count == 0) - { - return true; - } - - if (this.intersectList.Count == 1 || this.FixupIntersectionOrder()) - { - this.ProcessIntersectList(); - } - else - { - return false; - } - } - catch - { - this.sortedEdges = null; - this.intersectList.Clear(); - throw new ClipperException("ProcessIntersections error"); - } - - this.sortedEdges = null; - return true; - } - - private void BuildIntersectList(float topY) - { - if (this.activeEdges == null) - { - return; - } - - // prepare for sorting ... - TEdge e = this.activeEdges; - this.sortedEdges = e; - while (e != null) - { - e.PrevInSEL = e.PrevInAEL; - e.NextInSEL = e.NextInAEL; - e.Curr.X = TopX(e, topY); - e = e.NextInAEL; - } - - // bubblesort ... - bool isModified = true; - while (isModified && this.sortedEdges != null) - { - isModified = false; - e = this.sortedEdges; - while (e.NextInSEL != null) - { - TEdge eNext = e.NextInSEL; - Vector2 pt; - if (e.Curr.X > eNext.Curr.X) - { - this.IntersectPoint(e, eNext, out pt); - if (pt.Y < topY) - { - pt = new Vector2(TopX(e, topY), topY); - } - - IntersectNode newNode = new IntersectNode(); - newNode.Edge1 = e; - newNode.Edge2 = eNext; - newNode.Pt = pt; - this.intersectList.Add(newNode); - - this.SwapPositionsInSEL(e, eNext); - isModified = true; - } - else - { - e = eNext; - } - } - - if (e.PrevInSEL != null) - { - e.PrevInSEL.NextInSEL = null; - } - else - { - break; - } - } - - this.sortedEdges = null; - } - - private bool EdgesAdjacent(IntersectNode inode) - { - return (inode.Edge1.NextInSEL == inode.Edge2) || - (inode.Edge1.PrevInSEL == inode.Edge2); - } - - private bool FixupIntersectionOrder() - { - // pre-condition: intersections are sorted bottom-most first. - // Now it's crucial that intersections are made only between adjacent edges, - // so to ensure this the order of intersections may need adjusting ... - this.intersectList.Sort(this.intersectNodeComparer); - - this.CopyAELToSEL(); - int cnt = this.intersectList.Count; - for (int i = 0; i < cnt; i++) - { - if (!this.EdgesAdjacent(this.intersectList[i])) - { - int j = i + 1; - while (j < cnt && !this.EdgesAdjacent(this.intersectList[j])) - { - j++; - } - - if (j == cnt) - { - return false; - } - - IntersectNode tmp = this.intersectList[i]; - this.intersectList[i] = this.intersectList[j]; - this.intersectList[j] = tmp; - } - - this.SwapPositionsInSEL(this.intersectList[i].Edge1, this.intersectList[i].Edge2); - } - - return true; - } - - private void ProcessIntersectList() - { - for (int i = 0; i < this.intersectList.Count; i++) - { - IntersectNode iNode = this.intersectList[i]; - { - this.IntersectEdges(iNode.Edge1, iNode.Edge2, iNode.Pt); - this.SwapPositionsInAEL(iNode.Edge1, iNode.Edge2); - } - } - - this.intersectList.Clear(); - } - - private void IntersectPoint(TEdge edge1, TEdge edge2, out Vector2 ip) - { - ip = default(Vector2); - double b1, b2; - - // nb: with very large coordinate values, it's possible for SlopesEqual() to - // return false but for the edge.Dx value be equal due to double precision rounding. - if (edge1.Dx == edge2.Dx) - { - ip.Y = edge1.Curr.Y; - ip.X = TopX(edge1, ip.Y); - return; - } - - if (edge1.Delta.X == 0) - { - ip.X = edge1.Bot.X; - if (IsHorizontal(edge2)) - { - ip.Y = edge2.Bot.Y; - } - else - { - b2 = edge2.Bot.Y - (edge2.Bot.X / edge2.Dx); - ip.Y = Round((ip.X / edge2.Dx) + b2); - } - } - else if (edge2.Delta.X == 0) - { - ip.X = edge2.Bot.X; - if (IsHorizontal(edge1)) - { - ip.Y = edge1.Bot.Y; - } - else - { - b1 = edge1.Bot.Y - (edge1.Bot.X / edge1.Dx); - ip.Y = Round((ip.X / edge1.Dx) + b1); - } - } - else - { - b1 = edge1.Bot.X - (edge1.Bot.Y * edge1.Dx); - b2 = edge2.Bot.X - (edge2.Bot.Y * edge2.Dx); - double q = (b2 - b1) / (edge1.Dx - edge2.Dx); - ip.Y = Round(q); - if (Math.Abs(edge1.Dx) < Math.Abs(edge2.Dx)) - { - ip.X = Round((edge1.Dx * q) + b1); - } - else - { - ip.X = Round((edge2.Dx * q) + b2); - } - } - - if (ip.Y < edge1.Top.Y || ip.Y < edge2.Top.Y) - { - if (edge1.Top.Y > edge2.Top.Y) - { - ip.Y = edge1.Top.Y; - } - else - { - ip.Y = edge2.Top.Y; - } - - if (Math.Abs(edge1.Dx) < Math.Abs(edge2.Dx)) - { - ip.X = TopX(edge1, ip.Y); - } - else - { - ip.X = TopX(edge2, ip.Y); - } - } - - // finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ... - if (ip.Y > edge1.Curr.Y) - { - ip.Y = edge1.Curr.Y; - - // better to use the more vertical edge to derive X ... - if (Math.Abs(edge1.Dx) > Math.Abs(edge2.Dx)) - { - ip.X = TopX(edge2, ip.Y); - } - else - { - ip.X = TopX(edge1, ip.Y); - } - } - } - - private void ProcessEdgesAtTopOfScanbeam(float topY) - { - TEdge e = this.activeEdges; - while (e != null) - { - // 1. process maxima, treating them as if they're 'bent' horizontal edges, - // but exclude maxima with horizontal edges. nb: e can't be a horizontal. - bool isMaximaEdge = this.IsMaxima(e, topY); - - if (isMaximaEdge) - { - TEdge eMaxPair = this.GetMaximaPairEx(e); - isMaximaEdge = eMaxPair == null || !IsHorizontal(eMaxPair); - } - - if (isMaximaEdge) - { - TEdge ePrev = e.PrevInAEL; - this.DoMaxima(e); - if (ePrev == null) - { - e = this.activeEdges; - } - else - { - e = ePrev.NextInAEL; - } - } - else - { - // 2. promote horizontal edges, otherwise update Curr.X and Curr.Y ... - if (this.IsIntermediate(e, topY) && IsHorizontal(e.NextInLML)) - { - this.UpdateEdgeIntoAEL(ref e); - if (e.OutIdx >= 0) - { - this.AddOutPt(e, e.Bot); - } - - this.AddEdgeToSEL(e); - } - else - { - e.Curr.X = TopX(e, topY); - e.Curr.Y = topY; - } - - e = e.NextInAEL; - } - } - - // 3. Process horizontals at the Top of the scanbeam ... - this.ProcessHorizontals(); - this.maxima = null; - - // 4. Promote intermediate vertices ... - e = this.activeEdges; - while (e != null) - { - if (this.IsIntermediate(e, topY)) - { - OutPt op = null; - if (e.OutIdx >= 0) - { - op = this.AddOutPt(e, e.Top); - } - - this.UpdateEdgeIntoAEL(ref e); - - // if output polygons share an edge, they'll need joining later ... - TEdge ePrev = e.PrevInAEL; - TEdge eNext = e.NextInAEL; - if (ePrev != null && ePrev.Curr.X == e.Bot.X && - ePrev.Curr.Y == e.Bot.Y && op != null && - ePrev.OutIdx >= 0 && ePrev.Curr.Y > ePrev.Top.Y && - SlopesEqual(e.Curr, e.Top, ePrev.Curr, ePrev.Top) && - (e.WindDelta != 0) && (ePrev.WindDelta != 0)) - { - OutPt op2 = this.AddOutPt(ePrev, e.Bot); - this.AddJoin(op, op2, e.Top); - } - else if (eNext != null && eNext.Curr.X == e.Bot.X && - eNext.Curr.Y == e.Bot.Y && op != null && - eNext.OutIdx >= 0 && eNext.Curr.Y > eNext.Top.Y && - SlopesEqual(e.Curr, e.Top, eNext.Curr, eNext.Top) && - (e.WindDelta != 0) && (eNext.WindDelta != 0)) - { - OutPt op2 = this.AddOutPt(eNext, e.Bot); - this.AddJoin(op, op2, e.Top); - } - } - - e = e.NextInAEL; - } - } - - private void DoMaxima(TEdge e) - { - TEdge eMaxPair = this.GetMaximaPairEx(e); - if (eMaxPair == null) - { - if (e.OutIdx >= 0) - { - this.AddOutPt(e, e.Top); - } - - this.DeleteFromAEL(e); - return; - } - - TEdge eNext = e.NextInAEL; - while (eNext != null && eNext != eMaxPair) - { - this.IntersectEdges(e, eNext, e.Top); - this.SwapPositionsInAEL(e, eNext); - eNext = e.NextInAEL; - } - - if (e.OutIdx == Unassigned && eMaxPair.OutIdx == Unassigned) - { - this.DeleteFromAEL(e); - this.DeleteFromAEL(eMaxPair); - } - else if (e.OutIdx >= 0 && eMaxPair.OutIdx >= 0) - { - if (e.OutIdx >= 0) - { - this.AddLocalMaxPoly(e, eMaxPair, e.Top); - } - - this.DeleteFromAEL(e); - this.DeleteFromAEL(eMaxPair); - } - else - { - throw new ClipperException("DoMaxima error"); - } - } - - private int PointCount(OutPt pts) - { - if (pts == null) - { - return 0; - } - - int result = 0; - OutPt p = pts; - do - { - result++; - p = p.Next; - } - while (p != pts); - return result; - } - - private void BuildResult2(PolyTree polytree) - { - polytree.Clear(); - - // add each output polygon/contour to polytree ... - polytree.AllPolys.Capacity = this.polyOuts.Count; - for (int i = 0; i < this.polyOuts.Count; i++) - { - OutRec outRec = this.polyOuts[i]; - int cnt = this.PointCount(outRec.Pts); - if ((outRec.IsOpen && cnt < 2) || - (!outRec.IsOpen && cnt < 3)) - { - continue; - } - - FixHoleLinkage(outRec); - PolyNode pn = new PolyNode(); - pn.SourcePath = outRec.SourcePath; - polytree.AllPolys.Add(pn); - outRec.PolyNode = pn; - pn.Polygon.Capacity = cnt; - OutPt op = outRec.Pts.Prev; - for (int j = 0; j < cnt; j++) - { - pn.Polygon.Add(op.Pt); - op = op.Prev; - } - } - - // fixup PolyNode links etc ... - polytree.Children.Capacity = this.polyOuts.Count; - for (int i = 0; i < this.polyOuts.Count; i++) - { - OutRec outRec = this.polyOuts[i]; - if (outRec.PolyNode == null) - { - continue; - } - else if (outRec.IsOpen) - { - outRec.PolyNode.IsOpen = true; - polytree.AddChild(outRec.PolyNode); - } - else if (outRec.FirstLeft != null && - outRec.FirstLeft.PolyNode != null) - { - outRec.FirstLeft.PolyNode.AddChild(outRec.PolyNode); - } - else - { - polytree.AddChild(outRec.PolyNode); - } - } - } - - private void FixupOutPolyline(OutRec outrec) - { - OutPt pp = outrec.Pts; - OutPt lastPP = pp.Prev; - while (pp != lastPP) - { - pp = pp.Next; - if (pp.Pt == pp.Prev.Pt) - { - if (pp == lastPP) - { - lastPP = pp.Prev; - } - - OutPt tmpPP = pp.Prev; - tmpPP.Next = pp.Next; - pp.Next.Prev = tmpPP; - pp = tmpPP; - } - } - - if (pp == pp.Prev) - { - outrec.Pts = null; - } - } - - private void FixupOutPolygon(OutRec outRec) - { - // FixupOutPolygon() - removes duplicate points and simplifies consecutive - // parallel edges by removing the middle vertex. - OutPt lastOK = null; - outRec.BottomPt = null; - OutPt pp = outRec.Pts; - while (true) - { - if (pp.Prev == pp || pp.Prev == pp.Next) - { - outRec.Pts = null; - return; - } - - // test for duplicate points and collinear edges ... - if ((pp.Pt == pp.Next.Pt) || (pp.Pt == pp.Prev.Pt) || - SlopesEqual(pp.Prev.Pt, pp.Pt, pp.Next.Pt)) - { - lastOK = null; - pp.Prev.Next = pp.Next; - pp.Next.Prev = pp.Prev; - pp = pp.Prev; - } - else if (pp == lastOK) - { - break; - } - else - { - if (lastOK == null) - { - lastOK = pp; - } - - pp = pp.Next; - } - } - - outRec.Pts = pp; - } - - private OutPt DupOutPt(OutPt outPt, bool insertAfter) - { - OutPt result = new OutPt(); - result.Pt = outPt.Pt; - result.Idx = outPt.Idx; - if (insertAfter) - { - result.Next = outPt.Next; - result.Prev = outPt; - outPt.Next.Prev = result; - outPt.Next = result; - } - else - { - result.Prev = outPt.Prev; - result.Next = outPt; - outPt.Prev.Next = result; - outPt.Prev = result; - } - - return result; - } - - private bool GetOverlap(float a1, float a2, float b1, float b2, out float left, out float right) - { - if (a1 < a2) - { - if (b1 < b2) - { - left = Math.Max(a1, b1); - right = Math.Min(a2, b2); - } - else - { - left = Math.Max(a1, b2); - right = Math.Min(a2, b1); - } - } - else - { - if (b1 < b2) - { - left = Math.Max(a2, b1); - right = Math.Min(a1, b2); - } - else - { - left = Math.Max(a2, b2); - right = Math.Min(a1, b1); - } - } - - return left < right; - } - - private bool JoinHorz(OutPt op1, OutPt op1b, OutPt op2, OutPt op2b, Vector2 pt, bool discardLeft) - { - Direction dir1 = op1.Pt.X > op1b.Pt.X ? Direction.RightToLeft : Direction.LeftToRight; - Direction dir2 = op2.Pt.X > op2b.Pt.X ? Direction.RightToLeft : Direction.LeftToRight; - if (dir1 == dir2) - { - return false; - } - - // When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we - // want Op1b to be on the Right. (And likewise with Op2 and Op2b.) - // So, to facilitate this while inserting Op1b and Op2b ... - // when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b, - // otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.) - if (dir1 == Direction.LeftToRight) - { - while (op1.Next.Pt.X <= pt.X && - op1.Next.Pt.X >= op1.Pt.X && op1.Next.Pt.Y == pt.Y) - { - op1 = op1.Next; - } - - if (discardLeft && (op1.Pt.X != pt.X)) - { - op1 = op1.Next; - } - - op1b = this.DupOutPt(op1, !discardLeft); - if (op1b.Pt != pt) - { - op1 = op1b; - op1.Pt = pt; - op1b = this.DupOutPt(op1, !discardLeft); - } - } - else - { - while (op1.Next.Pt.X >= pt.X && - op1.Next.Pt.X <= op1.Pt.X && - op1.Next.Pt.Y == pt.Y) - { - op1 = op1.Next; - } - - if (!discardLeft && (op1.Pt.X != pt.X)) - { - op1 = op1.Next; - } - - op1b = this.DupOutPt(op1, discardLeft); - if (op1b.Pt != pt) - { - op1 = op1b; - op1.Pt = pt; - op1b = this.DupOutPt(op1, discardLeft); - } - } - - if (dir2 == Direction.LeftToRight) - { - while (op2.Next.Pt.X <= pt.X && - op2.Next.Pt.X >= op2.Pt.X && - op2.Next.Pt.Y == pt.Y) - { - op2 = op2.Next; - } - - if (discardLeft && (op2.Pt.X != pt.X)) - { - op2 = op2.Next; - } - - op2b = this.DupOutPt(op2, !discardLeft); - if (op2b.Pt != pt) - { - op2 = op2b; - op2.Pt = pt; - op2b = this.DupOutPt(op2, !discardLeft); - } - } - else - { - while (op2.Next.Pt.X >= pt.X && - op2.Next.Pt.X <= op2.Pt.X && - op2.Next.Pt.Y == pt.Y) - { - op2 = op2.Next; - } - - if (!discardLeft && (op2.Pt.X != pt.X)) - { - op2 = op2.Next; - } - - op2b = this.DupOutPt(op2, discardLeft); - if (op2b.Pt != pt) - { - op2 = op2b; - op2.Pt = pt; - op2b = this.DupOutPt(op2, discardLeft); - } - } - - if ((dir1 == Direction.LeftToRight) == discardLeft) - { - op1.Prev = op2; - op2.Next = op1; - op1b.Next = op2b; - op2b.Prev = op1b; - } - else - { - op1.Next = op2; - op2.Prev = op1; - op1b.Prev = op2b; - op2b.Next = op1b; - } - - return true; - } - - private bool JoinPoints(Join j, OutRec outRec1, OutRec outRec2) - { - OutPt op1 = j.OutPt1, op1b; - OutPt op2 = j.OutPt2, op2b; - - // There are 3 kinds of joins for output polygons ... - // 1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere - // along (horizontal) collinear edges (& Join.OffPt is on the same horizontal). - // 2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same - // location at the Bottom of the overlapping segment (& Join.OffPt is above). - // 3. StrictlySimple joins where edges touch but are not collinear and where - // Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point. - bool isHorizontal = j.OutPt1.Pt.Y == j.OffPt.Y; - - if (isHorizontal && (j.OffPt == j.OutPt1.Pt) && (j.OffPt == j.OutPt2.Pt)) - { - // Strictly Simple join ... - if (outRec1 != outRec2) - { - return false; - } - - op1b = j.OutPt1.Next; - while (op1b != op1 && (op1b.Pt == j.OffPt)) - { - op1b = op1b.Next; - } - - bool reverse1 = op1b.Pt.Y > j.OffPt.Y; - op2b = j.OutPt2.Next; - while (op2b != op2 && (op2b.Pt == j.OffPt)) - { - op2b = op2b.Next; - } - - bool reverse2 = op2b.Pt.Y > j.OffPt.Y; - if (reverse1 == reverse2) - { - return false; - } - - if (reverse1) - { - op1b = this.DupOutPt(op1, false); - op2b = this.DupOutPt(op2, true); - op1.Prev = op2; - op2.Next = op1; - op1b.Next = op2b; - op2b.Prev = op1b; - j.OutPt1 = op1; - j.OutPt2 = op1b; - return true; - } - else - { - op1b = this.DupOutPt(op1, true); - op2b = this.DupOutPt(op2, false); - op1.Next = op2; - op2.Prev = op1; - op1b.Prev = op2b; - op2b.Next = op1b; - j.OutPt1 = op1; - j.OutPt2 = op1b; - return true; - } - } - else if (isHorizontal) - { - // treat horizontal joins differently to non-horizontal joins since with - // them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt - // may be anywhere along the horizontal edge. - op1b = op1; - while (op1.Prev.Pt.Y == op1.Pt.Y && op1.Prev != op1b && op1.Prev != op2) - { - op1 = op1.Prev; - } - - while (op1b.Next.Pt.Y == op1b.Pt.Y && op1b.Next != op1 && op1b.Next != op2) - { - op1b = op1b.Next; - } - - if (op1b.Next == op1 || op1b.Next == op2) - { - return false; // a flat 'polygon' - } - - op2b = op2; - while (op2.Prev.Pt.Y == op2.Pt.Y && op2.Prev != op2b && op2.Prev != op1b) - { - op2 = op2.Prev; - } - - while (op2b.Next.Pt.Y == op2b.Pt.Y && op2b.Next != op2 && op2b.Next != op1) - { - op2b = op2b.Next; - } - - if (op2b.Next == op2 || op2b.Next == op1) - { - return false; // a flat 'polygon' - } - - float left, right; - - // Op1 -. Op1b & Op2 -. Op2b are the extremites of the horizontal edges - if (!this.GetOverlap(op1.Pt.X, op1b.Pt.X, op2.Pt.X, op2b.Pt.X, out left, out right)) - { - return false; - } - - // DiscardLeftSide: when overlapping edges are joined, a spike will created - // which needs to be cleaned up. However, we don't want Op1 or Op2 caught up - // on the discard Side as either may still be needed for other joins ... - Vector2 pt; - bool discardLeftSide; - if (op1.Pt.X >= left && op1.Pt.X <= right) - { - pt = op1.Pt; - discardLeftSide = op1.Pt.X > op1b.Pt.X; - } - else if (op2.Pt.X >= left && op2.Pt.X <= right) - { - pt = op2.Pt; - discardLeftSide = op2.Pt.X > op2b.Pt.X; - } - else if (op1b.Pt.X >= left && op1b.Pt.X <= right) - { - pt = op1b.Pt; - discardLeftSide = op1b.Pt.X > op1.Pt.X; - } - else - { - pt = op2b.Pt; - discardLeftSide = op2b.Pt.X > op2.Pt.X; - } - - j.OutPt1 = op1; - j.OutPt2 = op2; - return this.JoinHorz(op1, op1b, op2, op2b, pt, discardLeftSide); - } - else - { - // nb: For non-horizontal joins ... - // 1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y - // 2. Jr.OutPt1.Pt > Jr.OffPt.Y - - // make sure the polygons are correctly oriented ... - op1b = op1.Next; - while ((op1b.Pt == op1.Pt) && (op1b != op1)) - { - op1b = op1b.Next; - } - - bool reverse1 = (op1b.Pt.Y > op1.Pt.Y) || !SlopesEqual(op1.Pt, op1b.Pt, j.OffPt); - if (reverse1) - { - op1b = op1.Prev; - while ((op1b.Pt == op1.Pt) && (op1b != op1)) - { - op1b = op1b.Prev; - } - - if ((op1b.Pt.Y > op1.Pt.Y) || - !SlopesEqual(op1.Pt, op1b.Pt, j.OffPt)) - { - return false; - } - } - - op2b = op2.Next; - while ((op2b.Pt == op2.Pt) && (op2b != op2)) - { - op2b = op2b.Next; - } - - bool reverse2 = (op2b.Pt.Y > op2.Pt.Y) || !SlopesEqual(op2.Pt, op2b.Pt, j.OffPt); - if (reverse2) - { - op2b = op2.Prev; - while ((op2b.Pt == op2.Pt) && (op2b != op2)) - { - op2b = op2b.Prev; - } - - if ((op2b.Pt.Y > op2.Pt.Y) || - !SlopesEqual(op2.Pt, op2b.Pt, j.OffPt)) - { - return false; - } - } - - if ((op1b == op1) || (op2b == op2) || (op1b == op2b) || - ((outRec1 == outRec2) && (reverse1 == reverse2))) - { - return false; - } - - if (reverse1) - { - op1b = this.DupOutPt(op1, false); - op2b = this.DupOutPt(op2, true); - op1.Prev = op2; - op2.Next = op1; - op1b.Next = op2b; - op2b.Prev = op1b; - j.OutPt1 = op1; - j.OutPt2 = op1b; - return true; - } - else - { - op1b = this.DupOutPt(op1, true); - op2b = this.DupOutPt(op2, false); - op1.Next = op2; - op2.Prev = op1; - op1b.Prev = op2b; - op2b.Next = op1b; - j.OutPt1 = op1; - j.OutPt2 = op1b; - return true; - } - } - } - - private void FixupFirstLefts1(OutRec oldOutRec, OutRec newOutRec) - { - foreach (OutRec outRec in this.polyOuts) - { - OutRec firstLeft = ParseFirstLeft(outRec.FirstLeft); - if (outRec.Pts != null && firstLeft == oldOutRec) - { - if (Poly2ContainsPoly1(outRec.Pts, newOutRec.Pts)) - { - outRec.FirstLeft = newOutRec; - } - } - } - } - - private void FixupFirstLefts2(OutRec innerOutRec, OutRec outerOutRec) - { - // A polygon has split into two such that one is now the inner of the other. - // It's possible that these polygons now wrap around other polygons, so check - // every polygon that's also contained by OuterOutRec's FirstLeft container - // (including nil) to see if they've become inner to the new inner polygon ... - OutRec orfl = outerOutRec.FirstLeft; - foreach (OutRec outRec in this.polyOuts) - { - if (outRec.Pts == null || outRec == outerOutRec || outRec == innerOutRec) - { - continue; - } - - OutRec firstLeft = ParseFirstLeft(outRec.FirstLeft); - if (firstLeft != orfl && firstLeft != innerOutRec && firstLeft != outerOutRec) - { - continue; - } - - if (Poly2ContainsPoly1(outRec.Pts, innerOutRec.Pts)) - { - outRec.FirstLeft = innerOutRec; - } - else if (Poly2ContainsPoly1(outRec.Pts, outerOutRec.Pts)) - { - outRec.FirstLeft = outerOutRec; - } - else if (outRec.FirstLeft == innerOutRec || outRec.FirstLeft == outerOutRec) - { - outRec.FirstLeft = orfl; - } - } - } - - private void FixupFirstLefts3(OutRec oldOutRec, OutRec newOutRec) - { - // same as FixupFirstLefts1 but doesn't call Poly2ContainsPoly1() - foreach (OutRec outRec in this.polyOuts) - { - OutRec firstLeft = ParseFirstLeft(outRec.FirstLeft); - if (outRec.Pts != null && outRec.FirstLeft == oldOutRec) - { - outRec.FirstLeft = newOutRec; - } - } - } - - private void JoinCommonEdges() - { - for (int i = 0; i < this.joins.Count; i++) - { - Join join = this.joins[i]; - - OutRec outRec1 = this.GetOutRec(join.OutPt1.Idx); - OutRec outRec2 = this.GetOutRec(join.OutPt2.Idx); - - if (outRec1.Pts == null || outRec2.Pts == null) - { - continue; - } - - if (outRec1.IsOpen || outRec2.IsOpen) - { - continue; - } - - // get the polygon fragment with the correct hole state (FirstLeft) - // before calling JoinPoints() ... - OutRec holeStateRec; - if (outRec1 == outRec2) - { - holeStateRec = outRec1; - } - else if (this.OutRec1RightOfOutRec2(outRec1, outRec2)) - { - holeStateRec = outRec2; - } - else if (this.OutRec1RightOfOutRec2(outRec2, outRec1)) - { - holeStateRec = outRec1; - } - else - { - holeStateRec = this.GetLowermostRec(outRec1, outRec2); - } - - if (!this.JoinPoints(join, outRec1, outRec2)) - { - continue; - } - - if (outRec1 == outRec2) - { - // instead of joining two polygons, we've just created a new one by - // splitting one polygon into two. - outRec1.Pts = join.OutPt1; - outRec1.BottomPt = null; - outRec2 = this.CreateOutRec(); - outRec2.Pts = join.OutPt2; - - // update all OutRec2.Pts Idx's ... - this.UpdateOutPtIdxs(outRec2); - - if (Poly2ContainsPoly1(outRec2.Pts, outRec1.Pts)) - { - // outRec1 contains outRec2 ... - outRec2.IsHole = !outRec1.IsHole; - outRec2.FirstLeft = outRec1; - - if (this.usingPolyTree) - { - this.FixupFirstLefts2(outRec2, outRec1); - } - } - else if (Poly2ContainsPoly1(outRec1.Pts, outRec2.Pts)) - { - // outRec2 contains outRec1 ... - outRec2.IsHole = outRec1.IsHole; - outRec1.IsHole = !outRec2.IsHole; - outRec2.FirstLeft = outRec1.FirstLeft; - outRec1.FirstLeft = outRec2; - - if (this.usingPolyTree) - { - this.FixupFirstLefts2(outRec1, outRec2); - } - } - else - { - // the 2 polygons are completely separate ... - outRec2.IsHole = outRec1.IsHole; - outRec2.FirstLeft = outRec1.FirstLeft; - - // fixup FirstLeft pointers that may need reassigning to OutRec2 - if (this.usingPolyTree) - { - this.FixupFirstLefts1(outRec1, outRec2); - } - } - } - else - { - // joined 2 polygons together ... - outRec2.Pts = null; - outRec2.BottomPt = null; - outRec2.Idx = outRec1.Idx; - - outRec1.IsHole = holeStateRec.IsHole; - if (holeStateRec == outRec2) - { - outRec1.FirstLeft = outRec2.FirstLeft; - } - - outRec2.FirstLeft = outRec1; - - // fixup FirstLeft pointers that may need reassigning to OutRec1 - if (this.usingPolyTree) - { - this.FixupFirstLefts3(outRec2, outRec1); - } - } - } - } - - private void UpdateOutPtIdxs(OutRec outrec) - { - OutPt op = outrec.Pts; - do - { - op.Idx = outrec.Idx; - op = op.Prev; - } - while (op != outrec.Pts); - } - - private void DoSimplePolygons() - { - int i = 0; - while (i < this.polyOuts.Count) - { - OutRec outrec = this.polyOuts[i++]; - OutPt op = outrec.Pts; - if (op == null || outrec.IsOpen) - { - continue; - } - - do - { - // for each Pt in Polygon until duplicate found do ... - OutPt op2 = op.Next; - while (op2 != outrec.Pts) - { - if ((op.Pt == op2.Pt) && op2.Next != op && op2.Prev != op) - { - // split the polygon into two ... - OutPt op3 = op.Prev; - OutPt op4 = op2.Prev; - op.Prev = op4; - op4.Next = op; - op2.Prev = op3; - op3.Next = op2; - - outrec.Pts = op; - OutRec outrec2 = this.CreateOutRec(); - outrec2.Pts = op2; - this.UpdateOutPtIdxs(outrec2); - if (Poly2ContainsPoly1(outrec2.Pts, outrec.Pts)) - { - // OutRec2 is contained by OutRec1 ... - outrec2.IsHole = !outrec.IsHole; - outrec2.FirstLeft = outrec; - if (this.usingPolyTree) - { - this.FixupFirstLefts2(outrec2, outrec); - } - } - else - if (Poly2ContainsPoly1(outrec.Pts, outrec2.Pts)) - { - // OutRec1 is contained by OutRec2 ... - outrec2.IsHole = outrec.IsHole; - outrec.IsHole = !outrec2.IsHole; - outrec2.FirstLeft = outrec.FirstLeft; - outrec.FirstLeft = outrec2; - if (this.usingPolyTree) - { - this.FixupFirstLefts2(outrec, outrec2); - } - } - else - { - // the 2 polygons are separate ... - outrec2.IsHole = outrec.IsHole; - outrec2.FirstLeft = outrec.FirstLeft; - if (this.usingPolyTree) - { - this.FixupFirstLefts1(outrec, outrec2); - } - } - - op2 = op; // ie get ready for the next iteration - } - - op2 = op2.Next; - } - - op = op.Next; - } - while (op != outrec.Pts); - } - } - - private double Area(OutRec outRec) - { - return this.Area(outRec.Pts); - } - - private double Area(OutPt op) - { - OutPt opFirst = op; - if (op == null) - { - return 0; - } - - double a = 0; - do - { - a = a + ((op.Prev.Pt.X + op.Pt.X) * (op.Prev.Pt.Y - op.Pt.Y)); - op = op.Next; - } - while (op != opFirst); - - return a * 0.5; - } - - private void SetDx(TEdge e) - { - e.Delta.X = e.Top.X - e.Bot.X; - e.Delta.Y = e.Top.Y - e.Bot.Y; - if (e.Delta.Y == 0) - { - e.Dx = HorizontalDeltaLimit; - } - else - { - e.Dx = e.Delta.X / e.Delta.Y; - } - } - - private void InsertLocalMinima(LocalMinima newLm) - { - if (this.minimaList == null) - { - this.minimaList = newLm; - } - else if (newLm.Y >= this.minimaList.Y) - { - newLm.Next = this.minimaList; - this.minimaList = newLm; - } - else - { - LocalMinima tmpLm = this.minimaList; - while (tmpLm.Next != null && (newLm.Y < tmpLm.Next.Y)) - { - tmpLm = tmpLm.Next; - } - - newLm.Next = tmpLm.Next; - tmpLm.Next = newLm; - } - } - - private bool PopLocalMinima(float y, out LocalMinima current) - { - current = this.currentLM; - if (this.currentLM != null && this.currentLM.Y == y) - { - this.currentLM = this.currentLM.Next; - return true; - } - - return false; - } - - private void Reset() - { - this.currentLM = this.minimaList; - if (this.currentLM == null) - { - return; // ie nothing to process - } - - // reset all edges ... - this.scanbeam = null; - LocalMinima lm = this.minimaList; - while (lm != null) - { - this.InsertScanbeam(lm.Y); - TEdge e = lm.LeftBound; - if (e != null) - { - e.Curr = e.Bot; - e.OutIdx = Unassigned; - } - - e = lm.RightBound; - if (e != null) - { - e.Curr = e.Bot; - e.OutIdx = Unassigned; - } - - lm = lm.Next; - } - - this.activeEdges = null; - } - - private void InsertScanbeam(float y) - { - // single-linked list: sorted descending, ignoring dups. - if (this.scanbeam == null) - { - this.scanbeam = new Scanbeam(); - this.scanbeam.Next = null; - this.scanbeam.Y = y; - } - else if (y > this.scanbeam.Y) - { - Scanbeam newSb = new Scanbeam(); - newSb.Y = y; - newSb.Next = this.scanbeam; - this.scanbeam = newSb; - } - else - { - Scanbeam sb2 = this.scanbeam; - while (sb2.Next != null && (y <= sb2.Next.Y)) - { - sb2 = sb2.Next; - } - - if (y == sb2.Y) - { - return; // ie ignores duplicates - } - - Scanbeam newSb = new Scanbeam(); - newSb.Y = y; - newSb.Next = sb2.Next; - sb2.Next = newSb; - } - } - - private bool PopScanbeam(out float y) - { - if (this.scanbeam == null) - { - y = 0; - return false; - } - - y = this.scanbeam.Y; - this.scanbeam = this.scanbeam.Next; - return true; - } - - private bool LocalMinimaPending() - { - return this.currentLM != null; - } - - private OutRec CreateOutRec() - { - OutRec result = new OutRec(); - result.Idx = Unassigned; - result.IsHole = false; - result.IsOpen = false; - result.FirstLeft = null; - result.Pts = null; - result.BottomPt = null; - result.PolyNode = null; - this.polyOuts.Add(result); - result.Idx = this.polyOuts.Count - 1; - return result; - } - - private void DisposeOutRec(int index) - { - OutRec outRec = this.polyOuts[index]; - outRec.Pts = null; - outRec = null; - this.polyOuts[index] = null; - } - - private void UpdateEdgeIntoAEL(ref TEdge e) - { - if (e.NextInLML == null) - { - throw new ClipperException("UpdateEdgeIntoAEL: invalid call"); - } - - TEdge aelPrev = e.PrevInAEL; - TEdge aelNext = e.NextInAEL; - e.NextInLML.OutIdx = e.OutIdx; - if (aelPrev != null) - { - aelPrev.NextInAEL = e.NextInLML; - } - else - { - this.activeEdges = e.NextInLML; - } - - if (aelNext != null) - { - aelNext.PrevInAEL = e.NextInLML; - } - - e.NextInLML.Side = e.Side; - e.NextInLML.WindDelta = e.WindDelta; - e.NextInLML.WindCnt = e.WindCnt; - e.NextInLML.WindCnt2 = e.WindCnt2; - e = e.NextInLML; - e.Curr = e.Bot; - e.PrevInAEL = aelPrev; - e.NextInAEL = aelNext; - if (!IsHorizontal(e)) - { - this.InsertScanbeam(e.Top.Y); - } - } - - private void SwapPositionsInAEL(TEdge edge1, TEdge edge2) - { - // check that one or other edge hasn't already been removed from AEL ... - if (edge1.NextInAEL == edge1.PrevInAEL || - edge2.NextInAEL == edge2.PrevInAEL) - { - return; - } - - if (edge1.NextInAEL == edge2) - { - TEdge next = edge2.NextInAEL; - if (next != null) - { - next.PrevInAEL = edge1; - } - - TEdge prev = edge1.PrevInAEL; - if (prev != null) - { - prev.NextInAEL = edge2; - } - - edge2.PrevInAEL = prev; - edge2.NextInAEL = edge1; - edge1.PrevInAEL = edge2; - edge1.NextInAEL = next; - } - else if (edge2.NextInAEL == edge1) - { - TEdge next = edge1.NextInAEL; - if (next != null) - { - next.PrevInAEL = edge2; - } - - TEdge prev = edge2.PrevInAEL; - if (prev != null) - { - prev.NextInAEL = edge1; - } - - edge1.PrevInAEL = prev; - edge1.NextInAEL = edge2; - edge2.PrevInAEL = edge1; - edge2.NextInAEL = next; - } - else - { - TEdge next = edge1.NextInAEL; - TEdge prev = edge1.PrevInAEL; - edge1.NextInAEL = edge2.NextInAEL; - if (edge1.NextInAEL != null) - { - edge1.NextInAEL.PrevInAEL = edge1; - } - - edge1.PrevInAEL = edge2.PrevInAEL; - if (edge1.PrevInAEL != null) - { - edge1.PrevInAEL.NextInAEL = edge1; - } - - edge2.NextInAEL = next; - if (edge2.NextInAEL != null) - { - edge2.NextInAEL.PrevInAEL = edge2; - } - - edge2.PrevInAEL = prev; - if (edge2.PrevInAEL != null) - { - edge2.PrevInAEL.NextInAEL = edge2; - } - } - - if (edge1.PrevInAEL == null) - { - this.activeEdges = edge1; - } - else if (edge2.PrevInAEL == null) - { - this.activeEdges = edge2; - } - } - - private void DeleteFromAEL(TEdge e) - { - TEdge aelPrev = e.PrevInAEL; - TEdge aelNext = e.NextInAEL; - if (aelPrev == null && aelNext == null && (e != this.activeEdges)) - { - return; // already deleted - } - - if (aelPrev != null) - { - aelPrev.NextInAEL = aelNext; - } - else - { - this.activeEdges = aelNext; - } - - if (aelNext != null) - { - aelNext.PrevInAEL = aelPrev; - } - - e.NextInAEL = null; - e.PrevInAEL = null; - } - - private void InitEdge2(TEdge e, PolyType polyType) - { - if (e.Curr.Y >= e.Next.Curr.Y) - { - e.Bot = e.Curr; - e.Top = e.Next.Curr; - } - else - { - e.Top = e.Curr; - e.Bot = e.Next.Curr; - } - - this.SetDx(e); - e.PolyTyp = polyType; - } - - private TEdge ProcessBound(TEdge edge, bool leftBoundIsForward) - { - TEdge eStart, result = edge; - TEdge horz; - - if (result.OutIdx == Skip) - { - // check if there are edges beyond the skip edge in the bound and if so - // create another LocMin and calling ProcessBound once more ... - edge = result; - if (leftBoundIsForward) - { - while (edge.Top.Y == edge.Next.Bot.Y) - { - edge = edge.Next; - } - - while (edge != result && edge.Dx == HorizontalDeltaLimit) - { - edge = edge.Prev; - } - } - else - { - while (edge.Top.Y == edge.Prev.Bot.Y) - { - edge = edge.Prev; - } - - while (edge != result && edge.Dx == HorizontalDeltaLimit) - { - edge = edge.Next; - } - } - - if (edge == result) - { - if (leftBoundIsForward) - { - result = edge.Next; - } - else - { - result = edge.Prev; - } - } - else - { - // there are more edges in the bound beyond result starting with E - if (leftBoundIsForward) - { - edge = result.Next; - } - else - { - edge = result.Prev; - } - - LocalMinima locMin = new LocalMinima(); - locMin.Next = null; - locMin.Y = edge.Bot.Y; - locMin.LeftBound = null; - locMin.RightBound = edge; - edge.WindDelta = 0; - result = this.ProcessBound(edge, leftBoundIsForward); - this.InsertLocalMinima(locMin); - } - - return result; - } - - if (edge.Dx == HorizontalDeltaLimit) - { - // We need to be careful with open paths because this may not be a - // true local minima (ie E may be following a skip edge). - // Also, consecutive horz. edges may start heading left before going right. - if (leftBoundIsForward) - { - eStart = edge.Prev; - } - else - { - eStart = edge.Next; - } - - // ie an adjoining horizontal skip edge - if (eStart.Dx == HorizontalDeltaLimit) - { - if (eStart.Bot.X != edge.Bot.X && eStart.Top.X != edge.Bot.X) - { - ReverseHorizontal(edge); - } - } - else if (eStart.Bot.X != edge.Bot.X) - { - ReverseHorizontal(edge); - } - } - - eStart = edge; - if (leftBoundIsForward) - { - while (result.Top.Y == result.Next.Bot.Y && result.Next.OutIdx != Skip) - { - result = result.Next; - } - - if (result.Dx == HorizontalDeltaLimit && result.Next.OutIdx != Skip) - { - // nb: at the top of a bound, horizontals are added to the bound - // only when the preceding edge attaches to the horizontal's left vertex - // unless a Skip edge is encountered when that becomes the top divide - horz = result; - while (horz.Prev.Dx == HorizontalDeltaLimit) - { - horz = horz.Prev; - } - - if (horz.Prev.Top.X > result.Next.Top.X) - { - result = horz.Prev; - } - } - - while (edge != result) - { - edge.NextInLML = edge.Next; - if (edge.Dx == HorizontalDeltaLimit && edge != eStart && edge.Bot.X != edge.Prev.Top.X) - { - ReverseHorizontal(edge); - } - - edge = edge.Next; - } - - if (edge.Dx == HorizontalDeltaLimit && edge != eStart && edge.Bot.X != edge.Prev.Top.X) - { - ReverseHorizontal(edge); - } - - result = result.Next; // move to the edge just beyond current bound - } - else - { - while (result.Top.Y == result.Prev.Bot.Y && result.Prev.OutIdx != Skip) - { - result = result.Prev; - } - - if (result.Dx == HorizontalDeltaLimit && result.Prev.OutIdx != Skip) - { - horz = result; - while (horz.Next.Dx == HorizontalDeltaLimit) - { - horz = horz.Next; - } - - if (horz.Next.Top.X == result.Prev.Top.X || horz.Next.Top.X > result.Prev.Top.X) - { - result = horz.Next; - } - } - - while (edge != result) - { - edge.NextInLML = edge.Prev; - if (edge.Dx == HorizontalDeltaLimit && edge != eStart && edge.Bot.X != edge.Next.Top.X) - { - ReverseHorizontal(edge); - } - - edge = edge.Prev; - } - - if (edge.Dx == HorizontalDeltaLimit && edge != eStart && edge.Bot.X != edge.Next.Top.X) - { - ReverseHorizontal(edge); - } - - result = result.Prev; // move to the edge just beyond current bound - } - - return result; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/ClipperException.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/ClipperException.cs deleted file mode 100644 index cefd268af..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/ClipperException.cs +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// Clipper Exception - /// - /// - internal class ClipperException : Exception - { - /// - /// Initializes a new instance of the class. - /// - /// The description. - public ClipperException(string description) - : base(description) - { - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/Direction.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/Direction.cs deleted file mode 100644 index 5fa877fd4..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/Direction.cs +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// ??? - /// - internal enum Direction - { - /// - /// The right to left - /// - RightToLeft, - - /// - /// The left to right - /// - LeftToRight - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/EdgeSide.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/EdgeSide.cs deleted file mode 100644 index 5093958d1..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/EdgeSide.cs +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// ?? - /// - internal enum EdgeSide - { - /// - /// The left - /// - Left, - - /// - /// The right - /// - Right - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/IntersectNode.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/IntersectNode.cs deleted file mode 100644 index 7cd0562b0..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/IntersectNode.cs +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// ?? - /// - internal class IntersectNode - { -#pragma warning disable SA1401 // Field must be private - /// - /// The edge1 - /// - internal TEdge Edge1; - - /// - /// The edge2 - /// - internal TEdge Edge2; - - /// - /// The pt - /// - internal System.Numerics.Vector2 Pt; -#pragma warning restore SA1401 // Field must be private - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/IntersectNodeSort.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/IntersectNodeSort.cs deleted file mode 100644 index f4524fa9b..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/IntersectNodeSort.cs +++ /dev/null @@ -1,48 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// Compares s - /// - internal class IntersectNodeSort : IComparer - { - /// - /// Compares the specified node1. - /// - /// The node1. - /// The node2. - /// - /// 1 if node2 %gt; node1 - /// -1 if node2 $lt; node1 - /// 0 if same - /// - public int Compare(IntersectNode node1, IntersectNode node2) - { - float i = node2.Pt.Y - node1.Pt.Y; - if (i > 0) - { - return 1; - } - else if (i < 0) - { - return -1; - } - else - { - return 0; - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/Join.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/Join.cs deleted file mode 100644 index be948fbf7..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/Join.cs +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// ?? - /// - internal class Join - { -#pragma warning disable SA1401 // Field must be private - /// - /// The out PT1 - /// - internal OutPt OutPt1; - - /// - /// The out PT2 - /// - internal OutPt OutPt2; - - /// - /// The off pt - /// - internal System.Numerics.Vector2 OffPt; -#pragma warning restore SA1401 // Field must be private - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/LocalMinima.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/LocalMinima.cs deleted file mode 100644 index b48a53cab..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/LocalMinima.cs +++ /dev/null @@ -1,44 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// ?? - /// - internal class LocalMinima - { -#pragma warning disable SA1401 // Field must be private - /// - /// The y - /// - internal float Y; - - /// - /// The left bound - /// - internal TEdge LeftBound; - - /// - /// The right bound - /// - internal TEdge RightBound; - - /// - /// The next - /// - internal LocalMinima Next; - -#pragma warning restore SA1401 // Field must be private - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/Maxima.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/Maxima.cs deleted file mode 100644 index 85168e8e8..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/Maxima.cs +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// ?? - /// - internal class Maxima - { -#pragma warning disable SA1401 // Field must be private - /// - /// The x - /// - internal float X; - - /// - /// The next - /// - internal Maxima Next; - - /// - /// The previous - /// - internal Maxima Prev; -#pragma warning restore SA1401 // Field must be private - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/OutPt.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/OutPt.cs deleted file mode 100644 index 8dae5780a..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/OutPt.cs +++ /dev/null @@ -1,43 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// ?? - /// - internal class OutPt - { -#pragma warning disable SA1401 // Field must be private - /// - /// The index - /// - internal int Idx; - - /// - /// The pt - /// - internal System.Numerics.Vector2 Pt; - - /// - /// The next - /// - internal OutPt Next; - - /// - /// The previous - /// - internal OutPt Prev; -#pragma warning restore SA1401 // Field must be private - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/OutRec.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/OutRec.cs deleted file mode 100644 index 7c2d41a72..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/OutRec.cs +++ /dev/null @@ -1,64 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// OutRec: contains a path in the clipping solution. Edges in the AEL will - /// carry a pointer to an OutRec when they are part of the clipping solution. - /// - internal class OutRec - { -#pragma warning disable SA1401 // Field must be private - /// - /// The source path - /// - internal IPath SourcePath; - - /// - /// The index - /// - internal int Idx; - - /// - /// The is hole - /// - internal bool IsHole; - - /// - /// The is open - /// - internal bool IsOpen; - - /// - /// The first left - /// - internal OutRec FirstLeft; - - /// - /// The PTS - /// - internal OutPt Pts; - - /// - /// The bottom pt - /// - internal OutPt BottomPt; - - /// - /// The poly node - /// - internal PolyNode PolyNode; -#pragma warning restore SA1401 // Field must be private - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyNode.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyNode.cs deleted file mode 100644 index 9d9c35504..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyNode.cs +++ /dev/null @@ -1,179 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// Poly Node - /// - internal class PolyNode - { -#pragma warning disable SA1401 // Field must be private - /// - /// The polygon - /// - internal List Polygon = new List(); - - /// - /// The index - /// - internal int Index; - - /// - /// The childs - /// - protected List children = new List(); - - private PolyNode parent; -#pragma warning restore SA1401 // Field must be private - - /// - /// Gets the child count. - /// - /// - /// The child count. - /// - public int ChildCount - { - get { return this.children.Count; } - } - - /// - /// Gets the contour. - /// - /// - /// The contour. - /// - public List Contour - { - get { return this.Polygon; } - } - - /// - /// Gets the childs. - /// - /// - /// The childs. - /// - public List Children - { - get { return this.children; } - } - - /// - /// Gets or sets the parent. - /// - /// - /// The parent. - /// - public PolyNode Parent - { - get { return this.parent; } - internal set { this.parent = value; } - } - - /// - /// Gets a value indicating whether this instance is hole. - /// - /// - /// true if this instance is hole; otherwise, false. - /// - public bool IsHole - { - get { return this.IsHoleNode(); } - } - - /// - /// Gets or sets a value indicating whether this instance is open. - /// - /// - /// true if this instance is open; otherwise, false. - /// - public bool IsOpen { get; set; } - - /// - /// Gets or sets the source path. - /// - /// - /// The source path. - /// - public IPath SourcePath { get; internal set; } - - /// - /// Gets the next. - /// - /// The next node - public PolyNode GetNext() - { - if (this.children.Count > 0) - { - return this.children[0]; - } - else - { - return this.GetNextSiblingUp(); - } - } - - /// - /// Adds the child. - /// - /// The child. - internal void AddChild(PolyNode child) - { - int cnt = this.children.Count; - this.children.Add(child); - child.parent = this; - child.Index = cnt; - } - - /// - /// Gets the next sibling up. - /// - /// The next sibling up - internal PolyNode GetNextSiblingUp() - { - if (this.parent == null) - { - return null; - } - else if (this.Index == this.parent.children.Count - 1) - { - return this.parent.GetNextSiblingUp(); - } - else - { - return this.parent.Children[this.Index + 1]; - } - } - - /// - /// Determines whether [is hole node]. - /// - /// - /// true if [is hole node]; otherwise, false. - /// - private bool IsHoleNode() - { - bool result = true; - PolyNode node = this.parent; - while (node != null) - { - result = !result; - node = node.parent; - } - - return result; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyTree.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyTree.cs deleted file mode 100644 index 3c35f389c..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyTree.cs +++ /dev/null @@ -1,81 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// Poly Tree - /// - /// - internal class PolyTree : PolyNode - { -#pragma warning disable SA1401 // Field must be private - /// - /// All polys - /// - internal List AllPolys = new List(); -#pragma warning restore SA1401 // Field must be private - - /// - /// Gets the total. - /// - /// - /// The total. - /// - public int Total - { - get - { - int result = this.AllPolys.Count; - - // with negative offsets, ignore the hidden outer polygon ... - if (result > 0 && this.Children[0] != this.AllPolys[0]) - { - result--; - } - - return result; - } - } - - /// - /// Clears this instance. - /// - public void Clear() - { - for (int i = 0; i < this.AllPolys.Count; i++) - { - this.AllPolys[i] = null; - } - - this.AllPolys.Clear(); - this.Children.Clear(); - } - - /// - /// Gets the first. - /// - /// the first node - public PolyNode GetFirst() - { - if (this.Children.Count > 0) - { - return this.Children[0]; - } - else - { - return null; - } - } - } -} diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyType.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyType.cs deleted file mode 100644 index 2a130f509..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/PolyType.cs +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// Poly Type - /// - internal enum PolyType - { - /// - /// The subject - /// - Subject, - - /// - /// The clip - /// - Clip - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/README.md b/src/ImageSharp.Drawing/Shapes/PolygonClipper/README.md deleted file mode 100644 index c0f2ff65f..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# Clipper - -License details for code in this folder, this is code original written by **Angus Johnson** - -The license header onthe original file which has now be split across multiple files in this folder. - -``` -/******************************************************************************* -* * -* Author : Angus Johnson * -* Version : 6.4.0 * -* Date : 2 July 2015 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2015 * -* * -* License: * -* Use, modification & distribution is subject to Boost Software License Ver 1. * -* http://www.boost.org/LICENSE_1_0.txt * -* * -* Attributions: * -* The code in this library is an extension of Bala Vatti's clipping algorithm: * -* "A generic solution to polygon clipping" * -* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * -* http://portal.acm.org/citation.cfm?id=129906 * -* * -* Computer graphics and geometric modeling: implementation and algorithms * -* By Max K. Agoston * -* Springer; 1 edition (January 4, 2005) * -* http://books.google.com/books?q=vatti+clipping+agoston * -* * -* See also: * -* "Polygon Offsetting by Computing Winding Numbers" * -* Paper no. DETC2005-85513 pp. 565-575 * -* ASME 2005 International Design Engineering Technical Conferences * -* and Computers and Information in Engineering Conference (IDETC/CIE2005) * -* September 24-28, 2005 , Long Beach, California, USA * -* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * -* * -*******************************************************************************/ -``` \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/Scanbeam.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/Scanbeam.cs deleted file mode 100644 index 28b341004..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/Scanbeam.cs +++ /dev/null @@ -1,33 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// Scanbeam - /// - internal class Scanbeam // would this work as a struct? - { -#pragma warning disable SA1401 // Field must be private - /// - /// The y - /// - internal float Y; - - /// - /// The next - /// - internal Scanbeam Next; -#pragma warning restore SA1401 // Field must be private - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/PolygonClipper/TEdge.cs b/src/ImageSharp.Drawing/Shapes/PolygonClipper/TEdge.cs deleted file mode 100644 index 97f5b2ec7..000000000 --- a/src/ImageSharp.Drawing/Shapes/PolygonClipper/TEdge.cs +++ /dev/null @@ -1,118 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes.PolygonClipper -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Runtime.CompilerServices; - - using Paths; - - /// - /// TEdge - /// - internal class TEdge - { -#pragma warning disable SA1401 // Field must be private - /// - /// The source path, see if we can link this back later - /// - internal IPath SourcePath; - - /// - /// The bot - /// - internal System.Numerics.Vector2 Bot; - - /// - /// The current (updated for every new scanbeam) - /// - internal System.Numerics.Vector2 Curr; - - /// - /// The top - /// - internal System.Numerics.Vector2 Top; - - /// - /// The delta - /// - internal System.Numerics.Vector2 Delta; - - /// - /// The dx - /// - internal double Dx; - - /// - /// The poly type - /// - internal PolyType PolyTyp; - - /// - /// Side only refers to current side of solution poly - /// - internal EdgeSide Side; - - /// - /// 1 or -1 depending on winding direction - /// - internal int WindDelta; - - /// - /// The winding count - /// - internal int WindCnt; - - /// - /// The winding count of the opposite polytype - /// - internal int WindCnt2; - - /// - /// The out index - /// - internal int OutIdx; - - /// - /// The next - /// - internal TEdge Next; - - /// - /// The previous - /// - internal TEdge Prev; - - /// - /// The next in LML - /// - internal TEdge NextInLML; - - /// - /// The next in ael - /// - internal TEdge NextInAEL; - - /// - /// The previous in ael - /// - internal TEdge PrevInAEL; - - /// - /// The next in sel - /// - internal TEdge NextInSEL; - - /// - /// The previous in sel - /// - internal TEdge PrevInSEL; -#pragma warning restore SA1401 // Field must be - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/RectangularPolygon.cs b/src/ImageSharp.Drawing/Shapes/RectangularPolygon.cs deleted file mode 100644 index 5002bee40..000000000 --- a/src/ImageSharp.Drawing/Shapes/RectangularPolygon.cs +++ /dev/null @@ -1,281 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Shapes -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Threading.Tasks; - using Paths; - - /// - /// A way of optermising drawing rectangles. - /// - /// - public class RectangularPolygon : IShape, IPath - { - private readonly RectangleF rectangle; - private readonly Vector2 topLeft; - private readonly Vector2 bottomRight; - private readonly Vector2[] points; - private readonly IEnumerable pathCollection; - private readonly float halfLength; - - /// - /// Initializes a new instance of the class. - /// - /// The rect. - public RectangularPolygon(ImageSharp.Rectangle rect) - : this((RectangleF)rect) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The rect. - public RectangularPolygon(ImageSharp.RectangleF rect) - { - this.rectangle = rect; - this.points = new Vector2[4] - { - this.topLeft = new Vector2(rect.Left, rect.Top), - new Vector2(rect.Right, rect.Top), - this.bottomRight = new Vector2(rect.Right, rect.Bottom), - new Vector2(rect.Left, rect.Bottom) - }; - - this.halfLength = this.rectangle.Width + this.rectangle.Height; - this.Length = this.halfLength * 2; - this.pathCollection = new[] { this }; - } - - /// - /// Gets the bounding box of this shape. - /// - /// - /// The bounds. - /// - public RectangleF Bounds => this.rectangle; - - /// - /// Gets a value indicating whether this instance is closed. - /// - /// - /// true if this instance is closed; otherwise, false. - /// - public bool IsClosed => true; - - /// - /// Gets the length of the path - /// - /// - /// The length. - /// - public float Length { get; } - - /// - /// Gets the maximum number intersections that a shape can have when testing a line. - /// - /// - /// The maximum intersections. - /// - public int MaxIntersections => 4; - - /// - /// Calculates the distance along and away from the path for a specified point. - /// - /// The point along the path. - /// - /// Returns details about the point and its distance away from the path. - /// - PointInfo IPath.Distance(Vector2 point) - { - bool inside; // dont care about inside/outside for paths just distance - return this.Distance(point, false, out inside); - } - - /// - /// the distance of the point from the outline of the shape, if the value is negative it is inside the polygon bounds - /// - /// The point. - /// - /// Returns the distance from the shape to the point - /// - public float Distance(Vector2 point) - { - bool insidePoly; - PointInfo result = this.Distance(point, true, out insidePoly); - - // invert the distance from path when inside - return insidePoly ? -result.DistanceFromPath : result.DistanceFromPath; - } - - /// - /// Returns an enumerator that iterates through the collection. - /// - /// - /// An enumerator that can be used to iterate through the collection. - /// - public IEnumerator GetEnumerator() - { - return this.pathCollection.GetEnumerator(); - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() - { - return this.pathCollection.GetEnumerator(); - } - - /// - /// Converts the into a simple linear path.. - /// - /// - /// Returns the current as simple linear path. - /// - public Vector2[] AsSimpleLinearPath() - { - return this.points; - } - - /// - /// Based on a line described by and - /// populate a buffer for all points on the edges of the - /// that the line intersects. - /// - /// The start point of the line. - /// The end point of the line. - /// The buffer that will be populated with intersections. - /// The count. - /// The offset. - /// - /// The number of intersections populated into the buffer. - /// - public int FindIntersections(Vector2 start, Vector2 end, Vector2[] buffer, int count, int offset) - { - int discovered = 0; - Vector2 startPoint = Vector2.Clamp(start, this.topLeft, this.bottomRight); - Vector2 endPoint = Vector2.Clamp(end, this.topLeft, this.bottomRight); - - if (startPoint == Vector2.Clamp(startPoint, start, end)) - { - // if start closest is within line then its a valid point - discovered++; - buffer[offset++] = startPoint; - } - - if (endPoint == Vector2.Clamp(endPoint, start, end)) - { - // if start closest is within line then its a valid point - discovered++; - buffer[offset++] = endPoint; - } - - return discovered; - } - - private PointInfo Distance(Vector2 point, bool getDistanceAwayOnly, out bool isInside) - { - // point in rectangle - // if after its clamped by the extreams its still the same then it must be inside :) - Vector2 clamped = Vector2.Clamp(point, this.topLeft, this.bottomRight); - isInside = clamped == point; - - float distanceFromEdge = float.MaxValue; - float distanceAlongEdge = 0f; - - if (isInside) - { - // get the absolute distances from the extreams - Vector2 topLeftDist = Vector2.Abs(point - this.topLeft); - Vector2 bottomRightDist = Vector2.Abs(point - this.bottomRight); - - // get the min components - Vector2 minDists = Vector2.Min(topLeftDist, bottomRightDist); - - // and then the single smallest (dont have to worry about direction) - distanceFromEdge = Math.Min(minDists.X, minDists.Y); - - if (!getDistanceAwayOnly) - { - // we need to make clamped the closest point - if (this.topLeft.X + distanceFromEdge == point.X) - { - // closer to lhf - clamped.X = this.topLeft.X; // y is already the same - - // distance along edge is length minus the amout down we are from the top of the rect - distanceAlongEdge = this.Length - (clamped.Y - this.topLeft.Y); - } - else if (this.topLeft.Y + distanceFromEdge == point.Y) - { - // closer to top - clamped.Y = this.topLeft.Y; // x is already the same - - distanceAlongEdge = clamped.X - this.topLeft.X; - } - else if (this.bottomRight.Y - distanceFromEdge == point.Y) - { - // closer to bottom - clamped.Y = this.bottomRight.Y; // x is already the same - - distanceAlongEdge = (this.bottomRight.X - clamped.X) + this.halfLength; - } - else if (this.bottomRight.X - distanceFromEdge == point.X) - { - // closer to rhs - clamped.X = this.bottomRight.X; // x is already the same - - distanceAlongEdge = (this.bottomRight.Y - clamped.Y) + this.rectangle.Width; - } - } - } - else - { - // clamped is the point on the path thats closest no matter what - distanceFromEdge = (clamped - point).Length(); - - if (!getDistanceAwayOnly) - { - // we need to figure out whats the cloests edge now and thus what distance/poitn is closest - if (this.topLeft.X == clamped.X) - { - // distance along edge is length minus the amout down we are from the top of the rect - distanceAlongEdge = this.Length - (clamped.Y - this.topLeft.Y); - } - else if (this.topLeft.Y == clamped.Y) - { - distanceAlongEdge = clamped.X - this.topLeft.X; - } - else if (this.bottomRight.Y == clamped.Y) - { - distanceAlongEdge = (this.bottomRight.X - clamped.X) + this.halfLength; - } - else if (this.bottomRight.X == clamped.X) - { - distanceAlongEdge = (this.bottomRight.Y - clamped.Y) + this.rectangle.Width; - } - } - } - - return new PointInfo - { - SearchPoint = point, - DistanceFromPath = distanceFromEdge, - ClosestPointOnPath = clamped, - DistanceAlongPath = distanceAlongEdge - }; - } - } -} diff --git a/src/ImageSharp.Drawing/project.json b/src/ImageSharp.Drawing/project.json index b974684a5..f0d4c6243 100644 --- a/src/ImageSharp.Drawing/project.json +++ b/src/ImageSharp.Drawing/project.json @@ -46,6 +46,7 @@ "target": "project", "version": "1.0.0-*" }, + "SixLabors.Shapes": "0.1.0-ci0043", "StyleCop.Analyzers": { "version": "1.0.0", "type": "build" diff --git a/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs b/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs index 8e5c18d27..defb0e65e 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs @@ -39,7 +39,7 @@ namespace ImageSharp.Benchmarks { using (CoreImage image = new CoreImage(800, 800)) { - image.Fill(CoreColor.HotPink, new ImageSharp.Drawing.Shapes.RectangularPolygon(new CoreRectangle(10, 10, 190, 140))); + image.Fill(CoreColor.HotPink, new SixLabors.Shapes.Rectangle(10, 10, 190, 140)); return new CoreSize(image.Width, image.Height); } @@ -53,10 +53,10 @@ namespace ImageSharp.Benchmarks image.FillPolygon( CoreColor.HotPink, new[] { - new Vector2(10, 10), - new Vector2(200, 10), - new Vector2(200, 150), - new Vector2(10, 150) }); + new Vector2(10, 10), + new Vector2(200, 10), + new Vector2(200, 150), + new Vector2(10, 150) }); return new CoreSize(image.Width, image.Height); } From b8de0584530e527d4351b6ef369eaa32bca50042 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Mon, 30 Jan 2017 21:43:14 +0000 Subject: [PATCH 014/142] added IRegion and droped use of vectors in brushes --- .travis.yml | 4 +- ConsoleApp1/ConsoleApp1.csproj | 8 + ConsoleApp1/Program.cs | 9 + .../Brushes/ImageBrush{TColor}.cs | 20 +- .../Brushes/PatternBrush{TColor}.cs | 18 +- .../Brushes/Processors/BrushApplicator.cs | 16 +- .../Brushes/RecolorBrush{TColor}.cs | 31 +-- .../Brushes/SolidBrush{TColor}.cs | 8 +- src/ImageSharp.Drawing/Draw.cs | 6 +- src/ImageSharp.Drawing/DrawRectangle.cs | 2 +- src/ImageSharp.Drawing/Fill.cs | 4 +- src/ImageSharp.Drawing/FillRectangle.cs | 4 +- src/ImageSharp.Drawing/IDrawableRegion.cs | 24 +++ src/ImageSharp.Drawing/IRegion.cs | 51 +++++ src/ImageSharp.Drawing/Pens/Pen{TColor}.cs | 8 +- .../Pens/Processors/PenApplicator.cs | 9 +- .../{Pens/Processors => }/PointInfo.cs | 7 +- .../Processors/DrawPathProcessor.cs | 75 +------- .../Processors/FillProcessor.cs | 7 +- .../Processors/FillShapeProcessor.cs | 153 +++++---------- .../Processors/PointInfoExtensions.cs | 5 +- src/ImageSharp.Drawing/ShapeRegion.cs | 180 ++++++++++++++++++ src/ImageSharp.Drawing/project.json | 8 +- .../ImageSharp.Sandbox46.csproj | 3 + .../ImageSharp.Tests/Drawing/DrawPathTests.cs | 5 +- .../Drawing/Helpers/BezierPolygon.cs | 71 +++++++ .../Drawing/Helpers/LinearPolygon.cs | 71 +++++++ .../Drawing/LineComplexPolygonTests.cs | 22 ++- .../Drawing/SolidBezierTests.cs | 3 + .../Drawing/SolidComplexPolygonTests.cs | 16 +- .../Drawing/SolidPolygonTests.cs | 9 +- 31 files changed, 596 insertions(+), 261 deletions(-) create mode 100644 ConsoleApp1/ConsoleApp1.csproj create mode 100644 ConsoleApp1/Program.cs create mode 100644 src/ImageSharp.Drawing/IDrawableRegion.cs create mode 100644 src/ImageSharp.Drawing/IRegion.cs rename src/ImageSharp.Drawing/{Pens/Processors => }/PointInfo.cs (81%) create mode 100644 src/ImageSharp.Drawing/ShapeRegion.cs create mode 100644 tests/ImageSharp.Tests/Drawing/Helpers/BezierPolygon.cs create mode 100644 tests/ImageSharp.Tests/Drawing/Helpers/LinearPolygon.cs diff --git a/.travis.yml b/.travis.yml index 172079df2..ca8b90a06 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,8 +20,8 @@ branches: script: - dotnet restore - - dotnet build -c Release src/*/project.json - - dotnet test tests/ImageSharp.Tests/project.json -c Release -f "netcoreapp1.1" + - dotnet build -c Release src/*/project.csproj + - dotnet test tests/ImageSharp.Tests/project.csproj -c Release -f "netcoreapp1.1" env: global: diff --git a/ConsoleApp1/ConsoleApp1.csproj b/ConsoleApp1/ConsoleApp1.csproj new file mode 100644 index 000000000..68b6f2423 --- /dev/null +++ b/ConsoleApp1/ConsoleApp1.csproj @@ -0,0 +1,8 @@ + + + + Exe + netcoreapp1.0 + + + \ No newline at end of file diff --git a/ConsoleApp1/Program.cs b/ConsoleApp1/Program.cs new file mode 100644 index 000000000..104ecf026 --- /dev/null +++ b/ConsoleApp1/Program.cs @@ -0,0 +1,9 @@ +using System; + +class Program +{ + static void Main(string[] args) + { + Console.WriteLine("Hello World!"); + } +} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs b/src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs index 9ce235a84..b9bcc2f9a 100644 --- a/src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs +++ b/src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs @@ -82,18 +82,24 @@ namespace ImageSharp.Drawing.Brushes /// /// Gets the color for a single pixel. /// - /// The point. + /// The x. + /// The y. /// /// The color /// - public override TColor GetColor(Vector2 point) + public override TColor this[int x, int y] { - // Offset the requested pixel by the value in the rectangle (the shapes position) - point = point - this.offset; - int x = (int)point.X % this.xLength; - int y = (int)point.Y % this.yLength; + get + { + var point = new Vector2(x, y); - return this.source[x, y]; + // Offset the requested pixel by the value in the rectangle (the shapes position) + point = point - this.offset; + x = (int)point.X % this.xLength; + y = (int)point.Y % this.yLength; + + return this.source[x, y]; + } } /// diff --git a/src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs b/src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs index 7749f5ba8..3fea53052 100644 --- a/src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs +++ b/src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs @@ -134,17 +134,21 @@ namespace ImageSharp.Drawing.Brushes /// /// Gets the color for a single pixel. - /// - /// The point. + /// # + /// The x. + /// The y. /// - /// The color + /// The Color. /// - public override TColor GetColor(Vector2 point) + public override TColor this[int x, int y] { - int x = (int)point.X % this.xLength; - int y = (int)point.Y % this.stride; + get + { + x = x % this.xLength; + y = y % this.stride; - return this.pattern[x][y]; + return this.pattern[x][y]; + } } /// diff --git a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs index 885be5715..9103dfdf6 100644 --- a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs +++ b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs @@ -16,16 +16,20 @@ namespace ImageSharp.Drawing.Processors public abstract class BrushApplicator : IDisposable // disposable will be required if/when there is an ImageBrush where TColor : struct, IPackedPixel, IEquatable { + /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// Gets the color for a single pixel. /// - public abstract void Dispose(); + /// The x. + /// The y. + /// + /// The color + /// + public abstract TColor this[int x, int y] { get; } /// - /// Gets the color for a single pixel. + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// - /// The point. - /// The color - public abstract TColor GetColor(Vector2 point); + public abstract void Dispose(); } } diff --git a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs index 7149f22a0..33403facb 100644 --- a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs +++ b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs @@ -109,24 +109,31 @@ namespace ImageSharp.Drawing.Brushes /// /// Gets the color for a single pixel. /// - /// The point. + /// The x. + /// The y. /// /// The color /// - public override TColor GetColor(Vector2 point) + public override TColor this[int x, int y] { - // Offset the requested pixel by the value in the rectangle (the shapes position) - TColor result = this.source[(int)point.X, (int)point.Y]; - Vector4 background = result.ToVector4(); - float distance = Vector4.DistanceSquared(background, this.sourceColor); - if (distance <= this.threshold) + get { - var lerpAmount = (this.threshold - distance) / this.threshold; - Vector4 blended = Vector4BlendTransforms.PremultipliedLerp(background, this.targetColor, lerpAmount); - result.PackFromVector4(blended); + // Offset the requested pixel by the value in the rectangle (the shapes position) + TColor result = this.source[x, y]; + Vector4 background = result.ToVector4(); + float distance = Vector4.DistanceSquared(background, this.sourceColor); + if (distance <= this.threshold) + { + var lerpAmount = (this.threshold - distance) / this.threshold; + Vector4 blended = Vector4BlendTransforms.PremultipliedLerp( + background, + this.targetColor, + lerpAmount); + result.PackFromVector4(blended); + } + + return result; } - - return result; } /// diff --git a/src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs b/src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs index c3e311399..018c272b7 100644 --- a/src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs +++ b/src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs @@ -67,14 +67,12 @@ namespace ImageSharp.Drawing.Brushes /// /// Gets the color for a single pixel. /// - /// The point. + /// The x. + /// The y. /// /// The color /// - public override TColor GetColor(Vector2 point) - { - return this.color; - } + public override TColor this[int x, int y] => this.color; /// public override void Dispose() diff --git a/src/ImageSharp.Drawing/Draw.cs b/src/ImageSharp.Drawing/Draw.cs index 8c3dbb1b5..55dba9257 100644 --- a/src/ImageSharp.Drawing/Draw.cs +++ b/src/ImageSharp.Drawing/Draw.cs @@ -32,7 +32,7 @@ namespace ImageSharp public static Image DrawPolygon(this Image source, IPen pen, IShape shape, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(new DrawPathProcessor(pen, shape, options)); + return source.Apply(new DrawPathProcessor(pen, new ShapeRegion(shape), options)); } /// @@ -226,7 +226,7 @@ namespace ImageSharp public static Image DrawPath(this Image source, IPen pen, IPath path, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(new DrawPathProcessor(pen, path, options)); + return source.Apply(new DrawPathProcessor(pen, new ShapeRegion(path), options)); } /// @@ -240,7 +240,7 @@ namespace ImageSharp public static Image DrawPath(this Image source, IPen pen, IPath path) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(new DrawPathProcessor(pen, path, GraphicsOptions.Default)); + return source.Apply(new DrawPathProcessor(pen, new ShapeRegion(path), GraphicsOptions.Default)); } /// diff --git a/src/ImageSharp.Drawing/DrawRectangle.cs b/src/ImageSharp.Drawing/DrawRectangle.cs index 17d6d5f36..243aa082d 100644 --- a/src/ImageSharp.Drawing/DrawRectangle.cs +++ b/src/ImageSharp.Drawing/DrawRectangle.cs @@ -33,7 +33,7 @@ namespace ImageSharp public static Image DrawPolygon(this Image source, IPen pen, RectangleF shape, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(new DrawPathProcessor(pen, (IPath)new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height), options)); + return source.Apply(new DrawPathProcessor(pen, new ShapeRegion(new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height)), options)); } /// diff --git a/src/ImageSharp.Drawing/Fill.cs b/src/ImageSharp.Drawing/Fill.cs index 637950e76..f94d9ca6f 100644 --- a/src/ImageSharp.Drawing/Fill.cs +++ b/src/ImageSharp.Drawing/Fill.cs @@ -56,7 +56,7 @@ namespace ImageSharp public static Image Fill(this Image source, IBrush brush, IShape shape, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(new FillShapeProcessor(brush, shape, options)); + return source.Apply(new FillShapeProcessor(brush, new ShapeRegion(shape), options)); } /// @@ -70,7 +70,7 @@ namespace ImageSharp public static Image Fill(this Image source, IBrush brush, IShape shape) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(new FillShapeProcessor(brush, shape, GraphicsOptions.Default)); + return source.Apply(new FillShapeProcessor(brush, new ShapeRegion(shape), GraphicsOptions.Default)); } /// diff --git a/src/ImageSharp.Drawing/FillRectangle.cs b/src/ImageSharp.Drawing/FillRectangle.cs index 7749e7c92..d60a5a19d 100644 --- a/src/ImageSharp.Drawing/FillRectangle.cs +++ b/src/ImageSharp.Drawing/FillRectangle.cs @@ -30,7 +30,7 @@ namespace ImageSharp public static Image Fill(this Image source, IBrush brush, RectangleF shape, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(new FillShapeProcessor(brush, new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height), options)); + return source.Fill(brush, new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height), options); } /// @@ -44,7 +44,7 @@ namespace ImageSharp public static Image Fill(this Image source, IBrush brush, RectangleF shape) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(new FillShapeProcessor(brush, new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height), GraphicsOptions.Default)); + return source.Fill(brush, new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height)); } /// diff --git a/src/ImageSharp.Drawing/IDrawableRegion.cs b/src/ImageSharp.Drawing/IDrawableRegion.cs new file mode 100644 index 000000000..82e9c39ac --- /dev/null +++ b/src/ImageSharp.Drawing/IDrawableRegion.cs @@ -0,0 +1,24 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Drawing +{ + using System.Numerics; + + /// + /// Represents a region with knowledge about its outline. + /// + /// + public interface IDrawableRegion : IRegion + { + /// + /// Gets the point information for the specified x and y location. + /// + /// The x. + /// The y. + /// Information about the point in relation to a drawable edge + PointInfo GetPointInfo(int x, int y); + } +} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/IRegion.cs b/src/ImageSharp.Drawing/IRegion.cs new file mode 100644 index 000000000..2264a91be --- /dev/null +++ b/src/ImageSharp.Drawing/IRegion.cs @@ -0,0 +1,51 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Drawing +{ + using System.Numerics; + + /// + /// Represents a region of an image. + /// + public interface IRegion + { + /// + /// Gets the maximum number of intersections to could be returned. + /// + /// + /// The maximum intersections. + /// + int MaxIntersections { get; } + + /// + /// Gets the bounds. + /// + /// + /// The bounds. + /// + Rectangle Bounds { get; } + + /// + /// Scans the X axis for intersections. + /// + /// The x. + /// The buffer. + /// The length. + /// The offset. + /// The number of intersections found. + int ScanX(int x, float[] buffer, int length, int offset); + + /// + /// Scans the Y axis for intersections. + /// + /// The position along the y axis to find intersections. + /// The buffer. + /// The length. + /// The offset. + /// The number of intersections found. + int ScanY(int y, float[] buffer, int length, int offset); + } +} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Pens/Pen{TColor}.cs b/src/ImageSharp.Drawing/Pens/Pen{TColor}.cs index bbb3c2559..d0ea55c1e 100644 --- a/src/ImageSharp.Drawing/Pens/Pen{TColor}.cs +++ b/src/ImageSharp.Drawing/Pens/Pen{TColor}.cs @@ -144,10 +144,10 @@ namespace ImageSharp.Drawing.Pens this.brush.Dispose(); } - public override ColoredPointInfo GetColor(PointInfo info) + public override ColoredPointInfo GetColor(int x, int y, PointInfo info) { var result = default(ColoredPointInfo); - result.Color = this.brush.GetColor(info.SearchPoint); + result.Color = this.brush[x, y]; if (info.DistanceFromPath < this.halfWidth) { @@ -197,7 +197,7 @@ namespace ImageSharp.Drawing.Pens this.brush.Dispose(); } - public override ColoredPointInfo GetColor(PointInfo info) + public override ColoredPointInfo GetColor(int x, int y, PointInfo info) { var infoResult = default(ColoredPointInfo); infoResult.DistanceFromElement = float.MaxValue; // is really outside the element @@ -207,7 +207,7 @@ namespace ImageSharp.Drawing.Pens // we can treat the DistanceAlongPath and DistanceFromPath as x,y coords for the pattern // we need to calcualte the distance from the outside edge of the pattern // and set them on the ColoredPointInfo along with the color. - infoResult.Color = this.brush.GetColor(info.SearchPoint); + infoResult.Color = this.brush[x, y]; float distanceWAway = 0; diff --git a/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs b/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs index 222598d85..de680c809 100644 --- a/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs +++ b/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs @@ -6,6 +6,7 @@ namespace ImageSharp.Drawing.Processors { using System; + using System.Numerics; /// /// primitive that converts a into a color and a distance away from the drawable part of the path. @@ -30,8 +31,12 @@ namespace ImageSharp.Drawing.Processors /// /// Gets a from a point represented by a . /// + /// The x. + /// The y. /// The information to extract color details about. - /// Returns the color details and distance from a solid bit of the line. - public abstract ColoredPointInfo GetColor(PointInfo info); + /// + /// Returns the color details and distance from a solid bit of the line. + /// + public abstract ColoredPointInfo GetColor(int x, int y, PointInfo info); } } diff --git a/src/ImageSharp.Drawing/Pens/Processors/PointInfo.cs b/src/ImageSharp.Drawing/PointInfo.cs similarity index 81% rename from src/ImageSharp.Drawing/Pens/Processors/PointInfo.cs rename to src/ImageSharp.Drawing/PointInfo.cs index 6fc78b09b..e222a8146 100644 --- a/src/ImageSharp.Drawing/Pens/Processors/PointInfo.cs +++ b/src/ImageSharp.Drawing/PointInfo.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Drawing.Processors +namespace ImageSharp.Drawing { using System; using System.Numerics; @@ -22,10 +22,5 @@ namespace ImageSharp.Drawing.Processors /// The distance from path /// public float DistanceFromPath; - - /// - /// The search point - /// - public Vector2 SearchPoint; } } diff --git a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs index d6b2f3eb2..bbb5e1ab6 100644 --- a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs @@ -27,64 +27,27 @@ namespace ImageSharp.Drawing.Processors private const int PaddingFactor = 1; // needs to been the same or greater than AntialiasFactor private readonly IPen pen; - private readonly IPath[] paths; - private readonly RectangleF region; + private readonly IDrawableRegion region; private readonly GraphicsOptions options; /// /// Initializes a new instance of the class. /// /// The pen. - /// The shape. + /// The region. /// The options. - public DrawPathProcessor(IPen pen, IShape shape, GraphicsOptions options) - : this(pen, shape.Paths, options) + public DrawPathProcessor(IPen pen, IDrawableRegion region, GraphicsOptions options) { - } - - /// - /// Initializes a new instance of the class. - /// - /// The pen. - /// The path. - /// The options. - public DrawPathProcessor(IPen pen, IPath path, GraphicsOptions options) - : this(pen, new[] { path }, options) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The pen. - /// The paths. - /// The options. - public DrawPathProcessor(IPen pen, IEnumerable paths, GraphicsOptions options) - { - this.paths = paths.ToArray(); + this.region = region; this.pen = pen; this.options = options; - - if (this.paths.Length != 1) - { - var maxX = this.paths.Max(x => x.Bounds.Right); - var minX = this.paths.Min(x => x.Bounds.Left); - var maxY = this.paths.Max(x => x.Bounds.Bottom); - var minY = this.paths.Min(x => x.Bounds.Top); - - this.region = new RectangleF(minX, minY, maxX - minX, maxY - minY); - } - else - { - this.region = this.paths[0].Bounds.Convert(); - } } /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { using (PixelAccessor sourcePixels = source.Lock()) - using (PenApplicator applicator = this.pen.CreateApplicator(sourcePixels, this.region)) + using (PenApplicator applicator = this.pen.CreateApplicator(sourcePixels, this.region.Bounds)) { var rect = RectangleF.Ceiling(applicator.RequiredRegion); @@ -122,16 +85,14 @@ namespace ImageSharp.Drawing.Processors (int y) => { int offsetY = y - polyStartY; - var currentPoint = default(Vector2); + for (int x = minX; x < maxX; x++) { + // TODO add find intersections code to skip and scan large regions of this. int offsetX = x - startX; - currentPoint.X = offsetX; - currentPoint.Y = offsetY; + var info = this.region.GetPointInfo(offsetX, offsetY); - var dist = this.Closest(currentPoint); - - var color = applicator.GetColor(dist.Convert()); + var color = applicator.GetColor(offsetX, offsetY, info); var opacity = this.Opacity(color.DistanceFromElement); @@ -154,24 +115,6 @@ namespace ImageSharp.Drawing.Processors } } - private SixLabors.Shapes.PointInfo Closest(Vector2 point) - { - SixLabors.Shapes.PointInfo result = default(SixLabors.Shapes.PointInfo); - float distance = float.MaxValue; - - for (int i = 0; i < this.paths.Length; i++) - { - var p = this.paths[i].Distance(point); - if (p.DistanceFromPath < distance) - { - distance = p.DistanceFromPath; - result = p; - } - } - - return result; - } - private float Opacity(float distance) { if (distance <= 0) diff --git a/src/ImageSharp.Drawing/Processors/FillProcessor.cs b/src/ImageSharp.Drawing/Processors/FillProcessor.cs index dc87e6da6..37bdac90f 100644 --- a/src/ImageSharp.Drawing/Processors/FillProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillProcessor.cs @@ -71,17 +71,12 @@ namespace ImageSharp.Drawing.Processors y => { int offsetY = y - startY; - - Vector2 currentPoint = default(Vector2); for (int x = minX; x < maxX; x++) { int offsetX = x - startX; - int offsetColorX = x - minX; - currentPoint.X = offsetX; - currentPoint.Y = offsetY; Vector4 backgroundVector = sourcePixels[offsetX, offsetY].ToVector4(); - Vector4 sourceVector = applicator.GetColor(currentPoint).ToVector4(); + Vector4 sourceVector = applicator[offsetX, offsetY].ToVector4(); Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, 1); diff --git a/src/ImageSharp.Drawing/Processors/FillShapeProcessor.cs b/src/ImageSharp.Drawing/Processors/FillShapeProcessor.cs index 14e99cba3..bdec022d4 100644 --- a/src/ImageSharp.Drawing/Processors/FillShapeProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillShapeProcessor.cs @@ -25,18 +25,18 @@ namespace ImageSharp.Drawing.Processors private const float AntialiasFactor = 1f; private const int DrawPadding = 1; private readonly IBrush fillColor; - private readonly IShape poly; + private readonly IRegion region; private readonly GraphicsOptions options; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The brush. - /// The shape. + /// The region. /// The options. - public FillShapeProcessor(IBrush brush, IShape shape, GraphicsOptions options) + public FillShapeProcessor(IBrush brush, IRegion region, GraphicsOptions options) { - this.poly = shape; + this.region = region; this.fillColor = brush; this.options = options; } @@ -44,12 +44,12 @@ namespace ImageSharp.Drawing.Processors /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - Rectangle rect = RectangleF.Ceiling(this.poly.Bounds.Convert()); // rounds the points out away from the center + Rectangle rect = RectangleF.Ceiling(this.region.Bounds); // rounds the points out away from the center - int polyStartY = rect.Y - DrawPadding; - int polyEndY = rect.Bottom + DrawPadding; - int startX = rect.X - DrawPadding; - int endX = rect.Right + DrawPadding; + int polyStartY = sourceRectangle.Y - DrawPadding; + int polyEndY = sourceRectangle.Bottom + DrawPadding; + int startX = sourceRectangle.X - DrawPadding; + int endX = sourceRectangle.Right + DrawPadding; int minX = Math.Max(sourceRectangle.Left, startX); int maxX = Math.Min(sourceRectangle.Right - 1, endX); @@ -62,9 +62,9 @@ namespace ImageSharp.Drawing.Processors minY = Math.Max(0, minY); maxY = Math.Min(source.Height, maxY); - ArrayPool arrayPool = ArrayPool.Shared; + ArrayPool arrayPool = ArrayPool.Shared; - int maxIntersections = this.poly.MaxIntersections; + int maxIntersections = this.region.MaxIntersections; using (PixelAccessor sourcePixels = source.Lock()) using (BrushApplicator applicator = this.fillColor.CreateApplicator(sourcePixels, rect)) @@ -73,27 +73,26 @@ namespace ImageSharp.Drawing.Processors minY, maxY, this.ParallelOptions, - y => + (int y) => { - Vector2[] buffer = arrayPool.Rent(maxIntersections); + float[] buffer = arrayPool.Rent(maxIntersections); try { - Vector2 left = new Vector2(startX, y); - Vector2 right = new Vector2(endX, y); + float right = endX; // foreach line we get all the points where this line crosses the polygon - int pointsFound = this.poly.FindIntersections(left, right, buffer, maxIntersections, 0); + int pointsFound = this.region.ScanY(y, buffer, maxIntersections, 0); if (pointsFound == 0) { // nothign on this line skip return; } - QuickSortX(buffer, pointsFound); + QuickSort(buffer, pointsFound); int currentIntersection = 0; - float nextPoint = buffer[0].X; + float nextPoint = buffer[0]; float lastPoint = float.MinValue; bool isInside = false; @@ -108,7 +107,7 @@ namespace ImageSharp.Drawing.Processors { if (x < (nextPoint - DrawPadding) && x > (lastPoint + DrawPadding)) { - if (nextPoint == right.X) + if (nextPoint == right) { // we are in the ends run skip it x = maxX; @@ -129,11 +128,11 @@ namespace ImageSharp.Drawing.Processors lastPoint = nextPoint; if (currentIntersection == pointsFound) { - nextPoint = right.X; + nextPoint = right; } else { - nextPoint = buffer[currentIntersection].X; + nextPoint = buffer[currentIntersection]; // double point from a corner flip the bit back and move on again if (nextPoint == lastPoint) @@ -143,11 +142,11 @@ namespace ImageSharp.Drawing.Processors currentIntersection++; if (currentIntersection == pointsFound) { - nextPoint = right.X; + nextPoint = right; } else { - nextPoint = buffer[currentIntersection].X; + nextPoint = buffer[currentIntersection]; } } } @@ -192,7 +191,7 @@ namespace ImageSharp.Drawing.Processors if (opacity > Constants.Epsilon) { Vector4 backgroundVector = sourcePixels[x, y].ToVector4(); - Vector4 sourceVector = applicator.GetColor(currentPoint).ToVector4(); + Vector4 sourceVector = applicator[x, y].ToVector4(); Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); finalColor.W = backgroundVector.W; @@ -216,42 +215,37 @@ namespace ImageSharp.Drawing.Processors minX, maxX, this.ParallelOptions, - x => + (int x) => { - Vector2[] buffer = arrayPool.Rent(maxIntersections); + float[] buffer = arrayPool.Rent(maxIntersections); try { - Vector2 left = new Vector2(x, polyStartY); - Vector2 right = new Vector2(x, polyEndY); + float left = polyStartY; + float right = polyEndY; // foreach line we get all the points where this line crosses the polygon - int pointsFound = this.poly.FindIntersections(left, right, buffer, maxIntersections, 0); + int pointsFound = this.region.ScanX(x, buffer, maxIntersections, 0); if (pointsFound == 0) { // nothign on this line skip return; } - QuickSortY(buffer, pointsFound); + QuickSort(buffer, pointsFound); int currentIntersection = 0; - float nextPoint = buffer[0].Y; - float lastPoint = left.Y; + float nextPoint = buffer[0]; + float lastPoint = left; bool isInside = false; - // every odd point is the start of a line - Vector2 currentPoint = default(Vector2); - for (int y = minY; y < maxY; y++) { - currentPoint.X = x; - currentPoint.Y = y; if (!isInside) { if (y < (nextPoint - DrawPadding) && y > (lastPoint + DrawPadding)) { - if (nextPoint == right.Y) + if (nextPoint == right) { // we are in the ends run skip it y = maxY; @@ -266,7 +260,7 @@ namespace ImageSharp.Drawing.Processors { if (y < nextPoint - DrawPadding) { - if (nextPoint == right.Y) + if (nextPoint == right) { // we are in the ends run skip it y = maxY; @@ -286,11 +280,11 @@ namespace ImageSharp.Drawing.Processors lastPoint = nextPoint; if (currentIntersection == pointsFound) { - nextPoint = right.Y; + nextPoint = right; } else { - nextPoint = buffer[currentIntersection].Y; + nextPoint = buffer[currentIntersection]; // double point from a corner flip the bit back and move on again if (nextPoint == lastPoint) @@ -300,11 +294,11 @@ namespace ImageSharp.Drawing.Processors currentIntersection++; if (currentIntersection == pointsFound) { - nextPoint = right.Y; + nextPoint = right; } else { - nextPoint = buffer[currentIntersection].Y; + nextPoint = buffer[currentIntersection]; } } } @@ -350,8 +344,7 @@ namespace ImageSharp.Drawing.Processors if (opacity > Constants.Epsilon && opacity < 1) { Vector4 backgroundVector = sourcePixels[x, y].ToVector4(); - Vector4 sourceVector = applicator.GetColor(currentPoint).ToVector4(); - + Vector4 sourceVector = applicator[x, y].ToVector4(); Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); finalColor.W = backgroundVector.W; @@ -370,76 +363,32 @@ namespace ImageSharp.Drawing.Processors } } - private static void Swap(Vector2[] data, int left, int right) + private static void Swap(float[] data, int left, int right) { - Vector2 tmp = data[left]; + float tmp = data[left]; data[left] = data[right]; data[right] = tmp; } - private static void QuickSortY(Vector2[] data, int size) - { - int hi = Math.Min(data.Length - 1, size - 1); - QuickSortY(data, 0, hi); - } - - private static void QuickSortY(Vector2[] data, int lo, int hi) - { - if (lo < hi) - { - int p = PartitionY(data, lo, hi); - QuickSortY(data, lo, p); - QuickSortY(data, p + 1, hi); - } - } - - private static void QuickSortX(Vector2[] data, int size) + private static void QuickSort(float[] data, int size) { int hi = Math.Min(data.Length - 1, size - 1); - QuickSortX(data, 0, hi); + QuickSort(data, 0, hi); } - private static void QuickSortX(Vector2[] data, int lo, int hi) + private static void QuickSort(float[] data, int lo, int hi) { if (lo < hi) { - int p = PartitionX(data, lo, hi); - QuickSortX(data, lo, p); - QuickSortX(data, p + 1, hi); - } - } - - private static int PartitionX(Vector2[] data, int lo, int hi) - { - float pivot = data[lo].X; - int i = lo - 1; - int j = hi + 1; - while (true) - { - do - { - i = i + 1; - } - while (data[i].X < pivot && i < hi); - - do - { - j = j - 1; - } - while (data[j].X > pivot && j > lo); - - if (i >= j) - { - return j; - } - - Swap(data, i, j); + int p = Partition(data, lo, hi); + QuickSort(data, lo, p); + QuickSort(data, p + 1, hi); } } - private static int PartitionY(Vector2[] data, int lo, int hi) + private static int Partition(float[] data, int lo, int hi) { - float pivot = data[lo].Y; + float pivot = data[lo]; int i = lo - 1; int j = hi + 1; while (true) @@ -448,13 +397,13 @@ namespace ImageSharp.Drawing.Processors { i = i + 1; } - while (data[i].Y < pivot && i < hi); + while (data[i] < pivot && i < hi); do { j = j - 1; } - while (data[j].Y > pivot && j > lo); + while (data[j] > pivot && j > lo); if (i >= j) { diff --git a/src/ImageSharp.Drawing/Processors/PointInfoExtensions.cs b/src/ImageSharp.Drawing/Processors/PointInfoExtensions.cs index 939a71845..6613d15ac 100644 --- a/src/ImageSharp.Drawing/Processors/PointInfoExtensions.cs +++ b/src/ImageSharp.Drawing/Processors/PointInfoExtensions.cs @@ -12,6 +12,8 @@ namespace ImageSharp.Drawing.Processors using Drawing; using ImageSharp.Processing; using SixLabors.Shapes; + + using PointInfo = ImageSharp.Drawing.PointInfo; using Rectangle = ImageSharp.Rectangle; /// @@ -29,8 +31,7 @@ namespace ImageSharp.Drawing.Processors return new PointInfo { DistanceAlongPath = source.DistanceAlongPath, - DistanceFromPath = source.DistanceFromPath, - SearchPoint = source.SearchPoint + DistanceFromPath = source.DistanceFromPath }; } } diff --git a/src/ImageSharp.Drawing/ShapeRegion.cs b/src/ImageSharp.Drawing/ShapeRegion.cs new file mode 100644 index 000000000..6bd070367 --- /dev/null +++ b/src/ImageSharp.Drawing/ShapeRegion.cs @@ -0,0 +1,180 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Drawing +{ + using System.Buffers; + using System.Collections.Immutable; + using System.Numerics; + + using ImageSharp.Drawing.Processors; + + using SixLabors.Shapes; + + using Rectangle = ImageSharp.Rectangle; + + /// + /// A drawable mapping between a / and a drawable/fillable region. + /// + /// + internal class ShapeRegion : IDrawableRegion + { + /// + /// The fillable shape + /// + private readonly IShape shape; + + /// + /// The drawable paths + /// + private readonly ImmutableArray paths; + + /// + /// Initializes a new instance of the class. + /// + /// The path. + public ShapeRegion(IPath path) + : this(ImmutableArray.Create(path)) + { + this.shape = path.AsShape(); + this.Bounds = RectangleF.Ceiling(path.Bounds.Convert()); + } + + /// + /// Initializes a new instance of the class. + /// + /// The shape. + public ShapeRegion(IShape shape) + : this(shape.Paths) + { + this.shape = shape; + this.Bounds = RectangleF.Ceiling(shape.Bounds.Convert()); + } + + /// + /// Initializes a new instance of the class. + /// + /// The paths. + private ShapeRegion(ImmutableArray paths) + { + this.paths = paths; + } + + /// + /// Gets the maximum number of intersections to could be returned. + /// + /// + /// The maximum intersections. + /// + public int MaxIntersections => this.shape.MaxIntersections; + + /// + /// Gets the bounds. + /// + /// + /// The bounds. + /// + public Rectangle Bounds { get; } + + /// + /// Scans the X axis for intersections. + /// + /// The x. + /// The buffer. + /// The length. + /// The offset. + /// + /// The number of intersections found. + /// + public int ScanX(int x, float[] buffer, int length, int offset) + { + var start = new Vector2(x, this.Bounds.Top - 1); + var end = new Vector2(x, this.Bounds.Bottom + 1); + Vector2[] innerbuffer = ArrayPool.Shared.Rent(length); + try + { + int count = this.shape.FindIntersections( + start, + end, + innerbuffer, + length, + 0); + + for (var i = 0; i < count; i++) + { + buffer[i + offset] = innerbuffer[i].Y; + } + + return count; + } + finally + { + ArrayPool.Shared.Return(innerbuffer); + } + } + + /// + /// Scans the Y axis for intersections. + /// + /// The position along the y axis to find intersections. + /// The buffer. + /// The length. + /// The offset. + /// + /// The number of intersections found. + /// + public int ScanY(int y, float[] buffer, int length, int offset) + { + var start = new Vector2(this.Bounds.Left - 1, y); + var end = new Vector2(this.Bounds.Right + 1, y); + Vector2[] innerbuffer = ArrayPool.Shared.Rent(length); + try + { + int count = this.shape.FindIntersections( + start, + end, + innerbuffer, + length, + 0); + + for (var i = 0; i < count; i++) + { + buffer[i + offset] = innerbuffer[i].X; + } + + return count; + } + finally + { + ArrayPool.Shared.Return(innerbuffer); + } + } + + /// + /// Gets the point information for the specified x and y location. + /// + /// The x. + /// The y. + /// Information about the the point + public PointInfo GetPointInfo(int x, int y) + { + var point = new Vector2(x, y); + SixLabors.Shapes.PointInfo result = default(SixLabors.Shapes.PointInfo); + float distance = float.MaxValue; + + for (int i = 0; i < this.paths.Length; i++) + { + var p = this.paths[i].Distance(point); + if (p.DistanceFromPath < distance) + { + distance = p.DistanceFromPath; + result = p; + } + } + + return result.Convert(); + } + } +} diff --git a/src/ImageSharp.Drawing/project.json b/src/ImageSharp.Drawing/project.json index f0d4c6243..5fcee9b41 100644 --- a/src/ImageSharp.Drawing/project.json +++ b/src/ImageSharp.Drawing/project.json @@ -39,14 +39,12 @@ }, "dependencies": { "ImageSharp": { - "target": "project", - "version": "1.0.0-*" + "target": "project" }, "ImageSharp.Processing": { - "target": "project", - "version": "1.0.0-*" + "target": "project" }, - "SixLabors.Shapes": "0.1.0-ci0043", + "SixLabors.Shapes": "0.1.0-ci0047", "StyleCop.Analyzers": { "version": "1.0.0", "type": "build" diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj index 305fac636..75212d361 100644 --- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj +++ b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj @@ -179,6 +179,9 @@ ..\..\src\ImageSharp.Drawing\bin\$(Configuration)\net461\ImageSharp.Drawing.dll + + ..\..\src\ImageSharp.Drawing\bin\$(Configuration)\net461\SixLabors.Shapes.dll + ..\..\src\ImageSharp.Formats.Bmp\bin\$(Configuration)\net461\ImageSharp.Formats.Bmp.dll diff --git a/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs b/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs index b61948316..31aa87d4b 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs @@ -7,12 +7,13 @@ namespace ImageSharp.Tests.Drawing { using Drawing; using ImageSharp.Drawing; - using CorePath = ImageSharp.Drawing.Paths.Path; - using ImageSharp.Drawing.Paths; + using CorePath = SixLabors.Shapes.Path; + using SixLabors.Shapes; using System; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Numerics; + using Xunit; public class DrawPathTests : FileTestBase diff --git a/tests/ImageSharp.Tests/Drawing/Helpers/BezierPolygon.cs b/tests/ImageSharp.Tests/Drawing/Helpers/BezierPolygon.cs new file mode 100644 index 000000000..070e55501 --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/Helpers/BezierPolygon.cs @@ -0,0 +1,71 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace SixLabors.Shapes +{ + using System.Collections.Generic; + using System.Collections.Immutable; + using System.Numerics; + + using SixLabors.Shapes; + public class BezierPolygon : IShape + { + private Polygon polygon; + + public BezierPolygon(params Vector2[] points) + { + this.polygon = new Polygon(new BezierLineSegment(points)); + } + + public float Distance(Vector2 point) + { + return this.polygon.Distance(point); + } + + public bool Contains(Vector2 point) + { + return this.polygon.Contains(point); + } + + public int FindIntersections(Vector2 start, Vector2 end, Vector2[] buffer, int count, int offset) + { + return this.polygon.FindIntersections(start, end, buffer, count, offset); + } + + public IEnumerable FindIntersections(Vector2 start, Vector2 end) + { + return this.polygon.FindIntersections(start, end); + } + + public IShape Transform(Matrix3x2 matrix) + { + return ((IShape)this.polygon).Transform(matrix); + } + + public Rectangle Bounds + { + get + { + return this.polygon.Bounds; + } + } + + public ImmutableArray Paths + { + get + { + return this.polygon.Paths; + } + } + + public int MaxIntersections + { + get + { + return this.polygon.MaxIntersections; + } + } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/Helpers/LinearPolygon.cs b/tests/ImageSharp.Tests/Drawing/Helpers/LinearPolygon.cs new file mode 100644 index 000000000..fa9488266 --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/Helpers/LinearPolygon.cs @@ -0,0 +1,71 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace SixLabors.Shapes +{ + using System.Collections.Generic; + using System.Collections.Immutable; + using System.Numerics; + + using SixLabors.Shapes; + public class LinearPolygon : IShape + { + private Polygon polygon; + + public LinearPolygon(params Vector2[] points) + { + this.polygon = new Polygon(new LinearLineSegment(points)); + } + + public float Distance(Vector2 point) + { + return this.polygon.Distance(point); + } + + public bool Contains(Vector2 point) + { + return this.polygon.Contains(point); + } + + public int FindIntersections(Vector2 start, Vector2 end, Vector2[] buffer, int count, int offset) + { + return this.polygon.FindIntersections(start, end, buffer, count, offset); + } + + public IEnumerable FindIntersections(Vector2 start, Vector2 end) + { + return this.polygon.FindIntersections(start, end); + } + + public IShape Transform(Matrix3x2 matrix) + { + return ((IShape)this.polygon).Transform(matrix); + } + + public Rectangle Bounds + { + get + { + return this.polygon.Bounds; + } + } + + public ImmutableArray Paths + { + get + { + return this.polygon.Paths; + } + } + + public int MaxIntersections + { + get + { + return this.polygon.MaxIntersections; + } + } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs index 9ce93ee89..dd2ea5249 100644 --- a/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs @@ -7,26 +7,29 @@ namespace ImageSharp.Tests.Drawing { using System.IO; using Xunit; - + using Drawing; + using ImageSharp.Drawing; using System.Numerics; - using ImageSharp.Drawing.Shapes; using ImageSharp.Drawing.Pens; + using SixLabors.Shapes; + public class LineComplexPolygonTests : FileTestBase { [Fact] public void ImageShouldBeOverlayedByPolygonOutline() { string path = this.CreateOutputDirectory("Drawing", "LineComplexPolygon"); - LinearPolygon simplePath = new LinearPolygon( + + var simplePath = new Polygon(new LinearLineSegment( new Vector2(10, 10), new Vector2(200, 150), - new Vector2(50, 300)); + new Vector2(50, 300))); - LinearPolygon hole1 = new LinearPolygon( + var hole1 = new Polygon(new LinearLineSegment( new Vector2(37, 85), new Vector2(93, 85), - new Vector2(65, 137)); + new Vector2(65, 137))); using (Image image = new Image(500, 500)) { @@ -34,8 +37,8 @@ namespace ImageSharp.Tests.Drawing { image .BackgroundColor(Color.Blue) - .DrawPolygon(Color.HotPink, 5, new ComplexPolygon(simplePath, hole1)) - .Save(output); + .DrawPolygon(Color.HotPink, 5, new ComplexPolygon(simplePath, hole1)) + .Save(output); } using (PixelAccessor sourcePixels = image.Lock()) @@ -128,6 +131,7 @@ namespace ImageSharp.Tests.Drawing new Vector2(37, 85), new Vector2(130, 40), new Vector2(65, 137)); + var clipped = simplePath.Clip(hole1); using (Image image = new Image(500, 500)) { @@ -135,7 +139,7 @@ namespace ImageSharp.Tests.Drawing { image .BackgroundColor(Color.Blue) - .DrawPolygon(Color.HotPink, 5, new ComplexPolygon(simplePath, hole1)) + .DrawPolygon(Color.HotPink, 5, clipped) .Save(output); } diff --git a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs index 18275ef38..82e4ac33e 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs @@ -9,6 +9,9 @@ namespace ImageSharp.Tests.Drawing using System.IO; using System.Numerics; + + using SixLabors.Shapes; + using Xunit; public class SolidBezierTests : FileTestBase diff --git a/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs index 144a1398d..a1973a280 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs @@ -7,9 +7,11 @@ namespace ImageSharp.Tests.Drawing { using System.IO; using Xunit; - + using Drawing; + using ImageSharp.Drawing; using System.Numerics; - using ImageSharp.Drawing.Shapes; + + using SixLabors.Shapes; public class SolidComplexPolygonTests : FileTestBase { @@ -33,7 +35,7 @@ namespace ImageSharp.Tests.Drawing { image .BackgroundColor(Color.Blue) - .Fill(Color.HotPink, new ComplexPolygon(simplePath, hole1)) + .Fill(Color.HotPink, simplePath.Clip(hole1)) .Save(output); } @@ -76,7 +78,7 @@ namespace ImageSharp.Tests.Drawing { image .BackgroundColor(Color.Blue) - .Fill(Color.HotPink, new ComplexPolygon(simplePath, hole1)) + .Fill(Color.HotPink, simplePath.Clip(hole1)) .Save(output); } @@ -119,8 +121,8 @@ namespace ImageSharp.Tests.Drawing { image .BackgroundColor(Color.Blue) - .Fill(color, new ComplexPolygon(simplePath, hole1)) - .Save(output); + .Fill(color, simplePath.Clip(hole1)) + .Save(output); } //shift background color towards forground color by the opacity amount @@ -144,4 +146,4 @@ namespace ImageSharp.Tests.Drawing } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs index b4cf8e090..3e20b3a09 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs @@ -5,8 +5,10 @@ namespace ImageSharp.Tests.Drawing { + using Drawing; using ImageSharp.Drawing; - + using System; + using System.Diagnostics.CodeAnalysis; using System.IO; using System.Numerics; using Xunit; @@ -142,14 +144,15 @@ namespace ImageSharp.Tests.Drawing public void ImageShouldBeOverlayedByFilledRectangle() { string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/Rectangle.png")) { image .BackgroundColor(Color.Blue) - .Fill(Color.HotPink, new ImageSharp.Drawing.Shapes.RectangularPolygon(new Rectangle(10, 10, 190, 140))) - .Save(output); + .Fill(Color.HotPink, new SixLabors.Shapes.Rectangle(10,10, 190, 140)) + .Save(output); } using (PixelAccessor sourcePixels = image.Lock()) From 7fbc7559079bd767f25b822b361a1811c0d3d477 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Tue, 31 Jan 2017 22:12:26 +0000 Subject: [PATCH 015/142] split out paths from basic drawing --- ImageSharp.sln | 8 + src/ImageSharp.Drawing.Paths/DrawBeziers.cs | 120 +++++ src/ImageSharp.Drawing.Paths/DrawLines.cs | 120 +++++ src/ImageSharp.Drawing.Paths/DrawPath.cs | 124 +++++ src/ImageSharp.Drawing.Paths/DrawPolygon.cs | 104 ++++ .../DrawRectangle.cs | 24 +- src/ImageSharp.Drawing.Paths/DrawShape.cs | 118 ++++ src/ImageSharp.Drawing.Paths/FillPaths.cs | 87 +++ src/ImageSharp.Drawing.Paths/FillPolygon.cs | 83 +++ .../FillRectangle.cs | 8 +- src/ImageSharp.Drawing.Paths/FillShapes.cs | 83 +++ .../ImageSharp.Drawing.Paths.xproj | 25 + .../PointInfoExtensions.cs | 21 +- .../Properties/AssemblyInfo.cs | 6 + .../RectangleExtensions.cs | 12 +- .../ShapePath.cs} | 31 +- src/ImageSharp.Drawing.Paths/ShapeRegion.cs | 137 +++++ src/ImageSharp.Drawing.Paths/project.json | 96 ++++ .../Brushes/Processors/BrushApplicator.cs | 1 - src/ImageSharp.Drawing/Draw.cs | 506 ------------------ src/ImageSharp.Drawing/DrawPath.cs | 123 +++++ src/ImageSharp.Drawing/Fill.cs | 173 ------ src/ImageSharp.Drawing/FillRegion.cs | 111 ++++ src/ImageSharp.Drawing/IDrawableRegion.cs | 24 - src/ImageSharp.Drawing/Path.cs | 57 ++ .../Processors/DrawPathProcessor.cs | 5 +- ...apeProcessor.cs => FillRegionProcessor.cs} | 29 +- .../{IRegion.cs => Region.cs} | 14 +- src/ImageSharp.Drawing/project.json | 1 - .../Drawing/FillRectangle.cs | 2 +- tests/ImageSharp.Benchmarks/project.json | 24 +- .../ImageSharp.Sandbox46.csproj | 5 +- .../ImageSharp.Tests/Drawing/DrawPathTests.cs | 10 +- .../Drawing/LineComplexPolygonTests.cs | 12 +- .../ImageSharp.Tests/Drawing/PolygonTests.cs | 2 +- .../Drawing/SolidPolygonTests.cs | 2 +- tests/ImageSharp.Tests/project.json | 24 +- 37 files changed, 1509 insertions(+), 823 deletions(-) create mode 100644 src/ImageSharp.Drawing.Paths/DrawBeziers.cs create mode 100644 src/ImageSharp.Drawing.Paths/DrawLines.cs create mode 100644 src/ImageSharp.Drawing.Paths/DrawPath.cs create mode 100644 src/ImageSharp.Drawing.Paths/DrawPolygon.cs rename src/{ImageSharp.Drawing => ImageSharp.Drawing.Paths}/DrawRectangle.cs (73%) create mode 100644 src/ImageSharp.Drawing.Paths/DrawShape.cs create mode 100644 src/ImageSharp.Drawing.Paths/FillPaths.cs create mode 100644 src/ImageSharp.Drawing.Paths/FillPolygon.cs rename src/{ImageSharp.Drawing => ImageSharp.Drawing.Paths}/FillRectangle.cs (93%) create mode 100644 src/ImageSharp.Drawing.Paths/FillShapes.cs create mode 100644 src/ImageSharp.Drawing.Paths/ImageSharp.Drawing.Paths.xproj rename src/{ImageSharp.Drawing/Processors => ImageSharp.Drawing.Paths}/PointInfoExtensions.cs (62%) create mode 100644 src/ImageSharp.Drawing.Paths/Properties/AssemblyInfo.cs rename src/{ImageSharp.Drawing/Processors => ImageSharp.Drawing.Paths}/RectangleExtensions.cs (58%) rename src/{ImageSharp.Drawing/ShapeRegion.cs => ImageSharp.Drawing.Paths/ShapePath.cs} (83%) create mode 100644 src/ImageSharp.Drawing.Paths/ShapeRegion.cs create mode 100644 src/ImageSharp.Drawing.Paths/project.json delete mode 100644 src/ImageSharp.Drawing/Draw.cs create mode 100644 src/ImageSharp.Drawing/DrawPath.cs delete mode 100644 src/ImageSharp.Drawing/Fill.cs create mode 100644 src/ImageSharp.Drawing/FillRegion.cs delete mode 100644 src/ImageSharp.Drawing/IDrawableRegion.cs create mode 100644 src/ImageSharp.Drawing/Path.cs rename src/ImageSharp.Drawing/Processors/{FillShapeProcessor.cs => FillRegionProcessor.cs} (95%) rename src/ImageSharp.Drawing/{IRegion.cs => Region.cs} (78%) diff --git a/ImageSharp.sln b/ImageSharp.sln index f1e9fb104..503a5b860 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -60,11 +60,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Sandbox46", "tes {2AA31A1F-142C-43F4-8687-09ABCA4B3A26} = {2AA31A1F-142C-43F4-8687-09ABCA4B3A26} {27AD4B5F-ECC4-4C63-9ECB-04EC772FDB6F} = {27AD4B5F-ECC4-4C63-9ECB-04EC772FDB6F} {7213767C-0003-41CA-AB18-0223CFA7CE4B} = {7213767C-0003-41CA-AB18-0223CFA7CE4B} + {E5BD4F96-28A8-410C-8B63-1C5731948549} = {E5BD4F96-28A8-410C-8B63-1C5731948549} {C77661B9-F793-422E-8E27-AC60ECC5F215} = {C77661B9-F793-422E-8E27-AC60ECC5F215} {556ABDCF-ED93-4327-BE98-F6815F78B9B8} = {556ABDCF-ED93-4327-BE98-F6815F78B9B8} {A623CFE9-9D2B-4528-AD1F-2E834B061134} = {A623CFE9-9D2B-4528-AD1F-2E834B061134} EndProjectSection EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ImageSharp.Drawing.Paths", "src\ImageSharp.Drawing.Paths\ImageSharp.Drawing.Paths.xproj", "{E5BD4F96-28A8-410C-8B63-1C5731948549}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -115,6 +118,10 @@ Global {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Debug|Any CPU.Build.0 = Debug|Any CPU {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Release|Any CPU.ActiveCfg = Release|Any CPU {96188137-5FA6-4924-AB6E-4EFF79C6E0BB}.Release|Any CPU.Build.0 = Release|Any CPU + {E5BD4F96-28A8-410C-8B63-1C5731948549}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E5BD4F96-28A8-410C-8B63-1C5731948549}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E5BD4F96-28A8-410C-8B63-1C5731948549}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E5BD4F96-28A8-410C-8B63-1C5731948549}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -132,5 +139,6 @@ Global {A623CFE9-9D2B-4528-AD1F-2E834B061134} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} {9E574A07-F879-4811-9C41-5CBDC6BAFDB7} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} {96188137-5FA6-4924-AB6E-4EFF79C6E0BB} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} + {E5BD4F96-28A8-410C-8B63-1C5731948549} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} EndGlobalSection EndGlobal diff --git a/src/ImageSharp.Drawing.Paths/DrawBeziers.cs b/src/ImageSharp.Drawing.Paths/DrawBeziers.cs new file mode 100644 index 000000000..6515db577 --- /dev/null +++ b/src/ImageSharp.Drawing.Paths/DrawBeziers.cs @@ -0,0 +1,120 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Numerics; + using Drawing; + using Drawing.Brushes; + using Drawing.Pens; + using Drawing.Processors; + using SixLabors.Shapes; + + using Path = SixLabors.Shapes.Path; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Draws the provided Points as an open Bezier path at the provided thickness with the supplied brush + /// + /// The type of the color. + /// The source. + /// The brush. + /// The thickness. + /// The points. + /// The options. + /// + /// The Image + /// + public static Image DrawBeziers(this Image source, IBrush brush, float thickness, Vector2[] points, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(new Pen(brush, thickness), new Path(new BezierLineSegment(points)), options); + } + + /// + /// Draws the provided Points as an open Bezier path at the provided thickness with the supplied brush + /// + /// The type of the color. + /// The source. + /// The brush. + /// The thickness. + /// The points. + /// The Image + public static Image DrawBeziers(this Image source, IBrush brush, float thickness, Vector2[] points) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(new Pen(brush, thickness), new Path(new BezierLineSegment(points))); + } + + /// + /// Draws the provided Points as an open Bezier path at the provided thickness with the supplied brush + /// + /// The type of the color. + /// The source. + /// The color. + /// The thickness. + /// The points. + /// The Image + public static Image DrawBeziers(this Image source, TColor color, float thickness, Vector2[] points) + where TColor : struct, IPackedPixel, IEquatable + { + return source.DrawBeziers(new SolidBrush(color), thickness, points); + } + + /// + /// Draws the provided Points as an open Bezier path at the provided thickness with the supplied brush + /// + /// The type of the color. + /// The source. + /// The color. + /// The thickness. + /// The points. + /// The options. + /// + /// The Image + /// + public static Image DrawBeziers(this Image source, TColor color, float thickness, Vector2[] points, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.DrawBeziers(new SolidBrush(color), thickness, points, options); + } + + /// + /// Draws the provided Points as an open Bezier path with the supplied pen + /// + /// The type of the color. + /// The source. + /// The pen. + /// The points. + /// The options. + /// + /// The Image + /// + public static Image DrawBeziers(this Image source, IPen pen, Vector2[] points, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(pen, new Path(new BezierLineSegment(points)), options); + } + + /// + /// Draws the provided Points as an open Bezier path with the supplied pen + /// + /// The type of the color. + /// The source. + /// The pen. + /// The points. + /// The Image + public static Image DrawBeziers(this Image source, IPen pen, Vector2[] points) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(pen, new Path(new BezierLineSegment(points))); + } + } +} diff --git a/src/ImageSharp.Drawing.Paths/DrawLines.cs b/src/ImageSharp.Drawing.Paths/DrawLines.cs new file mode 100644 index 000000000..2e4f84987 --- /dev/null +++ b/src/ImageSharp.Drawing.Paths/DrawLines.cs @@ -0,0 +1,120 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Numerics; + using Drawing; + using Drawing.Brushes; + using Drawing.Pens; + using Drawing.Processors; + using SixLabors.Shapes; + + using Path = SixLabors.Shapes.Path; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush + /// + /// The type of the color. + /// The source. + /// The brush. + /// The thickness. + /// The points. + /// The options. + /// + /// The Image + /// + public static Image DrawLines(this Image source, IBrush brush, float thickness, Vector2[] points, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(new Pen(brush, thickness), new Path(new LinearLineSegment(points)), options); + } + + /// + /// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush + /// + /// The type of the color. + /// The source. + /// The brush. + /// The thickness. + /// The points. + /// The Image + public static Image DrawLines(this Image source, IBrush brush, float thickness, Vector2[] points) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(new Pen(brush, thickness), new Path(new LinearLineSegment(points))); + } + + /// + /// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush + /// + /// The type of the color. + /// The source. + /// The color. + /// The thickness. + /// The points. + /// The Image + public static Image DrawLines(this Image source, TColor color, float thickness, Vector2[] points) + where TColor : struct, IPackedPixel, IEquatable + { + return source.DrawLines(new SolidBrush(color), thickness, points); + } + + /// + /// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush + /// + /// The type of the color. + /// The source. + /// The color. + /// The thickness. + /// The points. + /// The options. + /// + /// The Image + /// + public static Image DrawLines(this Image source, TColor color, float thickness, Vector2[] points, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.DrawLines(new SolidBrush(color), thickness, points, options); + } + + /// + /// Draws the provided Points as an open Linear path with the supplied pen + /// + /// The type of the color. + /// The source. + /// The pen. + /// The points. + /// The options. + /// + /// The Image + /// + public static Image DrawLines(this Image source, IPen pen, Vector2[] points, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(pen, new Path(new LinearLineSegment(points)), options); + } + + /// + /// Draws the provided Points as an open Linear path with the supplied pen + /// + /// The type of the color. + /// The source. + /// The pen. + /// The points. + /// The Image + public static Image DrawLines(this Image source, IPen pen, Vector2[] points) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(pen, new Path(new LinearLineSegment(points))); + } + } +} diff --git a/src/ImageSharp.Drawing.Paths/DrawPath.cs b/src/ImageSharp.Drawing.Paths/DrawPath.cs new file mode 100644 index 000000000..201984e0a --- /dev/null +++ b/src/ImageSharp.Drawing.Paths/DrawPath.cs @@ -0,0 +1,124 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Numerics; + using Drawing; + using Drawing.Brushes; + using Drawing.Pens; + using Drawing.Processors; + using SixLabors.Shapes; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Draws the outline of the polygon with the provided pen. + /// + /// The type of the color. + /// The source. + /// The pen. + /// The path. + /// The options. + /// + /// The Image + /// + public static Image Draw(this Image source, IPen pen, IPath path, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(pen, new ShapePath(path), options); + } + + /// + /// Draws the outline of the polygon with the provided pen. + /// + /// The type of the color. + /// The source. + /// The pen. + /// The path. + /// + /// The Image + /// + public static Image Draw(this Image source, IPen pen, IPath path) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(pen, path, GraphicsOptions.Default); + } + + /// + /// Draws the outline of the polygon with the provided brush at the provided thickness. + /// + /// The type of the color. + /// The source. + /// The brush. + /// The thickness. + /// The shape. + /// The options. + /// + /// The Image + /// + public static Image Draw(this Image source, IBrush brush, float thickness, IPath path, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(new Pen(brush, thickness), path, options); + } + + /// + /// Draws the outline of the polygon with the provided brush at the provided thickness. + /// + /// The type of the color. + /// The source. + /// The brush. + /// The thickness. + /// The path. + /// + /// The Image + /// + public static Image Draw(this Image source, IBrush brush, float thickness, IPath path) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(new Pen(brush, thickness), path); + } + + /// + /// Draws the outline of the polygon with the provided brush at the provided thickness. + /// + /// The type of the color. + /// The source. + /// The color. + /// The thickness. + /// The path. + /// The options. + /// + /// The Image + /// + public static Image Draw(this Image source, TColor color, float thickness, IPath path, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(new SolidBrush(color), thickness, path, options); + } + + /// + /// Draws the outline of the polygon with the provided brush at the provided thickness. + /// + /// The type of the color. + /// The source. + /// The color. + /// The thickness. + /// The path. + /// + /// The Image + /// + public static Image Draw(this Image source, TColor color, float thickness, IPath path) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(new SolidBrush(color), thickness, path); + } + } +} diff --git a/src/ImageSharp.Drawing.Paths/DrawPolygon.cs b/src/ImageSharp.Drawing.Paths/DrawPolygon.cs new file mode 100644 index 000000000..73d8ec0e7 --- /dev/null +++ b/src/ImageSharp.Drawing.Paths/DrawPolygon.cs @@ -0,0 +1,104 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Numerics; + using Drawing; + using Drawing.Brushes; + using Drawing.Pens; + using Drawing.Processors; + using SixLabors.Shapes; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness. + /// + /// The type of the color. + /// The source. + /// The brush. + /// The thickness. + /// The points. + /// The options. + /// + /// The Image + /// + public static Image DrawPolygon(this Image source, IBrush brush, float thickness, Vector2[] points, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(new Pen(brush, thickness), new Polygon(new LinearLineSegment(points)), options); + } + + /// + /// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness. + /// + /// The type of the color. + /// The source. + /// The brush. + /// The thickness. + /// The points. + /// The Image + public static Image DrawPolygon(this Image source, IBrush brush, float thickness, Vector2[] points) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(new Pen(brush, thickness), new Polygon(new LinearLineSegment(points))); + } + + /// + /// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness. + /// + /// The type of the color. + /// The source. + /// The color. + /// The thickness. + /// The points. + /// The Image + public static Image DrawPolygon(this Image source, TColor color, float thickness, Vector2[] points) + where TColor : struct, IPackedPixel, IEquatable + { + return source.DrawPolygon(new SolidBrush(color), thickness, points); + } + + /// + /// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness. + /// + /// The type of the color. + /// The source. + /// The color. + /// The thickness. + /// The points. + /// The options. + /// + /// The Image + /// + public static Image DrawPolygon(this Image source, TColor color, float thickness, Vector2[] points, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.DrawPolygon(new SolidBrush(color), thickness, points, options); + } + + /// + /// Draws the provided Points as a closed Linear Polygon with the provided Pen. + /// + /// The type of the color. + /// The source. + /// The pen. + /// The points. + /// The options. + /// + /// The Image + /// + public static Image DrawPolygon(this Image source, IPen pen, Vector2[] points, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(pen, new Polygon(new LinearLineSegment(points)), options); + } + } +} diff --git a/src/ImageSharp.Drawing/DrawRectangle.cs b/src/ImageSharp.Drawing.Paths/DrawRectangle.cs similarity index 73% rename from src/ImageSharp.Drawing/DrawRectangle.cs rename to src/ImageSharp.Drawing.Paths/DrawRectangle.cs index 243aa082d..28ea3d6e0 100644 --- a/src/ImageSharp.Drawing/DrawRectangle.cs +++ b/src/ImageSharp.Drawing.Paths/DrawRectangle.cs @@ -30,10 +30,10 @@ namespace ImageSharp /// /// The Image /// - public static Image DrawPolygon(this Image source, IPen pen, RectangleF shape, GraphicsOptions options) + public static Image Draw(this Image source, IPen pen, Rectangle shape, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(new DrawPathProcessor(pen, new ShapeRegion(new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height)), options)); + return source.Draw(pen, new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height), options); } /// @@ -44,10 +44,10 @@ namespace ImageSharp /// The pen. /// The shape. /// The Image - public static Image DrawPolygon(this Image source, IPen pen, RectangleF shape) + public static Image Draw(this Image source, IPen pen, Rectangle shape) where TColor : struct, IPackedPixel, IEquatable { - return source.DrawPolygon(pen, shape, GraphicsOptions.Default); + return source.Draw(pen, shape, GraphicsOptions.Default); } /// @@ -62,10 +62,10 @@ namespace ImageSharp /// /// The Image /// - public static Image DrawPolygon(this Image source, IBrush brush, float thickness, RectangleF shape, GraphicsOptions options) + public static Image Draw(this Image source, IBrush brush, float thickness, Rectangle shape, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { - return source.DrawPolygon(new Pen(brush, thickness), shape, options); + return source.Draw(new Pen(brush, thickness), shape, options); } /// @@ -77,10 +77,10 @@ namespace ImageSharp /// The thickness. /// The shape. /// The Image - public static Image DrawPolygon(this Image source, IBrush brush, float thickness, RectangleF shape) + public static Image Draw(this Image source, IBrush brush, float thickness, Rectangle shape) where TColor : struct, IPackedPixel, IEquatable { - return source.DrawPolygon(new Pen(brush, thickness), shape); + return source.Draw(new Pen(brush, thickness), shape); } /// @@ -95,10 +95,10 @@ namespace ImageSharp /// /// The Image /// - public static Image DrawPolygon(this Image source, TColor color, float thickness, RectangleF shape, GraphicsOptions options) + public static Image Draw(this Image source, TColor color, float thickness, Rectangle shape, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { - return source.DrawPolygon(new SolidBrush(color), thickness, shape, options); + return source.Draw(new SolidBrush(color), thickness, shape, options); } /// @@ -110,10 +110,10 @@ namespace ImageSharp /// The thickness. /// The shape. /// The Image - public static Image DrawPolygon(this Image source, TColor color, float thickness, RectangleF shape) + public static Image Draw(this Image source, TColor color, float thickness, Rectangle shape) where TColor : struct, IPackedPixel, IEquatable { - return source.DrawPolygon(new SolidBrush(color), thickness, shape); + return source.Draw(new SolidBrush(color), thickness, shape); } } } diff --git a/src/ImageSharp.Drawing.Paths/DrawShape.cs b/src/ImageSharp.Drawing.Paths/DrawShape.cs new file mode 100644 index 000000000..15ae2b179 --- /dev/null +++ b/src/ImageSharp.Drawing.Paths/DrawShape.cs @@ -0,0 +1,118 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Numerics; + using Drawing; + using Drawing.Brushes; + using Drawing.Pens; + using Drawing.Processors; + using SixLabors.Shapes; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Draws the outline of the polygon with the provided pen. + /// + /// The type of the color. + /// The source. + /// The pen. + /// The shape. + /// The options. + /// + /// The Image + /// + public static Image Draw(this Image source, IPen pen, IShape shape, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(pen, new ShapePath(shape), options); + } + + /// + /// Draws the outline of the polygon with the provided pen. + /// + /// The type of the color. + /// The source. + /// The pen. + /// The shape. + /// The Image + public static Image Draw(this Image source, IPen pen, IShape shape) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(pen, shape, GraphicsOptions.Default); + } + + /// + /// Draws the outline of the polygon with the provided brush at the provided thickness. + /// + /// The type of the color. + /// The source. + /// The brush. + /// The thickness. + /// The shape. + /// The options. + /// + /// The Image + /// + public static Image Draw(this Image source, IBrush brush, float thickness, IShape shape, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(new Pen(brush, thickness), shape, options); + } + + /// + /// Draws the outline of the polygon with the provided brush at the provided thickness. + /// + /// The type of the color. + /// The source. + /// The brush. + /// The thickness. + /// The shape. + /// The Image + public static Image Draw(this Image source, IBrush brush, float thickness, IShape shape) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(new Pen(brush, thickness), shape); + } + + /// + /// Draws the outline of the polygon with the provided brush at the provided thickness. + /// + /// The type of the color. + /// The source. + /// The color. + /// The thickness. + /// The shape. + /// The options. + /// + /// The Image + /// + public static Image Draw(this Image source, TColor color, float thickness, IShape shape, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(new SolidBrush(color), thickness, shape, options); + } + + /// + /// Draws the outline of the polygon with the provided brush at the provided thickness. + /// + /// The type of the color. + /// The source. + /// The color. + /// The thickness. + /// The shape. + /// The Image + public static Image Draw(this Image source, TColor color, float thickness, IShape shape) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(new SolidBrush(color), thickness, shape); + } + } +} diff --git a/src/ImageSharp.Drawing.Paths/FillPaths.cs b/src/ImageSharp.Drawing.Paths/FillPaths.cs new file mode 100644 index 000000000..3095ee7cd --- /dev/null +++ b/src/ImageSharp.Drawing.Paths/FillPaths.cs @@ -0,0 +1,87 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Numerics; + using Drawing; + using Drawing.Brushes; + using Drawing.Processors; + + using SixLabors.Shapes; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Flood fills the image in the shape of the provided polygon with the specified brush.. + /// + /// The type of the color. + /// The source. + /// The brush. + /// The shape. + /// The graphics options. + /// + /// The Image + /// + public static Image Fill(this Image source, IBrush brush, IPath path, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Fill(brush, new ShapeRegion(path), options); + } + + /// + /// Flood fills the image in the shape of the provided polygon with the specified brush. + /// + /// The type of the color. + /// The source. + /// The brush. + /// The path. + /// + /// The Image + /// + public static Image Fill(this Image source, IBrush brush, IPath path) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Fill(brush, new ShapeRegion(path), GraphicsOptions.Default); + } + + /// + /// Flood fills the image in the shape of the provided polygon with the specified brush.. + /// + /// The type of the color. + /// The source. + /// The color. + /// The path. + /// The options. + /// + /// The Image + /// + public static Image Fill(this Image source, TColor color, IPath path, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Fill(new SolidBrush(color), path, options); + } + + /// + /// Flood fills the image in the shape of the provided polygon with the specified brush.. + /// + /// The type of the color. + /// The source. + /// The color. + /// The path. + /// + /// The Image + /// + public static Image Fill(this Image source, TColor color, IPath path) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Fill(new SolidBrush(color), path); + } + } +} diff --git a/src/ImageSharp.Drawing.Paths/FillPolygon.cs b/src/ImageSharp.Drawing.Paths/FillPolygon.cs new file mode 100644 index 000000000..4092e5fc6 --- /dev/null +++ b/src/ImageSharp.Drawing.Paths/FillPolygon.cs @@ -0,0 +1,83 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Numerics; + using Drawing; + using Drawing.Brushes; + using Drawing.Processors; + + using SixLabors.Shapes; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Flood fills the image in the shape of a Linear polygon described by the points + /// + /// The type of the color. + /// The source. + /// The brush. + /// The points. + /// The options. + /// + /// The Image + /// + public static Image FillPolygon(this Image source, IBrush brush, Vector2[] points, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Fill(brush, new Polygon(new LinearLineSegment(points)), options); + } + + /// + /// Flood fills the image in the shape of a Linear polygon described by the points + /// + /// The type of the color. + /// The source. + /// The brush. + /// The points. + /// The Image + public static Image FillPolygon(this Image source, IBrush brush, Vector2[] points) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Fill(brush, new Polygon(new LinearLineSegment(points))); + } + + /// + /// Flood fills the image in the shape of a Linear polygon described by the points + /// + /// The type of the color. + /// The source. + /// The color. + /// The points. + /// The options. + /// + /// The Image + /// + public static Image FillPolygon(this Image source, TColor color, Vector2[] points, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Fill(new SolidBrush(color), new Polygon(new LinearLineSegment(points)), options); + } + + /// + /// Flood fills the image in the shape of a Linear polygon described by the points + /// + /// The type of the color. + /// The source. + /// The color. + /// The points. + /// The Image + public static Image FillPolygon(this Image source, TColor color, Vector2[] points) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Fill(new SolidBrush(color), new Polygon(new LinearLineSegment(points))); + } + } +} diff --git a/src/ImageSharp.Drawing/FillRectangle.cs b/src/ImageSharp.Drawing.Paths/FillRectangle.cs similarity index 93% rename from src/ImageSharp.Drawing/FillRectangle.cs rename to src/ImageSharp.Drawing.Paths/FillRectangle.cs index d60a5a19d..aef777edd 100644 --- a/src/ImageSharp.Drawing/FillRectangle.cs +++ b/src/ImageSharp.Drawing.Paths/FillRectangle.cs @@ -27,7 +27,7 @@ namespace ImageSharp /// /// The Image /// - public static Image Fill(this Image source, IBrush brush, RectangleF shape, GraphicsOptions options) + public static Image Fill(this Image source, IBrush brush, Rectangle shape, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { return source.Fill(brush, new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height), options); @@ -41,7 +41,7 @@ namespace ImageSharp /// The brush. /// The shape. /// The Image - public static Image Fill(this Image source, IBrush brush, RectangleF shape) + public static Image Fill(this Image source, IBrush brush, Rectangle shape) where TColor : struct, IPackedPixel, IEquatable { return source.Fill(brush, new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height)); @@ -58,7 +58,7 @@ namespace ImageSharp /// /// The Image /// - public static Image Fill(this Image source, TColor color, RectangleF shape, GraphicsOptions options) + public static Image Fill(this Image source, TColor color, Rectangle shape, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { return source.Fill(new SolidBrush(color), shape, options); @@ -72,7 +72,7 @@ namespace ImageSharp /// The color. /// The shape. /// The Image - public static Image Fill(this Image source, TColor color, RectangleF shape) + public static Image Fill(this Image source, TColor color, Rectangle shape) where TColor : struct, IPackedPixel, IEquatable { return source.Fill(new SolidBrush(color), shape); diff --git a/src/ImageSharp.Drawing.Paths/FillShapes.cs b/src/ImageSharp.Drawing.Paths/FillShapes.cs new file mode 100644 index 000000000..9fe473ee1 --- /dev/null +++ b/src/ImageSharp.Drawing.Paths/FillShapes.cs @@ -0,0 +1,83 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Numerics; + using Drawing; + using Drawing.Brushes; + using Drawing.Processors; + + using SixLabors.Shapes; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Flood fills the image in the shape of the provided polygon with the specified brush.. + /// + /// The type of the color. + /// The source. + /// The brush. + /// The shape. + /// The graphics options. + /// + /// The Image + /// + public static Image Fill(this Image source, IBrush brush, IShape shape, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Fill(brush, new ShapeRegion(shape), options); + } + + /// + /// Flood fills the image in the shape of the provided polygon with the specified brush. + /// + /// The type of the color. + /// The source. + /// The brush. + /// The shape. + /// The Image + public static Image Fill(this Image source, IBrush brush, IShape shape) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Fill(brush, new ShapeRegion(shape), GraphicsOptions.Default); + } + + /// + /// Flood fills the image in the shape of the provided polygon with the specified brush.. + /// + /// The type of the color. + /// The source. + /// The color. + /// The shape. + /// The options. + /// + /// The Image + /// + public static Image Fill(this Image source, TColor color, IShape shape, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Fill(new SolidBrush(color), shape, options); + } + + /// + /// Flood fills the image in the shape of the provided polygon with the specified brush.. + /// + /// The type of the color. + /// The source. + /// The color. + /// The shape. + /// The Image + public static Image Fill(this Image source, TColor color, IShape shape) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Fill(new SolidBrush(color), shape); + } + } +} diff --git a/src/ImageSharp.Drawing.Paths/ImageSharp.Drawing.Paths.xproj b/src/ImageSharp.Drawing.Paths/ImageSharp.Drawing.Paths.xproj new file mode 100644 index 000000000..196f7bc1b --- /dev/null +++ b/src/ImageSharp.Drawing.Paths/ImageSharp.Drawing.Paths.xproj @@ -0,0 +1,25 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + e5bd4f96-28a8-410c-8b63-1c5731948549 + ImageSharp.Drawing + .\obj + .\bin\ + v4.5.1 + + + 2.0 + + + True + + + + + + \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processors/PointInfoExtensions.cs b/src/ImageSharp.Drawing.Paths/PointInfoExtensions.cs similarity index 62% rename from src/ImageSharp.Drawing/Processors/PointInfoExtensions.cs rename to src/ImageSharp.Drawing.Paths/PointInfoExtensions.cs index 6613d15ac..ea38dfb9d 100644 --- a/src/ImageSharp.Drawing/Processors/PointInfoExtensions.cs +++ b/src/ImageSharp.Drawing.Paths/PointInfoExtensions.cs @@ -3,19 +3,8 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Drawing.Processors +namespace ImageSharp.Drawing { - using System; - using System.Buffers; - using System.Numerics; - using System.Threading.Tasks; - using Drawing; - using ImageSharp.Processing; - using SixLabors.Shapes; - - using PointInfo = ImageSharp.Drawing.PointInfo; - using Rectangle = ImageSharp.Rectangle; - /// /// Extension methods for helping to bridge Shaper2D and ImageSharp primitives. /// @@ -29,10 +18,10 @@ namespace ImageSharp.Drawing.Processors public static PointInfo Convert(this SixLabors.Shapes.PointInfo source) { return new PointInfo - { - DistanceAlongPath = source.DistanceAlongPath, - DistanceFromPath = source.DistanceFromPath - }; + { + DistanceAlongPath = source.DistanceAlongPath, + DistanceFromPath = source.DistanceFromPath + }; } } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing.Paths/Properties/AssemblyInfo.cs b/src/ImageSharp.Drawing.Paths/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..fba25a9db --- /dev/null +++ b/src/ImageSharp.Drawing.Paths/Properties/AssemblyInfo.cs @@ -0,0 +1,6 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +// Common values read from `AssemblyInfo.Common.cs` diff --git a/src/ImageSharp.Drawing/Processors/RectangleExtensions.cs b/src/ImageSharp.Drawing.Paths/RectangleExtensions.cs similarity index 58% rename from src/ImageSharp.Drawing/Processors/RectangleExtensions.cs rename to src/ImageSharp.Drawing.Paths/RectangleExtensions.cs index 327b618fa..05d6bb6ce 100644 --- a/src/ImageSharp.Drawing/Processors/RectangleExtensions.cs +++ b/src/ImageSharp.Drawing.Paths/RectangleExtensions.cs @@ -20,13 +20,17 @@ namespace ImageSharp.Drawing.Processors internal static class RectangleExtensions { /// - /// Converts a Shaper2D to an ImageSharp . + /// Converts a Shaper2D to an ImageSharp by creating a the entirely surrounds the source. /// /// The source. - /// A representation of this - public static RectangleF Convert(this SixLabors.Shapes.Rectangle source) + /// A representation of this + public static Rectangle Convert(this SixLabors.Shapes.Rectangle source) { - return new RectangleF(source.Location.X, source.Location.Y, source.Size.Width, source.Size.Height); + int y = (int)Math.Floor(source.Y); + int width = (int)Math.Ceiling(source.Size.Width); + int x = (int)Math.Floor(source.X); + int height = (int)Math.Ceiling(source.Size.Height); + return new Rectangle(x, y, width, height); } } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/ShapeRegion.cs b/src/ImageSharp.Drawing.Paths/ShapePath.cs similarity index 83% rename from src/ImageSharp.Drawing/ShapeRegion.cs rename to src/ImageSharp.Drawing.Paths/ShapePath.cs index 6bd070367..63d5fc9e1 100644 --- a/src/ImageSharp.Drawing/ShapeRegion.cs +++ b/src/ImageSharp.Drawing.Paths/ShapePath.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -18,8 +18,7 @@ namespace ImageSharp.Drawing /// /// A drawable mapping between a / and a drawable/fillable region. /// - /// - internal class ShapeRegion : IDrawableRegion + internal class ShapePath : ImageSharp.Drawing.Path { /// /// The fillable shape @@ -32,10 +31,10 @@ namespace ImageSharp.Drawing private readonly ImmutableArray paths; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The path. - public ShapeRegion(IPath path) + public ShapePath(IPath path) : this(ImmutableArray.Create(path)) { this.shape = path.AsShape(); @@ -43,10 +42,10 @@ namespace ImageSharp.Drawing } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The shape. - public ShapeRegion(IShape shape) + public ShapePath(IShape shape) : this(shape.Paths) { this.shape = shape; @@ -54,10 +53,10 @@ namespace ImageSharp.Drawing } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The paths. - private ShapeRegion(ImmutableArray paths) + private ShapePath(ImmutableArray paths) { this.paths = paths; } @@ -68,7 +67,7 @@ namespace ImageSharp.Drawing /// /// The maximum intersections. /// - public int MaxIntersections => this.shape.MaxIntersections; + public override int MaxIntersections => this.shape.MaxIntersections; /// /// Gets the bounds. @@ -76,7 +75,7 @@ namespace ImageSharp.Drawing /// /// The bounds. /// - public Rectangle Bounds { get; } + public override Rectangle Bounds { get; } /// /// Scans the X axis for intersections. @@ -88,7 +87,7 @@ namespace ImageSharp.Drawing /// /// The number of intersections found. /// - public int ScanX(int x, float[] buffer, int length, int offset) + public override int ScanX(int x, float[] buffer, int length, int offset) { var start = new Vector2(x, this.Bounds.Top - 1); var end = new Vector2(x, this.Bounds.Bottom + 1); @@ -125,10 +124,10 @@ namespace ImageSharp.Drawing /// /// The number of intersections found. /// - public int ScanY(int y, float[] buffer, int length, int offset) + public override int ScanY(int y, float[] buffer, int length, int offset) { - var start = new Vector2(this.Bounds.Left - 1, y); - var end = new Vector2(this.Bounds.Right + 1, y); + var start = new Vector2(float.MinValue, y); + var end = new Vector2(float.MaxValue, y); Vector2[] innerbuffer = ArrayPool.Shared.Rent(length); try { @@ -158,7 +157,7 @@ namespace ImageSharp.Drawing /// The x. /// The y. /// Information about the the point - public PointInfo GetPointInfo(int x, int y) + public override PointInfo GetPointInfo(int x, int y) { var point = new Vector2(x, y); SixLabors.Shapes.PointInfo result = default(SixLabors.Shapes.PointInfo); diff --git a/src/ImageSharp.Drawing.Paths/ShapeRegion.cs b/src/ImageSharp.Drawing.Paths/ShapeRegion.cs new file mode 100644 index 000000000..d1a59a99d --- /dev/null +++ b/src/ImageSharp.Drawing.Paths/ShapeRegion.cs @@ -0,0 +1,137 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Drawing +{ + using System.Buffers; + using System.Collections.Immutable; + using System.Numerics; + + using ImageSharp.Drawing.Processors; + + using SixLabors.Shapes; + + using Rectangle = ImageSharp.Rectangle; + + /// + /// A drawable mapping between a / and a drawable/fillable region. + /// + internal class ShapeRegion : Region + { + /// + /// The fillable shape + /// + private readonly IShape shape; + + /// + /// Initializes a new instance of the class. + /// + /// The path. + public ShapeRegion(IPath path) + : this(path.AsShape()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The shape. + public ShapeRegion(IShape shape) + { + this.shape = shape; + this.Bounds = shape.Bounds.Convert(); + } + + /// + /// Gets the maximum number of intersections to could be returned. + /// + /// + /// The maximum intersections. + /// + public override int MaxIntersections => this.shape.MaxIntersections; + + /// + /// Gets the bounds. + /// + /// + /// The bounds. + /// + public override Rectangle Bounds { get; } + + /// + /// Scans the X axis for intersections. + /// + /// The x. + /// The buffer. + /// The length. + /// The offset. + /// + /// The number of intersections found. + /// + public override int ScanX(int x, float[] buffer, int length, int offset) + { + var start = new Vector2(x, this.Bounds.Top - 1); + var end = new Vector2(x, this.Bounds.Bottom + 1); + Vector2[] innerbuffer = ArrayPool.Shared.Rent(length); + try + { + int count = this.shape.FindIntersections( + start, + end, + innerbuffer, + length, + 0); + + for (var i = 0; i < count; i++) + { + buffer[i + offset] = innerbuffer[i].Y; + } + + return count; + } + finally + { + ArrayPool.Shared.Return(innerbuffer); + } + } + + /// + /// Scans the Y axis for intersections. + /// + /// The position along the y axis to find intersections. + /// The buffer. + /// The length. + /// The offset. + /// + /// The number of intersections found. + /// + public override int ScanY(int y, float[] buffer, int length, int offset) + { + var start = new Vector2(float.MinValue, y); + var end = new Vector2(float.MaxValue, y); + Vector2[] innerbuffer = ArrayPool.Shared.Rent(length); + try + { + int count = this.shape.FindIntersections( + start, + end, + innerbuffer, + length, + 0); + + for (var i = 0; i < count; i++) + { + buffer[i + offset] = innerbuffer[i].X; + } + + return count; + } + finally + { + ArrayPool.Shared.Return(innerbuffer); + } + } + } +} diff --git a/src/ImageSharp.Drawing.Paths/project.json b/src/ImageSharp.Drawing.Paths/project.json new file mode 100644 index 000000000..cdc5d2244 --- /dev/null +++ b/src/ImageSharp.Drawing.Paths/project.json @@ -0,0 +1,96 @@ +{ + "version": "1.0.0-alpha1-*", + "title": "ImageSharp.Drawing.Paths", + "description": "A cross-platform library for the processing of image files; written in C#", + "authors": [ + "James Jackson-South and contributors" + ], + "packOptions": { + "owners": [ + "James Jackson-South and contributors" + ], + "projectUrl": "https://github.com/JimBobSquarePants/ImageSharp", + "licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0", + "iconUrl": "https://raw.githubusercontent.com/JimBobSquarePants/ImageSharp/master/build/icons/imagesharp-logo-128.png", + "requireLicenseAcceptance": false, + "repository": { + "type": "git", + "url": "https://github.com/JimBobSquarePants/ImageSharp" + }, + "tags": [ + "Image Resize Crop Gif Jpg Jpeg Bitmap Png Core" + ] + }, + "buildOptions": { + "allowUnsafe": true, + "xmlDoc": true, + "additionalArguments": [ "/additionalfile:../Shared/stylecop.json", "/ruleset:../../ImageSharp.ruleset" ], + "compile": [ + "../Shared/*.cs" + ] + }, + "configurations": { + "Release": { + "buildOptions": { + "warningsAsErrors": true, + "optimize": true + } + } + }, + "dependencies": { + "ImageSharp": { + "target": "project" + }, + "ImageSharp.Drawing": { + "target": "project" + }, + "SixLabors.Shapes": "0.1.0-ci0050", + "StyleCop.Analyzers": { + "version": "1.0.0", + "type": "build" + }, + "System.Buffers": "4.0.0", + "System.Runtime.CompilerServices.Unsafe": "4.0.0" + }, + "frameworks": { + "netstandard1.1": { + "dependencies": { + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11", + "System.Diagnostics.Tools": "4.0.1", + "System.IO": "4.1.0", + "System.IO.Compression": "4.1.0", + "System.Linq": "4.1.0", + "System.Numerics.Vectors": "4.1.1", + "System.ObjectModel": "4.0.12", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime.Extensions": "4.1.0", + "System.Runtime.InteropServices": "4.1.0", + "System.Runtime.Numerics": "4.0.1", + "System.Text.Encoding.Extensions": "4.0.11", + "System.Threading": "4.0.11", + "System.Threading.Tasks": "4.0.11", + "System.Threading.Tasks.Parallel": "4.0.1" + } + }, + "net45": { + "dependencies": { + "System.Numerics.Vectors": "4.1.1", + "System.Threading.Tasks.Parallel": "4.0.0" + }, + "frameworkAssemblies": { + "System.Runtime": { "type": "build" } + } + }, + "net461": { + "dependencies": { + "System.Threading.Tasks.Parallel": "4.0.0" + }, + "frameworkAssemblies": { + "System.Runtime": { "type": "build" }, + "System.Numerics": "4.0.0.0", + "System.Numerics.Vectors": "4.0.0.0" + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs index 9103dfdf6..97d3f840c 100644 --- a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs +++ b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs @@ -16,7 +16,6 @@ namespace ImageSharp.Drawing.Processors public abstract class BrushApplicator : IDisposable // disposable will be required if/when there is an ImageBrush where TColor : struct, IPackedPixel, IEquatable { - /// /// Gets the color for a single pixel. /// diff --git a/src/ImageSharp.Drawing/Draw.cs b/src/ImageSharp.Drawing/Draw.cs deleted file mode 100644 index 55dba9257..000000000 --- a/src/ImageSharp.Drawing/Draw.cs +++ /dev/null @@ -1,506 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - using System.Numerics; - using Drawing; - using Drawing.Brushes; - using Drawing.Pens; - using Drawing.Processors; - using SixLabors.Shapes; - - /// - /// Extension methods for the type. - /// - public static partial class ImageExtensions - { - /// - /// Draws the outline of the polygon with the provided pen. - /// - /// The type of the color. - /// The source. - /// The pen. - /// The shape. - /// The options. - /// - /// The Image - /// - public static Image DrawPolygon(this Image source, IPen pen, IShape shape, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable - { - return source.Apply(new DrawPathProcessor(pen, new ShapeRegion(shape), options)); - } - - /// - /// Draws the outline of the polygon with the provided pen. - /// - /// The type of the color. - /// The source. - /// The pen. - /// The shape. - /// The Image - public static Image DrawPolygon(this Image source, IPen pen, IShape shape) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawPolygon(pen, shape, GraphicsOptions.Default); - } - - /// - /// Draws the outline of the polygon with the provided brush at the provided thickness. - /// - /// The type of the color. - /// The source. - /// The brush. - /// The thickness. - /// The shape. - /// The options. - /// - /// The Image - /// - public static Image DrawPolygon(this Image source, IBrush brush, float thickness, IShape shape, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawPolygon(new Pen(brush, thickness), shape, options); - } - - /// - /// Draws the outline of the polygon with the provided brush at the provided thickness. - /// - /// The type of the color. - /// The source. - /// The brush. - /// The thickness. - /// The shape. - /// The Image - public static Image DrawPolygon(this Image source, IBrush brush, float thickness, IShape shape) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawPolygon(new Pen(brush, thickness), shape); - } - - /// - /// Draws the outline of the polygon with the provided brush at the provided thickness. - /// - /// The type of the color. - /// The source. - /// The color. - /// The thickness. - /// The shape. - /// The options. - /// - /// The Image - /// - public static Image DrawPolygon(this Image source, TColor color, float thickness, IShape shape, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawPolygon(new SolidBrush(color), thickness, shape, options); - } - - /// - /// Draws the outline of the polygon with the provided brush at the provided thickness. - /// - /// The type of the color. - /// The source. - /// The color. - /// The thickness. - /// The shape. - /// The Image - public static Image DrawPolygon(this Image source, TColor color, float thickness, IShape shape) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawPolygon(new SolidBrush(color), thickness, shape); - } - - /// - /// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness. - /// - /// The type of the color. - /// The source. - /// The brush. - /// The thickness. - /// The points. - /// The options. - /// - /// The Image - /// - public static Image DrawPolygon(this Image source, IBrush brush, float thickness, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawPolygon(new Pen(brush, thickness), new Polygon(new LinearLineSegment(points)), options); - } - - /// - /// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness. - /// - /// The type of the color. - /// The source. - /// The brush. - /// The thickness. - /// The points. - /// The Image - public static Image DrawPolygon(this Image source, IBrush brush, float thickness, Vector2[] points) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawPolygon(new Pen(brush, thickness), new Polygon(new LinearLineSegment(points))); - } - - /// - /// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness. - /// - /// The type of the color. - /// The source. - /// The color. - /// The thickness. - /// The points. - /// The Image - public static Image DrawPolygon(this Image source, TColor color, float thickness, Vector2[] points) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawPolygon(new SolidBrush(color), thickness, points); - } - - /// - /// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness. - /// - /// The type of the color. - /// The source. - /// The color. - /// The thickness. - /// The points. - /// The options. - /// - /// The Image - /// - public static Image DrawPolygon(this Image source, TColor color, float thickness, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawPolygon(new SolidBrush(color), thickness, points, options); - } - - /// - /// Draws the provided Points as a closed Linear Polygon with the provided Pen. - /// - /// The type of the color. - /// The source. - /// The pen. - /// The points. - /// The options. - /// - /// The Image - /// - public static Image DrawPolygon(this Image source, IPen pen, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawPolygon(pen, new Polygon(new LinearLineSegment(points)), options); - } - - /// - /// Draws the provided Points as a closed Linear Polygon with the provided Pen. - /// - /// The type of the color. - /// The source. - /// The pen. - /// The points. - /// The Image - public static Image DrawPolygon(this Image source, IPen pen, Vector2[] points) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawPolygon(pen, new Polygon(new LinearLineSegment(points))); - } - - /// - /// Draws the path with the provided pen. - /// - /// The type of the color. - /// The source. - /// The pen. - /// The path. - /// The options. - /// - /// The Image - /// - public static Image DrawPath(this Image source, IPen pen, IPath path, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable - { - return source.Apply(new DrawPathProcessor(pen, new ShapeRegion(path), options)); - } - - /// - /// Draws the path with the provided pen. - /// - /// The type of the color. - /// The source. - /// The pen. - /// The path. - /// The Image - public static Image DrawPath(this Image source, IPen pen, IPath path) - where TColor : struct, IPackedPixel, IEquatable - { - return source.Apply(new DrawPathProcessor(pen, new ShapeRegion(path), GraphicsOptions.Default)); - } - - /// - /// Draws the path with the bursh at the privdied thickness. - /// - /// The type of the color. - /// The source. - /// The brush. - /// The thickness. - /// The path. - /// The options. - /// - /// The Image - /// - public static Image DrawPath(this Image source, IBrush brush, float thickness, IPath path, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawPath(new Pen(brush, thickness), path, options); - } - - /// - /// Draws the path with the bursh at the privdied thickness. - /// - /// The type of the color. - /// The source. - /// The brush. - /// The thickness. - /// The path. - /// The Image - public static Image DrawPath(this Image source, IBrush brush, float thickness, IPath path) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawPath(new Pen(brush, thickness), path); - } - - /// - /// Draws the path with the bursh at the privdied thickness. - /// - /// The type of the color. - /// The source. - /// The color. - /// The thickness. - /// The path. - /// The options. - /// - /// The Image - /// - public static Image DrawPath(this Image source, TColor color, float thickness, IPath path, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawPath(new SolidBrush(color), thickness, path, options); - } - - /// - /// Draws the path with the bursh at the privdied thickness. - /// - /// The type of the color. - /// The source. - /// The color. - /// The thickness. - /// The path. - /// The Image - public static Image DrawPath(this Image source, TColor color, float thickness, IPath path) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawPath(new SolidBrush(color), thickness, path); - } - - /// - /// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush - /// - /// The type of the color. - /// The source. - /// The brush. - /// The thickness. - /// The points. - /// The options. - /// - /// The Image - /// - public static Image DrawLines(this Image source, IBrush brush, float thickness, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawPath(new Pen(brush, thickness), new Path(new LinearLineSegment(points)), options); - } - - /// - /// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush - /// - /// The type of the color. - /// The source. - /// The brush. - /// The thickness. - /// The points. - /// The Image - public static Image DrawLines(this Image source, IBrush brush, float thickness, Vector2[] points) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawPath(new Pen(brush, thickness), new Path(new LinearLineSegment(points))); - } - - /// - /// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush - /// - /// The type of the color. - /// The source. - /// The color. - /// The thickness. - /// The points. - /// The Image - public static Image DrawLines(this Image source, TColor color, float thickness, Vector2[] points) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawLines(new SolidBrush(color), thickness, points); - } - - /// - /// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush - /// - /// The type of the color. - /// The source. - /// The color. - /// The thickness. - /// The points. - /// The options. - /// - /// The Image - /// - public static Image DrawLines(this Image source, TColor color, float thickness, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawLines(new SolidBrush(color), thickness, points, options); - } - - /// - /// Draws the provided Points as an open Linear path with the supplied pen - /// - /// The type of the color. - /// The source. - /// The pen. - /// The points. - /// The options. - /// - /// The Image - /// - public static Image DrawLines(this Image source, IPen pen, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawPath(pen, new Path(new LinearLineSegment(points)), options); - } - - /// - /// Draws the provided Points as an open Linear path with the supplied pen - /// - /// The type of the color. - /// The source. - /// The pen. - /// The points. - /// The Image - public static Image DrawLines(this Image source, IPen pen, Vector2[] points) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawPath(pen, new Path(new LinearLineSegment(points))); - } - - /// - /// Draws the provided Points as an open Bezier path at the provided thickness with the supplied brush - /// - /// The type of the color. - /// The source. - /// The brush. - /// The thickness. - /// The points. - /// The options. - /// - /// The Image - /// - public static Image DrawBeziers(this Image source, IBrush brush, float thickness, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawPath(new Pen(brush, thickness), new Path(new BezierLineSegment(points)), options); - } - - /// - /// Draws the provided Points as an open Bezier path at the provided thickness with the supplied brush - /// - /// The type of the color. - /// The source. - /// The brush. - /// The thickness. - /// The points. - /// The Image - public static Image DrawBeziers(this Image source, IBrush brush, float thickness, Vector2[] points) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawPath(new Pen(brush, thickness), new Path(new BezierLineSegment(points))); - } - - /// - /// Draws the provided Points as an open Bezier path at the provided thickness with the supplied brush - /// - /// The type of the color. - /// The source. - /// The color. - /// The thickness. - /// The points. - /// The Image - public static Image DrawBeziers(this Image source, TColor color, float thickness, Vector2[] points) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawBeziers(new SolidBrush(color), thickness, points); - } - - /// - /// Draws the provided Points as an open Bezier path at the provided thickness with the supplied brush - /// - /// The type of the color. - /// The source. - /// The color. - /// The thickness. - /// The points. - /// The options. - /// - /// The Image - /// - public static Image DrawBeziers(this Image source, TColor color, float thickness, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawBeziers(new SolidBrush(color), thickness, points, options); - } - - /// - /// Draws the provided Points as an open Bezier path with the supplied pen - /// - /// The type of the color. - /// The source. - /// The pen. - /// The points. - /// The options. - /// - /// The Image - /// - public static Image DrawBeziers(this Image source, IPen pen, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawPath(pen, new Path(new BezierLineSegment(points)), options); - } - - /// - /// Draws the provided Points as an open Bezier path with the supplied pen - /// - /// The type of the color. - /// The source. - /// The pen. - /// The points. - /// The Image - public static Image DrawBeziers(this Image source, IPen pen, Vector2[] points) - where TColor : struct, IPackedPixel, IEquatable - { - return source.DrawPath(pen, new Path(new BezierLineSegment(points))); - } - } -} diff --git a/src/ImageSharp.Drawing/DrawPath.cs b/src/ImageSharp.Drawing/DrawPath.cs new file mode 100644 index 000000000..828d50fb3 --- /dev/null +++ b/src/ImageSharp.Drawing/DrawPath.cs @@ -0,0 +1,123 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Numerics; + using Drawing; + using Drawing.Brushes; + using Drawing.Pens; + using Drawing.Processors; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Draws the outline of the region with the provided pen. + /// + /// The type of the color. + /// The source. + /// The pen. + /// The path. + /// The options. + /// + /// The Image + /// + public static Image Draw(this Image source, IPen pen, Path path, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Apply(new DrawPathProcessor(pen, path, options)); + } + + /// + /// Draws the outline of the polygon with the provided pen. + /// + /// The type of the color. + /// The source. + /// The pen. + /// The path. + /// + /// The Image + /// + public static Image Draw(this Image source, IPen pen, Path path) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(pen, path, GraphicsOptions.Default); + } + + /// + /// Draws the outline of the polygon with the provided brush at the provided thickness. + /// + /// The type of the color. + /// The source. + /// The brush. + /// The thickness. + /// The path. + /// The options. + /// + /// The Image + /// + public static Image Draw(this Image source, IBrush brush, float thickness, Path path, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(new Pen(brush, thickness), path, options); + } + + /// + /// Draws the outline of the polygon with the provided brush at the provided thickness. + /// + /// The type of the color. + /// The source. + /// The brush. + /// The thickness. + /// The path. + /// + /// The Image + /// + public static Image Draw(this Image source, IBrush brush, float thickness, Path path) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(new Pen(brush, thickness), path); + } + + /// + /// Draws the outline of the polygon with the provided brush at the provided thickness. + /// + /// The type of the color. + /// The source. + /// The color. + /// The thickness. + /// The path. + /// The options. + /// + /// The Image + /// + public static Image Draw(this Image source, TColor color, float thickness, Path path, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(new SolidBrush(color), thickness, path, options); + } + + /// + /// Draws the outline of the polygon with the provided brush at the provided thickness. + /// + /// The type of the color. + /// The source. + /// The color. + /// The thickness. + /// The path. + /// + /// The Image + /// + public static Image Draw(this Image source, TColor color, float thickness, Path path) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(new SolidBrush(color), thickness, path); + } + } +} diff --git a/src/ImageSharp.Drawing/Fill.cs b/src/ImageSharp.Drawing/Fill.cs deleted file mode 100644 index f94d9ca6f..000000000 --- a/src/ImageSharp.Drawing/Fill.cs +++ /dev/null @@ -1,173 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - using System.Numerics; - using Drawing; - using Drawing.Brushes; - using Drawing.Processors; - - using SixLabors.Shapes; - - /// - /// Extension methods for the type. - /// - public static partial class ImageExtensions - { - /// - /// Flood fills the image with the specified brush. - /// - /// The type of the color. - /// The source. - /// The brush. - /// The Image - public static Image Fill(this Image source, IBrush brush) - where TColor : struct, IPackedPixel, IEquatable - { - return source.Apply(new FillProcessor(brush)); - } - - /// - /// Flood fills the image with the specified color. - /// - /// The type of the color. - /// The source. - /// The color. - /// The Image - public static Image Fill(this Image source, TColor color) - where TColor : struct, IPackedPixel, IEquatable - { - return source.Fill(new SolidBrush(color)); - } - - /// - /// Flood fills the image in the shape o fhte provided polygon with the specified brush.. - /// - /// The type of the color. - /// The source. - /// The brush. - /// The shape. - /// The graphics options. - /// The Image - public static Image Fill(this Image source, IBrush brush, IShape shape, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable - { - return source.Apply(new FillShapeProcessor(brush, new ShapeRegion(shape), options)); - } - - /// - /// Flood fills the image in the shape of the provided polygon with the specified brush. - /// - /// The type of the color. - /// The source. - /// The brush. - /// The shape. - /// The Image - public static Image Fill(this Image source, IBrush brush, IShape shape) - where TColor : struct, IPackedPixel, IEquatable - { - return source.Apply(new FillShapeProcessor(brush, new ShapeRegion(shape), GraphicsOptions.Default)); - } - - /// - /// Flood fills the image in the shape o fhte provided polygon with the specified brush.. - /// - /// The type of the color. - /// The source. - /// The color. - /// The shape. - /// The options. - /// - /// The Image - /// - public static Image Fill(this Image source, TColor color, IShape shape, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable - { - return source.Fill(new SolidBrush(color), shape, options); - } - - /// - /// Flood fills the image in the shape o fhte provided polygon with the specified brush.. - /// - /// The type of the color. - /// The source. - /// The color. - /// The shape. - /// The Image - public static Image Fill(this Image source, TColor color, IShape shape) - where TColor : struct, IPackedPixel, IEquatable - { - return source.Fill(new SolidBrush(color), shape); - } - - /// - /// Flood fills the image in the shape of a Linear polygon described by the points - /// - /// The type of the color. - /// The source. - /// The brush. - /// The points. - /// The options. - /// - /// The Image - /// - public static Image FillPolygon(this Image source, IBrush brush, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable - { - // using Polygon directly instead of LinearPolygon as its will have less indirection - return source.Fill(brush, new Polygon(new LinearLineSegment(points)), options); - } - - /// - /// Flood fills the image in the shape of a Linear polygon described by the points - /// - /// The type of the color. - /// The source. - /// The brush. - /// The points. - /// The Image - public static Image FillPolygon(this Image source, IBrush brush, Vector2[] points) - where TColor : struct, IPackedPixel, IEquatable - { - // using Polygon directly instead of LinearPolygon as its will have less indirection - return source.Fill(brush, new Polygon(new LinearLineSegment(points))); - } - - /// - /// Flood fills the image in the shape of a Linear polygon described by the points - /// - /// The type of the color. - /// The source. - /// The color. - /// The points. - /// The options. - /// - /// The Image - /// - public static Image FillPolygon(this Image source, TColor color, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable - { - // using Polygon directly instead of LinearPolygon as its will have less indirection - return source.Fill(new SolidBrush(color), new Polygon(new LinearLineSegment(points)), options); - } - - /// - /// Flood fills the image in the shape of a Linear polygon described by the points - /// - /// The type of the color. - /// The source. - /// The color. - /// The points. - /// The Image - public static Image FillPolygon(this Image source, TColor color, Vector2[] points) - where TColor : struct, IPackedPixel, IEquatable - { - // using Polygon directly instead of LinearPolygon as its will have less indirection - return source.Fill(new SolidBrush(color), new Polygon(new LinearLineSegment(points))); - } - } -} diff --git a/src/ImageSharp.Drawing/FillRegion.cs b/src/ImageSharp.Drawing/FillRegion.cs new file mode 100644 index 000000000..8d4f20b67 --- /dev/null +++ b/src/ImageSharp.Drawing/FillRegion.cs @@ -0,0 +1,111 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Numerics; + using Drawing; + using Drawing.Brushes; + using Drawing.Processors; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Flood fills the image with the specified brush. + /// + /// The type of the color. + /// The source. + /// The brush. + /// The Image + public static Image Fill(this Image source, IBrush brush) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Apply(new FillProcessor(brush)); + } + + /// + /// Flood fills the image with the specified color. + /// + /// The type of the color. + /// The source. + /// The color. + /// The Image + public static Image Fill(this Image source, TColor color) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Fill(new SolidBrush(color)); + } + + /// + /// Flood fills the image with in the region with the specified brush. + /// + /// The type of the color. + /// The source. + /// The brush. + /// The region. + /// The graphics options. + /// + /// The Image + /// + public static Image Fill(this Image source, IBrush brush, Region region, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Apply(new FillRegionProcessor(brush, region, options)); + } + + /// + /// Flood fills the image with in the region with the specified brush. + /// + /// The type of the color. + /// The source. + /// The brush. + /// The region. + /// + /// The Image + /// + public static Image Fill(this Image source, IBrush brush, Region region) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Fill(brush, region, GraphicsOptions.Default); + } + + /// + /// Flood fills the image with in the region with the specified color. + /// + /// The type of the color. + /// The source. + /// The color. + /// The region. + /// The options. + /// + /// The Image + /// + public static Image Fill(this Image source, TColor color, Region region, GraphicsOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Fill(new SolidBrush(color), region, options); + } + + /// + /// Flood fills the image with in the region with the specified color. + /// + /// The type of the color. + /// The source. + /// The color. + /// The region. + /// + /// The Image + /// + public static Image Fill(this Image source, TColor color, Region region) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Fill(new SolidBrush(color), region); + } + } +} diff --git a/src/ImageSharp.Drawing/IDrawableRegion.cs b/src/ImageSharp.Drawing/IDrawableRegion.cs deleted file mode 100644 index 82e9c39ac..000000000 --- a/src/ImageSharp.Drawing/IDrawableRegion.cs +++ /dev/null @@ -1,24 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing -{ - using System.Numerics; - - /// - /// Represents a region with knowledge about its outline. - /// - /// - public interface IDrawableRegion : IRegion - { - /// - /// Gets the point information for the specified x and y location. - /// - /// The x. - /// The y. - /// Information about the point in relation to a drawable edge - PointInfo GetPointInfo(int x, int y); - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Path.cs b/src/ImageSharp.Drawing/Path.cs new file mode 100644 index 000000000..a997fa18f --- /dev/null +++ b/src/ImageSharp.Drawing/Path.cs @@ -0,0 +1,57 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Drawing +{ + /// + /// Represents a something that has knowledge about its outline. + /// + public abstract class Path + { + /// + /// Gets the maximum number of intersections to could be returned. + /// + /// + /// The maximum intersections. + /// + public abstract int MaxIntersections { get; } + + /// + /// Gets the bounds. + /// + /// + /// The bounds. + /// + public abstract Rectangle Bounds { get; } + + /// + /// Gets the point information for the specified x and y location. + /// + /// The x. + /// The y. + /// Information about the point in relation to a drawable edge + public abstract PointInfo GetPointInfo(int x, int y); + + /// + /// Scans the X axis for intersections. + /// + /// The x. + /// The buffer. + /// The length. + /// The offset. + /// The number of intersections found. + public abstract int ScanX(int x, float[] buffer, int length, int offset); + + /// + /// Scans the Y axis for intersections. + /// + /// The position along the y axis to find intersections. + /// The buffer. + /// The length. + /// The offset. + /// The number of intersections found. + public abstract int ScanY(int y, float[] buffer, int length, int offset); + } +} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs index bbb5e1ab6..1513443c4 100644 --- a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs @@ -12,7 +12,6 @@ namespace ImageSharp.Drawing.Processors using System.Threading.Tasks; using ImageSharp.Processing; using Pens; - using SixLabors.Shapes; using Rectangle = ImageSharp.Rectangle; /// @@ -27,7 +26,7 @@ namespace ImageSharp.Drawing.Processors private const int PaddingFactor = 1; // needs to been the same or greater than AntialiasFactor private readonly IPen pen; - private readonly IDrawableRegion region; + private readonly Path region; private readonly GraphicsOptions options; /// @@ -36,7 +35,7 @@ namespace ImageSharp.Drawing.Processors /// The pen. /// The region. /// The options. - public DrawPathProcessor(IPen pen, IDrawableRegion region, GraphicsOptions options) + public DrawPathProcessor(IPen pen, Path region, GraphicsOptions options) { this.region = region; this.pen = pen; diff --git a/src/ImageSharp.Drawing/Processors/FillShapeProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs similarity index 95% rename from src/ImageSharp.Drawing/Processors/FillShapeProcessor.cs rename to src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs index bdec022d4..2f6f8af7b 100644 --- a/src/ImageSharp.Drawing/Processors/FillShapeProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -11,30 +11,28 @@ namespace ImageSharp.Drawing.Processors using System.Threading.Tasks; using Drawing; using ImageSharp.Processing; - using SixLabors.Shapes; - using Rectangle = ImageSharp.Rectangle; /// /// Usinf a brsuh and a shape fills shape with contents of brush the /// /// The type of the color. /// - public class FillShapeProcessor : ImageProcessor + public class FillRegionProcessor : ImageProcessor where TColor : struct, IPackedPixel, IEquatable { private const float AntialiasFactor = 1f; private const int DrawPadding = 1; private readonly IBrush fillColor; - private readonly IRegion region; + private readonly Region region; private readonly GraphicsOptions options; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The brush. /// The region. /// The options. - public FillShapeProcessor(IBrush brush, IRegion region, GraphicsOptions options) + public FillRegionProcessor(IBrush brush, Region region, GraphicsOptions options) { this.region = region; this.fillColor = brush; @@ -89,6 +87,12 @@ namespace ImageSharp.Drawing.Processors return; } + if (pointsFound % 2 == 1) + { + // we seem to have just clipped a corner lets just skip it + return; + } + QuickSort(buffer, pointsFound); int currentIntersection = 0; @@ -96,13 +100,8 @@ namespace ImageSharp.Drawing.Processors float lastPoint = float.MinValue; bool isInside = false; - // every odd point is the start of a line - Vector2 currentPoint = default(Vector2); - for (int x = minX; x < maxX; x++) { - currentPoint.X = x; - currentPoint.Y = y; if (!isInside) { if (x < (nextPoint - DrawPadding) && x > (lastPoint + DrawPadding)) @@ -232,6 +231,12 @@ namespace ImageSharp.Drawing.Processors return; } + if (pointsFound % 2 == 1) + { + // we seem to have just clipped a corner lets just skip it + return; + } + QuickSort(buffer, pointsFound); int currentIntersection = 0; diff --git a/src/ImageSharp.Drawing/IRegion.cs b/src/ImageSharp.Drawing/Region.cs similarity index 78% rename from src/ImageSharp.Drawing/IRegion.cs rename to src/ImageSharp.Drawing/Region.cs index 2264a91be..95f0e1748 100644 --- a/src/ImageSharp.Drawing/IRegion.cs +++ b/src/ImageSharp.Drawing/Region.cs @@ -1,16 +1,14 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // namespace ImageSharp.Drawing { - using System.Numerics; - /// /// Represents a region of an image. /// - public interface IRegion + public abstract class Region { /// /// Gets the maximum number of intersections to could be returned. @@ -18,7 +16,7 @@ namespace ImageSharp.Drawing /// /// The maximum intersections. /// - int MaxIntersections { get; } + public abstract int MaxIntersections { get; } /// /// Gets the bounds. @@ -26,7 +24,7 @@ namespace ImageSharp.Drawing /// /// The bounds. /// - Rectangle Bounds { get; } + public abstract Rectangle Bounds { get; } /// /// Scans the X axis for intersections. @@ -36,7 +34,7 @@ namespace ImageSharp.Drawing /// The length. /// The offset. /// The number of intersections found. - int ScanX(int x, float[] buffer, int length, int offset); + public abstract int ScanX(int x, float[] buffer, int length, int offset); /// /// Scans the Y axis for intersections. @@ -46,6 +44,6 @@ namespace ImageSharp.Drawing /// The length. /// The offset. /// The number of intersections found. - int ScanY(int y, float[] buffer, int length, int offset); + public abstract int ScanY(int y, float[] buffer, int length, int offset); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/project.json b/src/ImageSharp.Drawing/project.json index 5fcee9b41..ff2900d9f 100644 --- a/src/ImageSharp.Drawing/project.json +++ b/src/ImageSharp.Drawing/project.json @@ -44,7 +44,6 @@ "ImageSharp.Processing": { "target": "project" }, - "SixLabors.Shapes": "0.1.0-ci0047", "StyleCop.Analyzers": { "version": "1.0.0", "type": "build" diff --git a/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs b/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs index defb0e65e..691955e8e 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs @@ -39,7 +39,7 @@ namespace ImageSharp.Benchmarks { using (CoreImage image = new CoreImage(800, 800)) { - image.Fill(CoreColor.HotPink, new SixLabors.Shapes.Rectangle(10, 10, 190, 140)); + image.Fill(CoreColor.HotPink, new CoreRectangle(10, 10, 190, 140)); return new CoreSize(image.Width, image.Height); } diff --git a/tests/ImageSharp.Benchmarks/project.json b/tests/ImageSharp.Benchmarks/project.json index 8650a8af4..866a36faa 100644 --- a/tests/ImageSharp.Benchmarks/project.json +++ b/tests/ImageSharp.Benchmarks/project.json @@ -16,32 +16,28 @@ "dependencies": { "BenchmarkDotNet.Diagnostics.Windows": "0.10.1", "ImageSharp": { - "target": "project", - "version": "1.0.0-*" + "target": "project" }, "ImageSharp.Drawing": { - "target": "project", - "version": "1.0.0-*" + "target": "project" + }, + "ImageSharp.Drawing.Paths": { + "target": "project" }, "ImageSharp.Formats.Jpeg": { - "target": "project", - "version": "1.0.0-*" + "target": "project" }, "ImageSharp.Formats.Png": { - "target": "project", - "version": "1.0.0-*" + "target": "project" }, "ImageSharp.Formats.Bmp": { - "target": "project", - "version": "1.0.0-*" + "target": "project" }, "ImageSharp.Formats.Gif": { - "target": "project", - "version": "1.0.0-*" + "target": "project" }, "ImageSharp.Processing": { - "target": "project", - "version": "1.0.0-*" + "target": "project" } }, "commands": { diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj index 75212d361..1bce8003e 100644 --- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj +++ b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj @@ -179,8 +179,11 @@ ..\..\src\ImageSharp.Drawing\bin\$(Configuration)\net461\ImageSharp.Drawing.dll + + ..\..\src\ImageSharp.Drawing.Paths\bin\$(Configuration)\net461\ImageSharp.Drawing.Paths.dll + - ..\..\src\ImageSharp.Drawing\bin\$(Configuration)\net461\SixLabors.Shapes.dll + ..\..\src\ImageSharp.Drawing.Paths\bin\$(Configuration)\net461\SixLabors.Shapes.dll ..\..\src\ImageSharp.Formats.Bmp\bin\$(Configuration)\net461\ImageSharp.Formats.Bmp.dll diff --git a/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs b/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs index 31aa87d4b..f4465027d 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs @@ -7,7 +7,7 @@ namespace ImageSharp.Tests.Drawing { using Drawing; using ImageSharp.Drawing; - using CorePath = SixLabors.Shapes.Path; + using ShapePath = SixLabors.Shapes.Path; using SixLabors.Shapes; using System; using System.Diagnostics.CodeAnalysis; @@ -33,13 +33,13 @@ namespace ImageSharp.Tests.Drawing new Vector2(60, 10), new Vector2(10, 400)); - CorePath p = new CorePath(linerSegemnt, bazierSegment); + ShapePath p = new ShapePath(linerSegemnt, bazierSegment); using (FileStream output = File.OpenWrite($"{path}/Simple.png")) { image .BackgroundColor(Color.Blue) - .DrawPath(Color.HotPink, 5, p) + .Draw(Color.HotPink, 5, p) .Save(output); } @@ -74,7 +74,7 @@ namespace ImageSharp.Tests.Drawing new Vector2(60, 10), new Vector2(10, 400)); - CorePath p = new CorePath(linerSegemnt, bazierSegment); + ShapePath p = new ShapePath(linerSegemnt, bazierSegment); using (Image image = new Image(500, 500)) { @@ -82,7 +82,7 @@ namespace ImageSharp.Tests.Drawing { image .BackgroundColor(Color.Blue) - .DrawPath(color, 10, p) + .Draw(color, 10, p) .Save(output); } diff --git a/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs index dd2ea5249..03ec5d0c8 100644 --- a/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs @@ -37,7 +37,7 @@ namespace ImageSharp.Tests.Drawing { image .BackgroundColor(Color.Blue) - .DrawPolygon(Color.HotPink, 5, new ComplexPolygon(simplePath, hole1)) + .Draw(Color.HotPink, 5, simplePath.Clip(hole1)) .Save(output); } @@ -87,7 +87,7 @@ namespace ImageSharp.Tests.Drawing { image .BackgroundColor(Color.Blue) - .DrawPolygon(Color.HotPink, 5, new ComplexPolygon(simplePath, hole1)) + .Draw(Color.HotPink, 5, simplePath.Clip(hole1)) .Save(output); } @@ -131,7 +131,7 @@ namespace ImageSharp.Tests.Drawing new Vector2(37, 85), new Vector2(130, 40), new Vector2(65, 137)); - var clipped = simplePath.Clip(hole1); + using (Image image = new Image(500, 500)) { @@ -139,7 +139,7 @@ namespace ImageSharp.Tests.Drawing { image .BackgroundColor(Color.Blue) - .DrawPolygon(Color.HotPink, 5, clipped) + .Draw(Color.HotPink, 5, simplePath.Clip(hole1)) .Save(output); } @@ -185,7 +185,7 @@ namespace ImageSharp.Tests.Drawing { image .BackgroundColor(Color.Blue) - .DrawPolygon(Pens.Dash(Color.HotPink, 5), new ComplexPolygon(simplePath, hole1)) + .Draw(Pens.Dash(Color.HotPink, 5), simplePath.Clip(hole1)) .Save(output); } } @@ -213,7 +213,7 @@ namespace ImageSharp.Tests.Drawing { image .BackgroundColor(Color.Blue) - .DrawPolygon(color, 5, new ComplexPolygon(simplePath, hole1)) + .Draw(color, 5, simplePath.Clip(hole1)) .Save(output); } diff --git a/tests/ImageSharp.Tests/Drawing/PolygonTests.cs b/tests/ImageSharp.Tests/Drawing/PolygonTests.cs index f98744075..a166464c9 100644 --- a/tests/ImageSharp.Tests/Drawing/PolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/PolygonTests.cs @@ -97,7 +97,7 @@ namespace ImageSharp.Tests.Drawing { image .BackgroundColor(Color.Blue) - .DrawPolygon(Color.HotPink, 10, new Rectangle(10, 10, 190, 140)) + .Draw(Color.HotPink, 10, new Rectangle(10, 10, 190, 140)) .Save(output); } diff --git a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs index 3e20b3a09..d5b206723 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs @@ -71,7 +71,7 @@ namespace ImageSharp.Tests.Drawing { Assert.Equal(Color.HotPink, sourcePixels[11, 11]); - Assert.Equal(Color.HotPink, sourcePixels[200, 150]); + Assert.Equal(Color.HotPink, sourcePixels[199, 150]); Assert.Equal(Color.HotPink, sourcePixels[50, 50]); diff --git a/tests/ImageSharp.Tests/project.json b/tests/ImageSharp.Tests/project.json index 9f9c0c715..2a01aff84 100644 --- a/tests/ImageSharp.Tests/project.json +++ b/tests/ImageSharp.Tests/project.json @@ -21,34 +21,30 @@ }, "dependencies": { "ImageSharp": { - "target": "project", - "version": "1.0.0-*" + "target": "project" }, "xunit": "2.2.0-*", "dotnet-test-xunit": "2.2.0-*", "ImageSharp.Drawing": { - "target": "project", - "version": "1.0.0-*" + "target": "project" + }, + "ImageSharp.Drawing.Paths": { + "target": "project" }, "ImageSharp.Formats.Png": { - "target": "project", - "version": "1.0.0-*" + "target": "project" }, "ImageSharp.Formats.Jpeg": { - "target": "project", - "version": "1.0.0-*" + "target": "project" }, "ImageSharp.Formats.Bmp": { - "target": "project", - "version": "1.0.0-*" + "target": "project" }, "ImageSharp.Formats.Gif": { - "target": "project", - "version": "1.0.0-*" + "target": "project" }, "ImageSharp.Processing": { - "target": "project", - "version": "1.0.0-*" + "target": "project" } }, "frameworks": { From ca01e617990e049e3a3be0968cb6155d2b7fc560 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Thu, 2 Feb 2017 10:41:07 +0000 Subject: [PATCH 016/142] use nuget.org feed --- NuGet.config | 1 - src/ImageSharp.Drawing.Paths/project.json | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/NuGet.config b/NuGet.config index 88d71e7ba..b2c967cc9 100644 --- a/NuGet.config +++ b/NuGet.config @@ -1,7 +1,6 @@  - diff --git a/src/ImageSharp.Drawing.Paths/project.json b/src/ImageSharp.Drawing.Paths/project.json index cdc5d2244..85ad75148 100644 --- a/src/ImageSharp.Drawing.Paths/project.json +++ b/src/ImageSharp.Drawing.Paths/project.json @@ -44,7 +44,7 @@ "ImageSharp.Drawing": { "target": "project" }, - "SixLabors.Shapes": "0.1.0-ci0050", + "SixLabors.Shapes": "0.1.0-alpha0001", "StyleCop.Analyzers": { "version": "1.0.0", "type": "build" From 4b8dcc28ec2f0a72283b87e5754e541668ea7783 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Thu, 2 Feb 2017 11:10:51 +0000 Subject: [PATCH 017/142] update readme for new package --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9d947f227..96d14c8be 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,9 @@ Packages include: Contains methods like Resize, Crop, Skew, Rotate - Anything that alters the dimensions of the image. Contains methods like Gaussian Blur, Pixelate, Edge Detection - Anything that maintains the original image dimensions. - **ImageSharp.Drawing** - Brushes and various drawing algorithms. + Brushes and various drawing algorithms, including drawing Images + - **ImageSharp.Drawing.Paths** + various vector drawing methods for drawing paths, polygons etc. ### Manual build From 885cc58485230b470f6242d90cdecce3d99c5b36 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Thu, 2 Feb 2017 13:22:24 +0000 Subject: [PATCH 018/142] update to alpha2 to fix beziers --- src/ImageSharp.Drawing.Paths/project.json | 2 +- tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ImageSharp.Drawing.Paths/project.json b/src/ImageSharp.Drawing.Paths/project.json index 85ad75148..a186f362c 100644 --- a/src/ImageSharp.Drawing.Paths/project.json +++ b/src/ImageSharp.Drawing.Paths/project.json @@ -44,7 +44,7 @@ "ImageSharp.Drawing": { "target": "project" }, - "SixLabors.Shapes": "0.1.0-alpha0001", + "SixLabors.Shapes": "0.1.0-alpha0002", "StyleCop.Analyzers": { "version": "1.0.0", "type": "build" diff --git a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs index 82e4ac33e..f63d90aa6 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs @@ -5,8 +5,6 @@ namespace ImageSharp.Tests.Drawing { - using ImageSharp.Drawing.Shapes; - using System.IO; using System.Numerics; From 59c9b88cbdfa9c21fdeff65038bb9b7d698962f0 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Thu, 2 Feb 2017 19:28:19 +0000 Subject: [PATCH 019/142] added unit tests for Paths --- src/ImageSharp.Drawing.Paths/DrawPolygon.cs | 16 ++ .../{FillShapes.cs => FillShape.cs} | 2 +- .../RectangleExtensions.cs | 10 +- src/ImageSharp.Drawing.Paths/ShapePath.cs | 19 +- src/ImageSharp.Drawing.Paths/ShapeRegion.cs | 18 +- .../Processors/DrawPathProcessor.cs | 40 +++- .../Processors/FillRegionProcessor.cs | 49 +++-- src/ImageSharp/Image/ImageBase{TColor}.cs | 11 + .../Image/ImageProcessingExtensions.cs | 8 +- src/ImageSharp/Image/Image{TColor}.cs | 16 ++ .../Drawing/Paths/DrawBeziersTests.cs | 168 ++++++++++++++++ .../Drawing/Paths/DrawLinesTests.cs | 168 ++++++++++++++++ .../Drawing/Paths/DrawPath.cs | 156 +++++++++++++++ .../Drawing/Paths/DrawPolygon.cs | 168 ++++++++++++++++ .../Drawing/Paths/DrawRectangle.cs | 187 +++++++++++++++++ .../Drawing/Paths/DrawShape.cs | 156 +++++++++++++++ .../Drawing/Paths/Extensions.cs | 49 +++++ .../Drawing/Paths/FillPath.cs | 112 +++++++++++ .../Drawing/Paths/FillPolygon.cs | 111 +++++++++++ .../Drawing/Paths/FillRectangle.cs | 119 +++++++++++ .../Drawing/Paths/FillShape.cs | 108 ++++++++++ .../Drawing/Paths/ProcessorWatchingImage.cs | 38 ++++ .../Drawing/Paths/ShapePathTests.cs | 188 ++++++++++++++++++ .../Drawing/Paths/ShapeRegionTests.cs | 170 ++++++++++++++++ tests/ImageSharp.Tests/project.json | 4 +- 25 files changed, 2036 insertions(+), 55 deletions(-) rename src/ImageSharp.Drawing.Paths/{FillShapes.cs => FillShape.cs} (98%) create mode 100644 tests/ImageSharp.Tests/Drawing/Paths/DrawBeziersTests.cs create mode 100644 tests/ImageSharp.Tests/Drawing/Paths/DrawLinesTests.cs create mode 100644 tests/ImageSharp.Tests/Drawing/Paths/DrawPath.cs create mode 100644 tests/ImageSharp.Tests/Drawing/Paths/DrawPolygon.cs create mode 100644 tests/ImageSharp.Tests/Drawing/Paths/DrawRectangle.cs create mode 100644 tests/ImageSharp.Tests/Drawing/Paths/DrawShape.cs create mode 100644 tests/ImageSharp.Tests/Drawing/Paths/Extensions.cs create mode 100644 tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs create mode 100644 tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs create mode 100644 tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs create mode 100644 tests/ImageSharp.Tests/Drawing/Paths/FillShape.cs create mode 100644 tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs create mode 100644 tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs create mode 100644 tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs diff --git a/src/ImageSharp.Drawing.Paths/DrawPolygon.cs b/src/ImageSharp.Drawing.Paths/DrawPolygon.cs index 73d8ec0e7..571b13c1e 100644 --- a/src/ImageSharp.Drawing.Paths/DrawPolygon.cs +++ b/src/ImageSharp.Drawing.Paths/DrawPolygon.cs @@ -84,6 +84,22 @@ namespace ImageSharp return source.DrawPolygon(new SolidBrush(color), thickness, points, options); } + /// + /// Draws the provided Points as a closed Linear Polygon with the provided Pen. + /// + /// The type of the color. + /// The source. + /// The pen. + /// The points. + /// + /// The Image + /// + public static Image DrawPolygon(this Image source, IPen pen, Vector2[] points) + where TColor : struct, IPackedPixel, IEquatable + { + return source.Draw(pen, new Polygon(new LinearLineSegment(points)), GraphicsOptions.Default); + } + /// /// Draws the provided Points as a closed Linear Polygon with the provided Pen. /// diff --git a/src/ImageSharp.Drawing.Paths/FillShapes.cs b/src/ImageSharp.Drawing.Paths/FillShape.cs similarity index 98% rename from src/ImageSharp.Drawing.Paths/FillShapes.cs rename to src/ImageSharp.Drawing.Paths/FillShape.cs index 9fe473ee1..6e50f7377 100644 --- a/src/ImageSharp.Drawing.Paths/FillShapes.cs +++ b/src/ImageSharp.Drawing.Paths/FillShape.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // diff --git a/src/ImageSharp.Drawing.Paths/RectangleExtensions.cs b/src/ImageSharp.Drawing.Paths/RectangleExtensions.cs index 05d6bb6ce..8369cc83f 100644 --- a/src/ImageSharp.Drawing.Paths/RectangleExtensions.cs +++ b/src/ImageSharp.Drawing.Paths/RectangleExtensions.cs @@ -26,11 +26,11 @@ namespace ImageSharp.Drawing.Processors /// A representation of this public static Rectangle Convert(this SixLabors.Shapes.Rectangle source) { - int y = (int)Math.Floor(source.Y); - int width = (int)Math.Ceiling(source.Size.Width); - int x = (int)Math.Floor(source.X); - int height = (int)Math.Ceiling(source.Size.Height); - return new Rectangle(x, y, width, height); + int left = (int)Math.Floor(source.Left); + int right = (int)Math.Ceiling(source.Right); + int top = (int)Math.Floor(source.Top); + int bottom = (int)Math.Ceiling(source.Bottom); + return new Rectangle(left, top, right - left, bottom - top); } } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing.Paths/ShapePath.cs b/src/ImageSharp.Drawing.Paths/ShapePath.cs index 63d5fc9e1..cd994515e 100644 --- a/src/ImageSharp.Drawing.Paths/ShapePath.cs +++ b/src/ImageSharp.Drawing.Paths/ShapePath.cs @@ -25,11 +25,6 @@ namespace ImageSharp.Drawing /// private readonly IShape shape; - /// - /// The drawable paths - /// - private readonly ImmutableArray paths; - /// /// Initializes a new instance of the class. /// @@ -58,9 +53,17 @@ namespace ImageSharp.Drawing /// The paths. private ShapePath(ImmutableArray paths) { - this.paths = paths; + this.Paths = paths; } + /// + /// Gets the drawable paths + /// + /// + /// The paths. + /// + public ImmutableArray Paths { get; } + /// /// Gets the maximum number of intersections to could be returned. /// @@ -163,9 +166,9 @@ namespace ImageSharp.Drawing SixLabors.Shapes.PointInfo result = default(SixLabors.Shapes.PointInfo); float distance = float.MaxValue; - for (int i = 0; i < this.paths.Length; i++) + for (int i = 0; i < this.Paths.Length; i++) { - var p = this.paths[i].Distance(point); + var p = this.Paths[i].Distance(point); if (p.DistanceFromPath < distance) { distance = p.DistanceFromPath; diff --git a/src/ImageSharp.Drawing.Paths/ShapeRegion.cs b/src/ImageSharp.Drawing.Paths/ShapeRegion.cs index d1a59a99d..b43ad26b4 100644 --- a/src/ImageSharp.Drawing.Paths/ShapeRegion.cs +++ b/src/ImageSharp.Drawing.Paths/ShapeRegion.cs @@ -20,11 +20,6 @@ namespace ImageSharp.Drawing /// internal class ShapeRegion : Region { - /// - /// The fillable shape - /// - private readonly IShape shape; - /// /// Initializes a new instance of the class. /// @@ -40,17 +35,22 @@ namespace ImageSharp.Drawing /// The shape. public ShapeRegion(IShape shape) { - this.shape = shape; + this.Shape = shape; this.Bounds = shape.Bounds.Convert(); } + /// + /// Gets the fillable shape + /// + public IShape Shape { get; } + /// /// Gets the maximum number of intersections to could be returned. /// /// /// The maximum intersections. /// - public override int MaxIntersections => this.shape.MaxIntersections; + public override int MaxIntersections => this.Shape.MaxIntersections; /// /// Gets the bounds. @@ -77,7 +77,7 @@ namespace ImageSharp.Drawing Vector2[] innerbuffer = ArrayPool.Shared.Rent(length); try { - int count = this.shape.FindIntersections( + int count = this.Shape.FindIntersections( start, end, innerbuffer, @@ -114,7 +114,7 @@ namespace ImageSharp.Drawing Vector2[] innerbuffer = ArrayPool.Shared.Rent(length); try { - int count = this.shape.FindIntersections( + int count = this.Shape.FindIntersections( start, end, innerbuffer, diff --git a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs index 1513443c4..3fe557080 100644 --- a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs @@ -25,10 +25,6 @@ namespace ImageSharp.Drawing.Processors private const float AntialiasFactor = 1f; private const int PaddingFactor = 1; // needs to been the same or greater than AntialiasFactor - private readonly IPen pen; - private readonly Path region; - private readonly GraphicsOptions options; - /// /// Initializes a new instance of the class. /// @@ -37,16 +33,40 @@ namespace ImageSharp.Drawing.Processors /// The options. public DrawPathProcessor(IPen pen, Path region, GraphicsOptions options) { - this.region = region; - this.pen = pen; - this.options = options; + this.Path = region; + this.Pen = pen; + this.Options = options; } + /// + /// Gets the options. + /// + /// + /// The options. + /// + public GraphicsOptions Options { get; } + + /// + /// Gets the pen. + /// + /// + /// The pen. + /// + public IPen Pen { get; } + + /// + /// Gets the path. + /// + /// + /// The path. + /// + public Path Path { get; } + /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { using (PixelAccessor sourcePixels = source.Lock()) - using (PenApplicator applicator = this.pen.CreateApplicator(sourcePixels, this.region.Bounds)) + using (PenApplicator applicator = this.Pen.CreateApplicator(sourcePixels, this.Path.Bounds)) { var rect = RectangleF.Ceiling(applicator.RequiredRegion); @@ -89,7 +109,7 @@ namespace ImageSharp.Drawing.Processors { // TODO add find intersections code to skip and scan large regions of this. int offsetX = x - startX; - var info = this.region.GetPointInfo(offsetX, offsetY); + var info = this.Path.GetPointInfo(offsetX, offsetY); var color = applicator.GetColor(offsetX, offsetY, info); @@ -120,7 +140,7 @@ namespace ImageSharp.Drawing.Processors { return 1; } - else if (this.options.Antialias && distance < AntialiasFactor) + else if (this.Options.Antialias && distance < AntialiasFactor) { return 1 - (distance / AntialiasFactor); } diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs index 2f6f8af7b..6719d365a 100644 --- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs @@ -22,9 +22,6 @@ namespace ImageSharp.Drawing.Processors { private const float AntialiasFactor = 1f; private const int DrawPadding = 1; - private readonly IBrush fillColor; - private readonly Region region; - private readonly GraphicsOptions options; /// /// Initializes a new instance of the class. @@ -34,15 +31,39 @@ namespace ImageSharp.Drawing.Processors /// The options. public FillRegionProcessor(IBrush brush, Region region, GraphicsOptions options) { - this.region = region; - this.fillColor = brush; - this.options = options; + this.Region = region; + this.Brush = brush; + this.Options = options; } + /// + /// Gets the brush. + /// + /// + /// The brush. + /// + public IBrush Brush { get; } + + /// + /// Gets the region. + /// + /// + /// The region. + /// + public Region Region { get; } + + /// + /// Gets the options. + /// + /// + /// The options. + /// + public GraphicsOptions Options { get; } + /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - Rectangle rect = RectangleF.Ceiling(this.region.Bounds); // rounds the points out away from the center + Rectangle rect = RectangleF.Ceiling(this.Region.Bounds); // rounds the points out away from the center int polyStartY = sourceRectangle.Y - DrawPadding; int polyEndY = sourceRectangle.Bottom + DrawPadding; @@ -62,10 +83,10 @@ namespace ImageSharp.Drawing.Processors ArrayPool arrayPool = ArrayPool.Shared; - int maxIntersections = this.region.MaxIntersections; + int maxIntersections = this.Region.MaxIntersections; using (PixelAccessor sourcePixels = source.Lock()) - using (BrushApplicator applicator = this.fillColor.CreateApplicator(sourcePixels, rect)) + using (BrushApplicator applicator = this.Brush.CreateApplicator(sourcePixels, rect)) { Parallel.For( minY, @@ -80,7 +101,7 @@ namespace ImageSharp.Drawing.Processors float right = endX; // foreach line we get all the points where this line crosses the polygon - int pointsFound = this.region.ScanY(y, buffer, maxIntersections, 0); + int pointsFound = this.Region.ScanY(y, buffer, maxIntersections, 0); if (pointsFound == 0) { // nothign on this line skip @@ -156,7 +177,7 @@ namespace ImageSharp.Drawing.Processors float opacity = 1; if (!isInside && !onCorner) { - if (this.options.Antialias) + if (this.Options.Antialias) { float distance = float.MaxValue; if (x == lastPoint || x == nextPoint) @@ -207,7 +228,7 @@ namespace ImageSharp.Drawing.Processors } }); - if (this.options.Antialias) + if (this.Options.Antialias) { // we only need to do the X can for antialiasing purposes Parallel.For( @@ -224,7 +245,7 @@ namespace ImageSharp.Drawing.Processors float right = polyEndY; // foreach line we get all the points where this line crosses the polygon - int pointsFound = this.region.ScanX(x, buffer, maxIntersections, 0); + int pointsFound = this.Region.ScanX(x, buffer, maxIntersections, 0); if (pointsFound == 0) { // nothign on this line skip @@ -314,7 +335,7 @@ namespace ImageSharp.Drawing.Processors float opacity = 1; if (!isInside && !onCorner) { - if (this.options.Antialias) + if (this.Options.Antialias) { float distance = float.MaxValue; if (y == lastPoint || y == nextPoint) diff --git a/src/ImageSharp/Image/ImageBase{TColor}.cs b/src/ImageSharp/Image/ImageBase{TColor}.cs index 1b3abd360..b179d1158 100644 --- a/src/ImageSharp/Image/ImageBase{TColor}.cs +++ b/src/ImageSharp/Image/ImageBase{TColor}.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Diagnostics; + using Processing; /// /// The base class of all images. Encapsulates the basic properties and methods required to manipulate @@ -120,6 +121,16 @@ namespace ImageSharp /// public Configuration Configuration { get; private set; } + /// + /// Applies the processor. + /// + /// The processor. + /// The rectangle. + public virtual void ApplyProcessor(IImageProcessor processor, Rectangle rectangle) + { + processor.Apply(this, rectangle); + } + /// public void Dispose() { diff --git a/src/ImageSharp/Image/ImageProcessingExtensions.cs b/src/ImageSharp/Image/ImageProcessingExtensions.cs index d9073ad16..db07afe2a 100644 --- a/src/ImageSharp/Image/ImageProcessingExtensions.cs +++ b/src/ImageSharp/Image/ImageProcessingExtensions.cs @@ -41,13 +41,7 @@ namespace ImageSharp public static Image Apply(this Image source, Rectangle sourceRectangle, IImageProcessor processor) where TColor : struct, IPackedPixel, IEquatable { - processor.Apply(source, sourceRectangle); - - foreach (ImageFrame sourceFrame in source.Frames) - { - processor.Apply(sourceFrame, sourceRectangle); - } - + source.ApplyProcessor(processor, sourceRectangle); return source; } } diff --git a/src/ImageSharp/Image/Image{TColor}.cs b/src/ImageSharp/Image/Image{TColor}.cs index 5c83ef9bb..84bae39cc 100644 --- a/src/ImageSharp/Image/Image{TColor}.cs +++ b/src/ImageSharp/Image/Image{TColor}.cs @@ -16,6 +16,7 @@ namespace ImageSharp using System.Threading.Tasks; using Formats; + using Processing; /// /// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes. @@ -219,6 +220,21 @@ namespace ImageSharp /// public ExifProfile ExifProfile { get; set; } + /// + /// Applies the processor to the image. + /// + /// The processor to apply to the image. + /// The structure that specifies the portion of the image object to draw. + public override void ApplyProcessor(IImageProcessor processor, Rectangle rectangle) + { + // we want to put this on on here as it gives us a really go place to test/verify processor settings + base.ApplyProcessor(processor, rectangle); + foreach (ImageFrame sourceFrame in this.Frames) + { + sourceFrame.ApplyProcessor(processor, rectangle); + } + } + /// /// Saves the image to the given stream using the currently loaded image format. /// diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawBeziersTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawBeziersTests.cs new file mode 100644 index 000000000..967cd1970 --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/Paths/DrawBeziersTests.cs @@ -0,0 +1,168 @@ + +namespace ImageSharp.Tests.Drawing.Paths +{ + using System; + using System.IO; + using ImageSharp; + using ImageSharp.Drawing.Brushes; + using Processing; + using System.Collections.Generic; + using Xunit; + using ImageSharp.Drawing; + using System.Numerics; + using SixLabors.Shapes; + using ImageSharp.Drawing.Processors; + using ImageSharp.Drawing.Pens; + + public class DrawBeziersTests : IDisposable + { + float thickness = 7.2f; + GraphicsOptions noneDefault = new GraphicsOptions(); + Color color = Color.HotPink; + SolidBrush brush = Brushes.Solid(Color.HotPink); + Pen pen = new Pen(Color.Firebrick, 99.9f); + Vector2[] points = new Vector2[] { + new Vector2(10,10), + new Vector2(20,10), + new Vector2(20,10), + new Vector2(30,10), + }; + private ProcessorWatchingImage img; + + public DrawBeziersTests() + { + this.img = new Paths.ProcessorWatchingImage(10, 10); + } + + public void Dispose() + { + img.Dispose(); + } + + [Fact] + public void Brush_Thickness_points() + { + img.DrawBeziers(brush, thickness, points); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var path = Assert.IsType(processor.Path); + Assert.NotEmpty(path.Paths); + + var vector = Assert.IsType(path.Paths[0]); + var segment = Assert.IsType(vector.LineSegments[0]); + + var pen = Assert.IsType>(processor.Pen); + Assert.Equal(brush, pen.Brush); + Assert.Equal(thickness, pen.Width); + } + + [Fact] + public void Brush_Thickness_points_options() + { + img.DrawBeziers(brush, thickness, points, noneDefault); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var path = Assert.IsType(processor.Path); + Assert.NotEmpty(path.Paths); + + var vector = Assert.IsType(path.Paths[0]); + var segment = Assert.IsType(vector.LineSegments[0]); + + var pen = Assert.IsType>(processor.Pen); + Assert.Equal(brush, pen.Brush); + Assert.Equal(thickness, pen.Width); + } + + [Fact] + public void color_Thickness_points() + { + img.DrawBeziers(color, thickness, points); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var path = Assert.IsType(processor.Path); + Assert.NotEmpty(path.Paths); + + var vector = Assert.IsType(path.Paths[0]); + var segment = Assert.IsType(vector.LineSegments[0]); + + var pen = Assert.IsType>(processor.Pen); + Assert.Equal(thickness, pen.Width); + + var brush = Assert.IsType>(pen.Brush); + Assert.Equal(color, brush.Color); + } + + [Fact] + public void color_Thickness_points_options() + { + img.DrawBeziers(color, thickness, points, noneDefault); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var path = Assert.IsType(processor.Path); + Assert.NotEmpty(path.Paths); + + var vector = Assert.IsType(path.Paths[0]); + var segment = Assert.IsType(vector.LineSegments[0]); + + var pen = Assert.IsType>(processor.Pen); + Assert.Equal(thickness, pen.Width); + + var brush = Assert.IsType>(pen.Brush); + Assert.Equal(color, brush.Color); + } + + [Fact] + public void pen_points() + { + img.DrawBeziers(pen, points); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var path = Assert.IsType(processor.Path); + Assert.NotEmpty(path.Paths); + + var vector = Assert.IsType(path.Paths[0]); + var segment = Assert.IsType(vector.LineSegments[0]); + + Assert.Equal(pen, processor.Pen); + } + + [Fact] + public void pen_points_options() + { + img.DrawBeziers(pen, points, noneDefault); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var path = Assert.IsType(processor.Path); + Assert.NotEmpty(path.Paths); + + var vector = Assert.IsType(path.Paths[0]); + var segment = Assert.IsType(vector.LineSegments[0]); + + Assert.Equal(pen, processor.Pen); + } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawLinesTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawLinesTests.cs new file mode 100644 index 000000000..3b203cdd8 --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/Paths/DrawLinesTests.cs @@ -0,0 +1,168 @@ + +namespace ImageSharp.Tests.Drawing.Paths +{ + using System; + using System.IO; + using ImageSharp; + using ImageSharp.Drawing.Brushes; + using Processing; + using System.Collections.Generic; + using Xunit; + using ImageSharp.Drawing; + using System.Numerics; + using SixLabors.Shapes; + using ImageSharp.Drawing.Processors; + using ImageSharp.Drawing.Pens; + + public class DrawLinesTests : IDisposable + { + float thickness = 7.2f; + GraphicsOptions noneDefault = new GraphicsOptions(); + Color color = Color.HotPink; + SolidBrush brush = Brushes.Solid(Color.HotPink); + Pen pen = new Pen(Color.Gray, 99.9f); + Vector2[] points = new Vector2[] { + new Vector2(10,10), + new Vector2(20,10), + new Vector2(20,10), + new Vector2(30,10), + }; + private ProcessorWatchingImage img; + + public DrawLinesTests() + { + this.img = new Paths.ProcessorWatchingImage(10, 10); + } + + public void Dispose() + { + img.Dispose(); + } + + [Fact] + public void Brush_Thickness_points() + { + img.DrawLines(brush, thickness, points); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var path = Assert.IsType(processor.Path); + Assert.NotEmpty(path.Paths); + + var vector = Assert.IsType(path.Paths[0]); + var segment = Assert.IsType(vector.LineSegments[0]); + + var pen = Assert.IsType>(processor.Pen); + Assert.Equal(brush, pen.Brush); + Assert.Equal(thickness, pen.Width); + } + + [Fact] + public void Brush_Thickness_points_options() + { + img.DrawLines(brush, thickness, points, noneDefault); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var path = Assert.IsType(processor.Path); + Assert.NotEmpty(path.Paths); + + var vector = Assert.IsType(path.Paths[0]); + var segment = Assert.IsType(vector.LineSegments[0]); + + var pen = Assert.IsType>(processor.Pen); + Assert.Equal(brush, pen.Brush); + Assert.Equal(thickness, pen.Width); + } + + [Fact] + public void color_Thickness_points() + { + img.DrawLines(color, thickness, points); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var path = Assert.IsType(processor.Path); + Assert.NotEmpty(path.Paths); + + var vector = Assert.IsType(path.Paths[0]); + var segment = Assert.IsType(vector.LineSegments[0]); + + var pen = Assert.IsType>(processor.Pen); + Assert.Equal(thickness, pen.Width); + + var brush = Assert.IsType>(pen.Brush); + Assert.Equal(color, brush.Color); + } + + [Fact] + public void color_Thickness_points_options() + { + img.DrawLines(color, thickness, points, noneDefault); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var path = Assert.IsType(processor.Path); + Assert.NotEmpty(path.Paths); + + var vector = Assert.IsType(path.Paths[0]); + var segment = Assert.IsType(vector.LineSegments[0]); + + var pen = Assert.IsType>(processor.Pen); + Assert.Equal(thickness, pen.Width); + + var brush = Assert.IsType>(pen.Brush); + Assert.Equal(color, brush.Color); + } + + [Fact] + public void pen_points() + { + img.DrawLines(pen, points); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var path = Assert.IsType(processor.Path); + Assert.NotEmpty(path.Paths); + + var vector = Assert.IsType(path.Paths[0]); + var segment = Assert.IsType(vector.LineSegments[0]); + + Assert.Equal(pen, processor.Pen); + } + + [Fact] + public void pen_points_options() + { + img.DrawLines(pen, points, noneDefault); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var path = Assert.IsType(processor.Path); + Assert.NotEmpty(path.Paths); + + var vector = Assert.IsType(path.Paths[0]); + var segment = Assert.IsType(vector.LineSegments[0]); + + Assert.Equal(pen, processor.Pen); + } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawPath.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawPath.cs new file mode 100644 index 000000000..8ce7dcba2 --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/Paths/DrawPath.cs @@ -0,0 +1,156 @@ + +namespace ImageSharp.Tests.Drawing.Paths +{ + using System; + using System.IO; + using ImageSharp; + using ImageSharp.Drawing.Brushes; + using Processing; + using System.Collections.Generic; + using Xunit; + using ImageSharp.Drawing; + using System.Numerics; + using SixLabors.Shapes; + using ImageSharp.Drawing.Processors; + using ImageSharp.Drawing.Pens; + + public class DrawPath : IDisposable + { + float thickness = 7.2f; + GraphicsOptions noneDefault = new GraphicsOptions(); + Color color = Color.HotPink; + SolidBrush brush = Brushes.Solid(Color.HotPink); + Pen pen = new Pen(Color.Gray, 99.9f); + IPath path = new SixLabors.Shapes.Path(new LinearLineSegment(new Vector2[] { + new Vector2(10,10), + new Vector2(20,10), + new Vector2(20,10), + new Vector2(30,10), + })); + private ProcessorWatchingImage img; + + public DrawPath() + { + this.img = new Paths.ProcessorWatchingImage(10, 10); + } + + public void Dispose() + { + img.Dispose(); + } + + [Fact] + public void Brush_Thickness_path() + { + img.Draw(brush, thickness, path); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var shapepath = Assert.IsType(processor.Path); + Assert.NotEmpty(shapepath.Paths); + Assert.Equal(path, shapepath.Paths[0]); + + var pen = Assert.IsType>(processor.Pen); + Assert.Equal(brush, pen.Brush); + Assert.Equal(thickness, pen.Width); + } + + [Fact] + public void Brush_Thickness_path_options() + { + img.Draw(brush, thickness, path, noneDefault); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var shapepath = Assert.IsType(processor.Path); + Assert.NotEmpty(shapepath.Paths); + Assert.Equal(path, shapepath.Paths[0]); + + var pen = Assert.IsType>(processor.Pen); + Assert.Equal(brush, pen.Brush); + Assert.Equal(thickness, pen.Width); + } + + [Fact] + public void color_Thickness_path() + { + img.Draw(color, thickness, path); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var shapepath = Assert.IsType(processor.Path); + Assert.NotEmpty(shapepath.Paths); + Assert.Equal(path, shapepath.Paths[0]); + + var pen = Assert.IsType>(processor.Pen); + Assert.Equal(thickness, pen.Width); + + var brush = Assert.IsType>(pen.Brush); + Assert.Equal(color, brush.Color); + } + + [Fact] + public void color_Thickness_path_options() + { + img.Draw(color, thickness, path, noneDefault); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var shapepath = Assert.IsType(processor.Path); + Assert.NotEmpty(shapepath.Paths); + Assert.Equal(path, shapepath.Paths[0]); + + var pen = Assert.IsType>(processor.Pen); + Assert.Equal(thickness, pen.Width); + + var brush = Assert.IsType>(pen.Brush); + Assert.Equal(color, brush.Color); + } + + [Fact] + public void pen_path() + { + img.Draw(pen, path); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var shapepath = Assert.IsType(processor.Path); + Assert.NotEmpty(shapepath.Paths); + Assert.Equal(path, shapepath.Paths[0]); + + Assert.Equal(pen, processor.Pen); + } + + [Fact] + public void pen_path_options() + { + img.Draw(pen, path, noneDefault); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var shapepath = Assert.IsType(processor.Path); + Assert.NotEmpty(shapepath.Paths); + Assert.Equal(path, shapepath.Paths[0]); + + Assert.Equal(pen, processor.Pen); + } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawPolygon.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawPolygon.cs new file mode 100644 index 000000000..9a931470d --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/Paths/DrawPolygon.cs @@ -0,0 +1,168 @@ + +namespace ImageSharp.Tests.Drawing.Paths +{ + using System; + using System.IO; + using ImageSharp; + using ImageSharp.Drawing.Brushes; + using Processing; + using System.Collections.Generic; + using Xunit; + using ImageSharp.Drawing; + using System.Numerics; + using SixLabors.Shapes; + using ImageSharp.Drawing.Processors; + using ImageSharp.Drawing.Pens; + + public class DrawPolygon : IDisposable + { + float thickness = 7.2f; + GraphicsOptions noneDefault = new GraphicsOptions(); + Color color = Color.HotPink; + SolidBrush brush = Brushes.Solid(Color.HotPink); + Pen pen = new Pen(Color.Gray, 99.9f); + Vector2[] points = new Vector2[] { + new Vector2(10,10), + new Vector2(20,10), + new Vector2(20,10), + new Vector2(30,10), + }; + private ProcessorWatchingImage img; + + public DrawPolygon() + { + this.img = new Paths.ProcessorWatchingImage(10, 10); + } + + public void Dispose() + { + img.Dispose(); + } + + [Fact] + public void Brush_Thickness_points() + { + img.DrawPolygon(brush, thickness, points); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var path = Assert.IsType(processor.Path); + Assert.NotEmpty(path.Paths); + + var vector = Assert.IsType(path.Paths[0].AsShape()); + var segment = Assert.IsType(vector.LineSegments[0]); + + var pen = Assert.IsType>(processor.Pen); + Assert.Equal(brush, pen.Brush); + Assert.Equal(thickness, pen.Width); + } + + [Fact] + public void Brush_Thickness_points_options() + { + img.DrawPolygon(brush, thickness, points, noneDefault); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var path = Assert.IsType(processor.Path); + Assert.NotEmpty(path.Paths); + + var vector = Assert.IsType(path.Paths[0].AsShape()); + var segment = Assert.IsType(vector.LineSegments[0]); + + var pen = Assert.IsType>(processor.Pen); + Assert.Equal(brush, pen.Brush); + Assert.Equal(thickness, pen.Width); + } + + [Fact] + public void color_Thickness_points() + { + img.DrawPolygon(color, thickness, points); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var path = Assert.IsType(processor.Path); + Assert.NotEmpty(path.Paths); + + var vector = Assert.IsType(path.Paths[0].AsShape()); + var segment = Assert.IsType(vector.LineSegments[0]); + + var pen = Assert.IsType>(processor.Pen); + Assert.Equal(thickness, pen.Width); + + var brush = Assert.IsType>(pen.Brush); + Assert.Equal(color, brush.Color); + } + + [Fact] + public void color_Thickness_points_options() + { + img.DrawPolygon(color, thickness, points, noneDefault); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var path = Assert.IsType(processor.Path); + Assert.NotEmpty(path.Paths); + + var vector = Assert.IsType(path.Paths[0].AsShape()); + var segment = Assert.IsType(vector.LineSegments[0]); + + var pen = Assert.IsType>(processor.Pen); + Assert.Equal(thickness, pen.Width); + + var brush = Assert.IsType>(pen.Brush); + Assert.Equal(color, brush.Color); + } + + [Fact] + public void pen_points() + { + img.DrawPolygon(pen, points); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var path = Assert.IsType(processor.Path); + Assert.NotEmpty(path.Paths); + + var vector = Assert.IsType(path.Paths[0].AsShape()); + var segment = Assert.IsType(vector.LineSegments[0]); + + Assert.Equal(pen, processor.Pen); + } + + [Fact] + public void pen_points_options() + { + img.DrawPolygon(pen, points, noneDefault); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var path = Assert.IsType(processor.Path); + Assert.NotEmpty(path.Paths); + + var vector = Assert.IsType(path.Paths[0].AsShape()); + var segment = Assert.IsType(vector.LineSegments[0]); + + Assert.Equal(pen, processor.Pen); + } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawRectangle.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawRectangle.cs new file mode 100644 index 000000000..f6680bc9c --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/Paths/DrawRectangle.cs @@ -0,0 +1,187 @@ + +namespace ImageSharp.Tests.Drawing.Paths +{ + using System; + using System.IO; + using ImageSharp; + using ImageSharp.Drawing.Brushes; + using Processing; + using System.Collections.Generic; + using Xunit; + using ImageSharp.Drawing; + using System.Numerics; + using SixLabors.Shapes; + using ImageSharp.Drawing.Processors; + using ImageSharp.Drawing.Pens; + + public class DrawRectangle : IDisposable + { + float thickness = 7.2f; + GraphicsOptions noneDefault = new GraphicsOptions(); + Color color = Color.HotPink; + SolidBrush brush = Brushes.Solid(Color.HotPink); + Pen pen = new Pen(Color.Gray, 99.9f); + ImageSharp.Rectangle rectangle = new ImageSharp.Rectangle(10, 10, 98, 324); + + private ProcessorWatchingImage img; + + public DrawRectangle() + { + this.img = new Paths.ProcessorWatchingImage(10, 10); + } + + public void Dispose() + { + img.Dispose(); + } + + [Fact] + public void Brush_Thickness_rectangle() + { + img.Draw(brush, thickness, rectangle); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var shapepath = Assert.IsType(processor.Path); + Assert.NotEmpty(shapepath.Paths); + var rect = Assert.IsType(shapepath.Paths[0].AsShape()); + + Assert.Equal(rect.Location.X, rectangle.X); + Assert.Equal(rect.Location.Y, rectangle.Y); + Assert.Equal(rect.Size.Width, rectangle.Width); + Assert.Equal(rect.Size.Height, rectangle.Height); + + var pen = Assert.IsType>(processor.Pen); + Assert.Equal(brush, pen.Brush); + Assert.Equal(thickness, pen.Width); + } + + [Fact] + public void Brush_Thickness_rectangle_options() + { + img.Draw(brush, thickness, rectangle, noneDefault); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var shapepath = Assert.IsType(processor.Path); + Assert.NotEmpty(shapepath.Paths); + + var rect = Assert.IsType(shapepath.Paths[0].AsShape()); + + Assert.Equal(rect.Location.X, rectangle.X); + Assert.Equal(rect.Location.Y, rectangle.Y); + Assert.Equal(rect.Size.Width, rectangle.Width); + Assert.Equal(rect.Size.Height, rectangle.Height); + + var pen = Assert.IsType>(processor.Pen); + Assert.Equal(brush, pen.Brush); + Assert.Equal(thickness, pen.Width); + } + + [Fact] + public void color_Thickness_rectangle() + { + img.Draw(color, thickness, rectangle); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var shapepath = Assert.IsType(processor.Path); + Assert.NotEmpty(shapepath.Paths); + + var rect = Assert.IsType(shapepath.Paths[0].AsShape()); + + Assert.Equal(rect.Location.X, rectangle.X); + Assert.Equal(rect.Location.Y, rectangle.Y); + Assert.Equal(rect.Size.Width, rectangle.Width); + Assert.Equal(rect.Size.Height, rectangle.Height); + + var pen = Assert.IsType>(processor.Pen); + Assert.Equal(thickness, pen.Width); + + var brush = Assert.IsType>(pen.Brush); + Assert.Equal(color, brush.Color); + } + + [Fact] + public void color_Thickness_rectangle_options() + { + img.Draw(color, thickness, rectangle, noneDefault); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var shapepath = Assert.IsType(processor.Path); + Assert.NotEmpty(shapepath.Paths); + + var rect = Assert.IsType(shapepath.Paths[0].AsShape()); + + Assert.Equal(rect.Location.X, rectangle.X); + Assert.Equal(rect.Location.Y, rectangle.Y); + Assert.Equal(rect.Size.Width, rectangle.Width); + Assert.Equal(rect.Size.Height, rectangle.Height); + + var pen = Assert.IsType>(processor.Pen); + Assert.Equal(thickness, pen.Width); + + var brush = Assert.IsType>(pen.Brush); + Assert.Equal(color, brush.Color); + } + + [Fact] + public void pen_rectangle() + { + img.Draw(pen, rectangle); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var shapepath = Assert.IsType(processor.Path); + Assert.NotEmpty(shapepath.Paths); + + var rect = Assert.IsType(shapepath.Paths[0].AsShape()); + + Assert.Equal(rect.Location.X, rectangle.X); + Assert.Equal(rect.Location.Y, rectangle.Y); + Assert.Equal(rect.Size.Width, rectangle.Width); + Assert.Equal(rect.Size.Height, rectangle.Height); + + Assert.Equal(pen, processor.Pen); + } + + [Fact] + public void pen_rectangle_options() + { + img.Draw(pen, rectangle, noneDefault); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var shapepath = Assert.IsType(processor.Path); + Assert.NotEmpty(shapepath.Paths); + + var rect = Assert.IsType(shapepath.Paths[0].AsShape()); + + Assert.Equal(rect.Location.X, rectangle.X); + Assert.Equal(rect.Location.Y, rectangle.Y); + Assert.Equal(rect.Size.Width, rectangle.Width); + Assert.Equal(rect.Size.Height, rectangle.Height); + + Assert.Equal(pen, processor.Pen); + } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawShape.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawShape.cs new file mode 100644 index 000000000..8572099db --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/Paths/DrawShape.cs @@ -0,0 +1,156 @@ + +namespace ImageSharp.Tests.Drawing.Paths +{ + using System; + using System.IO; + using ImageSharp; + using ImageSharp.Drawing.Brushes; + using Processing; + using System.Collections.Generic; + using Xunit; + using ImageSharp.Drawing; + using System.Numerics; + using SixLabors.Shapes; + using ImageSharp.Drawing.Processors; + using ImageSharp.Drawing.Pens; + + public class DrawShape: IDisposable + { + float thickness = 7.2f; + GraphicsOptions noneDefault = new GraphicsOptions(); + Color color = Color.HotPink; + SolidBrush brush = Brushes.Solid(Color.HotPink); + Pen pen = new Pen(Color.Gray, 99.9f); + IShape shape = new SixLabors.Shapes.Polygon(new LinearLineSegment(new Vector2[] { + new Vector2(10,10), + new Vector2(20,10), + new Vector2(20,10), + new Vector2(30,10), + })); + private ProcessorWatchingImage img; + + public DrawShape() + { + this.img = new Paths.ProcessorWatchingImage(10, 10); + } + + public void Dispose() + { + img.Dispose(); + } + + [Fact] + public void Brush_Thickness_shape() + { + img.Draw(brush, thickness, shape); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var shapepath = Assert.IsType(processor.Path); + Assert.NotEmpty(shapepath.Paths); + Assert.Equal(shape, shapepath.Paths[0].AsShape()); + + var pen = Assert.IsType>(processor.Pen); + Assert.Equal(brush, pen.Brush); + Assert.Equal(thickness, pen.Width); + } + + [Fact] + public void Brush_Thickness_shape_options() + { + img.Draw(brush, thickness, shape, noneDefault); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var shapepath = Assert.IsType(processor.Path); + Assert.NotEmpty(shapepath.Paths); + Assert.Equal(shape, shapepath.Paths[0].AsShape()); + + var pen = Assert.IsType>(processor.Pen); + Assert.Equal(brush, pen.Brush); + Assert.Equal(thickness, pen.Width); + } + + [Fact] + public void color_Thickness_shape() + { + img.Draw(color, thickness, shape); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var shapepath = Assert.IsType(processor.Path); + Assert.NotEmpty(shapepath.Paths); + Assert.Equal(shape, shapepath.Paths[0].AsShape()); + + var pen = Assert.IsType>(processor.Pen); + Assert.Equal(thickness, pen.Width); + + var brush = Assert.IsType>(pen.Brush); + Assert.Equal(color, brush.Color); + } + + [Fact] + public void color_Thickness_shape_options() + { + img.Draw(color, thickness, shape, noneDefault); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var shapepath = Assert.IsType(processor.Path); + Assert.NotEmpty(shapepath.Paths); + Assert.Equal(shape, shapepath.Paths[0].AsShape()); + + var pen = Assert.IsType>(processor.Pen); + Assert.Equal(thickness, pen.Width); + + var brush = Assert.IsType>(pen.Brush); + Assert.Equal(color, brush.Color); + } + + [Fact] + public void pen_shape() + { + img.Draw(pen, shape); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var shapepath = Assert.IsType(processor.Path); + Assert.NotEmpty(shapepath.Paths); + Assert.Equal(shape, shapepath.Paths[0].AsShape()); + + Assert.Equal(pen, processor.Pen); + } + + [Fact] + public void pen_path_options() + { + img.Draw(pen, shape, noneDefault); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var shapepath = Assert.IsType(processor.Path); + Assert.NotEmpty(shapepath.Paths); + Assert.Equal(shape, shapepath.Paths[0].AsShape()); + + Assert.Equal(pen, processor.Pen); + } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/Extensions.cs b/tests/ImageSharp.Tests/Drawing/Paths/Extensions.cs new file mode 100644 index 000000000..37720253d --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/Paths/Extensions.cs @@ -0,0 +1,49 @@ + +namespace ImageSharp.Tests.Drawing.Paths +{ + using System; + using System.IO; + using ImageSharp; + using ImageSharp.Drawing.Brushes; + using Processing; + using System.Collections.Generic; + using Xunit; + using ImageSharp.Drawing; + using System.Numerics; + using SixLabors.Shapes; + using ImageSharp.Drawing.Processors; + using ImageSharp.Drawing.Pens; + + public class Extensions + { + [Fact] + public void ConvertPointInfo() + { + SixLabors.Shapes.PointInfo src = new SixLabors.Shapes.PointInfo + { + ClosestPointOnPath = Vector2.UnitX, + SearchPoint = Vector2.UnitY, + DistanceAlongPath = 99f, + DistanceFromPath = 82f + }; + ImageSharp.Drawing.PointInfo info = src.Convert(); + + Assert.Equal(src.DistanceAlongPath, info.DistanceAlongPath); + Assert.Equal(src.DistanceFromPath, info.DistanceFromPath); + } + + [Theory] + [InlineData(0.5, 0.5, 5, 5, 0,0,6,6)] + [InlineData(1, 1, 5, 5, 1,1,5,5)] + public void ConvertRectangle(float x, float y, float width, float height, int expectedX, int expectedY, int expectedWidth, int expectedHeight) + { + SixLabors.Shapes.Rectangle src = new SixLabors.Shapes.Rectangle(x, y, width, height); + ImageSharp.Rectangle actual = src.Convert(); + + Assert.Equal(expectedX, actual.X); + Assert.Equal(expectedY, actual.Y); + Assert.Equal(expectedWidth, actual.Width); + Assert.Equal(expectedHeight, actual.Height); + } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs new file mode 100644 index 000000000..04a2a82ab --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs @@ -0,0 +1,112 @@ + +namespace ImageSharp.Tests.Drawing.Paths +{ + using System; + using System.IO; + using ImageSharp; + using ImageSharp.Drawing.Brushes; + using Processing; + using System.Collections.Generic; + using Xunit; + using ImageSharp.Drawing; + using System.Numerics; + using SixLabors.Shapes; + using ImageSharp.Drawing.Processors; + using ImageSharp.Drawing.Pens; + + public class FillPath : IDisposable + { + GraphicsOptions noneDefault = new GraphicsOptions(); + Color color = Color.HotPink; + SolidBrush brush = Brushes.Solid(Color.HotPink); + IPath path = new SixLabors.Shapes.Path(new LinearLineSegment(new Vector2[] { + new Vector2(10,10), + new Vector2(20,10), + new Vector2(20,10), + new Vector2(30,10), + })); + private ProcessorWatchingImage img; + + public FillPath() + { + this.img = new Paths.ProcessorWatchingImage(10, 10); + } + + public void Dispose() + { + img.Dispose(); + } + + [Fact] + public void Brush_path() + { + img.Fill(brush, path); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var region = Assert.IsType(processor.Region); + var polygon = Assert.IsType(region.Shape); + var segments = Assert.IsType(polygon.LineSegments[0]); + + Assert.Equal(brush, processor.Brush); + } + + [Fact] + public void Brush_path_options() + { + img.Fill(brush, path, noneDefault); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var region = Assert.IsType(processor.Region); + var polygon = Assert.IsType(region.Shape); + var segments = Assert.IsType(polygon.LineSegments[0]); + + Assert.Equal(brush, processor.Brush); + } + + [Fact] + public void color_path() + { + img.Fill(color, path); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var region = Assert.IsType(processor.Region); + var polygon = Assert.IsType(region.Shape); + var segments = Assert.IsType(polygon.LineSegments[0]); + + var brush = Assert.IsType>(processor.Brush); + Assert.Equal(color, brush.Color); + } + + [Fact] + public void color_path_options() + { + img.Fill(color, path, noneDefault); + + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var region = Assert.IsType(processor.Region); + var polygon = Assert.IsType(region.Shape); + var segments = Assert.IsType(polygon.LineSegments[0]); + + var brush = Assert.IsType>(processor.Brush); + Assert.Equal(color, brush.Color); + + } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs new file mode 100644 index 000000000..2c95eb209 --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs @@ -0,0 +1,111 @@ + +namespace ImageSharp.Tests.Drawing.Paths +{ + using System; + using System.IO; + using ImageSharp; + using ImageSharp.Drawing.Brushes; + using Processing; + using System.Collections.Generic; + using Xunit; + using ImageSharp.Drawing; + using System.Numerics; + using SixLabors.Shapes; + using ImageSharp.Drawing.Processors; + using ImageSharp.Drawing.Pens; + + public class FillPolygon : IDisposable + { + GraphicsOptions noneDefault = new GraphicsOptions(); + Color color = Color.HotPink; + SolidBrush brush = Brushes.Solid(Color.HotPink); + Vector2[] path = new Vector2[] { + new Vector2(10,10), + new Vector2(20,10), + new Vector2(20,10), + new Vector2(30,10), + }; + private ProcessorWatchingImage img; + + public FillPolygon() + { + this.img = new Paths.ProcessorWatchingImage(10, 10); + } + + public void Dispose() + { + img.Dispose(); + } + + [Fact] + public void Brush_path() + { + img.FillPolygon(brush, path); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var region = Assert.IsType(processor.Region); + var polygon = Assert.IsType(region.Shape); + var segemnt = Assert.IsType(polygon.LineSegments[0]); + + Assert.Equal(brush, processor.Brush); + } + + [Fact] + public void Brush_path_options() + { + img.FillPolygon(brush, path, noneDefault); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var region = Assert.IsType(processor.Region); + var polygon = Assert.IsType(region.Shape); + var segemnt = Assert.IsType(polygon.LineSegments[0]); + + Assert.Equal(brush, processor.Brush); + } + + [Fact] + public void color_path() + { + img.FillPolygon(color, path); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var region = Assert.IsType(processor.Region); + var polygon = Assert.IsType(region.Shape); + var segemnt = Assert.IsType(polygon.LineSegments[0]); + + var brush = Assert.IsType>(processor.Brush); + Assert.Equal(color, brush.Color); + } + + [Fact] + public void color_path_options() + { + img.FillPolygon(color, path, noneDefault); + + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var region = Assert.IsType(processor.Region); + var polygon = Assert.IsType(region.Shape); + var segemnt = Assert.IsType(polygon.LineSegments[0]); + + var brush = Assert.IsType>(processor.Brush); + Assert.Equal(color, brush.Color); + } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs new file mode 100644 index 000000000..520b107b9 --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs @@ -0,0 +1,119 @@ + +namespace ImageSharp.Tests.Drawing.Paths +{ + using System; + using System.IO; + using ImageSharp; + using ImageSharp.Drawing.Brushes; + using Processing; + using System.Collections.Generic; + using Xunit; + using ImageSharp.Drawing; + using System.Numerics; + using SixLabors.Shapes; + using ImageSharp.Drawing.Processors; + using ImageSharp.Drawing.Pens; + + public class FillRectangle : IDisposable + { + GraphicsOptions noneDefault = new GraphicsOptions(); + Color color = Color.HotPink; + SolidBrush brush = Brushes.Solid(Color.HotPink); + ImageSharp.Rectangle rectangle = new ImageSharp.Rectangle(10, 10, 77, 76); + + private ProcessorWatchingImage img; + + public FillRectangle() + { + this.img = new Paths.ProcessorWatchingImage(10, 10); + } + + public void Dispose() + { + img.Dispose(); + } + + [Fact] + public void Brush_path() + { + img.Fill(brush, rectangle); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var region = Assert.IsType(processor.Region); + var rect = Assert.IsType(region.Shape); + Assert.Equal(rect.Location.X, rectangle.X); + Assert.Equal(rect.Location.Y, rectangle.Y); + Assert.Equal(rect.Size.Width, rectangle.Width); + Assert.Equal(rect.Size.Height, rectangle.Height); + + Assert.Equal(brush, processor.Brush); + } + + [Fact] + public void Brush_path_options() + { + img.Fill(brush, rectangle, noneDefault); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var region = Assert.IsType(processor.Region); + var rect = Assert.IsType(region.Shape); + Assert.Equal(rect.Location.X, rectangle.X); + Assert.Equal(rect.Location.Y, rectangle.Y); + Assert.Equal(rect.Size.Width, rectangle.Width); + Assert.Equal(rect.Size.Height, rectangle.Height); + + Assert.Equal(brush, processor.Brush); + } + + [Fact] + public void color_path() + { + img.Fill(color, rectangle); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var region = Assert.IsType(processor.Region); + var rect = Assert.IsType(region.Shape); + Assert.Equal(rect.Location.X, rectangle.X); + Assert.Equal(rect.Location.Y, rectangle.Y); + Assert.Equal(rect.Size.Width, rectangle.Width); + Assert.Equal(rect.Size.Height, rectangle.Height); + + var brush = Assert.IsType>(processor.Brush); + Assert.Equal(color, brush.Color); + } + + [Fact] + public void color_path_options() + { + img.Fill(color, rectangle, noneDefault); + + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var region = Assert.IsType(processor.Region); + var rect = Assert.IsType(region.Shape); + Assert.Equal(rect.Location.X, rectangle.X); + Assert.Equal(rect.Location.Y, rectangle.Y); + Assert.Equal(rect.Size.Width, rectangle.Width); + Assert.Equal(rect.Size.Height, rectangle.Height); + + var brush = Assert.IsType>(processor.Brush); + Assert.Equal(color, brush.Color); + } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillShape.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillShape.cs new file mode 100644 index 000000000..26bf61b5c --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillShape.cs @@ -0,0 +1,108 @@ + +namespace ImageSharp.Tests.Drawing.Paths +{ + using System; + using System.IO; + using ImageSharp; + using ImageSharp.Drawing.Brushes; + using Processing; + using System.Collections.Generic; + using Xunit; + using ImageSharp.Drawing; + using System.Numerics; + using SixLabors.Shapes; + using ImageSharp.Drawing.Processors; + using ImageSharp.Drawing.Pens; + + public class FillShape : IDisposable + { + GraphicsOptions noneDefault = new GraphicsOptions(); + Color color = Color.HotPink; + SolidBrush brush = Brushes.Solid(Color.HotPink); + IShape shape = new SixLabors.Shapes.Polygon(new LinearLineSegment(new Vector2[] { + new Vector2(10,10), + new Vector2(20,10), + new Vector2(20,10), + new Vector2(30,10), + })); + private ProcessorWatchingImage img; + + public FillShape() + { + this.img = new Paths.ProcessorWatchingImage(10, 10); + } + + public void Dispose() + { + img.Dispose(); + } + + [Fact] + public void Brush_shape() + { + img.Fill(brush, shape); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var region = Assert.IsType(processor.Region); + Assert.Equal(shape, region.Shape); + + Assert.Equal(brush, processor.Brush); + } + + [Fact] + public void Brush_shape_options() + { + img.Fill(brush, shape, noneDefault); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var region = Assert.IsType(processor.Region); + Assert.Equal(shape, region.Shape); + + Assert.Equal(brush, processor.Brush); + } + + [Fact] + public void color_shape() + { + img.Fill(color, shape); + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + var region = Assert.IsType(processor.Region); + Assert.Equal(shape, region.Shape); + + var brush = Assert.IsType>(processor.Brush); + Assert.Equal(color, brush.Color); + } + + [Fact] + public void color_shape_options() + { + img.Fill(color, shape, noneDefault); + + + Assert.NotEmpty(img.ProcessorApplications); + var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + Assert.Equal(noneDefault, processor.Options); + + var region = Assert.IsType(processor.Region); + Assert.Equal(shape, region.Shape); + + var brush = Assert.IsType>(processor.Brush); + Assert.Equal(color, brush.Color); + + } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs b/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs new file mode 100644 index 000000000..ad3d41bc6 --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs @@ -0,0 +1,38 @@ + +namespace ImageSharp.Tests.Drawing.Paths +{ + using System; + using System.IO; + using ImageSharp; + using Processing; + using System.Collections.Generic; + + /// + /// Watches but does not actually run the processors against the image. + /// + /// + public class ProcessorWatchingImage : Image + { + public List ProcessorApplications { get; } = new List(); + + public ProcessorWatchingImage(int width, int height) + : base(width, height, Configuration.CreateDefaultInstance()) + { + } + + public override void ApplyProcessor(IImageProcessor processor, Rectangle rectangle) + { + ProcessorApplications.Add(new ProcessorDetails + { + processor = processor, + rectangle = rectangle + }); + } + + public struct ProcessorDetails + { + public IImageProcessor processor; + public Rectangle rectangle; + } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs new file mode 100644 index 000000000..24e921ccb --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs @@ -0,0 +1,188 @@ + +namespace ImageSharp.Tests.Drawing.Paths +{ + using System; + using System.IO; + using ImageSharp; + using ImageSharp.Drawing.Brushes; + using Processing; + using System.Collections.Generic; + using Xunit; + using ImageSharp.Drawing; + using System.Numerics; + using SixLabors.Shapes; + using ImageSharp.Drawing.Processors; + using ImageSharp.Drawing.Pens; + using Moq; + using System.Collections.Immutable; + + public class ShapePathTests + { + private readonly Mock pathMock1; + private readonly Mock pathMock2; + private readonly Mock shapeMock1; + private readonly SixLabors.Shapes.Rectangle bounds1; + + public ShapePathTests() + { + this.shapeMock1 = new Mock(); + this.pathMock2 = new Mock(); + this.pathMock1 = new Mock(); + + this.bounds1 = new SixLabors.Shapes.Rectangle(10.5f, 10, 10, 10); + pathMock1.Setup(x => x.Bounds).Returns(this.bounds1); + pathMock2.Setup(x => x.Bounds).Returns(this.bounds1); + shapeMock1.Setup(x => x.Bounds).Returns(this.bounds1); + // wire up the 2 mocks to reference eachother + pathMock1.Setup(x => x.AsShape()).Returns(() => shapeMock1.Object); + shapeMock1.Setup(x => x.Paths).Returns(() => ImmutableArray.Create(pathMock1.Object, pathMock2.Object)); + } + + [Fact] + public void ShapePathWithPath_CallsAsShape() + { + new ShapePath(pathMock1.Object); + + pathMock1.Verify(x => x.AsShape()); + } + + [Fact] + public void ShapePathFromPathConvertsBoundsDoesNotProxyToShape() + { + ShapePath region = new ShapePath(pathMock1.Object); + + Assert.Equal(Math.Floor(bounds1.Left), region.Bounds.Left); + Assert.Equal(Math.Ceiling(bounds1.Right), region.Bounds.Right); + + pathMock1.Verify(x => x.Bounds); + } + + [Fact] + public void ShapePathFromPathMaxIntersectionsProxyToShape() + { + ShapePath region = new ShapePath(pathMock1.Object); + + int i = region.MaxIntersections; + shapeMock1.Verify(x => x.MaxIntersections); + } + + [Fact] + public void ShapePathFromPathScanXProxyToShape() + { + int xToScan = 10; + ShapePath region = new ShapePath(pathMock1.Object); + + shapeMock1.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((s, e, b, c, o) => { + Assert.Equal(xToScan, s.X); + Assert.Equal(xToScan, e.X); + Assert.True(s.Y < bounds1.Top); + Assert.True(e.Y > bounds1.Bottom); + }).Returns(0); + + int i = region.ScanX(xToScan, new float[0], 0, 0); + + shapeMock1.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + public void ShapePathFromPathScanYProxyToShape() + { + int yToScan = 10; + ShapePath region = new ShapePath(pathMock1.Object); + + shapeMock1.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((s, e, b, c, o) => { + Assert.Equal(yToScan, s.Y); + Assert.Equal(yToScan, e.Y); + Assert.True(s.X < bounds1.Left); + Assert.True(e.X > bounds1.Right); + }).Returns(0); + + int i = region.ScanY(yToScan, new float[0], 0, 0); + + shapeMock1.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + } + + + [Fact] + public void ShapePathFromShapeScanXProxyToShape() + { + int xToScan = 10; + ShapePath region = new ShapePath(shapeMock1.Object); + + shapeMock1.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((s, e, b, c, o) => { + Assert.Equal(xToScan, s.X); + Assert.Equal(xToScan, e.X); + Assert.True(s.Y < bounds1.Top); + Assert.True(e.Y > bounds1.Bottom); + }).Returns(0); + + int i = region.ScanX(xToScan, new float[0], 0, 0); + + shapeMock1.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + public void ShapePathFromShapeScanYProxyToShape() + { + int yToScan = 10; + ShapePath region = new ShapePath(shapeMock1.Object); + + shapeMock1.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((s, e, b, c, o) => { + Assert.Equal(yToScan, s.Y); + Assert.Equal(yToScan, e.Y); + Assert.True(s.X < bounds1.Left); + Assert.True(e.X > bounds1.Right); + }).Returns(0); + + int i = region.ScanY(yToScan, new float[0], 0, 0); + + shapeMock1.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + public void ShapePathFromShapeConvertsBoundsProxyToShape() + { + ShapePath region = new ShapePath(shapeMock1.Object); + + Assert.Equal(Math.Floor(bounds1.Left), region.Bounds.Left); + Assert.Equal(Math.Ceiling(bounds1.Right), region.Bounds.Right); + + shapeMock1.Verify(x => x.Bounds); + } + + [Fact] + public void ShapePathFromShapeMaxIntersectionsProxyToShape() + { + ShapePath region = new ShapePath(shapeMock1.Object); + + int i = region.MaxIntersections; + shapeMock1.Verify(x => x.MaxIntersections); + } + + [Fact] + public void GetPointInfoCallAllPathsForShape() + { + ShapePath region = new ShapePath(shapeMock1.Object); + + ImageSharp.Drawing.PointInfo info = region.GetPointInfo(10, 1); + + pathMock1.Verify(x => x.Distance(new Vector2(10, 1)), Times.Once); + pathMock2.Verify(x => x.Distance(new Vector2(10, 1)), Times.Once); + } + + [Fact] + public void GetPointInfoCallSinglePathForPath() + { + ShapePath region = new ShapePath(pathMock1.Object); + + ImageSharp.Drawing.PointInfo info = region.GetPointInfo(10, 1); + + pathMock1.Verify(x => x.Distance(new Vector2(10, 1)), Times.Once); + pathMock2.Verify(x => x.Distance(new Vector2(10, 1)), Times.Never); + } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs new file mode 100644 index 000000000..e983dce66 --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs @@ -0,0 +1,170 @@ + +namespace ImageSharp.Tests.Drawing.Paths +{ + using System; + using System.IO; + using ImageSharp; + using ImageSharp.Drawing.Brushes; + using Processing; + using System.Collections.Generic; + using Xunit; + using ImageSharp.Drawing; + using System.Numerics; + using SixLabors.Shapes; + using ImageSharp.Drawing.Processors; + using ImageSharp.Drawing.Pens; + using Moq; + using System.Collections.Immutable; + + public class ShapeRegionTests + { + private readonly Mock pathMock; + private readonly Mock shapeMock; + private readonly SixLabors.Shapes.Rectangle bounds; + + public ShapeRegionTests() + { + this.shapeMock = new Mock(); + this.pathMock = new Mock(); + + this.bounds = new SixLabors.Shapes.Rectangle(10.5f, 10, 10, 10); + shapeMock.Setup(x => x.Bounds).Returns(this.bounds); + // wire up the 2 mocks to reference eachother + pathMock.Setup(x => x.AsShape()).Returns(() => shapeMock.Object); + shapeMock.Setup(x => x.Paths).Returns(() => ImmutableArray.Create(pathMock.Object)); + } + + [Fact] + public void ShapeRegionWithPath_CallsAsShape() + { + new ShapeRegion(pathMock.Object); + + pathMock.Verify(x => x.AsShape()); + } + + [Fact] + public void ShapeRegionWithPath_RetainsShape() + { + ShapeRegion region = new ShapeRegion(pathMock.Object); + + Assert.Equal(shapeMock.Object, region.Shape); + } + + [Fact] + public void ShapeRegionFromPathConvertsBoundsProxyToShape() + { + ShapeRegion region = new ShapeRegion(pathMock.Object); + + Assert.Equal(Math.Floor(bounds.Left), region.Bounds.Left); + Assert.Equal(Math.Ceiling(bounds.Right), region.Bounds.Right); + + shapeMock.Verify(x => x.Bounds); + } + + [Fact] + public void ShapeRegionFromPathMaxIntersectionsProxyToShape() + { + ShapeRegion region = new ShapeRegion(pathMock.Object); + + int i = region.MaxIntersections; + shapeMock.Verify(x => x.MaxIntersections); + } + + [Fact] + public void ShapeRegionFromPathScanXProxyToShape() + { + int xToScan = 10; + ShapeRegion region = new ShapeRegion(pathMock.Object); + + shapeMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((s, e, b, c, o) => { + Assert.Equal(xToScan, s.X); + Assert.Equal(xToScan, e.X); + Assert.True(s.Y < bounds.Top); + Assert.True(e.Y > bounds.Bottom); + }).Returns(0); + + int i = region.ScanX(xToScan, new float[0], 0, 0); + + shapeMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + public void ShapeRegionFromPathScanYProxyToShape() + { + int yToScan = 10; + ShapeRegion region = new ShapeRegion(pathMock.Object); + + shapeMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((s, e, b, c, o) => { + Assert.Equal(yToScan, s.Y); + Assert.Equal(yToScan, e.Y); + Assert.True(s.X < bounds.Left); + Assert.True(e.X > bounds.Right); + }).Returns(0); + + int i = region.ScanY(yToScan, new float[0], 0, 0); + + shapeMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + } + + + [Fact] + public void ShapeRegionFromShapeScanXProxyToShape() + { + int xToScan = 10; + ShapeRegion region = new ShapeRegion(shapeMock.Object); + + shapeMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((s, e, b, c, o) => { + Assert.Equal(xToScan, s.X); + Assert.Equal(xToScan, e.X); + Assert.True(s.Y < bounds.Top); + Assert.True(e.Y > bounds.Bottom); + }).Returns(0); + + int i = region.ScanX(xToScan, new float[0], 0, 0); + + shapeMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + public void ShapeRegionFromShapeScanYProxyToShape() + { + int yToScan = 10; + ShapeRegion region = new ShapeRegion(shapeMock.Object); + + shapeMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((s, e, b, c, o) => { + Assert.Equal(yToScan, s.Y); + Assert.Equal(yToScan, e.Y); + Assert.True(s.X < bounds.Left); + Assert.True(e.X > bounds.Right); + }).Returns(0); + + int i = region.ScanY(yToScan, new float[0], 0, 0); + + shapeMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + public void ShapeRegionFromShapeConvertsBoundsProxyToShape() + { + ShapeRegion region = new ShapeRegion(shapeMock.Object); + + Assert.Equal(Math.Floor(bounds.Left), region.Bounds.Left); + Assert.Equal(Math.Ceiling(bounds.Right), region.Bounds.Right); + + shapeMock.Verify(x => x.Bounds); + } + + [Fact] + public void ShapeRegionFromShapeMaxIntersectionsProxyToShape() + { + ShapeRegion region = new ShapeRegion(shapeMock.Object); + + int i = region.MaxIntersections; + shapeMock.Verify(x => x.MaxIntersections); + } + } +} diff --git a/tests/ImageSharp.Tests/project.json b/tests/ImageSharp.Tests/project.json index 2a01aff84..3761bb385 100644 --- a/tests/ImageSharp.Tests/project.json +++ b/tests/ImageSharp.Tests/project.json @@ -45,7 +45,9 @@ }, "ImageSharp.Processing": { "target": "project" - } + }, + //alpha supports netstandard + "Moq": "4.6.38-alpha" }, "frameworks": { "netcoreapp1.1": { From 2c5f60d7a871cc9fe3a05c4d8ef089e58951cc96 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Thu, 2 Feb 2017 19:44:33 +0000 Subject: [PATCH 020/142] revert accident additions --- .travis.yml | 4 ++-- ConsoleApp1/ConsoleApp1.csproj | 8 -------- ConsoleApp1/Program.cs | 9 --------- 3 files changed, 2 insertions(+), 19 deletions(-) delete mode 100644 ConsoleApp1/ConsoleApp1.csproj delete mode 100644 ConsoleApp1/Program.cs diff --git a/.travis.yml b/.travis.yml index ca8b90a06..172079df2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,8 +20,8 @@ branches: script: - dotnet restore - - dotnet build -c Release src/*/project.csproj - - dotnet test tests/ImageSharp.Tests/project.csproj -c Release -f "netcoreapp1.1" + - dotnet build -c Release src/*/project.json + - dotnet test tests/ImageSharp.Tests/project.json -c Release -f "netcoreapp1.1" env: global: diff --git a/ConsoleApp1/ConsoleApp1.csproj b/ConsoleApp1/ConsoleApp1.csproj deleted file mode 100644 index 68b6f2423..000000000 --- a/ConsoleApp1/ConsoleApp1.csproj +++ /dev/null @@ -1,8 +0,0 @@ - - - - Exe - netcoreapp1.0 - - - \ No newline at end of file diff --git a/ConsoleApp1/Program.cs b/ConsoleApp1/Program.cs deleted file mode 100644 index 104ecf026..000000000 --- a/ConsoleApp1/Program.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -class Program -{ - static void Main(string[] args) - { - Console.WriteLine("Hello World!"); - } -} \ No newline at end of file From 65ffb797e14a551ba85ed081ee61c383d4baf6be Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Fri, 3 Feb 2017 09:17:52 +0000 Subject: [PATCH 021/142] fix test names & vars --- .../Drawing/Helpers/BezierPolygon.cs | 71 ------------------ .../Drawing/Helpers/LinearPolygon.cs | 71 ------------------ .../Drawing/LineComplexPolygonTests.cs | 32 ++++----- .../Drawing/Paths/DrawBeziersTests.cs | 72 +++++++++---------- .../Drawing/Paths/DrawLinesTests.cs | 72 +++++++++---------- .../Drawing/Paths/DrawPath.cs | 48 ++++++------- .../Drawing/Paths/DrawPolygon.cs | 72 +++++++++---------- .../Drawing/Paths/DrawRectangle.cs | 60 ++++++++-------- .../Drawing/Paths/DrawShape.cs | 48 ++++++------- .../Drawing/Paths/FillPath.cs | 52 +++++++------- .../Drawing/Paths/FillPolygon.cs | 45 ++++++------ .../Drawing/Paths/FillRectangle.cs | 37 +++++----- .../Drawing/Paths/FillShape.cs | 31 ++++---- .../Drawing/Paths/ProcessorWatchingImage.cs | 4 ++ .../Drawing/Paths/ShapePathTests.cs | 2 +- .../Drawing/Paths/ShapeRegionTests.cs | 4 +- .../Drawing/SolidBezierTests.cs | 4 +- .../Drawing/SolidComplexPolygonTests.cs | 24 +++---- 18 files changed, 304 insertions(+), 445 deletions(-) delete mode 100644 tests/ImageSharp.Tests/Drawing/Helpers/BezierPolygon.cs delete mode 100644 tests/ImageSharp.Tests/Drawing/Helpers/LinearPolygon.cs diff --git a/tests/ImageSharp.Tests/Drawing/Helpers/BezierPolygon.cs b/tests/ImageSharp.Tests/Drawing/Helpers/BezierPolygon.cs deleted file mode 100644 index 070e55501..000000000 --- a/tests/ImageSharp.Tests/Drawing/Helpers/BezierPolygon.cs +++ /dev/null @@ -1,71 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace SixLabors.Shapes -{ - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Numerics; - - using SixLabors.Shapes; - public class BezierPolygon : IShape - { - private Polygon polygon; - - public BezierPolygon(params Vector2[] points) - { - this.polygon = new Polygon(new BezierLineSegment(points)); - } - - public float Distance(Vector2 point) - { - return this.polygon.Distance(point); - } - - public bool Contains(Vector2 point) - { - return this.polygon.Contains(point); - } - - public int FindIntersections(Vector2 start, Vector2 end, Vector2[] buffer, int count, int offset) - { - return this.polygon.FindIntersections(start, end, buffer, count, offset); - } - - public IEnumerable FindIntersections(Vector2 start, Vector2 end) - { - return this.polygon.FindIntersections(start, end); - } - - public IShape Transform(Matrix3x2 matrix) - { - return ((IShape)this.polygon).Transform(matrix); - } - - public Rectangle Bounds - { - get - { - return this.polygon.Bounds; - } - } - - public ImmutableArray Paths - { - get - { - return this.polygon.Paths; - } - } - - public int MaxIntersections - { - get - { - return this.polygon.MaxIntersections; - } - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/Helpers/LinearPolygon.cs b/tests/ImageSharp.Tests/Drawing/Helpers/LinearPolygon.cs deleted file mode 100644 index fa9488266..000000000 --- a/tests/ImageSharp.Tests/Drawing/Helpers/LinearPolygon.cs +++ /dev/null @@ -1,71 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace SixLabors.Shapes -{ - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Numerics; - - using SixLabors.Shapes; - public class LinearPolygon : IShape - { - private Polygon polygon; - - public LinearPolygon(params Vector2[] points) - { - this.polygon = new Polygon(new LinearLineSegment(points)); - } - - public float Distance(Vector2 point) - { - return this.polygon.Distance(point); - } - - public bool Contains(Vector2 point) - { - return this.polygon.Contains(point); - } - - public int FindIntersections(Vector2 start, Vector2 end, Vector2[] buffer, int count, int offset) - { - return this.polygon.FindIntersections(start, end, buffer, count, offset); - } - - public IEnumerable FindIntersections(Vector2 start, Vector2 end) - { - return this.polygon.FindIntersections(start, end); - } - - public IShape Transform(Matrix3x2 matrix) - { - return ((IShape)this.polygon).Transform(matrix); - } - - public Rectangle Bounds - { - get - { - return this.polygon.Bounds; - } - } - - public ImmutableArray Paths - { - get - { - return this.polygon.Paths; - } - } - - public int MaxIntersections - { - get - { - return this.polygon.MaxIntersections; - } - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs index 03ec5d0c8..0a4f2c4a0 100644 --- a/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs @@ -71,15 +71,15 @@ namespace ImageSharp.Tests.Drawing public void ImageShouldBeOverlayedByPolygonOutlineNoOverlapping() { string path = this.CreateOutputDirectory("Drawing", "LineComplexPolygon"); - LinearPolygon simplePath = new LinearPolygon( + Polygon simplePath = new Polygon(new LinearLineSegment( new Vector2(10, 10), new Vector2(200, 150), - new Vector2(50, 300)); + new Vector2(50, 300))); - LinearPolygon hole1 = new LinearPolygon( + Polygon hole1 = new Polygon(new LinearLineSegment( new Vector2(207, 25), new Vector2(263, 25), - new Vector2(235, 57)); + new Vector2(235, 57))); using (Image image = new Image(500, 500)) { @@ -122,15 +122,15 @@ namespace ImageSharp.Tests.Drawing public void ImageShouldBeOverlayedByPolygonOutlineOverlapping() { string path = this.CreateOutputDirectory("Drawing", "LineComplexPolygon"); - LinearPolygon simplePath = new LinearPolygon( + Polygon simplePath = new Polygon(new LinearLineSegment( new Vector2(10, 10), new Vector2(200, 150), - new Vector2(50, 300)); + new Vector2(50, 300))); - LinearPolygon hole1 = new LinearPolygon( + Polygon hole1 = new Polygon(new LinearLineSegment( new Vector2(37, 85), new Vector2(130, 40), - new Vector2(65, 137)); + new Vector2(65, 137))); using (Image image = new Image(500, 500)) @@ -169,15 +169,15 @@ namespace ImageSharp.Tests.Drawing public void ImageShouldBeOverlayedByPolygonOutlineDashed() { string path = this.CreateOutputDirectory("Drawing", "LineComplexPolygon"); - LinearPolygon simplePath = new LinearPolygon( + Polygon simplePath = new Polygon(new LinearLineSegment( new Vector2(10, 10), new Vector2(200, 150), - new Vector2(50, 300)); + new Vector2(50, 300))); - LinearPolygon hole1 = new LinearPolygon( + Polygon hole1 = new Polygon(new LinearLineSegment( new Vector2(37, 85), new Vector2(93, 85), - new Vector2(65, 137)); + new Vector2(65, 137))); using (Image image = new Image(500, 500)) { @@ -196,15 +196,15 @@ namespace ImageSharp.Tests.Drawing public void ImageShouldBeOverlayedPolygonOutlineWithOpacity() { string path = this.CreateOutputDirectory("Drawing", "LineComplexPolygon"); - LinearPolygon simplePath = new LinearPolygon( + Polygon simplePath = new Polygon(new LinearLineSegment( new Vector2(10, 10), new Vector2(200, 150), - new Vector2(50, 300)); + new Vector2(50, 300))); - LinearPolygon hole1 = new LinearPolygon( + Polygon hole1 = new Polygon(new LinearLineSegment( new Vector2(37, 85), new Vector2(93, 85), - new Vector2(65, 137)); + new Vector2(65, 137))); Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); using (Image image = new Image(500, 500)) diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawBeziersTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawBeziersTests.cs index 967cd1970..0d3b46981 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawBeziersTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/DrawBeziersTests.cs @@ -40,127 +40,127 @@ namespace ImageSharp.Tests.Drawing.Paths } [Fact] - public void Brush_Thickness_points() + public void CorrectlySetsBrushThicknessAndPoints() { img.DrawBeziers(brush, thickness, points); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var path = Assert.IsType(processor.Path); + ShapePath path = Assert.IsType(processor.Path); Assert.NotEmpty(path.Paths); - var vector = Assert.IsType(path.Paths[0]); - var segment = Assert.IsType(vector.LineSegments[0]); + SixLabors.Shapes.Path vector = Assert.IsType(path.Paths[0]); + BezierLineSegment segment = Assert.IsType(vector.LineSegments[0]); - var pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(brush, pen.Brush); Assert.Equal(thickness, pen.Width); } [Fact] - public void Brush_Thickness_points_options() + public void CorrectlySetsBrushThicknessPointsAndOptions() { img.DrawBeziers(brush, thickness, points, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var path = Assert.IsType(processor.Path); + ShapePath path = Assert.IsType(processor.Path); Assert.NotEmpty(path.Paths); - var vector = Assert.IsType(path.Paths[0]); - var segment = Assert.IsType(vector.LineSegments[0]); + SixLabors.Shapes.Path vector = Assert.IsType(path.Paths[0]); + BezierLineSegment segment = Assert.IsType(vector.LineSegments[0]); - var pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(brush, pen.Brush); Assert.Equal(thickness, pen.Width); } [Fact] - public void color_Thickness_points() + public void CorrectlySetsColorThicknessAndPoints() { img.DrawBeziers(color, thickness, points); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var path = Assert.IsType(processor.Path); + ShapePath path = Assert.IsType(processor.Path); Assert.NotEmpty(path.Paths); - var vector = Assert.IsType(path.Paths[0]); - var segment = Assert.IsType(vector.LineSegments[0]); + SixLabors.Shapes.Path vector = Assert.IsType(path.Paths[0]); + BezierLineSegment segment = Assert.IsType(vector.LineSegments[0]); - var pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(thickness, pen.Width); - var brush = Assert.IsType>(pen.Brush); + SolidBrush brush = Assert.IsType>(pen.Brush); Assert.Equal(color, brush.Color); } [Fact] - public void color_Thickness_points_options() + public void CorrectlySetsColorThicknessPointsAndOptions() { img.DrawBeziers(color, thickness, points, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var path = Assert.IsType(processor.Path); + ShapePath path = Assert.IsType(processor.Path); Assert.NotEmpty(path.Paths); - var vector = Assert.IsType(path.Paths[0]); - var segment = Assert.IsType(vector.LineSegments[0]); + SixLabors.Shapes.Path vector = Assert.IsType(path.Paths[0]); + BezierLineSegment segment = Assert.IsType(vector.LineSegments[0]); - var pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(thickness, pen.Width); - var brush = Assert.IsType>(pen.Brush); + SolidBrush brush = Assert.IsType>(pen.Brush); Assert.Equal(color, brush.Color); } [Fact] - public void pen_points() + public void CorrectlySetsPenAndPoints() { img.DrawBeziers(pen, points); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var path = Assert.IsType(processor.Path); + ShapePath path = Assert.IsType(processor.Path); Assert.NotEmpty(path.Paths); - var vector = Assert.IsType(path.Paths[0]); - var segment = Assert.IsType(vector.LineSegments[0]); + SixLabors.Shapes.Path vector = Assert.IsType(path.Paths[0]); + BezierLineSegment segment = Assert.IsType(vector.LineSegments[0]); Assert.Equal(pen, processor.Pen); } [Fact] - public void pen_points_options() + public void CorrectlySetsPenPointsAndOptions() { img.DrawBeziers(pen, points, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var path = Assert.IsType(processor.Path); + ShapePath path = Assert.IsType(processor.Path); Assert.NotEmpty(path.Paths); - var vector = Assert.IsType(path.Paths[0]); - var segment = Assert.IsType(vector.LineSegments[0]); + SixLabors.Shapes.Path vector = Assert.IsType(path.Paths[0]); + BezierLineSegment segment = Assert.IsType(vector.LineSegments[0]); Assert.Equal(pen, processor.Pen); } diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawLinesTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawLinesTests.cs index 3b203cdd8..bbed3cae6 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawLinesTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/DrawLinesTests.cs @@ -40,127 +40,127 @@ namespace ImageSharp.Tests.Drawing.Paths } [Fact] - public void Brush_Thickness_points() + public void CorrectlySetsBrushThicknessAndPoints() { img.DrawLines(brush, thickness, points); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var path = Assert.IsType(processor.Path); + ShapePath path = Assert.IsType(processor.Path); Assert.NotEmpty(path.Paths); - var vector = Assert.IsType(path.Paths[0]); - var segment = Assert.IsType(vector.LineSegments[0]); + SixLabors.Shapes.Path vector = Assert.IsType(path.Paths[0]); + LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - var pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(brush, pen.Brush); Assert.Equal(thickness, pen.Width); } [Fact] - public void Brush_Thickness_points_options() + public void CorrectlySetsBrushThicknessPointsAndOptions() { img.DrawLines(brush, thickness, points, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var path = Assert.IsType(processor.Path); + ShapePath path = Assert.IsType(processor.Path); Assert.NotEmpty(path.Paths); - var vector = Assert.IsType(path.Paths[0]); - var segment = Assert.IsType(vector.LineSegments[0]); + SixLabors.Shapes.Path vector = Assert.IsType(path.Paths[0]); + LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - var pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(brush, pen.Brush); Assert.Equal(thickness, pen.Width); } [Fact] - public void color_Thickness_points() + public void CorrectlySetsColorThicknessAndPoints() { img.DrawLines(color, thickness, points); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var path = Assert.IsType(processor.Path); + ShapePath path = Assert.IsType(processor.Path); Assert.NotEmpty(path.Paths); - var vector = Assert.IsType(path.Paths[0]); - var segment = Assert.IsType(vector.LineSegments[0]); + SixLabors.Shapes.Path vector = Assert.IsType(path.Paths[0]); + LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - var pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(thickness, pen.Width); - var brush = Assert.IsType>(pen.Brush); + SolidBrush brush = Assert.IsType>(pen.Brush); Assert.Equal(color, brush.Color); } [Fact] - public void color_Thickness_points_options() + public void CorrectlySetsColorThicknessPointsAndOptions() { img.DrawLines(color, thickness, points, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var path = Assert.IsType(processor.Path); + ShapePath path = Assert.IsType(processor.Path); Assert.NotEmpty(path.Paths); - var vector = Assert.IsType(path.Paths[0]); - var segment = Assert.IsType(vector.LineSegments[0]); + SixLabors.Shapes.Path vector = Assert.IsType(path.Paths[0]); + LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - var pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(thickness, pen.Width); - var brush = Assert.IsType>(pen.Brush); + SolidBrush brush = Assert.IsType>(pen.Brush); Assert.Equal(color, brush.Color); } [Fact] - public void pen_points() + public void CorrectlySetsPenAndPoints() { img.DrawLines(pen, points); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var path = Assert.IsType(processor.Path); + ShapePath path = Assert.IsType(processor.Path); Assert.NotEmpty(path.Paths); - var vector = Assert.IsType(path.Paths[0]); - var segment = Assert.IsType(vector.LineSegments[0]); + SixLabors.Shapes.Path vector = Assert.IsType(path.Paths[0]); + LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); Assert.Equal(pen, processor.Pen); } [Fact] - public void pen_points_options() + public void CorrectlySetsPenPointsAndOptions() { img.DrawLines(pen, points, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var path = Assert.IsType(processor.Path); + ShapePath path = Assert.IsType(processor.Path); Assert.NotEmpty(path.Paths); - var vector = Assert.IsType(path.Paths[0]); - var segment = Assert.IsType(vector.LineSegments[0]); + SixLabors.Shapes.Path vector = Assert.IsType(path.Paths[0]); + LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); Assert.Equal(pen, processor.Pen); } diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawPath.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawPath.cs index 8ce7dcba2..531546370 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawPath.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/DrawPath.cs @@ -40,96 +40,96 @@ namespace ImageSharp.Tests.Drawing.Paths } [Fact] - public void Brush_Thickness_path() + public void CorrectlySetsBrushThicknessAndPath() { img.Draw(brush, thickness, path); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var shapepath = Assert.IsType(processor.Path); + ShapePath shapepath = Assert.IsType(processor.Path); Assert.NotEmpty(shapepath.Paths); Assert.Equal(path, shapepath.Paths[0]); - var pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(brush, pen.Brush); Assert.Equal(thickness, pen.Width); } [Fact] - public void Brush_Thickness_path_options() + public void CorrectlySetsBrushThicknessPathAndOptions() { img.Draw(brush, thickness, path, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var shapepath = Assert.IsType(processor.Path); + ShapePath shapepath = Assert.IsType(processor.Path); Assert.NotEmpty(shapepath.Paths); Assert.Equal(path, shapepath.Paths[0]); - var pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(brush, pen.Brush); Assert.Equal(thickness, pen.Width); } [Fact] - public void color_Thickness_path() + public void CorrectlySetsColorThicknessAndPath() { img.Draw(color, thickness, path); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var shapepath = Assert.IsType(processor.Path); + ShapePath shapepath = Assert.IsType(processor.Path); Assert.NotEmpty(shapepath.Paths); Assert.Equal(path, shapepath.Paths[0]); - var pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(thickness, pen.Width); - var brush = Assert.IsType>(pen.Brush); + SolidBrush brush = Assert.IsType>(pen.Brush); Assert.Equal(color, brush.Color); } [Fact] - public void color_Thickness_path_options() + public void CorrectlySetsColorThicknessPathAndOptions() { img.Draw(color, thickness, path, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var shapepath = Assert.IsType(processor.Path); + ShapePath shapepath = Assert.IsType(processor.Path); Assert.NotEmpty(shapepath.Paths); Assert.Equal(path, shapepath.Paths[0]); - var pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(thickness, pen.Width); - var brush = Assert.IsType>(pen.Brush); + SolidBrush brush = Assert.IsType>(pen.Brush); Assert.Equal(color, brush.Color); } [Fact] - public void pen_path() + public void CorrectlySetsPenAndPath() { img.Draw(pen, path); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var shapepath = Assert.IsType(processor.Path); + ShapePath shapepath = Assert.IsType(processor.Path); Assert.NotEmpty(shapepath.Paths); Assert.Equal(path, shapepath.Paths[0]); @@ -137,16 +137,16 @@ namespace ImageSharp.Tests.Drawing.Paths } [Fact] - public void pen_path_options() + public void CorrectlySetsPenPathAndOptions() { img.Draw(pen, path, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var shapepath = Assert.IsType(processor.Path); + ShapePath shapepath = Assert.IsType(processor.Path); Assert.NotEmpty(shapepath.Paths); Assert.Equal(path, shapepath.Paths[0]); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawPolygon.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawPolygon.cs index 9a931470d..399e79dae 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawPolygon.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/DrawPolygon.cs @@ -40,127 +40,127 @@ namespace ImageSharp.Tests.Drawing.Paths } [Fact] - public void Brush_Thickness_points() + public void CorrectlySetsBrushThicknessAndPoints() { img.DrawPolygon(brush, thickness, points); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var path = Assert.IsType(processor.Path); + ShapePath path = Assert.IsType(processor.Path); Assert.NotEmpty(path.Paths); - var vector = Assert.IsType(path.Paths[0].AsShape()); - var segment = Assert.IsType(vector.LineSegments[0]); + Polygon vector = Assert.IsType(path.Paths[0].AsShape()); + LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - var pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(brush, pen.Brush); Assert.Equal(thickness, pen.Width); } [Fact] - public void Brush_Thickness_points_options() + public void CorrectlySetsBrushThicknessPointsAndOptions() { img.DrawPolygon(brush, thickness, points, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var path = Assert.IsType(processor.Path); + ShapePath path = Assert.IsType(processor.Path); Assert.NotEmpty(path.Paths); - var vector = Assert.IsType(path.Paths[0].AsShape()); - var segment = Assert.IsType(vector.LineSegments[0]); + Polygon vector = Assert.IsType(path.Paths[0].AsShape()); + LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - var pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(brush, pen.Brush); Assert.Equal(thickness, pen.Width); } [Fact] - public void color_Thickness_points() + public void CorrectlySetsColorThicknessAndPoints() { img.DrawPolygon(color, thickness, points); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var path = Assert.IsType(processor.Path); + ShapePath path = Assert.IsType(processor.Path); Assert.NotEmpty(path.Paths); - var vector = Assert.IsType(path.Paths[0].AsShape()); - var segment = Assert.IsType(vector.LineSegments[0]); + Polygon vector = Assert.IsType(path.Paths[0].AsShape()); + LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - var pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(thickness, pen.Width); - var brush = Assert.IsType>(pen.Brush); + SolidBrush brush = Assert.IsType>(pen.Brush); Assert.Equal(color, brush.Color); } [Fact] - public void color_Thickness_points_options() + public void CorrectlySetsColorThicknessPointsAndOptions() { img.DrawPolygon(color, thickness, points, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var path = Assert.IsType(processor.Path); + ShapePath path = Assert.IsType(processor.Path); Assert.NotEmpty(path.Paths); - var vector = Assert.IsType(path.Paths[0].AsShape()); - var segment = Assert.IsType(vector.LineSegments[0]); + Polygon vector = Assert.IsType(path.Paths[0].AsShape()); + LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - var pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(thickness, pen.Width); - var brush = Assert.IsType>(pen.Brush); + SolidBrush brush = Assert.IsType>(pen.Brush); Assert.Equal(color, brush.Color); } [Fact] - public void pen_points() + public void CorrectlySetsPenAndPoints() { img.DrawPolygon(pen, points); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var path = Assert.IsType(processor.Path); + ShapePath path = Assert.IsType(processor.Path); Assert.NotEmpty(path.Paths); - var vector = Assert.IsType(path.Paths[0].AsShape()); - var segment = Assert.IsType(vector.LineSegments[0]); + Polygon vector = Assert.IsType(path.Paths[0].AsShape()); + LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); Assert.Equal(pen, processor.Pen); } [Fact] - public void pen_points_options() + public void CorrectlySetsPenPointsAndOptions() { img.DrawPolygon(pen, points, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var path = Assert.IsType(processor.Path); + ShapePath path = Assert.IsType(processor.Path); Assert.NotEmpty(path.Paths); - var vector = Assert.IsType(path.Paths[0].AsShape()); - var segment = Assert.IsType(vector.LineSegments[0]); + Polygon vector = Assert.IsType(path.Paths[0].AsShape()); + LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); Assert.Equal(pen, processor.Pen); } diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawRectangle.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawRectangle.cs index f6680bc9c..3842063b3 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawRectangle.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/DrawRectangle.cs @@ -36,122 +36,122 @@ namespace ImageSharp.Tests.Drawing.Paths } [Fact] - public void Brush_Thickness_rectangle() + public void CorrectlySetsBrushThicknessAndRectangle() { img.Draw(brush, thickness, rectangle); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var shapepath = Assert.IsType(processor.Path); + ShapePath shapepath = Assert.IsType(processor.Path); Assert.NotEmpty(shapepath.Paths); - var rect = Assert.IsType(shapepath.Paths[0].AsShape()); + SixLabors.Shapes.Rectangle rect = Assert.IsType(shapepath.Paths[0].AsShape()); Assert.Equal(rect.Location.X, rectangle.X); Assert.Equal(rect.Location.Y, rectangle.Y); Assert.Equal(rect.Size.Width, rectangle.Width); Assert.Equal(rect.Size.Height, rectangle.Height); - var pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(brush, pen.Brush); Assert.Equal(thickness, pen.Width); } [Fact] - public void Brush_Thickness_rectangle_options() + public void CorrectlySetsBrushThicknessRectangleAndOptions() { img.Draw(brush, thickness, rectangle, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var shapepath = Assert.IsType(processor.Path); + ShapePath shapepath = Assert.IsType(processor.Path); Assert.NotEmpty(shapepath.Paths); - var rect = Assert.IsType(shapepath.Paths[0].AsShape()); + SixLabors.Shapes.Rectangle rect = Assert.IsType(shapepath.Paths[0].AsShape()); Assert.Equal(rect.Location.X, rectangle.X); Assert.Equal(rect.Location.Y, rectangle.Y); Assert.Equal(rect.Size.Width, rectangle.Width); Assert.Equal(rect.Size.Height, rectangle.Height); - var pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(brush, pen.Brush); Assert.Equal(thickness, pen.Width); } [Fact] - public void color_Thickness_rectangle() + public void CorrectlySetsColorThicknessAndRectangle() { img.Draw(color, thickness, rectangle); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var shapepath = Assert.IsType(processor.Path); + ShapePath shapepath = Assert.IsType(processor.Path); Assert.NotEmpty(shapepath.Paths); - var rect = Assert.IsType(shapepath.Paths[0].AsShape()); + SixLabors.Shapes.Rectangle rect = Assert.IsType(shapepath.Paths[0].AsShape()); Assert.Equal(rect.Location.X, rectangle.X); Assert.Equal(rect.Location.Y, rectangle.Y); Assert.Equal(rect.Size.Width, rectangle.Width); Assert.Equal(rect.Size.Height, rectangle.Height); - var pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(thickness, pen.Width); - var brush = Assert.IsType>(pen.Brush); + SolidBrush brush = Assert.IsType>(pen.Brush); Assert.Equal(color, brush.Color); } [Fact] - public void color_Thickness_rectangle_options() + public void CorrectlySetsColorThicknessRectangleAndOptions() { img.Draw(color, thickness, rectangle, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var shapepath = Assert.IsType(processor.Path); + ShapePath shapepath = Assert.IsType(processor.Path); Assert.NotEmpty(shapepath.Paths); - var rect = Assert.IsType(shapepath.Paths[0].AsShape()); + SixLabors.Shapes.Rectangle rect = Assert.IsType(shapepath.Paths[0].AsShape()); Assert.Equal(rect.Location.X, rectangle.X); Assert.Equal(rect.Location.Y, rectangle.Y); Assert.Equal(rect.Size.Width, rectangle.Width); Assert.Equal(rect.Size.Height, rectangle.Height); - var pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(thickness, pen.Width); - var brush = Assert.IsType>(pen.Brush); + SolidBrush brush = Assert.IsType>(pen.Brush); Assert.Equal(color, brush.Color); } [Fact] - public void pen_rectangle() + public void CorrectlySetsPenAndRectangle() { img.Draw(pen, rectangle); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var shapepath = Assert.IsType(processor.Path); + ShapePath shapepath = Assert.IsType(processor.Path); Assert.NotEmpty(shapepath.Paths); - var rect = Assert.IsType(shapepath.Paths[0].AsShape()); + SixLabors.Shapes.Rectangle rect = Assert.IsType(shapepath.Paths[0].AsShape()); Assert.Equal(rect.Location.X, rectangle.X); Assert.Equal(rect.Location.Y, rectangle.Y); @@ -162,19 +162,19 @@ namespace ImageSharp.Tests.Drawing.Paths } [Fact] - public void pen_rectangle_options() + public void CorrectlySetsPenRectangleAndOptions() { img.Draw(pen, rectangle, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var shapepath = Assert.IsType(processor.Path); + ShapePath shapepath = Assert.IsType(processor.Path); Assert.NotEmpty(shapepath.Paths); - var rect = Assert.IsType(shapepath.Paths[0].AsShape()); + SixLabors.Shapes.Rectangle rect = Assert.IsType(shapepath.Paths[0].AsShape()); Assert.Equal(rect.Location.X, rectangle.X); Assert.Equal(rect.Location.Y, rectangle.Y); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawShape.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawShape.cs index 8572099db..30a6646f4 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawShape.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/DrawShape.cs @@ -40,96 +40,96 @@ namespace ImageSharp.Tests.Drawing.Paths } [Fact] - public void Brush_Thickness_shape() + public void CorrectlySetsBrushThicknessAndShape() { img.Draw(brush, thickness, shape); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var shapepath = Assert.IsType(processor.Path); + ShapePath shapepath = Assert.IsType(processor.Path); Assert.NotEmpty(shapepath.Paths); Assert.Equal(shape, shapepath.Paths[0].AsShape()); - var pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(brush, pen.Brush); Assert.Equal(thickness, pen.Width); } [Fact] - public void Brush_Thickness_shape_options() + public void CorrectlySetsBrushThicknessShapeAndOptions() { img.Draw(brush, thickness, shape, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var shapepath = Assert.IsType(processor.Path); + ShapePath shapepath = Assert.IsType(processor.Path); Assert.NotEmpty(shapepath.Paths); Assert.Equal(shape, shapepath.Paths[0].AsShape()); - var pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(brush, pen.Brush); Assert.Equal(thickness, pen.Width); } [Fact] - public void color_Thickness_shape() + public void CorrectlySetsColorThicknessAndShape() { img.Draw(color, thickness, shape); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var shapepath = Assert.IsType(processor.Path); + ShapePath shapepath = Assert.IsType(processor.Path); Assert.NotEmpty(shapepath.Paths); Assert.Equal(shape, shapepath.Paths[0].AsShape()); - var pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(thickness, pen.Width); - var brush = Assert.IsType>(pen.Brush); + SolidBrush brush = Assert.IsType>(pen.Brush); Assert.Equal(color, brush.Color); } [Fact] - public void color_Thickness_shape_options() + public void CorrectlySetsColorThicknessShapeAndOptions() { img.Draw(color, thickness, shape, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var shapepath = Assert.IsType(processor.Path); + ShapePath shapepath = Assert.IsType(processor.Path); Assert.NotEmpty(shapepath.Paths); Assert.Equal(shape, shapepath.Paths[0].AsShape()); - var pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(thickness, pen.Width); - var brush = Assert.IsType>(pen.Brush); + SolidBrush brush = Assert.IsType>(pen.Brush); Assert.Equal(color, brush.Color); } [Fact] - public void pen_shape() + public void CorrectlySetsPenAndShape() { img.Draw(pen, shape); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var shapepath = Assert.IsType(processor.Path); + ShapePath shapepath = Assert.IsType(processor.Path); Assert.NotEmpty(shapepath.Paths); Assert.Equal(shape, shapepath.Paths[0].AsShape()); @@ -137,16 +137,16 @@ namespace ImageSharp.Tests.Drawing.Paths } [Fact] - public void pen_path_options() + public void CorrectlySetsPenShapeAndOptions() { img.Draw(pen, shape, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var shapepath = Assert.IsType(processor.Path); + ShapePath shapepath = Assert.IsType(processor.Path); Assert.NotEmpty(shapepath.Paths); Assert.Equal(shape, shapepath.Paths[0].AsShape()); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs index 04a2a82ab..5ba6580bd 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs @@ -29,7 +29,7 @@ namespace ImageSharp.Tests.Drawing.Paths public FillPath() { - this.img = new Paths.ProcessorWatchingImage(10, 10); + this.img = new ProcessorWatchingImage(10, 10); } public void Dispose() @@ -38,75 +38,75 @@ namespace ImageSharp.Tests.Drawing.Paths } [Fact] - public void Brush_path() + public void CorrectlySetsBrushAndPath() { img.Fill(brush, path); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var region = Assert.IsType(processor.Region); - var polygon = Assert.IsType(region.Shape); - var segments = Assert.IsType(polygon.LineSegments[0]); + ShapeRegion region = Assert.IsType(processor.Region); + + // path is converted to a polygon before filling + Polygon polygon = Assert.IsType(region.Shape); + LinearLineSegment segments = Assert.IsType(polygon.LineSegments[0]); Assert.Equal(brush, processor.Brush); } [Fact] - public void Brush_path_options() + public void CorrectlySetsBrushPathOptions() { img.Fill(brush, path, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var region = Assert.IsType(processor.Region); - var polygon = Assert.IsType(region.Shape); - var segments = Assert.IsType(polygon.LineSegments[0]); + ShapeRegion region = Assert.IsType(processor.Region); + Polygon polygon = Assert.IsType(region.Shape); + LinearLineSegment segments = Assert.IsType(polygon.LineSegments[0]); Assert.Equal(brush, processor.Brush); } [Fact] - public void color_path() + public void CorrectlySetsColorAndPath() { img.Fill(color, path); - + Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var region = Assert.IsType(processor.Region); - var polygon = Assert.IsType(region.Shape); - var segments = Assert.IsType(polygon.LineSegments[0]); + ShapeRegion region = Assert.IsType(processor.Region); + Polygon polygon = Assert.IsType(region.Shape); + LinearLineSegment segments = Assert.IsType(polygon.LineSegments[0]); - var brush = Assert.IsType>(processor.Brush); + SolidBrush brush = Assert.IsType>(processor.Brush); Assert.Equal(color, brush.Color); } [Fact] - public void color_path_options() + public void CorrectlySetsColorPathAndOptions() { img.Fill(color, path, noneDefault); - Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var region = Assert.IsType(processor.Region); - var polygon = Assert.IsType(region.Shape); - var segments = Assert.IsType(polygon.LineSegments[0]); + ShapeRegion region = Assert.IsType(processor.Region); + Polygon polygon = Assert.IsType(region.Shape); + LinearLineSegment segments = Assert.IsType(polygon.LineSegments[0]); - var brush = Assert.IsType>(processor.Brush); + SolidBrush brush = Assert.IsType>(processor.Brush); Assert.Equal(color, brush.Color); - } } } diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs index 2c95eb209..ad72d4c4e 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs @@ -38,73 +38,72 @@ namespace ImageSharp.Tests.Drawing.Paths } [Fact] - public void Brush_path() + public void CorrectlySetsBrushAndPath() { img.FillPolygon(brush, path); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var region = Assert.IsType(processor.Region); - var polygon = Assert.IsType(region.Shape); - var segemnt = Assert.IsType(polygon.LineSegments[0]); + ShapeRegion region = Assert.IsType(processor.Region); + Polygon polygon = Assert.IsType(region.Shape); + LinearLineSegment segemnt = Assert.IsType(polygon.LineSegments[0]); Assert.Equal(brush, processor.Brush); } [Fact] - public void Brush_path_options() + public void CorrectlySetsBrushPathAndOptions() { img.FillPolygon(brush, path, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var region = Assert.IsType(processor.Region); - var polygon = Assert.IsType(region.Shape); - var segemnt = Assert.IsType(polygon.LineSegments[0]); + ShapeRegion region = Assert.IsType(processor.Region); + Polygon polygon = Assert.IsType(region.Shape); + LinearLineSegment segemnt = Assert.IsType(polygon.LineSegments[0]); Assert.Equal(brush, processor.Brush); } [Fact] - public void color_path() + public void CorrectlySetsColorAndPath() { img.FillPolygon(color, path); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var region = Assert.IsType(processor.Region); - var polygon = Assert.IsType(region.Shape); - var segemnt = Assert.IsType(polygon.LineSegments[0]); + ShapeRegion region = Assert.IsType(processor.Region); + Polygon polygon = Assert.IsType(region.Shape); + LinearLineSegment segemnt = Assert.IsType(polygon.LineSegments[0]); - var brush = Assert.IsType>(processor.Brush); + SolidBrush brush = Assert.IsType>(processor.Brush); Assert.Equal(color, brush.Color); } [Fact] - public void color_path_options() + public void CorrectlySetsColorPathAndOptions() { img.FillPolygon(color, path, noneDefault); - Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var region = Assert.IsType(processor.Region); - var polygon = Assert.IsType(region.Shape); - var segemnt = Assert.IsType(polygon.LineSegments[0]); + ShapeRegion region = Assert.IsType(processor.Region); + Polygon polygon = Assert.IsType(region.Shape); + LinearLineSegment segemnt = Assert.IsType(polygon.LineSegments[0]); - var brush = Assert.IsType>(processor.Brush); + SolidBrush brush = Assert.IsType>(processor.Brush); Assert.Equal(color, brush.Color); } } diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs index 520b107b9..f6b1c4ade 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs @@ -34,17 +34,17 @@ namespace ImageSharp.Tests.Drawing.Paths } [Fact] - public void Brush_path() + public void CorrectlySetsBrushAndRectangle() { img.Fill(brush, rectangle); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var region = Assert.IsType(processor.Region); - var rect = Assert.IsType(region.Shape); + ShapeRegion region = Assert.IsType(processor.Region); + SixLabors.Shapes.Rectangle rect = Assert.IsType(region.Shape); Assert.Equal(rect.Location.X, rectangle.X); Assert.Equal(rect.Location.Y, rectangle.Y); Assert.Equal(rect.Size.Width, rectangle.Width); @@ -54,17 +54,17 @@ namespace ImageSharp.Tests.Drawing.Paths } [Fact] - public void Brush_path_options() + public void CorrectlySetsBrushRectangleAndOptions() { img.Fill(brush, rectangle, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var region = Assert.IsType(processor.Region); - var rect = Assert.IsType(region.Shape); + ShapeRegion region = Assert.IsType(processor.Region); + SixLabors.Shapes.Rectangle rect = Assert.IsType(region.Shape); Assert.Equal(rect.Location.X, rectangle.X); Assert.Equal(rect.Location.Y, rectangle.Y); Assert.Equal(rect.Size.Width, rectangle.Width); @@ -74,45 +74,44 @@ namespace ImageSharp.Tests.Drawing.Paths } [Fact] - public void color_path() + public void CorrectlySetsColorAndRectangle() { img.Fill(color, rectangle); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var region = Assert.IsType(processor.Region); - var rect = Assert.IsType(region.Shape); + ShapeRegion region = Assert.IsType(processor.Region); + SixLabors.Shapes.Rectangle rect = Assert.IsType(region.Shape); Assert.Equal(rect.Location.X, rectangle.X); Assert.Equal(rect.Location.Y, rectangle.Y); Assert.Equal(rect.Size.Width, rectangle.Width); Assert.Equal(rect.Size.Height, rectangle.Height); - var brush = Assert.IsType>(processor.Brush); + SolidBrush brush = Assert.IsType>(processor.Brush); Assert.Equal(color, brush.Color); } [Fact] - public void color_path_options() + public void CorrectlySetsColorRectangleAndOptions() { img.Fill(color, rectangle, noneDefault); - Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var region = Assert.IsType(processor.Region); - var rect = Assert.IsType(region.Shape); + ShapeRegion region = Assert.IsType(processor.Region); + SixLabors.Shapes.Rectangle rect = Assert.IsType(region.Shape); Assert.Equal(rect.Location.X, rectangle.X); Assert.Equal(rect.Location.Y, rectangle.Y); Assert.Equal(rect.Size.Width, rectangle.Width); Assert.Equal(rect.Size.Height, rectangle.Height); - var brush = Assert.IsType>(processor.Brush); + SolidBrush brush = Assert.IsType>(processor.Brush); Assert.Equal(color, brush.Color); } } diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillShape.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillShape.cs index 26bf61b5c..140870a3c 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillShape.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillShape.cs @@ -19,7 +19,7 @@ namespace ImageSharp.Tests.Drawing.Paths GraphicsOptions noneDefault = new GraphicsOptions(); Color color = Color.HotPink; SolidBrush brush = Brushes.Solid(Color.HotPink); - IShape shape = new SixLabors.Shapes.Polygon(new LinearLineSegment(new Vector2[] { + IShape shape = new Polygon(new LinearLineSegment(new Vector2[] { new Vector2(10,10), new Vector2(20,10), new Vector2(20,10), @@ -38,69 +38,68 @@ namespace ImageSharp.Tests.Drawing.Paths } [Fact] - public void Brush_shape() + public void CorrectlySetsBrushAndShape() { img.Fill(brush, shape); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var region = Assert.IsType(processor.Region); + ShapeRegion region = Assert.IsType(processor.Region); Assert.Equal(shape, region.Shape); Assert.Equal(brush, processor.Brush); } [Fact] - public void Brush_shape_options() + public void CorrectlySetsBrushShapeAndOptions() { img.Fill(brush, shape, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var region = Assert.IsType(processor.Region); + ShapeRegion region = Assert.IsType(processor.Region); Assert.Equal(shape, region.Shape); Assert.Equal(brush, processor.Brush); } [Fact] - public void color_shape() + public void CorrectlySetsColorAndShape() { img.Fill(color, shape); Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); - var region = Assert.IsType(processor.Region); + ShapeRegion region = Assert.IsType(processor.Region); Assert.Equal(shape, region.Shape); - var brush = Assert.IsType>(processor.Brush); + SolidBrush brush = Assert.IsType>(processor.Brush); Assert.Equal(color, brush.Color); } [Fact] - public void color_shape_options() + public void CorrectlySetsColorShapeAndOptions() { img.Fill(color, shape, noneDefault); - Assert.NotEmpty(img.ProcessorApplications); - var processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); - var region = Assert.IsType(processor.Region); + ShapeRegion region = Assert.IsType(processor.Region); Assert.Equal(shape, region.Shape); - var brush = Assert.IsType>(processor.Brush); + SolidBrush brush = Assert.IsType>(processor.Brush); Assert.Equal(color, brush.Color); } diff --git a/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs b/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs index ad3d41bc6..3bb3b3e77 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs @@ -27,6 +27,10 @@ namespace ImageSharp.Tests.Drawing.Paths processor = processor, rectangle = rectangle }); + + // doesn't really apply the processor to the fake images as this is supposed + // to be just used to test which processor was finally applied and to interogate + // its settings } public struct ProcessorDetails diff --git a/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs index 24e921ccb..5fc967846 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs @@ -39,7 +39,7 @@ namespace ImageSharp.Tests.Drawing.Paths } [Fact] - public void ShapePathWithPath_CallsAsShape() + public void ShapePathWithPathCallsAsShape() { new ShapePath(pathMock1.Object); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs index e983dce66..675494951 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs @@ -35,7 +35,7 @@ namespace ImageSharp.Tests.Drawing.Paths } [Fact] - public void ShapeRegionWithPath_CallsAsShape() + public void ShapeRegionWithPathCallsAsShape() { new ShapeRegion(pathMock.Object); @@ -43,7 +43,7 @@ namespace ImageSharp.Tests.Drawing.Paths } [Fact] - public void ShapeRegionWithPath_RetainsShape() + public void ShapeRegionWithPathRetainsShape() { ShapeRegion region = new ShapeRegion(pathMock.Object); diff --git a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs index f63d90aa6..80713468d 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs @@ -30,7 +30,7 @@ namespace ImageSharp.Tests.Drawing { image .BackgroundColor(Color.Blue) - .Fill(Color.HotPink,new BezierPolygon(simplePath)) + .Fill(Color.HotPink, new Polygon(new BezierLineSegment(simplePath))) .Save(output); } @@ -71,7 +71,7 @@ namespace ImageSharp.Tests.Drawing { image .BackgroundColor(Color.Blue) - .Fill(color, new BezierPolygon(simplePath)) + .Fill(color, new Polygon(new BezierLineSegment(simplePath))) .Save(output); } diff --git a/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs index a1973a280..45ee59d66 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs @@ -19,15 +19,15 @@ namespace ImageSharp.Tests.Drawing public void ImageShouldBeOverlayedByPolygonOutline() { string path = this.CreateOutputDirectory("Drawing", "ComplexPolygon"); - LinearPolygon simplePath = new LinearPolygon( + Polygon simplePath = new Polygon(new LinearLineSegment( new Vector2(10, 10), new Vector2(200, 150), - new Vector2(50, 300)); + new Vector2(50, 300))); - LinearPolygon hole1 = new LinearPolygon( + Polygon hole1 = new Polygon(new LinearLineSegment( new Vector2(37, 85), new Vector2(93, 85), - new Vector2(65, 137)); + new Vector2(65, 137))); using (Image image = new Image(500, 500)) { @@ -62,15 +62,15 @@ namespace ImageSharp.Tests.Drawing public void ImageShouldBeOverlayedPolygonOutlineWithOverlap() { string path = this.CreateOutputDirectory("Drawing", "ComplexPolygon"); - LinearPolygon simplePath = new LinearPolygon( + Polygon simplePath = new Polygon(new LinearLineSegment( new Vector2(10, 10), new Vector2(200, 150), - new Vector2(50, 300)); + new Vector2(50, 300))); - LinearPolygon hole1 = new LinearPolygon( + Polygon hole1 = new Polygon(new LinearLineSegment( new Vector2(37, 85), new Vector2(130, 40), - new Vector2(65, 137)); + new Vector2(65, 137))); using (Image image = new Image(500, 500)) { @@ -104,15 +104,15 @@ namespace ImageSharp.Tests.Drawing public void ImageShouldBeOverlayedPolygonOutlineWithOpacity() { string path = this.CreateOutputDirectory("Drawing", "ComplexPolygon"); - LinearPolygon simplePath = new LinearPolygon( + Polygon simplePath = new Polygon(new LinearLineSegment( new Vector2(10, 10), new Vector2(200, 150), - new Vector2(50, 300)); + new Vector2(50, 300))); - LinearPolygon hole1 = new LinearPolygon( + Polygon hole1 = new Polygon(new LinearLineSegment( new Vector2(37, 85), new Vector2(93, 85), - new Vector2(65, 137)); + new Vector2(65, 137))); Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); using (Image image = new Image(500, 500)) From 7cc3ecee46b56387e8a0ebaad66c764b2dd1c16f Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Fri, 3 Feb 2017 09:30:16 +0000 Subject: [PATCH 022/142] some cleanup --- src/ImageSharp.Drawing.Paths/ShapePath.cs | 18 +++++++++--------- src/ImageSharp.Drawing.Paths/ShapeRegion.cs | 12 ++++++------ src/ImageSharp.Drawing/DrawPath.cs | 12 ++++++------ .../{Path.cs => Drawable.cs} | 6 +++--- .../Processors/DrawPathProcessor.cs | 4 ++-- .../ImageSharp.Tests/Drawing/DrawPathTests.cs | 6 +++--- .../Drawing/LineComplexPolygonTests.cs | 13 ++++++------- tests/ImageSharp.Tests/Drawing/PolygonTests.cs | 2 +- .../Drawing/SolidComplexPolygonTests.cs | 8 ++++---- .../Drawing/SolidPolygonTests.cs | 6 +++--- 10 files changed, 43 insertions(+), 44 deletions(-) rename src/ImageSharp.Drawing/{Path.cs => Drawable.cs} (91%) diff --git a/src/ImageSharp.Drawing.Paths/ShapePath.cs b/src/ImageSharp.Drawing.Paths/ShapePath.cs index cd994515e..ee1f6e9d9 100644 --- a/src/ImageSharp.Drawing.Paths/ShapePath.cs +++ b/src/ImageSharp.Drawing.Paths/ShapePath.cs @@ -18,7 +18,7 @@ namespace ImageSharp.Drawing /// /// A drawable mapping between a / and a drawable/fillable region. /// - internal class ShapePath : ImageSharp.Drawing.Path + internal class ShapePath : ImageSharp.Drawing.Drawable { /// /// The fillable shape @@ -92,8 +92,8 @@ namespace ImageSharp.Drawing /// public override int ScanX(int x, float[] buffer, int length, int offset) { - var start = new Vector2(x, this.Bounds.Top - 1); - var end = new Vector2(x, this.Bounds.Bottom + 1); + Vector2 start = new Vector2(x, this.Bounds.Top - 1); + Vector2 end = new Vector2(x, this.Bounds.Bottom + 1); Vector2[] innerbuffer = ArrayPool.Shared.Rent(length); try { @@ -104,7 +104,7 @@ namespace ImageSharp.Drawing length, 0); - for (var i = 0; i < count; i++) + for (int i = 0; i < count; i++) { buffer[i + offset] = innerbuffer[i].Y; } @@ -129,8 +129,8 @@ namespace ImageSharp.Drawing /// public override int ScanY(int y, float[] buffer, int length, int offset) { - var start = new Vector2(float.MinValue, y); - var end = new Vector2(float.MaxValue, y); + Vector2 start = new Vector2(float.MinValue, y); + Vector2 end = new Vector2(float.MaxValue, y); Vector2[] innerbuffer = ArrayPool.Shared.Rent(length); try { @@ -141,7 +141,7 @@ namespace ImageSharp.Drawing length, 0); - for (var i = 0; i < count; i++) + for (int i = 0; i < count; i++) { buffer[i + offset] = innerbuffer[i].X; } @@ -162,13 +162,13 @@ namespace ImageSharp.Drawing /// Information about the the point public override PointInfo GetPointInfo(int x, int y) { - var point = new Vector2(x, y); + Vector2 point = new Vector2(x, y); SixLabors.Shapes.PointInfo result = default(SixLabors.Shapes.PointInfo); float distance = float.MaxValue; for (int i = 0; i < this.Paths.Length; i++) { - var p = this.Paths[i].Distance(point); + SixLabors.Shapes.PointInfo p = this.Paths[i].Distance(point); if (p.DistanceFromPath < distance) { distance = p.DistanceFromPath; diff --git a/src/ImageSharp.Drawing.Paths/ShapeRegion.cs b/src/ImageSharp.Drawing.Paths/ShapeRegion.cs index b43ad26b4..b6921f16e 100644 --- a/src/ImageSharp.Drawing.Paths/ShapeRegion.cs +++ b/src/ImageSharp.Drawing.Paths/ShapeRegion.cs @@ -72,8 +72,8 @@ namespace ImageSharp.Drawing /// public override int ScanX(int x, float[] buffer, int length, int offset) { - var start = new Vector2(x, this.Bounds.Top - 1); - var end = new Vector2(x, this.Bounds.Bottom + 1); + Vector2 start = new Vector2(x, this.Bounds.Top - 1); + Vector2 end = new Vector2(x, this.Bounds.Bottom + 1); Vector2[] innerbuffer = ArrayPool.Shared.Rent(length); try { @@ -84,7 +84,7 @@ namespace ImageSharp.Drawing length, 0); - for (var i = 0; i < count; i++) + for (int i = 0; i < count; i++) { buffer[i + offset] = innerbuffer[i].Y; } @@ -109,8 +109,8 @@ namespace ImageSharp.Drawing /// public override int ScanY(int y, float[] buffer, int length, int offset) { - var start = new Vector2(float.MinValue, y); - var end = new Vector2(float.MaxValue, y); + Vector2 start = new Vector2(float.MinValue, y); + Vector2 end = new Vector2(float.MaxValue, y); Vector2[] innerbuffer = ArrayPool.Shared.Rent(length); try { @@ -121,7 +121,7 @@ namespace ImageSharp.Drawing length, 0); - for (var i = 0; i < count; i++) + for (int i = 0; i < count; i++) { buffer[i + offset] = innerbuffer[i].X; } diff --git a/src/ImageSharp.Drawing/DrawPath.cs b/src/ImageSharp.Drawing/DrawPath.cs index 828d50fb3..fe833e3af 100644 --- a/src/ImageSharp.Drawing/DrawPath.cs +++ b/src/ImageSharp.Drawing/DrawPath.cs @@ -28,7 +28,7 @@ namespace ImageSharp /// /// The Image /// - public static Image Draw(this Image source, IPen pen, Path path, GraphicsOptions options) + public static Image Draw(this Image source, IPen pen, Drawable path, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { return source.Apply(new DrawPathProcessor(pen, path, options)); @@ -44,7 +44,7 @@ namespace ImageSharp /// /// The Image /// - public static Image Draw(this Image source, IPen pen, Path path) + public static Image Draw(this Image source, IPen pen, Drawable path) where TColor : struct, IPackedPixel, IEquatable { return source.Draw(pen, path, GraphicsOptions.Default); @@ -62,7 +62,7 @@ namespace ImageSharp /// /// The Image /// - public static Image Draw(this Image source, IBrush brush, float thickness, Path path, GraphicsOptions options) + public static Image Draw(this Image source, IBrush brush, float thickness, Drawable path, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { return source.Draw(new Pen(brush, thickness), path, options); @@ -79,7 +79,7 @@ namespace ImageSharp /// /// The Image /// - public static Image Draw(this Image source, IBrush brush, float thickness, Path path) + public static Image Draw(this Image source, IBrush brush, float thickness, Drawable path) where TColor : struct, IPackedPixel, IEquatable { return source.Draw(new Pen(brush, thickness), path); @@ -97,7 +97,7 @@ namespace ImageSharp /// /// The Image /// - public static Image Draw(this Image source, TColor color, float thickness, Path path, GraphicsOptions options) + public static Image Draw(this Image source, TColor color, float thickness, Drawable path, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { return source.Draw(new SolidBrush(color), thickness, path, options); @@ -114,7 +114,7 @@ namespace ImageSharp /// /// The Image /// - public static Image Draw(this Image source, TColor color, float thickness, Path path) + public static Image Draw(this Image source, TColor color, float thickness, Drawable path) where TColor : struct, IPackedPixel, IEquatable { return source.Draw(new SolidBrush(color), thickness, path); diff --git a/src/ImageSharp.Drawing/Path.cs b/src/ImageSharp.Drawing/Drawable.cs similarity index 91% rename from src/ImageSharp.Drawing/Path.cs rename to src/ImageSharp.Drawing/Drawable.cs index a997fa18f..b81827331 100644 --- a/src/ImageSharp.Drawing/Path.cs +++ b/src/ImageSharp.Drawing/Drawable.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -6,9 +6,9 @@ namespace ImageSharp.Drawing { /// - /// Represents a something that has knowledge about its outline. + /// Represents a path or set of paths that can be drawn as an outline. /// - public abstract class Path + public abstract class Drawable { /// /// Gets the maximum number of intersections to could be returned. diff --git a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs index 3fe557080..53f0408d4 100644 --- a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs @@ -31,7 +31,7 @@ namespace ImageSharp.Drawing.Processors /// The pen. /// The region. /// The options. - public DrawPathProcessor(IPen pen, Path region, GraphicsOptions options) + public DrawPathProcessor(IPen pen, Drawable region, GraphicsOptions options) { this.Path = region; this.Pen = pen; @@ -60,7 +60,7 @@ namespace ImageSharp.Drawing.Processors /// /// The path. /// - public Path Path { get; } + public Drawable Path { get; } /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) diff --git a/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs b/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs index f4465027d..fc231a89d 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs @@ -33,13 +33,13 @@ namespace ImageSharp.Tests.Drawing new Vector2(60, 10), new Vector2(10, 400)); - ShapePath p = new ShapePath(linerSegemnt, bazierSegment); + ShapePath p = new ShapePath(linerSegemnt, bazierSegment); using (FileStream output = File.OpenWrite($"{path}/Simple.png")) { image .BackgroundColor(Color.Blue) - .Draw(Color.HotPink, 5, p) + .Draw(Color.HotPink, 5, p) .Save(output); } @@ -82,7 +82,7 @@ namespace ImageSharp.Tests.Drawing { image .BackgroundColor(Color.Blue) - .Draw(color, 10, p) + .Draw(color, 10, p) .Save(output); } diff --git a/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs index 0a4f2c4a0..6153cb310 100644 --- a/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs @@ -37,8 +37,8 @@ namespace ImageSharp.Tests.Drawing { image .BackgroundColor(Color.Blue) - .Draw(Color.HotPink, 5, simplePath.Clip(hole1)) - .Save(output); + .Draw(Color.HotPink, 5, simplePath.Clip(hole1)) + .Save(output); } using (PixelAccessor sourcePixels = image.Lock()) @@ -87,7 +87,7 @@ namespace ImageSharp.Tests.Drawing { image .BackgroundColor(Color.Blue) - .Draw(Color.HotPink, 5, simplePath.Clip(hole1)) + .Draw(Color.HotPink, 5, simplePath.Clip(hole1)) .Save(output); } @@ -131,7 +131,6 @@ namespace ImageSharp.Tests.Drawing new Vector2(37, 85), new Vector2(130, 40), new Vector2(65, 137))); - using (Image image = new Image(500, 500)) { @@ -139,7 +138,7 @@ namespace ImageSharp.Tests.Drawing { image .BackgroundColor(Color.Blue) - .Draw(Color.HotPink, 5, simplePath.Clip(hole1)) + .Draw(Color.HotPink, 5, simplePath.Clip(hole1)) .Save(output); } @@ -185,7 +184,7 @@ namespace ImageSharp.Tests.Drawing { image .BackgroundColor(Color.Blue) - .Draw(Pens.Dash(Color.HotPink, 5), simplePath.Clip(hole1)) + .Draw(Pens.Dash(Color.HotPink, 5), simplePath.Clip(hole1)) .Save(output); } } @@ -213,7 +212,7 @@ namespace ImageSharp.Tests.Drawing { image .BackgroundColor(Color.Blue) - .Draw(color, 5, simplePath.Clip(hole1)) + .Draw(color, 5, simplePath.Clip(hole1)) .Save(output); } diff --git a/tests/ImageSharp.Tests/Drawing/PolygonTests.cs b/tests/ImageSharp.Tests/Drawing/PolygonTests.cs index a166464c9..3e06ca918 100644 --- a/tests/ImageSharp.Tests/Drawing/PolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/PolygonTests.cs @@ -97,7 +97,7 @@ namespace ImageSharp.Tests.Drawing { image .BackgroundColor(Color.Blue) - .Draw(Color.HotPink, 10, new Rectangle(10, 10, 190, 140)) + .Draw(Color.HotPink, 10, new Rectangle(10, 10, 190, 140)) .Save(output); } diff --git a/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs index 45ee59d66..98ec9ff83 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs @@ -35,7 +35,7 @@ namespace ImageSharp.Tests.Drawing { image .BackgroundColor(Color.Blue) - .Fill(Color.HotPink, simplePath.Clip(hole1)) + .Fill(Color.HotPink, simplePath.Clip(hole1)) .Save(output); } @@ -78,7 +78,7 @@ namespace ImageSharp.Tests.Drawing { image .BackgroundColor(Color.Blue) - .Fill(Color.HotPink, simplePath.Clip(hole1)) + .Fill(Color.HotPink, simplePath.Clip(hole1)) .Save(output); } @@ -121,8 +121,8 @@ namespace ImageSharp.Tests.Drawing { image .BackgroundColor(Color.Blue) - .Fill(color, simplePath.Clip(hole1)) - .Save(output); + .Fill(color, simplePath.Clip(hole1)) + .Save(output); } //shift background color towards forground color by the opacity amount diff --git a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs index d5b206723..5533fbc0a 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs @@ -71,7 +71,7 @@ namespace ImageSharp.Tests.Drawing { Assert.Equal(Color.HotPink, sourcePixels[11, 11]); - Assert.Equal(Color.HotPink, sourcePixels[199, 150]); + Assert.Equal(Color.HotPink, sourcePixels[199, 150]); Assert.Equal(Color.HotPink, sourcePixels[50, 50]); @@ -144,14 +144,14 @@ namespace ImageSharp.Tests.Drawing public void ImageShouldBeOverlayedByFilledRectangle() { string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); - + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/Rectangle.png")) { image .BackgroundColor(Color.Blue) - .Fill(Color.HotPink, new SixLabors.Shapes.Rectangle(10,10, 190, 140)) + .Fill(Color.HotPink, new SixLabors.Shapes.Rectangle(10,10, 190, 140)) .Save(output); } From 7f9a32b80ac50b51147d6832888d4b38e7c0f770 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Fri, 3 Feb 2017 09:51:54 +0000 Subject: [PATCH 023/142] removed extension method --- src/ImageSharp.Drawing.Paths/DrawLines.cs | 2 -- src/ImageSharp.Drawing/DrawImage.cs | 3 ++- .../Binarization/BinaryThreshold.cs | 3 ++- .../ColorMatrix/BlackWhite.cs | 3 ++- .../ColorMatrix/ColorBlindness.cs | 3 ++- .../ColorMatrix/Grayscale.cs | 3 ++- src/ImageSharp.Processing/ColorMatrix/Hue.cs | 3 ++- .../ColorMatrix/Kodachrome.cs | 3 ++- .../ColorMatrix/Lomograph.cs | 3 ++- .../ColorMatrix/Polaroid.cs | 3 ++- .../ColorMatrix/Saturation.cs | 3 ++- .../ColorMatrix/Sepia.cs | 3 ++- .../Convolution/BoxBlur.cs | 3 ++- .../Convolution/DetectEdges.cs | 3 ++- .../Convolution/GaussianBlur.cs | 3 ++- .../Convolution/GaussianSharpen.cs | 3 ++- src/ImageSharp.Processing/Effects/Alpha.cs | 3 ++- .../Effects/BackgroundColor.cs | 3 ++- .../Effects/Brightness.cs | 5 +++-- src/ImageSharp.Processing/Effects/Contrast.cs | 3 ++- src/ImageSharp.Processing/Effects/Invert.cs | 3 ++- .../Effects/OilPainting.cs | 3 ++- src/ImageSharp.Processing/Effects/Pixelate.cs | 3 ++- src/ImageSharp.Processing/Overlays/Glow.cs | 3 ++- .../Overlays/Vignette.cs | 3 ++- src/ImageSharp.Processing/Transforms/Crop.cs | 4 +++- .../Transforms/EntropyCrop.cs | 4 +++- src/ImageSharp.Processing/Transforms/Flip.cs | 4 +++- .../Transforms/Resize.cs | 3 ++- .../Transforms/Rotate.cs | 4 +++- src/ImageSharp.Processing/Transforms/Skew.cs | 4 +++- .../Image/ImageProcessingExtensions.cs | 19 +------------------ 32 files changed, 67 insertions(+), 51 deletions(-) diff --git a/src/ImageSharp.Drawing.Paths/DrawLines.cs b/src/ImageSharp.Drawing.Paths/DrawLines.cs index 2e4f84987..1ff58fd1d 100644 --- a/src/ImageSharp.Drawing.Paths/DrawLines.cs +++ b/src/ImageSharp.Drawing.Paths/DrawLines.cs @@ -13,8 +13,6 @@ namespace ImageSharp using Drawing.Processors; using SixLabors.Shapes; - using Path = SixLabors.Shapes.Path; - /// /// Extension methods for the type. /// diff --git a/src/ImageSharp.Drawing/DrawImage.cs b/src/ImageSharp.Drawing/DrawImage.cs index 4b3fd491d..2fba227ee 100644 --- a/src/ImageSharp.Drawing/DrawImage.cs +++ b/src/ImageSharp.Drawing/DrawImage.cs @@ -51,7 +51,8 @@ namespace ImageSharp location = Point.Empty; } - return source.Apply(source.Bounds, new DrawImageProcessor(image, size, location, percent)); + source.ApplyProcessor(new DrawImageProcessor(image, size, location, percent), source.Bounds); + return source; } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Binarization/BinaryThreshold.cs b/src/ImageSharp.Processing/Binarization/BinaryThreshold.cs index 1b5b6c9bb..e59369349 100644 --- a/src/ImageSharp.Processing/Binarization/BinaryThreshold.cs +++ b/src/ImageSharp.Processing/Binarization/BinaryThreshold.cs @@ -40,7 +40,8 @@ namespace ImageSharp public static Image BinaryThreshold(this Image source, float threshold, Rectangle rectangle) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(rectangle, new BinaryThresholdProcessor(threshold)); + source.ApplyProcessor(new BinaryThresholdProcessor(threshold), rectangle); + return source; } } } diff --git a/src/ImageSharp.Processing/ColorMatrix/BlackWhite.cs b/src/ImageSharp.Processing/ColorMatrix/BlackWhite.cs index e172a21be..05fb8f19c 100644 --- a/src/ImageSharp.Processing/ColorMatrix/BlackWhite.cs +++ b/src/ImageSharp.Processing/ColorMatrix/BlackWhite.cs @@ -39,7 +39,8 @@ namespace ImageSharp public static Image BlackWhite(this Image source, Rectangle rectangle) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(rectangle, new BlackWhiteProcessor()); + source.ApplyProcessor(new BlackWhiteProcessor(), rectangle); + return source; } } } diff --git a/src/ImageSharp.Processing/ColorMatrix/ColorBlindness.cs b/src/ImageSharp.Processing/ColorMatrix/ColorBlindness.cs index 2e90b059e..30b28861d 100644 --- a/src/ImageSharp.Processing/ColorMatrix/ColorBlindness.cs +++ b/src/ImageSharp.Processing/ColorMatrix/ColorBlindness.cs @@ -78,7 +78,8 @@ namespace ImageSharp break; } - return source.Apply(rectangle, processor); + source.ApplyProcessor(processor, rectangle); + return source; } } } diff --git a/src/ImageSharp.Processing/ColorMatrix/Grayscale.cs b/src/ImageSharp.Processing/ColorMatrix/Grayscale.cs index f1a17c02b..b2aadc46d 100644 --- a/src/ImageSharp.Processing/ColorMatrix/Grayscale.cs +++ b/src/ImageSharp.Processing/ColorMatrix/Grayscale.cs @@ -45,7 +45,8 @@ namespace ImageSharp ? (IImageProcessor)new GrayscaleBt709Processor() : new GrayscaleBt601Processor(); - return source.Apply(rectangle, processor); + source.ApplyProcessor(processor, rectangle); + return source; } } } diff --git a/src/ImageSharp.Processing/ColorMatrix/Hue.cs b/src/ImageSharp.Processing/ColorMatrix/Hue.cs index f03f65692..25fcc6c55 100644 --- a/src/ImageSharp.Processing/ColorMatrix/Hue.cs +++ b/src/ImageSharp.Processing/ColorMatrix/Hue.cs @@ -41,7 +41,8 @@ namespace ImageSharp public static Image Hue(this Image source, float degrees, Rectangle rectangle) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(rectangle, new HueProcessor(degrees)); + source.ApplyProcessor(new HueProcessor(degrees), rectangle); + return source; } } } diff --git a/src/ImageSharp.Processing/ColorMatrix/Kodachrome.cs b/src/ImageSharp.Processing/ColorMatrix/Kodachrome.cs index 2592d8090..dab0224c3 100644 --- a/src/ImageSharp.Processing/ColorMatrix/Kodachrome.cs +++ b/src/ImageSharp.Processing/ColorMatrix/Kodachrome.cs @@ -39,7 +39,8 @@ namespace ImageSharp public static Image Kodachrome(this Image source, Rectangle rectangle) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(rectangle, new KodachromeProcessor()); + source.ApplyProcessor(new KodachromeProcessor(), rectangle); + return source; } } } diff --git a/src/ImageSharp.Processing/ColorMatrix/Lomograph.cs b/src/ImageSharp.Processing/ColorMatrix/Lomograph.cs index 2605bc301..df34dc52e 100644 --- a/src/ImageSharp.Processing/ColorMatrix/Lomograph.cs +++ b/src/ImageSharp.Processing/ColorMatrix/Lomograph.cs @@ -39,7 +39,8 @@ namespace ImageSharp public static Image Lomograph(this Image source, Rectangle rectangle) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(rectangle, new LomographProcessor()); + source.ApplyProcessor(new LomographProcessor(), rectangle); + return source; } } } diff --git a/src/ImageSharp.Processing/ColorMatrix/Polaroid.cs b/src/ImageSharp.Processing/ColorMatrix/Polaroid.cs index 5c51a710b..4bb8f82a3 100644 --- a/src/ImageSharp.Processing/ColorMatrix/Polaroid.cs +++ b/src/ImageSharp.Processing/ColorMatrix/Polaroid.cs @@ -39,7 +39,8 @@ namespace ImageSharp public static Image Polaroid(this Image source, Rectangle rectangle) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(rectangle, new PolaroidProcessor()); + source.ApplyProcessor(new PolaroidProcessor(), rectangle); + return source; } } } diff --git a/src/ImageSharp.Processing/ColorMatrix/Saturation.cs b/src/ImageSharp.Processing/ColorMatrix/Saturation.cs index 773329ea6..a92483c9c 100644 --- a/src/ImageSharp.Processing/ColorMatrix/Saturation.cs +++ b/src/ImageSharp.Processing/ColorMatrix/Saturation.cs @@ -41,7 +41,8 @@ namespace ImageSharp public static Image Saturation(this Image source, int amount, Rectangle rectangle) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(rectangle, new SaturationProcessor(amount)); + source.ApplyProcessor(new SaturationProcessor(amount), rectangle); + return source; } } } diff --git a/src/ImageSharp.Processing/ColorMatrix/Sepia.cs b/src/ImageSharp.Processing/ColorMatrix/Sepia.cs index 3f29b93e5..1a8ec4b95 100644 --- a/src/ImageSharp.Processing/ColorMatrix/Sepia.cs +++ b/src/ImageSharp.Processing/ColorMatrix/Sepia.cs @@ -39,7 +39,8 @@ namespace ImageSharp public static Image Sepia(this Image source, Rectangle rectangle) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(rectangle, new SepiaProcessor()); + source.ApplyProcessor(new SepiaProcessor(), rectangle); + return source; } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Convolution/BoxBlur.cs b/src/ImageSharp.Processing/Convolution/BoxBlur.cs index e16c30516..a68c2fa44 100644 --- a/src/ImageSharp.Processing/Convolution/BoxBlur.cs +++ b/src/ImageSharp.Processing/Convolution/BoxBlur.cs @@ -41,7 +41,8 @@ namespace ImageSharp public static Image BoxBlur(this Image source, int radius, Rectangle rectangle) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(rectangle, new BoxBlurProcessor(radius)); + source.ApplyProcessor(new BoxBlurProcessor(), rectangle); + return source; } } } diff --git a/src/ImageSharp.Processing/Convolution/DetectEdges.cs b/src/ImageSharp.Processing/Convolution/DetectEdges.cs index 32fc167f1..a61726e74 100644 --- a/src/ImageSharp.Processing/Convolution/DetectEdges.cs +++ b/src/ImageSharp.Processing/Convolution/DetectEdges.cs @@ -146,7 +146,8 @@ namespace ImageSharp public static Image DetectEdges(this Image source, Rectangle rectangle, IEdgeDetectorProcessor filter) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(rectangle, filter); + source.ApplyProcessor(filter, rectangle); + return source; } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Convolution/GaussianBlur.cs b/src/ImageSharp.Processing/Convolution/GaussianBlur.cs index 7e8b9a403..893ebb264 100644 --- a/src/ImageSharp.Processing/Convolution/GaussianBlur.cs +++ b/src/ImageSharp.Processing/Convolution/GaussianBlur.cs @@ -41,7 +41,8 @@ namespace ImageSharp public static Image GaussianBlur(this Image source, float sigma, Rectangle rectangle) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(rectangle, new GaussianBlurProcessor(sigma)); + source.ApplyProcessor(new GaussianBlurProcessor(sigma), rectangle); + return source; } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Convolution/GaussianSharpen.cs b/src/ImageSharp.Processing/Convolution/GaussianSharpen.cs index ef4910459..e3f8e995b 100644 --- a/src/ImageSharp.Processing/Convolution/GaussianSharpen.cs +++ b/src/ImageSharp.Processing/Convolution/GaussianSharpen.cs @@ -41,7 +41,8 @@ namespace ImageSharp public static Image GaussianSharpen(this Image source, float sigma, Rectangle rectangle) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(rectangle, new GaussianSharpenProcessor(sigma)); + source.ApplyProcessor(new GaussianSharpenProcessor(sigma), rectangle); + return source; } } } diff --git a/src/ImageSharp.Processing/Effects/Alpha.cs b/src/ImageSharp.Processing/Effects/Alpha.cs index 856276a89..21b6dfbcb 100644 --- a/src/ImageSharp.Processing/Effects/Alpha.cs +++ b/src/ImageSharp.Processing/Effects/Alpha.cs @@ -40,7 +40,8 @@ namespace ImageSharp public static Image Alpha(this Image source, int percent, Rectangle rectangle) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(rectangle, new AlphaProcessor(percent)); + source.ApplyProcessor(new AlphaProcessor(percent), rectangle); + return source; } } } diff --git a/src/ImageSharp.Processing/Effects/BackgroundColor.cs b/src/ImageSharp.Processing/Effects/BackgroundColor.cs index ac1add351..0f724c08c 100644 --- a/src/ImageSharp.Processing/Effects/BackgroundColor.cs +++ b/src/ImageSharp.Processing/Effects/BackgroundColor.cs @@ -24,7 +24,8 @@ namespace ImageSharp public static Image BackgroundColor(this Image source, TColor color) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(source.Bounds, new BackgroundColorProcessor(color)); + source.ApplyProcessor(new BackgroundColorProcessor(color), source.Bounds); + return source; } } } diff --git a/src/ImageSharp.Processing/Effects/Brightness.cs b/src/ImageSharp.Processing/Effects/Brightness.cs index 8c9ff8946..bf5500faf 100644 --- a/src/ImageSharp.Processing/Effects/Brightness.cs +++ b/src/ImageSharp.Processing/Effects/Brightness.cs @@ -39,8 +39,9 @@ namespace ImageSharp /// The . public static Image Brightness(this Image source, int amount, Rectangle rectangle) where TColor : struct, IPackedPixel, IEquatable - { - return source.Apply(rectangle, new BrightnessProcessor(amount)); + { + source.ApplyProcessor(new BrightnessProcessor(amount), rectangle); + return source; } } } diff --git a/src/ImageSharp.Processing/Effects/Contrast.cs b/src/ImageSharp.Processing/Effects/Contrast.cs index 831028682..f05eea639 100644 --- a/src/ImageSharp.Processing/Effects/Contrast.cs +++ b/src/ImageSharp.Processing/Effects/Contrast.cs @@ -40,7 +40,8 @@ namespace ImageSharp public static Image Contrast(this Image source, int amount, Rectangle rectangle) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(rectangle, new ContrastProcessor(amount)); + source.ApplyProcessor(new ContrastProcessor(amount), rectangle); + return source; } } } diff --git a/src/ImageSharp.Processing/Effects/Invert.cs b/src/ImageSharp.Processing/Effects/Invert.cs index 31e524000..fff3108df 100644 --- a/src/ImageSharp.Processing/Effects/Invert.cs +++ b/src/ImageSharp.Processing/Effects/Invert.cs @@ -38,7 +38,8 @@ namespace ImageSharp public static Image Invert(this Image source, Rectangle rectangle) where TColor : struct, IPackedPixel, IEquatable { - return source.Apply(rectangle, new InvertProcessor()); + source.ApplyProcessor(new InvertProcessor(), rectangle); + return source; } } } diff --git a/src/ImageSharp.Processing/Effects/OilPainting.cs b/src/ImageSharp.Processing/Effects/OilPainting.cs index 463cfd675..fbd777711 100644 --- a/src/ImageSharp.Processing/Effects/OilPainting.cs +++ b/src/ImageSharp.Processing/Effects/OilPainting.cs @@ -49,7 +49,8 @@ namespace ImageSharp throw new ArgumentOutOfRangeException(nameof(brushSize)); } - return source.Apply(rectangle, new OilPaintingProcessor(levels, brushSize)); + source.ApplyProcessor(new OilPaintingProcessor(levels, brushSize), rectangle); + return source; } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Effects/Pixelate.cs b/src/ImageSharp.Processing/Effects/Pixelate.cs index 92d1fdd69..77b014e73 100644 --- a/src/ImageSharp.Processing/Effects/Pixelate.cs +++ b/src/ImageSharp.Processing/Effects/Pixelate.cs @@ -45,7 +45,8 @@ namespace ImageSharp throw new ArgumentOutOfRangeException(nameof(size)); } - return source.Apply(rectangle, new PixelateProcessor(size)); + source.ApplyProcessor(new PixelateProcessor(size), rectangle); + return source; } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Overlays/Glow.cs b/src/ImageSharp.Processing/Overlays/Glow.cs index 6511407da..91fbfc7f6 100644 --- a/src/ImageSharp.Processing/Overlays/Glow.cs +++ b/src/ImageSharp.Processing/Overlays/Glow.cs @@ -88,7 +88,8 @@ namespace ImageSharp processor.GlowColor = color; } - return source.Apply(rectangle, processor); + source.ApplyProcessor(processor, rectangle); + return source; } } } diff --git a/src/ImageSharp.Processing/Overlays/Vignette.cs b/src/ImageSharp.Processing/Overlays/Vignette.cs index f728a3e1c..c6cd0ab1e 100644 --- a/src/ImageSharp.Processing/Overlays/Vignette.cs +++ b/src/ImageSharp.Processing/Overlays/Vignette.cs @@ -90,7 +90,8 @@ namespace ImageSharp processor.VignetteColor = color; } - return source.Apply(rectangle, processor); + source.ApplyProcessor(processor, rectangle); + return source; } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Transforms/Crop.cs b/src/ImageSharp.Processing/Transforms/Crop.cs index 09309a805..bdcbe51d8 100644 --- a/src/ImageSharp.Processing/Transforms/Crop.cs +++ b/src/ImageSharp.Processing/Transforms/Crop.cs @@ -41,7 +41,9 @@ namespace ImageSharp where TColor : struct, IPackedPixel, IEquatable { CropProcessor processor = new CropProcessor(cropRectangle); - return source.Apply(source.Bounds, processor); + + source.ApplyProcessor(processor, source.Bounds); + return source; } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Transforms/EntropyCrop.cs b/src/ImageSharp.Processing/Transforms/EntropyCrop.cs index 8ba6baf19..fcfcf3813 100644 --- a/src/ImageSharp.Processing/Transforms/EntropyCrop.cs +++ b/src/ImageSharp.Processing/Transforms/EntropyCrop.cs @@ -25,7 +25,9 @@ namespace ImageSharp where TColor : struct, IPackedPixel, IEquatable { EntropyCropProcessor processor = new EntropyCropProcessor(threshold); - return source.Apply(source.Bounds, processor); + + source.ApplyProcessor(processor, source.Bounds); + return source; } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Transforms/Flip.cs b/src/ImageSharp.Processing/Transforms/Flip.cs index 4b4c1b7d6..82b664eb1 100644 --- a/src/ImageSharp.Processing/Transforms/Flip.cs +++ b/src/ImageSharp.Processing/Transforms/Flip.cs @@ -26,7 +26,9 @@ namespace ImageSharp where TColor : struct, IPackedPixel, IEquatable { FlipProcessor processor = new FlipProcessor(flipType); - return source.Apply(source.Bounds, processor); + + source.ApplyProcessor(processor, source.Bounds); + return source; } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Transforms/Resize.cs b/src/ImageSharp.Processing/Transforms/Resize.cs index 35ffc8b7d..aa4bf2439 100644 --- a/src/ImageSharp.Processing/Transforms/Resize.cs +++ b/src/ImageSharp.Processing/Transforms/Resize.cs @@ -167,7 +167,8 @@ namespace ImageSharp processor = new ResizeProcessor(sampler, width, height, targetRectangle); } - return source.Apply(sourceRectangle, processor); + source.ApplyProcessor(processor, sourceRectangle); + return source; } } } diff --git a/src/ImageSharp.Processing/Transforms/Rotate.cs b/src/ImageSharp.Processing/Transforms/Rotate.cs index b35bbc58a..e9ed4e97c 100644 --- a/src/ImageSharp.Processing/Transforms/Rotate.cs +++ b/src/ImageSharp.Processing/Transforms/Rotate.cs @@ -53,7 +53,9 @@ namespace ImageSharp where TColor : struct, IPackedPixel, IEquatable { RotateProcessor processor = new RotateProcessor { Angle = degrees, Expand = expand }; - return source.Apply(source.Bounds, processor); + + source.ApplyProcessor(processor, source.Bounds); + return source; } } } diff --git a/src/ImageSharp.Processing/Transforms/Skew.cs b/src/ImageSharp.Processing/Transforms/Skew.cs index 825dce555..5a662c300 100644 --- a/src/ImageSharp.Processing/Transforms/Skew.cs +++ b/src/ImageSharp.Processing/Transforms/Skew.cs @@ -41,7 +41,9 @@ namespace ImageSharp where TColor : struct, IPackedPixel, IEquatable { SkewProcessor processor = new SkewProcessor { AngleX = degreesX, AngleY = degreesY, Expand = expand }; - return source.Apply(source.Bounds, processor); + + source.ApplyProcessor(processor, source.Bounds); + return source; } } } diff --git a/src/ImageSharp/Image/ImageProcessingExtensions.cs b/src/ImageSharp/Image/ImageProcessingExtensions.cs index db07afe2a..db8fb4baa 100644 --- a/src/ImageSharp/Image/ImageProcessingExtensions.cs +++ b/src/ImageSharp/Image/ImageProcessingExtensions.cs @@ -24,24 +24,7 @@ namespace ImageSharp public static Image Apply(this Image source, IImageProcessor processor) where TColor : struct, IPackedPixel, IEquatable { - return Apply(source, source.Bounds, processor); - } - - /// - /// Applies the processor to the image. - /// This method does not resize the target image. - /// - /// The pixel format. - /// The image this method extends. - /// - /// The structure that specifies the portion of the image object to draw. - /// - /// The processors to apply to the image. - /// The . - public static Image Apply(this Image source, Rectangle sourceRectangle, IImageProcessor processor) - where TColor : struct, IPackedPixel, IEquatable - { - source.ApplyProcessor(processor, sourceRectangle); + source.ApplyProcessor(processor, source.Bounds); return source; } } From 00d6047fac6785a23b98d13eb610e15ccd7fdd06 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Fri, 3 Feb 2017 09:52:24 +0000 Subject: [PATCH 024/142] case fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 96d14c8be..79f899462 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Packages include: - **ImageSharp.Drawing** Brushes and various drawing algorithms, including drawing Images - **ImageSharp.Drawing.Paths** - various vector drawing methods for drawing paths, polygons etc. + Various vector drawing methods for drawing paths, polygons etc. ### Manual build From b471a0cfbafc7ec4a02936cb777424e8f7b44fc2 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Fri, 3 Feb 2017 10:21:50 +0000 Subject: [PATCH 025/142] xml doc fixes --- src/ImageSharp.Drawing.Paths/DrawBeziers.cs | 30 ++++++------- src/ImageSharp.Drawing.Paths/DrawLines.cs | 30 ++++++------- src/ImageSharp.Drawing.Paths/DrawPath.cs | 36 ++++++---------- src/ImageSharp.Drawing.Paths/DrawPolygon.cs | 32 ++++++-------- src/ImageSharp.Drawing.Paths/DrawRectangle.cs | 30 ++++++------- src/ImageSharp.Drawing.Paths/DrawShape.cs | 30 ++++++------- src/ImageSharp.Drawing.Paths/FillPaths.cs | 24 ++++------- src/ImageSharp.Drawing.Paths/FillPolygon.cs | 20 ++++----- src/ImageSharp.Drawing.Paths/FillRectangle.cs | 20 ++++----- src/ImageSharp.Drawing.Paths/FillShape.cs | 20 ++++----- .../PointInfoExtensions.cs | 2 +- .../RectangleExtensions.cs | 2 +- src/ImageSharp.Drawing.Paths/ShapePath.cs | 43 +++---------------- src/ImageSharp.Drawing.Paths/ShapeRegion.cs | 36 ++-------------- src/ImageSharp.Drawing/DrawPath.cs | 36 ++++++---------- src/ImageSharp.Drawing/Drawable.cs | 6 --- src/ImageSharp.Drawing/FillRegion.cs | 18 ++++---- .../Processors/DrawPathProcessor.cs | 10 ++--- .../Processors/FillRegionProcessor.cs | 6 +-- src/ImageSharp.Drawing/Region.cs | 13 +++--- 20 files changed, 149 insertions(+), 295 deletions(-) diff --git a/src/ImageSharp.Drawing.Paths/DrawBeziers.cs b/src/ImageSharp.Drawing.Paths/DrawBeziers.cs index 6515db577..1a511d84d 100644 --- a/src/ImageSharp.Drawing.Paths/DrawBeziers.cs +++ b/src/ImageSharp.Drawing.Paths/DrawBeziers.cs @@ -24,14 +24,12 @@ namespace ImageSharp /// Draws the provided Points as an open Bezier path at the provided thickness with the supplied brush /// /// The type of the color. - /// The source. + /// The image this method extends. /// The brush. /// The thickness. /// The points. /// The options. - /// - /// The Image - /// + /// The . public static Image DrawBeziers(this Image source, IBrush brush, float thickness, Vector2[] points, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -42,11 +40,11 @@ namespace ImageSharp /// Draws the provided Points as an open Bezier path at the provided thickness with the supplied brush /// /// The type of the color. - /// The source. + /// The image this method extends. /// The brush. /// The thickness. /// The points. - /// The Image + /// The . public static Image DrawBeziers(this Image source, IBrush brush, float thickness, Vector2[] points) where TColor : struct, IPackedPixel, IEquatable { @@ -57,11 +55,11 @@ namespace ImageSharp /// Draws the provided Points as an open Bezier path at the provided thickness with the supplied brush /// /// The type of the color. - /// The source. + /// The image this method extends. /// The color. /// The thickness. /// The points. - /// The Image + /// The . public static Image DrawBeziers(this Image source, TColor color, float thickness, Vector2[] points) where TColor : struct, IPackedPixel, IEquatable { @@ -72,14 +70,12 @@ namespace ImageSharp /// Draws the provided Points as an open Bezier path at the provided thickness with the supplied brush /// /// The type of the color. - /// The source. + /// The image this method extends. /// The color. /// The thickness. /// The points. /// The options. - /// - /// The Image - /// + /// The . public static Image DrawBeziers(this Image source, TColor color, float thickness, Vector2[] points, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -90,13 +86,11 @@ namespace ImageSharp /// Draws the provided Points as an open Bezier path with the supplied pen /// /// The type of the color. - /// The source. + /// The image this method extends. /// The pen. /// The points. /// The options. - /// - /// The Image - /// + /// The . public static Image DrawBeziers(this Image source, IPen pen, Vector2[] points, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -107,10 +101,10 @@ namespace ImageSharp /// Draws the provided Points as an open Bezier path with the supplied pen /// /// The type of the color. - /// The source. + /// The image this method extends. /// The pen. /// The points. - /// The Image + /// The . public static Image DrawBeziers(this Image source, IPen pen, Vector2[] points) where TColor : struct, IPackedPixel, IEquatable { diff --git a/src/ImageSharp.Drawing.Paths/DrawLines.cs b/src/ImageSharp.Drawing.Paths/DrawLines.cs index 1ff58fd1d..f6f8d8f6c 100644 --- a/src/ImageSharp.Drawing.Paths/DrawLines.cs +++ b/src/ImageSharp.Drawing.Paths/DrawLines.cs @@ -22,14 +22,12 @@ namespace ImageSharp /// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush /// /// The type of the color. - /// The source. + /// The image this method extends. /// The brush. /// The thickness. /// The points. /// The options. - /// - /// The Image - /// + /// The . public static Image DrawLines(this Image source, IBrush brush, float thickness, Vector2[] points, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -40,11 +38,11 @@ namespace ImageSharp /// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush /// /// The type of the color. - /// The source. + /// The image this method extends. /// The brush. /// The thickness. /// The points. - /// The Image + /// The . public static Image DrawLines(this Image source, IBrush brush, float thickness, Vector2[] points) where TColor : struct, IPackedPixel, IEquatable { @@ -55,11 +53,11 @@ namespace ImageSharp /// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush /// /// The type of the color. - /// The source. + /// The image this method extends. /// The color. /// The thickness. /// The points. - /// The Image + /// The . public static Image DrawLines(this Image source, TColor color, float thickness, Vector2[] points) where TColor : struct, IPackedPixel, IEquatable { @@ -70,14 +68,12 @@ namespace ImageSharp /// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush /// /// The type of the color. - /// The source. + /// The image this method extends. /// The color. /// The thickness. /// The points. /// The options. - /// - /// The Image - /// + /// The .> public static Image DrawLines(this Image source, TColor color, float thickness, Vector2[] points, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -88,13 +84,11 @@ namespace ImageSharp /// Draws the provided Points as an open Linear path with the supplied pen /// /// The type of the color. - /// The source. + /// The image this method extends. /// The pen. /// The points. /// The options. - /// - /// The Image - /// + /// The . public static Image DrawLines(this Image source, IPen pen, Vector2[] points, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -105,10 +99,10 @@ namespace ImageSharp /// Draws the provided Points as an open Linear path with the supplied pen /// /// The type of the color. - /// The source. + /// The image this method extends. /// The pen. /// The points. - /// The Image + /// The . public static Image DrawLines(this Image source, IPen pen, Vector2[] points) where TColor : struct, IPackedPixel, IEquatable { diff --git a/src/ImageSharp.Drawing.Paths/DrawPath.cs b/src/ImageSharp.Drawing.Paths/DrawPath.cs index 201984e0a..4e4275df6 100644 --- a/src/ImageSharp.Drawing.Paths/DrawPath.cs +++ b/src/ImageSharp.Drawing.Paths/DrawPath.cs @@ -22,13 +22,11 @@ namespace ImageSharp /// Draws the outline of the polygon with the provided pen. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The pen. /// The path. /// The options. - /// - /// The Image - /// + /// The . public static Image Draw(this Image source, IPen pen, IPath path, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -39,12 +37,10 @@ namespace ImageSharp /// Draws the outline of the polygon with the provided pen. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The pen. /// The path. - /// - /// The Image - /// + /// The . public static Image Draw(this Image source, IPen pen, IPath path) where TColor : struct, IPackedPixel, IEquatable { @@ -55,14 +51,12 @@ namespace ImageSharp /// Draws the outline of the polygon with the provided brush at the provided thickness. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The brush. /// The thickness. /// The shape. /// The options. - /// - /// The Image - /// + /// The . public static Image Draw(this Image source, IBrush brush, float thickness, IPath path, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -73,13 +67,11 @@ namespace ImageSharp /// Draws the outline of the polygon with the provided brush at the provided thickness. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The brush. /// The thickness. /// The path. - /// - /// The Image - /// + /// The . public static Image Draw(this Image source, IBrush brush, float thickness, IPath path) where TColor : struct, IPackedPixel, IEquatable { @@ -90,14 +82,12 @@ namespace ImageSharp /// Draws the outline of the polygon with the provided brush at the provided thickness. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The color. /// The thickness. /// The path. /// The options. - /// - /// The Image - /// + /// The . public static Image Draw(this Image source, TColor color, float thickness, IPath path, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -108,13 +98,11 @@ namespace ImageSharp /// Draws the outline of the polygon with the provided brush at the provided thickness. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The color. /// The thickness. /// The path. - /// - /// The Image - /// + /// The . public static Image Draw(this Image source, TColor color, float thickness, IPath path) where TColor : struct, IPackedPixel, IEquatable { diff --git a/src/ImageSharp.Drawing.Paths/DrawPolygon.cs b/src/ImageSharp.Drawing.Paths/DrawPolygon.cs index 571b13c1e..28785e5cb 100644 --- a/src/ImageSharp.Drawing.Paths/DrawPolygon.cs +++ b/src/ImageSharp.Drawing.Paths/DrawPolygon.cs @@ -22,14 +22,12 @@ namespace ImageSharp /// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The brush. /// The thickness. /// The points. /// The options. - /// - /// The Image - /// + /// The . public static Image DrawPolygon(this Image source, IBrush brush, float thickness, Vector2[] points, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -40,11 +38,11 @@ namespace ImageSharp /// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The brush. /// The thickness. /// The points. - /// The Image + /// The . public static Image DrawPolygon(this Image source, IBrush brush, float thickness, Vector2[] points) where TColor : struct, IPackedPixel, IEquatable { @@ -55,11 +53,11 @@ namespace ImageSharp /// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The color. /// The thickness. /// The points. - /// The Image + /// The . public static Image DrawPolygon(this Image source, TColor color, float thickness, Vector2[] points) where TColor : struct, IPackedPixel, IEquatable { @@ -70,14 +68,12 @@ namespace ImageSharp /// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The color. /// The thickness. /// The points. /// The options. - /// - /// The Image - /// + /// The . public static Image DrawPolygon(this Image source, TColor color, float thickness, Vector2[] points, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -88,12 +84,10 @@ namespace ImageSharp /// Draws the provided Points as a closed Linear Polygon with the provided Pen. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The pen. /// The points. - /// - /// The Image - /// + /// The . public static Image DrawPolygon(this Image source, IPen pen, Vector2[] points) where TColor : struct, IPackedPixel, IEquatable { @@ -104,13 +98,11 @@ namespace ImageSharp /// Draws the provided Points as a closed Linear Polygon with the provided Pen. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The pen. /// The points. /// The options. - /// - /// The Image - /// + /// The . public static Image DrawPolygon(this Image source, IPen pen, Vector2[] points, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { diff --git a/src/ImageSharp.Drawing.Paths/DrawRectangle.cs b/src/ImageSharp.Drawing.Paths/DrawRectangle.cs index 28ea3d6e0..4e8ec7135 100644 --- a/src/ImageSharp.Drawing.Paths/DrawRectangle.cs +++ b/src/ImageSharp.Drawing.Paths/DrawRectangle.cs @@ -23,13 +23,11 @@ namespace ImageSharp /// Draws the outline of the polygon with the provided pen. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The pen. /// The shape. /// The options. - /// - /// The Image - /// + /// The . public static Image Draw(this Image source, IPen pen, Rectangle shape, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -40,10 +38,10 @@ namespace ImageSharp /// Draws the outline of the polygon with the provided pen. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The pen. /// The shape. - /// The Image + /// The . public static Image Draw(this Image source, IPen pen, Rectangle shape) where TColor : struct, IPackedPixel, IEquatable { @@ -54,14 +52,12 @@ namespace ImageSharp /// Draws the outline of the polygon with the provided brush at the provided thickness. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The brush. /// The thickness. /// The shape. /// The options. - /// - /// The Image - /// + /// The . public static Image Draw(this Image source, IBrush brush, float thickness, Rectangle shape, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -72,11 +68,11 @@ namespace ImageSharp /// Draws the outline of the polygon with the provided brush at the provided thickness. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The brush. /// The thickness. /// The shape. - /// The Image + /// The . public static Image Draw(this Image source, IBrush brush, float thickness, Rectangle shape) where TColor : struct, IPackedPixel, IEquatable { @@ -87,14 +83,12 @@ namespace ImageSharp /// Draws the outline of the polygon with the provided brush at the provided thickness. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The color. /// The thickness. /// The shape. /// The options. - /// - /// The Image - /// + /// The . public static Image Draw(this Image source, TColor color, float thickness, Rectangle shape, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -105,11 +99,11 @@ namespace ImageSharp /// Draws the outline of the polygon with the provided brush at the provided thickness. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The color. /// The thickness. /// The shape. - /// The Image + /// The . public static Image Draw(this Image source, TColor color, float thickness, Rectangle shape) where TColor : struct, IPackedPixel, IEquatable { diff --git a/src/ImageSharp.Drawing.Paths/DrawShape.cs b/src/ImageSharp.Drawing.Paths/DrawShape.cs index 15ae2b179..6ddce65b9 100644 --- a/src/ImageSharp.Drawing.Paths/DrawShape.cs +++ b/src/ImageSharp.Drawing.Paths/DrawShape.cs @@ -22,13 +22,11 @@ namespace ImageSharp /// Draws the outline of the polygon with the provided pen. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The pen. /// The shape. /// The options. - /// - /// The Image - /// + /// The . public static Image Draw(this Image source, IPen pen, IShape shape, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -39,10 +37,10 @@ namespace ImageSharp /// Draws the outline of the polygon with the provided pen. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The pen. /// The shape. - /// The Image + /// The . public static Image Draw(this Image source, IPen pen, IShape shape) where TColor : struct, IPackedPixel, IEquatable { @@ -53,14 +51,12 @@ namespace ImageSharp /// Draws the outline of the polygon with the provided brush at the provided thickness. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The brush. /// The thickness. /// The shape. /// The options. - /// - /// The Image - /// + /// The . public static Image Draw(this Image source, IBrush brush, float thickness, IShape shape, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -71,11 +67,11 @@ namespace ImageSharp /// Draws the outline of the polygon with the provided brush at the provided thickness. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The brush. /// The thickness. /// The shape. - /// The Image + /// The . public static Image Draw(this Image source, IBrush brush, float thickness, IShape shape) where TColor : struct, IPackedPixel, IEquatable { @@ -86,14 +82,12 @@ namespace ImageSharp /// Draws the outline of the polygon with the provided brush at the provided thickness. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The color. /// The thickness. /// The shape. /// The options. - /// - /// The Image - /// + /// The . public static Image Draw(this Image source, TColor color, float thickness, IShape shape, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -104,11 +98,11 @@ namespace ImageSharp /// Draws the outline of the polygon with the provided brush at the provided thickness. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The color. /// The thickness. /// The shape. - /// The Image + /// The . public static Image Draw(this Image source, TColor color, float thickness, IShape shape) where TColor : struct, IPackedPixel, IEquatable { diff --git a/src/ImageSharp.Drawing.Paths/FillPaths.cs b/src/ImageSharp.Drawing.Paths/FillPaths.cs index 3095ee7cd..09a799794 100644 --- a/src/ImageSharp.Drawing.Paths/FillPaths.cs +++ b/src/ImageSharp.Drawing.Paths/FillPaths.cs @@ -22,13 +22,11 @@ namespace ImageSharp /// Flood fills the image in the shape of the provided polygon with the specified brush.. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The brush. /// The shape. /// The graphics options. - /// - /// The Image - /// + /// The . public static Image Fill(this Image source, IBrush brush, IPath path, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -39,12 +37,10 @@ namespace ImageSharp /// Flood fills the image in the shape of the provided polygon with the specified brush. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The brush. /// The path. - /// - /// The Image - /// + /// The . public static Image Fill(this Image source, IBrush brush, IPath path) where TColor : struct, IPackedPixel, IEquatable { @@ -55,13 +51,11 @@ namespace ImageSharp /// Flood fills the image in the shape of the provided polygon with the specified brush.. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The color. /// The path. /// The options. - /// - /// The Image - /// + /// The . public static Image Fill(this Image source, TColor color, IPath path, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -72,12 +66,10 @@ namespace ImageSharp /// Flood fills the image in the shape of the provided polygon with the specified brush.. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The color. /// The path. - /// - /// The Image - /// + /// The . public static Image Fill(this Image source, TColor color, IPath path) where TColor : struct, IPackedPixel, IEquatable { diff --git a/src/ImageSharp.Drawing.Paths/FillPolygon.cs b/src/ImageSharp.Drawing.Paths/FillPolygon.cs index 4092e5fc6..a609ceed2 100644 --- a/src/ImageSharp.Drawing.Paths/FillPolygon.cs +++ b/src/ImageSharp.Drawing.Paths/FillPolygon.cs @@ -22,13 +22,11 @@ namespace ImageSharp /// Flood fills the image in the shape of a Linear polygon described by the points /// /// The type of the color. - /// The source. + /// The image this method extends. /// The brush. /// The points. /// The options. - /// - /// The Image - /// + /// The . public static Image FillPolygon(this Image source, IBrush brush, Vector2[] points, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -39,10 +37,10 @@ namespace ImageSharp /// Flood fills the image in the shape of a Linear polygon described by the points /// /// The type of the color. - /// The source. + /// The image this method extends. /// The brush. /// The points. - /// The Image + /// The . public static Image FillPolygon(this Image source, IBrush brush, Vector2[] points) where TColor : struct, IPackedPixel, IEquatable { @@ -53,13 +51,11 @@ namespace ImageSharp /// Flood fills the image in the shape of a Linear polygon described by the points /// /// The type of the color. - /// The source. + /// The image this method extends. /// The color. /// The points. /// The options. - /// - /// The Image - /// + /// The . public static Image FillPolygon(this Image source, TColor color, Vector2[] points, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -70,10 +66,10 @@ namespace ImageSharp /// Flood fills the image in the shape of a Linear polygon described by the points /// /// The type of the color. - /// The source. + /// The image this method extends. /// The color. /// The points. - /// The Image + /// The . public static Image FillPolygon(this Image source, TColor color, Vector2[] points) where TColor : struct, IPackedPixel, IEquatable { diff --git a/src/ImageSharp.Drawing.Paths/FillRectangle.cs b/src/ImageSharp.Drawing.Paths/FillRectangle.cs index aef777edd..579a288a4 100644 --- a/src/ImageSharp.Drawing.Paths/FillRectangle.cs +++ b/src/ImageSharp.Drawing.Paths/FillRectangle.cs @@ -20,13 +20,11 @@ namespace ImageSharp /// Flood fills the image in the shape of the provided polygon with the specified brush.. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The brush. /// The shape. /// The options. - /// - /// The Image - /// + /// The . public static Image Fill(this Image source, IBrush brush, Rectangle shape, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -37,10 +35,10 @@ namespace ImageSharp /// Flood fills the image in the shape of the provided polygon with the specified brush.. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The brush. /// The shape. - /// The Image + /// The . public static Image Fill(this Image source, IBrush brush, Rectangle shape) where TColor : struct, IPackedPixel, IEquatable { @@ -51,13 +49,11 @@ namespace ImageSharp /// Flood fills the image in the shape of the provided polygon with the specified brush.. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The color. /// The shape. /// The options. - /// - /// The Image - /// + /// The . public static Image Fill(this Image source, TColor color, Rectangle shape, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -68,10 +64,10 @@ namespace ImageSharp /// Flood fills the image in the shape of the provided polygon with the specified brush.. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The color. /// The shape. - /// The Image + /// The . public static Image Fill(this Image source, TColor color, Rectangle shape) where TColor : struct, IPackedPixel, IEquatable { diff --git a/src/ImageSharp.Drawing.Paths/FillShape.cs b/src/ImageSharp.Drawing.Paths/FillShape.cs index 6e50f7377..52a3c60dd 100644 --- a/src/ImageSharp.Drawing.Paths/FillShape.cs +++ b/src/ImageSharp.Drawing.Paths/FillShape.cs @@ -22,13 +22,11 @@ namespace ImageSharp /// Flood fills the image in the shape of the provided polygon with the specified brush.. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The brush. /// The shape. /// The graphics options. - /// - /// The Image - /// + /// The . public static Image Fill(this Image source, IBrush brush, IShape shape, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -39,10 +37,10 @@ namespace ImageSharp /// Flood fills the image in the shape of the provided polygon with the specified brush. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The brush. /// The shape. - /// The Image + /// The . public static Image Fill(this Image source, IBrush brush, IShape shape) where TColor : struct, IPackedPixel, IEquatable { @@ -53,13 +51,11 @@ namespace ImageSharp /// Flood fills the image in the shape of the provided polygon with the specified brush.. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The color. /// The shape. /// The options. - /// - /// The Image - /// + /// The . public static Image Fill(this Image source, TColor color, IShape shape, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -70,10 +66,10 @@ namespace ImageSharp /// Flood fills the image in the shape of the provided polygon with the specified brush.. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The color. /// The shape. - /// The Image + /// The . public static Image Fill(this Image source, TColor color, IShape shape) where TColor : struct, IPackedPixel, IEquatable { diff --git a/src/ImageSharp.Drawing.Paths/PointInfoExtensions.cs b/src/ImageSharp.Drawing.Paths/PointInfoExtensions.cs index ea38dfb9d..18cd4e66c 100644 --- a/src/ImageSharp.Drawing.Paths/PointInfoExtensions.cs +++ b/src/ImageSharp.Drawing.Paths/PointInfoExtensions.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Drawing /// /// Converts a to an ImageSharp . /// - /// The source. + /// The image this method extends. /// A representation of this public static PointInfo Convert(this SixLabors.Shapes.PointInfo source) { diff --git a/src/ImageSharp.Drawing.Paths/RectangleExtensions.cs b/src/ImageSharp.Drawing.Paths/RectangleExtensions.cs index 8369cc83f..1faa6469a 100644 --- a/src/ImageSharp.Drawing.Paths/RectangleExtensions.cs +++ b/src/ImageSharp.Drawing.Paths/RectangleExtensions.cs @@ -22,7 +22,7 @@ namespace ImageSharp.Drawing.Processors /// /// Converts a Shaper2D to an ImageSharp by creating a the entirely surrounds the source. /// - /// The source. + /// The image this method extends. /// A representation of this public static Rectangle Convert(this SixLabors.Shapes.Rectangle source) { diff --git a/src/ImageSharp.Drawing.Paths/ShapePath.cs b/src/ImageSharp.Drawing.Paths/ShapePath.cs index ee1f6e9d9..f2f07ea3c 100644 --- a/src/ImageSharp.Drawing.Paths/ShapePath.cs +++ b/src/ImageSharp.Drawing.Paths/ShapePath.cs @@ -64,32 +64,13 @@ namespace ImageSharp.Drawing /// public ImmutableArray Paths { get; } - /// - /// Gets the maximum number of intersections to could be returned. - /// - /// - /// The maximum intersections. - /// + /// public override int MaxIntersections => this.shape.MaxIntersections; - /// - /// Gets the bounds. - /// - /// - /// The bounds. - /// + /// public override Rectangle Bounds { get; } - /// - /// Scans the X axis for intersections. - /// - /// The x. - /// The buffer. - /// The length. - /// The offset. - /// - /// The number of intersections found. - /// + /// public override int ScanX(int x, float[] buffer, int length, int offset) { Vector2 start = new Vector2(x, this.Bounds.Top - 1); @@ -117,16 +98,7 @@ namespace ImageSharp.Drawing } } - /// - /// Scans the Y axis for intersections. - /// - /// The position along the y axis to find intersections. - /// The buffer. - /// The length. - /// The offset. - /// - /// The number of intersections found. - /// + /// public override int ScanY(int y, float[] buffer, int length, int offset) { Vector2 start = new Vector2(float.MinValue, y); @@ -154,12 +126,7 @@ namespace ImageSharp.Drawing } } - /// - /// Gets the point information for the specified x and y location. - /// - /// The x. - /// The y. - /// Information about the the point + /// public override PointInfo GetPointInfo(int x, int y) { Vector2 point = new Vector2(x, y); diff --git a/src/ImageSharp.Drawing.Paths/ShapeRegion.cs b/src/ImageSharp.Drawing.Paths/ShapeRegion.cs index b6921f16e..a1fdd7b8c 100644 --- a/src/ImageSharp.Drawing.Paths/ShapeRegion.cs +++ b/src/ImageSharp.Drawing.Paths/ShapeRegion.cs @@ -44,32 +44,13 @@ namespace ImageSharp.Drawing /// public IShape Shape { get; } - /// - /// Gets the maximum number of intersections to could be returned. - /// - /// - /// The maximum intersections. - /// + /// public override int MaxIntersections => this.Shape.MaxIntersections; - /// - /// Gets the bounds. - /// - /// - /// The bounds. - /// + /// public override Rectangle Bounds { get; } - /// - /// Scans the X axis for intersections. - /// - /// The x. - /// The buffer. - /// The length. - /// The offset. - /// - /// The number of intersections found. - /// + /// public override int ScanX(int x, float[] buffer, int length, int offset) { Vector2 start = new Vector2(x, this.Bounds.Top - 1); @@ -97,16 +78,7 @@ namespace ImageSharp.Drawing } } - /// - /// Scans the Y axis for intersections. - /// - /// The position along the y axis to find intersections. - /// The buffer. - /// The length. - /// The offset. - /// - /// The number of intersections found. - /// + /// public override int ScanY(int y, float[] buffer, int length, int offset) { Vector2 start = new Vector2(float.MinValue, y); diff --git a/src/ImageSharp.Drawing/DrawPath.cs b/src/ImageSharp.Drawing/DrawPath.cs index fe833e3af..d92731270 100644 --- a/src/ImageSharp.Drawing/DrawPath.cs +++ b/src/ImageSharp.Drawing/DrawPath.cs @@ -21,13 +21,11 @@ namespace ImageSharp /// Draws the outline of the region with the provided pen. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The pen. /// The path. /// The options. - /// - /// The Image - /// + /// The . public static Image Draw(this Image source, IPen pen, Drawable path, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -38,12 +36,10 @@ namespace ImageSharp /// Draws the outline of the polygon with the provided pen. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The pen. /// The path. - /// - /// The Image - /// + /// The . public static Image Draw(this Image source, IPen pen, Drawable path) where TColor : struct, IPackedPixel, IEquatable { @@ -54,14 +50,12 @@ namespace ImageSharp /// Draws the outline of the polygon with the provided brush at the provided thickness. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The brush. /// The thickness. /// The path. /// The options. - /// - /// The Image - /// + /// The . public static Image Draw(this Image source, IBrush brush, float thickness, Drawable path, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -72,13 +66,11 @@ namespace ImageSharp /// Draws the outline of the polygon with the provided brush at the provided thickness. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The brush. /// The thickness. /// The path. - /// - /// The Image - /// + /// The . public static Image Draw(this Image source, IBrush brush, float thickness, Drawable path) where TColor : struct, IPackedPixel, IEquatable { @@ -89,14 +81,12 @@ namespace ImageSharp /// Draws the outline of the polygon with the provided brush at the provided thickness. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The color. /// The thickness. /// The path. /// The options. - /// - /// The Image - /// + /// The . public static Image Draw(this Image source, TColor color, float thickness, Drawable path, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -107,13 +97,11 @@ namespace ImageSharp /// Draws the outline of the polygon with the provided brush at the provided thickness. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The color. /// The thickness. /// The path. - /// - /// The Image - /// + /// The . public static Image Draw(this Image source, TColor color, float thickness, Drawable path) where TColor : struct, IPackedPixel, IEquatable { diff --git a/src/ImageSharp.Drawing/Drawable.cs b/src/ImageSharp.Drawing/Drawable.cs index b81827331..62f5e62c1 100644 --- a/src/ImageSharp.Drawing/Drawable.cs +++ b/src/ImageSharp.Drawing/Drawable.cs @@ -13,17 +13,11 @@ namespace ImageSharp.Drawing /// /// Gets the maximum number of intersections to could be returned. /// - /// - /// The maximum intersections. - /// public abstract int MaxIntersections { get; } /// /// Gets the bounds. /// - /// - /// The bounds. - /// public abstract Rectangle Bounds { get; } /// diff --git a/src/ImageSharp.Drawing/FillRegion.cs b/src/ImageSharp.Drawing/FillRegion.cs index 8d4f20b67..c634d6e64 100644 --- a/src/ImageSharp.Drawing/FillRegion.cs +++ b/src/ImageSharp.Drawing/FillRegion.cs @@ -20,9 +20,9 @@ namespace ImageSharp /// Flood fills the image with the specified brush. /// /// The type of the color. - /// The source. - /// The brush. - /// The Image + /// The image this method extends. + /// The details how to fill the region of interest. + /// The . public static Image Fill(this Image source, IBrush brush) where TColor : struct, IPackedPixel, IEquatable { @@ -33,9 +33,9 @@ namespace ImageSharp /// Flood fills the image with the specified color. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The color. - /// The Image + /// The . public static Image Fill(this Image source, TColor color) where TColor : struct, IPackedPixel, IEquatable { @@ -46,7 +46,7 @@ namespace ImageSharp /// Flood fills the image with in the region with the specified brush. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The brush. /// The region. /// The graphics options. @@ -63,7 +63,7 @@ namespace ImageSharp /// Flood fills the image with in the region with the specified brush. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The brush. /// The region. /// @@ -79,7 +79,7 @@ namespace ImageSharp /// Flood fills the image with in the region with the specified color. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The color. /// The region. /// The options. @@ -96,7 +96,7 @@ namespace ImageSharp /// Flood fills the image with in the region with the specified color. /// /// The type of the color. - /// The source. + /// The image this method extends. /// The color. /// The region. /// diff --git a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs index 53f0408d4..b4aa7fba5 100644 --- a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs @@ -28,12 +28,12 @@ namespace ImageSharp.Drawing.Processors /// /// Initializes a new instance of the class. /// - /// The pen. - /// The region. - /// The options. - public DrawPathProcessor(IPen pen, Drawable region, GraphicsOptions options) + /// The details how to draw the outline/path. + /// The details of the paths and outlines to draw. + /// The drawing configuration options. + public DrawPathProcessor(IPen pen, Drawable drawable, GraphicsOptions options) { - this.Path = region; + this.Path = drawable; this.Pen = pen; this.Options = options; } diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs index 6719d365a..67c3ad176 100644 --- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs @@ -26,9 +26,9 @@ namespace ImageSharp.Drawing.Processors /// /// Initializes a new instance of the class. /// - /// The brush. - /// The region. - /// The options. + /// The details how to fill the region of interest. + /// The region of interest to be filled. + /// The configuration options. public FillRegionProcessor(IBrush brush, Region region, GraphicsOptions options) { this.Region = region; diff --git a/src/ImageSharp.Drawing/Region.cs b/src/ImageSharp.Drawing/Region.cs index 95f0e1748..81f3dca5b 100644 --- a/src/ImageSharp.Drawing/Region.cs +++ b/src/ImageSharp.Drawing/Region.cs @@ -13,23 +13,20 @@ namespace ImageSharp.Drawing /// /// Gets the maximum number of intersections to could be returned. /// - /// - /// The maximum intersections. - /// public abstract int MaxIntersections { get; } /// - /// Gets the bounds. + /// Gets the bounding box that entirly surrounds this region. /// - /// - /// The bounds. - /// + /// + /// This should always contains all possible points returned from eather or . + /// public abstract Rectangle Bounds { get; } /// /// Scans the X axis for intersections. /// - /// The x. + /// The position along the X axies to find intersections. /// The buffer. /// The length. /// The offset. From 1275e42cec35e8f6230bbe2ee37016bf370d8541 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Fri, 3 Feb 2017 10:26:50 +0000 Subject: [PATCH 026/142] xml doc fixes --- src/ImageSharp.Drawing/FillRegion.cs | 16 ++++------------ .../Processors/FillRegionProcessor.cs | 8 +------- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/src/ImageSharp.Drawing/FillRegion.cs b/src/ImageSharp.Drawing/FillRegion.cs index c634d6e64..fbbf659d1 100644 --- a/src/ImageSharp.Drawing/FillRegion.cs +++ b/src/ImageSharp.Drawing/FillRegion.cs @@ -50,9 +50,7 @@ namespace ImageSharp /// The brush. /// The region. /// The graphics options. - /// - /// The Image - /// + /// The . public static Image Fill(this Image source, IBrush brush, Region region, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -66,9 +64,7 @@ namespace ImageSharp /// The image this method extends. /// The brush. /// The region. - /// - /// The Image - /// + /// The . public static Image Fill(this Image source, IBrush brush, Region region) where TColor : struct, IPackedPixel, IEquatable { @@ -83,9 +79,7 @@ namespace ImageSharp /// The color. /// The region. /// The options. - /// - /// The Image - /// + /// The . public static Image Fill(this Image source, TColor color, Region region, GraphicsOptions options) where TColor : struct, IPackedPixel, IEquatable { @@ -99,9 +93,7 @@ namespace ImageSharp /// The image this method extends. /// The color. /// The region. - /// - /// The Image - /// + /// The . public static Image Fill(this Image source, TColor color, Region region) where TColor : struct, IPackedPixel, IEquatable { diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs index 67c3ad176..6a37a1ae5 100644 --- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs @@ -39,17 +39,11 @@ namespace ImageSharp.Drawing.Processors /// /// Gets the brush. /// - /// - /// The brush. - /// public IBrush Brush { get; } /// - /// Gets the region. + /// Gets the region that this processor applies to. /// - /// - /// The region. - /// public Region Region { get; } /// From 361b762854df42afde38be1f789e1544eb63570f Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Fri, 3 Feb 2017 11:29:31 +0100 Subject: [PATCH 027/142] Removed obsolete IImageFrame. --- src/ImageSharp/Image/IImageFrame{TColor}.cs | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 src/ImageSharp/Image/IImageFrame{TColor}.cs diff --git a/src/ImageSharp/Image/IImageFrame{TColor}.cs b/src/ImageSharp/Image/IImageFrame{TColor}.cs deleted file mode 100644 index 6ebda36c8..000000000 --- a/src/ImageSharp/Image/IImageFrame{TColor}.cs +++ /dev/null @@ -1,18 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - - /// - /// Represents a single frame in a animation. - /// - /// The pixel format. - public interface IImageFrame : IImageBase - where TColor : struct, IPackedPixel, IEquatable - { - } -} From 82fbca72f6d169e08c02903f73ddf15aee569b01 Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Fri, 3 Feb 2017 11:32:15 +0100 Subject: [PATCH 028/142] Moved IImageBase to a separate file. --- src/ImageSharp/Image/IImageBase.cs | 56 ++++++++++++++++++++++ src/ImageSharp/Image/IImageBase{TColor}.cs | 49 ------------------- 2 files changed, 56 insertions(+), 49 deletions(-) create mode 100644 src/ImageSharp/Image/IImageBase.cs diff --git a/src/ImageSharp/Image/IImageBase.cs b/src/ImageSharp/Image/IImageBase.cs new file mode 100644 index 000000000..4f9425a1a --- /dev/null +++ b/src/ImageSharp/Image/IImageBase.cs @@ -0,0 +1,56 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + /// + /// Encapsulates the basic properties and methods required to manipulate images. + /// + public interface IImageBase + { + /// + /// Gets the representing the bounds of the image. + /// + Rectangle Bounds { get; } + + /// + /// Gets or sets the quality of the image. This affects the output quality of lossy image formats. + /// + int Quality { get; set; } + + /// + /// Gets or sets the frame delay for animated images. + /// If not 0, this field specifies the number of hundredths (1/100) of a second to + /// wait before continuing with the processing of the Data Stream. + /// The clock starts ticking immediately after the graphic is rendered. + /// + int FrameDelay { get; set; } + + /// + /// Gets or sets the maximum allowable width in pixels. + /// + int MaxWidth { get; set; } + + /// + /// Gets or sets the maximum allowable height in pixels. + /// + int MaxHeight { get; set; } + + /// + /// Gets the width in pixels. + /// + int Width { get; } + + /// + /// Gets the height in pixels. + /// + int Height { get; } + + /// + /// Gets the pixel ratio made up of the width and height. + /// + double PixelRatio { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/IImageBase{TColor}.cs b/src/ImageSharp/Image/IImageBase{TColor}.cs index 66746c993..f01a4b702 100644 --- a/src/ImageSharp/Image/IImageBase{TColor}.cs +++ b/src/ImageSharp/Image/IImageBase{TColor}.cs @@ -40,53 +40,4 @@ namespace ImageSharp /// The PixelAccessor Lock(); } - - /// - /// Encapsulates the basic properties and methods required to manipulate images. - /// - public interface IImageBase - { - /// - /// Gets the representing the bounds of the image. - /// - Rectangle Bounds { get; } - - /// - /// Gets or sets the quality of the image. This affects the output quality of lossy image formats. - /// - int Quality { get; set; } - - /// - /// Gets or sets the frame delay for animated images. - /// If not 0, this field specifies the number of hundredths (1/100) of a second to - /// wait before continuing with the processing of the Data Stream. - /// The clock starts ticking immediately after the graphic is rendered. - /// - int FrameDelay { get; set; } - - /// - /// Gets or sets the maximum allowable width in pixels. - /// - int MaxWidth { get; set; } - - /// - /// Gets or sets the maximum allowable height in pixels. - /// - int MaxHeight { get; set; } - - /// - /// Gets the width in pixels. - /// - int Width { get; } - - /// - /// Gets the height in pixels. - /// - int Height { get; } - - /// - /// Gets the pixel ratio made up of the width and height. - /// - double PixelRatio { get; } - } } \ No newline at end of file From 506b59686b3e43368069adf948af991d13372182 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Fri, 3 Feb 2017 10:32:54 +0000 Subject: [PATCH 029/142] xml doc fixes --- .../Brushes/Processors/BrushApplicator.cs | 12 ++++-------- .../Pens/Processors/PenApplicator.cs | 6 ++---- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs index 97d3f840c..37af8cd04 100644 --- a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs +++ b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs @@ -19,16 +19,12 @@ namespace ImageSharp.Drawing.Processors /// /// Gets the color for a single pixel. /// - /// The x. - /// The y. - /// - /// The color - /// + /// The x cordinate. + /// The y cordinate. + /// The a that should be applied to the pixel. public abstract TColor this[int x, int y] { get; } - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// + /// public abstract void Dispose(); } } diff --git a/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs b/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs index de680c809..cba4d9fdc 100644 --- a/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs +++ b/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs @@ -22,10 +22,8 @@ namespace ImageSharp.Drawing.Processors /// The required region. /// public abstract RectangleF RequiredRegion { get; } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// + + /// public abstract void Dispose(); /// From 1e79f41add09b3251b44846e7322079418d5efa5 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Fri, 3 Feb 2017 10:57:00 +0000 Subject: [PATCH 030/142] fix stylecop issue --- src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs b/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs index cba4d9fdc..86283b5bb 100644 --- a/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs +++ b/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs @@ -22,7 +22,7 @@ namespace ImageSharp.Drawing.Processors /// The required region. /// public abstract RectangleF RequiredRegion { get; } - + /// public abstract void Dispose(); From e153b7d567155e47c219988796561e7878b223ae Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Fri, 3 Feb 2017 12:10:52 +0100 Subject: [PATCH 031/142] Corrected header. --- src/ImageSharp/Image/IImageBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Image/IImageBase.cs b/src/ImageSharp/Image/IImageBase.cs index 4f9425a1a..113b94a18 100644 --- a/src/ImageSharp/Image/IImageBase.cs +++ b/src/ImageSharp/Image/IImageBase.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // From 2d3a9f1b0e053d9756aaf9d2f32f804f7d6f70b7 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Fri, 3 Feb 2017 11:29:08 +0000 Subject: [PATCH 032/142] reduce calls --- .../PointInfoExtensions.cs | 27 ------------------- src/ImageSharp.Drawing.Paths/ShapePath.cs | 16 ++++++----- 2 files changed, 10 insertions(+), 33 deletions(-) delete mode 100644 src/ImageSharp.Drawing.Paths/PointInfoExtensions.cs diff --git a/src/ImageSharp.Drawing.Paths/PointInfoExtensions.cs b/src/ImageSharp.Drawing.Paths/PointInfoExtensions.cs deleted file mode 100644 index 18cd4e66c..000000000 --- a/src/ImageSharp.Drawing.Paths/PointInfoExtensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing -{ - /// - /// Extension methods for helping to bridge Shaper2D and ImageSharp primitives. - /// - internal static class PointInfoExtensions - { - /// - /// Converts a to an ImageSharp . - /// - /// The image this method extends. - /// A representation of this - public static PointInfo Convert(this SixLabors.Shapes.PointInfo source) - { - return new PointInfo - { - DistanceAlongPath = source.DistanceAlongPath, - DistanceFromPath = source.DistanceFromPath - }; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing.Paths/ShapePath.cs b/src/ImageSharp.Drawing.Paths/ShapePath.cs index f2f07ea3c..8c9500e55 100644 --- a/src/ImageSharp.Drawing.Paths/ShapePath.cs +++ b/src/ImageSharp.Drawing.Paths/ShapePath.cs @@ -130,20 +130,24 @@ namespace ImageSharp.Drawing public override PointInfo GetPointInfo(int x, int y) { Vector2 point = new Vector2(x, y); - SixLabors.Shapes.PointInfo result = default(SixLabors.Shapes.PointInfo); - float distance = float.MaxValue; + float distanceFromPath = float.MaxValue; + float distanceAlongPath = 0; for (int i = 0; i < this.Paths.Length; i++) { SixLabors.Shapes.PointInfo p = this.Paths[i].Distance(point); - if (p.DistanceFromPath < distance) + if (p.DistanceFromPath < distanceFromPath) { - distance = p.DistanceFromPath; - result = p; + distanceFromPath = p.DistanceFromPath; + distanceAlongPath = p.DistanceAlongPath; } } - return result.Convert(); + return new PointInfo + { + DistanceAlongPath = distanceAlongPath, + DistanceFromPath = distanceFromPath + }; } } } From a7fab63cb195af8962aacaf0a797b88acef266ae Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Fri, 3 Feb 2017 11:36:37 +0000 Subject: [PATCH 033/142] remove old test --- .../ImageSharp.Tests/Drawing/Paths/Extensions.cs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/tests/ImageSharp.Tests/Drawing/Paths/Extensions.cs b/tests/ImageSharp.Tests/Drawing/Paths/Extensions.cs index 37720253d..a18dd90bc 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/Extensions.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/Extensions.cs @@ -16,22 +16,6 @@ namespace ImageSharp.Tests.Drawing.Paths public class Extensions { - [Fact] - public void ConvertPointInfo() - { - SixLabors.Shapes.PointInfo src = new SixLabors.Shapes.PointInfo - { - ClosestPointOnPath = Vector2.UnitX, - SearchPoint = Vector2.UnitY, - DistanceAlongPath = 99f, - DistanceFromPath = 82f - }; - ImageSharp.Drawing.PointInfo info = src.Convert(); - - Assert.Equal(src.DistanceAlongPath, info.DistanceAlongPath); - Assert.Equal(src.DistanceFromPath, info.DistanceFromPath); - } - [Theory] [InlineData(0.5, 0.5, 5, 5, 0,0,6,6)] [InlineData(1, 1, 5, 5, 1,1,5,5)] From 8c2a840f4d990bb9115acd4c5e2a852e5cc99e86 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Fri, 3 Feb 2017 19:39:40 +0000 Subject: [PATCH 034/142] fixes some corner clipping issues --- src/ImageSharp.Drawing.Paths/ShapePath.cs | 4 +- src/ImageSharp.Drawing.Paths/ShapeRegion.cs | 4 +- src/ImageSharp.Drawing.Paths/project.json | 2 +- .../Processors/FillRegionProcessor.cs | 16 +++- .../Drawing/SolidPolygonTests.cs | 76 ++++++++++++++++++- 5 files changed, 95 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp.Drawing.Paths/ShapePath.cs b/src/ImageSharp.Drawing.Paths/ShapePath.cs index 8c9500e55..34e77b807 100644 --- a/src/ImageSharp.Drawing.Paths/ShapePath.cs +++ b/src/ImageSharp.Drawing.Paths/ShapePath.cs @@ -101,8 +101,8 @@ namespace ImageSharp.Drawing /// public override int ScanY(int y, float[] buffer, int length, int offset) { - Vector2 start = new Vector2(float.MinValue, y); - Vector2 end = new Vector2(float.MaxValue, y); + Vector2 start = new Vector2(this.Bounds.Left - 1, y); + Vector2 end = new Vector2(this.Bounds.Right + 1, y); Vector2[] innerbuffer = ArrayPool.Shared.Rent(length); try { diff --git a/src/ImageSharp.Drawing.Paths/ShapeRegion.cs b/src/ImageSharp.Drawing.Paths/ShapeRegion.cs index a1fdd7b8c..5adec6790 100644 --- a/src/ImageSharp.Drawing.Paths/ShapeRegion.cs +++ b/src/ImageSharp.Drawing.Paths/ShapeRegion.cs @@ -81,8 +81,8 @@ namespace ImageSharp.Drawing /// public override int ScanY(int y, float[] buffer, int length, int offset) { - Vector2 start = new Vector2(float.MinValue, y); - Vector2 end = new Vector2(float.MaxValue, y); + Vector2 start = new Vector2(this.Bounds.Left - 1, y); + Vector2 end = new Vector2(this.Bounds.Right + 1, y); Vector2[] innerbuffer = ArrayPool.Shared.Rent(length); try { diff --git a/src/ImageSharp.Drawing.Paths/project.json b/src/ImageSharp.Drawing.Paths/project.json index a186f362c..1bd998ea7 100644 --- a/src/ImageSharp.Drawing.Paths/project.json +++ b/src/ImageSharp.Drawing.Paths/project.json @@ -44,7 +44,7 @@ "ImageSharp.Drawing": { "target": "project" }, - "SixLabors.Shapes": "0.1.0-alpha0002", + "SixLabors.Shapes": "0.1.0-alpha0003", "StyleCop.Analyzers": { "version": "1.0.0", "type": "build" diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs index 6a37a1ae5..4f1ee34ac 100644 --- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs @@ -57,7 +57,7 @@ namespace ImageSharp.Drawing.Processors /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - Rectangle rect = RectangleF.Ceiling(this.Region.Bounds); // rounds the points out away from the center + Rectangle rect = this.Region.Bounds; int polyStartY = sourceRectangle.Y - DrawPadding; int polyEndY = sourceRectangle.Bottom + DrawPadding; @@ -102,6 +102,13 @@ namespace ImageSharp.Drawing.Processors return; } + if (pointsFound == 1 && maxIntersections > 1) + { + // we must have clipped a corner lets just duplicate it into point 2 and continue :) + buffer[1] = buffer[0]; + pointsFound++; + } + if (pointsFound % 2 == 1) { // we seem to have just clipped a corner lets just skip it @@ -246,6 +253,13 @@ namespace ImageSharp.Drawing.Processors return; } + if (pointsFound == 1 && maxIntersections > 1) + { + // we must have clipped a corner lets just duplicate it into point 2 and continue :) + buffer[1] = buffer[0]; + pointsFound++; + } + if (pointsFound % 2 == 1) { // we seem to have just clipped a corner lets just skip it diff --git a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs index 5533fbc0a..a41afd333 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs @@ -13,6 +13,7 @@ namespace ImageSharp.Tests.Drawing using System.Numerics; using Xunit; using ImageSharp.Drawing.Brushes; + using SixLabors.Shapes; public class SolidPolygonTests : FileTestBase { @@ -151,7 +152,7 @@ namespace ImageSharp.Tests.Drawing { image .BackgroundColor(Color.Blue) - .Fill(Color.HotPink, new SixLabors.Shapes.Rectangle(10,10, 190, 140)) + .Fill(Color.HotPink, new SixLabors.Shapes.Rectangle(10, 10, 190, 140)) .Save(output); } @@ -169,5 +170,78 @@ namespace ImageSharp.Tests.Drawing } } } + + [Fact] + public void ImageShouldBeOverlayedByFilledTriangle() + { + string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + + using (Image image = new Image(100, 100)) + { + using (FileStream output = File.OpenWrite($"{path}/Triangle.png")) + { + image + .BackgroundColor(Color.Blue) + .Fill(Color.HotPink, new RegularPolygon(50, 50, 3, 30)) + .Save(output); + } + + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(Color.HotPink, sourcePixels[25, 35]); + + Assert.Equal(Color.HotPink, sourcePixels[50, 79]); + + Assert.Equal(Color.HotPink, sourcePixels[75, 35]); + + Assert.Equal(Color.HotPink, sourcePixels[50, 50]); + + Assert.Equal(Color.Blue, sourcePixels[2, 2]); + + Assert.Equal(Color.Blue, sourcePixels[28, 60]); + + Assert.Equal(Color.Blue, sourcePixels[67, 67]); + } + } + } + + [Fact] + public void ImageShouldBeOverlayedByFilledSeptagon() + { + string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + + var config = Configuration.CreateDefaultInstance(); + config.ParallelOptions.MaxDegreeOfParallelism = 1; + using (Image image = new Image(100, 100, config)) + { + using (FileStream output = File.OpenWrite($"{path}/Septagon.png")) + { + image + .BackgroundColor(Color.Blue) + .Fill(Color.HotPink, new RegularPolygon(50, 50, 7, 30, -(float)Math.PI)) + .Save(output); + } + } + } + + [Fact] + public void ImageShouldBeOverlayedByFilledEllipse() + { + string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + + var config = Configuration.CreateDefaultInstance(); + config.ParallelOptions.MaxDegreeOfParallelism = 1; + using (Image image = new Image(100, 100, config)) + { + using (FileStream output = File.OpenWrite($"{path}/ellipse.png")) + { + image + .BackgroundColor(Color.Blue) + .Fill(Color.HotPink, new Ellipse(50, 50, 30, 50) + .Rotate((float)(Math.PI / 3))) + .Save(output); + } + } + } } } From 7ea3238d833032745e0c24e666f3b6d61f3e831a Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sun, 5 Feb 2017 11:44:30 +0100 Subject: [PATCH 035/142] Moved the meta data of the image to a new MetaData property. --- ImageSharp.ruleset | 1 + src/ImageSharp.Formats.Gif/GifDecoderCore.cs | 4 +- src/ImageSharp.Formats.Gif/GifEncoderCore.cs | 4 +- .../JpegDecoderCore.cs | 6 +- src/ImageSharp.Formats.Jpeg/JpegEncoder.cs | 4 +- .../JpegEncoderCore.cs | 4 +- src/ImageSharp.Formats.Png/PngDecoderCore.cs | 8 +- src/ImageSharp.Formats.Png/PngEncoderCore.cs | 12 +- .../Transforms/AutoOrient.cs | 6 +- src/ImageSharp/Image/IImage.cs | 25 ++++ src/ImageSharp/Image/IImageBase.cs | 10 +- src/ImageSharp/Image/ImageBase{TColor}.cs | 14 +- src/ImageSharp/Image/ImageFrame{TColor}.cs | 7 +- src/ImageSharp/Image/Image{TColor}.cs | 113 +++------------ src/ImageSharp/Metadata/ImageMetaData.cs | 130 ++++++++++++++++++ .../{Image => Metadata}/ImageProperty.cs | 16 +++ .../Profiles/Exif/ExifDataType.cs | 0 .../{ => Metadata}/Profiles/Exif/ExifParts.cs | 0 .../Profiles/Exif/ExifProfile.cs | 0 .../Profiles/Exif/ExifReader.cs | 0 .../{ => Metadata}/Profiles/Exif/ExifTag.cs | 0 .../Exif/ExifTagDescriptionAttribute.cs | 0 .../{ => Metadata}/Profiles/Exif/ExifValue.cs | 0 .../Profiles/Exif/ExifWriter.cs | 0 .../{ => Metadata}/Profiles/Exif/README.md | 0 .../Formats/GeneralFormatTests.cs | 4 +- .../Formats/Jpg/JpegEncoderTests.cs | 4 +- .../ImageSharp.Tests/Formats/Png/PngTests.cs | 2 +- .../{Image => Metadata}/ImagePropertyTests.cs | 0 .../Profiles/Exif/ExifProfileTests.cs | 90 ++++++------ .../Exif/ExifTagDescriptionAttributeTests.cs | 0 .../Profiles/Exif/ExifValueTests.cs | 2 +- .../Processors/Filters/AutoOrientTests.cs | 4 +- 33 files changed, 282 insertions(+), 188 deletions(-) create mode 100644 src/ImageSharp/Image/IImage.cs create mode 100644 src/ImageSharp/Metadata/ImageMetaData.cs rename src/ImageSharp/{Image => Metadata}/ImageProperty.cs (91%) rename src/ImageSharp/{ => Metadata}/Profiles/Exif/ExifDataType.cs (100%) rename src/ImageSharp/{ => Metadata}/Profiles/Exif/ExifParts.cs (100%) rename src/ImageSharp/{ => Metadata}/Profiles/Exif/ExifProfile.cs (100%) rename src/ImageSharp/{ => Metadata}/Profiles/Exif/ExifReader.cs (100%) rename src/ImageSharp/{ => Metadata}/Profiles/Exif/ExifTag.cs (100%) rename src/ImageSharp/{ => Metadata}/Profiles/Exif/ExifTagDescriptionAttribute.cs (100%) rename src/ImageSharp/{ => Metadata}/Profiles/Exif/ExifValue.cs (100%) rename src/ImageSharp/{ => Metadata}/Profiles/Exif/ExifWriter.cs (100%) rename src/ImageSharp/{ => Metadata}/Profiles/Exif/README.md (100%) rename tests/ImageSharp.Tests/{Image => Metadata}/ImagePropertyTests.cs (100%) rename tests/ImageSharp.Tests/{ => Metadata}/Profiles/Exif/ExifProfileTests.cs (68%) rename tests/ImageSharp.Tests/{ => Metadata}/Profiles/Exif/ExifTagDescriptionAttributeTests.cs (100%) rename tests/ImageSharp.Tests/{ => Metadata}/Profiles/Exif/ExifValueTests.cs (96%) diff --git a/ImageSharp.ruleset b/ImageSharp.ruleset index 554dc16dd..2daf6243a 100644 --- a/ImageSharp.ruleset +++ b/ImageSharp.ruleset @@ -1,6 +1,7 @@  + diff --git a/src/ImageSharp.Formats.Gif/GifDecoderCore.cs b/src/ImageSharp.Formats.Gif/GifDecoderCore.cs index 2be8aed37..9c367c15a 100644 --- a/src/ImageSharp.Formats.Gif/GifDecoderCore.cs +++ b/src/ImageSharp.Formats.Gif/GifDecoderCore.cs @@ -239,7 +239,7 @@ namespace ImageSharp.Formats try { this.currentStream.Read(flagBuffer, 0, flag); - this.decodedImage.Properties.Add(new ImageProperty("Comments", BitConverter.ToString(flagBuffer, 0, flag))); + this.decodedImage.MetaData.Properties.Add(new ImageProperty("Comments", BitConverter.ToString(flagBuffer, 0, flag))); } finally { @@ -323,7 +323,7 @@ namespace ImageSharp.Formats { image = this.decodedImage; - image.Quality = colorTableLength / 3; + this.decodedImage.MetaData.Quality = colorTableLength / 3; // This initializes the image to become fully transparent because the alpha channel is zero. image.InitPixels(imageWidth, imageHeight); diff --git a/src/ImageSharp.Formats.Gif/GifEncoderCore.cs b/src/ImageSharp.Formats.Gif/GifEncoderCore.cs index e5b8ba08a..c5923c1a5 100644 --- a/src/ImageSharp.Formats.Gif/GifEncoderCore.cs +++ b/src/ImageSharp.Formats.Gif/GifEncoderCore.cs @@ -65,7 +65,7 @@ namespace ImageSharp.Formats EndianBinaryWriter writer = new EndianBinaryWriter(Endianness.LittleEndian, stream); // Ensure that quality can be set but has a fallback. - int quality = this.Quality > 0 ? this.Quality : image.Quality; + int quality = this.Quality > 0 ? this.Quality : image.MetaData.Quality; this.Quality = quality > 0 ? quality.Clamp(1, 256) : 256; // Get the number of bits. @@ -91,7 +91,7 @@ namespace ImageSharp.Formats // Write additional frames. if (image.Frames.Any()) { - this.WriteApplicationExtension(writer, image.RepeatCount, image.Frames.Count); + this.WriteApplicationExtension(writer, image.MetaData.RepeatCount, image.Frames.Count); // ReSharper disable once ForCanBeConvertedToForeach for (int i = 0; i < image.Frames.Count; i++) diff --git a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs index 1fd963292..9050c20e1 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs @@ -547,8 +547,8 @@ namespace ImageSharp.Formats { if (this.isJfif && this.horizontalResolution > 0 && this.verticalResolution > 0) { - image.HorizontalResolution = this.horizontalResolution; - image.VerticalResolution = this.verticalResolution; + image.MetaData.HorizontalResolution = this.horizontalResolution; + image.MetaData.VerticalResolution = this.verticalResolution; } } @@ -951,7 +951,7 @@ namespace ImageSharp.Formats if (profile[0] == 'E' && profile[1] == 'x' && profile[2] == 'i' && profile[3] == 'f' && profile[4] == '\0' && profile[5] == '\0') { - image.ExifProfile = new ExifProfile(profile); + image.MetaData.ExifProfile = new ExifProfile(profile); } } diff --git a/src/ImageSharp.Formats.Jpeg/JpegEncoder.cs b/src/ImageSharp.Formats.Jpeg/JpegEncoder.cs index 6f404c9bb..e56a9f2e8 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegEncoder.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegEncoder.cs @@ -65,9 +65,9 @@ namespace ImageSharp.Formats where TColor : struct, IPackedPixel, IEquatable { // Ensure that quality can be set but has a fallback. - if (image.Quality > 0) + if (image.MetaData.Quality > 0) { - this.Quality = image.Quality; + this.Quality = image.MetaData.Quality; } JpegEncoderCore encode = new JpegEncoderCore(); diff --git a/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs b/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs index 984418dd3..9cc51c777 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs @@ -200,7 +200,7 @@ namespace ImageSharp.Formats int componentCount = 3; // Write the Start Of Image marker. - this.WriteApplicationHeader((short)image.HorizontalResolution, (short)image.VerticalResolution); + this.WriteApplicationHeader((short)image.MetaData.HorizontalResolution, (short)image.MetaData.VerticalResolution); this.WriteProfiles(image); @@ -706,7 +706,7 @@ namespace ImageSharp.Formats private void WriteProfiles(Image image) where TColor : struct, IPackedPixel, IEquatable { - this.WriteProfile(image.ExifProfile); + this.WriteProfile(image.MetaData.ExifProfile); } /// diff --git a/src/ImageSharp.Formats.Png/PngDecoderCore.cs b/src/ImageSharp.Formats.Png/PngDecoderCore.cs index ffc037b62..3eaa8fde3 100644 --- a/src/ImageSharp.Formats.Png/PngDecoderCore.cs +++ b/src/ImageSharp.Formats.Png/PngDecoderCore.cs @@ -174,7 +174,7 @@ namespace ImageSharp.Formats byte[] pal = new byte[currentChunk.Length]; Buffer.BlockCopy(currentChunk.Data, 0, pal, 0, currentChunk.Length); this.palette = pal; - image.Quality = pal.Length / 3; + image.MetaData.Quality = pal.Length / 3; break; case PngChunkTypes.PaletteAlpha: byte[] alpha = new byte[currentChunk.Length]; @@ -268,8 +268,8 @@ namespace ImageSharp.Formats data.ReverseBytes(4, 4); // 39.3700787 = inches in a meter. - image.HorizontalResolution = BitConverter.ToInt32(data, 0) / 39.3700787d; - image.VerticalResolution = BitConverter.ToInt32(data, 4) / 39.3700787d; + image.MetaData.HorizontalResolution = BitConverter.ToInt32(data, 0) / 39.3700787d; + image.MetaData.VerticalResolution = BitConverter.ToInt32(data, 4) / 39.3700787d; } /// @@ -777,7 +777,7 @@ namespace ImageSharp.Formats string name = Encoding.Unicode.GetString(data, 0, zeroIndex); string value = Encoding.Unicode.GetString(data, zeroIndex + 1, length - zeroIndex - 1); - image.Properties.Add(new ImageProperty(name, value)); + image.MetaData.Properties.Add(new ImageProperty(name, value)); } /// diff --git a/src/ImageSharp.Formats.Png/PngEncoderCore.cs b/src/ImageSharp.Formats.Png/PngEncoderCore.cs index 2ab42623d..46aa2187b 100644 --- a/src/ImageSharp.Formats.Png/PngEncoderCore.cs +++ b/src/ImageSharp.Formats.Png/PngEncoderCore.cs @@ -126,12 +126,12 @@ namespace ImageSharp.Formats public byte Threshold { get; set; } /// - /// Encodes the image to the specified stream from the . + /// Encodes the image to the specified stream from the . /// /// The pixel format. /// The to encode from. /// The to encode the image data to. - public void Encode(ImageBase image, Stream stream) + public void Encode(Image image, Stream stream) where TColor : struct, IPackedPixel, IEquatable { Guard.NotNull(image, nameof(image)); @@ -153,7 +153,7 @@ namespace ImageSharp.Formats stream.Write(this.chunkDataBuffer, 0, 8); // Ensure that quality can be set but has a fallback. - int quality = this.Quality > 0 ? this.Quality : image.Quality; + int quality = this.Quality > 0 ? this.Quality : image.MetaData.Quality; this.Quality = quality > 0 ? quality.Clamp(1, int.MaxValue) : int.MaxValue; // Set correct color type if the color count is 256 or less. @@ -557,11 +557,11 @@ namespace ImageSharp.Formats where TColor : struct, IPackedPixel, IEquatable { Image image = imageBase as Image; - if (image != null && image.HorizontalResolution > 0 && image.VerticalResolution > 0) + if (image != null && image.MetaData.HorizontalResolution > 0 && image.MetaData.VerticalResolution > 0) { // 39.3700787 = inches in a meter. - int dpmX = (int)Math.Round(image.HorizontalResolution * 39.3700787D); - int dpmY = (int)Math.Round(image.VerticalResolution * 39.3700787D); + int dpmX = (int)Math.Round(image.MetaData.HorizontalResolution * 39.3700787D); + int dpmY = (int)Math.Round(image.MetaData.VerticalResolution * 39.3700787D); WriteInteger(this.chunkDataBuffer, 0, dpmX); WriteInteger(this.chunkDataBuffer, 4, dpmY); diff --git a/src/ImageSharp.Processing/Transforms/AutoOrient.cs b/src/ImageSharp.Processing/Transforms/AutoOrient.cs index 8d86ae814..cb4e72a94 100644 --- a/src/ImageSharp.Processing/Transforms/AutoOrient.cs +++ b/src/ImageSharp.Processing/Transforms/AutoOrient.cs @@ -66,12 +66,12 @@ namespace ImageSharp private static Orientation GetExifOrientation(Image source) where TColor : struct, IPackedPixel, IEquatable { - if (source.ExifProfile == null) + if (source.MetaData.ExifProfile == null) { return Orientation.Unknown; } - ExifValue value = source.ExifProfile.GetValue(ExifTag.Orientation); + ExifValue value = source.MetaData.ExifProfile.GetValue(ExifTag.Orientation); if (value == null) { return Orientation.Unknown; @@ -79,7 +79,7 @@ namespace ImageSharp Orientation orientation = (Orientation)value.Value; - source.ExifProfile.SetValue(ExifTag.Orientation, (ushort)Orientation.TopLeft); + source.MetaData.ExifProfile.SetValue(ExifTag.Orientation, (ushort)Orientation.TopLeft); return orientation; } diff --git a/src/ImageSharp/Image/IImage.cs b/src/ImageSharp/Image/IImage.cs new file mode 100644 index 000000000..55abdb244 --- /dev/null +++ b/src/ImageSharp/Image/IImage.cs @@ -0,0 +1,25 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using Formats; + + /// + /// Encapsulates the basic properties and methods required to manipulate images. + /// + internal interface IImage : IImageBase + { + /// + /// Gets the currently loaded image format. + /// + IImageFormat CurrentImageFormat { get; } + + /// + /// Gets the meta data of the image. + /// + ImageMetaData MetaData { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/IImageBase.cs b/src/ImageSharp/Image/IImageBase.cs index 113b94a18..effbd6006 100644 --- a/src/ImageSharp/Image/IImageBase.cs +++ b/src/ImageSharp/Image/IImageBase.cs @@ -15,11 +15,6 @@ namespace ImageSharp /// Rectangle Bounds { get; } - /// - /// Gets or sets the quality of the image. This affects the output quality of lossy image formats. - /// - int Quality { get; set; } - /// /// Gets or sets the frame delay for animated images. /// If not 0, this field specifies the number of hundredths (1/100) of a second to @@ -52,5 +47,10 @@ namespace ImageSharp /// Gets the pixel ratio made up of the width and height. /// double PixelRatio { get; } + + /// + /// Gets the configuration providing initialization code which allows extending the library. + /// + Configuration Configuration { get; } } } \ No newline at end of file diff --git a/src/ImageSharp/Image/ImageBase{TColor}.cs b/src/ImageSharp/Image/ImageBase{TColor}.cs index b179d1158..6fcd7a3b7 100644 --- a/src/ImageSharp/Image/ImageBase{TColor}.cs +++ b/src/ImageSharp/Image/ImageBase{TColor}.cs @@ -110,9 +110,6 @@ namespace ImageSharp /// public Rectangle Bounds => new Rectangle(0, 0, this.Width, this.Height); - /// - public int Quality { get; set; } - /// public int FrameDelay { get; set; } @@ -181,16 +178,17 @@ namespace ImageSharp } /// - /// Copies the properties from the other . + /// Copies the properties from the other . /// /// - /// The other to copy the properties from. + /// The other to copy the properties from. /// - protected void CopyProperties(ImageBase other) + protected void CopyProperties(IImageBase other) { - this.Configuration = other.Configuration; - this.Quality = other.Quality; + Debug.Assert(other != null); + this.FrameDelay = other.FrameDelay; + this.Configuration = other.Configuration; } /// diff --git a/src/ImageSharp/Image/ImageFrame{TColor}.cs b/src/ImageSharp/Image/ImageFrame{TColor}.cs index 809eca1a4..b06b10bc4 100644 --- a/src/ImageSharp/Image/ImageFrame{TColor}.cs +++ b/src/ImageSharp/Image/ImageFrame{TColor}.cs @@ -55,11 +55,8 @@ namespace ImageSharp { scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction(scaleFunc); - ImageFrame target = new ImageFrame(this.Width, this.Height, this.Configuration) - { - Quality = this.Quality, - FrameDelay = this.FrameDelay - }; + ImageFrame target = new ImageFrame(this.Width, this.Height, this.Configuration); + target.CopyProperties(this); using (PixelAccessor pixels = this.Lock()) using (PixelAccessor targetPixels = target.Lock()) diff --git a/src/ImageSharp/Image/Image{TColor}.cs b/src/ImageSharp/Image/Image{TColor}.cs index 84bae39cc..7223bc1d8 100644 --- a/src/ImageSharp/Image/Image{TColor}.cs +++ b/src/ImageSharp/Image/Image{TColor}.cs @@ -23,21 +23,9 @@ namespace ImageSharp /// /// The pixel format. [DebuggerDisplay("Image: {Width}x{Height}")] - public class Image : ImageBase + public class Image : ImageBase, IImage where TColor : struct, IPackedPixel, IEquatable { - /// - /// The default horizontal resolution value (dots per inch) in x direction. - /// The default value is 96 dots per inch. - /// - public const double DefaultHorizontalResolution = 96; - - /// - /// The default vertical resolution value (dots per inch) in y direction. - /// The default value is 96 dots per inch. - /// - public const double DefaultVerticalResolution = 96; - /// /// Initializes a new instance of the class /// with the height and the width of the image. @@ -129,18 +117,9 @@ namespace ImageSharp } /// - /// Gets or sets the resolution of the image in x- direction. It is defined as - /// number of dots per inch and should be an positive value. - /// - /// The density of the image in x- direction. - public double HorizontalResolution { get; set; } = DefaultHorizontalResolution; - - /// - /// Gets or sets the resolution of the image in y- direction. It is defined as - /// number of dots per inch and should be an positive value. + /// Gets the meta data of the image. /// - /// The density of the image in y- direction. - public double VerticalResolution { get; set; } = DefaultVerticalResolution; + public ImageMetaData MetaData { get; private set; } = new ImageMetaData(); /// /// Gets the width of the image in inches. It is calculated as the width of the image @@ -152,14 +131,7 @@ namespace ImageSharp { get { - double resolution = this.HorizontalResolution; - - if (resolution <= 0) - { - resolution = DefaultHorizontalResolution; - } - - return this.Width / resolution; + return this.Width / this.MetaData.HorizontalResolution; } } @@ -173,14 +145,7 @@ namespace ImageSharp { get { - double resolution = this.VerticalResolution; - - if (resolution <= 0) - { - resolution = DefaultVerticalResolution; - } - - return this.Height / resolution; + return this.Height / this.MetaData.VerticalResolution; } } @@ -192,34 +157,17 @@ namespace ImageSharp /// public bool IsAnimated => this.Frames.Count > 0; - /// - /// Gets or sets the number of times any animation is repeated. - /// 0 means to repeat indefinitely. - /// - public ushort RepeatCount { get; set; } - /// /// Gets the other frames for the animation. /// /// The list of frame images. public IList> Frames { get; } = new List>(); - /// - /// Gets the list of properties for storing meta information about this image. - /// - /// A list of image properties. - public IList Properties { get; } = new List(); - /// /// Gets the currently loaded image format. /// public IImageFormat CurrentImageFormat { get; internal set; } - /// - /// Gets or sets the Exif profile. - /// - public ExifProfile ExifProfile { get; set; } - /// /// Applies the processor to the image. /// @@ -317,15 +265,8 @@ namespace ImageSharp { scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction(scaleFunc); - Image target = new Image(this.Width, this.Height, this.Configuration) - { - Quality = this.Quality, - FrameDelay = this.FrameDelay, - HorizontalResolution = this.HorizontalResolution, - VerticalResolution = this.VerticalResolution, - CurrentImageFormat = this.CurrentImageFormat, - RepeatCount = this.RepeatCount - }; + Image target = new Image(this.Width, this.Height, this.Configuration); + target.CopyProperties(this); using (PixelAccessor pixels = this.Lock()) using (PixelAccessor targetPixels = target.Lock()) @@ -345,11 +286,6 @@ namespace ImageSharp }); } - if (this.ExifProfile != null) - { - target.ExifProfile = new ExifProfile(this.ExifProfile); - } - for (int i = 0; i < this.Frames.Count; i++) { target.Frames.Add(this.Frames[i].To()); @@ -358,27 +294,6 @@ namespace ImageSharp return target; } - /// - /// Copies the properties from the other . - /// - /// - /// The other to copy the properties from. - /// - internal void CopyProperties(Image other) - { - base.CopyProperties(other); - - this.HorizontalResolution = other.HorizontalResolution; - this.VerticalResolution = other.VerticalResolution; - this.CurrentImageFormat = other.CurrentImageFormat; - this.RepeatCount = other.RepeatCount; - - if (other.ExifProfile != null) - { - this.ExifProfile = new ExifProfile(other.ExifProfile); - } - } - /// /// Creates a new from this instance /// @@ -400,6 +315,20 @@ namespace ImageSharp base.Dispose(disposing); } + /// + /// Copies the properties from the other . + /// + /// + /// The other to copy the properties from. + /// + private void CopyProperties(IImage other) + { + base.CopyProperties(other); + + this.CurrentImageFormat = other.CurrentImageFormat; + this.MetaData = new ImageMetaData(other.MetaData); + } + /// /// Loads the image from the given stream. /// diff --git a/src/ImageSharp/Metadata/ImageMetaData.cs b/src/ImageSharp/Metadata/ImageMetaData.cs new file mode 100644 index 000000000..30bd2c348 --- /dev/null +++ b/src/ImageSharp/Metadata/ImageMetaData.cs @@ -0,0 +1,130 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System.Collections.Generic; + using System.Diagnostics; + + /// + /// Encapsulates the metadata of an image. + /// + public sealed class ImageMetaData + { + /// + /// The default horizontal resolution value (dots per inch) in x direction. + /// The default value is 96 dots per inch. + /// + public const double DefaultHorizontalResolution = 96; + + /// + /// The default vertical resolution value (dots per inch) in y direction. + /// The default value is 96 dots per inch. + /// + public const double DefaultVerticalResolution = 96; + + private double horizontalResolution; + private double verticalResolution; + + /// + /// Initializes a new instance of the class. + /// + internal ImageMetaData() + { + this.horizontalResolution = DefaultHorizontalResolution; + this.verticalResolution = DefaultVerticalResolution; + } + + /// + /// Initializes a new instance of the class + /// by making a copy from other metadata. + /// + /// + /// The other to create this instance from. + /// + internal ImageMetaData(ImageMetaData other) + { + Debug.Assert(other != null); + + this.HorizontalResolution = other.HorizontalResolution; + this.VerticalResolution = other.VerticalResolution; + this.Quality = other.Quality; + + foreach (ImageProperty property in other.Properties) + { + this.Properties.Add(new ImageProperty(property)); + } + + if (other.ExifProfile != null) + { + this.ExifProfile = new ExifProfile(other.ExifProfile); + } + } + + /// + /// Gets or sets the resolution of the image in x- direction. It is defined as + /// number of dots per inch and should be an positive value. + /// + /// The density of the image in x- direction. + public double HorizontalResolution + { + get + { + return this.horizontalResolution; + } + + set + { + if (value >= 0) + { + this.horizontalResolution = value; + } + } + } + + /// + /// Gets or sets the resolution of the image in y- direction. It is defined as + /// number of dots per inch and should be an positive value. + /// + /// The density of the image in y- direction. + public double VerticalResolution + { + get + { + return this.verticalResolution; + } + + set + { + if (value >= 0) + { + this.verticalResolution = value; + } + } + } + + /// + /// Gets or sets the Exif profile. + /// + public ExifProfile ExifProfile { get; set; } + + /// + /// Gets the list of properties for storing meta information about this image. + /// + /// A list of image properties. + public IList Properties { get; } = new List(); + + /// + /// Gets or sets the quality of the image. This affects the output quality of lossy image formats. + /// + public int Quality { get; set; } + + /// + /// Gets or sets the number of times any animation is repeated. + /// 0 means to repeat indefinitely. + /// + public ushort RepeatCount { get; set; } + } +} diff --git a/src/ImageSharp/Image/ImageProperty.cs b/src/ImageSharp/Metadata/ImageProperty.cs similarity index 91% rename from src/ImageSharp/Image/ImageProperty.cs rename to src/ImageSharp/Metadata/ImageProperty.cs index 7fda749e9..c8bd0b23d 100644 --- a/src/ImageSharp/Image/ImageProperty.cs +++ b/src/ImageSharp/Metadata/ImageProperty.cs @@ -6,6 +6,7 @@ namespace ImageSharp { using System; + using System.Diagnostics; /// /// Stores meta information about a image, like the name of the author, @@ -27,6 +28,21 @@ namespace ImageSharp this.Value = value; } + /// + /// Initializes a new instance of the class + /// by making a copy from another property. + /// + /// + /// The other to create this instance from. + /// + internal ImageProperty(ImageProperty other) + { + Debug.Assert(other != null); + + this.Name = other.Name; + this.Value = other.Value; + } + /// /// Gets the name of this indicating which kind of /// information this property stores. diff --git a/src/ImageSharp/Profiles/Exif/ExifDataType.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs similarity index 100% rename from src/ImageSharp/Profiles/Exif/ExifDataType.cs rename to src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs diff --git a/src/ImageSharp/Profiles/Exif/ExifParts.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifParts.cs similarity index 100% rename from src/ImageSharp/Profiles/Exif/ExifParts.cs rename to src/ImageSharp/Metadata/Profiles/Exif/ExifParts.cs diff --git a/src/ImageSharp/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs similarity index 100% rename from src/ImageSharp/Profiles/Exif/ExifProfile.cs rename to src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs diff --git a/src/ImageSharp/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs similarity index 100% rename from src/ImageSharp/Profiles/Exif/ExifReader.cs rename to src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs diff --git a/src/ImageSharp/Profiles/Exif/ExifTag.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifTag.cs similarity index 100% rename from src/ImageSharp/Profiles/Exif/ExifTag.cs rename to src/ImageSharp/Metadata/Profiles/Exif/ExifTag.cs diff --git a/src/ImageSharp/Profiles/Exif/ExifTagDescriptionAttribute.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs similarity index 100% rename from src/ImageSharp/Profiles/Exif/ExifTagDescriptionAttribute.cs rename to src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs diff --git a/src/ImageSharp/Profiles/Exif/ExifValue.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifValue.cs similarity index 100% rename from src/ImageSharp/Profiles/Exif/ExifValue.cs rename to src/ImageSharp/Metadata/Profiles/Exif/ExifValue.cs diff --git a/src/ImageSharp/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs similarity index 100% rename from src/ImageSharp/Profiles/Exif/ExifWriter.cs rename to src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs diff --git a/src/ImageSharp/Profiles/Exif/README.md b/src/ImageSharp/Metadata/Profiles/Exif/README.md similarity index 100% rename from src/ImageSharp/Profiles/Exif/README.md rename to src/ImageSharp/Metadata/Profiles/Exif/README.md diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index 6873717ed..97bd34def 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -24,8 +24,8 @@ namespace ImageSharp.Tests { using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { - image.VerticalResolution = 150; - image.HorizontalResolution = 150; + image.MetaData.VerticalResolution = 150; + image.MetaData.HorizontalResolution = 150; image.Save(output); } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 59e5eba5c..ef6671931 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -36,8 +36,8 @@ namespace ImageSharp.Tests { using (Image image = provider.GetImage().Resize(new ResizeOptions { Size = new Size(150, 100), Mode = ResizeMode.Max })) { - image.Quality = quality; - image.ExifProfile = null; // Reduce the size of the file + image.MetaData.Quality = quality; + image.MetaData.ExifProfile = null; // Reduce the size of the file JpegEncoder encoder = new JpegEncoder { Subsample = subsample, Quality = quality }; provider.Utility.TestName += $"{subsample}_Q{quality}"; diff --git a/tests/ImageSharp.Tests/Formats/Png/PngTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngTests.cs index ae6487ea9..cf485c593 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngTests.cs @@ -27,7 +27,7 @@ namespace ImageSharp.Tests { using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.png")) { - image.Quality = 256; + image.MetaData.Quality = 256; image.Save(output, new PngFormat()); } } diff --git a/tests/ImageSharp.Tests/Image/ImagePropertyTests.cs b/tests/ImageSharp.Tests/Metadata/ImagePropertyTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Image/ImagePropertyTests.cs rename to tests/ImageSharp.Tests/Metadata/ImagePropertyTests.cs diff --git a/tests/ImageSharp.Tests/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs similarity index 68% rename from tests/ImageSharp.Tests/Profiles/Exif/ExifProfileTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs index 1900b58c6..61307da20 100644 --- a/tests/ImageSharp.Tests/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs @@ -19,20 +19,18 @@ namespace ImageSharp.Tests { Image image = TestFile.Create(TestImages.Jpeg.Baseline.Calliphora).CreateImage(); - Assert.Null(image.ExifProfile); + Assert.Null(image.MetaData.ExifProfile); - image.ExifProfile = new ExifProfile(); - image.ExifProfile.SetValue(ExifTag.Copyright, "Dirk Lemstra"); + image.MetaData.ExifProfile = new ExifProfile(); + image.MetaData.ExifProfile.SetValue(ExifTag.Copyright, "Dirk Lemstra"); image = WriteAndRead(image); - Assert.NotNull(image.ExifProfile); - Assert.Equal(1, image.ExifProfile.Values.Count()); + Assert.NotNull(image.MetaData.ExifProfile); + Assert.Equal(1, image.MetaData.ExifProfile.Values.Count()); - ExifValue value = image.ExifProfile.Values.FirstOrDefault(val => val.Tag == ExifTag.Copyright); + ExifValue value = image.MetaData.ExifProfile.Values.FirstOrDefault(val => val.Tag == ExifTag.Copyright); TestValue(value, "Dirk Lemstra"); - - } [Fact] @@ -70,14 +68,14 @@ namespace ImageSharp.Tests profile.SetValue(ExifTag.ExposureTime, new Rational(exposureTime)); Image image = new Image(1, 1); - image.ExifProfile = profile; + image.MetaData.ExifProfile = profile; image.SaveAsJpeg(memStream); memStream.Position = 0; image = new Image(memStream); - profile = image.ExifProfile; + profile = image.MetaData.ExifProfile; Assert.NotNull(profile); ExifValue value = profile.GetValue(ExifTag.ExposureTime); @@ -88,14 +86,14 @@ namespace ImageSharp.Tests profile = GetExifProfile(); profile.SetValue(ExifTag.ExposureTime, new Rational(exposureTime, true)); - image.ExifProfile = profile; + image.MetaData.ExifProfile = profile; image.SaveAsJpeg(memStream); memStream.Position = 0; image = new Image(memStream); - profile = image.ExifProfile; + profile = image.MetaData.ExifProfile; Assert.NotNull(profile); value = profile.GetValue(ExifTag.ExposureTime); @@ -107,24 +105,24 @@ namespace ImageSharp.Tests public void ReadWriteInfinity() { Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage(); - image.ExifProfile.SetValue(ExifTag.ExposureBiasValue, new SignedRational(double.PositiveInfinity)); + image.MetaData.ExifProfile.SetValue(ExifTag.ExposureBiasValue, new SignedRational(double.PositiveInfinity)); image = WriteAndRead(image); - ExifValue value = image.ExifProfile.GetValue(ExifTag.ExposureBiasValue); + ExifValue value = image.MetaData.ExifProfile.GetValue(ExifTag.ExposureBiasValue); Assert.NotNull(value); Assert.Equal(new SignedRational(double.PositiveInfinity), value.Value); - image.ExifProfile.SetValue(ExifTag.ExposureBiasValue, new SignedRational(double.NegativeInfinity)); + image.MetaData.ExifProfile.SetValue(ExifTag.ExposureBiasValue, new SignedRational(double.NegativeInfinity)); image = WriteAndRead(image); - value = image.ExifProfile.GetValue(ExifTag.ExposureBiasValue); + value = image.MetaData.ExifProfile.GetValue(ExifTag.ExposureBiasValue); Assert.NotNull(value); Assert.Equal(new SignedRational(double.NegativeInfinity), value.Value); - image.ExifProfile.SetValue(ExifTag.FlashEnergy, new Rational(double.NegativeInfinity)); + image.MetaData.ExifProfile.SetValue(ExifTag.FlashEnergy, new Rational(double.NegativeInfinity)); image = WriteAndRead(image); - value = image.ExifProfile.GetValue(ExifTag.FlashEnergy); + value = image.MetaData.ExifProfile.GetValue(ExifTag.FlashEnergy); Assert.NotNull(value); Assert.Equal(new Rational(double.PositiveInfinity), value.Value); } @@ -135,71 +133,71 @@ namespace ImageSharp.Tests Rational[] latitude = new Rational[] { new Rational(12.3), new Rational(4.56), new Rational(789.0) }; Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage(); - image.ExifProfile.SetValue(ExifTag.Software, "ImageSharp"); + image.MetaData.ExifProfile.SetValue(ExifTag.Software, "ImageSharp"); - ExifValue value = image.ExifProfile.GetValue(ExifTag.Software); + ExifValue value = image.MetaData.ExifProfile.GetValue(ExifTag.Software); TestValue(value, "ImageSharp"); Assert.Throws(() => { value.Value = 15; }); - image.ExifProfile.SetValue(ExifTag.ShutterSpeedValue, new SignedRational(75.55)); + image.MetaData.ExifProfile.SetValue(ExifTag.ShutterSpeedValue, new SignedRational(75.55)); - value = image.ExifProfile.GetValue(ExifTag.ShutterSpeedValue); + value = image.MetaData.ExifProfile.GetValue(ExifTag.ShutterSpeedValue); TestValue(value, new SignedRational(7555, 100)); Assert.Throws(() => { value.Value = 75; }); - image.ExifProfile.SetValue(ExifTag.XResolution, new Rational(150.0)); + image.MetaData.ExifProfile.SetValue(ExifTag.XResolution, new Rational(150.0)); - value = image.ExifProfile.GetValue(ExifTag.XResolution); + value = image.MetaData.ExifProfile.GetValue(ExifTag.XResolution); TestValue(value, new Rational(150, 1)); Assert.Throws(() => { value.Value = "ImageSharp"; }); - image.ExifProfile.SetValue(ExifTag.ReferenceBlackWhite, null); + image.MetaData.ExifProfile.SetValue(ExifTag.ReferenceBlackWhite, null); - value = image.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite); + value = image.MetaData.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite); TestValue(value, (string)null); - image.ExifProfile.SetValue(ExifTag.GPSLatitude, latitude); + image.MetaData.ExifProfile.SetValue(ExifTag.GPSLatitude, latitude); - value = image.ExifProfile.GetValue(ExifTag.GPSLatitude); + value = image.MetaData.ExifProfile.GetValue(ExifTag.GPSLatitude); TestValue(value, latitude); image = WriteAndRead(image); - Assert.NotNull(image.ExifProfile); - Assert.Equal(17, image.ExifProfile.Values.Count()); + Assert.NotNull(image.MetaData.ExifProfile); + Assert.Equal(17, image.MetaData.ExifProfile.Values.Count()); - value = image.ExifProfile.GetValue(ExifTag.Software); + value = image.MetaData.ExifProfile.GetValue(ExifTag.Software); TestValue(value, "ImageSharp"); - value = image.ExifProfile.GetValue(ExifTag.ShutterSpeedValue); + value = image.MetaData.ExifProfile.GetValue(ExifTag.ShutterSpeedValue); TestValue(value, new SignedRational(75.55)); - value = image.ExifProfile.GetValue(ExifTag.XResolution); + value = image.MetaData.ExifProfile.GetValue(ExifTag.XResolution); TestValue(value, new Rational(150.0)); - value = image.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite); + value = image.MetaData.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite); Assert.Null(value); - value = image.ExifProfile.GetValue(ExifTag.GPSLatitude); + value = image.MetaData.ExifProfile.GetValue(ExifTag.GPSLatitude); TestValue(value, latitude); - image.ExifProfile.Parts = ExifParts.ExifTags; + image.MetaData.ExifProfile.Parts = ExifParts.ExifTags; image = WriteAndRead(image); - Assert.NotNull(image.ExifProfile); - Assert.Equal(8, image.ExifProfile.Values.Count()); + Assert.NotNull(image.MetaData.ExifProfile); + Assert.Equal(8, image.MetaData.ExifProfile.Values.Count()); - Assert.NotNull(image.ExifProfile.GetValue(ExifTag.ColorSpace)); - Assert.True(image.ExifProfile.RemoveValue(ExifTag.ColorSpace)); - Assert.False(image.ExifProfile.RemoveValue(ExifTag.ColorSpace)); - Assert.Null(image.ExifProfile.GetValue(ExifTag.ColorSpace)); + Assert.NotNull(image.MetaData.ExifProfile.GetValue(ExifTag.ColorSpace)); + Assert.True(image.MetaData.ExifProfile.RemoveValue(ExifTag.ColorSpace)); + Assert.False(image.MetaData.ExifProfile.RemoveValue(ExifTag.ColorSpace)); + Assert.Null(image.MetaData.ExifProfile.GetValue(ExifTag.ColorSpace)); - Assert.Equal(7, image.ExifProfile.Values.Count()); + Assert.Equal(7, image.MetaData.ExifProfile.Values.Count()); } [Fact] @@ -225,8 +223,8 @@ namespace ImageSharp.Tests } Image image = new Image(100, 100); - image.ExifProfile = new ExifProfile(); - image.ExifProfile.SetValue(ExifTag.ImageDescription, junk.ToString()); + image.MetaData.ExifProfile = new ExifProfile(); + image.MetaData.ExifProfile.SetValue(ExifTag.ImageDescription, junk.ToString()); using (MemoryStream memStream = new MemoryStream()) { @@ -238,7 +236,7 @@ namespace ImageSharp.Tests { Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage(); - ExifProfile profile = image.ExifProfile; + ExifProfile profile = image.MetaData.ExifProfile; Assert.NotNull(profile); return profile; diff --git a/tests/ImageSharp.Tests/Profiles/Exif/ExifTagDescriptionAttributeTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifTagDescriptionAttributeTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Profiles/Exif/ExifTagDescriptionAttributeTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifTagDescriptionAttributeTests.cs diff --git a/tests/ImageSharp.Tests/Profiles/Exif/ExifValueTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifValueTests.cs similarity index 96% rename from tests/ImageSharp.Tests/Profiles/Exif/ExifValueTests.cs rename to tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifValueTests.cs index e777d9e3b..2014d08dc 100644 --- a/tests/ImageSharp.Tests/Profiles/Exif/ExifValueTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifValueTests.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Tests ExifProfile profile; using (Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage()) { - profile = image.ExifProfile; + profile = image.MetaData.ExifProfile; } Assert.NotNull(profile); diff --git a/tests/ImageSharp.Tests/Processors/Filters/AutoOrientTests.cs b/tests/ImageSharp.Tests/Processors/Filters/AutoOrientTests.cs index 499bdff82..ef183480c 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/AutoOrientTests.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/AutoOrientTests.cs @@ -35,8 +35,8 @@ namespace ImageSharp.Tests using (Image image = file.CreateImage()) { - image.ExifProfile = new ExifProfile(); - image.ExifProfile.SetValue(ExifTag.Orientation, orientation); + image.MetaData.ExifProfile = new ExifProfile(); + image.MetaData.ExifProfile.SetValue(ExifTag.Orientation, orientation); using (FileStream before = File.OpenWrite($"{path}/before-{file.FileName}")) using (FileStream after = File.OpenWrite($"{path}/after-{file.FileName}")) From 42b2b666e08f610e9a4eb7db9f1622a565b35663 Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sun, 5 Feb 2017 12:18:03 +0100 Subject: [PATCH 036/142] Added metadata class for image frames. --- src/ImageSharp.Formats.Gif/GifDecoderCore.cs | 27 ++++++++---- src/ImageSharp.Formats.Gif/GifEncoderCore.cs | 31 ++++++++++++- src/ImageSharp/Image/IImageBase.cs | 8 ---- src/ImageSharp/Image/IImageFrame.cs | 18 ++++++++ src/ImageSharp/Image/ImageBase{TColor}.cs | 4 -- src/ImageSharp/Image/ImageFrame{TColor}.cs | 20 ++++++++- src/ImageSharp/Metadata/IMetaData.cs | 21 +++++++++ src/ImageSharp/Metadata/ImageFrameMetaData.cs | 44 +++++++++++++++++++ src/ImageSharp/Metadata/ImageMetaData.cs | 12 ++++- .../Metadata/ImageFrameMetaDataTests.cs | 26 +++++++++++ .../Metadata/ImageMetaDataTests.cs | 42 ++++++++++++++++++ 11 files changed, 229 insertions(+), 24 deletions(-) create mode 100644 src/ImageSharp/Image/IImageFrame.cs create mode 100644 src/ImageSharp/Metadata/IMetaData.cs create mode 100644 src/ImageSharp/Metadata/ImageFrameMetaData.cs create mode 100644 tests/ImageSharp.Tests/Metadata/ImageFrameMetaDataTests.cs create mode 100644 tests/ImageSharp.Tests/Metadata/ImageMetaDataTests.cs diff --git a/src/ImageSharp.Formats.Gif/GifDecoderCore.cs b/src/ImageSharp.Formats.Gif/GifDecoderCore.cs index 9c367c15a..e6f7b6136 100644 --- a/src/ImageSharp.Formats.Gif/GifDecoderCore.cs +++ b/src/ImageSharp.Formats.Gif/GifDecoderCore.cs @@ -321,12 +321,14 @@ namespace ImageSharp.Formats if (this.previousFrame == null) { - image = this.decodedImage; - this.decodedImage.MetaData.Quality = colorTableLength / 3; // This initializes the image to become fully transparent because the alpha channel is zero. - image.InitPixels(imageWidth, imageHeight); + this.decodedImage.InitPixels(imageWidth, imageHeight); + + this.SetFrameDelay(this.decodedImage.MetaData); + + image = this.decodedImage; } else { @@ -338,6 +340,8 @@ namespace ImageSharp.Formats currentFrame = this.previousFrame.Clone(); + this.SetFrameDelay(currentFrame.MetaData); + image = currentFrame; this.RestoreToBackground(image); @@ -345,11 +349,6 @@ namespace ImageSharp.Formats this.decodedImage.Frames.Add(currentFrame); } - if (this.graphicsControlExtension != null && this.graphicsControlExtension.DelayTime > 0) - { - image.FrameDelay = this.graphicsControlExtension.DelayTime; - } - int i = 0; int interlacePass = 0; // The interlace pass int interlaceIncrement = 8; // The interlacing line increment @@ -465,5 +464,17 @@ namespace ImageSharp.Formats this.restoreArea = null; } + + /// + /// Sets the frame delay in the metadata. + /// + /// The meta data. + private void SetFrameDelay(IMetaData metaData) + { + if (this.graphicsControlExtension != null && this.graphicsControlExtension.DelayTime > 0) + { + metaData.FrameDelay = this.graphicsControlExtension.DelayTime; + } + } } } \ No newline at end of file diff --git a/src/ImageSharp.Formats.Gif/GifEncoderCore.cs b/src/ImageSharp.Formats.Gif/GifEncoderCore.cs index c5923c1a5..80c9ee36b 100644 --- a/src/ImageSharp.Formats.Gif/GifEncoderCore.cs +++ b/src/ImageSharp.Formats.Gif/GifEncoderCore.cs @@ -229,14 +229,41 @@ namespace ImageSharp.Formats } } + /// + /// Writes the graphics control extension to the stream. + /// + /// The pixel format. + /// The to encode. + /// The stream to write to. + /// The index of the color in the color palette to make transparent. + private void WriteGraphicalControlExtension(Image image, EndianBinaryWriter writer, int transparencyIndex) + where TColor : struct, IPackedPixel, IEquatable + { + this.WriteGraphicalControlExtension(image, image.MetaData, writer, transparencyIndex); + } + + /// + /// Writes the graphics control extension to the stream. + /// + /// The pixel format. + /// The to encode. + /// The stream to write to. + /// The index of the color in the color palette to make transparent. + private void WriteGraphicalControlExtension(ImageFrame imageFrame, EndianBinaryWriter writer, int transparencyIndex) + where TColor : struct, IPackedPixel, IEquatable + { + this.WriteGraphicalControlExtension(imageFrame, imageFrame.MetaData, writer, transparencyIndex); + } + /// /// Writes the graphics control extension to the stream. /// /// The pixel format. /// The to encode. + /// The metadata of the image or frame. /// The stream to write to. /// The index of the color in the color palette to make transparent. - private void WriteGraphicalControlExtension(ImageBase image, EndianBinaryWriter writer, int transparencyIndex) + private void WriteGraphicalControlExtension(ImageBase image, IMetaData metaData, EndianBinaryWriter writer, int transparencyIndex) where TColor : struct, IPackedPixel, IEquatable { // TODO: Check transparency logic. @@ -250,7 +277,7 @@ namespace ImageSharp.Formats DisposalMethod = disposalMethod, TransparencyFlag = hasTransparent, TransparencyIndex = transparencyIndex, - DelayTime = image.FrameDelay + DelayTime = metaData.FrameDelay }; // Write the intro. diff --git a/src/ImageSharp/Image/IImageBase.cs b/src/ImageSharp/Image/IImageBase.cs index effbd6006..707fea235 100644 --- a/src/ImageSharp/Image/IImageBase.cs +++ b/src/ImageSharp/Image/IImageBase.cs @@ -15,14 +15,6 @@ namespace ImageSharp /// Rectangle Bounds { get; } - /// - /// Gets or sets the frame delay for animated images. - /// If not 0, this field specifies the number of hundredths (1/100) of a second to - /// wait before continuing with the processing of the Data Stream. - /// The clock starts ticking immediately after the graphic is rendered. - /// - int FrameDelay { get; set; } - /// /// Gets or sets the maximum allowable width in pixels. /// diff --git a/src/ImageSharp/Image/IImageFrame.cs b/src/ImageSharp/Image/IImageFrame.cs new file mode 100644 index 000000000..bf3261d93 --- /dev/null +++ b/src/ImageSharp/Image/IImageFrame.cs @@ -0,0 +1,18 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + /// + /// Encapsulates the basic properties and methods required to manipulate images. + /// + internal interface IImageFrame : IImageBase + { + /// + /// Gets the meta data of the image. + /// + ImageFrameMetaData MetaData { get; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/ImageBase{TColor}.cs b/src/ImageSharp/Image/ImageBase{TColor}.cs index 6fcd7a3b7..fd82f7730 100644 --- a/src/ImageSharp/Image/ImageBase{TColor}.cs +++ b/src/ImageSharp/Image/ImageBase{TColor}.cs @@ -110,9 +110,6 @@ namespace ImageSharp /// public Rectangle Bounds => new Rectangle(0, 0, this.Width, this.Height); - /// - public int FrameDelay { get; set; } - /// /// Gets the configuration providing initialization code which allows extending the library. /// @@ -187,7 +184,6 @@ namespace ImageSharp { Debug.Assert(other != null); - this.FrameDelay = other.FrameDelay; this.Configuration = other.Configuration; } diff --git a/src/ImageSharp/Image/ImageFrame{TColor}.cs b/src/ImageSharp/Image/ImageFrame{TColor}.cs index b06b10bc4..02e5b7161 100644 --- a/src/ImageSharp/Image/ImageFrame{TColor}.cs +++ b/src/ImageSharp/Image/ImageFrame{TColor}.cs @@ -13,7 +13,7 @@ namespace ImageSharp /// Represents a single frame in a animation. /// /// The pixel format. - public class ImageFrame : ImageBase + public class ImageFrame : ImageBase, IImageFrame where TColor : struct, IPackedPixel, IEquatable { /// @@ -38,6 +38,11 @@ namespace ImageSharp { } + /// + /// Gets the meta data of the frame. + /// + public ImageFrameMetaData MetaData { get; private set; } = new ImageFrameMetaData(); + /// public override string ToString() { @@ -87,5 +92,18 @@ namespace ImageSharp { return new ImageFrame(this); } + + /// + /// Copies the properties from the other . + /// + /// + /// The other to copy the properties from. + /// + private void CopyProperties(IImageFrame other) + { + base.CopyProperties(other); + + this.MetaData = new ImageFrameMetaData(other.MetaData); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Metadata/IMetaData.cs b/src/ImageSharp/Metadata/IMetaData.cs new file mode 100644 index 000000000..38fd31349 --- /dev/null +++ b/src/ImageSharp/Metadata/IMetaData.cs @@ -0,0 +1,21 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + /// + /// Encapsulates the metadata of an image frame. + /// + internal interface IMetaData + { + /// + /// Gets or sets the frame delay for animated images. + /// If not 0, this field specifies the number of hundredths (1/100) of a second to + /// wait before continuing with the processing of the Data Stream. + /// The clock starts ticking immediately after the graphic is rendered. + /// + int FrameDelay { get; set; } + } +} diff --git a/src/ImageSharp/Metadata/ImageFrameMetaData.cs b/src/ImageSharp/Metadata/ImageFrameMetaData.cs new file mode 100644 index 000000000..c2277686f --- /dev/null +++ b/src/ImageSharp/Metadata/ImageFrameMetaData.cs @@ -0,0 +1,44 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System.Diagnostics; + + /// + /// Encapsulates the metadata of an image frame. + /// + public sealed class ImageFrameMetaData : IMetaData + { + /// + /// Initializes a new instance of the class. + /// + internal ImageFrameMetaData() + { + } + + /// + /// Initializes a new instance of the class + /// by making a copy from other metadata. + /// + /// + /// The other to create this instance from. + /// + internal ImageFrameMetaData(ImageFrameMetaData other) + { + Debug.Assert(other != null); + + this.FrameDelay = other.FrameDelay; + } + + /// + /// Gets or sets the frame delay for animated images. + /// If not 0, this field specifies the number of hundredths (1/100) of a second to + /// wait before continuing with the processing of the Data Stream. + /// The clock starts ticking immediately after the graphic is rendered. + /// + public int FrameDelay { get; set; } + } +} diff --git a/src/ImageSharp/Metadata/ImageMetaData.cs b/src/ImageSharp/Metadata/ImageMetaData.cs index 30bd2c348..a38899840 100644 --- a/src/ImageSharp/Metadata/ImageMetaData.cs +++ b/src/ImageSharp/Metadata/ImageMetaData.cs @@ -11,7 +11,7 @@ namespace ImageSharp /// /// Encapsulates the metadata of an image. /// - public sealed class ImageMetaData + public sealed class ImageMetaData : IMetaData { /// /// The default horizontal resolution value (dots per inch) in x direction. @@ -51,6 +51,8 @@ namespace ImageSharp this.HorizontalResolution = other.HorizontalResolution; this.VerticalResolution = other.VerticalResolution; this.Quality = other.Quality; + this.FrameDelay = other.FrameDelay; + this.RepeatCount = other.RepeatCount; foreach (ImageProperty property in other.Properties) { @@ -110,6 +112,14 @@ namespace ImageSharp /// public ExifProfile ExifProfile { get; set; } + /// + /// Gets or sets the frame delay for animated images. + /// If not 0, this field specifies the number of hundredths (1/100) of a second to + /// wait before continuing with the processing of the Data Stream. + /// The clock starts ticking immediately after the graphic is rendered. + /// + public int FrameDelay { get; set; } + /// /// Gets the list of properties for storing meta information about this image. /// diff --git a/tests/ImageSharp.Tests/Metadata/ImageFrameMetaDataTests.cs b/tests/ImageSharp.Tests/Metadata/ImageFrameMetaDataTests.cs new file mode 100644 index 000000000..24dd2eac5 --- /dev/null +++ b/tests/ImageSharp.Tests/Metadata/ImageFrameMetaDataTests.cs @@ -0,0 +1,26 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using Xunit; + + /// + /// Tests the class. + /// + public class ImageFrameMetaDataTests + { + [Fact] + public void ConstructorImageFrameMetaData() + { + ImageFrameMetaData metaData = new ImageFrameMetaData(); + metaData.FrameDelay = 42; + + ImageFrameMetaData clone = new ImageFrameMetaData(metaData); + + Assert.Equal(42, clone.FrameDelay); + } + } +} diff --git a/tests/ImageSharp.Tests/Metadata/ImageMetaDataTests.cs b/tests/ImageSharp.Tests/Metadata/ImageMetaDataTests.cs new file mode 100644 index 000000000..9034b88c0 --- /dev/null +++ b/tests/ImageSharp.Tests/Metadata/ImageMetaDataTests.cs @@ -0,0 +1,42 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using Xunit; + + /// + /// Tests the class. + /// + public class ImageMetaDataTests + { + [Fact] + public void ConstructorImageMetaData() + { + ImageMetaData metaData = new ImageMetaData(); + + ExifProfile exifProfile = new ExifProfile(); + ImageProperty imageProperty = new ImageProperty("name", "value"); + + metaData.ExifProfile = exifProfile; + metaData.FrameDelay = 42; + metaData.HorizontalResolution = 4; + metaData.VerticalResolution = 2; + metaData.Properties.Add(imageProperty); + metaData.Quality = 24; + metaData.RepeatCount = 1; + + ImageMetaData clone = new ImageMetaData(metaData); + + Assert.Equal(exifProfile.ToByteArray(), clone.ExifProfile.ToByteArray()); + Assert.Equal(42, clone.FrameDelay); + Assert.Equal(4, clone.HorizontalResolution); + Assert.Equal(2, clone.VerticalResolution); + Assert.Equal(imageProperty, clone.Properties[0]); + Assert.Equal(24, clone.Quality); + Assert.Equal(1, clone.RepeatCount); + } + } +} From 9fbc44e14cfaf2eba0bfe7a1ff66d76712a239c1 Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sun, 5 Feb 2017 12:34:36 +0100 Subject: [PATCH 037/142] Fixed folder name. --- src/ImageSharp/{Metadata => MetaData}/IMetaData.cs | 0 src/ImageSharp/{Metadata => MetaData}/ImageFrameMetaData.cs | 0 src/ImageSharp/{Metadata => MetaData}/ImageMetaData.cs | 0 src/ImageSharp/{Metadata => MetaData}/ImageProperty.cs | 0 .../{Metadata => MetaData}/Profiles/Exif/ExifDataType.cs | 0 src/ImageSharp/{Metadata => MetaData}/Profiles/Exif/ExifParts.cs | 0 .../{Metadata => MetaData}/Profiles/Exif/ExifProfile.cs | 0 src/ImageSharp/{Metadata => MetaData}/Profiles/Exif/ExifReader.cs | 0 src/ImageSharp/{Metadata => MetaData}/Profiles/Exif/ExifTag.cs | 0 .../Profiles/Exif/ExifTagDescriptionAttribute.cs | 0 src/ImageSharp/{Metadata => MetaData}/Profiles/Exif/ExifValue.cs | 0 src/ImageSharp/{Metadata => MetaData}/Profiles/Exif/ExifWriter.cs | 0 src/ImageSharp/{Metadata => MetaData}/Profiles/Exif/README.md | 0 .../{Metadata => MetaData}/ImageFrameMetaDataTests.cs | 0 .../ImageSharp.Tests/{Metadata => MetaData}/ImageMetaDataTests.cs | 0 .../ImageSharp.Tests/{Metadata => MetaData}/ImagePropertyTests.cs | 0 .../{Metadata => MetaData}/Profiles/Exif/ExifProfileTests.cs | 0 .../Profiles/Exif/ExifTagDescriptionAttributeTests.cs | 0 .../{Metadata => MetaData}/Profiles/Exif/ExifValueTests.cs | 0 19 files changed, 0 insertions(+), 0 deletions(-) rename src/ImageSharp/{Metadata => MetaData}/IMetaData.cs (100%) rename src/ImageSharp/{Metadata => MetaData}/ImageFrameMetaData.cs (100%) rename src/ImageSharp/{Metadata => MetaData}/ImageMetaData.cs (100%) rename src/ImageSharp/{Metadata => MetaData}/ImageProperty.cs (100%) rename src/ImageSharp/{Metadata => MetaData}/Profiles/Exif/ExifDataType.cs (100%) rename src/ImageSharp/{Metadata => MetaData}/Profiles/Exif/ExifParts.cs (100%) rename src/ImageSharp/{Metadata => MetaData}/Profiles/Exif/ExifProfile.cs (100%) rename src/ImageSharp/{Metadata => MetaData}/Profiles/Exif/ExifReader.cs (100%) rename src/ImageSharp/{Metadata => MetaData}/Profiles/Exif/ExifTag.cs (100%) rename src/ImageSharp/{Metadata => MetaData}/Profiles/Exif/ExifTagDescriptionAttribute.cs (100%) rename src/ImageSharp/{Metadata => MetaData}/Profiles/Exif/ExifValue.cs (100%) rename src/ImageSharp/{Metadata => MetaData}/Profiles/Exif/ExifWriter.cs (100%) rename src/ImageSharp/{Metadata => MetaData}/Profiles/Exif/README.md (100%) rename tests/ImageSharp.Tests/{Metadata => MetaData}/ImageFrameMetaDataTests.cs (100%) rename tests/ImageSharp.Tests/{Metadata => MetaData}/ImageMetaDataTests.cs (100%) rename tests/ImageSharp.Tests/{Metadata => MetaData}/ImagePropertyTests.cs (100%) rename tests/ImageSharp.Tests/{Metadata => MetaData}/Profiles/Exif/ExifProfileTests.cs (100%) rename tests/ImageSharp.Tests/{Metadata => MetaData}/Profiles/Exif/ExifTagDescriptionAttributeTests.cs (100%) rename tests/ImageSharp.Tests/{Metadata => MetaData}/Profiles/Exif/ExifValueTests.cs (100%) diff --git a/src/ImageSharp/Metadata/IMetaData.cs b/src/ImageSharp/MetaData/IMetaData.cs similarity index 100% rename from src/ImageSharp/Metadata/IMetaData.cs rename to src/ImageSharp/MetaData/IMetaData.cs diff --git a/src/ImageSharp/Metadata/ImageFrameMetaData.cs b/src/ImageSharp/MetaData/ImageFrameMetaData.cs similarity index 100% rename from src/ImageSharp/Metadata/ImageFrameMetaData.cs rename to src/ImageSharp/MetaData/ImageFrameMetaData.cs diff --git a/src/ImageSharp/Metadata/ImageMetaData.cs b/src/ImageSharp/MetaData/ImageMetaData.cs similarity index 100% rename from src/ImageSharp/Metadata/ImageMetaData.cs rename to src/ImageSharp/MetaData/ImageMetaData.cs diff --git a/src/ImageSharp/Metadata/ImageProperty.cs b/src/ImageSharp/MetaData/ImageProperty.cs similarity index 100% rename from src/ImageSharp/Metadata/ImageProperty.cs rename to src/ImageSharp/MetaData/ImageProperty.cs diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs similarity index 100% rename from src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs rename to src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifParts.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifParts.cs similarity index 100% rename from src/ImageSharp/Metadata/Profiles/Exif/ExifParts.cs rename to src/ImageSharp/MetaData/Profiles/Exif/ExifParts.cs diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs similarity index 100% rename from src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs rename to src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs similarity index 100% rename from src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs rename to src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifTag.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifTag.cs similarity index 100% rename from src/ImageSharp/Metadata/Profiles/Exif/ExifTag.cs rename to src/ImageSharp/MetaData/Profiles/Exif/ExifTag.cs diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifTagDescriptionAttribute.cs similarity index 100% rename from src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs rename to src/ImageSharp/MetaData/Profiles/Exif/ExifTagDescriptionAttribute.cs diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifValue.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs similarity index 100% rename from src/ImageSharp/Metadata/Profiles/Exif/ExifValue.cs rename to src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs similarity index 100% rename from src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs rename to src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs diff --git a/src/ImageSharp/Metadata/Profiles/Exif/README.md b/src/ImageSharp/MetaData/Profiles/Exif/README.md similarity index 100% rename from src/ImageSharp/Metadata/Profiles/Exif/README.md rename to src/ImageSharp/MetaData/Profiles/Exif/README.md diff --git a/tests/ImageSharp.Tests/Metadata/ImageFrameMetaDataTests.cs b/tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Metadata/ImageFrameMetaDataTests.cs rename to tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs diff --git a/tests/ImageSharp.Tests/Metadata/ImageMetaDataTests.cs b/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Metadata/ImageMetaDataTests.cs rename to tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs diff --git a/tests/ImageSharp.Tests/Metadata/ImagePropertyTests.cs b/tests/ImageSharp.Tests/MetaData/ImagePropertyTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Metadata/ImagePropertyTests.cs rename to tests/ImageSharp.Tests/MetaData/ImagePropertyTests.cs diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs rename to tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifTagDescriptionAttributeTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifTagDescriptionAttributeTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifTagDescriptionAttributeTests.cs rename to tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifTagDescriptionAttributeTests.cs diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifValueTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifValueTests.cs rename to tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs From 1cfaa58723be68964047c72d9e3263b861f09e0b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 5 Feb 2017 23:01:25 +1100 Subject: [PATCH 038/142] Remove incorrect Vectors facade. --- src/ImageSharp.Drawing.Paths/project.json | 3 +-- src/ImageSharp.Drawing/project.json | 3 +-- src/ImageSharp.Formats.Bmp/project.json | 3 +-- src/ImageSharp.Formats.Gif/project.json | 3 +-- src/ImageSharp.Formats.Jpeg/project.json | 3 +-- src/ImageSharp.Formats.Png/project.json | 3 +-- src/ImageSharp.Processing/project.json | 3 +-- src/ImageSharp/project.json | 3 +-- 8 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/ImageSharp.Drawing.Paths/project.json b/src/ImageSharp.Drawing.Paths/project.json index 1bd998ea7..ee3239e79 100644 --- a/src/ImageSharp.Drawing.Paths/project.json +++ b/src/ImageSharp.Drawing.Paths/project.json @@ -88,8 +88,7 @@ }, "frameworkAssemblies": { "System.Runtime": { "type": "build" }, - "System.Numerics": "4.0.0.0", - "System.Numerics.Vectors": "4.0.0.0" + "System.Numerics": "4.0.0.0" } } } diff --git a/src/ImageSharp.Drawing/project.json b/src/ImageSharp.Drawing/project.json index ff2900d9f..98cf20c1d 100644 --- a/src/ImageSharp.Drawing/project.json +++ b/src/ImageSharp.Drawing/project.json @@ -87,8 +87,7 @@ }, "frameworkAssemblies": { "System.Runtime": { "type": "build" }, - "System.Numerics": "4.0.0.0", - "System.Numerics.Vectors": "4.0.0.0" + "System.Numerics": "4.0.0.0" } } } diff --git a/src/ImageSharp.Formats.Bmp/project.json b/src/ImageSharp.Formats.Bmp/project.json index e66ecaf24..0c96fd7df 100644 --- a/src/ImageSharp.Formats.Bmp/project.json +++ b/src/ImageSharp.Formats.Bmp/project.json @@ -85,8 +85,7 @@ }, "frameworkAssemblies": { "System.Runtime": { "type": "build" }, - "System.Numerics": "4.0.0.0", - "System.Numerics.Vectors": "4.0.0.0" + "System.Numerics": "4.0.0.0" } } } diff --git a/src/ImageSharp.Formats.Gif/project.json b/src/ImageSharp.Formats.Gif/project.json index a501fe1c6..6fe75dbcb 100644 --- a/src/ImageSharp.Formats.Gif/project.json +++ b/src/ImageSharp.Formats.Gif/project.json @@ -85,8 +85,7 @@ }, "frameworkAssemblies": { "System.Runtime": { "type": "build" }, - "System.Numerics": "4.0.0.0", - "System.Numerics.Vectors": "4.0.0.0" + "System.Numerics": "4.0.0.0" } } } diff --git a/src/ImageSharp.Formats.Jpeg/project.json b/src/ImageSharp.Formats.Jpeg/project.json index c7e6f7e67..2d84ff053 100644 --- a/src/ImageSharp.Formats.Jpeg/project.json +++ b/src/ImageSharp.Formats.Jpeg/project.json @@ -85,8 +85,7 @@ }, "frameworkAssemblies": { "System.Runtime": { "type": "build" }, - "System.Numerics": "4.0.0.0", - "System.Numerics.Vectors": "4.0.0.0" + "System.Numerics": "4.0.0.0" } } } diff --git a/src/ImageSharp.Formats.Png/project.json b/src/ImageSharp.Formats.Png/project.json index bd8c2a323..a56397fb0 100644 --- a/src/ImageSharp.Formats.Png/project.json +++ b/src/ImageSharp.Formats.Png/project.json @@ -85,8 +85,7 @@ }, "frameworkAssemblies": { "System.Runtime": { "type": "build" }, - "System.Numerics": "4.0.0.0", - "System.Numerics.Vectors": "4.0.0.0" + "System.Numerics": "4.0.0.0" } } } diff --git a/src/ImageSharp.Processing/project.json b/src/ImageSharp.Processing/project.json index cf4d2fc24..910d5fc83 100644 --- a/src/ImageSharp.Processing/project.json +++ b/src/ImageSharp.Processing/project.json @@ -84,8 +84,7 @@ }, "frameworkAssemblies": { "System.Runtime": { "type": "build" }, - "System.Numerics": "4.0.0.0", - "System.Numerics.Vectors": "4.0.0.0" + "System.Numerics": "4.0.0.0" } } } diff --git a/src/ImageSharp/project.json b/src/ImageSharp/project.json index f5f0f103b..8ad3fd71a 100644 --- a/src/ImageSharp/project.json +++ b/src/ImageSharp/project.json @@ -81,8 +81,7 @@ }, "frameworkAssemblies": { "System.Runtime": { "type": "build" }, - "System.Numerics": "4.0.0.0", - "System.Numerics.Vectors": "4.0.0.0" + "System.Numerics": "4.0.0.0" } } } From 7024deccab987d53bddc2a78cfaa00869dc9f1a5 Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sun, 5 Feb 2017 13:14:35 +0100 Subject: [PATCH 039/142] Added method to update the profile data (based on #96) --- .../JpegEncoderCore.cs | 1 + src/ImageSharp/MetaData/ImageMetaData.cs | 29 ++++++++++++++++ .../MetaData/ImageMetaDataTests.cs | 33 +++++++++++++++++++ .../Profiles/Exif/ExifProfileTests.cs | 3 ++ 4 files changed, 66 insertions(+) diff --git a/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs b/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs index 9cc51c777..657470f5c 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs @@ -706,6 +706,7 @@ namespace ImageSharp.Formats private void WriteProfiles(Image image) where TColor : struct, IPackedPixel, IEquatable { + image.MetaData.SyncProfiles(); this.WriteProfile(image.MetaData.ExifProfile); } diff --git a/src/ImageSharp/MetaData/ImageMetaData.cs b/src/ImageSharp/MetaData/ImageMetaData.cs index a38899840..0bd0d0f78 100644 --- a/src/ImageSharp/MetaData/ImageMetaData.cs +++ b/src/ImageSharp/MetaData/ImageMetaData.cs @@ -136,5 +136,34 @@ namespace ImageSharp /// 0 means to repeat indefinitely. /// public ushort RepeatCount { get; set; } + + /// + /// Synchronizes the profiles with the current meta data. + /// + internal void SyncProfiles() + { + this.SyncExifProfile(); + } + + private void SyncExifProfile() + { + if (this.ExifProfile == null) + { + return; + } + + this.SyncExifResolution(ExifTag.XResolution, this.HorizontalResolution); + this.SyncExifResolution(ExifTag.YResolution, this.VerticalResolution); + } + + private void SyncExifResolution(ExifTag tag, double resolution) + { + ExifValue value = this.ExifProfile.GetValue(tag); + if (value != null) + { + Rational newResolution = new Rational(resolution, false); + this.ExifProfile.SetValue(tag, newResolution); + } + } } } diff --git a/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs b/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs index 9034b88c0..d9bd52c73 100644 --- a/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs +++ b/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs @@ -38,5 +38,38 @@ namespace ImageSharp.Tests Assert.Equal(24, clone.Quality); Assert.Equal(1, clone.RepeatCount); } + + [Fact] + public void SyncProfiles() + { + ExifProfile exifProfile = new ExifProfile(); + exifProfile.SetValue(ExifTag.XResolution, new Rational(200)); + exifProfile.SetValue(ExifTag.YResolution, new Rational(300)); + + Image image = new Image(1, 1); + image.MetaData.ExifProfile = exifProfile; + image.MetaData.HorizontalResolution = 200; + image.MetaData.VerticalResolution = 300; + + image.MetaData.HorizontalResolution = 100; + + Assert.Equal(200, ((Rational)image.MetaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); + Assert.Equal(300, ((Rational)image.MetaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); + + image.MetaData.SyncProfiles(); + + Assert.Equal(100, ((Rational)image.MetaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); + Assert.Equal(300, ((Rational)image.MetaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); + + image.MetaData.VerticalResolution = 150; + + Assert.Equal(100, ((Rational)image.MetaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); + Assert.Equal(300, ((Rational)image.MetaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); + + image.MetaData.SyncProfiles(); + + Assert.Equal(100, ((Rational)image.MetaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); + Assert.Equal(150, ((Rational)image.MetaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); + } } } diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs index 61307da20..f4a2bcf40 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs @@ -150,6 +150,9 @@ namespace ImageSharp.Tests image.MetaData.ExifProfile.SetValue(ExifTag.XResolution, new Rational(150.0)); + // We also need to change this value because this overrides XResolution when the image is written. + image.MetaData.HorizontalResolution = 150.0; + value = image.MetaData.ExifProfile.GetValue(ExifTag.XResolution); TestValue(value, new Rational(150, 1)); From ca513fd0701697879e7bf9c6a9714e0297a99cde Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 5 Feb 2017 23:34:47 +1100 Subject: [PATCH 040/142] A little cleanup --- src/ImageSharp.Drawing.Paths/DrawBeziers.cs | 4 +- src/ImageSharp.Drawing.Paths/DrawLines.cs | 2 +- src/ImageSharp.Drawing.Paths/DrawPath.cs | 4 +- src/ImageSharp.Drawing.Paths/DrawPolygon.cs | 2 +- src/ImageSharp.Drawing.Paths/DrawRectangle.cs | 3 - src/ImageSharp.Drawing.Paths/DrawShape.cs | 4 +- src/ImageSharp.Drawing.Paths/FillPaths.cs | 3 +- src/ImageSharp.Drawing.Paths/FillPolygon.cs | 1 - src/ImageSharp.Drawing.Paths/FillRectangle.cs | 1 - src/ImageSharp.Drawing.Paths/FillShape.cs | 5 +- .../RectangleExtensions.cs | 9 +-- src/ImageSharp.Drawing.Paths/ShapePath.cs | 24 ++---- src/ImageSharp.Drawing.Paths/ShapeRegion.cs | 19 +---- src/ImageSharp.Drawing/DrawPath.cs | 2 +- src/ImageSharp.Drawing/FillRegion.cs | 2 +- src/ImageSharp.Drawing/PointInfo.cs | 3 - .../Processors/DrawPathProcessor.cs | 73 +++++++++---------- src/ImageSharp.Drawing/Region.cs | 6 +- 18 files changed, 57 insertions(+), 110 deletions(-) diff --git a/src/ImageSharp.Drawing.Paths/DrawBeziers.cs b/src/ImageSharp.Drawing.Paths/DrawBeziers.cs index 1a511d84d..ef11a3bac 100644 --- a/src/ImageSharp.Drawing.Paths/DrawBeziers.cs +++ b/src/ImageSharp.Drawing.Paths/DrawBeziers.cs @@ -10,10 +10,8 @@ namespace ImageSharp using Drawing; using Drawing.Brushes; using Drawing.Pens; - using Drawing.Processors; - using SixLabors.Shapes; - using Path = SixLabors.Shapes.Path; + using SixLabors.Shapes; /// /// Extension methods for the type. diff --git a/src/ImageSharp.Drawing.Paths/DrawLines.cs b/src/ImageSharp.Drawing.Paths/DrawLines.cs index f6f8d8f6c..fc046d627 100644 --- a/src/ImageSharp.Drawing.Paths/DrawLines.cs +++ b/src/ImageSharp.Drawing.Paths/DrawLines.cs @@ -10,7 +10,7 @@ namespace ImageSharp using Drawing; using Drawing.Brushes; using Drawing.Pens; - using Drawing.Processors; + using SixLabors.Shapes; /// diff --git a/src/ImageSharp.Drawing.Paths/DrawPath.cs b/src/ImageSharp.Drawing.Paths/DrawPath.cs index 4e4275df6..29b389fa2 100644 --- a/src/ImageSharp.Drawing.Paths/DrawPath.cs +++ b/src/ImageSharp.Drawing.Paths/DrawPath.cs @@ -6,11 +6,11 @@ namespace ImageSharp { using System; - using System.Numerics; + using Drawing; using Drawing.Brushes; using Drawing.Pens; - using Drawing.Processors; + using SixLabors.Shapes; /// diff --git a/src/ImageSharp.Drawing.Paths/DrawPolygon.cs b/src/ImageSharp.Drawing.Paths/DrawPolygon.cs index 28785e5cb..6b3a6f1f3 100644 --- a/src/ImageSharp.Drawing.Paths/DrawPolygon.cs +++ b/src/ImageSharp.Drawing.Paths/DrawPolygon.cs @@ -10,7 +10,7 @@ namespace ImageSharp using Drawing; using Drawing.Brushes; using Drawing.Pens; - using Drawing.Processors; + using SixLabors.Shapes; /// diff --git a/src/ImageSharp.Drawing.Paths/DrawRectangle.cs b/src/ImageSharp.Drawing.Paths/DrawRectangle.cs index 4e8ec7135..d42fee5dc 100644 --- a/src/ImageSharp.Drawing.Paths/DrawRectangle.cs +++ b/src/ImageSharp.Drawing.Paths/DrawRectangle.cs @@ -10,9 +10,6 @@ namespace ImageSharp using Drawing; using Drawing.Brushes; using Drawing.Pens; - using Drawing.Processors; - - using SixLabors.Shapes; /// /// Extension methods for the type. diff --git a/src/ImageSharp.Drawing.Paths/DrawShape.cs b/src/ImageSharp.Drawing.Paths/DrawShape.cs index 6ddce65b9..748bb6a11 100644 --- a/src/ImageSharp.Drawing.Paths/DrawShape.cs +++ b/src/ImageSharp.Drawing.Paths/DrawShape.cs @@ -6,11 +6,11 @@ namespace ImageSharp { using System; - using System.Numerics; + using Drawing; using Drawing.Brushes; using Drawing.Pens; - using Drawing.Processors; + using SixLabors.Shapes; /// diff --git a/src/ImageSharp.Drawing.Paths/FillPaths.cs b/src/ImageSharp.Drawing.Paths/FillPaths.cs index 09a799794..1dd0b0a3a 100644 --- a/src/ImageSharp.Drawing.Paths/FillPaths.cs +++ b/src/ImageSharp.Drawing.Paths/FillPaths.cs @@ -6,10 +6,9 @@ namespace ImageSharp { using System; - using System.Numerics; + using Drawing; using Drawing.Brushes; - using Drawing.Processors; using SixLabors.Shapes; diff --git a/src/ImageSharp.Drawing.Paths/FillPolygon.cs b/src/ImageSharp.Drawing.Paths/FillPolygon.cs index a609ceed2..b41267b9e 100644 --- a/src/ImageSharp.Drawing.Paths/FillPolygon.cs +++ b/src/ImageSharp.Drawing.Paths/FillPolygon.cs @@ -9,7 +9,6 @@ namespace ImageSharp using System.Numerics; using Drawing; using Drawing.Brushes; - using Drawing.Processors; using SixLabors.Shapes; diff --git a/src/ImageSharp.Drawing.Paths/FillRectangle.cs b/src/ImageSharp.Drawing.Paths/FillRectangle.cs index 579a288a4..3b2cef613 100644 --- a/src/ImageSharp.Drawing.Paths/FillRectangle.cs +++ b/src/ImageSharp.Drawing.Paths/FillRectangle.cs @@ -9,7 +9,6 @@ namespace ImageSharp using Drawing; using Drawing.Brushes; - using Drawing.Processors; /// /// Extension methods for the type. diff --git a/src/ImageSharp.Drawing.Paths/FillShape.cs b/src/ImageSharp.Drawing.Paths/FillShape.cs index 52a3c60dd..f143e75b7 100644 --- a/src/ImageSharp.Drawing.Paths/FillShape.cs +++ b/src/ImageSharp.Drawing.Paths/FillShape.cs @@ -6,10 +6,9 @@ namespace ImageSharp { using System; - using System.Numerics; + using Drawing; using Drawing.Brushes; - using Drawing.Processors; using SixLabors.Shapes; @@ -19,7 +18,7 @@ namespace ImageSharp public static partial class ImageExtensions { /// - /// Flood fills the image in the shape of the provided polygon with the specified brush.. + /// Flood fills the image in the shape of the provided polygon with the specified brush. /// /// The type of the color. /// The image this method extends. diff --git a/src/ImageSharp.Drawing.Paths/RectangleExtensions.cs b/src/ImageSharp.Drawing.Paths/RectangleExtensions.cs index 1faa6469a..2fa5fe43f 100644 --- a/src/ImageSharp.Drawing.Paths/RectangleExtensions.cs +++ b/src/ImageSharp.Drawing.Paths/RectangleExtensions.cs @@ -3,16 +3,9 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Drawing.Processors +namespace ImageSharp.Drawing { using System; - using System.Buffers; - using System.Numerics; - using System.Threading.Tasks; - using Drawing; - using ImageSharp.Processing; - using SixLabors.Shapes; - using Rectangle = ImageSharp.Rectangle; /// /// Extension methods for helping to bridge Shaper2D and ImageSharp primitives. diff --git a/src/ImageSharp.Drawing.Paths/ShapePath.cs b/src/ImageSharp.Drawing.Paths/ShapePath.cs index 34e77b807..5ea38a365 100644 --- a/src/ImageSharp.Drawing.Paths/ShapePath.cs +++ b/src/ImageSharp.Drawing.Paths/ShapePath.cs @@ -9,16 +9,14 @@ namespace ImageSharp.Drawing using System.Collections.Immutable; using System.Numerics; - using ImageSharp.Drawing.Processors; - using SixLabors.Shapes; using Rectangle = ImageSharp.Rectangle; /// - /// A drawable mapping between a / and a drawable/fillable region. + /// A drawable mapping between a / and a drawable/fillable region. /// - internal class ShapePath : ImageSharp.Drawing.Drawable + internal class ShapePath : Drawable { /// /// The fillable shape @@ -59,9 +57,6 @@ namespace ImageSharp.Drawing /// /// Gets the drawable paths /// - /// - /// The paths. - /// public ImmutableArray Paths { get; } /// @@ -78,12 +73,7 @@ namespace ImageSharp.Drawing Vector2[] innerbuffer = ArrayPool.Shared.Rent(length); try { - int count = this.shape.FindIntersections( - start, - end, - innerbuffer, - length, - 0); + int count = this.shape.FindIntersections(start, end, innerbuffer, length, 0); for (int i = 0; i < count; i++) { @@ -106,12 +96,7 @@ namespace ImageSharp.Drawing Vector2[] innerbuffer = ArrayPool.Shared.Rent(length); try { - int count = this.shape.FindIntersections( - start, - end, - innerbuffer, - length, - 0); + int count = this.shape.FindIntersections(start, end, innerbuffer, length, 0); for (int i = 0; i < count; i++) { @@ -133,6 +118,7 @@ namespace ImageSharp.Drawing float distanceFromPath = float.MaxValue; float distanceAlongPath = 0; + // ReSharper disable once ForCanBeConvertedToForeach for (int i = 0; i < this.Paths.Length; i++) { SixLabors.Shapes.PointInfo p = this.Paths[i].Distance(point); diff --git a/src/ImageSharp.Drawing.Paths/ShapeRegion.cs b/src/ImageSharp.Drawing.Paths/ShapeRegion.cs index 5adec6790..dc035e3b0 100644 --- a/src/ImageSharp.Drawing.Paths/ShapeRegion.cs +++ b/src/ImageSharp.Drawing.Paths/ShapeRegion.cs @@ -6,17 +6,14 @@ namespace ImageSharp.Drawing { using System.Buffers; - using System.Collections.Immutable; using System.Numerics; - using ImageSharp.Drawing.Processors; - using SixLabors.Shapes; using Rectangle = ImageSharp.Rectangle; /// - /// A drawable mapping between a / and a drawable/fillable region. + /// A drawable mapping between a / and a drawable/fillable region. /// internal class ShapeRegion : Region { @@ -58,12 +55,7 @@ namespace ImageSharp.Drawing Vector2[] innerbuffer = ArrayPool.Shared.Rent(length); try { - int count = this.Shape.FindIntersections( - start, - end, - innerbuffer, - length, - 0); + int count = this.Shape.FindIntersections(start, end, innerbuffer, length, 0); for (int i = 0; i < count; i++) { @@ -86,12 +78,7 @@ namespace ImageSharp.Drawing Vector2[] innerbuffer = ArrayPool.Shared.Rent(length); try { - int count = this.Shape.FindIntersections( - start, - end, - innerbuffer, - length, - 0); + int count = this.Shape.FindIntersections(start, end, innerbuffer, length, 0); for (int i = 0; i < count; i++) { diff --git a/src/ImageSharp.Drawing/DrawPath.cs b/src/ImageSharp.Drawing/DrawPath.cs index d92731270..75a0d8157 100644 --- a/src/ImageSharp.Drawing/DrawPath.cs +++ b/src/ImageSharp.Drawing/DrawPath.cs @@ -6,7 +6,7 @@ namespace ImageSharp { using System; - using System.Numerics; + using Drawing; using Drawing.Brushes; using Drawing.Pens; diff --git a/src/ImageSharp.Drawing/FillRegion.cs b/src/ImageSharp.Drawing/FillRegion.cs index fbbf659d1..6faf519af 100644 --- a/src/ImageSharp.Drawing/FillRegion.cs +++ b/src/ImageSharp.Drawing/FillRegion.cs @@ -6,7 +6,7 @@ namespace ImageSharp { using System; - using System.Numerics; + using Drawing; using Drawing.Brushes; using Drawing.Processors; diff --git a/src/ImageSharp.Drawing/PointInfo.cs b/src/ImageSharp.Drawing/PointInfo.cs index e222a8146..7eff24fac 100644 --- a/src/ImageSharp.Drawing/PointInfo.cs +++ b/src/ImageSharp.Drawing/PointInfo.cs @@ -5,9 +5,6 @@ namespace ImageSharp.Drawing { - using System; - using System.Numerics; - /// /// Returns details about how far away from the inside of a shape and the color the pixel could be. /// diff --git a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs index b4aa7fba5..83ae9521c 100644 --- a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs @@ -6,13 +6,11 @@ namespace ImageSharp.Drawing.Processors { using System; - using System.Collections.Generic; - using System.Linq; using System.Numerics; using System.Threading.Tasks; + using ImageSharp.Processing; using Pens; - using Rectangle = ImageSharp.Rectangle; /// /// Draws a path using the processor pipeline @@ -39,27 +37,18 @@ namespace ImageSharp.Drawing.Processors } /// - /// Gets the options. + /// Gets the graphics options. /// - /// - /// The options. - /// public GraphicsOptions Options { get; } /// /// Gets the pen. /// - /// - /// The pen. - /// public IPen Pen { get; } /// /// Gets the path. /// - /// - /// The path. - /// public Drawable Path { get; } /// @@ -68,7 +57,7 @@ namespace ImageSharp.Drawing.Processors using (PixelAccessor sourcePixels = source.Lock()) using (PenApplicator applicator = this.Pen.CreateApplicator(sourcePixels, this.Path.Bounds)) { - var rect = RectangleF.Ceiling(applicator.RequiredRegion); + Rectangle rect = RectangleF.Ceiling(applicator.RequiredRegion); int polyStartY = rect.Y - PaddingFactor; int polyEndY = rect.Bottom + PaddingFactor; @@ -98,49 +87,53 @@ namespace ImageSharp.Drawing.Processors } Parallel.For( - minY, - maxY, - this.ParallelOptions, - (int y) => - { - int offsetY = y - polyStartY; - - for (int x = minX; x < maxX; x++) + minY, + maxY, + this.ParallelOptions, + y => { - // TODO add find intersections code to skip and scan large regions of this. - int offsetX = x - startX; - var info = this.Path.GetPointInfo(offsetX, offsetY); + int offsetY = y - polyStartY; - var color = applicator.GetColor(offsetX, offsetY, info); + for (int x = minX; x < maxX; x++) + { + // TODO add find intersections code to skip and scan large regions of this. + int offsetX = x - startX; + PointInfo info = this.Path.GetPointInfo(offsetX, offsetY); - var opacity = this.Opacity(color.DistanceFromElement); + ColoredPointInfo color = applicator.GetColor(offsetX, offsetY, info); - if (opacity > Constants.Epsilon) - { - int offsetColorX = x - minX; + float opacity = this.Opacity(color.DistanceFromElement); - Vector4 backgroundVector = sourcePixels[offsetX, offsetY].ToVector4(); - Vector4 sourceVector = color.Color.ToVector4(); + if (opacity > Constants.Epsilon) + { + Vector4 backgroundVector = sourcePixels[offsetX, offsetY].ToVector4(); + Vector4 sourceVector = color.Color.ToVector4(); - var finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); - finalColor.W = backgroundVector.W; + Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); + finalColor.W = backgroundVector.W; - TColor packed = default(TColor); - packed.PackFromVector4(finalColor); - sourcePixels[offsetX, offsetY] = packed; + TColor packed = default(TColor); + packed.PackFromVector4(finalColor); + sourcePixels[offsetX, offsetY] = packed; + } } - } - }); + }); } } + /// + /// Returns the correct opacity for the given distance. + /// + /// Thw distance from the central point. + /// The private float Opacity(float distance) { if (distance <= 0) { return 1; } - else if (this.Options.Antialias && distance < AntialiasFactor) + + if (this.Options.Antialias && distance < AntialiasFactor) { return 1 - (distance / AntialiasFactor); } diff --git a/src/ImageSharp.Drawing/Region.cs b/src/ImageSharp.Drawing/Region.cs index 81f3dca5b..fe1dc5222 100644 --- a/src/ImageSharp.Drawing/Region.cs +++ b/src/ImageSharp.Drawing/Region.cs @@ -16,17 +16,17 @@ namespace ImageSharp.Drawing public abstract int MaxIntersections { get; } /// - /// Gets the bounding box that entirly surrounds this region. + /// Gets the bounding box that entirely surrounds this region. /// /// - /// This should always contains all possible points returned from eather or . + /// This should always contains all possible points returned from either or . /// public abstract Rectangle Bounds { get; } /// /// Scans the X axis for intersections. /// - /// The position along the X axies to find intersections. + /// The position along the X axis to find intersections. /// The buffer. /// The length. /// The offset. From 4d6cfcf8f5a842ed1b17d504ff4b4fc5f89d9195 Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sun, 5 Feb 2017 14:54:43 +0100 Subject: [PATCH 041/142] Added unit test for Horizontal and Vertical resolution. --- src/ImageSharp/MetaData/ImageMetaData.cs | 4 +-- .../MetaData/ImageMetaDataTests.cs | 32 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/MetaData/ImageMetaData.cs b/src/ImageSharp/MetaData/ImageMetaData.cs index 0bd0d0f78..f4aef9863 100644 --- a/src/ImageSharp/MetaData/ImageMetaData.cs +++ b/src/ImageSharp/MetaData/ImageMetaData.cs @@ -79,7 +79,7 @@ namespace ImageSharp set { - if (value >= 0) + if (value > 0) { this.horizontalResolution = value; } @@ -100,7 +100,7 @@ namespace ImageSharp set { - if (value >= 0) + if (value > 0) { this.verticalResolution = value; } diff --git a/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs b/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs index d9bd52c73..60e04e18d 100644 --- a/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs +++ b/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs @@ -39,6 +39,38 @@ namespace ImageSharp.Tests Assert.Equal(1, clone.RepeatCount); } + [Fact] + public void HorizontalResolution() + { + ImageMetaData metaData = new ImageMetaData(); + Assert.Equal(96, metaData.HorizontalResolution); + + metaData.HorizontalResolution=0; + Assert.Equal(96, metaData.HorizontalResolution); + + metaData.HorizontalResolution=-1; + Assert.Equal(96, metaData.HorizontalResolution); + + metaData.HorizontalResolution=1; + Assert.Equal(1, metaData.HorizontalResolution); + } + + [Fact] + public void VerticalResolution() + { + ImageMetaData metaData = new ImageMetaData(); + Assert.Equal(96, metaData.VerticalResolution); + + metaData.VerticalResolution = 0; + Assert.Equal(96, metaData.VerticalResolution); + + metaData.VerticalResolution = -1; + Assert.Equal(96, metaData.VerticalResolution); + + metaData.VerticalResolution = 1; + Assert.Equal(1, metaData.VerticalResolution); + } + [Fact] public void SyncProfiles() { From 60fb2fb7751c7756365e7afdfdeb77d2585e745e Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sun, 5 Feb 2017 20:35:49 +0000 Subject: [PATCH 042/142] update SixLabors.Shapes version fixes missing scan lane while clipping internal corner. --- src/ImageSharp.Drawing.Paths/project.json | 2 +- .../Processors/FillRegionProcessor.cs | 28 +------------------ .../Drawing/SolidComplexPolygonTests.cs | 7 +++-- 3 files changed, 7 insertions(+), 30 deletions(-) diff --git a/src/ImageSharp.Drawing.Paths/project.json b/src/ImageSharp.Drawing.Paths/project.json index ee3239e79..c903f805a 100644 --- a/src/ImageSharp.Drawing.Paths/project.json +++ b/src/ImageSharp.Drawing.Paths/project.json @@ -44,7 +44,7 @@ "ImageSharp.Drawing": { "target": "project" }, - "SixLabors.Shapes": "0.1.0-alpha0003", + "SixLabors.Shapes": "0.1.0-alpha0004", "StyleCop.Analyzers": { "version": "1.0.0", "type": "build" diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs index 4f1ee34ac..67313d84b 100644 --- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs @@ -98,20 +98,7 @@ namespace ImageSharp.Drawing.Processors int pointsFound = this.Region.ScanY(y, buffer, maxIntersections, 0); if (pointsFound == 0) { - // nothign on this line skip - return; - } - - if (pointsFound == 1 && maxIntersections > 1) - { - // we must have clipped a corner lets just duplicate it into point 2 and continue :) - buffer[1] = buffer[0]; - pointsFound++; - } - - if (pointsFound % 2 == 1) - { - // we seem to have just clipped a corner lets just skip it + // nothing on this line skip return; } @@ -253,19 +240,6 @@ namespace ImageSharp.Drawing.Processors return; } - if (pointsFound == 1 && maxIntersections > 1) - { - // we must have clipped a corner lets just duplicate it into point 2 and continue :) - buffer[1] = buffer[0]; - pointsFound++; - } - - if (pointsFound % 2 == 1) - { - // we seem to have just clipped a corner lets just skip it - return; - } - QuickSort(buffer, pointsFound); int currentIntersection = 0; diff --git a/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs index 98ec9ff83..cd3182d6c 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs @@ -28,14 +28,15 @@ namespace ImageSharp.Tests.Drawing new Vector2(37, 85), new Vector2(93, 85), new Vector2(65, 137))); - + var clipped = simplePath.Clip(hole1); + // var clipped = new Rectangle(10, 10, 100, 100).Clip(new Rectangle(20, 0, 20, 20)); using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/Simple.png")) { image .BackgroundColor(Color.Blue) - .Fill(Color.HotPink, simplePath.Clip(hole1)) + .Fill(Color.HotPink, clipped) .Save(output); } @@ -45,6 +46,8 @@ namespace ImageSharp.Tests.Drawing Assert.Equal(Color.HotPink, sourcePixels[200, 150]); + Assert.Equal(Color.HotPink, sourcePixels[70, 137]); + Assert.Equal(Color.HotPink, sourcePixels[50, 50]); Assert.Equal(Color.HotPink, sourcePixels[35, 100]); From 8d93109d55f085ac80b42d3d944d6d96b639270f Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 7 Feb 2017 02:10:13 +0100 Subject: [PATCH 043/142] [MethodImpl(MethodImplOptions.AggressiveInlining)] on packedpixel primitives --- src/ImageSharp/Colors/Color.cs | 23 ++++++++++++++++++ src/ImageSharp/Colors/PackedPixel/Alpha8.cs | 13 ++++++++++ src/ImageSharp/Colors/PackedPixel/Argb.cs | 24 +++++++++++++++++++ src/ImageSharp/Colors/PackedPixel/Bgr565.cs | 14 +++++++++++ src/ImageSharp/Colors/PackedPixel/Bgra4444.cs | 13 ++++++++++ src/ImageSharp/Colors/PackedPixel/Bgra5551.cs | 13 ++++++++++ src/ImageSharp/Colors/PackedPixel/Byte4.cs | 14 +++++++++++ .../Colors/PackedPixel/HalfSingle.cs | 13 ++++++++++ .../Colors/PackedPixel/HalfTypeHelper.cs | 2 ++ .../Colors/PackedPixel/HalfVector2.cs | 14 +++++++++++ .../Colors/PackedPixel/HalfVector4.cs | 13 ++++++++++ .../Colors/PackedPixel/NormalizedByte2.cs | 14 +++++++++++ .../Colors/PackedPixel/NormalizedByte4.cs | 13 ++++++++++ .../Colors/PackedPixel/NormalizedShort2.cs | 14 +++++++++++ .../Colors/PackedPixel/NormalizedShort4.cs | 13 ++++++++++ src/ImageSharp/Colors/PackedPixel/Rg32.cs | 14 +++++++++++ .../Colors/PackedPixel/Rgba1010102.cs | 13 ++++++++++ src/ImageSharp/Colors/PackedPixel/Rgba64.cs | 13 ++++++++++ src/ImageSharp/Colors/PackedPixel/Short2.cs | 14 +++++++++++ src/ImageSharp/Colors/PackedPixel/Short4.cs | 13 ++++++++++ 20 files changed, 277 insertions(+) diff --git a/src/ImageSharp/Colors/Color.cs b/src/ImageSharp/Colors/Color.cs index 036ee14c1..8d6ce954c 100644 --- a/src/ImageSharp/Colors/Color.cs +++ b/src/ImageSharp/Colors/Color.cs @@ -8,6 +8,7 @@ namespace ImageSharp using System; using System.Globalization; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255. @@ -138,11 +139,13 @@ namespace ImageSharp /// public byte R { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return (byte)(this.packedValue >> RedShift); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] set { this.packedValue = this.packedValue & 0xFFFFFF00 | (uint)value << RedShift; @@ -154,11 +157,13 @@ namespace ImageSharp /// public byte G { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return (byte)(this.packedValue >> GreenShift); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] set { this.packedValue = this.packedValue & 0xFFFF00FF | (uint)value << GreenShift; @@ -170,11 +175,13 @@ namespace ImageSharp /// public byte B { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return (byte)(this.packedValue >> BlueShift); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] set { this.packedValue = this.packedValue & 0xFF00FFFF | (uint)value << BlueShift; @@ -186,11 +193,13 @@ namespace ImageSharp /// public byte A { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return (byte)(this.packedValue >> AlphaShift); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] set { this.packedValue = this.packedValue & 0x00FFFFFF | (uint)value << AlphaShift; @@ -223,6 +232,7 @@ namespace ImageSharp /// /// True if the parameter is equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Color left, Color right) { return left.packedValue == right.packedValue; @@ -236,6 +246,7 @@ namespace ImageSharp /// /// True if the parameter is not equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Color left, Color right) { return left.packedValue != right.packedValue; @@ -257,6 +268,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromBytes(byte x, byte y, byte z, byte w) { this.packedValue = Pack(x, y, z, w); @@ -273,6 +285,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzBytes(byte[] bytes, int startIndex) { bytes[startIndex] = this.R; @@ -281,6 +294,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzwBytes(byte[] bytes, int startIndex) { bytes[startIndex] = this.R; @@ -290,6 +304,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxBytes(byte[] bytes, int startIndex) { bytes[startIndex] = this.B; @@ -298,6 +313,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxwBytes(byte[] bytes, int startIndex) { bytes[startIndex] = this.B; @@ -307,12 +323,14 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) { this.packedValue = Pack(ref vector); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; @@ -325,6 +343,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Color other) { return this.packedValue == other.packedValue; @@ -350,6 +369,7 @@ namespace ImageSharp /// /// The vector containing the values to pack. /// The containing the packed values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(ref Vector4 vector) { vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One); @@ -366,6 +386,7 @@ namespace ImageSharp /// /// The vector containing the values to pack. /// The containing the packed values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(ref Vector3 vector) { Vector4 value = new Vector4(vector, 1); @@ -380,6 +401,7 @@ namespace ImageSharp /// The z-component /// The w-component /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(float x, float y, float z, float w) { Vector4 value = new Vector4(x, y, z, w); @@ -394,6 +416,7 @@ namespace ImageSharp /// The z-component /// The w-component /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(byte x, byte y, byte z, byte w) { return (uint)(x << RedShift | y << GreenShift | z << BlueShift | w << AlphaShift); diff --git a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs index 361fe5b9e..95e620df0 100644 --- a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs +++ b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing a single 8 bit normalized W values that is ranging from 0 to 1. @@ -37,6 +38,7 @@ namespace ImageSharp /// /// True if the parameter is equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Alpha8 left, Alpha8 right) { return left.PackedValue == right.PackedValue; @@ -50,30 +52,35 @@ namespace ImageSharp /// /// True if the parameter is not equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Alpha8 left, Alpha8 right) { return left.PackedValue != right.PackedValue; } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) { this.PackedValue = Pack(vector.W); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4(0, 0, 0, this.PackedValue / 255F); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromBytes(byte x, byte y, byte z, byte w) { this.PackedValue = w; } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzBytes(byte[] bytes, int startIndex) { bytes[startIndex] = 0; @@ -82,6 +89,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzwBytes(byte[] bytes, int startIndex) { bytes[startIndex] = 0; @@ -91,6 +99,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxBytes(byte[] bytes, int startIndex) { bytes[startIndex] = 0; @@ -99,6 +108,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxwBytes(byte[] bytes, int startIndex) { bytes[startIndex] = 0; @@ -122,6 +132,7 @@ namespace ImageSharp /// /// The Alpha8 packed vector to compare. /// True if the packed vectors are equal. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Alpha8 other) { return this.PackedValue == other.PackedValue; @@ -137,6 +148,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return this.PackedValue.GetHashCode(); @@ -147,6 +159,7 @@ namespace ImageSharp /// /// The float containing the value to pack. /// The containing the packed values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static byte Pack(float alpha) { return (byte)Math.Round(alpha.Clamp(0, 1) * 255F); diff --git a/src/ImageSharp/Colors/PackedPixel/Argb.cs b/src/ImageSharp/Colors/PackedPixel/Argb.cs index 8ab8e3f43..432011702 100644 --- a/src/ImageSharp/Colors/PackedPixel/Argb.cs +++ b/src/ImageSharp/Colors/PackedPixel/Argb.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255. @@ -113,11 +114,13 @@ namespace ImageSharp /// public byte R { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return (byte)(this.PackedValue >> RedShift); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] set { this.PackedValue = this.PackedValue & 0xFF00FFFF | (uint)value << RedShift; @@ -129,11 +132,13 @@ namespace ImageSharp /// public byte G { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return (byte)(this.PackedValue >> GreenShift); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] set { this.PackedValue = this.PackedValue & 0xFFFF00FF | (uint)value << GreenShift; @@ -145,11 +150,13 @@ namespace ImageSharp /// public byte B { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return (byte)(this.PackedValue >> BlueShift); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] set { this.PackedValue = this.PackedValue & 0xFFFFFF00 | (uint)value << BlueShift; @@ -161,11 +168,13 @@ namespace ImageSharp /// public byte A { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return (byte)(this.PackedValue >> AlphaShift); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] set { this.PackedValue = this.PackedValue & 0x00FFFFFF | (uint)value << AlphaShift; @@ -184,6 +193,7 @@ namespace ImageSharp /// /// True if the parameter is equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Argb left, Argb right) { return left.PackedValue == right.PackedValue; @@ -197,30 +207,35 @@ namespace ImageSharp /// /// True if the parameter is not equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Argb left, Argb right) { return left.PackedValue != right.PackedValue; } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) { this.PackedValue = Pack(ref vector); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromBytes(byte x, byte y, byte z, byte w) { this.PackedValue = Pack(x, y, z, w); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzBytes(byte[] bytes, int startIndex) { bytes[startIndex] = this.R; @@ -229,6 +244,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzwBytes(byte[] bytes, int startIndex) { bytes[startIndex] = this.R; @@ -238,6 +254,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxBytes(byte[] bytes, int startIndex) { bytes[startIndex] = this.B; @@ -246,6 +263,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxwBytes(byte[] bytes, int startIndex) { bytes[startIndex] = this.B; @@ -261,12 +279,14 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Argb other) { return this.PackedValue == other.PackedValue; } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { // ReSharper disable once NonReadonlyMemberInGetHashCode @@ -281,6 +301,7 @@ namespace ImageSharp /// The z-component /// The w-component /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(float x, float y, float z, float w) { var value = new Vector4(x, y, z, w); @@ -295,6 +316,7 @@ namespace ImageSharp /// The z-component /// The w-component /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(byte x, byte y, byte z, byte w) { return (uint)(x << RedShift | y << GreenShift | z << BlueShift | w << AlphaShift); @@ -305,6 +327,7 @@ namespace ImageSharp /// /// The vector containing the values to pack. /// The containing the packed values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(ref Vector3 vector) { var value = new Vector4(vector, 1); @@ -316,6 +339,7 @@ namespace ImageSharp /// /// The vector containing the values to pack. /// The containing the packed values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(ref Vector4 vector) { vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One); diff --git a/src/ImageSharp/Colors/PackedPixel/Bgr565.cs b/src/ImageSharp/Colors/PackedPixel/Bgr565.cs index 1f1ce0a17..19c7f3d14 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgr565.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgr565.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. The x and z components use 5 bits, and the y component uses 6 bits. @@ -46,6 +47,7 @@ namespace ImageSharp /// /// True if the parameter is equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Bgr565 left, Bgr565 right) { return left.PackedValue == right.PackedValue; @@ -59,6 +61,7 @@ namespace ImageSharp /// /// True if the parameter is not equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Bgr565 left, Bgr565 right) { return left.PackedValue != right.PackedValue; @@ -69,6 +72,7 @@ namespace ImageSharp /// The vector components are typically expanded in least to greatest significance order. /// /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector3 ToVector3() { return new Vector3( @@ -78,24 +82,28 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) { this.PackedValue = Pack(vector.X, vector.Y, vector.Z); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4(this.ToVector3(), 1F); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromBytes(byte x, byte y, byte z, byte w) { this.PackFromVector4(new Vector4(x, y, z, w) / 255F); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -105,6 +113,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -115,6 +124,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -124,6 +134,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -140,6 +151,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Bgr565 other) { return this.PackedValue == other.PackedValue; @@ -152,6 +164,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return this.PackedValue.GetHashCode(); @@ -164,6 +177,7 @@ namespace ImageSharp /// The y-component /// The z-component /// The containing the packed values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ushort Pack(float x, float y, float z) { return (ushort)((((int)Math.Round(x.Clamp(0, 1) * 31F) & 0x1F) << 11) | diff --git a/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs b/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs index 1f33cb791..41f7cba04 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing unsigned normalized values, ranging from 0 to 1, using 4 bits each for x, y, z, and w. @@ -45,6 +46,7 @@ namespace ImageSharp /// /// True if the parameter is equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Bgra4444 left, Bgra4444 right) { return left.PackedValue == right.PackedValue; @@ -58,12 +60,14 @@ namespace ImageSharp /// /// True if the parameter is not equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Bgra4444 left, Bgra4444 right) { return left.PackedValue != right.PackedValue; } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { const float Max = 1 / 15F; @@ -76,18 +80,21 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) { this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromBytes(byte x, byte y, byte z, byte w) { this.PackFromVector4(new Vector4(x, y, z, w) / 255F); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -97,6 +104,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -107,6 +115,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -116,6 +125,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -132,6 +142,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Bgra4444 other) { return this.PackedValue == other.PackedValue; @@ -144,6 +155,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return this.PackedValue.GetHashCode(); @@ -157,6 +169,7 @@ namespace ImageSharp /// The z-component /// The w-component /// The containing the packed values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ushort Pack(float x, float y, float z, float w) { return (ushort)((((int)Math.Round(w.Clamp(0, 1) * 15F) & 0x0F) << 12) | diff --git a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs b/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs index d0c52068d..800cb0bf1 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. The x , y and z components use 5 bits, and the w component uses 1 bit. @@ -47,6 +48,7 @@ namespace ImageSharp /// /// True if the parameter is equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Bgra5551 left, Bgra5551 right) { return left.PackedValue == right.PackedValue; @@ -60,12 +62,14 @@ namespace ImageSharp /// /// True if the parameter is not equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Bgra5551 left, Bgra5551 right) { return left.PackedValue != right.PackedValue; } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4( @@ -76,18 +80,21 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) { this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromBytes(byte x, byte y, byte z, byte w) { this.PackFromVector4(new Vector4(x, y, z, w) / 255F); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -97,6 +104,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -107,6 +115,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -116,6 +125,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -132,6 +142,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Bgra5551 other) { return this.PackedValue == other.PackedValue; @@ -150,6 +161,7 @@ namespace ImageSharp /// Gets a hash code of the packed vector. /// /// The hash code for the packed vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return this.PackedValue.GetHashCode(); @@ -163,6 +175,7 @@ namespace ImageSharp /// The z-component /// The w-component /// The containing the packed values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ushort Pack(float x, float y, float z, float w) { return (ushort)( diff --git a/src/ImageSharp/Colors/PackedPixel/Byte4.cs b/src/ImageSharp/Colors/PackedPixel/Byte4.cs index 69c69ecaf..644acc509 100644 --- a/src/ImageSharp/Colors/PackedPixel/Byte4.cs +++ b/src/ImageSharp/Colors/PackedPixel/Byte4.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing four 8-bit unsigned integer values, ranging from 0 to 255. @@ -48,6 +49,7 @@ namespace ImageSharp /// /// True if the parameter is equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Byte4 left, Byte4 right) { return left.PackedValue == right.PackedValue; @@ -61,6 +63,7 @@ namespace ImageSharp /// /// True if the parameter is not equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Byte4 left, Byte4 right) { return left.PackedValue != right.PackedValue; @@ -70,6 +73,7 @@ namespace ImageSharp /// Sets the packed representation from a Vector4. /// /// The vector to create the packed representation from. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) { this.PackedValue = Pack(ref vector); @@ -79,6 +83,7 @@ namespace ImageSharp /// Expands the packed representation into a Vector4. /// /// The expanded vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4( @@ -89,12 +94,14 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromBytes(byte x, byte y, byte z, byte w) { this.PackFromVector4(new Vector4(x, y, z, w)); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -104,6 +111,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -114,6 +122,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -123,6 +132,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -139,12 +149,14 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Byte4 other) { return this == other; } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return this.PackedValue.GetHashCode(); @@ -164,12 +176,14 @@ namespace ImageSharp /// /// The vector containing the values to pack. /// The containing the packed values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(ref Vector4 vector) { const float Max = 255F; const float Min = 0F; // Clamp the value between min and max values + // TODO: Use Vector4.Clamp() here! uint byte4 = (uint)Math.Round(vector.X.Clamp(Min, Max)) & 0xFF; uint byte3 = ((uint)Math.Round(vector.Y.Clamp(Min, Max)) & 0xFF) << 0x8; uint byte2 = ((uint)Math.Round(vector.Z.Clamp(Min, Max)) & 0xFF) << 0x10; diff --git a/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs b/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs index 22e32aa56..29a55095b 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing a single 16 bit floating point value. @@ -47,6 +48,7 @@ namespace ImageSharp /// /// True if the parameter is equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(HalfSingle left, HalfSingle right) { return left.PackedValue == right.PackedValue; @@ -64,6 +66,7 @@ namespace ImageSharp /// /// True if the parameter is not equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(HalfSingle left, HalfSingle right) { return left.PackedValue != right.PackedValue; @@ -73,30 +76,35 @@ namespace ImageSharp /// Expands the packed representation into a . /// /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public float ToSingle() { return HalfTypeHelper.Unpack(this.PackedValue); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) { this.PackedValue = HalfTypeHelper.Pack(vector.X); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4(this.ToSingle(), 0, 0, 1); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromBytes(byte x, byte y, byte z, byte w) { this.PackFromVector4(new Vector4(x, y, z, w) / MaxBytes); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -110,6 +118,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -124,6 +133,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -137,6 +147,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -157,6 +168,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(HalfSingle other) { return this.PackedValue == other.PackedValue; @@ -169,6 +181,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return this.PackedValue.GetHashCode(); diff --git a/src/ImageSharp/Colors/PackedPixel/HalfTypeHelper.cs b/src/ImageSharp/Colors/PackedPixel/HalfTypeHelper.cs index d6d680ed4..a19b51323 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfTypeHelper.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfTypeHelper.cs @@ -5,6 +5,7 @@ namespace ImageSharp { + using System.Runtime.CompilerServices; using System.Runtime.InteropServices; /// @@ -17,6 +18,7 @@ namespace ImageSharp /// /// The float to pack /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static ushort Pack(float value) { Uif uif = new Uif { F = value }; diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs index 7c17dcea8..02e93b250 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing two 16-bit floating-point values. @@ -57,6 +58,7 @@ namespace ImageSharp /// /// True if the parameter is equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(HalfVector2 left, HalfVector2 right) { return left.Equals(right); @@ -74,6 +76,7 @@ namespace ImageSharp /// /// True if the parameter is not equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(HalfVector2 left, HalfVector2 right) { return !left.Equals(right); @@ -83,6 +86,7 @@ namespace ImageSharp /// Expands the packed representation into a . /// /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector2 ToVector2() { Vector2 vector; @@ -92,12 +96,14 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) { this.PackedValue = Pack(vector.X, vector.Y); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { Vector2 vector = this.ToVector2(); @@ -105,12 +111,14 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromBytes(byte x, byte y, byte z, byte w) { this.PackFromVector4(new Vector4(x, y, z, w) / MaxBytes); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -124,6 +132,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -138,6 +147,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -151,6 +161,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -171,6 +182,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return this.PackedValue.GetHashCode(); @@ -183,6 +195,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(HalfVector2 other) { return this.PackedValue.Equals(other.PackedValue); @@ -194,6 +207,7 @@ namespace ImageSharp /// The x-component /// The y-component /// The containing the packed values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(float x, float y) { uint num2 = HalfTypeHelper.Pack(x); diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs b/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs index a7f10fc71..38787acea 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing four 16-bit floating-point values. @@ -60,6 +61,7 @@ namespace ImageSharp /// /// True if the parameter is equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(HalfVector4 left, HalfVector4 right) { return left.Equals(right); @@ -77,18 +79,21 @@ namespace ImageSharp /// /// True if the parameter is not equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(HalfVector4 left, HalfVector4 right) { return !left.Equals(right); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) { this.PackedValue = Pack(ref vector); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4( @@ -99,12 +104,14 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromBytes(byte x, byte y, byte z, byte w) { this.PackFromVector4(new Vector4(x, y, z, w) / MaxBytes); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -118,6 +125,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -132,6 +140,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -145,6 +154,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -165,6 +175,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return this.PackedValue.GetHashCode(); @@ -177,6 +188,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(HalfVector4 other) { return this.PackedValue.Equals(other.PackedValue); @@ -187,6 +199,7 @@ namespace ImageSharp /// /// The vector containing the values to pack. /// The containing the packed values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong Pack(ref Vector4 vector) { ulong num4 = HalfTypeHelper.Pack(vector.X); diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs index f1dae1066..5196cbfaf 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed packed pixel type containing two 8-bit signed normalized values, ranging from −1 to 1. @@ -62,6 +63,7 @@ namespace ImageSharp /// /// True if the parameter is equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(NormalizedByte2 left, NormalizedByte2 right) { return left.PackedValue == right.PackedValue; @@ -79,6 +81,7 @@ namespace ImageSharp /// /// True if the parameter is not equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(NormalizedByte2 left, NormalizedByte2 right) { return left.PackedValue != right.PackedValue; @@ -89,6 +92,7 @@ namespace ImageSharp /// The vector components are typically expanded in least to greatest significance order. /// /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector2 ToVector2() { return new Vector2( @@ -97,18 +101,21 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) { this.PackedValue = Pack(vector.X, vector.Y); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4(this.ToVector2(), 0F, 1F); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromBytes(byte x, byte y, byte z, byte w) { Vector4 vector = new Vector4(x, y, z, w); @@ -120,6 +127,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -135,6 +143,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -151,6 +160,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -166,6 +176,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -188,12 +199,14 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(NormalizedByte2 other) { return this.PackedValue == other.PackedValue; } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return this.PackedValue.GetHashCode(); @@ -211,6 +224,7 @@ namespace ImageSharp /// The x-component /// The y-component /// The containing the packed values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ushort Pack(float x, float y) { int byte2 = ((ushort)Math.Round(x.Clamp(-1F, 1F) * 127F) & 0xFF) << 0; diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs index 22eba5182..c5de795e2 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing four 8-bit signed normalized values, ranging from −1 to 1. @@ -64,6 +65,7 @@ namespace ImageSharp /// /// True if the parameter is equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(NormalizedByte4 left, NormalizedByte4 right) { return left.PackedValue == right.PackedValue; @@ -81,18 +83,21 @@ namespace ImageSharp /// /// True if the parameter is not equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(NormalizedByte4 left, NormalizedByte4 right) { return left.PackedValue != right.PackedValue; } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) { this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4( @@ -103,6 +108,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromBytes(byte x, byte y, byte z, byte w) { Vector4 vector = new Vector4(x, y, z, w); @@ -114,6 +120,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -129,6 +136,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -145,6 +153,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -160,6 +169,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -182,12 +192,14 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(NormalizedByte4 other) { return this.PackedValue == other.PackedValue; } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return this.PackedValue.GetHashCode(); @@ -207,6 +219,7 @@ namespace ImageSharp /// The z-component /// The w-component /// The containing the packed values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(float x, float y, float z, float w) { uint byte4 = ((uint)Math.Round(x.Clamp(-1F, 1F) * 127F) & 0xFF) << 0; diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs index 34b2fc320..8e3570047 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing two 16-bit signed normalized values, ranging from −1 to 1. @@ -62,6 +63,7 @@ namespace ImageSharp /// /// True if the parameter is equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(NormalizedShort2 left, NormalizedShort2 right) { return left.Equals(right); @@ -79,24 +81,28 @@ namespace ImageSharp /// /// True if the parameter is not equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(NormalizedShort2 left, NormalizedShort2 right) { return !left.Equals(right); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) { this.PackedValue = Pack(vector.X, vector.Y); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4(this.ToVector2(), 0, 1); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromBytes(byte x, byte y, byte z, byte w) { Vector4 vector = new Vector4(x, y, z, w); @@ -108,6 +114,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -123,6 +130,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -139,6 +147,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -154,6 +163,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -174,6 +184,7 @@ namespace ImageSharp /// The vector components are typically expanded in least to greatest significance order. /// /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector2 ToVector2() { const float MaxVal = 0x7FFF; @@ -190,12 +201,14 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(NormalizedShort2 other) { return this.PackedValue.Equals(other.PackedValue); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return this.PackedValue.GetHashCode(); @@ -213,6 +226,7 @@ namespace ImageSharp /// The x-component /// The y-component /// The containing the packed values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(float x, float y) { const float MaxPos = 0x7FFF; diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs index 9742a5f34..81d182cee 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing four 16-bit signed normalized values, ranging from −1 to 1. @@ -64,6 +65,7 @@ namespace ImageSharp /// /// True if the parameter is equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(NormalizedShort4 left, NormalizedShort4 right) { return left.Equals(right); @@ -81,18 +83,21 @@ namespace ImageSharp /// /// True if the parameter is not equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(NormalizedShort4 left, NormalizedShort4 right) { return !left.Equals(right); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) { this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { const float MaxVal = 0x7FFF; @@ -105,6 +110,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromBytes(byte x, byte y, byte z, byte w) { Vector4 vector = new Vector4(x, y, z, w); @@ -116,6 +122,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -131,6 +138,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -147,6 +155,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -162,6 +171,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -184,12 +194,14 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(NormalizedShort4 other) { return this.PackedValue.Equals(other.PackedValue); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return this.PackedValue.GetHashCode(); @@ -209,6 +221,7 @@ namespace ImageSharp /// The z-component /// The w-component /// The containing the packed values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong Pack(float x, float y, float z, float w) { const float MaxPos = 0x7FFF; diff --git a/src/ImageSharp/Colors/PackedPixel/Rg32.cs b/src/ImageSharp/Colors/PackedPixel/Rg32.cs index d885a4470..f7dab68c3 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rg32.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rg32.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing two 16-bit unsigned normalized values ranging from 0 to 1. @@ -47,6 +48,7 @@ namespace ImageSharp /// /// True if the parameter is equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Rg32 left, Rg32 right) { return left.PackedValue == right.PackedValue; @@ -64,6 +66,7 @@ namespace ImageSharp /// /// True if the parameter is not equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rg32 left, Rg32 right) { return left.PackedValue != right.PackedValue; @@ -74,6 +77,7 @@ namespace ImageSharp /// The vector components are typically expanded in least to greatest significance order. /// /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector2 ToVector2() { return new Vector2( @@ -82,24 +86,28 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) { this.PackedValue = Pack(vector.X, vector.Y); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4(this.ToVector2(), 0F, 1F); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromBytes(byte x, byte y, byte z, byte w) { this.PackFromVector4(new Vector4(x, y, z, w) / 255F); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -110,6 +118,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -121,6 +130,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -131,6 +141,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -148,6 +159,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Rg32 other) { return this.PackedValue == other.PackedValue; @@ -160,6 +172,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return this.PackedValue.GetHashCode(); @@ -171,6 +184,7 @@ namespace ImageSharp /// The x-component /// The y-component /// The containing the packed values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(float x, float y) { return (uint)( diff --git a/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs b/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs index da7dbe1ee..8c2261028 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed vector type containing unsigned normalized values ranging from 0 to 1. @@ -50,6 +51,7 @@ namespace ImageSharp /// /// True if the parameter is equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Rgba1010102 left, Rgba1010102 right) { return left.PackedValue == right.PackedValue; @@ -67,12 +69,14 @@ namespace ImageSharp /// /// True if the parameter is not equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rgba1010102 left, Rgba1010102 right) { return left.PackedValue != right.PackedValue; } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4( @@ -83,18 +87,21 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) { this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromBytes(byte x, byte y, byte z, byte w) { this.PackFromVector4(new Vector4(x, y, z, w) / 255F); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -105,6 +112,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -116,6 +124,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -126,6 +135,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -143,6 +153,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Rgba1010102 other) { return this.PackedValue == other.PackedValue; @@ -155,6 +166,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return this.PackedValue.GetHashCode(); @@ -168,6 +180,7 @@ namespace ImageSharp /// The z-component /// The w-component /// The containing the packed values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(float x, float y, float z, float w) { return (uint)( diff --git a/src/ImageSharp/Colors/PackedPixel/Rgba64.cs b/src/ImageSharp/Colors/PackedPixel/Rgba64.cs index 64631f1e1..bb2182649 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rgba64.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rgba64.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing four 16-bit unsigned normalized values ranging from 0 to 1. @@ -49,6 +50,7 @@ namespace ImageSharp /// /// True if the parameter is equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Rgba64 left, Rgba64 right) { return left.PackedValue == right.PackedValue; @@ -66,12 +68,14 @@ namespace ImageSharp /// /// True if the parameter is not equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rgba64 left, Rgba64 right) { return left.PackedValue != right.PackedValue; } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4( @@ -82,18 +86,21 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) { this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromBytes(byte x, byte y, byte z, byte w) { this.PackFromVector4(new Vector4(x, y, z, w) / 255F); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -104,6 +111,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -115,6 +123,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -125,6 +134,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -142,6 +152,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Rgba64 other) { return this.PackedValue == other.PackedValue; @@ -154,6 +165,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return this.PackedValue.GetHashCode(); @@ -167,6 +179,7 @@ namespace ImageSharp /// The z-component /// The w-component /// The containing the packed values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong Pack(float x, float y, float z, float w) { return (ulong)Math.Round(x.Clamp(0, 1) * 65535F) | diff --git a/src/ImageSharp/Colors/PackedPixel/Short2.cs b/src/ImageSharp/Colors/PackedPixel/Short2.cs index d45a80fcb..6bfc5d40c 100644 --- a/src/ImageSharp/Colors/PackedPixel/Short2.cs +++ b/src/ImageSharp/Colors/PackedPixel/Short2.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing two 16-bit signed integer values. @@ -62,6 +63,7 @@ namespace ImageSharp /// /// True if the parameter is equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Short2 left, Short2 right) { return left.PackedValue == right.PackedValue; @@ -79,24 +81,28 @@ namespace ImageSharp /// /// True if the parameter is not equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Short2 left, Short2 right) { return left.PackedValue != right.PackedValue; } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) { this.PackedValue = Pack(vector.X, vector.Y); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4((short)(this.PackedValue & 0xFFFF), (short)(this.PackedValue >> 0x10), 0, 1); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromBytes(byte x, byte y, byte z, byte w) { Vector2 vector = new Vector2(x, y) / 255; @@ -106,6 +112,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzBytes(byte[] bytes, int startIndex) { Vector2 vector = this.ToVector2(); @@ -121,6 +128,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzwBytes(byte[] bytes, int startIndex) { Vector2 vector = this.ToVector2(); @@ -137,6 +145,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxBytes(byte[] bytes, int startIndex) { Vector2 vector = this.ToVector2(); @@ -152,6 +161,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxwBytes(byte[] bytes, int startIndex) { Vector2 vector = this.ToVector2(); @@ -172,6 +182,7 @@ namespace ImageSharp /// The vector components are typically expanded in least to greatest significance order. /// /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector2 ToVector2() { return new Vector2((short)(this.PackedValue & 0xFFFF), (short)(this.PackedValue >> 0x10)); @@ -184,12 +195,14 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Short2 other) { return this == other; } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return this.PackedValue.GetHashCode(); @@ -207,6 +220,7 @@ namespace ImageSharp /// The x-component /// The y-component /// The containing the packed values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(float x, float y) { // Largest two byte positive number 0xFFFF >> 1; diff --git a/src/ImageSharp/Colors/PackedPixel/Short4.cs b/src/ImageSharp/Colors/PackedPixel/Short4.cs index cd112a90f..4b1f3c253 100644 --- a/src/ImageSharp/Colors/PackedPixel/Short4.cs +++ b/src/ImageSharp/Colors/PackedPixel/Short4.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing four 16-bit signed integer values. @@ -64,6 +65,7 @@ namespace ImageSharp /// /// True if the parameter is not equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Short4 left, Short4 right) { return left.PackedValue == right.PackedValue; @@ -81,18 +83,21 @@ namespace ImageSharp /// /// True if the parameter is not equal to the parameter; otherwise, false. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Short4 left, Short4 right) { return left.PackedValue != right.PackedValue; } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) { this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4( @@ -103,6 +108,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromBytes(byte x, byte y, byte z, byte w) { Vector4 vector = new Vector4(x, y, z, w) / 255; @@ -112,6 +118,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -127,6 +134,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToXyzwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -143,6 +151,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -158,6 +167,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ToZyxwBytes(byte[] bytes, int startIndex) { Vector4 vector = this.ToVector4(); @@ -180,6 +190,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Short4 other) { return this == other; @@ -189,6 +200,7 @@ namespace ImageSharp /// Gets the hash code for the current instance. /// /// Hash code for the instance. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return this.PackedValue.GetHashCode(); @@ -211,6 +223,7 @@ namespace ImageSharp /// The z-component /// The w-component /// The containing the packed values. + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong Pack(float x, float y, float z, float w) { // Largest two byte positive number 0xFFFF >> 1; From 92405c30ab2dc2fa02f332ee662c4d2432304735 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 7 Feb 2017 12:11:22 +1100 Subject: [PATCH 044/142] Inline ToVector4() 10ms faster resize benchmark on my machine. --- src/ImageSharp/Colors/Color.cs | 2 ++ src/ImageSharp/Colors/PackedPixel/Alpha8.cs | 2 ++ src/ImageSharp/Colors/PackedPixel/Argb.cs | 2 ++ src/ImageSharp/Colors/PackedPixel/Bgr565.cs | 2 ++ src/ImageSharp/Colors/PackedPixel/Bgra4444.cs | 2 ++ src/ImageSharp/Colors/PackedPixel/Bgra5551.cs | 2 ++ src/ImageSharp/Colors/PackedPixel/Byte4.cs | 12 ++++-------- src/ImageSharp/Colors/PackedPixel/HalfSingle.cs | 2 ++ src/ImageSharp/Colors/PackedPixel/HalfVector2.cs | 2 ++ src/ImageSharp/Colors/PackedPixel/HalfVector4.cs | 2 ++ src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs | 2 ++ src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs | 2 ++ .../Colors/PackedPixel/NormalizedShort2.cs | 2 ++ .../Colors/PackedPixel/NormalizedShort4.cs | 2 ++ 14 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/ImageSharp/Colors/Color.cs b/src/ImageSharp/Colors/Color.cs index 036ee14c1..139a68292 100644 --- a/src/ImageSharp/Colors/Color.cs +++ b/src/ImageSharp/Colors/Color.cs @@ -8,6 +8,7 @@ namespace ImageSharp using System; using System.Globalization; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255. @@ -313,6 +314,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; diff --git a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs index 361fe5b9e..a8e962ac2 100644 --- a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs +++ b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing a single 8 bit normalized W values that is ranging from 0 to 1. @@ -62,6 +63,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4(0, 0, 0, this.PackedValue / 255F); diff --git a/src/ImageSharp/Colors/PackedPixel/Argb.cs b/src/ImageSharp/Colors/PackedPixel/Argb.cs index 8ab8e3f43..5478dd9ef 100644 --- a/src/ImageSharp/Colors/PackedPixel/Argb.cs +++ b/src/ImageSharp/Colors/PackedPixel/Argb.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255. @@ -209,6 +210,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; diff --git a/src/ImageSharp/Colors/PackedPixel/Bgr565.cs b/src/ImageSharp/Colors/PackedPixel/Bgr565.cs index 1f1ce0a17..0bce70363 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgr565.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgr565.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. The x and z components use 5 bits, and the y component uses 6 bits. @@ -84,6 +85,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4(this.ToVector3(), 1F); diff --git a/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs b/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs index 1f33cb791..892a3d4b6 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing unsigned normalized values, ranging from 0 to 1, using 4 bits each for x, y, z, and w. @@ -64,6 +65,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { const float Max = 1 / 15F; diff --git a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs b/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs index d0c52068d..64b506f7b 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. The x , y and z components use 5 bits, and the w component uses 1 bit. @@ -66,6 +67,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4( diff --git a/src/ImageSharp/Colors/PackedPixel/Byte4.cs b/src/ImageSharp/Colors/PackedPixel/Byte4.cs index 69c69ecaf..f15bbd711 100644 --- a/src/ImageSharp/Colors/PackedPixel/Byte4.cs +++ b/src/ImageSharp/Colors/PackedPixel/Byte4.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing four 8-bit unsigned integer values, ranging from 0 to 255. @@ -66,19 +67,14 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } - /// - /// Sets the packed representation from a Vector4. - /// - /// The vector to create the packed representation from. + /// public void PackFromVector4(Vector4 vector) { this.PackedValue = Pack(ref vector); } - /// - /// Expands the packed representation into a Vector4. - /// - /// The expanded vector. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4( diff --git a/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs b/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs index 22e32aa56..babb0ab71 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing a single 16 bit floating point value. @@ -85,6 +86,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4(this.ToSingle(), 0, 0, 1); diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs index 7c17dcea8..427779700 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing two 16-bit floating-point values. @@ -98,6 +99,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { Vector2 vector = this.ToVector2(); diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs b/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs index a7f10fc71..227618331 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing four 16-bit floating-point values. @@ -89,6 +90,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4( diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs index f1dae1066..a6448512b 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed packed pixel type containing two 8-bit signed normalized values, ranging from −1 to 1. @@ -103,6 +104,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4(this.ToVector2(), 0F, 1F); diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs index 22eba5182..9a42df1a9 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing four 8-bit signed normalized values, ranging from −1 to 1. @@ -93,6 +94,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4( diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs index 34b2fc320..7422bb598 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing two 16-bit signed normalized values, ranging from −1 to 1. @@ -91,6 +92,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { return new Vector4(this.ToVector2(), 0, 1); diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs index 9742a5f34..f3fb0c506 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Packed pixel type containing four 16-bit signed normalized values, ranging from −1 to 1. @@ -93,6 +94,7 @@ namespace ImageSharp } /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() { const float MaxVal = 0x7FFF; From 6550d3f1cfe56a7dc781dbcf5c0385419722f746 Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Tue, 7 Feb 2017 07:43:02 +0100 Subject: [PATCH 045/142] Moved synchronization code to the ExifProfile. --- src/ImageSharp/MetaData/ImageMetaData.cs | 23 +------------ .../MetaData/Profiles/Exif/ExifProfile.cs | 20 +++++++++++ .../MetaData/ImageMetaDataTests.cs | 23 +++---------- .../Profiles/Exif/ExifProfileTests.cs | 33 +++++++++++++++++++ 4 files changed, 58 insertions(+), 41 deletions(-) diff --git a/src/ImageSharp/MetaData/ImageMetaData.cs b/src/ImageSharp/MetaData/ImageMetaData.cs index f4aef9863..a40df3110 100644 --- a/src/ImageSharp/MetaData/ImageMetaData.cs +++ b/src/ImageSharp/MetaData/ImageMetaData.cs @@ -142,28 +142,7 @@ namespace ImageSharp /// internal void SyncProfiles() { - this.SyncExifProfile(); - } - - private void SyncExifProfile() - { - if (this.ExifProfile == null) - { - return; - } - - this.SyncExifResolution(ExifTag.XResolution, this.HorizontalResolution); - this.SyncExifResolution(ExifTag.YResolution, this.VerticalResolution); - } - - private void SyncExifResolution(ExifTag tag, double resolution) - { - ExifValue value = this.ExifProfile.GetValue(tag); - if (value != null) - { - Rational newResolution = new Rational(resolution, false); - this.ExifProfile.SetValue(tag, newResolution); - } + this.ExifProfile?.Sync(this); } } } diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs index 507463985..3bd5ef3cb 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs @@ -224,6 +224,26 @@ namespace ImageSharp return writer.GetData(); } + /// + /// Synchronizes the profiles with the specified meta data. + /// + /// The meta data. + internal void Sync(ImageMetaData metaData) + { + this.SyncResolution(ExifTag.XResolution, metaData.HorizontalResolution); + this.SyncResolution(ExifTag.YResolution, metaData.VerticalResolution); + } + + private void SyncResolution(ExifTag tag, double resolution) + { + ExifValue value = this.GetValue(tag); + if (value != null) + { + Rational newResolution = new Rational(resolution, false); + this.SetValue(tag, newResolution); + } + } + private void InitializeValues() { if (this.values != null) diff --git a/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs b/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs index 60e04e18d..3c0057b62 100644 --- a/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs +++ b/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs @@ -80,28 +80,13 @@ namespace ImageSharp.Tests Image image = new Image(1, 1); image.MetaData.ExifProfile = exifProfile; - image.MetaData.HorizontalResolution = 200; - image.MetaData.VerticalResolution = 300; - - image.MetaData.HorizontalResolution = 100; - - Assert.Equal(200, ((Rational)image.MetaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); - Assert.Equal(300, ((Rational)image.MetaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); - - image.MetaData.SyncProfiles(); - - Assert.Equal(100, ((Rational)image.MetaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); - Assert.Equal(300, ((Rational)image.MetaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); - - image.MetaData.VerticalResolution = 150; - - Assert.Equal(100, ((Rational)image.MetaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); - Assert.Equal(300, ((Rational)image.MetaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); + image.MetaData.HorizontalResolution = 400; + image.MetaData.VerticalResolution = 500; image.MetaData.SyncProfiles(); - Assert.Equal(100, ((Rational)image.MetaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); - Assert.Equal(150, ((Rational)image.MetaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); + Assert.Equal(400, ((Rational)image.MetaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); + Assert.Equal(500, ((Rational)image.MetaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); } } } diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs index f4a2bcf40..8ec57057f 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs @@ -203,6 +203,39 @@ namespace ImageSharp.Tests Assert.Equal(7, image.MetaData.ExifProfile.Values.Count()); } + [Fact] + public void Syncs() + { + ExifProfile exifProfile = new ExifProfile(); + exifProfile.SetValue(ExifTag.XResolution, new Rational(200)); + exifProfile.SetValue(ExifTag.YResolution, new Rational(300)); + + ImageMetaData metaData = new ImageMetaData(); + metaData.ExifProfile = exifProfile; + metaData.HorizontalResolution = 200; + metaData.VerticalResolution = 300; + + metaData.HorizontalResolution = 100; + + Assert.Equal(200, ((Rational)metaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); + Assert.Equal(300, ((Rational)metaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); + + exifProfile.Sync(metaData); + + Assert.Equal(100, ((Rational)metaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); + Assert.Equal(300, ((Rational)metaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); + + metaData.VerticalResolution = 150; + + Assert.Equal(100, ((Rational)metaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); + Assert.Equal(300, ((Rational)metaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); + + exifProfile.Sync(metaData); + + Assert.Equal(100, ((Rational)metaData.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); + Assert.Equal(150, ((Rational)metaData.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); + } + [Fact] public void Values() { From 1dd168e90d974b80ddfd49eda6bdd4cc7d9f34b7 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 8 Feb 2017 00:19:48 +1100 Subject: [PATCH 046/142] Correctly read resolution. Fix #96 --- .../JpegDecoderCore.cs | 24 +++++++++++++++++-- src/ImageSharp/Image/Image{TColor}.cs | 16 ++----------- .../ImageSharp.Sandbox46.csproj | 4 ++-- .../Formats/Jpg/JpegDecoderTests.cs | 20 ++++++++++++++++ 4 files changed, 46 insertions(+), 18 deletions(-) diff --git a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs index 9050c20e1..eb70c4f8c 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs @@ -2,6 +2,7 @@ // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // + namespace ImageSharp.Formats { using System; @@ -66,6 +67,11 @@ namespace ImageSharp.Formats /// private bool isJfif; + /// + /// Whether the image has a EXIF header + /// + private bool isExif; + /// /// The vertical resolution. Calculated if the image has a JFIF header. /// @@ -550,6 +556,19 @@ namespace ImageSharp.Formats image.MetaData.HorizontalResolution = this.horizontalResolution; image.MetaData.VerticalResolution = this.verticalResolution; } + else if (this.isExif) + { + ExifValue horizontal = image.MetaData.ExifProfile.GetValue(ExifTag.XResolution); + ExifValue vertical = image.MetaData.ExifProfile.GetValue(ExifTag.YResolution); + double horizontalValue = horizontal != null ? ((Rational)horizontal.Value).ToDouble() : 0; + double verticalValue = vertical != null ? ((Rational)vertical.Value).ToDouble() : 0; + + if (horizontalValue > 0 && verticalValue > 0) + { + image.MetaData.HorizontalResolution = horizontalValue; + image.MetaData.VerticalResolution = verticalValue; + } + } } /// @@ -951,6 +970,7 @@ namespace ImageSharp.Formats if (profile[0] == 'E' && profile[1] == 'x' && profile[2] == 'i' && profile[3] == 'f' && profile[4] == '\0' && profile[5] == '\0') { + this.isExif = true; image.MetaData.ExifProfile = new ExifProfile(profile); } } @@ -976,8 +996,8 @@ namespace ImageSharp.Formats if (this.isJfif) { - this.horizontalResolution = (short)(this.Temp[9] + (this.Temp[10] << 8)); - this.verticalResolution = (short)(this.Temp[11] + (this.Temp[12] << 8)); + this.horizontalResolution = (short)(this.Temp[9] + (this.Temp[8] << 8)); + this.verticalResolution = (short)(this.Temp[11] + (this.Temp[10] << 8)); } if (remaining > 0) diff --git a/src/ImageSharp/Image/Image{TColor}.cs b/src/ImageSharp/Image/Image{TColor}.cs index 7223bc1d8..c16bba344 100644 --- a/src/ImageSharp/Image/Image{TColor}.cs +++ b/src/ImageSharp/Image/Image{TColor}.cs @@ -127,13 +127,7 @@ namespace ImageSharp /// the default value is used. /// /// The width of the image in inches. - public double InchWidth - { - get - { - return this.Width / this.MetaData.HorizontalResolution; - } - } + public double InchWidth => this.Width / this.MetaData.HorizontalResolution; /// /// Gets the height of the image in inches. It is calculated as the height of the image @@ -141,13 +135,7 @@ namespace ImageSharp /// the default value is used. /// /// The height of the image in inches. - public double InchHeight - { - get - { - return this.Height / this.MetaData.VerticalResolution; - } - } + public double InchHeight => this.Height / this.MetaData.VerticalResolution; /// /// Gets a value indicating whether this image is animated. diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj index 1bce8003e..2444db031 100644 --- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj +++ b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj @@ -238,8 +238,8 @@ Tests\Formats\Jpg\YCbCrImageTests.cs - - Tests\Image\ImagePropertyTests.cs + + Tests\MetaData\ImagePropertyTests.cs Tests\Image\ImageTests.cs diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 25329d93a..8bce4c4e8 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -100,5 +100,25 @@ namespace ImageSharp.Tests } } } + + [Fact] + public void Decoder_Reads_Correct_Resolution_From_Jfif() + { + using (Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage()) + { + Assert.Equal(300, image.MetaData.HorizontalResolution); + Assert.Equal(300, image.MetaData.VerticalResolution); + } + } + + [Fact] + public void Decoder_Reads_Correct_Resolution_From_Exif() + { + using (Image image = TestFile.Create(TestImages.Jpeg.Baseline.Jpeg420).CreateImage()) + { + Assert.Equal(72, image.MetaData.HorizontalResolution); + Assert.Equal(72, image.MetaData.VerticalResolution); + } + } } } \ No newline at end of file From 6bd8848b48d0cafcae0ebb1e85495d3d88bd969e Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Thu, 9 Feb 2017 12:37:25 +0000 Subject: [PATCH 047/142] Update SixLabors.Shapes --- src/ImageSharp.Drawing.Paths/DrawShape.cs | 112 ------------- src/ImageSharp.Drawing.Paths/FillShape.cs | 78 --------- src/ImageSharp.Drawing.Paths/ShapePath.cs | 63 ++----- src/ImageSharp.Drawing.Paths/ShapeRegion.cs | 17 +- src/ImageSharp.Drawing.Paths/project.json | 2 +- .../Drawing/Paths/DrawBeziersTests.cs | 20 +-- .../Drawing/Paths/DrawLinesTests.cs | 18 +- .../Drawing/Paths/DrawPath.cs | 18 +- .../Drawing/Paths/DrawPolygon.cs | 18 +- .../Drawing/Paths/DrawRectangle.cs | 18 +- .../Drawing/Paths/DrawShape.cs | 156 ------------------ .../Drawing/Paths/FillShape.cs | 107 ------------ .../Drawing/Paths/ShapePathTests.cs | 82 ++------- .../Drawing/Paths/ShapeRegionTests.cs | 43 +++-- 14 files changed, 81 insertions(+), 671 deletions(-) delete mode 100644 src/ImageSharp.Drawing.Paths/DrawShape.cs delete mode 100644 src/ImageSharp.Drawing.Paths/FillShape.cs delete mode 100644 tests/ImageSharp.Tests/Drawing/Paths/DrawShape.cs delete mode 100644 tests/ImageSharp.Tests/Drawing/Paths/FillShape.cs diff --git a/src/ImageSharp.Drawing.Paths/DrawShape.cs b/src/ImageSharp.Drawing.Paths/DrawShape.cs deleted file mode 100644 index 748bb6a11..000000000 --- a/src/ImageSharp.Drawing.Paths/DrawShape.cs +++ /dev/null @@ -1,112 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - - using Drawing; - using Drawing.Brushes; - using Drawing.Pens; - - using SixLabors.Shapes; - - /// - /// Extension methods for the type. - /// - public static partial class ImageExtensions - { - /// - /// Draws the outline of the polygon with the provided pen. - /// - /// The type of the color. - /// The image this method extends. - /// The pen. - /// The shape. - /// The options. - /// The . - public static Image Draw(this Image source, IPen pen, IShape shape, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable - { - return source.Draw(pen, new ShapePath(shape), options); - } - - /// - /// Draws the outline of the polygon with the provided pen. - /// - /// The type of the color. - /// The image this method extends. - /// The pen. - /// The shape. - /// The . - public static Image Draw(this Image source, IPen pen, IShape shape) - where TColor : struct, IPackedPixel, IEquatable - { - return source.Draw(pen, shape, GraphicsOptions.Default); - } - - /// - /// Draws the outline of the polygon with the provided brush at the provided thickness. - /// - /// The type of the color. - /// The image this method extends. - /// The brush. - /// The thickness. - /// The shape. - /// The options. - /// The . - public static Image Draw(this Image source, IBrush brush, float thickness, IShape shape, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable - { - return source.Draw(new Pen(brush, thickness), shape, options); - } - - /// - /// Draws the outline of the polygon with the provided brush at the provided thickness. - /// - /// The type of the color. - /// The image this method extends. - /// The brush. - /// The thickness. - /// The shape. - /// The . - public static Image Draw(this Image source, IBrush brush, float thickness, IShape shape) - where TColor : struct, IPackedPixel, IEquatable - { - return source.Draw(new Pen(brush, thickness), shape); - } - - /// - /// Draws the outline of the polygon with the provided brush at the provided thickness. - /// - /// The type of the color. - /// The image this method extends. - /// The color. - /// The thickness. - /// The shape. - /// The options. - /// The . - public static Image Draw(this Image source, TColor color, float thickness, IShape shape, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable - { - return source.Draw(new SolidBrush(color), thickness, shape, options); - } - - /// - /// Draws the outline of the polygon with the provided brush at the provided thickness. - /// - /// The type of the color. - /// The image this method extends. - /// The color. - /// The thickness. - /// The shape. - /// The . - public static Image Draw(this Image source, TColor color, float thickness, IShape shape) - where TColor : struct, IPackedPixel, IEquatable - { - return source.Draw(new SolidBrush(color), thickness, shape); - } - } -} diff --git a/src/ImageSharp.Drawing.Paths/FillShape.cs b/src/ImageSharp.Drawing.Paths/FillShape.cs deleted file mode 100644 index f143e75b7..000000000 --- a/src/ImageSharp.Drawing.Paths/FillShape.cs +++ /dev/null @@ -1,78 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - - using Drawing; - using Drawing.Brushes; - - using SixLabors.Shapes; - - /// - /// Extension methods for the type. - /// - public static partial class ImageExtensions - { - /// - /// Flood fills the image in the shape of the provided polygon with the specified brush. - /// - /// The type of the color. - /// The image this method extends. - /// The brush. - /// The shape. - /// The graphics options. - /// The . - public static Image Fill(this Image source, IBrush brush, IShape shape, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable - { - return source.Fill(brush, new ShapeRegion(shape), options); - } - - /// - /// Flood fills the image in the shape of the provided polygon with the specified brush. - /// - /// The type of the color. - /// The image this method extends. - /// The brush. - /// The shape. - /// The . - public static Image Fill(this Image source, IBrush brush, IShape shape) - where TColor : struct, IPackedPixel, IEquatable - { - return source.Fill(brush, new ShapeRegion(shape), GraphicsOptions.Default); - } - - /// - /// Flood fills the image in the shape of the provided polygon with the specified brush.. - /// - /// The type of the color. - /// The image this method extends. - /// The color. - /// The shape. - /// The options. - /// The . - public static Image Fill(this Image source, TColor color, IShape shape, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable - { - return source.Fill(new SolidBrush(color), shape, options); - } - - /// - /// Flood fills the image in the shape of the provided polygon with the specified brush.. - /// - /// The type of the color. - /// The image this method extends. - /// The color. - /// The shape. - /// The . - public static Image Fill(this Image source, TColor color, IShape shape) - where TColor : struct, IPackedPixel, IEquatable - { - return source.Fill(new SolidBrush(color), shape); - } - } -} diff --git a/src/ImageSharp.Drawing.Paths/ShapePath.cs b/src/ImageSharp.Drawing.Paths/ShapePath.cs index 5ea38a365..d0376ccc1 100644 --- a/src/ImageSharp.Drawing.Paths/ShapePath.cs +++ b/src/ImageSharp.Drawing.Paths/ShapePath.cs @@ -14,53 +14,27 @@ namespace ImageSharp.Drawing using Rectangle = ImageSharp.Rectangle; /// - /// A drawable mapping between a / and a drawable/fillable region. + /// A drawable mapping between a and a drawable region. /// internal class ShapePath : Drawable { - /// - /// The fillable shape - /// - private readonly IShape shape; - /// /// Initializes a new instance of the class. /// /// The path. public ShapePath(IPath path) - : this(ImmutableArray.Create(path)) { - this.shape = path.AsShape(); - this.Bounds = RectangleF.Ceiling(path.Bounds.Convert()); + this.Path = path; + this.Bounds = path.Bounds.Convert(); } /// - /// Initializes a new instance of the class. + /// Gets the fillable shape /// - /// The shape. - public ShapePath(IShape shape) - : this(shape.Paths) - { - this.shape = shape; - this.Bounds = RectangleF.Ceiling(shape.Bounds.Convert()); - } - - /// - /// Initializes a new instance of the class. - /// - /// The paths. - private ShapePath(ImmutableArray paths) - { - this.Paths = paths; - } - - /// - /// Gets the drawable paths - /// - public ImmutableArray Paths { get; } + public IPath Path { get; } /// - public override int MaxIntersections => this.shape.MaxIntersections; + public override int MaxIntersections => this.Path.MaxIntersections; /// public override Rectangle Bounds { get; } @@ -73,7 +47,7 @@ namespace ImageSharp.Drawing Vector2[] innerbuffer = ArrayPool.Shared.Rent(length); try { - int count = this.shape.FindIntersections(start, end, innerbuffer, length, 0); + int count = this.Path.FindIntersections(start, end, innerbuffer, length, 0); for (int i = 0; i < count; i++) { @@ -96,7 +70,7 @@ namespace ImageSharp.Drawing Vector2[] innerbuffer = ArrayPool.Shared.Rent(length); try { - int count = this.shape.FindIntersections(start, end, innerbuffer, length, 0); + int count = this.Path.FindIntersections(start, end, innerbuffer, length, 0); for (int i = 0; i < count; i++) { @@ -115,24 +89,15 @@ namespace ImageSharp.Drawing public override PointInfo GetPointInfo(int x, int y) { Vector2 point = new Vector2(x, y); - float distanceFromPath = float.MaxValue; - float distanceAlongPath = 0; - - // ReSharper disable once ForCanBeConvertedToForeach - for (int i = 0; i < this.Paths.Length; i++) - { - SixLabors.Shapes.PointInfo p = this.Paths[i].Distance(point); - if (p.DistanceFromPath < distanceFromPath) - { - distanceFromPath = p.DistanceFromPath; - distanceAlongPath = p.DistanceAlongPath; - } - } + SixLabors.Shapes.PointInfo dist = this.Path.Distance(point); return new PointInfo { - DistanceAlongPath = distanceAlongPath, - DistanceFromPath = distanceFromPath + DistanceAlongPath = dist.DistanceAlongPath, + DistanceFromPath = + dist.DistanceFromPath < 0 + ? -dist.DistanceFromPath + : dist.DistanceFromPath }; } } diff --git a/src/ImageSharp.Drawing.Paths/ShapeRegion.cs b/src/ImageSharp.Drawing.Paths/ShapeRegion.cs index dc035e3b0..b02c5c2e5 100644 --- a/src/ImageSharp.Drawing.Paths/ShapeRegion.cs +++ b/src/ImageSharp.Drawing.Paths/ShapeRegion.cs @@ -13,33 +13,24 @@ namespace ImageSharp.Drawing using Rectangle = ImageSharp.Rectangle; /// - /// A drawable mapping between a / and a drawable/fillable region. + /// A mapping between a and a region. /// internal class ShapeRegion : Region { - /// - /// Initializes a new instance of the class. - /// - /// The path. - public ShapeRegion(IPath path) - : this(path.AsShape()) - { - } - /// /// Initializes a new instance of the class. /// /// The shape. - public ShapeRegion(IShape shape) + public ShapeRegion(IPath shape) { - this.Shape = shape; + this.Shape = shape.AsClosedPath(); this.Bounds = shape.Bounds.Convert(); } /// /// Gets the fillable shape /// - public IShape Shape { get; } + public IPath Shape { get; } /// public override int MaxIntersections => this.Shape.MaxIntersections; diff --git a/src/ImageSharp.Drawing.Paths/project.json b/src/ImageSharp.Drawing.Paths/project.json index c903f805a..1cc729380 100644 --- a/src/ImageSharp.Drawing.Paths/project.json +++ b/src/ImageSharp.Drawing.Paths/project.json @@ -44,7 +44,7 @@ "ImageSharp.Drawing": { "target": "project" }, - "SixLabors.Shapes": "0.1.0-alpha0004", + "SixLabors.Shapes": "0.1.0-alpha0005", "StyleCop.Analyzers": { "version": "1.0.0", "type": "build" diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawBeziersTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawBeziersTests.cs index 0d3b46981..82e2f72a2 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawBeziersTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/DrawBeziersTests.cs @@ -50,9 +50,10 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(GraphicsOptions.Default, processor.Options); ShapePath path = Assert.IsType(processor.Path); - Assert.NotEmpty(path.Paths); + Assert.NotNull(path.Path); + + SixLabors.Shapes.Path vector = Assert.IsType(path.Path); - SixLabors.Shapes.Path vector = Assert.IsType(path.Paths[0]); BezierLineSegment segment = Assert.IsType(vector.LineSegments[0]); Pen pen = Assert.IsType>(processor.Pen); @@ -71,9 +72,8 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(noneDefault, processor.Options); ShapePath path = Assert.IsType(processor.Path); - Assert.NotEmpty(path.Paths); - SixLabors.Shapes.Path vector = Assert.IsType(path.Paths[0]); + SixLabors.Shapes.Path vector = Assert.IsType(path.Path); BezierLineSegment segment = Assert.IsType(vector.LineSegments[0]); Pen pen = Assert.IsType>(processor.Pen); @@ -92,9 +92,8 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(GraphicsOptions.Default, processor.Options); ShapePath path = Assert.IsType(processor.Path); - Assert.NotEmpty(path.Paths); - SixLabors.Shapes.Path vector = Assert.IsType(path.Paths[0]); + SixLabors.Shapes.Path vector = Assert.IsType(path.Path); BezierLineSegment segment = Assert.IsType(vector.LineSegments[0]); Pen pen = Assert.IsType>(processor.Pen); @@ -115,9 +114,8 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(noneDefault, processor.Options); ShapePath path = Assert.IsType(processor.Path); - Assert.NotEmpty(path.Paths); - SixLabors.Shapes.Path vector = Assert.IsType(path.Paths[0]); + SixLabors.Shapes.Path vector = Assert.IsType(path.Path); BezierLineSegment segment = Assert.IsType(vector.LineSegments[0]); Pen pen = Assert.IsType>(processor.Pen); @@ -138,9 +136,8 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(GraphicsOptions.Default, processor.Options); ShapePath path = Assert.IsType(processor.Path); - Assert.NotEmpty(path.Paths); - SixLabors.Shapes.Path vector = Assert.IsType(path.Paths[0]); + SixLabors.Shapes.Path vector = Assert.IsType(path.Path); BezierLineSegment segment = Assert.IsType(vector.LineSegments[0]); Assert.Equal(pen, processor.Pen); @@ -157,9 +154,8 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(noneDefault, processor.Options); ShapePath path = Assert.IsType(processor.Path); - Assert.NotEmpty(path.Paths); - SixLabors.Shapes.Path vector = Assert.IsType(path.Paths[0]); + SixLabors.Shapes.Path vector = Assert.IsType(path.Path); BezierLineSegment segment = Assert.IsType(vector.LineSegments[0]); Assert.Equal(pen, processor.Pen); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawLinesTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawLinesTests.cs index bbed3cae6..cc126614f 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawLinesTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/DrawLinesTests.cs @@ -50,9 +50,8 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(GraphicsOptions.Default, processor.Options); ShapePath path = Assert.IsType(processor.Path); - Assert.NotEmpty(path.Paths); - SixLabors.Shapes.Path vector = Assert.IsType(path.Paths[0]); + SixLabors.Shapes.Path vector = Assert.IsType(path.Path); LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); Pen pen = Assert.IsType>(processor.Pen); @@ -71,9 +70,8 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(noneDefault, processor.Options); ShapePath path = Assert.IsType(processor.Path); - Assert.NotEmpty(path.Paths); - SixLabors.Shapes.Path vector = Assert.IsType(path.Paths[0]); + SixLabors.Shapes.Path vector = Assert.IsType(path.Path); LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); Pen pen = Assert.IsType>(processor.Pen); @@ -92,9 +90,8 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(GraphicsOptions.Default, processor.Options); ShapePath path = Assert.IsType(processor.Path); - Assert.NotEmpty(path.Paths); - SixLabors.Shapes.Path vector = Assert.IsType(path.Paths[0]); + SixLabors.Shapes.Path vector = Assert.IsType(path.Path); LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); Pen pen = Assert.IsType>(processor.Pen); @@ -115,9 +112,8 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(noneDefault, processor.Options); ShapePath path = Assert.IsType(processor.Path); - Assert.NotEmpty(path.Paths); - SixLabors.Shapes.Path vector = Assert.IsType(path.Paths[0]); + SixLabors.Shapes.Path vector = Assert.IsType(path.Path); LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); Pen pen = Assert.IsType>(processor.Pen); @@ -138,9 +134,8 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(GraphicsOptions.Default, processor.Options); ShapePath path = Assert.IsType(processor.Path); - Assert.NotEmpty(path.Paths); - SixLabors.Shapes.Path vector = Assert.IsType(path.Paths[0]); + SixLabors.Shapes.Path vector = Assert.IsType(path.Path); LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); Assert.Equal(pen, processor.Pen); @@ -157,9 +152,8 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(noneDefault, processor.Options); ShapePath path = Assert.IsType(processor.Path); - Assert.NotEmpty(path.Paths); - SixLabors.Shapes.Path vector = Assert.IsType(path.Paths[0]); + SixLabors.Shapes.Path vector = Assert.IsType(path.Path); LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); Assert.Equal(pen, processor.Pen); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawPath.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawPath.cs index 531546370..6c1c06813 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawPath.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/DrawPath.cs @@ -50,8 +50,7 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(GraphicsOptions.Default, processor.Options); ShapePath shapepath = Assert.IsType(processor.Path); - Assert.NotEmpty(shapepath.Paths); - Assert.Equal(path, shapepath.Paths[0]); + Assert.Equal(path, shapepath.Path); Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(brush, pen.Brush); @@ -69,8 +68,7 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(noneDefault, processor.Options); ShapePath shapepath = Assert.IsType(processor.Path); - Assert.NotEmpty(shapepath.Paths); - Assert.Equal(path, shapepath.Paths[0]); + Assert.Equal(path, shapepath.Path); Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(brush, pen.Brush); @@ -88,8 +86,7 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(GraphicsOptions.Default, processor.Options); ShapePath shapepath = Assert.IsType(processor.Path); - Assert.NotEmpty(shapepath.Paths); - Assert.Equal(path, shapepath.Paths[0]); + Assert.Equal(path, shapepath.Path); Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(thickness, pen.Width); @@ -109,8 +106,7 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(noneDefault, processor.Options); ShapePath shapepath = Assert.IsType(processor.Path); - Assert.NotEmpty(shapepath.Paths); - Assert.Equal(path, shapepath.Paths[0]); + Assert.Equal(path, shapepath.Path); Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(thickness, pen.Width); @@ -130,8 +126,7 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(GraphicsOptions.Default, processor.Options); ShapePath shapepath = Assert.IsType(processor.Path); - Assert.NotEmpty(shapepath.Paths); - Assert.Equal(path, shapepath.Paths[0]); + Assert.Equal(path, shapepath.Path); Assert.Equal(pen, processor.Pen); } @@ -147,8 +142,7 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(noneDefault, processor.Options); ShapePath shapepath = Assert.IsType(processor.Path); - Assert.NotEmpty(shapepath.Paths); - Assert.Equal(path, shapepath.Paths[0]); + Assert.Equal(path, shapepath.Path); Assert.Equal(pen, processor.Pen); } diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawPolygon.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawPolygon.cs index 399e79dae..9de052331 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawPolygon.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/DrawPolygon.cs @@ -50,9 +50,8 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(GraphicsOptions.Default, processor.Options); ShapePath path = Assert.IsType(processor.Path); - Assert.NotEmpty(path.Paths); - Polygon vector = Assert.IsType(path.Paths[0].AsShape()); + Polygon vector = Assert.IsType(path.Path); LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); Pen pen = Assert.IsType>(processor.Pen); @@ -71,9 +70,8 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(noneDefault, processor.Options); ShapePath path = Assert.IsType(processor.Path); - Assert.NotEmpty(path.Paths); - Polygon vector = Assert.IsType(path.Paths[0].AsShape()); + Polygon vector = Assert.IsType(path.Path); LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); Pen pen = Assert.IsType>(processor.Pen); @@ -92,9 +90,8 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(GraphicsOptions.Default, processor.Options); ShapePath path = Assert.IsType(processor.Path); - Assert.NotEmpty(path.Paths); - Polygon vector = Assert.IsType(path.Paths[0].AsShape()); + Polygon vector = Assert.IsType(path.Path); LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); Pen pen = Assert.IsType>(processor.Pen); @@ -115,9 +112,8 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(noneDefault, processor.Options); ShapePath path = Assert.IsType(processor.Path); - Assert.NotEmpty(path.Paths); - Polygon vector = Assert.IsType(path.Paths[0].AsShape()); + Polygon vector = Assert.IsType(path.Path); LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); Pen pen = Assert.IsType>(processor.Pen); @@ -138,9 +134,8 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(GraphicsOptions.Default, processor.Options); ShapePath path = Assert.IsType(processor.Path); - Assert.NotEmpty(path.Paths); - Polygon vector = Assert.IsType(path.Paths[0].AsShape()); + Polygon vector = Assert.IsType(path.Path); LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); Assert.Equal(pen, processor.Pen); @@ -157,9 +152,8 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(noneDefault, processor.Options); ShapePath path = Assert.IsType(processor.Path); - Assert.NotEmpty(path.Paths); - Polygon vector = Assert.IsType(path.Paths[0].AsShape()); + Polygon vector = Assert.IsType(path.Path); LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); Assert.Equal(pen, processor.Pen); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawRectangle.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawRectangle.cs index 3842063b3..215d5a7c7 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawRectangle.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/DrawRectangle.cs @@ -46,8 +46,7 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(GraphicsOptions.Default, processor.Options); ShapePath shapepath = Assert.IsType(processor.Path); - Assert.NotEmpty(shapepath.Paths); - SixLabors.Shapes.Rectangle rect = Assert.IsType(shapepath.Paths[0].AsShape()); + SixLabors.Shapes.Rectangle rect = Assert.IsType(shapepath.Path); Assert.Equal(rect.Location.X, rectangle.X); Assert.Equal(rect.Location.Y, rectangle.Y); @@ -70,9 +69,8 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(noneDefault, processor.Options); ShapePath shapepath = Assert.IsType(processor.Path); - Assert.NotEmpty(shapepath.Paths); - SixLabors.Shapes.Rectangle rect = Assert.IsType(shapepath.Paths[0].AsShape()); + SixLabors.Shapes.Rectangle rect = Assert.IsType(shapepath.Path); Assert.Equal(rect.Location.X, rectangle.X); Assert.Equal(rect.Location.Y, rectangle.Y); @@ -95,9 +93,8 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(GraphicsOptions.Default, processor.Options); ShapePath shapepath = Assert.IsType(processor.Path); - Assert.NotEmpty(shapepath.Paths); - SixLabors.Shapes.Rectangle rect = Assert.IsType(shapepath.Paths[0].AsShape()); + SixLabors.Shapes.Rectangle rect = Assert.IsType(shapepath.Path); Assert.Equal(rect.Location.X, rectangle.X); Assert.Equal(rect.Location.Y, rectangle.Y); @@ -122,9 +119,8 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(noneDefault, processor.Options); ShapePath shapepath = Assert.IsType(processor.Path); - Assert.NotEmpty(shapepath.Paths); - SixLabors.Shapes.Rectangle rect = Assert.IsType(shapepath.Paths[0].AsShape()); + SixLabors.Shapes.Rectangle rect = Assert.IsType(shapepath.Path); Assert.Equal(rect.Location.X, rectangle.X); Assert.Equal(rect.Location.Y, rectangle.Y); @@ -149,9 +145,8 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(GraphicsOptions.Default, processor.Options); ShapePath shapepath = Assert.IsType(processor.Path); - Assert.NotEmpty(shapepath.Paths); - SixLabors.Shapes.Rectangle rect = Assert.IsType(shapepath.Paths[0].AsShape()); + SixLabors.Shapes.Rectangle rect = Assert.IsType(shapepath.Path); Assert.Equal(rect.Location.X, rectangle.X); Assert.Equal(rect.Location.Y, rectangle.Y); @@ -172,9 +167,8 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(noneDefault, processor.Options); ShapePath shapepath = Assert.IsType(processor.Path); - Assert.NotEmpty(shapepath.Paths); - SixLabors.Shapes.Rectangle rect = Assert.IsType(shapepath.Paths[0].AsShape()); + SixLabors.Shapes.Rectangle rect = Assert.IsType(shapepath.Path); Assert.Equal(rect.Location.X, rectangle.X); Assert.Equal(rect.Location.Y, rectangle.Y); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawShape.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawShape.cs deleted file mode 100644 index 30a6646f4..000000000 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawShape.cs +++ /dev/null @@ -1,156 +0,0 @@ - -namespace ImageSharp.Tests.Drawing.Paths -{ - using System; - using System.IO; - using ImageSharp; - using ImageSharp.Drawing.Brushes; - using Processing; - using System.Collections.Generic; - using Xunit; - using ImageSharp.Drawing; - using System.Numerics; - using SixLabors.Shapes; - using ImageSharp.Drawing.Processors; - using ImageSharp.Drawing.Pens; - - public class DrawShape: IDisposable - { - float thickness = 7.2f; - GraphicsOptions noneDefault = new GraphicsOptions(); - Color color = Color.HotPink; - SolidBrush brush = Brushes.Solid(Color.HotPink); - Pen pen = new Pen(Color.Gray, 99.9f); - IShape shape = new SixLabors.Shapes.Polygon(new LinearLineSegment(new Vector2[] { - new Vector2(10,10), - new Vector2(20,10), - new Vector2(20,10), - new Vector2(30,10), - })); - private ProcessorWatchingImage img; - - public DrawShape() - { - this.img = new Paths.ProcessorWatchingImage(10, 10); - } - - public void Dispose() - { - img.Dispose(); - } - - [Fact] - public void CorrectlySetsBrushThicknessAndShape() - { - img.Draw(brush, thickness, shape); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapePath shapepath = Assert.IsType(processor.Path); - Assert.NotEmpty(shapepath.Paths); - Assert.Equal(shape, shapepath.Paths[0].AsShape()); - - Pen pen = Assert.IsType>(processor.Pen); - Assert.Equal(brush, pen.Brush); - Assert.Equal(thickness, pen.Width); - } - - [Fact] - public void CorrectlySetsBrushThicknessShapeAndOptions() - { - img.Draw(brush, thickness, shape, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(noneDefault, processor.Options); - - ShapePath shapepath = Assert.IsType(processor.Path); - Assert.NotEmpty(shapepath.Paths); - Assert.Equal(shape, shapepath.Paths[0].AsShape()); - - Pen pen = Assert.IsType>(processor.Pen); - Assert.Equal(brush, pen.Brush); - Assert.Equal(thickness, pen.Width); - } - - [Fact] - public void CorrectlySetsColorThicknessAndShape() - { - img.Draw(color, thickness, shape); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapePath shapepath = Assert.IsType(processor.Path); - Assert.NotEmpty(shapepath.Paths); - Assert.Equal(shape, shapepath.Paths[0].AsShape()); - - Pen pen = Assert.IsType>(processor.Pen); - Assert.Equal(thickness, pen.Width); - - SolidBrush brush = Assert.IsType>(pen.Brush); - Assert.Equal(color, brush.Color); - } - - [Fact] - public void CorrectlySetsColorThicknessShapeAndOptions() - { - img.Draw(color, thickness, shape, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(noneDefault, processor.Options); - - ShapePath shapepath = Assert.IsType(processor.Path); - Assert.NotEmpty(shapepath.Paths); - Assert.Equal(shape, shapepath.Paths[0].AsShape()); - - Pen pen = Assert.IsType>(processor.Pen); - Assert.Equal(thickness, pen.Width); - - SolidBrush brush = Assert.IsType>(pen.Brush); - Assert.Equal(color, brush.Color); - } - - [Fact] - public void CorrectlySetsPenAndShape() - { - img.Draw(pen, shape); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapePath shapepath = Assert.IsType(processor.Path); - Assert.NotEmpty(shapepath.Paths); - Assert.Equal(shape, shapepath.Paths[0].AsShape()); - - Assert.Equal(pen, processor.Pen); - } - - [Fact] - public void CorrectlySetsPenShapeAndOptions() - { - img.Draw(pen, shape, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(noneDefault, processor.Options); - - ShapePath shapepath = Assert.IsType(processor.Path); - Assert.NotEmpty(shapepath.Paths); - Assert.Equal(shape, shapepath.Paths[0].AsShape()); - - Assert.Equal(pen, processor.Pen); - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillShape.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillShape.cs deleted file mode 100644 index 140870a3c..000000000 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillShape.cs +++ /dev/null @@ -1,107 +0,0 @@ - -namespace ImageSharp.Tests.Drawing.Paths -{ - using System; - using System.IO; - using ImageSharp; - using ImageSharp.Drawing.Brushes; - using Processing; - using System.Collections.Generic; - using Xunit; - using ImageSharp.Drawing; - using System.Numerics; - using SixLabors.Shapes; - using ImageSharp.Drawing.Processors; - using ImageSharp.Drawing.Pens; - - public class FillShape : IDisposable - { - GraphicsOptions noneDefault = new GraphicsOptions(); - Color color = Color.HotPink; - SolidBrush brush = Brushes.Solid(Color.HotPink); - IShape shape = new Polygon(new LinearLineSegment(new Vector2[] { - new Vector2(10,10), - new Vector2(20,10), - new Vector2(20,10), - new Vector2(30,10), - })); - private ProcessorWatchingImage img; - - public FillShape() - { - this.img = new Paths.ProcessorWatchingImage(10, 10); - } - - public void Dispose() - { - img.Dispose(); - } - - [Fact] - public void CorrectlySetsBrushAndShape() - { - img.Fill(brush, shape); - - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapeRegion region = Assert.IsType(processor.Region); - Assert.Equal(shape, region.Shape); - - Assert.Equal(brush, processor.Brush); - } - - [Fact] - public void CorrectlySetsBrushShapeAndOptions() - { - img.Fill(brush, shape, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(noneDefault, processor.Options); - - ShapeRegion region = Assert.IsType(processor.Region); - Assert.Equal(shape, region.Shape); - - Assert.Equal(brush, processor.Brush); - } - - [Fact] - public void CorrectlySetsColorAndShape() - { - img.Fill(color, shape); - - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapeRegion region = Assert.IsType(processor.Region); - Assert.Equal(shape, region.Shape); - - SolidBrush brush = Assert.IsType>(processor.Brush); - Assert.Equal(color, brush.Color); - } - - [Fact] - public void CorrectlySetsColorShapeAndOptions() - { - img.Fill(color, shape, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(noneDefault, processor.Options); - - ShapeRegion region = Assert.IsType(processor.Region); - Assert.Equal(shape, region.Shape); - - SolidBrush brush = Assert.IsType>(processor.Brush); - Assert.Equal(color, brush.Color); - - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs index 5fc967846..494e2a672 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs @@ -20,30 +20,18 @@ namespace ImageSharp.Tests.Drawing.Paths { private readonly Mock pathMock1; private readonly Mock pathMock2; - private readonly Mock shapeMock1; private readonly SixLabors.Shapes.Rectangle bounds1; public ShapePathTests() { - this.shapeMock1 = new Mock(); this.pathMock2 = new Mock(); this.pathMock1 = new Mock(); this.bounds1 = new SixLabors.Shapes.Rectangle(10.5f, 10, 10, 10); pathMock1.Setup(x => x.Bounds).Returns(this.bounds1); pathMock2.Setup(x => x.Bounds).Returns(this.bounds1); - shapeMock1.Setup(x => x.Bounds).Returns(this.bounds1); // wire up the 2 mocks to reference eachother - pathMock1.Setup(x => x.AsShape()).Returns(() => shapeMock1.Object); - shapeMock1.Setup(x => x.Paths).Returns(() => ImmutableArray.Create(pathMock1.Object, pathMock2.Object)); - } - - [Fact] - public void ShapePathWithPathCallsAsShape() - { - new ShapePath(pathMock1.Object); - - pathMock1.Verify(x => x.AsShape()); + pathMock1.Setup(x => x.AsClosedPath()).Returns(() => pathMock2.Object); } [Fact] @@ -63,7 +51,7 @@ namespace ImageSharp.Tests.Drawing.Paths ShapePath region = new ShapePath(pathMock1.Object); int i = region.MaxIntersections; - shapeMock1.Verify(x => x.MaxIntersections); + pathMock1.Verify(x => x.MaxIntersections); } [Fact] @@ -72,7 +60,7 @@ namespace ImageSharp.Tests.Drawing.Paths int xToScan = 10; ShapePath region = new ShapePath(pathMock1.Object); - shapeMock1.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + pathMock1.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Callback((s, e, b, c, o) => { Assert.Equal(xToScan, s.X); Assert.Equal(xToScan, e.X); @@ -82,7 +70,7 @@ namespace ImageSharp.Tests.Drawing.Paths int i = region.ScanX(xToScan, new float[0], 0, 0); - shapeMock1.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + pathMock1.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } [Fact] @@ -91,7 +79,7 @@ namespace ImageSharp.Tests.Drawing.Paths int yToScan = 10; ShapePath region = new ShapePath(pathMock1.Object); - shapeMock1.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + pathMock1.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Callback((s, e, b, c, o) => { Assert.Equal(yToScan, s.Y); Assert.Equal(yToScan, e.Y); @@ -101,7 +89,7 @@ namespace ImageSharp.Tests.Drawing.Paths int i = region.ScanY(yToScan, new float[0], 0, 0); - shapeMock1.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + pathMock1.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } @@ -109,9 +97,9 @@ namespace ImageSharp.Tests.Drawing.Paths public void ShapePathFromShapeScanXProxyToShape() { int xToScan = 10; - ShapePath region = new ShapePath(shapeMock1.Object); + ShapePath region = new ShapePath(pathMock1.Object); - shapeMock1.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + pathMock1.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Callback((s, e, b, c, o) => { Assert.Equal(xToScan, s.X); Assert.Equal(xToScan, e.X); @@ -121,59 +109,9 @@ namespace ImageSharp.Tests.Drawing.Paths int i = region.ScanX(xToScan, new float[0], 0, 0); - shapeMock1.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); - } - - [Fact] - public void ShapePathFromShapeScanYProxyToShape() - { - int yToScan = 10; - ShapePath region = new ShapePath(shapeMock1.Object); - - shapeMock1.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((s, e, b, c, o) => { - Assert.Equal(yToScan, s.Y); - Assert.Equal(yToScan, e.Y); - Assert.True(s.X < bounds1.Left); - Assert.True(e.X > bounds1.Right); - }).Returns(0); - - int i = region.ScanY(yToScan, new float[0], 0, 0); - - shapeMock1.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); - } - - [Fact] - public void ShapePathFromShapeConvertsBoundsProxyToShape() - { - ShapePath region = new ShapePath(shapeMock1.Object); - - Assert.Equal(Math.Floor(bounds1.Left), region.Bounds.Left); - Assert.Equal(Math.Ceiling(bounds1.Right), region.Bounds.Right); - - shapeMock1.Verify(x => x.Bounds); - } - - [Fact] - public void ShapePathFromShapeMaxIntersectionsProxyToShape() - { - ShapePath region = new ShapePath(shapeMock1.Object); - - int i = region.MaxIntersections; - shapeMock1.Verify(x => x.MaxIntersections); + pathMock1.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } - - [Fact] - public void GetPointInfoCallAllPathsForShape() - { - ShapePath region = new ShapePath(shapeMock1.Object); - - ImageSharp.Drawing.PointInfo info = region.GetPointInfo(10, 1); - - pathMock1.Verify(x => x.Distance(new Vector2(10, 1)), Times.Once); - pathMock2.Verify(x => x.Distance(new Vector2(10, 1)), Times.Once); - } - + [Fact] public void GetPointInfoCallSinglePathForPath() { diff --git a/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs index 675494951..aa7c0575c 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs @@ -19,19 +19,16 @@ namespace ImageSharp.Tests.Drawing.Paths public class ShapeRegionTests { private readonly Mock pathMock; - private readonly Mock shapeMock; private readonly SixLabors.Shapes.Rectangle bounds; public ShapeRegionTests() { - this.shapeMock = new Mock(); this.pathMock = new Mock(); this.bounds = new SixLabors.Shapes.Rectangle(10.5f, 10, 10, 10); - shapeMock.Setup(x => x.Bounds).Returns(this.bounds); + pathMock.Setup(x => x.Bounds).Returns(this.bounds); // wire up the 2 mocks to reference eachother - pathMock.Setup(x => x.AsShape()).Returns(() => shapeMock.Object); - shapeMock.Setup(x => x.Paths).Returns(() => ImmutableArray.Create(pathMock.Object)); + pathMock.Setup(x => x.AsClosedPath()).Returns(() => pathMock.Object); } [Fact] @@ -39,7 +36,7 @@ namespace ImageSharp.Tests.Drawing.Paths { new ShapeRegion(pathMock.Object); - pathMock.Verify(x => x.AsShape()); + pathMock.Verify(x => x.AsClosedPath()); } [Fact] @@ -47,7 +44,7 @@ namespace ImageSharp.Tests.Drawing.Paths { ShapeRegion region = new ShapeRegion(pathMock.Object); - Assert.Equal(shapeMock.Object, region.Shape); + Assert.Equal(pathMock.Object, region.Shape); } [Fact] @@ -58,7 +55,7 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(Math.Floor(bounds.Left), region.Bounds.Left); Assert.Equal(Math.Ceiling(bounds.Right), region.Bounds.Right); - shapeMock.Verify(x => x.Bounds); + pathMock.Verify(x => x.Bounds); } [Fact] @@ -67,7 +64,7 @@ namespace ImageSharp.Tests.Drawing.Paths ShapeRegion region = new ShapeRegion(pathMock.Object); int i = region.MaxIntersections; - shapeMock.Verify(x => x.MaxIntersections); + pathMock.Verify(x => x.MaxIntersections); } [Fact] @@ -76,7 +73,7 @@ namespace ImageSharp.Tests.Drawing.Paths int xToScan = 10; ShapeRegion region = new ShapeRegion(pathMock.Object); - shapeMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + pathMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Callback((s, e, b, c, o) => { Assert.Equal(xToScan, s.X); Assert.Equal(xToScan, e.X); @@ -86,7 +83,7 @@ namespace ImageSharp.Tests.Drawing.Paths int i = region.ScanX(xToScan, new float[0], 0, 0); - shapeMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + pathMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } [Fact] @@ -95,7 +92,7 @@ namespace ImageSharp.Tests.Drawing.Paths int yToScan = 10; ShapeRegion region = new ShapeRegion(pathMock.Object); - shapeMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + pathMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Callback((s, e, b, c, o) => { Assert.Equal(yToScan, s.Y); Assert.Equal(yToScan, e.Y); @@ -105,7 +102,7 @@ namespace ImageSharp.Tests.Drawing.Paths int i = region.ScanY(yToScan, new float[0], 0, 0); - shapeMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + pathMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } @@ -113,9 +110,9 @@ namespace ImageSharp.Tests.Drawing.Paths public void ShapeRegionFromShapeScanXProxyToShape() { int xToScan = 10; - ShapeRegion region = new ShapeRegion(shapeMock.Object); + ShapeRegion region = new ShapeRegion(pathMock.Object); - shapeMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + pathMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Callback((s, e, b, c, o) => { Assert.Equal(xToScan, s.X); Assert.Equal(xToScan, e.X); @@ -125,16 +122,16 @@ namespace ImageSharp.Tests.Drawing.Paths int i = region.ScanX(xToScan, new float[0], 0, 0); - shapeMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + pathMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } [Fact] public void ShapeRegionFromShapeScanYProxyToShape() { int yToScan = 10; - ShapeRegion region = new ShapeRegion(shapeMock.Object); + ShapeRegion region = new ShapeRegion(pathMock.Object); - shapeMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + pathMock.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Callback((s, e, b, c, o) => { Assert.Equal(yToScan, s.Y); Assert.Equal(yToScan, e.Y); @@ -144,27 +141,27 @@ namespace ImageSharp.Tests.Drawing.Paths int i = region.ScanY(yToScan, new float[0], 0, 0); - shapeMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + pathMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } [Fact] public void ShapeRegionFromShapeConvertsBoundsProxyToShape() { - ShapeRegion region = new ShapeRegion(shapeMock.Object); + ShapeRegion region = new ShapeRegion(pathMock.Object); Assert.Equal(Math.Floor(bounds.Left), region.Bounds.Left); Assert.Equal(Math.Ceiling(bounds.Right), region.Bounds.Right); - shapeMock.Verify(x => x.Bounds); + pathMock.Verify(x => x.Bounds); } [Fact] public void ShapeRegionFromShapeMaxIntersectionsProxyToShape() { - ShapeRegion region = new ShapeRegion(shapeMock.Object); + ShapeRegion region = new ShapeRegion(pathMock.Object); int i = region.MaxIntersections; - shapeMock.Verify(x => x.MaxIntersections); + pathMock.Verify(x => x.MaxIntersections); } } } From e92cc1e95c1d61255e98d84d5d030b8db66653aa Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Thu, 9 Feb 2017 14:24:18 +0000 Subject: [PATCH 048/142] update package version inline with rest of project --- src/ImageSharp.Drawing.Paths/project.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp.Drawing.Paths/project.json b/src/ImageSharp.Drawing.Paths/project.json index 1cc729380..bf6b1fae8 100644 --- a/src/ImageSharp.Drawing.Paths/project.json +++ b/src/ImageSharp.Drawing.Paths/project.json @@ -1,5 +1,5 @@ { - "version": "1.0.0-alpha1-*", + "version": "1.0.0-alpha2-*", "title": "ImageSharp.Drawing.Paths", "description": "A cross-platform library for the processing of image files; written in C#", "authors": [ From eb46afae69899b8c27ad3098733834c8ec8e73b3 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sun, 12 Feb 2017 16:09:42 +0000 Subject: [PATCH 049/142] file path based apis --- README.md | 10 +++ src/ImageSharp/Image.cs | 17 ++++ src/ImageSharp/Image/Image{TColor}.cs | 80 ++++++++++++++++++- src/ImageSharp/project.json | 24 ++++++ .../Formats/Bmp/BitmapTests.cs | 5 +- tests/ImageSharp.Tests/Image/ImageTests.cs | 31 +++++++ tests/ImageSharp.Tests/TestFile.cs | 5 ++ 7 files changed, 166 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 79f899462..cfbd18de6 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,16 @@ Many `Image` methods are also fluent. Here's an example of the code required to resize an image using the default Bicubic resampler then turn the colors into their grayscale equivalent using the BT709 standard matrix. +On platforms supporting netstandard 1.3+ +```csharp +using (Image image = new Image("foo.jpg")) +{ + image.Resize(image.Width / 2, image.Height / 2) + .Grayscale() + .Save("bar.jpg"); // automatic encoder selected based on extension. +} +``` +on netstandard 1.1 - 1.2 ```csharp using (FileStream stream = File.OpenRead("foo.jpg")) using (FileStream output = File.OpenWrite("bar.jpg")) diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index 7adea78b2..4da9cac36 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -44,6 +44,23 @@ namespace ImageSharp { } +#if !NO_FILE_IO + /// + /// Initializes a new instance of the class. + /// + /// + /// A file path to read image information. + /// + /// + /// The configuration providing initialization code which allows extending the library. + /// + /// Thrown if the is null. + public Image(string filePath, Configuration configuration = null) + : base(filePath, configuration) + { + } +#endif + /// /// Initializes a new instance of the class. /// diff --git a/src/ImageSharp/Image/Image{TColor}.cs b/src/ImageSharp/Image/Image{TColor}.cs index c16bba344..ded3e376e 100644 --- a/src/ImageSharp/Image/Image{TColor}.cs +++ b/src/ImageSharp/Image/Image{TColor}.cs @@ -63,6 +63,28 @@ namespace ImageSharp this.Load(stream); } +#if !NO_FILE_IO + /// + /// Initializes a new instance of the class. + /// + /// + /// The file containing image information. + /// + /// + /// The configuration providing initialization code which allows extending the library. + /// + /// Thrown if the is null. + public Image(string filePath, Configuration configuration = null) + : base(configuration) + { + Guard.NotNull(filePath, nameof(filePath)); + using (var fs = File.OpenRead(filePath)) + { + this.Load(fs); + } + } +#endif + /// /// Initializes a new instance of the class. /// @@ -189,11 +211,12 @@ namespace ImageSharp /// /// The stream to save the image to. /// The format to save the image as. - /// Thrown if the stream is null. + /// Thrown if the stream or format is null. /// The public Image Save(Stream stream, IImageFormat format) { Guard.NotNull(stream, nameof(stream)); + Guard.NotNull(format, nameof(format)); format.Encoder.Encode(this, stream); return this; } @@ -203,13 +226,14 @@ namespace ImageSharp /// /// The stream to save the image to. /// The encoder to save the image with. - /// Thrown if the stream is null. + /// Thrown if the stream or encoder is null. /// /// The . /// public Image Save(Stream stream, IImageEncoder encoder) { Guard.NotNull(stream, nameof(stream)); + Guard.NotNull(encoder, nameof(encoder)); encoder.Encode(this, stream); // Reset to the start of the stream. @@ -221,6 +245,58 @@ namespace ImageSharp return this; } +#if !NO_FILE_IO + /// + /// Saves the image to the given stream using the currently loaded image format. + /// + /// The file path to save the image to. + /// Thrown if the stream is null. + /// The + public Image Save(string filePath) + { + var ext = Path.GetExtension(filePath).Trim('.'); + var format = this.Configuration.ImageFormats.SingleOrDefault(f => f.SupportedExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase)); + if (format == null) + { + throw new InvalidOperationException($"No image formats have been registered for the file extension '{ext}'."); + } + + return this.Save(filePath, format); + } + + /// + /// Saves the image to the given stream using the currently loaded image format. + /// + /// The file path to save the image to. + /// The format to save the image as. + /// Thrown if the format is null. + /// The + public Image Save(string filePath, IImageFormat format) + { + Guard.NotNull(format, nameof(format)); + using (var fs = File.Create(filePath)) + { + return this.Save(fs, format); + } + } + + /// + /// Saves the image to the given stream using the currently loaded image format. + /// + /// The file path to save the image to. + /// The encoder to save the image with. + /// Thrown if the encoder is null. + /// The + public Image Save(string filePath, IImageEncoder encoder) + { + Guard.NotNull(encoder, nameof(encoder)); + using (var fs = File.Create(filePath)) + { + return this.Save(fs, encoder); + } + } +#endif + /// public override string ToString() { diff --git a/src/ImageSharp/project.json b/src/ImageSharp/project.json index 8ad3fd71a..a5d1bb93f 100644 --- a/src/ImageSharp/project.json +++ b/src/ImageSharp/project.json @@ -46,7 +46,31 @@ "System.Runtime.CompilerServices.Unsafe": "4.0.0" }, "frameworks": { + "netstandard1.3": { + "dependencies": { + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11", + "System.Diagnostics.Tools": "4.0.1", + "System.IO": "4.1.0", + "System.IO.FileSystem": "4.1.0", + "System.IO.Compression": "4.1.0", + "System.Linq": "4.1.0", + "System.Numerics.Vectors": "4.1.1", + "System.ObjectModel": "4.0.12", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime.Extensions": "4.1.0", + "System.Runtime.InteropServices": "4.1.0", + "System.Runtime.Numerics": "4.0.1", + "System.Text.Encoding.Extensions": "4.0.11", + "System.Threading": "4.0.11", + "System.Threading.Tasks": "4.0.11", + "System.Threading.Tasks.Parallel": "4.0.1" + } + }, "netstandard1.1": { + "buildOptions": { + "define": [ "NO_FILE_IO" ] + }, "dependencies": { "System.Collections": "4.0.11", "System.Diagnostics.Debug": "4.0.11", diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BitmapTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BitmapTests.cs index 2eb81a623..c1275335d 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BitmapTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BitmapTests.cs @@ -31,10 +31,7 @@ namespace ImageSharp.Tests string filename = file.GetFileNameWithoutExtension(bitsPerPixel); using (Image image = file.CreateImage()) { - using (FileStream output = File.OpenWrite($"{path}/{filename}.bmp")) - { - image.Save(output, new BmpEncoder { BitsPerPixel = bitsPerPixel }); - } + image.Save($"{path}/{filename}.bmp", new BmpEncoder { BitsPerPixel = bitsPerPixel }); } } } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.cs b/tests/ImageSharp.Tests/Image/ImageTests.cs index c3c092e8e..a22456e52 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.cs @@ -29,5 +29,36 @@ namespace ImageSharp.Tests Assert.Equal(450, image.Height); } } + + [Fact] + public void ConstructorFileSystem() + { + TestFile file = TestFile.Create(TestImages.Bmp.Car); + using (Image image = new Image(file.FilePath)) + { + Assert.Equal(600, image.Width); + Assert.Equal(450, image.Height); + } + } + + [Fact] + public void ConstructorFileSystem_FileNotFound() + { + System.IO.FileNotFoundException ex = Assert.Throws( + () => + { + new Image(Guid.NewGuid().ToString()); + }); + } + + [Fact] + public void ConstructorFileSystem_NullPath() + { + ArgumentNullException ex = Assert.Throws( + () => + { + new Image(null); + }); + } } } diff --git a/tests/ImageSharp.Tests/TestFile.cs b/tests/ImageSharp.Tests/TestFile.cs index 3d6695841..891a45cec 100644 --- a/tests/ImageSharp.Tests/TestFile.cs +++ b/tests/ImageSharp.Tests/TestFile.cs @@ -54,6 +54,11 @@ namespace ImageSharp.Tests /// public byte[] Bytes { get; } + /// + /// The file name. + /// + public string FilePath => this.file; + /// /// The file name. /// From 388887dc02230be75f1b25a2c1fad2e653f832b7 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sun, 12 Feb 2017 19:09:11 +0000 Subject: [PATCH 050/142] improved test coverage --- tests/ImageSharp.Tests/Image/ImageTests.cs | 67 ++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/tests/ImageSharp.Tests/Image/ImageTests.cs b/tests/ImageSharp.Tests/Image/ImageTests.cs index a22456e52..aea4330c6 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests { using System; + using ImageSharp.Formats; + using Xunit; /// @@ -60,5 +62,70 @@ namespace ImageSharp.Tests new Image(null); }); } + + [Fact] + public void Save_DetecedEncoding() + { + string file = TestFile.GetPath("../../TestOutput/Save_DetecedEncoding.png"); + var dir = System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(file)); + using (Image image = new Image(10, 10)) + { + image.Save(file); + } + + var c = TestFile.Create("../../TestOutput/Save_DetecedEncoding.png"); + using (var img = c.CreateImage()) + { + Assert.IsType(img.CurrentImageFormat); + } + } + + [Fact] + public void Save_UnknownExtensionsEncoding() + { + string file = TestFile.GetPath("../../TestOutput/Save_DetecedEncoding.tmp"); + var ex = Assert.Throws( + () => + { + using (Image image = new Image(10, 10)) + { + image.Save(file); + } + }); + } + + [Fact] + public void Save_SetFormat() + { + string file = TestFile.GetPath("../../TestOutput/Save_SetFormat.dat"); + var dir = System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(file)); + using (Image image = new Image(10, 10)) + { + image.Save(file, new PngFormat()); + } + + var c = TestFile.Create("../../TestOutput/Save_SetFormat.dat"); + using (var img = c.CreateImage()) + { + Assert.IsType(img.CurrentImageFormat); + } + } + + [Fact] + public void Save_SetEncoding() + { + string file = TestFile.GetPath("../../TestOutput/Save_SetEncoding.dat"); + var dir = System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(file)); + using (Image image = new Image(10, 10)) + { + image.Save(file, new PngEncoder()); + } + + var c = TestFile.Create("../../TestOutput/Save_SetEncoding.dat"); + using (var img = c.CreateImage()) + { + Assert.IsType(img.CurrentImageFormat); + } + } } } From 15d942e66f72228be588b391499afe7a458f55b4 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 13 Feb 2017 15:11:12 +1100 Subject: [PATCH 051/142] Fix PackVector method in Color and Argb --- src/ImageSharp/Colors/Color.cs | 2 +- src/ImageSharp/Colors/PackedPixel/Argb.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Colors/Color.cs b/src/ImageSharp/Colors/Color.cs index 8d6ce954c..b2f9437ca 100644 --- a/src/ImageSharp/Colors/Color.cs +++ b/src/ImageSharp/Colors/Color.cs @@ -372,9 +372,9 @@ namespace ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(ref Vector4 vector) { - vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One); vector *= MaxBytes; vector += Half; + vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); return (uint)(((byte)vector.X << RedShift) | ((byte)vector.Y << GreenShift) | ((byte)vector.Z << BlueShift) diff --git a/src/ImageSharp/Colors/PackedPixel/Argb.cs b/src/ImageSharp/Colors/PackedPixel/Argb.cs index 432011702..783545c68 100644 --- a/src/ImageSharp/Colors/PackedPixel/Argb.cs +++ b/src/ImageSharp/Colors/PackedPixel/Argb.cs @@ -342,9 +342,9 @@ namespace ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Pack(ref Vector4 vector) { - vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One); vector *= MaxBytes; vector += Half; + vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); return (uint)(((byte)vector.X << RedShift) | ((byte)vector.Y << GreenShift) | ((byte)vector.Z << BlueShift) From f2d507eab5df2c0e590493795bcd6866b8fb8d0d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 13 Feb 2017 15:15:11 +1100 Subject: [PATCH 052/142] Add dithering to quantizers. FIx #15 --- src/ImageSharp/Dithering/Atkinson.cs | 32 ++++++ src/ImageSharp/Dithering/Burks.cs | 31 +++++ src/ImageSharp/Dithering/ErrorDiffusion.cs | 107 ++++++++++++++++++ src/ImageSharp/Dithering/FloydSteinberg.cs | 31 +++++ src/ImageSharp/Dithering/IErrorDiffusion.cs | 34 ++++++ src/ImageSharp/Dithering/JarvisJudiceNinke.cs | 32 ++++++ src/ImageSharp/Dithering/Sierra2.cs | 31 +++++ src/ImageSharp/Dithering/Sierra3.cs | 32 ++++++ src/ImageSharp/Dithering/SierraLite.cs | 31 +++++ src/ImageSharp/Dithering/Stucki.cs | 32 ++++++ src/ImageSharp/Quantizers/IQuantizer.cs | 20 ++++ src/ImageSharp/Quantizers/Octree/Quantizer.cs | 79 ++++++++++++- .../Quantizers/Palette/PaletteQuantizer.cs | 2 +- 13 files changed, 487 insertions(+), 7 deletions(-) create mode 100644 src/ImageSharp/Dithering/Atkinson.cs create mode 100644 src/ImageSharp/Dithering/Burks.cs create mode 100644 src/ImageSharp/Dithering/ErrorDiffusion.cs create mode 100644 src/ImageSharp/Dithering/FloydSteinberg.cs create mode 100644 src/ImageSharp/Dithering/IErrorDiffusion.cs create mode 100644 src/ImageSharp/Dithering/JarvisJudiceNinke.cs create mode 100644 src/ImageSharp/Dithering/Sierra2.cs create mode 100644 src/ImageSharp/Dithering/Sierra3.cs create mode 100644 src/ImageSharp/Dithering/SierraLite.cs create mode 100644 src/ImageSharp/Dithering/Stucki.cs diff --git a/src/ImageSharp/Dithering/Atkinson.cs b/src/ImageSharp/Dithering/Atkinson.cs new file mode 100644 index 000000000..6d1580171 --- /dev/null +++ b/src/ImageSharp/Dithering/Atkinson.cs @@ -0,0 +1,32 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Dithering +{ + /// + /// Applies error diffusion based dithering using the Atkinson image dithering algorithm. + /// + /// + public class Atkinson : ErrorDiffusion + { + /// + /// The diffusion matrix + /// + private static readonly byte[,] AtkinsonMatrix = + { + { 0, 0, 1, 1 }, + { 1, 1, 1, 0 }, + { 0, 1, 0, 0 } + }; + + /// + /// Initializes a new instance of the class. + /// + public Atkinson() + : base(AtkinsonMatrix, 8) + { + } + } +} diff --git a/src/ImageSharp/Dithering/Burks.cs b/src/ImageSharp/Dithering/Burks.cs new file mode 100644 index 000000000..3e2d19e57 --- /dev/null +++ b/src/ImageSharp/Dithering/Burks.cs @@ -0,0 +1,31 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Dithering +{ + /// + /// Applies error diffusion based dithering using the Burks image dithering algorithm. + /// + /// + public class Burks : ErrorDiffusion + { + /// + /// The diffusion matrix + /// + private static readonly byte[,] BurksMatrix = + { + { 0, 0, 0, 8, 4 }, + { 2, 4, 8, 4, 2 } + }; + + /// + /// Initializes a new instance of the class. + /// + public Burks() + : base(BurksMatrix, 32) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Dithering/ErrorDiffusion.cs b/src/ImageSharp/Dithering/ErrorDiffusion.cs new file mode 100644 index 000000000..481e8b4a6 --- /dev/null +++ b/src/ImageSharp/Dithering/ErrorDiffusion.cs @@ -0,0 +1,107 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Dithering +{ + using System; + using System.Numerics; + using System.Runtime.CompilerServices; + + /// + /// The base class for performing effor diffusion based dithering. + /// + public abstract class ErrorDiffusion : IErrorDiffusion + { + /// + /// The vector to perform division. + /// + private readonly Vector4 divisorVector; + + /// + /// The matrix width + /// + private readonly byte matrixHeight; + + /// + /// The matrix height + /// + private readonly byte matrixWidth; + + /// + /// The offset at which to start the dithering operation. + /// + private readonly int startingOffset; + + /// + /// Initializes a new instance of the class. + /// + /// The dithering matrix. + /// The divisor. + protected ErrorDiffusion(byte[,] matrix, byte divisor) + { + Guard.NotNull(matrix, nameof(matrix)); + Guard.MustBeGreaterThan(divisor, 0, nameof(divisor)); + + this.Matrix = matrix; + this.matrixWidth = (byte)(matrix.GetUpperBound(1) + 1); + this.matrixHeight = (byte)(matrix.GetUpperBound(0) + 1); + this.divisorVector = new Vector4(divisor); + + this.startingOffset = 0; + for (int i = 0; i < this.matrixWidth; i++) + { + if (matrix[0, i] != 0) + { + this.startingOffset = (byte)(i - 1); + break; + } + } + } + + /// + public byte[,] Matrix { get; } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dither(PixelAccessor pixels, TColor source, TColor transformed, int x, int y, int width, int height) + where TColor : struct, IPackedPixel, IEquatable + { + // Assign the transformed pixel to the array. + pixels[x, y] = transformed; + + // Calculate the error + Vector4 error = source.ToVector4() - transformed.ToVector4(); + + // Loop through and distribute the error amongst neighbouring pixels. + for (int row = 0; row < this.matrixHeight; row++) + { + int matrixY = y + row; + + for (int col = 0; col < this.matrixWidth; col++) + { + int matrixX = x + (col - this.startingOffset); + + if (matrixX > 0 && matrixX < width && matrixY > 0 && matrixY < height) + { + byte coefficient = this.Matrix[row, col]; + if (coefficient == 0) + { + continue; + } + + Vector4 coefficientVector = new Vector4(this.Matrix[row, col]); + Vector4 offsetColor = pixels[matrixX, matrixY].ToVector4(); + Vector4 result = ((error * coefficientVector) / this.divisorVector) + offsetColor; + result.W = offsetColor.W; + + TColor packed = default(TColor); + packed.PackFromVector4(result); + pixels[matrixX, matrixY] = packed; + } + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Dithering/FloydSteinberg.cs b/src/ImageSharp/Dithering/FloydSteinberg.cs new file mode 100644 index 000000000..a87421b95 --- /dev/null +++ b/src/ImageSharp/Dithering/FloydSteinberg.cs @@ -0,0 +1,31 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Dithering +{ + /// + /// Applies error diffusion based dithering using the Floyd–Steinberg image dithering algorithm. + /// + /// + public class FloydSteinberg : ErrorDiffusion + { + /// + /// The diffusion matrix + /// + private static readonly byte[,] FloydSteinbergMatrix = + { + { 0, 0, 7 }, + { 3, 5, 1 } + }; + + /// + /// Initializes a new instance of the class. + /// + public FloydSteinberg() + : base(FloydSteinbergMatrix, 16) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Dithering/IErrorDiffusion.cs b/src/ImageSharp/Dithering/IErrorDiffusion.cs new file mode 100644 index 000000000..cd38cd1cd --- /dev/null +++ b/src/ImageSharp/Dithering/IErrorDiffusion.cs @@ -0,0 +1,34 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Dithering +{ + using System; + + /// + /// Encapsulates properties and methods required to perfom diffused error dithering on an image. + /// + public interface IErrorDiffusion + { + /// + /// Gets the dithering matrix + /// + byte[,] Matrix { get; } + + /// + /// Transforms the image applying the dither matrix. This method alters the input pixels array + /// + /// The pixel accessor + /// The source pixel + /// The transformed pixel + /// The column index. + /// The row index. + /// The image width. + /// The image height. + /// The pixel format. + void Dither(PixelAccessor pixels, TColor source, TColor transformed, int x, int y, int width, int height) + where TColor : struct, IPackedPixel, IEquatable; + } +} diff --git a/src/ImageSharp/Dithering/JarvisJudiceNinke.cs b/src/ImageSharp/Dithering/JarvisJudiceNinke.cs new file mode 100644 index 000000000..a495f6001 --- /dev/null +++ b/src/ImageSharp/Dithering/JarvisJudiceNinke.cs @@ -0,0 +1,32 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Dithering +{ + /// + /// Applies error diffusion based dithering using the JarvisJudiceNinke image dithering algorithm. + /// + /// + public class JarvisJudiceNinke : ErrorDiffusion + { + /// + /// The diffusion matrix + /// + private static readonly byte[,] JarvisJudiceNinkeMatrix = + { + { 0, 0, 0, 7, 5 }, + { 3, 5, 7, 5, 3 }, + { 1, 3, 5, 3, 1 } + }; + + /// + /// Initializes a new instance of the class. + /// + public JarvisJudiceNinke() + : base(JarvisJudiceNinkeMatrix, 48) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Sierra2.cs b/src/ImageSharp/Dithering/Sierra2.cs new file mode 100644 index 000000000..a2a6db36d --- /dev/null +++ b/src/ImageSharp/Dithering/Sierra2.cs @@ -0,0 +1,31 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Dithering +{ + /// + /// Applies error diffusion based dithering using the Sierra2 image dithering algorithm. + /// + /// + public class Sierra2 : ErrorDiffusion + { + /// + /// The diffusion matrix + /// + private static readonly byte[,] Sierra2Matrix = + { + { 0, 0, 0, 4, 3 }, + { 1, 2, 3, 2, 1 } + }; + + /// + /// Initializes a new instance of the class. + /// + public Sierra2() + : base(Sierra2Matrix, 16) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Sierra3.cs b/src/ImageSharp/Dithering/Sierra3.cs new file mode 100644 index 000000000..8ab9279f3 --- /dev/null +++ b/src/ImageSharp/Dithering/Sierra3.cs @@ -0,0 +1,32 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Dithering +{ + /// + /// Applies error diffusion based dithering using the Sierra3 image dithering algorithm. + /// + /// + public class Sierra3 : ErrorDiffusion + { + /// + /// The diffusion matrix + /// + private static readonly byte[,] Sierra3Matrix = + { + { 0, 0, 0, 5, 3 }, + { 2, 4, 5, 4, 2 }, + { 0, 2, 3, 2, 0 } + }; + + /// + /// Initializes a new instance of the class. + /// + public Sierra3() + : base(Sierra3Matrix, 32) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Dithering/SierraLite.cs b/src/ImageSharp/Dithering/SierraLite.cs new file mode 100644 index 000000000..217b6ac5f --- /dev/null +++ b/src/ImageSharp/Dithering/SierraLite.cs @@ -0,0 +1,31 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Dithering +{ + /// + /// Applies error diffusion based dithering using the SierraLite image dithering algorithm. + /// + /// + public class SierraLite : ErrorDiffusion + { + /// + /// The diffusion matrix + /// + private static readonly byte[,] SierraLiteMatrix = + { + { 0, 0, 2 }, + { 1, 1, 0 } + }; + + /// + /// Initializes a new instance of the class. + /// + public SierraLite() + : base(SierraLiteMatrix, 4) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Stucki.cs b/src/ImageSharp/Dithering/Stucki.cs new file mode 100644 index 000000000..0b9b40f73 --- /dev/null +++ b/src/ImageSharp/Dithering/Stucki.cs @@ -0,0 +1,32 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Dithering +{ + /// + /// Applies error diffusion based dithering using the Stucki image dithering algorithm. + /// + /// + public class Stucki : ErrorDiffusion + { + /// + /// The diffusion matrix + /// + private static readonly byte[,] StuckiMatrix = + { + { 0, 0, 0, 8, 4 }, + { 2, 4, 8, 4, 2 }, + { 1, 2, 4, 2, 1 } + }; + + /// + /// Initializes a new instance of the class. + /// + public Stucki() + : base(StuckiMatrix, 4) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Quantizers/IQuantizer.cs b/src/ImageSharp/Quantizers/IQuantizer.cs index 878e9775b..a027ca94c 100644 --- a/src/ImageSharp/Quantizers/IQuantizer.cs +++ b/src/ImageSharp/Quantizers/IQuantizer.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Quantizers { using System; + using ImageSharp.Dithering; + /// /// Provides methods for allowing quantization of images pixels. /// @@ -25,6 +27,24 @@ namespace ImageSharp.Quantizers QuantizedImage Quantize(ImageBase image, int maxColors); } + /// + /// Provides methods for allowing dithering of quantized image pixels. + /// + /// The pixel format. + public interface IDitheredQuantizer : IQuantizer + where TColor : struct, IPackedPixel, IEquatable + { + /// + /// Gets or sets a value indicating whether to apply dithering to the output image. + /// + bool Dither { get; set; } + + /// + /// Gets or sets the dithering algorithm to apply to the output image. + /// + IErrorDiffusion DitherType { get; set; } + } + /// /// Provides methods for allowing quantization of images pixels. /// diff --git a/src/ImageSharp/Quantizers/Octree/Quantizer.cs b/src/ImageSharp/Quantizers/Octree/Quantizer.cs index 74aa6aade..65a9d1ede 100644 --- a/src/ImageSharp/Quantizers/Octree/Quantizer.cs +++ b/src/ImageSharp/Quantizers/Octree/Quantizer.cs @@ -6,12 +6,16 @@ namespace ImageSharp.Quantizers { using System; + using System.Numerics; + using System.Runtime.CompilerServices; + + using ImageSharp.Dithering; /// /// Encapsulates methods to calculate the color palette of an image. /// /// The pixel format. - public abstract class Quantizer : IQuantizer + public abstract class Quantizer : IDitheredQuantizer where TColor : struct, IPackedPixel, IEquatable { /// @@ -19,6 +23,11 @@ namespace ImageSharp.Quantizers /// private readonly bool singlePass; + /// + /// The reduced image palette + /// + private TColor[] palette; + /// /// Initializes a new instance of the class. /// @@ -35,6 +44,12 @@ namespace ImageSharp.Quantizers this.singlePass = singlePass; } + /// + public bool Dither { get; set; } = true; + + /// + public IErrorDiffusion DitherType { get; set; } = new SierraLite(); + /// public virtual QuantizedImage Quantize(ImageBase image, int maxColors) { @@ -44,7 +59,6 @@ namespace ImageSharp.Quantizers int height = image.Height; int width = image.Width; byte[] quantizedPixels = new byte[width * height]; - TColor[] palette; using (PixelAccessor pixels = image.Lock()) { @@ -57,12 +71,24 @@ namespace ImageSharp.Quantizers } // Get the palette - palette = this.GetPalette(); + this.palette = this.GetPalette(); - this.SecondPass(pixels, quantizedPixels, width, height); + if (this.Dither) + { + // We clone the image as we don't want to alter the original. + using (Image clone = new Image(image)) + using (PixelAccessor clonedPixels = clone.Lock()) + { + this.SecondPass(clonedPixels, quantizedPixels, width, height); + } + } + else + { + this.SecondPass(pixels, quantizedPixels, width, height); + } } - return new QuantizedImage(width, height, palette, quantizedPixels); + return new QuantizedImage(width, height, this.palette, quantizedPixels); } /// @@ -99,6 +125,14 @@ namespace ImageSharp.Quantizers // And loop through each column for (int x = 0; x < width; x++) { + if (this.Dither) + { + // Apply the dithering matrix + TColor sourcePixel = source[x, y]; + TColor transformedPixel = this.palette[GetClosestColor(sourcePixel, this.palette)]; + this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, width, height); + } + output[(y * source.Width) + x] = this.QuantizePixel(source[x, y]); } } @@ -129,8 +163,41 @@ namespace ImageSharp.Quantizers /// Retrieve the palette for the quantized image /// /// - /// The new color palette + /// /// protected abstract TColor[] GetPalette(); + + /// + /// Returns the closest color from the palette to the given color by calculating the Euclidean distance. + /// + /// The color. + /// The color palette. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static byte GetClosestColor(TColor pixel, TColor[] palette) + { + float leastDistance = int.MaxValue; + Vector4 vector = pixel.ToVector4(); + + byte colorIndex = 0; + for (int index = 0; index < palette.Length; index++) + { + float distance = Vector4.Distance(vector, palette[index].ToVector4()); + + if (distance < leastDistance) + { + colorIndex = (byte)index; + leastDistance = distance; + + // And if it's an exact match, exit the loop + if (Math.Abs(distance) < Constants.Epsilon) + { + break; + } + } + } + + return colorIndex; + } } } \ No newline at end of file diff --git a/src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs b/src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs index 6edb7801b..abf1e5dc5 100644 --- a/src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs +++ b/src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs @@ -97,7 +97,7 @@ namespace ImageSharp.Quantizers leastDistance = distance; // And if it's an exact match, exit the loop - if (Math.Abs(distance) < .0001F) + if (Math.Abs(distance) < Constants.Epsilon) { break; } From c7c05c9b1d52d22aa7d7e9f0adb86f0e1a15b013 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 13 Feb 2017 17:00:48 +1100 Subject: [PATCH 053/142] Rename base interface, fix Stucki filter --- src/ImageSharp/Dithering/Atkinson.cs | 2 +- src/ImageSharp/Dithering/Burks.cs | 2 +- .../Dithering/{ErrorDiffusion.cs => ErrorDiffuser.cs} | 8 ++++---- src/ImageSharp/Dithering/FloydSteinberg.cs | 2 +- .../Dithering/{IErrorDiffusion.cs => IErrorDiffuser.cs} | 4 ++-- src/ImageSharp/Dithering/JarvisJudiceNinke.cs | 2 +- src/ImageSharp/Dithering/Sierra2.cs | 2 +- src/ImageSharp/Dithering/Sierra3.cs | 2 +- src/ImageSharp/Dithering/SierraLite.cs | 2 +- src/ImageSharp/Dithering/Stucki.cs | 4 ++-- src/ImageSharp/Quantizers/IQuantizer.cs | 2 +- src/ImageSharp/Quantizers/Octree/Quantizer.cs | 2 +- 12 files changed, 17 insertions(+), 17 deletions(-) rename src/ImageSharp/Dithering/{ErrorDiffusion.cs => ErrorDiffuser.cs} (94%) rename src/ImageSharp/Dithering/{IErrorDiffusion.cs => IErrorDiffuser.cs} (91%) diff --git a/src/ImageSharp/Dithering/Atkinson.cs b/src/ImageSharp/Dithering/Atkinson.cs index 6d1580171..934df7e4a 100644 --- a/src/ImageSharp/Dithering/Atkinson.cs +++ b/src/ImageSharp/Dithering/Atkinson.cs @@ -9,7 +9,7 @@ namespace ImageSharp.Dithering /// Applies error diffusion based dithering using the Atkinson image dithering algorithm. /// /// - public class Atkinson : ErrorDiffusion + public sealed class Atkinson : ErrorDiffuser { /// /// The diffusion matrix diff --git a/src/ImageSharp/Dithering/Burks.cs b/src/ImageSharp/Dithering/Burks.cs index 3e2d19e57..311316685 100644 --- a/src/ImageSharp/Dithering/Burks.cs +++ b/src/ImageSharp/Dithering/Burks.cs @@ -9,7 +9,7 @@ namespace ImageSharp.Dithering /// Applies error diffusion based dithering using the Burks image dithering algorithm. /// /// - public class Burks : ErrorDiffusion + public sealed class Burks : ErrorDiffuser { /// /// The diffusion matrix diff --git a/src/ImageSharp/Dithering/ErrorDiffusion.cs b/src/ImageSharp/Dithering/ErrorDiffuser.cs similarity index 94% rename from src/ImageSharp/Dithering/ErrorDiffusion.cs rename to src/ImageSharp/Dithering/ErrorDiffuser.cs index 481e8b4a6..1de6cd814 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion.cs +++ b/src/ImageSharp/Dithering/ErrorDiffuser.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -12,7 +12,7 @@ namespace ImageSharp.Dithering /// /// The base class for performing effor diffusion based dithering. /// - public abstract class ErrorDiffusion : IErrorDiffusion + public abstract class ErrorDiffuser : IErrorDiffuser { /// /// The vector to perform division. @@ -35,11 +35,11 @@ namespace ImageSharp.Dithering private readonly int startingOffset; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The dithering matrix. /// The divisor. - protected ErrorDiffusion(byte[,] matrix, byte divisor) + protected ErrorDiffuser(byte[,] matrix, byte divisor) { Guard.NotNull(matrix, nameof(matrix)); Guard.MustBeGreaterThan(divisor, 0, nameof(divisor)); diff --git a/src/ImageSharp/Dithering/FloydSteinberg.cs b/src/ImageSharp/Dithering/FloydSteinberg.cs index a87421b95..a392c9dc9 100644 --- a/src/ImageSharp/Dithering/FloydSteinberg.cs +++ b/src/ImageSharp/Dithering/FloydSteinberg.cs @@ -9,7 +9,7 @@ namespace ImageSharp.Dithering /// Applies error diffusion based dithering using the Floyd–Steinberg image dithering algorithm. /// /// - public class FloydSteinberg : ErrorDiffusion + public sealed class FloydSteinberg : ErrorDiffuser { /// /// The diffusion matrix diff --git a/src/ImageSharp/Dithering/IErrorDiffusion.cs b/src/ImageSharp/Dithering/IErrorDiffuser.cs similarity index 91% rename from src/ImageSharp/Dithering/IErrorDiffusion.cs rename to src/ImageSharp/Dithering/IErrorDiffuser.cs index cd38cd1cd..22cbad1e6 100644 --- a/src/ImageSharp/Dithering/IErrorDiffusion.cs +++ b/src/ImageSharp/Dithering/IErrorDiffuser.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -10,7 +10,7 @@ namespace ImageSharp.Dithering /// /// Encapsulates properties and methods required to perfom diffused error dithering on an image. /// - public interface IErrorDiffusion + public interface IErrorDiffuser { /// /// Gets the dithering matrix diff --git a/src/ImageSharp/Dithering/JarvisJudiceNinke.cs b/src/ImageSharp/Dithering/JarvisJudiceNinke.cs index a495f6001..b5876d777 100644 --- a/src/ImageSharp/Dithering/JarvisJudiceNinke.cs +++ b/src/ImageSharp/Dithering/JarvisJudiceNinke.cs @@ -9,7 +9,7 @@ namespace ImageSharp.Dithering /// Applies error diffusion based dithering using the JarvisJudiceNinke image dithering algorithm. /// /// - public class JarvisJudiceNinke : ErrorDiffusion + public sealed class JarvisJudiceNinke : ErrorDiffuser { /// /// The diffusion matrix diff --git a/src/ImageSharp/Dithering/Sierra2.cs b/src/ImageSharp/Dithering/Sierra2.cs index a2a6db36d..d7cc84254 100644 --- a/src/ImageSharp/Dithering/Sierra2.cs +++ b/src/ImageSharp/Dithering/Sierra2.cs @@ -9,7 +9,7 @@ namespace ImageSharp.Dithering /// Applies error diffusion based dithering using the Sierra2 image dithering algorithm. /// /// - public class Sierra2 : ErrorDiffusion + public sealed class Sierra2 : ErrorDiffuser { /// /// The diffusion matrix diff --git a/src/ImageSharp/Dithering/Sierra3.cs b/src/ImageSharp/Dithering/Sierra3.cs index 8ab9279f3..c3d1fe756 100644 --- a/src/ImageSharp/Dithering/Sierra3.cs +++ b/src/ImageSharp/Dithering/Sierra3.cs @@ -9,7 +9,7 @@ namespace ImageSharp.Dithering /// Applies error diffusion based dithering using the Sierra3 image dithering algorithm. /// /// - public class Sierra3 : ErrorDiffusion + public sealed class Sierra3 : ErrorDiffuser { /// /// The diffusion matrix diff --git a/src/ImageSharp/Dithering/SierraLite.cs b/src/ImageSharp/Dithering/SierraLite.cs index 217b6ac5f..7d855b84e 100644 --- a/src/ImageSharp/Dithering/SierraLite.cs +++ b/src/ImageSharp/Dithering/SierraLite.cs @@ -9,7 +9,7 @@ namespace ImageSharp.Dithering /// Applies error diffusion based dithering using the SierraLite image dithering algorithm. /// /// - public class SierraLite : ErrorDiffusion + public sealed class SierraLite : ErrorDiffuser { /// /// The diffusion matrix diff --git a/src/ImageSharp/Dithering/Stucki.cs b/src/ImageSharp/Dithering/Stucki.cs index 0b9b40f73..3cc01aa79 100644 --- a/src/ImageSharp/Dithering/Stucki.cs +++ b/src/ImageSharp/Dithering/Stucki.cs @@ -9,7 +9,7 @@ namespace ImageSharp.Dithering /// Applies error diffusion based dithering using the Stucki image dithering algorithm. /// /// - public class Stucki : ErrorDiffusion + public sealed class Stucki : ErrorDiffuser { /// /// The diffusion matrix @@ -25,7 +25,7 @@ namespace ImageSharp.Dithering /// Initializes a new instance of the class. /// public Stucki() - : base(StuckiMatrix, 4) + : base(StuckiMatrix, 42) { } } diff --git a/src/ImageSharp/Quantizers/IQuantizer.cs b/src/ImageSharp/Quantizers/IQuantizer.cs index a027ca94c..ef2f0bb98 100644 --- a/src/ImageSharp/Quantizers/IQuantizer.cs +++ b/src/ImageSharp/Quantizers/IQuantizer.cs @@ -42,7 +42,7 @@ namespace ImageSharp.Quantizers /// /// Gets or sets the dithering algorithm to apply to the output image. /// - IErrorDiffusion DitherType { get; set; } + IErrorDiffuser DitherType { get; set; } } /// diff --git a/src/ImageSharp/Quantizers/Octree/Quantizer.cs b/src/ImageSharp/Quantizers/Octree/Quantizer.cs index 65a9d1ede..4d16c5df3 100644 --- a/src/ImageSharp/Quantizers/Octree/Quantizer.cs +++ b/src/ImageSharp/Quantizers/Octree/Quantizer.cs @@ -48,7 +48,7 @@ namespace ImageSharp.Quantizers public bool Dither { get; set; } = true; /// - public IErrorDiffusion DitherType { get; set; } = new SierraLite(); + public IErrorDiffuser DitherType { get; set; } = new SierraLite(); /// public virtual QuantizedImage Quantize(ImageBase image, int maxColors) From 6d9d6616fc9221db4f846e7c19e10237b8ca65d2 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 13 Feb 2017 17:02:20 +1100 Subject: [PATCH 054/142] Add error diffusion binerization --- .../Binarization/Dither.cs | 50 ++++++++ .../Binarization/BinaryThresholdProcessor.cs | 16 ++- .../ErrorDiffusionDitherProcessor.cs | 111 ++++++++++++++++++ .../Formats/GeneralFormatTests.cs | 54 +++++---- .../Processors/Filters/DitherTest.cs | 47 ++++++++ 5 files changed, 244 insertions(+), 34 deletions(-) create mode 100644 src/ImageSharp.Processing/Binarization/Dither.cs create mode 100644 src/ImageSharp.Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs create mode 100644 tests/ImageSharp.Tests/Processors/Filters/DitherTest.cs diff --git a/src/ImageSharp.Processing/Binarization/Dither.cs b/src/ImageSharp.Processing/Binarization/Dither.cs new file mode 100644 index 000000000..f481ac4df --- /dev/null +++ b/src/ImageSharp.Processing/Binarization/Dither.cs @@ -0,0 +1,50 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + + using ImageSharp.Dithering; + using ImageSharp.Processing.Processors; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Alters the alpha component of the image. + /// + /// The pixel format. + /// The image this method extends. + /// The diffusion algorithm to apply. + /// The threshold to apply binarization of the image. Must be between 0 and 1. + /// The . + public static Image Dither(this Image source, IErrorDiffuser diffuser, float threshold) + where TColor : struct, IPackedPixel, IEquatable + { + return Dither(source, diffuser, threshold, source.Bounds); + } + + /// + /// Alters the alpha component of the image. + /// + /// The pixel format. + /// The image this method extends. + /// The diffusion algorithm to apply. + /// The threshold to apply binarization of the image. Must be between 0 and 1. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static Image Dither(this Image source, IErrorDiffuser diffuser, float threshold, Rectangle rectangle) + where TColor : struct, IPackedPixel, IEquatable + { + source.ApplyProcessor(new ErrorDiffusionDitherProcessor(diffuser, threshold), rectangle); + return source; + } + } +} diff --git a/src/ImageSharp.Processing/Processors/Binarization/BinaryThresholdProcessor.cs b/src/ImageSharp.Processing/Processors/Binarization/BinaryThresholdProcessor.cs index 2eb5225f8..cb3758748 100644 --- a/src/ImageSharp.Processing/Processors/Binarization/BinaryThresholdProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Binarization/BinaryThresholdProcessor.cs @@ -20,28 +20,26 @@ namespace ImageSharp.Processing.Processors /// Initializes a new instance of the class. /// /// The threshold to split the image. Must be between 0 and 1. - /// - /// is less than 0 or is greater than 1. - /// public BinaryThresholdProcessor(float threshold) { - // TODO: Check limit. + // TODO: Check thresholding limit. Colors should probably have Max/Min/Middle properties. Guard.MustBeBetweenOrEqualTo(threshold, 0, 1, nameof(threshold)); - this.Value = threshold; + this.Threshold = threshold; + // Default to white/black for upper/lower. TColor upper = default(TColor); - upper.PackFromVector4(Color.White.ToVector4()); + upper.PackFromBytes(255, 255, 255, 255); this.UpperColor = upper; TColor lower = default(TColor); - lower.PackFromVector4(Color.Black.ToVector4()); + lower.PackFromBytes(0, 0, 0, 255); this.LowerColor = lower; } /// /// Gets the threshold value. /// - public float Value { get; } + public float Threshold { get; } /// /// Gets or sets the color to use for pixels that are above the threshold. @@ -62,7 +60,7 @@ namespace ImageSharp.Processing.Processors /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - float threshold = this.Value; + float threshold = this.Threshold; TColor upper = this.UpperColor; TColor lower = this.LowerColor; diff --git a/src/ImageSharp.Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs b/src/ImageSharp.Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs new file mode 100644 index 000000000..c5b78b639 --- /dev/null +++ b/src/ImageSharp.Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs @@ -0,0 +1,111 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Processing.Processors +{ + using System; + + using ImageSharp.Dithering; + + /// + /// An that dithers an image using error diffusion. + /// + /// The pixel format. + public class ErrorDiffusionDitherProcessor : ImageProcessor + where TColor : struct, IPackedPixel, IEquatable + { + /// + /// Initializes a new instance of the class. + /// + /// The error diffuser + /// The threshold to split the image. Must be between 0 and 1. + public ErrorDiffusionDitherProcessor(IErrorDiffuser diffuser, float threshold) + { + Guard.NotNull(diffuser, nameof(diffuser)); + + // TODO: Check thresholding limit. Colors should probably have Max/Min/Middle properties. + Guard.MustBeBetweenOrEqualTo(threshold, 0, 1, nameof(threshold)); + + this.Diffuser = diffuser; + this.Threshold = threshold; + + // Default to white/black for upper/lower. + TColor upper = default(TColor); + upper.PackFromBytes(255, 255, 255, 255); + this.UpperColor = upper; + + TColor lower = default(TColor); + lower.PackFromBytes(0, 0, 0, 255); + this.LowerColor = lower; + } + + /// + /// Gets the error diffuser. + /// + public IErrorDiffuser Diffuser { get; } + + /// + /// Gets the threshold value. + /// + public float Threshold { get; } + + /// + /// Gets or sets the color to use for pixels that are above the threshold. + /// + public TColor UpperColor { get; set; } + + /// + /// Gets or sets the color to use for pixels that fall below the threshold. + /// + public TColor LowerColor { get; set; } + + /// + protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + { + new GrayscaleBt709Processor().Apply(source, sourceRectangle); + } + + /// + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + { + int startY = sourceRectangle.Y; + int endY = sourceRectangle.Bottom; + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + + // Align start/end positions. + int minX = Math.Max(0, startX); + int maxX = Math.Min(source.Width, endX); + int minY = Math.Max(0, startY); + int maxY = Math.Min(source.Height, endY); + + // Reset offset if necessary. + if (minX > 0) + { + startX = 0; + } + + if (minY > 0) + { + startY = 0; + } + + using (PixelAccessor sourcePixels = source.Lock()) + { + for (int y = minY; y < maxY; y++) + { + int offsetY = y - startY; + for (int x = minX; x < maxX; x++) + { + int offsetX = x - startX; + TColor sourceColor = sourcePixels[offsetX, offsetY]; + TColor transformedColor = sourceColor.ToVector4().X >= this.Threshold ? this.UpperColor : this.LowerColor; + this.Diffuser.Dither(sourcePixels, sourceColor, transformedColor, offsetX, offsetY, maxX, maxY); + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index 97bd34def..7e47501f3 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -111,26 +111,27 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { - Image image = file.CreateImage(); - - using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.gif")) + using (Image image = file.CreateImage()) { - image.SaveAsGif(output); - } + using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.bmp")) + { + image.SaveAsBmp(output); + } - using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.bmp")) - { - image.SaveAsBmp(output); - } + using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.jpg")) + { + image.SaveAsJpeg(output); + } - using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.jpg")) - { - image.SaveAsJpeg(output); - } + using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.png")) + { + image.SaveAsPng(output); + } - using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.png")) - { - image.SaveAsPng(output); + using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.gif")) + { + image.SaveAsGif(output); + } } } } @@ -142,22 +143,25 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { - Image image = file.CreateImage(); - byte[] serialized; - using (MemoryStream memoryStream = new MemoryStream()) + using (Image image = file.CreateImage()) { - image.Save(memoryStream); - memoryStream.Flush(); - serialized = memoryStream.ToArray(); + using (MemoryStream memoryStream = new MemoryStream()) + { + image.Save(memoryStream); + memoryStream.Flush(); + serialized = memoryStream.ToArray(); + } } using (MemoryStream memoryStream = new MemoryStream(serialized)) { - Image image2 = new Image(memoryStream); - using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) + using (Image image2 = new Image(memoryStream)) { - image2.Save(output); + using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) + { + image2.Save(output); + } } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/DitherTest.cs b/tests/ImageSharp.Tests/Processors/Filters/DitherTest.cs new file mode 100644 index 000000000..3de2481e5 --- /dev/null +++ b/tests/ImageSharp.Tests/Processors/Filters/DitherTest.cs @@ -0,0 +1,47 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.IO; + + using ImageSharp.Dithering; + + using Xunit; + + public class DitherTest : FileTestBase + { + [Fact] + public void ImageShouldApplyDitherFilter() + { + string path = this.CreateOutputDirectory("Dither"); + + foreach (TestFile file in Files) + { + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) + { + image.Dither(new SierraLite(), .5F).Save(output); + } + } + } + + [Fact] + public void ImageShouldApplyDitherFilterInBox() + { + string path = this.CreateOutputDirectory("Dither"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName("-InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.Dither(new SierraLite(), .5F, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + } + } + } + } +} \ No newline at end of file From 7360c9da49c27881784fd257faee34bbea55a957 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 13 Feb 2017 17:05:54 +1100 Subject: [PATCH 055/142] Add information files --- src/ImageSharp/Dithering/DHALF.TXT | 1331 +++++++++++++++++++++++++++ src/ImageSharp/Dithering/DITHER.TXT | 547 +++++++++++ 2 files changed, 1878 insertions(+) create mode 100644 src/ImageSharp/Dithering/DHALF.TXT create mode 100644 src/ImageSharp/Dithering/DITHER.TXT diff --git a/src/ImageSharp/Dithering/DHALF.TXT b/src/ImageSharp/Dithering/DHALF.TXT new file mode 100644 index 000000000..dce9ef924 --- /dev/null +++ b/src/ImageSharp/Dithering/DHALF.TXT @@ -0,0 +1,1331 @@ +DHALF.TXT +June 20, 1991 + +Original name: DITHER.TXT +Original date: January 2, 1989 + + +===================================== +ORIGINAL FOREWORD BY LEE CROCKER + +What follows is everything you ever wanted to know (for the time being) +about digital halftoning, or dithering. I'm sure it will be out of date as +soon as it is released, but it does serve to collect data from a wide +variety of sources into a single document, and should save you considerable +searching time. + +Numbers in brackets (e.g. [4] or [12]) are references. A list of these +works appears at the end of this document. + +Because this document describes ideas and algorithms which are constantly +changing, I expect that it may have many editions, additions, and +corrections before it gets to you. I will list my name below as original +author, but I do not wish to deter others from adding their own thoughts and +discoveries. This is not copyrighted in any way, and was created solely +for the purpose of organizing my own knowledge on the subject, and sharing +this with others. Please distribute it to anyone who might be interested. + +If you add anything to this document, please feel free to include your name +below as a contributor or as a reference. I would particularly like to see +additions to the "Other books of interest" section. Please keep the text in +this simple format: no margins, no pagination, no lines longer than 79 +characters, and no non-ASCII or non-printing characters other than a CR/LF +pair at the end of each line. It is intended that this be read on as many +different machines as possible. + + +Original Author: + + Lee Daniel Crocker [73407,2030] + + +Contributors: + + Paul Boulay [72117,446] + + Mike Morra [76703,4051] + + +===================================== +COMMENTS BY MIKE MORRA + +I first entered the world of imaging in the fall of 1990 when my employer, +Epson America Inc., began shipping the ES-300C color flatbed scanner. +Suddenly, here I was, a field systems analyst who had worked almost +exclusively with printers and PCs, thrust into a new and arcane world of +look-up tables and dithering and color reduction and .GIF files! I realized +right away that I had a lot of catching up to do (and it needed to be done +quickly), so I began to frequent the CompuServe Information Service's +Graphics Support Forum on a very regular basis. + +Lee Crocker's excellent paper called DITHER.TXT was one of the first pieces +of information that I came across, and it went a very long way toward +answering a lot of questions that I'd had about the subject of dithering. +It also provided me with the names of other essential reference works upon +which Lee had based his paper, and I immediately began an eager search for +these other references. + +In the course of my self-study, however, I found that DITHER.TXT does +presume the reader's familiarity with some fundamental imaging concepts, +which meant that I needed to do a little "cramming." I get the impression +that Lee was directing his paper more toward graphics programmers than to +complete neophytes like me. I decided that I would rewrite and append to +DITHER.TXT and try to incorporate some of the more elementary information +that I'd absorbed along the way. In doing so, I hope that it will make it +even more comprehensive, and thus even more useful to first-time users. + +I elected to rename the revised file and chose the name DHALF.TXT in homage +to the term "digital halftoning," as used in Robert Ulichney's splendid +reference work. Notwithstanding, this paper is still very much Lee's +original work, and I certainly do not propose that I have created something +new and original here. It is also quite possible that in changing the +presentation of some of the material therein, I may have unwittingly +corrupted Lee's original intent and delivery, and this was also not my +intention. + +Accordingly, I've submitted this paper to the Graphics Support Forum as a +draft work only, at least for the time being. Quite honestly, I don't know +whether it would be appropriate as a replacement to DITHER.TXT, or as a +second, distinct document. Too, I may very well have misconstrued or +misinterpreted some factual information in my revision. As such, I welcome +criticism and comment from all the original authors and contributors, and +any readers, with the hope that their feedback will help me to address these +issues. + +If this revision it is received favorably, I will submit it to the public +domain; if it is met with brickbats (for whatever reason), I will withdraw +it. Whatever the outcome, though, it will at least represent a very +rewarding learning experience on my part! + +With the unselfish help of many of the denizens of the Graphics Support +Forum, I was ultimately able to thrash out (in my own mind) the answers to +my questions that I needed. I'd like to publicly thank the whole Forum +community in general for putting up with my unending barrage of questions +and inquiries over the past few months . In particular, I would thank +John Swenson, Chris Young, and (of course) Lee Crocker for their invaluable +assistance. + +Mike Morra [76703,4051] +June 20, 1991 + + +===================================== +What is Digital Halftoning? + +Throughout much of the course of computer imaging technology, experimenters +and users have been challenged with attempting to acceptably render +digitized images on display devices which were incapable of reproducing the +full spectrum of intensities or colors present in the source image. The +challenge is even more pronounced in today's world of personal computing +because of the technology gap between image generation and image rendering +equipment. + +Today, we now have affordable 24-bit image scanners which can generate +nearly true-to-life scans having as many as 256 shades of gray, or in excess +of 16.7 million colors. Mainstream display technology, however, still lags +behind with 16- and 256-color VGA/SVGA video monitors and printers with +binary (black/white) "marking engines" as the norm. Without specialized +techniques for color reduction -- the process of finding the "best fit" of +the display device's available gray shades and/or colors -- the imaging +experimenter would be plagued with blotchy, noisy, off-color images. + +(As of this writing, "true color" 24-bit video display devices, capable of +reproducing all of the color/intensity information in the source image, are +now beginning to migrate downward into the PC environment, but they exact a +premium in cost and processor power which many users are loathe to pay. So- +called "high-color" video displays -- typically 16-bit, with 32,768-color +capability -- are moving into the mainstream, but color reduction techniques +would still be required with these devices.) + +The science of digital halftoning (more commonly referred to as dithering, +or spatial dithering) is one of the techniques used to achieve satisfactory +image rendering and color reduction. Initially, it was principally +associated with the rendering of continuous-tone (grayscale) images on +"binary" (i.e. 1-bit) video displays which could only display full black or +full white pixels, or on printers which could produce only full black spots +on a printed page. Indeed, Ulichney [3] gives a definition of digital +halftoning as "... any algorithmic process which creates the illusion of +continuous-tone images from the judicious arrangement of binary picture +elements." + +Ulichney's study, as well as the earlier literature on the subject (and this +paper itself), discusses the process mostly in this context. Since we in +the PC world are still saddled primarily with black/white marking engines in +our hardcopy devices, this binary interpretation of digital halftoning is +still very pertinent. However, as we will see later in this discussion, the +concept can also be extended to include display devices (typically video +monitors) which support limited grayscale or color palettes. Accordingly, +we can broaden the traditional definition of digital halftoning to refer to +rendering an image on any display device which is unable to show the entire +range of colors or gray shades that are contained in the source image. + + +===================================== +Intensity/Color Resolution + +The concept of resolution is essential to the understanding of digital +halftoning. Resolution can be defined as "fineness" and is used to +describe the level of detail in a digitally sampled signal. + +Typically, when we hear the term "resolution" applied to images, we think of +what's known as "spatial resolution," which is the basic sampling rate for +the image. It describes the fineness of the "dots" (pixels or ink/toner +spots) which comprise the image, i.e. how many of them are present along +each horizontal and vertical inch. However, we can also speak of "intensity +resolution" or "color resolution," which describes the fineness of detail +available at each spot, i.e. the number of different gray shades or colors +in the image. (I will go back and forth between the two terms depending on +the type of image being discussed, but the reader should be aware that the +concepts are analogous to each other.) + +As you might expect, the higher the resolution of a digital sample, the +better it can reproduce high frequency detail in the particular domain +described by that resolution. A VGA display, for example, has a relatively +good spatial resolution of 640 x 480 and a relatively poor color resolution +of 8 bits (256 colors). By comparison, an NTSC color television receiver +has a spatial resolution of approximately 350 x 525 and an excellent, nearly +infinite color resolution. Thus, images rendered on a VGA screen will be +quite sharp, but rather blotchy in color. The same image displayed on the +television receiver will not be as crisp, but will have much more accurate +color rendition. + +It is often possible to "trade" one kind of resolution for another. If your +display device has a higher spatial resolution than the image you are trying +to reproduce, it can show a very good image even if its color resolution is +less. This is what most of us know as "dithering" and is the subject of +this paper. (The other tradeoff, i.e., trading color resolution for spatial +resolution, is called "anti-aliasing," and is not discussed here.) + + +For the following discussions I will assume that we are given a grayscale +image with 256 shades of gray, which are assigned intensity values from 0 +(black) through 255 (white), and that we are trying to reproduce it on a +black and white output device, e.g. something like an Epson impact dotmatrix +printer, or an HP LaserJet laser printer. Most of these methods can be +extended in obvious ways to deal with displays that have more than two +levels (but still fewer than the source image), or to color images. Where +such extension is not obvious, or where better results can be obtained, I +will go into more detail. + + +===================================== +Fixed Thresholding + +A good place to start is with the example of performing a simple (or fixed) +thresholding operation on our grayscale image in order to display it on our +black and white device. This is accomplished by establishing a demarcation +point, or threshold, at the 50% gray level. Each dot of the source image is +compared against this threshold value: if it is darker than the value, the +device plots it black, and if it's lighter, the device plots it white. + +What happens to the image during this operation? Well, some detail +survives, but our perception of gray levels is completely gone. This means +that a lot of the image content is obliterated. Take an area of the image +which is made up of various gray shades in the range of 60-90%. After fixed +thresholding, all of those shades (being darker than the 50% gray threshold) +will be mapped to solid black. So much for variations of intensity. + +Another portion of the image might show an object with an increasing, +diffused shadow across one of its surfaces, with gray shades in the range of +20-70%. This gradual variation in intensity will be lost in fixed +thresholding, giving way to two separate areas (one white, one black) and a +distinct, visible boundary between them. The situation where a transition +from one intensity or shade to another is very conspicuous is known as +contouring. + + +===================================== +Artifacts + +Phenomena like contouring, which are not present in the source image but +produced by the digital signal processing, are called artifacts. The most +common type of artifact is the Moire' pattern. If you display or print an +image of several lines, closely spaced and radiating from a single point, +you will see what appear to be flower-like patterns. These are not part of +the original image but are an illusion produced by the jaggedness of the +display. We will encounter and discuss other forms of artifacts later in +this paper. + + +===================================== +Error Noise + +Returning to our fixed-thresholded (and badly-rendered) image, how could we +document what has taken place to make this image so inaccurate? Expressing +it in technical terms, a relatively large amount of error "noise" is present +in the fixed-thresholded image. The error value is the difference between +the image's original intensity at a given dot and the intensity of the +displayed dot. Obviously, very dark values like 1 or 2 (which are almost +full black) incur very small errors when they are rendered as a 0 value +(black) dot. On the other hand, a gross error is incurred when a 129 value +dot (a medium gray) is displayed at 255 value (white), for instance. + +Simply put, digital halftoning redistributes this "noise energy" in a way +which makes it less visible. This brings up an important concept: digital +halftoning does not INCREASE the noise energy. In some of the literature, +reference is made to the "addition of dither noise," which might give this +impression. This is not the case, however: effective digital halftoning +acts upon the low-frequency component of the error noise (the component +which contributes to graininess) and scatters it in higher-frequency +components where it is not as obvious. + + +===================================== +Classes of digital halftoning algorithms + +The algorithms we will discuss in this paper can be subdivided into four +categories: + + 1. Random dither + 2. Patterning + 3. Ordered dither + 4. Error-diffusion halftoning + +Each of these methods is generally better than those listed before it, but +other considerations such as processing time, memory constraints, etc. may +weigh in favor of one of the simpler methods. + +To convert any of the first three methods into color, simply apply the +algorithm separately for each primary color and mix the resulting values. +This assumes that you have at least eight output colors: black, red, green, +blue, cyan, magenta, yellow, and white. Though this will work for error +diffusion as well, there are better methods which will be discussed in more +detail later. + + +===================================== +Random dither + +Random dithering could be termed the "bubblesort" of digital halftoning +algorithms. It was the first attempt (documented as far back as 1951) to +correct the contouring produced by fixed thresholding, and it has +traditionally been referenced for comparison in most studies of digital +halftoning. In fact, the name "ordered dither" (which will be discussed +later) was chosen to contrast random dither. + +While it is not really acceptable as a production method, it is very simple +to describe and implement. For each dot in our grayscale image, we generate +a random number in the range 0 - 255: if the random number is greater than +the image value at that dot, the display device plots the dot white; +otherwise, it plots it black. That's it. + +This generates a picture with a lot of "white noise", which looks like TV +picture "snow". Although inaccurate and grainy, the image is free from +artifacts. Interestingly enough, this digital halftoning method is useful +in reproducing very low-frequency images, where the absence of artifacts is +more important than noise. For example, a whole screen containing a +gradient of all levels from black to white would actually look best with a +random dither. With this image, other digital halftoning algorithms would +produce significant artifacts like diagonal patterns (in ordered dithering) +and clustering (in error diffusion halftones). + +I should mention, of course, that unless your computer has a hardware-based +random number generator (and most don't), there may be some artifacts from +the random number generation algorithm itself. For efficiency, you can take +the random number generator "out of the loop" by generating a list of random +numbers beforehand for use in the dither. Make sure that the list is larger +than the number of dots in the image or you may get artifacts from the reuse +of numbers. The worst case would be if the size of your list of random +numbers is a multiple or near-multiple of the horizontal size of the image; +in this case, unwanted vertical or diagonal lines will appear. + +As unattractive as it is, random dithering can actually be related to a +pleasing, centuries-old art know as mezzotinting (the name itself is an +Italianized derivative of the English "halftone"). In a mezzotint, the +skilled craftsman worked a soft metal (usually copper) printing plate, and +roughened or ground the dark regions of the image by hand and in a seemingly +random fashion. Analyzing it in scientific terms (which would surely insult +any mezzotinting artisan who might read this!) the pattern created is not +very regular or periodic at all, but the absence of low frequency noise +leads to a very attractive image without much graininess. A similar process +is still in use today, in the form of modern gravure printing. + + +===================================== +"Classical" halftoning + +Let's take a short departure from the digital domain and look at the +traditional or "classical" printing technique of halftoning. This technique +is over a century old, dating back to the weaving of silk pictures in the +mid 1800's. Modern halftone printing was invented in the late 1800's, and +halftones of that period are even today considered to be attractive +renditions of their subjects. + +Essentially, halftoning involves the printing of dots of different sizes in +an ordered and closely spaced pattern in order to simulate various +intensities. The early halftoning artisans realized that when we view a +very small area at normal viewing distances, our eyes perform a blending or +smoothing function on the fine detail within that area. As a result, we +perceive only the overall intensity of the area. This is known as spatial +integration. + +Although the tools of halftoning (the "screens" and screening process used +to generate the varying dots of the printed image) have undergone +improvements throughout the years, the fundamental principles remain +unchanged. This includes the 45-degree "screen angle" of the lines of dots, +which was known even to the earliest halftone artisans as giving more +pleasing images than dot lines running horizontally and vertically. + + +===================================== +Patterning + +This was the first digital technique to pay homage to the classical +halftone. It takes advantage of the fact that the spatial resolution of +display devices had improved to the point where one could trade some of it +for better intensity resolution. Like random dither, it is also a simple +concept, but is much more effective. + +For each possible value in the image, we create and display a pattern of +pixels (which can be either video pixels or printer "spots") that +approximates that value. Remembering the concept of spatial integration, if +we choose the appropriate patterns we can simulate the appearance of various +intensity levels -- even though our display can only generate a limited set +of intensities. + +For example, consider a 3 x 3 pattern. It can have one of 512 different +arrangements of pixels: however, in terms of intensity, not all of them are +unique. Since the number of black pixels in the pattern determines the +darkness of the pattern, we really have only 10 discrete intensity patterns +(including the all-white pattern), each one having one more black pixel than +the previous one. + +But which 10 patterns? Well, we can eliminate, right off the bat, patterns +like: + + --- X-- --X X-- + XXX or -X- or -X- or X-- + --- --X X-- X-- + + +because if they were repeated over a large area (a common occurrence in many +images [1]) they would create vertical, horizontal, or diagonal lines. +Also, studies [1] have shown that the patterns should form a "growth +sequence:" once a pixel is intensified for a particular value, it should +remain intensified for all subsequent values. In this fashion, each pattern +is a superset of the previous one; this similarity between adjacent +intensity patterns minimizes any contouring artifacts. + +Here is a good pattern for a 3-by-3 matrix which subscribes to the rules set +forth above: + + + --- --- --- -X- -XX -XX -XX -XX XXX XXX + --- -X- -XX -XX -XX -XX XXX XXX XXX XXX + --- --- --- --- --- -X- -X- XX- XX- XXX + + +This pattern matrix effectively simulates a screened halftone with dots of +various sizes. In large areas of constant value, the repetitive pattern +formed will be mostly artifact-free. + +No doubt, the reader will realize that applying this patterning process to +our image will triple its size in each direction. Because of this, +patterning can only be used where the display's spatial resolution is much +greater than that of the image. + +Another limitation of patterning is that the effective spatial resolution is +decreased, since a multiple-pixel "cell" is used to simulate the single, +larger halftone dot. The more intensity resolution we want, the larger the +halftone cell used and, by extension, the lower the spatial resolution. + +In the above example, using 3 x 3 patterning, we are able to simulate 10 +intensity levels (not a very good rendering) but we must reduce the spatial +resolution to 1/3 of the original figure. To get 64 intensity levels (a +very acceptable rendering), we would have to go to an 8 x 8 pattern and an +eight-fold decrease in spatial resolution. And to get the full 256 levels +of intensity in our source image, we would need a 16 x 16 pattern and would +incur a 16-fold reduction in spatial resolution. Because of this size +distortion of the image, and with the development of more effective digital +halftoning methods, patterning is only infrequently used today. + +To extend this method to color images, we would use patterns of colored +pixels to represent shades not directly printable by the hardware. For +example, if your hardware is capable of printing only red, green, blue, and +black (the minimal case for color dithering), other colors can be +represented with 2 x 2 patterns of these four: + + + Yellow = R G Cyan = G B Magenta = R B Gray = R G + G R B G B R B K + + +(B here represents blue, K is black). In this particular example, there are +a total of 31 such distinct patterns which can be used; their enumeration is +left "as an exercise for the reader" (don't you hate books that do that?). + + +===================================== +Clustered vs. dispersed patterns + +The pattern diagrammed above is called a "clustered" pattern, so called +because as new pixels are intensified in each pattern, they are placed +adjacent to the already-intensified pixels. Clustered-dot patterns were +used on many of the early display devices which could not render individual +pixels very distinctly, e.g. printing presses or other printers which smear +the printed spots slightly (a condition known as dot gain), or video +monitors which introduce some blurriness to the pixels. Clustered-dot +groupings tend to hide the effect of dot gain, but also produce a somewhat +grainy image. + +As video and hardcopy display technology improved, newer devices (such as +electrophotographic laser printers and high-res video displays) were better +able to accurately place and size their pixels. Further research showed +that, especially with larger patterns, the dispersed (non-clustered) layout +was more pleasing. Here is one such pattern: + + + --- X-- X-- X-- X-X X-X X-X XXX XXX XXX + --- --- --- --X --X X-X X-X X-X XXX XXX + --- --- -X- -X- -X- -X- XX- XX- XX- XXX + + + +Since clustering is not used, dispersed-dot patterns produce less grainy +images. + + +===================================== +Ordered dither + +While patterning was an important step toward the digital reproduction of +the classic halftone, its main shortcoming was the spatial enlargement (and +corresponding reduction in resolution) of the image. Ordered dither +represents a major improvement in digital halftoning where this spatial +distortion was eliminated and the image could then be rendered in its +original size. + +Obviously, in order to accomplish this, each dot in the source image must be +mapped to a pixel on the display device on a one-to-one basis. Accordingly, +the patterning concept was redefined so that instead of plotting the whole +pattern for each image dot, THE IMAGE DOT IS MAPPED ONLY TO ONE PIXEL IN THE +PATTERN. Returning to our example of a 3 x 3 pattern, this means that we +would be mapping NINE image dots into this pattern. + +The simplest way to do this in programming is to map the X and Y coordinates +of each image dot into the pixel (X mod 3, Y mod 3) in the pattern. + +Returning to our two patterns (clustered and dispersed) as defined earlier, +we can derive an effective mathematical algorithm that can be used to plot +the correct pixel patterns. Because each of the patterns above is a +superset of the previous, we can express the patterns in a compact array +form as the order of pixels added: + + + 8 3 4 1 7 4 + 6 1 2 and 5 8 3 + 7 5 9 6 2 9 + + +Then we can simply use the value in the array as a threshold. If the value +of the original image dot (scaled into the 0-9 range) is less than the +number in the corresponding cell of the matrix, we plot that pixel black; +otherwise, we plot it white. Note that in large areas of constant value, we +will get repetitions of the pattern just as we did with patterning. + +As before, clustered patterns should be used for those display devices which +blur the pixels. In fact, the clustered-dot ordered dither is the process +used by most newspapers, and in the computer imaging world the term +"halftoning" has come to refer to this method if not otherwise qualified. + + +As noted earlier, the dispersed-dot method (where the display hardware +allows) is preferred in order to decrease the graininess of the displayed +images. Bayer [2] has shown that for matrices of orders which are powers of +two there is an optimal pattern of dispersed dots which results in the +pattern noise being as high-frequency as possible. The pattern for a 2x2 +and 4x4 matrices are as follows: + + +1 3 1 9 3 11 These patterns (and their rotations +4 2 13 5 15 7 and reflections) are optimal for a + 4 12 2 10 dispersed-dot ordered dither. + 16 8 14 6 + + +Ulichney [3] shows a recursive technique can be used to generate the larger +patterns. (To fully reproduce our 256-level image, we would need to use an +8x8 pattern.) + +The Bayer ordered dither is in very common use and is easily identified by +the cross-hatch pattern artifacts it produces in the resulting display. +This artifacting is the major drawback of an otherwise powerful and very +fast technique. + + +===================================== +Dithering with "blue noise" + +Up to this point in our discussion, we have (with the exception of dithering +with white noise) discussed digital halftoning schemes which rely on the +application of some fairly regular mathematical processes in order to +redistribute the error noise of the image. Unfortunately, the regularity of +these algorithms leads to different kinds of artifacting which detracts from +the rendered image. In addition, these images all tend to reflect the +display device's row-and-column dot pattern to some extent, and this further +contributes to the "mechanical" character of the output image. + +Dithering with white noise, on the other hand, introduces enough randomness +to suppress the artifacting and the gridlike appearance, but the low- +frequency component of this noise introduces graininess. + +Obviously, what is needed is a method which falls somewhere in the middle of +these two extremes. In theoretical terms, if we could take white noise and +remove its low-frequency content, this would be an ideal way to disperse the +error content of our image. Many of the digital halftoning developers, +making an analogy to the audio world, refer to this concept as dithering +with blue noise. (In audio theory, "pink noise," which is often used as a +diagnostic and testing tool, is white noise from which some level of high- +frequency content has been filtered.) + +Alas, while an audio-frequency analog low-pass filter is a relatively simple +device to construct and operate, implementing a digital high-pass filter in +program code -- and one which operates efficiently enough so as not to +degrade display response time -- is no trivial task. + + +===================================== +Error-diffusion halftoning + +After considerable research, it was found that a set of techniques known as +error diffusion (also termed error dispersion or error distribution) +accomplished this quite effectively. In fact, error diffusion generates the +best results of any of the digital halftoning methods described here. Much +of the low-frequency noise component is suppressed, producing images with +very little grain. Error-diffusion halftones also display a very pleasing +randomness, without the visual sensation of rows and columns of dots; this +effect is known as the "grid defiance illusion." + +As in other areas of life, though, there ain't no such thing as a free +lunch. Error diffusion is, by nature, the slowest method of digital +halftoning. In fact, there are several variants of this technique, and the +better they get, the slower they are. However, one will realize a very +significant improvement in the quality of the processed images which easily +justifies the time and computational power required. + +Error diffusion is very simple to describe. For each point in our image, we +first find the closest intensity (or color) available. We then calculate +the difference between the image value at that point and that nearest +available intensity/color: this difference is our error value. Now we +divide up the error value and distribute it to some of the neighboring image +areas which we have not visited (or processed) yet. When we get to these +later dots, we add in the portions of error values which were distributed +there from the preceding dots, and clip the cumulative value to an allowed +range if needed. This new, modified value now becomes the image value that +we use for processing this point. + +If we are dithering our sample grayscale image for output to a black-and- +white device, the "find closest intensity/color" operation is just a simple +thresholding (the closest intensity is going to be either black or white). +In color imaging -- for instance, color-reducing a 24-bit true color Targa +file to an 8-bit, mapped GIF file -- this involves matching the input color +to the closest available hardware color. Depending on how the display +hardware manages its intensity/color palette, this matching process can be a +difficult task. (This is covered in more detail in the "Color issues" +section later in this paper.) + +Up till now, all other methods of digital halftoning were point operations, +where any adjustments that were made to a given dot had no effect on any of +the surrounding dots. With error diffusion, we are doing a "neighborhood +operation." Dispersing the error value over a larger area is the key to the +success of these methods. + +The different ways of dividing up the error can be expressed as patterns +called filters. In the following sections, I will list a number of the most +commonly-used filters and some info on each. + + +===================================== +The Floyd-Steinberg filter + +This is where it all began, with Floyd and Steinberg's [4] pioneering +research in 1975. The filter can be diagrammed thus: + + + * 7 + 3 5 1 (1/16) + + +In this (and all subsequent) filter diagrams, the "*" represents the pixel +currently being scanning, and the neighboring numbers (called weights) +represent the portion of the error distributed to the pixel in that +position. The expression in parentheses is the divisor used to break up the +error weights. In the Floyd-Steinberg filter, each pixel "communicates" +with 4 "neighbors." The pixel immediately to the right gets 7/16 of the +error value, the pixel directly below gets 5/16 of the error, and the +diagonally adjacent pixels get 3/16 and 1/16. + +The weighting shown is for the traditional left-to-right scanning of the +image. If the line were scanned right-to-left (more about this later), this +pattern would be reversed. In either case, the weights calculated for the +subsequent line must be held by the program, usually in an array of some +sort, until that line is visited later. + +Floyd and Steinberg carefully chose this filter so that it would produce a +checkerboard pattern in areas with intensity of 1/2 (or 128, in our sample +image). It is also fairly easy to execute in programming code, since the +division by 16 is accomplished by simple, fast bit-shifting instructions +(this is the case whenever the divisor is a power of 2). + + +===================================== +The "false" Floyd-Steinberg filter + +Occasionally, you will see the following filter erroneously called the +Floyd-Steinberg filter: + + + * 3 + 3 2 (1/8) + + +The output from this filter is nowhere near as good as that from the real +Floyd-Steinberg filter. There aren't enough weights to the dispersion, +which means that the error value isn't distributed finely enough. With the +entire image scanned left-to-right, the artifacting produced would be +totally unacceptable. + +Much better results would be obtained by using an alternating, or +serpentine, raster scan: processing the first line left-to-right, the next +line right-to-left, and so on (reversing the filter pattern appropriately). +Serpentine scanning -- which can be used with any of the error-diffusion +filters detailed here -- introduces an additional perturbation which +contributes more randomness to the resultant halftone. Even with serpentine +scanning, however, this filter would need additional perturbations (see +below) to give acceptable results. + + +===================================== +The Jarvis, Judice, and Ninke filter + +If the false Floyd-Steinberg filter fails because the error isn't +distributed well enough, then it follows that a filter with a wider +distribution would be better. This is exactly what Jarvis, Judice, and +Ninke [6] did in 1976 with their filter: + + + * 7 5 + 3 5 7 5 3 + 1 3 5 3 1 (1/48) + + +While producing nicer output than Floyd-Steinberg, this filter is much +slower to implement. With the divisor of 48, we can no longer use bit- +shifting to calculate the weights but must invoke actual DIV (divide) +processor instructions. This is further exacerbated by the fact that the +filter must communicate with 12 neighbors; three times as many in the Floyd- +Steinberg filter. Furthermore, with the errors distributed over three +lines, this means that the program must keep two forward error arrays, which +requires extra memory and time for processing. + + +===================================== +The Stucki filter + +P. Stucki [7] offered a rework of the Jarvis, Judice, and Ninke filter in +1981: + + + * 8 4 + 2 4 8 4 2 + 1 2 4 2 1 (1/42) + + +Once again, division by 42 is quite slow to calculate (requiring DIVs). +However, after the initial 8/42 is calculated, some time can be saved by +producing the remaining fractions by shifts. The Stucki filter has been +observed to give very clean, sharp output, which helps to offset the slow +processing time. + + +===================================== +The Burkes filter + +Daniel Burkes [5] of TerraVision undertook to improve upon the Stucki filter +in 1988: + + + * 8 4 The Burkes filter + 2 4 8 4 2 (1/32) + + +Notice that this is just a simplification of the Stucki filter with the +bottom row removed. The main improvement is that the divisor is now 32, +which allows the error values to be calculated using shifts once more, and +the number of neighbors communicated with has been reduced to seven. +Furthermore, the removal of one row reduces the memory requirements of the +filter by eliminating the second forward array which would otherwise be +needed. + + +===================================== +The Sierra filters + +In 1989, Frankie Sierra came out with his three-line filter: + + + * 5 3 The Sierra3 filter + 2 4 5 4 2 + 2 3 2 (1/32) + + +A year later, Sierra followed up with a two-line modification: + + + * 4 3 The Sierra2 filter + 1 2 3 2 1 (1/16) + + +and a very simple "Filter Lite," as he calls it: + + + * 2 The Sierra-2-4A filter + 1 1 (1/4) + + +Even this very simple filter, according to Sierra, produces better results +than the original Floyd-Steinberg filter. + + +===================================== +Miscellaneous filters + +Many image processing software packages offer one or more of the filters +listed above as dithering options. In nearly every case, the Floyd- +Steinberg filter (or a variant thereof) is included. The Bayer ordered +dither is sometimes offered, although the Floyd-Steinberg filter will do a +better job in essentially the same processing time. Higher-quality filters +like Burkes or Stucki are usually also present. + +All of the filters described above are used on display devices which have +"square pixels." This is to say that the display lays out the pixels in +rows and columns, aligned horizontally and vertically and spaced equally in +both directions. This applies to the commonly-used video modes in VGA and +SVGA: 640 x 480, 800 x 600, and 1024 x 768, with a 4:3 "aspect ratio." It +would also include HP-compatible and PostScript desktop laser printers using +300dpi marking engines. + +Some displays may use "rectangular pixels," where the horizontal and +vertical spacings are unequal. This would include various EGA and CGA video +modes and other specialized video displays, and most dot-matrix printers. +In many cases, the filters described earlier will do a decent job on +rectangular pixel grids, but an optimized filter would be preferred. +Slinkman [10] describes one such filter for his 640 x 240 monochrome display +with a 1:2 aspect ratio. + +In other cases, video displays might use a "hexagonal grid" of pixels, where +rows of pixels are offset or staggered, in much the same fashion used on +broadcast television receivers. This is illustrated below: + + + . . . . . . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . . . . . . + square/rectangular hexagonal + + +Hexagonal grids are given a very thorough treatment by Ulichney, should you +be interested in further information. + +While technically not an error-diffusion filter, a method proposed by Gozum +[11] offers color resolutions in excess of 256 colors by plotting red, +green, and blue pixel "triplets" or triads to simulate an "interlaced" +television display (sacrificing some horizontal resolution in the process). +Again, I would refer interested readers to his document for more +information. + + +===================================== +Special considerations + +The speed disadvantages of the more complex filters can be eliminated +somewhat by performing the divisions beforehand and using lookup tables +instead of doing the math inside the loop. This makes it harder to use +various filters in the same program, but the speed benefits are enormous. + +It is critical with all of these algorithms that when error values are added +to neighboring pixels, the resultant summed values must be truncated to fit +within the limits of hardware. Otherwise, an area of very intense color may +cause streaks into an adjacent area of less intense color. + +This truncation is known as "clipping," and is analogous to the audio +world's concept of the same name. As in the case of an audio amplifier, +clipping adds undesired noise to the data. Unlike the audio world, however, +the visual clipping performed in error-diffusion halftoning is acceptable +since it is not nearly so offensive as the color streaking that would occur +otherwise. It is mainly for this reason that the larger filters work better +-- they split the errors up more finely and produce less clipping noise. + +With all of these filters, it is also important to ensure that the sum of +the distributed error values is equal to the original error value. This is +most easily accomplished by subtracting each fraction, as it is calculated, +from the whole error value, and using the final remainder as the last +fraction. + + +===================================== +Further perturbations + +As alluded to earlier, there are various techniques for the reduction of +digital artifacts, most of which involve using a little randomness to +lightly "perturb" a regular algorithm (particularly the simpler ones). It +could be said that random dither takes this concept to the extreme. + +Serpentine scanning is one of these techniques, as noted earlier. Other +techniques include the addition of small amounts of white noise, or +randomizing the positions of the error weights (essentially, using a +constantly-varying pattern). As you might imagine, any of these methods +incur a penalty in processing time. + +Indeed, some of the above filters (particularly the simpler ones) can be +greatly improved by skewing the weights with a little randomness [3]. + + +===================================== +Nearest available color + +Calculating the nearest available intensity is trivial with a monochrome +image; calculating the nearest available color in a color image requires +more work. + +A table of RGB values of all available colors must be scanned sequentially +for each input pixel to find the closest. The "distance" formula most often +used is a simple pythagorean "least squares". The difference for each color +is squared, and the three squares added to produce the distance value. This +value is equivalent to the square of the distance between the points in RGB- +space. It is not necessary to compute the square root of this value because +we are not interested in the actual distance, only in which is smallest. +The square root function is a monotonic increasing function and does not +affect the order of its operands. If the total number of colors with which +you are dealing is small, this part of the algorithm can be replaced by a +lookup table as well. + +When your hardware allows you to select the available colors, very good +results can be achieved by selecting colors from the image itself. You must +reserve at least 8 colors for the primaries, secondaries, black, and white +for best results. If you do not know the colors in your image ahead of +time, or if you are going to use the same map to dither several different +images, you will have to fill your color map with a good range of colors. +This can be done either by assigning a certain number of bits to each +primary and computing all combinations, or by a smoother distribution as +suggested by Heckbert [8]. + +An alternate method of color selection, based on a tetrahedral color space, +has been proposed by Crawford [12]. His algorithm has been optimized for +either dispersed-dot ordered dither or Floyd-Steinberg error diffusion with +serpentine scan. + + +===================================== +Hardware halftoning + +In some cases, image scanning hardware may be able to digitally halftone and +dither the image "on the fly" as it is being scanned. The data produced by +the "raw" scan is then already in a 1- or 2-bit/pixel format. While this +feature would probably be unsuitable for cases where the image would need +further processing (see the "Loss of image information" section below), it +is very useful where the operator wants to generate a final image, ready for +printing or displaying, with little or no subsequent processing. + +As an example, the Epson ES-300C color scanner (and its European equivalent, +the Epson GT-6000) offers three internal halftone modes. One is a standard +"halftone" algorithm, i.e. a clustered-dot ordered dither. The other two +are error-diffusion filters (one "sharp," the other "soft") which are +proprietary Epson-developed filters. + + +===================================== +Loss of image information incurred by digital halftoning + +It is important to emphasize here that digital halftoning is a ONE-WAY +operation. Once an image has been halftoned or dithered, although it may +look like a good reproduction of the original, INFORMATION IS PERMANENTLY +LOST. Many image processing functions fail on dithered images; in fact, you +would not want to dither an image which had already been dithered to some +extent. + +For these reasons, digital halftoning must be considered primarily as a way +TO PRODUCE AN IMAGE ON HARDWARE THAT WOULD OTHERWISE BE INCAPABLE OF +DISPLAYING IT. This would hold true wherever a grayscale or color image +needs to be rendered on a bilevel display device. In this situation, one +would almost never want to store the dithered image. + +On the other hand, when color images are dithered for display on color +displays with a lower color resolution, the dithered images are more useful. +In fact, the bulk of today's scanned-image GIF files which abound on +electronic BBSs and information services are 8-bit (256 color), colormapped +and dithered files created from 24-bit true-color scans. Only rarely are +the 24-bit files exchanged, because of the huge amount of data contained in +them. + +In some cases, these mapped GIF files may be further processed with special +paint/processing utilities, with very respectable results. However, the +previous warning still applies: one can never obtain the same image fidelity +when operating on the mapped GIF file as they could if they were operating +on the true-color image file. + +Generally speaking, digital halftoning and dithering should be the last +stage in producing a physical display from a digitally stored image. The +data representing an image should always be kept in full detail in case you +should want to reprocess it in any way. As affordable display technology +improves, the day may soon come where you might possess the hardware to +allow you to use all of the original image information without the need for +digital halftoning or color reduction. + + +===================================== +Sample code + +Despite my best efforts in expository writing, nothing explains an algorithm +better than real code. With that in mind, presented here are a few programs +which implement some of the concepts presented in this paper. + + +1) This code (in the C programming language) dithers a 256-level + monochrome image onto a black-and-white display with the Bayer ordered + dither. + +/* Bayer-method ordered dither. The array line[] contains the intensity +** values for the line being processed. As you can see, the ordered +** dither is much simpler than the error dispersion dither. It is also +** many times faster, but it is not as accurate and produces cross-hatch +** patterns on the output. +*/ + +unsigned char line[WIDTH]; + +int pattern[8][8] = { + { 0, 32, 8, 40, 2, 34, 10, 42}, /* 8x8 Bayer ordered dithering */ + {48, 16, 56, 24, 50, 18, 58, 26}, /* pattern. Each input pixel */ + {12, 44, 4, 36, 14, 46, 6, 38}, /* is scaled to the 0..63 range */ + {60, 28, 52, 20, 62, 30, 54, 22}, /* before looking in this table */ + { 3, 35, 11, 43, 1, 33, 9, 41}, /* to determine the action. */ + {51, 19, 59, 27, 49, 17, 57, 25}, + {15, 47, 7, 39, 13, 45, 5, 37}, + {63, 31, 55, 23, 61, 29, 53, 21} }; + +int getline(); /* Function to read line[] from image */ + /* file; must return EOF when done. */ +putdot(int x, int y); /* Plot white dot at given x, y. */ + +dither() +{ + int x, y; + + while (getline() != EOF) { + for (x=0; x> 2; /* Scale value to 0..63 range */ + + if (c > pattern[x & 7][y & 7]) putdot(x, y); + } + ++y; + } +} + + +2) This program (also written in C) dithers a color image onto an 8-color + display by error-diffusion using the Burkes filter. + +/* Burkes filter error diffusion dithering algorithm in color. The array +** line[][] contains the RGB values for the current line being processed; +** line[0][x] = red, line[1][x] = green, line[2][x] = blue. +*/ + +unsigned char line[3][WIDTH]; +unsigned char colormap[3][COLORS] = { + 0, 0, 0, /* Black This color map should be replaced */ + 255, 0, 0, /* Red by one available on your hardware */ + 0, 255, 0, /* Green */ + 0, 0, 255, /* Blue */ + 255, 255, 0, /* Yellow */ + 255, 0, 255, /* Magenta */ + 0, 255, 255, /* Cyan */ + 255, 255, 255 }; /* White */ + +int getline(); /* Function to read line[][] from image */ + /* file; must return EOF when done. */ +putdot(int x, int y, int c); /* Plot dot of given color at given x, y. */ + +dither() +{ + static int ed[3][WIDTH] = {0}; /* Errors distributed down, i.e., */ + /* to the next line. */ + int x, y, h, c, nc, v, /* Working variables */ + e[4], /* Error parts (7/8,1/8,5/8,3/8). */ + ef[3]; /* Error distributed forward. */ + long dist, sdist; /* Used for least-squares match. */ + + for (x=0; x 255) v = 255; /* and clip. */ + line[c][x] = v; + } + + sdist = 255L * 255L * 255L + 1L; /* Compute the color */ + for (c=0; c> 1; /* half of v, e[1..4] */ + e[1] = (7 * h) >> 3; /* will be filled */ + e[2] = h - e[1]; /* with the Floyd and */ + h = v - h; /* Steinberg weights. */ + e[3] = (5 * h) >> 3; + e[4] = h = e[3]; + + ef[c] = e[1]; /* Distribute errors. */ + if (x < WIDTH-1) ed[c][x+1] = e[2]; + if (x == 0) ed[c][x] = e[3]; else ed[c][x] += e[3]; + if (x > 0) ed[c][x-1] += e[4]; + } + } + ++y; + } +} + + +3) This program (in somewhat incomplete, very inefficient pseudo-C) + implements error diffusion dithering with the Floyd and Steinberg + filter. It is not efficiently coded, but its purpose is to show the + method, which I believe it does. + +/* Floyd/Steinberg error diffusion dithering algorithm in color. The array +** line[][] contains the RGB values for the current line being processed; +** line[0][x] = red, line[1][x] = green, line[2][x] = blue. It uses the +** external functions getline() and putdot(), whose purpose should be easy +** to see from the code. +*/ + +unsigned char line[3][WIDTH]; +unsigned char colormap[3][COLORS] = { + 0, 0, 0, /* Black This color map should be replaced */ + 255, 0, 0, /* Red by one available on your hardware. */ + 0, 255, 0, /* Green It may contain any number of colors */ + 0, 0, 255, /* Blue as long as the constant COLORS is */ + 255, 255, 0, /* Yellow set correctly. */ + 255, 0, 255, /* Magenta */ + 0, 255, 255, /* Cyan */ + 255, 255, 255 }; /* White */ + +int getline(); /* Function to read line[] from image file; */ + /* must return EOF when done. */ +putdot(int x, int y, int c); /* Plot dot of color c at location x, y. */ + +dither() +{ + static int ed[3][WIDTH] = {0}; /* Errors distributed down, i.e., */ + /* to the next line. */ + int x, y, h, c, nc, v, /* Working variables */ + e[4], /* Error parts (7/8,1/8,5/8,3/8). */ + ef[3]; /* Error distributed forward. */ + long dist, sdist; /* Used for least-squares match. */ + + for (x=0; x 255) v = 255; /* and clip. */ + line[c][x] = v; + } + + sdist = 255L * 255L * 255L + 1L; /* Compute the color */ + for (c=0; c> 1; /* half of v, e[1..4] */ + e[1] = (7 * h) >> 3; /* will be filled */ + e[2] = h - e[1]; /* with the Floyd and */ + h = v - h; /* Steinberg weights. */ + e[3] = (5 * h) >> 3; + e[4] = h = e[3]; + + ef[c] = e[1]; /* Distribute errors. */ + if (x < WIDTH-1) ed[c][x+1] = e[2]; + if (x == 0) ed[c][x] = e[3]; else ed[c][x] += e[3]; + if (x > 0) ed[c][x-1] += e[4]; + } + } /* next x */ + + ++y; + } /* next y */ +} + + +===================================== +Bibliography + +[1] Foley, J.D. and A. van Dam, Fundamentals of Interactive Computer + Graphics, Addison-Wesley, Reading, MA, 1982. + + This is a standard reference for many graphic techniques which has + not declined with age. Highly recommended. This edition is out + of print but can be found in many university and engineering + libraries. NOTE: This book has been updated and rewritten, and + this new version is currently in print as: + + Foley, J.D., A. van Dam, S.K. Feiner, and J.F. Hughes; Computer + Graphics: Principles and Practice. Addison-Wesley, Reading, MA, 1990. + + This rewrite omits some of the more technical data of the 1982 + edition, but has been updated to include information on error- + diffusion and the Floyd-Steinberg filter. Currently on computer + bookstore shelves and rather expensive (around $75 list price). + +[2] Bayer, B.E., "An Optimum Method for Two-Level Rendition of Continuous + Tone Pictures," IEEE International Conference on Communications, + Conference Records, 1973, pp. 26-11 to 26-15. + + A short article proving the optimality of Bayer's pattern in the + dispersed-dot ordered dither. + +[3] Ulichney, R., Digital Halftoning, The MIT Press, Cambridge, MA, 1987. + + This is the best book I know of for describing the various black + and white dithering methods. It has clear explanations (a little + higher math may come in handy) and wonderful illustrations. It + does not contain any code, but don't let that keep you from + getting this book. Computer Literacy normally carries it but the + title is often sold out. + + [MFM note: I can't describe how much information I got from this + book! Several different writers have praised this reference to + the skies, and I can only concur. Some of it went right over my + head -- it's heavenly for someone who is thrilled by Fourier + analysis -- but the rest of it is a clear and excellent treatment + of the subject. I had to request it on an interlibrary loan, but + it was worth the two weeks' wait and the 25 cents it cost me for + the search. University or engineering libraries would be your + best bet, as would technical bookstores.] + +[4] Floyd, R.W. and L. Steinberg, "An Adaptive Algorithm for Spatial Gray + Scale." SID 1975, International Symposium Digest of Technical Papers, + vol 1975m, pp. 36-37. + + Short article in which Floyd and Steinberg introduce their filter. + +[5] Daniel Burkes is unpublished, but can be reached at this address: + + Daniel Burkes + TerraVision, Inc. + 2351 College Station Road, Suite 563 + Athens, GA 30305 + + or via CIS at UID# 72077,356. The Burkes error filter was submitted to + the public domain on September 15, 1988 in an unpublished document, + "Presentation of the Burkes error filter for use in preparing + continuous-tone images for presentation on bi-level devices." The file + BURKES.ARC, in LIB 15 (Publications) of the CIS Graphics Support Forum, + contains this document as well as sample images. + +[6] Jarvis, J.F., C.N. Judice, and W.H. Ninke, "A Survey of Techniques for + the Display of Continuous Tone Pictures on Bi-Level Displays," Computer + Graphics and Image Processing, vol. 5, pp. 13-40, 1976. + +[7] Stucki, P., "MECCA - a multiple-error correcting computation algorithm + for bilevel image hardcopy reproduction." Research Report RZ1060, IBM + Research Laboratory, Zurich, Switzerland, 1981. + +[8] Heckbert, P. "Color Image Quantization for Frame Buffer Display." + Computer Graphics (SIGGRAPH 82), vol. 16, pp. 297-307, 1982. + +[9] Frankie Sierra is unpublished, but can be reached via CIS at UID# + 76356,2254. Pictorial presentations of his filters can be found in LIB + 17 (Developer's Den) of the CIS Graphics Support Forum as the files + DITER1.GIF, DITER2.GIF, DITER6.GIF, DITER7.GIF, DITER8.GIF, and + DITER9.GIF. + +[10] J.F.R. "Frank" Slinkman is unpublished, but can be reached via CIS at + UID# 72411,650. The file NUDTHR.ARC in LIB 17 (Developer's Den) of the + CIS Graphics Support Forum contains his document "New Dithering Method + for Non-Square Pixels" as well as sample images and encoding program. + +[11] Lawrence Gozum is unpublished, but can be reached via CIS at UID# + 73437,2372. His document "Notes of IDTVGA Dithering Method" can be + found in LIB 17 (Developer's Den) of the CIS Graphics Support Forum as + the file IDTVGA.TXT. + +[12] Robert M. Crawford is unpublished, but can be reached via CIS at UID# + 76356,741. The file DGIF.ZIP in LIB 17 (Developer's Den) of the CIS + Graphics Support Forum contains documentation, sample images, and demo + program. + + +======================================================================== +Other works of interest: + +Knuth, D.E., "Digital Halftones by Dot Diffusion." ACM Transactions on +Graphics, Vol. 6, No. 4, October 1987, pp 245-273. + + Surveys the various methods available for mapping grayscale images to + B&W for high-quality phototypesetting and laser printer reproduction. + Presents an algorithm for smooth dot diffusion. (With 22 references.) + +Newman, W.M. and R.F.S. Sproull, Principles of Interactive Computer +Graphics, 2nd edition, McGraw-Hill, New York, 1979. + + Similar to Foley and van Dam in scope and content. + +Rogers, D.F., Procedural Elements for Computer Graphics, McGraw-Hill, New +York, 1985. + + More of a conceptual treatment of the subject -- for something with + more programming code, see the following work. Alas, the author errs + in his discussion of the Floyd-Steinberg filter and uses the "false" + filter pattern discussed earlier. + +Rogers, D.F. and J. A. Adams, Mathematical Elements for Computer Graphics, +McGraw-Hill, New York, 1976. + + A good detailed discussion of producing graphic images on a computer. + Plenty of sample code. + +Kuto, S., "Continuous Color Presentation Using a Low-Cost Ink Jet Printer," +Proc. Computer Graphics Tokyo 84, 24-27 April, 1984, Tokyo, Japan. + +Mitchell, W.J., R.S. Liggett, and T. Kvan, The Art of Computer Graphics +Programming, Van Nostrand Reinhold Co., New York, 1987. + +Pavlidis, T., Algorithms for Graphics and Image Processing, Computer Science +Press, Rockville, MD, 1982. + diff --git a/src/ImageSharp/Dithering/DITHER.TXT b/src/ImageSharp/Dithering/DITHER.TXT new file mode 100644 index 000000000..4d29a533e --- /dev/null +++ b/src/ImageSharp/Dithering/DITHER.TXT @@ -0,0 +1,547 @@ +DITHER.TXT + +What follows is everything you ever wanted to know (for the time being) about +dithering. I'm sure it will be out of date as soon as it is released, but it +does serve to collect data from a wide variety of sources into a single +document, and should save you considerable searching time. + +Numbers in brackets (like this [0]) are references. A list of these works +appears at the end of this document. + +Because this document describes ideas and algorithms which are constantly +changing, I expect that it may have many editions, additions, and corrections +before it gets to you. I will list my name below as original author, but I +do not wish to deter others from adding their own thoughts and discoveries. +This is not copyrighted in any way, and was created solely for the purpose of +organizing my own knowledge on the subject, and sharing this with others. +Please distribute it to anyonw who might be interested. + +If you add anything to this document, please feel free to include your name +below as a contributor or as a reference. I would particularly like to see +additions to the "Other books of interest" section. Please keep the text in +this simple format: no margins, no pagination, no lines longer that 79 +characters, and no non-ASCII or non-printing characters other than a CR/LF +pair at the end of each line. It is intended that this be read on as many +different machines as possible. + +Original Author: + +Lee Crocker I can be reached in the CompuServe Graphics +1380 Jewett Ave Support Forum (GO PICS) with ID # 73407,2030. +Pittsburg, CA 94565 + +Contributors: + +======================================================================== +What is Dithering? + +Dithering, also called Halftoning or Color Reduction, is the process of +rendering an image on a display device with fewer colors than are in the +image. The number of different colors in an image or on a device I will call +its Color Resolution. The term "resolution" means "fineness" and is used to +describe the level of detail in a digitally sampled signal. It is used most +often in referring to the Spatial Resolution, which is the basic sampling +rate for a digitized image. + +Spatial resolution describes the fineness of the "dots" used in an image. +Color resolution describes the fineness of detail available at each dot. The +higher the resolution of a digital sample, the better it can reproduce high +frequency detail. A compact disc, for example, has a temporal (time) +resolution of 44,000 samples per second, and a dynamic (volume) resolution of +16 bits (0..65535). It can therefore reproduce sounds with a vast dynamic +range (from barely audible to ear-splitting) with great detail, but it has +problems with very high-frequency sounds, like violins and piccolos. + +It is often possible to "trade" one kind of resolution for another. If your +display device has a higher spatial resolution than the image you are trying +to reproduce, it can show a very good image even if its color resolution is +less. This is what we will call "dithering" and is the subject of this +paper. The other tradeoff, i.e., trading color resolution for spatial +resolution, is called "anti-aliasing" and is not discussed here. + +It is important to emphasize here that dithering is a one-way operation. +Once an image has been dithered, although it may look like a good +reproduction of the original, information is permanently lost. Many image +processing functions fail on dithered images. For these reasons, dithering +must be considered only as a way to produce an image on hardware that would +otherwise be incapable of displaying it. The data representing an image +should always be kept in full detail. + + +======================================================================== +Classes of dithering algorithms + +The classes of dithering algorithms we will discuss here are these: + +1. Random +2. Pattern +3. Ordered +4. Error dispersion + +Each of these methods is generally better than those listed before it, but +other considerations such as processing time, memory constraints, etc. may +weigh in favor of one of the simpler methods. + +For the following discussions I will assume that we are given an image with +256 shades of gray (0=black..255=white) that we are trying to reproduce on a +black and white ouput device. Most of these methods can be extended in +obvious ways to deal with displays that have more than two levels but fewer +than the image, or to color images. Where such extension is not obvious, or +where better results can be obtained, I will go into more detail. + +To convert any of the first three methods into color, simply apply the +algorithm separately for each primary and mix the resulting values. This +assumes that you have at least eight output colors: black, red, green, blue, +cyan, magenta, yellow, and white. Though this will work for error dispersion +as well, there are better methods in this case. + + +======================================================================== +Random dither + +This is the bubblesort of dithering algorithms. It is not really acceptable +as a production method, but it is very simple to describe and implement. For +each value in the image, simply generate a random number 1..256; if it is +geater than the image value at that point, plot the point white, otherwise +plot it black. That's it. This generates a picture with a lot of "white +noise", which looks like TV picture "snow". Though the image produced is +very inaccurate and noisy, it is free from "artifacts" which are phenomena +produced by digital signal processing. + +The most common type of artifact is the Moire pattern (Contributors: please +resist the urge to put an accent on the "e", as no portable character set +exists for this). If you draw several lines close together radiating from a +single point on a computer display, you will see what appear to be flower- +like patterns. These patterns are not part of the original idea of lines, +but are an illusion produced by the jaggedness of the display. + +Many techniques exist for the reduction of digital artifacts like these, most +of which involve using a little randomness to "perturb" a regular algorithm a +little. Random dither obviously takes this to extreme. + +I should mention, of course, that unless your computer has a hardware-based +random number generator (and most don't) there may be some artifacts from the +random number generation algorithm itself. + +While random dither adds a lot of high-frequency noise to a picture, it is +useful in reproducing very low-frequency images where the absence of +artifacts is more important than noise. For example, a whole screen +containing a gradient of all levels from black to white would actually look +best with a random dither. In this case, ordered dithering would produce +diagonal patterns, and error dispersion would produce clustering. + +For efficiency, you can take the random number generator "out of the loop" by +generating a list of random numbers beforehand for use in the dither. Make +sure that the list is larger than the number of pixels in the image or you +may get artifacts from the reuse of numbers. The worst case would be if the +size of your list of random numbers is a multiple or near-multiple of the +horizontal size of the image, in which case unwanted vertical or diagonal +lines will appear. + + +======================================================================== +Pattern dither + +This is also a simple concept, but much more effective than random dither. +For each possible value in the image, create a pattern of dots that +approximates that value. For instance, a 3-by-3 block of dots can have one +of 512 patterns, but for our purposes, there are only 10; the number of black +dots in the pattern determines the darkness of the pattern. + +Which 10 patterns do we choose? Obviously, we need the all-white and all- +black patterns. We can eliminate those patterns which would create vertical +or horizontal lines if repeated over a large area because many images have +such regions of similar value [1]. It has been shown [1] that patterns for +adjacent colors should be similar to reduce an artifact called "contouring", +or visible edges between regions of adjacent values. One easy way to assure +this is to make each pattern a superset of the previous. Here are two good +sets of patterns for a 3-by-3 matrix: + + --- --- --- -X- -XX -XX -XX -XX XXX XXX + --- -X- -XX -XX -XX -XX XXX XXX XXX XXX + --- --- --- --- --- -X- -X- XX- XX- XXX +or + --- X-- X-- X-- X-X X-X X-X XXX XXX XXX + --- --- --- --X --X X-X X-X X-X XXX XXX + --- --- -X- -X- -X- -X- XX- XX- XX- XXX + +The first set of patterns above are "clustered" in that as new dots are added +to each pattern, they are added next to dots already there. The second set +is "dispersed" as the dots are spread out more. This distinction is more +important on larger patterns. Dispersed-dot patterns produce less grainy +images, but require that the output device render each dot distinctly. When +this is not the case, as with a printing press which smears the dots a +little, clustered patterns are better. + +For each pixel in the image we now print the pattern which is closest to its +value. This will triple the size of the image in each direction, so this +method can only be used where the display spatial resolution is much greater +than that of the image. + +We can exploit the fact that most images have large areas of similar value to +reduce our need for extra spatial resolution. Instead of plotting a whole +pattern for each pixel, map each pixel in the image to a dot in the pattern +an only plot the corresponding dot for each pixel. + +The simplest way to do this is to map the X and Y coordinates of each pixel +into the dot (X mod 3, Y mod 3) in the pattern. Large areas of constant +value will come out as repetitions of the pattern as before. + +To extend this method to color images, we must use patterns of colored dots +to represent shades not directly printable by the hardware. For example, if +your hardware is capable of printing only red, green, blue, and black (the +minimal case for color dithering), other colors can be represented with +patterns of these four: + + Yellow = R G Cyan = G B Magenta = R B Gray = R G + G R B G B R B K + +(B here represents blue, K is black). There are a total of 31 such distinct +patterns which can be used; I will leave their enumeration "as an exercise +for the reader" (don't you hate books that do that?). + + +======================================================================== +Ordered dither + +Because each of the patterns above is a superset of the previous, we can +express the patterns in compact form as the order of dots added: + + 8 3 4 and 1 7 4 + 6 1 2 5 8 3 + 7 5 9 6 2 9 + +Then we can simply use the value in the array as a threshhold. If the value +of the pixel (scaled into the 0-9 range) is less than the number in the +corresponding cell of the matrix, plot that pixel black, otherwise, plot it +white. This process is called ordered dither. As before, clustered patterns +should be used for devices which blur dots. In fact, the clustered pattern +ordered dither is the process used by most newspapers, and the term +halftoning refers to this method if not otherwise qualified. + +Bayer [2] has shown that for matrices of orders which are powers of two there +is an optimal pattern of dispersed dots which results in the pattern noise +being as high-frequency as possible. The pattern for a 2x2 and 4x4 matrices +are as follows: + + 1 3 1 9 3 11 These patterns (and their rotations + 4 2 13 5 15 7 and reflections) are optimal for a + 4 12 2 10 dispersed-pattern ordered dither. + 16 8 14 6 + +Ulichney [3] shows a recursive technique can be used to generate the larger +patterns. To fully reproduce our 256-level image, we would need to use the +8x8 pattern. + +Bayer's method is in very common use and is easily identified by the cross- +hatch pattern artifacts it produces in the resulting display. This +artifacting is the major drawback of the technique wich is otherwise very +fast and powerful. Ordered dithering also performs very badly on images +which have already been dithered to some extent. As stated earlier, +dithering should be the last stage in producing a physical display from a +digitally stored image. The dithered image should never be stored itself. + + +======================================================================== +Error dispersion + +This technique generates the best results of any method here, and is +naturally the slowest. In fact, there are many variants of this technique as +well, and the better they get, the slower they are. + +Error dispersion is very simple to describe: for each point in the image, +first find the closest color available. Calculate the difference between the +value in the image and the color you have. Now divide up these error values +and distribute them over the neighboring pixels which you have not visited +yet. When you get to these later pixels, just add the errors distributed +from the earlier ones, clip the values to the allowed range if needed, then +continue as above. + +If you are dithering a grayscale image for output to a black-and-white +device, the "find closest color" is just a simle threshholding operation. In +color, it involves matching the input color to the closest available hardware +color, which can be difficult depending on the hardware palette. + +There are many ways to distribute the errors and many ways to scan the +image, but I will deal here with only a few. The two basic ways to scan the +image are with a normal left-to-right, top-to-bottom raster, or with an +alternating left-to-right then right-to-left raster. The latter method +generally produces fewer artifacts and can be used with all the error +diffusion patterns discussed below. + +The different ways of dividing up the error can be expressed as patterns +(called filters, for reasons too boring to go into here). + + X 7 This is the Floyd and Steinberg [4] + 3 5 1 error diffusion filter. + +In this filter, the X represents the pixel you are currently scanning, and +the numbers (called weights, for equally boring reasons) represent the +proportion of the error distributed to the pixel in that position. Here, the +pixel immediately to the right gets 7/16 of the error (the divisor is 16 +because the weights add to 16), the pixel directly below gets 5/16 of the +error, and the diagonally adjacent pixels get 3/16 and 1/16. When scanning a +line right-to-left, this pattern is reversed. This pattern was chosen +carefully so that it would produce a checkerboard pattern in areas with +intensity of 1/2 (or 128 in our image). It is also fairly easy to calculate +when the division by 16 is replaced by shifts. + +Another filter in common use, but not recommended: + + X 3 A simpler filter. + 3 2 + +This is often erroneously called the Floyd-Steinberg filter, but it does not +produce as good results. An alternating raster scan of the image is +necessary with this filter to reduce artifacts. Additional perturbations of +the formula are frequently necessary also. + +Burke [5] suggests the following filter: + + X 8 4 The Burke filter. + 2 4 8 4 2 + +Notice that this is just a simplification of the Stucki filter (below) with +the bottom row removed. The main improvement is that the divisor is now 32, +which makes calculating the errors faster, and the removal of one row +reduces the memory requirements of the method. + +This is also fairly easy to calculate and produces better results than Floyd +and Steinberg. Jarvis, Judice, and Ninke [6] use the following: + + X 7 5 The Jarvis, et al. pattern. + 3 5 7 5 3 + 1 3 5 3 1 + +The divisor here is 48, which is a little more expensive to calculate, and +the errors are distributed over three lines, requiring extra memory and time +for processing. Probably the best filter is from Stucki [7]: + + X 8 4 The Stucki pattern. + 2 4 8 4 2 + 1 2 4 2 1 + +This one takes a division by 42 for each pixel and is therefore slow if math +is done inside the loop. After the initial 8/42 is calculated, some time can +be saved by producing the remaining fractions by shifts. + +The speed advantages of the simpler filters can be eliminated somewhat by +performing the divisions beforehand and using lookup tables instead of per- +forming math inside the loop. This makes it harder to use various filters +in the same program, but the speed benefits are enormous. + +It is critical with all of these algorithms that when error values are added +to neighboring pixels, the values must be truncated to fit within the limits +of hardware, otherwise and area of very intense color may cause streaks into +an adjacent area of less intense color. This truncation adds noise to the +image anagous to clipping in an audio amplifier, but it is not nearly so +offensive as the streaking. It is mainly for this reason that the larger +filters work better--they split the errors up more finely and produce less of +this clipping noise. + +With all of these filters, it is also important to ensure that the errors +you distribute properly add to the original error value. This is easiest to +accomplish by subtracting each fraction from the whole error as it is +calculated, and using the final remainder as the last fraction. + +Some of these methods (particularly the simpler ones) can be greatly improved +by skewing the weights with a little randomness [3]. + +Calculating the "nearest available color" is trivial with a monochrome image; +with color images it requires more work. A table of RGB values of all +available colors must be scanned sequentially for each input pixel to find +the closest. The "distance" formula most often used is a simple pythagorean +"least squares". The difference for each color is squared, and the three +squares added to produce the distance value. This value is equivalent to the +square of the distance between the points in RGB-space. It is not necessary +to compute the square root of this value because we are not interested in the +actual distance, only in which is smallest. The square root function is a +monotonic increasing function and does not affect the order of its operands. +If the total number of colors with which you are dealing is small, this part +of the algorithm can be replaced by a lookup table as well. + +When your hardware allows you to select the available colors, very good +results can be achieved by selecting colors from the image itself. You must +reserve at least 8 colors for the primaries, secondaries, black, and white +for best results. If you do not know the colors in your image ahead of time, +or if you are going to use the same map to dither several different images, +you will have to fill your color map with a good range of colors. This can +be done either by assigning a certain number of bits to each primary and +computing all combinations, or by a smoother distribution as suggested by +Heckbert [8]. + + +======================================================================== +Sample code + +Despite my best efforts in expository writing, nothing explains an algorithm +better than real code. With that in mind, presented here below is an +algorithm (in somewhat incomplete, very inefficient pseudo-C) which +implements error diffusion dithering with the Floyd and Steinberg filter. It +is not efficiently coded, but its purpose is to show the method, which I +believe it does. + +/* Floyd/Steinberg error diffusion dithering algorithm in color. The array +** line[][] contains the RGB values for the current line being processed; +** line[0][x] = red, line[1][x] = green, line[2][x] = blue. It uses the +** external functions getline() and putdot(), whose pupose should be easy +** to see from the code. +*/ + +unsigned char line[3][WIDTH]; +unsigned char colormap[3][COLORS] = { + 0, 0, 0, /* Black This color map should be replaced */ + 255, 0, 0, /* Red by one available on your hardware. */ + 0, 255, 0, /* Green It may contain any number of colors */ + 0, 0, 255, /* Blue as long as the constant COLORS is */ + 255, 255, 0, /* Yellow set correctly. */ + 255, 0, 255, /* Magenta */ + 0, 255, 255, /* Cyan */ + 255, 255, 255 }; /* White */ + +int getline(); /* Function to read line[] from image file; */ + /* must return EOF when done. */ +putdot(int x, int y, int c); /* Plot dot of color c at location x, y. */ + +dither() +{ + static int ed[3][WIDTH] = {0}; /* Errors distributed down, i.e., */ + /* to the next line. */ + int x, y, h, c, nc, v, /* Working variables */ + e[4], /* Error parts (7/8,1/8,5/8,3/8). */ + ef[3]; /* Error distributed forward. */ + long dist, sdist; /* Used for least-squares match. */ + + for (x=0; x 255) v = 255; /* and clip. */ + line[c][x] = v; + } + + sdist = 255L * 255L * 255L + 1L; /* Compute the color */ + for (c=0; c> 1; /* half of v, e[1..4] */ + e[1] = (7 * h) >> 3; /* will be filled */ + e[2] = h - e[1]; /* with the Floyd and */ + h = v - h; /* Steinberg weights. */ + e[3] = (5 * h) >> 3; + e[4] = h = e[3]; + + ef[c] = e[1]; /* Distribute errors. */ + if (x < WIDTH-1) ed[c][x+1] = e[2]; + if (x == 0) ed[c][x] = e[3]; else ed[c][x] += e[3]; + if (x > 0) ed[c][x-1] += e[4]; + } + } /* next x */ + + ++y; + } /* next y */ +} + + +======================================================================== +Bibliography + +[1] Foley, J. D. and Andries Van Dam (1982) + Fundamentals of Interactive Computer Graphics. Reading, MA: Addisson + Wesley. + + This is a standard reference for many graphic techniques which has not + declined with age. Highly recommended. + +[2] Bayer, B. E. (1973) + "An Optimum Method for Two-Level Rendition of Continuous Tone Pictures," + IEEE International Conference on Communications, Conference Records, pp. + 26-11 to 26-15. + + A short article proving the optimality of Bayer's pattern in the + dispersed-dot ordered dither. + +[3] Ulichney, R. (1987) + Digital Halftoning. Cambridge, MA: The MIT Press. + + This is the best book I know of for describing the various black and + white dithering methods. It has clear explanations (a little higher math + may come in handy) and wonderful illustrations. It does not contain any + code, but don't let that keep you from getting this book. Computer + Literacy carries it but is often sold out. + +[4] Floyd, R.W. and L. Steinberg (1975) + "An Adaptive Algorithm for Spatial Gray Scale." SID International + Symposium Digest of Technical Papers, vol 1975m, pp. 36-37. + + Short article in which Floyd and Steinberg introduce their filter. + +[5] Daniel Burkes is unpublished, but can be reached at this address: + + Daniel Burkes + TerraVision Inc. + 2351 College Station Road Suite 563 + Athens, GA 30305 + + or via CompuServe's Graphics Support Forum, ID # 72077,356. + +[6] Jarvis, J. F., C. N. Judice, and W. H. Ninke (1976) + "A Survey of Techniques for the Display of Continuous Tone Pictures on + Bi-Level Displays." Computer Graphics and Image Processing, vol. 5, pp. + 13-40. + +[7] Stucki, P. (1981) + "MECCA - a multiple-error correcting computation algorithm for bilevel + image hardcopy reproduction." Research Report RZ1060, IBM Research + Laboratory, Zurich, Switzerland. + +[8] Heckbert, Paul (9182) + "Color Image Quantization for Frame Buffer Display." Computer Graphics + (SIGGRAPH 82), vol. 16, pp. 297-307. + + +======================================================================== +Other works of interest: + +Newman, William M., and Robert F. S. Sproull (1979) +Principles of Interactive Computer Graphics. 2nd edition. New York: +McGraw-Hill. + +Rogers, David F. (1985) +Procedural Elements for Computer Graphics. New York: McGraw-Hill. + +Rogers, David F., and J. A. Adams (1976) +Mathematical Elements for Computer Graphics. New York: McGraw-Hill. + + +======================================================================== +About CompuServe Graphics Support Forum: + +CompuServe Information Service is a service of the H&R Block companies +providing computer users with electronic mail, teleconferencing, and many +other telecommunications services. Call 800-848-8199 for more information. + +The Graphics Support Forum is dedicated to helping its users get the most out +of their computers' graphics capabilities. It has a small staff and a large +number of "Developers" who create images and software on all types of +machines from Apple IIs to Sun workstations. While on CompuServe, type GO +PICS from any "!" prompt to gain access to the forum. \ No newline at end of file From 11ffe9be571f6ebe7d17ff894ebff7cdd6b1270b Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Mon, 13 Feb 2017 08:12:56 +0000 Subject: [PATCH 056/142] fix vars --- src/ImageSharp/Image/Image{TColor}.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Image/Image{TColor}.cs b/src/ImageSharp/Image/Image{TColor}.cs index ded3e376e..75d855ad4 100644 --- a/src/ImageSharp/Image/Image{TColor}.cs +++ b/src/ImageSharp/Image/Image{TColor}.cs @@ -254,8 +254,8 @@ namespace ImageSharp /// The public Image Save(string filePath) { - var ext = Path.GetExtension(filePath).Trim('.'); - var format = this.Configuration.ImageFormats.SingleOrDefault(f => f.SupportedExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase)); + string ext = Path.GetExtension(filePath).Trim('.'); + IImageFormat format = this.Configuration.ImageFormats.SingleOrDefault(f => f.SupportedExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase)); if (format == null) { throw new InvalidOperationException($"No image formats have been registered for the file extension '{ext}'."); @@ -274,7 +274,7 @@ namespace ImageSharp public Image Save(string filePath, IImageFormat format) { Guard.NotNull(format, nameof(format)); - using (var fs = File.Create(filePath)) + using (FileStream fs = File.Create(filePath)) { return this.Save(fs, format); } @@ -290,7 +290,7 @@ namespace ImageSharp public Image Save(string filePath, IImageEncoder encoder) { Guard.NotNull(encoder, nameof(encoder)); - using (var fs = File.Create(filePath)) + using (FileStream fs = File.Create(filePath)) { return this.Save(fs, encoder); } From b2dfc0fd285cb34e9b8284816b76dde5b0a04af7 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 14 Feb 2017 09:27:00 +1100 Subject: [PATCH 057/142] Whitespace --- src/ImageSharp/Dithering/DHALF.TXT | 134 +++---- src/ImageSharp/Dithering/DITHER.TXT | 530 ++++++++++++++-------------- 2 files changed, 332 insertions(+), 332 deletions(-) diff --git a/src/ImageSharp/Dithering/DHALF.TXT b/src/ImageSharp/Dithering/DHALF.TXT index dce9ef924..ec6ba3d6e 100644 --- a/src/ImageSharp/Dithering/DHALF.TXT +++ b/src/ImageSharp/Dithering/DHALF.TXT @@ -12,10 +12,10 @@ What follows is everything you ever wanted to know (for the time being) about digital halftoning, or dithering. I'm sure it will be out of date as soon as it is released, but it does serve to collect data from a wide variety of sources into a single document, and should save you considerable -searching time. +searching time. Numbers in brackets (e.g. [4] or [12]) are references. A list of these -works appears at the end of this document. +works appears at the end of this document. Because this document describes ideas and algorithms which are constantly changing, I expect that it may have many editions, additions, and @@ -23,7 +23,7 @@ corrections before it gets to you. I will list my name below as original author, but I do not wish to deter others from adding their own thoughts and discoveries. This is not copyrighted in any way, and was created solely for the purpose of organizing my own knowledge on the subject, and sharing -this with others. Please distribute it to anyone who might be interested. +this with others. Please distribute it to anyone who might be interested. If you add anything to this document, please feel free to include your name below as a contributor or as a reference. I would particularly like to see @@ -31,7 +31,7 @@ additions to the "Other books of interest" section. Please keep the text in this simple format: no margins, no pagination, no lines longer than 79 characters, and no non-ASCII or non-printing characters other than a CR/LF pair at the end of each line. It is intended that this be read on as many -different machines as possible. +different machines as possible. Original Author: @@ -50,7 +50,7 @@ Contributors: COMMENTS BY MIKE MORRA I first entered the world of imaging in the fall of 1990 when my employer, -Epson America Inc., began shipping the ES-300C color flatbed scanner. +Epson America Inc., began shipping the ES-300C color flatbed scanner. Suddenly, here I was, a field systems analyst who had worked almost exclusively with printers and PCs, thrust into a new and arcane world of look-up tables and dithering and color reduction and .GIF files! I realized @@ -60,10 +60,10 @@ Graphics Support Forum on a very regular basis. Lee Crocker's excellent paper called DITHER.TXT was one of the first pieces of information that I came across, and it went a very long way toward -answering a lot of questions that I'd had about the subject of dithering. +answering a lot of questions that I'd had about the subject of dithering. It also provided me with the names of other essential reference works upon which Lee had based his paper, and I immediately began an eager search for -these other references. +these other references. In the course of my self-study, however, I found that DITHER.TXT does presume the reader's familiarity with some fundamental imaging concepts, @@ -90,7 +90,7 @@ second, distinct document. Too, I may very well have misconstrued or misinterpreted some factual information in my revision. As such, I welcome criticism and comment from all the original authors and contributors, and any readers, with the hope that their feedback will help me to address these -issues. +issues. If this revision it is received favorably, I will submit it to the public domain; if it is met with brickbats (for whatever reason), I will withdraw @@ -103,7 +103,7 @@ my questions that I needed. I'd like to publicly thank the whole Forum community in general for putting up with my unending barrage of questions and inquiries over the past few months . In particular, I would thank John Swenson, Chris Young, and (of course) Lee Crocker for their invaluable -assistance. +assistance. Mike Morra [76703,4051] June 20, 1991 @@ -118,7 +118,7 @@ digitized images on display devices which were incapable of reproducing the full spectrum of intensities or colors present in the source image. The challenge is even more pronounced in today's world of personal computing because of the technology gap between image generation and image rendering -equipment. +equipment. Today, we now have affordable 24-bit image scanners which can generate nearly true-to-life scans having as many as 256 shades of gray, or in excess @@ -127,7 +127,7 @@ behind with 16- and 256-color VGA/SVGA video monitors and printers with binary (black/white) "marking engines" as the norm. Without specialized techniques for color reduction -- the process of finding the "best fit" of the display device's available gray shades and/or colors -- the imaging -experimenter would be plagued with blotchy, noisy, off-color images. +experimenter would be plagued with blotchy, noisy, off-color images. (As of this writing, "true color" 24-bit video display devices, capable of reproducing all of the color/intensity information in the source image, are @@ -135,7 +135,7 @@ now beginning to migrate downward into the PC environment, but they exact a premium in cost and processor power which many users are loathe to pay. So- called "high-color" video displays -- typically 16-bit, with 32,768-color capability -- are moving into the mainstream, but color reduction techniques -would still be required with these devices.) +would still be required with these devices.) The science of digital halftoning (more commonly referred to as dithering, or spatial dithering) is one of the techniques used to achieve satisfactory @@ -146,7 +146,7 @@ full white pixels, or on printers which could produce only full black spots on a printed page. Indeed, Ulichney [3] gives a definition of digital halftoning as "... any algorithmic process which creates the illusion of continuous-tone images from the judicious arrangement of binary picture -elements." +elements." Ulichney's study, as well as the earlier literature on the subject (and this paper itself), discusses the process mostly in this context. Since we in @@ -164,8 +164,8 @@ range of colors or gray shades that are contained in the source image. Intensity/Color Resolution The concept of resolution is essential to the understanding of digital -halftoning. Resolution can be defined as "fineness" and is used to -describe the level of detail in a digitally sampled signal. +halftoning. Resolution can be defined as "fineness" and is used to +describe the level of detail in a digitally sampled signal. Typically, when we hear the term "resolution" applied to images, we think of what's known as "spatial resolution," which is the basic sampling rate for @@ -194,7 +194,7 @@ display device has a higher spatial resolution than the image you are trying to reproduce, it can show a very good image even if its color resolution is less. This is what most of us know as "dithering" and is the subject of this paper. (The other tradeoff, i.e., trading color resolution for spatial -resolution, is called "anti-aliasing," and is not discussed here.) +resolution, is called "anti-aliasing," and is not discussed here.) For the following discussions I will assume that we are given a grayscale @@ -205,7 +205,7 @@ printer, or an HP LaserJet laser printer. Most of these methods can be extended in obvious ways to deal with displays that have more than two levels (but still fewer than the source image), or to color images. Where such extension is not obvious, or where better results can be obtained, I -will go into more detail. +will go into more detail. ===================================== @@ -217,7 +217,7 @@ black and white device. This is accomplished by establishing a demarcation point, or threshold, at the 50% gray level. Each dot of the source image is compared against this threshold value: if it is darker than the value, the device plots it black, and if it's lighter, the device plots it white. - + What happens to the image during this operation? Well, some detail survives, but our perception of gray levels is completely gone. This means that a lot of the image content is obliterated. Take an area of the image @@ -281,16 +281,16 @@ categories: 3. Ordered dither 4. Error-diffusion halftoning -Each of these methods is generally better than those listed before it, but -other considerations such as processing time, memory constraints, etc. may -weigh in favor of one of the simpler methods. +Each of these methods is generally better than those listed before it, but +other considerations such as processing time, memory constraints, etc. may +weigh in favor of one of the simpler methods. To convert any of the first three methods into color, simply apply the -algorithm separately for each primary color and mix the resulting values. +algorithm separately for each primary color and mix the resulting values. This assumes that you have at least eight output colors: black, red, green, blue, cyan, magenta, yellow, and white. Though this will work for error diffusion as well, there are better methods which will be discussed in more -detail later. +detail later. ===================================== @@ -307,7 +307,7 @@ While it is not really acceptable as a production method, it is very simple to describe and implement. For each dot in our grayscale image, we generate a random number in the range 0 - 255: if the random number is greater than the image value at that dot, the display device plots the dot white; -otherwise, it plots it black. That's it. +otherwise, it plots it black. That's it. This generates a picture with a lot of "white noise", which looks like TV picture "snow". Although inaccurate and grainy, the image is free from @@ -317,7 +317,7 @@ more important than noise. For example, a whole screen containing a gradient of all levels from black to white would actually look best with a random dither. With this image, other digital halftoning algorithms would produce significant artifacts like diagonal patterns (in ordered dithering) -and clustering (in error diffusion halftones). +and clustering (in error diffusion halftones). I should mention, of course, that unless your computer has a hardware-based random number generator (and most don't), there may be some artifacts from @@ -399,7 +399,7 @@ like: because if they were repeated over a large area (a common occurrence in many -images [1]) they would create vertical, horizontal, or diagonal lines. +images [1]) they would create vertical, horizontal, or diagonal lines. Also, studies [1] have shown that the patterns should form a "growth sequence:" once a pixel is intensified for a particular value, it should remain intensified for all subsequent values. In this fashion, each pattern @@ -407,7 +407,7 @@ is a superset of the previous one; this similarity between adjacent intensity patterns minimizes any contouring artifacts. Here is a good pattern for a 3-by-3 matrix which subscribes to the rules set -forth above: +forth above: --- --- --- -X- -XX -XX -XX -XX XXX XXX @@ -427,7 +427,7 @@ greater than that of the image. Another limitation of patterning is that the effective spatial resolution is decreased, since a multiple-pixel "cell" is used to simulate the single, larger halftone dot. The more intensity resolution we want, the larger the -halftone cell used and, by extension, the lower the spatial resolution. +halftone cell used and, by extension, the lower the spatial resolution. In the above example, using 3 x 3 patterning, we are able to simulate 10 intensity levels (not a very good rendering) but we must reduce the spatial @@ -437,22 +437,22 @@ eight-fold decrease in spatial resolution. And to get the full 256 levels of intensity in our source image, we would need a 16 x 16 pattern and would incur a 16-fold reduction in spatial resolution. Because of this size distortion of the image, and with the development of more effective digital -halftoning methods, patterning is only infrequently used today. +halftoning methods, patterning is only infrequently used today. To extend this method to color images, we would use patterns of colored pixels to represent shades not directly printable by the hardware. For example, if your hardware is capable of printing only red, green, blue, and black (the minimal case for color dithering), other colors can be -represented with 2 x 2 patterns of these four: +represented with 2 x 2 patterns of these four: - Yellow = R G Cyan = G B Magenta = R B Gray = R G + Yellow = R G Cyan = G B Magenta = R B Gray = R G G R B G B R B K (B here represents blue, K is black). In this particular example, there are a total of 31 such distinct patterns which can be used; their enumeration is -left "as an exercise for the reader" (don't you hate books that do that?). +left "as an exercise for the reader" (don't you hate books that do that?). ===================================== @@ -503,13 +503,13 @@ PATTERN. Returning to our example of a 3 x 3 pattern, this means that we would be mapping NINE image dots into this pattern. The simplest way to do this in programming is to map the X and Y coordinates -of each image dot into the pixel (X mod 3, Y mod 3) in the pattern. +of each image dot into the pixel (X mod 3, Y mod 3) in the pattern. Returning to our two patterns (clustered and dispersed) as defined earlier, we can derive an effective mathematical algorithm that can be used to plot the correct pixel patterns. Because each of the patterns above is a superset of the previous, we can express the patterns in a compact array -form as the order of pixels added: +form as the order of pixels added: 8 3 4 1 7 4 @@ -534,7 +534,7 @@ allows) is preferred in order to decrease the graininess of the displayed images. Bayer [2] has shown that for matrices of orders which are powers of two there is an optimal pattern of dispersed dots which results in the pattern noise being as high-frequency as possible. The pattern for a 2x2 -and 4x4 matrices are as follows: +and 4x4 matrices are as follows: 1 3 1 9 3 11 These patterns (and their rotations @@ -548,7 +548,7 @@ patterns. (To fully reproduce our 256-level image, we would need to use an 8x8 pattern.) The Bayer ordered dither is in very common use and is easily identified by -the cross-hatch pattern artifacts it produces in the resulting display. +the cross-hatch pattern artifacts it produces in the resulting display. This artifacting is the major drawback of an otherwise powerful and very fast technique. @@ -616,7 +616,7 @@ we use for processing this point. If we are dithering our sample grayscale image for output to a black-and- white device, the "find closest intensity/color" operation is just a simple -thresholding (the closest intensity is going to be either black or white). +thresholding (the closest intensity is going to be either black or white). In color imaging -- for instance, color-reducing a 24-bit true color Targa file to an 8-bit, mapped GIF file -- this involves matching the input color to the closest available hardware color. Depending on how the display @@ -653,7 +653,7 @@ position. The expression in parentheses is the divisor used to break up the error weights. In the Floyd-Steinberg filter, each pixel "communicates" with 4 "neighbors." The pixel immediately to the right gets 7/16 of the error value, the pixel directly below gets 5/16 of the error, and the -diagonally adjacent pixels get 3/16 and 1/16. +diagonally adjacent pixels get 3/16 and 1/16. The weighting shown is for the traditional left-to-right scanning of the image. If the line were scanned right-to-left (more about this later), this @@ -683,11 +683,11 @@ The output from this filter is nowhere near as good as that from the real Floyd-Steinberg filter. There aren't enough weights to the dispersion, which means that the error value isn't distributed finely enough. With the entire image scanned left-to-right, the artifacting produced would be -totally unacceptable. +totally unacceptable. Much better results would be obtained by using an alternating, or serpentine, raster scan: processing the first line left-to-right, the next -line right-to-left, and so on (reversing the filter pattern appropriately). +line right-to-left, and so on (reversing the filter pattern appropriately). Serpentine scanning -- which can be used with any of the error-diffusion filters detailed here -- introduces an additional perturbation which contributes more randomness to the resultant halftone. Even with serpentine @@ -702,9 +702,9 @@ If the false Floyd-Steinberg filter fails because the error isn't distributed well enough, then it follows that a filter with a wider distribution would be better. This is exactly what Jarvis, Judice, and Ninke [6] did in 1976 with their filter: - - * 7 5 + + * 7 5 3 5 7 5 3 1 3 5 3 1 (1/48) @@ -723,7 +723,7 @@ requires extra memory and time for processing. The Stucki filter P. Stucki [7] offered a rework of the Jarvis, Judice, and Ninke filter in -1981: +1981: * 8 4 @@ -731,12 +731,12 @@ P. Stucki [7] offered a rework of the Jarvis, Judice, and Ninke filter in 1 2 4 2 1 (1/42) -Once again, division by 42 is quite slow to calculate (requiring DIVs). +Once again, division by 42 is quite slow to calculate (requiring DIVs). However, after the initial 8/42 is calculated, some time can be saved by producing the remaining fractions by shifts. The Stucki filter has been observed to give very clean, sharp output, which helps to offset the slow processing time. - + ===================================== The Burkes filter @@ -752,7 +752,7 @@ in 1988: Notice that this is just a simplification of the Stucki filter with the bottom row removed. The main improvement is that the divisor is now 32, which allows the error values to be calculated using shifts once more, and -the number of neighbors communicated with has been reduced to seven. +the number of neighbors communicated with has been reduced to seven. Furthermore, the removal of one row reduces the memory requirements of the filter by eliminating the second forward array which would otherwise be needed. @@ -807,9 +807,9 @@ would also include HP-compatible and PostScript desktop laser printers using Some displays may use "rectangular pixels," where the horizontal and vertical spacings are unequal. This would include various EGA and CGA video -modes and other specialized video displays, and most dot-matrix printers. +modes and other specialized video displays, and most dot-matrix printers. In many cases, the filters described earlier will do a decent job on -rectangular pixel grids, but an optimized filter would be preferred. +rectangular pixel grids, but an optimized filter would be preferred. Slinkman [10] describes one such filter for his 640 x 240 monochrome display with a 1:2 aspect ratio. @@ -832,7 +832,7 @@ be interested in further information. While technically not an error-diffusion filter, a method proposed by Gozum [11] offers color resolutions in excess of 256 colors by plotting red, green, and blue pixel "triplets" or triads to simulate an "interlaced" -television display (sacrificing some horizontal resolution in the process). +television display (sacrificing some horizontal resolution in the process). Again, I would refer interested readers to his document for more information. @@ -848,7 +848,7 @@ various filters in the same program, but the speed benefits are enormous. It is critical with all of these algorithms that when error values are added to neighboring pixels, the resultant summed values must be truncated to fit within the limits of hardware. Otherwise, an area of very intense color may -cause streaks into an adjacent area of less intense color. +cause streaks into an adjacent area of less intense color. This truncation is known as "clipping," and is analogous to the audio world's concept of the same name. As in the case of an audio amplifier, @@ -856,13 +856,13 @@ clipping adds undesired noise to the data. Unlike the audio world, however, the visual clipping performed in error-diffusion halftoning is acceptable since it is not nearly so offensive as the color streaking that would occur otherwise. It is mainly for this reason that the larger filters work better --- they split the errors up more finely and produce less clipping noise. +-- they split the errors up more finely and produce less clipping noise. With all of these filters, it is also important to ensure that the sum of the distributed error values is equal to the original error value. This is most easily accomplished by subtracting each fraction, as it is calculated, from the whole error value, and using the final remainder as the last -fraction. +fraction. ===================================== @@ -880,7 +880,7 @@ constantly-varying pattern). As you might imagine, any of these methods incur a penalty in processing time. Indeed, some of the above filters (particularly the simpler ones) can be -greatly improved by skewing the weights with a little randomness [3]. +greatly improved by skewing the weights with a little randomness [3]. ===================================== @@ -888,7 +888,7 @@ Nearest available color Calculating the nearest available intensity is trivial with a monochrome image; calculating the nearest available color in a color image requires -more work. +more work. A table of RGB values of all available colors must be scanned sequentially for each input pixel to find the closest. The "distance" formula most often @@ -896,7 +896,7 @@ used is a simple pythagorean "least squares". The difference for each color is squared, and the three squares added to produce the distance value. This value is equivalent to the square of the distance between the points in RGB- space. It is not necessary to compute the square root of this value because -we are not interested in the actual distance, only in which is smallest. +we are not interested in the actual distance, only in which is smallest. The square root function is a monotonic increasing function and does not affect the order of its operands. If the total number of colors with which you are dealing is small, this part of the algorithm can be replaced by a @@ -907,7 +907,7 @@ results can be achieved by selecting colors from the image itself. You must reserve at least 8 colors for the primaries, secondaries, black, and white for best results. If you do not know the colors in your image ahead of time, or if you are going to use the same map to dither several different -images, you will have to fill your color map with a good range of colors. +images, you will have to fill your color map with a good range of colors. This can be done either by assigning a certain number of bits to each primary and computing all combinations, or by a smoother distribution as suggested by Heckbert [8]. @@ -927,7 +927,7 @@ the "raw" scan is then already in a 1- or 2-bit/pixel format. While this feature would probably be unsuitable for cases where the image would need further processing (see the "Loss of image information" section below), it is very useful where the operator wants to generate a final image, ready for -printing or displaying, with little or no subsequent processing. +printing or displaying, with little or no subsequent processing. As an example, the Epson ES-300C color scanner (and its European equivalent, the Epson GT-6000) offers three internal halftone modes. One is a standard @@ -953,12 +953,12 @@ needs to be rendered on a bilevel display device. In this situation, one would almost never want to store the dithered image. On the other hand, when color images are dithered for display on color -displays with a lower color resolution, the dithered images are more useful. +displays with a lower color resolution, the dithered images are more useful. In fact, the bulk of today's scanned-image GIF files which abound on electronic BBSs and information services are 8-bit (256 color), colormapped and dithered files created from 24-bit true-color scans. Only rarely are the 24-bit files exchanged, because of the huge amount of data contained in -them. +them. In some cases, these mapped GIF files may be further processed with special paint/processing utilities, with very respectable results. However, the @@ -1214,10 +1214,10 @@ Bibliography [2] Bayer, B.E., "An Optimum Method for Two-Level Rendition of Continuous Tone Pictures," IEEE International Conference on Communications, - Conference Records, 1973, pp. 26-11 to 26-15. + Conference Records, 1973, pp. 26-11 to 26-15. A short article proving the optimality of Bayer's pattern in the - dispersed-dot ordered dither. + dispersed-dot ordered dither. [3] Ulichney, R., Digital Halftoning, The MIT Press, Cambridge, MA, 1987. @@ -1226,7 +1226,7 @@ Bibliography higher math may come in handy) and wonderful illustrations. It does not contain any code, but don't let that keep you from getting this book. Computer Literacy normally carries it but the - title is often sold out. + title is often sold out. [MFM note: I can't describe how much information I got from this book! Several different writers have praised this reference to @@ -1242,8 +1242,8 @@ Bibliography Scale." SID 1975, International Symposium Digest of Technical Papers, vol 1975m, pp. 36-37. - Short article in which Floyd and Steinberg introduce their filter. - + Short article in which Floyd and Steinberg introduce their filter. + [5] Daniel Burkes is unpublished, but can be reached at this address: Daniel Burkes @@ -1266,7 +1266,7 @@ Bibliography for bilevel image hardcopy reproduction." Research Report RZ1060, IBM Research Laboratory, Zurich, Switzerland, 1981. -[8] Heckbert, P. "Color Image Quantization for Frame Buffer Display." +[8] Heckbert, P. "Color Image Quantization for Frame Buffer Display." Computer Graphics (SIGGRAPH 82), vol. 16, pp. 297-307, 1982. [9] Frankie Sierra is unpublished, but can be reached via CIS at UID# @@ -1317,13 +1317,13 @@ York, 1985. Rogers, D.F. and J. A. Adams, Mathematical Elements for Computer Graphics, McGraw-Hill, New York, 1976. - A good detailed discussion of producing graphic images on a computer. + A good detailed discussion of producing graphic images on a computer. Plenty of sample code. Kuto, S., "Continuous Color Presentation Using a Low-Cost Ink Jet Printer," Proc. Computer Graphics Tokyo 84, 24-27 April, 1984, Tokyo, Japan. -Mitchell, W.J., R.S. Liggett, and T. Kvan, The Art of Computer Graphics +Mitchell, W.J., R.S. Liggett, and T. Kvan, The Art of Computer Graphics Programming, Van Nostrand Reinhold Co., New York, 1987. Pavlidis, T., Algorithms for Graphics and Image Processing, Computer Science diff --git a/src/ImageSharp/Dithering/DITHER.TXT b/src/ImageSharp/Dithering/DITHER.TXT index 4d29a533e..1f49fd6eb 100644 --- a/src/ImageSharp/Dithering/DITHER.TXT +++ b/src/ImageSharp/Dithering/DITHER.TXT @@ -1,28 +1,28 @@ DITHER.TXT -What follows is everything you ever wanted to know (for the time being) about -dithering. I'm sure it will be out of date as soon as it is released, but it -does serve to collect data from a wide variety of sources into a single -document, and should save you considerable searching time. - -Numbers in brackets (like this [0]) are references. A list of these works -appears at the end of this document. - -Because this document describes ideas and algorithms which are constantly -changing, I expect that it may have many editions, additions, and corrections -before it gets to you. I will list my name below as original author, but I -do not wish to deter others from adding their own thoughts and discoveries. -This is not copyrighted in any way, and was created solely for the purpose of -organizing my own knowledge on the subject, and sharing this with others. -Please distribute it to anyonw who might be interested. - -If you add anything to this document, please feel free to include your name -below as a contributor or as a reference. I would particularly like to see -additions to the "Other books of interest" section. Please keep the text in -this simple format: no margins, no pagination, no lines longer that 79 -characters, and no non-ASCII or non-printing characters other than a CR/LF -pair at the end of each line. It is intended that this be read on as many -different machines as possible. +What follows is everything you ever wanted to know (for the time being) about +dithering. I'm sure it will be out of date as soon as it is released, but it +does serve to collect data from a wide variety of sources into a single +document, and should save you considerable searching time. + +Numbers in brackets (like this [0]) are references. A list of these works +appears at the end of this document. + +Because this document describes ideas and algorithms which are constantly +changing, I expect that it may have many editions, additions, and corrections +before it gets to you. I will list my name below as original author, but I +do not wish to deter others from adding their own thoughts and discoveries. +This is not copyrighted in any way, and was created solely for the purpose of +organizing my own knowledge on the subject, and sharing this with others. +Please distribute it to anyonw who might be interested. + +If you add anything to this document, please feel free to include your name +below as a contributor or as a reference. I would particularly like to see +additions to the "Other books of interest" section. Please keep the text in +this simple format: no margins, no pagination, no lines longer that 79 +characters, and no non-ASCII or non-printing characters other than a CR/LF +pair at the end of each line. It is intended that this be read on as many +different machines as possible. Original Author: @@ -35,37 +35,37 @@ Contributors: ======================================================================== What is Dithering? -Dithering, also called Halftoning or Color Reduction, is the process of -rendering an image on a display device with fewer colors than are in the -image. The number of different colors in an image or on a device I will call -its Color Resolution. The term "resolution" means "fineness" and is used to -describe the level of detail in a digitally sampled signal. It is used most -often in referring to the Spatial Resolution, which is the basic sampling -rate for a digitized image. - -Spatial resolution describes the fineness of the "dots" used in an image. -Color resolution describes the fineness of detail available at each dot. The -higher the resolution of a digital sample, the better it can reproduce high -frequency detail. A compact disc, for example, has a temporal (time) -resolution of 44,000 samples per second, and a dynamic (volume) resolution of -16 bits (0..65535). It can therefore reproduce sounds with a vast dynamic -range (from barely audible to ear-splitting) with great detail, but it has -problems with very high-frequency sounds, like violins and piccolos. - -It is often possible to "trade" one kind of resolution for another. If your -display device has a higher spatial resolution than the image you are trying -to reproduce, it can show a very good image even if its color resolution is -less. This is what we will call "dithering" and is the subject of this -paper. The other tradeoff, i.e., trading color resolution for spatial -resolution, is called "anti-aliasing" and is not discussed here. - -It is important to emphasize here that dithering is a one-way operation. -Once an image has been dithered, although it may look like a good -reproduction of the original, information is permanently lost. Many image -processing functions fail on dithered images. For these reasons, dithering -must be considered only as a way to produce an image on hardware that would -otherwise be incapable of displaying it. The data representing an image -should always be kept in full detail. +Dithering, also called Halftoning or Color Reduction, is the process of +rendering an image on a display device with fewer colors than are in the +image. The number of different colors in an image or on a device I will call +its Color Resolution. The term "resolution" means "fineness" and is used to +describe the level of detail in a digitally sampled signal. It is used most +often in referring to the Spatial Resolution, which is the basic sampling +rate for a digitized image. + +Spatial resolution describes the fineness of the "dots" used in an image. +Color resolution describes the fineness of detail available at each dot. The +higher the resolution of a digital sample, the better it can reproduce high +frequency detail. A compact disc, for example, has a temporal (time) +resolution of 44,000 samples per second, and a dynamic (volume) resolution of +16 bits (0..65535). It can therefore reproduce sounds with a vast dynamic +range (from barely audible to ear-splitting) with great detail, but it has +problems with very high-frequency sounds, like violins and piccolos. + +It is often possible to "trade" one kind of resolution for another. If your +display device has a higher spatial resolution than the image you are trying +to reproduce, it can show a very good image even if its color resolution is +less. This is what we will call "dithering" and is the subject of this +paper. The other tradeoff, i.e., trading color resolution for spatial +resolution, is called "anti-aliasing" and is not discussed here. + +It is important to emphasize here that dithering is a one-way operation. +Once an image has been dithered, although it may look like a good +reproduction of the original, information is permanently lost. Many image +processing functions fail on dithered images. For these reasons, dithering +must be considered only as a way to produce an image on hardware that would +otherwise be incapable of displaying it. The data representing an image +should always be kept in full detail. ======================================================================== @@ -78,84 +78,84 @@ The classes of dithering algorithms we will discuss here are these: 3. Ordered 4. Error dispersion -Each of these methods is generally better than those listed before it, but -other considerations such as processing time, memory constraints, etc. may -weigh in favor of one of the simpler methods. +Each of these methods is generally better than those listed before it, but +other considerations such as processing time, memory constraints, etc. may +weigh in favor of one of the simpler methods. -For the following discussions I will assume that we are given an image with -256 shades of gray (0=black..255=white) that we are trying to reproduce on a -black and white ouput device. Most of these methods can be extended in -obvious ways to deal with displays that have more than two levels but fewer -than the image, or to color images. Where such extension is not obvious, or -where better results can be obtained, I will go into more detail. +For the following discussions I will assume that we are given an image with +256 shades of gray (0=black..255=white) that we are trying to reproduce on a +black and white ouput device. Most of these methods can be extended in +obvious ways to deal with displays that have more than two levels but fewer +than the image, or to color images. Where such extension is not obvious, or +where better results can be obtained, I will go into more detail. -To convert any of the first three methods into color, simply apply the -algorithm separately for each primary and mix the resulting values. This -assumes that you have at least eight output colors: black, red, green, blue, -cyan, magenta, yellow, and white. Though this will work for error dispersion -as well, there are better methods in this case. +To convert any of the first three methods into color, simply apply the +algorithm separately for each primary and mix the resulting values. This +assumes that you have at least eight output colors: black, red, green, blue, +cyan, magenta, yellow, and white. Though this will work for error dispersion +as well, there are better methods in this case. ======================================================================== Random dither -This is the bubblesort of dithering algorithms. It is not really acceptable -as a production method, but it is very simple to describe and implement. For -each value in the image, simply generate a random number 1..256; if it is -geater than the image value at that point, plot the point white, otherwise -plot it black. That's it. This generates a picture with a lot of "white -noise", which looks like TV picture "snow". Though the image produced is -very inaccurate and noisy, it is free from "artifacts" which are phenomena -produced by digital signal processing. +This is the bubblesort of dithering algorithms. It is not really acceptable +as a production method, but it is very simple to describe and implement. For +each value in the image, simply generate a random number 1..256; if it is +geater than the image value at that point, plot the point white, otherwise +plot it black. That's it. This generates a picture with a lot of "white +noise", which looks like TV picture "snow". Though the image produced is +very inaccurate and noisy, it is free from "artifacts" which are phenomena +produced by digital signal processing. The most common type of artifact is the Moire pattern (Contributors: please resist the urge to put an accent on the "e", as no portable character set -exists for this). If you draw several lines close together radiating from a +exists for this). If you draw several lines close together radiating from a single point on a computer display, you will see what appear to be flower- -like patterns. These patterns are not part of the original idea of lines, -but are an illusion produced by the jaggedness of the display. +like patterns. These patterns are not part of the original idea of lines, +but are an illusion produced by the jaggedness of the display. -Many techniques exist for the reduction of digital artifacts like these, most -of which involve using a little randomness to "perturb" a regular algorithm a -little. Random dither obviously takes this to extreme. +Many techniques exist for the reduction of digital artifacts like these, most +of which involve using a little randomness to "perturb" a regular algorithm a +little. Random dither obviously takes this to extreme. -I should mention, of course, that unless your computer has a hardware-based -random number generator (and most don't) there may be some artifacts from the -random number generation algorithm itself. +I should mention, of course, that unless your computer has a hardware-based +random number generator (and most don't) there may be some artifacts from the +random number generation algorithm itself. -While random dither adds a lot of high-frequency noise to a picture, it is -useful in reproducing very low-frequency images where the absence of -artifacts is more important than noise. For example, a whole screen -containing a gradient of all levels from black to white would actually look -best with a random dither. In this case, ordered dithering would produce -diagonal patterns, and error dispersion would produce clustering. +While random dither adds a lot of high-frequency noise to a picture, it is +useful in reproducing very low-frequency images where the absence of +artifacts is more important than noise. For example, a whole screen +containing a gradient of all levels from black to white would actually look +best with a random dither. In this case, ordered dithering would produce +diagonal patterns, and error dispersion would produce clustering. -For efficiency, you can take the random number generator "out of the loop" by -generating a list of random numbers beforehand for use in the dither. Make -sure that the list is larger than the number of pixels in the image or you -may get artifacts from the reuse of numbers. The worst case would be if the -size of your list of random numbers is a multiple or near-multiple of the -horizontal size of the image, in which case unwanted vertical or diagonal -lines will appear. +For efficiency, you can take the random number generator "out of the loop" by +generating a list of random numbers beforehand for use in the dither. Make +sure that the list is larger than the number of pixels in the image or you +may get artifacts from the reuse of numbers. The worst case would be if the +size of your list of random numbers is a multiple or near-multiple of the +horizontal size of the image, in which case unwanted vertical or diagonal +lines will appear. ======================================================================== Pattern dither -This is also a simple concept, but much more effective than random dither. -For each possible value in the image, create a pattern of dots that -approximates that value. For instance, a 3-by-3 block of dots can have one -of 512 patterns, but for our purposes, there are only 10; the number of black -dots in the pattern determines the darkness of the pattern. +This is also a simple concept, but much more effective than random dither. +For each possible value in the image, create a pattern of dots that +approximates that value. For instance, a 3-by-3 block of dots can have one +of 512 patterns, but for our purposes, there are only 10; the number of black +dots in the pattern determines the darkness of the pattern. Which 10 patterns do we choose? Obviously, we need the all-white and all- -black patterns. We can eliminate those patterns which would create vertical -or horizontal lines if repeated over a large area because many images have -such regions of similar value [1]. It has been shown [1] that patterns for -adjacent colors should be similar to reduce an artifact called "contouring", -or visible edges between regions of adjacent values. One easy way to assure -this is to make each pattern a superset of the previous. Here are two good -sets of patterns for a 3-by-3 matrix: +black patterns. We can eliminate those patterns which would create vertical +or horizontal lines if repeated over a large area because many images have +such regions of similar value [1]. It has been shown [1] that patterns for +adjacent colors should be similar to reduce an artifact called "contouring", +or visible edges between regions of adjacent values. One easy way to assure +this is to make each pattern a superset of the previous. Here are two good +sets of patterns for a 3-by-3 matrix: --- --- --- -X- -XX -XX -XX -XX XXX XXX --- -X- -XX -XX -XX -XX XXX XXX XXX XXX @@ -165,102 +165,102 @@ or --- --- --- --X --X X-X X-X X-X XXX XXX --- --- -X- -X- -X- -X- XX- XX- XX- XXX -The first set of patterns above are "clustered" in that as new dots are added -to each pattern, they are added next to dots already there. The second set -is "dispersed" as the dots are spread out more. This distinction is more -important on larger patterns. Dispersed-dot patterns produce less grainy -images, but require that the output device render each dot distinctly. When -this is not the case, as with a printing press which smears the dots a -little, clustered patterns are better. - -For each pixel in the image we now print the pattern which is closest to its -value. This will triple the size of the image in each direction, so this -method can only be used where the display spatial resolution is much greater -than that of the image. - -We can exploit the fact that most images have large areas of similar value to -reduce our need for extra spatial resolution. Instead of plotting a whole -pattern for each pixel, map each pixel in the image to a dot in the pattern -an only plot the corresponding dot for each pixel. - -The simplest way to do this is to map the X and Y coordinates of each pixel -into the dot (X mod 3, Y mod 3) in the pattern. Large areas of constant -value will come out as repetitions of the pattern as before. - -To extend this method to color images, we must use patterns of colored dots -to represent shades not directly printable by the hardware. For example, if -your hardware is capable of printing only red, green, blue, and black (the -minimal case for color dithering), other colors can be represented with -patterns of these four: +The first set of patterns above are "clustered" in that as new dots are added +to each pattern, they are added next to dots already there. The second set +is "dispersed" as the dots are spread out more. This distinction is more +important on larger patterns. Dispersed-dot patterns produce less grainy +images, but require that the output device render each dot distinctly. When +this is not the case, as with a printing press which smears the dots a +little, clustered patterns are better. + +For each pixel in the image we now print the pattern which is closest to its +value. This will triple the size of the image in each direction, so this +method can only be used where the display spatial resolution is much greater +than that of the image. + +We can exploit the fact that most images have large areas of similar value to +reduce our need for extra spatial resolution. Instead of plotting a whole +pattern for each pixel, map each pixel in the image to a dot in the pattern +an only plot the corresponding dot for each pixel. + +The simplest way to do this is to map the X and Y coordinates of each pixel +into the dot (X mod 3, Y mod 3) in the pattern. Large areas of constant +value will come out as repetitions of the pattern as before. + +To extend this method to color images, we must use patterns of colored dots +to represent shades not directly printable by the hardware. For example, if +your hardware is capable of printing only red, green, blue, and black (the +minimal case for color dithering), other colors can be represented with +patterns of these four: Yellow = R G Cyan = G B Magenta = R B Gray = R G G R B G B R B K -(B here represents blue, K is black). There are a total of 31 such distinct -patterns which can be used; I will leave their enumeration "as an exercise -for the reader" (don't you hate books that do that?). +(B here represents blue, K is black). There are a total of 31 such distinct +patterns which can be used; I will leave their enumeration "as an exercise +for the reader" (don't you hate books that do that?). ======================================================================== Ordered dither -Because each of the patterns above is a superset of the previous, we can -express the patterns in compact form as the order of dots added: +Because each of the patterns above is a superset of the previous, we can +express the patterns in compact form as the order of dots added: 8 3 4 and 1 7 4 6 1 2 5 8 3 7 5 9 6 2 9 -Then we can simply use the value in the array as a threshhold. If the value -of the pixel (scaled into the 0-9 range) is less than the number in the -corresponding cell of the matrix, plot that pixel black, otherwise, plot it -white. This process is called ordered dither. As before, clustered patterns -should be used for devices which blur dots. In fact, the clustered pattern -ordered dither is the process used by most newspapers, and the term -halftoning refers to this method if not otherwise qualified. +Then we can simply use the value in the array as a threshhold. If the value +of the pixel (scaled into the 0-9 range) is less than the number in the +corresponding cell of the matrix, plot that pixel black, otherwise, plot it +white. This process is called ordered dither. As before, clustered patterns +should be used for devices which blur dots. In fact, the clustered pattern +ordered dither is the process used by most newspapers, and the term +halftoning refers to this method if not otherwise qualified. -Bayer [2] has shown that for matrices of orders which are powers of two there -is an optimal pattern of dispersed dots which results in the pattern noise -being as high-frequency as possible. The pattern for a 2x2 and 4x4 matrices -are as follows: +Bayer [2] has shown that for matrices of orders which are powers of two there +is an optimal pattern of dispersed dots which results in the pattern noise +being as high-frequency as possible. The pattern for a 2x2 and 4x4 matrices +are as follows: 1 3 1 9 3 11 These patterns (and their rotations 4 2 13 5 15 7 and reflections) are optimal for a 4 12 2 10 dispersed-pattern ordered dither. 16 8 14 6 -Ulichney [3] shows a recursive technique can be used to generate the larger -patterns. To fully reproduce our 256-level image, we would need to use the -8x8 pattern. +Ulichney [3] shows a recursive technique can be used to generate the larger +patterns. To fully reproduce our 256-level image, we would need to use the +8x8 pattern. Bayer's method is in very common use and is easily identified by the cross- -hatch pattern artifacts it produces in the resulting display. This -artifacting is the major drawback of the technique wich is otherwise very -fast and powerful. Ordered dithering also performs very badly on images -which have already been dithered to some extent. As stated earlier, -dithering should be the last stage in producing a physical display from a -digitally stored image. The dithered image should never be stored itself. +hatch pattern artifacts it produces in the resulting display. This +artifacting is the major drawback of the technique wich is otherwise very +fast and powerful. Ordered dithering also performs very badly on images +which have already been dithered to some extent. As stated earlier, +dithering should be the last stage in producing a physical display from a +digitally stored image. The dithered image should never be stored itself. ======================================================================== Error dispersion -This technique generates the best results of any method here, and is -naturally the slowest. In fact, there are many variants of this technique as -well, and the better they get, the slower they are. +This technique generates the best results of any method here, and is +naturally the slowest. In fact, there are many variants of this technique as +well, and the better they get, the slower they are. -Error dispersion is very simple to describe: for each point in the image, -first find the closest color available. Calculate the difference between the -value in the image and the color you have. Now divide up these error values -and distribute them over the neighboring pixels which you have not visited -yet. When you get to these later pixels, just add the errors distributed -from the earlier ones, clip the values to the allowed range if needed, then -continue as above. +Error dispersion is very simple to describe: for each point in the image, +first find the closest color available. Calculate the difference between the +value in the image and the color you have. Now divide up these error values +and distribute them over the neighboring pixels which you have not visited +yet. When you get to these later pixels, just add the errors distributed +from the earlier ones, clip the values to the allowed range if needed, then +continue as above. -If you are dithering a grayscale image for output to a black-and-white -device, the "find closest color" is just a simle threshholding operation. In -color, it involves matching the input color to the closest available hardware -color, which can be difficult depending on the hardware palette. +If you are dithering a grayscale image for output to a black-and-white +device, the "find closest color" is just a simle threshholding operation. In +color, it involves matching the input color to the closest available hardware +color, which can be difficult depending on the hardware palette. There are many ways to distribute the errors and many ways to scan the image, but I will deal here with only a few. The two basic ways to scan the @@ -269,22 +269,22 @@ alternating left-to-right then right-to-left raster. The latter method generally produces fewer artifacts and can be used with all the error diffusion patterns discussed below. -The different ways of dividing up the error can be expressed as patterns +The different ways of dividing up the error can be expressed as patterns (called filters, for reasons too boring to go into here). X 7 This is the Floyd and Steinberg [4] 3 5 1 error diffusion filter. In this filter, the X represents the pixel you are currently scanning, and -the numbers (called weights, for equally boring reasons) represent the -proportion of the error distributed to the pixel in that position. Here, the -pixel immediately to the right gets 7/16 of the error (the divisor is 16 -because the weights add to 16), the pixel directly below gets 5/16 of the -error, and the diagonally adjacent pixels get 3/16 and 1/16. When scanning a -line right-to-left, this pattern is reversed. This pattern was chosen -carefully so that it would produce a checkerboard pattern in areas with -intensity of 1/2 (or 128 in our image). It is also fairly easy to calculate -when the division by 16 is replaced by shifts. +the numbers (called weights, for equally boring reasons) represent the +proportion of the error distributed to the pixel in that position. Here, the +pixel immediately to the right gets 7/16 of the error (the divisor is 16 +because the weights add to 16), the pixel directly below gets 5/16 of the +error, and the diagonally adjacent pixels get 3/16 and 1/16. When scanning a +line right-to-left, this pattern is reversed. This pattern was chosen +carefully so that it would produce a checkerboard pattern in areas with +intensity of 1/2 (or 128 in our image). It is also fairly easy to calculate +when the division by 16 is replaced by shifts. Another filter in common use, but not recommended: @@ -306,24 +306,24 @@ the bottom row removed. The main improvement is that the divisor is now 32, which makes calculating the errors faster, and the removal of one row reduces the memory requirements of the method. -This is also fairly easy to calculate and produces better results than Floyd -and Steinberg. Jarvis, Judice, and Ninke [6] use the following: +This is also fairly easy to calculate and produces better results than Floyd +and Steinberg. Jarvis, Judice, and Ninke [6] use the following: X 7 5 The Jarvis, et al. pattern. 3 5 7 5 3 1 3 5 3 1 -The divisor here is 48, which is a little more expensive to calculate, and -the errors are distributed over three lines, requiring extra memory and time -for processing. Probably the best filter is from Stucki [7]: +The divisor here is 48, which is a little more expensive to calculate, and +the errors are distributed over three lines, requiring extra memory and time +for processing. Probably the best filter is from Stucki [7]: X 8 4 The Stucki pattern. 2 4 8 4 2 1 2 4 2 1 -This one takes a division by 42 for each pixel and is therefore slow if math -is done inside the loop. After the initial 8/42 is calculated, some time can -be saved by producing the remaining fractions by shifts. +This one takes a division by 42 for each pixel and is therefore slow if math +is done inside the loop. After the initial 8/42 is calculated, some time can +be saved by producing the remaining fractions by shifts. The speed advantages of the simpler filters can be eliminated somewhat by performing the divisions beforehand and using lookup tables instead of per- @@ -334,52 +334,52 @@ It is critical with all of these algorithms that when error values are added to neighboring pixels, the values must be truncated to fit within the limits of hardware, otherwise and area of very intense color may cause streaks into an adjacent area of less intense color. This truncation adds noise to the -image anagous to clipping in an audio amplifier, but it is not nearly so -offensive as the streaking. It is mainly for this reason that the larger -filters work better--they split the errors up more finely and produce less of -this clipping noise. +image anagous to clipping in an audio amplifier, but it is not nearly so +offensive as the streaking. It is mainly for this reason that the larger +filters work better--they split the errors up more finely and produce less of +this clipping noise. With all of these filters, it is also important to ensure that the errors -you distribute properly add to the original error value. This is easiest to -accomplish by subtracting each fraction from the whole error as it is -calculated, and using the final remainder as the last fraction. - -Some of these methods (particularly the simpler ones) can be greatly improved -by skewing the weights with a little randomness [3]. - -Calculating the "nearest available color" is trivial with a monochrome image; -with color images it requires more work. A table of RGB values of all -available colors must be scanned sequentially for each input pixel to find -the closest. The "distance" formula most often used is a simple pythagorean -"least squares". The difference for each color is squared, and the three -squares added to produce the distance value. This value is equivalent to the -square of the distance between the points in RGB-space. It is not necessary -to compute the square root of this value because we are not interested in the -actual distance, only in which is smallest. The square root function is a -monotonic increasing function and does not affect the order of its operands. -If the total number of colors with which you are dealing is small, this part +you distribute properly add to the original error value. This is easiest to +accomplish by subtracting each fraction from the whole error as it is +calculated, and using the final remainder as the last fraction. + +Some of these methods (particularly the simpler ones) can be greatly improved +by skewing the weights with a little randomness [3]. + +Calculating the "nearest available color" is trivial with a monochrome image; +with color images it requires more work. A table of RGB values of all +available colors must be scanned sequentially for each input pixel to find +the closest. The "distance" formula most often used is a simple pythagorean +"least squares". The difference for each color is squared, and the three +squares added to produce the distance value. This value is equivalent to the +square of the distance between the points in RGB-space. It is not necessary +to compute the square root of this value because we are not interested in the +actual distance, only in which is smallest. The square root function is a +monotonic increasing function and does not affect the order of its operands. +If the total number of colors with which you are dealing is small, this part of the algorithm can be replaced by a lookup table as well. -When your hardware allows you to select the available colors, very good -results can be achieved by selecting colors from the image itself. You must -reserve at least 8 colors for the primaries, secondaries, black, and white -for best results. If you do not know the colors in your image ahead of time, -or if you are going to use the same map to dither several different images, -you will have to fill your color map with a good range of colors. This can -be done either by assigning a certain number of bits to each primary and -computing all combinations, or by a smoother distribution as suggested by -Heckbert [8]. +When your hardware allows you to select the available colors, very good +results can be achieved by selecting colors from the image itself. You must +reserve at least 8 colors for the primaries, secondaries, black, and white +for best results. If you do not know the colors in your image ahead of time, +or if you are going to use the same map to dither several different images, +you will have to fill your color map with a good range of colors. This can +be done either by assigning a certain number of bits to each primary and +computing all combinations, or by a smoother distribution as suggested by +Heckbert [8]. ======================================================================== Sample code -Despite my best efforts in expository writing, nothing explains an algorithm -better than real code. With that in mind, presented here below is an -algorithm (in somewhat incomplete, very inefficient pseudo-C) which -implements error diffusion dithering with the Floyd and Steinberg filter. It -is not efficiently coded, but its purpose is to show the method, which I -believe it does. +Despite my best efforts in expository writing, nothing explains an algorithm +better than real code. With that in mind, presented here below is an +algorithm (in somewhat incomplete, very inefficient pseudo-C) which +implements error diffusion dithering with the Floyd and Steinberg filter. It +is not efficiently coded, but its purpose is to show the method, which I +believe it does. /* Floyd/Steinberg error diffusion dithering algorithm in color. The array ** line[][] contains the RGB values for the current line being processed; @@ -466,34 +466,34 @@ dither() Bibliography [1] Foley, J. D. and Andries Van Dam (1982) - Fundamentals of Interactive Computer Graphics. Reading, MA: Addisson - Wesley. + Fundamentals of Interactive Computer Graphics. Reading, MA: Addisson + Wesley. - This is a standard reference for many graphic techniques which has not - declined with age. Highly recommended. + This is a standard reference for many graphic techniques which has not + declined with age. Highly recommended. [2] Bayer, B. E. (1973) - "An Optimum Method for Two-Level Rendition of Continuous Tone Pictures," - IEEE International Conference on Communications, Conference Records, pp. - 26-11 to 26-15. + "An Optimum Method for Two-Level Rendition of Continuous Tone Pictures," + IEEE International Conference on Communications, Conference Records, pp. + 26-11 to 26-15. - A short article proving the optimality of Bayer's pattern in the - dispersed-dot ordered dither. + A short article proving the optimality of Bayer's pattern in the + dispersed-dot ordered dither. [3] Ulichney, R. (1987) Digital Halftoning. Cambridge, MA: The MIT Press. - This is the best book I know of for describing the various black and - white dithering methods. It has clear explanations (a little higher math - may come in handy) and wonderful illustrations. It does not contain any - code, but don't let that keep you from getting this book. Computer - Literacy carries it but is often sold out. + This is the best book I know of for describing the various black and + white dithering methods. It has clear explanations (a little higher math + may come in handy) and wonderful illustrations. It does not contain any + code, but don't let that keep you from getting this book. Computer + Literacy carries it but is often sold out. [4] Floyd, R.W. and L. Steinberg (1975) - "An Adaptive Algorithm for Spatial Gray Scale." SID International - Symposium Digest of Technical Papers, vol 1975m, pp. 36-37. + "An Adaptive Algorithm for Spatial Gray Scale." SID International + Symposium Digest of Technical Papers, vol 1975m, pp. 36-37. - Short article in which Floyd and Steinberg introduce their filter. + Short article in which Floyd and Steinberg introduce their filter. [5] Daniel Burkes is unpublished, but can be reached at this address: @@ -502,21 +502,21 @@ Bibliography 2351 College Station Road Suite 563 Athens, GA 30305 - or via CompuServe's Graphics Support Forum, ID # 72077,356. + or via CompuServe's Graphics Support Forum, ID # 72077,356. [6] Jarvis, J. F., C. N. Judice, and W. H. Ninke (1976) - "A Survey of Techniques for the Display of Continuous Tone Pictures on - Bi-Level Displays." Computer Graphics and Image Processing, vol. 5, pp. - 13-40. + "A Survey of Techniques for the Display of Continuous Tone Pictures on + Bi-Level Displays." Computer Graphics and Image Processing, vol. 5, pp. + 13-40. [7] Stucki, P. (1981) - "MECCA - a multiple-error correcting computation algorithm for bilevel - image hardcopy reproduction." Research Report RZ1060, IBM Research - Laboratory, Zurich, Switzerland. + "MECCA - a multiple-error correcting computation algorithm for bilevel + image hardcopy reproduction." Research Report RZ1060, IBM Research + Laboratory, Zurich, Switzerland. [8] Heckbert, Paul (9182) - "Color Image Quantization for Frame Buffer Display." Computer Graphics - (SIGGRAPH 82), vol. 16, pp. 297-307. + "Color Image Quantization for Frame Buffer Display." Computer Graphics + (SIGGRAPH 82), vol. 16, pp. 297-307. ======================================================================== @@ -536,12 +536,12 @@ Mathematical Elements for Computer Graphics. New York: McGraw-Hill. ======================================================================== About CompuServe Graphics Support Forum: -CompuServe Information Service is a service of the H&R Block companies -providing computer users with electronic mail, teleconferencing, and many -other telecommunications services. Call 800-848-8199 for more information. +CompuServe Information Service is a service of the H&R Block companies +providing computer users with electronic mail, teleconferencing, and many +other telecommunications services. Call 800-848-8199 for more information. -The Graphics Support Forum is dedicated to helping its users get the most out -of their computers' graphics capabilities. It has a small staff and a large -number of "Developers" who create images and software on all types of -machines from Apple IIs to Sun workstations. While on CompuServe, type GO -PICS from any "!" prompt to gain access to the forum. \ No newline at end of file +The Graphics Support Forum is dedicated to helping its users get the most out +of their computers' graphics capabilities. It has a small staff and a large +number of "Developers" who create images and software on all types of +machines from Apple IIs to Sun workstations. While on CompuServe, type GO +PICS from any "!" prompt to gain access to the forum. \ No newline at end of file From 972e345ab83e9930d0203c00f0d6d45abe3a21b2 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 14 Feb 2017 09:53:12 +1100 Subject: [PATCH 058/142] Ignore chunk length of 1. Fix #103 TODO: Read libpng for matching behaviour --- src/ImageSharp.Formats.Png/PngDecoderCore.cs | 4 ++-- tests/ImageSharp.Tests/FileTestBase.cs | 14 +++++++------- tests/ImageSharp.Tests/TestImages.cs | 3 +++ .../TestImages/Formats/Png/chunklength1.png | 3 +++ 4 files changed, 15 insertions(+), 9 deletions(-) create mode 100644 tests/ImageSharp.Tests/TestImages/Formats/Png/chunklength1.png diff --git a/src/ImageSharp.Formats.Png/PngDecoderCore.cs b/src/ImageSharp.Formats.Png/PngDecoderCore.cs index 3eaa8fde3..9e871e987 100644 --- a/src/ImageSharp.Formats.Png/PngDecoderCore.cs +++ b/src/ImageSharp.Formats.Png/PngDecoderCore.cs @@ -927,12 +927,12 @@ namespace ImageSharp.Formats private void ReadChunkLength(PngChunk chunk) { int numBytes = this.currentStream.Read(this.chunkLengthBuffer, 0, 4); - if (numBytes >= 1 && numBytes <= 3) + if (numBytes > 1 && numBytes <= 3) { throw new ImageFormatException("Image stream is not valid!"); } - if (numBytes <= 0) + if (numBytes <= 1) { chunk.Length = -1; return; diff --git a/tests/ImageSharp.Tests/FileTestBase.cs b/tests/ImageSharp.Tests/FileTestBase.cs index f7f4386aa..b147e97e8 100644 --- a/tests/ImageSharp.Tests/FileTestBase.cs +++ b/tests/ImageSharp.Tests/FileTestBase.cs @@ -6,7 +6,6 @@ namespace ImageSharp.Tests { using System.Collections.Generic; - using ImageSharp.Formats; /// /// The test base class for reading and writing to files. @@ -18,8 +17,8 @@ namespace ImageSharp.Tests /// protected static readonly List Files = new List { - TestFile.Create(TestImages.Jpeg.Baseline.Calliphora), - TestFile.Create(TestImages.Jpeg.Baseline.Turtle), + TestFile.Create(TestImages.Jpeg.Baseline.Calliphora), + // TestFile.Create(TestImages.Jpeg.Baseline.Turtle), // Perf: Enable for local testing only // TestFile.Create(TestImages.Jpeg.Baseline.Ycck), // Perf: Enable for local testing only // TestFile.Create(TestImages.Jpeg.Baseline.Cmyk), // Perf: Enable for local testing only // TestFile.Create(TestImages.Jpeg.Baseline.Floorplan), // Perf: Enable for local testing only @@ -28,10 +27,11 @@ namespace ImageSharp.Tests // TestFile.Create(TestImages.Jpeg.Progressive.Progress), // Perf: Enable for local testing only // TestFile.Create(TestImages.Jpeg.Baseline.GammaDalaiLamaGray), // Perf: Enable for local testing only // TestFile.Create(TestImages.Jpeg.Progressive.Bad.BadEOF), // Perf: Enable for local testing only - TestFile.Create(TestImages.Bmp.Car), + TestFile.Create(TestImages.Bmp.Car), // TestFile.Create(TestImages.Bmp.Neg_height), // Perf: Enable for local testing only - TestFile.Create(TestImages.Png.Splash), - TestFile.Create(TestImages.Png.Powerpoint), + TestFile.Create(TestImages.Png.Splash), + // TestFile.Create(TestImages.Png.ChunkLength), // Perf: Enable for local testing only + // TestFile.Create(TestImages.Png.Powerpoint), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.Blur), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.Indexed), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.SplashInterlaced), // Perf: Enable for local testing only @@ -44,7 +44,7 @@ namespace ImageSharp.Tests // TestFile.Create(TestImages.Png.FilterVar), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.P1), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.Pd), // Perf: Enable for local testing only - TestFile.Create(TestImages.Gif.Rings), // Perf: Enable for local testing only + TestFile.Create(TestImages.Gif.Rings), // TestFile.Create(TestImages.Gif.Cheers), // Perf: Enable for local testing only // TestFile.Create(TestImages.Gif.Giphy) // Perf: Enable for local testing only }; diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index cd365156a..4924cc1de 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -34,6 +34,9 @@ namespace ImageSharp.Tests // filter changing per scanline public const string FilterVar = "Png/filterVar.png"; + + // Chunk length of 1 by end marker + public const string ChunkLength = "Png/chunklength1.png"; } public static class Jpeg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/chunklength1.png b/tests/ImageSharp.Tests/TestImages/Formats/Png/chunklength1.png new file mode 100644 index 000000000..c6cf867b2 --- /dev/null +++ b/tests/ImageSharp.Tests/TestImages/Formats/Png/chunklength1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7b9957a86738acc0b682618a6677c8d659614cd6be5728e85185aef314c21981 +size 149591 From 203fe3cfd5464f978505d09619f9a57a0275f7c9 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Tue, 14 Feb 2017 08:40:04 +0000 Subject: [PATCH 059/142] change NO_FILE_IO to NETSTANDARD11 --- src/ImageSharp/Image.cs | 2 +- src/ImageSharp/Image/Image{TColor}.cs | 4 ++-- src/ImageSharp/project.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index 4da9cac36..284a7c29b 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -44,7 +44,7 @@ namespace ImageSharp { } -#if !NO_FILE_IO +#if !NETSTANDARD11 /// /// Initializes a new instance of the class. /// diff --git a/src/ImageSharp/Image/Image{TColor}.cs b/src/ImageSharp/Image/Image{TColor}.cs index 75d855ad4..0931a12da 100644 --- a/src/ImageSharp/Image/Image{TColor}.cs +++ b/src/ImageSharp/Image/Image{TColor}.cs @@ -63,7 +63,7 @@ namespace ImageSharp this.Load(stream); } -#if !NO_FILE_IO +#if !NETSTANDARD11 /// /// Initializes a new instance of the class. /// @@ -245,7 +245,7 @@ namespace ImageSharp return this; } -#if !NO_FILE_IO +#if !NETSTANDARD11 /// /// Saves the image to the given stream using the currently loaded image format. /// diff --git a/src/ImageSharp/project.json b/src/ImageSharp/project.json index a5d1bb93f..7b2dbed08 100644 --- a/src/ImageSharp/project.json +++ b/src/ImageSharp/project.json @@ -69,7 +69,7 @@ }, "netstandard1.1": { "buildOptions": { - "define": [ "NO_FILE_IO" ] + "define": [ "NETSTANDARD11" ] }, "dependencies": { "System.Collections": "4.0.11", From d56cb876159deecb93dc584d510ef36f0ee37fc2 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Tue, 14 Feb 2017 09:43:56 +0000 Subject: [PATCH 060/142] correct define tag --- src/ImageSharp/Image.cs | 2 +- src/ImageSharp/Image/Image{TColor}.cs | 4 ++-- src/ImageSharp/project.json | 3 --- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index 284a7c29b..bdb6b49a9 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -44,7 +44,7 @@ namespace ImageSharp { } -#if !NETSTANDARD11 +#if !NETSTANDARD1_1 /// /// Initializes a new instance of the class. /// diff --git a/src/ImageSharp/Image/Image{TColor}.cs b/src/ImageSharp/Image/Image{TColor}.cs index 0931a12da..33d6a6965 100644 --- a/src/ImageSharp/Image/Image{TColor}.cs +++ b/src/ImageSharp/Image/Image{TColor}.cs @@ -63,7 +63,7 @@ namespace ImageSharp this.Load(stream); } -#if !NETSTANDARD11 +#if !NETSTANDARD1_1 /// /// Initializes a new instance of the class. /// @@ -245,7 +245,7 @@ namespace ImageSharp return this; } -#if !NETSTANDARD11 +#if !NETSTANDARD1_1 /// /// Saves the image to the given stream using the currently loaded image format. /// diff --git a/src/ImageSharp/project.json b/src/ImageSharp/project.json index 7b2dbed08..639773377 100644 --- a/src/ImageSharp/project.json +++ b/src/ImageSharp/project.json @@ -68,9 +68,6 @@ } }, "netstandard1.1": { - "buildOptions": { - "define": [ "NETSTANDARD11" ] - }, "dependencies": { "System.Collections": "4.0.11", "System.Diagnostics.Debug": "4.0.11", From 531c7aa32d11c9ab65ead1cc6afc6bf23d306bce Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 15 Feb 2017 01:15:34 +1100 Subject: [PATCH 061/142] Add ordered dithering --- .../Binarization/Dither.cs | 36 +++++- .../ErrorDiffusionDitherProcessor.cs | 3 - .../Binarization/OrderedDitherProcessor.cs | 119 ++++++++++++++++++ src/ImageSharp/Colors/PackedPixel/Alpha8.cs | 2 +- .../{ => ErrorDiffusion}/Atkinson.cs | 0 .../Dithering/{ => ErrorDiffusion}/Burks.cs | 0 .../{ => ErrorDiffusion}/ErrorDiffuser.cs | 0 .../{ => ErrorDiffusion}/FloydSteinberg.cs | 0 .../{ => ErrorDiffusion}/IErrorDiffuser.cs | 0 .../{ => ErrorDiffusion}/JarvisJudiceNinke.cs | 0 .../Dithering/{ => ErrorDiffusion}/Sierra2.cs | 0 .../Dithering/{ => ErrorDiffusion}/Sierra3.cs | 0 .../{ => ErrorDiffusion}/SierraLite.cs | 0 .../Dithering/{ => ErrorDiffusion}/Stucki.cs | 0 src/ImageSharp/Dithering/Ordered/Bayer.cs | 38 ++++++ .../Dithering/Ordered/IOrderedDither.cs | 37 ++++++ src/ImageSharp/Dithering/Ordered/Ordered.cs | 38 ++++++ .../Colors/PackedPixelTests.cs | 1 + .../Processors/Filters/DitherTest.cs | 38 +++++- 19 files changed, 303 insertions(+), 9 deletions(-) create mode 100644 src/ImageSharp.Processing/Processors/Binarization/OrderedDitherProcessor.cs rename src/ImageSharp/Dithering/{ => ErrorDiffusion}/Atkinson.cs (100%) rename src/ImageSharp/Dithering/{ => ErrorDiffusion}/Burks.cs (100%) rename src/ImageSharp/Dithering/{ => ErrorDiffusion}/ErrorDiffuser.cs (100%) rename src/ImageSharp/Dithering/{ => ErrorDiffusion}/FloydSteinberg.cs (100%) rename src/ImageSharp/Dithering/{ => ErrorDiffusion}/IErrorDiffuser.cs (100%) rename src/ImageSharp/Dithering/{ => ErrorDiffusion}/JarvisJudiceNinke.cs (100%) rename src/ImageSharp/Dithering/{ => ErrorDiffusion}/Sierra2.cs (100%) rename src/ImageSharp/Dithering/{ => ErrorDiffusion}/Sierra3.cs (100%) rename src/ImageSharp/Dithering/{ => ErrorDiffusion}/SierraLite.cs (100%) rename src/ImageSharp/Dithering/{ => ErrorDiffusion}/Stucki.cs (100%) create mode 100644 src/ImageSharp/Dithering/Ordered/Bayer.cs create mode 100644 src/ImageSharp/Dithering/Ordered/IOrderedDither.cs create mode 100644 src/ImageSharp/Dithering/Ordered/Ordered.cs diff --git a/src/ImageSharp.Processing/Binarization/Dither.cs b/src/ImageSharp.Processing/Binarization/Dither.cs index f481ac4df..6a4f7f005 100644 --- a/src/ImageSharp.Processing/Binarization/Dither.cs +++ b/src/ImageSharp.Processing/Binarization/Dither.cs @@ -16,7 +16,39 @@ namespace ImageSharp public static partial class ImageExtensions { /// - /// Alters the alpha component of the image. + /// Dithers the image reducing it to two colors using ordered dithering. + /// + /// The pixel format. + /// The image this method extends. + /// The ordered ditherer. + /// The component index to test the threshold against. Must range from 0 to 3. + /// The . + public static Image Dither(this Image source, IOrderedDither dither, int index = 0) + where TColor : struct, IPackedPixel, IEquatable + { + return Dither(source, dither, source.Bounds, index); + } + + /// + /// Dithers the image reducing it to two colors using ordered dithering. + /// + /// The pixel format. + /// The image this method extends. + /// The ordered ditherer. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The component index to test the threshold against. Must range from 0 to 3. + /// The . + public static Image Dither(this Image source, IOrderedDither dither, Rectangle rectangle, int index = 0) + where TColor : struct, IPackedPixel, IEquatable + { + source.ApplyProcessor(new OrderedDitherProcessor(dither, index), rectangle); + return source; + } + + /// + /// Dithers the image reducing it to two colors using error diffusion. /// /// The pixel format. /// The image this method extends. @@ -30,7 +62,7 @@ namespace ImageSharp } /// - /// Alters the alpha component of the image. + /// Dithers the image reducing it to two colors using error diffusion. /// /// The pixel format. /// The image this method extends. diff --git a/src/ImageSharp.Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs b/src/ImageSharp.Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs index c5b78b639..6429ce6f0 100644 --- a/src/ImageSharp.Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs @@ -25,9 +25,6 @@ namespace ImageSharp.Processing.Processors { Guard.NotNull(diffuser, nameof(diffuser)); - // TODO: Check thresholding limit. Colors should probably have Max/Min/Middle properties. - Guard.MustBeBetweenOrEqualTo(threshold, 0, 1, nameof(threshold)); - this.Diffuser = diffuser; this.Threshold = threshold; diff --git a/src/ImageSharp.Processing/Processors/Binarization/OrderedDitherProcessor.cs b/src/ImageSharp.Processing/Processors/Binarization/OrderedDitherProcessor.cs new file mode 100644 index 000000000..b2a1e9a22 --- /dev/null +++ b/src/ImageSharp.Processing/Processors/Binarization/OrderedDitherProcessor.cs @@ -0,0 +1,119 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Processing.Processors +{ + using System; + using System.Buffers; + + using ImageSharp.Dithering; + + /// + /// An that dithers an image using error diffusion. + /// + /// The pixel format. + public class OrderedDitherProcessor : ImageProcessor + where TColor : struct, IPackedPixel, IEquatable + { + /// + /// Initializes a new instance of the class. + /// + /// The ordered ditherer. + /// The component index to test the threshold against. Must range from 0 to 3. + public OrderedDitherProcessor(IOrderedDither dither, int index) + { + Guard.NotNull(dither, nameof(dither)); + Guard.MustBeBetweenOrEqualTo(index, 0, 3, nameof(index)); + + // Alpha8 only stores the pixel data in the alpha channel. + if (typeof(TColor) == typeof(Alpha8)) + { + index = 3; + } + + this.Dither = dither; + this.Index = index; + + // Default to white/black for upper/lower. + TColor upper = default(TColor); + upper.PackFromBytes(255, 255, 255, 255); + this.UpperColor = upper; + + TColor lower = default(TColor); + lower.PackFromBytes(0, 0, 0, 255); + this.LowerColor = lower; + } + + /// + /// Gets the ditherer. + /// + public IOrderedDither Dither { get; } + + /// + /// Gets the component index to test the threshold against. + /// + public int Index { get; } + + /// + /// Gets or sets the color to use for pixels that are above the threshold. + /// + public TColor UpperColor { get; set; } + + /// + /// Gets or sets the color to use for pixels that fall below the threshold. + /// + public TColor LowerColor { get; set; } + + /// + protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + { + new GrayscaleBt709Processor().Apply(source, sourceRectangle); + } + + /// + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + { + int startY = sourceRectangle.Y; + int endY = sourceRectangle.Bottom; + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + + // Align start/end positions. + int minX = Math.Max(0, startX); + int maxX = Math.Min(source.Width, endX); + int minY = Math.Max(0, startY); + int maxY = Math.Min(source.Height, endY); + + // Reset offset if necessary. + if (minX > 0) + { + startX = 0; + } + + if (minY > 0) + { + startY = 0; + } + + using (PixelAccessor sourcePixels = source.Lock()) + { + for (int y = minY; y < maxY; y++) + { + int offsetY = y - startY; + byte[] bytes = ArrayPool.Shared.Rent(4); + + for (int x = minX; x < maxX; x++) + { + int offsetX = x - startX; + TColor sourceColor = sourcePixels[offsetX, offsetY]; + this.Dither.Dither(sourcePixels, sourceColor, this.UpperColor, this.LowerColor, bytes, this.Index, offsetX, offsetY, maxX, maxY); + } + + ArrayPool.Shared.Return(bytes); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs index 95e620df0..5642e6201 100644 --- a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs +++ b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs @@ -10,7 +10,7 @@ namespace ImageSharp using System.Runtime.CompilerServices; /// - /// Packed pixel type containing a single 8 bit normalized W values that is ranging from 0 to 1. + /// Packed pixel type containing a single 8 bit normalized W values ranging from 0 to 1. /// public struct Alpha8 : IPackedPixel, IEquatable { diff --git a/src/ImageSharp/Dithering/Atkinson.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs similarity index 100% rename from src/ImageSharp/Dithering/Atkinson.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs diff --git a/src/ImageSharp/Dithering/Burks.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs similarity index 100% rename from src/ImageSharp/Dithering/Burks.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs diff --git a/src/ImageSharp/Dithering/ErrorDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs similarity index 100% rename from src/ImageSharp/Dithering/ErrorDiffuser.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs diff --git a/src/ImageSharp/Dithering/FloydSteinberg.cs b/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs similarity index 100% rename from src/ImageSharp/Dithering/FloydSteinberg.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs diff --git a/src/ImageSharp/Dithering/IErrorDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs similarity index 100% rename from src/ImageSharp/Dithering/IErrorDiffuser.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs diff --git a/src/ImageSharp/Dithering/JarvisJudiceNinke.cs b/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs similarity index 100% rename from src/ImageSharp/Dithering/JarvisJudiceNinke.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs diff --git a/src/ImageSharp/Dithering/Sierra2.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs similarity index 100% rename from src/ImageSharp/Dithering/Sierra2.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs diff --git a/src/ImageSharp/Dithering/Sierra3.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs similarity index 100% rename from src/ImageSharp/Dithering/Sierra3.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs diff --git a/src/ImageSharp/Dithering/SierraLite.cs b/src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs similarity index 100% rename from src/ImageSharp/Dithering/SierraLite.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs diff --git a/src/ImageSharp/Dithering/Stucki.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs similarity index 100% rename from src/ImageSharp/Dithering/Stucki.cs rename to src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs diff --git a/src/ImageSharp/Dithering/Ordered/Bayer.cs b/src/ImageSharp/Dithering/Ordered/Bayer.cs new file mode 100644 index 000000000..dc731cf89 --- /dev/null +++ b/src/ImageSharp/Dithering/Ordered/Bayer.cs @@ -0,0 +1,38 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Dithering.Ordered +{ + using System; + + /// + /// Applies error diffusion based dithering using the 4x4 Bayer dithering matrix. + /// + /// + public class Bayer : IOrderedDither + { + /// + /// The threshold matrix. + /// This is calculated by multiplying each value in the original matrix by 16 and subtracting 1 + /// + private static readonly byte[,] ThresholdMatrix = { + { 15, 143, 47, 175 }, + { 207, 79, 239, 111 }, + { 63, 191, 31, 159 }, + { 255, 127, 223, 95 } + }; + + /// + public byte[,] Matrix { get; } = ThresholdMatrix; + + /// + public void Dither(PixelAccessor pixels, TColor source, TColor upper, TColor lower, byte[] bytes, int index, int x, int y, int width, int height) + where TColor : struct, IPackedPixel, IEquatable + { + source.ToXyzwBytes(bytes, 0); + pixels[x, y] = ThresholdMatrix[x % 3, y % 3] >= bytes[index] ? upper : lower; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs b/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs new file mode 100644 index 000000000..910b275f9 --- /dev/null +++ b/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs @@ -0,0 +1,37 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Dithering +{ + using System; + + /// + /// Encapsulates properties and methods required to perfom ordered dithering on an image. + /// + public interface IOrderedDither + { + /// + /// Gets the dithering matrix + /// + byte[,] Matrix { get; } + + /// + /// Transforms the image applying the dither matrix. This method alters the input pixels array + /// + /// The pixel accessor + /// The source pixel + /// The color to apply to the pixels above the threshold. + /// The color to apply to the pixels below the threshold. + /// The byte array to pack/unpack to. Must have a length of 4. Bytes are unpacked to Xyzw order. + /// The component index to test the threshold against. Must range from 0 to 3. + /// The column index. + /// The row index. + /// The image width. + /// The image height. + /// The pixel format. + void Dither(PixelAccessor pixels, TColor source, TColor upper, TColor lower, byte[] bytes, int index, int x, int y, int width, int height) + where TColor : struct, IPackedPixel, IEquatable; + } +} diff --git a/src/ImageSharp/Dithering/Ordered/Ordered.cs b/src/ImageSharp/Dithering/Ordered/Ordered.cs new file mode 100644 index 000000000..0cfc53244 --- /dev/null +++ b/src/ImageSharp/Dithering/Ordered/Ordered.cs @@ -0,0 +1,38 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Dithering.Ordered +{ + using System; + + /// + /// Applies error diffusion based dithering using the 4x4 ordered dithering matrix. + /// + /// + public class Ordered : IOrderedDither + { + /// + /// The threshold matrix. + /// This is calculated by multiplying each value in the original matrix by 16 + /// + private static readonly byte[,] ThresholdMatrix = { + { 0, 128, 32, 160 }, + { 192, 64, 224, 96 }, + { 48, 176, 16, 144 }, + { 240, 112, 208, 80 } + }; + + /// + public byte[,] Matrix { get; } = ThresholdMatrix; + + /// + public void Dither(PixelAccessor pixels, TColor source, TColor upper, TColor lower, byte[] bytes, int index, int x, int y, int width, int height) + where TColor : struct, IPackedPixel, IEquatable + { + source.ToXyzwBytes(bytes, 0); + pixels[x, y] = ThresholdMatrix[x % 3, y % 3] >= bytes[index] ? upper : lower; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colors/PackedPixelTests.cs b/tests/ImageSharp.Tests/Colors/PackedPixelTests.cs index a79ef620e..3e2b6fcd5 100644 --- a/tests/ImageSharp.Tests/Colors/PackedPixelTests.cs +++ b/tests/ImageSharp.Tests/Colors/PackedPixelTests.cs @@ -6,6 +6,7 @@ namespace ImageSharp.Tests.Colors { using System; + using System.Diagnostics; using System.Numerics; using Xunit; diff --git a/tests/ImageSharp.Tests/Processors/Filters/DitherTest.cs b/tests/ImageSharp.Tests/Processors/Filters/DitherTest.cs index 3de2481e5..db473901d 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/DitherTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/DitherTest.cs @@ -8,6 +8,7 @@ namespace ImageSharp.Tests using System.IO; using ImageSharp.Dithering; + using ImageSharp.Dithering.Ordered; using Xunit; @@ -16,14 +17,14 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldApplyDitherFilter() { - string path = this.CreateOutputDirectory("Dither"); + string path = this.CreateOutputDirectory("Dither", "Dither"); foreach (TestFile file in Files) { using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { - image.Dither(new SierraLite(), .5F).Save(output); + image.Dither(new Bayer()).Save(output); } } } @@ -31,7 +32,38 @@ namespace ImageSharp.Tests [Fact] public void ImageShouldApplyDitherFilterInBox() { - string path = this.CreateOutputDirectory("Dither"); + string path = this.CreateOutputDirectory("Dither", "Dither"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName("-InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.Dither(new Bayer(), new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + } + } + } + + [Fact] + public void ImageShouldApplyDiffusionFilter() + { + string path = this.CreateOutputDirectory("Dither", "Diffusion"); + + foreach (TestFile file in Files) + { + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) + { + image.Dither(new SierraLite(), .5F).Save(output); + } + } + } + + [Fact] + public void ImageShouldApplyDiffusionFilterInBox() + { + string path = this.CreateOutputDirectory("Dither", "Diffusion"); foreach (TestFile file in Files) { From 94bc2209decdc3fad63d2bf0190d72b8c60650cf Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 15 Feb 2017 09:34:07 +1100 Subject: [PATCH 062/142] Fix terniary --- src/ImageSharp/Dithering/Ordered/Bayer.cs | 2 +- src/ImageSharp/Dithering/Ordered/Ordered.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Dithering/Ordered/Bayer.cs b/src/ImageSharp/Dithering/Ordered/Bayer.cs index dc731cf89..52e91946b 100644 --- a/src/ImageSharp/Dithering/Ordered/Bayer.cs +++ b/src/ImageSharp/Dithering/Ordered/Bayer.cs @@ -32,7 +32,7 @@ namespace ImageSharp.Dithering.Ordered where TColor : struct, IPackedPixel, IEquatable { source.ToXyzwBytes(bytes, 0); - pixels[x, y] = ThresholdMatrix[x % 3, y % 3] >= bytes[index] ? upper : lower; + pixels[x, y] = ThresholdMatrix[x % 3, y % 3] >= bytes[index] ? lower : upper; } } } \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Ordered/Ordered.cs b/src/ImageSharp/Dithering/Ordered/Ordered.cs index 0cfc53244..7c8a15386 100644 --- a/src/ImageSharp/Dithering/Ordered/Ordered.cs +++ b/src/ImageSharp/Dithering/Ordered/Ordered.cs @@ -32,7 +32,7 @@ namespace ImageSharp.Dithering.Ordered where TColor : struct, IPackedPixel, IEquatable { source.ToXyzwBytes(bytes, 0); - pixels[x, y] = ThresholdMatrix[x % 3, y % 3] >= bytes[index] ? upper : lower; + pixels[x, y] = ThresholdMatrix[x % 3, y % 3] >= bytes[index] ? lower : upper; } } } \ No newline at end of file From 1c24bdc96b3b90b35d11f1dfb4c38dbaa488314c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 16 Feb 2017 00:00:28 +1100 Subject: [PATCH 063/142] Use Octree for Png for dithering --- src/ImageSharp.Formats.Png/PngEncoderCore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp.Formats.Png/PngEncoderCore.cs b/src/ImageSharp.Formats.Png/PngEncoderCore.cs index 46aa2187b..9acaf5b9f 100644 --- a/src/ImageSharp.Formats.Png/PngEncoderCore.cs +++ b/src/ImageSharp.Formats.Png/PngEncoderCore.cs @@ -495,7 +495,7 @@ namespace ImageSharp.Formats if (this.Quantizer == null) { - this.Quantizer = new WuQuantizer(); + this.Quantizer = new OctreeQuantizer(); } // Quantize the image returning a palette. This boxing is icky. From 056588aef4378e699f4c12f0752139402c1bf889 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 16 Feb 2017 00:01:02 +1100 Subject: [PATCH 064/142] Cleanup --- .../Processors/Binarization/OrderedDitherProcessor.cs | 4 ++-- src/ImageSharp/Dithering/Ordered/Bayer.cs | 11 ++++++----- src/ImageSharp/Dithering/Ordered/Ordered.cs | 11 ++++++----- src/ImageSharp/Quantizers/Octree/OctreeQuantizer.cs | 2 +- src/ImageSharp/Quantizers/Quantize.cs | 6 ++---- tests/ImageSharp.Tests/Formats/Png/PngTests.cs | 2 +- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/ImageSharp.Processing/Processors/Binarization/OrderedDitherProcessor.cs b/src/ImageSharp.Processing/Processors/Binarization/OrderedDitherProcessor.cs index b2a1e9a22..ae1224bad 100644 --- a/src/ImageSharp.Processing/Processors/Binarization/OrderedDitherProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Binarization/OrderedDitherProcessor.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -18,7 +18,7 @@ namespace ImageSharp.Processing.Processors where TColor : struct, IPackedPixel, IEquatable { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The ordered ditherer. /// The component index to test the threshold against. Must range from 0 to 3. diff --git a/src/ImageSharp/Dithering/Ordered/Bayer.cs b/src/ImageSharp/Dithering/Ordered/Bayer.cs index 52e91946b..3674be0aa 100644 --- a/src/ImageSharp/Dithering/Ordered/Bayer.cs +++ b/src/ImageSharp/Dithering/Ordered/Bayer.cs @@ -17,11 +17,12 @@ namespace ImageSharp.Dithering.Ordered /// The threshold matrix. /// This is calculated by multiplying each value in the original matrix by 16 and subtracting 1 /// - private static readonly byte[,] ThresholdMatrix = { - { 15, 143, 47, 175 }, - { 207, 79, 239, 111 }, - { 63, 191, 31, 159 }, - { 255, 127, 223, 95 } + private static readonly byte[,] ThresholdMatrix = + { + { 15, 143, 47, 175 }, + { 207, 79, 239, 111 }, + { 63, 191, 31, 159 }, + { 255, 127, 223, 95 } }; /// diff --git a/src/ImageSharp/Dithering/Ordered/Ordered.cs b/src/ImageSharp/Dithering/Ordered/Ordered.cs index 7c8a15386..2e7766e0b 100644 --- a/src/ImageSharp/Dithering/Ordered/Ordered.cs +++ b/src/ImageSharp/Dithering/Ordered/Ordered.cs @@ -17,11 +17,12 @@ namespace ImageSharp.Dithering.Ordered /// The threshold matrix. /// This is calculated by multiplying each value in the original matrix by 16 /// - private static readonly byte[,] ThresholdMatrix = { - { 0, 128, 32, 160 }, - { 192, 64, 224, 96 }, - { 48, 176, 16, 144 }, - { 240, 112, 208, 80 } + private static readonly byte[,] ThresholdMatrix = + { + { 0, 128, 32, 160 }, + { 192, 64, 224, 96 }, + { 48, 176, 16, 144 }, + { 240, 112, 208, 80 } }; /// diff --git a/src/ImageSharp/Quantizers/Octree/OctreeQuantizer.cs b/src/ImageSharp/Quantizers/Octree/OctreeQuantizer.cs index 2cd2de4f5..c0aab00ed 100644 --- a/src/ImageSharp/Quantizers/Octree/OctreeQuantizer.cs +++ b/src/ImageSharp/Quantizers/Octree/OctreeQuantizer.cs @@ -437,7 +437,7 @@ namespace ImageSharp.Quantizers { if (this.leaf) { - // TODO: Test Vector4 here + // This seems faster than using Vector4 byte r = (this.red / this.pixelCount).ToByte(); byte g = (this.green / this.pixelCount).ToByte(); byte b = (this.blue / this.pixelCount).ToByte(); diff --git a/src/ImageSharp/Quantizers/Quantize.cs b/src/ImageSharp/Quantizers/Quantize.cs index a03833b25..8a26fb4ba 100644 --- a/src/ImageSharp/Quantizers/Quantize.cs +++ b/src/ImageSharp/Quantizers/Quantize.cs @@ -57,8 +57,6 @@ namespace ImageSharp where TColor : struct, IPackedPixel, IEquatable { QuantizedImage quantized = quantizer.Quantize(source, maxColors); - - int pixelCount = quantized.Pixels.Length; int palleteCount = quantized.Palette.Length - 1; using (PixelAccessor pixels = new PixelAccessor(quantized.Width, quantized.Height)) @@ -69,9 +67,9 @@ namespace ImageSharp source.Configuration.ParallelOptions, y => { - for (var x = 0; x < pixels.Width; x++) + for (int x = 0; x < pixels.Width; x++) { - var i = x + (y * pixels.Width); + int i = x + (y * pixels.Width); TColor color = quantized.Palette[Math.Min(palleteCount, quantized.Pixels[i])]; pixels[x, y] = color; } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngTests.cs index cf485c593..5ba00eb4d 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngTests.cs @@ -19,7 +19,7 @@ namespace ImageSharp.Tests [Fact] public void ImageCanSaveIndexedPng() { - string path = CreateOutputDirectory("Png"); + string path = CreateOutputDirectory("Png", "Indexed"); foreach (TestFile file in Files) { From dfbdba57637f0819d983afc5a450cd07fc3352df Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 16 Feb 2017 00:02:01 +1100 Subject: [PATCH 065/142] Use caching for the distance calculation --- src/ImageSharp/Quantizers/Octree/Quantizer.cs | 48 +++++++++++++------ .../Quantizers/Palette/PaletteQuantizer.cs | 42 ++-------------- 2 files changed, 37 insertions(+), 53 deletions(-) diff --git a/src/ImageSharp/Quantizers/Octree/Quantizer.cs b/src/ImageSharp/Quantizers/Octree/Quantizer.cs index 4d16c5df3..87705de94 100644 --- a/src/ImageSharp/Quantizers/Octree/Quantizer.cs +++ b/src/ImageSharp/Quantizers/Octree/Quantizer.cs @@ -6,6 +6,7 @@ namespace ImageSharp.Quantizers { using System; + using System.Collections.Generic; using System.Numerics; using System.Runtime.CompilerServices; @@ -18,6 +19,11 @@ namespace ImageSharp.Quantizers public abstract class Quantizer : IDitheredQuantizer where TColor : struct, IPackedPixel, IEquatable { + /// + /// A lookup table for colors + /// + private readonly Dictionary colorMap = new Dictionary(); + /// /// Flag used to indicate whether a single pass or two passes are needed for quantization. /// @@ -129,7 +135,7 @@ namespace ImageSharp.Quantizers { // Apply the dithering matrix TColor sourcePixel = source[x, y]; - TColor transformedPixel = this.palette[GetClosestColor(sourcePixel, this.palette)]; + TColor transformedPixel = this.palette[this.GetClosestColor(sourcePixel, this.palette, this.colorMap)]; this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, width, height); } @@ -171,32 +177,46 @@ namespace ImageSharp.Quantizers /// Returns the closest color from the palette to the given color by calculating the Euclidean distance. /// /// The color. - /// The color palette. + /// The color palette. + /// The cache to store the result in. /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static byte GetClosestColor(TColor pixel, TColor[] palette) + protected byte GetClosestColor(TColor pixel, TColor[] colorPalette, Dictionary cache) { + // Check if the color is in the lookup table + if (this.colorMap.ContainsKey(pixel)) + { + return this.colorMap[pixel]; + } + + // Not found - loop through the palette and find the nearest match. + byte colorIndex = 0; float leastDistance = int.MaxValue; Vector4 vector = pixel.ToVector4(); - byte colorIndex = 0; - for (int index = 0; index < palette.Length; index++) + for (int index = 0; index < colorPalette.Length; index++) { - float distance = Vector4.Distance(vector, palette[index].ToVector4()); + float distance = Vector4.Distance(vector, colorPalette[index].ToVector4()); - if (distance < leastDistance) + // Greater... Move on. + if (!(distance < leastDistance)) { - colorIndex = (byte)index; - leastDistance = distance; + continue; + } - // And if it's an exact match, exit the loop - if (Math.Abs(distance) < Constants.Epsilon) - { - break; - } + colorIndex = (byte)index; + leastDistance = distance; + + // And if it's an exact match, exit the loop + if (Math.Abs(distance) < Constants.Epsilon) + { + break; } } + // Now I have the index, pop it into the cache for next time + this.colorMap.Add(pixel, colorIndex); + return colorIndex; } } diff --git a/src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs b/src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs index abf1e5dc5..e55d67037 100644 --- a/src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs +++ b/src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs @@ -7,15 +7,14 @@ namespace ImageSharp.Quantizers { using System; using System.Collections.Generic; - using System.Numerics; /// /// Encapsulates methods to create a quantized image based upon the given palette. /// /// /// The pixel format. - public class PaletteQuantizer : Quantizer - where TColor : struct, IPackedPixel, IEquatable + public sealed class PaletteQuantizer : Quantizer + where TColor : struct, IPackedPixel, IEquatable { /// /// The pixel buffer, used to reduce allocations. @@ -73,42 +72,7 @@ namespace ImageSharp.Quantizers /// protected override byte QuantizePixel(TColor pixel) { - byte colorIndex = 0; - TColor colorHash = pixel; - - // Check if the color is in the lookup table - if (this.colorMap.ContainsKey(colorHash)) - { - colorIndex = this.colorMap[colorHash]; - } - else - { - // Not found - loop through the palette and find the nearest match. - float leastDistance = int.MaxValue; - Vector4 vector = pixel.ToVector4(); - - for (int index = 0; index < this.colors.Length; index++) - { - float distance = Vector4.Distance(vector, this.colors[index].ToVector4()); - - if (distance < leastDistance) - { - colorIndex = (byte)index; - leastDistance = distance; - - // And if it's an exact match, exit the loop - if (Math.Abs(distance) < Constants.Epsilon) - { - break; - } - } - } - - // Now I have the color, pop it into the cache for next time - this.colorMap.Add(colorHash, colorIndex); - } - - return colorIndex; + return this.GetClosestColor(pixel, this.colors, this.colorMap); } /// From f4f84af2a8d9eecc86bfbb754d94cf5018367f34 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 16 Feb 2017 00:26:16 +1100 Subject: [PATCH 066/142] Better integration tests --- .../Processors/Filters/DitherTest.cs | 56 +++++++++++++------ 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/tests/ImageSharp.Tests/Processors/Filters/DitherTest.cs b/tests/ImageSharp.Tests/Processors/Filters/DitherTest.cs index db473901d..e89a1b1ec 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/DitherTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/DitherTest.cs @@ -14,64 +14,88 @@ namespace ImageSharp.Tests public class DitherTest : FileTestBase { - [Fact] - public void ImageShouldApplyDitherFilter() + public static readonly TheoryData Ditherers = new TheoryData + { + { "Ordered", new Ordered() }, + { "Bayer", new Bayer() } + }; + + public static readonly TheoryData ErrorDiffusers = new TheoryData + { + { "Atkinson", new Atkinson() }, + { "Burks", new Burks() }, + { "FloydSteinberg", new FloydSteinberg() }, + { "JarvisJudiceNinke", new JarvisJudiceNinke() }, + { "Sierra2", new Sierra2() }, + { "Sierra3", new Sierra3() }, + { "SierraLite", new SierraLite() }, + { "Stucki", new Stucki() }, + }; + + [Theory] + [MemberData(nameof(Ditherers))] + public void ImageShouldApplyDitherFilter(string name, IOrderedDither ditherer) { string path = this.CreateOutputDirectory("Dither", "Dither"); foreach (TestFile file in Files) { + string filename = file.GetFileName(name); using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Dither(new Bayer()).Save(output); + image.Dither(ditherer).Save(output); } } } - [Fact] - public void ImageShouldApplyDitherFilterInBox() + [Theory] + [MemberData(nameof(Ditherers))] + public void ImageShouldApplyDitherFilterInBox(string name, IOrderedDither ditherer) { string path = this.CreateOutputDirectory("Dither", "Dither"); foreach (TestFile file in Files) { - string filename = file.GetFileName("-InBox"); + string filename = file.GetFileName($"{name}-InBox"); using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Dither(new Bayer(), new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + image.Dither(ditherer, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); } } } - [Fact] - public void ImageShouldApplyDiffusionFilter() + [Theory] + [MemberData(nameof(ErrorDiffusers))] + public void ImageShouldApplyDiffusionFilter(string name, IErrorDiffuser diffuser) { string path = this.CreateOutputDirectory("Dither", "Diffusion"); foreach (TestFile file in Files) { + string filename = file.GetFileName(name); using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Dither(new SierraLite(), .5F).Save(output); + image.Dither(diffuser, .5F).Save(output); } } } - [Fact] - public void ImageShouldApplyDiffusionFilterInBox() + [Theory] + [MemberData(nameof(ErrorDiffusers))] + public void ImageShouldApplyDiffusionFilterInBox(string name, IErrorDiffuser diffuser) { string path = this.CreateOutputDirectory("Dither", "Diffusion"); foreach (TestFile file in Files) { - string filename = file.GetFileName("-InBox"); + string filename = file.GetFileName($"{name}-InBox"); using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Dither(new SierraLite(), .5F, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + image.Dither(diffuser, .5F, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); } } } From a8517788a3a96cea5917a844f99632d0141ef551 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 16 Feb 2017 09:08:15 +1100 Subject: [PATCH 067/142] Remove extra stream --- .../Formats/GeneralFormatTests.cs | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index 7e47501f3..ae795c2ec 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -5,9 +5,7 @@ namespace ImageSharp.Tests { - using System; using System.IO; - using System.Numerics; using Xunit; @@ -145,24 +143,17 @@ namespace ImageSharp.Tests { byte[] serialized; using (Image image = file.CreateImage()) + using (MemoryStream memoryStream = new MemoryStream()) { - using (MemoryStream memoryStream = new MemoryStream()) - { - image.Save(memoryStream); - memoryStream.Flush(); - serialized = memoryStream.ToArray(); - } + image.Save(memoryStream); + memoryStream.Flush(); + serialized = memoryStream.ToArray(); } - using (MemoryStream memoryStream = new MemoryStream(serialized)) + using (Image image2 = new Image(serialized)) + using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { - using (Image image2 = new Image(memoryStream)) - { - using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) - { - image2.Save(output); - } - } + image2.Save(output); } } } From f75ab4c4359c91ee5c83f0220f01529d41c3a792 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 16 Feb 2017 09:55:16 +1100 Subject: [PATCH 068/142] Use jagged arrays --- .../Dithering/ErrorDiffusion/Atkinson.cs | 8 ++++---- src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs | 6 +++--- .../Dithering/ErrorDiffusion/ErrorDiffuser.cs | 14 +++++++------- .../Dithering/ErrorDiffusion/FloydSteinberg.cs | 6 +++--- .../Dithering/ErrorDiffusion/IErrorDiffuser.cs | 2 +- .../Dithering/ErrorDiffusion/JarvisJudiceNinke.cs | 8 ++++---- src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs | 6 +++--- src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs | 8 ++++---- .../Dithering/ErrorDiffusion/SierraLite.cs | 6 +++--- src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs | 8 ++++---- src/ImageSharp/Dithering/Ordered/Bayer.cs | 14 +++++++------- src/ImageSharp/Dithering/Ordered/IOrderedDither.cs | 2 +- src/ImageSharp/Dithering/Ordered/Ordered.cs | 14 +++++++------- 13 files changed, 51 insertions(+), 51 deletions(-) diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs index 934df7e4a..76a5f96ec 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs @@ -14,11 +14,11 @@ namespace ImageSharp.Dithering /// /// The diffusion matrix /// - private static readonly byte[,] AtkinsonMatrix = + private static readonly byte[][] AtkinsonMatrix = { - { 0, 0, 1, 1 }, - { 1, 1, 1, 0 }, - { 0, 1, 0, 0 } + new byte[] { 0, 0, 1, 1 }, + new byte[] { 1, 1, 1, 0 }, + new byte[] { 0, 1, 0, 0 } }; /// diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs index 311316685..2d71b9cc3 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs @@ -14,10 +14,10 @@ namespace ImageSharp.Dithering /// /// The diffusion matrix /// - private static readonly byte[,] BurksMatrix = + private static readonly byte[][] BurksMatrix = { - { 0, 0, 0, 8, 4 }, - { 2, 4, 8, 4, 2 } + new byte[] { 0, 0, 0, 8, 4 }, + new byte[] { 2, 4, 8, 4, 2 } }; /// diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs index 1de6cd814..0c2a1dc40 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs @@ -39,20 +39,20 @@ namespace ImageSharp.Dithering /// /// The dithering matrix. /// The divisor. - protected ErrorDiffuser(byte[,] matrix, byte divisor) + protected ErrorDiffuser(byte[][] matrix, byte divisor) { Guard.NotNull(matrix, nameof(matrix)); Guard.MustBeGreaterThan(divisor, 0, nameof(divisor)); this.Matrix = matrix; - this.matrixWidth = (byte)(matrix.GetUpperBound(1) + 1); - this.matrixHeight = (byte)(matrix.GetUpperBound(0) + 1); + this.matrixWidth = (byte)matrix[0].Length; + this.matrixHeight = (byte)matrix.Length; this.divisorVector = new Vector4(divisor); this.startingOffset = 0; for (int i = 0; i < this.matrixWidth; i++) { - if (matrix[0, i] != 0) + if (matrix[0][i] != 0) { this.startingOffset = (byte)(i - 1); break; @@ -61,7 +61,7 @@ namespace ImageSharp.Dithering } /// - public byte[,] Matrix { get; } + public byte[][] Matrix { get; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -85,13 +85,13 @@ namespace ImageSharp.Dithering if (matrixX > 0 && matrixX < width && matrixY > 0 && matrixY < height) { - byte coefficient = this.Matrix[row, col]; + byte coefficient = this.Matrix[row][col]; if (coefficient == 0) { continue; } - Vector4 coefficientVector = new Vector4(this.Matrix[row, col]); + Vector4 coefficientVector = new Vector4(this.Matrix[row][col]); Vector4 offsetColor = pixels[matrixX, matrixY].ToVector4(); Vector4 result = ((error * coefficientVector) / this.divisorVector) + offsetColor; result.W = offsetColor.W; diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs b/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs index a392c9dc9..cf91bd8fa 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs @@ -14,10 +14,10 @@ namespace ImageSharp.Dithering /// /// The diffusion matrix /// - private static readonly byte[,] FloydSteinbergMatrix = + private static readonly byte[][] FloydSteinbergMatrix = { - { 0, 0, 7 }, - { 3, 5, 1 } + new byte[] { 0, 0, 7 }, + new byte[] { 3, 5, 1 } }; /// diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs index 22cbad1e6..eb6c27167 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Dithering /// /// Gets the dithering matrix /// - byte[,] Matrix { get; } + byte[][] Matrix { get; } /// /// Transforms the image applying the dither matrix. This method alters the input pixels array diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs b/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs index b5876d777..5f26d8b24 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs @@ -14,11 +14,11 @@ namespace ImageSharp.Dithering /// /// The diffusion matrix /// - private static readonly byte[,] JarvisJudiceNinkeMatrix = + private static readonly byte[][] JarvisJudiceNinkeMatrix = { - { 0, 0, 0, 7, 5 }, - { 3, 5, 7, 5, 3 }, - { 1, 3, 5, 3, 1 } + new byte[] { 0, 0, 0, 7, 5 }, + new byte[] { 3, 5, 7, 5, 3 }, + new byte[] { 1, 3, 5, 3, 1 } }; /// diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs index d7cc84254..44132747d 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs @@ -14,10 +14,10 @@ namespace ImageSharp.Dithering /// /// The diffusion matrix /// - private static readonly byte[,] Sierra2Matrix = + private static readonly byte[][] Sierra2Matrix = { - { 0, 0, 0, 4, 3 }, - { 1, 2, 3, 2, 1 } + new byte[] { 0, 0, 0, 4, 3 }, + new byte[] { 1, 2, 3, 2, 1 } }; /// diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs index c3d1fe756..01f6b5ded 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs @@ -14,11 +14,11 @@ namespace ImageSharp.Dithering /// /// The diffusion matrix /// - private static readonly byte[,] Sierra3Matrix = + private static readonly byte[][] Sierra3Matrix = { - { 0, 0, 0, 5, 3 }, - { 2, 4, 5, 4, 2 }, - { 0, 2, 3, 2, 0 } + new byte[] { 0, 0, 0, 5, 3 }, + new byte[] { 2, 4, 5, 4, 2 }, + new byte[] { 0, 2, 3, 2, 0 } }; /// diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs b/src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs index 7d855b84e..42fa8c23d 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs @@ -14,10 +14,10 @@ namespace ImageSharp.Dithering /// /// The diffusion matrix /// - private static readonly byte[,] SierraLiteMatrix = + private static readonly byte[][] SierraLiteMatrix = { - { 0, 0, 2 }, - { 1, 1, 0 } + new byte[] { 0, 0, 2 }, + new byte[] { 1, 1, 0 } }; /// diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs index 3cc01aa79..0e462630e 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs @@ -14,11 +14,11 @@ namespace ImageSharp.Dithering /// /// The diffusion matrix /// - private static readonly byte[,] StuckiMatrix = + private static readonly byte[][] StuckiMatrix = { - { 0, 0, 0, 8, 4 }, - { 2, 4, 8, 4, 2 }, - { 1, 2, 4, 2, 1 } + new byte[] { 0, 0, 0, 8, 4 }, + new byte[] { 2, 4, 8, 4, 2 }, + new byte[] { 1, 2, 4, 2, 1 } }; /// diff --git a/src/ImageSharp/Dithering/Ordered/Bayer.cs b/src/ImageSharp/Dithering/Ordered/Bayer.cs index 3674be0aa..c56088979 100644 --- a/src/ImageSharp/Dithering/Ordered/Bayer.cs +++ b/src/ImageSharp/Dithering/Ordered/Bayer.cs @@ -17,23 +17,23 @@ namespace ImageSharp.Dithering.Ordered /// The threshold matrix. /// This is calculated by multiplying each value in the original matrix by 16 and subtracting 1 /// - private static readonly byte[,] ThresholdMatrix = + private static readonly byte[][] ThresholdMatrix = { - { 15, 143, 47, 175 }, - { 207, 79, 239, 111 }, - { 63, 191, 31, 159 }, - { 255, 127, 223, 95 } + new byte[] { 15, 143, 47, 175 }, + new byte[] { 207, 79, 239, 111 }, + new byte[] { 63, 191, 31, 159 }, + new byte[] { 255, 127, 223, 95 } }; /// - public byte[,] Matrix { get; } = ThresholdMatrix; + public byte[][] Matrix { get; } = ThresholdMatrix; /// public void Dither(PixelAccessor pixels, TColor source, TColor upper, TColor lower, byte[] bytes, int index, int x, int y, int width, int height) where TColor : struct, IPackedPixel, IEquatable { source.ToXyzwBytes(bytes, 0); - pixels[x, y] = ThresholdMatrix[x % 3, y % 3] >= bytes[index] ? lower : upper; + pixels[x, y] = ThresholdMatrix[x % 3][y % 3] >= bytes[index] ? lower : upper; } } } \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs b/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs index 910b275f9..464b9d419 100644 --- a/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs +++ b/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Dithering /// /// Gets the dithering matrix /// - byte[,] Matrix { get; } + byte[][] Matrix { get; } /// /// Transforms the image applying the dither matrix. This method alters the input pixels array diff --git a/src/ImageSharp/Dithering/Ordered/Ordered.cs b/src/ImageSharp/Dithering/Ordered/Ordered.cs index 2e7766e0b..38f8f21a5 100644 --- a/src/ImageSharp/Dithering/Ordered/Ordered.cs +++ b/src/ImageSharp/Dithering/Ordered/Ordered.cs @@ -17,23 +17,23 @@ namespace ImageSharp.Dithering.Ordered /// The threshold matrix. /// This is calculated by multiplying each value in the original matrix by 16 /// - private static readonly byte[,] ThresholdMatrix = + private static readonly byte[][] ThresholdMatrix = { - { 0, 128, 32, 160 }, - { 192, 64, 224, 96 }, - { 48, 176, 16, 144 }, - { 240, 112, 208, 80 } + new byte[] { 0, 128, 32, 160 }, + new byte[] { 192, 64, 224, 96 }, + new byte[] { 48, 176, 16, 144 }, + new byte[] { 240, 112, 208, 80 } }; /// - public byte[,] Matrix { get; } = ThresholdMatrix; + public byte[][] Matrix { get; } = ThresholdMatrix; /// public void Dither(PixelAccessor pixels, TColor source, TColor upper, TColor lower, byte[] bytes, int index, int x, int y, int width, int height) where TColor : struct, IPackedPixel, IEquatable { source.ToXyzwBytes(bytes, 0); - pixels[x, y] = ThresholdMatrix[x % 3, y % 3] >= bytes[index] ? lower : upper; + pixels[x, y] = ThresholdMatrix[x % 3][y % 3] >= bytes[index] ? lower : upper; } } } \ No newline at end of file From 3ff7942475d8f6ea54fca10ab3b6df063375335f Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 17 Feb 2017 09:40:32 +1100 Subject: [PATCH 069/142] Use Fast2DAarray for ordered dither. --- .../Common/Helpers/Fast2DArray{T}.cs | 102 ++++++++++++++++++ src/ImageSharp/Dithering/Ordered/Bayer.cs | 19 ++-- .../Dithering/Ordered/IOrderedDither.cs | 2 +- src/ImageSharp/Dithering/Ordered/Ordered.cs | 19 ++-- src/ImageSharp/Image/PixelAccessor{TColor}.cs | 12 +-- .../ImageSharp.Benchmarks/General/Array2D.cs | 57 ++++++++++ .../General/ArrayCopy.cs | 5 +- .../Common/Fast2DArrayTests.cs | 71 ++++++++++++ 8 files changed, 260 insertions(+), 27 deletions(-) create mode 100644 src/ImageSharp/Common/Helpers/Fast2DArray{T}.cs create mode 100644 tests/ImageSharp.Benchmarks/General/Array2D.cs create mode 100644 tests/ImageSharp.Tests/Common/Fast2DArrayTests.cs diff --git a/src/ImageSharp/Common/Helpers/Fast2DArray{T}.cs b/src/ImageSharp/Common/Helpers/Fast2DArray{T}.cs new file mode 100644 index 000000000..26ec816ce --- /dev/null +++ b/src/ImageSharp/Common/Helpers/Fast2DArray{T}.cs @@ -0,0 +1,102 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Diagnostics; + using System.Runtime.CompilerServices; + + /// + /// Provides fast access to 2D arrays. + /// + /// The type of elements in the array. + public struct Fast2DArray + { + /// + /// The 1D representation of the 2D array. + /// + public T[] Data; + + /// + /// Gets the width of the 2D array. + /// + public int Width; + + /// + /// Gets the height of the 2D array. + /// + public int Height; + + /// + /// Initializes a new instance of the struct. + /// + /// The 2D array to provide access to. + public Fast2DArray(T[,] data) + { + Guard.NotNull(data, nameof(data)); + this.Height = data.GetLength(0); + this.Width = data.GetLength(1); + + Guard.MustBeGreaterThan(this.Width, 0, nameof(this.Width)); + Guard.MustBeGreaterThan(this.Height, 0, nameof(this.Height)); + + this.Data = new T[this.Width * this.Height]; + + for (int y = 0; y < this.Height; y++) + { + for (int x = 0; x < this.Width; x++) + { + this.Data[(y * this.Width) + x] = data[y, x]; + } + } + } + + /// + /// Gets or sets the item at the specified position. + /// + /// The row-coordinate of the item. Must be greater than or equal to zero and less than the height of the array. + /// The column-coordinate of the item. Must be greater than or equal to zero and less than the width of the array. + /// The at the specified position. + public T this[int row, int column] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + this.CheckCoordinates(row, column); + return this.Data[(row * this.Width) + column]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + this.CheckCoordinates(row, column); + this.Data[(row * this.Width) + column] = value; + } + } + + /// + /// Checks the coordinates to ensure they are within bounds. + /// + /// The row-coordinate of the item. Must be greater than zero and smaller than the height of the array. + /// The column-coordinate of the item. Must be greater than zero and smaller than the width of the array. + /// + /// Thrown if the coordinates are not within the bounds of the array. + /// + [Conditional("DEBUG")] + private void CheckCoordinates(int row, int column) + { + if (row < 0 || row >= this.Height) + { + throw new ArgumentOutOfRangeException(nameof(row), row, $"{row} is outwith the array bounds."); + } + + if (column < 0 || column >= this.Width) + { + throw new ArgumentOutOfRangeException(nameof(column), column, $"{column} is outwith the array bounds."); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Ordered/Bayer.cs b/src/ImageSharp/Dithering/Ordered/Bayer.cs index c56088979..3d3d90023 100644 --- a/src/ImageSharp/Dithering/Ordered/Bayer.cs +++ b/src/ImageSharp/Dithering/Ordered/Bayer.cs @@ -17,23 +17,24 @@ namespace ImageSharp.Dithering.Ordered /// The threshold matrix. /// This is calculated by multiplying each value in the original matrix by 16 and subtracting 1 /// - private static readonly byte[][] ThresholdMatrix = - { - new byte[] { 15, 143, 47, 175 }, - new byte[] { 207, 79, 239, 111 }, - new byte[] { 63, 191, 31, 159 }, - new byte[] { 255, 127, 223, 95 } - }; + private static readonly Fast2DArray ThresholdMatrix = + new Fast2DArray(new byte[,] + { + { 15, 143, 47, 175 }, + { 207, 79, 239, 111 }, + { 63, 191, 31, 159 }, + { 255, 127, 223, 95 } + }); /// - public byte[][] Matrix { get; } = ThresholdMatrix; + public Fast2DArray Matrix { get; } = ThresholdMatrix; /// public void Dither(PixelAccessor pixels, TColor source, TColor upper, TColor lower, byte[] bytes, int index, int x, int y, int width, int height) where TColor : struct, IPackedPixel, IEquatable { source.ToXyzwBytes(bytes, 0); - pixels[x, y] = ThresholdMatrix[x % 3][y % 3] >= bytes[index] ? lower : upper; + pixels[x, y] = ThresholdMatrix[y % 3, x % 3] >= bytes[index] ? lower : upper; } } } \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs b/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs index 464b9d419..e74a863a5 100644 --- a/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs +++ b/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Dithering /// /// Gets the dithering matrix /// - byte[][] Matrix { get; } + Fast2DArray Matrix { get; } /// /// Transforms the image applying the dither matrix. This method alters the input pixels array diff --git a/src/ImageSharp/Dithering/Ordered/Ordered.cs b/src/ImageSharp/Dithering/Ordered/Ordered.cs index 38f8f21a5..bb1f21060 100644 --- a/src/ImageSharp/Dithering/Ordered/Ordered.cs +++ b/src/ImageSharp/Dithering/Ordered/Ordered.cs @@ -17,23 +17,24 @@ namespace ImageSharp.Dithering.Ordered /// The threshold matrix. /// This is calculated by multiplying each value in the original matrix by 16 /// - private static readonly byte[][] ThresholdMatrix = - { - new byte[] { 0, 128, 32, 160 }, - new byte[] { 192, 64, 224, 96 }, - new byte[] { 48, 176, 16, 144 }, - new byte[] { 240, 112, 208, 80 } - }; + private static readonly Fast2DArray ThresholdMatrix = + new Fast2DArray(new byte[,] + { + { 0, 128, 32, 160 }, + { 192, 64, 224, 96 }, + { 48, 176, 16, 144 }, + { 240, 112, 208, 80 } + }); /// - public byte[][] Matrix { get; } = ThresholdMatrix; + public Fast2DArray Matrix { get; } = ThresholdMatrix; /// public void Dither(PixelAccessor pixels, TColor source, TColor upper, TColor lower, byte[] bytes, int index, int x, int y, int width, int height) where TColor : struct, IPackedPixel, IEquatable { source.ToXyzwBytes(bytes, 0); - pixels[x, y] = ThresholdMatrix[x % 3][y % 3] >= bytes[index] ? lower : upper; + pixels[x, y] = ThresholdMatrix[y % 3, x % 3] >= bytes[index] ? lower : upper; } } } \ No newline at end of file diff --git a/src/ImageSharp/Image/PixelAccessor{TColor}.cs b/src/ImageSharp/Image/PixelAccessor{TColor}.cs index f37ba7496..55b16235a 100644 --- a/src/ImageSharp/Image/PixelAccessor{TColor}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TColor}.cs @@ -153,8 +153,8 @@ namespace ImageSharp /// /// Gets or sets the pixel at the specified position. /// - /// The x-coordinate of the pixel. Must be greater than zero and smaller than the width of the pixel. - /// The y-coordinate of the pixel. Must be greater than zero and smaller than the width of the pixel. + /// The x-coordinate of the pixel. Must be greater than or equal to zero and less than the width of the image. + /// The y-coordinate of the pixel. Must be greater than or equal to zero and less than the height of the image. /// The at the specified position. public TColor this[int x, int y] { @@ -625,8 +625,8 @@ namespace ImageSharp /// Checks that the given area and offset are within the bounds of the image. /// /// The area. - /// The x-coordinate of the pixel. Must be greater than zero and smaller than the width of the pixel. - /// The y-coordinate of the pixel. Must be greater than zero and smaller than the width of the pixel. + /// The x-coordinate of the pixel. Must be greater than zero and less than the width of the image. + /// The y-coordinate of the pixel. Must be greater than zero and less than the height of the image. /// /// Thrown if the dimensions are not within the bounds of the image. /// @@ -649,8 +649,8 @@ namespace ImageSharp /// /// Checks the coordinates to ensure they are within bounds. /// - /// The x-coordinate of the pixel. Must be greater than zero and smaller than the width of the pixel. - /// The y-coordinate of the pixel. Must be greater than zero and smaller than the width of the pixel. + /// The x-coordinate of the pixel. Must be greater than zero and less than the width of the image. + /// The y-coordinate of the pixel. Must be greater than zero and less than the height of the image. /// /// Thrown if the coordinates are not within the bounds of the image. /// diff --git a/tests/ImageSharp.Benchmarks/General/Array2D.cs b/tests/ImageSharp.Benchmarks/General/Array2D.cs new file mode 100644 index 000000000..a01ba77ad --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/Array2D.cs @@ -0,0 +1,57 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Benchmarks.General +{ + using BenchmarkDotNet.Attributes; + + public class Array2D + { + private float[,] data; + + private float[][] jaggedData; + + private Fast2DArray fastData; + + [Params(10, 100, 1000, 10000)] + public int Count { get; set; } + + public int Index { get; set; } + + [Setup] + public void SetUp() + { + this.data = new float[this.Count, this.Count]; + this.jaggedData = new float[this.Count][]; + + for (int i = 0; i < this.Count; i++) + { + this.jaggedData[i] = new float[this.Count]; + } + + this.fastData = new Fast2DArray(this.data); + + this.Index = this.Count / 2; + } + + [Benchmark(Baseline = true, Description = "Array access using 2D array")] + public float ArrayIndex() + { + return this.data[this.Index, this.Index]; + } + + [Benchmark(Description = "Array access using a jagged array")] + public float ArrayJaggedIndex() + { + return this.jaggedData[this.Index][this.Index]; + } + + [Benchmark(Description = "Array access using Fast2DArray")] + public float ArrayFastIndex() + { + return this.fastData[this.Index, this.Index]; + } + } +} diff --git a/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs b/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs index 88d47db51..dddd83e42 100644 --- a/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs +++ b/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs @@ -2,6 +2,7 @@ // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // + namespace ImageSharp.Benchmarks.General { using System; @@ -14,9 +15,9 @@ namespace ImageSharp.Benchmarks.General [Params(100, 1000, 10000)] public int Count { get; set; } - byte[] source; + private byte[] source; - byte[] destination; + private byte[] destination; [Setup] public void SetUp() diff --git a/tests/ImageSharp.Tests/Common/Fast2DArrayTests.cs b/tests/ImageSharp.Tests/Common/Fast2DArrayTests.cs new file mode 100644 index 000000000..903ea6f8d --- /dev/null +++ b/tests/ImageSharp.Tests/Common/Fast2DArrayTests.cs @@ -0,0 +1,71 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Common +{ + using System; + + using Xunit; + + public class Fast2DArrayTests + { + private static readonly float[,] FloydSteinbergMatrix = + { + { 0, 0, 7 }, + { 3, 5, 1 } + }; + + [Fact] + public void Fast2DArrayThrowsOnNullInitializer() + { + Assert.Throws(() => + { + Fast2DArray fast = new Fast2DArray(null); + }); + } + + [Fact] + public void Fast2DArrayThrowsOnEmptyInitializer() + { + Assert.Throws(() => + { + Fast2DArray fast = new Fast2DArray(new float[0, 0]); + }); + } + + [Fact] + public void Fast2DArrayReturnsCorrectDimensions() + { + Fast2DArray fast = new Fast2DArray(FloydSteinbergMatrix); + Assert.True(fast.Width == FloydSteinbergMatrix.GetLength(1)); + Assert.True(fast.Height == FloydSteinbergMatrix.GetLength(0)); + } + + [Fact] + public void Fast2DArrayGetReturnsCorrectResults() + { + Fast2DArray fast = new Fast2DArray(FloydSteinbergMatrix); + + for (int row = 0; row < fast.Height; row++) + { + for (int column = 0; column < fast.Width; column++) + { + Assert.True(Math.Abs(fast[row, column] - FloydSteinbergMatrix[row, column]) < Constants.Epsilon); + } + } + } + + [Fact] + public void Fast2DArrayGetSetReturnsCorrectResults() + { + Fast2DArray fast = new Fast2DArray(new float[4, 4]); + const float Val = 5F; + + fast[3, 3] = Val; + + Assert.True(Math.Abs(Val - fast[3, 3]) < Constants.Epsilon); + } + } +} \ No newline at end of file From e06b30f04496a8de2d78be91aacc2ff152e074d7 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 17 Feb 2017 09:59:19 +1100 Subject: [PATCH 070/142] Use Fast2DArray for error diffusion. --- .../Dithering/ErrorDiffusion/Atkinson.cs | 11 +++++---- .../Dithering/ErrorDiffusion/Burks.cs | 9 ++++---- .../Dithering/ErrorDiffusion/ErrorDiffuser.cs | 23 +++++++++++-------- .../ErrorDiffusion/FloydSteinberg.cs | 9 ++++---- .../ErrorDiffusion/IErrorDiffuser.cs | 2 +- .../ErrorDiffusion/JarvisJudiceNinke.cs | 11 +++++---- .../Dithering/ErrorDiffusion/Sierra2.cs | 9 ++++---- .../Dithering/ErrorDiffusion/Sierra3.cs | 11 +++++---- .../Dithering/ErrorDiffusion/SierraLite.cs | 9 ++++---- .../Dithering/ErrorDiffusion/Stucki.cs | 11 +++++---- 10 files changed, 59 insertions(+), 46 deletions(-) diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs index 76a5f96ec..1fa6852c2 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs @@ -14,12 +14,13 @@ namespace ImageSharp.Dithering /// /// The diffusion matrix /// - private static readonly byte[][] AtkinsonMatrix = + private static readonly Fast2DArray AtkinsonMatrix = + new Fast2DArray(new float[,] { - new byte[] { 0, 0, 1, 1 }, - new byte[] { 1, 1, 1, 0 }, - new byte[] { 0, 1, 0, 0 } - }; + { 0, 0, 1, 1 }, + { 1, 1, 1, 0 }, + { 0, 1, 0, 0 } + }); /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs index 2d71b9cc3..a4adcb0a4 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs @@ -14,11 +14,12 @@ namespace ImageSharp.Dithering /// /// The diffusion matrix /// - private static readonly byte[][] BurksMatrix = + private static readonly Fast2DArray BurksMatrix = + new Fast2DArray(new float[,] { - new byte[] { 0, 0, 0, 8, 4 }, - new byte[] { 2, 4, 8, 4, 2 } - }; + { 0, 0, 0, 8, 4 }, + { 2, 4, 8, 4, 2 } + }); /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs index 0c2a1dc40..4fd5476f0 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs @@ -22,12 +22,12 @@ namespace ImageSharp.Dithering /// /// The matrix width /// - private readonly byte matrixHeight; + private readonly int matrixHeight; /// /// The matrix height /// - private readonly byte matrixWidth; + private readonly int matrixWidth; /// /// The offset at which to start the dithering operation. @@ -39,20 +39,22 @@ namespace ImageSharp.Dithering /// /// The dithering matrix. /// The divisor. - protected ErrorDiffuser(byte[][] matrix, byte divisor) + protected ErrorDiffuser(Fast2DArray matrix, byte divisor) { Guard.NotNull(matrix, nameof(matrix)); Guard.MustBeGreaterThan(divisor, 0, nameof(divisor)); this.Matrix = matrix; - this.matrixWidth = (byte)matrix[0].Length; - this.matrixHeight = (byte)matrix.Length; + this.matrixWidth = this.Matrix.Width; + this.matrixHeight = this.Matrix.Height; this.divisorVector = new Vector4(divisor); this.startingOffset = 0; for (int i = 0; i < this.matrixWidth; i++) { - if (matrix[0][i] != 0) + // Good to disable here as we are not comparing matematical output. + // ReSharper disable once CompareOfFloatsByEqualityOperator + if (matrix[0, i] != 0) { this.startingOffset = (byte)(i - 1); break; @@ -61,7 +63,7 @@ namespace ImageSharp.Dithering } /// - public byte[][] Matrix { get; } + public Fast2DArray Matrix { get; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -85,13 +87,16 @@ namespace ImageSharp.Dithering if (matrixX > 0 && matrixX < width && matrixY > 0 && matrixY < height) { - byte coefficient = this.Matrix[row][col]; + float coefficient = this.Matrix[row, col]; + + // Good to disable here as we are not comparing matematical output. + // ReSharper disable once CompareOfFloatsByEqualityOperator if (coefficient == 0) { continue; } - Vector4 coefficientVector = new Vector4(this.Matrix[row][col]); + Vector4 coefficientVector = new Vector4(coefficient); Vector4 offsetColor = pixels[matrixX, matrixY].ToVector4(); Vector4 result = ((error * coefficientVector) / this.divisorVector) + offsetColor; result.W = offsetColor.W; diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs b/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs index cf91bd8fa..7b67d2dd1 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs @@ -14,11 +14,12 @@ namespace ImageSharp.Dithering /// /// The diffusion matrix /// - private static readonly byte[][] FloydSteinbergMatrix = + private static readonly Fast2DArray FloydSteinbergMatrix = + new Fast2DArray(new float[,] { - new byte[] { 0, 0, 7 }, - new byte[] { 3, 5, 1 } - }; + { 0, 0, 7 }, + { 3, 5, 1 } + }); /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs index eb6c27167..fbee27088 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Dithering /// /// Gets the dithering matrix /// - byte[][] Matrix { get; } + Fast2DArray Matrix { get; } /// /// Transforms the image applying the dither matrix. This method alters the input pixels array diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs b/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs index 5f26d8b24..32f38fbc8 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs @@ -14,12 +14,13 @@ namespace ImageSharp.Dithering /// /// The diffusion matrix /// - private static readonly byte[][] JarvisJudiceNinkeMatrix = + private static readonly Fast2DArray JarvisJudiceNinkeMatrix = + new Fast2DArray(new float[,] { - new byte[] { 0, 0, 0, 7, 5 }, - new byte[] { 3, 5, 7, 5, 3 }, - new byte[] { 1, 3, 5, 3, 1 } - }; + { 0, 0, 0, 7, 5 }, + { 3, 5, 7, 5, 3 }, + { 1, 3, 5, 3, 1 } + }); /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs index 44132747d..47b14944e 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs @@ -14,11 +14,12 @@ namespace ImageSharp.Dithering /// /// The diffusion matrix /// - private static readonly byte[][] Sierra2Matrix = + private static readonly Fast2DArray Sierra2Matrix = + new Fast2DArray(new float[,] { - new byte[] { 0, 0, 0, 4, 3 }, - new byte[] { 1, 2, 3, 2, 1 } - }; + { 0, 0, 0, 4, 3 }, + { 1, 2, 3, 2, 1 } + }); /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs index 01f6b5ded..ae33954cf 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs @@ -14,12 +14,13 @@ namespace ImageSharp.Dithering /// /// The diffusion matrix /// - private static readonly byte[][] Sierra3Matrix = + private static readonly Fast2DArray Sierra3Matrix = + new Fast2DArray(new float[,] { - new byte[] { 0, 0, 0, 5, 3 }, - new byte[] { 2, 4, 5, 4, 2 }, - new byte[] { 0, 2, 3, 2, 0 } - }; + { 0, 0, 0, 5, 3 }, + { 2, 4, 5, 4, 2 }, + { 0, 2, 3, 2, 0 } + }); /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs b/src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs index 42fa8c23d..8a1e17816 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs @@ -14,11 +14,12 @@ namespace ImageSharp.Dithering /// /// The diffusion matrix /// - private static readonly byte[][] SierraLiteMatrix = + private static readonly Fast2DArray SierraLiteMatrix = + new Fast2DArray(new float[,] { - new byte[] { 0, 0, 2 }, - new byte[] { 1, 1, 0 } - }; + { 0, 0, 2 }, + { 1, 1, 0 } + }); /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs index 0e462630e..b5d22b259 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs @@ -14,12 +14,13 @@ namespace ImageSharp.Dithering /// /// The diffusion matrix /// - private static readonly byte[][] StuckiMatrix = + private static readonly Fast2DArray StuckiMatrix = + new Fast2DArray(new float[,] { - new byte[] { 0, 0, 0, 8, 4 }, - new byte[] { 2, 4, 8, 4, 2 }, - new byte[] { 1, 2, 4, 2, 1 } - }; + { 0, 0, 0, 8, 4 }, + { 2, 4, 8, 4, 2 }, + { 1, 2, 4, 2, 1 } + }); /// /// Initializes a new instance of the class. From cfefe30aa92aa7aa446eff7620024afcd0f1cb37 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Wed, 15 Feb 2017 21:02:21 +0000 Subject: [PATCH 071/142] Add namedcolors to work for all TColor types --- src/ImageSharp.Processing/Overlays/Glow.cs | 14 +- .../Overlays/Vignette.cs | 14 +- .../Binarization/BinaryThresholdProcessor.cs | 9 +- .../ErrorDiffusionDitherProcessor.cs | 9 +- .../Binarization/OrderedDitherProcessor.cs | 9 +- .../ColorMatrix/LomographProcessor.cs | 6 +- .../ColorMatrix/PolaroidProcessor.cs | 12 +- .../Processors/Overlays/GlowProcessor.cs | 7 +- .../Processors/Overlays/VignetteProcessor.cs | 7 +- src/ImageSharp/Colors/Color.cs | 60 +- src/ImageSharp/Colors/ColorBuilder{TColor}.cs | 110 +++ src/ImageSharp/Colors/ColorDefinitions.cs | 284 +++---- src/ImageSharp/Colors/NamedColors{TColor}.cs | 727 ++++++++++++++++++ .../Colors/ColorDefinitionTests.cs | 38 + tests/ImageSharp.Tests/Colors/ColorTests.cs | 20 +- .../Image/PixelAccessorTests.cs | 9 +- 16 files changed, 1056 insertions(+), 279 deletions(-) create mode 100644 src/ImageSharp/Colors/ColorBuilder{TColor}.cs create mode 100644 src/ImageSharp/Colors/NamedColors{TColor}.cs create mode 100644 tests/ImageSharp.Tests/Colors/ColorDefinitionTests.cs diff --git a/src/ImageSharp.Processing/Overlays/Glow.cs b/src/ImageSharp.Processing/Overlays/Glow.cs index 91fbfc7f6..9c5fe017f 100644 --- a/src/ImageSharp.Processing/Overlays/Glow.cs +++ b/src/ImageSharp.Processing/Overlays/Glow.cs @@ -23,7 +23,7 @@ namespace ImageSharp public static Image Glow(this Image source) where TColor : struct, IPackedPixel, IEquatable { - return Glow(source, default(TColor), source.Bounds.Width * .5F, source.Bounds); + return Glow(source, NamedColors.Black, source.Bounds.Width * .5F, source.Bounds); } /// @@ -49,7 +49,7 @@ namespace ImageSharp public static Image Glow(this Image source, float radius) where TColor : struct, IPackedPixel, IEquatable { - return Glow(source, default(TColor), radius, source.Bounds); + return Glow(source, NamedColors.Black, radius, source.Bounds); } /// @@ -64,7 +64,7 @@ namespace ImageSharp public static Image Glow(this Image source, Rectangle rectangle) where TColor : struct, IPackedPixel, IEquatable { - return Glow(source, default(TColor), 0, rectangle); + return Glow(source, NamedColors.Black, 0, rectangle); } /// @@ -81,13 +81,7 @@ namespace ImageSharp public static Image Glow(this Image source, TColor color, float radius, Rectangle rectangle) where TColor : struct, IPackedPixel, IEquatable { - GlowProcessor processor = new GlowProcessor { Radius = radius, }; - - if (!color.Equals(default(TColor))) - { - processor.GlowColor = color; - } - + GlowProcessor processor = new GlowProcessor(color) { Radius = radius, }; source.ApplyProcessor(processor, rectangle); return source; } diff --git a/src/ImageSharp.Processing/Overlays/Vignette.cs b/src/ImageSharp.Processing/Overlays/Vignette.cs index c6cd0ab1e..4a505ad9b 100644 --- a/src/ImageSharp.Processing/Overlays/Vignette.cs +++ b/src/ImageSharp.Processing/Overlays/Vignette.cs @@ -23,7 +23,7 @@ namespace ImageSharp public static Image Vignette(this Image source) where TColor : struct, IPackedPixel, IEquatable { - return Vignette(source, default(TColor), source.Bounds.Width * .5F, source.Bounds.Height * .5F, source.Bounds); + return Vignette(source, NamedColors.Black, source.Bounds.Width * .5F, source.Bounds.Height * .5F, source.Bounds); } /// @@ -50,7 +50,7 @@ namespace ImageSharp public static Image Vignette(this Image source, float radiusX, float radiusY) where TColor : struct, IPackedPixel, IEquatable { - return Vignette(source, default(TColor), radiusX, radiusY, source.Bounds); + return Vignette(source, NamedColors.Black, radiusX, radiusY, source.Bounds); } /// @@ -65,7 +65,7 @@ namespace ImageSharp public static Image Vignette(this Image source, Rectangle rectangle) where TColor : struct, IPackedPixel, IEquatable { - return Vignette(source, default(TColor), 0, 0, rectangle); + return Vignette(source, NamedColors.Black, 0, 0, rectangle); } /// @@ -83,13 +83,7 @@ namespace ImageSharp public static Image Vignette(this Image source, TColor color, float radiusX, float radiusY, Rectangle rectangle) where TColor : struct, IPackedPixel, IEquatable { - VignetteProcessor processor = new VignetteProcessor { RadiusX = radiusX, RadiusY = radiusY }; - - if (!color.Equals(default(TColor))) - { - processor.VignetteColor = color; - } - + VignetteProcessor processor = new VignetteProcessor(color) { RadiusX = radiusX, RadiusY = radiusY }; source.ApplyProcessor(processor, rectangle); return source; } diff --git a/src/ImageSharp.Processing/Processors/Binarization/BinaryThresholdProcessor.cs b/src/ImageSharp.Processing/Processors/Binarization/BinaryThresholdProcessor.cs index cb3758748..e4010413f 100644 --- a/src/ImageSharp.Processing/Processors/Binarization/BinaryThresholdProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Binarization/BinaryThresholdProcessor.cs @@ -27,13 +27,8 @@ namespace ImageSharp.Processing.Processors this.Threshold = threshold; // Default to white/black for upper/lower. - TColor upper = default(TColor); - upper.PackFromBytes(255, 255, 255, 255); - this.UpperColor = upper; - - TColor lower = default(TColor); - lower.PackFromBytes(0, 0, 0, 255); - this.LowerColor = lower; + this.UpperColor = NamedColors.White; + this.LowerColor = NamedColors.Black; } /// diff --git a/src/ImageSharp.Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs b/src/ImageSharp.Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs index 6429ce6f0..25b464c69 100644 --- a/src/ImageSharp.Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs @@ -29,13 +29,8 @@ namespace ImageSharp.Processing.Processors this.Threshold = threshold; // Default to white/black for upper/lower. - TColor upper = default(TColor); - upper.PackFromBytes(255, 255, 255, 255); - this.UpperColor = upper; - - TColor lower = default(TColor); - lower.PackFromBytes(0, 0, 0, 255); - this.LowerColor = lower; + this.UpperColor = NamedColors.White; + this.LowerColor = NamedColors.Black; } /// diff --git a/src/ImageSharp.Processing/Processors/Binarization/OrderedDitherProcessor.cs b/src/ImageSharp.Processing/Processors/Binarization/OrderedDitherProcessor.cs index ae1224bad..f201ba49a 100644 --- a/src/ImageSharp.Processing/Processors/Binarization/OrderedDitherProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Binarization/OrderedDitherProcessor.cs @@ -37,13 +37,8 @@ namespace ImageSharp.Processing.Processors this.Index = index; // Default to white/black for upper/lower. - TColor upper = default(TColor); - upper.PackFromBytes(255, 255, 255, 255); - this.UpperColor = upper; - - TColor lower = default(TColor); - lower.PackFromBytes(0, 0, 0, 255); - this.LowerColor = lower; + this.UpperColor = NamedColors.White; + this.LowerColor = NamedColors.Black; } /// diff --git a/src/ImageSharp.Processing/Processors/ColorMatrix/LomographProcessor.cs b/src/ImageSharp.Processing/Processors/ColorMatrix/LomographProcessor.cs index 731e04bf7..ad166b134 100644 --- a/src/ImageSharp.Processing/Processors/ColorMatrix/LomographProcessor.cs +++ b/src/ImageSharp.Processing/Processors/ColorMatrix/LomographProcessor.cs @@ -15,6 +15,8 @@ namespace ImageSharp.Processing.Processors public class LomographProcessor : ColorMatrixFilter where TColor : struct, IPackedPixel, IEquatable { + private static readonly TColor VeryDarkGreen = ColorBuilder.FromRGBA(0, 10, 0, 255); + /// public override Matrix4x4 Matrix => new Matrix4x4() { @@ -30,9 +32,7 @@ namespace ImageSharp.Processing.Processors /// protected override void AfterApply(ImageBase source, Rectangle sourceRectangle) { - TColor packed = default(TColor); - packed.PackFromVector4(new Color(0, 10, 0).ToVector4()); // Very dark (mostly black) lime green. - new VignetteProcessor { VignetteColor = packed }.Apply(source, sourceRectangle); + new VignetteProcessor(VeryDarkGreen).Apply(source, sourceRectangle); } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/ColorMatrix/PolaroidProcessor.cs b/src/ImageSharp.Processing/Processors/ColorMatrix/PolaroidProcessor.cs index 678edf011..5df11160f 100644 --- a/src/ImageSharp.Processing/Processors/ColorMatrix/PolaroidProcessor.cs +++ b/src/ImageSharp.Processing/Processors/ColorMatrix/PolaroidProcessor.cs @@ -15,6 +15,9 @@ namespace ImageSharp.Processing.Processors public class PolaroidProcessor : ColorMatrixFilter where TColor : struct, IPackedPixel, IEquatable { + private static TColor veryDarkOrange = ColorBuilder.FromRGB(102, 34, 0); + private static TColor lightOrange = ColorBuilder.FromRGBA(255, 153, 102, 178); + /// public override Matrix4x4 Matrix => new Matrix4x4() { @@ -36,13 +39,8 @@ namespace ImageSharp.Processing.Processors /// protected override void AfterApply(ImageBase source, Rectangle sourceRectangle) { - TColor packedV = default(TColor); - packedV.PackFromVector4(new Color(102, 34, 0).ToVector4()); // Very dark orange [Brown tone] - new VignetteProcessor { VignetteColor = packedV }.Apply(source, sourceRectangle); - - TColor packedG = default(TColor); - packedG.PackFromVector4(new Color(255, 153, 102, 178).ToVector4()); // Light orange - new GlowProcessor { GlowColor = packedG, Radius = source.Width / 4F }.Apply(source, sourceRectangle); + new VignetteProcessor(veryDarkOrange).Apply(source, sourceRectangle); + new GlowProcessor(lightOrange) { Radius = source.Width / 4F }.Apply(source, sourceRectangle); } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp.Processing/Processors/Overlays/GlowProcessor.cs index f0e32f1fa..690f57ab7 100644 --- a/src/ImageSharp.Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Overlays/GlowProcessor.cs @@ -17,12 +17,11 @@ namespace ImageSharp.Processing.Processors where TColor : struct, IPackedPixel, IEquatable { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public GlowProcessor() + /// The color or the glow. + public GlowProcessor(TColor color) { - TColor color = default(TColor); - color.PackFromVector4(Color.Black.ToVector4()); this.GlowColor = color; } diff --git a/src/ImageSharp.Processing/Processors/Overlays/VignetteProcessor.cs b/src/ImageSharp.Processing/Processors/Overlays/VignetteProcessor.cs index 8449f1833..6e94a4c2a 100644 --- a/src/ImageSharp.Processing/Processors/Overlays/VignetteProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Overlays/VignetteProcessor.cs @@ -17,12 +17,11 @@ namespace ImageSharp.Processing.Processors where TColor : struct, IPackedPixel, IEquatable { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public VignetteProcessor() + /// The color of the vignette. + public VignetteProcessor(TColor color) { - TColor color = default(TColor); - color.PackFromVector4(Color.Black.ToVector4()); this.VignetteColor = color; } diff --git a/src/ImageSharp/Colors/Color.cs b/src/ImageSharp/Colors/Color.cs index b2f9437ca..730fe45a5 100644 --- a/src/ImageSharp/Colors/Color.cs +++ b/src/ImageSharp/Colors/Color.cs @@ -67,28 +67,6 @@ namespace ImageSharp this.packedValue = Pack(r, g, b, a); } - /// - /// Initializes a new instance of the struct. - /// - /// - /// The hexadecimal representation of the combined color components arranged - /// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax. - /// - public Color(string hex) - { - Guard.NotNullOrEmpty(hex, nameof(hex)); - - hex = ToRgbaHex(hex); - - if (hex == null || !uint.TryParse(hex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out this.packedValue)) - { - throw new ArgumentException("Hexadecimal string is not in the correct format.", nameof(hex)); - } - - // Order parsed from hex string will be backwards, so reverse it. - this.packedValue = Pack(this.A, this.B, this.G, this.R); - } - /// /// Initializes a new instance of the struct. /// @@ -264,7 +242,7 @@ namespace ImageSharp /// public static Color FromHex(string hex) { - return new Color(hex); + return ColorBuilder.FromHex(hex); } /// @@ -421,39 +399,5 @@ namespace ImageSharp { return (uint)(x << RedShift | y << GreenShift | z << BlueShift | w << AlphaShift); } - - /// - /// Converts the specified hex value to an rrggbbaa hex value. - /// - /// The hex value to convert. - /// - /// A rrggbbaa hex value. - /// - private static string ToRgbaHex(string hex) - { - hex = hex.StartsWith("#") ? hex.Substring(1) : hex; - - if (hex.Length == 8) - { - return hex; - } - - if (hex.Length == 6) - { - return hex + "FF"; - } - - if (hex.Length < 3 || hex.Length > 4) - { - return null; - } - - string red = char.ToString(hex[0]); - string green = char.ToString(hex[1]); - string blue = char.ToString(hex[2]); - string alpha = hex.Length == 3 ? "F" : char.ToString(hex[3]); - - return red + red + green + green + blue + blue + alpha + alpha; - } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Colors/ColorBuilder{TColor}.cs b/src/ImageSharp/Colors/ColorBuilder{TColor}.cs new file mode 100644 index 000000000..47506af49 --- /dev/null +++ b/src/ImageSharp/Colors/ColorBuilder{TColor}.cs @@ -0,0 +1,110 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Globalization; + + /// + /// A set of named colors mapped to the provided Color space. + /// + /// The type of the color. + public static class ColorBuilder + where TColor : struct, IPackedPixel, IEquatable + { + /// + /// Creates a new representation from the string representing a color. + /// + /// + /// The hexadecimal representation of the combined color components arranged + /// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax. + /// + /// Returns a that represents the color defined by the provided RGBA heax string. + public static TColor FromHex(string hex) + { + Guard.NotNullOrEmpty(hex, nameof(hex)); + + hex = ToRgbaHex(hex); + uint packedValue; + if (hex == null || !uint.TryParse(hex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out packedValue)) + { + throw new ArgumentException("Hexadecimal string is not in the correct format.", nameof(hex)); + } + + TColor result = default(TColor); + + result.PackFromBytes( + (byte)(packedValue >> 24), + (byte)(packedValue >> 16), + (byte)(packedValue >> 8), + (byte)(packedValue >> 0)); + return result; + } + + /// + /// Creates a new representation from standard RGB bytes with 100% opacity. + /// + /// The red intensity. + /// The green intensity. + /// The blue intensity. + /// Returns a that represents the color defined by the provided RGB values with 100% opacity. + public static TColor FromRGB(byte red, byte green, byte blue) + { + TColor color = default(TColor); + color.PackFromBytes(red, green, blue, 255); + return color; + } + + /// + /// Creates a new representation from standard RGBA bytes. + /// + /// The red intensity. + /// The green intensity. + /// The blue intensity. + /// The alpha intensity. + /// Returns a that represents the color defined by the provided RGBA values. + public static TColor FromRGBA(byte red, byte green, byte blue, byte alpha) + { + TColor color = default(TColor); + color.PackFromBytes(red, green, blue, alpha); + return color; + } + + /// + /// Converts the specified hex value to an rrggbbaa hex value. + /// + /// The hex value to convert. + /// + /// A rrggbbaa hex value. + /// + private static string ToRgbaHex(string hex) + { + hex = hex.StartsWith("#") ? hex.Substring(1) : hex; + + if (hex.Length == 8) + { + return hex; + } + + if (hex.Length == 6) + { + return hex + "FF"; + } + + if (hex.Length < 3 || hex.Length > 4) + { + return null; + } + + string red = char.ToString(hex[0]); + string green = char.ToString(hex[1]); + string blue = char.ToString(hex[2]); + string alpha = hex.Length == 3 ? "F" : char.ToString(hex[3]); + + return string.Concat(red, red, green, green, blue, blue, alpha, alpha); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Colors/ColorDefinitions.cs b/src/ImageSharp/Colors/ColorDefinitions.cs index 5c1c30fbd..65165289d 100644 --- a/src/ImageSharp/Colors/ColorDefinitions.cs +++ b/src/ImageSharp/Colors/ColorDefinitions.cs @@ -18,711 +18,711 @@ namespace ImageSharp /// /// Represents a matching the W3C definition that has an hex value of #F0F8FF. /// - public static readonly Color AliceBlue = new Color(240, 248, 255, 255); + public static readonly Color AliceBlue = NamedColors.AliceBlue; /// /// Represents a matching the W3C definition that has an hex value of #FAEBD7. /// - public static readonly Color AntiqueWhite = new Color(250, 235, 215, 255); + public static readonly Color AntiqueWhite = NamedColors.AntiqueWhite; /// /// Represents a matching the W3C definition that has an hex value of #00FFFF. /// - public static readonly Color Aqua = new Color(0, 255, 255, 255); + public static readonly Color Aqua = NamedColors.Aqua; /// /// Represents a matching the W3C definition that has an hex value of #7FFFD4. /// - public static readonly Color Aquamarine = new Color(127, 255, 212, 255); + public static readonly Color Aquamarine = NamedColors.Aquamarine; /// /// Represents a matching the W3C definition that has an hex value of #F0FFFF. /// - public static readonly Color Azure = new Color(240, 255, 255, 255); + public static readonly Color Azure = NamedColors.Azure; /// /// Represents a matching the W3C definition that has an hex value of #F5F5DC. /// - public static readonly Color Beige = new Color(245, 245, 220, 255); + public static readonly Color Beige = NamedColors.Beige; /// /// Represents a matching the W3C definition that has an hex value of #FFE4C4. /// - public static readonly Color Bisque = new Color(255, 228, 196, 255); + public static readonly Color Bisque = NamedColors.Bisque; /// /// Represents a matching the W3C definition that has an hex value of #000000. /// - public static readonly Color Black = new Color(0, 0, 0, 255); + public static readonly Color Black = NamedColors.Black; /// /// Represents a matching the W3C definition that has an hex value of #FFEBCD. /// - public static readonly Color BlanchedAlmond = new Color(255, 235, 205, 255); + public static readonly Color BlanchedAlmond = NamedColors.BlanchedAlmond; /// /// Represents a matching the W3C definition that has an hex value of #0000FF. /// - public static readonly Color Blue = new Color(0, 0, 255, 255); + public static readonly Color Blue = NamedColors.Blue; /// /// Represents a matching the W3C definition that has an hex value of #8A2BE2. /// - public static readonly Color BlueViolet = new Color(138, 43, 226, 255); + public static readonly Color BlueViolet = NamedColors.BlueViolet; /// /// Represents a matching the W3C definition that has an hex value of #A52A2A. /// - public static readonly Color Brown = new Color(165, 42, 42, 255); + public static readonly Color Brown = NamedColors.Brown; /// /// Represents a matching the W3C definition that has an hex value of #DEB887. /// - public static readonly Color BurlyWood = new Color(222, 184, 135, 255); + public static readonly Color BurlyWood = NamedColors.BurlyWood; /// /// Represents a matching the W3C definition that has an hex value of #5F9EA0. /// - public static readonly Color CadetBlue = new Color(95, 158, 160, 255); + public static readonly Color CadetBlue = NamedColors.CadetBlue; /// /// Represents a matching the W3C definition that has an hex value of #7FFF00. /// - public static readonly Color Chartreuse = new Color(127, 255, 0, 255); + public static readonly Color Chartreuse = NamedColors.Chartreuse; /// /// Represents a matching the W3C definition that has an hex value of #D2691E. /// - public static readonly Color Chocolate = new Color(210, 105, 30, 255); + public static readonly Color Chocolate = NamedColors.Chocolate; /// /// Represents a matching the W3C definition that has an hex value of #FF7F50. /// - public static readonly Color Coral = new Color(255, 127, 80, 255); + public static readonly Color Coral = NamedColors.Coral; /// /// Represents a matching the W3C definition that has an hex value of #6495ED. /// - public static readonly Color CornflowerBlue = new Color(100, 149, 237, 255); + public static readonly Color CornflowerBlue = NamedColors.CornflowerBlue; /// /// Represents a matching the W3C definition that has an hex value of #FFF8DC. /// - public static readonly Color Cornsilk = new Color(255, 248, 220, 255); + public static readonly Color Cornsilk = NamedColors.Cornsilk; /// /// Represents a matching the W3C definition that has an hex value of #DC143C. /// - public static readonly Color Crimson = new Color(220, 20, 60, 255); + public static readonly Color Crimson = NamedColors.Crimson; /// /// Represents a matching the W3C definition that has an hex value of #00FFFF. /// - public static readonly Color Cyan = new Color(0, 255, 255, 255); + public static readonly Color Cyan = NamedColors.Cyan; /// /// Represents a matching the W3C definition that has an hex value of #00008B. /// - public static readonly Color DarkBlue = new Color(0, 0, 139, 255); + public static readonly Color DarkBlue = NamedColors.DarkBlue; /// /// Represents a matching the W3C definition that has an hex value of #008B8B. /// - public static readonly Color DarkCyan = new Color(0, 139, 139, 255); + public static readonly Color DarkCyan = NamedColors.DarkCyan; /// /// Represents a matching the W3C definition that has an hex value of #B8860B. /// - public static readonly Color DarkGoldenrod = new Color(184, 134, 11, 255); + public static readonly Color DarkGoldenrod = NamedColors.DarkGoldenrod; /// /// Represents a matching the W3C definition that has an hex value of #A9A9A9. /// - public static readonly Color DarkGray = new Color(169, 169, 169, 255); + public static readonly Color DarkGray = NamedColors.DarkGray; /// /// Represents a matching the W3C definition that has an hex value of #006400. /// - public static readonly Color DarkGreen = new Color(0, 100, 0, 255); + public static readonly Color DarkGreen = NamedColors.DarkGreen; /// /// Represents a matching the W3C definition that has an hex value of #BDB76B. /// - public static readonly Color DarkKhaki = new Color(189, 183, 107, 255); + public static readonly Color DarkKhaki = NamedColors.DarkKhaki; /// /// Represents a matching the W3C definition that has an hex value of #8B008B. /// - public static readonly Color DarkMagenta = new Color(139, 0, 139, 255); + public static readonly Color DarkMagenta = NamedColors.DarkMagenta; /// /// Represents a matching the W3C definition that has an hex value of #556B2F. /// - public static readonly Color DarkOliveGreen = new Color(85, 107, 47, 255); + public static readonly Color DarkOliveGreen = NamedColors.DarkOliveGreen; /// /// Represents a matching the W3C definition that has an hex value of #FF8C00. /// - public static readonly Color DarkOrange = new Color(255, 140, 0, 255); + public static readonly Color DarkOrange = NamedColors.DarkOrange; /// /// Represents a matching the W3C definition that has an hex value of #9932CC. /// - public static readonly Color DarkOrchid = new Color(153, 50, 204, 255); + public static readonly Color DarkOrchid = NamedColors.DarkOrchid; /// /// Represents a matching the W3C definition that has an hex value of #8B0000. /// - public static readonly Color DarkRed = new Color(139, 0, 0, 255); + public static readonly Color DarkRed = NamedColors.DarkRed; /// /// Represents a matching the W3C definition that has an hex value of #E9967A. /// - public static readonly Color DarkSalmon = new Color(233, 150, 122, 255); + public static readonly Color DarkSalmon = NamedColors.DarkSalmon; /// /// Represents a matching the W3C definition that has an hex value of #8FBC8B. /// - public static readonly Color DarkSeaGreen = new Color(143, 188, 139, 255); + public static readonly Color DarkSeaGreen = NamedColors.DarkSeaGreen; /// /// Represents a matching the W3C definition that has an hex value of #483D8B. /// - public static readonly Color DarkSlateBlue = new Color(72, 61, 139, 255); + public static readonly Color DarkSlateBlue = NamedColors.DarkSlateBlue; /// /// Represents a matching the W3C definition that has an hex value of #2F4F4F. /// - public static readonly Color DarkSlateGray = new Color(47, 79, 79, 255); + public static readonly Color DarkSlateGray = NamedColors.DarkSlateGray; /// /// Represents a matching the W3C definition that has an hex value of #00CED1. /// - public static readonly Color DarkTurquoise = new Color(0, 206, 209, 255); + public static readonly Color DarkTurquoise = NamedColors.DarkTurquoise; /// /// Represents a matching the W3C definition that has an hex value of #9400D3. /// - public static readonly Color DarkViolet = new Color(148, 0, 211, 255); + public static readonly Color DarkViolet = NamedColors.DarkViolet; /// /// Represents a matching the W3C definition that has an hex value of #FF1493. /// - public static readonly Color DeepPink = new Color(255, 20, 147, 255); + public static readonly Color DeepPink = NamedColors.DeepPink; /// /// Represents a matching the W3C definition that has an hex value of #00BFFF. /// - public static readonly Color DeepSkyBlue = new Color(0, 191, 255, 255); + public static readonly Color DeepSkyBlue = NamedColors.DeepSkyBlue; /// /// Represents a matching the W3C definition that has an hex value of #696969. /// - public static readonly Color DimGray = new Color(105, 105, 105, 255); + public static readonly Color DimGray = NamedColors.DimGray; /// /// Represents a matching the W3C definition that has an hex value of #1E90FF. /// - public static readonly Color DodgerBlue = new Color(30, 144, 255, 255); + public static readonly Color DodgerBlue = NamedColors.DodgerBlue; /// /// Represents a matching the W3C definition that has an hex value of #B22222. /// - public static readonly Color Firebrick = new Color(178, 34, 34, 255); + public static readonly Color Firebrick = NamedColors.Firebrick; /// /// Represents a matching the W3C definition that has an hex value of #FFFAF0. /// - public static readonly Color FloralWhite = new Color(255, 250, 240, 255); + public static readonly Color FloralWhite = NamedColors.FloralWhite; /// /// Represents a matching the W3C definition that has an hex value of #228B22. /// - public static readonly Color ForestGreen = new Color(34, 139, 34, 255); + public static readonly Color ForestGreen = NamedColors.ForestGreen; /// /// Represents a matching the W3C definition that has an hex value of #FF00FF. /// - public static readonly Color Fuchsia = new Color(255, 0, 255, 255); + public static readonly Color Fuchsia = NamedColors.Fuchsia; /// /// Represents a matching the W3C definition that has an hex value of #DCDCDC. /// - public static readonly Color Gainsboro = new Color(220, 220, 220, 255); + public static readonly Color Gainsboro = NamedColors.Gainsboro; /// /// Represents a matching the W3C definition that has an hex value of #F8F8FF. /// - public static readonly Color GhostWhite = new Color(248, 248, 255, 255); + public static readonly Color GhostWhite = NamedColors.GhostWhite; /// /// Represents a matching the W3C definition that has an hex value of #FFD700. /// - public static readonly Color Gold = new Color(255, 215, 0, 255); + public static readonly Color Gold = NamedColors.Gold; /// /// Represents a matching the W3C definition that has an hex value of #DAA520. /// - public static readonly Color Goldenrod = new Color(218, 165, 32, 255); + public static readonly Color Goldenrod = NamedColors.Goldenrod; /// /// Represents a matching the W3C definition that has an hex value of #808080. /// - public static readonly Color Gray = new Color(128, 128, 128, 255); + public static readonly Color Gray = NamedColors.Gray; /// /// Represents a matching the W3C definition that has an hex value of #008000. /// - public static readonly Color Green = new Color(0, 128, 0, 255); + public static readonly Color Green = NamedColors.Green; /// /// Represents a matching the W3C definition that has an hex value of #ADFF2F. /// - public static readonly Color GreenYellow = new Color(173, 255, 47, 255); + public static readonly Color GreenYellow = NamedColors.GreenYellow; /// /// Represents a matching the W3C definition that has an hex value of #F0FFF0. /// - public static readonly Color Honeydew = new Color(240, 255, 240, 255); + public static readonly Color Honeydew = NamedColors.Honeydew; /// /// Represents a matching the W3C definition that has an hex value of #FF69B4. /// - public static readonly Color HotPink = new Color(255, 105, 180, 255); + public static readonly Color HotPink = NamedColors.HotPink; /// /// Represents a matching the W3C definition that has an hex value of #CD5C5C. /// - public static readonly Color IndianRed = new Color(205, 92, 92, 255); + public static readonly Color IndianRed = NamedColors.IndianRed; /// /// Represents a matching the W3C definition that has an hex value of #4B0082. /// - public static readonly Color Indigo = new Color(75, 0, 130, 255); + public static readonly Color Indigo = NamedColors.Indigo; /// /// Represents a matching the W3C definition that has an hex value of #FFFFF0. /// - public static readonly Color Ivory = new Color(255, 255, 240, 255); + public static readonly Color Ivory = NamedColors.Ivory; /// /// Represents a matching the W3C definition that has an hex value of #F0E68C. /// - public static readonly Color Khaki = new Color(240, 230, 140, 255); + public static readonly Color Khaki = NamedColors.Khaki; /// /// Represents a matching the W3C definition that has an hex value of #E6E6FA. /// - public static readonly Color Lavender = new Color(230, 230, 250, 255); + public static readonly Color Lavender = NamedColors.Lavender; /// /// Represents a matching the W3C definition that has an hex value of #FFF0F5. /// - public static readonly Color LavenderBlush = new Color(255, 240, 245, 255); + public static readonly Color LavenderBlush = NamedColors.LavenderBlush; /// /// Represents a matching the W3C definition that has an hex value of #7CFC00. /// - public static readonly Color LawnGreen = new Color(124, 252, 0, 255); + public static readonly Color LawnGreen = NamedColors.LawnGreen; /// /// Represents a matching the W3C definition that has an hex value of #FFFACD. /// - public static readonly Color LemonChiffon = new Color(255, 250, 205, 255); + public static readonly Color LemonChiffon = NamedColors.LemonChiffon; /// /// Represents a matching the W3C definition that has an hex value of #ADD8E6. /// - public static readonly Color LightBlue = new Color(173, 216, 230, 255); + public static readonly Color LightBlue = NamedColors.LightBlue; /// /// Represents a matching the W3C definition that has an hex value of #F08080. /// - public static readonly Color LightCoral = new Color(240, 128, 128, 255); + public static readonly Color LightCoral = NamedColors.LightCoral; /// /// Represents a matching the W3C definition that has an hex value of #E0FFFF. /// - public static readonly Color LightCyan = new Color(224, 255, 255, 255); + public static readonly Color LightCyan = NamedColors.LightCyan; /// /// Represents a matching the W3C definition that has an hex value of #FAFAD2. /// - public static readonly Color LightGoldenrodYellow = new Color(250, 250, 210, 255); + public static readonly Color LightGoldenrodYellow = NamedColors.LightGoldenrodYellow; /// /// Represents a matching the W3C definition that has an hex value of #D3D3D3. /// - public static readonly Color LightGray = new Color(211, 211, 211, 255); + public static readonly Color LightGray = NamedColors.LightGray; /// /// Represents a matching the W3C definition that has an hex value of #90EE90. /// - public static readonly Color LightGreen = new Color(144, 238, 144, 255); + public static readonly Color LightGreen = NamedColors.LightGreen; /// /// Represents a matching the W3C definition that has an hex value of #FFB6C1. /// - public static readonly Color LightPink = new Color(255, 182, 193, 255); + public static readonly Color LightPink = NamedColors.LightPink; /// /// Represents a matching the W3C definition that has an hex value of #FFA07A. /// - public static readonly Color LightSalmon = new Color(255, 160, 122, 255); + public static readonly Color LightSalmon = NamedColors.LightSalmon; /// /// Represents a matching the W3C definition that has an hex value of #20B2AA. /// - public static readonly Color LightSeaGreen = new Color(32, 178, 170, 255); + public static readonly Color LightSeaGreen = NamedColors.LightSeaGreen; /// /// Represents a matching the W3C definition that has an hex value of #87CEFA. /// - public static readonly Color LightSkyBlue = new Color(135, 206, 250, 255); + public static readonly Color LightSkyBlue = NamedColors.LightSkyBlue; /// /// Represents a matching the W3C definition that has an hex value of #778899. /// - public static readonly Color LightSlateGray = new Color(119, 136, 153, 255); + public static readonly Color LightSlateGray = NamedColors.LightSlateGray; /// /// Represents a matching the W3C definition that has an hex value of #B0C4DE. /// - public static readonly Color LightSteelBlue = new Color(176, 196, 222, 255); + public static readonly Color LightSteelBlue = NamedColors.LightSteelBlue; /// /// Represents a matching the W3C definition that has an hex value of #FFFFE0. /// - public static readonly Color LightYellow = new Color(255, 255, 224, 255); + public static readonly Color LightYellow = NamedColors.LightYellow; /// /// Represents a matching the W3C definition that has an hex value of #00FF00. /// - public static readonly Color Lime = new Color(0, 255, 0, 255); + public static readonly Color Lime = NamedColors.Lime; /// /// Represents a matching the W3C definition that has an hex value of #32CD32. /// - public static readonly Color LimeGreen = new Color(50, 205, 50, 255); + public static readonly Color LimeGreen = NamedColors.LimeGreen; /// /// Represents a matching the W3C definition that has an hex value of #FAF0E6. /// - public static readonly Color Linen = new Color(250, 240, 230, 255); + public static readonly Color Linen = NamedColors.Linen; /// /// Represents a matching the W3C definition that has an hex value of #FF00FF. /// - public static readonly Color Magenta = new Color(255, 0, 255, 255); + public static readonly Color Magenta = NamedColors.Magenta; /// /// Represents a matching the W3C definition that has an hex value of #800000. /// - public static readonly Color Maroon = new Color(128, 0, 0, 255); + public static readonly Color Maroon = NamedColors.Maroon; /// /// Represents a matching the W3C definition that has an hex value of #66CDAA. /// - public static readonly Color MediumAquamarine = new Color(102, 205, 170, 255); + public static readonly Color MediumAquamarine = NamedColors.MediumAquamarine; /// /// Represents a matching the W3C definition that has an hex value of #0000CD. /// - public static readonly Color MediumBlue = new Color(0, 0, 205, 255); + public static readonly Color MediumBlue = NamedColors.MediumBlue; /// /// Represents a matching the W3C definition that has an hex value of #BA55D3. /// - public static readonly Color MediumOrchid = new Color(186, 85, 211, 255); + public static readonly Color MediumOrchid = NamedColors.MediumOrchid; /// /// Represents a matching the W3C definition that has an hex value of #9370DB. /// - public static readonly Color MediumPurple = new Color(147, 112, 219, 255); + public static readonly Color MediumPurple = NamedColors.MediumPurple; /// /// Represents a matching the W3C definition that has an hex value of #3CB371. /// - public static readonly Color MediumSeaGreen = new Color(60, 179, 113, 255); + public static readonly Color MediumSeaGreen = NamedColors.MediumSeaGreen; /// /// Represents a matching the W3C definition that has an hex value of #7B68EE. /// - public static readonly Color MediumSlateBlue = new Color(123, 104, 238, 255); + public static readonly Color MediumSlateBlue = NamedColors.MediumSlateBlue; /// /// Represents a matching the W3C definition that has an hex value of #00FA9A. /// - public static readonly Color MediumSpringGreen = new Color(0, 250, 154, 255); + public static readonly Color MediumSpringGreen = NamedColors.MediumSpringGreen; /// /// Represents a matching the W3C definition that has an hex value of #48D1CC. /// - public static readonly Color MediumTurquoise = new Color(72, 209, 204, 255); + public static readonly Color MediumTurquoise = NamedColors.MediumTurquoise; /// /// Represents a matching the W3C definition that has an hex value of #C71585. /// - public static readonly Color MediumVioletRed = new Color(199, 21, 133, 255); + public static readonly Color MediumVioletRed = NamedColors.MediumVioletRed; /// /// Represents a matching the W3C definition that has an hex value of #191970. /// - public static readonly Color MidnightBlue = new Color(25, 25, 112, 255); + public static readonly Color MidnightBlue = NamedColors.MidnightBlue; /// /// Represents a matching the W3C definition that has an hex value of #F5FFFA. /// - public static readonly Color MintCream = new Color(245, 255, 250, 255); + public static readonly Color MintCream = NamedColors.MintCream; /// /// Represents a matching the W3C definition that has an hex value of #FFE4E1. /// - public static readonly Color MistyRose = new Color(255, 228, 225, 255); + public static readonly Color MistyRose = NamedColors.MistyRose; /// /// Represents a matching the W3C definition that has an hex value of #FFE4B5. /// - public static readonly Color Moccasin = new Color(255, 228, 181, 255); + public static readonly Color Moccasin = NamedColors.Moccasin; /// /// Represents a matching the W3C definition that has an hex value of #FFDEAD. /// - public static readonly Color NavajoWhite = new Color(255, 222, 173, 255); + public static readonly Color NavajoWhite = NamedColors.NavajoWhite; /// /// Represents a matching the W3C definition that has an hex value of #000080. /// - public static readonly Color Navy = new Color(0, 0, 128, 255); + public static readonly Color Navy = NamedColors.Navy; /// /// Represents a matching the W3C definition that has an hex value of #FDF5E6. /// - public static readonly Color OldLace = new Color(253, 245, 230, 255); + public static readonly Color OldLace = NamedColors.OldLace; /// /// Represents a matching the W3C definition that has an hex value of #808000. /// - public static readonly Color Olive = new Color(128, 128, 0, 255); + public static readonly Color Olive = NamedColors.Olive; /// /// Represents a matching the W3C definition that has an hex value of #6B8E23. /// - public static readonly Color OliveDrab = new Color(107, 142, 35, 255); + public static readonly Color OliveDrab = NamedColors.OliveDrab; /// /// Represents a matching the W3C definition that has an hex value of #FFA500. /// - public static readonly Color Orange = new Color(255, 165, 0, 255); + public static readonly Color Orange = NamedColors.Orange; /// /// Represents a matching the W3C definition that has an hex value of #FF4500. /// - public static readonly Color OrangeRed = new Color(255, 69, 0, 255); + public static readonly Color OrangeRed = NamedColors.OrangeRed; /// /// Represents a matching the W3C definition that has an hex value of #DA70D6. /// - public static readonly Color Orchid = new Color(218, 112, 214, 255); + public static readonly Color Orchid = NamedColors.Orchid; /// /// Represents a matching the W3C definition that has an hex value of #EEE8AA. /// - public static readonly Color PaleGoldenrod = new Color(238, 232, 170, 255); + public static readonly Color PaleGoldenrod = NamedColors.PaleGoldenrod; /// /// Represents a matching the W3C definition that has an hex value of #98FB98. /// - public static readonly Color PaleGreen = new Color(152, 251, 152, 255); + public static readonly Color PaleGreen = NamedColors.PaleGreen; /// /// Represents a matching the W3C definition that has an hex value of #AFEEEE. /// - public static readonly Color PaleTurquoise = new Color(175, 238, 238, 255); + public static readonly Color PaleTurquoise = NamedColors.PaleTurquoise; /// /// Represents a matching the W3C definition that has an hex value of #DB7093. /// - public static readonly Color PaleVioletRed = new Color(219, 112, 147, 255); + public static readonly Color PaleVioletRed = NamedColors.PaleVioletRed; /// /// Represents a matching the W3C definition that has an hex value of #FFEFD5. /// - public static readonly Color PapayaWhip = new Color(255, 239, 213, 255); + public static readonly Color PapayaWhip = NamedColors.PapayaWhip; /// /// Represents a matching the W3C definition that has an hex value of #FFDAB9. /// - public static readonly Color PeachPuff = new Color(255, 218, 185, 255); + public static readonly Color PeachPuff = NamedColors.PeachPuff; /// /// Represents a matching the W3C definition that has an hex value of #CD853F. /// - public static readonly Color Peru = new Color(205, 133, 63, 255); + public static readonly Color Peru = NamedColors.Peru; /// /// Represents a matching the W3C definition that has an hex value of #FFC0CB. /// - public static readonly Color Pink = new Color(255, 192, 203, 255); + public static readonly Color Pink = NamedColors.Pink; /// /// Represents a matching the W3C definition that has an hex value of #DDA0DD. /// - public static readonly Color Plum = new Color(221, 160, 221, 255); + public static readonly Color Plum = NamedColors.Plum; /// /// Represents a matching the W3C definition that has an hex value of #B0E0E6. /// - public static readonly Color PowderBlue = new Color(176, 224, 230, 255); + public static readonly Color PowderBlue = NamedColors.PowderBlue; /// /// Represents a matching the W3C definition that has an hex value of #800080. /// - public static readonly Color Purple = new Color(128, 0, 128, 255); + public static readonly Color Purple = NamedColors.Purple; /// /// Represents a matching the W3C definition that has an hex value of #663399. /// - public static readonly Color RebeccaPurple = new Color(102, 51, 153, 255); + public static readonly Color RebeccaPurple = NamedColors.RebeccaPurple; /// /// Represents a matching the W3C definition that has an hex value of #FF0000. /// - public static readonly Color Red = new Color(255, 0, 0, 255); + public static readonly Color Red = NamedColors.Red; /// /// Represents a matching the W3C definition that has an hex value of #BC8F8F. /// - public static readonly Color RosyBrown = new Color(188, 143, 143, 255); + public static readonly Color RosyBrown = NamedColors.RosyBrown; /// /// Represents a matching the W3C definition that has an hex value of #4169E1. /// - public static readonly Color RoyalBlue = new Color(65, 105, 225, 255); + public static readonly Color RoyalBlue = NamedColors.RoyalBlue; /// /// Represents a matching the W3C definition that has an hex value of #8B4513. /// - public static readonly Color SaddleBrown = new Color(139, 69, 19, 255); + public static readonly Color SaddleBrown = NamedColors.SaddleBrown; /// /// Represents a matching the W3C definition that has an hex value of #FA8072. /// - public static readonly Color Salmon = new Color(250, 128, 114, 255); + public static readonly Color Salmon = NamedColors.Salmon; /// /// Represents a matching the W3C definition that has an hex value of #F4A460. /// - public static readonly Color SandyBrown = new Color(244, 164, 96, 255); + public static readonly Color SandyBrown = NamedColors.SandyBrown; /// /// Represents a matching the W3C definition that has an hex value of #2E8B57. /// - public static readonly Color SeaGreen = new Color(46, 139, 87, 255); + public static readonly Color SeaGreen = NamedColors.SeaGreen; /// /// Represents a matching the W3C definition that has an hex value of #FFF5EE. /// - public static readonly Color SeaShell = new Color(255, 245, 238, 255); + public static readonly Color SeaShell = NamedColors.SeaShell; /// /// Represents a matching the W3C definition that has an hex value of #A0522D. /// - public static readonly Color Sienna = new Color(160, 82, 45, 255); + public static readonly Color Sienna = NamedColors.Sienna; /// /// Represents a matching the W3C definition that has an hex value of #C0C0C0. /// - public static readonly Color Silver = new Color(192, 192, 192, 255); + public static readonly Color Silver = NamedColors.Silver; /// /// Represents a matching the W3C definition that has an hex value of #87CEEB. /// - public static readonly Color SkyBlue = new Color(135, 206, 235, 255); + public static readonly Color SkyBlue = NamedColors.SkyBlue; /// /// Represents a matching the W3C definition that has an hex value of #6A5ACD. /// - public static readonly Color SlateBlue = new Color(106, 90, 205, 255); + public static readonly Color SlateBlue = NamedColors.SlateBlue; /// /// Represents a matching the W3C definition that has an hex value of #708090. /// - public static readonly Color SlateGray = new Color(112, 128, 144, 255); + public static readonly Color SlateGray = NamedColors.SlateGray; /// /// Represents a matching the W3C definition that has an hex value of #FFFAFA. /// - public static readonly Color Snow = new Color(255, 250, 250, 255); + public static readonly Color Snow = NamedColors.Snow; /// /// Represents a matching the W3C definition that has an hex value of #00FF7F. /// - public static readonly Color SpringGreen = new Color(0, 255, 127, 255); + public static readonly Color SpringGreen = NamedColors.SpringGreen; /// /// Represents a matching the W3C definition that has an hex value of #4682B4. /// - public static readonly Color SteelBlue = new Color(70, 130, 180, 255); + public static readonly Color SteelBlue = NamedColors.SteelBlue; /// /// Represents a matching the W3C definition that has an hex value of #D2B48C. /// - public static readonly Color Tan = new Color(210, 180, 140, 255); + public static readonly Color Tan = NamedColors.Tan; /// /// Represents a matching the W3C definition that has an hex value of #008080. /// - public static readonly Color Teal = new Color(0, 128, 128, 255); + public static readonly Color Teal = NamedColors.Teal; /// /// Represents a matching the W3C definition that has an hex value of #D8BFD8. /// - public static readonly Color Thistle = new Color(216, 191, 216, 255); + public static readonly Color Thistle = NamedColors.Thistle; /// /// Represents a matching the W3C definition that has an hex value of #FF6347. /// - public static readonly Color Tomato = new Color(255, 99, 71, 255); + public static readonly Color Tomato = NamedColors.Tomato; /// /// Represents a matching the W3C definition that has an hex value of #FFFFFF. /// - public static readonly Color Transparent = new Color(255, 255, 255, 0); + public static readonly Color Transparent = NamedColors.Transparent; /// /// Represents a matching the W3C definition that has an hex value of #40E0D0. /// - public static readonly Color Turquoise = new Color(64, 224, 208, 255); + public static readonly Color Turquoise = NamedColors.Turquoise; /// /// Represents a matching the W3C definition that has an hex value of #EE82EE. /// - public static readonly Color Violet = new Color(238, 130, 238, 255); + public static readonly Color Violet = NamedColors.Violet; /// /// Represents a matching the W3C definition that has an hex value of #F5DEB3. /// - public static readonly Color Wheat = new Color(245, 222, 179, 255); + public static readonly Color Wheat = NamedColors.Wheat; /// /// Represents a matching the W3C definition that has an hex value of #FFFFFF. /// - public static readonly Color White = new Color(255, 255, 255, 255); + public static readonly Color White = NamedColors.White; /// /// Represents a matching the W3C definition that has an hex value of #F5F5F5. /// - public static readonly Color WhiteSmoke = new Color(245, 245, 245, 255); + public static readonly Color WhiteSmoke = NamedColors.WhiteSmoke; /// /// Represents a matching the W3C definition that has an hex value of #FFFF00. /// - public static readonly Color Yellow = new Color(255, 255, 0, 255); + public static readonly Color Yellow = NamedColors.Yellow; /// /// Represents a matching the W3C definition that has an hex value of #9ACD32. /// - public static readonly Color YellowGreen = new Color(154, 205, 50, 255); + public static readonly Color YellowGreen = NamedColors.YellowGreen; } } \ No newline at end of file diff --git a/src/ImageSharp/Colors/NamedColors{TColor}.cs b/src/ImageSharp/Colors/NamedColors{TColor}.cs new file mode 100644 index 000000000..77a537f0f --- /dev/null +++ b/src/ImageSharp/Colors/NamedColors{TColor}.cs @@ -0,0 +1,727 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + + /// + /// A set of named colors mapped to the provided Color space. + /// + /// The type of the color. + public static class NamedColors + where TColor : struct, IPackedPixel, IEquatable + { + /// + /// Represents a matching the W3C definition that has an hex value of #F0F8FF. + /// + public static readonly TColor AliceBlue = ColorBuilder.FromRGBA(240, 248, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FAEBD7. + /// + public static readonly TColor AntiqueWhite = ColorBuilder.FromRGBA(250, 235, 215, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #00FFFF. + /// + public static readonly TColor Aqua = ColorBuilder.FromRGBA(0, 255, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #7FFFD4. + /// + public static readonly TColor Aquamarine = ColorBuilder.FromRGBA(127, 255, 212, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F0FFFF. + /// + public static readonly TColor Azure = ColorBuilder.FromRGBA(240, 255, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F5F5DC. + /// + public static readonly TColor Beige = ColorBuilder.FromRGBA(245, 245, 220, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFE4C4. + /// + public static readonly TColor Bisque = ColorBuilder.FromRGBA(255, 228, 196, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #000000. + /// + public static readonly TColor Black = ColorBuilder.FromRGBA(0, 0, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFEBCD. + /// + public static readonly TColor BlanchedAlmond = ColorBuilder.FromRGBA(255, 235, 205, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #0000FF. + /// + public static readonly TColor Blue = ColorBuilder.FromRGBA(0, 0, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #8A2BE2. + /// + public static readonly TColor BlueViolet = ColorBuilder.FromRGBA(138, 43, 226, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #A52A2A. + /// + public static readonly TColor Brown = ColorBuilder.FromRGBA(165, 42, 42, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #DEB887. + /// + public static readonly TColor BurlyWood = ColorBuilder.FromRGBA(222, 184, 135, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #5F9EA0. + /// + public static readonly TColor CadetBlue = ColorBuilder.FromRGBA(95, 158, 160, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #7FFF00. + /// + public static readonly TColor Chartreuse = ColorBuilder.FromRGBA(127, 255, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #D2691E. + /// + public static readonly TColor Chocolate = ColorBuilder.FromRGBA(210, 105, 30, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF7F50. + /// + public static readonly TColor Coral = ColorBuilder.FromRGBA(255, 127, 80, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #6495ED. + /// + public static readonly TColor CornflowerBlue = ColorBuilder.FromRGBA(100, 149, 237, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFF8DC. + /// + public static readonly TColor Cornsilk = ColorBuilder.FromRGBA(255, 248, 220, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #DC143C. + /// + public static readonly TColor Crimson = ColorBuilder.FromRGBA(220, 20, 60, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #00FFFF. + /// + public static readonly TColor Cyan = ColorBuilder.FromRGBA(0, 255, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #00008B. + /// + public static readonly TColor DarkBlue = ColorBuilder.FromRGBA(0, 0, 139, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #008B8B. + /// + public static readonly TColor DarkCyan = ColorBuilder.FromRGBA(0, 139, 139, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #B8860B. + /// + public static readonly TColor DarkGoldenrod = ColorBuilder.FromRGBA(184, 134, 11, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #A9A9A9. + /// + public static readonly TColor DarkGray = ColorBuilder.FromRGBA(169, 169, 169, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #006400. + /// + public static readonly TColor DarkGreen = ColorBuilder.FromRGBA(0, 100, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #BDB76B. + /// + public static readonly TColor DarkKhaki = ColorBuilder.FromRGBA(189, 183, 107, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #8B008B. + /// + public static readonly TColor DarkMagenta = ColorBuilder.FromRGBA(139, 0, 139, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #556B2F. + /// + public static readonly TColor DarkOliveGreen = ColorBuilder.FromRGBA(85, 107, 47, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF8C00. + /// + public static readonly TColor DarkOrange = ColorBuilder.FromRGBA(255, 140, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #9932CC. + /// + public static readonly TColor DarkOrchid = ColorBuilder.FromRGBA(153, 50, 204, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #8B0000. + /// + public static readonly TColor DarkRed = ColorBuilder.FromRGBA(139, 0, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #E9967A. + /// + public static readonly TColor DarkSalmon = ColorBuilder.FromRGBA(233, 150, 122, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #8FBC8B. + /// + public static readonly TColor DarkSeaGreen = ColorBuilder.FromRGBA(143, 188, 139, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #483D8B. + /// + public static readonly TColor DarkSlateBlue = ColorBuilder.FromRGBA(72, 61, 139, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #2F4F4F. + /// + public static readonly TColor DarkSlateGray = ColorBuilder.FromRGBA(47, 79, 79, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #00CED1. + /// + public static readonly TColor DarkTurquoise = ColorBuilder.FromRGBA(0, 206, 209, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #9400D3. + /// + public static readonly TColor DarkViolet = ColorBuilder.FromRGBA(148, 0, 211, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF1493. + /// + public static readonly TColor DeepPink = ColorBuilder.FromRGBA(255, 20, 147, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #00BFFF. + /// + public static readonly TColor DeepSkyBlue = ColorBuilder.FromRGBA(0, 191, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #696969. + /// + public static readonly TColor DimGray = ColorBuilder.FromRGBA(105, 105, 105, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #1E90FF. + /// + public static readonly TColor DodgerBlue = ColorBuilder.FromRGBA(30, 144, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #B22222. + /// + public static readonly TColor Firebrick = ColorBuilder.FromRGBA(178, 34, 34, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFAF0. + /// + public static readonly TColor FloralWhite = ColorBuilder.FromRGBA(255, 250, 240, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #228B22. + /// + public static readonly TColor ForestGreen = ColorBuilder.FromRGBA(34, 139, 34, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF00FF. + /// + public static readonly TColor Fuchsia = ColorBuilder.FromRGBA(255, 0, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #DCDCDC. + /// + public static readonly TColor Gainsboro = ColorBuilder.FromRGBA(220, 220, 220, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F8F8FF. + /// + public static readonly TColor GhostWhite = ColorBuilder.FromRGBA(248, 248, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFD700. + /// + public static readonly TColor Gold = ColorBuilder.FromRGBA(255, 215, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #DAA520. + /// + public static readonly TColor Goldenrod = ColorBuilder.FromRGBA(218, 165, 32, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #808080. + /// + public static readonly TColor Gray = ColorBuilder.FromRGBA(128, 128, 128, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #008000. + /// + public static readonly TColor Green = ColorBuilder.FromRGBA(0, 128, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #ADFF2F. + /// + public static readonly TColor GreenYellow = ColorBuilder.FromRGBA(173, 255, 47, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F0FFF0. + /// + public static readonly TColor Honeydew = ColorBuilder.FromRGBA(240, 255, 240, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF69B4. + /// + public static readonly TColor HotPink = ColorBuilder.FromRGBA(255, 105, 180, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #CD5C5C. + /// + public static readonly TColor IndianRed = ColorBuilder.FromRGBA(205, 92, 92, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #4B0082. + /// + public static readonly TColor Indigo = ColorBuilder.FromRGBA(75, 0, 130, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFFF0. + /// + public static readonly TColor Ivory = ColorBuilder.FromRGBA(255, 255, 240, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F0E68C. + /// + public static readonly TColor Khaki = ColorBuilder.FromRGBA(240, 230, 140, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #E6E6FA. + /// + public static readonly TColor Lavender = ColorBuilder.FromRGBA(230, 230, 250, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFF0F5. + /// + public static readonly TColor LavenderBlush = ColorBuilder.FromRGBA(255, 240, 245, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #7CFC00. + /// + public static readonly TColor LawnGreen = ColorBuilder.FromRGBA(124, 252, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFACD. + /// + public static readonly TColor LemonChiffon = ColorBuilder.FromRGBA(255, 250, 205, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #ADD8E6. + /// + public static readonly TColor LightBlue = ColorBuilder.FromRGBA(173, 216, 230, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F08080. + /// + public static readonly TColor LightCoral = ColorBuilder.FromRGBA(240, 128, 128, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #E0FFFF. + /// + public static readonly TColor LightCyan = ColorBuilder.FromRGBA(224, 255, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FAFAD2. + /// + public static readonly TColor LightGoldenrodYellow = ColorBuilder.FromRGBA(250, 250, 210, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #D3D3D3. + /// + public static readonly TColor LightGray = ColorBuilder.FromRGBA(211, 211, 211, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #90EE90. + /// + public static readonly TColor LightGreen = ColorBuilder.FromRGBA(144, 238, 144, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFB6C1. + /// + public static readonly TColor LightPink = ColorBuilder.FromRGBA(255, 182, 193, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFA07A. + /// + public static readonly TColor LightSalmon = ColorBuilder.FromRGBA(255, 160, 122, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #20B2AA. + /// + public static readonly TColor LightSeaGreen = ColorBuilder.FromRGBA(32, 178, 170, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #87CEFA. + /// + public static readonly TColor LightSkyBlue = ColorBuilder.FromRGBA(135, 206, 250, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #778899. + /// + public static readonly TColor LightSlateGray = ColorBuilder.FromRGBA(119, 136, 153, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #B0C4DE. + /// + public static readonly TColor LightSteelBlue = ColorBuilder.FromRGBA(176, 196, 222, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFFE0. + /// + public static readonly TColor LightYellow = ColorBuilder.FromRGBA(255, 255, 224, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #00FF00. + /// + public static readonly TColor Lime = ColorBuilder.FromRGBA(0, 255, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #32CD32. + /// + public static readonly TColor LimeGreen = ColorBuilder.FromRGBA(50, 205, 50, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FAF0E6. + /// + public static readonly TColor Linen = ColorBuilder.FromRGBA(250, 240, 230, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF00FF. + /// + public static readonly TColor Magenta = ColorBuilder.FromRGBA(255, 0, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #800000. + /// + public static readonly TColor Maroon = ColorBuilder.FromRGBA(128, 0, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #66CDAA. + /// + public static readonly TColor MediumAquamarine = ColorBuilder.FromRGBA(102, 205, 170, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #0000CD. + /// + public static readonly TColor MediumBlue = ColorBuilder.FromRGBA(0, 0, 205, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #BA55D3. + /// + public static readonly TColor MediumOrchid = ColorBuilder.FromRGBA(186, 85, 211, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #9370DB. + /// + public static readonly TColor MediumPurple = ColorBuilder.FromRGBA(147, 112, 219, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #3CB371. + /// + public static readonly TColor MediumSeaGreen = ColorBuilder.FromRGBA(60, 179, 113, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #7B68EE. + /// + public static readonly TColor MediumSlateBlue = ColorBuilder.FromRGBA(123, 104, 238, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #00FA9A. + /// + public static readonly TColor MediumSpringGreen = ColorBuilder.FromRGBA(0, 250, 154, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #48D1CC. + /// + public static readonly TColor MediumTurquoise = ColorBuilder.FromRGBA(72, 209, 204, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #C71585. + /// + public static readonly TColor MediumVioletRed = ColorBuilder.FromRGBA(199, 21, 133, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #191970. + /// + public static readonly TColor MidnightBlue = ColorBuilder.FromRGBA(25, 25, 112, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F5FFFA. + /// + public static readonly TColor MintCream = ColorBuilder.FromRGBA(245, 255, 250, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFE4E1. + /// + public static readonly TColor MistyRose = ColorBuilder.FromRGBA(255, 228, 225, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFE4B5. + /// + public static readonly TColor Moccasin = ColorBuilder.FromRGBA(255, 228, 181, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFDEAD. + /// + public static readonly TColor NavajoWhite = ColorBuilder.FromRGBA(255, 222, 173, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #000080. + /// + public static readonly TColor Navy = ColorBuilder.FromRGBA(0, 0, 128, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FDF5E6. + /// + public static readonly TColor OldLace = ColorBuilder.FromRGBA(253, 245, 230, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #808000. + /// + public static readonly TColor Olive = ColorBuilder.FromRGBA(128, 128, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #6B8E23. + /// + public static readonly TColor OliveDrab = ColorBuilder.FromRGBA(107, 142, 35, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFA500. + /// + public static readonly TColor Orange = ColorBuilder.FromRGBA(255, 165, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF4500. + /// + public static readonly TColor OrangeRed = ColorBuilder.FromRGBA(255, 69, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #DA70D6. + /// + public static readonly TColor Orchid = ColorBuilder.FromRGBA(218, 112, 214, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #EEE8AA. + /// + public static readonly TColor PaleGoldenrod = ColorBuilder.FromRGBA(238, 232, 170, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #98FB98. + /// + public static readonly TColor PaleGreen = ColorBuilder.FromRGBA(152, 251, 152, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #AFEEEE. + /// + public static readonly TColor PaleTurquoise = ColorBuilder.FromRGBA(175, 238, 238, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #DB7093. + /// + public static readonly TColor PaleVioletRed = ColorBuilder.FromRGBA(219, 112, 147, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFEFD5. + /// + public static readonly TColor PapayaWhip = ColorBuilder.FromRGBA(255, 239, 213, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFDAB9. + /// + public static readonly TColor PeachPuff = ColorBuilder.FromRGBA(255, 218, 185, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #CD853F. + /// + public static readonly TColor Peru = ColorBuilder.FromRGBA(205, 133, 63, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFC0CB. + /// + public static readonly TColor Pink = ColorBuilder.FromRGBA(255, 192, 203, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #DDA0DD. + /// + public static readonly TColor Plum = ColorBuilder.FromRGBA(221, 160, 221, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #B0E0E6. + /// + public static readonly TColor PowderBlue = ColorBuilder.FromRGBA(176, 224, 230, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #800080. + /// + public static readonly TColor Purple = ColorBuilder.FromRGBA(128, 0, 128, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #663399. + /// + public static readonly TColor RebeccaPurple = ColorBuilder.FromRGBA(102, 51, 153, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF0000. + /// + public static readonly TColor Red = ColorBuilder.FromRGBA(255, 0, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #BC8F8F. + /// + public static readonly TColor RosyBrown = ColorBuilder.FromRGBA(188, 143, 143, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #4169E1. + /// + public static readonly TColor RoyalBlue = ColorBuilder.FromRGBA(65, 105, 225, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #8B4513. + /// + public static readonly TColor SaddleBrown = ColorBuilder.FromRGBA(139, 69, 19, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FA8072. + /// + public static readonly TColor Salmon = ColorBuilder.FromRGBA(250, 128, 114, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F4A460. + /// + public static readonly TColor SandyBrown = ColorBuilder.FromRGBA(244, 164, 96, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #2E8B57. + /// + public static readonly TColor SeaGreen = ColorBuilder.FromRGBA(46, 139, 87, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFF5EE. + /// + public static readonly TColor SeaShell = ColorBuilder.FromRGBA(255, 245, 238, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #A0522D. + /// + public static readonly TColor Sienna = ColorBuilder.FromRGBA(160, 82, 45, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #C0C0C0. + /// + public static readonly TColor Silver = ColorBuilder.FromRGBA(192, 192, 192, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #87CEEB. + /// + public static readonly TColor SkyBlue = ColorBuilder.FromRGBA(135, 206, 235, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #6A5ACD. + /// + public static readonly TColor SlateBlue = ColorBuilder.FromRGBA(106, 90, 205, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #708090. + /// + public static readonly TColor SlateGray = ColorBuilder.FromRGBA(112, 128, 144, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFAFA. + /// + public static readonly TColor Snow = ColorBuilder.FromRGBA(255, 250, 250, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #00FF7F. + /// + public static readonly TColor SpringGreen = ColorBuilder.FromRGBA(0, 255, 127, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #4682B4. + /// + public static readonly TColor SteelBlue = ColorBuilder.FromRGBA(70, 130, 180, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #D2B48C. + /// + public static readonly TColor Tan = ColorBuilder.FromRGBA(210, 180, 140, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #008080. + /// + public static readonly TColor Teal = ColorBuilder.FromRGBA(0, 128, 128, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #D8BFD8. + /// + public static readonly TColor Thistle = ColorBuilder.FromRGBA(216, 191, 216, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF6347. + /// + public static readonly TColor Tomato = ColorBuilder.FromRGBA(255, 99, 71, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFFFF. + /// + public static readonly TColor Transparent = ColorBuilder.FromRGBA(255, 255, 255, 0); + + /// + /// Represents a matching the W3C definition that has an hex value of #40E0D0. + /// + public static readonly TColor Turquoise = ColorBuilder.FromRGBA(64, 224, 208, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #EE82EE. + /// + public static readonly TColor Violet = ColorBuilder.FromRGBA(238, 130, 238, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F5DEB3. + /// + public static readonly TColor Wheat = ColorBuilder.FromRGBA(245, 222, 179, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFFFF. + /// + public static readonly TColor White = ColorBuilder.FromRGBA(255, 255, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F5F5F5. + /// + public static readonly TColor WhiteSmoke = ColorBuilder.FromRGBA(245, 245, 245, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFF00. + /// + public static readonly TColor Yellow = ColorBuilder.FromRGBA(255, 255, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #9ACD32. + /// + public static readonly TColor YellowGreen = ColorBuilder.FromRGBA(154, 205, 50, 255); + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colors/ColorDefinitionTests.cs b/tests/ImageSharp.Tests/Colors/ColorDefinitionTests.cs new file mode 100644 index 000000000..899ce4f77 --- /dev/null +++ b/tests/ImageSharp.Tests/Colors/ColorDefinitionTests.cs @@ -0,0 +1,38 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Linq; + using System.Reflection; + + using ImageSharp.Colors.Spaces; + using Xunit; + public class ColorDefinitionTests + { + public static IEnumerable ColorNames => typeof(NamedColors).GetTypeInfo().GetFields().Select(x => new[] { x.Name }); + + [Theory] + [MemberData(nameof(ColorNames))] + public void AllColorsAreOnGenericAndBaseColor(string name) + { + FieldInfo generic = typeof(NamedColors).GetTypeInfo().GetField(name); + FieldInfo specific = typeof(Color).GetTypeInfo().GetField(name); + + Assert.NotNull(specific); + Assert.NotNull(generic); + Assert.True(specific.Attributes.HasFlag(FieldAttributes.Public), "specific must be public"); + Assert.True(specific.Attributes.HasFlag(FieldAttributes.Static), "specific must be static"); + Assert.True(generic.Attributes.HasFlag(FieldAttributes.Public), "generic must be public"); + Assert.True(generic.Attributes.HasFlag(FieldAttributes.Static), "generic must be static"); + Color expected = (Color)generic.GetValue(null); + Color actual = (Color)specific.GetValue(null); + Assert.Equal(expected, actual); + } + } +} diff --git a/tests/ImageSharp.Tests/Colors/ColorTests.cs b/tests/ImageSharp.Tests/Colors/ColorTests.cs index ec75ae6f7..312e66466 100644 --- a/tests/ImageSharp.Tests/Colors/ColorTests.cs +++ b/tests/ImageSharp.Tests/Colors/ColorTests.cs @@ -23,10 +23,10 @@ namespace ImageSharp.Tests { Color color1 = new Color(0, 0, 0); Color color2 = new Color(0, 0, 0, 1F); - Color color3 = new Color("#000"); - Color color4 = new Color("#000F"); - Color color5 = new Color("#000000"); - Color color6 = new Color("#000000FF"); + Color color3 = Color.FromHex("#000"); + Color color4 = Color.FromHex("#000F"); + Color color5 = Color.FromHex("#000000"); + Color color6 = Color.FromHex("#000000FF"); Assert.Equal(color1, color2); Assert.Equal(color1, color3); @@ -43,9 +43,9 @@ namespace ImageSharp.Tests { Color color1 = new Color(255, 0, 0, 255); Color color2 = new Color(0, 0, 0, 255); - Color color3 = new Color("#000"); - Color color4 = new Color("#000000"); - Color color5 = new Color("#FF000000"); + Color color3 = Color.FromHex("#000"); + Color color4 = Color.FromHex("#000000"); + Color color5 = Color.FromHex("#FF000000"); Assert.NotEqual(color1, color2); Assert.NotEqual(color1, color3); @@ -71,12 +71,6 @@ namespace ImageSharp.Tests Assert.Equal(Math.Round(.133f * 255), color2.B); Assert.Equal(255, color2.A); - Color color3 = new Color("#FF0000"); - Assert.Equal(255, color3.R); - Assert.Equal(0, color3.G); - Assert.Equal(0, color3.B); - Assert.Equal(255, color3.A); - Color color4 = new Color(new Vector3(1, .1f, .133f)); Assert.Equal(255, color4.R); Assert.Equal(Math.Round(.1f * 255), color4.G); diff --git a/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs b/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs index a0bf2cf7a..46f667e70 100644 --- a/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs +++ b/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs @@ -91,17 +91,12 @@ namespace ImageSharp.Tests [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyxw)] public void CopyToThenCopyFromWithOffset(TestImageProvider provider, ComponentOrder order) where TColor : struct, IPackedPixel, IEquatable - { using (Image destImage = new Image(8, 8)) { - TColor color; using (Image srcImage = provider.GetImage()) { - color = default(TColor); - color.PackFromBytes(255, 0, 0, 255); - - Fill(srcImage, new Rectangle(4, 4, 8, 8), color); + Fill(srcImage, new Rectangle(4, 4, 8, 8), NamedColors.Red); using (PixelAccessor srcPixels = srcImage.Lock()) { using (PixelArea area = new PixelArea(8, 8, order)) @@ -119,7 +114,7 @@ namespace ImageSharp.Tests provider.Utility.SourceFileOrDescription = order.ToString(); provider.Utility.SaveTestOutputFile(destImage, "bmp"); - using (Image expectedImage = new Image(8, 8).Fill(color)) + using (Image expectedImage = new Image(8, 8).Fill(NamedColors.Red)) { Assert.True(destImage.IsEquivalentTo(expectedImage)); } From ef09d48c1148b4331f8234ab0ef15cf03b4686fc Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 18 Feb 2017 12:55:26 +1100 Subject: [PATCH 072/142] Use Fast2DArray for all convolution algorithms. --- .../Convolution/BoxBlur.cs | 3 +- .../Convolution/BoxBlurProcessor.cs | 30 ++--- .../Convolution/Convolution2DProcessor.cs | 31 +++-- .../Convolution/Convolution2PassProcessor.cs | 74 +++++------ .../Convolution/ConvolutionProcessor.cs | 29 ++--- .../EdgeDetection/EdgeDetector2DProcessor.cs | 15 ++- .../EdgeDetectorCompassProcessor.cs | 36 +++--- .../EdgeDetection/EdgeDetectorProcessor.cs | 23 +++- .../EdgeDetection/KayyaliProcessor.cs | 38 +++--- .../EdgeDetection/KirschProcessor.cs | 121 ++++++++++-------- .../EdgeDetection/Laplacian3X3Processor.cs | 22 ++-- .../EdgeDetection/Laplacian5X5Processor.cs | 26 ++-- .../LaplacianOfGaussianProcessor.cs | 26 ++-- .../EdgeDetection/PrewittProcessor.cs | 38 +++--- .../EdgeDetection/RobertsCrossProcessor.cs | 34 ++--- .../EdgeDetection/RobinsonProcessor.cs | 121 ++++++++++-------- .../EdgeDetection/ScharrProcessor.cs | 38 +++--- .../EdgeDetection/SobelProcessor.cs | 38 +++--- .../Convolution/GaussianBlurProcessor.cs | 31 ++--- .../Convolution/GaussianSharpenProcessor.cs | 33 +++-- src/ImageSharp/Common/Helpers/ImageMaths.cs | 6 + 21 files changed, 436 insertions(+), 377 deletions(-) diff --git a/src/ImageSharp.Processing/Convolution/BoxBlur.cs b/src/ImageSharp.Processing/Convolution/BoxBlur.cs index a68c2fa44..6bdd68b9b 100644 --- a/src/ImageSharp.Processing/Convolution/BoxBlur.cs +++ b/src/ImageSharp.Processing/Convolution/BoxBlur.cs @@ -7,7 +7,6 @@ namespace ImageSharp { using System; - using Processing; using Processing.Processors; /// @@ -41,7 +40,7 @@ namespace ImageSharp public static Image BoxBlur(this Image source, int radius, Rectangle rectangle) where TColor : struct, IPackedPixel, IEquatable { - source.ApplyProcessor(new BoxBlurProcessor(), rectangle); + source.ApplyProcessor(new BoxBlurProcessor(radius), rectangle); return source; } } diff --git a/src/ImageSharp.Processing/Processors/Convolution/BoxBlurProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/BoxBlurProcessor.cs index 272b3cc8b..8ca1ca666 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/BoxBlurProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/BoxBlurProcessor.cs @@ -35,12 +35,12 @@ namespace ImageSharp.Processing.Processors /// /// Gets the horizontal gradient operator. /// - public float[][] KernelX { get; } + public Fast2DArray KernelX { get; } /// /// Gets the vertical gradient operator. /// - public float[][] KernelY { get; } + public Fast2DArray KernelY { get; } /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) @@ -52,46 +52,42 @@ namespace ImageSharp.Processing.Processors /// Create a 1 dimensional Box kernel. /// /// Whether to calculate a horizontal kernel. - /// The - private float[][] CreateBoxKernel(bool horizontal) + /// The + private Fast2DArray CreateBoxKernel(bool horizontal) { int size = this.kernelSize; - float[][] kernel = horizontal ? new float[1][] : new float[size][]; - - if (horizontal) - { - kernel[0] = new float[size]; - } - - float sum = 0.0f; + Fast2DArray kernel = horizontal + ? new Fast2DArray(new float[1, size]) + : new Fast2DArray(new float[size, 1]); + float sum = 0F; for (int i = 0; i < size; i++) { float x = 1; sum += x; if (horizontal) { - kernel[0][i] = x; + kernel[0, i] = x; } else { - kernel[i] = new[] { x }; + kernel[i, 0] = x; } } - // Normalise kernel so that the sum of all weights equals 1 + // Normalize kernel so that the sum of all weights equals 1 if (horizontal) { for (int i = 0; i < size; i++) { - kernel[0][i] = kernel[0][i] / sum; + kernel[0, i] = kernel[0, i] / sum; } } else { for (int i = 0; i < size; i++) { - kernel[i][0] = kernel[i][0] / sum; + kernel[i, 0] = kernel[i, 0] / sum; } } diff --git a/src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs index cdea43e85..71b71ba5d 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs @@ -21,7 +21,7 @@ namespace ImageSharp.Processing.Processors /// /// The horizontal gradient operator. /// The vertical gradient operator. - public Convolution2DProcessor(float[][] kernelX, float[][] kernelY) + public Convolution2DProcessor(Fast2DArray kernelX, Fast2DArray kernelY) { this.KernelX = kernelX; this.KernelY = kernelY; @@ -30,20 +30,20 @@ namespace ImageSharp.Processing.Processors /// /// Gets the horizontal gradient operator. /// - public float[][] KernelX { get; } + public Fast2DArray KernelX { get; } /// /// Gets the vertical gradient operator. /// - public float[][] KernelY { get; } + public Fast2DArray KernelY { get; } /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - int kernelYHeight = this.KernelY.Length; - int kernelYWidth = this.KernelY[0].Length; - int kernelXHeight = this.KernelX.Length; - int kernelXWidth = this.KernelX[0].Length; + int kernelYHeight = this.KernelY.Height; + int kernelYWidth = this.KernelY.Width; + int kernelXHeight = this.KernelX.Height; + int kernelXWidth = this.KernelX.Width; int radiusY = kernelYHeight >> 1; int radiusX = kernelXWidth >> 1; @@ -89,22 +89,21 @@ namespace ImageSharp.Processing.Processors offsetX = offsetX.Clamp(0, maxX); Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); - float r = currentColor.X; - float g = currentColor.Y; - float b = currentColor.Z; if (fy < kernelXHeight) { - rX += this.KernelX[fy][fx] * r; - gX += this.KernelX[fy][fx] * g; - bX += this.KernelX[fy][fx] * b; + Vector4 kx = this.KernelX[fy, fx] * currentColor; + rX += kx.X; + gX += kx.Y; + bX += kx.Z; } if (fx < kernelYWidth) { - rY += this.KernelY[fy][fx] * r; - gY += this.KernelY[fy][fx] * g; - bY += this.KernelY[fy][fx] * b; + Vector4 ky = this.KernelY[fy, fx] * currentColor; + rY += ky.X; + gY += ky.Y; + bY += ky.Z; } } } diff --git a/src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs index 71b806261..495bfa5db 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs @@ -14,14 +14,14 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class Convolution2PassProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPackedPixel, IEquatable { /// /// Initializes a new instance of the class. /// /// The horizontal gradient operator. /// The vertical gradient operator. - public Convolution2PassProcessor(float[][] kernelX, float[][] kernelY) + public Convolution2PassProcessor(Fast2DArray kernelX, Fast2DArray kernelY) { this.KernelX = kernelX; this.KernelY = kernelY; @@ -30,18 +30,16 @@ namespace ImageSharp.Processing.Processors /// /// Gets the horizontal gradient operator. /// - public float[][] KernelX { get; } + public Fast2DArray KernelX { get; } /// /// Gets the vertical gradient operator. /// - public float[][] KernelY { get; } + public Fast2DArray KernelY { get; } /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - float[][] kernelX = this.KernelX; - float[][] kernelY = this.KernelY; int width = source.Width; int height = source.Height; @@ -50,8 +48,8 @@ namespace ImageSharp.Processing.Processors using (PixelAccessor firstPassPixels = new PixelAccessor(width, height)) using (PixelAccessor sourcePixels = source.Lock()) { - this.ApplyConvolution(width, height, firstPassPixels, sourcePixels, sourceRectangle, kernelX); - this.ApplyConvolution(width, height, targetPixels, firstPassPixels, sourceRectangle, kernelY); + this.ApplyConvolution(firstPassPixels, sourcePixels, sourceRectangle, this.KernelX); + this.ApplyConvolution(targetPixels, firstPassPixels, sourceRectangle, this.KernelY); } source.SwapPixelsBuffers(targetPixels); @@ -62,18 +60,16 @@ namespace ImageSharp.Processing.Processors /// Applies the process to the specified portion of the specified at the specified location /// and with the specified size. /// - /// The image width. - /// The image height. /// The target pixels to apply the process to. /// The source pixels. Cannot be null. /// /// The structure that specifies the portion of the image object to draw. /// /// The kernel operator. - private void ApplyConvolution(int width, int height, PixelAccessor targetPixels, PixelAccessor sourcePixels, Rectangle sourceRectangle, float[][] kernel) + private void ApplyConvolution(PixelAccessor targetPixels, PixelAccessor sourcePixels, Rectangle sourceRectangle, Fast2DArray kernel) { - int kernelHeight = kernel.Length; - int kernelWidth = kernel[0].Length; + int kernelHeight = kernel.Height; + int kernelWidth = kernel.Width; int radiusY = kernelHeight >> 1; int radiusX = kernelWidth >> 1; @@ -85,40 +81,40 @@ namespace ImageSharp.Processing.Processors int maxX = endX - 1; Parallel.For( - startY, - endY, - this.ParallelOptions, - y => - { - for (int x = startX; x < endX; x++) + startY, + endY, + this.ParallelOptions, + y => { - Vector4 destination = default(Vector4); - - // Apply each matrix multiplier to the color components for each pixel. - for (int fy = 0; fy < kernelHeight; fy++) + for (int x = startX; x < endX; x++) { - int fyr = fy - radiusY; - int offsetY = y + fyr; + Vector4 destination = default(Vector4); - offsetY = offsetY.Clamp(0, maxY); - - for (int fx = 0; fx < kernelWidth; fx++) + // Apply each matrix multiplier to the color components for each pixel. + for (int fy = 0; fy < kernelHeight; fy++) { - int fxr = fx - radiusX; - int offsetX = x + fxr; + int fyr = fy - radiusY; + int offsetY = y + fyr; + + offsetY = offsetY.Clamp(0, maxY); - offsetX = offsetX.Clamp(0, maxX); + for (int fx = 0; fx < kernelWidth; fx++) + { + int fxr = fx - radiusX; + int offsetX = x + fxr; - Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); - destination += kernel[fy][fx] * currentColor; + offsetX = offsetX.Clamp(0, maxX); + + Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); + destination += kernel[fy, fx] * currentColor; + } } - } - TColor packed = default(TColor); - packed.PackFromVector4(destination); - targetPixels[x, y] = packed; - } - }); + TColor packed = default(TColor); + packed.PackFromVector4(destination); + targetPixels[x, y] = packed; + } + }); } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs index aa4940192..46df9c6ee 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs @@ -14,13 +14,13 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class ConvolutionProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPackedPixel, IEquatable { /// /// Initializes a new instance of the class. /// /// The 2d gradient operator. - public ConvolutionProcessor(float[][] kernelXY) + public ConvolutionProcessor(Fast2DArray kernelXY) { this.KernelXY = kernelXY; } @@ -28,13 +28,12 @@ namespace ImageSharp.Processing.Processors /// /// Gets the 2d gradient operator. /// - public virtual float[][] KernelXY { get; } + public Fast2DArray KernelXY { get; } /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - float[][] kernelX = this.KernelXY; - int kernelLength = kernelX.GetLength(0); + int kernelLength = this.KernelXY.Height; int radius = kernelLength >> 1; int startY = sourceRectangle.Y; @@ -56,9 +55,9 @@ namespace ImageSharp.Processing.Processors { for (int x = startX; x < endX; x++) { - float rX = 0; - float gX = 0; - float bX = 0; + float red = 0; + float green = 0; + float blue = 0; // Apply each matrix multiplier to the color components for each pixel. for (int fy = 0; fy < kernelLength; fy++) @@ -76,20 +75,14 @@ namespace ImageSharp.Processing.Processors offsetX = offsetX.Clamp(0, maxX); Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); - float r = currentColor.X; - float g = currentColor.Y; - float b = currentColor.Z; + currentColor *= this.KernelXY[fy, fx]; - rX += kernelX[fy][fx] * r; - gX += kernelX[fy][fx] * g; - bX += kernelX[fy][fx] * b; + red += currentColor.X; + green += currentColor.Y; + blue += currentColor.Z; } } - float red = rX; - float green = gX; - float blue = bX; - TColor packed = default(TColor); packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W)); targetPixels[x, y] = packed; diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs index 6ee5d0f96..f86940ade 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs @@ -14,15 +14,26 @@ namespace ImageSharp.Processing.Processors public abstract class EdgeDetector2DProcessor : ImageProcessor, IEdgeDetectorProcessor where TColor : struct, IPackedPixel, IEquatable { + /// + /// Initializes a new instance of the class. + /// + /// The horizontal gradient operator. + /// The vertical gradient operator. + protected EdgeDetector2DProcessor(Fast2DArray kernelX, Fast2DArray kernelY) + { + this.KernelX = kernelX; + this.KernelY = kernelY; + } + /// /// Gets the horizontal gradient operator. /// - public abstract float[][] KernelX { get; } + public Fast2DArray KernelX { get; } /// /// Gets the vertical gradient operator. /// - public abstract float[][] KernelY { get; } + public Fast2DArray KernelY { get; } /// public bool Grayscale { get; set; } diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs index 1a88dbe34..58967cb5b 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs @@ -19,50 +19,59 @@ namespace ImageSharp.Processing.Processors /// /// Gets the North gradient operator /// - public abstract float[][] North { get; } + public abstract Fast2DArray North { get; } /// /// Gets the NorthWest gradient operator /// - public abstract float[][] NorthWest { get; } + public abstract Fast2DArray NorthWest { get; } /// /// Gets the West gradient operator /// - public abstract float[][] West { get; } + public abstract Fast2DArray West { get; } /// /// Gets the SouthWest gradient operator /// - public abstract float[][] SouthWest { get; } + public abstract Fast2DArray SouthWest { get; } /// /// Gets the South gradient operator /// - public abstract float[][] South { get; } + public abstract Fast2DArray South { get; } /// /// Gets the SouthEast gradient operator /// - public abstract float[][] SouthEast { get; } + public abstract Fast2DArray SouthEast { get; } /// /// Gets the East gradient operator /// - public abstract float[][] East { get; } + public abstract Fast2DArray East { get; } /// /// Gets the NorthEast gradient operator /// - public abstract float[][] NorthEast { get; } + public abstract Fast2DArray NorthEast { get; } /// public bool Grayscale { get; set; } + /// + protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + { + if (this.Grayscale) + { + new GrayscaleBt709Processor().Apply(source, sourceRectangle); + } + } + /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - float[][][] kernels = { this.North, this.NorthWest, this.West, this.SouthWest, this.South, this.SouthEast, this.East, this.NorthEast }; + Fast2DArray[] kernels = { this.North, this.NorthWest, this.West, this.SouthWest, this.South, this.SouthEast, this.East, this.NorthEast }; int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -132,14 +141,5 @@ namespace ImageSharp.Processing.Processors } } } - - /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) - { - if (this.Grayscale) - { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); - } - } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs index 1033111fc..cd2b91f16 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs @@ -14,19 +14,22 @@ namespace ImageSharp.Processing.Processors public abstract class EdgeDetectorProcessor : ImageProcessor, IEdgeDetectorProcessor where TColor : struct, IPackedPixel, IEquatable { + /// + /// Initializes a new instance of the class. + /// + /// The 2d gradient operator. + protected EdgeDetectorProcessor(Fast2DArray kernelXY) + { + this.KernelXY = kernelXY; + } + /// public bool Grayscale { get; set; } /// /// Gets the 2d gradient operator. /// - public abstract float[][] KernelXY { get; } - - /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) - { - new ConvolutionProcessor(this.KernelXY).Apply(source, sourceRectangle); - } + public Fast2DArray KernelXY { get; } /// protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) @@ -36,5 +39,11 @@ namespace ImageSharp.Processing.Processors new GrayscaleBt709Processor().Apply(source, sourceRectangle); } } + + /// + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + { + new ConvolutionProcessor(this.KernelXY).Apply(source, sourceRectangle); + } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs index f628ea1b9..6e1452e17 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs @@ -20,27 +20,31 @@ namespace ImageSharp.Processing.Processors /// /// The horizontal gradient operator. /// - private static readonly float[][] KayyaliX = - { - new float[] { 6, 0, -6 }, - new float[] { 0, 0, 0 }, - new float[] { -6, 0, 6 } - }; + private static readonly Fast2DArray KayyaliX = + new Fast2DArray(new float[,] + { + { 6, 0, -6 }, + { 0, 0, 0 }, + { -6, 0, 6 } + }); /// /// The vertical gradient operator. /// - private static readonly float[][] KayyaliY = - { - new float[] { -6, 0, 6 }, - new float[] { 0, 0, 0 }, - new float[] { 6, 0, -6 } - }; - - /// - public override float[][] KernelX => KayyaliX; + private static readonly Fast2DArray KayyaliY = + new Fast2DArray(new float[,] + { + { -6, 0, 6 }, + { 0, 0, 0 }, + { 6, 0, -6 } + }); - /// - public override float[][] KernelY => KayyaliY; + /// + /// Initializes a new instance of the class. + /// + public KayyaliProcessor() + : base(KayyaliX, KayyaliY) + { + } } } diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs index 3f7e0a00e..f8cb9aba9 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs @@ -2,6 +2,7 @@ // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // + namespace ImageSharp.Processing.Processors { using System; @@ -19,105 +20,113 @@ namespace ImageSharp.Processing.Processors /// /// The North gradient operator /// - private static readonly float[][] KirschNorth = - { - new float[] { 5, 5, 5 }, - new float[] { -3, 0, -3 }, - new float[] { -3, -3, -3 } - }; + private static readonly Fast2DArray KirschNorth = + new Fast2DArray(new float[,] + { + { 5, 5, 5 }, + { -3, 0, -3 }, + { -3, -3, -3 } + }); /// /// The NorthWest gradient operator /// - private static readonly float[][] KirschNorthWest = - { - new float[] { 5, 5, -3 }, - new float[] { 5, 0, -3 }, - new float[] { -3, -3, -3 } - }; + private static readonly Fast2DArray KirschNorthWest = + new Fast2DArray(new float[,] + { + { 5, 5, -3 }, + { 5, 0, -3 }, + { -3, -3, -3 } + }); /// /// The West gradient operator /// - private static readonly float[][] KirschWest = - { - new float[] { 5, -3, -3 }, - new float[] { 5, 0, -3 }, - new float[] { 5, -3, -3 } - }; + private static readonly Fast2DArray KirschWest = + new Fast2DArray(new float[,] + { + { 5, -3, -3 }, + { 5, 0, -3 }, + { 5, -3, -3 } + }); /// /// The SouthWest gradient operator /// - private static readonly float[][] KirschSouthWest = - { - new float[] { -3, -3, -3 }, - new float[] { 5, 0, -3 }, - new float[] { 5, 5, -3 } - }; + private static readonly Fast2DArray KirschSouthWest = + new Fast2DArray(new float[,] + { + { -3, -3, -3 }, + { 5, 0, -3 }, + { 5, 5, -3 } + }); /// /// The South gradient operator /// - private static readonly float[][] KirschSouth = - { - new float[] { -3, -3, -3 }, - new float[] { -3, 0, -3 }, - new float[] { 5, 5, 5 } - }; + private static readonly Fast2DArray KirschSouth = + new Fast2DArray(new float[,] + { + { -3, -3, -3 }, + { -3, 0, -3 }, + { 5, 5, 5 } + }); /// /// The SouthEast gradient operator /// - private static readonly float[][] KirschSouthEast = - { - new float[] { -3, -3, -3 }, - new float[] { -3, 0, 5 }, - new float[] { -3, 5, 5 } - }; + private static readonly Fast2DArray KirschSouthEast = + new Fast2DArray(new float[,] + { + { -3, -3, -3 }, + { -3, 0, 5 }, + { -3, 5, 5 } + }); /// /// The East gradient operator /// - private static readonly float[][] KirschEast = - { - new float[] { -3, -3, 5 }, - new float[] { -3, 0, 5 }, - new float[] { -3, -3, 5 } - }; + private static readonly Fast2DArray KirschEast = + new Fast2DArray(new float[,] + { + { -3, -3, 5 }, + { -3, 0, 5 }, + { -3, -3, 5 } + }); /// /// The NorthEast gradient operator /// - private static readonly float[][] KirschNorthEast = - { - new float[] { -3, 5, 5 }, - new float[] { -3, 0, 5 }, - new float[] { -3, -3, -3 } - }; + private static readonly Fast2DArray KirschNorthEast = + new Fast2DArray(new float[,] + { + { -3, 5, 5 }, + { -3, 0, 5 }, + { -3, -3, -3 } + }); /// - public override float[][] North => KirschNorth; + public override Fast2DArray North => KirschNorth; /// - public override float[][] NorthWest => KirschNorthWest; + public override Fast2DArray NorthWest => KirschNorthWest; /// - public override float[][] West => KirschWest; + public override Fast2DArray West => KirschWest; /// - public override float[][] SouthWest => KirschSouthWest; + public override Fast2DArray SouthWest => KirschSouthWest; /// - public override float[][] South => KirschSouth; + public override Fast2DArray South => KirschSouth; /// - public override float[][] SouthEast => KirschSouthEast; + public override Fast2DArray SouthEast => KirschSouthEast; /// - public override float[][] East => KirschEast; + public override Fast2DArray East => KirschEast; /// - public override float[][] NorthEast => KirschNorthEast; + public override Fast2DArray NorthEast => KirschNorthEast; } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs index b19c5c773..4b23dfe47 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs @@ -20,14 +20,20 @@ namespace ImageSharp.Processing.Processors /// /// The 2d gradient operator. /// - private static readonly float[][] Laplacian3X3XY = new float[][] - { - new float[] { -1, -1, -1 }, - new float[] { -1, 8, -1 }, - new float[] { -1, -1, -1 } - }; + private static readonly Fast2DArray Laplacian3X3XY = + new Fast2DArray(new float[,] + { + { -1, -1, -1 }, + { -1, 8, -1 }, + { -1, -1, -1 } + }); - /// - public override float[][] KernelXY => Laplacian3X3XY; + /// + /// Initializes a new instance of the class. + /// + public Laplacian3X3Processor() + : base(Laplacian3X3XY) + { + } } } diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs index efa6c28c5..304dafbbd 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs @@ -20,16 +20,22 @@ namespace ImageSharp.Processing.Processors /// /// The 2d gradient operator. /// - private static readonly float[][] Laplacian5X5XY = - { - new float[] { -1, -1, -1, -1, -1 }, - new float[] { -1, -1, -1, -1, -1 }, - new float[] { -1, -1, 24, -1, -1 }, - new float[] { -1, -1, -1, -1, -1 }, - new float[] { -1, -1, -1, -1, -1 } - }; + private static readonly Fast2DArray Laplacian5X5XY = + new Fast2DArray(new float[,] + { + { -1, -1, -1, -1, -1 }, + { -1, -1, -1, -1, -1 }, + { -1, -1, 24, -1, -1 }, + { -1, -1, -1, -1, -1 }, + { -1, -1, -1, -1, -1 } + }); - /// - public override float[][] KernelXY => Laplacian5X5XY; + /// + /// Initializes a new instance of the class. + /// + public Laplacian5X5Processor() + : base(Laplacian5X5XY) + { + } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs index 595ca6a4b..e18957a69 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs @@ -20,16 +20,22 @@ namespace ImageSharp.Processing.Processors /// /// The 2d gradient operator. /// - private static readonly float[][] LaplacianOfGaussianXY = - { - new float[] { 0, 0, -1, 0, 0 }, - new float[] { 0, -1, -2, -1, 0 }, - new float[] { -1, -2, 16, -2, -1 }, - new float[] { 0, -1, -2, -1, 0 }, - new float[] { 0, 0, -1, 0, 0 } - }; + private static readonly Fast2DArray LaplacianOfGaussianXY = + new Fast2DArray(new float[,] + { + { 0, 0, -1, 0, 0 }, + { 0, -1, -2, -1, 0 }, + { -1, -2, 16, -2, -1 }, + { 0, -1, -2, -1, 0 }, + { 0, 0, -1, 0, 0 } + }); - /// - public override float[][] KernelXY => LaplacianOfGaussianXY; + /// + /// Initializes a new instance of the class. + /// + public LaplacianOfGaussianProcessor() + : base(LaplacianOfGaussianXY) + { + } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs index 5c48722ef..1d61b8cd9 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs @@ -20,27 +20,31 @@ namespace ImageSharp.Processing.Processors /// /// The horizontal gradient operator. /// - private static readonly float[][] PrewittX = - { - new float[] { -1, 0, 1 }, - new float[] { -1, 0, 1 }, - new float[] { -1, 0, 1 } - }; + private static readonly Fast2DArray PrewittX = + new Fast2DArray(new float[,] + { + { -1, 0, 1 }, + { -1, 0, 1 }, + { -1, 0, 1 } + }); /// /// The vertical gradient operator. /// - private static readonly float[][] PrewittY = - { - new float[] { 1, 1, 1 }, - new float[] { 0, 0, 0 }, - new float[] { -1, -1, -1 } - }; - - /// - public override float[][] KernelX => PrewittX; + private static readonly Fast2DArray PrewittY = + new Fast2DArray(new float[,] + { + { 1, 1, 1 }, + { 0, 0, 0 }, + { -1, -1, -1 } + }); - /// - public override float[][] KernelY => PrewittY; + /// + /// Initializes a new instance of the class. + /// + public PrewittProcessor() + : base(PrewittX, PrewittY) + { + } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs index c64ee8abe..83f13a342 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs @@ -20,25 +20,29 @@ namespace ImageSharp.Processing.Processors /// /// The horizontal gradient operator. /// - private static readonly float[][] RobertsCrossX = - { - new float[] { 1, 0 }, - new float[] { 0, -1 } - }; + private static readonly Fast2DArray RobertsCrossX = + new Fast2DArray(new float[,] + { + { 1, 0 }, + { 0, -1 } + }); /// /// The vertical gradient operator. /// - private static readonly float[][] RobertsCrossY = - { - new float[] { 0, 1 }, - new float[] { -1, 0 } - }; - - /// - public override float[][] KernelX => RobertsCrossX; + private static readonly Fast2DArray RobertsCrossY = + new Fast2DArray(new float[,] + { + { 0, 1 }, + { -1, 0 } + }); - /// - public override float[][] KernelY => RobertsCrossY; + /// + /// Initializes a new instance of the class. + /// + public RobertsCrossProcessor() + : base(RobertsCrossX, RobertsCrossY) + { + } } } diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs index 4e61707c4..a8187caca 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs @@ -2,6 +2,7 @@ // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // + namespace ImageSharp.Processing.Processors { using System; @@ -19,105 +20,113 @@ namespace ImageSharp.Processing.Processors /// /// The North gradient operator /// - private static readonly float[][] RobinsonNorth = - { - new float[] { 1, 2, 1 }, - new float[] { 0, 0, 0 }, - new float[] { -1, -2, -1 } - }; + private static readonly Fast2DArray RobinsonNorth = + new Fast2DArray(new float[,] + { + { 1, 2, 1 }, + { 0, 0, 0 }, + { -1, -2, -1 } + }); /// /// The NorthWest gradient operator /// - private static readonly float[][] RobinsonNorthWest = - { - new float[] { 2, 1, 0 }, - new float[] { 1, 0, -1 }, - new float[] { 0, -1, -2 } - }; + private static readonly Fast2DArray RobinsonNorthWest = + new Fast2DArray(new float[,] + { + { 2, 1, 0 }, + { 1, 0, -1 }, + { 0, -1, -2 } + }); /// /// The West gradient operator /// - private static readonly float[][] RobinsonWest = - { - new float[] { 1, 0, -1 }, - new float[] { 2, 0, -2 }, - new float[] { 1, 0, -1 } - }; + private static readonly Fast2DArray RobinsonWest = + new Fast2DArray(new float[,] + { + { 1, 0, -1 }, + { 2, 0, -2 }, + { 1, 0, -1 } + }); /// /// The SouthWest gradient operator /// - private static readonly float[][] RobinsonSouthWest = - { - new float[] { 0, -1, -2 }, - new float[] { 1, 0, -1 }, - new float[] { 2, 1, 0 } - }; + private static readonly Fast2DArray RobinsonSouthWest = + new Fast2DArray(new float[,] + { + { 0, -1, -2 }, + { 1, 0, -1 }, + { 2, 1, 0 } + }); /// /// The South gradient operator /// - private static readonly float[][] RobinsonSouth = - { - new float[] { -1, -2, -1 }, - new float[] { 0, 0, 0 }, - new float[] { 1, 2, 1 } - }; + private static readonly Fast2DArray RobinsonSouth = + new Fast2DArray(new float[,] + { + { -1, -2, -1 }, + { 0, 0, 0 }, + { 1, 2, 1 } + }); /// /// The SouthEast gradient operator /// - private static readonly float[][] RobinsonSouthEast = - { - new float[] { -2, -1, 0 }, - new float[] { -1, 0, 1 }, - new float[] { 0, 1, 2 } - }; + private static readonly Fast2DArray RobinsonSouthEast = + new Fast2DArray(new float[,] + { + { -2, -1, 0 }, + { -1, 0, 1 }, + { 0, 1, 2 } + }); /// /// The East gradient operator /// - private static readonly float[][] RobinsonEast = - { - new float[] { -1, 0, 1 }, - new float[] { -2, 0, 2 }, - new float[] { -1, 0, 1 } - }; + private static readonly Fast2DArray RobinsonEast = + new Fast2DArray(new float[,] + { + { -1, 0, 1 }, + { -2, 0, 2 }, + { -1, 0, 1 } + }); /// /// The NorthEast gradient operator /// - private static readonly float[][] RobinsonNorthEast = - { - new float[] { 0, 1, 2 }, - new float[] { -1, 0, 1 }, - new float[] { -2, -1, 0 } - }; + private static readonly Fast2DArray RobinsonNorthEast = + new Fast2DArray(new float[,] + { + { 0, 1, 2 }, + { -1, 0, 1 }, + { -2, -1, 0 } + }); /// - public override float[][] North => RobinsonNorth; + public override Fast2DArray North => RobinsonNorth; /// - public override float[][] NorthWest => RobinsonNorthWest; + public override Fast2DArray NorthWest => RobinsonNorthWest; /// - public override float[][] West => RobinsonWest; + public override Fast2DArray West => RobinsonWest; /// - public override float[][] SouthWest => RobinsonSouthWest; + public override Fast2DArray SouthWest => RobinsonSouthWest; /// - public override float[][] South => RobinsonSouth; + public override Fast2DArray South => RobinsonSouth; /// - public override float[][] SouthEast => RobinsonSouthEast; + public override Fast2DArray SouthEast => RobinsonSouthEast; /// - public override float[][] East => RobinsonEast; + public override Fast2DArray East => RobinsonEast; /// - public override float[][] NorthEast => RobinsonNorthEast; + public override Fast2DArray NorthEast => RobinsonNorthEast; } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs index de2a185f8..6b9e67ce9 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs @@ -20,27 +20,31 @@ namespace ImageSharp.Processing.Processors /// /// The horizontal gradient operator. /// - private static readonly float[][] ScharrX = new float[3][] - { - new float[] { -3, 0, 3 }, - new float[] { -10, 0, 10 }, - new float[] { -3, 0, 3 } - }; + private static readonly Fast2DArray ScharrX = + new Fast2DArray(new float[,] + { + { -3, 0, 3 }, + { -10, 0, 10 }, + { -3, 0, 3 } + }); /// /// The vertical gradient operator. /// - private static readonly float[][] ScharrY = new float[3][] - { - new float[] { 3, 10, 3 }, - new float[] { 0, 0, 0 }, - new float[] { -3, -10, -3 } - }; - - /// - public override float[][] KernelX => ScharrX; + private static readonly Fast2DArray ScharrY = + new Fast2DArray(new float[,] + { + { 3, 10, 3 }, + { 0, 0, 0 }, + { -3, -10, -3 } + }); - /// - public override float[][] KernelY => ScharrY; + /// + /// Initializes a new instance of the class. + /// + public ScharrProcessor() + : base(ScharrX, ScharrY) + { + } } } \ No newline at end of file diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs index 328c903dc..160788967 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs @@ -20,27 +20,31 @@ namespace ImageSharp.Processing.Processors /// /// The horizontal gradient operator. /// - private static readonly float[][] SobelX = - { - new float[] { -1, 0, 1 }, - new float[] { -2, 0, 2 }, - new float[] { -1, 0, 1 } - }; + private static readonly Fast2DArray SobelX = + new Fast2DArray(new float[,] + { + { -1, 0, 1 }, + { -2, 0, 2 }, + { -1, 0, 1 } + }); /// /// The vertical gradient operator. /// - private static readonly float[][] SobelY = - { - new float[] { -1, -2, -1 }, - new float[] { 0, 0, 0 }, - new float[] { 1, 2, 1 } - }; - - /// - public override float[][] KernelX => SobelX; + private static readonly Fast2DArray SobelY = + new Fast2DArray(new float[,] + { + { -1, -2, -1 }, + { 0, 0, 0 }, + { 1, 2, 1 } + }); - /// - public override float[][] KernelY => SobelY; + /// + /// Initializes a new instance of the class. + /// + public SobelProcessor() + : base(SobelX, SobelY) + { + } } } diff --git a/src/ImageSharp.Processing/Processors/Convolution/GaussianBlurProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/GaussianBlurProcessor.cs index 7cd3bbe9c..49733a9bd 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/GaussianBlurProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/GaussianBlurProcessor.cs @@ -71,12 +71,12 @@ namespace ImageSharp.Processing.Processors /// /// Gets the horizontal gradient operator. /// - public float[][] KernelX { get; } + public Fast2DArray KernelX { get; } /// /// Gets the vertical gradient operator. /// - public float[][] KernelY { get; } + public Fast2DArray KernelY { get; } /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) @@ -88,21 +88,18 @@ namespace ImageSharp.Processing.Processors /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function /// /// Whether to calculate a horizontal kernel. - /// The - private float[][] CreateGaussianKernel(bool horizontal) + /// The + private Fast2DArray CreateGaussianKernel(bool horizontal) { int size = this.kernelSize; float weight = this.sigma; - float[][] kernel = horizontal ? new float[1][] : new float[size][]; + Fast2DArray kernel = horizontal + ? new Fast2DArray(new float[1, size]) + : new Fast2DArray(new float[size, 1]); - if (horizontal) - { - kernel[0] = new float[size]; - } - - float sum = 0.0f; + float sum = 0F; + float midpoint = (size - 1) / 2F; - float midpoint = (size - 1) / 2f; for (int i = 0; i < size; i++) { float x = i - midpoint; @@ -110,27 +107,27 @@ namespace ImageSharp.Processing.Processors sum += gx; if (horizontal) { - kernel[0][i] = gx; + kernel[0, i] = gx; } else { - kernel[i] = new[] { gx }; + kernel[i, 0] = gx; } } - // Normalise kernel so that the sum of all weights equals 1 + // Normalize kernel so that the sum of all weights equals 1 if (horizontal) { for (int i = 0; i < size; i++) { - kernel[0][i] = kernel[0][i] / sum; + kernel[0, i] = kernel[0, i] / sum; } } else { for (int i = 0; i < size; i++) { - kernel[i][0] = kernel[i][0] / sum; + kernel[i, 0] = kernel[i, 0] / sum; } } diff --git a/src/ImageSharp.Processing/Processors/Convolution/GaussianSharpenProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/GaussianSharpenProcessor.cs index d0654dd77..2254a61b6 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/GaussianSharpenProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/GaussianSharpenProcessor.cs @@ -73,12 +73,12 @@ namespace ImageSharp.Processing.Processors /// /// Gets the horizontal gradient operator. /// - public float[][] KernelX { get; } + public Fast2DArray KernelX { get; } /// /// Gets the vertical gradient operator. /// - public float[][] KernelY { get; } + public Fast2DArray KernelY { get; } /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) @@ -90,17 +90,14 @@ namespace ImageSharp.Processing.Processors /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function /// /// Whether to calculate a horizontal kernel. - /// The - private float[][] CreateGaussianKernel(bool horizontal) + /// The + private Fast2DArray CreateGaussianKernel(bool horizontal) { int size = this.kernelSize; float weight = this.sigma; - float[][] kernel = horizontal ? new float[1][] : new float[size][]; - - if (horizontal) - { - kernel[0] = new float[size]; - } + Fast2DArray kernel = horizontal + ? new Fast2DArray(new float[1, size]) + : new Fast2DArray(new float[size, 1]); float sum = 0; @@ -112,11 +109,11 @@ namespace ImageSharp.Processing.Processors sum += gx; if (horizontal) { - kernel[0][i] = gx; + kernel[0, i] = gx; } else { - kernel[i] = new[] { gx }; + kernel[i, 0] = gx; } } @@ -130,12 +127,12 @@ namespace ImageSharp.Processing.Processors if (i == midpointRounded) { // Calculate central value - kernel[0][i] = (2f * sum) - kernel[0][i]; + kernel[0, i] = (2F * sum) - kernel[0, i]; } else { // invert value - kernel[0][i] = -kernel[0][i]; + kernel[0, i] = -kernel[0, i]; } } } @@ -146,12 +143,12 @@ namespace ImageSharp.Processing.Processors if (i == midpointRounded) { // Calculate central value - kernel[i][0] = (2 * sum) - kernel[i][0]; + kernel[i, 0] = (2 * sum) - kernel[i, 0]; } else { // invert value - kernel[i][0] = -kernel[i][0]; + kernel[i, 0] = -kernel[i, 0]; } } } @@ -161,14 +158,14 @@ namespace ImageSharp.Processing.Processors { for (int i = 0; i < size; i++) { - kernel[0][i] = kernel[0][i] / sum; + kernel[0, i] = kernel[0, i] / sum; } } else { for (int i = 0; i < size; i++) { - kernel[i][0] = kernel[i][0] / sum; + kernel[i, 0] = kernel[i, 0] / sum; } } diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs index fc94c689f..5720d8218 100644 --- a/src/ImageSharp/Common/Helpers/ImageMaths.cs +++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs @@ -37,6 +37,7 @@ namespace ImageSharp /// /// The /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetBitsNeededForColorDepth(int colors) { return (int)Math.Ceiling(Math.Log(colors, 2)); @@ -48,6 +49,7 @@ namespace ImageSharp /// The x provided to G(x). /// The spread of the blur. /// The Gaussian G(x) + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Gaussian(float x, float sigma) { const float Numerator = 1.0f; @@ -72,6 +74,7 @@ namespace ImageSharp /// /// The . /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float GetBcValue(float x, float b, float c) { float temp; @@ -104,6 +107,7 @@ namespace ImageSharp /// /// The . /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float SinC(float x) { if (Math.Abs(x) > Constants.Epsilon) @@ -122,6 +126,7 @@ namespace ImageSharp /// /// The representing the degree as radians. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float DegreesToRadians(float degrees) { return degrees * (float)(Math.PI / 180); @@ -139,6 +144,7 @@ namespace ImageSharp /// /// The bounding . /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rectangle GetBoundingRectangle(Point topLeft, Point bottomRight) { return new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y); From 680ac0de5af23c72281d259a8abfe97f4c4cc18e Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 18 Feb 2017 12:12:04 +0100 Subject: [PATCH 073/142] better Array2D benchmark --- .../ImageSharp.Benchmarks/General/Array2D.cs | 73 ++++++++++++++++--- 1 file changed, 62 insertions(+), 11 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/General/Array2D.cs b/tests/ImageSharp.Benchmarks/General/Array2D.cs index a01ba77ad..fce92e9be 100644 --- a/tests/ImageSharp.Benchmarks/General/Array2D.cs +++ b/tests/ImageSharp.Benchmarks/General/Array2D.cs @@ -5,25 +5,31 @@ namespace ImageSharp.Benchmarks.General { + using System; + using BenchmarkDotNet.Attributes; public class Array2D { - private float[,] data; + private float[] flatArray; + + private float[,] array2D; private float[][] jaggedData; private Fast2DArray fastData; - - [Params(10, 100, 1000, 10000)] + + [Params(4, 16, 128)] public int Count { get; set; } - public int Index { get; set; } + public int Min { get; private set; } + public int Max { get; private set; } [Setup] public void SetUp() { - this.data = new float[this.Count, this.Count]; + this.flatArray = new float[this.Count * this.Count]; + this.array2D = new float[this.Count, this.Count]; this.jaggedData = new float[this.Count][]; for (int i = 0; i < this.Count; i++) @@ -31,27 +37,72 @@ namespace ImageSharp.Benchmarks.General this.jaggedData[i] = new float[this.Count]; } - this.fastData = new Fast2DArray(this.data); + this.fastData = new Fast2DArray(this.array2D); - this.Index = this.Count / 2; + this.Min = this.Count / 2 - 10; + this.Min = Math.Max(0, this.Min); + this.Max = this.Min + Math.Min(10, this.Count); + } + + [Benchmark(Description = "Emulated 2D array access using flat array")] + public float FlatArrayIndex() + { + float[] a = this.flatArray; + float s = 0; + int count = this.Count; + for (int i = this.Min; i < this.Max; i++) + { + for (int j = this.Min; j < this.Max; j++) + { + s += a[count * i + j]; + } + } + return s; } [Benchmark(Baseline = true, Description = "Array access using 2D array")] - public float ArrayIndex() + public float Array2DIndex() { - return this.data[this.Index, this.Index]; + float s = 0; + float[,] a = this.array2D; + for (int i = this.Min; i < this.Max; i++) + { + for (int j = this.Min; j < this.Max; j++) + { + s += a[i, j]; + } + } + return s; } [Benchmark(Description = "Array access using a jagged array")] public float ArrayJaggedIndex() { - return this.jaggedData[this.Index][this.Index]; + float s = 0; + float[][] a = this.jaggedData; + for (int i = this.Min; i < this.Max; i++) + { + for (int j = this.Min; j < this.Max; j++) + { + s += a[i][j]; + } + } + return s; } [Benchmark(Description = "Array access using Fast2DArray")] public float ArrayFastIndex() { - return this.fastData[this.Index, this.Index]; + float s = 0; + Fast2DArray a = this.fastData; + for (int i = this.Min; i < this.Max; i++) + { + for (int j = this.Min; j < this.Max; j++) + { + s += a[i, j]; + } + } + return s; } } } From 54c65567cea09ecb63382a27ec611ef1e15a54a3 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sat, 18 Feb 2017 11:14:53 +0000 Subject: [PATCH 074/142] Improve Fast2DArray initialisation --- .../Convolution/BoxBlurProcessor.cs | 4 +-- .../EdgeDetection/KayyaliProcessor.cs | 8 ++--- .../EdgeDetection/KirschProcessor.cs | 32 +++++++++---------- .../EdgeDetection/Laplacian3X3Processor.cs | 4 +-- .../EdgeDetection/Laplacian5X5Processor.cs | 4 +-- .../LaplacianOfGaussianProcessor.cs | 4 +-- .../EdgeDetection/PrewittProcessor.cs | 8 ++--- .../EdgeDetection/RobertsCrossProcessor.cs | 8 ++--- .../EdgeDetection/RobinsonProcessor.cs | 32 +++++++++---------- .../EdgeDetection/ScharrProcessor.cs | 8 ++--- .../EdgeDetection/SobelProcessor.cs | 8 ++--- .../Convolution/GaussianBlurProcessor.cs | 4 +-- .../Convolution/GaussianSharpenProcessor.cs | 4 +-- .../Common/Helpers/Fast2DArray{T}.cs | 29 +++++++++++++++++ .../Dithering/ErrorDiffusion/Atkinson.cs | 4 +-- .../Dithering/ErrorDiffusion/Burks.cs | 4 +-- .../ErrorDiffusion/FloydSteinberg.cs | 4 +-- .../ErrorDiffusion/JarvisJudiceNinke.cs | 4 +-- .../Dithering/ErrorDiffusion/Sierra2.cs | 4 +-- .../Dithering/ErrorDiffusion/Sierra3.cs | 4 +-- .../Dithering/ErrorDiffusion/SierraLite.cs | 4 +-- .../Dithering/ErrorDiffusion/Stucki.cs | 4 +-- src/ImageSharp/Dithering/Ordered/Bayer.cs | 4 +-- src/ImageSharp/Dithering/Ordered/Ordered.cs | 4 +-- .../Common/Fast2DArrayTests.cs | 28 +++++++++++++--- 25 files changed, 136 insertions(+), 89 deletions(-) diff --git a/src/ImageSharp.Processing/Processors/Convolution/BoxBlurProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/BoxBlurProcessor.cs index 8ca1ca666..a9caea555 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/BoxBlurProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/BoxBlurProcessor.cs @@ -57,8 +57,8 @@ namespace ImageSharp.Processing.Processors { int size = this.kernelSize; Fast2DArray kernel = horizontal - ? new Fast2DArray(new float[1, size]) - : new Fast2DArray(new float[size, 1]); + ? new Fast2DArray(size, 1) + : new Fast2DArray(1, size); float sum = 0F; for (int i = 0; i < size; i++) diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs index 6e1452e17..bda3c9317 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs @@ -21,23 +21,23 @@ namespace ImageSharp.Processing.Processors /// The horizontal gradient operator. /// private static readonly Fast2DArray KayyaliX = - new Fast2DArray(new float[,] + new float[,] { { 6, 0, -6 }, { 0, 0, 0 }, { -6, 0, 6 } - }); + }; /// /// The vertical gradient operator. /// private static readonly Fast2DArray KayyaliY = - new Fast2DArray(new float[,] + new float[,] { { -6, 0, 6 }, { 0, 0, 0 }, { 6, 0, -6 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs index f8cb9aba9..4a555137e 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs @@ -21,89 +21,89 @@ namespace ImageSharp.Processing.Processors /// The North gradient operator /// private static readonly Fast2DArray KirschNorth = - new Fast2DArray(new float[,] + new float[,] { { 5, 5, 5 }, { -3, 0, -3 }, { -3, -3, -3 } - }); + }; /// /// The NorthWest gradient operator /// private static readonly Fast2DArray KirschNorthWest = - new Fast2DArray(new float[,] + new float[,] { { 5, 5, -3 }, { 5, 0, -3 }, { -3, -3, -3 } - }); + }; /// /// The West gradient operator /// private static readonly Fast2DArray KirschWest = - new Fast2DArray(new float[,] + new float[,] { { 5, -3, -3 }, { 5, 0, -3 }, { 5, -3, -3 } - }); + }; /// /// The SouthWest gradient operator /// private static readonly Fast2DArray KirschSouthWest = - new Fast2DArray(new float[,] + new float[,] { { -3, -3, -3 }, { 5, 0, -3 }, { 5, 5, -3 } - }); + }; /// /// The South gradient operator /// private static readonly Fast2DArray KirschSouth = - new Fast2DArray(new float[,] + new float[,] { { -3, -3, -3 }, { -3, 0, -3 }, { 5, 5, 5 } - }); + }; /// /// The SouthEast gradient operator /// private static readonly Fast2DArray KirschSouthEast = - new Fast2DArray(new float[,] + new float[,] { { -3, -3, -3 }, { -3, 0, 5 }, { -3, 5, 5 } - }); + }; /// /// The East gradient operator /// private static readonly Fast2DArray KirschEast = - new Fast2DArray(new float[,] + new float[,] { { -3, -3, 5 }, { -3, 0, 5 }, { -3, -3, 5 } - }); + }; /// /// The NorthEast gradient operator /// private static readonly Fast2DArray KirschNorthEast = - new Fast2DArray(new float[,] + new float[,] { { -3, 5, 5 }, { -3, 0, 5 }, { -3, -3, -3 } - }); + }; /// public override Fast2DArray North => KirschNorth; diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs index 4b23dfe47..74fcfcad5 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs @@ -21,12 +21,12 @@ namespace ImageSharp.Processing.Processors /// The 2d gradient operator. /// private static readonly Fast2DArray Laplacian3X3XY = - new Fast2DArray(new float[,] + new float[,] { { -1, -1, -1 }, { -1, 8, -1 }, { -1, -1, -1 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs index 304dafbbd..c00831a69 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs @@ -21,14 +21,14 @@ namespace ImageSharp.Processing.Processors /// The 2d gradient operator. /// private static readonly Fast2DArray Laplacian5X5XY = - new Fast2DArray(new float[,] + new float[,] { { -1, -1, -1, -1, -1 }, { -1, -1, -1, -1, -1 }, { -1, -1, 24, -1, -1 }, { -1, -1, -1, -1, -1 }, { -1, -1, -1, -1, -1 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs index e18957a69..8ee01047b 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs @@ -21,14 +21,14 @@ namespace ImageSharp.Processing.Processors /// The 2d gradient operator. /// private static readonly Fast2DArray LaplacianOfGaussianXY = - new Fast2DArray(new float[,] + new float[,] { { 0, 0, -1, 0, 0 }, { 0, -1, -2, -1, 0 }, { -1, -2, 16, -2, -1 }, { 0, -1, -2, -1, 0 }, { 0, 0, -1, 0, 0 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs index 1d61b8cd9..cf213e724 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs @@ -21,23 +21,23 @@ namespace ImageSharp.Processing.Processors /// The horizontal gradient operator. /// private static readonly Fast2DArray PrewittX = - new Fast2DArray(new float[,] + new float[,] { { -1, 0, 1 }, { -1, 0, 1 }, { -1, 0, 1 } - }); + }; /// /// The vertical gradient operator. /// private static readonly Fast2DArray PrewittY = - new Fast2DArray(new float[,] + new float[,] { { 1, 1, 1 }, { 0, 0, 0 }, { -1, -1, -1 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs index 83f13a342..de1f279ea 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs @@ -21,21 +21,21 @@ namespace ImageSharp.Processing.Processors /// The horizontal gradient operator. /// private static readonly Fast2DArray RobertsCrossX = - new Fast2DArray(new float[,] + new float[,] { { 1, 0 }, { 0, -1 } - }); + }; /// /// The vertical gradient operator. /// private static readonly Fast2DArray RobertsCrossY = - new Fast2DArray(new float[,] + new float[,] { { 0, 1 }, { -1, 0 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs index a8187caca..13d7a3f49 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs @@ -21,89 +21,89 @@ namespace ImageSharp.Processing.Processors /// The North gradient operator /// private static readonly Fast2DArray RobinsonNorth = - new Fast2DArray(new float[,] + new float[,] { { 1, 2, 1 }, { 0, 0, 0 }, { -1, -2, -1 } - }); + }; /// /// The NorthWest gradient operator /// private static readonly Fast2DArray RobinsonNorthWest = - new Fast2DArray(new float[,] + new float[,] { { 2, 1, 0 }, { 1, 0, -1 }, { 0, -1, -2 } - }); + }; /// /// The West gradient operator /// private static readonly Fast2DArray RobinsonWest = - new Fast2DArray(new float[,] + new float[,] { { 1, 0, -1 }, { 2, 0, -2 }, { 1, 0, -1 } - }); + }; /// /// The SouthWest gradient operator /// private static readonly Fast2DArray RobinsonSouthWest = - new Fast2DArray(new float[,] + new float[,] { { 0, -1, -2 }, { 1, 0, -1 }, { 2, 1, 0 } - }); + }; /// /// The South gradient operator /// private static readonly Fast2DArray RobinsonSouth = - new Fast2DArray(new float[,] + new float[,] { { -1, -2, -1 }, { 0, 0, 0 }, { 1, 2, 1 } - }); + }; /// /// The SouthEast gradient operator /// private static readonly Fast2DArray RobinsonSouthEast = - new Fast2DArray(new float[,] + new float[,] { { -2, -1, 0 }, { -1, 0, 1 }, { 0, 1, 2 } - }); + }; /// /// The East gradient operator /// private static readonly Fast2DArray RobinsonEast = - new Fast2DArray(new float[,] + new float[,] { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } - }); + }; /// /// The NorthEast gradient operator /// private static readonly Fast2DArray RobinsonNorthEast = - new Fast2DArray(new float[,] + new float[,] { { 0, 1, 2 }, { -1, 0, 1 }, { -2, -1, 0 } - }); + }; /// public override Fast2DArray North => RobinsonNorth; diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs index 6b9e67ce9..11cb91b0d 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs @@ -21,23 +21,23 @@ namespace ImageSharp.Processing.Processors /// The horizontal gradient operator. /// private static readonly Fast2DArray ScharrX = - new Fast2DArray(new float[,] + new float[,] { { -3, 0, 3 }, { -10, 0, 10 }, { -3, 0, 3 } - }); + }; /// /// The vertical gradient operator. /// private static readonly Fast2DArray ScharrY = - new Fast2DArray(new float[,] + new float[,] { { 3, 10, 3 }, { 0, 0, 0 }, { -3, -10, -3 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs index 160788967..347a19dab 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs @@ -21,23 +21,23 @@ namespace ImageSharp.Processing.Processors /// The horizontal gradient operator. /// private static readonly Fast2DArray SobelX = - new Fast2DArray(new float[,] + new float[,] { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } - }); + }; /// /// The vertical gradient operator. /// private static readonly Fast2DArray SobelY = - new Fast2DArray(new float[,] + new float[,] { { -1, -2, -1 }, { 0, 0, 0 }, { 1, 2, 1 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Convolution/GaussianBlurProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/GaussianBlurProcessor.cs index 49733a9bd..87dde5b47 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/GaussianBlurProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/GaussianBlurProcessor.cs @@ -94,8 +94,8 @@ namespace ImageSharp.Processing.Processors int size = this.kernelSize; float weight = this.sigma; Fast2DArray kernel = horizontal - ? new Fast2DArray(new float[1, size]) - : new Fast2DArray(new float[size, 1]); + ? new Fast2DArray(size, 1) + : new Fast2DArray(1, size); float sum = 0F; float midpoint = (size - 1) / 2F; diff --git a/src/ImageSharp.Processing/Processors/Convolution/GaussianSharpenProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/GaussianSharpenProcessor.cs index 2254a61b6..5cf8cdf54 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/GaussianSharpenProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/GaussianSharpenProcessor.cs @@ -96,8 +96,8 @@ namespace ImageSharp.Processing.Processors int size = this.kernelSize; float weight = this.sigma; Fast2DArray kernel = horizontal - ? new Fast2DArray(new float[1, size]) - : new Fast2DArray(new float[size, 1]); + ? new Fast2DArray(size, 1) + : new Fast2DArray(1, size); float sum = 0; diff --git a/src/ImageSharp/Common/Helpers/Fast2DArray{T}.cs b/src/ImageSharp/Common/Helpers/Fast2DArray{T}.cs index 26ec816ce..09cb0fec2 100644 --- a/src/ImageSharp/Common/Helpers/Fast2DArray{T}.cs +++ b/src/ImageSharp/Common/Helpers/Fast2DArray{T}.cs @@ -30,6 +30,22 @@ namespace ImageSharp /// public int Height; + /// + /// Initializes a new instance of the struct. + /// + /// The width. + /// The height. + public Fast2DArray(int width, int height) + { + this.Height = height; + this.Width = width; + + Guard.MustBeGreaterThan(width, 0, nameof(width)); + Guard.MustBeGreaterThan(height, 0, nameof(height)); + + this.Data = new T[this.Width * this.Height]; + } + /// /// Initializes a new instance of the struct. /// @@ -77,6 +93,19 @@ namespace ImageSharp } } + /// + /// Performs an implicit conversion from a 2D array to a . + /// + /// The source array. + /// + /// The represenation on the source data. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Fast2DArray(T[,] data) + { + return new Fast2DArray(data); + } + /// /// Checks the coordinates to ensure they are within bounds. /// diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs index 1fa6852c2..b94b87255 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs @@ -15,12 +15,12 @@ namespace ImageSharp.Dithering /// The diffusion matrix /// private static readonly Fast2DArray AtkinsonMatrix = - new Fast2DArray(new float[,] + new float[,] { { 0, 0, 1, 1 }, { 1, 1, 1, 0 }, { 0, 1, 0, 0 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs index a4adcb0a4..894b6e236 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs @@ -15,11 +15,11 @@ namespace ImageSharp.Dithering /// The diffusion matrix /// private static readonly Fast2DArray BurksMatrix = - new Fast2DArray(new float[,] + new float[,] { { 0, 0, 0, 8, 4 }, { 2, 4, 8, 4, 2 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs b/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs index 7b67d2dd1..f7a93667f 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs @@ -15,11 +15,11 @@ namespace ImageSharp.Dithering /// The diffusion matrix /// private static readonly Fast2DArray FloydSteinbergMatrix = - new Fast2DArray(new float[,] + new float[,] { { 0, 0, 7 }, { 3, 5, 1 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs b/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs index 32f38fbc8..60fef8121 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs @@ -15,12 +15,12 @@ namespace ImageSharp.Dithering /// The diffusion matrix /// private static readonly Fast2DArray JarvisJudiceNinkeMatrix = - new Fast2DArray(new float[,] + new float[,] { { 0, 0, 0, 7, 5 }, { 3, 5, 7, 5, 3 }, { 1, 3, 5, 3, 1 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs index 47b14944e..4325438e0 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs @@ -15,11 +15,11 @@ namespace ImageSharp.Dithering /// The diffusion matrix /// private static readonly Fast2DArray Sierra2Matrix = - new Fast2DArray(new float[,] + new float[,] { { 0, 0, 0, 4, 3 }, { 1, 2, 3, 2, 1 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs index ae33954cf..25ea70d0a 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs @@ -15,12 +15,12 @@ namespace ImageSharp.Dithering /// The diffusion matrix /// private static readonly Fast2DArray Sierra3Matrix = - new Fast2DArray(new float[,] + new float[,] { { 0, 0, 0, 5, 3 }, { 2, 4, 5, 4, 2 }, { 0, 2, 3, 2, 0 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs b/src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs index 8a1e17816..c7b1d214f 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs @@ -15,11 +15,11 @@ namespace ImageSharp.Dithering /// The diffusion matrix /// private static readonly Fast2DArray SierraLiteMatrix = - new Fast2DArray(new float[,] + new float[,] { { 0, 0, 2 }, { 1, 1, 0 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs index b5d22b259..93258c350 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs @@ -15,12 +15,12 @@ namespace ImageSharp.Dithering /// The diffusion matrix /// private static readonly Fast2DArray StuckiMatrix = - new Fast2DArray(new float[,] + new float[,] { { 0, 0, 0, 8, 4 }, { 2, 4, 8, 4, 2 }, { 1, 2, 4, 2, 1 } - }); + }; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Dithering/Ordered/Bayer.cs b/src/ImageSharp/Dithering/Ordered/Bayer.cs index 3d3d90023..ff56196c5 100644 --- a/src/ImageSharp/Dithering/Ordered/Bayer.cs +++ b/src/ImageSharp/Dithering/Ordered/Bayer.cs @@ -18,13 +18,13 @@ namespace ImageSharp.Dithering.Ordered /// This is calculated by multiplying each value in the original matrix by 16 and subtracting 1 /// private static readonly Fast2DArray ThresholdMatrix = - new Fast2DArray(new byte[,] + new byte[,] { { 15, 143, 47, 175 }, { 207, 79, 239, 111 }, { 63, 191, 31, 159 }, { 255, 127, 223, 95 } - }); + }; /// public Fast2DArray Matrix { get; } = ThresholdMatrix; diff --git a/src/ImageSharp/Dithering/Ordered/Ordered.cs b/src/ImageSharp/Dithering/Ordered/Ordered.cs index bb1f21060..86c325cd1 100644 --- a/src/ImageSharp/Dithering/Ordered/Ordered.cs +++ b/src/ImageSharp/Dithering/Ordered/Ordered.cs @@ -18,13 +18,13 @@ namespace ImageSharp.Dithering.Ordered /// This is calculated by multiplying each value in the original matrix by 16 /// private static readonly Fast2DArray ThresholdMatrix = - new Fast2DArray(new byte[,] + new byte[,] { { 0, 128, 32, 160 }, { 192, 64, 224, 96 }, { 48, 176, 16, 144 }, { 240, 112, 208, 80 } - }); + }; /// public Fast2DArray Matrix { get; } = ThresholdMatrix; diff --git a/tests/ImageSharp.Tests/Common/Fast2DArrayTests.cs b/tests/ImageSharp.Tests/Common/Fast2DArrayTests.cs index 903ea6f8d..7db7a4820 100644 --- a/tests/ImageSharp.Tests/Common/Fast2DArrayTests.cs +++ b/tests/ImageSharp.Tests/Common/Fast2DArrayTests.cs @@ -21,9 +21,27 @@ namespace ImageSharp.Tests.Common public void Fast2DArrayThrowsOnNullInitializer() { Assert.Throws(() => - { - Fast2DArray fast = new Fast2DArray(null); - }); + { + Fast2DArray fast = new Fast2DArray(null); + }); + } + + [Fact] + public void Fast2DArrayThrowsOnEmptyZeroWidth() + { + Assert.Throws(() => + { + Fast2DArray fast = new Fast2DArray(0, 10); + }); + } + + [Fact] + public void Fast2DArrayThrowsOnEmptyZeroHeight() + { + Assert.Throws(() => + { + Fast2DArray fast = new Fast2DArray(10, 0); + }); } [Fact] @@ -46,7 +64,7 @@ namespace ImageSharp.Tests.Common [Fact] public void Fast2DArrayGetReturnsCorrectResults() { - Fast2DArray fast = new Fast2DArray(FloydSteinbergMatrix); + Fast2DArray fast = FloydSteinbergMatrix; for (int row = 0; row < fast.Height; row++) { @@ -60,7 +78,7 @@ namespace ImageSharp.Tests.Common [Fact] public void Fast2DArrayGetSetReturnsCorrectResults() { - Fast2DArray fast = new Fast2DArray(new float[4, 4]); + Fast2DArray fast = new Fast2DArray(4, 4); const float Val = 5F; fast[3, 3] = Val; From f137b8bc1496d9551e9d8059315b6846405c91b0 Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sat, 18 Feb 2017 12:30:55 +0100 Subject: [PATCH 075/142] Whitespace. --- src/ImageSharp.Formats.Bmp/BmpDecoder.cs | 4 ++-- src/ImageSharp.Formats.Bmp/BmpEncoder.cs | 2 +- src/ImageSharp.Formats.Gif/GifDecoder.cs | 2 +- src/ImageSharp.Formats.Gif/GifEncoder.cs | 4 ++-- src/ImageSharp.Formats.Jpeg/JpegDecoder.cs | 2 +- src/ImageSharp.Formats.Png/PngEncoder.cs | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ImageSharp.Formats.Bmp/BmpDecoder.cs b/src/ImageSharp.Formats.Bmp/BmpDecoder.cs index ae7e6c72a..b91f727c3 100644 --- a/src/ImageSharp.Formats.Bmp/BmpDecoder.cs +++ b/src/ImageSharp.Formats.Bmp/BmpDecoder.cs @@ -27,8 +27,8 @@ namespace ImageSharp.Formats { /// public void Decode(Image image, Stream stream) - where TColor : struct, IPackedPixel, IEquatable - { + where TColor : struct, IPackedPixel, IEquatable + { Guard.NotNull(image, "image"); Guard.NotNull(stream, "stream"); diff --git a/src/ImageSharp.Formats.Bmp/BmpEncoder.cs b/src/ImageSharp.Formats.Bmp/BmpEncoder.cs index 2ed6b3b98..d43efc513 100644 --- a/src/ImageSharp.Formats.Bmp/BmpEncoder.cs +++ b/src/ImageSharp.Formats.Bmp/BmpEncoder.cs @@ -22,7 +22,7 @@ namespace ImageSharp.Formats /// public void Encode(Image image, Stream stream) where TColor : struct, IPackedPixel, IEquatable - { + { BmpEncoderCore encoder = new BmpEncoderCore(); encoder.Encode(image, stream, this.BitsPerPixel); } diff --git a/src/ImageSharp.Formats.Gif/GifDecoder.cs b/src/ImageSharp.Formats.Gif/GifDecoder.cs index ce8c0c06e..f329744ab 100644 --- a/src/ImageSharp.Formats.Gif/GifDecoder.cs +++ b/src/ImageSharp.Formats.Gif/GifDecoder.cs @@ -16,7 +16,7 @@ namespace ImageSharp.Formats /// public void Decode(Image image, Stream stream) where TColor : struct, IPackedPixel, IEquatable - { + { new GifDecoderCore().Decode(image, stream); } } diff --git a/src/ImageSharp.Formats.Gif/GifEncoder.cs b/src/ImageSharp.Formats.Gif/GifEncoder.cs index 2446f0f68..45c2164e1 100644 --- a/src/ImageSharp.Formats.Gif/GifEncoder.cs +++ b/src/ImageSharp.Formats.Gif/GifEncoder.cs @@ -33,8 +33,8 @@ namespace ImageSharp.Formats /// public void Encode(Image image, Stream stream) - where TColor : struct, IPackedPixel, IEquatable - { + where TColor : struct, IPackedPixel, IEquatable + { GifEncoderCore encoder = new GifEncoderCore { Quality = this.Quality, diff --git a/src/ImageSharp.Formats.Jpeg/JpegDecoder.cs b/src/ImageSharp.Formats.Jpeg/JpegDecoder.cs index 9ee216b4d..41d2a2ad4 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegDecoder.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegDecoder.cs @@ -16,7 +16,7 @@ namespace ImageSharp.Formats /// public void Decode(Image image, Stream stream) where TColor : struct, IPackedPixel, IEquatable - { + { Guard.NotNull(image, "image"); Guard.NotNull(stream, "stream"); diff --git a/src/ImageSharp.Formats.Png/PngEncoder.cs b/src/ImageSharp.Formats.Png/PngEncoder.cs index 13125e30e..7f71d1f39 100644 --- a/src/ImageSharp.Formats.Png/PngEncoder.cs +++ b/src/ImageSharp.Formats.Png/PngEncoder.cs @@ -58,7 +58,7 @@ namespace ImageSharp.Formats /// public void Encode(Image image, Stream stream) where TColor : struct, IPackedPixel, IEquatable - { + { PngEncoderCore encoder = new PngEncoderCore { CompressionLevel = this.CompressionLevel, From c082056db7fa04f8cb380a7a81fab5ca65da651b Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 18 Feb 2017 15:00:12 +0100 Subject: [PATCH 076/142] ArrayPointer --- src/ImageSharp/Common/Helpers/ThrowHelper.cs | 17 ++++ .../Common/Memory/ArrayPointer{T}.cs | 57 +++++++++++ .../{Helpers => Memory}/Fast2DArray{T}.cs | 2 +- .../Common/ArrayPointerTests.cs | 97 +++++++++++++++++++ 4 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 src/ImageSharp/Common/Helpers/ThrowHelper.cs create mode 100644 src/ImageSharp/Common/Memory/ArrayPointer{T}.cs rename src/ImageSharp/Common/{Helpers => Memory}/Fast2DArray{T}.cs (98%) create mode 100644 tests/ImageSharp.Tests/Common/ArrayPointerTests.cs diff --git a/src/ImageSharp/Common/Helpers/ThrowHelper.cs b/src/ImageSharp/Common/Helpers/ThrowHelper.cs new file mode 100644 index 000000000..a83264533 --- /dev/null +++ b/src/ImageSharp/Common/Helpers/ThrowHelper.cs @@ -0,0 +1,17 @@ +namespace ImageSharp +{ + using System; + using System.Runtime.CompilerServices; + + /// + /// Helps removing exception throwing code from hot path by providing non-inlined exception thrower methods. + /// + internal static class ThrowHelper + { + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ThrowArgumentNullException(string paramName) + { + throw new ArgumentNullException(nameof(paramName)); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs b/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs new file mode 100644 index 000000000..c3fc32234 --- /dev/null +++ b/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs @@ -0,0 +1,57 @@ +namespace ImageSharp +{ + using System; + using System.Runtime.CompilerServices; + + /// + /// Provides access to elements in an array from a given position. + /// This struct shares many similarities with corefx System.Span<T> but there are differences in functionalities and semantics: + /// - It's not possible to use it with stack objects or pointers to unmanaged memory, only with managed arrays + /// - There is no bounds checking for performance reasons. Therefore we don't need to store length. (However this could be added as DEBUG-only feature.) + /// - Currently the arrays provided to ArrayPointer need to be pinned. This behaviour could be changed using C#7 features. + /// + internal unsafe struct ArrayPointer + where T : struct + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ArrayPointer(T[] array, void* pointerToArray, int offset) + { + // TODO: Use Guard.NotNull() here after optimizing it with ThrowHelper! + if (array == null) + { + ThrowHelper.ThrowArgumentNullException(nameof(array)); + } + this.Array = array; + this.Offset = offset; + this.PointerAtOffset = (IntPtr)pointerToArray + Unsafe.SizeOf()*offset; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ArrayPointer(T[] array, void* pointerToArray) + { + // TODO: Use Guard.NotNull() here after optimizing it with ThrowHelper! + if (array == null) + { + ThrowHelper.ThrowArgumentNullException(nameof(array)); + } + this.Array = array; + this.Offset = 0; + this.PointerAtOffset = (IntPtr)pointerToArray; + } + + public T[] Array { get; private set; } + + public int Offset { get; private set; } + + public IntPtr PointerAtOffset { get; private set; } + + public ArrayPointer Slice(int offset) + { + ArrayPointer result = default(ArrayPointer); + result.Array = this.Array; + result.Offset = this.Offset + offset; + result.PointerAtOffset = this.PointerAtOffset + Unsafe.SizeOf() * offset; + return result; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Common/Helpers/Fast2DArray{T}.cs b/src/ImageSharp/Common/Memory/Fast2DArray{T}.cs similarity index 98% rename from src/ImageSharp/Common/Helpers/Fast2DArray{T}.cs rename to src/ImageSharp/Common/Memory/Fast2DArray{T}.cs index 26ec816ce..88a979757 100644 --- a/src/ImageSharp/Common/Helpers/Fast2DArray{T}.cs +++ b/src/ImageSharp/Common/Memory/Fast2DArray{T}.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // diff --git a/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs b/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs new file mode 100644 index 000000000..1d229f86a --- /dev/null +++ b/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs @@ -0,0 +1,97 @@ +// ReSharper disable ObjectCreationAsStatement +// ReSharper disable InconsistentNaming +namespace ImageSharp.Tests.Common +{ + using System; + + using Xunit; + + public unsafe class ArrayPointerTests + { + public struct Foo + { + private int a; + + private double b; + + internal static Foo[] CreateArray(int size) + { + Foo[] result = new Foo[size]; + for (int i = 0; i < size; i++) + { + result[i] = new Foo() { a = i, b = i }; + } + return result; + } + } + + [Fact] + public void ConstructWithNullArray_Throws() + { + Assert.Throws( + () => + { + new ArrayPointer(null, (void*)0); + }); + + Assert.Throws( + () => + { + new ArrayPointer(null, (void*)0); + }); + } + + [Fact] + public void ConstructWithoutOffset() + { + Foo[] array = Foo.CreateArray(3); + fixed (Foo* p = array) + { + // Act: + ArrayPointer ap = new ArrayPointer(array, p); + + // Assert: + Assert.Equal(array, ap.Array); + Assert.Equal((IntPtr)p, ap.PointerAtOffset); + } + } + + [Fact] + public void ConstructWithOffset() + { + Foo[] array = Foo.CreateArray(3); + int offset = 2; + fixed (Foo* p = array) + { + // Act: + ArrayPointer ap = new ArrayPointer(array, p, offset); + + // Assert: + Assert.Equal(array, ap.Array); + Assert.Equal(offset, ap.Offset); + Assert.Equal((IntPtr)(p+offset), ap.PointerAtOffset); + } + } + + [Fact] + public void Slice() + { + Foo[] array = Foo.CreateArray(5); + int offset0 = 2; + int offset1 = 2; + int totalOffset = offset0 + offset1; + fixed (Foo* p = array) + { + ArrayPointer ap = new ArrayPointer(array, p, offset0); + + // Act: + ap = ap.Slice(offset1); + + // Assert: + Assert.Equal(array, ap.Array); + Assert.Equal(totalOffset, ap.Offset); + Assert.Equal((IntPtr)(p + totalOffset), ap.PointerAtOffset); + } + } + } +} \ No newline at end of file From 8df8fc11d9c3389bd29076a2d5cd0e94bfe995da Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 18 Feb 2017 15:09:16 +0100 Subject: [PATCH 077/142] IPixel --- src/ImageSharp/Colors/PackedPixel/IPixel.cs | 68 +++++++++++++++++++ .../Common/Memory/ArrayPointer{T}.cs | 4 +- 2 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 src/ImageSharp/Colors/PackedPixel/IPixel.cs diff --git a/src/ImageSharp/Colors/PackedPixel/IPixel.cs b/src/ImageSharp/Colors/PackedPixel/IPixel.cs new file mode 100644 index 000000000..a222edd9a --- /dev/null +++ b/src/ImageSharp/Colors/PackedPixel/IPixel.cs @@ -0,0 +1,68 @@ +namespace ImageSharp +{ + using System; + using System.Numerics; + + public interface IPixel + { + /// + /// Sets the packed representation from a . + /// + /// The vector to create the packed representation from. + void PackFromVector4(Vector4 vector); + + /// + /// Expands the packed representation into a . + /// The vector components are typically expanded in least to greatest significance order. + /// + /// The . + Vector4 ToVector4(); + + /// + /// Sets the packed representation from the given byte array. + /// + /// The x-component. + /// The y-component. + /// The z-component. + /// The w-component. + void PackFromBytes(byte x, byte y, byte z, byte w); + + /// + /// Expands the packed representation into a given byte array. + /// Output is expanded to X-> Y-> Z order. Equivalent to R-> G-> B in + /// + /// The bytes to set the color in. + /// The starting index of the . + void ToXyzBytes(byte[] bytes, int startIndex); + + /// + /// Expands the packed representation into a given byte array. + /// Output is expanded to X-> Y-> Z-> W order. Equivalent to R-> G-> B-> A in + /// + /// The bytes to set the color in. + /// The starting index of the . + void ToXyzwBytes(byte[] bytes, int startIndex); + + /// + /// Expands the packed representation into a given byte array. + /// Output is expanded to Z-> Y-> X order. Equivalent to B-> G-> R in + /// + /// The bytes to set the color in. + /// The starting index of the . + void ToZyxBytes(byte[] bytes, int startIndex); + + /// + /// Expands the packed representation into a given byte array. + /// Output is expanded to Z-> Y-> X-> W order. Equivalent to B-> G-> R-> A in + /// + /// The bytes to set the color in. + /// The starting index of the . + void ToZyxwBytes(byte[] bytes, int startIndex); + } + + public interface IPixel : IPixel, IEquatable + where TSelf : struct, IPixel + { + + } +} \ No newline at end of file diff --git a/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs b/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs index c3fc32234..06de06107 100644 --- a/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs +++ b/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs @@ -16,7 +16,7 @@ namespace ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] public ArrayPointer(T[] array, void* pointerToArray, int offset) { - // TODO: Use Guard.NotNull() here after optimizing it with ThrowHelper! + // TODO: Use Guard.NotNull() here after optimizing it by eliminating the default argument case and applying ThrowHelper! if (array == null) { ThrowHelper.ThrowArgumentNullException(nameof(array)); @@ -29,7 +29,7 @@ namespace ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] public ArrayPointer(T[] array, void* pointerToArray) { - // TODO: Use Guard.NotNull() here after optimizing it with ThrowHelper! + // TODO: Use Guard.NotNull() here after optimizing it by eliminating the default argument case and applying ThrowHelper! if (array == null) { ThrowHelper.ThrowArgumentNullException(nameof(array)); From 1125edcef8907b79c3e62e34b2e273a1ccda7013 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 18 Feb 2017 17:27:57 +0100 Subject: [PATCH 078/142] IPixel --- global.json | 2 +- src/ImageSharp.Drawing.Paths/DrawBeziers.cs | 12 ++-- src/ImageSharp.Drawing.Paths/DrawLines.cs | 12 ++-- src/ImageSharp.Drawing.Paths/DrawPath.cs | 12 ++-- src/ImageSharp.Drawing.Paths/DrawPolygon.cs | 12 ++-- src/ImageSharp.Drawing.Paths/DrawRectangle.cs | 12 ++-- src/ImageSharp.Drawing.Paths/FillPaths.cs | 8 +-- src/ImageSharp.Drawing.Paths/FillPolygon.cs | 8 +-- src/ImageSharp.Drawing.Paths/FillRectangle.cs | 8 +-- .../Brushes/Brushes{TColor}.cs | 2 +- src/ImageSharp.Drawing/Brushes/IBrush.cs | 2 +- .../Brushes/ImageBrush{TColor}.cs | 2 +- .../Brushes/PatternBrush{TColor}.cs | 2 +- .../Brushes/Processors/BrushApplicator.cs | 2 +- .../Brushes/RecolorBrush{TColor}.cs | 2 +- .../Brushes/SolidBrush{TColor}.cs | 2 +- src/ImageSharp.Drawing/DrawImage.cs | 4 +- src/ImageSharp.Drawing/DrawPath.cs | 12 ++-- src/ImageSharp.Drawing/FillRegion.cs | 12 ++-- src/ImageSharp.Drawing/Pens/IPen.cs | 2 +- src/ImageSharp.Drawing/Pens/Pens{TColor}.cs | 2 +- src/ImageSharp.Drawing/Pens/Pen{TColor}.cs | 2 +- .../Pens/Processors/ColoredPointInfo.cs | 2 +- .../Pens/Processors/PenApplicator.cs | 2 +- .../Processors/DrawImageProcessor.cs | 2 +- .../Processors/DrawPathProcessor.cs | 2 +- .../Processors/FillProcessor.cs | 2 +- .../Processors/FillRegionProcessor.cs | 2 +- src/ImageSharp.Formats.Bmp/BmpDecoder.cs | 2 +- src/ImageSharp.Formats.Bmp/BmpDecoderCore.cs | 10 ++-- src/ImageSharp.Formats.Bmp/BmpEncoder.cs | 2 +- src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs | 8 +-- src/ImageSharp.Formats.Bmp/ImageExtensions.cs | 2 +- src/ImageSharp.Formats.Gif/GifDecoder.cs | 2 +- src/ImageSharp.Formats.Gif/GifDecoderCore.cs | 2 +- src/ImageSharp.Formats.Gif/GifEncoder.cs | 2 +- src/ImageSharp.Formats.Gif/GifEncoderCore.cs | 18 +++--- src/ImageSharp.Formats.Gif/ImageExtensions.cs | 2 +- .../ImageExtensions.cs | 2 +- src/ImageSharp.Formats.Jpeg/JpegDecoder.cs | 2 +- .../JpegDecoderCore.cs | 28 +++++----- src/ImageSharp.Formats.Jpeg/JpegEncoder.cs | 2 +- .../JpegEncoderCore.cs | 12 ++-- .../Utils/JpegUtils.cs | 6 +- src/ImageSharp.Formats.Png/ImageExtensions.cs | 2 +- src/ImageSharp.Formats.Png/PngDecoder.cs | 2 +- src/ImageSharp.Formats.Png/PngDecoderCore.cs | 16 +++--- src/ImageSharp.Formats.Png/PngEncoder.cs | 2 +- src/ImageSharp.Formats.Png/PngEncoderCore.cs | 16 +++--- .../Binarization/BinaryThreshold.cs | 4 +- .../Binarization/Dither.cs | 8 +-- .../ColorMatrix/BlackWhite.cs | 4 +- .../ColorMatrix/ColorBlindness.cs | 4 +- .../ColorMatrix/Grayscale.cs | 4 +- src/ImageSharp.Processing/ColorMatrix/Hue.cs | 4 +- .../ColorMatrix/Kodachrome.cs | 4 +- .../ColorMatrix/Lomograph.cs | 4 +- .../ColorMatrix/Polaroid.cs | 4 +- .../ColorMatrix/Saturation.cs | 4 +- .../ColorMatrix/Sepia.cs | 4 +- .../Convolution/BoxBlur.cs | 4 +- .../Convolution/DetectEdges.cs | 12 ++-- .../Convolution/GaussianBlur.cs | 4 +- .../Convolution/GaussianSharpen.cs | 4 +- src/ImageSharp.Processing/Effects/Alpha.cs | 4 +- .../Effects/BackgroundColor.cs | 2 +- .../Effects/Brightness.cs | 4 +- src/ImageSharp.Processing/Effects/Contrast.cs | 4 +- src/ImageSharp.Processing/Effects/Invert.cs | 4 +- .../Effects/OilPainting.cs | 4 +- src/ImageSharp.Processing/Effects/Pixelate.cs | 4 +- src/ImageSharp.Processing/Overlays/Glow.cs | 10 ++-- .../Overlays/Vignette.cs | 10 ++-- .../Binarization/BinaryThresholdProcessor.cs | 2 +- .../ErrorDiffusionDitherProcessor.cs | 2 +- .../Binarization/OrderedDitherProcessor.cs | 2 +- .../ColorMatrix/BlackWhiteProcessor.cs | 2 +- .../ColorBlindness/AchromatomalyProcessor.cs | 2 +- .../ColorBlindness/AchromatopsiaProcessor.cs | 2 +- .../ColorBlindness/DeuteranomalyProcessor.cs | 2 +- .../ColorBlindness/DeuteranopiaProcessor.cs | 2 +- .../ColorBlindness/ProtanomalyProcessor.cs | 2 +- .../ColorBlindness/ProtanopiaProcessor.cs | 2 +- .../ColorBlindness/TritanomalyProcessor.cs | 2 +- .../ColorBlindness/TritanopiaProcessor.cs | 2 +- .../ColorMatrix/ColorMatrixFilter.cs | 2 +- .../ColorMatrix/GrayscaleBt601Processor.cs | 2 +- .../ColorMatrix/GrayscaleBt709Processor.cs | 2 +- .../Processors/ColorMatrix/HueProcessor.cs | 2 +- .../ColorMatrix/IColorMatrixFilter.cs | 2 +- .../ColorMatrix/KodachromeProcessor.cs | 2 +- .../ColorMatrix/LomographProcessor.cs | 2 +- .../ColorMatrix/PolaroidProcessor.cs | 2 +- .../ColorMatrix/SaturationProcessor.cs | 2 +- .../Processors/ColorMatrix/SepiaProcessor.cs | 2 +- .../Convolution/BoxBlurProcessor.cs | 2 +- .../Convolution/Convolution2DProcessor.cs | 2 +- .../Convolution/Convolution2PassProcessor.cs | 2 +- .../Convolution/ConvolutionProcessor.cs | 2 +- .../EdgeDetection/EdgeDetector2DProcessor.cs | 2 +- .../EdgeDetectorCompassProcessor.cs | 2 +- .../EdgeDetection/EdgeDetectorProcessor.cs | 2 +- .../EdgeDetection/IEdgeDetectorProcessor.cs | 2 +- .../EdgeDetection/KayyaliProcessor.cs | 2 +- .../EdgeDetection/KirschProcessor.cs | 2 +- .../EdgeDetection/Laplacian3X3Processor.cs | 2 +- .../EdgeDetection/Laplacian5X5Processor.cs | 2 +- .../LaplacianOfGaussianProcessor.cs | 2 +- .../EdgeDetection/PrewittProcessor.cs | 2 +- .../EdgeDetection/RobertsCrossProcessor.cs | 2 +- .../EdgeDetection/RobinsonProcessor.cs | 2 +- .../EdgeDetection/ScharrProcessor.cs | 2 +- .../EdgeDetection/SobelProcessor.cs | 2 +- .../Convolution/GaussianBlurProcessor.cs | 2 +- .../Convolution/GaussianSharpenProcessor.cs | 2 +- .../Processors/Effects/AlphaProcessor.cs | 2 +- .../Effects/BackgroundColorProcessor.cs | 2 +- .../Processors/Effects/BrightnessProcessor.cs | 2 +- .../Processors/Effects/ContrastProcessor.cs | 2 +- .../Processors/Effects/InvertProcessor.cs | 2 +- .../Effects/OilPaintingProcessor.cs | 2 +- .../Processors/Effects/PixelateProcessor.cs | 2 +- .../Processors/Overlays/GlowProcessor.cs | 2 +- .../Processors/Overlays/VignetteProcessor.cs | 2 +- .../Transforms/CompandingResizeProcessor.cs | 2 +- .../Processors/Transforms/CropProcessor.cs | 2 +- .../Transforms/EntropyCropProcessor.cs | 2 +- .../Processors/Transforms/FlipProcessor.cs | 2 +- .../Transforms/Matrix3x2Processor.cs | 2 +- .../Transforms/ResamplingWeightedProcessor.cs | 2 +- .../Processors/Transforms/ResizeProcessor.cs | 2 +- .../Processors/Transforms/RotateProcessor.cs | 2 +- .../Processors/Transforms/SkewProcessor.cs | 2 +- .../Transforms/AutoOrient.cs | 4 +- src/ImageSharp.Processing/Transforms/Crop.cs | 4 +- .../Transforms/EntropyCrop.cs | 2 +- src/ImageSharp.Processing/Transforms/Flip.cs | 2 +- .../Transforms/Options/ResizeHelper.cs | 12 ++-- src/ImageSharp.Processing/Transforms/Pad.cs | 2 +- .../Transforms/Resize.cs | 14 ++--- .../Transforms/Rotate.cs | 6 +- .../Transforms/RotateFlip.cs | 2 +- src/ImageSharp.Processing/Transforms/Skew.cs | 4 +- src/ImageSharp/Colors/Color.cs | 6 +- src/ImageSharp/Colors/ColorBuilder{TColor}.cs | 2 +- src/ImageSharp/Colors/NamedColors{TColor}.cs | 2 +- src/ImageSharp/Colors/PackedPixel/Alpha8.cs | 6 +- src/ImageSharp/Colors/PackedPixel/Argb.cs | 6 +- src/ImageSharp/Colors/PackedPixel/Bgr565.cs | 6 +- src/ImageSharp/Colors/PackedPixel/Bgra4444.cs | 6 +- src/ImageSharp/Colors/PackedPixel/Bgra5551.cs | 6 +- src/ImageSharp/Colors/PackedPixel/Byte4.cs | 6 +- .../Colors/PackedPixel/HalfSingle.cs | 6 +- .../Colors/PackedPixel/HalfVector2.cs | 6 +- .../Colors/PackedPixel/HalfVector4.cs | 6 +- .../Colors/PackedPixel/IPackedBytes.cs | 55 ------------------- .../Colors/PackedPixel/IPackedPixel.cs | 25 --------- .../Colors/PackedPixel/IPackedVector.cs | 43 --------------- src/ImageSharp/Colors/PackedPixel/IPixel.cs | 25 ++++++--- .../Colors/PackedPixel/NormalizedByte2.cs | 6 +- .../Colors/PackedPixel/NormalizedByte4.cs | 6 +- .../Colors/PackedPixel/NormalizedShort2.cs | 6 +- .../Colors/PackedPixel/NormalizedShort4.cs | 6 +- src/ImageSharp/Colors/PackedPixel/Rg32.cs | 6 +- .../Colors/PackedPixel/Rgba1010102.cs | 6 +- src/ImageSharp/Colors/PackedPixel/Rgba64.cs | 6 +- src/ImageSharp/Colors/PackedPixel/Short2.cs | 6 +- src/ImageSharp/Colors/PackedPixel/Short4.cs | 6 +- .../Common/Extensions/ArrayExtensions.cs | 2 +- src/ImageSharp/Common/Helpers/ImageMaths.cs | 2 +- src/ImageSharp/Common/Helpers/ThrowHelper.cs | 9 +++ .../Common/Memory/ArrayPointer{T}.cs | 44 +++++++++++++-- .../Dithering/ErrorDiffusion/ErrorDiffuser.cs | 2 +- .../ErrorDiffusion/IErrorDiffuser.cs | 2 +- src/ImageSharp/Dithering/Ordered/Bayer.cs | 2 +- .../Dithering/Ordered/IOrderedDither.cs | 2 +- src/ImageSharp/Dithering/Ordered/Ordered.cs | 2 +- src/ImageSharp/Formats/IImageDecoder.cs | 2 +- src/ImageSharp/Formats/IImageEncoder.cs | 2 +- src/ImageSharp/Image/IImageBase{TColor}.cs | 2 +- src/ImageSharp/Image/IImageProcessor.cs | 2 +- src/ImageSharp/Image/ImageBase{TColor}.cs | 2 +- src/ImageSharp/Image/ImageFrame{TColor}.cs | 4 +- .../Image/ImageProcessingExtensions.cs | 2 +- src/ImageSharp/Image/Image{TColor}.cs | 4 +- src/ImageSharp/Image/PixelAccessor{TColor}.cs | 2 +- src/ImageSharp/Image/PixelArea{TColor}.cs | 2 +- src/ImageSharp/Image/PixelPool{TColor}.cs | 2 +- src/ImageSharp/ImageProcessor.cs | 2 +- .../MetaData/Profiles/Exif/ExifProfile.cs | 2 +- src/ImageSharp/Quantizers/IQuantizer.cs | 4 +- .../Quantizers/Octree/OctreeQuantizer.cs | 2 +- src/ImageSharp/Quantizers/Octree/Quantizer.cs | 2 +- .../Quantizers/Palette/PaletteQuantizer.cs | 2 +- src/ImageSharp/Quantizers/Quantize.cs | 4 +- src/ImageSharp/Quantizers/QuantizedImage.cs | 2 +- src/ImageSharp/Quantizers/Wu/WuQuantizer.cs | 2 +- .../Colors/ColorConstructorTests.cs | 2 +- .../Colors/ColorPackingTests.cs | 2 +- .../Formats/Jpg/BadEofJpegTests.cs | 4 +- .../Formats/Jpg/JpegDecoderTests.cs | 8 +-- .../Formats/Jpg/JpegEncoderTests.cs | 4 +- .../Formats/Jpg/JpegUtilsTests.cs | 6 +- .../Image/PixelAccessorTests.cs | 16 +++--- .../TestUtilities/Factories/GenericFactory.cs | 2 +- .../ImageProviders/BlankProvider.cs | 2 +- .../ImageProviders/FileProvider.cs | 2 +- .../ImageProviders/LambdaProvider.cs | 2 +- .../ImageProviders/SolidProvider.cs | 2 +- .../ImageProviders/TestImageProvider.cs | 2 +- .../TestUtilities/ImagingTestCaseUtility.cs | 2 +- .../TestUtilities/TestUtilityExtensions.cs | 2 +- .../Tests/TestImageProviderTests.cs | 22 ++++---- .../Tests/TestUtilityExtensionsTests.cs | 6 +- 214 files changed, 507 insertions(+), 536 deletions(-) delete mode 100644 src/ImageSharp/Colors/PackedPixel/IPackedBytes.cs delete mode 100644 src/ImageSharp/Colors/PackedPixel/IPackedPixel.cs delete mode 100644 src/ImageSharp/Colors/PackedPixel/IPackedVector.cs diff --git a/global.json b/global.json index 7346bdc28..6da79b611 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "projects": [ "src" ], "sdk": { - "version": "1.0.0-preview2-003121" + "version": "1.0.0-preview2-003131" } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing.Paths/DrawBeziers.cs b/src/ImageSharp.Drawing.Paths/DrawBeziers.cs index ef11a3bac..936d5a9ce 100644 --- a/src/ImageSharp.Drawing.Paths/DrawBeziers.cs +++ b/src/ImageSharp.Drawing.Paths/DrawBeziers.cs @@ -29,7 +29,7 @@ namespace ImageSharp /// The options. /// The . public static Image DrawBeziers(this Image source, IBrush brush, float thickness, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(new Pen(brush, thickness), new Path(new BezierLineSegment(points)), options); } @@ -44,7 +44,7 @@ namespace ImageSharp /// The points. /// The . public static Image DrawBeziers(this Image source, IBrush brush, float thickness, Vector2[] points) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(new Pen(brush, thickness), new Path(new BezierLineSegment(points))); } @@ -59,7 +59,7 @@ namespace ImageSharp /// The points. /// The . public static Image DrawBeziers(this Image source, TColor color, float thickness, Vector2[] points) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.DrawBeziers(new SolidBrush(color), thickness, points); } @@ -75,7 +75,7 @@ namespace ImageSharp /// The options. /// The . public static Image DrawBeziers(this Image source, TColor color, float thickness, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.DrawBeziers(new SolidBrush(color), thickness, points, options); } @@ -90,7 +90,7 @@ namespace ImageSharp /// The options. /// The . public static Image DrawBeziers(this Image source, IPen pen, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(pen, new Path(new BezierLineSegment(points)), options); } @@ -104,7 +104,7 @@ namespace ImageSharp /// The points. /// The . public static Image DrawBeziers(this Image source, IPen pen, Vector2[] points) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(pen, new Path(new BezierLineSegment(points))); } diff --git a/src/ImageSharp.Drawing.Paths/DrawLines.cs b/src/ImageSharp.Drawing.Paths/DrawLines.cs index fc046d627..42f4406e8 100644 --- a/src/ImageSharp.Drawing.Paths/DrawLines.cs +++ b/src/ImageSharp.Drawing.Paths/DrawLines.cs @@ -29,7 +29,7 @@ namespace ImageSharp /// The options. /// The . public static Image DrawLines(this Image source, IBrush brush, float thickness, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(new Pen(brush, thickness), new Path(new LinearLineSegment(points)), options); } @@ -44,7 +44,7 @@ namespace ImageSharp /// The points. /// The . public static Image DrawLines(this Image source, IBrush brush, float thickness, Vector2[] points) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(new Pen(brush, thickness), new Path(new LinearLineSegment(points))); } @@ -59,7 +59,7 @@ namespace ImageSharp /// The points. /// The . public static Image DrawLines(this Image source, TColor color, float thickness, Vector2[] points) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.DrawLines(new SolidBrush(color), thickness, points); } @@ -75,7 +75,7 @@ namespace ImageSharp /// The options. /// The .> public static Image DrawLines(this Image source, TColor color, float thickness, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.DrawLines(new SolidBrush(color), thickness, points, options); } @@ -90,7 +90,7 @@ namespace ImageSharp /// The options. /// The . public static Image DrawLines(this Image source, IPen pen, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(pen, new Path(new LinearLineSegment(points)), options); } @@ -104,7 +104,7 @@ namespace ImageSharp /// The points. /// The . public static Image DrawLines(this Image source, IPen pen, Vector2[] points) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(pen, new Path(new LinearLineSegment(points))); } diff --git a/src/ImageSharp.Drawing.Paths/DrawPath.cs b/src/ImageSharp.Drawing.Paths/DrawPath.cs index 29b389fa2..e2c1442de 100644 --- a/src/ImageSharp.Drawing.Paths/DrawPath.cs +++ b/src/ImageSharp.Drawing.Paths/DrawPath.cs @@ -28,7 +28,7 @@ namespace ImageSharp /// The options. /// The . public static Image Draw(this Image source, IPen pen, IPath path, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(pen, new ShapePath(path), options); } @@ -42,7 +42,7 @@ namespace ImageSharp /// The path. /// The . public static Image Draw(this Image source, IPen pen, IPath path) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(pen, path, GraphicsOptions.Default); } @@ -58,7 +58,7 @@ namespace ImageSharp /// The options. /// The . public static Image Draw(this Image source, IBrush brush, float thickness, IPath path, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(new Pen(brush, thickness), path, options); } @@ -73,7 +73,7 @@ namespace ImageSharp /// The path. /// The . public static Image Draw(this Image source, IBrush brush, float thickness, IPath path) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(new Pen(brush, thickness), path); } @@ -89,7 +89,7 @@ namespace ImageSharp /// The options. /// The . public static Image Draw(this Image source, TColor color, float thickness, IPath path, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(new SolidBrush(color), thickness, path, options); } @@ -104,7 +104,7 @@ namespace ImageSharp /// The path. /// The . public static Image Draw(this Image source, TColor color, float thickness, IPath path) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(new SolidBrush(color), thickness, path); } diff --git a/src/ImageSharp.Drawing.Paths/DrawPolygon.cs b/src/ImageSharp.Drawing.Paths/DrawPolygon.cs index 6b3a6f1f3..8043d18e5 100644 --- a/src/ImageSharp.Drawing.Paths/DrawPolygon.cs +++ b/src/ImageSharp.Drawing.Paths/DrawPolygon.cs @@ -29,7 +29,7 @@ namespace ImageSharp /// The options. /// The . public static Image DrawPolygon(this Image source, IBrush brush, float thickness, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(new Pen(brush, thickness), new Polygon(new LinearLineSegment(points)), options); } @@ -44,7 +44,7 @@ namespace ImageSharp /// The points. /// The . public static Image DrawPolygon(this Image source, IBrush brush, float thickness, Vector2[] points) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(new Pen(brush, thickness), new Polygon(new LinearLineSegment(points))); } @@ -59,7 +59,7 @@ namespace ImageSharp /// The points. /// The . public static Image DrawPolygon(this Image source, TColor color, float thickness, Vector2[] points) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.DrawPolygon(new SolidBrush(color), thickness, points); } @@ -75,7 +75,7 @@ namespace ImageSharp /// The options. /// The . public static Image DrawPolygon(this Image source, TColor color, float thickness, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.DrawPolygon(new SolidBrush(color), thickness, points, options); } @@ -89,7 +89,7 @@ namespace ImageSharp /// The points. /// The . public static Image DrawPolygon(this Image source, IPen pen, Vector2[] points) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(pen, new Polygon(new LinearLineSegment(points)), GraphicsOptions.Default); } @@ -104,7 +104,7 @@ namespace ImageSharp /// The options. /// The . public static Image DrawPolygon(this Image source, IPen pen, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(pen, new Polygon(new LinearLineSegment(points)), options); } diff --git a/src/ImageSharp.Drawing.Paths/DrawRectangle.cs b/src/ImageSharp.Drawing.Paths/DrawRectangle.cs index d42fee5dc..b35665240 100644 --- a/src/ImageSharp.Drawing.Paths/DrawRectangle.cs +++ b/src/ImageSharp.Drawing.Paths/DrawRectangle.cs @@ -26,7 +26,7 @@ namespace ImageSharp /// The options. /// The . public static Image Draw(this Image source, IPen pen, Rectangle shape, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(pen, new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height), options); } @@ -40,7 +40,7 @@ namespace ImageSharp /// The shape. /// The . public static Image Draw(this Image source, IPen pen, Rectangle shape) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(pen, shape, GraphicsOptions.Default); } @@ -56,7 +56,7 @@ namespace ImageSharp /// The options. /// The . public static Image Draw(this Image source, IBrush brush, float thickness, Rectangle shape, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(new Pen(brush, thickness), shape, options); } @@ -71,7 +71,7 @@ namespace ImageSharp /// The shape. /// The . public static Image Draw(this Image source, IBrush brush, float thickness, Rectangle shape) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(new Pen(brush, thickness), shape); } @@ -87,7 +87,7 @@ namespace ImageSharp /// The options. /// The . public static Image Draw(this Image source, TColor color, float thickness, Rectangle shape, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(new SolidBrush(color), thickness, shape, options); } @@ -102,7 +102,7 @@ namespace ImageSharp /// The shape. /// The . public static Image Draw(this Image source, TColor color, float thickness, Rectangle shape) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(new SolidBrush(color), thickness, shape); } diff --git a/src/ImageSharp.Drawing.Paths/FillPaths.cs b/src/ImageSharp.Drawing.Paths/FillPaths.cs index 1dd0b0a3a..92e227ce1 100644 --- a/src/ImageSharp.Drawing.Paths/FillPaths.cs +++ b/src/ImageSharp.Drawing.Paths/FillPaths.cs @@ -27,7 +27,7 @@ namespace ImageSharp /// The graphics options. /// The . public static Image Fill(this Image source, IBrush brush, IPath path, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Fill(brush, new ShapeRegion(path), options); } @@ -41,7 +41,7 @@ namespace ImageSharp /// The path. /// The . public static Image Fill(this Image source, IBrush brush, IPath path) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Fill(brush, new ShapeRegion(path), GraphicsOptions.Default); } @@ -56,7 +56,7 @@ namespace ImageSharp /// The options. /// The . public static Image Fill(this Image source, TColor color, IPath path, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Fill(new SolidBrush(color), path, options); } @@ -70,7 +70,7 @@ namespace ImageSharp /// The path. /// The . public static Image Fill(this Image source, TColor color, IPath path) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Fill(new SolidBrush(color), path); } diff --git a/src/ImageSharp.Drawing.Paths/FillPolygon.cs b/src/ImageSharp.Drawing.Paths/FillPolygon.cs index b41267b9e..cd3d15466 100644 --- a/src/ImageSharp.Drawing.Paths/FillPolygon.cs +++ b/src/ImageSharp.Drawing.Paths/FillPolygon.cs @@ -27,7 +27,7 @@ namespace ImageSharp /// The options. /// The . public static Image FillPolygon(this Image source, IBrush brush, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Fill(brush, new Polygon(new LinearLineSegment(points)), options); } @@ -41,7 +41,7 @@ namespace ImageSharp /// The points. /// The . public static Image FillPolygon(this Image source, IBrush brush, Vector2[] points) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Fill(brush, new Polygon(new LinearLineSegment(points))); } @@ -56,7 +56,7 @@ namespace ImageSharp /// The options. /// The . public static Image FillPolygon(this Image source, TColor color, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Fill(new SolidBrush(color), new Polygon(new LinearLineSegment(points)), options); } @@ -70,7 +70,7 @@ namespace ImageSharp /// The points. /// The . public static Image FillPolygon(this Image source, TColor color, Vector2[] points) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Fill(new SolidBrush(color), new Polygon(new LinearLineSegment(points))); } diff --git a/src/ImageSharp.Drawing.Paths/FillRectangle.cs b/src/ImageSharp.Drawing.Paths/FillRectangle.cs index 3b2cef613..1928e54d3 100644 --- a/src/ImageSharp.Drawing.Paths/FillRectangle.cs +++ b/src/ImageSharp.Drawing.Paths/FillRectangle.cs @@ -25,7 +25,7 @@ namespace ImageSharp /// The options. /// The . public static Image Fill(this Image source, IBrush brush, Rectangle shape, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Fill(brush, new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height), options); } @@ -39,7 +39,7 @@ namespace ImageSharp /// The shape. /// The . public static Image Fill(this Image source, IBrush brush, Rectangle shape) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Fill(brush, new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height)); } @@ -54,7 +54,7 @@ namespace ImageSharp /// The options. /// The . public static Image Fill(this Image source, TColor color, Rectangle shape, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Fill(new SolidBrush(color), shape, options); } @@ -68,7 +68,7 @@ namespace ImageSharp /// The shape. /// The . public static Image Fill(this Image source, TColor color, Rectangle shape) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Fill(new SolidBrush(color), shape); } diff --git a/src/ImageSharp.Drawing/Brushes/Brushes{TColor}.cs b/src/ImageSharp.Drawing/Brushes/Brushes{TColor}.cs index a5cf02b8a..d77a6d594 100644 --- a/src/ImageSharp.Drawing/Brushes/Brushes{TColor}.cs +++ b/src/ImageSharp.Drawing/Brushes/Brushes{TColor}.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Drawing.Brushes /// The pixel format. /// A Brush public class Brushes - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Percent10 Hatch Pattern diff --git a/src/ImageSharp.Drawing/Brushes/IBrush.cs b/src/ImageSharp.Drawing/Brushes/IBrush.cs index b28180204..df05fa23e 100644 --- a/src/ImageSharp.Drawing/Brushes/IBrush.cs +++ b/src/ImageSharp.Drawing/Brushes/IBrush.cs @@ -18,7 +18,7 @@ namespace ImageSharp.Drawing /// logic for converting a pixel location to a . /// public interface IBrush - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Creates the applicator for this brush. diff --git a/src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs b/src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs index b9bcc2f9a..2707c0064 100644 --- a/src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs +++ b/src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Drawing.Brushes /// /// The pixel format. public class ImageBrush : IBrush - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The image to paint. diff --git a/src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs b/src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs index 3fea53052..741ab3f00 100644 --- a/src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs +++ b/src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs @@ -42,7 +42,7 @@ namespace ImageSharp.Drawing.Brushes /// /// The pixel format. public class PatternBrush : IBrush - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The pattern. diff --git a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs index 37af8cd04..b66827e49 100644 --- a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs +++ b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Drawing.Processors /// The pixel format. /// public abstract class BrushApplicator : IDisposable // disposable will be required if/when there is an ImageBrush - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Gets the color for a single pixel. diff --git a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs index 33403facb..542c3cfed 100644 --- a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs +++ b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Drawing.Brushes /// /// The pixel format. public class RecolorBrush : IBrush - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs b/src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs index 018c272b7..30351dbe1 100644 --- a/src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs +++ b/src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Drawing.Brushes /// /// The pixel format. public class SolidBrush : IBrush - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The color to paint. diff --git a/src/ImageSharp.Drawing/DrawImage.cs b/src/ImageSharp.Drawing/DrawImage.cs index 2fba227ee..16582e7ee 100644 --- a/src/ImageSharp.Drawing/DrawImage.cs +++ b/src/ImageSharp.Drawing/DrawImage.cs @@ -23,7 +23,7 @@ namespace ImageSharp /// The opacity of the image image to blend. Must be between 0 and 100. /// The . public static Image Blend(this Image source, Image image, int percent = 50) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return DrawImage(source, image, percent, default(Size), default(Point)); } @@ -39,7 +39,7 @@ namespace ImageSharp /// The location to draw the blended image. /// The . public static Image DrawImage(this Image source, Image image, int percent, Size size, Point location) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { if (size == default(Size)) { diff --git a/src/ImageSharp.Drawing/DrawPath.cs b/src/ImageSharp.Drawing/DrawPath.cs index 75a0d8157..e91b97203 100644 --- a/src/ImageSharp.Drawing/DrawPath.cs +++ b/src/ImageSharp.Drawing/DrawPath.cs @@ -27,7 +27,7 @@ namespace ImageSharp /// The options. /// The . public static Image Draw(this Image source, IPen pen, Drawable path, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Apply(new DrawPathProcessor(pen, path, options)); } @@ -41,7 +41,7 @@ namespace ImageSharp /// The path. /// The . public static Image Draw(this Image source, IPen pen, Drawable path) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(pen, path, GraphicsOptions.Default); } @@ -57,7 +57,7 @@ namespace ImageSharp /// The options. /// The . public static Image Draw(this Image source, IBrush brush, float thickness, Drawable path, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(new Pen(brush, thickness), path, options); } @@ -72,7 +72,7 @@ namespace ImageSharp /// The path. /// The . public static Image Draw(this Image source, IBrush brush, float thickness, Drawable path) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(new Pen(brush, thickness), path); } @@ -88,7 +88,7 @@ namespace ImageSharp /// The options. /// The . public static Image Draw(this Image source, TColor color, float thickness, Drawable path, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(new SolidBrush(color), thickness, path, options); } @@ -103,7 +103,7 @@ namespace ImageSharp /// The path. /// The . public static Image Draw(this Image source, TColor color, float thickness, Drawable path) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Draw(new SolidBrush(color), thickness, path); } diff --git a/src/ImageSharp.Drawing/FillRegion.cs b/src/ImageSharp.Drawing/FillRegion.cs index 6faf519af..8aab20251 100644 --- a/src/ImageSharp.Drawing/FillRegion.cs +++ b/src/ImageSharp.Drawing/FillRegion.cs @@ -24,7 +24,7 @@ namespace ImageSharp /// The details how to fill the region of interest. /// The . public static Image Fill(this Image source, IBrush brush) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Apply(new FillProcessor(brush)); } @@ -37,7 +37,7 @@ namespace ImageSharp /// The color. /// The . public static Image Fill(this Image source, TColor color) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Fill(new SolidBrush(color)); } @@ -52,7 +52,7 @@ namespace ImageSharp /// The graphics options. /// The . public static Image Fill(this Image source, IBrush brush, Region region, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Apply(new FillRegionProcessor(brush, region, options)); } @@ -66,7 +66,7 @@ namespace ImageSharp /// The region. /// The . public static Image Fill(this Image source, IBrush brush, Region region) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Fill(brush, region, GraphicsOptions.Default); } @@ -81,7 +81,7 @@ namespace ImageSharp /// The options. /// The . public static Image Fill(this Image source, TColor color, Region region, GraphicsOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Fill(new SolidBrush(color), region, options); } @@ -95,7 +95,7 @@ namespace ImageSharp /// The region. /// The . public static Image Fill(this Image source, TColor color, Region region) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Fill(new SolidBrush(color), region); } diff --git a/src/ImageSharp.Drawing/Pens/IPen.cs b/src/ImageSharp.Drawing/Pens/IPen.cs index 0cf473427..72a5ffc36 100644 --- a/src/ImageSharp.Drawing/Pens/IPen.cs +++ b/src/ImageSharp.Drawing/Pens/IPen.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Drawing.Pens /// /// The type of the color. public interface IPen - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Creates the applicator for applying this pen to an Image diff --git a/src/ImageSharp.Drawing/Pens/Pens{TColor}.cs b/src/ImageSharp.Drawing/Pens/Pens{TColor}.cs index 94a282659..49eed370d 100644 --- a/src/ImageSharp.Drawing/Pens/Pens{TColor}.cs +++ b/src/ImageSharp.Drawing/Pens/Pens{TColor}.cs @@ -12,7 +12,7 @@ namespace ImageSharp.Drawing.Pens /// /// The type of the color. public class Pens - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { private static readonly float[] DashDotPattern = new[] { 3f, 1f, 1f, 1f }; private static readonly float[] DashDotDotPattern = new[] { 3f, 1f, 1f, 1f, 1f, 1f }; diff --git a/src/ImageSharp.Drawing/Pens/Pen{TColor}.cs b/src/ImageSharp.Drawing/Pens/Pen{TColor}.cs index d0ea55c1e..79a5d6b15 100644 --- a/src/ImageSharp.Drawing/Pens/Pen{TColor}.cs +++ b/src/ImageSharp.Drawing/Pens/Pen{TColor}.cs @@ -24,7 +24,7 @@ namespace ImageSharp.Drawing.Pens /// the the pattern will imidiatly repeat without gap. /// public class Pen : IPen - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { private static readonly float[] EmptyPattern = new float[0]; private readonly float[] pattern; diff --git a/src/ImageSharp.Drawing/Pens/Processors/ColoredPointInfo.cs b/src/ImageSharp.Drawing/Pens/Processors/ColoredPointInfo.cs index 494f0f4e4..d042bdccb 100644 --- a/src/ImageSharp.Drawing/Pens/Processors/ColoredPointInfo.cs +++ b/src/ImageSharp.Drawing/Pens/Processors/ColoredPointInfo.cs @@ -12,7 +12,7 @@ namespace ImageSharp.Drawing.Processors /// /// The type of the color. public struct ColoredPointInfo - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The color diff --git a/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs b/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs index 86283b5bb..8cdb04b45 100644 --- a/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs +++ b/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Drawing.Processors /// /// The type of the color. public abstract class PenApplicator : IDisposable - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Gets the required region. diff --git a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs index 81b40e655..1c1de45cb 100644 --- a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs @@ -16,7 +16,7 @@ namespace ImageSharp.Drawing.Processors /// /// The pixel format. public class DrawImageProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs index 83ae9521c..913293ff3 100644 --- a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs @@ -18,7 +18,7 @@ namespace ImageSharp.Drawing.Processors /// The type of the color. /// public class DrawPathProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { private const float AntialiasFactor = 1f; private const int PaddingFactor = 1; // needs to been the same or greater than AntialiasFactor diff --git a/src/ImageSharp.Drawing/Processors/FillProcessor.cs b/src/ImageSharp.Drawing/Processors/FillProcessor.cs index 37bdac90f..9fa01075d 100644 --- a/src/ImageSharp.Drawing/Processors/FillProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillProcessor.cs @@ -17,7 +17,7 @@ namespace ImageSharp.Drawing.Processors /// /// The pixel format. public class FillProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The brush. diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs index 67313d84b..fed97275d 100644 --- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs @@ -18,7 +18,7 @@ namespace ImageSharp.Drawing.Processors /// The type of the color. /// public class FillRegionProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { private const float AntialiasFactor = 1f; private const int DrawPadding = 1; diff --git a/src/ImageSharp.Formats.Bmp/BmpDecoder.cs b/src/ImageSharp.Formats.Bmp/BmpDecoder.cs index ae7e6c72a..7546972dc 100644 --- a/src/ImageSharp.Formats.Bmp/BmpDecoder.cs +++ b/src/ImageSharp.Formats.Bmp/BmpDecoder.cs @@ -27,7 +27,7 @@ namespace ImageSharp.Formats { /// public void Decode(Image image, Stream stream) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { Guard.NotNull(image, "image"); Guard.NotNull(stream, "stream"); diff --git a/src/ImageSharp.Formats.Bmp/BmpDecoderCore.cs b/src/ImageSharp.Formats.Bmp/BmpDecoderCore.cs index 562417601..a75031ea1 100644 --- a/src/ImageSharp.Formats.Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp.Formats.Bmp/BmpDecoderCore.cs @@ -58,7 +58,7 @@ namespace ImageSharp.Formats /// is null. /// public void Decode(Image image, Stream stream) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { this.currentStream = stream; @@ -212,7 +212,7 @@ namespace ImageSharp.Formats /// The number of bits per pixel. /// Whether the bitmap is inverted. private void ReadRgbPalette(PixelAccessor pixels, byte[] colors, int width, int height, int bits, bool inverted) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { // Pixels per byte (bits per pixel) int ppb = 8 / bits; @@ -267,7 +267,7 @@ namespace ImageSharp.Formats /// The height of the bitmap. /// Whether the bitmap is inverted. private void ReadRgb16(PixelAccessor pixels, int width, int height, bool inverted) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { // We divide here as we will store the colors in our floating point format. const int ScaleR = 8; // 256/32 @@ -309,7 +309,7 @@ namespace ImageSharp.Formats /// The height of the bitmap. /// Whether the bitmap is inverted. private void ReadRgb24(PixelAccessor pixels, int width, int height, bool inverted) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { int padding = CalculatePadding(width, 3); using (PixelArea row = new PixelArea(width, ComponentOrder.Zyx, padding)) @@ -333,7 +333,7 @@ namespace ImageSharp.Formats /// The height of the bitmap. /// Whether the bitmap is inverted. private void ReadRgb32(PixelAccessor pixels, int width, int height, bool inverted) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { int padding = CalculatePadding(width, 4); using (PixelArea row = new PixelArea(width, ComponentOrder.Zyxw, padding)) diff --git a/src/ImageSharp.Formats.Bmp/BmpEncoder.cs b/src/ImageSharp.Formats.Bmp/BmpEncoder.cs index 2ed6b3b98..36b56b392 100644 --- a/src/ImageSharp.Formats.Bmp/BmpEncoder.cs +++ b/src/ImageSharp.Formats.Bmp/BmpEncoder.cs @@ -21,7 +21,7 @@ namespace ImageSharp.Formats /// public void Encode(Image image, Stream stream) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { BmpEncoderCore encoder = new BmpEncoderCore(); encoder.Encode(image, stream, this.BitsPerPixel); diff --git a/src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs b/src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs index 8abe7cbfc..02d270a0a 100644 --- a/src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs @@ -33,7 +33,7 @@ namespace ImageSharp.Formats /// The to encode the image data to. /// The public void Encode(ImageBase image, Stream stream, BmpBitsPerPixel bitsPerPixel) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); @@ -124,7 +124,7 @@ namespace ImageSharp.Formats /// The containing pixel data. /// private void WriteImage(EndianBinaryWriter writer, ImageBase image) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { using (PixelAccessor pixels = image.Lock()) { @@ -148,7 +148,7 @@ namespace ImageSharp.Formats /// The containing the stream to write to. /// The containing pixel data. private void Write32Bit(EndianBinaryWriter writer, PixelAccessor pixels) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { using (PixelArea row = new PixelArea(pixels.Width, ComponentOrder.Zyxw, this.padding)) { @@ -167,7 +167,7 @@ namespace ImageSharp.Formats /// The containing the stream to write to. /// The containing pixel data. private void Write24Bit(EndianBinaryWriter writer, PixelAccessor pixels) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { using (PixelArea row = new PixelArea(pixels.Width, ComponentOrder.Zyx, this.padding)) { diff --git a/src/ImageSharp.Formats.Bmp/ImageExtensions.cs b/src/ImageSharp.Formats.Bmp/ImageExtensions.cs index 8bbae8487..5b92b90d6 100644 --- a/src/ImageSharp.Formats.Bmp/ImageExtensions.cs +++ b/src/ImageSharp.Formats.Bmp/ImageExtensions.cs @@ -26,7 +26,7 @@ namespace ImageSharp /// The . /// public static Image SaveAsBmp(this Image source, Stream stream) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel => source.Save(stream, new BmpEncoder()); } } diff --git a/src/ImageSharp.Formats.Gif/GifDecoder.cs b/src/ImageSharp.Formats.Gif/GifDecoder.cs index ce8c0c06e..914a9098e 100644 --- a/src/ImageSharp.Formats.Gif/GifDecoder.cs +++ b/src/ImageSharp.Formats.Gif/GifDecoder.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Formats { /// public void Decode(Image image, Stream stream) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { new GifDecoderCore().Decode(image, stream); } diff --git a/src/ImageSharp.Formats.Gif/GifDecoderCore.cs b/src/ImageSharp.Formats.Gif/GifDecoderCore.cs index e6f7b6136..5812b9f29 100644 --- a/src/ImageSharp.Formats.Gif/GifDecoderCore.cs +++ b/src/ImageSharp.Formats.Gif/GifDecoderCore.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Formats /// /// The pixel format. internal class GifDecoderCore - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The temp buffer used to reduce allocations. diff --git a/src/ImageSharp.Formats.Gif/GifEncoder.cs b/src/ImageSharp.Formats.Gif/GifEncoder.cs index 2446f0f68..9000cdb64 100644 --- a/src/ImageSharp.Formats.Gif/GifEncoder.cs +++ b/src/ImageSharp.Formats.Gif/GifEncoder.cs @@ -33,7 +33,7 @@ namespace ImageSharp.Formats /// public void Encode(Image image, Stream stream) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { GifEncoderCore encoder = new GifEncoderCore { diff --git a/src/ImageSharp.Formats.Gif/GifEncoderCore.cs b/src/ImageSharp.Formats.Gif/GifEncoderCore.cs index 80c9ee36b..36cf614d9 100644 --- a/src/ImageSharp.Formats.Gif/GifEncoderCore.cs +++ b/src/ImageSharp.Formats.Gif/GifEncoderCore.cs @@ -51,7 +51,7 @@ namespace ImageSharp.Formats /// The to encode from. /// The to encode the image data to. public void Encode(Image image, Stream stream) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); @@ -121,7 +121,7 @@ namespace ImageSharp.Formats /// The . /// private int GetTransparentIndex(QuantizedImage quantized) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { // Find the lowest alpha value and make it the transparent index. int index = 255; @@ -171,7 +171,7 @@ namespace ImageSharp.Formats /// The writer to write to the stream with. /// The transparency index to set the default background index to. private void WriteLogicalScreenDescriptor(Image image, EndianBinaryWriter writer, int tranparencyIndex) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { GifLogicalScreenDescriptor descriptor = new GifLogicalScreenDescriptor { @@ -237,7 +237,7 @@ namespace ImageSharp.Formats /// The stream to write to. /// The index of the color in the color palette to make transparent. private void WriteGraphicalControlExtension(Image image, EndianBinaryWriter writer, int transparencyIndex) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { this.WriteGraphicalControlExtension(image, image.MetaData, writer, transparencyIndex); } @@ -250,7 +250,7 @@ namespace ImageSharp.Formats /// The stream to write to. /// The index of the color in the color palette to make transparent. private void WriteGraphicalControlExtension(ImageFrame imageFrame, EndianBinaryWriter writer, int transparencyIndex) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { this.WriteGraphicalControlExtension(imageFrame, imageFrame.MetaData, writer, transparencyIndex); } @@ -264,7 +264,7 @@ namespace ImageSharp.Formats /// The stream to write to. /// The index of the color in the color palette to make transparent. private void WriteGraphicalControlExtension(ImageBase image, IMetaData metaData, EndianBinaryWriter writer, int transparencyIndex) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { // TODO: Check transparency logic. bool hasTransparent = transparencyIndex < 255; @@ -306,7 +306,7 @@ namespace ImageSharp.Formats /// The to be encoded. /// The stream to write to. private void WriteImageDescriptor(ImageBase image, EndianBinaryWriter writer) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { writer.Write(GifConstants.ImageDescriptorLabel); // 2c @@ -332,7 +332,7 @@ namespace ImageSharp.Formats /// The to encode. /// The writer to write to the stream with. private void WriteColorTable(QuantizedImage image, EndianBinaryWriter writer) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { // Grab the palette and write it to the stream. int pixelCount = image.Palette.Length; @@ -367,7 +367,7 @@ namespace ImageSharp.Formats /// The containing indexed pixels. /// The stream to write to. private void WriteImageData(QuantizedImage image, EndianBinaryWriter writer) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { using (LzwEncoder encoder = new LzwEncoder(image.Pixels, (byte)this.bitDepth)) { diff --git a/src/ImageSharp.Formats.Gif/ImageExtensions.cs b/src/ImageSharp.Formats.Gif/ImageExtensions.cs index 09c836a68..e0ec2e8c8 100644 --- a/src/ImageSharp.Formats.Gif/ImageExtensions.cs +++ b/src/ImageSharp.Formats.Gif/ImageExtensions.cs @@ -27,7 +27,7 @@ namespace ImageSharp /// The . /// public static Image SaveAsGif(this Image source, Stream stream, int quality = 256) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel => source.Save(stream, new GifEncoder { Quality = quality }); } } diff --git a/src/ImageSharp.Formats.Jpeg/ImageExtensions.cs b/src/ImageSharp.Formats.Jpeg/ImageExtensions.cs index c0cda4d46..2cbba02a9 100644 --- a/src/ImageSharp.Formats.Jpeg/ImageExtensions.cs +++ b/src/ImageSharp.Formats.Jpeg/ImageExtensions.cs @@ -27,7 +27,7 @@ namespace ImageSharp /// The . /// public static Image SaveAsJpeg(this Image source, Stream stream, int quality = 75) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel => source.Save(stream, new JpegEncoder { Quality = quality }); } } diff --git a/src/ImageSharp.Formats.Jpeg/JpegDecoder.cs b/src/ImageSharp.Formats.Jpeg/JpegDecoder.cs index 9ee216b4d..b4df94d52 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegDecoder.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegDecoder.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Formats { /// public void Decode(Image image, Stream stream) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { Guard.NotNull(image, "image"); Guard.NotNull(stream, "stream"); diff --git a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs index eb70c4f8c..657787682 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs @@ -177,7 +177,7 @@ namespace ImageSharp.Formats /// The stream, where the image should be. /// Whether to decode metadata only. public void Decode(Image image, Stream stream, bool metadataOnly) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { this.ProcessStream(image, stream, metadataOnly); if (!metadataOnly) @@ -248,7 +248,7 @@ namespace ImageSharp.Formats /// The cr chroma component. [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void PackYcbCr(ref TColor packed, byte y, byte cb, byte cr) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { int ccb = cb - 128; int ccr = cr - 128; @@ -274,7 +274,7 @@ namespace ImageSharp.Formats /// The stream /// Whether to decode metadata only. private void ProcessStream(Image image, Stream stream, bool metadataOnly) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { this.InputStream = stream; this.InputProcessor = new InputProcessor(stream, this.Temp); @@ -472,7 +472,7 @@ namespace ImageSharp.Formats /// /// The pixel type private void ProcessBlocksIntoJpegImageChannels() - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { Parallel.For( 0, @@ -491,7 +491,7 @@ namespace ImageSharp.Formats /// The pixel type /// The destination image private void ConvertJpegPixelsToImagePixels(Image image) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { if (this.grayImage.IsInitialized) { @@ -549,7 +549,7 @@ namespace ImageSharp.Formats /// The pixel format. /// The image to assign the resolution to. private void AssignResolution(Image image) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { if (this.isJfif && this.horizontalResolution > 0 && this.verticalResolution > 0) { @@ -579,7 +579,7 @@ namespace ImageSharp.Formats /// The image height. /// The image. private void ConvertFromCmyk(int width, int height, Image image) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; @@ -620,7 +620,7 @@ namespace ImageSharp.Formats /// The image height. /// The image. private void ConvertFromGrayScale(int width, int height, Image image) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { image.InitPixels(width, height); @@ -655,7 +655,7 @@ namespace ImageSharp.Formats /// The height. /// The image. private void ConvertFromRGB(int width, int height, Image image) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; image.InitPixels(width, height); @@ -696,7 +696,7 @@ namespace ImageSharp.Formats /// The image height. /// The image. private void ConvertFromYCbCr(int width, int height, Image image) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; image.InitPixels(width, height); @@ -737,7 +737,7 @@ namespace ImageSharp.Formats /// The image height. /// The image. private void ConvertFromYcck(int width, int height, Image image) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; @@ -862,7 +862,7 @@ namespace ImageSharp.Formats /// The x-position within the image. /// The y-position within the image. private void PackCmyk(ref TColor packed, byte c, byte m, byte y, int xx, int yy) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { // Get keyline float keyline = (255 - this.blackImage[xx, yy]) / 255F; @@ -887,7 +887,7 @@ namespace ImageSharp.Formats /// The x-position within the image. /// The y-position within the image. private void PackYcck(ref TColor packed, byte y, byte cb, byte cr, int xx, int yy) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { // Convert the YCbCr part of the YCbCrK to RGB, invert the RGB to get // CMY, and patch in the original K. The RGB to CMY inversion cancels @@ -956,7 +956,7 @@ namespace ImageSharp.Formats /// The remaining bytes in the segment block. /// The image. private void ProcessApp1Marker(int remaining, Image image) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { if (remaining < 6) { diff --git a/src/ImageSharp.Formats.Jpeg/JpegEncoder.cs b/src/ImageSharp.Formats.Jpeg/JpegEncoder.cs index e56a9f2e8..07d9b24cd 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegEncoder.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegEncoder.cs @@ -62,7 +62,7 @@ namespace ImageSharp.Formats /// public void Encode(Image image, Stream stream) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { // Ensure that quality can be set but has a fallback. if (image.MetaData.Quality > 0) diff --git a/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs b/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs index 657470f5c..0309af129 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs @@ -157,7 +157,7 @@ namespace ImageSharp.Formats /// The quality. /// The subsampling mode. public void Encode(Image image, Stream stream, int quality, JpegSubsample sample) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); @@ -287,7 +287,7 @@ namespace ImageSharp.Formats Block8x8F* cbBlock, Block8x8F* crBlock, PixelArea rgbBytes) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { float* yBlockRaw = (float*)yBlock; float* cbBlockRaw = (float*)cbBlock; @@ -433,7 +433,7 @@ namespace ImageSharp.Formats /// The pixel format. /// The pixel accessor providing access to the image pixels. private void Encode444(PixelAccessor pixels) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { // TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) Block8x8F b = default(Block8x8F); @@ -704,7 +704,7 @@ namespace ImageSharp.Formats /// The image. /// The pixel format. private void WriteProfiles(Image image) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { image.MetaData.SyncProfiles(); this.WriteProfile(image.MetaData.ExifProfile); @@ -772,7 +772,7 @@ namespace ImageSharp.Formats /// The pixel format. /// The pixel accessor providing access to the image pixels. private void WriteStartOfScan(PixelAccessor pixels) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { // TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) // TODO: We should allow grayscale writing. @@ -799,7 +799,7 @@ namespace ImageSharp.Formats /// The pixel format. /// The pixel accessor providing access to the image pixels. private void Encode420(PixelAccessor pixels) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { // TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) Block8x8F b = default(Block8x8F); diff --git a/src/ImageSharp.Formats.Jpeg/Utils/JpegUtils.cs b/src/ImageSharp.Formats.Jpeg/Utils/JpegUtils.cs index 7f62058b7..dd96985d9 100644 --- a/src/ImageSharp.Formats.Jpeg/Utils/JpegUtils.cs +++ b/src/ImageSharp.Formats.Jpeg/Utils/JpegUtils.cs @@ -25,7 +25,7 @@ namespace ImageSharp.Formats.Jpg PixelArea dest, int sourceY, int sourceX) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { pixels.SafeCopyTo(dest, sourceY, sourceX); int stretchFromX = pixels.Width - sourceX; @@ -49,13 +49,13 @@ namespace ImageSharp.Formats.Jpg // Nothing to stretch if (fromX, fromY) is outside the area, or is at (0,0) [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsInvalidStretchStartingPosition(PixelArea area, int fromX, int fromY) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return fromX <= 0 || fromY <= 0 || fromX >= area.Width || fromY >= area.Height; } private static void StretchPixels(PixelArea area, int fromX, int fromY) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { if (IsInvalidStretchStartingPosition(area, fromX, fromY)) { diff --git a/src/ImageSharp.Formats.Png/ImageExtensions.cs b/src/ImageSharp.Formats.Png/ImageExtensions.cs index d46c46217..dcb1c988b 100644 --- a/src/ImageSharp.Formats.Png/ImageExtensions.cs +++ b/src/ImageSharp.Formats.Png/ImageExtensions.cs @@ -29,7 +29,7 @@ namespace ImageSharp /// The . /// public static Image SaveAsPng(this Image source, Stream stream, int quality = int.MaxValue) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel => source.Save(stream, new PngEncoder { Quality = quality }); } } diff --git a/src/ImageSharp.Formats.Png/PngDecoder.cs b/src/ImageSharp.Formats.Png/PngDecoder.cs index 845f0f222..088bea591 100644 --- a/src/ImageSharp.Formats.Png/PngDecoder.cs +++ b/src/ImageSharp.Formats.Png/PngDecoder.cs @@ -37,7 +37,7 @@ namespace ImageSharp.Formats /// The to decode to. /// The containing image data. public void Decode(Image image, Stream stream) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { new PngDecoderCore().Decode(image, stream); } diff --git a/src/ImageSharp.Formats.Png/PngDecoderCore.cs b/src/ImageSharp.Formats.Png/PngDecoderCore.cs index 9e871e987..47b09d5ff 100644 --- a/src/ImageSharp.Formats.Png/PngDecoderCore.cs +++ b/src/ImageSharp.Formats.Png/PngDecoderCore.cs @@ -138,7 +138,7 @@ namespace ImageSharp.Formats /// Thrown if the image is larger than the maximum allowable size. /// public void Decode(Image image, Stream stream) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { Image currentImage = image; this.currentStream = stream; @@ -262,7 +262,7 @@ namespace ImageSharp.Formats /// The image to read to. /// The data containing physical data. private void ReadPhysicalChunk(Image image, byte[] data) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { data.ReverseBytes(0, 4); data.ReverseBytes(4, 4); @@ -325,7 +325,7 @@ namespace ImageSharp.Formats /// The containing data. /// The pixel data. private void ReadScanlines(MemoryStream dataStream, PixelAccessor pixels) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { this.bytesPerPixel = this.CalculateBytesPerPixel(); this.bytesPerScanline = this.CalculateScanlineLength(this.header.Width) + 1; @@ -356,7 +356,7 @@ namespace ImageSharp.Formats /// The compressed pixel data stream. /// The image pixel accessor. private void DecodePixelData(Stream compressedStream, PixelAccessor pixels) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { byte[] previousScanline = ArrayPool.Shared.Rent(this.bytesPerScanline); byte[] scanline = ArrayPool.Shared.Rent(this.bytesPerScanline); @@ -429,7 +429,7 @@ namespace ImageSharp.Formats /// The compressed pixel data stream. /// The image pixel accessor. private void DecodeInterlacedPixelData(Stream compressedStream, PixelAccessor pixels) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { byte[] previousScanline = ArrayPool.Shared.Rent(this.bytesPerScanline); byte[] scanline = ArrayPool.Shared.Rent(this.bytesPerScanline); @@ -518,7 +518,7 @@ namespace ImageSharp.Formats /// The current image row. /// The image pixels private void ProcessDefilteredScanline(byte[] defilteredScanline, int row, PixelAccessor pixels) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { TColor color = default(TColor); switch (this.PngColorType) @@ -643,7 +643,7 @@ namespace ImageSharp.Formats /// The column start index. Always 0 for none interlaced images. /// The column increment. Always 1 for none interlaced images. private void ProcessInterlacedDefilteredScanline(byte[] defilteredScanline, int row, PixelAccessor pixels, int pixelOffset = 0, int increment = 1) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { TColor color = default(TColor); @@ -761,7 +761,7 @@ namespace ImageSharp.Formats /// The containing data. /// The maximum length to read. private void ReadTextChunk(Image image, byte[] data, int length) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { int zeroIndex = 0; diff --git a/src/ImageSharp.Formats.Png/PngEncoder.cs b/src/ImageSharp.Formats.Png/PngEncoder.cs index 13125e30e..a6f5e38a5 100644 --- a/src/ImageSharp.Formats.Png/PngEncoder.cs +++ b/src/ImageSharp.Formats.Png/PngEncoder.cs @@ -57,7 +57,7 @@ namespace ImageSharp.Formats /// public void Encode(Image image, Stream stream) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { PngEncoderCore encoder = new PngEncoderCore { diff --git a/src/ImageSharp.Formats.Png/PngEncoderCore.cs b/src/ImageSharp.Formats.Png/PngEncoderCore.cs index 9acaf5b9f..2324853cb 100644 --- a/src/ImageSharp.Formats.Png/PngEncoderCore.cs +++ b/src/ImageSharp.Formats.Png/PngEncoderCore.cs @@ -132,7 +132,7 @@ namespace ImageSharp.Formats /// The to encode from. /// The to encode the image data to. public void Encode(Image image, Stream stream) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); @@ -262,7 +262,7 @@ namespace ImageSharp.Formats /// The containing image data. /// The . private void CollectIndexedBytes(ImageBase image, Stream stream, PngHeader header) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { // Quantize the image and get the pixels. QuantizedImage quantized = this.WritePaletteChunk(stream, header, image); @@ -277,7 +277,7 @@ namespace ImageSharp.Formats /// The row index. /// The raw scanline. private void CollectGrayscaleBytes(PixelAccessor pixels, int row, byte[] rawScanline) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { // Copy the pixels across from the image. // Reuse the chunk type buffer. @@ -311,7 +311,7 @@ namespace ImageSharp.Formats /// The row index. /// The raw scanline. private void CollectColorBytes(PixelAccessor pixels, int row, byte[] rawScanline) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { // We can use the optimized PixelAccessor here and copy the bytes in unmanaged memory. using (PixelArea pixelRow = new PixelArea(this.width, rawScanline, this.bytesPerPixel == 4 ? ComponentOrder.Xyzw : ComponentOrder.Xyz)) @@ -332,7 +332,7 @@ namespace ImageSharp.Formats /// The filtered scanline result. /// The private byte[] EncodePixelRow(PixelAccessor pixels, int row, byte[] previousScanline, byte[] rawScanline, byte[] result) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { switch (this.PngColorType) { @@ -486,7 +486,7 @@ namespace ImageSharp.Formats /// The image to encode. /// The private QuantizedImage WritePaletteChunk(Stream stream, PngHeader header, ImageBase image) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { if (this.Quality > 256) { @@ -554,7 +554,7 @@ namespace ImageSharp.Formats /// The containing image data. /// The image base. private void WritePhysicalChunk(Stream stream, ImageBase imageBase) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { Image image = imageBase as Image; if (image != null && image.MetaData.HorizontalResolution > 0 && image.MetaData.VerticalResolution > 0) @@ -600,7 +600,7 @@ namespace ImageSharp.Formats /// The pixel accessor. /// The stream. private void WriteDataChunks(PixelAccessor pixels, Stream stream) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { int bytesPerScanline = this.width * this.bytesPerPixel; byte[] previousScanline = new byte[bytesPerScanline]; diff --git a/src/ImageSharp.Processing/Binarization/BinaryThreshold.cs b/src/ImageSharp.Processing/Binarization/BinaryThreshold.cs index e59369349..672726d92 100644 --- a/src/ImageSharp.Processing/Binarization/BinaryThreshold.cs +++ b/src/ImageSharp.Processing/Binarization/BinaryThreshold.cs @@ -22,7 +22,7 @@ namespace ImageSharp /// The threshold to apply binarization of the image. Must be between 0 and 1. /// The . public static Image BinaryThreshold(this Image source, float threshold) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return BinaryThreshold(source, threshold, source.Bounds); } @@ -38,7 +38,7 @@ namespace ImageSharp /// /// The . public static Image BinaryThreshold(this Image source, float threshold, Rectangle rectangle) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { source.ApplyProcessor(new BinaryThresholdProcessor(threshold), rectangle); return source; diff --git a/src/ImageSharp.Processing/Binarization/Dither.cs b/src/ImageSharp.Processing/Binarization/Dither.cs index 6a4f7f005..dd6dfe8a1 100644 --- a/src/ImageSharp.Processing/Binarization/Dither.cs +++ b/src/ImageSharp.Processing/Binarization/Dither.cs @@ -24,7 +24,7 @@ namespace ImageSharp /// The component index to test the threshold against. Must range from 0 to 3. /// The . public static Image Dither(this Image source, IOrderedDither dither, int index = 0) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Dither(source, dither, source.Bounds, index); } @@ -41,7 +41,7 @@ namespace ImageSharp /// The component index to test the threshold against. Must range from 0 to 3. /// The . public static Image Dither(this Image source, IOrderedDither dither, Rectangle rectangle, int index = 0) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { source.ApplyProcessor(new OrderedDitherProcessor(dither, index), rectangle); return source; @@ -56,7 +56,7 @@ namespace ImageSharp /// The threshold to apply binarization of the image. Must be between 0 and 1. /// The . public static Image Dither(this Image source, IErrorDiffuser diffuser, float threshold) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Dither(source, diffuser, threshold, source.Bounds); } @@ -73,7 +73,7 @@ namespace ImageSharp /// /// The . public static Image Dither(this Image source, IErrorDiffuser diffuser, float threshold, Rectangle rectangle) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { source.ApplyProcessor(new ErrorDiffusionDitherProcessor(diffuser, threshold), rectangle); return source; diff --git a/src/ImageSharp.Processing/ColorMatrix/BlackWhite.cs b/src/ImageSharp.Processing/ColorMatrix/BlackWhite.cs index 05fb8f19c..63d6dd33c 100644 --- a/src/ImageSharp.Processing/ColorMatrix/BlackWhite.cs +++ b/src/ImageSharp.Processing/ColorMatrix/BlackWhite.cs @@ -22,7 +22,7 @@ namespace ImageSharp /// The image this method extends. /// The . public static Image BlackWhite(this Image source) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return BlackWhite(source, source.Bounds); } @@ -37,7 +37,7 @@ namespace ImageSharp /// /// The . public static Image BlackWhite(this Image source, Rectangle rectangle) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { source.ApplyProcessor(new BlackWhiteProcessor(), rectangle); return source; diff --git a/src/ImageSharp.Processing/ColorMatrix/ColorBlindness.cs b/src/ImageSharp.Processing/ColorMatrix/ColorBlindness.cs index 30b28861d..36a139d0e 100644 --- a/src/ImageSharp.Processing/ColorMatrix/ColorBlindness.cs +++ b/src/ImageSharp.Processing/ColorMatrix/ColorBlindness.cs @@ -23,7 +23,7 @@ namespace ImageSharp /// The type of color blindness simulator to apply. /// The . public static Image ColorBlindness(this Image source, ColorBlindness colorBlindness) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return ColorBlindness(source, colorBlindness, source.Bounds); } @@ -39,7 +39,7 @@ namespace ImageSharp /// /// The . public static Image ColorBlindness(this Image source, ColorBlindness colorBlindness, Rectangle rectangle) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { IImageProcessor processor; diff --git a/src/ImageSharp.Processing/ColorMatrix/Grayscale.cs b/src/ImageSharp.Processing/ColorMatrix/Grayscale.cs index b2aadc46d..613b999d4 100644 --- a/src/ImageSharp.Processing/ColorMatrix/Grayscale.cs +++ b/src/ImageSharp.Processing/ColorMatrix/Grayscale.cs @@ -23,7 +23,7 @@ namespace ImageSharp /// The formula to apply to perform the operation. /// The . public static Image Grayscale(this Image source, GrayscaleMode mode = GrayscaleMode.Bt709) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Grayscale(source, source.Bounds, mode); } @@ -39,7 +39,7 @@ namespace ImageSharp /// The formula to apply to perform the operation. /// The . public static Image Grayscale(this Image source, Rectangle rectangle, GrayscaleMode mode = GrayscaleMode.Bt709) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { IImageProcessor processor = mode == GrayscaleMode.Bt709 ? (IImageProcessor)new GrayscaleBt709Processor() diff --git a/src/ImageSharp.Processing/ColorMatrix/Hue.cs b/src/ImageSharp.Processing/ColorMatrix/Hue.cs index 25fcc6c55..8edeb2ff3 100644 --- a/src/ImageSharp.Processing/ColorMatrix/Hue.cs +++ b/src/ImageSharp.Processing/ColorMatrix/Hue.cs @@ -23,7 +23,7 @@ namespace ImageSharp /// The angle in degrees to adjust the image. /// The . public static Image Hue(this Image source, float degrees) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Hue(source, degrees, source.Bounds); } @@ -39,7 +39,7 @@ namespace ImageSharp /// /// The . public static Image Hue(this Image source, float degrees, Rectangle rectangle) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { source.ApplyProcessor(new HueProcessor(degrees), rectangle); return source; diff --git a/src/ImageSharp.Processing/ColorMatrix/Kodachrome.cs b/src/ImageSharp.Processing/ColorMatrix/Kodachrome.cs index dab0224c3..5084c96b2 100644 --- a/src/ImageSharp.Processing/ColorMatrix/Kodachrome.cs +++ b/src/ImageSharp.Processing/ColorMatrix/Kodachrome.cs @@ -22,7 +22,7 @@ namespace ImageSharp /// The image this method extends. /// The . public static Image Kodachrome(this Image source) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Kodachrome(source, source.Bounds); } @@ -37,7 +37,7 @@ namespace ImageSharp /// /// The . public static Image Kodachrome(this Image source, Rectangle rectangle) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { source.ApplyProcessor(new KodachromeProcessor(), rectangle); return source; diff --git a/src/ImageSharp.Processing/ColorMatrix/Lomograph.cs b/src/ImageSharp.Processing/ColorMatrix/Lomograph.cs index df34dc52e..ef6b23d5d 100644 --- a/src/ImageSharp.Processing/ColorMatrix/Lomograph.cs +++ b/src/ImageSharp.Processing/ColorMatrix/Lomograph.cs @@ -22,7 +22,7 @@ namespace ImageSharp /// The image this method extends. /// The . public static Image Lomograph(this Image source) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Lomograph(source, source.Bounds); } @@ -37,7 +37,7 @@ namespace ImageSharp /// /// The . public static Image Lomograph(this Image source, Rectangle rectangle) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { source.ApplyProcessor(new LomographProcessor(), rectangle); return source; diff --git a/src/ImageSharp.Processing/ColorMatrix/Polaroid.cs b/src/ImageSharp.Processing/ColorMatrix/Polaroid.cs index 4bb8f82a3..68b10173c 100644 --- a/src/ImageSharp.Processing/ColorMatrix/Polaroid.cs +++ b/src/ImageSharp.Processing/ColorMatrix/Polaroid.cs @@ -22,7 +22,7 @@ namespace ImageSharp /// The image this method extends. /// The . public static Image Polaroid(this Image source) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Polaroid(source, source.Bounds); } @@ -37,7 +37,7 @@ namespace ImageSharp /// /// The . public static Image Polaroid(this Image source, Rectangle rectangle) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { source.ApplyProcessor(new PolaroidProcessor(), rectangle); return source; diff --git a/src/ImageSharp.Processing/ColorMatrix/Saturation.cs b/src/ImageSharp.Processing/ColorMatrix/Saturation.cs index a92483c9c..7a6359744 100644 --- a/src/ImageSharp.Processing/ColorMatrix/Saturation.cs +++ b/src/ImageSharp.Processing/ColorMatrix/Saturation.cs @@ -23,7 +23,7 @@ namespace ImageSharp /// The new saturation of the image. Must be between -100 and 100. /// The . public static Image Saturation(this Image source, int amount) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Saturation(source, amount, source.Bounds); } @@ -39,7 +39,7 @@ namespace ImageSharp /// /// The . public static Image Saturation(this Image source, int amount, Rectangle rectangle) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { source.ApplyProcessor(new SaturationProcessor(amount), rectangle); return source; diff --git a/src/ImageSharp.Processing/ColorMatrix/Sepia.cs b/src/ImageSharp.Processing/ColorMatrix/Sepia.cs index 1a8ec4b95..4943635e0 100644 --- a/src/ImageSharp.Processing/ColorMatrix/Sepia.cs +++ b/src/ImageSharp.Processing/ColorMatrix/Sepia.cs @@ -22,7 +22,7 @@ namespace ImageSharp /// The image this method extends. /// The . public static Image Sepia(this Image source) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Sepia(source, source.Bounds); } @@ -37,7 +37,7 @@ namespace ImageSharp /// /// The . public static Image Sepia(this Image source, Rectangle rectangle) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { source.ApplyProcessor(new SepiaProcessor(), rectangle); return source; diff --git a/src/ImageSharp.Processing/Convolution/BoxBlur.cs b/src/ImageSharp.Processing/Convolution/BoxBlur.cs index 6bdd68b9b..428142ffa 100644 --- a/src/ImageSharp.Processing/Convolution/BoxBlur.cs +++ b/src/ImageSharp.Processing/Convolution/BoxBlur.cs @@ -22,7 +22,7 @@ namespace ImageSharp /// The 'radius' value representing the size of the area to sample. /// The . public static Image BoxBlur(this Image source, int radius = 7) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return BoxBlur(source, radius, source.Bounds); } @@ -38,7 +38,7 @@ namespace ImageSharp /// /// The . public static Image BoxBlur(this Image source, int radius, Rectangle rectangle) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { source.ApplyProcessor(new BoxBlurProcessor(radius), rectangle); return source; diff --git a/src/ImageSharp.Processing/Convolution/DetectEdges.cs b/src/ImageSharp.Processing/Convolution/DetectEdges.cs index a61726e74..dba062b56 100644 --- a/src/ImageSharp.Processing/Convolution/DetectEdges.cs +++ b/src/ImageSharp.Processing/Convolution/DetectEdges.cs @@ -23,7 +23,7 @@ namespace ImageSharp /// The image this method extends. /// The . public static Image DetectEdges(this Image source) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return DetectEdges(source, source.Bounds, new SobelProcessor { Grayscale = true }); } @@ -39,7 +39,7 @@ namespace ImageSharp /// /// The . public static Image DetectEdges(this Image source, Rectangle rectangle) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return DetectEdges(source, rectangle, new SobelProcessor { Grayscale = true }); } @@ -53,7 +53,7 @@ namespace ImageSharp /// Whether to convert the image to Grayscale first. Defaults to true. /// The . public static Image DetectEdges(this Image source, EdgeDetection filter, bool grayscale = true) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return DetectEdges(source, filter, source.Bounds, grayscale); } @@ -70,7 +70,7 @@ namespace ImageSharp /// Whether to convert the image to Grayscale first. Defaults to true. /// The . public static Image DetectEdges(this Image source, EdgeDetection filter, Rectangle rectangle, bool grayscale = true) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { IEdgeDetectorProcessor processor; @@ -128,7 +128,7 @@ namespace ImageSharp /// The filter for detecting edges. /// The . public static Image DetectEdges(this Image source, IEdgeDetectorProcessor filter) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return DetectEdges(source, source.Bounds, filter); } @@ -144,7 +144,7 @@ namespace ImageSharp /// The filter for detecting edges. /// The . public static Image DetectEdges(this Image source, Rectangle rectangle, IEdgeDetectorProcessor filter) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { source.ApplyProcessor(filter, rectangle); return source; diff --git a/src/ImageSharp.Processing/Convolution/GaussianBlur.cs b/src/ImageSharp.Processing/Convolution/GaussianBlur.cs index 893ebb264..81f854638 100644 --- a/src/ImageSharp.Processing/Convolution/GaussianBlur.cs +++ b/src/ImageSharp.Processing/Convolution/GaussianBlur.cs @@ -23,7 +23,7 @@ namespace ImageSharp /// The 'sigma' value representing the weight of the blur. /// The . public static Image GaussianBlur(this Image source, float sigma = 3f) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return GaussianBlur(source, sigma, source.Bounds); } @@ -39,7 +39,7 @@ namespace ImageSharp /// /// The . public static Image GaussianBlur(this Image source, float sigma, Rectangle rectangle) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { source.ApplyProcessor(new GaussianBlurProcessor(sigma), rectangle); return source; diff --git a/src/ImageSharp.Processing/Convolution/GaussianSharpen.cs b/src/ImageSharp.Processing/Convolution/GaussianSharpen.cs index e3f8e995b..61816198a 100644 --- a/src/ImageSharp.Processing/Convolution/GaussianSharpen.cs +++ b/src/ImageSharp.Processing/Convolution/GaussianSharpen.cs @@ -23,7 +23,7 @@ namespace ImageSharp /// The 'sigma' value representing the weight of the blur. /// The . public static Image GaussianSharpen(this Image source, float sigma = 3f) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return GaussianSharpen(source, sigma, source.Bounds); } @@ -39,7 +39,7 @@ namespace ImageSharp /// /// The . public static Image GaussianSharpen(this Image source, float sigma, Rectangle rectangle) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { source.ApplyProcessor(new GaussianSharpenProcessor(sigma), rectangle); return source; diff --git a/src/ImageSharp.Processing/Effects/Alpha.cs b/src/ImageSharp.Processing/Effects/Alpha.cs index 21b6dfbcb..39849d4d4 100644 --- a/src/ImageSharp.Processing/Effects/Alpha.cs +++ b/src/ImageSharp.Processing/Effects/Alpha.cs @@ -22,7 +22,7 @@ namespace ImageSharp /// The new opacity of the image. Must be between 0 and 100. /// The . public static Image Alpha(this Image source, int percent) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Alpha(source, percent, source.Bounds); } @@ -38,7 +38,7 @@ namespace ImageSharp /// /// The . public static Image Alpha(this Image source, int percent, Rectangle rectangle) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { source.ApplyProcessor(new AlphaProcessor(percent), rectangle); return source; diff --git a/src/ImageSharp.Processing/Effects/BackgroundColor.cs b/src/ImageSharp.Processing/Effects/BackgroundColor.cs index 0f724c08c..2e621172e 100644 --- a/src/ImageSharp.Processing/Effects/BackgroundColor.cs +++ b/src/ImageSharp.Processing/Effects/BackgroundColor.cs @@ -22,7 +22,7 @@ namespace ImageSharp /// The color to set as the background. /// The . public static Image BackgroundColor(this Image source, TColor color) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { source.ApplyProcessor(new BackgroundColorProcessor(color), source.Bounds); return source; diff --git a/src/ImageSharp.Processing/Effects/Brightness.cs b/src/ImageSharp.Processing/Effects/Brightness.cs index bf5500faf..8ba702c4f 100644 --- a/src/ImageSharp.Processing/Effects/Brightness.cs +++ b/src/ImageSharp.Processing/Effects/Brightness.cs @@ -22,7 +22,7 @@ namespace ImageSharp /// The new brightness of the image. Must be between -100 and 100. /// The . public static Image Brightness(this Image source, int amount) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Brightness(source, amount, source.Bounds); } @@ -38,7 +38,7 @@ namespace ImageSharp /// /// The . public static Image Brightness(this Image source, int amount, Rectangle rectangle) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { source.ApplyProcessor(new BrightnessProcessor(amount), rectangle); return source; diff --git a/src/ImageSharp.Processing/Effects/Contrast.cs b/src/ImageSharp.Processing/Effects/Contrast.cs index f05eea639..0228f4fe3 100644 --- a/src/ImageSharp.Processing/Effects/Contrast.cs +++ b/src/ImageSharp.Processing/Effects/Contrast.cs @@ -22,7 +22,7 @@ namespace ImageSharp /// The new contrast of the image. Must be between -100 and 100. /// The . public static Image Contrast(this Image source, int amount) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Contrast(source, amount, source.Bounds); } @@ -38,7 +38,7 @@ namespace ImageSharp /// /// The . public static Image Contrast(this Image source, int amount, Rectangle rectangle) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { source.ApplyProcessor(new ContrastProcessor(amount), rectangle); return source; diff --git a/src/ImageSharp.Processing/Effects/Invert.cs b/src/ImageSharp.Processing/Effects/Invert.cs index fff3108df..6c51ad3eb 100644 --- a/src/ImageSharp.Processing/Effects/Invert.cs +++ b/src/ImageSharp.Processing/Effects/Invert.cs @@ -21,7 +21,7 @@ namespace ImageSharp /// The image this method extends. /// The . public static Image Invert(this Image source) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Invert(source, source.Bounds); } @@ -36,7 +36,7 @@ namespace ImageSharp /// /// The . public static Image Invert(this Image source, Rectangle rectangle) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { source.ApplyProcessor(new InvertProcessor(), rectangle); return source; diff --git a/src/ImageSharp.Processing/Effects/OilPainting.cs b/src/ImageSharp.Processing/Effects/OilPainting.cs index fbd777711..d7d8444c0 100644 --- a/src/ImageSharp.Processing/Effects/OilPainting.cs +++ b/src/ImageSharp.Processing/Effects/OilPainting.cs @@ -23,7 +23,7 @@ namespace ImageSharp /// The number of neighboring pixels used in calculating each individual pixel value. /// The . public static Image OilPaint(this Image source, int levels = 10, int brushSize = 15) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return OilPaint(source, levels, brushSize, source.Bounds); } @@ -40,7 +40,7 @@ namespace ImageSharp /// /// The . public static Image OilPaint(this Image source, int levels, int brushSize, Rectangle rectangle) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { Guard.MustBeGreaterThan(levels, 0, nameof(levels)); diff --git a/src/ImageSharp.Processing/Effects/Pixelate.cs b/src/ImageSharp.Processing/Effects/Pixelate.cs index 77b014e73..721dd930b 100644 --- a/src/ImageSharp.Processing/Effects/Pixelate.cs +++ b/src/ImageSharp.Processing/Effects/Pixelate.cs @@ -22,7 +22,7 @@ namespace ImageSharp /// The size of the pixels. /// The . public static Image Pixelate(this Image source, int size = 4) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Pixelate(source, size, source.Bounds); } @@ -38,7 +38,7 @@ namespace ImageSharp /// /// The . public static Image Pixelate(this Image source, int size, Rectangle rectangle) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { if (size <= 0 || size > source.Height || size > source.Width) { diff --git a/src/ImageSharp.Processing/Overlays/Glow.cs b/src/ImageSharp.Processing/Overlays/Glow.cs index 9c5fe017f..e8dfbdf0e 100644 --- a/src/ImageSharp.Processing/Overlays/Glow.cs +++ b/src/ImageSharp.Processing/Overlays/Glow.cs @@ -21,7 +21,7 @@ namespace ImageSharp /// The image this method extends. /// The . public static Image Glow(this Image source) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Glow(source, NamedColors.Black, source.Bounds.Width * .5F, source.Bounds); } @@ -34,7 +34,7 @@ namespace ImageSharp /// The color to set as the glow. /// The . public static Image Glow(this Image source, TColor color) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Glow(source, color, source.Bounds.Width * .5F, source.Bounds); } @@ -47,7 +47,7 @@ namespace ImageSharp /// The the radius. /// The . public static Image Glow(this Image source, float radius) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Glow(source, NamedColors.Black, radius, source.Bounds); } @@ -62,7 +62,7 @@ namespace ImageSharp /// /// The . public static Image Glow(this Image source, Rectangle rectangle) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Glow(source, NamedColors.Black, 0, rectangle); } @@ -79,7 +79,7 @@ namespace ImageSharp /// /// The . public static Image Glow(this Image source, TColor color, float radius, Rectangle rectangle) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { GlowProcessor processor = new GlowProcessor(color) { Radius = radius, }; source.ApplyProcessor(processor, rectangle); diff --git a/src/ImageSharp.Processing/Overlays/Vignette.cs b/src/ImageSharp.Processing/Overlays/Vignette.cs index 4a505ad9b..e42ead8d3 100644 --- a/src/ImageSharp.Processing/Overlays/Vignette.cs +++ b/src/ImageSharp.Processing/Overlays/Vignette.cs @@ -21,7 +21,7 @@ namespace ImageSharp /// The image this method extends. /// The . public static Image Vignette(this Image source) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Vignette(source, NamedColors.Black, source.Bounds.Width * .5F, source.Bounds.Height * .5F, source.Bounds); } @@ -34,7 +34,7 @@ namespace ImageSharp /// The color to set as the vignette. /// The . public static Image Vignette(this Image source, TColor color) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Vignette(source, color, source.Bounds.Width * .5F, source.Bounds.Height * .5F, source.Bounds); } @@ -48,7 +48,7 @@ namespace ImageSharp /// The the y-radius. /// The . public static Image Vignette(this Image source, float radiusX, float radiusY) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Vignette(source, NamedColors.Black, radiusX, radiusY, source.Bounds); } @@ -63,7 +63,7 @@ namespace ImageSharp /// /// The . public static Image Vignette(this Image source, Rectangle rectangle) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Vignette(source, NamedColors.Black, 0, 0, rectangle); } @@ -81,7 +81,7 @@ namespace ImageSharp /// /// The . public static Image Vignette(this Image source, TColor color, float radiusX, float radiusY, Rectangle rectangle) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { VignetteProcessor processor = new VignetteProcessor(color) { RadiusX = radiusX, RadiusY = radiusY }; source.ApplyProcessor(processor, rectangle); diff --git a/src/ImageSharp.Processing/Processors/Binarization/BinaryThresholdProcessor.cs b/src/ImageSharp.Processing/Processors/Binarization/BinaryThresholdProcessor.cs index e4010413f..2d57957d3 100644 --- a/src/ImageSharp.Processing/Processors/Binarization/BinaryThresholdProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Binarization/BinaryThresholdProcessor.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class BinaryThresholdProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs b/src/ImageSharp.Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs index 25b464c69..ce03c58a8 100644 --- a/src/ImageSharp.Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class ErrorDiffusionDitherProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Binarization/OrderedDitherProcessor.cs b/src/ImageSharp.Processing/Processors/Binarization/OrderedDitherProcessor.cs index f201ba49a..4126419f2 100644 --- a/src/ImageSharp.Processing/Processors/Binarization/OrderedDitherProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Binarization/OrderedDitherProcessor.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class OrderedDitherProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/ColorMatrix/BlackWhiteProcessor.cs b/src/ImageSharp.Processing/Processors/ColorMatrix/BlackWhiteProcessor.cs index 305375eca..0ea821bef 100644 --- a/src/ImageSharp.Processing/Processors/ColorMatrix/BlackWhiteProcessor.cs +++ b/src/ImageSharp.Processing/Processors/ColorMatrix/BlackWhiteProcessor.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class BlackWhiteProcessor : ColorMatrixFilter - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs b/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs index 3e34d0838..15e7c78da 100644 --- a/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs +++ b/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class AchromatomalyProcessor : ColorMatrixFilter - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs b/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs index 53a7a3556..adca0fe98 100644 --- a/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs +++ b/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class AchromatopsiaProcessor : ColorMatrixFilter - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs b/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs index 5d252961c..6de54beea 100644 --- a/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs +++ b/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class DeuteranomalyProcessor : ColorMatrixFilter - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs b/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs index cd48df401..4729ccc61 100644 --- a/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs +++ b/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class DeuteranopiaProcessor : ColorMatrixFilter - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs b/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs index 234c2e13b..200fff24d 100644 --- a/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs +++ b/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class ProtanomalyProcessor : ColorMatrixFilter - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs b/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs index a88b8812e..7c0f03543 100644 --- a/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs +++ b/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class ProtanopiaProcessor : ColorMatrixFilter - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs b/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs index 1f68bddbb..63f1fd9eb 100644 --- a/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs +++ b/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class TritanomalyProcessor : ColorMatrixFilter - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs b/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs index 85332e810..2200414fe 100644 --- a/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs +++ b/src/ImageSharp.Processing/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class TritanopiaProcessor : ColorMatrixFilter - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageSharp.Processing/Processors/ColorMatrix/ColorMatrixFilter.cs b/src/ImageSharp.Processing/Processors/ColorMatrix/ColorMatrixFilter.cs index b11b82b10..a37228a9b 100644 --- a/src/ImageSharp.Processing/Processors/ColorMatrix/ColorMatrixFilter.cs +++ b/src/ImageSharp.Processing/Processors/ColorMatrix/ColorMatrixFilter.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public abstract class ColorMatrixFilter : ImageProcessor, IColorMatrixFilter - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// public abstract Matrix4x4 Matrix { get; } diff --git a/src/ImageSharp.Processing/Processors/ColorMatrix/GrayscaleBt601Processor.cs b/src/ImageSharp.Processing/Processors/ColorMatrix/GrayscaleBt601Processor.cs index 364919e74..bd14da59e 100644 --- a/src/ImageSharp.Processing/Processors/ColorMatrix/GrayscaleBt601Processor.cs +++ b/src/ImageSharp.Processing/Processors/ColorMatrix/GrayscaleBt601Processor.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class GrayscaleBt601Processor : ColorMatrixFilter - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageSharp.Processing/Processors/ColorMatrix/GrayscaleBt709Processor.cs b/src/ImageSharp.Processing/Processors/ColorMatrix/GrayscaleBt709Processor.cs index 37d41ab74..925a36c75 100644 --- a/src/ImageSharp.Processing/Processors/ColorMatrix/GrayscaleBt709Processor.cs +++ b/src/ImageSharp.Processing/Processors/ColorMatrix/GrayscaleBt709Processor.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class GrayscaleBt709Processor : ColorMatrixFilter - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageSharp.Processing/Processors/ColorMatrix/HueProcessor.cs b/src/ImageSharp.Processing/Processors/ColorMatrix/HueProcessor.cs index 0de0891fe..fdf5ffdb4 100644 --- a/src/ImageSharp.Processing/Processors/ColorMatrix/HueProcessor.cs +++ b/src/ImageSharp.Processing/Processors/ColorMatrix/HueProcessor.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class HueProcessor : ColorMatrixFilter - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/ColorMatrix/IColorMatrixFilter.cs b/src/ImageSharp.Processing/Processors/ColorMatrix/IColorMatrixFilter.cs index 4230fda12..faee890eb 100644 --- a/src/ImageSharp.Processing/Processors/ColorMatrix/IColorMatrixFilter.cs +++ b/src/ImageSharp.Processing/Processors/ColorMatrix/IColorMatrixFilter.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public interface IColorMatrixFilter : IImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Gets the used to alter the image. diff --git a/src/ImageSharp.Processing/Processors/ColorMatrix/KodachromeProcessor.cs b/src/ImageSharp.Processing/Processors/ColorMatrix/KodachromeProcessor.cs index 84a05e579..fee168498 100644 --- a/src/ImageSharp.Processing/Processors/ColorMatrix/KodachromeProcessor.cs +++ b/src/ImageSharp.Processing/Processors/ColorMatrix/KodachromeProcessor.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class KodachromeProcessor : ColorMatrixFilter - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageSharp.Processing/Processors/ColorMatrix/LomographProcessor.cs b/src/ImageSharp.Processing/Processors/ColorMatrix/LomographProcessor.cs index ad166b134..0e614afe8 100644 --- a/src/ImageSharp.Processing/Processors/ColorMatrix/LomographProcessor.cs +++ b/src/ImageSharp.Processing/Processors/ColorMatrix/LomographProcessor.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class LomographProcessor : ColorMatrixFilter - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { private static readonly TColor VeryDarkGreen = ColorBuilder.FromRGBA(0, 10, 0, 255); diff --git a/src/ImageSharp.Processing/Processors/ColorMatrix/PolaroidProcessor.cs b/src/ImageSharp.Processing/Processors/ColorMatrix/PolaroidProcessor.cs index 5df11160f..666fe5bc0 100644 --- a/src/ImageSharp.Processing/Processors/ColorMatrix/PolaroidProcessor.cs +++ b/src/ImageSharp.Processing/Processors/ColorMatrix/PolaroidProcessor.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class PolaroidProcessor : ColorMatrixFilter - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { private static TColor veryDarkOrange = ColorBuilder.FromRGB(102, 34, 0); private static TColor lightOrange = ColorBuilder.FromRGBA(255, 153, 102, 178); diff --git a/src/ImageSharp.Processing/Processors/ColorMatrix/SaturationProcessor.cs b/src/ImageSharp.Processing/Processors/ColorMatrix/SaturationProcessor.cs index 430228d53..d63326385 100644 --- a/src/ImageSharp.Processing/Processors/ColorMatrix/SaturationProcessor.cs +++ b/src/ImageSharp.Processing/Processors/ColorMatrix/SaturationProcessor.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class SaturationProcessor : ColorMatrixFilter - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/ColorMatrix/SepiaProcessor.cs b/src/ImageSharp.Processing/Processors/ColorMatrix/SepiaProcessor.cs index 1170fc3a9..d8fdc6cd1 100644 --- a/src/ImageSharp.Processing/Processors/ColorMatrix/SepiaProcessor.cs +++ b/src/ImageSharp.Processing/Processors/ColorMatrix/SepiaProcessor.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class SepiaProcessor : ColorMatrixFilter - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4 diff --git a/src/ImageSharp.Processing/Processors/Convolution/BoxBlurProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/BoxBlurProcessor.cs index 8ca1ca666..367095b2b 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/BoxBlurProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/BoxBlurProcessor.cs @@ -12,7 +12,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class BoxBlurProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The maximum size of the kernel in either direction. diff --git a/src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs index 71b71ba5d..d104f5d35 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/Convolution2DProcessor.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class Convolution2DProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs index 495bfa5db..1d118443c 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/Convolution2PassProcessor.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class Convolution2PassProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs index 46df9c6ee..6b5b6d3fe 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/ConvolutionProcessor.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class ConvolutionProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs index f86940ade..a8c786f71 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs @@ -12,7 +12,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public abstract class EdgeDetector2DProcessor : ImageProcessor, IEdgeDetectorProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs index 58967cb5b..eb8491d4c 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public abstract class EdgeDetectorCompassProcessor : ImageProcessor, IEdgeDetectorProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Gets the North gradient operator diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs index cd2b91f16..a963bb578 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs @@ -12,7 +12,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public abstract class EdgeDetectorProcessor : ImageProcessor, IEdgeDetectorProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/IEdgeDetectorProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/IEdgeDetectorProcessor.cs index 68dc7ccdb..7c0923bbb 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/IEdgeDetectorProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/IEdgeDetectorProcessor.cs @@ -12,7 +12,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public interface IEdgeDetectorProcessor : IImageProcessor, IEdgeDetectorProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { } diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs index 6e1452e17..6cdd5757e 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Processing.Processors /// The pixel format. [SuppressMessage("ReSharper", "StaticMemberInGenericType", Justification = "We want to use only one instance of each array field for each generic type.")] public class KayyaliProcessor : EdgeDetector2DProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The horizontal gradient operator. diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs index f8cb9aba9..e4e4e7bfa 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Processing.Processors /// The pixel format. [SuppressMessage("ReSharper", "StaticMemberInGenericType", Justification = "We want to use only one instance of each array field for each generic type.")] public class KirschProcessor : EdgeDetectorCompassProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The North gradient operator diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs index 4b23dfe47..6dcea8b00 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Processing.Processors /// The pixel format. [SuppressMessage("ReSharper", "StaticMemberInGenericType", Justification = "We want to use only one instance of each array field for each generic type.")] public class Laplacian3X3Processor : EdgeDetectorProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The 2d gradient operator. diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs index 304dafbbd..239cbb968 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Processing.Processors /// The pixel format. [SuppressMessage("ReSharper", "StaticMemberInGenericType", Justification = "We want to use only one instance of each array field for each generic type.")] public class Laplacian5X5Processor : EdgeDetectorProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The 2d gradient operator. diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs index e18957a69..73bc453a3 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Processing.Processors /// The pixel format. [SuppressMessage("ReSharper", "StaticMemberInGenericType", Justification = "We want to use only one instance of each array field for each generic type.")] public class LaplacianOfGaussianProcessor : EdgeDetectorProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The 2d gradient operator. diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs index 1d61b8cd9..5d4737e51 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Processing.Processors /// The pixel format. [SuppressMessage("ReSharper", "StaticMemberInGenericType", Justification = "We want to use only one instance of each array field for each generic type.")] public class PrewittProcessor : EdgeDetector2DProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The horizontal gradient operator. diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs index 83f13a342..1be583cf3 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Processing.Processors /// The pixel format. [SuppressMessage("ReSharper", "StaticMemberInGenericType", Justification = "We want to use only one instance of each array field for each generic type.")] public class RobertsCrossProcessor : EdgeDetector2DProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The horizontal gradient operator. diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs index a8187caca..87651c3c6 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Processing.Processors /// The pixel format. [SuppressMessage("ReSharper", "StaticMemberInGenericType", Justification = "We want to use only one instance of each array field for each generic type.")] public class RobinsonProcessor : EdgeDetectorCompassProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The North gradient operator diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs index 6b9e67ce9..4be2e720b 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Processing.Processors /// The pixel format. [SuppressMessage("ReSharper", "StaticMemberInGenericType", Justification = "We want to use only one instance of each array field for each generic type.")] public class ScharrProcessor : EdgeDetector2DProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The horizontal gradient operator. diff --git a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs index 160788967..bb64f3e13 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Processing.Processors /// The pixel format. [SuppressMessage("ReSharper", "StaticMemberInGenericType", Justification = "We want to use only one instance of each array field for each generic type.")] public class SobelProcessor : EdgeDetector2DProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The horizontal gradient operator. diff --git a/src/ImageSharp.Processing/Processors/Convolution/GaussianBlurProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/GaussianBlurProcessor.cs index 49733a9bd..daa660f84 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/GaussianBlurProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/GaussianBlurProcessor.cs @@ -12,7 +12,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class GaussianBlurProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The maximum size of the kernel in either direction. diff --git a/src/ImageSharp.Processing/Processors/Convolution/GaussianSharpenProcessor.cs b/src/ImageSharp.Processing/Processors/Convolution/GaussianSharpenProcessor.cs index 2254a61b6..a51bb5b9f 100644 --- a/src/ImageSharp.Processing/Processors/Convolution/GaussianSharpenProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Convolution/GaussianSharpenProcessor.cs @@ -12,7 +12,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class GaussianSharpenProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The maximum size of the kernel in either direction. diff --git a/src/ImageSharp.Processing/Processors/Effects/AlphaProcessor.cs b/src/ImageSharp.Processing/Processors/Effects/AlphaProcessor.cs index ecf47a036..11af92ea7 100644 --- a/src/ImageSharp.Processing/Processors/Effects/AlphaProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Effects/AlphaProcessor.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class AlphaProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Effects/BackgroundColorProcessor.cs b/src/ImageSharp.Processing/Processors/Effects/BackgroundColorProcessor.cs index 356b2e925..d6d209dc7 100644 --- a/src/ImageSharp.Processing/Processors/Effects/BackgroundColorProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Effects/BackgroundColorProcessor.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class BackgroundColorProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Effects/BrightnessProcessor.cs b/src/ImageSharp.Processing/Processors/Effects/BrightnessProcessor.cs index eb88b9c41..566f2c6d7 100644 --- a/src/ImageSharp.Processing/Processors/Effects/BrightnessProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Effects/BrightnessProcessor.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class BrightnessProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Effects/ContrastProcessor.cs b/src/ImageSharp.Processing/Processors/Effects/ContrastProcessor.cs index 0cc56cc8e..f4acc42bf 100644 --- a/src/ImageSharp.Processing/Processors/Effects/ContrastProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Effects/ContrastProcessor.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class ContrastProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Effects/InvertProcessor.cs b/src/ImageSharp.Processing/Processors/Effects/InvertProcessor.cs index ec1ea7786..641cd1b47 100644 --- a/src/ImageSharp.Processing/Processors/Effects/InvertProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Effects/InvertProcessor.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class InvertProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) diff --git a/src/ImageSharp.Processing/Processors/Effects/OilPaintingProcessor.cs b/src/ImageSharp.Processing/Processors/Effects/OilPaintingProcessor.cs index 5c16af2f7..6b9558ad2 100644 --- a/src/ImageSharp.Processing/Processors/Effects/OilPaintingProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Effects/OilPaintingProcessor.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Processing.Processors /// Adapted from by Dewald Esterhuizen. /// The pixel format. public class OilPaintingProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Effects/PixelateProcessor.cs b/src/ImageSharp.Processing/Processors/Effects/PixelateProcessor.cs index c197ce356..d9d78dfa8 100644 --- a/src/ImageSharp.Processing/Processors/Effects/PixelateProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Effects/PixelateProcessor.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class PixelateProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp.Processing/Processors/Overlays/GlowProcessor.cs index 690f57ab7..9043b66cb 100644 --- a/src/ImageSharp.Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Overlays/GlowProcessor.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class GlowProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Overlays/VignetteProcessor.cs b/src/ImageSharp.Processing/Processors/Overlays/VignetteProcessor.cs index 6e94a4c2a..f1872d0b8 100644 --- a/src/ImageSharp.Processing/Processors/Overlays/VignetteProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Overlays/VignetteProcessor.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class VignetteProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Transforms/CompandingResizeProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/CompandingResizeProcessor.cs index ac8a52321..53da21483 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/CompandingResizeProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Transforms/CompandingResizeProcessor.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class CompandingResizeProcessor : ResamplingWeightedProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Transforms/CropProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/CropProcessor.cs index bdfbc496c..88f23a7fd 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/CropProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Transforms/CropProcessor.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class CropProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Transforms/EntropyCropProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/EntropyCropProcessor.cs index 98297eed9..d150c59d4 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/EntropyCropProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Transforms/EntropyCropProcessor.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class EntropyCropProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Transforms/FlipProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/FlipProcessor.cs index ad375ce0f..da4f2b3a8 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/FlipProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Transforms/FlipProcessor.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class FlipProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Transforms/Matrix3x2Processor.cs b/src/ImageSharp.Processing/Processors/Transforms/Matrix3x2Processor.cs index 209ad3914..7e8757687 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/Matrix3x2Processor.cs +++ b/src/ImageSharp.Processing/Processors/Transforms/Matrix3x2Processor.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public abstract class Matrix3x2Processor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Gets the rectangle designating the target canvas. diff --git a/src/ImageSharp.Processing/Processors/Transforms/ResamplingWeightedProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/ResamplingWeightedProcessor.cs index cac887153..25a7a4efd 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/ResamplingWeightedProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Transforms/ResamplingWeightedProcessor.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public abstract class ResamplingWeightedProcessor : ImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/ResizeProcessor.cs index f5d630808..f4ec39f78 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Transforms/ResizeProcessor.cs @@ -17,7 +17,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class ResizeProcessor : ResamplingWeightedProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp.Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/RotateProcessor.cs index a5a762b91..22fbb895c 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Transforms/RotateProcessor.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class RotateProcessor : Matrix3x2Processor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The transform matrix to apply. diff --git a/src/ImageSharp.Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/SkewProcessor.cs index 4daa46491..c6afbedcc 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/SkewProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Transforms/SkewProcessor.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Processing.Processors /// /// The pixel format. public class SkewProcessor : Matrix3x2Processor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The transform matrix to apply. diff --git a/src/ImageSharp.Processing/Transforms/AutoOrient.cs b/src/ImageSharp.Processing/Transforms/AutoOrient.cs index cb4e72a94..8c5e22b99 100644 --- a/src/ImageSharp.Processing/Transforms/AutoOrient.cs +++ b/src/ImageSharp.Processing/Transforms/AutoOrient.cs @@ -21,7 +21,7 @@ namespace ImageSharp /// The image to auto rotate. /// The public static Image AutoOrient(this Image source) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { Orientation orientation = GetExifOrientation(source); @@ -64,7 +64,7 @@ namespace ImageSharp /// The image to auto rotate. /// The private static Orientation GetExifOrientation(Image source) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { if (source.MetaData.ExifProfile == null) { diff --git a/src/ImageSharp.Processing/Transforms/Crop.cs b/src/ImageSharp.Processing/Transforms/Crop.cs index bdcbe51d8..92773aaea 100644 --- a/src/ImageSharp.Processing/Transforms/Crop.cs +++ b/src/ImageSharp.Processing/Transforms/Crop.cs @@ -23,7 +23,7 @@ namespace ImageSharp /// The target image height. /// The public static Image Crop(this Image source, int width, int height) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Crop(source, new Rectangle(0, 0, width, height)); } @@ -38,7 +38,7 @@ namespace ImageSharp /// /// The public static Image Crop(this Image source, Rectangle cropRectangle) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { CropProcessor processor = new CropProcessor(cropRectangle); diff --git a/src/ImageSharp.Processing/Transforms/EntropyCrop.cs b/src/ImageSharp.Processing/Transforms/EntropyCrop.cs index fcfcf3813..ad2ce89e3 100644 --- a/src/ImageSharp.Processing/Transforms/EntropyCrop.cs +++ b/src/ImageSharp.Processing/Transforms/EntropyCrop.cs @@ -22,7 +22,7 @@ namespace ImageSharp /// The threshold for entropic density. /// The public static Image EntropyCrop(this Image source, float threshold = .5f) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { EntropyCropProcessor processor = new EntropyCropProcessor(threshold); diff --git a/src/ImageSharp.Processing/Transforms/Flip.cs b/src/ImageSharp.Processing/Transforms/Flip.cs index 82b664eb1..ed096eb75 100644 --- a/src/ImageSharp.Processing/Transforms/Flip.cs +++ b/src/ImageSharp.Processing/Transforms/Flip.cs @@ -23,7 +23,7 @@ namespace ImageSharp /// The to perform the flip. /// The public static Image Flip(this Image source, FlipType flipType) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { FlipProcessor processor = new FlipProcessor(flipType); diff --git a/src/ImageSharp.Processing/Transforms/Options/ResizeHelper.cs b/src/ImageSharp.Processing/Transforms/Options/ResizeHelper.cs index 4ed147aed..3dc37e52d 100644 --- a/src/ImageSharp.Processing/Transforms/Options/ResizeHelper.cs +++ b/src/ImageSharp.Processing/Transforms/Options/ResizeHelper.cs @@ -24,7 +24,7 @@ namespace ImageSharp.Processing /// The . /// public static Rectangle CalculateTargetLocationAndBounds(ImageBase source, ResizeOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { switch (options.Mode) { @@ -55,7 +55,7 @@ namespace ImageSharp.Processing /// The . /// private static Rectangle CalculateCropRectangle(ImageBase source, ResizeOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { int width = options.Size.Width; int height = options.Size.Height; @@ -174,7 +174,7 @@ namespace ImageSharp.Processing /// The . /// private static Rectangle CalculatePadRectangle(ImageBase source, ResizeOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { int width = options.Size.Width; int height = options.Size.Height; @@ -255,7 +255,7 @@ namespace ImageSharp.Processing /// The . /// private static Rectangle CalculateBoxPadRectangle(ImageBase source, ResizeOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { int width = options.Size.Width; int height = options.Size.Height; @@ -342,7 +342,7 @@ namespace ImageSharp.Processing /// The . /// private static Rectangle CalculateMaxRectangle(ImageBase source, ResizeOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { int width = options.Size.Width; int height = options.Size.Height; @@ -383,7 +383,7 @@ namespace ImageSharp.Processing /// The . /// private static Rectangle CalculateMinRectangle(ImageBase source, ResizeOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { int width = options.Size.Width; int height = options.Size.Height; diff --git a/src/ImageSharp.Processing/Transforms/Pad.cs b/src/ImageSharp.Processing/Transforms/Pad.cs index df45a94f3..bd530ecd8 100644 --- a/src/ImageSharp.Processing/Transforms/Pad.cs +++ b/src/ImageSharp.Processing/Transforms/Pad.cs @@ -24,7 +24,7 @@ namespace ImageSharp /// The new height. /// The . public static Image Pad(this Image source, int width, int height) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { ResizeOptions options = new ResizeOptions { diff --git a/src/ImageSharp.Processing/Transforms/Resize.cs b/src/ImageSharp.Processing/Transforms/Resize.cs index aa4bf2439..ab256c4ae 100644 --- a/src/ImageSharp.Processing/Transforms/Resize.cs +++ b/src/ImageSharp.Processing/Transforms/Resize.cs @@ -24,7 +24,7 @@ namespace ImageSharp /// The /// Passing zero for one of height or width within the resize options will automatically preserve the aspect ratio of the original image public static Image Resize(this Image source, ResizeOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { // Ensure size is populated across both dimensions. if (options.Size.Width == 0 && options.Size.Height > 0) @@ -51,7 +51,7 @@ namespace ImageSharp /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image public static Image Resize(this Image source, Size size) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Resize(source, size.Width, size.Height, new BicubicResampler(), false); } @@ -66,7 +66,7 @@ namespace ImageSharp /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image public static Image Resize(this Image source, int width, int height) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Resize(source, width, height, new BicubicResampler(), false); } @@ -82,7 +82,7 @@ namespace ImageSharp /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image public static Image Resize(this Image source, int width, int height, bool compand) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Resize(source, width, height, new BicubicResampler(), compand); } @@ -98,7 +98,7 @@ namespace ImageSharp /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image public static Image Resize(this Image source, int width, int height, IResampler sampler) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Resize(source, width, height, sampler, false); } @@ -115,7 +115,7 @@ namespace ImageSharp /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image public static Image Resize(this Image source, int width, int height, IResampler sampler, bool compand) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Resize(source, width, height, sampler, source.Bounds, new Rectangle(0, 0, width, height), compand); } @@ -139,7 +139,7 @@ namespace ImageSharp /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image public static Image Resize(this Image source, int width, int height, IResampler sampler, Rectangle sourceRectangle, Rectangle targetRectangle, bool compand = false) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { if (width == 0 && height > 0) { diff --git a/src/ImageSharp.Processing/Transforms/Rotate.cs b/src/ImageSharp.Processing/Transforms/Rotate.cs index e9ed4e97c..76311ef25 100644 --- a/src/ImageSharp.Processing/Transforms/Rotate.cs +++ b/src/ImageSharp.Processing/Transforms/Rotate.cs @@ -23,7 +23,7 @@ namespace ImageSharp /// The angle in degrees to perform the rotation. /// The public static Image Rotate(this Image source, float degrees) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Rotate(source, degrees, true); } @@ -36,7 +36,7 @@ namespace ImageSharp /// The to perform the rotation. /// The public static Image Rotate(this Image source, RotateType rotateType) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Rotate(source, (float)rotateType, false); } @@ -50,7 +50,7 @@ namespace ImageSharp /// Whether to expand the image to fit the rotated result. /// The public static Image Rotate(this Image source, float degrees, bool expand) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { RotateProcessor processor = new RotateProcessor { Angle = degrees, Expand = expand }; diff --git a/src/ImageSharp.Processing/Transforms/RotateFlip.cs b/src/ImageSharp.Processing/Transforms/RotateFlip.cs index e69f18338..d6050db3f 100644 --- a/src/ImageSharp.Processing/Transforms/RotateFlip.cs +++ b/src/ImageSharp.Processing/Transforms/RotateFlip.cs @@ -22,7 +22,7 @@ namespace ImageSharp /// The to perform the flip. /// The public static Image RotateFlip(this Image source, RotateType rotateType, FlipType flipType) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return source.Rotate(rotateType).Flip(flipType); } diff --git a/src/ImageSharp.Processing/Transforms/Skew.cs b/src/ImageSharp.Processing/Transforms/Skew.cs index 5a662c300..03fdbcceb 100644 --- a/src/ImageSharp.Processing/Transforms/Skew.cs +++ b/src/ImageSharp.Processing/Transforms/Skew.cs @@ -23,7 +23,7 @@ namespace ImageSharp /// The angle in degrees to perform the rotation along the y-axis. /// The public static Image Skew(this Image source, float degreesX, float degreesY) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return Skew(source, degreesX, degreesY, true); } @@ -38,7 +38,7 @@ namespace ImageSharp /// Whether to expand the image to fit the skewed result. /// The public static Image Skew(this Image source, float degreesX, float degreesY, bool expand) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { SkewProcessor processor = new SkewProcessor { AngleX = degreesX, AngleY = degreesY, Expand = expand }; diff --git a/src/ImageSharp/Colors/Color.cs b/src/ImageSharp/Colors/Color.cs index 730fe45a5..b82628b3b 100644 --- a/src/ImageSharp/Colors/Color.cs +++ b/src/ImageSharp/Colors/Color.cs @@ -18,7 +18,7 @@ namespace ImageSharp /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, /// as it avoids the need to create new values for modification operations. /// - public partial struct Color : IPackedPixel, IEquatable + public partial struct Color : IPixel { /// /// The shift count for the red component @@ -184,7 +184,9 @@ namespace ImageSharp } } - /// + /// + /// Gets or sets the packed representation of the value. + /// public uint PackedValue { get diff --git a/src/ImageSharp/Colors/ColorBuilder{TColor}.cs b/src/ImageSharp/Colors/ColorBuilder{TColor}.cs index 47506af49..f60b5c8c0 100644 --- a/src/ImageSharp/Colors/ColorBuilder{TColor}.cs +++ b/src/ImageSharp/Colors/ColorBuilder{TColor}.cs @@ -13,7 +13,7 @@ namespace ImageSharp /// /// The type of the color. public static class ColorBuilder - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Creates a new representation from the string representing a color. diff --git a/src/ImageSharp/Colors/NamedColors{TColor}.cs b/src/ImageSharp/Colors/NamedColors{TColor}.cs index 77a537f0f..bcad4dd40 100644 --- a/src/ImageSharp/Colors/NamedColors{TColor}.cs +++ b/src/ImageSharp/Colors/NamedColors{TColor}.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// The type of the color. public static class NamedColors - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Represents a matching the W3C definition that has an hex value of #F0F8FF. diff --git a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs index 5642e6201..5141cbca4 100644 --- a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs +++ b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing a single 8 bit normalized W values ranging from 0 to 1. /// - public struct Alpha8 : IPackedPixel, IEquatable + public struct Alpha8 : IPixel { /// /// Initializes a new instance of the struct. @@ -23,7 +23,9 @@ namespace ImageSharp this.PackedValue = Pack(alpha); } - /// + /// + /// Gets or sets the packed representation of the value. + /// public byte PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/Argb.cs b/src/ImageSharp/Colors/PackedPixel/Argb.cs index 783545c68..57ae0898b 100644 --- a/src/ImageSharp/Colors/PackedPixel/Argb.cs +++ b/src/ImageSharp/Colors/PackedPixel/Argb.cs @@ -17,7 +17,7 @@ namespace ImageSharp /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, /// as it avoids the need to create new values for modification operations. /// - public struct Argb : IPackedPixel, IEquatable + public struct Argb : IPixel { /// /// The shift count for the blue component @@ -106,7 +106,9 @@ namespace ImageSharp this.PackedValue = packed; } - /// + /// + /// Gets or sets the packed representation of the value. + /// public uint PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/Bgr565.cs b/src/ImageSharp/Colors/PackedPixel/Bgr565.cs index 19c7f3d14..042c8c0f5 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgr565.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgr565.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. The x and z components use 5 bits, and the y component uses 6 bits. /// - public struct Bgr565 : IPackedPixel, IEquatable + public struct Bgr565 : IPixel { /// /// Initializes a new instance of the struct. @@ -36,7 +36,9 @@ namespace ImageSharp this.PackedValue = Pack(vector.X, vector.Y, vector.Z); } - /// + /// + /// Gets or sets the packed representation of the value. + /// public ushort PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs b/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs index 41f7cba04..8bae542c2 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing unsigned normalized values, ranging from 0 to 1, using 4 bits each for x, y, z, and w. /// - public struct Bgra4444 : IPackedPixel, IEquatable + public struct Bgra4444 : IPixel { /// /// Initializes a new instance of the struct. @@ -35,7 +35,9 @@ namespace ImageSharp this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); } - /// + /// + /// Gets or sets the packed representation of the value. + /// public ushort PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs b/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs index 800cb0bf1..0ad7dbd0e 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. The x , y and z components use 5 bits, and the w component uses 1 bit. /// - public struct Bgra5551 : IPackedPixel, IEquatable + public struct Bgra5551 : IPixel { /// /// Initializes a new instance of the struct. @@ -37,7 +37,9 @@ namespace ImageSharp this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); } - /// + /// + /// Gets or sets the packed representation of the value. + /// public ushort PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/Byte4.cs b/src/ImageSharp/Colors/PackedPixel/Byte4.cs index 16e066890..b1f8d5a6e 100644 --- a/src/ImageSharp/Colors/PackedPixel/Byte4.cs +++ b/src/ImageSharp/Colors/PackedPixel/Byte4.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing four 8-bit unsigned integer values, ranging from 0 to 255. /// - public struct Byte4 : IPackedPixel, IEquatable + public struct Byte4 : IPixel { /// /// Initializes a new instance of the struct. @@ -38,7 +38,9 @@ namespace ImageSharp this.PackedValue = Pack(ref vector); } - /// + /// + /// Gets or sets the packed representation of the value. + /// public uint PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs b/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs index 29a55095b..bd4ba3840 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing a single 16 bit floating point value. /// - public struct HalfSingle : IPackedPixel, IEquatable + public struct HalfSingle : IPixel { /// /// The maximum byte value. @@ -33,7 +33,9 @@ namespace ImageSharp this.PackedValue = HalfTypeHelper.Pack(single); } - /// + /// + /// Gets or sets the packed representation of the value. + /// public ushort PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs index 02e93b250..4d1f0a2cb 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing two 16-bit floating-point values. /// - public struct HalfVector2 : IPackedPixel, IEquatable + public struct HalfVector2 : IPixel { /// /// The maximum byte value. @@ -43,7 +43,9 @@ namespace ImageSharp this.PackedValue = Pack(vector.X, vector.Y); } - /// + /// + /// Gets or sets the packed representation of the value. + /// public uint PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs b/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs index 38787acea..e0108404d 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing four 16-bit floating-point values. /// - public struct HalfVector4 : IPackedPixel, IEquatable + public struct HalfVector4 : IPixel { /// /// The maximum byte value. @@ -46,7 +46,9 @@ namespace ImageSharp this.PackedValue = Pack(ref vector); } - /// + /// + /// Gets or sets the packed representation of the value. + /// public ulong PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/IPackedBytes.cs b/src/ImageSharp/Colors/PackedPixel/IPackedBytes.cs deleted file mode 100644 index 3343a92c7..000000000 --- a/src/ImageSharp/Colors/PackedPixel/IPackedBytes.cs +++ /dev/null @@ -1,55 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - /// - /// An interface that converts packed vector types to and from values, - /// allowing multiple encodings to be manipulated in a generic manner. - /// - public interface IPackedBytes - { - /// - /// Sets the packed representation from the given byte array. - /// - /// The x-component. - /// The y-component. - /// The z-component. - /// The w-component. - void PackFromBytes(byte x, byte y, byte z, byte w); - - /// - /// Expands the packed representation into a given byte array. - /// Output is expanded to X-> Y-> Z order. Equivalent to R-> G-> B in - /// - /// The bytes to set the color in. - /// The starting index of the . - void ToXyzBytes(byte[] bytes, int startIndex); - - /// - /// Expands the packed representation into a given byte array. - /// Output is expanded to X-> Y-> Z-> W order. Equivalent to R-> G-> B-> A in - /// - /// The bytes to set the color in. - /// The starting index of the . - void ToXyzwBytes(byte[] bytes, int startIndex); - - /// - /// Expands the packed representation into a given byte array. - /// Output is expanded to Z-> Y-> X order. Equivalent to B-> G-> R in - /// - /// The bytes to set the color in. - /// The starting index of the . - void ToZyxBytes(byte[] bytes, int startIndex); - - /// - /// Expands the packed representation into a given byte array. - /// Output is expanded to Z-> Y-> X-> W order. Equivalent to B-> G-> R-> A in - /// - /// The bytes to set the color in. - /// The starting index of the . - void ToZyxwBytes(byte[] bytes, int startIndex); - } -} diff --git a/src/ImageSharp/Colors/PackedPixel/IPackedPixel.cs b/src/ImageSharp/Colors/PackedPixel/IPackedPixel.cs deleted file mode 100644 index f9b8dc0a2..000000000 --- a/src/ImageSharp/Colors/PackedPixel/IPackedPixel.cs +++ /dev/null @@ -1,25 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - - /// - /// An interface that represents a generic packed pixel type. - /// - /// The packed format. uint, long, float. - public interface IPackedPixel : IPackedPixel, IPackedVector - where TPacked : struct, IEquatable - { - } - - /// - /// An interface that represents a packed pixel type. - /// - public interface IPackedPixel : IPackedVector, IPackedBytes - { - } -} \ No newline at end of file diff --git a/src/ImageSharp/Colors/PackedPixel/IPackedVector.cs b/src/ImageSharp/Colors/PackedPixel/IPackedVector.cs deleted file mode 100644 index 4d9a89712..000000000 --- a/src/ImageSharp/Colors/PackedPixel/IPackedVector.cs +++ /dev/null @@ -1,43 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - using System.Numerics; - - /// - /// An interface that converts packed vector types to and from values, - /// allowing multiple encodings to be manipulated in a generic manner. - /// - /// The packed format. uint, long, float. - public interface IPackedVector : IPackedVector - where TPacked : struct, IEquatable - { - /// - /// Gets or sets the packed representation of the value. - /// - TPacked PackedValue { get; set; } - } - - /// - /// An interface that converts packed vector types to and from values. - /// - public interface IPackedVector - { - /// - /// Sets the packed representation from a . - /// - /// The vector to create the packed representation from. - void PackFromVector4(Vector4 vector); - - /// - /// Expands the packed representation into a . - /// The vector components are typically expanded in least to greatest significance order. - /// - /// The . - Vector4 ToVector4(); - } -} \ No newline at end of file diff --git a/src/ImageSharp/Colors/PackedPixel/IPixel.cs b/src/ImageSharp/Colors/PackedPixel/IPixel.cs index a222edd9a..1c3e20a7e 100644 --- a/src/ImageSharp/Colors/PackedPixel/IPixel.cs +++ b/src/ImageSharp/Colors/PackedPixel/IPixel.cs @@ -1,8 +1,25 @@ -namespace ImageSharp +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp { using System; using System.Numerics; + /// + /// An interface that represents a generic pixel type. + /// + /// The type implementing this interface + public interface IPixel : IPixel, IEquatable + where TSelf : struct, IPixel + { + } + + /// + /// An interface that represents a pixel type. + /// public interface IPixel { /// @@ -59,10 +76,4 @@ /// The starting index of the . void ToZyxwBytes(byte[] bytes, int startIndex); } - - public interface IPixel : IPixel, IEquatable - where TSelf : struct, IPixel - { - - } } \ No newline at end of file diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs index 5196cbfaf..0567b4445 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed packed pixel type containing two 8-bit signed normalized values, ranging from −1 to 1. /// - public struct NormalizedByte2 : IPackedPixel, IEquatable + public struct NormalizedByte2 : IPixel { /// /// The maximum byte value. @@ -48,7 +48,9 @@ namespace ImageSharp this.PackedValue = Pack(x, y); } - /// + /// + /// Gets or sets the packed representation of the value. + /// public ushort PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs index c5de795e2..a45f601c4 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing four 8-bit signed normalized values, ranging from −1 to 1. /// - public struct NormalizedByte4 : IPackedPixel, IEquatable + public struct NormalizedByte4 : IPixel { /// /// The maximum byte value. @@ -50,7 +50,9 @@ namespace ImageSharp this.PackedValue = Pack(x, y, z, w); } - /// + /// + /// Gets or sets the packed representation of the value. + /// public uint PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs index 8e3570047..3642aed5b 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing two 16-bit signed normalized values, ranging from −1 to 1. /// - public struct NormalizedShort2 : IPackedPixel, IEquatable + public struct NormalizedShort2 : IPixel { /// /// The maximum byte value. @@ -48,7 +48,9 @@ namespace ImageSharp this.PackedValue = Pack(x, y); } - /// + /// + /// Gets or sets the packed representation of the value. + /// public uint PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs index 81d182cee..d5a3de79e 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing four 16-bit signed normalized values, ranging from −1 to 1. /// - public struct NormalizedShort4 : IPackedPixel, IEquatable + public struct NormalizedShort4 : IPixel { /// /// The maximum byte value. @@ -50,7 +50,9 @@ namespace ImageSharp this.PackedValue = Pack(x, y, z, w); } - /// + /// + /// Gets or sets the packed representation of the value. + /// public ulong PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/Rg32.cs b/src/ImageSharp/Colors/PackedPixel/Rg32.cs index f7dab68c3..9bda5f605 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rg32.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rg32.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing two 16-bit unsigned normalized values ranging from 0 to 1. /// - public struct Rg32 : IPackedPixel, IEquatable, IPackedVector + public struct Rg32 : IPixel { /// /// Initializes a new instance of the struct. @@ -33,7 +33,9 @@ namespace ImageSharp this.PackedValue = Pack(vector.X, vector.Y); } - /// + /// + /// Gets or sets the packed representation of the value. + /// public uint PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs b/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs index 8c2261028..69c097f96 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs @@ -13,7 +13,7 @@ namespace ImageSharp /// Packed vector type containing unsigned normalized values ranging from 0 to 1. /// The x, y and z components use 10 bits, and the w component uses 2 bits. /// - public struct Rgba1010102 : IPackedPixel, IEquatable, IPackedVector + public struct Rgba1010102 : IPixel { /// /// Initializes a new instance of the struct. @@ -36,7 +36,9 @@ namespace ImageSharp this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); } - /// + /// + /// Gets or sets the packed representation of the value. + /// public uint PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/Rgba64.cs b/src/ImageSharp/Colors/PackedPixel/Rgba64.cs index bb2182649..470556435 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rgba64.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rgba64.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing four 16-bit unsigned normalized values ranging from 0 to 1. /// - public struct Rgba64 : IPackedPixel, IEquatable, IPackedVector + public struct Rgba64 : IPixel { /// /// Initializes a new instance of the struct. @@ -35,7 +35,9 @@ namespace ImageSharp this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); } - /// + /// + /// Gets or sets the packed representation of the value. + /// public ulong PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/Short2.cs b/src/ImageSharp/Colors/PackedPixel/Short2.cs index 6bfc5d40c..3f83a4cd5 100644 --- a/src/ImageSharp/Colors/PackedPixel/Short2.cs +++ b/src/ImageSharp/Colors/PackedPixel/Short2.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing two 16-bit signed integer values. /// - public struct Short2 : IPackedPixel, IEquatable + public struct Short2 : IPixel { /// /// The maximum byte value. @@ -48,7 +48,9 @@ namespace ImageSharp this.PackedValue = Pack(x, y); } - /// + /// + /// Gets or sets the packed representation of the value. + /// public uint PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/Short4.cs b/src/ImageSharp/Colors/PackedPixel/Short4.cs index 4b1f3c253..1696cb40a 100644 --- a/src/ImageSharp/Colors/PackedPixel/Short4.cs +++ b/src/ImageSharp/Colors/PackedPixel/Short4.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing four 16-bit signed integer values. /// - public struct Short4 : IPackedPixel, IEquatable + public struct Short4 : IPixel { /// /// The maximum byte value. @@ -50,7 +50,9 @@ namespace ImageSharp this.PackedValue = Pack(x, y, z, w); } - /// + /// + /// Gets or sets the packed representation of the value. + /// public ulong PackedValue { get; set; } /// diff --git a/src/ImageSharp/Common/Extensions/ArrayExtensions.cs b/src/ImageSharp/Common/Extensions/ArrayExtensions.cs index be3d3f1dc..cce442c52 100644 --- a/src/ImageSharp/Common/Extensions/ArrayExtensions.cs +++ b/src/ImageSharp/Common/Extensions/ArrayExtensions.cs @@ -21,7 +21,7 @@ namespace ImageSharp /// The height of the image represented by the pixel buffer. /// The public static PixelAccessor Lock(this TColor[] pixels, int width, int height) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return new PixelAccessor(width, height, pixels); } diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs index 5720d8218..b59cf54f5 100644 --- a/src/ImageSharp/Common/Helpers/ImageMaths.cs +++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs @@ -183,7 +183,7 @@ namespace ImageSharp /// The . /// public static Rectangle GetFilteredBoundingRectangle(ImageBase bitmap, float componentValue, RgbaComponent channel = RgbaComponent.B) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { int width = bitmap.Width; int height = bitmap.Height; diff --git a/src/ImageSharp/Common/Helpers/ThrowHelper.cs b/src/ImageSharp/Common/Helpers/ThrowHelper.cs index a83264533..97d75717a 100644 --- a/src/ImageSharp/Common/Helpers/ThrowHelper.cs +++ b/src/ImageSharp/Common/Helpers/ThrowHelper.cs @@ -1,3 +1,8 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + namespace ImageSharp { using System; @@ -8,6 +13,10 @@ namespace ImageSharp /// internal static class ThrowHelper { + /// + /// Throws an + /// + /// The parameter name [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowArgumentNullException(string paramName) { diff --git a/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs b/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs index 06de06107..95d2e51e6 100644 --- a/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs +++ b/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs @@ -1,18 +1,33 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + namespace ImageSharp { using System; using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; /// - /// Provides access to elements in an array from a given position. - /// This struct shares many similarities with corefx System.Span<T> but there are differences in functionalities and semantics: + /// Provides access to elements in an array from a given position. + /// This type shares many similarities with corefx System.Span<T> but there are significant differences in it's functionalities and semantics: /// - It's not possible to use it with stack objects or pointers to unmanaged memory, only with managed arrays + /// - It's possible to retrieve a reference to the array () so we can pass it to API-s like /// - There is no bounds checking for performance reasons. Therefore we don't need to store length. (However this could be added as DEBUG-only feature.) + /// This makes an unsafe type! /// - Currently the arrays provided to ArrayPointer need to be pinned. This behaviour could be changed using C#7 features. /// + /// The type of elements of the array internal unsafe struct ArrayPointer where T : struct { + /// + /// Initializes a new instance of the struct from a pinned array and an offset. + /// + /// The pinned array + /// Pointer to the beginning of array + /// The offset inside the array [MethodImpl(MethodImplOptions.AggressiveInlining)] public ArrayPointer(T[] array, void* pointerToArray, int offset) { @@ -21,11 +36,17 @@ namespace ImageSharp { ThrowHelper.ThrowArgumentNullException(nameof(array)); } + this.Array = array; this.Offset = offset; - this.PointerAtOffset = (IntPtr)pointerToArray + Unsafe.SizeOf()*offset; + this.PointerAtOffset = (IntPtr)pointerToArray + (Unsafe.SizeOf() * offset); } + /// + /// Initializes a new instance of the struct from a pinned array. + /// + /// The pinned array + /// Pointer to the start of 'array' [MethodImpl(MethodImplOptions.AggressiveInlining)] public ArrayPointer(T[] array, void* pointerToArray) { @@ -34,23 +55,38 @@ namespace ImageSharp { ThrowHelper.ThrowArgumentNullException(nameof(array)); } + this.Array = array; this.Offset = 0; this.PointerAtOffset = (IntPtr)pointerToArray; } + /// + /// Gets the array + /// public T[] Array { get; private set; } + /// + /// Gets the offset inside + /// public int Offset { get; private set; } + /// + /// Gets the pointer to the offseted array position + /// public IntPtr PointerAtOffset { get; private set; } + /// + /// Forms a slice out of the given ArrayPointer, beginning at 'offset'. + /// + /// The offset in number of elements + /// The offseted (sliced) ArrayPointer public ArrayPointer Slice(int offset) { ArrayPointer result = default(ArrayPointer); result.Array = this.Array; result.Offset = this.Offset + offset; - result.PointerAtOffset = this.PointerAtOffset + Unsafe.SizeOf() * offset; + result.PointerAtOffset = this.PointerAtOffset + (Unsafe.SizeOf() * offset); return result; } } diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs index 4fd5476f0..20a45d4df 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs @@ -68,7 +68,7 @@ namespace ImageSharp.Dithering /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dither(PixelAccessor pixels, TColor source, TColor transformed, int x, int y, int width, int height) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { // Assign the transformed pixel to the array. pixels[x, y] = transformed; diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs index fbee27088..4fb31c13e 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs @@ -29,6 +29,6 @@ namespace ImageSharp.Dithering /// The image height. /// The pixel format. void Dither(PixelAccessor pixels, TColor source, TColor transformed, int x, int y, int width, int height) - where TColor : struct, IPackedPixel, IEquatable; + where TColor : struct, IPixel; } } diff --git a/src/ImageSharp/Dithering/Ordered/Bayer.cs b/src/ImageSharp/Dithering/Ordered/Bayer.cs index 3d3d90023..95545f46b 100644 --- a/src/ImageSharp/Dithering/Ordered/Bayer.cs +++ b/src/ImageSharp/Dithering/Ordered/Bayer.cs @@ -31,7 +31,7 @@ namespace ImageSharp.Dithering.Ordered /// public void Dither(PixelAccessor pixels, TColor source, TColor upper, TColor lower, byte[] bytes, int index, int x, int y, int width, int height) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { source.ToXyzwBytes(bytes, 0); pixels[x, y] = ThresholdMatrix[y % 3, x % 3] >= bytes[index] ? lower : upper; diff --git a/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs b/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs index e74a863a5..162cdb6a1 100644 --- a/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs +++ b/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs @@ -32,6 +32,6 @@ namespace ImageSharp.Dithering /// The image height. /// The pixel format. void Dither(PixelAccessor pixels, TColor source, TColor upper, TColor lower, byte[] bytes, int index, int x, int y, int width, int height) - where TColor : struct, IPackedPixel, IEquatable; + where TColor : struct, IPixel; } } diff --git a/src/ImageSharp/Dithering/Ordered/Ordered.cs b/src/ImageSharp/Dithering/Ordered/Ordered.cs index bb1f21060..df48a3167 100644 --- a/src/ImageSharp/Dithering/Ordered/Ordered.cs +++ b/src/ImageSharp/Dithering/Ordered/Ordered.cs @@ -31,7 +31,7 @@ namespace ImageSharp.Dithering.Ordered /// public void Dither(PixelAccessor pixels, TColor source, TColor upper, TColor lower, byte[] bytes, int index, int x, int y, int width, int height) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { source.ToXyzwBytes(bytes, 0); pixels[x, y] = ThresholdMatrix[y % 3, x % 3] >= bytes[index] ? lower : upper; diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs index d723b82f0..28bda7837 100644 --- a/src/ImageSharp/Formats/IImageDecoder.cs +++ b/src/ImageSharp/Formats/IImageDecoder.cs @@ -20,6 +20,6 @@ namespace ImageSharp.Formats /// The to decode to. /// The containing image data. void Decode(Image image, Stream stream) - where TColor : struct, IPackedPixel, IEquatable; + where TColor : struct, IPixel; } } diff --git a/src/ImageSharp/Formats/IImageEncoder.cs b/src/ImageSharp/Formats/IImageEncoder.cs index edde5347e..0ba56477a 100644 --- a/src/ImageSharp/Formats/IImageEncoder.cs +++ b/src/ImageSharp/Formats/IImageEncoder.cs @@ -20,6 +20,6 @@ namespace ImageSharp.Formats /// The to encode from. /// The to encode the image data to. void Encode(Image image, Stream stream) - where TColor : struct, IPackedPixel, IEquatable; + where TColor : struct, IPixel; } } diff --git a/src/ImageSharp/Image/IImageBase{TColor}.cs b/src/ImageSharp/Image/IImageBase{TColor}.cs index f01a4b702..e894fba4a 100644 --- a/src/ImageSharp/Image/IImageBase{TColor}.cs +++ b/src/ImageSharp/Image/IImageBase{TColor}.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// The pixel format. public interface IImageBase : IImageBase, IDisposable - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Gets the pixels as an array of the given packed pixel format. diff --git a/src/ImageSharp/Image/IImageProcessor.cs b/src/ImageSharp/Image/IImageProcessor.cs index fae0c23b8..0440cdd76 100644 --- a/src/ImageSharp/Image/IImageProcessor.cs +++ b/src/ImageSharp/Image/IImageProcessor.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Processing /// /// The pixel format. public interface IImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Gets or sets the parallel options for processing tasks in parallel. diff --git a/src/ImageSharp/Image/ImageBase{TColor}.cs b/src/ImageSharp/Image/ImageBase{TColor}.cs index fd82f7730..f530c0ccf 100644 --- a/src/ImageSharp/Image/ImageBase{TColor}.cs +++ b/src/ImageSharp/Image/ImageBase{TColor}.cs @@ -16,7 +16,7 @@ namespace ImageSharp /// The pixel format. [DebuggerDisplay("Image: {Width}x{Height}")] public abstract class ImageBase : IImageBase - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The image pixels diff --git a/src/ImageSharp/Image/ImageFrame{TColor}.cs b/src/ImageSharp/Image/ImageFrame{TColor}.cs index 02e5b7161..2712dc687 100644 --- a/src/ImageSharp/Image/ImageFrame{TColor}.cs +++ b/src/ImageSharp/Image/ImageFrame{TColor}.cs @@ -14,7 +14,7 @@ namespace ImageSharp /// /// The pixel format. public class ImageFrame : ImageBase, IImageFrame - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. @@ -56,7 +56,7 @@ namespace ImageSharp /// The pixel format. /// The public ImageFrame To(Func scaleFunc = null) - where TColor2 : struct, IPackedPixel, IEquatable + where TColor2 : struct, IPixel { scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction(scaleFunc); diff --git a/src/ImageSharp/Image/ImageProcessingExtensions.cs b/src/ImageSharp/Image/ImageProcessingExtensions.cs index db8fb4baa..ff3ecd5ee 100644 --- a/src/ImageSharp/Image/ImageProcessingExtensions.cs +++ b/src/ImageSharp/Image/ImageProcessingExtensions.cs @@ -22,7 +22,7 @@ namespace ImageSharp /// The processor to apply to the image. /// The . public static Image Apply(this Image source, IImageProcessor processor) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { source.ApplyProcessor(processor, source.Bounds); return source; diff --git a/src/ImageSharp/Image/Image{TColor}.cs b/src/ImageSharp/Image/Image{TColor}.cs index 33d6a6965..bbd839d16 100644 --- a/src/ImageSharp/Image/Image{TColor}.cs +++ b/src/ImageSharp/Image/Image{TColor}.cs @@ -24,7 +24,7 @@ namespace ImageSharp /// The pixel format. [DebuggerDisplay("Image: {Width}x{Height}")] public class Image : ImageBase, IImage - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class @@ -325,7 +325,7 @@ namespace ImageSharp /// The pixel format. /// The public Image To(Func scaleFunc = null) - where TColor2 : struct, IPackedPixel, IEquatable + where TColor2 : struct, IPixel { scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction(scaleFunc); diff --git a/src/ImageSharp/Image/PixelAccessor{TColor}.cs b/src/ImageSharp/Image/PixelAccessor{TColor}.cs index 55b16235a..338f49182 100644 --- a/src/ImageSharp/Image/PixelAccessor{TColor}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TColor}.cs @@ -16,7 +16,7 @@ namespace ImageSharp /// /// The pixel format. public unsafe class PixelAccessor : IDisposable - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The pointer to the pixel buffer. diff --git a/src/ImageSharp/Image/PixelArea{TColor}.cs b/src/ImageSharp/Image/PixelArea{TColor}.cs index 99e927375..77b648ca5 100644 --- a/src/ImageSharp/Image/PixelArea{TColor}.cs +++ b/src/ImageSharp/Image/PixelArea{TColor}.cs @@ -16,7 +16,7 @@ namespace ImageSharp /// /// The pixel format. public sealed unsafe class PixelArea : IDisposable - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// True if was rented from by the constructor diff --git a/src/ImageSharp/Image/PixelPool{TColor}.cs b/src/ImageSharp/Image/PixelPool{TColor}.cs index 1f3392621..8193600da 100644 --- a/src/ImageSharp/Image/PixelPool{TColor}.cs +++ b/src/ImageSharp/Image/PixelPool{TColor}.cs @@ -13,7 +13,7 @@ namespace ImageSharp /// /// The pixel format. public static class PixelPool - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The used to pool data. TODO: Choose sensible default size and count diff --git a/src/ImageSharp/ImageProcessor.cs b/src/ImageSharp/ImageProcessor.cs index 79bc3ee1e..a0766d1ed 100644 --- a/src/ImageSharp/ImageProcessor.cs +++ b/src/ImageSharp/ImageProcessor.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Processing /// /// The pixel format. public abstract class ImageProcessor : IImageProcessor - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// public virtual ParallelOptions ParallelOptions { get; set; } diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs index 3bd5ef3cb..b363286b0 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs @@ -121,7 +121,7 @@ namespace ImageSharp /// The . /// public Image CreateThumbnail() - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { this.InitializeValues(); diff --git a/src/ImageSharp/Quantizers/IQuantizer.cs b/src/ImageSharp/Quantizers/IQuantizer.cs index ef2f0bb98..9ee307266 100644 --- a/src/ImageSharp/Quantizers/IQuantizer.cs +++ b/src/ImageSharp/Quantizers/IQuantizer.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Quantizers /// /// The pixel format. public interface IQuantizer : IQuantizer - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Quantize an image and return the resulting output pixels. @@ -32,7 +32,7 @@ namespace ImageSharp.Quantizers /// /// The pixel format. public interface IDitheredQuantizer : IQuantizer - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Gets or sets a value indicating whether to apply dithering to the output image. diff --git a/src/ImageSharp/Quantizers/Octree/OctreeQuantizer.cs b/src/ImageSharp/Quantizers/Octree/OctreeQuantizer.cs index c0aab00ed..c047e1af4 100644 --- a/src/ImageSharp/Quantizers/Octree/OctreeQuantizer.cs +++ b/src/ImageSharp/Quantizers/Octree/OctreeQuantizer.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Quantizers /// /// The pixel format. public sealed class OctreeQuantizer : Quantizer - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The pixel buffer, used to reduce allocations. diff --git a/src/ImageSharp/Quantizers/Octree/Quantizer.cs b/src/ImageSharp/Quantizers/Octree/Quantizer.cs index 87705de94..8af638de3 100644 --- a/src/ImageSharp/Quantizers/Octree/Quantizer.cs +++ b/src/ImageSharp/Quantizers/Octree/Quantizer.cs @@ -17,7 +17,7 @@ namespace ImageSharp.Quantizers /// /// The pixel format. public abstract class Quantizer : IDitheredQuantizer - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// A lookup table for colors diff --git a/src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs b/src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs index e55d67037..19f10aabb 100644 --- a/src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs +++ b/src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Quantizers /// /// The pixel format. public sealed class PaletteQuantizer : Quantizer - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The pixel buffer, used to reduce allocations. diff --git a/src/ImageSharp/Quantizers/Quantize.cs b/src/ImageSharp/Quantizers/Quantize.cs index 8a26fb4ba..f45cd3f79 100644 --- a/src/ImageSharp/Quantizers/Quantize.cs +++ b/src/ImageSharp/Quantizers/Quantize.cs @@ -24,7 +24,7 @@ namespace ImageSharp /// The maximum number of colors to return. Defaults to 256. /// The . public static Image Quantize(this Image source, Quantization mode = Quantization.Octree, int maxColors = 256) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { IQuantizer quantizer; switch (mode) @@ -54,7 +54,7 @@ namespace ImageSharp /// The maximum number of colors to return. /// The . public static Image Quantize(this Image source, IQuantizer quantizer, int maxColors) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { QuantizedImage quantized = quantizer.Quantize(source, maxColors); int palleteCount = quantized.Palette.Length - 1; diff --git a/src/ImageSharp/Quantizers/QuantizedImage.cs b/src/ImageSharp/Quantizers/QuantizedImage.cs index cc740f9be..528da7717 100644 --- a/src/ImageSharp/Quantizers/QuantizedImage.cs +++ b/src/ImageSharp/Quantizers/QuantizedImage.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Quantizers /// /// The pixel format. public class QuantizedImage - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Quantizers/Wu/WuQuantizer.cs b/src/ImageSharp/Quantizers/Wu/WuQuantizer.cs index 55743f81e..d632cdd73 100644 --- a/src/ImageSharp/Quantizers/Wu/WuQuantizer.cs +++ b/src/ImageSharp/Quantizers/Wu/WuQuantizer.cs @@ -31,7 +31,7 @@ namespace ImageSharp.Quantizers /// /// The pixel format. public sealed class WuQuantizer : IQuantizer - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { /// /// The index bits. diff --git a/tests/ImageSharp.Tests/Colors/ColorConstructorTests.cs b/tests/ImageSharp.Tests/Colors/ColorConstructorTests.cs index ac1c3f351..08b9375f8 100644 --- a/tests/ImageSharp.Tests/Colors/ColorConstructorTests.cs +++ b/tests/ImageSharp.Tests/Colors/ColorConstructorTests.cs @@ -132,7 +132,7 @@ namespace ImageSharp.Tests.Colors [MemberData(nameof(Vector3Data))] [MemberData(nameof(Float4Data))] [MemberData(nameof(Float3Data))] - public void ConstructorToVector4(IPackedVector packedVector, float[] expectedVector4Components) + public void ConstructorToVector4(IPixel packedVector, float[] expectedVector4Components) { // Arrange var precision = 2; diff --git a/tests/ImageSharp.Tests/Colors/ColorPackingTests.cs b/tests/ImageSharp.Tests/Colors/ColorPackingTests.cs index a1057a2c2..ac5f8d6b4 100644 --- a/tests/ImageSharp.Tests/Colors/ColorPackingTests.cs +++ b/tests/ImageSharp.Tests/Colors/ColorPackingTests.cs @@ -69,7 +69,7 @@ namespace ImageSharp.Tests.Colors [Theory] [MemberData(nameof(Vector4PackData))] [MemberData(nameof(Vector3PackData))] - public void FromVector4ToVector4(IPackedVector packedVector, float[] vector4ComponentsToPack) + public void FromVector4ToVector4(IPixel packedVector, float[] vector4ComponentsToPack) { // Arrange var precision = 2; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/BadEofJpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/BadEofJpegTests.cs index adc568c82..8dbdb998c 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/BadEofJpegTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/BadEofJpegTests.cs @@ -29,7 +29,7 @@ namespace ImageSharp.Tests [Theory] [WithFile(TestImages.Jpeg.Baseline.Bad.MissingEOF, PixelTypes.Color)] public void LoadBaselineImage(TestImageProvider provider) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { using (Image image = provider.GetImage()) { @@ -41,7 +41,7 @@ namespace ImageSharp.Tests [Theory] // TODO: #18 [WithFile(TestImages.Jpeg.Progressive.Bad.BadEOF, PixelTypes.Color)] public void LoadProgressiveImage(TestImageProvider provider) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { using (Image image = provider.GetImage()) { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 8bce4c4e8..71ce4e165 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -27,7 +27,7 @@ namespace ImageSharp.Tests [Theory] [WithFileCollection(nameof(BaselineTestJpegs), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb)] public void OpenBaselineJpeg_SaveBmp(TestImageProvider provider) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { using (Image image = provider.GetImage()) { @@ -38,7 +38,7 @@ namespace ImageSharp.Tests [Theory] [WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb)] public void OpenProgressiveJpeg_SaveBmp(TestImageProvider provider) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { using (Image image = provider.GetImage()) { @@ -56,7 +56,7 @@ namespace ImageSharp.Tests TestImageProvider provider, JpegSubsample subsample, int quality) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { byte[] data; using (Image image = provider.GetImage()) @@ -80,7 +80,7 @@ namespace ImageSharp.Tests [WithSolidFilledImages(42, 88, 255, 0, 0, PixelTypes.StandardImageClass)] public void DecodeGenerated_MetadataOnly( TestImageProvider provider) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { using (Image image = provider.GetImage()) { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index ef6671931..6518c3e6b 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -32,7 +32,7 @@ namespace ImageSharp.Tests [WithFile(TestImages.Jpeg.Baseline.Snake, PixelTypes.StandardImageClass, 75, JpegSubsample.Ratio444)] [WithFile(TestImages.Jpeg.Baseline.Lake, PixelTypes.StandardImageClass, 75, JpegSubsample.Ratio444)] public void LoadResizeSave(TestImageProvider provider, int quality, JpegSubsample subsample) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { using (Image image = provider.GetImage().Resize(new ResizeOptions { Size = new Size(150, 100), Mode = ResizeMode.Max })) { @@ -50,7 +50,7 @@ namespace ImageSharp.Tests [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb, JpegSubsample.Ratio420, 75)] [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb, JpegSubsample.Ratio444, 75)] public void OpenBmp_SaveJpeg(TestImageProvider provider, JpegSubsample subSample, int quality) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { using (Image image = provider.GetImage()) { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs index 0956131b1..d0a7fae33 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs @@ -16,7 +16,7 @@ namespace ImageSharp.Tests public class JpegUtilsTests : TestBase { public static Image CreateTestImage(GenericFactory factory) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { Image image = factory.CreateImage(10, 10); using (PixelAccessor pixels = image.Lock()) @@ -41,7 +41,7 @@ namespace ImageSharp.Tests [Theory] [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb)] public void CopyStretchedRGBTo_FromOrigo(TestImageProvider provider) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { using (Image src = provider.GetImage()) using (Image dest = provider.Factory.CreateImage(8, 8)) @@ -63,7 +63,7 @@ namespace ImageSharp.Tests [Theory] [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb)] public void CopyStretchedRGBTo_WithOffset(TestImageProvider provider) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { using (Image src = provider.GetImage()) using (PixelArea area = new PixelArea(8, 8, ComponentOrder.Xyz)) diff --git a/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs b/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs index 46f667e70..f3cd20f45 100644 --- a/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs +++ b/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs @@ -16,7 +16,7 @@ namespace ImageSharp.Tests public class PixelAccessorTests { public static Image CreateTestImage(GenericFactory factory) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { Image image = factory.CreateImage(10, 10); using (PixelAccessor pixels = image.Lock()) @@ -44,7 +44,7 @@ namespace ImageSharp.Tests [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.Xyzw)] [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.Zyxw)] public void CopyTo_Then_CopyFrom_OnFullImageRect(TestImageProvider provider, ComponentOrder order) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { using (Image src = provider.GetImage()) { @@ -70,7 +70,7 @@ namespace ImageSharp.Tests // TODO: Need a processor in the library with this signature private static void Fill(Image image, Rectangle region, TColor color) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { using (PixelAccessor pixels = image.Lock()) { @@ -90,7 +90,7 @@ namespace ImageSharp.Tests [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Xyzw)] [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyxw)] public void CopyToThenCopyFromWithOffset(TestImageProvider provider, ComponentOrder order) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { using (Image destImage = new Image(8, 8)) { @@ -195,7 +195,7 @@ namespace ImageSharp.Tests } private static void CopyFromZYX(Image image) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { using (PixelAccessor pixels = image.Lock()) { @@ -222,7 +222,7 @@ namespace ImageSharp.Tests } private static void CopyFromZYXW(Image image) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { using (PixelAccessor pixels = image.Lock()) { @@ -250,7 +250,7 @@ namespace ImageSharp.Tests } private static void CopyToZYX(Image image) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { using (PixelAccessor pixels = image.Lock()) { @@ -272,7 +272,7 @@ namespace ImageSharp.Tests } private static void CopyToZYXW(Image image) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { using (PixelAccessor pixels = image.Lock()) { diff --git a/tests/ImageSharp.Tests/TestUtilities/Factories/GenericFactory.cs b/tests/ImageSharp.Tests/TestUtilities/Factories/GenericFactory.cs index c2f0aed84..87b7ace6a 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Factories/GenericFactory.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Factories/GenericFactory.cs @@ -12,7 +12,7 @@ namespace ImageSharp.Tests /// Used as parameter for -based factory methods /// public class GenericFactory - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { public virtual Image CreateImage(int width, int height) { diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs index c14f56588..ad4d2cc98 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs @@ -8,7 +8,7 @@ namespace ImageSharp.Tests using System; public abstract partial class TestImageProvider - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { private class BlankProvider : TestImageProvider { diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs index 1b8c1498e..cd403caed 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs @@ -9,7 +9,7 @@ namespace ImageSharp.Tests using System.Collections.Concurrent; public abstract partial class TestImageProvider - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { private class FileProvider : TestImageProvider { diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/LambdaProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/LambdaProvider.cs index c8bc705f1..9addc8ca6 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/LambdaProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/LambdaProvider.cs @@ -12,7 +12,7 @@ namespace ImageSharp.Tests /// /// The pixel format of the image public abstract partial class TestImageProvider - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { private class LambdaProvider : TestImageProvider { diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs index f7d3a0bf8..855374f55 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs @@ -12,7 +12,7 @@ namespace ImageSharp.Tests /// /// The pixel format of the image public abstract partial class TestImageProvider - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { private class SolidProvider : BlankProvider { diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs index 911719afa..cdb31ab69 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Tests /// /// The pixel format of the image public abstract partial class TestImageProvider - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { public PixelTypes PixelType { get; private set; } = typeof(TColor).GetPixelType(); diff --git a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs index 8eb658073..54be62d37 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs @@ -76,7 +76,7 @@ namespace ImageSharp.Tests /// The requested extension /// Optional encoder public void SaveTestOutputFile(Image image, string extension = null, IImageEncoder encoder = null) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { string path = this.GetTestOutputFileName(extension); diff --git a/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs index d2726c16a..748bd5293 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs @@ -57,7 +57,7 @@ namespace ImageSharp.Tests public static bool HasFlag(this PixelTypes pixelTypes, PixelTypes flag) => (pixelTypes & flag) == flag; public static bool IsEquivalentTo(this Image a, Image b, bool compareAlpha = true) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { if (a.Width != b.Width || a.Height != b.Height) { diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index 2f5ec6c28..09c81b761 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -22,7 +22,7 @@ namespace ImageSharp.Tests [Theory] [WithBlankImages(42, 666, PixelTypes.Color | PixelTypes.Argb | PixelTypes.HalfSingle, "hello")] public void Use_WithEmptyImageAttribute(TestImageProvider provider, string message) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { var img = provider.GetImage(); @@ -36,7 +36,7 @@ namespace ImageSharp.Tests public void Use_WithBlankImagesAttribute_WithAllPixelTypes( TestImageProvider provider, string message) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { var img = provider.GetImage(); @@ -50,7 +50,7 @@ namespace ImageSharp.Tests [WithBlankImages(1, 1, PixelTypes.Alpha8, PixelTypes.Alpha8)] [WithBlankImages(1, 1, PixelTypes.StandardImageClass, PixelTypes.StandardImageClass)] public void PixelType_PropertyValueIsCorrect(TestImageProvider provider, PixelTypes expected) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { Assert.Equal(expected, provider.PixelType); } @@ -60,7 +60,7 @@ namespace ImageSharp.Tests [WithFile(TestImages.Bmp.F, PixelTypes.StandardImageClass)] public void PixelTypes_ColorWithDefaultImageClass_TriggersCreatingTheNonGenericDerivedImageClass( TestImageProvider provider) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { var img = provider.GetImage(); @@ -71,7 +71,7 @@ namespace ImageSharp.Tests [WithFile(TestImages.Bmp.Car, PixelTypes.All, 88)] [WithFile(TestImages.Bmp.F, PixelTypes.All, 88)] public void Use_WithFileAttribute(TestImageProvider provider, int yo) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { Assert.NotNull(provider.Utility.SourceFileOrDescription); var img = provider.GetImage(); @@ -88,7 +88,7 @@ namespace ImageSharp.Tests [Theory] [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Color | PixelTypes.Argb)] public void Use_WithFileCollection(TestImageProvider provider) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { Assert.NotNull(provider.Utility.SourceFileOrDescription); var image = provider.GetImage(); @@ -98,7 +98,7 @@ namespace ImageSharp.Tests [Theory] [WithSolidFilledImages(10, 20, 255, 100, 50, 200, PixelTypes.Color | PixelTypes.Argb)] public void Use_WithSolidFilledImagesAttribute(TestImageProvider provider) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { var img = provider.GetImage(); Assert.Equal(img.Width, 10); @@ -130,7 +130,7 @@ namespace ImageSharp.Tests /// /// public static Image CreateTestImage(GenericFactory factory) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { return factory.CreateImage(3, 3); } @@ -138,7 +138,7 @@ namespace ImageSharp.Tests [Theory] [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All)] public void Use_WithMemberFactoryAttribute(TestImageProvider provider) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { var img = provider.GetImage(); Assert.Equal(img.Width, 3); @@ -160,7 +160,7 @@ namespace ImageSharp.Tests [Theory] [MemberData(nameof(BasicData))] public void Blank_MemberData(TestImageProvider provider) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { var img = provider.GetImage(); @@ -178,7 +178,7 @@ namespace ImageSharp.Tests [Theory] [MemberData(nameof(FileData))] public void File_MemberData(TestImageProvider provider) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { this.Output.WriteLine("SRC: " + provider.Utility.SourceFileOrDescription); this.Output.WriteLine("OUT: " + provider.Utility.GetTestOutputFileName()); diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs index 23a42c1a6..ad35cb80f 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs @@ -24,7 +24,7 @@ namespace ImageSharp.Tests private ITestOutputHelper Output { get; } public static Image CreateTestImage(GenericFactory factory) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { Image image = factory.CreateImage(10, 10); @@ -70,7 +70,7 @@ namespace ImageSharp.Tests [WithFile(TestImages.Bmp.Car, PixelTypes.Color, true)] [WithFile(TestImages.Bmp.Car, PixelTypes.Color, false)] public void IsEquivalentTo_WhenFalse(TestImageProvider provider, bool compareAlpha) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { var a = provider.GetImage(); var b = provider.GetImage(); @@ -83,7 +83,7 @@ namespace ImageSharp.Tests [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Color | PixelTypes.Bgr565, true)] [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Color | PixelTypes.Bgr565, false)] public void IsEquivalentTo_WhenTrue(TestImageProvider provider, bool compareAlpha) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { var a = provider.GetImage(); var b = provider.GetImage(); From bba886e214e9dd28156d5d21bf96dc2c41367405 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 18 Feb 2017 17:45:21 +0100 Subject: [PATCH 079/142] fix --- global.json | 2 +- tests/ImageSharp.Tests/Common/ArrayPointerTests.cs | 2 ++ .../TestUtilities/TestUtilityExtensions.cs | 9 --------- .../TestUtilities/Tests/TestUtilityExtensionsTests.cs | 10 +--------- 4 files changed, 4 insertions(+), 19 deletions(-) diff --git a/global.json b/global.json index 6da79b611..7346bdc28 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "projects": [ "src" ], "sdk": { - "version": "1.0.0-preview2-003131" + "version": "1.0.0-preview2-003121" } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs b/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs index 1d229f86a..64cd306f3 100644 --- a/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs +++ b/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs @@ -10,9 +10,11 @@ namespace ImageSharp.Tests.Common { public struct Foo { +#pragma warning disable CS0414 private int a; private double b; +#pragma warning restore CS0414 internal static Foo[] CreateArray(int size) { diff --git a/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs index 748bd5293..ca3340238 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs @@ -45,15 +45,6 @@ namespace ImageSharp.Tests PixelTypes2ClrTypes[PixelTypes.StandardImageClass] = typeof(Color); } - public static Type GetPackedType(Type pixelType) - { - var intrfcType = - pixelType.GetInterfaces() - .Single(i => i.IsConstructedGenericType && i.GetGenericTypeDefinition() == typeof(IPackedPixel<>)); - - return intrfcType.GetGenericArguments().Single(); - } - public static bool HasFlag(this PixelTypes pixelTypes, PixelTypes flag) => (pixelTypes & flag) == flag; public static bool IsEquivalentTo(this Image a, Image b, bool compareAlpha = true) diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs index ad35cb80f..1db209dcd 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs @@ -57,15 +57,7 @@ namespace ImageSharp.Tests var fake = typeof(Color).GetTypeInfo().Assembly.GetType("ImageSharp.dsaada_DASqewrr"); Assert.Null(fake); } - - [Fact] - public void GetPackedType() - { - Type shouldBeUIint32 = TestUtilityExtensions.GetPackedType(typeof(Color)); - - Assert.Equal(shouldBeUIint32, typeof(uint)); - } - + [Theory] [WithFile(TestImages.Bmp.Car, PixelTypes.Color, true)] [WithFile(TestImages.Bmp.Car, PixelTypes.Color, false)] From b4057ad78ab8d63bad49a42013c4509102671f6f Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sat, 18 Feb 2017 19:15:52 +0100 Subject: [PATCH 080/142] Added a debug version of the Guard class so we get exceptions for debug asserts instead of a dialog. --- src/ImageSharp/Common/Helpers/DebugGuard.cs | 33 +++++++++++++++++++ src/ImageSharp/Common/Helpers/ThrowHelper.cs | 26 --------------- .../Common/Memory/ArrayPointer{T}.cs | 12 ++----- src/ImageSharp/Image/ImageBase{TColor}.cs | 2 +- src/ImageSharp/MetaData/ImageFrameMetaData.cs | 4 +-- src/ImageSharp/MetaData/ImageMetaData.cs | 3 +- src/ImageSharp/MetaData/ImageProperty.cs | 3 +- 7 files changed, 39 insertions(+), 44 deletions(-) create mode 100644 src/ImageSharp/Common/Helpers/DebugGuard.cs delete mode 100644 src/ImageSharp/Common/Helpers/ThrowHelper.cs diff --git a/src/ImageSharp/Common/Helpers/DebugGuard.cs b/src/ImageSharp/Common/Helpers/DebugGuard.cs new file mode 100644 index 000000000..0ca6f0912 --- /dev/null +++ b/src/ImageSharp/Common/Helpers/DebugGuard.cs @@ -0,0 +1,33 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Diagnostics; + + /// + /// Provides methods to protect against invalid parameters for a DEBUG build. + /// + [DebuggerStepThrough] + internal static class DebugGuard + { + /// + /// Verifies, that the method parameter with specified object value is not null + /// and throws an exception if it is found to be so. + /// + /// The target object, which cannot be null. + /// The name of the parameter that is to be checked. + /// is null + [Conditional("DEBUG")] + public static void NotNull(object target, string parameterName) + { + if (target == null) + { + throw new ArgumentNullException(parameterName); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Common/Helpers/ThrowHelper.cs b/src/ImageSharp/Common/Helpers/ThrowHelper.cs deleted file mode 100644 index 97d75717a..000000000 --- a/src/ImageSharp/Common/Helpers/ThrowHelper.cs +++ /dev/null @@ -1,26 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - using System.Runtime.CompilerServices; - - /// - /// Helps removing exception throwing code from hot path by providing non-inlined exception thrower methods. - /// - internal static class ThrowHelper - { - /// - /// Throws an - /// - /// The parameter name - [MethodImpl(MethodImplOptions.NoInlining)] - public static void ThrowArgumentNullException(string paramName) - { - throw new ArgumentNullException(nameof(paramName)); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs b/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs index 95d2e51e6..1ea7706d4 100644 --- a/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs +++ b/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs @@ -31,11 +31,7 @@ namespace ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] public ArrayPointer(T[] array, void* pointerToArray, int offset) { - // TODO: Use Guard.NotNull() here after optimizing it by eliminating the default argument case and applying ThrowHelper! - if (array == null) - { - ThrowHelper.ThrowArgumentNullException(nameof(array)); - } + DebugGuard.NotNull(array, nameof(array)); this.Array = array; this.Offset = offset; @@ -50,11 +46,7 @@ namespace ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] public ArrayPointer(T[] array, void* pointerToArray) { - // TODO: Use Guard.NotNull() here after optimizing it by eliminating the default argument case and applying ThrowHelper! - if (array == null) - { - ThrowHelper.ThrowArgumentNullException(nameof(array)); - } + DebugGuard.NotNull(array, nameof(array)); this.Array = array; this.Offset = 0; diff --git a/src/ImageSharp/Image/ImageBase{TColor}.cs b/src/ImageSharp/Image/ImageBase{TColor}.cs index f530c0ccf..e4b4485c7 100644 --- a/src/ImageSharp/Image/ImageBase{TColor}.cs +++ b/src/ImageSharp/Image/ImageBase{TColor}.cs @@ -182,7 +182,7 @@ namespace ImageSharp /// protected void CopyProperties(IImageBase other) { - Debug.Assert(other != null); + DebugGuard.NotNull(other, nameof(other)); this.Configuration = other.Configuration; } diff --git a/src/ImageSharp/MetaData/ImageFrameMetaData.cs b/src/ImageSharp/MetaData/ImageFrameMetaData.cs index c2277686f..50119096e 100644 --- a/src/ImageSharp/MetaData/ImageFrameMetaData.cs +++ b/src/ImageSharp/MetaData/ImageFrameMetaData.cs @@ -5,8 +5,6 @@ namespace ImageSharp { - using System.Diagnostics; - /// /// Encapsulates the metadata of an image frame. /// @@ -28,7 +26,7 @@ namespace ImageSharp /// internal ImageFrameMetaData(ImageFrameMetaData other) { - Debug.Assert(other != null); + DebugGuard.NotNull(other, nameof(other)); this.FrameDelay = other.FrameDelay; } diff --git a/src/ImageSharp/MetaData/ImageMetaData.cs b/src/ImageSharp/MetaData/ImageMetaData.cs index a40df3110..de1e42476 100644 --- a/src/ImageSharp/MetaData/ImageMetaData.cs +++ b/src/ImageSharp/MetaData/ImageMetaData.cs @@ -6,7 +6,6 @@ namespace ImageSharp { using System.Collections.Generic; - using System.Diagnostics; /// /// Encapsulates the metadata of an image. @@ -46,7 +45,7 @@ namespace ImageSharp /// internal ImageMetaData(ImageMetaData other) { - Debug.Assert(other != null); + DebugGuard.NotNull(other, nameof(other)); this.HorizontalResolution = other.HorizontalResolution; this.VerticalResolution = other.VerticalResolution; diff --git a/src/ImageSharp/MetaData/ImageProperty.cs b/src/ImageSharp/MetaData/ImageProperty.cs index c8bd0b23d..178794283 100644 --- a/src/ImageSharp/MetaData/ImageProperty.cs +++ b/src/ImageSharp/MetaData/ImageProperty.cs @@ -6,7 +6,6 @@ namespace ImageSharp { using System; - using System.Diagnostics; /// /// Stores meta information about a image, like the name of the author, @@ -37,7 +36,7 @@ namespace ImageSharp /// internal ImageProperty(ImageProperty other) { - Debug.Assert(other != null); + DebugGuard.NotNull(other, nameof(other)); this.Name = other.Name; this.Value = other.Value; From 3cdc423500b4894ce8c0f7d43073ec16d1e90a5d Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 19 Feb 2017 14:18:41 +0100 Subject: [PATCH 081/142] The return of IPackedVector --- src/ImageSharp/Colors/Color.cs | 2 +- src/ImageSharp/Colors/PackedPixel/Alpha8.cs | 2 +- src/ImageSharp/Colors/PackedPixel/Argb.cs | 2 +- src/ImageSharp/Colors/PackedPixel/Bgr565.cs | 2 +- src/ImageSharp/Colors/PackedPixel/Bgra4444.cs | 2 +- src/ImageSharp/Colors/PackedPixel/Bgra5551.cs | 2 +- src/ImageSharp/Colors/PackedPixel/Byte4.cs | 2 +- .../Colors/PackedPixel/HalfSingle.cs | 2 +- .../Colors/PackedPixel/HalfVector2.cs | 2 +- .../Colors/PackedPixel/HalfVector4.cs | 2 +- .../PackedPixel/IPackedVector{TPacked}.cs | 24 +++++++++++++++++++ .../Colors/PackedPixel/NormalizedByte2.cs | 2 +- .../Colors/PackedPixel/NormalizedByte4.cs | 2 +- .../Colors/PackedPixel/NormalizedShort2.cs | 2 +- .../Colors/PackedPixel/NormalizedShort4.cs | 2 +- src/ImageSharp/Colors/PackedPixel/Rg32.cs | 2 +- .../Colors/PackedPixel/Rgba1010102.cs | 2 +- src/ImageSharp/Colors/PackedPixel/Rgba64.cs | 2 +- src/ImageSharp/Colors/PackedPixel/Short2.cs | 2 +- src/ImageSharp/Colors/PackedPixel/Short4.cs | 2 +- .../Common/ArrayPointerTests.cs | 18 +------------- 21 files changed, 44 insertions(+), 36 deletions(-) create mode 100644 src/ImageSharp/Colors/PackedPixel/IPackedVector{TPacked}.cs diff --git a/src/ImageSharp/Colors/Color.cs b/src/ImageSharp/Colors/Color.cs index b82628b3b..2c0d80e52 100644 --- a/src/ImageSharp/Colors/Color.cs +++ b/src/ImageSharp/Colors/Color.cs @@ -18,7 +18,7 @@ namespace ImageSharp /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, /// as it avoids the need to create new values for modification operations. /// - public partial struct Color : IPixel + public partial struct Color : IPixel, IPackedVector { /// /// The shift count for the red component diff --git a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs index 5141cbca4..6339b66a8 100644 --- a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs +++ b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing a single 8 bit normalized W values ranging from 0 to 1. /// - public struct Alpha8 : IPixel + public struct Alpha8 : IPixel, IPackedVector { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/Colors/PackedPixel/Argb.cs b/src/ImageSharp/Colors/PackedPixel/Argb.cs index 57ae0898b..da3c50917 100644 --- a/src/ImageSharp/Colors/PackedPixel/Argb.cs +++ b/src/ImageSharp/Colors/PackedPixel/Argb.cs @@ -17,7 +17,7 @@ namespace ImageSharp /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, /// as it avoids the need to create new values for modification operations. /// - public struct Argb : IPixel + public struct Argb : IPixel, IPackedVector { /// /// The shift count for the blue component diff --git a/src/ImageSharp/Colors/PackedPixel/Bgr565.cs b/src/ImageSharp/Colors/PackedPixel/Bgr565.cs index 042c8c0f5..e11aa9c65 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgr565.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgr565.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. The x and z components use 5 bits, and the y component uses 6 bits. /// - public struct Bgr565 : IPixel + public struct Bgr565 : IPixel, IPackedVector { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs b/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs index 8bae542c2..9853409ea 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing unsigned normalized values, ranging from 0 to 1, using 4 bits each for x, y, z, and w. /// - public struct Bgra4444 : IPixel + public struct Bgra4444 : IPixel, IPackedVector { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs b/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs index 0ad7dbd0e..bed658133 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. The x , y and z components use 5 bits, and the w component uses 1 bit. /// - public struct Bgra5551 : IPixel + public struct Bgra5551 : IPixel, IPackedVector { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/Colors/PackedPixel/Byte4.cs b/src/ImageSharp/Colors/PackedPixel/Byte4.cs index b1f8d5a6e..79a625c31 100644 --- a/src/ImageSharp/Colors/PackedPixel/Byte4.cs +++ b/src/ImageSharp/Colors/PackedPixel/Byte4.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing four 8-bit unsigned integer values, ranging from 0 to 255. /// - public struct Byte4 : IPixel + public struct Byte4 : IPixel, IPackedVector { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs b/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs index bd4ba3840..9737a99ae 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing a single 16 bit floating point value. /// - public struct HalfSingle : IPixel + public struct HalfSingle : IPixel, IPackedVector { /// /// The maximum byte value. diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs index 4d1f0a2cb..57649dbd5 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing two 16-bit floating-point values. /// - public struct HalfVector2 : IPixel + public struct HalfVector2 : IPixel, IPackedVector { /// /// The maximum byte value. diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs b/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs index e0108404d..f4f08a25c 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing four 16-bit floating-point values. /// - public struct HalfVector4 : IPixel + public struct HalfVector4 : IPixel, IPackedVector { /// /// The maximum byte value. diff --git a/src/ImageSharp/Colors/PackedPixel/IPackedVector{TPacked}.cs b/src/ImageSharp/Colors/PackedPixel/IPackedVector{TPacked}.cs new file mode 100644 index 000000000..0450fb8fb --- /dev/null +++ b/src/ImageSharp/Colors/PackedPixel/IPackedVector{TPacked}.cs @@ -0,0 +1,24 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Numerics; + + /// + /// This interface exists for ensuring signature compatibility to MonoGame and XNA packed color types. + /// + /// + /// The packed format. uint, long, float. + public interface IPackedVector : IPixel + where TPacked : struct, IEquatable + { + /// + /// Gets or sets the packed representation of the value. + /// + TPacked PackedValue { get; set; } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs index 0567b4445..1fece550f 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed packed pixel type containing two 8-bit signed normalized values, ranging from −1 to 1. /// - public struct NormalizedByte2 : IPixel + public struct NormalizedByte2 : IPixel, IPackedVector { /// /// The maximum byte value. diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs index a45f601c4..d49ea8a65 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing four 8-bit signed normalized values, ranging from −1 to 1. /// - public struct NormalizedByte4 : IPixel + public struct NormalizedByte4 : IPixel, IPackedVector { /// /// The maximum byte value. diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs index 3642aed5b..b9a2dc533 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing two 16-bit signed normalized values, ranging from −1 to 1. /// - public struct NormalizedShort2 : IPixel + public struct NormalizedShort2 : IPixel, IPackedVector { /// /// The maximum byte value. diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs index d5a3de79e..2cf90d208 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing four 16-bit signed normalized values, ranging from −1 to 1. /// - public struct NormalizedShort4 : IPixel + public struct NormalizedShort4 : IPixel, IPackedVector { /// /// The maximum byte value. diff --git a/src/ImageSharp/Colors/PackedPixel/Rg32.cs b/src/ImageSharp/Colors/PackedPixel/Rg32.cs index 9bda5f605..0dbcf483a 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rg32.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rg32.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing two 16-bit unsigned normalized values ranging from 0 to 1. /// - public struct Rg32 : IPixel + public struct Rg32 : IPixel, IPackedVector { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs b/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs index 69c097f96..f867afb01 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs @@ -13,7 +13,7 @@ namespace ImageSharp /// Packed vector type containing unsigned normalized values ranging from 0 to 1. /// The x, y and z components use 10 bits, and the w component uses 2 bits. /// - public struct Rgba1010102 : IPixel + public struct Rgba1010102 : IPixel, IPackedVector { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/Colors/PackedPixel/Rgba64.cs b/src/ImageSharp/Colors/PackedPixel/Rgba64.cs index 470556435..a99e127f3 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rgba64.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rgba64.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing four 16-bit unsigned normalized values ranging from 0 to 1. /// - public struct Rgba64 : IPixel + public struct Rgba64 : IPixel, IPackedVector { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/Colors/PackedPixel/Short2.cs b/src/ImageSharp/Colors/PackedPixel/Short2.cs index 3f83a4cd5..a89005ce5 100644 --- a/src/ImageSharp/Colors/PackedPixel/Short2.cs +++ b/src/ImageSharp/Colors/PackedPixel/Short2.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing two 16-bit signed integer values. /// - public struct Short2 : IPixel + public struct Short2 : IPixel, IPackedVector { /// /// The maximum byte value. diff --git a/src/ImageSharp/Colors/PackedPixel/Short4.cs b/src/ImageSharp/Colors/PackedPixel/Short4.cs index 1696cb40a..4b6a6d00b 100644 --- a/src/ImageSharp/Colors/PackedPixel/Short4.cs +++ b/src/ImageSharp/Colors/PackedPixel/Short4.cs @@ -12,7 +12,7 @@ namespace ImageSharp /// /// Packed pixel type containing four 16-bit signed integer values. /// - public struct Short4 : IPixel + public struct Short4 : IPixel, IPackedVector { /// /// The maximum byte value. diff --git a/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs b/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs index 64cd306f3..076e2512c 100644 --- a/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs +++ b/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs @@ -26,23 +26,7 @@ namespace ImageSharp.Tests.Common return result; } } - - [Fact] - public void ConstructWithNullArray_Throws() - { - Assert.Throws( - () => - { - new ArrayPointer(null, (void*)0); - }); - - Assert.Throws( - () => - { - new ArrayPointer(null, (void*)0); - }); - } - + [Fact] public void ConstructWithoutOffset() { From c3ef4a0160a43ed0ab9f2489122fe49bca24ec40 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 19 Feb 2017 21:48:50 +0100 Subject: [PATCH 082/142] restored on PackedValue-s --- src/ImageSharp/Colors/Color.cs | 4 +--- src/ImageSharp/Colors/PackedPixel/Alpha8.cs | 4 +--- src/ImageSharp/Colors/PackedPixel/Argb.cs | 4 +--- src/ImageSharp/Colors/PackedPixel/Bgr565.cs | 4 +--- src/ImageSharp/Colors/PackedPixel/Bgra4444.cs | 4 +--- src/ImageSharp/Colors/PackedPixel/Bgra5551.cs | 4 +--- src/ImageSharp/Colors/PackedPixel/Byte4.cs | 4 +--- src/ImageSharp/Colors/PackedPixel/HalfSingle.cs | 4 +--- src/ImageSharp/Colors/PackedPixel/HalfVector2.cs | 4 +--- src/ImageSharp/Colors/PackedPixel/HalfVector4.cs | 4 +--- src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs | 4 +--- src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs | 4 +--- src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs | 4 +--- src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs | 4 +--- src/ImageSharp/Colors/PackedPixel/Rg32.cs | 4 +--- src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs | 4 +--- src/ImageSharp/Colors/PackedPixel/Rgba64.cs | 4 +--- src/ImageSharp/Colors/PackedPixel/Short2.cs | 4 +--- src/ImageSharp/Colors/PackedPixel/Short4.cs | 4 +--- 19 files changed, 19 insertions(+), 57 deletions(-) diff --git a/src/ImageSharp/Colors/Color.cs b/src/ImageSharp/Colors/Color.cs index 2c0d80e52..469774b34 100644 --- a/src/ImageSharp/Colors/Color.cs +++ b/src/ImageSharp/Colors/Color.cs @@ -184,9 +184,7 @@ namespace ImageSharp } } - /// - /// Gets or sets the packed representation of the value. - /// + /// public uint PackedValue { get diff --git a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs index 6339b66a8..485725d71 100644 --- a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs +++ b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs @@ -23,9 +23,7 @@ namespace ImageSharp this.PackedValue = Pack(alpha); } - /// - /// Gets or sets the packed representation of the value. - /// + /// public byte PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/Argb.cs b/src/ImageSharp/Colors/PackedPixel/Argb.cs index da3c50917..bef986fb9 100644 --- a/src/ImageSharp/Colors/PackedPixel/Argb.cs +++ b/src/ImageSharp/Colors/PackedPixel/Argb.cs @@ -106,9 +106,7 @@ namespace ImageSharp this.PackedValue = packed; } - /// - /// Gets or sets the packed representation of the value. - /// + /// public uint PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/Bgr565.cs b/src/ImageSharp/Colors/PackedPixel/Bgr565.cs index e11aa9c65..ebe8d2533 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgr565.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgr565.cs @@ -36,9 +36,7 @@ namespace ImageSharp this.PackedValue = Pack(vector.X, vector.Y, vector.Z); } - /// - /// Gets or sets the packed representation of the value. - /// + /// public ushort PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs b/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs index 9853409ea..ccd6ab1f3 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs @@ -35,9 +35,7 @@ namespace ImageSharp this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); } - /// - /// Gets or sets the packed representation of the value. - /// + /// public ushort PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs b/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs index bed658133..a7a2e899a 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs @@ -37,9 +37,7 @@ namespace ImageSharp this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); } - /// - /// Gets or sets the packed representation of the value. - /// + /// public ushort PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/Byte4.cs b/src/ImageSharp/Colors/PackedPixel/Byte4.cs index 79a625c31..9d5eb9be8 100644 --- a/src/ImageSharp/Colors/PackedPixel/Byte4.cs +++ b/src/ImageSharp/Colors/PackedPixel/Byte4.cs @@ -38,9 +38,7 @@ namespace ImageSharp this.PackedValue = Pack(ref vector); } - /// - /// Gets or sets the packed representation of the value. - /// + /// public uint PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs b/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs index 9737a99ae..acfa639b7 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs @@ -33,9 +33,7 @@ namespace ImageSharp this.PackedValue = HalfTypeHelper.Pack(single); } - /// - /// Gets or sets the packed representation of the value. - /// + /// public ushort PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs index 57649dbd5..e02c226dd 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs @@ -43,9 +43,7 @@ namespace ImageSharp this.PackedValue = Pack(vector.X, vector.Y); } - /// - /// Gets or sets the packed representation of the value. - /// + /// public uint PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs b/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs index f4f08a25c..7c7f640e4 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs @@ -46,9 +46,7 @@ namespace ImageSharp this.PackedValue = Pack(ref vector); } - /// - /// Gets or sets the packed representation of the value. - /// + /// public ulong PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs index 1fece550f..116a68172 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs @@ -48,9 +48,7 @@ namespace ImageSharp this.PackedValue = Pack(x, y); } - /// - /// Gets or sets the packed representation of the value. - /// + /// public ushort PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs index d49ea8a65..7aaa30c52 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs @@ -50,9 +50,7 @@ namespace ImageSharp this.PackedValue = Pack(x, y, z, w); } - /// - /// Gets or sets the packed representation of the value. - /// + /// public uint PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs index b9a2dc533..2f4ef89d6 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs @@ -48,9 +48,7 @@ namespace ImageSharp this.PackedValue = Pack(x, y); } - /// - /// Gets or sets the packed representation of the value. - /// + /// public uint PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs index 2cf90d208..60c5c9805 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs @@ -50,9 +50,7 @@ namespace ImageSharp this.PackedValue = Pack(x, y, z, w); } - /// - /// Gets or sets the packed representation of the value. - /// + /// public ulong PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/Rg32.cs b/src/ImageSharp/Colors/PackedPixel/Rg32.cs index 0dbcf483a..9e5e5a711 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rg32.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rg32.cs @@ -33,9 +33,7 @@ namespace ImageSharp this.PackedValue = Pack(vector.X, vector.Y); } - /// - /// Gets or sets the packed representation of the value. - /// + /// public uint PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs b/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs index f867afb01..95a8d3b97 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs @@ -36,9 +36,7 @@ namespace ImageSharp this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); } - /// - /// Gets or sets the packed representation of the value. - /// + /// public uint PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/Rgba64.cs b/src/ImageSharp/Colors/PackedPixel/Rgba64.cs index a99e127f3..679a55c4e 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rgba64.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rgba64.cs @@ -35,9 +35,7 @@ namespace ImageSharp this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); } - /// - /// Gets or sets the packed representation of the value. - /// + /// public ulong PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/Short2.cs b/src/ImageSharp/Colors/PackedPixel/Short2.cs index a89005ce5..1c1cb28c3 100644 --- a/src/ImageSharp/Colors/PackedPixel/Short2.cs +++ b/src/ImageSharp/Colors/PackedPixel/Short2.cs @@ -48,9 +48,7 @@ namespace ImageSharp this.PackedValue = Pack(x, y); } - /// - /// Gets or sets the packed representation of the value. - /// + /// public uint PackedValue { get; set; } /// diff --git a/src/ImageSharp/Colors/PackedPixel/Short4.cs b/src/ImageSharp/Colors/PackedPixel/Short4.cs index 4b6a6d00b..2c11a1f8b 100644 --- a/src/ImageSharp/Colors/PackedPixel/Short4.cs +++ b/src/ImageSharp/Colors/PackedPixel/Short4.cs @@ -50,9 +50,7 @@ namespace ImageSharp this.PackedValue = Pack(x, y, z, w); } - /// - /// Gets or sets the packed representation of the value. - /// + /// public ulong PackedValue { get; set; } /// From e5ea99f222a6da1e9d76ea0ab3f769294aeb2f23 Mon Sep 17 00:00:00 2001 From: Toxantron Date: Sun, 19 Feb 2017 21:57:01 +0100 Subject: [PATCH 083/142] Develop with VSCode --- .vscode/tasks.json | 24 ++++++++++++++++++++++++ README.md | 5 +++++ 2 files changed, 29 insertions(+) create mode 100644 .vscode/tasks.json diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000..aeae5c6ca --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,24 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "0.1.0", + "command": "dotnet", + "isShellCommand": true, + "args": [], + "tasks": [ + { + "taskName": "build", + "args": [ "src/*/project.json", "-f", "netstandard1.1" ], + "isBuildCommand": true, + "showOutput": "always", + "problemMatcher": "$msCompile" + }, + { + "taskName": "test", + "args": ["tests/ImageSharp.Tests/project.json", "-f", "netcoreapp1.1"], + "isTestCommand": true, + "showOutput": "always", + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index cfbd18de6..cbe03bb4e 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,11 @@ If you prefer, you can compile ImageSharp yourself (please do and help!), you'll - [Visual Studio 2015 with Update 3 (or above)](https://www.visualstudio.com/news/releasenotes/vs2015-update3-vs) - The [.NET Core 1.0 SDK Installer](https://www.microsoft.com/net/core#windows) - Non VSCode link. +Alternatively on Linux you can use: + +- [Visual Studio Code](https://code.visualstudio.com/) with [C# Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.csharp) +- [.Net Core 1.1](https://www.microsoft.com/net/core#linuxubuntu) + To clone it locally click the "Clone in Windows" button above or run the following git commands. ```bash From 33627ee257d4ff5581870b82b73e6d0590702e1e Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 20 Feb 2017 01:17:31 +0100 Subject: [PATCH 084/142] Benchmarks: PixelAccessorVirtualCopy, vectorization --- src/ImageSharp/Image/PixelAccessor{TColor}.cs | 5 + .../Color/Bulk/PixelAccessorVirtualCopy.cs | 129 ++++++++++++++++++ .../Drawing/DrawBeziers.cs | 2 +- .../Drawing/DrawLines.cs | 2 +- .../Drawing/DrawPolygon.cs | 2 +- .../Drawing/FillWithPattern.cs | 2 +- .../General/Vectorization/BitwiseOrUint32.cs | 54 ++++++++ .../General/Vectorization/DivFloat.cs | 54 ++++++++ .../General/Vectorization/DivUInt32.cs | 54 ++++++++ .../General/Vectorization/MulFloat.cs | 54 ++++++++ .../General/Vectorization/MulUInt32.cs | 54 ++++++++ .../Vectorization/ReinterpretUInt32AsFloat.cs | 62 +++++++++ .../ImageSharp.Sandbox46.csproj | 11 +- tests/ImageSharp.Sandbox46/Program.cs | 19 ++- tests/ImageSharp.Sandbox46/app.config | 4 + tests/ImageSharp.Sandbox46/packages.config | 1 + 16 files changed, 501 insertions(+), 8 deletions(-) create mode 100644 tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs create mode 100644 tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs create mode 100644 tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs create mode 100644 tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs create mode 100644 tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs create mode 100644 tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs create mode 100644 tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs diff --git a/src/ImageSharp/Image/PixelAccessor{TColor}.cs b/src/ImageSharp/Image/PixelAccessor{TColor}.cs index 338f49182..b31ada10b 100644 --- a/src/ImageSharp/Image/PixelAccessor{TColor}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TColor}.cs @@ -120,6 +120,11 @@ namespace ImageSharp /// public bool PooledMemory { get; private set; } + /// + /// Gets the pixel buffer array. + /// + public TColor[] PixelBuffer => this.pixelBuffer; + /// /// Gets the pointer to the pixel buffer. /// diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs new file mode 100644 index 000000000..ed649792f --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace ImageSharp.Benchmarks.Color.Bulk +{ + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + + using BenchmarkDotNet.Attributes; + + using Color = ImageSharp.Color; + + /// + /// Benchmark to measure the effect of using virtual bulk-copy calls inside PixelAccessor methods + /// + public unsafe class PixelAccessorVirtualCopy + { + abstract class CopyExecutor + { + internal abstract void VirtualCopy(ArrayPointer destination, ArrayPointer source, int count); + } + + class UnsafeCopyExecutor : CopyExecutor + { + [MethodImpl(MethodImplOptions.NoInlining)] + internal override unsafe void VirtualCopy(ArrayPointer destination, ArrayPointer source, int count) + { + Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, (uint)count*4); + } + } + + private PixelAccessor pixelAccessor; + + private PixelArea area; + + private CopyExecutor executor; + + [Params(64, 256)] + public int Width { get; set; } + + public int Height { get; set; } = 256; + + + [Setup] + public void Setup() + { + this.pixelAccessor = new PixelAccessor(this.Width, this.Height); + this.area = new PixelArea(this.Width / 2, this.Height, ComponentOrder.Xyzw); + this.executor = new UnsafeCopyExecutor(); + } + + [Cleanup] + public void Cleanup() + { + this.pixelAccessor.Dispose(); + this.area.Dispose(); + } + + [Benchmark(Baseline = true)] + public void CopyRawUnsafeInlined() + { + uint byteCount = (uint)this.area.Width * 4; + + int targetX = this.Width / 4; + int targetY = 0; + + for (int y = 0; y < this.Height; y++) + { + byte* source = this.area.PixelBase + (y * this.area.RowStride); + byte* destination = this.GetRowPointer(targetX, targetY + y); + + Unsafe.CopyBlock(destination, source, byteCount); + } + } + + [Benchmark] + public void CopyArrayPointerUnsafeInlined() + { + uint byteCount = (uint)this.area.Width * 4; + + int targetX = this.Width / 4; + int targetY = 0; + + for (int y = 0; y < this.Height; y++) + { + ArrayPointer source = this.GetAreaRow(y); + ArrayPointer destination = this.GetPixelAccessorRow(targetX, targetY + y); + Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, byteCount); + } + } + + [Benchmark] + public void CopyArrayPointerUnsafeVirtual() + { + int targetX = this.Width / 4; + int targetY = 0; + + for (int y = 0; y < this.Height; y++) + { + ArrayPointer source = this.GetAreaRow(y); + ArrayPointer destination = this.GetPixelAccessorRow(targetX, targetY + y); + this.executor.VirtualCopy(destination, source, this.area.Width); + } + } + + private byte* GetRowPointer(int x, int y) + { + return (byte*)this.pixelAccessor.DataPointer + (((y * this.pixelAccessor.Width) + x) * Unsafe.SizeOf()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ArrayPointer GetPixelAccessorRow(int x, int y) + { + return new ArrayPointer( + this.pixelAccessor.PixelBuffer, + (void*)this.pixelAccessor.DataPointer, + (y * this.pixelAccessor.Width) + x + ); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ArrayPointer GetAreaRow(int y) + { + return new ArrayPointer(this.area.Bytes, this.area.PixelBase, y * this.area.RowStride); + } + } +} diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs index c066ac18c..a10417b90 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs @@ -28,7 +28,7 @@ namespace ImageSharp.Benchmarks { graphics.InterpolationMode = InterpolationMode.Default; graphics.SmoothingMode = SmoothingMode.AntiAlias; - var pen = new Pen(Color.HotPink, 10); + var pen = new Pen(System.Drawing.Color.HotPink, 10); graphics.DrawBeziers(pen, new[] { new PointF(10, 500), new PointF(30, 10), diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs index 78f71b660..146def363 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs @@ -28,7 +28,7 @@ namespace ImageSharp.Benchmarks { graphics.InterpolationMode = InterpolationMode.Default; graphics.SmoothingMode = SmoothingMode.AntiAlias; - var pen = new Pen(Color.HotPink, 10); + var pen = new Pen(System.Drawing.Color.HotPink, 10); graphics.DrawLines(pen, new[] { new PointF(10, 10), new PointF(550, 50), diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs index 88618b912..e6c1ac0d6 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs @@ -27,7 +27,7 @@ namespace ImageSharp.Benchmarks { graphics.InterpolationMode = InterpolationMode.Default; graphics.SmoothingMode = SmoothingMode.AntiAlias; - var pen = new Pen(Color.HotPink, 10); + var pen = new Pen(System.Drawing.Color.HotPink, 10); graphics.DrawPolygon(pen, new[] { new PointF(10, 10), new PointF(550, 50), diff --git a/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs b/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs index 718474f1f..589ac0cd4 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs @@ -25,7 +25,7 @@ namespace ImageSharp.Benchmarks using (Graphics graphics = Graphics.FromImage(destination)) { graphics.SmoothingMode = SmoothingMode.AntiAlias; - var brush = new HatchBrush(HatchStyle.BackwardDiagonal, Color.HotPink); + var brush = new HatchBrush(HatchStyle.BackwardDiagonal, System.Drawing.Color.HotPink); graphics.FillRectangle(brush, new Rectangle(0,0, 800,800)); // can't find a way to flood fill with a brush } using (MemoryStream ms = new MemoryStream()) diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs new file mode 100644 index 000000000..dd20e85d5 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs @@ -0,0 +1,54 @@ +namespace ImageSharp.Benchmarks.General.Vectorization +{ + using System.Numerics; + + using BenchmarkDotNet.Attributes; + + public class BitwiseOrUInt32 + { + private uint[] input; + + private uint[] result; + + [Params(32)] + public int InputSize { get; set; } + + private uint testValue; + + [Setup] + public void Setup() + { + this.input = new uint[this.InputSize]; + this.result = new uint[this.InputSize]; + this.testValue = 42; + + for (int i = 0; i < this.InputSize; i++) + { + this.input[i] = (uint) i; + } + } + + [Benchmark(Baseline = true)] + public void Standard() + { + uint v = this.testValue; + for (int i = 0; i < this.input.Length; i++) + { + this.result[i] = this.input[i] | v; + } + } + + [Benchmark] + public void Simd() + { + Vector v = new Vector(this.testValue); + + for (int i = 0; i < this.input.Length; i+=Vector.Count) + { + Vector a = new Vector(this.input, i); + a = Vector.BitwiseOr(a, v); + a.CopyTo(this.result, i); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs new file mode 100644 index 000000000..61582b7dc --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs @@ -0,0 +1,54 @@ +namespace ImageSharp.Benchmarks.General.Vectorization +{ + using System.Numerics; + + using BenchmarkDotNet.Attributes; + + public class DivFloat + { + private float[] input; + + private float[] result; + + [Params(32)] + public int InputSize { get; set; } + + private float testValue; + + [Setup] + public void Setup() + { + this.input = new float[this.InputSize]; + this.result = new float[this.InputSize]; + this.testValue = 42; + + for (int i = 0; i < this.InputSize; i++) + { + this.input[i] = (uint)i; + } + } + + [Benchmark(Baseline = true)] + public void Standard() + { + float v = this.testValue; + for (int i = 0; i < this.input.Length; i++) + { + this.result[i] = this.input[i] / v; + } + } + + [Benchmark] + public void Simd() + { + Vector v = new Vector(this.testValue); + + for (int i = 0; i < this.input.Length; i += Vector.Count) + { + Vector a = new Vector(this.input, i); + a = a / v; + a.CopyTo(this.result, i); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs new file mode 100644 index 000000000..75fa03c04 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs @@ -0,0 +1,54 @@ +namespace ImageSharp.Benchmarks.General.Vectorization +{ + using System.Numerics; + + using BenchmarkDotNet.Attributes; + + public class DivUInt32 + { + private uint[] input; + + private uint[] result; + + [Params(32)] + public int InputSize { get; set; } + + private uint testValue; + + [Setup] + public void Setup() + { + this.input = new uint[this.InputSize]; + this.result = new uint[this.InputSize]; + this.testValue = 42; + + for (int i = 0; i < this.InputSize; i++) + { + this.input[i] = (uint)i; + } + } + + [Benchmark(Baseline = true)] + public void Standard() + { + uint v = this.testValue; + for (int i = 0; i < this.input.Length; i++) + { + this.result[i] = this.input[i] / v; + } + } + + [Benchmark] + public void Simd() + { + Vector v = new Vector(this.testValue); + + for (int i = 0; i < this.input.Length; i += Vector.Count) + { + Vector a = new Vector(this.input, i); + a = a / v; + a.CopyTo(this.result, i); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs new file mode 100644 index 000000000..151145e12 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs @@ -0,0 +1,54 @@ +namespace ImageSharp.Benchmarks.General.Vectorization +{ + using System.Numerics; + + using BenchmarkDotNet.Attributes; + + public class MulFloat + { + private float[] input; + + private float[] result; + + [Params(32)] + public int InputSize { get; set; } + + private float testValue; + + [Setup] + public void Setup() + { + this.input = new float[this.InputSize]; + this.result = new float[this.InputSize]; + this.testValue = 42; + + for (int i = 0; i < this.InputSize; i++) + { + this.input[i] = (uint)i; + } + } + + [Benchmark(Baseline = true)] + public void Standard() + { + float v = this.testValue; + for (int i = 0; i < this.input.Length; i++) + { + this.result[i] = this.input[i] * v; + } + } + + [Benchmark] + public void Simd() + { + Vector v = new Vector(this.testValue); + + for (int i = 0; i < this.input.Length; i += Vector.Count) + { + Vector a = new Vector(this.input, i); + a = a * v; + a.CopyTo(this.result, i); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs new file mode 100644 index 000000000..f7d6cf9b9 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs @@ -0,0 +1,54 @@ +namespace ImageSharp.Benchmarks.General.Vectorization +{ + using System.Numerics; + + using BenchmarkDotNet.Attributes; + + public class MulUInt32 + { + private uint[] input; + + private uint[] result; + + [Params(32)] + public int InputSize { get; set; } + + private uint testValue; + + [Setup] + public void Setup() + { + this.input = new uint[this.InputSize]; + this.result = new uint[this.InputSize]; + this.testValue = 42; + + for (int i = 0; i < this.InputSize; i++) + { + this.input[i] = (uint)i; + } + } + + [Benchmark(Baseline = true)] + public void Standard() + { + uint v = this.testValue; + for (int i = 0; i < this.input.Length; i++) + { + this.result[i] = this.input[i] * v; + } + } + + [Benchmark] + public void Simd() + { + Vector v = new Vector(this.testValue); + + for (int i = 0; i < this.input.Length; i += Vector.Count) + { + Vector a = new Vector(this.input, i); + a = a * v; + a.CopyTo(this.result, i); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs new file mode 100644 index 000000000..b0ca181cd --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs @@ -0,0 +1,62 @@ +namespace ImageSharp.Benchmarks.General.Vectorization +{ + using System.Numerics; + using System.Runtime.InteropServices; + + using BenchmarkDotNet.Attributes; + + public class ReinterpretUInt32AsFloat + { + private uint[] input; + + private float[] result; + + [Params(32)] + public int InputSize { get; set; } + + [StructLayout(LayoutKind.Explicit)] + struct UIntFloatUnion + { + [FieldOffset(0)] + public float f; + + [FieldOffset(0)] + public uint i; + } + + + [Setup] + public void Setup() + { + this.input = new uint[this.InputSize]; + this.result = new float[this.InputSize]; + + for (int i = 0; i < this.InputSize; i++) + { + this.input[i] = (uint)i; + } + } + + [Benchmark(Baseline = true)] + public void Standard() + { + UIntFloatUnion u = default(UIntFloatUnion); + for (int i = 0; i < this.input.Length; i++) + { + u.i = this.input[i]; + this.result[i] = u.f; + } + } + + [Benchmark] + public void Simd() + { + for (int i = 0; i < this.input.Length; i += Vector.Count) + { + Vector a = new Vector(this.input, i); + Vector b = Vector.AsVectorSingle(a); + b.CopyTo(this.result, i); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj index 2444db031..d1b059f44 100644 --- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj +++ b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj @@ -109,6 +109,10 @@ ..\..\packages\System.Reflection.Metadata.1.3.0\lib\portable-net45+win8\System.Reflection.Metadata.dll True + + ..\..\packages\System.Runtime.CompilerServices.Unsafe.4.3.0\lib\netstandard1.0\System.Runtime.CompilerServices.Unsafe.dll + True + ..\..\packages\System.Security.Cryptography.Algorithms.4.2.0\lib\net461\System.Security.Cryptography.Algorithms.dll True @@ -202,6 +206,9 @@ + + Benchmarks\PixelAccessorVirtualCopy.cs + Tests\Drawing\PolygonTests.cs @@ -327,9 +334,7 @@ - - - + diff --git a/tests/ImageSharp.Sandbox46/Program.cs b/tests/ImageSharp.Sandbox46/Program.cs index 48219902b..f289ac2db 100644 --- a/tests/ImageSharp.Sandbox46/Program.cs +++ b/tests/ImageSharp.Sandbox46/Program.cs @@ -8,6 +8,7 @@ namespace ImageSharp.Sandbox46 using System; using System.Runtime.DesignerServices; + using ImageSharp.Benchmarks.Color.Bulk; using ImageSharp.Tests; using Xunit.Abstractions; @@ -36,7 +37,23 @@ namespace ImageSharp.Sandbox46 /// public static void Main(string[] args) { - RunDecodeJpegProfilingTests(); + //RunDecodeJpegProfilingTests(); + TestPixelAccessorCopyFromXyzw(); + Console.ReadLine(); + } + + private static void TestPixelAccessorCopyFromXyzw() + { + PixelAccessorVirtualCopy benchmark = new PixelAccessorVirtualCopy(); + benchmark.Width = 64; + benchmark.Setup(); + + benchmark.CopyRawUnsafeInlined(); + benchmark.CopyArrayPointerUnsafe(); + benchmark.CopyArrayPointerVirtualUnsafe(); + benchmark.CopyArrayPointerVirtualMarshal(); + + benchmark.Cleanup(); } private static void RunDecodeJpegProfilingTests() diff --git a/tests/ImageSharp.Sandbox46/app.config b/tests/ImageSharp.Sandbox46/app.config index 5a049c66e..3328297a5 100644 --- a/tests/ImageSharp.Sandbox46/app.config +++ b/tests/ImageSharp.Sandbox46/app.config @@ -10,6 +10,10 @@ + + + + \ No newline at end of file diff --git a/tests/ImageSharp.Sandbox46/packages.config b/tests/ImageSharp.Sandbox46/packages.config index 65ad74fa6..426f5f1b5 100644 --- a/tests/ImageSharp.Sandbox46/packages.config +++ b/tests/ImageSharp.Sandbox46/packages.config @@ -29,6 +29,7 @@ + From 553728691ec71c27e03a38b36a2e81e17360cd0c Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 20 Feb 2017 01:25:11 +0100 Subject: [PATCH 085/142] additional PixelAccessorVirtualCopy Param --- .../Color/Bulk/PixelAccessorVirtualCopy.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs index ed649792f..9222d6bac 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs @@ -37,7 +37,7 @@ namespace ImageSharp.Benchmarks.Color.Bulk private CopyExecutor executor; - [Params(64, 256)] + [Params(64, 256, 512)] public int Width { get; set; } public int Height { get; set; } = 256; From 395505ac66338d9a648fb2095d3ac21b5032e3ab Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sat, 18 Feb 2017 13:16:49 +0100 Subject: [PATCH 086/142] Added options for the decoder. --- src/ImageSharp.Formats.Bmp/BmpDecoder.cs | 2 +- src/ImageSharp.Formats.Gif/GifDecoder.cs | 2 +- src/ImageSharp.Formats.Jpeg/JpegDecoder.cs | 2 +- src/ImageSharp.Formats.Png/PngDecoder.cs | 9 ++---- src/ImageSharp/Formats/DecoderOptions.cs | 18 ++++++++++++ src/ImageSharp/Formats/IDecoderOptions.cs | 18 ++++++++++++ src/ImageSharp/Formats/IImageDecoder.cs | 3 +- src/ImageSharp/Image.cs | 23 +++++++++++---- src/ImageSharp/Image/Image{TColor}.cs | 33 ++++++++++++++-------- 9 files changed, 82 insertions(+), 28 deletions(-) create mode 100644 src/ImageSharp/Formats/DecoderOptions.cs create mode 100644 src/ImageSharp/Formats/IDecoderOptions.cs diff --git a/src/ImageSharp.Formats.Bmp/BmpDecoder.cs b/src/ImageSharp.Formats.Bmp/BmpDecoder.cs index 4b7da38af..9f490a3a9 100644 --- a/src/ImageSharp.Formats.Bmp/BmpDecoder.cs +++ b/src/ImageSharp.Formats.Bmp/BmpDecoder.cs @@ -26,7 +26,7 @@ namespace ImageSharp.Formats public class BmpDecoder : IImageDecoder { /// - public void Decode(Image image, Stream stream) + public void Decode(Image image, Stream stream, IDecoderOptions options) where TColor : struct, IPixel { Guard.NotNull(image, "image"); diff --git a/src/ImageSharp.Formats.Gif/GifDecoder.cs b/src/ImageSharp.Formats.Gif/GifDecoder.cs index 76530dc50..aea96bb65 100644 --- a/src/ImageSharp.Formats.Gif/GifDecoder.cs +++ b/src/ImageSharp.Formats.Gif/GifDecoder.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Formats public class GifDecoder : IImageDecoder { /// - public void Decode(Image image, Stream stream) + public void Decode(Image image, Stream stream, IDecoderOptions options) where TColor : struct, IPixel { new GifDecoderCore().Decode(image, stream); diff --git a/src/ImageSharp.Formats.Jpeg/JpegDecoder.cs b/src/ImageSharp.Formats.Jpeg/JpegDecoder.cs index 435ae51cf..a399bb6b3 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegDecoder.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegDecoder.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Formats public class JpegDecoder : IImageDecoder { /// - public void Decode(Image image, Stream stream) + public void Decode(Image image, Stream stream, IDecoderOptions options) where TColor : struct, IPixel { Guard.NotNull(image, "image"); diff --git a/src/ImageSharp.Formats.Png/PngDecoder.cs b/src/ImageSharp.Formats.Png/PngDecoder.cs index 088bea591..9aab6673a 100644 --- a/src/ImageSharp.Formats.Png/PngDecoder.cs +++ b/src/ImageSharp.Formats.Png/PngDecoder.cs @@ -30,13 +30,8 @@ namespace ImageSharp.Formats /// public class PngDecoder : IImageDecoder { - /// - /// Decodes the image from the specified stream to the . - /// - /// The pixel format. - /// The to decode to. - /// The containing image data. - public void Decode(Image image, Stream stream) + /// + public void Decode(Image image, Stream stream, IDecoderOptions options) where TColor : struct, IPixel { new PngDecoderCore().Decode(image, stream); diff --git a/src/ImageSharp/Formats/DecoderOptions.cs b/src/ImageSharp/Formats/DecoderOptions.cs new file mode 100644 index 000000000..f25e33d71 --- /dev/null +++ b/src/ImageSharp/Formats/DecoderOptions.cs @@ -0,0 +1,18 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Encapsulates the shared decoder options. + /// + public class DecoderOptions : IDecoderOptions + { + /// + /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. + /// + public bool IgnoreMetadata { get; set; } + } +} diff --git a/src/ImageSharp/Formats/IDecoderOptions.cs b/src/ImageSharp/Formats/IDecoderOptions.cs new file mode 100644 index 000000000..460de3eb2 --- /dev/null +++ b/src/ImageSharp/Formats/IDecoderOptions.cs @@ -0,0 +1,18 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Encapsulates the shared decoder options. + /// + public interface IDecoderOptions + { + /// + /// Gets a value indicating whether the metadata should be ignored when the image is being decoded. + /// + bool IgnoreMetadata { get; } + } +} diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs index 28bda7837..df98870dd 100644 --- a/src/ImageSharp/Formats/IImageDecoder.cs +++ b/src/ImageSharp/Formats/IImageDecoder.cs @@ -19,7 +19,8 @@ namespace ImageSharp.Formats /// The pixel format. /// The to decode to. /// The containing image data. - void Decode(Image image, Stream stream) + /// The options for the decoder. + void Decode(Image image, Stream stream, IDecoderOptions options) where TColor : struct, IPixel; } } diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index bdb6b49a9..b1c1252ab 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -8,6 +8,8 @@ namespace ImageSharp using System.Diagnostics; using System.IO; + using Formats; + /// /// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha /// packed into a single unsigned integer value. @@ -35,12 +37,15 @@ namespace ImageSharp /// /// The stream containing image information. /// + /// + /// The options for the decoder. + /// /// /// The configuration providing initialization code which allows extending the library. /// /// Thrown if the is null. - public Image(Stream stream, Configuration configuration = null) - : base(stream, configuration) + public Image(Stream stream, IDecoderOptions options = null, Configuration configuration = null) + : base(stream, options, configuration) { } @@ -51,12 +56,15 @@ namespace ImageSharp /// /// A file path to read image information. /// + /// + /// The options for the decoder. + /// /// /// The configuration providing initialization code which allows extending the library. /// /// Thrown if the is null. - public Image(string filePath, Configuration configuration = null) - : base(filePath, configuration) + public Image(string filePath, IDecoderOptions options = null, Configuration configuration = null) + : base(filePath, options, configuration) { } #endif @@ -67,12 +75,15 @@ namespace ImageSharp /// /// The byte array containing image information. /// + /// + /// The options for the decoder. + /// /// /// The configuration providing initialization code which allows extending the library. /// /// Thrown if the is null. - public Image(byte[] bytes, Configuration configuration = null) - : base(bytes, configuration) + public Image(byte[] bytes, IDecoderOptions options = null, Configuration configuration = null) + : base(bytes, options, configuration) { } diff --git a/src/ImageSharp/Image/Image{TColor}.cs b/src/ImageSharp/Image/Image{TColor}.cs index bbd839d16..9e4ecba2f 100644 --- a/src/ImageSharp/Image/Image{TColor}.cs +++ b/src/ImageSharp/Image/Image{TColor}.cs @@ -52,15 +52,18 @@ namespace ImageSharp /// /// The stream containing image information. /// + /// + /// The options for the decoder. + /// /// /// The configuration providing initialization code which allows extending the library. /// /// Thrown if the is null. - public Image(Stream stream, Configuration configuration = null) + public Image(Stream stream, IDecoderOptions options = null, Configuration configuration = null) : base(configuration) { Guard.NotNull(stream, nameof(stream)); - this.Load(stream); + this.Load(stream, options); } #if !NETSTANDARD1_1 @@ -70,17 +73,20 @@ namespace ImageSharp /// /// The file containing image information. /// + /// + /// The options for the decoder. + /// /// /// The configuration providing initialization code which allows extending the library. /// /// Thrown if the is null. - public Image(string filePath, Configuration configuration = null) + public Image(string filePath, IDecoderOptions options = null, Configuration configuration = null) : base(configuration) { Guard.NotNull(filePath, nameof(filePath)); using (var fs = File.OpenRead(filePath)) { - this.Load(fs); + this.Load(fs, options); } } #endif @@ -91,18 +97,21 @@ namespace ImageSharp /// /// The byte array containing image information. /// + /// + /// The options for the decoder. + /// /// /// The configuration providing initialization code which allows extending the library. /// /// Thrown if the is null. - public Image(byte[] bytes, Configuration configuration = null) + public Image(byte[] bytes, IDecoderOptions options = null, Configuration configuration = null) : base(configuration) { Guard.NotNull(bytes, nameof(bytes)); using (MemoryStream stream = new MemoryStream(bytes, false)) { - this.Load(stream); + this.Load(stream, options); } } @@ -397,10 +406,11 @@ namespace ImageSharp /// Loads the image from the given stream. /// /// The stream containing image information. + /// The options for the decoder. /// /// Thrown if the stream is not readable nor seekable. /// - private void Load(Stream stream) + private void Load(Stream stream, IDecoderOptions options) { if (!this.Configuration.ImageFormats.Any()) { @@ -414,7 +424,7 @@ namespace ImageSharp if (stream.CanSeek) { - if (this.Decode(stream)) + if (this.Decode(stream, options)) { return; } @@ -427,7 +437,7 @@ namespace ImageSharp stream.CopyTo(ms); ms.Position = 0; - if (this.Decode(ms)) + if (this.Decode(ms, options)) { return; } @@ -449,10 +459,11 @@ namespace ImageSharp /// Decodes the image stream to the current image. /// /// The stream. + /// The options for the decoder. /// /// The . /// - private bool Decode(Stream stream) + private bool Decode(Stream stream, IDecoderOptions options) { int maxHeaderSize = this.Configuration.MaxHeaderSize; if (maxHeaderSize <= 0) @@ -479,7 +490,7 @@ namespace ImageSharp return false; } - format.Decoder.Decode(this, stream); + format.Decoder.Decode(this, stream, options); this.CurrentImageFormat = format; return true; } From 32481be496245d2917609ca23fcbd8a8ca74b49a Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sat, 18 Feb 2017 18:59:59 +0100 Subject: [PATCH 087/142] Implemented the IDecoderOptions inside the gif decoder. --- src/ImageSharp.Formats.Gif/GifDecoder.cs | 2 +- src/ImageSharp.Formats.Gif/GifDecoderCore.cs | 38 +++++++++++---- src/ImageSharp/Formats/DecoderOptions.cs | 16 ++++++- src/ImageSharp/Formats/IDecoderOptions.cs | 2 +- .../Formats/Gif/GifDecoderCoreTests.cs | 46 +++++++++++++++++++ .../TestImages/Formats/Gif/rings.gif | 4 +- 6 files changed, 95 insertions(+), 13 deletions(-) create mode 100644 tests/ImageSharp.Tests/Formats/Gif/GifDecoderCoreTests.cs diff --git a/src/ImageSharp.Formats.Gif/GifDecoder.cs b/src/ImageSharp.Formats.Gif/GifDecoder.cs index aea96bb65..42dbf4fc7 100644 --- a/src/ImageSharp.Formats.Gif/GifDecoder.cs +++ b/src/ImageSharp.Formats.Gif/GifDecoder.cs @@ -17,7 +17,7 @@ namespace ImageSharp.Formats public void Decode(Image image, Stream stream, IDecoderOptions options) where TColor : struct, IPixel { - new GifDecoderCore().Decode(image, stream); + new GifDecoderCore(options).Decode(image, stream); } } } diff --git a/src/ImageSharp.Formats.Gif/GifDecoderCore.cs b/src/ImageSharp.Formats.Gif/GifDecoderCore.cs index 5812b9f29..50d4da0eb 100644 --- a/src/ImageSharp.Formats.Gif/GifDecoderCore.cs +++ b/src/ImageSharp.Formats.Gif/GifDecoderCore.cs @@ -8,6 +8,7 @@ namespace ImageSharp.Formats using System; using System.Buffers; using System.IO; + using System.Text; /// /// Performs the gif decoding operation. @@ -21,6 +22,11 @@ namespace ImageSharp.Formats /// private readonly byte[] buffer = new byte[16]; + /// + /// The decoder options. + /// + private readonly IDecoderOptions options; + /// /// The image to decode the information to. /// @@ -61,6 +67,15 @@ namespace ImageSharp.Formats /// private GifGraphicsControlExtension graphicsControlExtension; + /// + /// Initializes a new instance of the class. + /// + /// The decoder options. + public GifDecoderCore(IDecoderOptions options) + { + this.options = options ?? DecoderOptions.Default; + } + /// /// Decodes the stream to the image. /// @@ -225,25 +240,32 @@ namespace ImageSharp.Formats /// private void ReadComments() { - int flag; + int length; - while ((flag = this.currentStream.ReadByte()) != 0) + while ((length = this.currentStream.ReadByte()) != 0) { - if (flag > GifConstants.MaxCommentLength) + if (length > GifConstants.MaxCommentLength) + { + throw new ImageFormatException($"Gif comment length '{length}' exceeds max '{GifConstants.MaxCommentLength}'"); + } + + if (this.options.IgnoreMetadata) { - throw new ImageFormatException($"Gif comment length '{flag}' exceeds max '{GifConstants.MaxCommentLength}'"); + this.currentStream.Seek(length, SeekOrigin.Current); + continue; } - byte[] flagBuffer = ArrayPool.Shared.Rent(flag); + byte[] commentsBuffer = ArrayPool.Shared.Rent(length); try { - this.currentStream.Read(flagBuffer, 0, flag); - this.decodedImage.MetaData.Properties.Add(new ImageProperty("Comments", BitConverter.ToString(flagBuffer, 0, flag))); + this.currentStream.Read(commentsBuffer, 0, length); + string comments = Encoding.GetEncoding("ASCII").GetString(commentsBuffer, 0, length); + this.decodedImage.MetaData.Properties.Add(new ImageProperty("Comments", comments)); } finally { - ArrayPool.Shared.Return(flagBuffer); + ArrayPool.Shared.Return(commentsBuffer); } } } diff --git a/src/ImageSharp/Formats/DecoderOptions.cs b/src/ImageSharp/Formats/DecoderOptions.cs index f25e33d71..a9bb152f1 100644 --- a/src/ImageSharp/Formats/DecoderOptions.cs +++ b/src/ImageSharp/Formats/DecoderOptions.cs @@ -3,13 +3,27 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Formats +namespace ImageSharp { /// /// Encapsulates the shared decoder options. /// public class DecoderOptions : IDecoderOptions { + /// + /// Gets the default decoder options + /// + public static DecoderOptions Default + { + get + { + return new DecoderOptions() + { + IgnoreMetadata = false + }; + } + } + /// /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. /// diff --git a/src/ImageSharp/Formats/IDecoderOptions.cs b/src/ImageSharp/Formats/IDecoderOptions.cs index 460de3eb2..cdfd90d5e 100644 --- a/src/ImageSharp/Formats/IDecoderOptions.cs +++ b/src/ImageSharp/Formats/IDecoderOptions.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Formats +namespace ImageSharp { /// /// Encapsulates the shared decoder options. diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderCoreTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderCoreTests.cs new file mode 100644 index 000000000..758c72400 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderCoreTests.cs @@ -0,0 +1,46 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using Xunit; + + public class GifDecoderCoreTests + { + [Fact] + public void Decode_IgnoreMetadataIsFalse_CommentsAreRead() + { + var options = new DecoderOptions() + { + IgnoreMetadata = false + }; + + TestFile testFile = TestFile.Create(TestImages.Gif.Rings); + + using (Image image = new Image(testFile.FilePath, options)) + { + Assert.Equal(1, image.MetaData.Properties.Count); + Assert.Equal("Comments", image.MetaData.Properties[0].Name); + Assert.Equal("ImageSharp", image.MetaData.Properties[0].Value); + } + } + + [Fact] + public void Decode_IgnoreMetadataIsTrue_CommentsAreIgnored() + { + var options = new DecoderOptions() + { + IgnoreMetadata = true + }; + + TestFile testFile = TestFile.Create(TestImages.Gif.Rings); + + using (Image image = new Image(testFile.FilePath, options)) + { + Assert.Equal(0, image.MetaData.Properties.Count); + } + } + } +} diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Gif/rings.gif b/tests/ImageSharp.Tests/TestImages/Formats/Gif/rings.gif index acd5d6339..a714dbfbb 100644 --- a/tests/ImageSharp.Tests/TestImages/Formats/Gif/rings.gif +++ b/tests/ImageSharp.Tests/TestImages/Formats/Gif/rings.gif @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:716448da88152225767c024aac498f5b7562b6e8391907cefc9d03dba40050fd -size 53435 +oid sha256:a7b9b5f5056a8d90134d72b462bf37675ab37aa2551ffeae0072037a0a27ad74 +size 53457 From c7ced5bcdfaac6569e94830032de2835a02844a2 Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sat, 18 Feb 2017 20:42:10 +0100 Subject: [PATCH 088/142] Implemented the IDecoderOptions inside the jpeg decoder. --- src/ImageSharp.Formats.Jpeg/JpegDecoder.cs | 2 +- .../JpegDecoderCore.cs | 11 ++++- .../Formats/Jpg/JpegDecoderCoreTests.cs | 44 +++++++++++++++++++ .../Formats/Jpg/JpegDecoderTests.cs | 2 +- 4 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderCoreTests.cs diff --git a/src/ImageSharp.Formats.Jpeg/JpegDecoder.cs b/src/ImageSharp.Formats.Jpeg/JpegDecoder.cs index a399bb6b3..eeb371d1e 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegDecoder.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegDecoder.cs @@ -20,7 +20,7 @@ namespace ImageSharp.Formats Guard.NotNull(image, "image"); Guard.NotNull(stream, "stream"); - using (JpegDecoderCore decoder = new JpegDecoderCore()) + using (JpegDecoderCore decoder = new JpegDecoderCore(options)) { decoder.Decode(image, stream, false); } diff --git a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs index 657787682..19b3df536 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs @@ -37,6 +37,11 @@ namespace ImageSharp.Formats public InputProcessor InputProcessor; #pragma warning restore SA401 + /// + /// The decoder options. + /// + private readonly IDecoderOptions options; + /// /// The App14 marker color-space /// @@ -85,8 +90,10 @@ namespace ImageSharp.Formats /// /// Initializes a new instance of the class. /// - public JpegDecoderCore() + /// The decoder options. + public JpegDecoderCore(IDecoderOptions options) { + this.options = options ?? DecoderOptions.Default; this.HuffmanTrees = HuffmanTree.CreateHuffmanTrees(); this.QuantizationTables = new Block8x8F[MaxTq + 1]; this.Temp = new byte[2 * Block8x8F.ScalarCount]; @@ -958,7 +965,7 @@ namespace ImageSharp.Formats private void ProcessApp1Marker(int remaining, Image image) where TColor : struct, IPixel { - if (remaining < 6) + if (remaining < 6 || this.options.IgnoreMetadata) { this.InputProcessor.Skip(remaining); return; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderCoreTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderCoreTests.cs new file mode 100644 index 000000000..18d9e3acf --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderCoreTests.cs @@ -0,0 +1,44 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using Xunit; + + public class JpegDecoderCoreTests + { + [Fact] + public void Decode_IgnoreMetadataIsFalse_ExifProfileIsRead() + { + var options = new DecoderOptions() + { + IgnoreMetadata = false + }; + + TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); + + using (Image image = new Image(testFile.FilePath, options)) + { + Assert.NotNull(image.MetaData.ExifProfile); + } + } + + [Fact] + public void Decode_IgnoreMetadataIsTrue_ExifProfileIgnored() + { + var options = new DecoderOptions() + { + IgnoreMetadata = true + }; + + TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); + + using (Image image = new Image(testFile.FilePath, options)) + { + Assert.Null(image.MetaData.ExifProfile); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 71ce4e165..b8ec9d49e 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -90,7 +90,7 @@ namespace ImageSharp.Tests ms.Seek(0, SeekOrigin.Begin); Image mirror = provider.Factory.CreateImage(1, 1); - using (JpegDecoderCore decoder = new JpegDecoderCore()) + using (JpegDecoderCore decoder = new JpegDecoderCore(null)) { decoder.Decode(mirror, ms, true); From 966648ed0cbf1bc2f3de9455365659f8c53a2399 Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sat, 18 Feb 2017 21:41:47 +0100 Subject: [PATCH 089/142] Use constructor instead of property to get the default decoder options. --- src/ImageSharp.Formats.Gif/GifDecoderCore.cs | 2 +- .../JpegDecoderCore.cs | 2 +- src/ImageSharp/Formats/DecoderOptions.cs | 28 ++++++++++++++----- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp.Formats.Gif/GifDecoderCore.cs b/src/ImageSharp.Formats.Gif/GifDecoderCore.cs index 50d4da0eb..2a0d53bde 100644 --- a/src/ImageSharp.Formats.Gif/GifDecoderCore.cs +++ b/src/ImageSharp.Formats.Gif/GifDecoderCore.cs @@ -73,7 +73,7 @@ namespace ImageSharp.Formats /// The decoder options. public GifDecoderCore(IDecoderOptions options) { - this.options = options ?? DecoderOptions.Default; + this.options = options ?? new DecoderOptions(); } /// diff --git a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs index 19b3df536..f1b85fa0b 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs @@ -93,7 +93,7 @@ namespace ImageSharp.Formats /// The decoder options. public JpegDecoderCore(IDecoderOptions options) { - this.options = options ?? DecoderOptions.Default; + this.options = options ?? new DecoderOptions(); this.HuffmanTrees = HuffmanTree.CreateHuffmanTrees(); this.QuantizationTables = new Block8x8F[MaxTq + 1]; this.Temp = new byte[2 * Block8x8F.ScalarCount]; diff --git a/src/ImageSharp/Formats/DecoderOptions.cs b/src/ImageSharp/Formats/DecoderOptions.cs index a9bb152f1..4755ddcb6 100644 --- a/src/ImageSharp/Formats/DecoderOptions.cs +++ b/src/ImageSharp/Formats/DecoderOptions.cs @@ -11,22 +11,36 @@ namespace ImageSharp public class DecoderOptions : IDecoderOptions { /// - /// Gets the default decoder options + /// Initializes a new instance of the class. /// - public static DecoderOptions Default + public DecoderOptions() { - get + this.InitializeWithDefaults(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The decoder options + protected DecoderOptions(IDecoderOptions options) + { + if (options == null) { - return new DecoderOptions() - { - IgnoreMetadata = false - }; + this.InitializeWithDefaults(); + return; } + + this.IgnoreMetadata = options.IgnoreMetadata; } /// /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. /// public bool IgnoreMetadata { get; set; } + + private void InitializeWithDefaults() + { + this.IgnoreMetadata = false; + } } } From b27fa62a6af44e76e3d70d305db8356f78a2a45f Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sat, 18 Feb 2017 21:42:11 +0100 Subject: [PATCH 090/142] Implemented the IDecoderOptions inside the png decoder. --- .../IPngDecoderOptions.cs | 20 ++++++ src/ImageSharp.Formats.Png/PngDecoder.cs | 4 +- src/ImageSharp.Formats.Png/PngDecoderCore.cs | 23 ++++++- .../PngDecoderOptions.cs | 62 +++++++++++++++++ .../Formats/Png/PngDecoderCoreTests.cs | 66 +++++++++++++++++++ 5 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 src/ImageSharp.Formats.Png/IPngDecoderOptions.cs create mode 100644 src/ImageSharp.Formats.Png/PngDecoderOptions.cs create mode 100644 tests/ImageSharp.Tests/Formats/Png/PngDecoderCoreTests.cs diff --git a/src/ImageSharp.Formats.Png/IPngDecoderOptions.cs b/src/ImageSharp.Formats.Png/IPngDecoderOptions.cs new file mode 100644 index 000000000..f60f3d1cd --- /dev/null +++ b/src/ImageSharp.Formats.Png/IPngDecoderOptions.cs @@ -0,0 +1,20 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + using System.Text; + + /// + /// Encapsulates the png decoder options. + /// + public interface IPngDecoderOptions : IDecoderOptions + { + /// + /// Gets the encoding that should be used when reading text chunks. + /// + Encoding TextEncoding { get; } + } +} diff --git a/src/ImageSharp.Formats.Png/PngDecoder.cs b/src/ImageSharp.Formats.Png/PngDecoder.cs index 9aab6673a..c731ae448 100644 --- a/src/ImageSharp.Formats.Png/PngDecoder.cs +++ b/src/ImageSharp.Formats.Png/PngDecoder.cs @@ -34,7 +34,9 @@ namespace ImageSharp.Formats public void Decode(Image image, Stream stream, IDecoderOptions options) where TColor : struct, IPixel { - new PngDecoderCore().Decode(image, stream); + IPngDecoderOptions pngOptions = PngDecoderOptions.Create(options); + + new PngDecoderCore(pngOptions).Decode(image, stream); } } } diff --git a/src/ImageSharp.Formats.Png/PngDecoderCore.cs b/src/ImageSharp.Formats.Png/PngDecoderCore.cs index 47b09d5ff..4a5ad3648 100644 --- a/src/ImageSharp.Formats.Png/PngDecoderCore.cs +++ b/src/ImageSharp.Formats.Png/PngDecoderCore.cs @@ -64,6 +64,11 @@ namespace ImageSharp.Formats /// private readonly char[] chars = new char[4]; + /// + /// The decoder options. + /// + private readonly IPngDecoderOptions options; + /// /// Reusable crc for validating chunks. /// @@ -120,6 +125,15 @@ namespace ImageSharp.Formats ColorTypes.Add((int)PngColorType.RgbWithAlpha, new byte[] { 8 }); } + /// + /// Initializes a new instance of the class. + /// + /// The decoder options. + public PngDecoderCore(IPngDecoderOptions options) + { + this.options = options ?? new PngDecoderOptions(); + } + /// /// Gets or sets the png color type /// @@ -763,6 +777,11 @@ namespace ImageSharp.Formats private void ReadTextChunk(Image image, byte[] data, int length) where TColor : struct, IPixel { + if (this.options.IgnoreMetadata) + { + return; + } + int zeroIndex = 0; for (int i = 0; i < length; i++) @@ -774,8 +793,8 @@ namespace ImageSharp.Formats } } - string name = Encoding.Unicode.GetString(data, 0, zeroIndex); - string value = Encoding.Unicode.GetString(data, zeroIndex + 1, length - zeroIndex - 1); + string name = this.options.TextEncoding.GetString(data, 0, zeroIndex); + string value = this.options.TextEncoding.GetString(data, zeroIndex + 1, length - zeroIndex - 1); image.MetaData.Properties.Add(new ImageProperty(name, value)); } diff --git a/src/ImageSharp.Formats.Png/PngDecoderOptions.cs b/src/ImageSharp.Formats.Png/PngDecoderOptions.cs new file mode 100644 index 000000000..07c0c2739 --- /dev/null +++ b/src/ImageSharp.Formats.Png/PngDecoderOptions.cs @@ -0,0 +1,62 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + using System.Text; + + /// + /// Encapsulates the png decoder options. + /// + public sealed class PngDecoderOptions : DecoderOptions, IPngDecoderOptions + { + private static readonly Encoding DefaultEncoding = Encoding.GetEncoding("ASCII"); + + /// + /// Initializes a new instance of the class. + /// + public PngDecoderOptions() + { + this.InitializeWithDefaults(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The options for the decoder. + private PngDecoderOptions(IDecoderOptions options) + : base(options) + { + this.InitializeWithDefaults(); + } + + /// + /// Gets or sets the encoding that should be used when reading text chunks. + /// + public Encoding TextEncoding { get; set; } + + /// + /// Converts the options to a instance with a cast + /// or by creating a new instance with the specfied options. + /// + /// The options for the decoder. + /// The options for the png decoder. + internal static IPngDecoderOptions Create(IDecoderOptions options) + { + IPngDecoderOptions pngOptions = options as IPngDecoderOptions; + if (pngOptions != null) + { + return pngOptions; + } + + return new PngDecoderOptions(options); + } + + private void InitializeWithDefaults() + { + this.TextEncoding = DefaultEncoding; + } + } +} diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderCoreTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderCoreTests.cs new file mode 100644 index 000000000..3ee2ea826 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderCoreTests.cs @@ -0,0 +1,66 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.Text; + using Xunit; + + using ImageSharp.Formats; + + public class PngDecoderCoreTests + { + [Fact] + public void Decode_IgnoreMetadataIsFalse_TextChunckIsRead() + { + var options = new PngDecoderOptions() + { + IgnoreMetadata = false + }; + + TestFile testFile = TestFile.Create(TestImages.Png.Blur); + + using (Image image = new Image(testFile.FilePath, options)) + { + Assert.Equal(1, image.MetaData.Properties.Count); + Assert.Equal("Software", image.MetaData.Properties[0].Name); + Assert.Equal("paint.net 4.0.6", image.MetaData.Properties[0].Value); + } + } + + [Fact] + public void Decode_IgnoreMetadataIsTrue_TextChunksAreIgnored() + { + var options = new PngDecoderOptions() + { + IgnoreMetadata = true + }; + + TestFile testFile = TestFile.Create(TestImages.Png.Blur); + + using (Image image = new Image(testFile.FilePath, options)) + { + Assert.Equal(0, image.MetaData.Properties.Count); + } + } + + [Fact] + public void Decode_TextEncodingSetToUnicode_TextIsReadWithCorrectEncoding() + { + var options = new PngDecoderOptions() + { + TextEncoding = Encoding.Unicode + }; + + TestFile testFile = TestFile.Create(TestImages.Png.Blur); + + using (Image image = new Image(testFile.FilePath, options)) + { + Assert.Equal(1, image.MetaData.Properties.Count); + Assert.Equal("潓瑦慷敲", image.MetaData.Properties[0].Name); + } + } + } +} \ No newline at end of file From 6878997af77f4b9406ad7f0c4d9c429df494f7db Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sat, 18 Feb 2017 21:56:56 +0100 Subject: [PATCH 091/142] Also allow specification of the text encoding inside the gif decoder. --- src/ImageSharp.Formats.Gif/GifDecoder.cs | 4 +- src/ImageSharp.Formats.Gif/GifDecoderCore.cs | 8 +-- .../GifDecoderOptions.cs | 62 +++++++++++++++++++ .../IGifDecoderOptions.cs | 20 ++++++ .../Formats/Gif/GifDecoderCoreTests.cs | 20 ++++++ 5 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 src/ImageSharp.Formats.Gif/GifDecoderOptions.cs create mode 100644 src/ImageSharp.Formats.Gif/IGifDecoderOptions.cs diff --git a/src/ImageSharp.Formats.Gif/GifDecoder.cs b/src/ImageSharp.Formats.Gif/GifDecoder.cs index 42dbf4fc7..ce97689e9 100644 --- a/src/ImageSharp.Formats.Gif/GifDecoder.cs +++ b/src/ImageSharp.Formats.Gif/GifDecoder.cs @@ -17,7 +17,9 @@ namespace ImageSharp.Formats public void Decode(Image image, Stream stream, IDecoderOptions options) where TColor : struct, IPixel { - new GifDecoderCore(options).Decode(image, stream); + IGifDecoderOptions gifOptions = GifDecoderOptions.Create(options); + + new GifDecoderCore(gifOptions).Decode(image, stream); } } } diff --git a/src/ImageSharp.Formats.Gif/GifDecoderCore.cs b/src/ImageSharp.Formats.Gif/GifDecoderCore.cs index 2a0d53bde..3ab0e6ca8 100644 --- a/src/ImageSharp.Formats.Gif/GifDecoderCore.cs +++ b/src/ImageSharp.Formats.Gif/GifDecoderCore.cs @@ -25,7 +25,7 @@ namespace ImageSharp.Formats /// /// The decoder options. /// - private readonly IDecoderOptions options; + private readonly IGifDecoderOptions options; /// /// The image to decode the information to. @@ -71,9 +71,9 @@ namespace ImageSharp.Formats /// Initializes a new instance of the class. /// /// The decoder options. - public GifDecoderCore(IDecoderOptions options) + public GifDecoderCore(IGifDecoderOptions options) { - this.options = options ?? new DecoderOptions(); + this.options = options ?? new GifDecoderOptions(); } /// @@ -260,7 +260,7 @@ namespace ImageSharp.Formats try { this.currentStream.Read(commentsBuffer, 0, length); - string comments = Encoding.GetEncoding("ASCII").GetString(commentsBuffer, 0, length); + string comments = this.options.TextEncoding.GetString(commentsBuffer, 0, length); this.decodedImage.MetaData.Properties.Add(new ImageProperty("Comments", comments)); } finally diff --git a/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs b/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs new file mode 100644 index 000000000..8bff3e38d --- /dev/null +++ b/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs @@ -0,0 +1,62 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + using System.Text; + + /// + /// Encapsulates the gif decoder options. + /// + public sealed class GifDecoderOptions : DecoderOptions, IGifDecoderOptions + { + private static readonly Encoding DefaultEncoding = Encoding.GetEncoding("ASCII"); + + /// + /// Initializes a new instance of the class. + /// + public GifDecoderOptions() + { + this.InitializeWithDefaults(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The options for the decoder. + private GifDecoderOptions(IDecoderOptions options) + : base(options) + { + this.InitializeWithDefaults(); + } + + /// + /// Gets the encoding that should be used when reading comments. + /// + public Encoding TextEncoding { get; set; } + + /// + /// Converts the options to a instance with a cast + /// or by creating a new instance with the specfied options. + /// + /// The options for the decoder. + /// The options for the png decoder. + internal static IGifDecoderOptions Create(IDecoderOptions options) + { + IGifDecoderOptions gifOptions = options as IGifDecoderOptions; + if (gifOptions != null) + { + return gifOptions; + } + + return new GifDecoderOptions(options); + } + + private void InitializeWithDefaults() + { + this.TextEncoding = DefaultEncoding; + } + } +} diff --git a/src/ImageSharp.Formats.Gif/IGifDecoderOptions.cs b/src/ImageSharp.Formats.Gif/IGifDecoderOptions.cs new file mode 100644 index 000000000..dd94a616f --- /dev/null +++ b/src/ImageSharp.Formats.Gif/IGifDecoderOptions.cs @@ -0,0 +1,20 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + using System.Text; + + /// + /// Encapsulates the gif decoder options. + /// + public interface IGifDecoderOptions : IDecoderOptions + { + /// + /// Gets the encoding that should be used when reading comments. + /// + Encoding TextEncoding { get; } + } +} diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderCoreTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderCoreTests.cs index 758c72400..3e3010cfc 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderCoreTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderCoreTests.cs @@ -5,8 +5,11 @@ namespace ImageSharp.Tests { + using System.Text; using Xunit; + using ImageSharp.Formats; + public class GifDecoderCoreTests { [Fact] @@ -42,5 +45,22 @@ namespace ImageSharp.Tests Assert.Equal(0, image.MetaData.Properties.Count); } } + + [Fact] + public void Decode_TextEncodingSetToUnicode_TextIsReadWithCorrectEncoding() + { + var options = new GifDecoderOptions() + { + TextEncoding = Encoding.Unicode + }; + + TestFile testFile = TestFile.Create(TestImages.Gif.Rings); + + using (Image image = new Image(testFile.FilePath, options)) + { + Assert.Equal(1, image.MetaData.Properties.Count); + Assert.Equal("浉条卥慨灲", image.MetaData.Properties[0].Value); + } + } } } From 839ca54291a2fc77510b42551b9de8005753a83d Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sun, 19 Feb 2017 09:52:01 +0100 Subject: [PATCH 092/142] StyleCop fix. --- src/ImageSharp.Formats.Gif/GifDecoderOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs b/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs index 8bff3e38d..e2506db70 100644 --- a/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs +++ b/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs @@ -33,7 +33,7 @@ namespace ImageSharp.Formats } /// - /// Gets the encoding that should be used when reading comments. + /// Gets or sets the encoding that should be used when reading comments. /// public Encoding TextEncoding { get; set; } From 68ac2112691f2a6c2f663aac4984e805ed0a8626 Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sun, 19 Feb 2017 14:51:52 +0100 Subject: [PATCH 093/142] Added options for the encoder. --- src/ImageSharp.Formats.Bmp/BmpEncoder.cs | 2 +- src/ImageSharp.Formats.Gif/GifEncoder.cs | 2 +- src/ImageSharp.Formats.Jpeg/JpegEncoder.cs | 2 +- src/ImageSharp.Formats.Png/PngEncoder.cs | 2 +- src/ImageSharp/Formats/EncoderOptions.cs | 46 ++++++++++++++++++++++ src/ImageSharp/Formats/IEncoderOptions.cs | 18 +++++++++ src/ImageSharp/Formats/IImageEncoder.cs | 3 +- src/ImageSharp/Image/Image{TColor}.cs | 16 ++++---- 8 files changed, 79 insertions(+), 12 deletions(-) create mode 100644 src/ImageSharp/Formats/EncoderOptions.cs create mode 100644 src/ImageSharp/Formats/IEncoderOptions.cs diff --git a/src/ImageSharp.Formats.Bmp/BmpEncoder.cs b/src/ImageSharp.Formats.Bmp/BmpEncoder.cs index 6edaf178b..ea1342272 100644 --- a/src/ImageSharp.Formats.Bmp/BmpEncoder.cs +++ b/src/ImageSharp.Formats.Bmp/BmpEncoder.cs @@ -20,7 +20,7 @@ namespace ImageSharp.Formats public BmpBitsPerPixel BitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel24; /// - public void Encode(Image image, Stream stream) + public void Encode(Image image, Stream stream, IEncoderOptions options) where TColor : struct, IPixel { BmpEncoderCore encoder = new BmpEncoderCore(); diff --git a/src/ImageSharp.Formats.Gif/GifEncoder.cs b/src/ImageSharp.Formats.Gif/GifEncoder.cs index de7e03322..402d2d09a 100644 --- a/src/ImageSharp.Formats.Gif/GifEncoder.cs +++ b/src/ImageSharp.Formats.Gif/GifEncoder.cs @@ -32,7 +32,7 @@ namespace ImageSharp.Formats public IQuantizer Quantizer { get; set; } /// - public void Encode(Image image, Stream stream) + public void Encode(Image image, Stream stream, IEncoderOptions options) where TColor : struct, IPixel { GifEncoderCore encoder = new GifEncoderCore diff --git a/src/ImageSharp.Formats.Jpeg/JpegEncoder.cs b/src/ImageSharp.Formats.Jpeg/JpegEncoder.cs index 07d9b24cd..46972cd93 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegEncoder.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegEncoder.cs @@ -61,7 +61,7 @@ namespace ImageSharp.Formats } /// - public void Encode(Image image, Stream stream) + public void Encode(Image image, Stream stream, IEncoderOptions options) where TColor : struct, IPixel { // Ensure that quality can be set but has a fallback. diff --git a/src/ImageSharp.Formats.Png/PngEncoder.cs b/src/ImageSharp.Formats.Png/PngEncoder.cs index ba790f4ae..8d9c4bc08 100644 --- a/src/ImageSharp.Formats.Png/PngEncoder.cs +++ b/src/ImageSharp.Formats.Png/PngEncoder.cs @@ -56,7 +56,7 @@ namespace ImageSharp.Formats public bool WriteGamma { get; set; } /// - public void Encode(Image image, Stream stream) + public void Encode(Image image, Stream stream, IEncoderOptions options) where TColor : struct, IPixel { PngEncoderCore encoder = new PngEncoderCore diff --git a/src/ImageSharp/Formats/EncoderOptions.cs b/src/ImageSharp/Formats/EncoderOptions.cs new file mode 100644 index 000000000..1f92927f1 --- /dev/null +++ b/src/ImageSharp/Formats/EncoderOptions.cs @@ -0,0 +1,46 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + /// + /// Encapsulates the shared encoder options. + /// + public class EncoderOptions : IEncoderOptions + { + /// + /// Initializes a new instance of the class. + /// + public EncoderOptions() + { + this.InitializeWithDefaults(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The encoder options + protected EncoderOptions(IEncoderOptions options) + { + if (options == null) + { + this.InitializeWithDefaults(); + return; + } + + this.IgnoreMetadata = options.IgnoreMetadata; + } + + /// + /// Gets or sets a value indicating whether the metadata should be ignored when the image is being encoded. + /// + public bool IgnoreMetadata { get; set; } + + private void InitializeWithDefaults() + { + this.IgnoreMetadata = false; + } + } +} diff --git a/src/ImageSharp/Formats/IEncoderOptions.cs b/src/ImageSharp/Formats/IEncoderOptions.cs new file mode 100644 index 000000000..0fd3d1c43 --- /dev/null +++ b/src/ImageSharp/Formats/IEncoderOptions.cs @@ -0,0 +1,18 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + /// + /// Encapsulates the shared encoder options. + /// + public interface IEncoderOptions + { + /// + /// Gets a value indicating whether the metadata should be ignored when the image is being encoded. + /// + bool IgnoreMetadata { get; } + } +} diff --git a/src/ImageSharp/Formats/IImageEncoder.cs b/src/ImageSharp/Formats/IImageEncoder.cs index 0ba56477a..918f0d273 100644 --- a/src/ImageSharp/Formats/IImageEncoder.cs +++ b/src/ImageSharp/Formats/IImageEncoder.cs @@ -19,7 +19,8 @@ namespace ImageSharp.Formats /// The pixel format. /// The to encode from. /// The to encode the image data to. - void Encode(Image image, Stream stream) + /// The options for the encoder. + void Encode(Image image, Stream stream, IEncoderOptions options) where TColor : struct, IPixel; } } diff --git a/src/ImageSharp/Image/Image{TColor}.cs b/src/ImageSharp/Image/Image{TColor}.cs index 9e4ecba2f..d8890f5fa 100644 --- a/src/ImageSharp/Image/Image{TColor}.cs +++ b/src/ImageSharp/Image/Image{TColor}.cs @@ -206,12 +206,13 @@ namespace ImageSharp /// Saves the image to the given stream using the currently loaded image format. /// /// The stream to save the image to. + /// The options for the encoder. /// Thrown if the stream is null. /// The - public Image Save(Stream stream) + public Image Save(Stream stream, IEncoderOptions options = null) { Guard.NotNull(stream, nameof(stream)); - this.CurrentImageFormat.Encoder.Encode(this, stream); + this.CurrentImageFormat.Encoder.Encode(this, stream, options); return this; } @@ -220,13 +221,13 @@ namespace ImageSharp /// /// The stream to save the image to. /// The format to save the image as. - /// Thrown if the stream or format is null. + /// The options for the encoder. /// The - public Image Save(Stream stream, IImageFormat format) + public Image Save(Stream stream, IImageFormat format, IEncoderOptions options = null) { Guard.NotNull(stream, nameof(stream)); Guard.NotNull(format, nameof(format)); - format.Encoder.Encode(this, stream); + format.Encoder.Encode(this, stream, options); return this; } @@ -235,15 +236,16 @@ namespace ImageSharp /// /// The stream to save the image to. /// The encoder to save the image with. + /// The options for the encoder. /// Thrown if the stream or encoder is null. /// /// The . /// - public Image Save(Stream stream, IImageEncoder encoder) + public Image Save(Stream stream, IImageEncoder encoder, IEncoderOptions options = null) { Guard.NotNull(stream, nameof(stream)); Guard.NotNull(encoder, nameof(encoder)); - encoder.Encode(this, stream); + encoder.Encode(this, stream, options); // Reset to the start of the stream. if (stream.CanSeek) From 9ac270c6d64be2df9e472f6fd449003997efc5bc Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sun, 19 Feb 2017 14:55:42 +0100 Subject: [PATCH 094/142] Added overloads to the decoder that use the coder specific options. --- src/ImageSharp.Formats.Gif/GifDecoder.cs | 15 ++++++++++++++- src/ImageSharp.Formats.Png/PngDecoder.cs | 15 ++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp.Formats.Gif/GifDecoder.cs b/src/ImageSharp.Formats.Gif/GifDecoder.cs index ce97689e9..88a3d5e43 100644 --- a/src/ImageSharp.Formats.Gif/GifDecoder.cs +++ b/src/ImageSharp.Formats.Gif/GifDecoder.cs @@ -19,7 +19,20 @@ namespace ImageSharp.Formats { IGifDecoderOptions gifOptions = GifDecoderOptions.Create(options); - new GifDecoderCore(gifOptions).Decode(image, stream); + this.Decode(image, stream, gifOptions); + } + + /// + /// Decodes the image from the specified stream to the . + /// + /// The pixel format. + /// The to decode to. + /// The containing image data. + /// The options for the decoder. + public void Decode(Image image, Stream stream, IGifDecoderOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + new GifDecoderCore(options).Decode(image, stream); } } } diff --git a/src/ImageSharp.Formats.Png/PngDecoder.cs b/src/ImageSharp.Formats.Png/PngDecoder.cs index c731ae448..658de9e66 100644 --- a/src/ImageSharp.Formats.Png/PngDecoder.cs +++ b/src/ImageSharp.Formats.Png/PngDecoder.cs @@ -36,7 +36,20 @@ namespace ImageSharp.Formats { IPngDecoderOptions pngOptions = PngDecoderOptions.Create(options); - new PngDecoderCore(pngOptions).Decode(image, stream); + this.Decode(image, stream, pngOptions); + } + + /// + /// Decodes the image from the specified stream to the . + /// + /// The pixel format. + /// The to decode to. + /// The containing image data. + /// The options for the decoder. + public void Decode(Image image, Stream stream, IPngDecoderOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + new PngDecoderCore(options).Decode(image, stream); } } } From 01d2f42cdd823225eca69658825a1f278392f55c Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sun, 19 Feb 2017 15:01:44 +0100 Subject: [PATCH 095/142] Nicer initialization of the options. --- src/ImageSharp.Formats.Gif/GifDecoderOptions.cs | 9 +-------- src/ImageSharp.Formats.Png/PngDecoderOptions.cs | 9 +-------- src/ImageSharp/Formats/DecoderOptions.cs | 15 +++------------ src/ImageSharp/Formats/EncoderOptions.cs | 15 +++------------ 4 files changed, 8 insertions(+), 40 deletions(-) diff --git a/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs b/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs index e2506db70..ff8ad19df 100644 --- a/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs +++ b/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs @@ -19,7 +19,6 @@ namespace ImageSharp.Formats /// public GifDecoderOptions() { - this.InitializeWithDefaults(); } /// @@ -29,13 +28,12 @@ namespace ImageSharp.Formats private GifDecoderOptions(IDecoderOptions options) : base(options) { - this.InitializeWithDefaults(); } /// /// Gets or sets the encoding that should be used when reading comments. /// - public Encoding TextEncoding { get; set; } + public Encoding TextEncoding { get; set; } = DefaultEncoding; /// /// Converts the options to a instance with a cast @@ -53,10 +51,5 @@ namespace ImageSharp.Formats return new GifDecoderOptions(options); } - - private void InitializeWithDefaults() - { - this.TextEncoding = DefaultEncoding; - } } } diff --git a/src/ImageSharp.Formats.Png/PngDecoderOptions.cs b/src/ImageSharp.Formats.Png/PngDecoderOptions.cs index 07c0c2739..8630cd1b0 100644 --- a/src/ImageSharp.Formats.Png/PngDecoderOptions.cs +++ b/src/ImageSharp.Formats.Png/PngDecoderOptions.cs @@ -19,7 +19,6 @@ namespace ImageSharp.Formats /// public PngDecoderOptions() { - this.InitializeWithDefaults(); } /// @@ -29,13 +28,12 @@ namespace ImageSharp.Formats private PngDecoderOptions(IDecoderOptions options) : base(options) { - this.InitializeWithDefaults(); } /// /// Gets or sets the encoding that should be used when reading text chunks. /// - public Encoding TextEncoding { get; set; } + public Encoding TextEncoding { get; set; } = DefaultEncoding; /// /// Converts the options to a instance with a cast @@ -53,10 +51,5 @@ namespace ImageSharp.Formats return new PngDecoderOptions(options); } - - private void InitializeWithDefaults() - { - this.TextEncoding = DefaultEncoding; - } } } diff --git a/src/ImageSharp/Formats/DecoderOptions.cs b/src/ImageSharp/Formats/DecoderOptions.cs index 4755ddcb6..5257b07b3 100644 --- a/src/ImageSharp/Formats/DecoderOptions.cs +++ b/src/ImageSharp/Formats/DecoderOptions.cs @@ -15,7 +15,6 @@ namespace ImageSharp /// public DecoderOptions() { - this.InitializeWithDefaults(); } /// @@ -24,23 +23,15 @@ namespace ImageSharp /// The decoder options protected DecoderOptions(IDecoderOptions options) { - if (options == null) + if (options != null) { - this.InitializeWithDefaults(); - return; + this.IgnoreMetadata = options.IgnoreMetadata; } - - this.IgnoreMetadata = options.IgnoreMetadata; } /// /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded. /// - public bool IgnoreMetadata { get; set; } - - private void InitializeWithDefaults() - { - this.IgnoreMetadata = false; - } + public bool IgnoreMetadata { get; set; } = false; } } diff --git a/src/ImageSharp/Formats/EncoderOptions.cs b/src/ImageSharp/Formats/EncoderOptions.cs index 1f92927f1..27a7e9781 100644 --- a/src/ImageSharp/Formats/EncoderOptions.cs +++ b/src/ImageSharp/Formats/EncoderOptions.cs @@ -15,7 +15,6 @@ namespace ImageSharp /// public EncoderOptions() { - this.InitializeWithDefaults(); } /// @@ -24,23 +23,15 @@ namespace ImageSharp /// The encoder options protected EncoderOptions(IEncoderOptions options) { - if (options == null) + if (options != null) { - this.InitializeWithDefaults(); - return; + this.IgnoreMetadata = options.IgnoreMetadata; } - - this.IgnoreMetadata = options.IgnoreMetadata; } /// /// Gets or sets a value indicating whether the metadata should be ignored when the image is being encoded. /// - public bool IgnoreMetadata { get; set; } - - private void InitializeWithDefaults() - { - this.IgnoreMetadata = false; - } + public bool IgnoreMetadata { get; set; } = false; } } From df963df9cbb73798e5cb05c2e3b4c24cef71e20a Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sun, 19 Feb 2017 15:08:05 +0100 Subject: [PATCH 096/142] Minor comment change. --- src/ImageSharp.Formats.Gif/GifDecoderOptions.cs | 4 ++-- src/ImageSharp.Formats.Gif/IGifDecoderOptions.cs | 2 +- src/ImageSharp.Formats.Png/IPngDecoderOptions.cs | 2 +- src/ImageSharp.Formats.Png/PngDecoderOptions.cs | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs b/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs index ff8ad19df..8184c19d3 100644 --- a/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs +++ b/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs @@ -8,7 +8,7 @@ namespace ImageSharp.Formats using System.Text; /// - /// Encapsulates the gif decoder options. + /// Encapsulates the options for the . /// public sealed class GifDecoderOptions : DecoderOptions, IGifDecoderOptions { @@ -40,7 +40,7 @@ namespace ImageSharp.Formats /// or by creating a new instance with the specfied options. /// /// The options for the decoder. - /// The options for the png decoder. + /// The options for the . internal static IGifDecoderOptions Create(IDecoderOptions options) { IGifDecoderOptions gifOptions = options as IGifDecoderOptions; diff --git a/src/ImageSharp.Formats.Gif/IGifDecoderOptions.cs b/src/ImageSharp.Formats.Gif/IGifDecoderOptions.cs index dd94a616f..729bf1d11 100644 --- a/src/ImageSharp.Formats.Gif/IGifDecoderOptions.cs +++ b/src/ImageSharp.Formats.Gif/IGifDecoderOptions.cs @@ -8,7 +8,7 @@ namespace ImageSharp.Formats using System.Text; /// - /// Encapsulates the gif decoder options. + /// Encapsulates the options for the . /// public interface IGifDecoderOptions : IDecoderOptions { diff --git a/src/ImageSharp.Formats.Png/IPngDecoderOptions.cs b/src/ImageSharp.Formats.Png/IPngDecoderOptions.cs index f60f3d1cd..cc6d194bf 100644 --- a/src/ImageSharp.Formats.Png/IPngDecoderOptions.cs +++ b/src/ImageSharp.Formats.Png/IPngDecoderOptions.cs @@ -8,7 +8,7 @@ namespace ImageSharp.Formats using System.Text; /// - /// Encapsulates the png decoder options. + /// Encapsulates the options for the . /// public interface IPngDecoderOptions : IDecoderOptions { diff --git a/src/ImageSharp.Formats.Png/PngDecoderOptions.cs b/src/ImageSharp.Formats.Png/PngDecoderOptions.cs index 8630cd1b0..83716e5d1 100644 --- a/src/ImageSharp.Formats.Png/PngDecoderOptions.cs +++ b/src/ImageSharp.Formats.Png/PngDecoderOptions.cs @@ -8,7 +8,7 @@ namespace ImageSharp.Formats using System.Text; /// - /// Encapsulates the png decoder options. + /// Encapsulates the options for the . /// public sealed class PngDecoderOptions : DecoderOptions, IPngDecoderOptions { @@ -40,7 +40,7 @@ namespace ImageSharp.Formats /// or by creating a new instance with the specfied options. /// /// The options for the decoder. - /// The options for the png decoder. + /// The options for the . internal static IPngDecoderOptions Create(IDecoderOptions options) { IPngDecoderOptions pngOptions = options as IPngDecoderOptions; From c56ab2fdab2fe9924f00fd93dece182766f60f3c Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sun, 19 Feb 2017 15:32:16 +0100 Subject: [PATCH 097/142] Added extra overload for TestFile.CreateImage and used it in the tests. --- .../Formats/Gif/GifDecoderCoreTests.cs | 6 +++--- .../Formats/Jpg/JpegDecoderCoreTests.cs | 4 ++-- .../Formats/Png/PngDecoderCoreTests.cs | 6 +++--- tests/ImageSharp.Tests/TestFile.cs | 12 ++++++++++++ 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderCoreTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderCoreTests.cs index 3e3010cfc..e1e3e94b0 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderCoreTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderCoreTests.cs @@ -22,7 +22,7 @@ namespace ImageSharp.Tests TestFile testFile = TestFile.Create(TestImages.Gif.Rings); - using (Image image = new Image(testFile.FilePath, options)) + using (Image image = testFile.CreateImage(options)) { Assert.Equal(1, image.MetaData.Properties.Count); Assert.Equal("Comments", image.MetaData.Properties[0].Name); @@ -40,7 +40,7 @@ namespace ImageSharp.Tests TestFile testFile = TestFile.Create(TestImages.Gif.Rings); - using (Image image = new Image(testFile.FilePath, options)) + using (Image image = testFile.CreateImage(options)) { Assert.Equal(0, image.MetaData.Properties.Count); } @@ -56,7 +56,7 @@ namespace ImageSharp.Tests TestFile testFile = TestFile.Create(TestImages.Gif.Rings); - using (Image image = new Image(testFile.FilePath, options)) + using (Image image = testFile.CreateImage(options)) { Assert.Equal(1, image.MetaData.Properties.Count); Assert.Equal("浉条卥慨灲", image.MetaData.Properties[0].Value); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderCoreTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderCoreTests.cs index 18d9e3acf..38d2a455e 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderCoreTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderCoreTests.cs @@ -19,7 +19,7 @@ namespace ImageSharp.Tests TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); - using (Image image = new Image(testFile.FilePath, options)) + using (Image image = testFile.CreateImage(options)) { Assert.NotNull(image.MetaData.ExifProfile); } @@ -35,7 +35,7 @@ namespace ImageSharp.Tests TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); - using (Image image = new Image(testFile.FilePath, options)) + using (Image image = testFile.CreateImage(options)) { Assert.Null(image.MetaData.ExifProfile); } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderCoreTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderCoreTests.cs index 3ee2ea826..766172ec6 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderCoreTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderCoreTests.cs @@ -22,7 +22,7 @@ namespace ImageSharp.Tests TestFile testFile = TestFile.Create(TestImages.Png.Blur); - using (Image image = new Image(testFile.FilePath, options)) + using (Image image = testFile.CreateImage(options)) { Assert.Equal(1, image.MetaData.Properties.Count); Assert.Equal("Software", image.MetaData.Properties[0].Name); @@ -40,7 +40,7 @@ namespace ImageSharp.Tests TestFile testFile = TestFile.Create(TestImages.Png.Blur); - using (Image image = new Image(testFile.FilePath, options)) + using (Image image = testFile.CreateImage(options)) { Assert.Equal(0, image.MetaData.Properties.Count); } @@ -56,7 +56,7 @@ namespace ImageSharp.Tests TestFile testFile = TestFile.Create(TestImages.Png.Blur); - using (Image image = new Image(testFile.FilePath, options)) + using (Image image = testFile.CreateImage(options)) { Assert.Equal(1, image.MetaData.Properties.Count); Assert.Equal("潓瑦慷敲", image.MetaData.Properties[0].Name); diff --git a/tests/ImageSharp.Tests/TestFile.cs b/tests/ImageSharp.Tests/TestFile.cs index 891a45cec..0c9cc5f47 100644 --- a/tests/ImageSharp.Tests/TestFile.cs +++ b/tests/ImageSharp.Tests/TestFile.cs @@ -130,6 +130,18 @@ namespace ImageSharp.Tests return new Image(this.image); } + /// + /// Creates a new image. + /// + /// The options for the decoder. + /// + /// The . + /// + public Image CreateImage(IDecoderOptions options) + { + return new Image(this.Bytes, options); + } + /// /// Gets the correct path to the formats directory. /// From 0ace64794a001cd97b4ab4100e14f9a152c7b5cb Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sun, 19 Feb 2017 19:02:21 +0100 Subject: [PATCH 098/142] Added options argument to the filePath overloads. --- src/ImageSharp/Image/Image{TColor}.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Image/Image{TColor}.cs b/src/ImageSharp/Image/Image{TColor}.cs index d8890f5fa..69b99ce13 100644 --- a/src/ImageSharp/Image/Image{TColor}.cs +++ b/src/ImageSharp/Image/Image{TColor}.cs @@ -261,9 +261,10 @@ namespace ImageSharp /// Saves the image to the given stream using the currently loaded image format. /// /// The file path to save the image to. + /// The options for the encoder. /// Thrown if the stream is null. /// The - public Image Save(string filePath) + public Image Save(string filePath, IEncoderOptions options = null) { string ext = Path.GetExtension(filePath).Trim('.'); IImageFormat format = this.Configuration.ImageFormats.SingleOrDefault(f => f.SupportedExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase)); @@ -280,9 +281,10 @@ namespace ImageSharp /// /// The file path to save the image to. /// The format to save the image as. + /// The options for the encoder. /// Thrown if the format is null. /// The - public Image Save(string filePath, IImageFormat format) + public Image Save(string filePath, IImageFormat format, IEncoderOptions options = null) { Guard.NotNull(format, nameof(format)); using (FileStream fs = File.Create(filePath)) @@ -296,9 +298,10 @@ namespace ImageSharp /// /// The file path to save the image to. /// The encoder to save the image with. + /// The options for the encoder. /// Thrown if the encoder is null. /// The - public Image Save(string filePath, IImageEncoder encoder) + public Image Save(string filePath, IImageEncoder encoder, IEncoderOptions options = null) { Guard.NotNull(encoder, nameof(encoder)); using (FileStream fs = File.Create(filePath)) From 26aec54e05a57f4b204fefb1972c3b3eda97ab48 Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sun, 19 Feb 2017 19:02:55 +0100 Subject: [PATCH 099/142] Implemented the IEncoderOptions inside the bmp encoder. --- src/ImageSharp.Formats.Bmp/BmpEncoder.cs | 24 ++++++--- src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs | 24 +++++---- .../BmpEncoderOptions.cs | 51 +++++++++++++++++++ .../IBmpEncoderOptions.cs | 18 +++++++ .../Formats/Bmp/BitmapTests.cs | 6 +-- 5 files changed, 103 insertions(+), 20 deletions(-) create mode 100644 src/ImageSharp.Formats.Bmp/BmpEncoderOptions.cs create mode 100644 src/ImageSharp.Formats.Bmp/IBmpEncoderOptions.cs diff --git a/src/ImageSharp.Formats.Bmp/BmpEncoder.cs b/src/ImageSharp.Formats.Bmp/BmpEncoder.cs index ea1342272..2c0bb5b5d 100644 --- a/src/ImageSharp.Formats.Bmp/BmpEncoder.cs +++ b/src/ImageSharp.Formats.Bmp/BmpEncoder.cs @@ -14,17 +14,27 @@ namespace ImageSharp.Formats /// The encoder can currently only write 24-bit rgb images to streams. public class BmpEncoder : IImageEncoder { - /// - /// Gets or sets the number of bits per pixel. - /// - public BmpBitsPerPixel BitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel24; - /// public void Encode(Image image, Stream stream, IEncoderOptions options) where TColor : struct, IPixel { - BmpEncoderCore encoder = new BmpEncoderCore(); - encoder.Encode(image, stream, this.BitsPerPixel); + IBmpEncoderOptions bmpOptions = BmpEncoderOptions.Create(options); + + this.Encode(image, stream, bmpOptions); + } + + /// + /// Encodes the image to the specified stream from the . + /// + /// The pixel format. + /// The to encode from. + /// The to encode the image data to. + /// The options for the encoder. + public void Encode(Image image, Stream stream, IBmpEncoderOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + BmpEncoderCore encoder = new BmpEncoderCore(options); + encoder.Encode(image, stream); } } } diff --git a/src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs b/src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs index 02d270a0a..9696eb66d 100644 --- a/src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs @@ -16,34 +16,40 @@ namespace ImageSharp.Formats internal sealed class BmpEncoderCore { /// - /// The number of bits per pixel. + /// The options for the encoder. /// - private BmpBitsPerPixel bmpBitsPerPixel; + private IBmpEncoderOptions options; /// /// The amount to pad each row by. /// private int padding; + /// + /// Initializes a new instance of the class. + /// + /// The options for the encoder. + public BmpEncoderCore(IBmpEncoderOptions options) + { + this.options = options ?? new BmpEncoderOptions(); + } + /// /// Encodes the image to the specified stream from the . /// /// The pixel format. /// The to encode from. /// The to encode the image data to. - /// The - public void Encode(ImageBase image, Stream stream, BmpBitsPerPixel bitsPerPixel) + public void Encode(ImageBase image, Stream stream) where TColor : struct, IPixel { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); - this.bmpBitsPerPixel = bitsPerPixel; - // Cast to int will get the bytes per pixel - short bpp = (short)(8 * (int)bitsPerPixel); + short bpp = (short)(8 * (int)this.options.BitsPerPixel); int bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32); - this.padding = bytesPerLine - (image.Width * (int)bitsPerPixel); + this.padding = bytesPerLine - (image.Width * (int)this.options.BitsPerPixel); // Do not use IDisposable pattern here as we want to preserve the stream. EndianBinaryWriter writer = new EndianBinaryWriter(Endianness.LittleEndian, stream); @@ -128,7 +134,7 @@ namespace ImageSharp.Formats { using (PixelAccessor pixels = image.Lock()) { - switch (this.bmpBitsPerPixel) + switch (this.options.BitsPerPixel) { case BmpBitsPerPixel.Pixel32: this.Write32Bit(writer, pixels); diff --git a/src/ImageSharp.Formats.Bmp/BmpEncoderOptions.cs b/src/ImageSharp.Formats.Bmp/BmpEncoderOptions.cs new file mode 100644 index 000000000..f0106b481 --- /dev/null +++ b/src/ImageSharp.Formats.Bmp/BmpEncoderOptions.cs @@ -0,0 +1,51 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Encapsulates the options for the . + /// + public sealed class BmpEncoderOptions : EncoderOptions, IBmpEncoderOptions + { + /// + /// Initializes a new instance of the class. + /// + public BmpEncoderOptions() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The options for the encoder. + private BmpEncoderOptions(IEncoderOptions options) + : base(options) + { + } + + /// + /// Gets or sets the number of bits per pixel. + /// + public BmpBitsPerPixel BitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel24; + + /// + /// Converts the options to a instance with a cast + /// or by creating a new instance with the specfied options. + /// + /// The options for the encoder. + /// The options for the . + internal static IBmpEncoderOptions Create(IEncoderOptions options) + { + IBmpEncoderOptions bmpOptions = options as IBmpEncoderOptions; + if (bmpOptions != null) + { + return bmpOptions; + } + + return new BmpEncoderOptions(options); + } + } +} diff --git a/src/ImageSharp.Formats.Bmp/IBmpEncoderOptions.cs b/src/ImageSharp.Formats.Bmp/IBmpEncoderOptions.cs new file mode 100644 index 000000000..6cf37cbae --- /dev/null +++ b/src/ImageSharp.Formats.Bmp/IBmpEncoderOptions.cs @@ -0,0 +1,18 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Encapsulates the options for the . + /// + public interface IBmpEncoderOptions : IEncoderOptions + { + /// + /// Gets the number of bits per pixel. + /// + BmpBitsPerPixel BitsPerPixel { get; } + } +} diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BitmapTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BitmapTests.cs index c1275335d..7579cc86e 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BitmapTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BitmapTests.cs @@ -7,8 +7,6 @@ using ImageSharp.Formats; namespace ImageSharp.Tests { - using System.IO; - using Xunit; public class BitmapTests : FileTestBase @@ -16,7 +14,7 @@ namespace ImageSharp.Tests public static readonly TheoryData BitsPerPixel = new TheoryData { - BmpBitsPerPixel.Pixel24 , + BmpBitsPerPixel.Pixel24, BmpBitsPerPixel.Pixel32 }; @@ -31,7 +29,7 @@ namespace ImageSharp.Tests string filename = file.GetFileNameWithoutExtension(bitsPerPixel); using (Image image = file.CreateImage()) { - image.Save($"{path}/{filename}.bmp", new BmpEncoder { BitsPerPixel = bitsPerPixel }); + image.Save($"{path}/{filename}.bmp", new BmpEncoderOptions { BitsPerPixel = bitsPerPixel }); } } } From 20a295afeca79cb83a0046f15a7a24957e9eaeef Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sun, 19 Feb 2017 19:11:05 +0100 Subject: [PATCH 100/142] Comment fixes. --- src/ImageSharp.Formats.Bmp/BmpEncoderOptions.cs | 2 +- src/ImageSharp.Formats.Gif/GifDecoderOptions.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp.Formats.Bmp/BmpEncoderOptions.cs b/src/ImageSharp.Formats.Bmp/BmpEncoderOptions.cs index f0106b481..2c06d8a66 100644 --- a/src/ImageSharp.Formats.Bmp/BmpEncoderOptions.cs +++ b/src/ImageSharp.Formats.Bmp/BmpEncoderOptions.cs @@ -32,7 +32,7 @@ namespace ImageSharp.Formats public BmpBitsPerPixel BitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel24; /// - /// Converts the options to a instance with a cast + /// Converts the options to a instance with a cast /// or by creating a new instance with the specfied options. /// /// The options for the encoder. diff --git a/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs b/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs index 8184c19d3..5932425e5 100644 --- a/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs +++ b/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs @@ -36,7 +36,7 @@ namespace ImageSharp.Formats public Encoding TextEncoding { get; set; } = DefaultEncoding; /// - /// Converts the options to a instance with a cast + /// Converts the options to a instance with a cast /// or by creating a new instance with the specfied options. /// /// The options for the decoder. From 5d71c2ceaed113b402f77b09791aeb82445ec335 Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sun, 19 Feb 2017 19:16:09 +0100 Subject: [PATCH 101/142] Options should be readonly. --- src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs b/src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs index 9696eb66d..df62fb6f4 100644 --- a/src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs @@ -18,7 +18,7 @@ namespace ImageSharp.Formats /// /// The options for the encoder. /// - private IBmpEncoderOptions options; + private readonly IBmpEncoderOptions options; /// /// The amount to pad each row by. From 216191656464130b979eabc91c56f7e47e77aa81 Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sun, 19 Feb 2017 21:41:56 +0100 Subject: [PATCH 102/142] Implemented the IEncoderOptions inside the gif encoder. --- src/ImageSharp.Formats.Gif/GifConstants.cs | 14 +++- src/ImageSharp.Formats.Gif/GifDecoderCore.cs | 2 +- .../GifDecoderOptions.cs | 4 +- src/ImageSharp.Formats.Gif/GifEncoder.cs | 37 ++++------ src/ImageSharp.Formats.Gif/GifEncoderCore.cs | 68 +++++++++++++----- .../GifEncoderOptions.cs | 71 +++++++++++++++++++ .../IGifEncoderOptions.cs | 38 ++++++++++ src/ImageSharp.Formats.Gif/ImageExtensions.cs | 11 ++- .../Formats/Gif/GifEncoderCoreTests.cs | 67 +++++++++++++++++ 9 files changed, 264 insertions(+), 48 deletions(-) create mode 100644 src/ImageSharp.Formats.Gif/GifEncoderOptions.cs create mode 100644 src/ImageSharp.Formats.Gif/IGifEncoderOptions.cs create mode 100644 tests/ImageSharp.Tests/Formats/Gif/GifEncoderCoreTests.cs diff --git a/src/ImageSharp.Formats.Gif/GifConstants.cs b/src/ImageSharp.Formats.Gif/GifConstants.cs index 5334bcba3..4af291c2b 100644 --- a/src/ImageSharp.Formats.Gif/GifConstants.cs +++ b/src/ImageSharp.Formats.Gif/GifConstants.cs @@ -5,10 +5,12 @@ namespace ImageSharp.Formats { + using System.Text; + /// /// Constants that define specific points within a gif. /// - internal sealed class GifConstants + internal static class GifConstants { /// /// The file type. @@ -50,6 +52,11 @@ namespace ImageSharp.Formats /// public const byte CommentLabel = 0xFE; + /// + /// The name of the property inside the image properties for the comments. + /// + public const string Comments = "Comments"; + /// /// The maximum comment length. /// @@ -79,5 +86,10 @@ namespace ImageSharp.Formats /// The end introducer trailer ;. /// public const byte EndIntroducer = 0x3B; + + /// + /// Gets the default encoding to use when reading comments. + /// + public static Encoding DefaultEncoding { get; } = Encoding.GetEncoding("ASCII"); } } diff --git a/src/ImageSharp.Formats.Gif/GifDecoderCore.cs b/src/ImageSharp.Formats.Gif/GifDecoderCore.cs index 3ab0e6ca8..ab1edc2c7 100644 --- a/src/ImageSharp.Formats.Gif/GifDecoderCore.cs +++ b/src/ImageSharp.Formats.Gif/GifDecoderCore.cs @@ -261,7 +261,7 @@ namespace ImageSharp.Formats { this.currentStream.Read(commentsBuffer, 0, length); string comments = this.options.TextEncoding.GetString(commentsBuffer, 0, length); - this.decodedImage.MetaData.Properties.Add(new ImageProperty("Comments", comments)); + this.decodedImage.MetaData.Properties.Add(new ImageProperty(GifConstants.Comments, comments)); } finally { diff --git a/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs b/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs index 5932425e5..8722c5fe8 100644 --- a/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs +++ b/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs @@ -12,8 +12,6 @@ namespace ImageSharp.Formats /// public sealed class GifDecoderOptions : DecoderOptions, IGifDecoderOptions { - private static readonly Encoding DefaultEncoding = Encoding.GetEncoding("ASCII"); - /// /// Initializes a new instance of the class. /// @@ -33,7 +31,7 @@ namespace ImageSharp.Formats /// /// Gets or sets the encoding that should be used when reading comments. /// - public Encoding TextEncoding { get; set; } = DefaultEncoding; + public Encoding TextEncoding { get; set; } = GifConstants.DefaultEncoding; /// /// Converts the options to a instance with a cast diff --git a/src/ImageSharp.Formats.Gif/GifEncoder.cs b/src/ImageSharp.Formats.Gif/GifEncoder.cs index 402d2d09a..34027ee0c 100644 --- a/src/ImageSharp.Formats.Gif/GifEncoder.cs +++ b/src/ImageSharp.Formats.Gif/GifEncoder.cs @@ -8,40 +8,31 @@ namespace ImageSharp.Formats using System; using System.IO; - using ImageSharp.Quantizers; - /// /// Image encoder for writing image data to a stream in gif format. /// public class GifEncoder : IImageEncoder { - /// - /// Gets or sets the quality of output for images. - /// - /// For gifs the value ranges from 1 to 256. - public int Quality { get; set; } + /// + public void Encode(Image image, Stream stream, IEncoderOptions options) + where TColor : struct, IPackedPixel, IEquatable + { + IGifEncoderOptions gifOptions = GifEncoderOptions.Create(options); - /// - /// Gets or sets the transparency threshold. - /// - public byte Threshold { get; set; } = 128; + this.Encode(image, stream, gifOptions); + } /// - /// Gets or sets the quantizer for reducing the color count. + /// Encodes the image to the specified stream from the . /// - public IQuantizer Quantizer { get; set; } - - /// - public void Encode(Image image, Stream stream, IEncoderOptions options) + /// The pixel format. + /// The to encode from. + /// The to encode the image data to. + /// The options for the encoder. + public void Encode(Image image, Stream stream, IGifEncoderOptions options) where TColor : struct, IPixel { - GifEncoderCore encoder = new GifEncoderCore - { - Quality = this.Quality, - Quantizer = this.Quantizer, - Threshold = this.Threshold - }; - + GifEncoderCore encoder = new GifEncoderCore(options); encoder.Encode(image, stream); } } diff --git a/src/ImageSharp.Formats.Gif/GifEncoderCore.cs b/src/ImageSharp.Formats.Gif/GifEncoderCore.cs index 36cf614d9..2c9a0c8b0 100644 --- a/src/ImageSharp.Formats.Gif/GifEncoderCore.cs +++ b/src/ImageSharp.Formats.Gif/GifEncoderCore.cs @@ -24,20 +24,23 @@ namespace ImageSharp.Formats private readonly byte[] buffer = new byte[16]; /// - /// The number of bits requires to store the image palette. + /// The options for the encoder. /// - private int bitDepth; + private readonly IGifEncoderOptions options; /// - /// Gets or sets the quality of output for images. + /// The number of bits requires to store the image palette. /// - /// For gifs the value ranges from 1 to 256. - public int Quality { get; set; } + private int bitDepth; /// - /// Gets or sets the transparency threshold. + /// Initializes a new instance of the class. /// - public byte Threshold { get; set; } = 128; + /// The options for the encoder. + public GifEncoderCore(IGifEncoderOptions options) + { + this.options = options ?? new GifEncoderOptions(); + } /// /// Gets or sets the quantizer for reducing the color count. @@ -56,23 +59,20 @@ namespace ImageSharp.Formats Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); - if (this.Quantizer == null) - { - this.Quantizer = new OctreeQuantizer(); - } + this.Quantizer = this.options.Quantizer ?? new OctreeQuantizer(); // Do not use IDisposable pattern here as we want to preserve the stream. EndianBinaryWriter writer = new EndianBinaryWriter(Endianness.LittleEndian, stream); // Ensure that quality can be set but has a fallback. - int quality = this.Quality > 0 ? this.Quality : image.MetaData.Quality; - this.Quality = quality > 0 ? quality.Clamp(1, 256) : 256; + int quality = this.options.Quality > 0 ? this.options.Quality : image.MetaData.Quality; + quality = quality > 0 ? quality.Clamp(1, 256) : 256; // Get the number of bits. - this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(this.Quality); + this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quality); // Quantize the image returning a palette. - QuantizedImage quantized = ((IQuantizer)this.Quantizer).Quantize(image, this.Quality); + QuantizedImage quantized = ((IQuantizer)this.Quantizer).Quantize(image, quality); int index = this.GetTransparentIndex(quantized); @@ -84,6 +84,7 @@ namespace ImageSharp.Formats // Write the first frame. this.WriteGraphicalControlExtension(image, writer, index); + this.WriteComments(image, writer); this.WriteImageDescriptor(image, writer); this.WriteColorTable(quantized, writer); this.WriteImageData(quantized, writer); @@ -97,7 +98,7 @@ namespace ImageSharp.Formats for (int i = 0; i < image.Frames.Count; i++) { ImageFrame frame = image.Frames[i]; - QuantizedImage quantizedFrame = ((IQuantizer)this.Quantizer).Quantize(frame, this.Quality); + QuantizedImage quantizedFrame = ((IQuantizer)this.Quantizer).Quantize(frame, quality); this.WriteGraphicalControlExtension(frame, writer, this.GetTransparentIndex(quantizedFrame)); this.WriteImageDescriptor(frame, writer); @@ -106,7 +107,7 @@ namespace ImageSharp.Formats } } - // TODO: Write Comments extension etc + // TODO: Write extension etc writer.Write(GifConstants.EndIntroducer); } @@ -229,6 +230,39 @@ namespace ImageSharp.Formats } } + /// + /// Writes the image comments to the stream. + /// + /// The pixel format. + /// The to be encoded. + /// The stream to write to. + private void WriteComments(Image image, EndianBinaryWriter writer) + where TColor : struct, IPackedPixel, IEquatable + { + if (this.options.IgnoreMetadata == true) + { + return; + } + + ImageProperty property = image.MetaData.Properties.FirstOrDefault(p => p.Name == GifConstants.Comments); + if (property == null || string.IsNullOrEmpty(property.Value)) + { + return; + } + + byte[] comments = this.options.TextEncoding.GetBytes(property.Value); + + int count = Math.Min(comments.Length, 255); + + this.buffer[0] = GifConstants.ExtensionIntroducer; + this.buffer[1] = GifConstants.CommentLabel; + this.buffer[2] = (byte)count; + + writer.Write(this.buffer, 0, 3); + writer.Write(comments, 0, count); + writer.Write(GifConstants.Terminator); + } + /// /// Writes the graphics control extension to the stream. /// diff --git a/src/ImageSharp.Formats.Gif/GifEncoderOptions.cs b/src/ImageSharp.Formats.Gif/GifEncoderOptions.cs new file mode 100644 index 000000000..94cad9603 --- /dev/null +++ b/src/ImageSharp.Formats.Gif/GifEncoderOptions.cs @@ -0,0 +1,71 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + using System.Text; + + using Quantizers; + + /// + /// Encapsulates the options for the . + /// + public sealed class GifEncoderOptions : EncoderOptions, IGifEncoderOptions + { + /// + /// Initializes a new instance of the class. + /// + public GifEncoderOptions() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The options for the encoder. + private GifEncoderOptions(IEncoderOptions options) + : base(options) + { + } + + /// + /// Gets or sets the encoding that should be used when writing comments. + /// + public Encoding TextEncoding { get; set; } = GifConstants.DefaultEncoding; + + /// + /// Gets or sets the quality of output for images. + /// + /// For gifs the value ranges from 1 to 256. + public int Quality { get; set; } + + /// + /// Gets or sets the transparency threshold. + /// + public byte Threshold { get; set; } = 128; + + /// + /// Gets or sets the quantizer for reducing the color count. + /// + public IQuantizer Quantizer { get; set; } + + /// + /// Converts the options to a instance with a + /// cast or by creating a new instance with the specfied options. + /// + /// The options for the encoder. + /// The options for the . + internal static IGifEncoderOptions Create(IEncoderOptions options) + { + IGifEncoderOptions gifOptions = options as IGifEncoderOptions; + if (gifOptions != null) + { + return gifOptions; + } + + return new GifEncoderOptions(options); + } + } +} diff --git a/src/ImageSharp.Formats.Gif/IGifEncoderOptions.cs b/src/ImageSharp.Formats.Gif/IGifEncoderOptions.cs new file mode 100644 index 000000000..c1d6b7ad8 --- /dev/null +++ b/src/ImageSharp.Formats.Gif/IGifEncoderOptions.cs @@ -0,0 +1,38 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + using System.Text; + + using Quantizers; + + /// + /// Encapsulates the options for the . + /// + public interface IGifEncoderOptions : IEncoderOptions + { + /// + /// Gets the encoding that should be used when writing comments. + /// + Encoding TextEncoding { get; } + + /// + /// Gets the quality of output for images. + /// + /// For gifs the value ranges from 1 to 256. + int Quality { get; } + + /// + /// Gets the transparency threshold. + /// + byte Threshold { get; } + + /// + /// Gets the quantizer for reducing the color count. + /// + IQuantizer Quantizer { get; } + } +} diff --git a/src/ImageSharp.Formats.Gif/ImageExtensions.cs b/src/ImageSharp.Formats.Gif/ImageExtensions.cs index e0ec2e8c8..dcf4ab29a 100644 --- a/src/ImageSharp.Formats.Gif/ImageExtensions.cs +++ b/src/ImageSharp.Formats.Gif/ImageExtensions.cs @@ -21,13 +21,18 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The stream to save the image to. - /// The quality to save the image to representing the number of colors. Between 1 and 256. + /// The options for the encoder. /// Thrown if the stream is null. /// /// The . /// - public static Image SaveAsGif(this Image source, Stream stream, int quality = 256) + public static Image SaveAsGif(this Image source, Stream stream, IGifEncoderOptions options = null) where TColor : struct, IPixel - => source.Save(stream, new GifEncoder { Quality = quality }); + { + GifEncoder encoder = new GifEncoder(); + encoder.Encode(source, stream, options); + + return source; + } } } diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderCoreTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderCoreTests.cs new file mode 100644 index 000000000..6f163bc6e --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderCoreTests.cs @@ -0,0 +1,67 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.IO; + using Xunit; + + using ImageSharp.Formats; + + public class GifEncoderCoreTests + { + [Fact] + public void Encode_IgnoreMetadataIsFalse_CommentsAreWritten() + { + var options = new EncoderOptions() + { + IgnoreMetadata = false + }; + + TestFile testFile = TestFile.Create(TestImages.Gif.Rings); + + using (Image input = testFile.CreateImage()) + { + using (MemoryStream memStream = new MemoryStream()) + { + input.Save(memStream, new GifFormat(), options); + + memStream.Position = 0; + using (Image output = new Image(memStream)) + { + Assert.Equal(1, output.MetaData.Properties.Count); + Assert.Equal("Comments", output.MetaData.Properties[0].Name); + Assert.Equal("ImageSharp", output.MetaData.Properties[0].Value); + } + } + } + } + + [Fact] + public void Encode_IgnoreMetadataIsTrue_CommentsAreNotWritten() + { + var options = new GifEncoderOptions() + { + IgnoreMetadata = true + }; + + TestFile testFile = TestFile.Create(TestImages.Gif.Rings); + + using (Image input = testFile.CreateImage()) + { + using (MemoryStream memStream = new MemoryStream()) + { + input.SaveAsGif(memStream, options); + + memStream.Position = 0; + using (Image output = new Image(memStream)) + { + Assert.Equal(0, output.MetaData.Properties.Count); + } + } + } + } + } +} From de7aa060540ded3bb35f60a179ff4a43395ab935 Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sun, 19 Feb 2017 21:58:48 +0100 Subject: [PATCH 103/142] Build fixes due to re-base. --- src/ImageSharp.Formats.Bmp/BmpEncoder.cs | 2 +- src/ImageSharp.Formats.Gif/GifDecoder.cs | 2 +- src/ImageSharp.Formats.Gif/GifEncoder.cs | 2 +- src/ImageSharp.Formats.Gif/GifEncoderCore.cs | 2 +- src/ImageSharp.Formats.Png/PngDecoder.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp.Formats.Bmp/BmpEncoder.cs b/src/ImageSharp.Formats.Bmp/BmpEncoder.cs index 2c0bb5b5d..d0a3550f6 100644 --- a/src/ImageSharp.Formats.Bmp/BmpEncoder.cs +++ b/src/ImageSharp.Formats.Bmp/BmpEncoder.cs @@ -31,7 +31,7 @@ namespace ImageSharp.Formats /// The to encode the image data to. /// The options for the encoder. public void Encode(Image image, Stream stream, IBmpEncoderOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { BmpEncoderCore encoder = new BmpEncoderCore(options); encoder.Encode(image, stream); diff --git a/src/ImageSharp.Formats.Gif/GifDecoder.cs b/src/ImageSharp.Formats.Gif/GifDecoder.cs index 88a3d5e43..b1e8ba928 100644 --- a/src/ImageSharp.Formats.Gif/GifDecoder.cs +++ b/src/ImageSharp.Formats.Gif/GifDecoder.cs @@ -30,7 +30,7 @@ namespace ImageSharp.Formats /// The containing image data. /// The options for the decoder. public void Decode(Image image, Stream stream, IGifDecoderOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { new GifDecoderCore(options).Decode(image, stream); } diff --git a/src/ImageSharp.Formats.Gif/GifEncoder.cs b/src/ImageSharp.Formats.Gif/GifEncoder.cs index 34027ee0c..cc8516ed9 100644 --- a/src/ImageSharp.Formats.Gif/GifEncoder.cs +++ b/src/ImageSharp.Formats.Gif/GifEncoder.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Formats { /// public void Encode(Image image, Stream stream, IEncoderOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { IGifEncoderOptions gifOptions = GifEncoderOptions.Create(options); diff --git a/src/ImageSharp.Formats.Gif/GifEncoderCore.cs b/src/ImageSharp.Formats.Gif/GifEncoderCore.cs index 2c9a0c8b0..38cbba850 100644 --- a/src/ImageSharp.Formats.Gif/GifEncoderCore.cs +++ b/src/ImageSharp.Formats.Gif/GifEncoderCore.cs @@ -237,7 +237,7 @@ namespace ImageSharp.Formats /// The to be encoded. /// The stream to write to. private void WriteComments(Image image, EndianBinaryWriter writer) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { if (this.options.IgnoreMetadata == true) { diff --git a/src/ImageSharp.Formats.Png/PngDecoder.cs b/src/ImageSharp.Formats.Png/PngDecoder.cs index 658de9e66..d527e1654 100644 --- a/src/ImageSharp.Formats.Png/PngDecoder.cs +++ b/src/ImageSharp.Formats.Png/PngDecoder.cs @@ -47,7 +47,7 @@ namespace ImageSharp.Formats /// The containing image data. /// The options for the decoder. public void Decode(Image image, Stream stream, IPngDecoderOptions options) - where TColor : struct, IPackedPixel, IEquatable + where TColor : struct, IPixel { new PngDecoderCore(options).Decode(image, stream); } From 98f18cbfeb542fd62338ca7670d39fc972c689dc Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Mon, 20 Feb 2017 07:14:23 +0100 Subject: [PATCH 104/142] Added extra test for trimming comments. --- .../Formats/Gif/GifEncoderCoreTests.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderCoreTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderCoreTests.cs index 6f163bc6e..0e8e21780 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderCoreTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderCoreTests.cs @@ -63,5 +63,28 @@ namespace ImageSharp.Tests } } } + + [Fact] + public void Encode_CommentIsToLong_CommentIsTrimmed() + { + using (Image input = new Image(1, 1)) + { + string comments = new string('c', 256); + input.MetaData.Properties.Add(new ImageProperty("Comments", comments)); + + using (MemoryStream memStream = new MemoryStream()) + { + input.Save(memStream, new GifFormat()); + + memStream.Position = 0; + using (Image output = new Image(memStream)) + { + Assert.Equal(1, output.MetaData.Properties.Count); + Assert.Equal("Comments", output.MetaData.Properties[0].Name); + Assert.Equal(255, output.MetaData.Properties[0].Value.Length); + } + } + } + } } } From e429b812ccf9a755f06bdedb8201304ae34432fc Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Mon, 20 Feb 2017 23:37:08 +0100 Subject: [PATCH 105/142] Implemented the IEncoderOptions inside the jpeg encoder. --- .../IJpegEncoderOptions.cs | 29 +++++++ .../ImageExtensions.cs | 11 ++- src/ImageSharp.Formats.Jpeg/JpegEncoder.cs | 75 ++++--------------- .../JpegEncoderCore.cs | 40 +++++++--- .../JpegEncoderOptions.cs | 62 +++++++++++++++ .../Formats/Jpg/JpegDecoderTests.cs | 5 +- .../Formats/Jpg/JpegEncoderTests.cs | 67 +++++++++++++++-- .../Formats/Jpg/JpegProfilingBenchmarks.cs | 5 +- .../TestUtilities/ImagingTestCaseUtility.cs | 5 +- 9 files changed, 209 insertions(+), 90 deletions(-) create mode 100644 src/ImageSharp.Formats.Jpeg/IJpegEncoderOptions.cs create mode 100644 src/ImageSharp.Formats.Jpeg/JpegEncoderOptions.cs diff --git a/src/ImageSharp.Formats.Jpeg/IJpegEncoderOptions.cs b/src/ImageSharp.Formats.Jpeg/IJpegEncoderOptions.cs new file mode 100644 index 000000000..7d62dd4ef --- /dev/null +++ b/src/ImageSharp.Formats.Jpeg/IJpegEncoderOptions.cs @@ -0,0 +1,29 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Encapsulates the options for the . + /// + public interface IJpegEncoderOptions : IEncoderOptions + { + /// + /// Gets the quality, that will be used to encode the image. Quality + /// index must be between 0 and 100 (compression from max to min). + /// + /// + /// If the quality is less than or equal to 80, the subsampling ratio will switch to + /// + /// The quality of the jpg image from 0 to 100. + int Quality { get; } + + /// + /// Gets the subsample ration, that will be used to encode the image. + /// + /// The subsample ratio of the jpg image. + JpegSubsample? Subsample { get; } + } +} diff --git a/src/ImageSharp.Formats.Jpeg/ImageExtensions.cs b/src/ImageSharp.Formats.Jpeg/ImageExtensions.cs index 2cbba02a9..311bcc643 100644 --- a/src/ImageSharp.Formats.Jpeg/ImageExtensions.cs +++ b/src/ImageSharp.Formats.Jpeg/ImageExtensions.cs @@ -21,13 +21,18 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The stream to save the image to. - /// The quality to save the image to. Between 1 and 100. + /// The options for the encoder. /// Thrown if the stream is null. /// /// The . /// - public static Image SaveAsJpeg(this Image source, Stream stream, int quality = 75) + public static Image SaveAsJpeg(this Image source, Stream stream, IJpegEncoderOptions options = null) where TColor : struct, IPixel - => source.Save(stream, new JpegEncoder { Quality = quality }); + { + JpegEncoder encoder = new JpegEncoder(); + encoder.Encode(source, stream, options); + + return source; + } } } diff --git a/src/ImageSharp.Formats.Jpeg/JpegEncoder.cs b/src/ImageSharp.Formats.Jpeg/JpegEncoder.cs index 46972cd93..2f2823fa2 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegEncoder.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegEncoder.cs @@ -5,7 +5,6 @@ namespace ImageSharp.Formats { - using System; using System.IO; /// @@ -13,73 +12,27 @@ namespace ImageSharp.Formats /// public class JpegEncoder : IImageEncoder { - /// - /// The quality used to encode the image. - /// - private int quality = 75; - - /// - /// The subsamples scheme used to encode the image. - /// - private JpegSubsample subsample = JpegSubsample.Ratio420; - - /// - /// Whether subsampling has been specifically set. - /// - private bool subsampleSet; - - /// - /// Gets or sets the quality, that will be used to encode the image. Quality - /// index must be between 0 and 100 (compression from max to min). - /// - /// - /// If the quality is less than or equal to 80, the subsampling ratio will switch to - /// - /// The quality of the jpg image from 0 to 100. - public int Quality + /// + public void Encode(Image image, Stream stream, IEncoderOptions options) + where TColor : struct, IPixel { - get { return this.quality; } - set { this.quality = value.Clamp(1, 100); } + IJpegEncoderOptions gifOptions = JpegEncoderOptions.Create(options); + + this.Encode(image, stream, gifOptions); } /// - /// Gets or sets the subsample ration, that will be used to encode the image. + /// Encodes the image to the specified stream from the . /// - /// The subsample ratio of the jpg image. - public JpegSubsample Subsample - { - get - { - return this.subsample; - } - - set - { - this.subsample = value; - this.subsampleSet = true; - } - } - - /// - public void Encode(Image image, Stream stream, IEncoderOptions options) + /// The pixel format. + /// The to encode from. + /// The to encode the image data to. + /// The options for the encoder. + public void Encode(Image image, Stream stream, IJpegEncoderOptions options) where TColor : struct, IPixel { - // Ensure that quality can be set but has a fallback. - if (image.MetaData.Quality > 0) - { - this.Quality = image.MetaData.Quality; - } - - JpegEncoderCore encode = new JpegEncoderCore(); - if (this.subsampleSet) - { - encode.Encode(image, stream, this.Quality, this.Subsample); - } - else - { - // Use 4:2:0 Subsampling at quality < 91% for reduced filesize. - encode.Encode(image, stream, this.Quality, this.Quality >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420); - } + JpegEncoderCore encode = new JpegEncoderCore(options); + encode.Encode(image, stream); } } } diff --git a/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs b/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs index 0309af129..66f400c01 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs @@ -118,6 +118,11 @@ namespace ImageSharp.Formats /// private readonly byte[] huffmanBuffer = new byte[179]; + /// + /// The options for the encoder. + /// + private readonly IJpegEncoderOptions options; + /// /// The accumulated bits to write to the stream. /// @@ -148,15 +153,22 @@ namespace ImageSharp.Formats /// private JpegSubsample subsample; + /// + /// Initializes a new instance of the class. + /// + /// The options for the encoder. + public JpegEncoderCore(IJpegEncoderOptions options) + { + this.options = options ?? new JpegEncoderOptions(); + } + /// /// Encode writes the image to the jpeg baseline format with the given options. /// /// The pixel format. /// The image to write from. /// The stream to write to. - /// The quality. - /// The subsampling mode. - public void Encode(Image image, Stream stream, int quality, JpegSubsample sample) + public void Encode(Image image, Stream stream) where TColor : struct, IPixel { Guard.NotNull(image, nameof(image)); @@ -168,18 +180,17 @@ namespace ImageSharp.Formats throw new ImageFormatException($"Image is too large to encode at {image.Width}x{image.Height}."); } - this.outputStream = stream; - this.subsample = sample; - - if (quality < 1) + // Ensure that quality can be set but has a fallback. + int quality = this.options.Quality > 0 ? this.options.Quality : image.MetaData.Quality; + if (quality == 0) { - quality = 1; + quality = 75; } - if (quality > 100) - { - quality = 100; - } + quality = quality.Clamp(1, 100); + + this.outputStream = stream; + this.subsample = this.options.Subsample ?? (quality >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420); // Convert from a quality rating to a scaling factor. int scale; @@ -706,6 +717,11 @@ namespace ImageSharp.Formats private void WriteProfiles(Image image) where TColor : struct, IPixel { + if (this.options.IgnoreMetadata) + { + return; + } + image.MetaData.SyncProfiles(); this.WriteProfile(image.MetaData.ExifProfile); } diff --git a/src/ImageSharp.Formats.Jpeg/JpegEncoderOptions.cs b/src/ImageSharp.Formats.Jpeg/JpegEncoderOptions.cs new file mode 100644 index 000000000..6be36627c --- /dev/null +++ b/src/ImageSharp.Formats.Jpeg/JpegEncoderOptions.cs @@ -0,0 +1,62 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + /// + /// Encapsulates the options for the . + /// + public sealed class JpegEncoderOptions : EncoderOptions, IJpegEncoderOptions + { + /// + /// Initializes a new instance of the class. + /// + public JpegEncoderOptions() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The options for the encoder. + private JpegEncoderOptions(IEncoderOptions options) + : base(options) + { + } + + /// + /// Gets or sets the quality, that will be used to encode the image. Quality + /// index must be between 0 and 100 (compression from max to min). + /// + /// + /// If the quality is less than or equal to 80, the subsampling ratio will switch to + /// + /// The quality of the jpg image from 0 to 100. + public int Quality { get; set; } + + /// + /// Gets or sets the subsample ration, that will be used to encode the image. + /// + /// The subsample ratio of the jpg image. + public JpegSubsample? Subsample { get; set; } + + /// + /// Converts the options to a instance with a + /// cast or by creating a new instance with the specfied options. + /// + /// The options for the encoder. + /// The options for the . + internal static IJpegEncoderOptions Create(IEncoderOptions options) + { + IJpegEncoderOptions jpegOptions = options as IJpegEncoderOptions; + if (jpegOptions != null) + { + return jpegOptions; + } + + return new JpegEncoderOptions(options); + } + } +} diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index b8ec9d49e..476de95ff 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -61,12 +61,13 @@ namespace ImageSharp.Tests byte[] data; using (Image image = provider.GetImage()) { - JpegEncoder encoder = new JpegEncoder() { Subsample = subsample, Quality = quality }; + JpegEncoder encoder = new JpegEncoder(); + JpegEncoderOptions options = new JpegEncoderOptions { Subsample = subsample, Quality = quality }; data = new byte[65536]; using (MemoryStream ms = new MemoryStream(data)) { - image.Save(ms, encoder); + image.Save(ms, encoder, options); } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 6518c3e6b..741e785c0 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -38,11 +38,12 @@ namespace ImageSharp.Tests { image.MetaData.Quality = quality; image.MetaData.ExifProfile = null; // Reduce the size of the file - JpegEncoder encoder = new JpegEncoder { Subsample = subsample, Quality = quality }; + JpegEncoder encoder = new JpegEncoder(); + JpegEncoderOptions options = new JpegEncoderOptions { Subsample = subsample, Quality = quality }; provider.Utility.TestName += $"{subsample}_Q{quality}"; provider.Utility.SaveTestOutputFile(image, "png"); - provider.Utility.SaveTestOutputFile(image, "jpg", encoder); + provider.Utility.SaveTestOutputFile(image, "jpg", encoder, options); } } @@ -59,13 +60,63 @@ namespace ImageSharp.Tests using (FileStream outputStream = File.OpenWrite(utility.GetTestOutputFileName("jpg"))) { - JpegEncoder encoder = new JpegEncoder() - { - Subsample = subSample, - Quality = quality - }; + JpegEncoder encoder = new JpegEncoder(); - image.Save(outputStream, encoder); + image.Save(outputStream, encoder, new JpegEncoderOptions() + { + Subsample = subSample, + Quality = quality + }); + } + } + } + + [Fact] + public void Encode_IgnoreMetadataIsFalse_ExifProfileIsWritten() + { + var options = new EncoderOptions() + { + IgnoreMetadata = false + }; + + TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); + + using (Image input = testFile.CreateImage()) + { + using (MemoryStream memStream = new MemoryStream()) + { + input.Save(memStream, new JpegFormat(), options); + + memStream.Position = 0; + using (Image output = new Image(memStream)) + { + Assert.NotNull(output.MetaData.ExifProfile); + } + } + } + } + + [Fact] + public void Encode_IgnoreMetadataIsTrue_ExifProfileIgnored() + { + var options = new JpegEncoderOptions() + { + IgnoreMetadata = true + }; + + TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); + + using (Image input = testFile.CreateImage()) + { + using (MemoryStream memStream = new MemoryStream()) + { + input.SaveAsJpeg(memStream, options); + + memStream.Position = 0; + using (Image output = new Image(memStream)) + { + Assert.Null(output.MetaData.ExifProfile); + } } } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs index bfe1f1e76..50e678bf0 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs @@ -81,8 +81,9 @@ namespace ImageSharp.Tests { foreach (Image img in testImages) { - JpegEncoder encoder = new JpegEncoder() { Quality = quality, Subsample = subsample }; - img.Save(ms, encoder); + JpegEncoder encoder = new JpegEncoder(); + JpegEncoderOptions options = new JpegEncoderOptions { Quality = quality, Subsample = subsample }; + img.Save(ms, encoder, options); ms.Seek(0, SeekOrigin.Begin); } }, diff --git a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs index 54be62d37..38429b278 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs @@ -75,7 +75,8 @@ namespace ImageSharp.Tests /// The image instance /// The requested extension /// Optional encoder - public void SaveTestOutputFile(Image image, string extension = null, IImageEncoder encoder = null) + /// Optional encoder options + public void SaveTestOutputFile(Image image, string extension = null, IImageEncoder encoder = null, IEncoderOptions options = null) where TColor : struct, IPixel { string path = this.GetTestOutputFileName(extension); @@ -86,7 +87,7 @@ namespace ImageSharp.Tests using (var stream = File.OpenWrite(path)) { - image.Save(stream, encoder); + image.Save(stream, encoder, options); } } From 1b4b8043bc57f73579ef61ef90030fe978de8714 Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Mon, 20 Feb 2017 23:40:34 +0100 Subject: [PATCH 106/142] Renamed test classes. --- .../{BitmapTests.cs => BmpEncoderTests.cs} | 4 +- ...DecoderCoreTests.cs => GifDecoderTests.cs} | 4 +- ...EncoderCoreTests.cs => GifEncoderTests.cs} | 4 +- .../Formats/Jpg/JpegDecoderCoreTests.cs | 44 ------------------- .../Formats/Jpg/JpegDecoderTests.cs | 32 ++++++++++++++ ...DecoderCoreTests.cs => PngDecoderTests.cs} | 4 +- .../Png/{PngTests.cs => PngEncoderTests.cs} | 6 +-- 7 files changed, 42 insertions(+), 56 deletions(-) rename tests/ImageSharp.Tests/Formats/Bmp/{BitmapTests.cs => BmpEncoderTests.cs} (89%) rename tests/ImageSharp.Tests/Formats/Gif/{GifDecoderCoreTests.cs => GifDecoderTests.cs} (94%) rename tests/ImageSharp.Tests/Formats/Gif/{GifEncoderCoreTests.cs => GifEncoderTests.cs} (96%) delete mode 100644 tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderCoreTests.cs rename tests/ImageSharp.Tests/Formats/Png/{PngDecoderCoreTests.cs => PngDecoderTests.cs} (94%) rename tests/ImageSharp.Tests/Formats/Png/{PngTests.cs => PngEncoderTests.cs} (91%) diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BitmapTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs similarity index 89% rename from tests/ImageSharp.Tests/Formats/Bmp/BitmapTests.cs rename to tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index 7579cc86e..497abb7d5 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BitmapTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -9,7 +9,7 @@ namespace ImageSharp.Tests { using Xunit; - public class BitmapTests : FileTestBase + public class BmpEncoderTests : FileTestBase { public static readonly TheoryData BitsPerPixel = new TheoryData diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderCoreTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs similarity index 94% rename from tests/ImageSharp.Tests/Formats/Gif/GifDecoderCoreTests.cs rename to tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index e1e3e94b0..b874a1585 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderCoreTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -10,7 +10,7 @@ namespace ImageSharp.Tests using ImageSharp.Formats; - public class GifDecoderCoreTests + public class GifDecoderTests { [Fact] public void Decode_IgnoreMetadataIsFalse_CommentsAreRead() diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderCoreTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs similarity index 96% rename from tests/ImageSharp.Tests/Formats/Gif/GifEncoderCoreTests.cs rename to tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs index 0e8e21780..da1323627 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderCoreTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -10,7 +10,7 @@ namespace ImageSharp.Tests using ImageSharp.Formats; - public class GifEncoderCoreTests + public class GifEncoderTests { [Fact] public void Encode_IgnoreMetadataIsFalse_CommentsAreWritten() diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderCoreTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderCoreTests.cs deleted file mode 100644 index 38d2a455e..000000000 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderCoreTests.cs +++ /dev/null @@ -1,44 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using Xunit; - - public class JpegDecoderCoreTests - { - [Fact] - public void Decode_IgnoreMetadataIsFalse_ExifProfileIsRead() - { - var options = new DecoderOptions() - { - IgnoreMetadata = false - }; - - TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); - - using (Image image = testFile.CreateImage(options)) - { - Assert.NotNull(image.MetaData.ExifProfile); - } - } - - [Fact] - public void Decode_IgnoreMetadataIsTrue_ExifProfileIgnored() - { - var options = new DecoderOptions() - { - IgnoreMetadata = true - }; - - TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); - - using (Image image = testFile.CreateImage(options)) - { - Assert.Null(image.MetaData.ExifProfile); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 476de95ff..7cb9a7cf2 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -121,5 +121,37 @@ namespace ImageSharp.Tests Assert.Equal(72, image.MetaData.VerticalResolution); } } + + [Fact] + public void Decode_IgnoreMetadataIsFalse_ExifProfileIsRead() + { + var options = new DecoderOptions() + { + IgnoreMetadata = false + }; + + TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); + + using (Image image = testFile.CreateImage(options)) + { + Assert.NotNull(image.MetaData.ExifProfile); + } + } + + [Fact] + public void Decode_IgnoreMetadataIsTrue_ExifProfileIgnored() + { + var options = new DecoderOptions() + { + IgnoreMetadata = true + }; + + TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); + + using (Image image = testFile.CreateImage(options)) + { + Assert.Null(image.MetaData.ExifProfile); + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderCoreTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs similarity index 94% rename from tests/ImageSharp.Tests/Formats/Png/PngDecoderCoreTests.cs rename to tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 766172ec6..921530806 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderCoreTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -10,7 +10,7 @@ namespace ImageSharp.Tests using ImageSharp.Formats; - public class PngDecoderCoreTests + public class PngDecoderTests { [Fact] public void Decode_IgnoreMetadataIsFalse_TextChunckIsRead() diff --git a/tests/ImageSharp.Tests/Formats/Png/PngTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs similarity index 91% rename from tests/ImageSharp.Tests/Formats/Png/PngTests.cs rename to tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 5ba00eb4d..49be75139 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -10,11 +10,9 @@ namespace ImageSharp.Tests using System.IO; using System.Threading.Tasks; - using Formats; - using Xunit; - public class PngTests : FileTestBase + public class PngEncoderTests : FileTestBase { [Fact] public void ImageCanSaveIndexedPng() From ce2e1292871431e542b2fdbe96c32f391296bb83 Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Tue, 21 Feb 2017 21:59:06 +0100 Subject: [PATCH 107/142] Implemented the IEncoderOptions inside the png encoder. --- .../IPngEncoderOptions.cs | 54 +++++++++++ src/ImageSharp.Formats.Png/ImageExtensions.cs | 14 +-- src/ImageSharp.Formats.Png/PngDecoderCore.cs | 1 - src/ImageSharp.Formats.Png/PngEncoder.cs | 68 +++----------- src/ImageSharp.Formats.Png/PngEncoderCore.cs | 91 +++++++++---------- .../PngEncoderOptions.cs | 88 ++++++++++++++++++ 6 files changed, 206 insertions(+), 110 deletions(-) create mode 100644 src/ImageSharp.Formats.Png/IPngEncoderOptions.cs create mode 100644 src/ImageSharp.Formats.Png/PngEncoderOptions.cs diff --git a/src/ImageSharp.Formats.Png/IPngEncoderOptions.cs b/src/ImageSharp.Formats.Png/IPngEncoderOptions.cs new file mode 100644 index 000000000..0008080d3 --- /dev/null +++ b/src/ImageSharp.Formats.Png/IPngEncoderOptions.cs @@ -0,0 +1,54 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + using Quantizers; + + /// + /// Encapsulates the options for the . + /// + public interface IPngEncoderOptions : IEncoderOptions + { + /// + /// Gets the quality of output for images. + /// + int Quality { get; } + + /// + /// Gets the png color type + /// + PngColorType PngColorType { get; } + + /// + /// Gets the compression level 1-9. + /// + int CompressionLevel { get; } + + /// + /// Gets the gamma value, that will be written + /// the the stream, when the property + /// is set to true. + /// + /// The gamma value of the image. + float Gamma { get; } + + /// + /// Gets quantizer for reducing the color count. + /// + IQuantizer Quantizer { get; } + + /// + /// Gets the transparency threshold. + /// + byte Threshold { get; } + + /// + /// Gets a value indicating whether this instance should write + /// gamma information to the stream. + /// + bool WriteGamma { get; } + } +} diff --git a/src/ImageSharp.Formats.Png/ImageExtensions.cs b/src/ImageSharp.Formats.Png/ImageExtensions.cs index dcb1c988b..f08ab8ee7 100644 --- a/src/ImageSharp.Formats.Png/ImageExtensions.cs +++ b/src/ImageSharp.Formats.Png/ImageExtensions.cs @@ -5,7 +5,6 @@ namespace ImageSharp { - using System; using System.IO; using Formats; @@ -21,15 +20,18 @@ namespace ImageSharp /// The pixel format. /// The image this method extends. /// The stream to save the image to. - /// The quality to save the image to representing the number of colors. - /// Anything equal to 256 and below will cause the encoder to save the image in an indexed format. - /// + /// The options for the encoder. /// Thrown if the stream is null. /// /// The . /// - public static Image SaveAsPng(this Image source, Stream stream, int quality = int.MaxValue) + public static Image SaveAsPng(this Image source, Stream stream, IPngEncoderOptions options = null) where TColor : struct, IPixel - => source.Save(stream, new PngEncoder { Quality = quality }); + { + PngEncoder encoder = new PngEncoder(); + encoder.Encode(source, stream, options); + + return source; + } } } diff --git a/src/ImageSharp.Formats.Png/PngDecoderCore.cs b/src/ImageSharp.Formats.Png/PngDecoderCore.cs index 4a5ad3648..076770ce5 100644 --- a/src/ImageSharp.Formats.Png/PngDecoderCore.cs +++ b/src/ImageSharp.Formats.Png/PngDecoderCore.cs @@ -10,7 +10,6 @@ namespace ImageSharp.Formats using System.Collections.Generic; using System.IO; using System.Linq; - using System.Text; using static ComparableExtensions; diff --git a/src/ImageSharp.Formats.Png/PngEncoder.cs b/src/ImageSharp.Formats.Png/PngEncoder.cs index 8d9c4bc08..e583f381f 100644 --- a/src/ImageSharp.Formats.Png/PngEncoder.cs +++ b/src/ImageSharp.Formats.Png/PngEncoder.cs @@ -5,72 +5,34 @@ namespace ImageSharp.Formats { - using System; using System.IO; - using ImageSharp.Quantizers; - /// /// Image encoder for writing image data to a stream in png format. /// public class PngEncoder : IImageEncoder { - /// - /// Gets or sets the quality of output for images. - /// - public int Quality { get; set; } - - /// - /// Gets or sets the png color type - /// - public PngColorType PngColorType { get; set; } = PngColorType.RgbWithAlpha; - - /// - /// Gets or sets the compression level 1-9. - /// Defaults to 6. - /// - public int CompressionLevel { get; set; } = 6; - - /// - /// Gets or sets the gamma value, that will be written - /// the the stream, when the property - /// is set to true. The default value is 2.2F. - /// - /// The gamma value of the image. - public float Gamma { get; set; } = 2.2F; - - /// - /// Gets or sets quantizer for reducing the color count. - /// - public IQuantizer Quantizer { get; set; } + /// + public void Encode(Image image, Stream stream, IEncoderOptions options) + where TColor : struct, IPixel + { + IPngEncoderOptions pngOptions = PngEncoderOptions.Create(options); - /// - /// Gets or sets the transparency threshold. - /// - public byte Threshold { get; set; } = 0; + this.Encode(image, stream, pngOptions); + } /// - /// Gets or sets a value indicating whether this instance should write - /// gamma information to the stream. The default value is false. + /// Encodes the image to the specified stream from the . /// - public bool WriteGamma { get; set; } - - /// - public void Encode(Image image, Stream stream, IEncoderOptions options) + /// The pixel format. + /// The to encode from. + /// The to encode the image data to. + /// The options for the encoder. + public void Encode(Image image, Stream stream, IPngEncoderOptions options) where TColor : struct, IPixel { - PngEncoderCore encoder = new PngEncoderCore - { - CompressionLevel = this.CompressionLevel, - Gamma = this.Gamma, - Quality = this.Quality, - PngColorType = this.PngColorType, - Quantizer = this.Quantizer, - WriteGamma = this.WriteGamma, - Threshold = this.Threshold - }; - - encoder.Encode(image, stream); + PngEncoderCore encode = new PngEncoderCore(options); + encode.Encode(image, stream); } } } diff --git a/src/ImageSharp.Formats.Png/PngEncoderCore.cs b/src/ImageSharp.Formats.Png/PngEncoderCore.cs index 2324853cb..8a00c40b2 100644 --- a/src/ImageSharp.Formats.Png/PngEncoderCore.cs +++ b/src/ImageSharp.Formats.Png/PngEncoderCore.cs @@ -40,6 +40,11 @@ namespace ImageSharp.Formats /// private readonly Crc32 crc = new Crc32(); + /// + /// The options for the encoder. + /// + private readonly IPngEncoderOptions options; + /// /// Contains the raw pixel data from an indexed image. /// @@ -86,44 +91,28 @@ namespace ImageSharp.Formats private byte[] paeth; /// - /// Gets or sets the quality of output for images. - /// - public int Quality { get; set; } - - /// - /// Gets or sets the png color type + /// The quality of output for images. /// - public PngColorType PngColorType { get; set; } + private int quality; /// - /// Gets or sets the compression level 1-9. - /// Defaults to 6. + /// The png color type. /// - public int CompressionLevel { get; set; } = 6; + private PngColorType pngColorType; /// - /// Gets or sets a value indicating whether this instance should write - /// gamma information to the stream. The default value is false. + /// The quantizer for reducing the color count. /// - public bool WriteGamma { get; set; } + private IQuantizer quantizer; /// - /// Gets or sets the gamma value, that will be written - /// the the stream, when the property - /// is set to true. The default value is 2.2F. + /// Initializes a new instance of the class. /// - /// The gamma value of the image. - public float Gamma { get; set; } = 2.2F; - - /// - /// Gets or sets the quantizer for reducing the color count. - /// - public IQuantizer Quantizer { get; set; } - - /// - /// Gets or sets the transparency threshold. - /// - public byte Threshold { get; set; } + /// The options for the encoder. + public PngEncoderCore(IPngEncoderOptions options) + { + this.options = options ?? new PngEncoderOptions(); + } /// /// Encodes the image to the specified stream from the . @@ -153,23 +142,25 @@ namespace ImageSharp.Formats stream.Write(this.chunkDataBuffer, 0, 8); // Ensure that quality can be set but has a fallback. - int quality = this.Quality > 0 ? this.Quality : image.MetaData.Quality; - this.Quality = quality > 0 ? quality.Clamp(1, int.MaxValue) : int.MaxValue; + this.quality = this.options.Quality > 0 ? this.options.Quality : image.MetaData.Quality; + this.quality = this.quality > 0 ? this.quality.Clamp(1, int.MaxValue) : int.MaxValue; + + this.pngColorType = this.options.PngColorType; // Set correct color type if the color count is 256 or less. - if (this.Quality <= 256) + if (this.quality <= 256) { - this.PngColorType = PngColorType.Palette; + this.pngColorType = PngColorType.Palette; } - if (this.PngColorType == PngColorType.Palette && this.Quality > 256) + if (this.pngColorType == PngColorType.Palette && this.quality > 256) { - this.Quality = 256; + this.quality = 256; } // Set correct bit depth. - this.bitDepth = this.Quality <= 256 - ? (byte)ImageMaths.GetBitsNeededForColorDepth(this.Quality).Clamp(1, 8) + this.bitDepth = this.quality <= 256 + ? (byte)ImageMaths.GetBitsNeededForColorDepth(this.quality).Clamp(1, 8) : (byte)8; // Png only supports in four pixel depths: 1, 2, 4, and 8 bits when using the PLTE chunk @@ -188,7 +179,7 @@ namespace ImageSharp.Formats { Width = image.Width, Height = image.Height, - ColorType = (byte)this.PngColorType, + ColorType = (byte)this.pngColorType, BitDepth = this.bitDepth, FilterMethod = 0, // None CompressionMethod = 0, @@ -198,7 +189,7 @@ namespace ImageSharp.Formats this.WriteHeaderChunk(stream, header); // Collect the indexed pixel data - if (this.PngColorType == PngColorType.Palette) + if (this.pngColorType == PngColorType.Palette) { this.CollectIndexedBytes(image, stream, header); } @@ -334,7 +325,7 @@ namespace ImageSharp.Formats private byte[] EncodePixelRow(PixelAccessor pixels, int row, byte[] previousScanline, byte[] rawScanline, byte[] result) where TColor : struct, IPixel { - switch (this.PngColorType) + switch (this.pngColorType) { case PngColorType.Palette: Buffer.BlockCopy(this.palettePixelData, row * rawScanline.Length, rawScanline, 0, rawScanline.Length); @@ -362,7 +353,7 @@ namespace ImageSharp.Formats private byte[] GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, byte[] result) { // Palette images don't compress well with adaptive filtering. - if (this.PngColorType == PngColorType.Palette || this.bitDepth < 8) + if (this.pngColorType == PngColorType.Palette || this.bitDepth < 8) { NoneFilter.Encode(rawScanline, result); return result; @@ -436,7 +427,7 @@ namespace ImageSharp.Formats /// The private int CalculateBytesPerPixel() { - switch (this.PngColorType) + switch (this.pngColorType) { case PngColorType.Grayscale: return 1; @@ -488,18 +479,18 @@ namespace ImageSharp.Formats private QuantizedImage WritePaletteChunk(Stream stream, PngHeader header, ImageBase image) where TColor : struct, IPixel { - if (this.Quality > 256) + if (this.quality > 256) { return null; } - if (this.Quantizer == null) + if (this.quantizer == null) { - this.Quantizer = new OctreeQuantizer(); + this.quantizer = new OctreeQuantizer(); } // Quantize the image returning a palette. This boxing is icky. - QuantizedImage quantized = ((IQuantizer)this.Quantizer).Quantize(image, this.Quality); + QuantizedImage quantized = ((IQuantizer)this.quantizer).Quantize(image, this.quality); // Grab the palette and write it to the stream. TColor[] palette = quantized.Palette; @@ -524,7 +515,7 @@ namespace ImageSharp.Formats colorTable[offset + 1] = bytes[1]; colorTable[offset + 2] = bytes[2]; - if (alpha <= this.Threshold) + if (alpha <= this.options.Threshold) { transparentPixels.Add((byte)offset); } @@ -578,9 +569,9 @@ namespace ImageSharp.Formats /// The containing image data. private void WriteGammaChunk(Stream stream) { - if (this.WriteGamma) + if (this.options.WriteGamma) { - int gammaValue = (int)(this.Gamma * 100000F); + int gammaValue = (int)(this.options.Gamma * 100000F); byte[] size = BitConverter.GetBytes(gammaValue); @@ -608,7 +599,7 @@ namespace ImageSharp.Formats int resultLength = bytesPerScanline + 1; byte[] result = new byte[resultLength]; - if (this.PngColorType != PngColorType.Palette) + if (this.pngColorType != PngColorType.Palette) { this.sub = new byte[resultLength]; this.up = new byte[resultLength]; @@ -622,7 +613,7 @@ namespace ImageSharp.Formats try { memoryStream = new MemoryStream(); - using (ZlibDeflateStream deflateStream = new ZlibDeflateStream(memoryStream, this.CompressionLevel)) + using (ZlibDeflateStream deflateStream = new ZlibDeflateStream(memoryStream, this.options.CompressionLevel)) { for (int y = 0; y < this.height; y++) { diff --git a/src/ImageSharp.Formats.Png/PngEncoderOptions.cs b/src/ImageSharp.Formats.Png/PngEncoderOptions.cs new file mode 100644 index 000000000..9e6e851de --- /dev/null +++ b/src/ImageSharp.Formats.Png/PngEncoderOptions.cs @@ -0,0 +1,88 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats +{ + using Quantizers; + + /// + /// Encapsulates the options for the . + /// + public sealed class PngEncoderOptions : EncoderOptions, IPngEncoderOptions + { + /// + /// Initializes a new instance of the class. + /// + public PngEncoderOptions() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The options for the encoder. + private PngEncoderOptions(IEncoderOptions options) + : base(options) + { + } + + /// + /// Gets or sets the quality of output for images. + /// + public int Quality { get; set; } + + /// + /// Gets or sets the png color type + /// + public PngColorType PngColorType { get; set; } = PngColorType.RgbWithAlpha; + + /// + /// Gets or sets the compression level 1-9. + /// Defaults to 6. + /// + public int CompressionLevel { get; set; } = 6; + + /// + /// Gets or sets the gamma value, that will be written + /// the the stream, when the property + /// is set to true. The default value is 2.2F. + /// + /// The gamma value of the image. + public float Gamma { get; set; } = 2.2F; + + /// + /// Gets or sets quantizer for reducing the color count. + /// + public IQuantizer Quantizer { get; set; } + + /// + /// Gets or sets the transparency threshold. + /// + public byte Threshold { get; set; } = 0; + + /// + /// Gets or sets a value indicating whether this instance should write + /// gamma information to the stream. The default value is false. + /// + public bool WriteGamma { get; set; } + + /// + /// Converts the options to a instance with a + /// cast or by creating a new instance with the specfied options. + /// + /// The options for the encoder. + /// The options for the . + internal static IPngEncoderOptions Create(IEncoderOptions options) + { + IPngEncoderOptions pngOptions = options as IPngEncoderOptions; + if (pngOptions != null) + { + return pngOptions; + } + + return new PngEncoderOptions(options); + } + } +} From 35fb2974dc9f987ad842aa27bb0ebb442677d824 Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Wed, 22 Feb 2017 17:24:01 +0100 Subject: [PATCH 108/142] Refactored the Create method for the encoder/decoder options. --- src/ImageSharp.Formats.Bmp/BmpEncoderOptions.cs | 8 +------- src/ImageSharp.Formats.Gif/GifDecoderOptions.cs | 8 +------- src/ImageSharp.Formats.Gif/GifEncoderOptions.cs | 8 +------- src/ImageSharp.Formats.Jpeg/JpegEncoderOptions.cs | 8 +------- src/ImageSharp.Formats.Png/PngDecoderOptions.cs | 8 +------- src/ImageSharp.Formats.Png/PngEncoderOptions.cs | 8 +------- 6 files changed, 6 insertions(+), 42 deletions(-) diff --git a/src/ImageSharp.Formats.Bmp/BmpEncoderOptions.cs b/src/ImageSharp.Formats.Bmp/BmpEncoderOptions.cs index 2c06d8a66..a0f9ff8e0 100644 --- a/src/ImageSharp.Formats.Bmp/BmpEncoderOptions.cs +++ b/src/ImageSharp.Formats.Bmp/BmpEncoderOptions.cs @@ -39,13 +39,7 @@ namespace ImageSharp.Formats /// The options for the . internal static IBmpEncoderOptions Create(IEncoderOptions options) { - IBmpEncoderOptions bmpOptions = options as IBmpEncoderOptions; - if (bmpOptions != null) - { - return bmpOptions; - } - - return new BmpEncoderOptions(options); + return options as IBmpEncoderOptions ?? new BmpEncoderOptions(options); } } } diff --git a/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs b/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs index 8722c5fe8..bc7709f75 100644 --- a/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs +++ b/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs @@ -41,13 +41,7 @@ namespace ImageSharp.Formats /// The options for the . internal static IGifDecoderOptions Create(IDecoderOptions options) { - IGifDecoderOptions gifOptions = options as IGifDecoderOptions; - if (gifOptions != null) - { - return gifOptions; - } - - return new GifDecoderOptions(options); + return options as IGifDecoderOptions ?? new GifDecoderOptions(options); } } } diff --git a/src/ImageSharp.Formats.Gif/GifEncoderOptions.cs b/src/ImageSharp.Formats.Gif/GifEncoderOptions.cs index 94cad9603..5d7c6e40b 100644 --- a/src/ImageSharp.Formats.Gif/GifEncoderOptions.cs +++ b/src/ImageSharp.Formats.Gif/GifEncoderOptions.cs @@ -59,13 +59,7 @@ namespace ImageSharp.Formats /// The options for the . internal static IGifEncoderOptions Create(IEncoderOptions options) { - IGifEncoderOptions gifOptions = options as IGifEncoderOptions; - if (gifOptions != null) - { - return gifOptions; - } - - return new GifEncoderOptions(options); + return options as IGifEncoderOptions ?? new GifEncoderOptions(options); } } } diff --git a/src/ImageSharp.Formats.Jpeg/JpegEncoderOptions.cs b/src/ImageSharp.Formats.Jpeg/JpegEncoderOptions.cs index 6be36627c..454afa6ae 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegEncoderOptions.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegEncoderOptions.cs @@ -50,13 +50,7 @@ namespace ImageSharp.Formats /// The options for the . internal static IJpegEncoderOptions Create(IEncoderOptions options) { - IJpegEncoderOptions jpegOptions = options as IJpegEncoderOptions; - if (jpegOptions != null) - { - return jpegOptions; - } - - return new JpegEncoderOptions(options); + return options as IJpegEncoderOptions ?? new JpegEncoderOptions(options); } } } diff --git a/src/ImageSharp.Formats.Png/PngDecoderOptions.cs b/src/ImageSharp.Formats.Png/PngDecoderOptions.cs index 83716e5d1..e8990ec45 100644 --- a/src/ImageSharp.Formats.Png/PngDecoderOptions.cs +++ b/src/ImageSharp.Formats.Png/PngDecoderOptions.cs @@ -43,13 +43,7 @@ namespace ImageSharp.Formats /// The options for the . internal static IPngDecoderOptions Create(IDecoderOptions options) { - IPngDecoderOptions pngOptions = options as IPngDecoderOptions; - if (pngOptions != null) - { - return pngOptions; - } - - return new PngDecoderOptions(options); + return options as IPngDecoderOptions ?? new PngDecoderOptions(options); } } } diff --git a/src/ImageSharp.Formats.Png/PngEncoderOptions.cs b/src/ImageSharp.Formats.Png/PngEncoderOptions.cs index 9e6e851de..2891f1974 100644 --- a/src/ImageSharp.Formats.Png/PngEncoderOptions.cs +++ b/src/ImageSharp.Formats.Png/PngEncoderOptions.cs @@ -76,13 +76,7 @@ namespace ImageSharp.Formats /// The options for the . internal static IPngEncoderOptions Create(IEncoderOptions options) { - IPngEncoderOptions pngOptions = options as IPngEncoderOptions; - if (pngOptions != null) - { - return pngOptions; - } - - return new PngEncoderOptions(options); + return options as IPngEncoderOptions ?? new PngEncoderOptions(options); } } } From 347dcaa82599216c4015ea574af5b713636dcc46 Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Wed, 22 Feb 2017 17:47:26 +0100 Subject: [PATCH 109/142] Fixed text in comments. --- src/ImageSharp.Formats.Jpeg/IJpegEncoderOptions.cs | 3 --- src/ImageSharp.Formats.Jpeg/JpegEncoderOptions.cs | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/ImageSharp.Formats.Jpeg/IJpegEncoderOptions.cs b/src/ImageSharp.Formats.Jpeg/IJpegEncoderOptions.cs index 7d62dd4ef..a54517965 100644 --- a/src/ImageSharp.Formats.Jpeg/IJpegEncoderOptions.cs +++ b/src/ImageSharp.Formats.Jpeg/IJpegEncoderOptions.cs @@ -14,9 +14,6 @@ namespace ImageSharp.Formats /// Gets the quality, that will be used to encode the image. Quality /// index must be between 0 and 100 (compression from max to min). /// - /// - /// If the quality is less than or equal to 80, the subsampling ratio will switch to - /// /// The quality of the jpg image from 0 to 100. int Quality { get; } diff --git a/src/ImageSharp.Formats.Jpeg/JpegEncoderOptions.cs b/src/ImageSharp.Formats.Jpeg/JpegEncoderOptions.cs index 454afa6ae..73e483164 100644 --- a/src/ImageSharp.Formats.Jpeg/JpegEncoderOptions.cs +++ b/src/ImageSharp.Formats.Jpeg/JpegEncoderOptions.cs @@ -31,7 +31,7 @@ namespace ImageSharp.Formats /// index must be between 0 and 100 (compression from max to min). /// /// - /// If the quality is less than or equal to 80, the subsampling ratio will switch to + /// If the quality is less than or equal to 90, the subsampling ratio will switch to /// /// The quality of the jpg image from 0 to 100. public int Quality { get; set; } From 48baea5ffef6dfb3eefb592b7a3c58acacb28b64 Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Wed, 22 Feb 2017 17:50:03 +0100 Subject: [PATCH 110/142] Added missing initialization of the quantizer field. --- src/ImageSharp.Formats.Png/PngEncoderCore.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ImageSharp.Formats.Png/PngEncoderCore.cs b/src/ImageSharp.Formats.Png/PngEncoderCore.cs index 8a00c40b2..7950d260c 100644 --- a/src/ImageSharp.Formats.Png/PngEncoderCore.cs +++ b/src/ImageSharp.Formats.Png/PngEncoderCore.cs @@ -146,6 +146,7 @@ namespace ImageSharp.Formats this.quality = this.quality > 0 ? this.quality.Clamp(1, int.MaxValue) : int.MaxValue; this.pngColorType = this.options.PngColorType; + this.quantizer = this.options.Quantizer; // Set correct color type if the color count is 256 or less. if (this.quality <= 256) From 052ecdc337c40fef91535e952c9f97e880be8389 Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Wed, 22 Feb 2017 18:12:35 +0100 Subject: [PATCH 111/142] Changed optional arguments into overloads. --- src/ImageSharp.Formats.Gif/ImageExtensions.cs | 18 +- .../ImageExtensions.cs | 18 +- src/ImageSharp.Formats.Png/ImageExtensions.cs | 18 +- src/ImageSharp/Image.cs | 132 ++++++++++- src/ImageSharp/Image/Image{TColor}.cs | 215 +++++++++++++++++- tests/ImageSharp.Tests/Image/ImageTests.cs | 2 +- 6 files changed, 387 insertions(+), 16 deletions(-) diff --git a/src/ImageSharp.Formats.Gif/ImageExtensions.cs b/src/ImageSharp.Formats.Gif/ImageExtensions.cs index dcf4ab29a..1ba03ed35 100644 --- a/src/ImageSharp.Formats.Gif/ImageExtensions.cs +++ b/src/ImageSharp.Formats.Gif/ImageExtensions.cs @@ -15,6 +15,22 @@ namespace ImageSharp /// public static partial class ImageExtensions { + /// + /// Saves the image to the given stream with the gif format. + /// + /// The pixel format. + /// The image this method extends. + /// The stream to save the image to. + /// Thrown if the stream is null. + /// + /// The . + /// + public static Image SaveAsGif(this Image source, Stream stream) + where TColor : struct, IPixel + { + return SaveAsGif(source, stream, null); + } + /// /// Saves the image to the given stream with the gif format. /// @@ -26,7 +42,7 @@ namespace ImageSharp /// /// The . /// - public static Image SaveAsGif(this Image source, Stream stream, IGifEncoderOptions options = null) + public static Image SaveAsGif(this Image source, Stream stream, IGifEncoderOptions options) where TColor : struct, IPixel { GifEncoder encoder = new GifEncoder(); diff --git a/src/ImageSharp.Formats.Jpeg/ImageExtensions.cs b/src/ImageSharp.Formats.Jpeg/ImageExtensions.cs index 311bcc643..351275ebb 100644 --- a/src/ImageSharp.Formats.Jpeg/ImageExtensions.cs +++ b/src/ImageSharp.Formats.Jpeg/ImageExtensions.cs @@ -15,6 +15,22 @@ namespace ImageSharp /// public static partial class ImageExtensions { + /// + /// Saves the image to the given stream with the jpeg format. + /// + /// The pixel format. + /// The image this method extends. + /// The stream to save the image to. + /// Thrown if the stream is null. + /// + /// The . + /// + public static Image SaveAsJpeg(this Image source, Stream stream) + where TColor : struct, IPixel + { + return SaveAsJpeg(source, stream, null); + } + /// /// Saves the image to the given stream with the jpeg format. /// @@ -26,7 +42,7 @@ namespace ImageSharp /// /// The . /// - public static Image SaveAsJpeg(this Image source, Stream stream, IJpegEncoderOptions options = null) + public static Image SaveAsJpeg(this Image source, Stream stream, IJpegEncoderOptions options) where TColor : struct, IPixel { JpegEncoder encoder = new JpegEncoder(); diff --git a/src/ImageSharp.Formats.Png/ImageExtensions.cs b/src/ImageSharp.Formats.Png/ImageExtensions.cs index f08ab8ee7..79e96175c 100644 --- a/src/ImageSharp.Formats.Png/ImageExtensions.cs +++ b/src/ImageSharp.Formats.Png/ImageExtensions.cs @@ -14,6 +14,22 @@ namespace ImageSharp /// public static partial class ImageExtensions { + /// + /// Saves the image to the given stream with the png format. + /// + /// The pixel format. + /// The image this method extends. + /// The stream to save the image to. + /// Thrown if the stream is null. + /// + /// The . + /// + public static Image SaveAsPng(this Image source, Stream stream) + where TColor : struct, IPixel + { + return SaveAsPng(source, stream, null); + } + /// /// Saves the image to the given stream with the png format. /// @@ -25,7 +41,7 @@ namespace ImageSharp /// /// The . /// - public static Image SaveAsPng(this Image source, Stream stream, IPngEncoderOptions options = null) + public static Image SaveAsPng(this Image source, Stream stream, IPngEncoderOptions options) where TColor : struct, IPixel { PngEncoder encoder = new PngEncoder(); diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index b1c1252ab..af31eff79 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -31,6 +31,48 @@ namespace ImageSharp { } + /// + /// Initializes a new instance of the class. + /// + /// + /// The stream containing image information. + /// + /// Thrown if the is null. + public Image(Stream stream) + : base(stream, null, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The stream containing image information. + /// + /// + /// The options for the decoder. + /// + /// Thrown if the is null. + public Image(Stream stream, IDecoderOptions options) + : base(stream, options, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The stream containing image information. + /// + /// + /// The configuration providing initialization code which allows extending the library. + /// + /// Thrown if the is null. + public Image(Stream stream, Configuration configuration) + : base(stream, null, configuration) + { + } + /// /// Initializes a new instance of the class. /// @@ -44,12 +86,54 @@ namespace ImageSharp /// The configuration providing initialization code which allows extending the library. /// /// Thrown if the is null. - public Image(Stream stream, IDecoderOptions options = null, Configuration configuration = null) + public Image(Stream stream, IDecoderOptions options, Configuration configuration) : base(stream, options, configuration) { } #if !NETSTANDARD1_1 + /// + /// Initializes a new instance of the class. + /// + /// + /// A file path to read image information. + /// + /// Thrown if the is null. + public Image(string filePath) + : base(filePath, null, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// A file path to read image information. + /// + /// + /// The options for the decoder. + /// + /// Thrown if the is null. + public Image(string filePath, IDecoderOptions options) + : base(filePath, options, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// A file path to read image information. + /// + /// + /// The configuration providing initialization code which allows extending the library. + /// + /// Thrown if the is null. + public Image(string filePath, Configuration configuration) + : base(filePath, null, configuration) + { + } + /// /// Initializes a new instance of the class. /// @@ -63,12 +147,54 @@ namespace ImageSharp /// The configuration providing initialization code which allows extending the library. /// /// Thrown if the is null. - public Image(string filePath, IDecoderOptions options = null, Configuration configuration = null) + public Image(string filePath, IDecoderOptions options, Configuration configuration) : base(filePath, options, configuration) { } #endif + /// + /// Initializes a new instance of the class. + /// + /// + /// The byte array containing image information. + /// + /// Thrown if the is null. + public Image(byte[] bytes) + : base(bytes, null, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The byte array containing image information. + /// + /// + /// The options for the decoder. + /// + /// Thrown if the is null. + public Image(byte[] bytes, IDecoderOptions options) + : base(bytes, options, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The byte array containing image information. + /// + /// + /// The configuration providing initialization code which allows extending the library. + /// + /// Thrown if the is null. + public Image(byte[] bytes, Configuration configuration) + : base(bytes, null, configuration) + { + } + /// /// Initializes a new instance of the class. /// @@ -82,7 +208,7 @@ namespace ImageSharp /// The configuration providing initialization code which allows extending the library. /// /// Thrown if the is null. - public Image(byte[] bytes, IDecoderOptions options = null, Configuration configuration = null) + public Image(byte[] bytes, IDecoderOptions options, Configuration configuration) : base(bytes, options, configuration) { } diff --git a/src/ImageSharp/Image/Image{TColor}.cs b/src/ImageSharp/Image/Image{TColor}.cs index 69b99ce13..27dee5434 100644 --- a/src/ImageSharp/Image/Image{TColor}.cs +++ b/src/ImageSharp/Image/Image{TColor}.cs @@ -46,6 +46,48 @@ namespace ImageSharp this.CurrentImageFormat = this.Configuration.ImageFormats.First(); } + /// + /// Initializes a new instance of the class. + /// + /// + /// The stream containing image information. + /// + /// Thrown if the is null. + public Image(Stream stream) + : this(stream, null, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The stream containing image information. + /// + /// + /// The options for the decoder. + /// + /// Thrown if the is null. + public Image(Stream stream, IDecoderOptions options) + : this(stream, options, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The stream containing image information. + /// + /// + /// The configuration providing initialization code which allows extending the library. + /// + /// Thrown if the is null. + public Image(Stream stream, Configuration configuration) + : this(stream, null, configuration) + { + } + /// /// Initializes a new instance of the class. /// @@ -59,7 +101,7 @@ namespace ImageSharp /// The configuration providing initialization code which allows extending the library. /// /// Thrown if the is null. - public Image(Stream stream, IDecoderOptions options = null, Configuration configuration = null) + public Image(Stream stream, IDecoderOptions options, Configuration configuration) : base(configuration) { Guard.NotNull(stream, nameof(stream)); @@ -67,6 +109,48 @@ namespace ImageSharp } #if !NETSTANDARD1_1 + /// + /// Initializes a new instance of the class. + /// + /// + /// The file containing image information. + /// + /// Thrown if the is null. + public Image(string filePath) + : this(filePath, null, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The file containing image information. + /// + /// + /// The options for the decoder. + /// + /// Thrown if the is null. + public Image(string filePath, IDecoderOptions options) + : this(filePath, options, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The file containing image information. + /// + /// + /// The configuration providing initialization code which allows extending the library. + /// + /// Thrown if the is null. + public Image(string filePath, Configuration configuration) + : this(filePath, null, configuration) + { + } + /// /// Initializes a new instance of the class. /// @@ -80,7 +164,7 @@ namespace ImageSharp /// The configuration providing initialization code which allows extending the library. /// /// Thrown if the is null. - public Image(string filePath, IDecoderOptions options = null, Configuration configuration = null) + public Image(string filePath, IDecoderOptions options, Configuration configuration) : base(configuration) { Guard.NotNull(filePath, nameof(filePath)); @@ -91,6 +175,48 @@ namespace ImageSharp } #endif + /// + /// Initializes a new instance of the class. + /// + /// + /// The byte array containing image information. + /// + /// Thrown if the is null. + public Image(byte[] bytes) + : this(bytes, null, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The byte array containing image information. + /// + /// + /// The options for the decoder. + /// + /// Thrown if the is null. + public Image(byte[] bytes, IDecoderOptions options) + : this(bytes, options, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The byte array containing image information. + /// + /// + /// The configuration providing initialization code which allows extending the library. + /// + /// Thrown if the is null. + public Image(byte[] bytes, Configuration configuration) + : this(bytes, null, configuration) + { + } + /// /// Initializes a new instance of the class. /// @@ -104,7 +230,7 @@ namespace ImageSharp /// The configuration providing initialization code which allows extending the library. /// /// Thrown if the is null. - public Image(byte[] bytes, IDecoderOptions options = null, Configuration configuration = null) + public Image(byte[] bytes, IDecoderOptions options, Configuration configuration) : base(configuration) { Guard.NotNull(bytes, nameof(bytes)); @@ -202,6 +328,17 @@ namespace ImageSharp } } + /// + /// Saves the image to the given stream using the currently loaded image format. + /// + /// The stream to save the image to. + /// Thrown if the stream is null. + /// The + public Image Save(Stream stream) + { + return this.Save(stream, (IEncoderOptions)null); + } + /// /// Saves the image to the given stream using the currently loaded image format. /// @@ -209,13 +346,24 @@ namespace ImageSharp /// The options for the encoder. /// Thrown if the stream is null. /// The - public Image Save(Stream stream, IEncoderOptions options = null) + public Image Save(Stream stream, IEncoderOptions options) { Guard.NotNull(stream, nameof(stream)); this.CurrentImageFormat.Encoder.Encode(this, stream, options); return this; } + /// + /// Saves the image to the given stream using the given image format. + /// + /// The stream to save the image to. + /// The format to save the image as. + /// The + public Image Save(Stream stream, IImageFormat format) + { + return this.Save(stream, format, null); + } + /// /// Saves the image to the given stream using the given image format. /// @@ -223,7 +371,7 @@ namespace ImageSharp /// The format to save the image as. /// The options for the encoder. /// The - public Image Save(Stream stream, IImageFormat format, IEncoderOptions options = null) + public Image Save(Stream stream, IImageFormat format, IEncoderOptions options) { Guard.NotNull(stream, nameof(stream)); Guard.NotNull(format, nameof(format)); @@ -231,6 +379,20 @@ namespace ImageSharp return this; } + /// + /// Saves the image to the given stream using the given image encoder. + /// + /// The stream to save the image to. + /// The encoder to save the image with. + /// Thrown if the stream or encoder is null. + /// + /// The . + /// + public Image Save(Stream stream, IImageEncoder encoder) + { + return this.Save(stream, encoder, null); + } + /// /// Saves the image to the given stream using the given image encoder. /// @@ -241,7 +403,7 @@ namespace ImageSharp /// /// The . /// - public Image Save(Stream stream, IImageEncoder encoder, IEncoderOptions options = null) + public Image Save(Stream stream, IImageEncoder encoder, IEncoderOptions options) { Guard.NotNull(stream, nameof(stream)); Guard.NotNull(encoder, nameof(encoder)); @@ -257,6 +419,17 @@ namespace ImageSharp } #if !NETSTANDARD1_1 + /// + /// Saves the image to the given stream using the currently loaded image format. + /// + /// The file path to save the image to. + /// Thrown if the stream is null. + /// The + public Image Save(string filePath) + { + return this.Save(filePath, (IEncoderOptions)null); + } + /// /// Saves the image to the given stream using the currently loaded image format. /// @@ -264,7 +437,7 @@ namespace ImageSharp /// The options for the encoder. /// Thrown if the stream is null. /// The - public Image Save(string filePath, IEncoderOptions options = null) + public Image Save(string filePath, IEncoderOptions options) { string ext = Path.GetExtension(filePath).Trim('.'); IImageFormat format = this.Configuration.ImageFormats.SingleOrDefault(f => f.SupportedExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase)); @@ -276,6 +449,18 @@ namespace ImageSharp return this.Save(filePath, format); } + /// + /// Saves the image to the given stream using the currently loaded image format. + /// + /// The file path to save the image to. + /// The format to save the image as. + /// Thrown if the format is null. + /// The + public Image Save(string filePath, IImageFormat format) + { + return this.Save(filePath, format, null); + } + /// /// Saves the image to the given stream using the currently loaded image format. /// @@ -284,7 +469,7 @@ namespace ImageSharp /// The options for the encoder. /// Thrown if the format is null. /// The - public Image Save(string filePath, IImageFormat format, IEncoderOptions options = null) + public Image Save(string filePath, IImageFormat format, IEncoderOptions options) { Guard.NotNull(format, nameof(format)); using (FileStream fs = File.Create(filePath)) @@ -293,6 +478,18 @@ namespace ImageSharp } } + /// + /// Saves the image to the given stream using the currently loaded image format. + /// + /// The file path to save the image to. + /// The encoder to save the image with. + /// Thrown if the encoder is null. + /// The + public Image Save(string filePath, IImageEncoder encoder) + { + return this.Save(filePath, encoder, null); + } + /// /// Saves the image to the given stream using the currently loaded image format. /// @@ -301,7 +498,7 @@ namespace ImageSharp /// The options for the encoder. /// Thrown if the encoder is null. /// The - public Image Save(string filePath, IImageEncoder encoder, IEncoderOptions options = null) + public Image Save(string filePath, IImageEncoder encoder, IEncoderOptions options) { Guard.NotNull(encoder, nameof(encoder)); using (FileStream fs = File.Create(filePath)) diff --git a/tests/ImageSharp.Tests/Image/ImageTests.cs b/tests/ImageSharp.Tests/Image/ImageTests.cs index aea4330c6..0ed724fad 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.cs @@ -59,7 +59,7 @@ namespace ImageSharp.Tests ArgumentNullException ex = Assert.Throws( () => { - new Image(null); + new Image((string) null); }); } From c45b94548935d4bbb936f189f080a3a39eef645c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 23 Feb 2017 11:07:21 +1100 Subject: [PATCH 112/142] Fix #115 --- src/ImageSharp.Formats.Png/PngDecoderCore.cs | 9 +++------ tests/ImageSharp.Tests/FileTestBase.cs | 3 ++- tests/ImageSharp.Tests/TestImages.cs | 9 +++++---- .../TestImages/Formats/Png/chunklength2.png | 3 +++ 4 files changed, 13 insertions(+), 11 deletions(-) create mode 100644 tests/ImageSharp.Tests/TestImages/Formats/Png/chunklength2.png diff --git a/src/ImageSharp.Formats.Png/PngDecoderCore.cs b/src/ImageSharp.Formats.Png/PngDecoderCore.cs index 076770ce5..a7765342e 100644 --- a/src/ImageSharp.Formats.Png/PngDecoderCore.cs +++ b/src/ImageSharp.Formats.Png/PngDecoderCore.cs @@ -10,6 +10,7 @@ namespace ImageSharp.Formats using System.Collections.Generic; using System.IO; using System.Linq; + using System.Runtime.CompilerServices; using static ComparableExtensions; @@ -945,12 +946,7 @@ namespace ImageSharp.Formats private void ReadChunkLength(PngChunk chunk) { int numBytes = this.currentStream.Read(this.chunkLengthBuffer, 0, 4); - if (numBytes > 1 && numBytes <= 3) - { - throw new ImageFormatException("Image stream is not valid!"); - } - - if (numBytes <= 1) + if (numBytes < 4) { chunk.Length = -1; return; @@ -966,6 +962,7 @@ namespace ImageSharp.Formats /// /// Th current pass index /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] private int ComputeColumnsAdam7(int pass) { int width = this.header.Width; diff --git a/tests/ImageSharp.Tests/FileTestBase.cs b/tests/ImageSharp.Tests/FileTestBase.cs index b147e97e8..765ff3a42 100644 --- a/tests/ImageSharp.Tests/FileTestBase.cs +++ b/tests/ImageSharp.Tests/FileTestBase.cs @@ -30,7 +30,8 @@ namespace ImageSharp.Tests TestFile.Create(TestImages.Bmp.Car), // TestFile.Create(TestImages.Bmp.Neg_height), // Perf: Enable for local testing only TestFile.Create(TestImages.Png.Splash), - // TestFile.Create(TestImages.Png.ChunkLength), // Perf: Enable for local testing only + // TestFile.Create(TestImages.Png.ChunkLength1), // Perf: Enable for local testing only + // TestFile.Create(TestImages.Png.ChunkLength2), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.Powerpoint), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.Blur), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.Indexed), // Perf: Enable for local testing only diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 4924cc1de..f0a0e8dd8 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -25,18 +25,19 @@ namespace ImageSharp.Tests public const string SplashInterlaced = "Png/splash-interlaced.png"; public const string Interlaced = "Png/interlaced.png"; - // filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html + // Filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html public const string Filter0 = "Png/filter0.png"; public const string Filter1 = "Png/filter1.png"; public const string Filter2 = "Png/filter2.png"; public const string Filter3 = "Png/filter3.png"; public const string Filter4 = "Png/filter4.png"; - // filter changing per scanline + // Filter changing per scanline public const string FilterVar = "Png/filterVar.png"; - // Chunk length of 1 by end marker - public const string ChunkLength = "Png/chunklength1.png"; + // Odd chunk lengths + public const string ChunkLength1 = "Png/chunklength1.png"; + public const string ChunkLength2 = "Png/chunklength2.png"; } public static class Jpeg diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/chunklength2.png b/tests/ImageSharp.Tests/TestImages/Formats/Png/chunklength2.png new file mode 100644 index 000000000..85929cb1e --- /dev/null +++ b/tests/ImageSharp.Tests/TestImages/Formats/Png/chunklength2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b508f05da9f50edd87ed387abc8222620682ef2f59f036fd66ac7cbaf95ffe44 +size 12181 From ab0c5d01c9706f390d80bc966ea31e5db04d416a Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 23 Feb 2017 11:07:28 +1100 Subject: [PATCH 113/142] Inline filters --- src/ImageSharp.Formats.Png/Filters/AverageFilter.cs | 5 +++++ src/ImageSharp.Formats.Png/Filters/NoneFilter.cs | 3 +++ src/ImageSharp.Formats.Png/Filters/PaethFilter.cs | 4 ++++ src/ImageSharp.Formats.Png/Filters/SubFilter.cs | 4 ++++ src/ImageSharp.Formats.Png/Filters/UpFilter.cs | 4 ++++ 5 files changed, 20 insertions(+) diff --git a/src/ImageSharp.Formats.Png/Filters/AverageFilter.cs b/src/ImageSharp.Formats.Png/Filters/AverageFilter.cs index d5f810708..b4ec49946 100644 --- a/src/ImageSharp.Formats.Png/Filters/AverageFilter.cs +++ b/src/ImageSharp.Formats.Png/Filters/AverageFilter.cs @@ -5,6 +5,8 @@ namespace ImageSharp.Formats { + using System.Runtime.CompilerServices; + /// /// The Average filter uses the average of the two neighboring pixels (left and above) to predict /// the value of a pixel. @@ -19,6 +21,7 @@ namespace ImageSharp.Formats /// The previous scanline. /// The number of bytes per scanline /// The bytes per pixel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(byte[] scanline, byte[] previousScanline, int bytesPerScanline, int bytesPerPixel) { // Average(x) + floor((Raw(x-bpp)+Prior(x))/2) @@ -42,6 +45,7 @@ namespace ImageSharp.Formats /// The previous scanline. /// The filtered scanline result. /// The bytes per pixel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Encode(byte[] scanline, byte[] previousScanline, byte[] result, int bytesPerPixel) { // Average(x) = Raw(x) - floor((Raw(x-bpp)+Prior(x))/2) @@ -67,6 +71,7 @@ namespace ImageSharp.Formats /// The left byte /// The above byte /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Average(byte left, byte above) { return (left + above) >> 1; diff --git a/src/ImageSharp.Formats.Png/Filters/NoneFilter.cs b/src/ImageSharp.Formats.Png/Filters/NoneFilter.cs index e5787a944..5abd89296 100644 --- a/src/ImageSharp.Formats.Png/Filters/NoneFilter.cs +++ b/src/ImageSharp.Formats.Png/Filters/NoneFilter.cs @@ -6,6 +6,7 @@ namespace ImageSharp.Formats { using System; + using System.Runtime.CompilerServices; /// /// The None filter, the scanline is transmitted unmodified; it is only necessary to @@ -19,6 +20,7 @@ namespace ImageSharp.Formats /// /// The scanline to decode /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte[] Decode(byte[] scanline) { // No change required. @@ -30,6 +32,7 @@ namespace ImageSharp.Formats /// /// The scanline to encode /// The filtered scanline result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Encode(byte[] scanline, byte[] result) { // Insert a byte before the data. diff --git a/src/ImageSharp.Formats.Png/Filters/PaethFilter.cs b/src/ImageSharp.Formats.Png/Filters/PaethFilter.cs index ff208f3d7..a43d4f080 100644 --- a/src/ImageSharp.Formats.Png/Filters/PaethFilter.cs +++ b/src/ImageSharp.Formats.Png/Filters/PaethFilter.cs @@ -6,6 +6,7 @@ namespace ImageSharp.Formats { using System; + using System.Runtime.CompilerServices; /// /// The Paeth filter computes a simple linear function of the three neighboring pixels (left, above, upper left), @@ -22,6 +23,7 @@ namespace ImageSharp.Formats /// The previous scanline. /// The number of bytes per scanline /// The bytes per pixel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(byte[] scanline, byte[] previousScanline, int bytesPerScanline, int bytesPerPixel) { // Paeth(x) + PaethPredictor(Raw(x-bpp), Prior(x), Prior(x-bpp)) @@ -46,6 +48,7 @@ namespace ImageSharp.Formats /// The previous scanline. /// The filtered scanline result. /// The bytes per pixel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Encode(byte[] scanline, byte[] previousScanline, byte[] result, int bytesPerPixel) { // Paeth(x) = Raw(x) - PaethPredictor(Raw(x-bpp), Prior(x), Prior(x - bpp)) @@ -76,6 +79,7 @@ namespace ImageSharp.Formats /// /// The . /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static byte PaethPredicator(byte left, byte above, byte upperLeft) { int p = left + above - upperLeft; diff --git a/src/ImageSharp.Formats.Png/Filters/SubFilter.cs b/src/ImageSharp.Formats.Png/Filters/SubFilter.cs index 65e86f4f1..4610ed0ae 100644 --- a/src/ImageSharp.Formats.Png/Filters/SubFilter.cs +++ b/src/ImageSharp.Formats.Png/Filters/SubFilter.cs @@ -5,6 +5,8 @@ namespace ImageSharp.Formats { + using System.Runtime.CompilerServices; + /// /// The Sub filter transmits the difference between each byte and the value of the corresponding byte /// of the prior pixel. @@ -18,6 +20,7 @@ namespace ImageSharp.Formats /// The scanline to decode /// The number of bytes per scanline /// The bytes per pixel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(byte[] scanline, int bytesPerScanline, int bytesPerPixel) { // Sub(x) + Raw(x-bpp) @@ -37,6 +40,7 @@ namespace ImageSharp.Formats /// The scanline to encode /// The filtered scanline result. /// The bytes per pixel. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Encode(byte[] scanline, byte[] result, int bytesPerPixel) { // Sub(x) = Raw(x) - Raw(x-bpp) diff --git a/src/ImageSharp.Formats.Png/Filters/UpFilter.cs b/src/ImageSharp.Formats.Png/Filters/UpFilter.cs index 036862ddc..525f50d0f 100644 --- a/src/ImageSharp.Formats.Png/Filters/UpFilter.cs +++ b/src/ImageSharp.Formats.Png/Filters/UpFilter.cs @@ -5,6 +5,8 @@ namespace ImageSharp.Formats { + using System.Runtime.CompilerServices; + /// /// The Up filter is just like the Sub filter except that the pixel immediately above the current pixel, /// rather than just to its left, is used as the predictor. @@ -18,6 +20,7 @@ namespace ImageSharp.Formats /// The scanline to decode /// The previous scanline. /// The number of bytes per scanline + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Decode(byte[] scanline, byte[] previousScanline, int bytesPerScanline) { // Up(x) + Prior(x) @@ -39,6 +42,7 @@ namespace ImageSharp.Formats /// The scanline to encode /// The previous scanline. /// The filtered scanline result. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Encode(byte[] scanline, byte[] previousScanline, byte[] result) { // Up(x) = Raw(x) - Prior(x) From f6be42a48690a8e168bc9d9aee2d4471956235ff Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 24 Feb 2017 00:44:06 +1100 Subject: [PATCH 114/142] Branding.... No PR on this one. I AM THE ABSOLUTE RULER!! :tongue: --- build/icons/imagesharp-logo-128.png | 4 +- build/icons/imagesharp-logo-256.png | 4 +- build/icons/imagesharp-logo-32.png | 4 +- build/icons/imagesharp-logo-512.png | 4 +- build/icons/imagesharp-logo-64.png | 4 +- build/icons/imagesharp-logo.png | 4 +- build/icons/imagesharp-logo.svg | 60 +---------------------------- 7 files changed, 13 insertions(+), 71 deletions(-) diff --git a/build/icons/imagesharp-logo-128.png b/build/icons/imagesharp-logo-128.png index 81e29fee5..a7be6e512 100644 --- a/build/icons/imagesharp-logo-128.png +++ b/build/icons/imagesharp-logo-128.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:01f79400a4a77c764273a97fbc76982546e88510fa4cc64a7b2e83e265b0e141 -size 2490 +oid sha256:7ae6327b2d554c25640756650865eeb5e96aa5bd58b3922ae5247400dc516ed0 +size 6622 diff --git a/build/icons/imagesharp-logo-256.png b/build/icons/imagesharp-logo-256.png index 5b78542cb..2fbd57d8c 100644 --- a/build/icons/imagesharp-logo-256.png +++ b/build/icons/imagesharp-logo-256.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5598d4cb5bad33aefc1084c4f389b071143a59505f00f7b7831c81254f1140f8 -size 4225 +oid sha256:a78ad79663c58544bba12b431ed83659e60a12a4fcb9b358e7a1c22bc76034bc +size 14020 diff --git a/build/icons/imagesharp-logo-32.png b/build/icons/imagesharp-logo-32.png index 31e32300d..3a8e28e85 100644 --- a/build/icons/imagesharp-logo-32.png +++ b/build/icons/imagesharp-logo-32.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:536e75abeaa2c35f34a95d34bee4f8bd13cf5514a979960566945da4ec8827d1 -size 979 +oid sha256:a911690b45b1614ca0dcd08d09ec4d257edc244ccc352e6384370cc197cac1e0 +size 1479 diff --git a/build/icons/imagesharp-logo-512.png b/build/icons/imagesharp-logo-512.png index ee02fb650..eabef882f 100644 --- a/build/icons/imagesharp-logo-512.png +++ b/build/icons/imagesharp-logo-512.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c8288a69f4182b25e04ba6419b452c6cdbca0013856352c0aeb08abc67f67e4d -size 7951 +oid sha256:1381533f41d07c6d3d2604e20c605cd7dd70654056cffd73e67bdc75353476b2 +size 31672 diff --git a/build/icons/imagesharp-logo-64.png b/build/icons/imagesharp-logo-64.png index 9919f97d6..a45ea0afc 100644 --- a/build/icons/imagesharp-logo-64.png +++ b/build/icons/imagesharp-logo-64.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ab6d98a7a55caf570016a62a2f4d72842e22cbe03460ad60a4dc196b7b1d303 -size 1698 +oid sha256:d28e405019add224bd0156332e16e152c6b3822de406c7d5a79cc78e15396a8f +size 3143 diff --git a/build/icons/imagesharp-logo.png b/build/icons/imagesharp-logo.png index 0f80ceeaa..e0ab59b8d 100644 --- a/build/icons/imagesharp-logo.png +++ b/build/icons/imagesharp-logo.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f20a3b2613811efa3455ee65b532284d88636d8796ee2279c08a584aa01744e -size 19129 +oid sha256:6d21730b397705c2312383d1f7c3f614d2bc2daecf3f9b667f8cbf48a679b4da +size 59600 diff --git a/build/icons/imagesharp-logo.svg b/build/icons/imagesharp-logo.svg index 9638e9785..2df3cc80c 100644 --- a/build/icons/imagesharp-logo.svg +++ b/build/icons/imagesharp-logo.svg @@ -1,59 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file From 3f16b57988d032c6e51f15536619e53c358d1b47 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 24 Feb 2017 07:56:09 +1100 Subject: [PATCH 115/142] Re export images [skip ci] --- build/icons/imagesharp-logo-128.png | 4 ++-- build/icons/imagesharp-logo-256.png | 4 ++-- build/icons/imagesharp-logo-32.png | 4 ++-- build/icons/imagesharp-logo-512.png | 4 ++-- build/icons/imagesharp-logo-64.png | 4 ++-- build/icons/imagesharp-logo.png | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/build/icons/imagesharp-logo-128.png b/build/icons/imagesharp-logo-128.png index a7be6e512..d9ae997ba 100644 --- a/build/icons/imagesharp-logo-128.png +++ b/build/icons/imagesharp-logo-128.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ae6327b2d554c25640756650865eeb5e96aa5bd58b3922ae5247400dc516ed0 -size 6622 +oid sha256:47f14bb7d24f7228cd8833d8d1881a72750b2c7813f391bd2a0dd0eeea936841 +size 6569 diff --git a/build/icons/imagesharp-logo-256.png b/build/icons/imagesharp-logo-256.png index 2fbd57d8c..f1e67dd78 100644 --- a/build/icons/imagesharp-logo-256.png +++ b/build/icons/imagesharp-logo-256.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a78ad79663c58544bba12b431ed83659e60a12a4fcb9b358e7a1c22bc76034bc -size 14020 +oid sha256:757ec2f45cc5f9c2083fc65a236100f1a7776eee16bd1095a550e05783106a9f +size 13949 diff --git a/build/icons/imagesharp-logo-32.png b/build/icons/imagesharp-logo-32.png index 3a8e28e85..80435989a 100644 --- a/build/icons/imagesharp-logo-32.png +++ b/build/icons/imagesharp-logo-32.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a911690b45b1614ca0dcd08d09ec4d257edc244ccc352e6384370cc197cac1e0 -size 1479 +oid sha256:0f3a5375ce20321c2cfdc888a21dcb629d3e6a85641df5cca7c66e5b2a5f70f6 +size 1439 diff --git a/build/icons/imagesharp-logo-512.png b/build/icons/imagesharp-logo-512.png index eabef882f..a5f880e3a 100644 --- a/build/icons/imagesharp-logo-512.png +++ b/build/icons/imagesharp-logo-512.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1381533f41d07c6d3d2604e20c605cd7dd70654056cffd73e67bdc75353476b2 -size 31672 +oid sha256:0e4cd18406375999c2bee1c39ad439b37f9524485d6e247ab0f14d2eb90a65f3 +size 31256 diff --git a/build/icons/imagesharp-logo-64.png b/build/icons/imagesharp-logo-64.png index a45ea0afc..f59e202bf 100644 --- a/build/icons/imagesharp-logo-64.png +++ b/build/icons/imagesharp-logo-64.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d28e405019add224bd0156332e16e152c6b3822de406c7d5a79cc78e15396a8f -size 3143 +oid sha256:fa25e5dbe84f942107a1c29f4f68ff2a73f497412ae91b6e60fc5464bc9b5f05 +size 3132 diff --git a/build/icons/imagesharp-logo.png b/build/icons/imagesharp-logo.png index e0ab59b8d..e0f1854cc 100644 --- a/build/icons/imagesharp-logo.png +++ b/build/icons/imagesharp-logo.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d21730b397705c2312383d1f7c3f614d2bc2daecf3f9b667f8cbf48a679b4da -size 59600 +oid sha256:e4217fe820af06a593903441f0719cab1ca650fd4de795f0e6808c4240a89819 +size 59646 From a97e6c9ec8180eea04c70ae95b18e8a814d17f4f Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 24 Feb 2017 09:15:09 +1100 Subject: [PATCH 116/142] Update readme [skip ci] --- README.md | 2 +- build/icons/imagesharp-logo-heading.png | 3 +++ features.md | 16 +++++++++++++--- 3 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 build/icons/imagesharp-logo-heading.png diff --git a/README.md b/README.md index cbe03bb4e..9d5c9788a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# ImageSharp +# ImageSharp **ImageSharp** is a new cross-platform 2D graphics API designed to allow the processing of images without the use of `System.Drawing`. diff --git a/build/icons/imagesharp-logo-heading.png b/build/icons/imagesharp-logo-heading.png new file mode 100644 index 000000000..4cd4e0390 --- /dev/null +++ b/build/icons/imagesharp-logo-heading.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f79e49b0965696c2106dc3654aa18ae7334e07d2e691c0b96acddb8150c25a88 +size 8609 diff --git a/features.md b/features.md index 764d7c4a6..6bc5630ee 100644 --- a/features.md +++ b/features.md @@ -10,13 +10,23 @@ We've achieved a lot so far and hope to do a lot more in the future. We're alway - [x] Bmp (Read: 32bit, 24bit, 16 bit. Write: 32bit, 24bit just now) - [x] Png (Read: Rgb, Rgba, Grayscale, Grayscale + alpha, Palette. Write: Rgb, Rgba, Grayscale, Grayscale + alpha, Palette) Supports interlaced decoding - [x] Gif (Includes animated) - - [ ] Tiff + - [ ] Tiff (Help needed) - **Metadata** - [x] EXIF Read/Write (Jpeg just now) -- **Quantizers (IQuantizer with alpha channel support + thresholding)** +- **Quantizers (IQuantizer with alpha channel support, dithering, and thresholding)** - [x] Octree - [x] Xiaolin Wu - [x] Palette +- **DIthering (Error diffusion and Ordered)** + - [x] Atkinson + - [x] Burks + - [x] FloydSteinburg + - [x] JarvisJudiceNinke + - [x] Sieera2 + - [x] Sierra3 + - [x] SerraLite + - [x] Bayer + - [x] Ordered - **Basic color structs with implicit operators.** - [x] Color - 32bit color in RGBA order (IPackedPixel\). - [x] Bgra32 @@ -133,5 +143,5 @@ We've achieved a lot so far and hope to do a lot more in the future. We're alway - [x] DrawImage - [ ] Gradient brush (Need help) - **DrawingText** - - [x] DrawString (Single variant support just now, no italic,bold) + - [ ] DrawString (In-progress. Single variant support just now, no italic,bold) - Other stuff I haven't thought of. \ No newline at end of file From f0089cff96f5a3ca4770d02ba2f67a88ab942a6c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 24 Feb 2017 09:20:25 +1100 Subject: [PATCH 117/142] Reduce logo height [skip ci] --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 9d5c9788a..87708cf05 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -# ImageSharp **ImageSharp** is a new cross-platform 2D graphics API designed to allow the processing of images without the use of `System.Drawing`. From 525c1cf98d18c394251026cb2efcdf6984627c6b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 24 Feb 2017 09:20:45 +1100 Subject: [PATCH 118/142] Drop px [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 87708cf05..d44a15bfd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ +# ImageSharp **ImageSharp** is a new cross-platform 2D graphics API designed to allow the processing of images without the use of `System.Drawing`. From 2032017dbf2c080176fab3e4eedc37e0e54bec8a Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 25 Feb 2017 13:58:02 +1100 Subject: [PATCH 119/142] Prevent logo squish on mobile [skip ci] --- README.md | 2 +- build/icons/imagesharp-logo-heading.png | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d44a15bfd..9d5c9788a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# ImageSharp +# ImageSharp **ImageSharp** is a new cross-platform 2D graphics API designed to allow the processing of images without the use of `System.Drawing`. diff --git a/build/icons/imagesharp-logo-heading.png b/build/icons/imagesharp-logo-heading.png index 4cd4e0390..20779215f 100644 --- a/build/icons/imagesharp-logo-heading.png +++ b/build/icons/imagesharp-logo-heading.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f79e49b0965696c2106dc3654aa18ae7334e07d2e691c0b96acddb8150c25a88 -size 8609 +oid sha256:bf2335642c6fd291befa0b203dbfb3387d99434369399b35aeea037c0f9eba45 +size 10474 From c2904aa234be00d62ac59cb58a02b1795d5cfd29 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sat, 25 Feb 2017 17:46:20 +0000 Subject: [PATCH 120/142] Update SixLabors.Shapes version --- src/ImageSharp.Drawing.Paths/project.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp.Drawing.Paths/project.json b/src/ImageSharp.Drawing.Paths/project.json index bf6b1fae8..b761233c3 100644 --- a/src/ImageSharp.Drawing.Paths/project.json +++ b/src/ImageSharp.Drawing.Paths/project.json @@ -44,7 +44,7 @@ "ImageSharp.Drawing": { "target": "project" }, - "SixLabors.Shapes": "0.1.0-alpha0005", + "SixLabors.Shapes": "0.1.0-alpha0006", "StyleCop.Analyzers": { "version": "1.0.0", "type": "build" From c3e6980609883bc6433473541c6f2751480c3ac9 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sat, 25 Feb 2017 17:59:11 +0000 Subject: [PATCH 121/142] Fix drawing on transparent background --- src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs | 1 - src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs | 1 - tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs | 3 +-- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs index 913293ff3..95f4ab472 100644 --- a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs @@ -110,7 +110,6 @@ namespace ImageSharp.Drawing.Processors Vector4 sourceVector = color.Color.ToVector4(); Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); - finalColor.W = backgroundVector.W; TColor packed = default(TColor); packed.PackFromVector4(finalColor); diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs index fed97275d..4f468c707 100644 --- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs @@ -202,7 +202,6 @@ namespace ImageSharp.Drawing.Processors Vector4 sourceVector = applicator[x, y].ToVector4(); Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); - finalColor.W = backgroundVector.W; TColor packed = default(TColor); packed.PackFromVector4(finalColor); diff --git a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs index a41afd333..1d3ead81f 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs @@ -32,7 +32,6 @@ namespace ImageSharp.Tests.Drawing using (FileStream output = File.OpenWrite($"{path}/Simple.png")) { image - .BackgroundColor(Color.Blue) .FillPolygon(Color.HotPink, simplePath, new GraphicsOptions(true)) .Save(output); } @@ -45,7 +44,7 @@ namespace ImageSharp.Tests.Drawing Assert.Equal(Color.HotPink, sourcePixels[50, 50]); - Assert.Equal(Color.Blue, sourcePixels[2, 2]); + Assert.NotEqual(Color.HotPink, sourcePixels[2, 2]); } } } From 784ded5ce084d539bc7729bd059830c0a8fbec4a Mon Sep 17 00:00:00 2001 From: Toxantron Date: Sun, 26 Feb 2017 19:02:00 +0100 Subject: [PATCH 122/142] Benchmark running on linux Script for benchmarks Fix from cherry-picking CPU governor not necessary --- .vscode/tasks.json | 7 +++++++ global.json | 4 ++-- tests/ImageSharp.Benchmarks/Program.cs | 5 +++-- tests/ImageSharp.Benchmarks/benchmark.sh | 7 +++++++ tests/ImageSharp.Benchmarks/project.json | 14 +++++++++++++- 5 files changed, 32 insertions(+), 5 deletions(-) create mode 100755 tests/ImageSharp.Benchmarks/benchmark.sh diff --git a/.vscode/tasks.json b/.vscode/tasks.json index aeae5c6ca..128265ff6 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -13,6 +13,13 @@ "showOutput": "always", "problemMatcher": "$msCompile" }, + { + "taskName": "build benchmark", + "suppressTaskName": true, + "args": [ "build", "tests/ImageSharp.Benchmarks/project.json", "-f", "netcoreapp1.1", "-c", "Release" ], + "showOutput": "always", + "problemMatcher": "$msCompile" + }, { "taskName": "test", "args": ["tests/ImageSharp.Tests/project.json", "-f", "netcoreapp1.1"], diff --git a/global.json b/global.json index 7346bdc28..c1b6f3363 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { - "projects": [ "src" ], - "sdk": { + "projects": [ "src", "tests" ], + "sdk": { "version": "1.0.0-preview2-003121" } } \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Program.cs b/tests/ImageSharp.Benchmarks/Program.cs index 8c609f015..789068426 100644 --- a/tests/ImageSharp.Benchmarks/Program.cs +++ b/tests/ImageSharp.Benchmarks/Program.cs @@ -6,8 +6,9 @@ namespace ImageSharp.Benchmarks { using BenchmarkDotNet.Running; - + using ImageSharp.Formats; + using System.Reflection; public class Program { @@ -19,7 +20,7 @@ namespace ImageSharp.Benchmarks /// public static void Main(string[] args) { - new BenchmarkSwitcher(typeof(Program).Assembly).Run(args); + new BenchmarkSwitcher(typeof(Program).GetTypeInfo().Assembly).Run(args); } } } diff --git a/tests/ImageSharp.Benchmarks/benchmark.sh b/tests/ImageSharp.Benchmarks/benchmark.sh new file mode 100755 index 000000000..1966475bc --- /dev/null +++ b/tests/ImageSharp.Benchmarks/benchmark.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# Build in release mode +dotnet build -c Release -f netcoreapp1.1 + +# Run benchmarks +dotnet bin/Release/netcoreapp1.1/ImageSharp.Benchmarks.dll \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/project.json b/tests/ImageSharp.Benchmarks/project.json index 866a36faa..6a8be9f89 100644 --- a/tests/ImageSharp.Benchmarks/project.json +++ b/tests/ImageSharp.Benchmarks/project.json @@ -14,7 +14,6 @@ "allowUnsafe": true }, "dependencies": { - "BenchmarkDotNet.Diagnostics.Windows": "0.10.1", "ImageSharp": { "target": "project" }, @@ -46,10 +45,23 @@ "frameworks": { "net46": { "dependencies": { + "BenchmarkDotNet.Diagnostics.Windows": "0.10.1" }, "frameworkAssemblies": { "System.Drawing": "" } + }, + "netcoreapp1.1": { + "dependencies": { + "Microsoft.NETCore.App": { + "type": "platform", + "version": "1.1.0-*" + }, + "BenchmarkDotNet": "0.10.2", + "CoreCompat.System.Drawing": "1.0.0-beta006", + "runtime.linux-x64.CoreCompat.System.Drawing": "1.0.0-beta009", + "System.Reflection": "4.3.0" + } } } } From bd9301fbdd7b4dfdbbda9670be78cabb7a51a64c Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 1 Mar 2017 02:25:33 +0100 Subject: [PATCH 123/142] added "BenchmarkDotNet.Core" dependency --- tests/ImageSharp.Benchmarks/project.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/ImageSharp.Benchmarks/project.json b/tests/ImageSharp.Benchmarks/project.json index 6a8be9f89..810c666bc 100644 --- a/tests/ImageSharp.Benchmarks/project.json +++ b/tests/ImageSharp.Benchmarks/project.json @@ -60,7 +60,8 @@ "BenchmarkDotNet": "0.10.2", "CoreCompat.System.Drawing": "1.0.0-beta006", "runtime.linux-x64.CoreCompat.System.Drawing": "1.0.0-beta009", - "System.Reflection": "4.3.0" + "System.Reflection": "4.3.0", + "BenchmarkDotNet.Core": "0.10.2" } } } From 87b2be9e5fcda4b6ddf8816525d58009d25dc2c5 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 2 Mar 2017 00:23:49 +0100 Subject: [PATCH 124/142] revert adding explicit "BenchmarkDotNet.Core" dependency --- tests/ImageSharp.Benchmarks/project.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/project.json b/tests/ImageSharp.Benchmarks/project.json index 810c666bc..6a8be9f89 100644 --- a/tests/ImageSharp.Benchmarks/project.json +++ b/tests/ImageSharp.Benchmarks/project.json @@ -60,8 +60,7 @@ "BenchmarkDotNet": "0.10.2", "CoreCompat.System.Drawing": "1.0.0-beta006", "runtime.linux-x64.CoreCompat.System.Drawing": "1.0.0-beta009", - "System.Reflection": "4.3.0", - "BenchmarkDotNet.Core": "0.10.2" + "System.Reflection": "4.3.0" } } } From 598ebdbd964a969cecc9d34aec9d3e4cbd5b4660 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 2 Mar 2017 01:50:24 +0100 Subject: [PATCH 125/142] BulkPixelOperations skeleton --- src/ImageSharp/Colors/Color.cs | 3 + src/ImageSharp/Colors/PackedPixel/Alpha8.cs | 3 + src/ImageSharp/Colors/PackedPixel/Argb.cs | 3 + src/ImageSharp/Colors/PackedPixel/Bgr565.cs | 3 + src/ImageSharp/Colors/PackedPixel/Bgra4444.cs | 3 + src/ImageSharp/Colors/PackedPixel/Bgra5551.cs | 3 + .../Colors/PackedPixel/BulkPixelOperations.cs | 56 ++++++++++ src/ImageSharp/Colors/PackedPixel/Byte4.cs | 3 + .../Colors/PackedPixel/HalfSingle.cs | 3 + .../Colors/PackedPixel/HalfVector2.cs | 3 + .../Colors/PackedPixel/HalfVector4.cs | 3 + src/ImageSharp/Colors/PackedPixel/IPixel.cs | 4 + .../Colors/PackedPixel/NormalizedByte2.cs | 3 + .../Colors/PackedPixel/NormalizedByte4.cs | 3 + .../Colors/PackedPixel/NormalizedShort2.cs | 3 + .../Colors/PackedPixel/NormalizedShort4.cs | 3 + src/ImageSharp/Colors/PackedPixel/Rg32.cs | 3 + .../Colors/PackedPixel/Rgba1010102.cs | 3 + src/ImageSharp/Colors/PackedPixel/Rgba64.cs | 3 + src/ImageSharp/Colors/PackedPixel/Short2.cs | 3 + src/ImageSharp/Colors/PackedPixel/Short4.cs | 3 + src/ImageSharp/Common/Memory/ArrayPointer.cs | 50 +++++++++ .../General/ArrayCopy.cs | 25 ++++- .../Colors/BulkPixelOperationsTests.cs | 104 ++++++++++++++++++ .../Common/ArrayPointerTests.cs | 94 +++++++++++++++- 25 files changed, 381 insertions(+), 9 deletions(-) create mode 100644 src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs create mode 100644 src/ImageSharp/Common/Memory/ArrayPointer.cs create mode 100644 tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs diff --git a/src/ImageSharp/Colors/Color.cs b/src/ImageSharp/Colors/Color.cs index 469774b34..8a869935c 100644 --- a/src/ImageSharp/Colors/Color.cs +++ b/src/ImageSharp/Colors/Color.cs @@ -112,6 +112,9 @@ namespace ImageSharp this.packedValue = packed; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Gets or sets the red component. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs index 485725d71..1181eb9e4 100644 --- a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs +++ b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs @@ -26,6 +26,9 @@ namespace ImageSharp /// public byte PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Argb.cs b/src/ImageSharp/Colors/PackedPixel/Argb.cs index bef986fb9..1b97d2337 100644 --- a/src/ImageSharp/Colors/PackedPixel/Argb.cs +++ b/src/ImageSharp/Colors/PackedPixel/Argb.cs @@ -109,6 +109,9 @@ namespace ImageSharp /// public uint PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Gets or sets the red component. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Bgr565.cs b/src/ImageSharp/Colors/PackedPixel/Bgr565.cs index ebe8d2533..41b2bdc2e 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgr565.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgr565.cs @@ -39,6 +39,9 @@ namespace ImageSharp /// public ushort PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs b/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs index ccd6ab1f3..99659a36b 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs @@ -38,6 +38,9 @@ namespace ImageSharp /// public ushort PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs b/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs index a7a2e899a..86864fd48 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs @@ -40,6 +40,9 @@ namespace ImageSharp /// public ushort PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs new file mode 100644 index 000000000..c914b3921 --- /dev/null +++ b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs @@ -0,0 +1,56 @@ +namespace ImageSharp +{ + using System.Numerics; + + public unsafe class BulkPixelOperations + where TColor : struct, IPixel + { + public static BulkPixelOperations Instance { get; } = default(TColor).BulkOperations; + + internal virtual void PackFromVector4( + ArrayPointer sourceVectors, + ArrayPointer destColors, + int count) + { + } + + internal virtual void PackToVector4( + ArrayPointer sourceColors, + ArrayPointer destVectors, + int count) + { + } + + internal virtual void PackToXyzBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + { + } + + internal virtual void PackFromXyzBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) + { + } + + internal virtual void PackToXyzwBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + { + } + + internal virtual void PackFromXyzwBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) + { + } + + internal virtual void PackToZyxBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + { + } + + internal virtual void PackFromZyxBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) + { + } + + internal virtual void PackToZyxwBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + { + } + + internal virtual void PackFromZyxwBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Colors/PackedPixel/Byte4.cs b/src/ImageSharp/Colors/PackedPixel/Byte4.cs index 9d5eb9be8..5712027d9 100644 --- a/src/ImageSharp/Colors/PackedPixel/Byte4.cs +++ b/src/ImageSharp/Colors/PackedPixel/Byte4.cs @@ -41,6 +41,9 @@ namespace ImageSharp /// public uint PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs b/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs index acfa639b7..0bc82c3a6 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs @@ -36,6 +36,9 @@ namespace ImageSharp /// public ushort PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs index e02c226dd..778f86e0f 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs @@ -45,6 +45,9 @@ namespace ImageSharp /// public uint PackedValue { get; set; } + + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); /// /// Compares two objects for equality. diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs b/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs index 7c7f640e4..c24553d3f 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs @@ -49,6 +49,9 @@ namespace ImageSharp /// public ulong PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/IPixel.cs b/src/ImageSharp/Colors/PackedPixel/IPixel.cs index 1c3e20a7e..c17fe86cc 100644 --- a/src/ImageSharp/Colors/PackedPixel/IPixel.cs +++ b/src/ImageSharp/Colors/PackedPixel/IPixel.cs @@ -15,6 +15,10 @@ namespace ImageSharp public interface IPixel : IPixel, IEquatable where TSelf : struct, IPixel { + /// + /// Gets the instance for this pixel type. + /// + BulkPixelOperations BulkOperations { get; } } /// diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs index 116a68172..d425806c2 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs @@ -51,6 +51,9 @@ namespace ImageSharp /// public ushort PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs index 7aaa30c52..cba3f0e86 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs @@ -52,6 +52,9 @@ namespace ImageSharp /// public uint PackedValue { get; set; } + + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); /// /// Compares two objects for equality. diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs index 2f4ef89d6..4bc7f9427 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs @@ -51,6 +51,9 @@ namespace ImageSharp /// public uint PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs index 60c5c9805..c848b7236 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs @@ -53,6 +53,9 @@ namespace ImageSharp /// public ulong PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Rg32.cs b/src/ImageSharp/Colors/PackedPixel/Rg32.cs index 9e5e5a711..9eb2247c9 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rg32.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rg32.cs @@ -36,6 +36,9 @@ namespace ImageSharp /// public uint PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs b/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs index 95a8d3b97..4f99feb6e 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs @@ -39,6 +39,9 @@ namespace ImageSharp /// public uint PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Rgba64.cs b/src/ImageSharp/Colors/PackedPixel/Rgba64.cs index 679a55c4e..a23e57ec3 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rgba64.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rgba64.cs @@ -38,6 +38,9 @@ namespace ImageSharp /// public ulong PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Short2.cs b/src/ImageSharp/Colors/PackedPixel/Short2.cs index 1c1cb28c3..f26e82578 100644 --- a/src/ImageSharp/Colors/PackedPixel/Short2.cs +++ b/src/ImageSharp/Colors/PackedPixel/Short2.cs @@ -51,6 +51,9 @@ namespace ImageSharp /// public uint PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Short4.cs b/src/ImageSharp/Colors/PackedPixel/Short4.cs index 2c11a1f8b..6dc7545e1 100644 --- a/src/ImageSharp/Colors/PackedPixel/Short4.cs +++ b/src/ImageSharp/Colors/PackedPixel/Short4.cs @@ -53,6 +53,9 @@ namespace ImageSharp /// public ulong PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Common/Memory/ArrayPointer.cs b/src/ImageSharp/Common/Memory/ArrayPointer.cs new file mode 100644 index 000000000..c864d31fd --- /dev/null +++ b/src/ImageSharp/Common/Memory/ArrayPointer.cs @@ -0,0 +1,50 @@ +namespace ImageSharp +{ + using System.Runtime.CompilerServices; + + /// + /// Utility methods to + /// + internal static class ArrayPointer + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void Copy(ArrayPointer source, ArrayPointer destination, int count) + where T : struct + { + Unsafe.CopyBlock((void*)source.PointerAtOffset, (void*)destination.PointerAtOffset, USizeOf(count)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void Copy(ArrayPointer source, ArrayPointer destination, int countInSource) + where T : struct + { + Unsafe.CopyBlock((void*)source.PointerAtOffset, (void*)destination.PointerAtOffset, USizeOf(countInSource)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void Copy(ArrayPointer source, ArrayPointer destination, int countInDest) + where T : struct + { + Unsafe.CopyBlock((void*)source.PointerAtOffset, (void*)destination.PointerAtOffset, USizeOf(countInDest)); + } + + /// + /// Gets the size of `count` elements in bytes. + /// + /// The count of the elements + /// The size in bytes as int + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int SizeOf(int count) + where T : struct => Unsafe.SizeOf() * count; + + /// + /// Gets the size of `count` elements in bytes as UInt32 + /// + /// The count of the elements + /// The size in bytes as UInt32 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint USizeOf(int count) + where T : struct + => (uint)SizeOf(count); + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs b/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs index dddd83e42..72fd6dc24 100644 --- a/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs +++ b/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs @@ -2,22 +2,23 @@ // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // - namespace ImageSharp.Benchmarks.General { using System; using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; using BenchmarkDotNet.Attributes; + [Config(typeof(Config.Short))] public class ArrayCopy { - [Params(100, 1000, 10000)] + [Params(10, 100, 1000, 10000)] public int Count { get; set; } - private byte[] source; + byte[] source; - private byte[] destination; + byte[] destination; [Setup] public void SetUp() @@ -42,6 +43,12 @@ namespace ImageSharp.Benchmarks.General } } + [Benchmark(Description = "Copy using Buffer.BlockCopy()")] + public void CopyUsingBufferBlockCopy() + { + Buffer.BlockCopy(this.source, 0, this.destination, 0, this.Count); + } + [Benchmark(Description = "Copy using Buffer.MemoryCopy")] public unsafe void CopyUsingBufferMemoryCopy() { @@ -51,5 +58,15 @@ namespace ImageSharp.Benchmarks.General Buffer.MemoryCopy(pinnedSource, pinnedDestination, this.Count, this.Count); } } + + + [Benchmark(Description = "Copy using Marshal.Copy")] + public unsafe void CopyUsingMarshalCopy() + { + fixed (byte* pinnedDestination = this.destination) + { + Marshal.Copy(this.source, 0, (IntPtr)pinnedDestination, this.Count); + } + } } } diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs new file mode 100644 index 000000000..413bd9451 --- /dev/null +++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs @@ -0,0 +1,104 @@ +namespace ImageSharp.Tests.Colors +{ + using System; + + using Xunit; + + public class BulkPixelOperationsTests + { + public class TypeParam + { + } + + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackFromVector4(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackToVector4(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackToXyzBytes(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackFromXyzBytes(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackToXyzwBytes(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackFromXyzwBytes(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackToZyxBytes(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackFromZyxBytes(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackToZyxwBytes(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackFromZyxwBytes(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs b/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs index 076e2512c..916a10947 100644 --- a/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs +++ b/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs @@ -3,6 +3,7 @@ namespace ImageSharp.Tests.Common { using System; + using System.Runtime.CompilerServices; using Xunit; @@ -10,18 +11,16 @@ namespace ImageSharp.Tests.Common { public struct Foo { -#pragma warning disable CS0414 - private int a; + public int A; - private double b; -#pragma warning restore CS0414 + public double B; internal static Foo[] CreateArray(int size) { Foo[] result = new Foo[size]; for (int i = 0; i < size; i++) { - result[i] = new Foo() { a = i, b = i }; + result[i] = new Foo() { A = i, B = i }; } return result; } @@ -79,5 +78,90 @@ namespace ImageSharp.Tests.Common Assert.Equal((IntPtr)(p + totalOffset), ap.PointerAtOffset); } } + + public class Copy + { + [Theory] + [InlineData(4)] + [InlineData(1500)] + public void GenericToOwnType(int count) + { + Foo[] source = Foo.CreateArray(count + 2); + Foo[] dest = new Foo[count + 5]; + + fixed (Foo* pSource = source) + fixed (Foo* pDest = dest) + { + ArrayPointer apSource = new ArrayPointer(source, pSource); + ArrayPointer apDest = new ArrayPointer(dest, pDest); + + ArrayPointer.Copy(apSource, apDest, count); + } + + Assert.Equal(source[0], dest[0]); + Assert.Equal(source[count-1], dest[count-1]); + Assert.NotEqual(source[count], dest[count]); + } + + [Theory] + [InlineData(4)] + [InlineData(1500)] + public void GenericToBytes(int count) + { + int destCount = count * sizeof(Foo); + Foo[] source = Foo.CreateArray(count + 2); + byte[] dest = new byte[destCount + sizeof(Foo) + 1]; + + fixed (Foo* pSource = source) + fixed (byte* pDest = dest) + { + ArrayPointer apSource = new ArrayPointer(source, pSource); + ArrayPointer apDest = new ArrayPointer(dest, pDest); + + ArrayPointer.Copy(apSource, apDest, count); + } + + Assert.True(ElementsAreEqual(source, dest, 0)); + Assert.True(ElementsAreEqual(source, dest, count - 1)); + Assert.False(ElementsAreEqual(source, dest, count)); + } + + [Theory] + [InlineData(4)] + [InlineData(1500)] + public void BytesToGeneric(int count) + { + int destCount = count * sizeof(Foo); + byte[] source = new byte[destCount + sizeof(Foo) + 1]; + Foo[] dest = Foo.CreateArray(count + 2); + + fixed(byte* pSource = source) + fixed (Foo* pDest = dest) + { + ArrayPointer apSource = new ArrayPointer(source, pSource); + ArrayPointer apDest = new ArrayPointer(dest, pDest); + + ArrayPointer.Copy(apSource, apDest, count); + } + + Assert.True(ElementsAreEqual(dest, source, 0)); + Assert.True(ElementsAreEqual(dest, source, count - 1)); + Assert.False(ElementsAreEqual(dest, source, count)); + } + + private static bool ElementsAreEqual(Foo[] array, byte[] rawArray, int index) + { + fixed (Foo* pArray = array) + fixed (byte* pRaw = rawArray) + { + Foo* pCasted = (Foo*)pRaw; + + Foo val1 = pArray[index]; + Foo val2 = pCasted[index]; + + return val1.Equals(val2); + } + } + } } } \ No newline at end of file From 1d2d7cb6e25ab5a60ce653443e107ab9de888612 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 2 Mar 2017 03:10:00 +0100 Subject: [PATCH 126/142] PinnedBuffer, better tests --- src/ImageSharp/Common/Memory/ArrayPointer.cs | 7 ++ src/ImageSharp/Common/Memory/PinnedBuffer.cs | 109 +++++++++++++++++ src/ImageSharp/Image/PixelPool{TColor}.cs | 1 + .../Colors/BulkPixelOperationsTests.cs | 114 +++++++++++------- .../Common/PinnedBufferTests.cs | 69 +++++++++++ 5 files changed, 257 insertions(+), 43 deletions(-) create mode 100644 src/ImageSharp/Common/Memory/PinnedBuffer.cs create mode 100644 tests/ImageSharp.Tests/Common/PinnedBufferTests.cs diff --git a/src/ImageSharp/Common/Memory/ArrayPointer.cs b/src/ImageSharp/Common/Memory/ArrayPointer.cs index c864d31fd..56cae35a4 100644 --- a/src/ImageSharp/Common/Memory/ArrayPointer.cs +++ b/src/ImageSharp/Common/Memory/ArrayPointer.cs @@ -7,6 +7,13 @@ namespace ImageSharp /// internal static class ArrayPointer { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe ArrayPointer GetArrayPointer(this PinnedBuffer buffer) + where T : struct + { + return new ArrayPointer(buffer.Array, (void*)buffer.Pointer); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void Copy(ArrayPointer source, ArrayPointer destination, int count) where T : struct diff --git a/src/ImageSharp/Common/Memory/PinnedBuffer.cs b/src/ImageSharp/Common/Memory/PinnedBuffer.cs new file mode 100644 index 000000000..c6e0c7c6f --- /dev/null +++ b/src/ImageSharp/Common/Memory/PinnedBuffer.cs @@ -0,0 +1,109 @@ +namespace ImageSharp +{ + using System; + using System.Buffers; + using System.Runtime.InteropServices; + + /// + /// Manages a pinned buffer of 'T' as a Disposable resource. + /// The backing array is either pooled or comes from the outside. + /// TODO: Should replace the pinning/dispose logic in several classes like or ! + /// + /// The value type. + internal class PinnedBuffer : IDisposable + where T : struct + { + private GCHandle handle; + + private bool isBufferRented; + + private bool isDisposed; + + /// + /// TODO: Consider reusing functionality of + /// + private static readonly ArrayPool ArrayPool = ArrayPool.Create(); + + /// + /// Initializes a new instance of the class. + /// + /// The desired count of elements. (Minimum size for ) + public PinnedBuffer(int count) + { + this.Count = count; + this.Array = ArrayPool.Rent(count); + this.isBufferRented = true; + this.Pin(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The array to pin. + public PinnedBuffer(T[] array) + { + this.Count = array.Length; + this.Array = array; + this.Pin(); + } + + /// + /// The count of "relevant" elements. Usually be smaller than 'Array.Length' when is pooled. + /// + public int Count { get; private set; } + + /// + /// The (pinned) array of elements. + /// + public T[] Array { get; private set; } + + /// + /// Pointer to the pinned . + /// + public IntPtr Pointer { get; private set; } + + /// + /// Disposes the instance by unpinning the array, and returning the pooled buffer when necessary. + /// + public void Dispose() + { + if (this.isDisposed) + { + return; + } + this.isDisposed = true; + this.UnPin(); + + if (this.isBufferRented) + { + ArrayPool.Return(this.Array, true); + } + + this.Array = null; + this.Count = 0; + + GC.SuppressFinalize(this); + } + + private void Pin() + { + this.handle = GCHandle.Alloc(this.Array, GCHandleType.Pinned); + this.Pointer = this.handle.AddrOfPinnedObject(); + } + + private void UnPin() + { + if (this.Pointer == IntPtr.Zero || !this.handle.IsAllocated) + { + return; + } + this.handle.Free(); + this.Pointer = IntPtr.Zero; + } + + ~PinnedBuffer() + { + this.UnPin(); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/PixelPool{TColor}.cs b/src/ImageSharp/Image/PixelPool{TColor}.cs index 8193600da..ea6dad6b1 100644 --- a/src/ImageSharp/Image/PixelPool{TColor}.cs +++ b/src/ImageSharp/Image/PixelPool{TColor}.cs @@ -8,6 +8,7 @@ namespace ImageSharp using System; using System.Buffers; + // TODO: Consider refactoring this into a more general ClearPool, so we can use it in PinnedBuffer! /// /// Provides a resource pool that enables reusing instances of type . /// diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs index 413bd9451..f2081b943 100644 --- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs @@ -1,104 +1,132 @@ namespace ImageSharp.Tests.Colors { using System; + using System.Numerics; using Xunit; - - public class BulkPixelOperationsTests + + public abstract class BulkPixelOperationsTests + where TColor : struct, IPixel { - public class TypeParam + public class ColorPixels : BulkPixelOperationsTests { } + public class ArgbPixels : BulkPixelOperationsTests + { + } + public static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; + [Theory] - [InlineData(default(TypeParam))] - [InlineData(default(TypeParam))] - public virtual void PackFromVector4(TypeParam dummy) - where TColor : struct, IPixel + [MemberData(nameof(ArraySizesData))] + public virtual void PackFromVector4(int count) { throw new NotImplementedException(); } [Theory] - [InlineData(default(TypeParam))] - [InlineData(default(TypeParam))] - public virtual void PackToVector4(TypeParam dummy) - where TColor : struct, IPixel + [MemberData(nameof(ArraySizesData))] + public virtual void PackToVector4(int count) { throw new NotImplementedException(); } [Theory] - [InlineData(default(TypeParam))] - [InlineData(default(TypeParam))] - public virtual void PackToXyzBytes(TypeParam dummy) - where TColor : struct, IPixel + [MemberData(nameof(ArraySizesData))] + public virtual void PackToXyzBytes(int count) { throw new NotImplementedException(); } [Theory] - [InlineData(default(TypeParam))] - [InlineData(default(TypeParam))] - public virtual void PackFromXyzBytes(TypeParam dummy) - where TColor : struct, IPixel + [MemberData(nameof(ArraySizesData))] + public virtual void PackFromXyzBytes(int count) { throw new NotImplementedException(); } [Theory] - [InlineData(default(TypeParam))] - [InlineData(default(TypeParam))] - public virtual void PackToXyzwBytes(TypeParam dummy) - where TColor : struct, IPixel + [MemberData(nameof(ArraySizesData))] + public virtual void PackToXyzwBytes(int count) { throw new NotImplementedException(); } [Theory] - [InlineData(default(TypeParam))] - [InlineData(default(TypeParam))] - public virtual void PackFromXyzwBytes(TypeParam dummy) - where TColor : struct, IPixel + [MemberData(nameof(ArraySizesData))] + public virtual void PackFromXyzwBytes(int count) { throw new NotImplementedException(); } [Theory] - [InlineData(default(TypeParam))] - [InlineData(default(TypeParam))] - public virtual void PackToZyxBytes(TypeParam dummy) - where TColor : struct, IPixel + [MemberData(nameof(ArraySizesData))] + public virtual void PackToZyxBytes(int count) { throw new NotImplementedException(); } [Theory] - [InlineData(default(TypeParam))] - [InlineData(default(TypeParam))] - public virtual void PackFromZyxBytes(TypeParam dummy) - where TColor : struct, IPixel + [MemberData(nameof(ArraySizesData))] + public virtual void PackFromZyxBytes(int count) { throw new NotImplementedException(); } [Theory] - [InlineData(default(TypeParam))] - [InlineData(default(TypeParam))] - public virtual void PackToZyxwBytes(TypeParam dummy) - where TColor : struct, IPixel + [MemberData(nameof(ArraySizesData))] + public virtual void PackToZyxwBytes(int count) { throw new NotImplementedException(); } [Theory] - [InlineData(default(TypeParam))] - [InlineData(default(TypeParam))] - public virtual void PackFromZyxwBytes(TypeParam dummy) - where TColor : struct, IPixel + [MemberData(nameof(ArraySizesData))] + public virtual void PackFromZyxwBytes(int count) { throw new NotImplementedException(); } + + public class TestBuffers + { + internal static PinnedBuffer Vector4(int length) + { + Vector4[] result = new Vector4[length]; + Random rnd = new Random(42); // Deterministic random values + + for (int i = 0; i < result.Length; i++) + { + result[i] = GetVector(rnd); + } + + return new PinnedBuffer(result); + } + + internal static PinnedBuffer Pixel(int length) + { + TColor[] result = new TColor[length]; + + Random rnd = new Random(42); // Deterministic random values + + for (int i = 0; i < result.Length; i++) + { + Vector4 v = GetVector(rnd); + result[i].PackFromVector4(v); + } + + return new PinnedBuffer(result); + } + + private static Vector4 GetVector(Random rnd) + { + return new Vector4( + (float)rnd.NextDouble(), + (float)rnd.NextDouble(), + (float)rnd.NextDouble(), + (float)rnd.NextDouble() + ); + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs b/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs new file mode 100644 index 000000000..e0783f716 --- /dev/null +++ b/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs @@ -0,0 +1,69 @@ +namespace ImageSharp.Tests.Common +{ + using System; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + + using Xunit; + + public unsafe class PinnedBufferTests + { + public struct Foo + { + public int A; + + public double B; + } + + [Theory] + [InlineData(42)] + [InlineData(1111)] + public void ConstructWithOwnArray(int count) + { + using (PinnedBuffer buffer = new PinnedBuffer(count)) + { + Assert.NotNull(buffer.Array); + Assert.Equal(count, buffer.Count); + Assert.True(buffer.Array.Length >= count); + + VerifyPointer(buffer); + } + } + + [Theory] + [InlineData(42)] + [InlineData(1111)] + public void ConstructWithExistingArray(int count) + { + Foo[] array = new Foo[count]; + using (PinnedBuffer buffer = new PinnedBuffer(array)) + { + Assert.Equal(array, buffer.Array); + Assert.Equal(count, buffer.Count); + + VerifyPointer(buffer); + } + } + + [Fact] + public void GetArrayPointer() + { + Foo[] a = { new Foo() { A = 1, B = 2 }, new Foo() { A = 3, B = 4 } }; + + using (PinnedBuffer buffer = new PinnedBuffer(a)) + { + var arrayPtr = buffer.GetArrayPointer(); + + Assert.Equal(a, arrayPtr.Array); + Assert.Equal(0, arrayPtr.Offset); + Assert.Equal(buffer.Pointer, arrayPtr.PointerAtOffset); + } + } + + private static void VerifyPointer(PinnedBuffer buffer) + { + IntPtr ptr = (IntPtr)Unsafe.AsPointer(ref buffer.Array[0]); + Assert.Equal(ptr, buffer.Pointer); + } + } +} \ No newline at end of file From bca8c6f5ea655932191f29f4f2b0112c7794d2be Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 3 Mar 2017 01:58:56 +0100 Subject: [PATCH 127/142] better tests --- .../Colors/BulkPixelOperationsTests.cs | 197 ++++++++++++++---- 1 file changed, 156 insertions(+), 41 deletions(-) diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs index f2081b943..441b9daca 100644 --- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs @@ -4,9 +4,8 @@ using System.Numerics; using Xunit; - - public abstract class BulkPixelOperationsTests - where TColor : struct, IPixel + + public abstract class BulkPixelOperationsTests { public class ColorPixels : BulkPixelOperationsTests { @@ -15,118 +14,234 @@ public class ArgbPixels : BulkPixelOperationsTests { } + } + public abstract class BulkPixelOperationsTests + where TColor : struct, IPixel + { public static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; [Theory] [MemberData(nameof(ArraySizesData))] - public virtual void PackFromVector4(int count) + public void PackFromVector4(int count) { - throw new NotImplementedException(); + Vector4[] source = CreateVector4TestData(count); + TColor[] expected = new TColor[count]; + + for (int i = 0; i < count; i++) + { + expected[i].PackFromVector4(source[i]); + } + + TestOperation( + source, + expected, + (ops, s, d) => ops.PackFromVector4(s, d, count) + ); } [Theory] [MemberData(nameof(ArraySizesData))] - public virtual void PackToVector4(int count) + public void PackToVector4(int count) { - throw new NotImplementedException(); + TColor[] source = CreatePixelTestData(count); + Vector4[] expected = new Vector4[count]; + + for (int i = 0; i < count; i++) + { + expected[i] = source[i].ToVector4(); + } + + TestOperation( + source, + expected, + (ops, s, d) => ops.PackToVector4(s, d, count) + ); } + [Theory] [MemberData(nameof(ArraySizesData))] - public virtual void PackToXyzBytes(int count) + public void PackFromXyzBytes(int count) { - throw new NotImplementedException(); + byte[] source = CreateByteTestData(count * 3); + TColor[] expected = new TColor[count]; + + for (int i = 0; i < count; i++) + { + int i3 = i * 3; + + expected[i].PackFromBytes(source[i3 + 0], source[i3 + 1], source[i3 + 2], 255); + } + + TestOperation( + source, + expected, + (ops, s, d) => ops.PackFromXyzBytes(s, d, count) + ); } [Theory] [MemberData(nameof(ArraySizesData))] - public virtual void PackFromXyzBytes(int count) + public void PackToXyzBytes(int count) { - throw new NotImplementedException(); + TColor[] source = CreatePixelTestData(count); + byte[] expected = new byte[count * 3]; + + for (int i = 0; i < count; i++) + { + int i3 = i * 3; + source[i].ToXyzBytes(expected, i3); + } + + TestOperation( + source, + expected, + (ops, s, d) => ops.PackToXyzBytes(s, d, count) + ); } + [Theory] [MemberData(nameof(ArraySizesData))] - public virtual void PackToXyzwBytes(int count) + public void PackToXyzwBytes(int count) { throw new NotImplementedException(); } [Theory] [MemberData(nameof(ArraySizesData))] - public virtual void PackFromXyzwBytes(int count) + public void PackFromXyzwBytes(int count) { throw new NotImplementedException(); } [Theory] [MemberData(nameof(ArraySizesData))] - public virtual void PackToZyxBytes(int count) + public void PackToZyxBytes(int count) { throw new NotImplementedException(); } [Theory] [MemberData(nameof(ArraySizesData))] - public virtual void PackFromZyxBytes(int count) + public void PackFromZyxBytes(int count) { throw new NotImplementedException(); } [Theory] [MemberData(nameof(ArraySizesData))] - public virtual void PackToZyxwBytes(int count) + public void PackToZyxwBytes(int count) { throw new NotImplementedException(); } [Theory] [MemberData(nameof(ArraySizesData))] - public virtual void PackFromZyxwBytes(int count) + public void PackFromZyxwBytes(int count) { throw new NotImplementedException(); } - - public class TestBuffers + + private class TestBuffers : IDisposable + where TSource : struct + where TDest : struct { - internal static PinnedBuffer Vector4(int length) + public PinnedBuffer SourceBuffer { get; } + public PinnedBuffer ActualDestBuffer { get; } + public PinnedBuffer ExpectedDestBuffer { get; } + + public ArrayPointer Source => this.SourceBuffer.GetArrayPointer(); + public ArrayPointer ActualDest => this.ActualDestBuffer.GetArrayPointer(); + + public TestBuffers(TSource[] source, TDest[] expectedDest) + { + this.SourceBuffer = new PinnedBuffer(source); + this.ExpectedDestBuffer = new PinnedBuffer(expectedDest); + this.ActualDestBuffer = new PinnedBuffer(expectedDest.Length); + } + + public void Dispose() { - Vector4[] result = new Vector4[length]; - Random rnd = new Random(42); // Deterministic random values + this.SourceBuffer.Dispose(); + this.ActualDestBuffer.Dispose(); + this.ExpectedDestBuffer.Dispose(); + } - for (int i = 0; i < result.Length; i++) + public void Verify() + { + int count = this.ExpectedDestBuffer.Count; + TDest[] expected = this.ExpectedDestBuffer.Array; + TDest[] actual = this.ActualDestBuffer.Array; + for (int i = 0; i < count; i++) { - result[i] = GetVector(rnd); + Assert.Equal(expected[i], actual[i]); } + } + } - return new PinnedBuffer(result); + private static void TestOperation( + TSource[] source, + TDest[] expected, + Action, ArrayPointer, ArrayPointer> action) + where TSource : struct + where TDest : struct + { + using (var buffers = new TestBuffers(source, expected)) + { + action(BulkPixelOperations.Instance, buffers.Source, buffers.ActualDest); + buffers.Verify(); } + } - internal static PinnedBuffer Pixel(int length) + private static Vector4[] CreateVector4TestData(int length) + { + Vector4[] result = new Vector4[length]; + Random rnd = new Random(42); // Deterministic random values + + for (int i = 0; i < result.Length; i++) { - TColor[] result = new TColor[length]; + result[i] = GetVector(rnd); + } + return result; + } - Random rnd = new Random(42); // Deterministic random values + private static TColor[] CreatePixelTestData(int length) + { + TColor[] result = new TColor[length]; - for (int i = 0; i < result.Length; i++) - { - Vector4 v = GetVector(rnd); - result[i].PackFromVector4(v); - } + Random rnd = new Random(42); // Deterministic random values - return new PinnedBuffer(result); + for (int i = 0; i < result.Length; i++) + { + Vector4 v = GetVector(rnd); + result[i].PackFromVector4(v); } - private static Vector4 GetVector(Random rnd) + return result; + } + + private static byte[] CreateByteTestData(int length) + { + byte[] result = new byte[length]; + Random rnd = new Random(42); // Deterministic random values + + for (int i = 0; i < result.Length; i++) { - return new Vector4( - (float)rnd.NextDouble(), - (float)rnd.NextDouble(), - (float)rnd.NextDouble(), - (float)rnd.NextDouble() - ); + result[i] = (byte)rnd.Next(255); } + return result; + } + + private static Vector4 GetVector(Random rnd) + { + return new Vector4( + (float)rnd.NextDouble(), + (float)rnd.NextDouble(), + (float)rnd.NextDouble(), + (float)rnd.NextDouble() + ); } } } \ No newline at end of file From bf0d03fe89725d678479752a5214a5e958608e8b Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 3 Mar 2017 02:46:26 +0100 Subject: [PATCH 128/142] started implementing operations --- .../Colors/PackedPixel/BulkPixelOperations.cs | 58 ++++++++++++++++++- .../Common/Memory/ArrayPointer{T}.cs | 21 +++++++ .../Colors/BulkPixelOperationsTests.cs | 6 +- .../Formats/Jpg/JpegProfilingBenchmarks.cs | 8 +-- 4 files changed, 84 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs index c914b3921..a0dceaded 100644 --- a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs +++ b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs @@ -1,17 +1,34 @@ namespace ImageSharp { + using System; using System.Numerics; + using System.Runtime.CompilerServices; public unsafe class BulkPixelOperations where TColor : struct, IPixel { public static BulkPixelOperations Instance { get; } = default(TColor).BulkOperations; + + private static readonly int ColorSize = Unsafe.SizeOf(); internal virtual void PackFromVector4( ArrayPointer sourceVectors, ArrayPointer destColors, int count) { + Vector4* sp = (Vector4*)sourceVectors.PointerAtOffset; + byte* dp = (byte*)destColors; + + for (int i = 0; i < count; i++) + { + Vector4 v = Unsafe.Read(sp); + TColor c = default(TColor); + c.PackFromVector4(v); + Unsafe.Write(dp, c); + + sp++; + dp += ColorSize; + } } internal virtual void PackToVector4( @@ -19,16 +36,51 @@ ArrayPointer destVectors, int count) { + byte* sp = (byte*)sourceColors; + Vector4* dp = (Vector4*)destVectors.PointerAtOffset; + + for (int i = 0; i < count; i++) + { + TColor c = Unsafe.Read(sp); + *dp = c.ToVector4(); + sp += ColorSize; + dp++; + } } - internal virtual void PackToXyzBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + internal virtual void PackFromXyzBytes( + ArrayPointer sourceBytes, + ArrayPointer destColors, + int count) { + byte* sp = (byte*)sourceBytes; + byte* dp = (byte*)destColors.PointerAtOffset; + + for (int i = 0; i < count; i++) + { + TColor c = default(TColor); + c.PackFromBytes(sp[0], sp[1], sp[2], 255); + Unsafe.Write(dp, c); + sp += 3; + dp += ColorSize; + } } - internal virtual void PackFromXyzBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) + internal virtual void PackToXyzBytes( + ArrayPointer sourceColors, + ArrayPointer destBytes, int count) { - } + byte* sp = (byte*)sourceColors; + + byte[] dest = destBytes.Array; + for (int i = destBytes.Offset; i < destBytes.Offset + count*3; i+=3) + { + TColor c = Unsafe.Read(sp); + c.ToXyzBytes(dest, i); + } + } + internal virtual void PackToXyzwBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) { } diff --git a/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs b/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs index 1ea7706d4..8f99327ba 100644 --- a/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs +++ b/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs @@ -73,6 +73,7 @@ namespace ImageSharp /// /// The offset in number of elements /// The offseted (sliced) ArrayPointer + [MethodImpl(MethodImplOptions.AggressiveInlining)] public ArrayPointer Slice(int offset) { ArrayPointer result = default(ArrayPointer); @@ -81,5 +82,25 @@ namespace ImageSharp result.PointerAtOffset = this.PointerAtOffset + (Unsafe.SizeOf() * offset); return result; } + + /// + /// Convertes instance to a raw 'void*' pointer + /// + /// The to convert + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator void*(ArrayPointer arrayPointer) + { + return (void*)arrayPointer.PointerAtOffset; + } + + /// + /// Convertes instance to a raw 'byte*' pointer + /// + /// The to convert + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator byte* (ArrayPointer arrayPointer) + { + return (byte*)arrayPointer.PointerAtOffset; + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs index 441b9daca..472582310 100644 --- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs @@ -7,12 +7,14 @@ public abstract class BulkPixelOperationsTests { - public class ColorPixels : BulkPixelOperationsTests + public class Color : BulkPixelOperationsTests { + public static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; } - public class ArgbPixels : BulkPixelOperationsTests + public class Argb : BulkPixelOperationsTests { + public static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs index 50e678bf0..12deda577 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs @@ -38,10 +38,10 @@ namespace ImageSharp.Tests { const int ExecutionCount = 30; - if (!Vector.IsHardwareAccelerated) - { - throw new Exception("Vector.IsHardwareAccelerated == false! ('prefer32 bit' enabled?)"); - } + //if (!Vector.IsHardwareAccelerated) + //{ + // throw new Exception("Vector.IsHardwareAccelerated == false! ('prefer32 bit' enabled?)"); + //} string path = TestFile.GetPath(fileName); byte[] bytes = File.ReadAllBytes(path); From 739db5f3bc734daa30e0a9e129c2fe05133699ae Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 3 Mar 2017 03:05:06 +0100 Subject: [PATCH 129/142] fixed PackToXyzBytes --- src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs | 1 + tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj | 3 +++ tests/ImageSharp.Sandbox46/Program.cs | 3 --- .../Formats/Jpg/JpegProfilingBenchmarks.cs | 8 ++++---- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs index a0dceaded..c1f6001af 100644 --- a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs +++ b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs @@ -78,6 +78,7 @@ { TColor c = Unsafe.Read(sp); c.ToXyzBytes(dest, i); + sp += ColorSize; } } diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj index d1b059f44..ad436793d 100644 --- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj +++ b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj @@ -209,6 +209,9 @@ Benchmarks\PixelAccessorVirtualCopy.cs + + Tests\Colors\BulkPixelOperationsTests.cs + Tests\Drawing\PolygonTests.cs diff --git a/tests/ImageSharp.Sandbox46/Program.cs b/tests/ImageSharp.Sandbox46/Program.cs index f289ac2db..3afd18094 100644 --- a/tests/ImageSharp.Sandbox46/Program.cs +++ b/tests/ImageSharp.Sandbox46/Program.cs @@ -49,9 +49,6 @@ namespace ImageSharp.Sandbox46 benchmark.Setup(); benchmark.CopyRawUnsafeInlined(); - benchmark.CopyArrayPointerUnsafe(); - benchmark.CopyArrayPointerVirtualUnsafe(); - benchmark.CopyArrayPointerVirtualMarshal(); benchmark.Cleanup(); } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs index 12deda577..50e678bf0 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs @@ -38,10 +38,10 @@ namespace ImageSharp.Tests { const int ExecutionCount = 30; - //if (!Vector.IsHardwareAccelerated) - //{ - // throw new Exception("Vector.IsHardwareAccelerated == false! ('prefer32 bit' enabled?)"); - //} + if (!Vector.IsHardwareAccelerated) + { + throw new Exception("Vector.IsHardwareAccelerated == false! ('prefer32 bit' enabled?)"); + } string path = TestFile.GetPath(fileName); byte[] bytes = File.ReadAllBytes(path); From a98be90e19a87c05e9a075dd638d146a2df83c55 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sat, 4 Mar 2017 18:46:52 +0000 Subject: [PATCH 130/142] update SixLabors.Shapes --- src/ImageSharp.Drawing.Paths/project.json | 2 +- .../Drawing/SolidPolygonTests.cs | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp.Drawing.Paths/project.json b/src/ImageSharp.Drawing.Paths/project.json index b761233c3..cca2f9bf9 100644 --- a/src/ImageSharp.Drawing.Paths/project.json +++ b/src/ImageSharp.Drawing.Paths/project.json @@ -44,7 +44,7 @@ "ImageSharp.Drawing": { "target": "project" }, - "SixLabors.Shapes": "0.1.0-alpha0006", + "SixLabors.Shapes": "0.1.0-alpha0007", "StyleCop.Analyzers": { "version": "1.0.0", "type": "build" diff --git a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs index 1d3ead81f..9c6c6d234 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs @@ -242,5 +242,32 @@ namespace ImageSharp.Tests.Drawing } } } + + [Fact] + public void ImageShouldBeOverlayedBySquareWithCornerClipped() + { + string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); + + var config = Configuration.CreateDefaultInstance(); + config.ParallelOptions.MaxDegreeOfParallelism = 1; + using (Image image = new Image(200, 200, config)) + { + using (FileStream output = File.OpenWrite($"{path}/clipped-corner.png")) + { + image + .Fill(Color.Blue) + .FillPolygon(Color.HotPink, new[] + { + new Vector2( 8, 8 ), + new Vector2( 64, 8 ), + new Vector2( 64, 64 ), + new Vector2( 120, 64 ), + new Vector2( 120, 120 ), + new Vector2( 8, 120 ) + } ) + .Save(output); + } + } + } } } From fa060e98faf07a150c3585c79fb09763ce63e2da Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 Mar 2017 00:47:40 +0100 Subject: [PATCH 131/142] default BulkPixelOperations passing --- .../Colors/PackedPixel/BulkPixelOperations.cs | 75 ++++++++++-- .../Colors/BulkPixelOperationsTests.cs | 109 +++++++++++++++--- 2 files changed, 162 insertions(+), 22 deletions(-) diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs index c1f6001af..ffa28fc13 100644 --- a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs +++ b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs @@ -71,7 +71,6 @@ ArrayPointer destBytes, int count) { byte* sp = (byte*)sourceColors; - byte[] dest = destBytes.Array; for (int i = destBytes.Offset; i < destBytes.Offset + count*3; i+=3) @@ -81,29 +80,89 @@ sp += ColorSize; } } - - internal virtual void PackToXyzwBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) - { - } internal virtual void PackFromXyzwBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) { + byte* sp = (byte*)sourceBytes; + byte* dp = (byte*)destColors.PointerAtOffset; + + for (int i = 0; i < count; i++) + { + TColor c = default(TColor); + c.PackFromBytes(sp[0], sp[1], sp[2], sp[3]); + Unsafe.Write(dp, c); + sp += 4; + dp += ColorSize; + } } - - internal virtual void PackToZyxBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + + internal virtual void PackToXyzwBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) { + byte* sp = (byte*)sourceColors; + byte[] dest = destBytes.Array; + + for (int i = destBytes.Offset; i < destBytes.Offset + count * 4; i += 4) + { + TColor c = Unsafe.Read(sp); + c.ToXyzwBytes(dest, i); + sp += ColorSize; + } } internal virtual void PackFromZyxBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) { + byte* sp = (byte*)sourceBytes; + byte* dp = (byte*)destColors.PointerAtOffset; + + for (int i = 0; i < count; i++) + { + TColor c = default(TColor); + c.PackFromBytes(sp[2], sp[1], sp[0], 255); + Unsafe.Write(dp, c); + sp += 3; + dp += ColorSize; + } } - internal virtual void PackToZyxwBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + internal virtual void PackToZyxBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) { + byte* sp = (byte*)sourceColors; + byte[] dest = destBytes.Array; + + for (int i = destBytes.Offset; i < destBytes.Offset + count * 3; i += 3) + { + TColor c = Unsafe.Read(sp); + c.ToZyxBytes(dest, i); + sp += ColorSize; + } } internal virtual void PackFromZyxwBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) { + byte* sp = (byte*)sourceBytes; + byte* dp = (byte*)destColors.PointerAtOffset; + + for (int i = 0; i < count; i++) + { + TColor c = default(TColor); + c.PackFromBytes(sp[2], sp[1], sp[0], sp[3]); + Unsafe.Write(dp, c); + sp += 4; + dp += ColorSize; + } + } + + internal virtual void PackToZyxwBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + { + byte* sp = (byte*)sourceColors; + byte[] dest = destBytes.Array; + + for (int i = destBytes.Offset; i < destBytes.Offset + count * 4; i += 4) + { + TColor c = Unsafe.Read(sp); + c.ToZyxwBytes(dest, i); + sp += ColorSize; + } } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs index 472582310..3682aa78a 100644 --- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs @@ -21,7 +21,7 @@ public abstract class BulkPixelOperationsTests where TColor : struct, IPixel { - public static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; + protected static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; [Theory] [MemberData(nameof(ArraySizesData))] @@ -103,48 +103,129 @@ ); } - [Theory] [MemberData(nameof(ArraySizesData))] - public void PackToXyzwBytes(int count) + public void PackFromXyzwBytes(int count) { - throw new NotImplementedException(); + byte[] source = CreateByteTestData(count * 4); + TColor[] expected = new TColor[count]; + + for (int i = 0; i < count; i++) + { + int i4 = i * 4; + + expected[i].PackFromBytes(source[i4 + 0], source[i4 + 1], source[i4 + 2], source[i4 + 3]); + } + + TestOperation( + source, + expected, + (ops, s, d) => ops.PackFromXyzwBytes(s, d, count) + ); } [Theory] [MemberData(nameof(ArraySizesData))] - public void PackFromXyzwBytes(int count) + public void PackToXyzwBytes(int count) { - throw new NotImplementedException(); + TColor[] source = CreatePixelTestData(count); + byte[] expected = new byte[count * 4]; + + for (int i = 0; i < count; i++) + { + int i4 = i * 4; + source[i].ToXyzwBytes(expected, i4); + } + + TestOperation( + source, + expected, + (ops, s, d) => ops.PackToXyzwBytes(s, d, count) + ); } [Theory] [MemberData(nameof(ArraySizesData))] - public void PackToZyxBytes(int count) + public void PackFromZyxBytes(int count) { - throw new NotImplementedException(); + byte[] source = CreateByteTestData(count * 3); + TColor[] expected = new TColor[count]; + + for (int i = 0; i < count; i++) + { + int i3 = i * 3; + + expected[i].PackFromBytes(source[i3 + 2], source[i3 + 1], source[i3 + 0], 255); + } + + TestOperation( + source, + expected, + (ops, s, d) => ops.PackFromZyxBytes(s, d, count) + ); } [Theory] [MemberData(nameof(ArraySizesData))] - public void PackFromZyxBytes(int count) + public void PackToZyxBytes(int count) { - throw new NotImplementedException(); + TColor[] source = CreatePixelTestData(count); + byte[] expected = new byte[count * 3]; + + for (int i = 0; i < count; i++) + { + int i3 = i * 3; + source[i].ToZyxBytes(expected, i3); + } + + TestOperation( + source, + expected, + (ops, s, d) => ops.PackToZyxBytes(s, d, count) + ); } [Theory] [MemberData(nameof(ArraySizesData))] - public void PackToZyxwBytes(int count) + public void PackFromZyxwBytes(int count) { - throw new NotImplementedException(); + byte[] source = CreateByteTestData(count * 4); + TColor[] expected = new TColor[count]; + + for (int i = 0; i < count; i++) + { + int i4 = i * 4; + + expected[i].PackFromBytes(source[i4 + 2], source[i4 + 1], source[i4 + 0], source[i4 + 3]); + } + + TestOperation( + source, + expected, + (ops, s, d) => ops.PackFromZyxwBytes(s, d, count) + ); } [Theory] [MemberData(nameof(ArraySizesData))] - public void PackFromZyxwBytes(int count) + public void PackToZyxwBytes(int count) { - throw new NotImplementedException(); + TColor[] source = CreatePixelTestData(count); + byte[] expected = new byte[count * 4]; + + for (int i = 0; i < count; i++) + { + int i4 = i * 4; + source[i].ToZyxwBytes(expected, i4); + } + + TestOperation( + source, + expected, + (ops, s, d) => ops.PackToZyxwBytes(s, d, count) + ); } + private class TestBuffers : IDisposable where TSource : struct From 76905dab5e181f31805d87c97f8db0c09439b163 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sat, 4 Mar 2017 18:34:43 +0000 Subject: [PATCH 132/142] render using sixlabors.fonts --- ImageSharp.sln | 7 + NuGet.config | 1 + src/ImageSharp.Drawing.Text/DrawText.cs | 204 ++++++++++++++++++ src/ImageSharp.Drawing.Text/GlyphBuilder.cs | 126 +++++++++++ .../ImageSharp.Drawing.Text.xproj | 25 +++ .../Properties/AssemblyInfo.cs | 6 + .../TextGraphicsOptions.cs | 68 ++++++ src/ImageSharp.Drawing.Text/project.json | 95 ++++++++ .../ImageSharp.Tests/Drawing/Text/DrawText.cs | 194 +++++++++++++++++ .../Drawing/Text/GlyphBuilder.cs | 68 ++++++ .../Drawing/Text/OutputText.cs | 41 ++++ tests/ImageSharp.Tests/TestFont.cs | 90 ++++++++ .../TestFonts/SixLaborsSampleAB.woff | Bin 0 -> 1352 bytes tests/ImageSharp.Tests/project.json | 3 + 14 files changed, 928 insertions(+) create mode 100644 src/ImageSharp.Drawing.Text/DrawText.cs create mode 100644 src/ImageSharp.Drawing.Text/GlyphBuilder.cs create mode 100644 src/ImageSharp.Drawing.Text/ImageSharp.Drawing.Text.xproj create mode 100644 src/ImageSharp.Drawing.Text/Properties/AssemblyInfo.cs create mode 100644 src/ImageSharp.Drawing.Text/TextGraphicsOptions.cs create mode 100644 src/ImageSharp.Drawing.Text/project.json create mode 100644 tests/ImageSharp.Tests/Drawing/Text/DrawText.cs create mode 100644 tests/ImageSharp.Tests/Drawing/Text/GlyphBuilder.cs create mode 100644 tests/ImageSharp.Tests/Drawing/Text/OutputText.cs create mode 100644 tests/ImageSharp.Tests/TestFont.cs create mode 100644 tests/ImageSharp.Tests/TestFonts/SixLaborsSampleAB.woff diff --git a/ImageSharp.sln b/ImageSharp.sln index 503a5b860..1bcea0b92 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -68,6 +68,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Sandbox46", "tes EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ImageSharp.Drawing.Paths", "src\ImageSharp.Drawing.Paths\ImageSharp.Drawing.Paths.xproj", "{E5BD4F96-28A8-410C-8B63-1C5731948549}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ImageSharp.Drawing.Text", "src\ImageSharp.Drawing.Text\ImageSharp.Drawing.Text.xproj", "{329D7698-65BC-48AD-A16F-428682964493}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -122,6 +124,10 @@ Global {E5BD4F96-28A8-410C-8B63-1C5731948549}.Debug|Any CPU.Build.0 = Debug|Any CPU {E5BD4F96-28A8-410C-8B63-1C5731948549}.Release|Any CPU.ActiveCfg = Release|Any CPU {E5BD4F96-28A8-410C-8B63-1C5731948549}.Release|Any CPU.Build.0 = Release|Any CPU + {329D7698-65BC-48AD-A16F-428682964493}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {329D7698-65BC-48AD-A16F-428682964493}.Debug|Any CPU.Build.0 = Debug|Any CPU + {329D7698-65BC-48AD-A16F-428682964493}.Release|Any CPU.ActiveCfg = Release|Any CPU + {329D7698-65BC-48AD-A16F-428682964493}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -140,5 +146,6 @@ Global {9E574A07-F879-4811-9C41-5CBDC6BAFDB7} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} {96188137-5FA6-4924-AB6E-4EFF79C6E0BB} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC} {E5BD4F96-28A8-410C-8B63-1C5731948549} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} + {329D7698-65BC-48AD-A16F-428682964493} = {815C0625-CD3D-440F-9F80-2D83856AB7AE} EndGlobalSection EndGlobal diff --git a/NuGet.config b/NuGet.config index b2c967cc9..322105d4d 100644 --- a/NuGet.config +++ b/NuGet.config @@ -1,6 +1,7 @@  + diff --git a/src/ImageSharp.Drawing.Text/DrawText.cs b/src/ImageSharp.Drawing.Text/DrawText.cs new file mode 100644 index 000000000..486aa6e40 --- /dev/null +++ b/src/ImageSharp.Drawing.Text/DrawText.cs @@ -0,0 +1,204 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System.Numerics; + + using Drawing; + using Drawing.Brushes; + using Drawing.Pens; + + using SixLabors.Fonts; + using System.Linq; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Draws the text onto the the image filled via the brush. + /// + /// The type of the color. + /// The image this method extends. + /// The text. + /// The font. + /// The color. + /// The location. + /// + /// The . + /// + public static Image DrawText(this Image source, string text, Font font, TColor color, Vector2 location) + where TColor : struct, IPixel + { + return source.DrawText(text, font, color, location, TextGraphicsOptions.Default); + } + + /// + /// Draws the text onto the the image filled via the brush. + /// + /// The type of the color. + /// The image this method extends. + /// The text. + /// The font. + /// The color. + /// The location. + /// The options. + /// + /// The . + /// + public static Image DrawText(this Image source, string text, Font font, TColor color, Vector2 location, TextGraphicsOptions options) + where TColor : struct, IPixel + { + return source.DrawText(text, font, Brushes.Solid(color), null, location, options); + } + + /// + /// Draws the text onto the the image filled via the brush. + /// + /// The type of the color. + /// The image this method extends. + /// The text. + /// The font. + /// The brush. + /// The location. + /// + /// The . + /// + public static Image DrawText(this Image source, string text, Font font, IBrush brush, Vector2 location) + where TColor : struct, IPixel + { + return source.DrawText(text, font, brush, location, TextGraphicsOptions.Default); + } + + /// + /// Draws the text onto the the image filled via the brush. + /// + /// The type of the color. + /// The image this method extends. + /// The text. + /// The font. + /// The brush. + /// The location. + /// The options. + /// + /// The . + /// + public static Image DrawText(this Image source, string text, Font font, IBrush brush, Vector2 location, TextGraphicsOptions options) + where TColor : struct, IPixel + { + return source.DrawText(text, font, brush, null, location, options); + } + + /// + /// Draws the text onto the the image outlined via the pen. + /// + /// The type of the color. + /// The image this method extends. + /// The text. + /// The font. + /// The pen. + /// The location. + /// + /// The . + /// + public static Image DrawText(this Image source, string text, Font font, IPen pen, Vector2 location) + where TColor : struct, IPixel + { + return source.DrawText(text, font, pen, location, TextGraphicsOptions.Default); + } + + /// + /// Draws the text onto the the image outlined via the pen. + /// + /// The type of the color. + /// The image this method extends. + /// The text. + /// The font. + /// The pen. + /// The location. + /// The options. + /// + /// The . + /// + public static Image DrawText(this Image source, string text, Font font, IPen pen, Vector2 location, TextGraphicsOptions options) + where TColor : struct, IPixel + { + return source.DrawText(text, font, null, pen, location, options); + } + + /// + /// Draws the text onto the the image filled via the brush then outlined via the pen. + /// + /// The type of the color. + /// The image this method extends. + /// The text. + /// The font. + /// The brush. + /// The pen. + /// The location. + /// + /// The . + /// + public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPen pen, Vector2 location) + where TColor : struct, IPixel + { + return source.DrawText(text, font, brush, pen, location, TextGraphicsOptions.Default); + } + + /// + /// Draws the text onto the the image filled via the brush then outlined via the pen. + /// + /// The type of the color. + /// The image this method extends. + /// The text. + /// The font. + /// The brush. + /// The pen. + /// The location. + /// The options. + /// + /// The . + /// + public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPen pen, Vector2 location, TextGraphicsOptions options) + where TColor : struct, IPixel + { + GlyphBuilder glyphBuilder = new GlyphBuilder(location); + + TextRenderer renderer = new TextRenderer(glyphBuilder); + + Vector2 dpi = new Vector2((float)source.MetaData.HorizontalResolution, (float)source.MetaData.VerticalResolution); + FontSpan style = new FontSpan(font) + { + ApplyKerning = options.ApplyKerning, + TabWidth = options.TabWidth + }; + + renderer.RenderText(text, style, dpi); + + System.Collections.Generic.IEnumerable shapesToDraw = glyphBuilder.Paths; + + GraphicsOptions pathOptions = (GraphicsOptions)options; + if (brush != null) + { + foreach (SixLabors.Shapes.IPath s in shapesToDraw) + { + source.Fill(brush, s, pathOptions); + } + } + + if (pen != null) + { + foreach (SixLabors.Shapes.IPath s in shapesToDraw) + { + source.Draw(pen, s, pathOptions); + } + } + + return source; + } + } +} diff --git a/src/ImageSharp.Drawing.Text/GlyphBuilder.cs b/src/ImageSharp.Drawing.Text/GlyphBuilder.cs new file mode 100644 index 000000000..ac5d01de7 --- /dev/null +++ b/src/ImageSharp.Drawing.Text/GlyphBuilder.cs @@ -0,0 +1,126 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Drawing +{ + using System.Collections.Generic; + using System.Numerics; + + using SixLabors.Fonts; + using SixLabors.Shapes; + + /// + /// rendering surface that Fonts can use to generate Shapes. + /// + internal class GlyphBuilder : IGlyphRenderer + { + private readonly PathBuilder builder = new PathBuilder(); + private readonly List paths = new List(); + private Vector2 currentPoint = default(Vector2); + + /// + /// Initializes a new instance of the class. + /// + public GlyphBuilder() + : this(Vector2.Zero) + { + // glyphs are renderd realative to bottom left so invert the Y axis to allow it to render on top left origin surface + this.builder = new PathBuilder(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The origin. + public GlyphBuilder(Vector2 origin) + { + this.builder = new PathBuilder(); + this.builder.SetOrigin(origin); + } + + /// + /// Gets the paths that have been rendered by this. + /// + public IEnumerable Paths => this.paths; + + /// + /// Begins the glyph. + /// + void IGlyphRenderer.BeginGlyph() + { + this.builder.Clear(); + } + + /// + /// Begins the figure. + /// + void IGlyphRenderer.BeginFigure() + { + this.builder.StartFigure(); + } + + /// + /// Draws a cubic bezier from the current point to the + /// + /// The second control point. + /// The third control point. + /// The point. + void IGlyphRenderer.CubicBezierTo(Vector2 secondControlPoint, Vector2 thirdControlPoint, Vector2 point) + { + this.builder.AddBezier(this.currentPoint, secondControlPoint, thirdControlPoint, point); + this.currentPoint = point; + } + + /// + /// Ends the glyph. + /// + void IGlyphRenderer.EndGlyph() + { + this.paths.Add(this.builder.Build()); + } + + /// + /// Ends the figure. + /// + void IGlyphRenderer.EndFigure() + { + this.builder.CloseFigure(); + } + + /// + /// Draws a line from the current point to the . + /// + /// The point. + void IGlyphRenderer.LineTo(Vector2 point) + { + this.builder.AddLine(this.currentPoint, point); + this.currentPoint = point; + } + + /// + /// Moves to current point to the supplied vector. + /// + /// The point. + void IGlyphRenderer.MoveTo(Vector2 point) + { + this.builder.StartFigure(); + this.currentPoint = point; + } + + /// + /// Draws a quadratics bezier from the current point to the + /// + /// The second control point. + /// The point. + void IGlyphRenderer.QuadraticBezierTo(Vector2 secondControlPoint, Vector2 point) + { + Vector2 c1 = (((secondControlPoint - this.currentPoint) * 2) / 3) + this.currentPoint; + Vector2 c2 = (((secondControlPoint - point) * 2) / 3) + point; + + this.builder.AddBezier(this.currentPoint, c1, c2, point); + this.currentPoint = point; + } + } +} diff --git a/src/ImageSharp.Drawing.Text/ImageSharp.Drawing.Text.xproj b/src/ImageSharp.Drawing.Text/ImageSharp.Drawing.Text.xproj new file mode 100644 index 000000000..4dfb394cf --- /dev/null +++ b/src/ImageSharp.Drawing.Text/ImageSharp.Drawing.Text.xproj @@ -0,0 +1,25 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 329d7698-65bc-48ad-a16f-428682964493 + ImageSharp.Drawing + .\obj + .\bin\ + v4.5.1 + + + 2.0 + + + True + + + + + + \ No newline at end of file diff --git a/src/ImageSharp.Drawing.Text/Properties/AssemblyInfo.cs b/src/ImageSharp.Drawing.Text/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..fba25a9db --- /dev/null +++ b/src/ImageSharp.Drawing.Text/Properties/AssemblyInfo.cs @@ -0,0 +1,6 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +// Common values read from `AssemblyInfo.Common.cs` diff --git a/src/ImageSharp.Drawing.Text/TextGraphicsOptions.cs b/src/ImageSharp.Drawing.Text/TextGraphicsOptions.cs new file mode 100644 index 000000000..e707ef5e5 --- /dev/null +++ b/src/ImageSharp.Drawing.Text/TextGraphicsOptions.cs @@ -0,0 +1,68 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Drawing +{ + /// + /// Options for influencing the drawing functions. + /// + public struct TextGraphicsOptions + { + /// + /// Represents the default . + /// + public static readonly TextGraphicsOptions Default = new TextGraphicsOptions(true); + + /// + /// Whether antialiasing should be applied. + /// + public bool Antialias; + + /// + /// Whether the text should be drawing with kerning enabled. + /// + public bool ApplyKerning; + + /// + /// The number of space widths a tab should lock to. + /// + public float TabWidth; + + /// + /// Initializes a new instance of the struct. + /// + /// If set to true [enable antialiasing]. + public TextGraphicsOptions(bool enableAntialiasing) + { + this.Antialias = enableAntialiasing; + this.ApplyKerning = true; + this.TabWidth = 4; + } + + /// + /// Performs an implicit conversion from to . + /// + /// The options. + /// + /// The result of the conversion. + /// + public static implicit operator TextGraphicsOptions(GraphicsOptions options) + { + return new TextGraphicsOptions(options.Antialias); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The options. + /// + /// The result of the conversion. + /// + public static explicit operator GraphicsOptions(TextGraphicsOptions options) + { + return new GraphicsOptions(options.Antialias); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp.Drawing.Text/project.json b/src/ImageSharp.Drawing.Text/project.json new file mode 100644 index 000000000..f446f9119 --- /dev/null +++ b/src/ImageSharp.Drawing.Text/project.json @@ -0,0 +1,95 @@ +{ + "version": "1.0.0-alpha2-*", + "title": "ImageSharp.Drawing.Text", + "description": "A cross-platform library for the processing of image files; written in C#", + "authors": [ + "James Jackson-South and contributors" + ], + "packOptions": { + "owners": [ + "James Jackson-South and contributors" + ], + "projectUrl": "https://github.com/JimBobSquarePants/ImageSharp", + "licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0", + "iconUrl": "https://raw.githubusercontent.com/JimBobSquarePants/ImageSharp/master/build/icons/imagesharp-logo-128.png", + "requireLicenseAcceptance": false, + "repository": { + "type": "git", + "url": "https://github.com/JimBobSquarePants/ImageSharp" + }, + "tags": [ + "Image Resize Crop Gif Jpg Jpeg Bitmap Png Core" + ] + }, + "buildOptions": { + "allowUnsafe": true, + "xmlDoc": true, + "additionalArguments": [ "/additionalfile:../Shared/stylecop.json", "/ruleset:../../ImageSharp.ruleset" ], + "compile": [ + "../Shared/*.cs" + ] + }, + "configurations": { + "Release": { + "buildOptions": { + "warningsAsErrors": true, + "optimize": true + } + } + }, + "dependencies": { + "ImageSharp": { + "target": "project" + }, + "SixLabors.Fonts": "0.1.0-ci0041", + "ImageSharp.Drawing.Paths": { + "target": "project" + }, + "StyleCop.Analyzers": { + "version": "1.0.0", + "type": "build" + }, + "System.Buffers": "4.0.0", + "System.Runtime.CompilerServices.Unsafe": "4.0.0" + }, + "frameworks": { + "netstandard1.1": { + "dependencies": { + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11", + "System.Diagnostics.Tools": "4.0.1", + "System.IO": "4.1.0", + "System.IO.Compression": "4.1.0", + "System.Linq": "4.1.0", + "System.Numerics.Vectors": "4.1.1", + "System.ObjectModel": "4.0.12", + "System.Resources.ResourceManager": "4.0.1", + "System.Runtime.Extensions": "4.1.0", + "System.Runtime.InteropServices": "4.1.0", + "System.Runtime.Numerics": "4.0.1", + "System.Text.Encoding.Extensions": "4.0.11", + "System.Threading": "4.0.11", + "System.Threading.Tasks": "4.0.11", + "System.Threading.Tasks.Parallel": "4.0.1" + } + }, + "net45": { + "dependencies": { + "System.Numerics.Vectors": "4.1.1", + "System.Threading.Tasks.Parallel": "4.0.0" + }, + "frameworkAssemblies": { + "System.Runtime": { "type": "build" } + } + }, + "net461": { + "dependencies": { + "System.Threading.Tasks.Parallel": "4.0.0" + }, + "frameworkAssemblies": { + "System.Runtime": { "type": "build" }, + "System.Numerics": "4.0.0.0" + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs new file mode 100644 index 000000000..2a2cb8a07 --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs @@ -0,0 +1,194 @@ + +namespace ImageSharp.Tests.Drawing.Text +{ + using System; + using System.IO; + using ImageSharp; + using ImageSharp.Drawing.Brushes; + using Processing; + using System.Collections.Generic; + using Xunit; + using ImageSharp.Drawing; + using System.Numerics; + using SixLabors.Shapes; + using ImageSharp.Drawing.Processors; + using ImageSharp.Drawing.Pens; + using SixLabors.Fonts; + using Paths; + + public class DrawText : IDisposable + { + Color color = Color.HotPink; + SolidBrush brush = Brushes.Solid(Color.HotPink); + IPath path = new SixLabors.Shapes.Path(new LinearLineSegment(new Vector2[] { + new Vector2(10,10), + new Vector2(20,10), + new Vector2(20,10), + new Vector2(30,10), + })); + private ProcessorWatchingImage img; + private readonly FontCollection FontCollection; + private readonly Font Font; + + public DrawText() + { + this.FontCollection = new FontCollection(); + this.Font = FontCollection.Install(TestFontUtilities.GetPath("SixLaborsSampleAB.woff")); + this.img = new ProcessorWatchingImage(10, 10); + } + + public void Dispose() + { + img.Dispose(); + } + + [Fact] + public void FillsForEachACharachterWhenBrushSetAndNotPen() + { + img.DrawText("123", this.Font, Brushes.Solid(Color.Red), null, Vector2.Zero, new TextGraphicsOptions(true)); + + Assert.NotEmpty(img.ProcessorApplications); + Assert.Equal(3, img.ProcessorApplications.Count); // 3 fills where applied + Assert.IsType>(img.ProcessorApplications[0].processor); + } + + [Fact] + public void FillsForEachACharachterWhenBrushSetAndNotPenDefaultOptions() + { + img.DrawText("123", this.Font, Brushes.Solid(Color.Red), null, Vector2.Zero); + + Assert.NotEmpty(img.ProcessorApplications); + Assert.Equal(3, img.ProcessorApplications.Count); // 3 fills where applied + Assert.IsType>(img.ProcessorApplications[0].processor); + } + + + [Fact] + public void FillsForEachACharachterWhenBrushSet() + { + img.DrawText("123", this.Font, Brushes.Solid(Color.Red), Vector2.Zero, new TextGraphicsOptions(true)); + + Assert.NotEmpty(img.ProcessorApplications); + Assert.Equal(3, img.ProcessorApplications.Count); // 3 fills where applied + Assert.IsType>(img.ProcessorApplications[0].processor); + } + + [Fact] + public void FillsForEachACharachterWhenBrushSetDefaultOptions() + { + img.DrawText("123", this.Font, Brushes.Solid(Color.Red), Vector2.Zero); + + Assert.NotEmpty(img.ProcessorApplications); + Assert.Equal(3, img.ProcessorApplications.Count); // 3 fills where applied + Assert.IsType>(img.ProcessorApplications[0].processor); + } + + [Fact] + public void FillsForEachACharachterWhenColorSet() + { + img.DrawText("123", this.Font, Color.Red, Vector2.Zero, new TextGraphicsOptions(true)); + + Assert.NotEmpty(img.ProcessorApplications); + Assert.Equal(3, img.ProcessorApplications.Count); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + SolidBrush brush = Assert.IsType>(processor.Brush); + Assert.Equal(Color.Red, brush.Color); + } + + [Fact] + public void FillsForEachACharachterWhenColorSetDefaultOptions() + { + img.DrawText("123", this.Font, Color.Red, Vector2.Zero); + + Assert.NotEmpty(img.ProcessorApplications); + Assert.Equal(3, img.ProcessorApplications.Count); + Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + + SolidBrush brush = Assert.IsType>(processor.Brush); + Assert.Equal(Color.Red, brush.Color); + } + + [Fact] + public void DrawForEachACharachterWhenPenSetAndNotBrush() + { + img.DrawText("123", this.Font, null, Pens.Dash(Color.Red, 1), Vector2.Zero, new TextGraphicsOptions(true)); + + Assert.NotEmpty(img.ProcessorApplications); + Assert.Equal(3, img.ProcessorApplications.Count); // 3 fills where applied + Assert.IsType>(img.ProcessorApplications[0].processor); + } + + [Fact] + public void DrawForEachACharachterWhenPenSetAndNotBrushDefaultOptions() + { + img.DrawText("123", this.Font, null, Pens.Dash(Color.Red, 1), Vector2.Zero); + + Assert.NotEmpty(img.ProcessorApplications); + Assert.Equal(3, img.ProcessorApplications.Count); // 3 fills where applied + Assert.IsType>(img.ProcessorApplications[0].processor); + } + + + [Fact] + public void DrawForEachACharachterWhenPenSet() + { + img.DrawText("123", this.Font, Pens.Dash(Color.Red, 1), Vector2.Zero, new TextGraphicsOptions(true)); + + Assert.NotEmpty(img.ProcessorApplications); + Assert.Equal(3, img.ProcessorApplications.Count); // 3 fills where applied + Assert.IsType>(img.ProcessorApplications[0].processor); + } + + [Fact] + public void DrawForEachACharachterWhenPenSetDefaultOptions() + { + img.DrawText("123", this.Font, Pens.Dash(Color.Red, 1), Vector2.Zero); + + Assert.NotEmpty(img.ProcessorApplications); + Assert.Equal(3, img.ProcessorApplications.Count); // 3 fills where applied + Assert.IsType>(img.ProcessorApplications[0].processor); + } + + [Fact] + public void DrawForEachACharachterWhenPenSetAndFillFroEachWhenBrushSet() + { + img.DrawText("123", this.Font, Brushes.Solid(Color.Red), Pens.Dash(Color.Red, 1), Vector2.Zero, new TextGraphicsOptions(true)); + + Assert.NotEmpty(img.ProcessorApplications); + Assert.Equal(6, img.ProcessorApplications.Count); + } + + [Fact] + public void DrawForEachACharachterWhenPenSetAndFillFroEachWhenBrushSetDefaultOptions() + { + img.DrawText("123", this.Font, Brushes.Solid(Color.Red), Pens.Dash(Color.Red, 1), Vector2.Zero); + + Assert.NotEmpty(img.ProcessorApplications); + Assert.Equal(6, img.ProcessorApplications.Count); + } + + [Fact] + public void BrushAppliesBeforPen() + { + img.DrawText("1", this.Font, Brushes.Solid(Color.Red), Pens.Dash(Color.Red, 1), Vector2.Zero, new TextGraphicsOptions(true)); + + Assert.NotEmpty(img.ProcessorApplications); + Assert.Equal(2, img.ProcessorApplications.Count); + Assert.IsType>(img.ProcessorApplications[0].processor); + Assert.IsType>(img.ProcessorApplications[1].processor); + } + + [Fact] + public void BrushAppliesBeforPenDefaultOptions() + { + img.DrawText("1", this.Font, Brushes.Solid(Color.Red), Pens.Dash(Color.Red, 1), Vector2.Zero); + + Assert.NotEmpty(img.ProcessorApplications); + Assert.Equal(2, img.ProcessorApplications.Count); + Assert.IsType>(img.ProcessorApplications[0].processor); + Assert.IsType>(img.ProcessorApplications[1].processor); + } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/Text/GlyphBuilder.cs b/tests/ImageSharp.Tests/Drawing/Text/GlyphBuilder.cs new file mode 100644 index 000000000..1faa5edd3 --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/Text/GlyphBuilder.cs @@ -0,0 +1,68 @@ + +namespace ImageSharp.Tests.Drawing.Text +{ + using ImageSharp.Drawing; + using SixLabors.Fonts; + using System; + using System.Collections.Generic; + using System.Linq; + using System.Numerics; + using System.Threading.Tasks; + using Xunit; + + public class GlyphBuilderTests + { + [Fact] + public void OriginUsed() + { + // Y axis is inverted as it expects to be drawing for bottom left + var fullBuilder = new GlyphBuilder(new System.Numerics.Vector2(10, 99)); + IGlyphRenderer builder = fullBuilder; + + builder.BeginGlyph(); + builder.BeginFigure(); + builder.MoveTo(new Vector2(0, 0)); + builder.LineTo(new Vector2(0, 10)); // becomes 0, -10 + + builder.CubicBezierTo( + new Vector2(15, 15), // control point - will not be in the final point collection + new Vector2(15, 10), // control point - will not be in the final point collection + new Vector2(10, 10));// becomes 10, -10 + + builder.QuadraticBezierTo( + new Vector2(10, 5), // control point - will not be in the final point collection + new Vector2(10, 0)); + + builder.EndFigure(); + builder.EndGlyph(); + + var points = fullBuilder.Paths.Single().Flatten().Single().Points; + + Assert.Contains(new Vector2(10, 99), points); + Assert.Contains(new Vector2(10, 109), points); + Assert.Contains(new Vector2(20, 99), points); + Assert.Contains(new Vector2(20, 109), points); + } + + [Fact] + public void EachGlypeCausesNewPath() + { + // Y axis is inverted as it expects to be drawing for bottom left + GlyphBuilder fullBuilder = new GlyphBuilder(); + IGlyphRenderer builder = fullBuilder; + for (var i = 0; i < 10; i++) + { + builder.BeginGlyph(); + builder.BeginFigure(); + builder.MoveTo(new Vector2(0, 0)); + builder.LineTo(new Vector2(0, 10)); // becomes 0, -10 + builder.LineTo(new Vector2(10, 10));// becomes 10, -10 + builder.LineTo(new Vector2(10, 0)); + builder.EndFigure(); + builder.EndGlyph(); + } + + Assert.Equal(10, fullBuilder.Paths.Count()); + } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/Text/OutputText.cs b/tests/ImageSharp.Tests/Drawing/Text/OutputText.cs new file mode 100644 index 000000000..ae007727a --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/Text/OutputText.cs @@ -0,0 +1,41 @@ + +namespace ImageSharp.Tests.Drawing.Text +{ + using System; + using System.IO; + using ImageSharp; + using ImageSharp.Drawing.Brushes; + using Processing; + using System.Collections.Generic; + using Xunit; + using ImageSharp.Drawing; + using System.Numerics; + using SixLabors.Shapes; + using ImageSharp.Drawing.Processors; + using ImageSharp.Drawing.Pens; + using SixLabors.Fonts; + + public class OutputText : FileTestBase + { + private readonly FontCollection FontCollection; + private readonly Font Font; + + public OutputText() + { + this.FontCollection = new FontCollection(); + this.Font = FontCollection.Install(TestFontUtilities.GetPath("SixLaborsSampleAB.woff")); + } + + [Fact] + public void DrawAB() + { + //draws 2 overlapping triangle glyphs twice 1 set on each line + using (var img = new Image(100, 200)) + { + img.Fill(Color.DarkBlue) + .DrawText("AB\nAB", new Font(this.Font, 50), Color.Red, new Vector2(0, 0)); + img.Save($"{this.CreateOutputDirectory("Drawing", "Text")}/AB.png"); + } + } + } +} diff --git a/tests/ImageSharp.Tests/TestFont.cs b/tests/ImageSharp.Tests/TestFont.cs new file mode 100644 index 000000000..3a5bb2b2c --- /dev/null +++ b/tests/ImageSharp.Tests/TestFont.cs @@ -0,0 +1,90 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.Collections.Concurrent; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Reflection; + + /// + /// A test image file. + /// + public static class TestFontUtilities + { + /// + /// The formats directory. + /// + private static readonly string FormatsDirectory = GetFontsDirectory(); + + /// + /// Gets the full qualified path to the file. + /// + /// + /// The file path. + /// + /// + /// The . + /// + public static string GetPath(string file) + { + return Path.Combine(FormatsDirectory, file); + } + + /// + /// Gets the correct path to the formats directory. + /// + /// + /// The . + /// + private static string GetFontsDirectory() + { + List directories = new List< string > { + "TestFonts/", // Here for code coverage tests. + "tests/ImageSharp.Tests/TestFonts/", // from travis/build script + "../../../ImageSharp.Tests/TestFonts/", // from Sandbox46 + "../../../../TestFonts/" + }; + + directories = directories.SelectMany(x => new[] + { + Path.GetFullPath(x) + }).ToList(); + + AddFormatsDirectoryFromTestAssebmlyPath(directories); + + var directory = directories.FirstOrDefault(x => Directory.Exists(x)); + + if(directory != null) + { + return directory; + } + + throw new System.Exception($"Unable to find Fonts directory at any of these locations [{string.Join(", ", directories)}]"); + } + + /// + /// The path returned by Path.GetFullPath(x) can be relative to dotnet framework directory + /// in certain scenarios like dotTrace test profiling. + /// This method calculates and adds the format directory based on the ImageSharp.Tests assembly location. + /// + /// The directories list + private static void AddFormatsDirectoryFromTestAssebmlyPath(List directories) + { + string assemblyLocation = typeof(TestFile).GetTypeInfo().Assembly.Location; + assemblyLocation = Path.GetDirectoryName(assemblyLocation); + + if (assemblyLocation != null) + { + string dirFromAssemblyLocation = Path.Combine(assemblyLocation, "../../../TestFonts/"); + dirFromAssemblyLocation = Path.GetFullPath(dirFromAssemblyLocation); + directories.Add(dirFromAssemblyLocation); + } + } + } +} diff --git a/tests/ImageSharp.Tests/TestFonts/SixLaborsSampleAB.woff b/tests/ImageSharp.Tests/TestFonts/SixLaborsSampleAB.woff new file mode 100644 index 0000000000000000000000000000000000000000..277749dfb19f13eaa020c9e823b76e5a91cf6d49 GIT binary patch literal 1352 zcmZuweM}Qc6rXLoYxyV?DB22##Z=Vus~{3g#k2&9U;{;jLy5ZELR%H23MwKhs}&F| zQ9!LxQ4uKuQ9vtP@~DA#V{0MMKoM$zko!Og&|SzTOz;g$YmIAeAn8$W#H-(0Xy0qyTnb05Z0dpY& zz#gzY3sK_9IBrp#Q1%me=FV{k!h+*OE|9X#(M!7Ng_3>Zm43or4{yU(D}eW5AGY|4oW5X^JVlZcI=vhhqsJe48+$4^ z*bEHK6TCGXO6RUKD7oSIHbd%JWo{Xb?g7k$4zEEL(%aR*IxOdi2gd)J6Nm-X^P}fJ zS&Fo*Ja#4c$i&UdeE)%`r7wmmnA!x!&e-z>r$(3}y=$jy(?tBJ2bad zjW3f;9Ba3F_2;Fd6BbN^u(`KqA5TI(RO=Tf4zwZW5jOu@x(LjhX3rVNg{t$Nw~EP9 ziGd{DJZNutk9lSx=%6-GdXiJH`}F0~Sa#3zsrE^#obnW_W+!g*PzEGUex}|~wY(4a zQZF1j=`7Tun#%e2XZmff;$jH~z1S@XL-M?(MrumtPZ_5~jD&M&nPc*Z%AE)Ztxj>b zQh&oQj{2ZnfRCsiDQ=;^`m+|yGqJm~&WyR1+$mvZ@F>t5BKz?U7}+Ul`qn={7>64w z#wLTd@R~Pp#g^{a6p7%uAyT*XyAvukPP>MI(V-b8SmVF_F1CKnBS}*+>`751W5Hp^ zU=Y(vdtzTNQRmX7ZW!h#!eaO?3`HcH1!j8EY`=rO+Z;F}0JC2h1EAAKVa(iI_rO+A z&?AUidPy zW%He9ANBjS=`Te?ljSR7)-A3jkDb|ZJm*hc`Hk|%`b+B3#qU?H75#=@*)-ukdijTs zP5Xy^UL<`j-g>aF<5j!4YjgZbuJirAtCdgu2A)P2A-+OO!2$2pU1C|NyX239`3)O& zA^A&6-%agnAJoKOoL957v#M^+yoA`=yElLDXs>IjS~uhCBgvQiQdys++j`;6Td8%Z zCWkR=;V;JO+u7q+tr4%=np&4v6?eW%*gYXJ2At*jYadjMn3!(i5py5Q6ITs?PC99A zt1#YgR2fW8MZ-}QJvsNr_#W&p-od`IRSv<9>Q~7+uVOdr(JislQO*1!bLr_*NrC4s zW=xOY73rm_{EnW?N{cD*`|J&F{cDGH*5+x@_&jf+y$1?#grc}yUOnQ%S21c7Dmfuf O(Gpq|B>YSp1mGVP7wVk= literal 0 HcmV?d00001 diff --git a/tests/ImageSharp.Tests/project.json b/tests/ImageSharp.Tests/project.json index 3761bb385..7c67a5c70 100644 --- a/tests/ImageSharp.Tests/project.json +++ b/tests/ImageSharp.Tests/project.json @@ -31,6 +31,9 @@ "ImageSharp.Drawing.Paths": { "target": "project" }, + "ImageSharp.Drawing.Text": { + "target": "project" + }, "ImageSharp.Formats.Png": { "target": "project" }, From bc1440798f6d522bdc366b9ab3922bfdb5c26c44 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sun, 5 Mar 2017 15:24:03 +0000 Subject: [PATCH 133/142] move to alpha release --- NuGet.config | 1 - src/ImageSharp.Drawing.Text/DrawText.cs | 1 - src/ImageSharp.Drawing.Text/project.json | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/NuGet.config b/NuGet.config index 322105d4d..b2c967cc9 100644 --- a/NuGet.config +++ b/NuGet.config @@ -1,7 +1,6 @@  - diff --git a/src/ImageSharp.Drawing.Text/DrawText.cs b/src/ImageSharp.Drawing.Text/DrawText.cs index 486aa6e40..28781fab2 100644 --- a/src/ImageSharp.Drawing.Text/DrawText.cs +++ b/src/ImageSharp.Drawing.Text/DrawText.cs @@ -12,7 +12,6 @@ namespace ImageSharp using Drawing.Pens; using SixLabors.Fonts; - using System.Linq; /// /// Extension methods for the type. diff --git a/src/ImageSharp.Drawing.Text/project.json b/src/ImageSharp.Drawing.Text/project.json index f446f9119..66d0e7d26 100644 --- a/src/ImageSharp.Drawing.Text/project.json +++ b/src/ImageSharp.Drawing.Text/project.json @@ -41,7 +41,7 @@ "ImageSharp": { "target": "project" }, - "SixLabors.Fonts": "0.1.0-ci0041", + "SixLabors.Fonts": "0.1.0-alpha0001", "ImageSharp.Drawing.Paths": { "target": "project" }, From c9e17d896644503b8e0db2c40de859214ef4cd0a Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 Mar 2017 17:56:30 +0100 Subject: [PATCH 134/142] PixelAccessor and PixelArea using PinnedBuffer --- .../Colors/PackedPixel/BulkPixelOperations.cs | 168 ------------ .../BulkPixelOperations{TColor}.cs | 256 ++++++++++++++++++ .../Common/Memory/ArrayPointer{T}.cs | 32 +-- src/ImageSharp/Common/Memory/PinnedBuffer.cs | 89 ++++-- .../Common/Memory/PixelDataPool{T}.cs | 60 ++++ src/ImageSharp/Image/ImageBase{TColor}.cs | 10 +- src/ImageSharp/Image/PixelAccessor{TColor}.cs | 139 ++++------ src/ImageSharp/Image/PixelArea{TColor}.cs | 108 ++------ src/ImageSharp/Image/PixelPool{TColor}.cs | 43 --- .../ImageSharp.Sandbox46.csproj | 6 + .../Common/PinnedBufferTests.cs | 25 ++ .../ImageSharp.Tests/Image/PixelPoolTests.cs | 49 +++- 12 files changed, 552 insertions(+), 433 deletions(-) delete mode 100644 src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs create mode 100644 src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs create mode 100644 src/ImageSharp/Common/Memory/PixelDataPool{T}.cs delete mode 100644 src/ImageSharp/Image/PixelPool{TColor}.cs diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs deleted file mode 100644 index ffa28fc13..000000000 --- a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs +++ /dev/null @@ -1,168 +0,0 @@ -namespace ImageSharp -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - - public unsafe class BulkPixelOperations - where TColor : struct, IPixel - { - public static BulkPixelOperations Instance { get; } = default(TColor).BulkOperations; - - private static readonly int ColorSize = Unsafe.SizeOf(); - - internal virtual void PackFromVector4( - ArrayPointer sourceVectors, - ArrayPointer destColors, - int count) - { - Vector4* sp = (Vector4*)sourceVectors.PointerAtOffset; - byte* dp = (byte*)destColors; - - for (int i = 0; i < count; i++) - { - Vector4 v = Unsafe.Read(sp); - TColor c = default(TColor); - c.PackFromVector4(v); - Unsafe.Write(dp, c); - - sp++; - dp += ColorSize; - } - } - - internal virtual void PackToVector4( - ArrayPointer sourceColors, - ArrayPointer destVectors, - int count) - { - byte* sp = (byte*)sourceColors; - Vector4* dp = (Vector4*)destVectors.PointerAtOffset; - - for (int i = 0; i < count; i++) - { - TColor c = Unsafe.Read(sp); - *dp = c.ToVector4(); - sp += ColorSize; - dp++; - } - } - - internal virtual void PackFromXyzBytes( - ArrayPointer sourceBytes, - ArrayPointer destColors, - int count) - { - byte* sp = (byte*)sourceBytes; - byte* dp = (byte*)destColors.PointerAtOffset; - - for (int i = 0; i < count; i++) - { - TColor c = default(TColor); - c.PackFromBytes(sp[0], sp[1], sp[2], 255); - Unsafe.Write(dp, c); - sp += 3; - dp += ColorSize; - } - } - - internal virtual void PackToXyzBytes( - ArrayPointer sourceColors, - ArrayPointer destBytes, int count) - { - byte* sp = (byte*)sourceColors; - byte[] dest = destBytes.Array; - - for (int i = destBytes.Offset; i < destBytes.Offset + count*3; i+=3) - { - TColor c = Unsafe.Read(sp); - c.ToXyzBytes(dest, i); - sp += ColorSize; - } - } - - internal virtual void PackFromXyzwBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) - { - byte* sp = (byte*)sourceBytes; - byte* dp = (byte*)destColors.PointerAtOffset; - - for (int i = 0; i < count; i++) - { - TColor c = default(TColor); - c.PackFromBytes(sp[0], sp[1], sp[2], sp[3]); - Unsafe.Write(dp, c); - sp += 4; - dp += ColorSize; - } - } - - internal virtual void PackToXyzwBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) - { - byte* sp = (byte*)sourceColors; - byte[] dest = destBytes.Array; - - for (int i = destBytes.Offset; i < destBytes.Offset + count * 4; i += 4) - { - TColor c = Unsafe.Read(sp); - c.ToXyzwBytes(dest, i); - sp += ColorSize; - } - } - - internal virtual void PackFromZyxBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) - { - byte* sp = (byte*)sourceBytes; - byte* dp = (byte*)destColors.PointerAtOffset; - - for (int i = 0; i < count; i++) - { - TColor c = default(TColor); - c.PackFromBytes(sp[2], sp[1], sp[0], 255); - Unsafe.Write(dp, c); - sp += 3; - dp += ColorSize; - } - } - - internal virtual void PackToZyxBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) - { - byte* sp = (byte*)sourceColors; - byte[] dest = destBytes.Array; - - for (int i = destBytes.Offset; i < destBytes.Offset + count * 3; i += 3) - { - TColor c = Unsafe.Read(sp); - c.ToZyxBytes(dest, i); - sp += ColorSize; - } - } - - internal virtual void PackFromZyxwBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) - { - byte* sp = (byte*)sourceBytes; - byte* dp = (byte*)destColors.PointerAtOffset; - - for (int i = 0; i < count; i++) - { - TColor c = default(TColor); - c.PackFromBytes(sp[2], sp[1], sp[0], sp[3]); - Unsafe.Write(dp, c); - sp += 4; - dp += ColorSize; - } - } - - internal virtual void PackToZyxwBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) - { - byte* sp = (byte*)sourceColors; - byte[] dest = destBytes.Array; - - for (int i = destBytes.Offset; i < destBytes.Offset + count * 4; i += 4) - { - TColor c = Unsafe.Read(sp); - c.ToZyxwBytes(dest, i); - sp += ColorSize; - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs new file mode 100644 index 000000000..557d59a16 --- /dev/null +++ b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs @@ -0,0 +1,256 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System.Numerics; + using System.Runtime.CompilerServices; + + /// + /// A stateless class implementing Strategy Pattern for batched pixel-data conversion operations + /// for pixel buffers of type . + /// + /// The pixel format. + public unsafe class BulkPixelOperations + where TColor : struct, IPixel + { + /// + /// The size of in bytes + /// + private static readonly int ColorSize = Unsafe.SizeOf(); + + /// + /// Gets the global instance for the pixel type + /// + public static BulkPixelOperations Instance { get; } = default(TColor).BulkOperations; + + /// + /// Bulk version of + /// + /// The to the source vectors. + /// The to the destination colors. + /// The number of pixels to convert. + internal virtual void PackFromVector4( + ArrayPointer sourceVectors, + ArrayPointer destColors, + int count) + { + Vector4* sp = (Vector4*)sourceVectors.PointerAtOffset; + byte* dp = (byte*)destColors; + + for (int i = 0; i < count; i++) + { + Vector4 v = Unsafe.Read(sp); + TColor c = default(TColor); + c.PackFromVector4(v); + Unsafe.Write(dp, c); + + sp++; + dp += ColorSize; + } + } + + /// + /// Bulk version of . + /// + /// The to the source colors. + /// The to the destination vectors. + /// The number of pixels to convert. + internal virtual void PackToVector4( + ArrayPointer sourceColors, + ArrayPointer destVectors, + int count) + { + byte* sp = (byte*)sourceColors; + Vector4* dp = (Vector4*)destVectors.PointerAtOffset; + + for (int i = 0; i < count; i++) + { + TColor c = Unsafe.Read(sp); + *dp = c.ToVector4(); + sp += ColorSize; + dp++; + } + } + + /// + /// Bulk version of that converts data in . + /// + /// + /// + /// + internal virtual void PackFromXyzBytes( + ArrayPointer sourceBytes, + ArrayPointer destColors, + int count) + { + byte* sp = (byte*)sourceBytes; + byte* dp = (byte*)destColors.PointerAtOffset; + + for (int i = 0; i < count; i++) + { + TColor c = default(TColor); + c.PackFromBytes(sp[0], sp[1], sp[2], 255); + Unsafe.Write(dp, c); + sp += 3; + dp += ColorSize; + } + } + + /// + /// Bulk version of . + /// + /// + /// + /// + internal virtual void PackToXyzBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + { + byte* sp = (byte*)sourceColors; + byte[] dest = destBytes.Array; + + for (int i = destBytes.Offset; i < destBytes.Offset + count * 3; i += 3) + { + TColor c = Unsafe.Read(sp); + c.ToXyzBytes(dest, i); + sp += ColorSize; + } + } + + /// + /// Bulk version of that converts data in . + /// + /// + /// + /// + internal virtual void PackFromXyzwBytes( + ArrayPointer sourceBytes, + ArrayPointer destColors, + int count) + { + byte* sp = (byte*)sourceBytes; + byte* dp = (byte*)destColors.PointerAtOffset; + + for (int i = 0; i < count; i++) + { + TColor c = default(TColor); + c.PackFromBytes(sp[0], sp[1], sp[2], sp[3]); + Unsafe.Write(dp, c); + sp += 4; + dp += ColorSize; + } + } + + /// + /// Bulk version of . + /// + /// + /// + /// + internal virtual void PackToXyzwBytes( + ArrayPointer sourceColors, + ArrayPointer destBytes, + int count) + { + byte* sp = (byte*)sourceColors; + byte[] dest = destBytes.Array; + + for (int i = destBytes.Offset; i < destBytes.Offset + count * 4; i += 4) + { + TColor c = Unsafe.Read(sp); + c.ToXyzwBytes(dest, i); + sp += ColorSize; + } + } + + /// + /// Bulk version of that converts data in . + /// + /// + /// + /// + internal virtual void PackFromZyxBytes( + ArrayPointer sourceBytes, + ArrayPointer destColors, + int count) + { + byte* sp = (byte*)sourceBytes; + byte* dp = (byte*)destColors.PointerAtOffset; + + for (int i = 0; i < count; i++) + { + TColor c = default(TColor); + c.PackFromBytes(sp[2], sp[1], sp[0], 255); + Unsafe.Write(dp, c); + sp += 3; + dp += ColorSize; + } + } + + /// + /// Bulk version of . + /// + /// + /// + /// + internal virtual void PackToZyxBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + { + byte* sp = (byte*)sourceColors; + byte[] dest = destBytes.Array; + + for (int i = destBytes.Offset; i < destBytes.Offset + count * 3; i += 3) + { + TColor c = Unsafe.Read(sp); + c.ToZyxBytes(dest, i); + sp += ColorSize; + } + } + + /// + /// Bulk version of that converts data in . + /// + /// + /// + /// + internal virtual void PackFromZyxwBytes( + ArrayPointer sourceBytes, + ArrayPointer destColors, + int count) + { + byte* sp = (byte*)sourceBytes; + byte* dp = (byte*)destColors.PointerAtOffset; + + for (int i = 0; i < count; i++) + { + TColor c = default(TColor); + c.PackFromBytes(sp[2], sp[1], sp[0], sp[3]); + Unsafe.Write(dp, c); + sp += 4; + dp += ColorSize; + } + } + + /// + /// Bulk version of . + /// + /// + /// + /// + internal virtual void PackToZyxwBytes( + ArrayPointer sourceColors, + ArrayPointer destBytes, + int count) + { + byte* sp = (byte*)sourceColors; + byte[] dest = destBytes.Array; + + for (int i = destBytes.Offset; i < destBytes.Offset + count * 4; i += 4) + { + TColor c = Unsafe.Read(sp); + c.ToZyxwBytes(dest, i); + sp += ColorSize; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs b/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs index 8f99327ba..e0b728095 100644 --- a/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs +++ b/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs @@ -68,27 +68,12 @@ namespace ImageSharp /// public IntPtr PointerAtOffset { get; private set; } - /// - /// Forms a slice out of the given ArrayPointer, beginning at 'offset'. - /// - /// The offset in number of elements - /// The offseted (sliced) ArrayPointer - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ArrayPointer Slice(int offset) - { - ArrayPointer result = default(ArrayPointer); - result.Array = this.Array; - result.Offset = this.Offset + offset; - result.PointerAtOffset = this.PointerAtOffset + (Unsafe.SizeOf() * offset); - return result; - } - /// /// Convertes instance to a raw 'void*' pointer /// /// The to convert [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator void*(ArrayPointer arrayPointer) + public static explicit operator void* (ArrayPointer arrayPointer) { return (void*)arrayPointer.PointerAtOffset; } @@ -102,5 +87,20 @@ namespace ImageSharp { return (byte*)arrayPointer.PointerAtOffset; } + + /// + /// Forms a slice out of the given ArrayPointer, beginning at 'offset'. + /// + /// The offset in number of elements + /// The offseted (sliced) ArrayPointer + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ArrayPointer Slice(int offset) + { + ArrayPointer result = default(ArrayPointer); + result.Array = this.Array; + result.Offset = this.Offset + offset; + result.PointerAtOffset = this.PointerAtOffset + (Unsafe.SizeOf() * offset); + return result; + } } } \ No newline at end of file diff --git a/src/ImageSharp/Common/Memory/PinnedBuffer.cs b/src/ImageSharp/Common/Memory/PinnedBuffer.cs index c6e0c7c6f..201d93b56 100644 --- a/src/ImageSharp/Common/Memory/PinnedBuffer.cs +++ b/src/ImageSharp/Common/Memory/PinnedBuffer.cs @@ -5,25 +5,23 @@ namespace ImageSharp using System.Runtime.InteropServices; /// - /// Manages a pinned buffer of 'T' as a Disposable resource. + /// Manages a pinned buffer of value type data 'T' as a Disposable resource. /// The backing array is either pooled or comes from the outside. - /// TODO: Should replace the pinning/dispose logic in several classes like or ! /// /// The value type. internal class PinnedBuffer : IDisposable where T : struct { + /// + /// A handle that allows to access the managed as an unmanaged memory by pinning. + /// private GCHandle handle; - private bool isBufferRented; - - private bool isDisposed; - /// - /// TODO: Consider reusing functionality of + /// A value indicating wether this instance should return the array to the pool. /// - private static readonly ArrayPool ArrayPool = ArrayPool.Create(); - + private bool isPoolingOwner; + /// /// Initializes a new instance of the class. /// @@ -31,8 +29,8 @@ namespace ImageSharp public PinnedBuffer(int count) { this.Count = count; - this.Array = ArrayPool.Rent(count); - this.isBufferRented = true; + this.Array = PixelDataPool.Rent(count); + this.isPoolingOwner = true; this.Pin(); } @@ -48,7 +46,34 @@ namespace ImageSharp } /// - /// The count of "relevant" elements. Usually be smaller than 'Array.Length' when is pooled. + /// Initializes a new instance of the class. + /// + /// The count of "relevant" elements in 'array'. + /// The array to pin. + public PinnedBuffer(int count, T[] array) + { + if (array.Length < count) + { + throw new ArgumentException("Can't initialize a PinnedBuffer with array.Length < count", nameof(array)); + } + this.Count = count; + this.Array = array; + this.Pin(); + } + + ~PinnedBuffer() + { + this.UnPin(); + } + + /// + /// Gets a value indicating whether this instance is disposed, or has lost ownership of . + /// + public bool IsDisposedOrLostArrayOwnership { get; private set; } + + + /// + /// Gets the count of "relevant" elements. Usually be smaller than 'Array.Length' when is pooled. /// public int Count { get; private set; } @@ -58,7 +83,7 @@ namespace ImageSharp public T[] Array { get; private set; } /// - /// Pointer to the pinned . + /// Gets a pointer to the pinned . /// public IntPtr Pointer { get; private set; } @@ -67,16 +92,16 @@ namespace ImageSharp /// public void Dispose() { - if (this.isDisposed) + if (this.IsDisposedOrLostArrayOwnership) { return; } - this.isDisposed = true; + this.IsDisposedOrLostArrayOwnership = true; this.UnPin(); - if (this.isBufferRented) + if (this.isPoolingOwner) { - ArrayPool.Return(this.Array, true); + PixelDataPool.Return(this.Array); } this.Array = null; @@ -85,12 +110,37 @@ namespace ImageSharp GC.SuppressFinalize(this); } + /// + /// Unpins and makes the object "quasi-disposed" so the array is no longer owned by this object. + /// If is rented, it's the callers responsibility to return it to it's pool. (Most likely ) + /// + /// The unpinned + public T[] UnPinAndTakeArrayOwnership() + { + if (this.IsDisposedOrLostArrayOwnership) + { + throw new InvalidOperationException("UnPinAndTakeArrayOwnership() is invalid: either PinnedBuffer is disposed or UnPinAndTakeArrayOwnership() has been called multiple times!"); + } + + this.IsDisposedOrLostArrayOwnership = true; + this.UnPin(); + T[] array = this.Array; + this.Array = null; + return array; + } + + /// + /// Pins . + /// private void Pin() { this.handle = GCHandle.Alloc(this.Array, GCHandleType.Pinned); this.Pointer = this.handle.AddrOfPinnedObject(); } + /// + /// Unpins . + /// private void UnPin() { if (this.Pointer == IntPtr.Zero || !this.handle.IsAllocated) @@ -100,10 +150,5 @@ namespace ImageSharp this.handle.Free(); this.Pointer = IntPtr.Zero; } - - ~PinnedBuffer() - { - this.UnPin(); - } } } \ No newline at end of file diff --git a/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs b/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs new file mode 100644 index 000000000..f6f6a1042 --- /dev/null +++ b/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs @@ -0,0 +1,60 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Buffers; + + /// + /// Provides a resource pool that enables reusing instances of value type arrays . + /// will always return arrays initialized with 'default(T)' + /// + /// The value type. + public static class PixelDataPool + where T : struct + { + /// + /// The used to pool data. + /// + private static readonly ArrayPool ArrayPool = ArrayPool.Create(CalculateMaxArrayLength(), 50); + + /// + /// Rents the pixel array from the pool. + /// + /// The minimum length of the array to return. + /// The + public static T[] Rent(int minimumLength) + { + return ArrayPool.Rent(minimumLength); + } + + /// + /// Returns the rented pixel array back to the pool. + /// + /// The array to return to the buffer pool. + public static void Return(T[] array) + { + ArrayPool.Return(array, true); + } + + /// + /// Heuristically calculates a reasonable maxArrayLength value for the backing . + /// + /// The maxArrayLength value + internal static int CalculateMaxArrayLength() + { + if (typeof(IPixel).IsAssignableFrom(typeof(T))) + { + const int MaximumExpectedImageSize = 16384; + return MaximumExpectedImageSize * MaximumExpectedImageSize; + } + else + { + return int.MaxValue; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/ImageBase{TColor}.cs b/src/ImageSharp/Image/ImageBase{TColor}.cs index e4b4485c7..9bd760805 100644 --- a/src/ImageSharp/Image/ImageBase{TColor}.cs +++ b/src/ImageSharp/Image/ImageBase{TColor}.cs @@ -162,13 +162,15 @@ namespace ImageSharp internal void SwapPixelsBuffers(PixelAccessor pixelSource) { Guard.NotNull(pixelSource, nameof(pixelSource)); - Guard.IsTrue(pixelSource.PooledMemory, nameof(pixelSource.PooledMemory), "pixelSource must be using pooled memory"); + + // TODO: This check was useful. We can introduce a bool PixelAccessor.IsBoundToImage to re-introduce it. + // Guard.IsTrue(pixelSource.PooledMemory, nameof(pixelSource.PooledMemory), "pixelSource must be using pooled memory"); int newWidth = pixelSource.Width; int newHeight = pixelSource.Height; // Push my memory into the accessor (which in turn unpins the old puffer ready for the images use) - TColor[] newPixels = pixelSource.ReturnCurrentPixelsAndReplaceThemInternally(this.Width, this.Height, this.pixelBuffer, true); + TColor[] newPixels = pixelSource.ReturnCurrentPixelsAndReplaceThemInternally(this.Width, this.Height, this.pixelBuffer); this.Width = newWidth; this.Height = newHeight; this.pixelBuffer = newPixels; @@ -222,7 +224,7 @@ namespace ImageSharp /// private void RentPixels() { - this.pixelBuffer = PixelPool.RentPixels(this.Width * this.Height); + this.pixelBuffer = PixelDataPool.Rent(this.Width * this.Height); } /// @@ -230,7 +232,7 @@ namespace ImageSharp /// private void ReturnPixels() { - PixelPool.ReturnPixels(this.pixelBuffer); + PixelDataPool.Return(this.pixelBuffer); this.pixelBuffer = null; } } diff --git a/src/ImageSharp/Image/PixelAccessor{TColor}.cs b/src/ImageSharp/Image/PixelAccessor{TColor}.cs index b31ada10b..3a3f0f5c7 100644 --- a/src/ImageSharp/Image/PixelAccessor{TColor}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TColor}.cs @@ -18,21 +18,11 @@ namespace ImageSharp public unsafe class PixelAccessor : IDisposable where TColor : struct, IPixel { - /// - /// The pointer to the pixel buffer. - /// - private IntPtr dataPointer; - /// /// The position of the first pixel in the image. /// private byte* pixelsBase; - - /// - /// Provides a way to access the pixels from unmanaged memory. - /// - private GCHandle pixelsHandle; - + /// /// A value indicating whether this instance of the given entity has been disposed. /// @@ -45,9 +35,9 @@ namespace ImageSharp private bool isDisposed; /// - /// The pixel buffer + /// The containing the pixel data. /// - private TColor[] pixelBuffer; + private PinnedBuffer pixelBuffer; /// /// Initializes a new instance of the class. @@ -59,7 +49,7 @@ namespace ImageSharp Guard.MustBeGreaterThan(image.Width, 0, "image width"); Guard.MustBeGreaterThan(image.Height, 0, "image height"); - this.SetPixelBufferUnsafe(image.Width, image.Height, image.Pixels, false); + this.SetPixelBufferUnsafe(image.Width, image.Height, image.Pixels); this.ParallelOptions = image.Configuration.ParallelOptions; } @@ -70,7 +60,7 @@ namespace ImageSharp /// The height of the image represented by the pixel buffer. /// The pixel buffer. public PixelAccessor(int width, int height, TColor[] pixels) - : this(width, height, pixels, false) + : this(width, height, new PinnedBuffer(width * height, pixels)) { } @@ -80,7 +70,7 @@ namespace ImageSharp /// The width of the image represented by the pixel buffer. /// The height of the image represented by the pixel buffer. public PixelAccessor(int width, int height) - : this(width, height, PixelPool.RentPixels(width * height), true) + : this(width, height, new PinnedBuffer(width * height)) { } @@ -90,19 +80,18 @@ namespace ImageSharp /// The width of the image represented by the pixel buffer. /// The height of the image represented by the pixel buffer. /// The pixel buffer. - /// if set to true then the is from the thus should be returned once disposed. - private PixelAccessor(int width, int height, TColor[] pixels, bool pooledMemory) + private PixelAccessor(int width, int height, PinnedBuffer pixels) { Guard.NotNull(pixels, nameof(pixels)); Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(height, 0, nameof(height)); - if (!(pixels.Length >= width * height)) - { - throw new ArgumentException($"Pixel array must have the length of at least {width * height}."); - } + //if (!(pixels.Length >= width * height)) + //{ + // throw new ArgumentException($"Pixel array must have the length of at least {width * height}."); + //} - this.SetPixelBufferUnsafe(width, height, pixels, pooledMemory); + this.SetPixelBufferUnsafe(width, height, pixels); this.ParallelOptions = Configuration.Default.ParallelOptions; } @@ -114,21 +103,16 @@ namespace ImageSharp { this.Dispose(); } - - /// - /// Gets a value indicating whether the current pixel buffer is from a pooled source. - /// - public bool PooledMemory { get; private set; } - + /// /// Gets the pixel buffer array. /// - public TColor[] PixelBuffer => this.pixelBuffer; + public TColor[] PixelBuffer => this.pixelBuffer.Array; /// /// Gets the pointer to the pixel buffer. /// - public IntPtr DataPointer => this.dataPointer; + public IntPtr DataPointer => this.pixelBuffer.Pointer; /// /// Gets the size of a single pixel in the number of bytes. @@ -246,24 +230,18 @@ namespace ImageSharp { return; } - - this.UnPinPixels(); - + // Note disposing is done. this.isDisposed = true; + this.pixelBuffer.Dispose(); + // This object will be cleaned up by the Dispose method. // Therefore, you should call GC.SuppressFinalize to // take this object off the finalization queue // and prevent finalization code for this object // from executing a second time. GC.SuppressFinalize(this); - - if (this.PooledMemory) - { - PixelPool.ReturnPixels(this.pixelBuffer); - this.pixelBuffer = null; - } } /// @@ -280,13 +258,12 @@ namespace ImageSharp /// The width. /// The height. /// The pixels. - /// If set to true this indicates that the pixel buffer is from a pooled source. /// Returns the old pixel data thats has gust been replaced. - /// If is true then caller is responsible for ensuring is called. - internal TColor[] ReturnCurrentPixelsAndReplaceThemInternally(int width, int height, TColor[] pixels, bool pooledMemory) + /// If is true then caller is responsible for ensuring is called. + internal TColor[] ReturnCurrentPixelsAndReplaceThemInternally(int width, int height, TColor[] pixels) { - TColor[] oldPixels = this.pixelBuffer; - this.SetPixelBufferUnsafe(width, height, pixels, pooledMemory); + TColor[] oldPixels = this.pixelBuffer.UnPinAndTakeArrayOwnership(); + this.SetPixelBufferUnsafe(width, height, pixels); return oldPixels; } @@ -514,53 +491,57 @@ namespace ImageSharp return this.pixelsBase + (((y * this.Width) + x) * Unsafe.SizeOf()); } + private void SetPixelBufferUnsafe(int width, int height, TColor[] pixels) + { + this.SetPixelBufferUnsafe(width, height, new PinnedBuffer(width * height, pixels)); + } + /// /// Sets the pixel buffer in an unsafe manor this should not be used unless you know what its doing!!! /// /// The width. /// The height. - /// The pixels. - /// If set to true this indicates that the pixel buffer is from a pooled source. - private void SetPixelBufferUnsafe(int width, int height, TColor[] pixels, bool pooledMemory) + /// The pixel buffer + private void SetPixelBufferUnsafe(int width, int height, PinnedBuffer pixels) { this.pixelBuffer = pixels; - this.PooledMemory = pooledMemory; + this.pixelsBase = (byte*)pixels.Pointer; + this.Width = width; this.Height = height; - this.PinPixels(); this.PixelSize = Unsafe.SizeOf(); this.RowStride = this.Width * this.PixelSize; } - /// - /// Pins the pixels data. - /// - private void PinPixels() - { - // unpin any old pixels just incase - this.UnPinPixels(); - - this.pixelsHandle = GCHandle.Alloc(this.pixelBuffer, GCHandleType.Pinned); - this.dataPointer = this.pixelsHandle.AddrOfPinnedObject(); - this.pixelsBase = (byte*)this.dataPointer.ToPointer(); - } - - /// - /// Unpins pixels data. - /// - private void UnPinPixels() - { - if (this.pixelsBase != null) - { - if (this.pixelsHandle.IsAllocated) - { - this.pixelsHandle.Free(); - } - - this.dataPointer = IntPtr.Zero; - this.pixelsBase = null; - } - } + ///// + ///// Pins the pixels data. + ///// + //private void PinPixels() + //{ + // // unpin any old pixels just incase + // this.UnPinPixels(); + + // this.pixelsHandle = GCHandle.Alloc(this.pixelBuffer, GCHandleType.Pinned); + // this.dataPointer = this.pixelsHandle.AddrOfPinnedObject(); + // this.pixelsBase = (byte*)this.dataPointer.ToPointer(); + //} + + ///// + ///// Unpins pixels data. + ///// + //private void UnPinPixels() + //{ + // if (this.pixelsBase != null) + // { + // if (this.pixelsHandle.IsAllocated) + // { + // this.pixelsHandle.Free(); + // } + + // this.dataPointer = IntPtr.Zero; + // this.pixelsBase = null; + // } + //} /// /// Copy an area of pixels to the image. diff --git a/src/ImageSharp/Image/PixelArea{TColor}.cs b/src/ImageSharp/Image/PixelArea{TColor}.cs index 77b648ca5..25840167e 100644 --- a/src/ImageSharp/Image/PixelArea{TColor}.cs +++ b/src/ImageSharp/Image/PixelArea{TColor}.cs @@ -18,21 +18,6 @@ namespace ImageSharp public sealed unsafe class PixelArea : IDisposable where TColor : struct, IPixel { - /// - /// True if was rented from by the constructor - /// - private readonly bool isBufferRented; - - /// - /// Provides a way to access the pixels from unmanaged memory. - /// - private readonly GCHandle pixelsHandle; - - /// - /// The pointer to the pixel buffer. - /// - private IntPtr dataPointer; - /// /// A value indicating whether this instance of the given entity has been disposed. /// @@ -44,6 +29,11 @@ namespace ImageSharp /// private bool isDisposed; + /// + /// The underlying buffer containing the raw pixel data. + /// + private PinnedBuffer byteBuffer; + /// /// Initializes a new instance of the class. /// @@ -76,14 +66,11 @@ namespace ImageSharp this.Height = height; this.ComponentOrder = componentOrder; this.RowStride = width * GetComponentCount(componentOrder); - this.Bytes = bytes; - this.Length = bytes.Length; - this.isBufferRented = false; - this.pixelsHandle = GCHandle.Alloc(this.Bytes, GCHandleType.Pinned); + this.Length = bytes.Length; // TODO: Is this the right value for Length? - // TODO: Why is Resharper warning us about an impure method call? - this.dataPointer = this.pixelsHandle.AddrOfPinnedObject(); - this.PixelBase = (byte*)this.dataPointer.ToPointer(); + this.byteBuffer = new PinnedBuffer(bytes); + + this.PixelBase = (byte*)this.byteBuffer.Pointer; } /// @@ -132,27 +119,15 @@ namespace ImageSharp this.ComponentOrder = componentOrder; this.RowStride = (width * GetComponentCount(componentOrder)) + padding; this.Length = this.RowStride * height; - this.Bytes = BytesPool.Rent(this.Length); - this.isBufferRented = true; - this.pixelsHandle = GCHandle.Alloc(this.Bytes, GCHandleType.Pinned); - // TODO: Why is Resharper warning us about an impure method call? - this.dataPointer = this.pixelsHandle.AddrOfPinnedObject(); - this.PixelBase = (byte*)this.dataPointer.ToPointer(); + this.byteBuffer = new PinnedBuffer(this.Length); + this.PixelBase = (byte*)this.byteBuffer.Pointer; } - - /// - /// Finalizes an instance of the class. - /// - ~PixelArea() - { - this.Dispose(false); - } - + /// /// Gets the data in bytes. /// - public byte[] Bytes { get; } + public byte[] Bytes => this.byteBuffer.Array; /// /// Gets the length of the buffer. @@ -167,7 +142,7 @@ namespace ImageSharp /// /// Gets the pointer to the pixel buffer. /// - public IntPtr DataPointer => this.dataPointer; + public IntPtr DataPointer => this.byteBuffer.Pointer; /// /// Gets the height. @@ -188,26 +163,19 @@ namespace ImageSharp /// Gets the width. /// public int Width { get; } - - /// - /// Gets the pool used to rent bytes, when it's not coming from an external source. - /// - // TODO: Use own pool? - private static ArrayPool BytesPool => ArrayPool.Shared; - + /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - this.Dispose(true); + if (this.isDisposed) + { + return; + } - // This object will be cleaned up by the Dispose method. - // Therefore, you should call GC.SuppressFinalize to - // take this object off the finalization queue - // and prevent finalization code for this object - // from executing a second time. - GC.SuppressFinalize(this); + this.byteBuffer.Dispose(); + this.isDisposed = true; } /// @@ -281,38 +249,6 @@ namespace ImageSharp nameof(bytes), $"Invalid byte array length. Length {bytes.Length}; Should be {requiredLength}."); } - } - - /// - /// Disposes the object and frees resources for the Garbage Collector. - /// - /// If true, the object gets disposed. - private void Dispose(bool disposing) - { - if (this.isDisposed) - { - return; - } - - if (this.PixelBase == null) - { - return; - } - - if (this.pixelsHandle.IsAllocated) - { - this.pixelsHandle.Free(); - } - - if (disposing && this.isBufferRented) - { - BytesPool.Return(this.Bytes); - } - - this.dataPointer = IntPtr.Zero; - this.PixelBase = null; - - this.isDisposed = true; - } + } } } \ No newline at end of file diff --git a/src/ImageSharp/Image/PixelPool{TColor}.cs b/src/ImageSharp/Image/PixelPool{TColor}.cs deleted file mode 100644 index ea6dad6b1..000000000 --- a/src/ImageSharp/Image/PixelPool{TColor}.cs +++ /dev/null @@ -1,43 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - using System.Buffers; - - // TODO: Consider refactoring this into a more general ClearPool, so we can use it in PinnedBuffer! - /// - /// Provides a resource pool that enables reusing instances of type . - /// - /// The pixel format. - public static class PixelPool - where TColor : struct, IPixel - { - /// - /// The used to pool data. TODO: Choose sensible default size and count - /// - private static readonly ArrayPool ArrayPool = ArrayPool.Create(int.MaxValue, 50); - - /// - /// Rents the pixel array from the pool. - /// - /// The minimum length of the array to return. - /// The - public static TColor[] RentPixels(int minimumLength) - { - return ArrayPool.Rent(minimumLength); - } - - /// - /// Returns the rented pixel array back to the pool. - /// - /// The array to return to the buffer pool. - public static void ReturnPixels(TColor[] array) - { - ArrayPool.Return(array, true); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj index ad436793d..094eedb18 100644 --- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj +++ b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj @@ -212,6 +212,9 @@ Tests\Colors\BulkPixelOperationsTests.cs + + Tests\Common\PinnedBufferTests.cs + Tests\Drawing\PolygonTests.cs @@ -248,6 +251,9 @@ Tests\Formats\Jpg\YCbCrImageTests.cs + + Tests\Image\PixelPoolTests.cs + Tests\MetaData\ImagePropertyTests.cs diff --git a/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs b/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs index e0783f716..c5eb2a510 100644 --- a/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs +++ b/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs @@ -22,6 +22,7 @@ { using (PinnedBuffer buffer = new PinnedBuffer(count)) { + Assert.False(buffer.IsDisposedOrLostArrayOwnership); Assert.NotNull(buffer.Array); Assert.Equal(count, buffer.Count); Assert.True(buffer.Array.Length >= count); @@ -38,6 +39,7 @@ Foo[] array = new Foo[count]; using (PinnedBuffer buffer = new PinnedBuffer(array)) { + Assert.False(buffer.IsDisposedOrLostArrayOwnership); Assert.Equal(array, buffer.Array); Assert.Equal(count, buffer.Count); @@ -45,6 +47,15 @@ } } + [Fact] + public void Dispose() + { + PinnedBuffer buffer = new PinnedBuffer(42); + buffer.Dispose(); + + Assert.True(buffer.IsDisposedOrLostArrayOwnership); + } + [Fact] public void GetArrayPointer() { @@ -60,6 +71,20 @@ } } + [Fact] + public void UnPinAndTakeArrayOwnership() + { + Foo[] data = null; + using (PinnedBuffer buffer = new PinnedBuffer(42)) + { + data = buffer.UnPinAndTakeArrayOwnership(); + Assert.True(buffer.IsDisposedOrLostArrayOwnership); + } + + Assert.NotNull(data); + Assert.True(data.Length >= 42); + } + private static void VerifyPointer(PinnedBuffer buffer) { IntPtr ptr = (IntPtr)Unsafe.AsPointer(ref buffer.Array[0]); diff --git a/tests/ImageSharp.Tests/Image/PixelPoolTests.cs b/tests/ImageSharp.Tests/Image/PixelPoolTests.cs index 0b762cf7c..001785d60 100644 --- a/tests/ImageSharp.Tests/Image/PixelPoolTests.cs +++ b/tests/ImageSharp.Tests/Image/PixelPoolTests.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -10,54 +10,54 @@ namespace ImageSharp.Tests using Xunit; /// - /// Tests the class. + /// Tests the class. /// - public class PixelPoolTests + public class PixelDataPoolTests { [Fact] - public void PixelPoolRentsMinimumSize() + public void PixelDataPoolRentsMinimumSize() { - Color[] pixels = PixelPool.RentPixels(1024); + Color[] pixels = PixelDataPool.Rent(1024); Assert.True(pixels.Length >= 1024); } [Fact] - public void PixelPoolRentsEmptyArray() + public void PixelDataPoolRentsEmptyArray() { for (int i = 16; i < 1024; i += 16) { - Color[] pixels = PixelPool.RentPixels(i); + Color[] pixels = PixelDataPool.Rent(i); Assert.True(pixels.All(p => p == default(Color))); - PixelPool.ReturnPixels(pixels); + PixelDataPool.Return(pixels); } for (int i = 16; i < 1024; i += 16) { - Color[] pixels = PixelPool.RentPixels(i); + Color[] pixels = PixelDataPool.Rent(i); Assert.True(pixels.All(p => p == default(Color))); - PixelPool.ReturnPixels(pixels); + PixelDataPool.Return(pixels); } } [Fact] - public void PixelPoolDoesNotThrowWhenReturningNonPooled() + public void PixelDataPoolDoesNotThrowWhenReturningNonPooled() { Color[] pixels = new Color[1024]; - PixelPool.ReturnPixels(pixels); + PixelDataPool.Return(pixels); Assert.True(pixels.Length >= 1024); } [Fact] - public void PixelPoolCleansRentedArray() + public void PixelDataPoolCleansRentedArray() { - Color[] pixels = PixelPool.RentPixels(256); + Color[] pixels = PixelDataPool.Rent(256); for (int i = 0; i < pixels.Length; i++) { @@ -66,9 +66,28 @@ namespace ImageSharp.Tests Assert.True(pixels.All(p => p == Color.Azure)); - PixelPool.ReturnPixels(pixels); + PixelDataPool.Return(pixels); Assert.True(pixels.All(p => p == default(Color))); } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void CalculateMaxArrayLength(bool isRawData) + { + int max = isRawData ? PixelDataPool.CalculateMaxArrayLength() + : PixelDataPool.CalculateMaxArrayLength(); + + Assert.Equal(max < int.MaxValue, !isRawData); + } + + [Fact] + public void RentNonIPixelData() + { + byte[] data = PixelDataPool.Rent(16384); + + Assert.True(data.Length >= 16384); + } } } \ No newline at end of file From e59255d43fb8f144691426a891dbe689643ea1fe Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 Mar 2017 18:18:09 +0100 Subject: [PATCH 135/142] cleanup & stylecop --- .../BulkPixelOperations{TColor}.cs | 56 +++++++++---------- .../Colors/PackedPixel/HalfVector2.cs | 2 +- .../Colors/PackedPixel/NormalizedByte4.cs | 2 +- src/ImageSharp/Common/Memory/ArrayPointer.cs | 34 +++++++++++ .../Common/Memory/ArrayPointer{T}.cs | 4 +- .../{PinnedBuffer.cs => PinnedBuffer{T}.cs} | 16 +++++- .../Common/Memory/PixelDataPool{T}.cs | 2 +- src/ImageSharp/Image/ImageBase{TColor}.cs | 4 +- src/ImageSharp/Image/PixelAccessor{TColor}.cs | 41 +------------- src/ImageSharp/Image/PixelArea{TColor}.cs | 7 +-- .../Colors/BulkPixelOperationsTests.cs | 5 +- 11 files changed, 92 insertions(+), 81 deletions(-) rename src/ImageSharp/Common/Memory/{PinnedBuffer.cs => PinnedBuffer{T}.cs} (93%) diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs index 557d59a16..31f872e42 100644 --- a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs +++ b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs @@ -78,9 +78,9 @@ namespace ImageSharp /// /// Bulk version of that converts data in . /// - /// - /// - /// + /// The to the source bytes. + /// The to the destination colors. + /// The number of pixels to convert. internal virtual void PackFromXyzBytes( ArrayPointer sourceBytes, ArrayPointer destColors, @@ -102,15 +102,15 @@ namespace ImageSharp /// /// Bulk version of . /// - /// - /// - /// + /// The to the source colors. + /// The to the destination bytes. + /// The number of pixels to convert. internal virtual void PackToXyzBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) { byte* sp = (byte*)sourceColors; byte[] dest = destBytes.Array; - for (int i = destBytes.Offset; i < destBytes.Offset + count * 3; i += 3) + for (int i = destBytes.Offset; i < destBytes.Offset + (count * 3); i += 3) { TColor c = Unsafe.Read(sp); c.ToXyzBytes(dest, i); @@ -121,9 +121,9 @@ namespace ImageSharp /// /// Bulk version of that converts data in . /// - /// - /// - /// + /// The to the source bytes. + /// The to the destination colors. + /// The number of pixels to convert. internal virtual void PackFromXyzwBytes( ArrayPointer sourceBytes, ArrayPointer destColors, @@ -145,9 +145,9 @@ namespace ImageSharp /// /// Bulk version of . /// - /// - /// - /// + /// The to the source colors. + /// The to the destination bytes. + /// The number of pixels to convert. internal virtual void PackToXyzwBytes( ArrayPointer sourceColors, ArrayPointer destBytes, @@ -156,7 +156,7 @@ namespace ImageSharp byte* sp = (byte*)sourceColors; byte[] dest = destBytes.Array; - for (int i = destBytes.Offset; i < destBytes.Offset + count * 4; i += 4) + for (int i = destBytes.Offset; i < destBytes.Offset + (count * 4); i += 4) { TColor c = Unsafe.Read(sp); c.ToXyzwBytes(dest, i); @@ -167,9 +167,9 @@ namespace ImageSharp /// /// Bulk version of that converts data in . /// - /// - /// - /// + /// The to the source bytes. + /// The to the destination colors. + /// The number of pixels to convert. internal virtual void PackFromZyxBytes( ArrayPointer sourceBytes, ArrayPointer destColors, @@ -191,15 +191,15 @@ namespace ImageSharp /// /// Bulk version of . /// - /// - /// - /// + /// The to the source colors. + /// The to the destination bytes. + /// The number of pixels to convert. internal virtual void PackToZyxBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) { byte* sp = (byte*)sourceColors; byte[] dest = destBytes.Array; - for (int i = destBytes.Offset; i < destBytes.Offset + count * 3; i += 3) + for (int i = destBytes.Offset; i < destBytes.Offset + (count * 3); i += 3) { TColor c = Unsafe.Read(sp); c.ToZyxBytes(dest, i); @@ -210,9 +210,9 @@ namespace ImageSharp /// /// Bulk version of that converts data in . /// - /// - /// - /// + /// The to the source bytes. + /// The to the destination colors. + /// The number of pixels to convert. internal virtual void PackFromZyxwBytes( ArrayPointer sourceBytes, ArrayPointer destColors, @@ -234,9 +234,9 @@ namespace ImageSharp /// /// Bulk version of . /// - /// - /// - /// + /// The to the source colors. + /// The to the destination bytes. + /// The number of pixels to convert. internal virtual void PackToZyxwBytes( ArrayPointer sourceColors, ArrayPointer destBytes, @@ -245,7 +245,7 @@ namespace ImageSharp byte* sp = (byte*)sourceColors; byte[] dest = destBytes.Array; - for (int i = destBytes.Offset; i < destBytes.Offset + count * 4; i += 4) + for (int i = destBytes.Offset; i < destBytes.Offset + (count * 4); i += 4) { TColor c = Unsafe.Read(sp); c.ToZyxwBytes(dest, i); diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs index 778f86e0f..68cb42735 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs @@ -45,7 +45,7 @@ namespace ImageSharp /// public uint PackedValue { get; set; } - + /// public BulkPixelOperations BulkOperations => new BulkPixelOperations(); diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs index cba3f0e86..4511060e4 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs @@ -52,7 +52,7 @@ namespace ImageSharp /// public uint PackedValue { get; set; } - + /// public BulkPixelOperations BulkOperations => new BulkPixelOperations(); diff --git a/src/ImageSharp/Common/Memory/ArrayPointer.cs b/src/ImageSharp/Common/Memory/ArrayPointer.cs index 56cae35a4..342eaa6c8 100644 --- a/src/ImageSharp/Common/Memory/ArrayPointer.cs +++ b/src/ImageSharp/Common/Memory/ArrayPointer.cs @@ -1,3 +1,8 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + namespace ImageSharp { using System.Runtime.CompilerServices; @@ -7,6 +12,12 @@ namespace ImageSharp /// internal static class ArrayPointer { + /// + /// Gets an to the beginning of the raw data in 'buffer'. + /// + /// The element type + /// The input + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe ArrayPointer GetArrayPointer(this PinnedBuffer buffer) where T : struct @@ -14,6 +25,13 @@ namespace ImageSharp return new ArrayPointer(buffer.Array, (void*)buffer.Pointer); } + /// + /// Copy 'count' number of elements of the same type from 'source' to 'dest' + /// + /// The element type. + /// The input + /// The destination . + /// The number of elements to copy [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void Copy(ArrayPointer source, ArrayPointer destination, int count) where T : struct @@ -21,6 +39,13 @@ namespace ImageSharp Unsafe.CopyBlock((void*)source.PointerAtOffset, (void*)destination.PointerAtOffset, USizeOf(count)); } + /// + /// Copy 'countInSource' elements of from 'source' into the raw byte buffer 'destination'. + /// + /// The element type. + /// The source buffer of elements to copy from. + /// The destination buffer. + /// The number of elements to copy from 'source' [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void Copy(ArrayPointer source, ArrayPointer destination, int countInSource) where T : struct @@ -28,6 +53,13 @@ namespace ImageSharp Unsafe.CopyBlock((void*)source.PointerAtOffset, (void*)destination.PointerAtOffset, USizeOf(countInSource)); } + /// + /// Copy 'countInDest' number of elements into 'dest' from a raw byte buffer defined by 'source'. + /// + /// The element type. + /// The raw source buffer to copy from"/> + /// The destination buffer"/> + /// The number of elements to copy. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void Copy(ArrayPointer source, ArrayPointer destination, int countInDest) where T : struct @@ -38,6 +70,7 @@ namespace ImageSharp /// /// Gets the size of `count` elements in bytes. /// + /// The element type. /// The count of the elements /// The size in bytes as int [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -47,6 +80,7 @@ namespace ImageSharp /// /// Gets the size of `count` elements in bytes as UInt32 /// + /// The element type. /// The count of the elements /// The size in bytes as UInt32 [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs b/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs index e0b728095..9cbbaf094 100644 --- a/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs +++ b/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs @@ -73,7 +73,7 @@ namespace ImageSharp /// /// The to convert [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator void* (ArrayPointer arrayPointer) + public static explicit operator void*(ArrayPointer arrayPointer) { return (void*)arrayPointer.PointerAtOffset; } @@ -83,7 +83,7 @@ namespace ImageSharp /// /// The to convert [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator byte* (ArrayPointer arrayPointer) + public static explicit operator byte*(ArrayPointer arrayPointer) { return (byte*)arrayPointer.PointerAtOffset; } diff --git a/src/ImageSharp/Common/Memory/PinnedBuffer.cs b/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs similarity index 93% rename from src/ImageSharp/Common/Memory/PinnedBuffer.cs rename to src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs index 201d93b56..d95d67557 100644 --- a/src/ImageSharp/Common/Memory/PinnedBuffer.cs +++ b/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs @@ -1,3 +1,8 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + namespace ImageSharp { using System; @@ -21,7 +26,7 @@ namespace ImageSharp /// A value indicating wether this instance should return the array to the pool. /// private bool isPoolingOwner; - + /// /// Initializes a new instance of the class. /// @@ -56,11 +61,15 @@ namespace ImageSharp { throw new ArgumentException("Can't initialize a PinnedBuffer with array.Length < count", nameof(array)); } + this.Count = count; this.Array = array; this.Pin(); } + /// + /// Finalizes an instance of the class. + /// ~PinnedBuffer() { this.UnPin(); @@ -71,14 +80,13 @@ namespace ImageSharp /// public bool IsDisposedOrLostArrayOwnership { get; private set; } - /// /// Gets the count of "relevant" elements. Usually be smaller than 'Array.Length' when is pooled. /// public int Count { get; private set; } /// - /// The (pinned) array of elements. + /// Gets the backing pinned array. /// public T[] Array { get; private set; } @@ -96,6 +104,7 @@ namespace ImageSharp { return; } + this.IsDisposedOrLostArrayOwnership = true; this.UnPin(); @@ -147,6 +156,7 @@ namespace ImageSharp { return; } + this.handle.Free(); this.Pointer = IntPtr.Zero; } diff --git a/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs b/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs index f6f6a1042..74f0c1e8e 100644 --- a/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs +++ b/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // diff --git a/src/ImageSharp/Image/ImageBase{TColor}.cs b/src/ImageSharp/Image/ImageBase{TColor}.cs index 9bd760805..c2110eca4 100644 --- a/src/ImageSharp/Image/ImageBase{TColor}.cs +++ b/src/ImageSharp/Image/ImageBase{TColor}.cs @@ -163,9 +163,9 @@ namespace ImageSharp { Guard.NotNull(pixelSource, nameof(pixelSource)); - // TODO: This check was useful. We can introduce a bool PixelAccessor.IsBoundToImage to re-introduce it. - // Guard.IsTrue(pixelSource.PooledMemory, nameof(pixelSource.PooledMemory), "pixelSource must be using pooled memory"); + // TODO: Was this check really useful? If yes, we can define a bool PixelAccessor.IsBoundToImage to re-introduce it: + // Guard.IsTrue(pixelSource.PooledMemory, nameof(pixelSource.PooledMemory), "pixelSource must be using pooled memory"); int newWidth = pixelSource.Width; int newHeight = pixelSource.Height; diff --git a/src/ImageSharp/Image/PixelAccessor{TColor}.cs b/src/ImageSharp/Image/PixelAccessor{TColor}.cs index 3a3f0f5c7..9201270d9 100644 --- a/src/ImageSharp/Image/PixelAccessor{TColor}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TColor}.cs @@ -22,7 +22,7 @@ namespace ImageSharp /// The position of the first pixel in the image. /// private byte* pixelsBase; - + /// /// A value indicating whether this instance of the given entity has been disposed. /// @@ -86,11 +86,6 @@ namespace ImageSharp Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(height, 0, nameof(height)); - //if (!(pixels.Length >= width * height)) - //{ - // throw new ArgumentException($"Pixel array must have the length of at least {width * height}."); - //} - this.SetPixelBufferUnsafe(width, height, pixels); this.ParallelOptions = Configuration.Default.ParallelOptions; @@ -103,7 +98,7 @@ namespace ImageSharp { this.Dispose(); } - + /// /// Gets the pixel buffer array. /// @@ -230,7 +225,7 @@ namespace ImageSharp { return; } - + // Note disposing is done. this.isDisposed = true; @@ -513,36 +508,6 @@ namespace ImageSharp this.RowStride = this.Width * this.PixelSize; } - ///// - ///// Pins the pixels data. - ///// - //private void PinPixels() - //{ - // // unpin any old pixels just incase - // this.UnPinPixels(); - - // this.pixelsHandle = GCHandle.Alloc(this.pixelBuffer, GCHandleType.Pinned); - // this.dataPointer = this.pixelsHandle.AddrOfPinnedObject(); - // this.pixelsBase = (byte*)this.dataPointer.ToPointer(); - //} - - ///// - ///// Unpins pixels data. - ///// - //private void UnPinPixels() - //{ - // if (this.pixelsBase != null) - // { - // if (this.pixelsHandle.IsAllocated) - // { - // this.pixelsHandle.Free(); - // } - - // this.dataPointer = IntPtr.Zero; - // this.pixelsBase = null; - // } - //} - /// /// Copy an area of pixels to the image. /// diff --git a/src/ImageSharp/Image/PixelArea{TColor}.cs b/src/ImageSharp/Image/PixelArea{TColor}.cs index 25840167e..c54de12d6 100644 --- a/src/ImageSharp/Image/PixelArea{TColor}.cs +++ b/src/ImageSharp/Image/PixelArea{TColor}.cs @@ -69,7 +69,6 @@ namespace ImageSharp this.Length = bytes.Length; // TODO: Is this the right value for Length? this.byteBuffer = new PinnedBuffer(bytes); - this.PixelBase = (byte*)this.byteBuffer.Pointer; } @@ -123,7 +122,7 @@ namespace ImageSharp this.byteBuffer = new PinnedBuffer(this.Length); this.PixelBase = (byte*)this.byteBuffer.Pointer; } - + /// /// Gets the data in bytes. /// @@ -163,7 +162,7 @@ namespace ImageSharp /// Gets the width. /// public int Width { get; } - + /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// @@ -249,6 +248,6 @@ namespace ImageSharp nameof(bytes), $"Invalid byte array length. Length {bytes.Length}; Should be {requiredLength}."); } - } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs index 3682aa78a..4e1083033 100644 --- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs @@ -9,11 +9,13 @@ { public class Color : BulkPixelOperationsTests { + // MemberData does not work without redeclaring the public field in the derived test class: public static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; } public class Argb : BulkPixelOperationsTests { + // MemberData does not work without redeclaring the public field in the derived test class: public static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; } } @@ -21,7 +23,8 @@ public abstract class BulkPixelOperationsTests where TColor : struct, IPixel { - protected static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; + // The field is private so it can be safely redeclared in inherited non-abstract test classes. + private static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; [Theory] [MemberData(nameof(ArraySizesData))] From 74f853a55bdc4ff2f414c0cf7361df8df0c8d3f1 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 Mar 2017 18:32:06 +0100 Subject: [PATCH 136/142] build and testrunning fix --- src/ImageSharp/Common/Memory/PixelDataPool{T}.cs | 3 ++- .../Colors/BulkPixelOperationsTests.cs | 11 +++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs b/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs index 74f0c1e8e..a97d17fdb 100644 --- a/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs +++ b/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs @@ -46,7 +46,8 @@ namespace ImageSharp /// The maxArrayLength value internal static int CalculateMaxArrayLength() { - if (typeof(IPixel).IsAssignableFrom(typeof(T))) + // ReSharper disable once SuspiciousTypeConversion.Global + if (default(T) is IPixel) { const int MaximumExpectedImageSize = 16384; return MaximumExpectedImageSize * MaximumExpectedImageSize; diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs index 4e1083033..0e69a54f5 100644 --- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs @@ -9,22 +9,21 @@ { public class Color : BulkPixelOperationsTests { - // MemberData does not work without redeclaring the public field in the derived test class: - public static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; + // For 4.6 test runner MemberData does not work without redeclaring the public field in the derived test class: + public static new TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 }; } public class Argb : BulkPixelOperationsTests { - // MemberData does not work without redeclaring the public field in the derived test class: - public static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; + // For 4.6 test runner MemberData does not work without redeclaring the public field in the derived test class: + public static new TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 }; } } public abstract class BulkPixelOperationsTests where TColor : struct, IPixel { - // The field is private so it can be safely redeclared in inherited non-abstract test classes. - private static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; + public static TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 }; [Theory] [MemberData(nameof(ArraySizesData))] From ddc6bf5b18dfe759000246eb18c19af0c44e6a51 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 Mar 2017 19:25:31 +0100 Subject: [PATCH 137/142] BulkPixelOperationsTests.GetGlobalInstance() to make CodeCov happy --- .../Colors/BulkPixelOperationsTests.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs index 0e69a54f5..c179b01a1 100644 --- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs @@ -5,7 +5,7 @@ using Xunit; - public abstract class BulkPixelOperationsTests + public class BulkPixelOperationsTests { public class Color : BulkPixelOperationsTests { @@ -18,6 +18,14 @@ // For 4.6 test runner MemberData does not work without redeclaring the public field in the derived test class: public static new TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 }; } + + [Theory] + [WithBlankImages(1, 1, PixelTypes.All)] + public void GetGlobalInstance(TestImageProvider dummy) + where TColor:struct, IPixel + { + Assert.NotNull(BulkPixelOperations.Instance); + } } public abstract class BulkPixelOperationsTests From 2b3b3ea93bea0167bcdca1b4fc4707c9d595e3dd Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 Mar 2017 21:18:31 +0100 Subject: [PATCH 138/142] IPixel.BulkOperations ---> IPixel.CreateBulkOperations() --- src/ImageSharp/Colors/Color.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/Alpha8.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/Argb.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/Bgr565.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/Bgra4444.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/Bgra5551.cs | 6 +++--- .../Colors/PackedPixel/BulkPixelOperations{TColor}.cs | 2 +- src/ImageSharp/Colors/PackedPixel/Byte4.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/HalfSingle.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/HalfVector2.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/HalfVector4.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/IPixel.cs | 6 ++++-- src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/Rg32.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/Rgba64.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/Short2.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/Short4.cs | 6 +++--- 21 files changed, 62 insertions(+), 60 deletions(-) diff --git a/src/ImageSharp/Colors/Color.cs b/src/ImageSharp/Colors/Color.cs index 8a869935c..1ad6f9a64 100644 --- a/src/ImageSharp/Colors/Color.cs +++ b/src/ImageSharp/Colors/Color.cs @@ -112,9 +112,6 @@ namespace ImageSharp this.packedValue = packed; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Gets or sets the red component. /// @@ -248,6 +245,9 @@ namespace ImageSharp return ColorBuilder.FromHex(hex); } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromBytes(byte x, byte y, byte z, byte w) diff --git a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs index 1181eb9e4..9a340544c 100644 --- a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs +++ b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs @@ -26,9 +26,6 @@ namespace ImageSharp /// public byte PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -61,6 +58,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) diff --git a/src/ImageSharp/Colors/PackedPixel/Argb.cs b/src/ImageSharp/Colors/PackedPixel/Argb.cs index 1b97d2337..70fd7de8a 100644 --- a/src/ImageSharp/Colors/PackedPixel/Argb.cs +++ b/src/ImageSharp/Colors/PackedPixel/Argb.cs @@ -109,9 +109,6 @@ namespace ImageSharp /// public uint PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Gets or sets the red component. /// @@ -223,6 +220,9 @@ namespace ImageSharp this.PackedValue = Pack(ref vector); } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() diff --git a/src/ImageSharp/Colors/PackedPixel/Bgr565.cs b/src/ImageSharp/Colors/PackedPixel/Bgr565.cs index 41b2bdc2e..77d943478 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgr565.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgr565.cs @@ -39,9 +39,6 @@ namespace ImageSharp /// public ushort PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -70,6 +67,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// /// Expands the packed representation into a . /// The vector components are typically expanded in least to greatest significance order. diff --git a/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs b/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs index 99659a36b..1346a54ef 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs @@ -38,9 +38,6 @@ namespace ImageSharp /// public ushort PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -69,6 +66,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() diff --git a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs b/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs index 86864fd48..7989804cf 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs @@ -40,9 +40,6 @@ namespace ImageSharp /// public ushort PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -71,6 +68,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs index 31f872e42..986994af7 100644 --- a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs +++ b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs @@ -24,7 +24,7 @@ namespace ImageSharp /// /// Gets the global instance for the pixel type /// - public static BulkPixelOperations Instance { get; } = default(TColor).BulkOperations; + public static BulkPixelOperations Instance { get; } = default(TColor).CreateBulkOperations(); /// /// Bulk version of diff --git a/src/ImageSharp/Colors/PackedPixel/Byte4.cs b/src/ImageSharp/Colors/PackedPixel/Byte4.cs index 5712027d9..11ec5eaf4 100644 --- a/src/ImageSharp/Colors/PackedPixel/Byte4.cs +++ b/src/ImageSharp/Colors/PackedPixel/Byte4.cs @@ -41,9 +41,6 @@ namespace ImageSharp /// public uint PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -72,6 +69,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) diff --git a/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs b/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs index 0bc82c3a6..4c785a863 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs @@ -36,9 +36,6 @@ namespace ImageSharp /// public ushort PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -75,6 +72,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// /// Expands the packed representation into a . /// diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs index 68cb42735..d06ab6ba0 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs @@ -46,9 +46,6 @@ namespace ImageSharp /// public uint PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -85,6 +82,9 @@ namespace ImageSharp return !left.Equals(right); } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// /// Expands the packed representation into a . /// diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs b/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs index c24553d3f..a5fa796e1 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs @@ -49,9 +49,6 @@ namespace ImageSharp /// public ulong PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -88,6 +85,9 @@ namespace ImageSharp return !left.Equals(right); } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) diff --git a/src/ImageSharp/Colors/PackedPixel/IPixel.cs b/src/ImageSharp/Colors/PackedPixel/IPixel.cs index c17fe86cc..67e013a42 100644 --- a/src/ImageSharp/Colors/PackedPixel/IPixel.cs +++ b/src/ImageSharp/Colors/PackedPixel/IPixel.cs @@ -16,9 +16,11 @@ namespace ImageSharp where TSelf : struct, IPixel { /// - /// Gets the instance for this pixel type. + /// Creates a instance for this pixel type. + /// This method is not intended to be consumed directly. Use instead. /// - BulkPixelOperations BulkOperations { get; } + /// The instance. + BulkPixelOperations CreateBulkOperations(); } /// diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs index d425806c2..56be64a86 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs @@ -51,9 +51,6 @@ namespace ImageSharp /// public ushort PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -90,6 +87,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// /// Expands the packed representation into a . /// The vector components are typically expanded in least to greatest significance order. diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs index 4511060e4..a1f9b8d84 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs @@ -53,9 +53,6 @@ namespace ImageSharp /// public uint PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -92,6 +89,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs index 4bc7f9427..b34c1e88b 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs @@ -51,9 +51,6 @@ namespace ImageSharp /// public uint PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -90,6 +87,9 @@ namespace ImageSharp return !left.Equals(right); } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs index c848b7236..f33ac25a6 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs @@ -53,9 +53,6 @@ namespace ImageSharp /// public ulong PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -92,6 +89,9 @@ namespace ImageSharp return !left.Equals(right); } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) diff --git a/src/ImageSharp/Colors/PackedPixel/Rg32.cs b/src/ImageSharp/Colors/PackedPixel/Rg32.cs index 9eb2247c9..f8486f7f2 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rg32.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rg32.cs @@ -36,9 +36,6 @@ namespace ImageSharp /// public uint PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -75,6 +72,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// /// Expands the packed representation into a . /// The vector components are typically expanded in least to greatest significance order. diff --git a/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs b/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs index 4f99feb6e..56f304070 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs @@ -39,9 +39,6 @@ namespace ImageSharp /// public uint PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -78,6 +75,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() diff --git a/src/ImageSharp/Colors/PackedPixel/Rgba64.cs b/src/ImageSharp/Colors/PackedPixel/Rgba64.cs index a23e57ec3..816401d4e 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rgba64.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rgba64.cs @@ -38,9 +38,6 @@ namespace ImageSharp /// public ulong PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -77,6 +74,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() diff --git a/src/ImageSharp/Colors/PackedPixel/Short2.cs b/src/ImageSharp/Colors/PackedPixel/Short2.cs index f26e82578..802df7c1d 100644 --- a/src/ImageSharp/Colors/PackedPixel/Short2.cs +++ b/src/ImageSharp/Colors/PackedPixel/Short2.cs @@ -51,9 +51,6 @@ namespace ImageSharp /// public uint PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -90,6 +87,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) diff --git a/src/ImageSharp/Colors/PackedPixel/Short4.cs b/src/ImageSharp/Colors/PackedPixel/Short4.cs index 6dc7545e1..2517ef7a8 100644 --- a/src/ImageSharp/Colors/PackedPixel/Short4.cs +++ b/src/ImageSharp/Colors/PackedPixel/Short4.cs @@ -53,9 +53,6 @@ namespace ImageSharp /// public ulong PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -92,6 +89,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) From 0032e2b4575c05ee8213168a855e273c791ea548 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 6 Mar 2017 01:15:07 +0100 Subject: [PATCH 139/142] Renamed BulkPixelOperation methods, removed ArrayExtensions and the unused dangerous PixelAccessor ctr. --- .../BulkPixelOperations{TColor}.cs | 10 +++---- .../Common/Extensions/ArrayExtensions.cs | 29 ------------------- .../Common/Memory/PinnedBuffer{T}.cs | 2 +- src/ImageSharp/Image/ImageBase{TColor}.cs | 3 -- src/ImageSharp/Image/PixelAccessor{TColor}.cs | 11 ------- .../ImageSharp.Sandbox46.csproj | 3 -- tests/ImageSharp.Sandbox46/Program.cs | 2 +- .../Colors/BulkPixelOperationsTests.cs | 10 +++---- .../PixelDataPoolTests.cs} | 0 9 files changed, 12 insertions(+), 58 deletions(-) delete mode 100644 src/ImageSharp/Common/Extensions/ArrayExtensions.cs rename tests/ImageSharp.Tests/{Image/PixelPoolTests.cs => Common/PixelDataPoolTests.cs} (100%) diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs index 986994af7..a84c3edf3 100644 --- a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs +++ b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs @@ -58,7 +58,7 @@ namespace ImageSharp /// The to the source colors. /// The to the destination vectors. /// The number of pixels to convert. - internal virtual void PackToVector4( + internal virtual void ToVector4( ArrayPointer sourceColors, ArrayPointer destVectors, int count) @@ -105,7 +105,7 @@ namespace ImageSharp /// The to the source colors. /// The to the destination bytes. /// The number of pixels to convert. - internal virtual void PackToXyzBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + internal virtual void ToXyzBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) { byte* sp = (byte*)sourceColors; byte[] dest = destBytes.Array; @@ -148,7 +148,7 @@ namespace ImageSharp /// The to the source colors. /// The to the destination bytes. /// The number of pixels to convert. - internal virtual void PackToXyzwBytes( + internal virtual void ToXyzwBytes( ArrayPointer sourceColors, ArrayPointer destBytes, int count) @@ -194,7 +194,7 @@ namespace ImageSharp /// The to the source colors. /// The to the destination bytes. /// The number of pixels to convert. - internal virtual void PackToZyxBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + internal virtual void ToZyxBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) { byte* sp = (byte*)sourceColors; byte[] dest = destBytes.Array; @@ -237,7 +237,7 @@ namespace ImageSharp /// The to the source colors. /// The to the destination bytes. /// The number of pixels to convert. - internal virtual void PackToZyxwBytes( + internal virtual void ToZyxwBytes( ArrayPointer sourceColors, ArrayPointer destBytes, int count) diff --git a/src/ImageSharp/Common/Extensions/ArrayExtensions.cs b/src/ImageSharp/Common/Extensions/ArrayExtensions.cs deleted file mode 100644 index cce442c52..000000000 --- a/src/ImageSharp/Common/Extensions/ArrayExtensions.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - - /// - /// Extension methods for arrays. - /// - public static class ArrayExtensions - { - /// - /// Locks the pixel buffer providing access to the pixels. - /// - /// The pixel format. - /// The pixel buffer. - /// Gets the width of the image represented by the pixel buffer. - /// The height of the image represented by the pixel buffer. - /// The - public static PixelAccessor Lock(this TColor[] pixels, int width, int height) - where TColor : struct, IPixel - { - return new PixelAccessor(width, height, pixels); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs b/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs index d95d67557..04217a012 100644 --- a/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs +++ b/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs @@ -23,7 +23,7 @@ namespace ImageSharp private GCHandle handle; /// - /// A value indicating wether this instance should return the array to the pool. + /// A value indicating whether this instance should return the array to the pool. /// private bool isPoolingOwner; diff --git a/src/ImageSharp/Image/ImageBase{TColor}.cs b/src/ImageSharp/Image/ImageBase{TColor}.cs index c2110eca4..2badc008a 100644 --- a/src/ImageSharp/Image/ImageBase{TColor}.cs +++ b/src/ImageSharp/Image/ImageBase{TColor}.cs @@ -163,9 +163,6 @@ namespace ImageSharp { Guard.NotNull(pixelSource, nameof(pixelSource)); - // TODO: Was this check really useful? If yes, we can define a bool PixelAccessor.IsBoundToImage to re-introduce it: - - // Guard.IsTrue(pixelSource.PooledMemory, nameof(pixelSource.PooledMemory), "pixelSource must be using pooled memory"); int newWidth = pixelSource.Width; int newHeight = pixelSource.Height; diff --git a/src/ImageSharp/Image/PixelAccessor{TColor}.cs b/src/ImageSharp/Image/PixelAccessor{TColor}.cs index 9201270d9..e104b8ae7 100644 --- a/src/ImageSharp/Image/PixelAccessor{TColor}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TColor}.cs @@ -53,17 +53,6 @@ namespace ImageSharp this.ParallelOptions = image.Configuration.ParallelOptions; } - /// - /// Initializes a new instance of the class. - /// - /// The width of the image represented by the pixel buffer. - /// The height of the image represented by the pixel buffer. - /// The pixel buffer. - public PixelAccessor(int width, int height, TColor[] pixels) - : this(width, height, new PinnedBuffer(width * height, pixels)) - { - } - /// /// Initializes a new instance of the class. /// diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj index 094eedb18..1d7899486 100644 --- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj +++ b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj @@ -251,9 +251,6 @@ Tests\Formats\Jpg\YCbCrImageTests.cs - - Tests\Image\PixelPoolTests.cs - Tests\MetaData\ImagePropertyTests.cs diff --git a/tests/ImageSharp.Sandbox46/Program.cs b/tests/ImageSharp.Sandbox46/Program.cs index 3afd18094..4d6d15925 100644 --- a/tests/ImageSharp.Sandbox46/Program.cs +++ b/tests/ImageSharp.Sandbox46/Program.cs @@ -37,7 +37,7 @@ namespace ImageSharp.Sandbox46 /// public static void Main(string[] args) { - //RunDecodeJpegProfilingTests(); + // RunDecodeJpegProfilingTests(); TestPixelAccessorCopyFromXyzw(); Console.ReadLine(); } diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs index c179b01a1..1c22411d0 100644 --- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs @@ -67,7 +67,7 @@ TestOperation( source, expected, - (ops, s, d) => ops.PackToVector4(s, d, count) + (ops, s, d) => ops.ToVector4(s, d, count) ); } @@ -109,7 +109,7 @@ TestOperation( source, expected, - (ops, s, d) => ops.PackToXyzBytes(s, d, count) + (ops, s, d) => ops.ToXyzBytes(s, d, count) ); } @@ -150,7 +150,7 @@ TestOperation( source, expected, - (ops, s, d) => ops.PackToXyzwBytes(s, d, count) + (ops, s, d) => ops.ToXyzwBytes(s, d, count) ); } @@ -191,7 +191,7 @@ TestOperation( source, expected, - (ops, s, d) => ops.PackToZyxBytes(s, d, count) + (ops, s, d) => ops.ToZyxBytes(s, d, count) ); } @@ -232,7 +232,7 @@ TestOperation( source, expected, - (ops, s, d) => ops.PackToZyxwBytes(s, d, count) + (ops, s, d) => ops.ToZyxwBytes(s, d, count) ); } diff --git a/tests/ImageSharp.Tests/Image/PixelPoolTests.cs b/tests/ImageSharp.Tests/Common/PixelDataPoolTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Image/PixelPoolTests.cs rename to tests/ImageSharp.Tests/Common/PixelDataPoolTests.cs From d04c424bfa1f44989e6f3be254e66a4527f17116 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 6 Mar 2017 21:47:45 +0100 Subject: [PATCH 140/142] renamed ArrayPointer to BufferPointer --- .../BulkPixelOperations{TColor}.cs | 76 +++++++++---------- .../{ArrayPointer.cs => BufferPointer.cs} | 24 +++--- ...ArrayPointer{T}.cs => BufferPointer{T}.cs} | 40 +++++----- .../Color/Bulk/PixelAccessorVirtualCopy.cs | 24 +++--- .../Colors/BulkPixelOperationsTests.cs | 6 +- ...yPointerTests.cs => BufferPointerTests.cs} | 26 +++---- .../Common/PinnedBufferTests.cs | 4 +- 7 files changed, 100 insertions(+), 100 deletions(-) rename src/ImageSharp/Common/Memory/{ArrayPointer.cs => BufferPointer.cs} (79%) rename src/ImageSharp/Common/Memory/{ArrayPointer{T}.cs => BufferPointer{T}.cs} (66%) rename tests/ImageSharp.Tests/Common/{ArrayPointerTests.cs => BufferPointerTests.cs} (81%) diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs index a84c3edf3..259b1c9b4 100644 --- a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs +++ b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs @@ -29,12 +29,12 @@ namespace ImageSharp /// /// Bulk version of /// - /// The to the source vectors. - /// The to the destination colors. + /// The to the source vectors. + /// The to the destination colors. /// The number of pixels to convert. internal virtual void PackFromVector4( - ArrayPointer sourceVectors, - ArrayPointer destColors, + BufferPointer sourceVectors, + BufferPointer destColors, int count) { Vector4* sp = (Vector4*)sourceVectors.PointerAtOffset; @@ -55,12 +55,12 @@ namespace ImageSharp /// /// Bulk version of . /// - /// The to the source colors. - /// The to the destination vectors. + /// The to the source colors. + /// The to the destination vectors. /// The number of pixels to convert. internal virtual void ToVector4( - ArrayPointer sourceColors, - ArrayPointer destVectors, + BufferPointer sourceColors, + BufferPointer destVectors, int count) { byte* sp = (byte*)sourceColors; @@ -78,12 +78,12 @@ namespace ImageSharp /// /// Bulk version of that converts data in . /// - /// The to the source bytes. - /// The to the destination colors. + /// The to the source bytes. + /// The to the destination colors. /// The number of pixels to convert. internal virtual void PackFromXyzBytes( - ArrayPointer sourceBytes, - ArrayPointer destColors, + BufferPointer sourceBytes, + BufferPointer destColors, int count) { byte* sp = (byte*)sourceBytes; @@ -102,10 +102,10 @@ namespace ImageSharp /// /// Bulk version of . /// - /// The to the source colors. - /// The to the destination bytes. + /// The to the source colors. + /// The to the destination bytes. /// The number of pixels to convert. - internal virtual void ToXyzBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + internal virtual void ToXyzBytes(BufferPointer sourceColors, BufferPointer destBytes, int count) { byte* sp = (byte*)sourceColors; byte[] dest = destBytes.Array; @@ -121,12 +121,12 @@ namespace ImageSharp /// /// Bulk version of that converts data in . /// - /// The to the source bytes. - /// The to the destination colors. + /// The to the source bytes. + /// The to the destination colors. /// The number of pixels to convert. internal virtual void PackFromXyzwBytes( - ArrayPointer sourceBytes, - ArrayPointer destColors, + BufferPointer sourceBytes, + BufferPointer destColors, int count) { byte* sp = (byte*)sourceBytes; @@ -145,12 +145,12 @@ namespace ImageSharp /// /// Bulk version of . /// - /// The to the source colors. - /// The to the destination bytes. + /// The to the source colors. + /// The to the destination bytes. /// The number of pixels to convert. internal virtual void ToXyzwBytes( - ArrayPointer sourceColors, - ArrayPointer destBytes, + BufferPointer sourceColors, + BufferPointer destBytes, int count) { byte* sp = (byte*)sourceColors; @@ -167,12 +167,12 @@ namespace ImageSharp /// /// Bulk version of that converts data in . /// - /// The to the source bytes. - /// The to the destination colors. + /// The to the source bytes. + /// The to the destination colors. /// The number of pixels to convert. internal virtual void PackFromZyxBytes( - ArrayPointer sourceBytes, - ArrayPointer destColors, + BufferPointer sourceBytes, + BufferPointer destColors, int count) { byte* sp = (byte*)sourceBytes; @@ -191,10 +191,10 @@ namespace ImageSharp /// /// Bulk version of . /// - /// The to the source colors. - /// The to the destination bytes. + /// The to the source colors. + /// The to the destination bytes. /// The number of pixels to convert. - internal virtual void ToZyxBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + internal virtual void ToZyxBytes(BufferPointer sourceColors, BufferPointer destBytes, int count) { byte* sp = (byte*)sourceColors; byte[] dest = destBytes.Array; @@ -210,12 +210,12 @@ namespace ImageSharp /// /// Bulk version of that converts data in . /// - /// The to the source bytes. - /// The to the destination colors. + /// The to the source bytes. + /// The to the destination colors. /// The number of pixels to convert. internal virtual void PackFromZyxwBytes( - ArrayPointer sourceBytes, - ArrayPointer destColors, + BufferPointer sourceBytes, + BufferPointer destColors, int count) { byte* sp = (byte*)sourceBytes; @@ -234,12 +234,12 @@ namespace ImageSharp /// /// Bulk version of . /// - /// The to the source colors. - /// The to the destination bytes. + /// The to the source colors. + /// The to the destination bytes. /// The number of pixels to convert. internal virtual void ToZyxwBytes( - ArrayPointer sourceColors, - ArrayPointer destBytes, + BufferPointer sourceColors, + BufferPointer destBytes, int count) { byte* sp = (byte*)sourceColors; diff --git a/src/ImageSharp/Common/Memory/ArrayPointer.cs b/src/ImageSharp/Common/Memory/BufferPointer.cs similarity index 79% rename from src/ImageSharp/Common/Memory/ArrayPointer.cs rename to src/ImageSharp/Common/Memory/BufferPointer.cs index 342eaa6c8..c80e22e21 100644 --- a/src/ImageSharp/Common/Memory/ArrayPointer.cs +++ b/src/ImageSharp/Common/Memory/BufferPointer.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -8,32 +8,32 @@ namespace ImageSharp using System.Runtime.CompilerServices; /// - /// Utility methods to + /// Utility methods for /// - internal static class ArrayPointer + internal static class BufferPointer { /// - /// Gets an to the beginning of the raw data in 'buffer'. + /// Gets a to the beginning of the raw data in 'buffer'. /// /// The element type /// The input - /// The + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe ArrayPointer GetArrayPointer(this PinnedBuffer buffer) + public static unsafe BufferPointer Slice(this PinnedBuffer buffer) where T : struct { - return new ArrayPointer(buffer.Array, (void*)buffer.Pointer); + return new BufferPointer(buffer.Array, (void*)buffer.Pointer); } /// /// Copy 'count' number of elements of the same type from 'source' to 'dest' /// /// The element type. - /// The input - /// The destination . + /// The input + /// The destination . /// The number of elements to copy [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void Copy(ArrayPointer source, ArrayPointer destination, int count) + public static unsafe void Copy(BufferPointer source, BufferPointer destination, int count) where T : struct { Unsafe.CopyBlock((void*)source.PointerAtOffset, (void*)destination.PointerAtOffset, USizeOf(count)); @@ -47,7 +47,7 @@ namespace ImageSharp /// The destination buffer. /// The number of elements to copy from 'source' [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void Copy(ArrayPointer source, ArrayPointer destination, int countInSource) + public static unsafe void Copy(BufferPointer source, BufferPointer destination, int countInSource) where T : struct { Unsafe.CopyBlock((void*)source.PointerAtOffset, (void*)destination.PointerAtOffset, USizeOf(countInSource)); @@ -61,7 +61,7 @@ namespace ImageSharp /// The destination buffer"/> /// The number of elements to copy. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void Copy(ArrayPointer source, ArrayPointer destination, int countInDest) + public static unsafe void Copy(BufferPointer source, BufferPointer destination, int countInDest) where T : struct { Unsafe.CopyBlock((void*)source.PointerAtOffset, (void*)destination.PointerAtOffset, USizeOf(countInDest)); diff --git a/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs b/src/ImageSharp/Common/Memory/BufferPointer{T}.cs similarity index 66% rename from src/ImageSharp/Common/Memory/ArrayPointer{T}.cs rename to src/ImageSharp/Common/Memory/BufferPointer{T}.cs index 9cbbaf094..fff4e513e 100644 --- a/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs +++ b/src/ImageSharp/Common/Memory/BufferPointer{T}.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -15,21 +15,21 @@ namespace ImageSharp /// - It's not possible to use it with stack objects or pointers to unmanaged memory, only with managed arrays /// - It's possible to retrieve a reference to the array () so we can pass it to API-s like /// - There is no bounds checking for performance reasons. Therefore we don't need to store length. (However this could be added as DEBUG-only feature.) - /// This makes an unsafe type! - /// - Currently the arrays provided to ArrayPointer need to be pinned. This behaviour could be changed using C#7 features. + /// This makes an unsafe type! + /// - Currently the arrays provided to BufferPointer need to be pinned. This behaviour could be changed using C#7 features. /// /// The type of elements of the array - internal unsafe struct ArrayPointer + internal unsafe struct BufferPointer where T : struct { /// - /// Initializes a new instance of the struct from a pinned array and an offset. + /// Initializes a new instance of the struct from a pinned array and an offset. /// /// The pinned array /// Pointer to the beginning of array /// The offset inside the array [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ArrayPointer(T[] array, void* pointerToArray, int offset) + public BufferPointer(T[] array, void* pointerToArray, int offset) { DebugGuard.NotNull(array, nameof(array)); @@ -39,12 +39,12 @@ namespace ImageSharp } /// - /// Initializes a new instance of the struct from a pinned array. + /// Initializes a new instance of the struct from a pinned array. /// /// The pinned array /// Pointer to the start of 'array' [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ArrayPointer(T[] array, void* pointerToArray) + public BufferPointer(T[] array, void* pointerToArray) { DebugGuard.NotNull(array, nameof(array)); @@ -69,34 +69,34 @@ namespace ImageSharp public IntPtr PointerAtOffset { get; private set; } /// - /// Convertes instance to a raw 'void*' pointer + /// Convertes instance to a raw 'void*' pointer /// - /// The to convert + /// The to convert [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator void*(ArrayPointer arrayPointer) + public static explicit operator void*(BufferPointer bufferPointer) { - return (void*)arrayPointer.PointerAtOffset; + return (void*)bufferPointer.PointerAtOffset; } /// - /// Convertes instance to a raw 'byte*' pointer + /// Convertes instance to a raw 'byte*' pointer /// - /// The to convert + /// The to convert [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator byte*(ArrayPointer arrayPointer) + public static explicit operator byte*(BufferPointer bufferPointer) { - return (byte*)arrayPointer.PointerAtOffset; + return (byte*)bufferPointer.PointerAtOffset; } /// - /// Forms a slice out of the given ArrayPointer, beginning at 'offset'. + /// Forms a slice out of the given BufferPointer, beginning at 'offset'. /// /// The offset in number of elements - /// The offseted (sliced) ArrayPointer + /// The offseted (sliced) BufferPointer [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ArrayPointer Slice(int offset) + public BufferPointer Slice(int offset) { - ArrayPointer result = default(ArrayPointer); + BufferPointer result = default(BufferPointer); result.Array = this.Array; result.Offset = this.Offset + offset; result.PointerAtOffset = this.PointerAtOffset + (Unsafe.SizeOf() * offset); diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs index 9222d6bac..694a26f3d 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs @@ -19,13 +19,13 @@ namespace ImageSharp.Benchmarks.Color.Bulk { abstract class CopyExecutor { - internal abstract void VirtualCopy(ArrayPointer destination, ArrayPointer source, int count); + internal abstract void VirtualCopy(BufferPointer destination, BufferPointer source, int count); } class UnsafeCopyExecutor : CopyExecutor { [MethodImpl(MethodImplOptions.NoInlining)] - internal override unsafe void VirtualCopy(ArrayPointer destination, ArrayPointer source, int count) + internal override unsafe void VirtualCopy(BufferPointer destination, BufferPointer source, int count) { Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, (uint)count*4); } @@ -76,7 +76,7 @@ namespace ImageSharp.Benchmarks.Color.Bulk } [Benchmark] - public void CopyArrayPointerUnsafeInlined() + public void CopyBufferPointerUnsafeInlined() { uint byteCount = (uint)this.area.Width * 4; @@ -85,22 +85,22 @@ namespace ImageSharp.Benchmarks.Color.Bulk for (int y = 0; y < this.Height; y++) { - ArrayPointer source = this.GetAreaRow(y); - ArrayPointer destination = this.GetPixelAccessorRow(targetX, targetY + y); + BufferPointer source = this.GetAreaRow(y); + BufferPointer destination = this.GetPixelAccessorRow(targetX, targetY + y); Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, byteCount); } } [Benchmark] - public void CopyArrayPointerUnsafeVirtual() + public void CopyBufferPointerUnsafeVirtual() { int targetX = this.Width / 4; int targetY = 0; for (int y = 0; y < this.Height; y++) { - ArrayPointer source = this.GetAreaRow(y); - ArrayPointer destination = this.GetPixelAccessorRow(targetX, targetY + y); + BufferPointer source = this.GetAreaRow(y); + BufferPointer destination = this.GetPixelAccessorRow(targetX, targetY + y); this.executor.VirtualCopy(destination, source, this.area.Width); } } @@ -111,9 +111,9 @@ namespace ImageSharp.Benchmarks.Color.Bulk } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ArrayPointer GetPixelAccessorRow(int x, int y) + private BufferPointer GetPixelAccessorRow(int x, int y) { - return new ArrayPointer( + return new BufferPointer( this.pixelAccessor.PixelBuffer, (void*)this.pixelAccessor.DataPointer, (y * this.pixelAccessor.Width) + x @@ -121,9 +121,9 @@ namespace ImageSharp.Benchmarks.Color.Bulk } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ArrayPointer GetAreaRow(int y) + private BufferPointer GetAreaRow(int y) { - return new ArrayPointer(this.area.Bytes, this.area.PixelBase, y * this.area.RowStride); + return new BufferPointer(this.area.Bytes, this.area.PixelBase, y * this.area.RowStride); } } } diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs index 1c22411d0..80d5952a1 100644 --- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs @@ -245,8 +245,8 @@ public PinnedBuffer ActualDestBuffer { get; } public PinnedBuffer ExpectedDestBuffer { get; } - public ArrayPointer Source => this.SourceBuffer.GetArrayPointer(); - public ArrayPointer ActualDest => this.ActualDestBuffer.GetArrayPointer(); + public BufferPointer Source => this.SourceBuffer.Slice(); + public BufferPointer ActualDest => this.ActualDestBuffer.Slice(); public TestBuffers(TSource[] source, TDest[] expectedDest) { @@ -277,7 +277,7 @@ private static void TestOperation( TSource[] source, TDest[] expected, - Action, ArrayPointer, ArrayPointer> action) + Action, BufferPointer, BufferPointer> action) where TSource : struct where TDest : struct { diff --git a/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs b/tests/ImageSharp.Tests/Common/BufferPointerTests.cs similarity index 81% rename from tests/ImageSharp.Tests/Common/ArrayPointerTests.cs rename to tests/ImageSharp.Tests/Common/BufferPointerTests.cs index 916a10947..4b5847d53 100644 --- a/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs +++ b/tests/ImageSharp.Tests/Common/BufferPointerTests.cs @@ -7,7 +7,7 @@ namespace ImageSharp.Tests.Common using Xunit; - public unsafe class ArrayPointerTests + public unsafe class BufferPointerTests { public struct Foo { @@ -33,7 +33,7 @@ namespace ImageSharp.Tests.Common fixed (Foo* p = array) { // Act: - ArrayPointer ap = new ArrayPointer(array, p); + BufferPointer ap = new BufferPointer(array, p); // Assert: Assert.Equal(array, ap.Array); @@ -49,7 +49,7 @@ namespace ImageSharp.Tests.Common fixed (Foo* p = array) { // Act: - ArrayPointer ap = new ArrayPointer(array, p, offset); + BufferPointer ap = new BufferPointer(array, p, offset); // Assert: Assert.Equal(array, ap.Array); @@ -67,7 +67,7 @@ namespace ImageSharp.Tests.Common int totalOffset = offset0 + offset1; fixed (Foo* p = array) { - ArrayPointer ap = new ArrayPointer(array, p, offset0); + BufferPointer ap = new BufferPointer(array, p, offset0); // Act: ap = ap.Slice(offset1); @@ -92,10 +92,10 @@ namespace ImageSharp.Tests.Common fixed (Foo* pSource = source) fixed (Foo* pDest = dest) { - ArrayPointer apSource = new ArrayPointer(source, pSource); - ArrayPointer apDest = new ArrayPointer(dest, pDest); + BufferPointer apSource = new BufferPointer(source, pSource); + BufferPointer apDest = new BufferPointer(dest, pDest); - ArrayPointer.Copy(apSource, apDest, count); + BufferPointer.Copy(apSource, apDest, count); } Assert.Equal(source[0], dest[0]); @@ -115,10 +115,10 @@ namespace ImageSharp.Tests.Common fixed (Foo* pSource = source) fixed (byte* pDest = dest) { - ArrayPointer apSource = new ArrayPointer(source, pSource); - ArrayPointer apDest = new ArrayPointer(dest, pDest); + BufferPointer apSource = new BufferPointer(source, pSource); + BufferPointer apDest = new BufferPointer(dest, pDest); - ArrayPointer.Copy(apSource, apDest, count); + BufferPointer.Copy(apSource, apDest, count); } Assert.True(ElementsAreEqual(source, dest, 0)); @@ -138,10 +138,10 @@ namespace ImageSharp.Tests.Common fixed(byte* pSource = source) fixed (Foo* pDest = dest) { - ArrayPointer apSource = new ArrayPointer(source, pSource); - ArrayPointer apDest = new ArrayPointer(dest, pDest); + BufferPointer apSource = new BufferPointer(source, pSource); + BufferPointer apDest = new BufferPointer(dest, pDest); - ArrayPointer.Copy(apSource, apDest, count); + BufferPointer.Copy(apSource, apDest, count); } Assert.True(ElementsAreEqual(dest, source, 0)); diff --git a/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs b/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs index c5eb2a510..65077ae7f 100644 --- a/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs +++ b/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs @@ -57,13 +57,13 @@ } [Fact] - public void GetArrayPointer() + public void Slice() { Foo[] a = { new Foo() { A = 1, B = 2 }, new Foo() { A = 3, B = 4 } }; using (PinnedBuffer buffer = new PinnedBuffer(a)) { - var arrayPtr = buffer.GetArrayPointer(); + var arrayPtr = buffer.Slice(); Assert.Equal(a, arrayPtr.Array); Assert.Equal(0, arrayPtr.Offset); From 0b08485708df38d353df07368a9d9b5456c5c764 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 8 Mar 2017 22:07:23 +1100 Subject: [PATCH 141/142] Fix #127 Stop processing once we hit the end marker. --- src/ImageSharp.Formats.Png/PngDecoderCore.cs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp.Formats.Png/PngDecoderCore.cs b/src/ImageSharp.Formats.Png/PngDecoderCore.cs index a7765342e..fd03ed39b 100644 --- a/src/ImageSharp.Formats.Png/PngDecoderCore.cs +++ b/src/ImageSharp.Formats.Png/PngDecoderCore.cs @@ -109,6 +109,11 @@ namespace ImageSharp.Formats /// private byte[] paletteAlpha; + /// + /// A value indicating whether the end chunk has been reached. + /// + private bool isEndChunkReached; + /// /// Initializes static members of the class. /// @@ -158,18 +163,11 @@ namespace ImageSharp.Formats this.currentStream = stream; this.currentStream.Skip(8); - bool isEndChunkReached = false; - using (MemoryStream dataStream = new MemoryStream()) { PngChunk currentChunk; - while ((currentChunk = this.ReadChunk()) != null) + while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null) { - if (isEndChunkReached) - { - throw new ImageFormatException("Image does not end with end chunk."); - } - try { switch (currentChunk.Type) @@ -199,7 +197,7 @@ namespace ImageSharp.Formats this.ReadTextChunk(currentImage, currentChunk.Data, currentChunk.Length); break; case PngChunkTypes.End: - isEndChunkReached = true; + this.isEndChunkReached = true; break; } } From 43b08e509abe1467dccfb6d718058571a2214da8 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 9 Mar 2017 21:37:41 +1100 Subject: [PATCH 142/142] Resize should respect source rectangle Fix #118 --- .../Transforms/CompandingResizeProcessor.cs | 12 ++++++----- .../Processors/Transforms/ResizeProcessor.cs | 12 ++++++----- .../Processors/Filters/ResizeTests.cs | 21 +++++++++++++++++++ 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp.Processing/Processors/Transforms/CompandingResizeProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/CompandingResizeProcessor.cs index 53da21483..2190254f0 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/CompandingResizeProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Transforms/CompandingResizeProcessor.cs @@ -56,6 +56,8 @@ namespace ImageSharp.Processing.Processors int width = this.Width; int height = this.Height; + int sourceX = sourceRectangle.X; + int sourceY = sourceRectangle.Y; int startY = this.ResizeRectangle.Y; int endY = this.ResizeRectangle.Bottom; int startX = this.ResizeRectangle.X; @@ -83,12 +85,12 @@ namespace ImageSharp.Processing.Processors y => { // Y coordinates of source points - int originY = (int)((y - startY) * heightFactor); + int originY = (int)(((y - startY) * heightFactor) + sourceY); for (int x = minX; x < maxX; x++) { // X coordinates of source points - targetPixels[x, y] = sourcePixels[(int)((x - startX) * widthFactor), originY]; + targetPixels[x, y] = sourcePixels[(int)(((x - startX) * widthFactor) + sourceX), originY]; } }); } @@ -110,7 +112,7 @@ namespace ImageSharp.Processing.Processors { Parallel.For( 0, - sourceRectangle.Height, + sourceRectangle.Bottom, this.ParallelOptions, y => { @@ -125,7 +127,7 @@ namespace ImageSharp.Processing.Processors for (int i = 0; i < horizontalValues.Length; i++) { Weight xw = horizontalValues[i]; - destination += sourcePixels[xw.Index, y].ToVector4().Expand() * xw.Value; + destination += sourcePixels[xw.Index + sourceX, y].ToVector4().Expand() * xw.Value; } TColor d = default(TColor); @@ -152,7 +154,7 @@ namespace ImageSharp.Processing.Processors for (int i = 0; i < verticalValues.Length; i++) { Weight yw = verticalValues[i]; - destination += firstPassPixels[x, yw.Index].ToVector4().Expand() * yw.Value; + destination += firstPassPixels[x, yw.Index + sourceY].ToVector4().Expand() * yw.Value; } TColor d = default(TColor); diff --git a/src/ImageSharp.Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp.Processing/Processors/Transforms/ResizeProcessor.cs index f4ec39f78..9ec804aa4 100644 --- a/src/ImageSharp.Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp.Processing/Processors/Transforms/ResizeProcessor.cs @@ -55,6 +55,8 @@ namespace ImageSharp.Processing.Processors int width = this.Width; int height = this.Height; + int sourceX = sourceRectangle.X; + int sourceY = sourceRectangle.Y; int startY = this.ResizeRectangle.Y; int endY = this.ResizeRectangle.Bottom; int startX = this.ResizeRectangle.X; @@ -82,12 +84,12 @@ namespace ImageSharp.Processing.Processors y => { // Y coordinates of source points - int originY = (int)((y - startY) * heightFactor); + int originY = (int)(((y - startY) * heightFactor) + sourceY); for (int x = minX; x < maxX; x++) { // X coordinates of source points - targetPixels[x, y] = sourcePixels[(int)((x - startX) * widthFactor), originY]; + targetPixels[x, y] = sourcePixels[(int)(((x - startX) * widthFactor) + sourceX), originY]; } }); } @@ -109,7 +111,7 @@ namespace ImageSharp.Processing.Processors { Parallel.For( 0, - sourceRectangle.Height, + sourceRectangle.Bottom, this.ParallelOptions, y => { @@ -124,7 +126,7 @@ namespace ImageSharp.Processing.Processors for (int i = 0; i < horizontalValues.Length; i++) { Weight xw = horizontalValues[i]; - destination += sourcePixels[xw.Index, y].ToVector4() * xw.Value; + destination += sourcePixels[xw.Index + sourceX, y].ToVector4() * xw.Value; } TColor d = default(TColor); @@ -151,7 +153,7 @@ namespace ImageSharp.Processing.Processors for (int i = 0; i < verticalValues.Length; i++) { Weight yw = verticalValues[i]; - destination += firstPassPixels[x, yw.Index].ToVector4() * yw.Value; + destination += firstPassPixels[x, yw.Index + sourceY].ToVector4() * yw.Value; } TColor d = default(TColor); diff --git a/tests/ImageSharp.Tests/Processors/Filters/ResizeTests.cs b/tests/ImageSharp.Tests/Processors/Filters/ResizeTests.cs index 5acbe0f3e..06ab245c9 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/ResizeTests.cs @@ -50,6 +50,27 @@ namespace ImageSharp.Tests } } + [Theory] + [MemberData(nameof(ReSamplers))] + public void ImageShouldResizeFromSourceRectangle(string name, IResampler sampler) + { + name = $"{name}-SourceRect"; + + string path = this.CreateOutputDirectory("Resize"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName(name); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + var sourceRectangle = new Rectangle(image.Width / 8, image.Height / 8, image.Width / 4, image.Height / 4); + var destRectangle = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); + image.Resize(image.Width, image.Height, sampler, sourceRectangle, destRectangle, false).Save(output); + } + } + } + [Theory] [MemberData(nameof(ReSamplers))] public void ImageShouldResizeWidthAndKeepAspect(string name, IResampler sampler)