/// <summary> /// Applies quantization to the image. /// </summary> /// <typeparam name="TPixel">The pixel format.</typeparam> /// <param name="source">The image this method extends.</param> /// <param name="quantizer">The quantizer to apply to perform the operation.</param> /// <param name="maxColors">The maximum number of colors to return.</param> /// <returns>The <see cref="Image{TPixel}"/>.</returns> public static IImageProcessingContext <TPixel> Quantize <TPixel>(this IImageProcessingContext <TPixel> source, IQuantizer <TPixel> quantizer, int maxColors) where TPixel : struct, IPixel <TPixel> { return(source.Apply(img => { // TODO : move helper logic into the processor QuantizedImage <TPixel> quantized = quantizer.Quantize(img.Frames.RootFrame, maxColors); int palleteCount = quantized.Palette.Length - 1; using (Buffer2D <TPixel> pixels = source.MemoryManager.Allocate2D <TPixel>(quantized.Width, quantized.Height)) { Parallel.For( 0, pixels.Height, img.GetConfiguration().ParallelOptions, y => { Span <TPixel> row = pixels.GetRowSpan(y); int yy = y * pixels.Width; for (int x = 0; x < pixels.Width; x++) { int i = x + yy; TPixel color = quantized.Palette[Math.Min(palleteCount, quantized.Pixels[i])]; row[x] = color; } }); Buffer2D <TPixel> .SwapContents(img.Frames[0].PixelBuffer, pixels); } })); }
/// <summary> /// Writes the color table to the stream. /// </summary> /// <typeparam name="T">The pixel format.</typeparam> /// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam> /// <param name="image">The <see cref="ImageBase{T,TP}"/> to encode.</param> /// <param name="writer">The writer to write to the stream with.</param> private void WriteColorTable <T, TP>(QuantizedImage <T, TP> image, EndianBinaryWriter writer) where T : IPackedVector <TP> where TP : struct { // Grab the palette and write it to the stream. T[] palette = image.Palette; int pixelCount = palette.Length; // Get max colors for bit depth. int colorTableLength = (int)Math.Pow(2, this.bitDepth) * 3; byte[] colorTable = new byte[colorTableLength]; Parallel.For(0, pixelCount, i => { int offset = i * 3; byte[] color = palette[i].ToBytes(); colorTable[offset] = color[0]; colorTable[offset + 1] = color[1]; colorTable[offset + 2] = color[2]; }); writer.Write(colorTable, 0, colorTableLength); }
/// <summary> /// Encodes the image to the specified stream from the <see cref="Image{TColor}"/>. /// </summary> /// <typeparam name="TColor">The pixel format.</typeparam> /// <param name="image">The <see cref="Image{TColor}"/> to encode from.</param> /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param> public void Encode <TColor>(Image <TColor> image, Stream stream) where TColor : struct, IPackedPixel, IEquatable <TColor> { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); if (this.Quantizer == null) { this.Quantizer = new OctreeQuantizer <TColor>(); } // 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; // Get the number of bits. this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(this.Quality); // Quantize the image returning a palette. QuantizedImage <TColor> quantized = ((IQuantizer <TColor>) this.Quantizer).Quantize(image, this.Quality); int index = this.GetTransparentIndex(quantized); // Write the header. this.WriteHeader(writer); // Write the LSD. We'll use local color tables for now. this.WriteLogicalScreenDescriptor(image, writer, index); // Write the first frame. this.WriteGraphicalControlExtension(image, writer, index); this.WriteImageDescriptor(image, writer); this.WriteColorTable(quantized, writer); this.WriteImageData(quantized, writer); // Write additional frames. if (image.Frames.Any()) { this.WriteApplicationExtension(writer, image.MetaData.RepeatCount, image.Frames.Count); // ReSharper disable once ForCanBeConvertedToForeach for (int i = 0; i < image.Frames.Count; i++) { ImageFrame <TColor> frame = image.Frames[i]; QuantizedImage <TColor> quantizedFrame = ((IQuantizer <TColor>) this.Quantizer).Quantize(frame, this.Quality); this.WriteGraphicalControlExtension(frame, writer, this.GetTransparentIndex(quantizedFrame)); this.WriteImageDescriptor(frame, writer); this.WriteColorTable(quantizedFrame, writer); this.WriteImageData(quantizedFrame, writer); } } // TODO: Write Comments extension etc writer.Write(GifConstants.EndIntroducer); }
/// <summary> /// Writes the color table to the stream. /// </summary> /// <typeparam name="TPixel">The pixel format.</typeparam> /// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode.</param> /// <param name="writer">The writer to write to the stream with.</param> private void WriteColorTable <TPixel>(QuantizedImage <TPixel> image, EndianBinaryWriter writer) where TPixel : struct, IPixel <TPixel> { // Grab the palette and write it to the stream. int pixelCount = image.Palette.Length; // Get max colors for bit depth. int colorTableLength = (int)Math.Pow(2, this.bitDepth) * 3; var rgb = default(Rgb24); using (IManagedByteBuffer colorTable = this.memoryManager.AllocateManagedByteBuffer(colorTableLength)) { Span <byte> colorTableSpan = colorTable.Span; for (int i = 0; i < pixelCount; i++) { int offset = i * 3; image.Palette[i].ToRgb24(ref rgb); colorTableSpan[offset] = rgb.R; colorTableSpan[offset + 1] = rgb.G; colorTableSpan[offset + 2] = rgb.B; } writer.Write(colorTable.Array, 0, colorTableLength); } }
/// <summary> /// Writes the color table to the stream. /// </summary> /// <typeparam name="TPixel">The pixel format.</typeparam> /// <param name="image">The <see cref="ImageBase{TPixel}"/> to encode.</param> /// <param name="writer">The writer to write to the stream with.</param> private void WriteColorTable <TPixel>(QuantizedImage <TPixel> image, EndianBinaryWriter writer) where TPixel : struct, IPixel <TPixel> { // Grab the palette and write it to the stream. int pixelCount = image.Palette.Length; // Get max colors for bit depth. int colorTableLength = (int)Math.Pow(2, this.bitDepth) * 3; byte[] colorTable = ArrayPool <byte> .Shared.Rent(colorTableLength); try { for (int i = 0; i < pixelCount; i++) { int offset = i * 3; image.Palette[i].ToXyzBytes(this.buffer, 0); colorTable[offset] = this.buffer[0]; colorTable[offset + 1] = this.buffer[1]; colorTable[offset + 2] = this.buffer[2]; } writer.Write(colorTable, 0, colorTableLength); } finally { ArrayPool <byte> .Shared.Return(colorTable); } }
/// <summary> /// Writes the color table to the stream. /// </summary> /// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <param name="image">The <see cref="ImageBase{TColor, TPacked}"/> to encode.</param> /// <param name="writer">The writer to write to the stream with.</param> private void WriteColorTable <TColor, TPacked>(QuantizedImage <TColor, TPacked> image, EndianBinaryWriter writer) where TColor : struct, IPackedPixel <TPacked> where TPacked : struct { // Grab the palette and write it to the stream. TColor[] palette = image.Palette; int pixelCount = palette.Length; // Get max colors for bit depth. int colorTableLength = (int)Math.Pow(2, this.bitDepth) * 3; byte[] colorTable = new byte[colorTableLength]; Parallel.For( 0, pixelCount, i => { int offset = i * 3; Color color = new Color(palette[i].ToVector4()); colorTable[offset] = color.R; colorTable[offset + 1] = color.G; colorTable[offset + 2] = color.B; }); writer.Write(colorTable, 0, colorTableLength); }
private void WriteImageData(QuantizedImage image, EndianBinaryWriter writer) { byte[] indexedPixels = image.Pixels; LzwEncoder encoder = new LzwEncoder(indexedPixels, (byte)bitDepth); encoder.Encode(writer.BaseStream); }
/// <summary> /// Applies quantization to the image. /// </summary> /// <typeparam name="TColor">The pixel format.</typeparam> /// <param name="source">The image this method extends.</param> /// <param name="quantizer">The quantizer to apply to perform the operation.</param> /// <param name="maxColors">The maximum number of colors to return.</param> /// <returns>The <see cref="Image{TColor}"/>.</returns> public static Image <TColor> Quantize <TColor>(this Image <TColor> source, IQuantizer <TColor> quantizer, int maxColors) where TColor : struct, IPackedPixel, IEquatable <TColor> { QuantizedImage <TColor> quantized = quantizer.Quantize(source, maxColors); int pixelCount = quantized.Pixels.Length; int palleteCount = quantized.Palette.Length - 1; using (PixelAccessor <TColor> pixels = new PixelAccessor <TColor>(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.SwapPixelsBuffers(pixels); return(source); } }
public static Image2 Quantize(this Image2 source, IQuantizer quantizer, int maxColors) { QuantizedImage quantizedImage = quantizer.Quantize(source, maxColors); source.SetPixels(source.Width, source.Height, quantizedImage.ToImage().Pixels); return(source); }
/// <summary> /// Applies quantization to the image. /// </summary> /// <typeparam name="TPixel">The pixel format.</typeparam> /// <param name="source">The image this method extends.</param> /// <param name="quantizer">The quantizer to apply to perform the operation.</param> /// <param name="maxColors">The maximum number of colors to return.</param> /// <returns>The <see cref="Image{TPixel}"/>.</returns> public static IImageProcessingContext <TPixel> Quantize <TPixel>(this IImageProcessingContext <TPixel> source, IQuantizer <TPixel> quantizer, int maxColors) where TPixel : struct, IPixel <TPixel> { return(source.Apply(img => { // TODO : move helper logic into the processor QuantizedImage <TPixel> quantized = quantizer.Quantize(img.Frames.RootFrame, maxColors); int palleteCount = quantized.Palette.Length - 1; using (var pixels = new PixelAccessor <TPixel>(quantized.Width, quantized.Height)) { Parallel.For( 0, pixels.Height, img.GetConfiguration().ParallelOptions, y => { for (int x = 0; x < pixels.Width; x++) { int i = x + (y * pixels.Width); TPixel color = quantized.Palette[Math.Min(palleteCount, quantized.Pixels[i])]; pixels[x, y] = color; } }); img.Frames[0].SwapPixelsBuffers(pixels); } })); }
private QuantizedImage WritePaletteChunk(Stream stream, PngHeader header, ImageBase image) { if (Quality > 256) { return(null); } if (Quantizer == null) { Quantizer = new WuQuantizer { Threshold = Threshold }; } QuantizedImage quantized = ((IQuantizer)Quantizer).Quantize(image, Quality); Color2[] palette = quantized.Palette; int pixelCount = palette.Length; int colorTableLength = (int)Math.Pow(2, header.BitDepth) * 3; byte[] colorTable = new byte[colorTableLength]; Parallel.For(0, pixelCount, Bootstrapper.instance.ParallelOptions, i => { int offset = i * 3; byte[] color = palette[i].ToBytes(); colorTable[offset] = color[0]; colorTable[offset + 1] = color[1]; colorTable[offset + 2] = color[2]; }); WriteChunk(stream, PngChunkTypes.Palette, colorTable); if (quantized.TransparentIndex > -1) { WriteChunk(stream, PngChunkTypes.PaletteAlpha, new[] { (byte)quantized.TransparentIndex }); } return(quantized); }
/// <summary> /// Returns the index of the most transparent color in the palette. /// </summary> /// <param name="quantized"> /// The quantized. /// </param> /// <typeparam name="TPixel">The pixel format.</typeparam> /// <returns> /// The <see cref="int"/>. /// </returns> private int GetTransparentIndex <TPixel>(QuantizedImage <TPixel> quantized) where TPixel : struct, IPixel <TPixel> { // Find the lowest alpha value and make it the transparent index. int index = 255; byte alpha = 255; bool hasEmpty = false; // Some images may have more than one quantized pixel returned with an alpha value of zero // so we should always ignore if we have empty pixels present. for (int i = 0; i < quantized.Palette.Length; i++) { quantized.Palette[i].ToXyzwBytes(this.buffer, 0); if (!hasEmpty) { if (this.buffer[0] == 0 && this.buffer[1] == 0 && this.buffer[2] == 0 && this.buffer[3] == 0) { alpha = this.buffer[3]; index = i; hasEmpty = true; } if (this.buffer[3] < alpha) { alpha = this.buffer[3]; index = i; } } } return(index); }
/// <summary> /// Initializes a new instance of the <see cref="Frame"/> class. /// </summary> /// <param name="image">The image.</param> /// <param name="quantizedImage">The quantized image.</param> /// <param name="bitDepth">The bit depth.</param> /// <param name="delay">The delay.</param> public Frame(Image image, QuantizedImage quantizedImage, int bitDepth, short delay) : this(new GraphicsControl(image, quantizedImage, delay), new ImageDescriptor(image, bitDepth), new ColorTable(quantizedImage, bitDepth), new FrameIndices(quantizedImage, bitDepth), null) { }
/// <summary> /// Writes the image pixel data to the stream. /// </summary> /// <typeparam name="TPixel">The pixel format.</typeparam> /// <param name="image">The <see cref="QuantizedImage{TPixel}"/> containing indexed pixels.</param> /// <param name="writer">The stream to write to.</param> private void WriteImageData <TPixel>(QuantizedImage <TPixel> image, EndianBinaryWriter writer) where TPixel : struct, IPixel <TPixel> { using (LzwEncoder encoder = new LzwEncoder(image.Pixels, (byte)this.bitDepth)) { encoder.Encode(writer.BaseStream); } }
/// <summary> /// Writes the image pixel data to the stream. /// </summary> /// <typeparam name="TColor">The pixel format.</typeparam> /// <param name="image">The <see cref="QuantizedImage{TColor}"/> containing indexed pixels.</param> /// <param name="writer">The stream to write to.</param> private void WriteImageData <TColor>(QuantizedImage <TColor> image, EndianBinaryWriter writer) where TColor : struct, IPackedPixel, IEquatable <TColor> { using (LzwEncoder encoder = new LzwEncoder(image.Pixels, (byte)this.bitDepth)) { encoder.Encode(writer.BaseStream); } }
/// <summary> /// Collects the indexed pixel data. /// </summary> /// <typeparam name="TColor">The pixel format.</typeparam> /// <param name="image">The image to encode.</param> /// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="header">The <see cref="PngHeader"/>.</param> private void CollectIndexedBytes <TColor>(ImageBase <TColor> image, Stream stream, PngHeader header) where TColor : struct, IPackedPixel, IEquatable <TColor> { // Quantize the image and get the pixels. QuantizedImage <TColor> quantized = this.WritePaletteChunk(stream, header, image); this.palettePixelData = quantized.Pixels; }
/// <summary> /// Collects the indexed pixel data. /// </summary> /// <typeparam name="TPixel">The pixel format.</typeparam> /// <param name="image">The image to encode.</param> /// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="header">The <see cref="PngHeader"/>.</param> private void CollectIndexedBytes <TPixel>(ImageFrame <TPixel> image, Stream stream, PngHeader header) where TPixel : struct, IPixel <TPixel> { // Quantize the image and get the pixels. QuantizedImage <TPixel> quantized = this.WritePaletteChunk(stream, header, image); this.palettePixelData = quantized.Pixels; }
public void Encode(ImageBase imageBase, Stream stream) { if (imageBase == null || stream == null) { throw new ArgumentNullException(); } Image2 image = (Image2)imageBase; if (Quantizer == null) { Quantizer = new OctreeQuantizer { Threshold = Threshold }; } // Do not use IDisposable pattern here as we want to preserve the stream. EndianBinaryWriter writer = new EndianBinaryWriter(EndianBitConverter.Little, stream); // Ensure that quality can be set but has a fallback. int quality = Quality > 0 ? Quality : imageBase.Quality; Quality = quality > 0 ? NumUtils.Clamp(quality, 1, 256) : 256; // Get the number of bits. bitDepth = GetBitsNeededForColorDepth(Quality); // Quantize the image returning a palette. QuantizedImage quantized = Quantizer.Quantize(image, Quality); // Write the header. WriteHeader(writer); // Write the LSD. We'll use local color tables for now. WriteLogicalScreenDescriptor(image, writer, quantized.TransparentIndex); // Write the first frame. WriteGraphicalControlExtension(imageBase, writer, quantized.TransparentIndex); WriteImageDescriptor(image, writer); WriteColorTable(quantized, writer); WriteImageData(quantized, writer); // Write additional frames. if (image.Frames.Any()) { WriteApplicationExtension(writer, image.RepeatCount, image.Frames.Count); foreach (ImageFrame frame in image.Frames) { QuantizedImage quantizedFrame = Quantizer.Quantize(frame, Quality); WriteGraphicalControlExtension(frame, writer, quantizedFrame.TransparentIndex); WriteImageDescriptor(frame, writer); WriteColorTable(quantizedFrame, writer); WriteImageData(quantizedFrame, writer); } } // TODO: Write Comments extension etc writer.Write(GifConstants.endIntroducer); }
/// <summary> /// Applies quantization to the image. /// </summary> /// <typeparam name="T">The pixel format.</typeparam> /// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam> /// <param name="source">The image this method extends.</param> /// <param name="quantizer">The quantizer to apply to perform the operation.</param> /// <param name="maxColors">The maximum number of colors to return.</param> /// <returns>The <see cref="Image{T,TP}"/>.</returns> public static Image <T, TP> Quantize <T, TP>(this Image <T, TP> source, IQuantizer <T, TP> quantizer, int maxColors) where T : IPackedVector <TP> where TP : struct { QuantizedImage <T, TP> quantizedImage = quantizer.Quantize(source, maxColors); source.SetPixels(source.Width, source.Height, quantizedImage.ToImage().Pixels); return(source); }
/// <summary> /// Encodes the image to the specified stream from the <see cref="Image{T,TP}"/>. /// </summary> /// <typeparam name="T">The pixel format.</typeparam> /// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam> /// <param name="image">The <see cref="Image{T,TP}"/> to encode from.</param> /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param> public void Encode <T, TP>(Image <T, TP> image, Stream stream) where T : IPackedVector <TP> where TP : struct { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); if (this.Quantizer == null) { this.Quantizer = new OctreeQuantizer <T, TP> { Threshold = this.Threshold }; } // Do not use IDisposable pattern here as we want to preserve the stream. EndianBinaryWriter writer = new EndianBinaryWriter(EndianBitConverter.Little, stream); // Ensure that quality can be set but has a fallback. int quality = this.Quality > 0 ? this.Quality : image.Quality; this.Quality = quality > 0 ? quality.Clamp(1, 256) : 256; // Get the number of bits. this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(this.Quality); // Quantize the image returning a palette. QuantizedImage <T, TP> quantized = ((IQuantizer <T, TP>) this.Quantizer).Quantize(image, this.Quality); // Write the header. this.WriteHeader(writer); // Write the LSD. We'll use local color tables for now. this.WriteLogicalScreenDescriptor(image, writer, quantized.TransparentIndex); // Write the first frame. this.WriteGraphicalControlExtension(image, writer, quantized.TransparentIndex); this.WriteImageDescriptor(image, writer); this.WriteColorTable(quantized, writer); this.WriteImageData(quantized, writer); // Write additional frames. if (image.Frames.Any()) { this.WriteApplicationExtension(writer, image.RepeatCount, image.Frames.Count); foreach (ImageFrame <T, TP> frame in image.Frames) { QuantizedImage <T, TP> quantizedFrame = ((IQuantizer <T, TP>) this.Quantizer).Quantize(frame, this.Quality); this.WriteGraphicalControlExtension(frame, writer, quantizedFrame.TransparentIndex); this.WriteImageDescriptor(frame, writer); this.WriteColorTable(quantizedFrame, writer); this.WriteImageData(quantizedFrame, writer); } } // TODO: Write Comments extension etc writer.Write(GifConstants.EndIntroducer); }
/// <summary> /// Collects the indexed pixel data. /// </summary> /// <typeparam name="T">The pixel format.</typeparam> /// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam> /// <param name="image">The image to encode.</param> /// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="header">The <see cref="PngHeader"/>.</param> private void CollectIndexedBytes <T, TP>(ImageBase <T, TP> image, Stream stream, PngHeader header) where T : IPackedVector <TP> where TP : struct { // Quatize the image and get the pixels QuantizedImage <T, TP> quantized = this.WritePaletteChunk(stream, header, image); pixelData = quantized.Pixels; }
/// <summary> /// Applies quantization to the image. /// </summary> /// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <param name="source">The image this method extends.</param> /// <param name="quantizer">The quantizer to apply to perform the operation.</param> /// <param name="maxColors">The maximum number of colors to return.</param> /// <returns>The <see cref="Image{TColor, TPacked}"/>.</returns> public static Image <TColor, TPacked> Quantize <TColor, TPacked>(this Image <TColor, TPacked> source, IQuantizer <TColor, TPacked> quantizer, int maxColors) where TColor : struct, IPackedPixel <TPacked> where TPacked : struct, IEquatable <TPacked> { QuantizedImage <TColor, TPacked> quantizedImage = quantizer.Quantize(source, maxColors); source.SetPixels(source.Width, source.Height, quantizedImage.ToImage().Pixels); return(source); }
/// <summary> /// Writes the image pixel data to the stream. /// </summary> /// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <param name="image">The <see cref="QuantizedImage{TColor, TPacked}"/> containing indexed pixels.</param> /// <param name="writer">The stream to write to.</param> private void WriteImageData <TColor, TPacked>(QuantizedImage <TColor, TPacked> image, EndianBinaryWriter writer) where TColor : struct, IPackedPixel <TPacked> where TPacked : struct { byte[] indexedPixels = image.Pixels; LzwEncoder encoder = new LzwEncoder(indexedPixels, (byte)this.bitDepth); encoder.Encode(writer.BaseStream); }
/// <summary> /// Writes the palette chunk to the stream. /// </summary> /// <typeparam name="T">The pixel format.</typeparam> /// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam> /// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <param name="header">The <see cref="PngHeader"/>.</param> /// <param name="image">The image to encode.</param> private QuantizedImage <T, TP> WritePaletteChunk <T, TP>(Stream stream, PngHeader header, ImageBase <T, TP> image) where T : IPackedVector <TP> where TP : struct { if (this.Quality > 256) { return(null); } if (this.Quantizer == null) { this.Quantizer = new WuQuantizer <T, TP> { Threshold = this.Threshold }; } // Quantize the image returning a palette. This boxing is icky. QuantizedImage <T, TP> quantized = ((IQuantizer <T, TP>) this.Quantizer).Quantize(image, this.Quality); // Grab the palette and write it to the stream. T[] palette = quantized.Palette; int pixelCount = palette.Length; // Get max colors for bit depth. int colorTableLength = (int)Math.Pow(2, header.BitDepth) * 3; byte[] colorTable = new byte[colorTableLength]; Parallel.For( 0, pixelCount, Bootstrapper.Instance.ParallelOptions, i => { int offset = i * 3; byte[] color = palette[i].ToBytes(); // Expected format r->g->b colorTable[offset] = color[0]; colorTable[offset + 1] = color[1]; colorTable[offset + 2] = color[2]; }); this.WriteChunk(stream, PngChunkTypes.Palette, colorTable); // Write the transparency data if (quantized.TransparentIndex > -1) { this.WriteChunk(stream, PngChunkTypes.PaletteAlpha, new[] { (byte)quantized.TransparentIndex }); } return(quantized); }
public void Encode(ImageBase image, Stream stream) { if (image == null || stream == null) { throw new ArgumentNullException(); } stream.Write(new byte[] { 0x89, // Set the high bit. 0x50, // P 0x4E, // N 0x47, // G 0x0D, // Line ending CRLF 0x0A, // Line ending CRLF 0x1A, // EOF 0x0A // LF }, 0, 8); int quality = Quality > 0 ? Quality : image.Quality; Quality = quality > 0 ? NumUtils.Clamp(quality, 1, int.MaxValue) : int.MaxValue; bitDepth = Quality <= 256 ? (byte)NumUtils.Clamp(GifEncoderCore.GetBitsNeededForColorDepth(Quality), 1, 8) : (byte)8; if (bitDepth == 3) { bitDepth = 4; } else if (bitDepth >= 5 || bitDepth <= 7) { bitDepth = 8; } PngHeader header = new PngHeader { Width = image.Width, Height = image.Height, ColorType = (byte)(Quality <= 256 ? 3 : 6), BitDepth = bitDepth, FilterMethod = 0, // None CompressionMethod = 0, InterlaceMethod = 0 }; WriteHeaderChunk(stream, header); QuantizedImage quantized = WritePaletteChunk(stream, header, image); WritePhysicalChunk(stream, image); WriteGammaChunk(stream); using (IPixelAccessor pixels = image.Lock()){ WriteDataChunks(stream, pixels, quantized); } WriteEndChunk(stream); stream.Flush(); }
private int GetTransparentIndex <TPixel>(QuantizedImage <TPixel> quantized) where TPixel : struct, IPixel <TPixel> { // Transparent pixels are much more likely to be found at the end of a palette int index = -1; var trans = default(Rgba32); for (int i = quantized.Palette.Length - 1; i >= 0; i--) { quantized.Palette[i].ToRgba32(ref trans); if (trans.Equals(default(Rgba32))) { index = i; } } return(index); }
/// <summary> /// Loads the animation. /// </summary> /// <param name="animation">The animation.</param> /// <param name="quantizedImage">The quantized image.</param> private void LoadAnimation(Animation animation, QuantizedImage quantizedImage) { var TempImage = animation[0]; var TransparencyIndex = quantizedImage.TransparentIndex; Header = new FileHeader(); ScreenDescriptor = new LogicalScreenDescriptor(TempImage, TransparencyIndex, BitDepth); Frames.Add(new Frame(TempImage, quantizedImage, BitDepth, animation.Delay)); if (animation.Count > 1) { AppExtension = new ApplicationExtension(animation.RepeatCount, animation.Count); for (int x = 1; x < animation.Count; ++x) { quantizedImage = Quantizer.Quantize(animation[x], Quality); TempImage = animation[x]; Frames.Add(new Frame(TempImage, quantizedImage, BitDepth, animation.Delay)); } } }
public void OctreeQuantizerYieldsCorrectTransparentPixel <TPixel>(TestImageProvider <TPixel> provider, bool dither) where TPixel : struct, IPixel <TPixel> { using (Image <TPixel> image = provider.GetImage()) { Assert.True(image[0, 0].Equals(default(TPixel))); IQuantizer <TPixel> quantizer = new OctreeQuantizer <TPixel> { Dither = dither }; foreach (ImageFrame <TPixel> frame in image.Frames) { QuantizedImage <TPixel> quantized = quantizer.Quantize(frame, 256); int index = this.GetTransparentIndex(quantized); Assert.Equal(index, quantized.Pixels[0]); } } }
/// <summary> /// Returns the index of the most transparent color in the palette. /// </summary> /// <param name="quantized"> /// The quantized. /// </param> /// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <returns> /// The <see cref="int"/>. /// </returns> private static int GetTransparentIndex <TColor, TPacked>(QuantizedImage <TColor, TPacked> quantized) where TColor : struct, IPackedPixel <TPacked> where TPacked : struct { // Find the lowest alpha value and make it the transparent index. int index = 255; float alpha = 1; for (int i = 0; i < quantized.Palette.Length; i++) { float a = quantized.Palette[i].ToVector4().W; if (a < alpha) { alpha = a; index = i; } } return(index); }
/// <summary> /// Initializes a new instance of the <see cref="ColorTable"/> class. /// </summary> /// <param name="image">The image.</param> /// <param name="BitDepth">The bit depth.</param> public ColorTable(QuantizedImage image, int BitDepth) { Bgra[] palette = image.Palette; int pixelCount = palette.Length; // Get max colors for bit depth. int colorTableLength = (int)Math.Pow(2, BitDepth) * 3; byte[] colorTable = new byte[colorTableLength]; Parallel.For(0, pixelCount, i => { int offset = i * 3; Bgra color = palette[i]; colorTable[offset] = color.Red; colorTable[offset + 1] = color.Green; colorTable[offset + 2] = color.Blue; }); Data = colorTable; }