Browse Source

Use core gif encoder.

Former-commit-id: 548bcc2f52fff34c699dae622988797facbc4bad
Former-commit-id: 3533dab899bff33bfa7d4485b51a6bba1f3154a7
Former-commit-id: de8e3008aef00c8685117416a54d919ad892931f
af/merge-core
James South 10 years ago
parent
commit
96e108c798
  1. 13
      src/ImageProcessorCore/Common/Helpers/ImageMaths.cs
  2. 306
      src/ImageProcessorCore/Formats/Gif/GifEncoder.cs
  3. 343
      src/ImageProcessorCore/Formats/Gif/GifEncoderCore.cs
  4. 25
      src/ImageProcessorCore/Formats/Png/PngEncoderCore.cs

13
src/ImageProcessorCore/Common/Helpers/ImageMaths.cs

@ -19,6 +19,19 @@ namespace ImageProcessorCore
// ReSharper disable once InconsistentNaming
public const float PI = 3.1415926535897931f;
/// <summary>
/// Returns how many bits are required to store the specified number of colors.
/// Performs a Log2() on the value.
/// </summary>
/// <param name="colors">The number of colors.</param>
/// <returns>
/// The <see cref="int"/>
/// </returns>
public static int GetBitsNeededForColorDepth(int colors)
{
return (int)Math.Ceiling(Math.Log(colors, 2));
}
/// <summary>
/// Implementation of 1D Gaussian G(x) function
/// </summary>

306
src/ImageProcessorCore/Formats/Gif/GifEncoder.cs

@ -7,8 +7,6 @@ namespace ImageProcessorCore.Formats
{
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using ImageProcessorCore.Quantizers;
@ -49,308 +47,16 @@ namespace ImageProcessorCore.Formats
}
/// <inheritdoc/>
public void Encode(ImageBase imageBase, Stream stream)
public void Encode(ImageBase image, Stream stream)
{
Guard.NotNull(imageBase, nameof(imageBase));
Guard.NotNull(stream, nameof(stream));
Image image = (Image)imageBase;
if (this.Quantizer == null)
{
this.Quantizer = new OctreeQuantizer { Threshold = this.Threshold };
}
// Write the header.
// File Header signature and version.
this.WriteString(stream, GifConstants.FileType);
this.WriteString(stream, GifConstants.FileVersion);
// Calculate the quality.
int quality = this.Quality > 0 ? this.Quality : imageBase.Quality;
quality = quality > 0 ? quality.Clamp(1, 256) : 256;
// Get the number of bits.
int bitDepth = this.GetBitsNeededForColorDepth(quality);
// Write the LSD and check to see if we need a global color table.
// Always true just now.
this.WriteGlobalLogicalScreenDescriptor(image, stream, bitDepth);
QuantizedImage quantized = this.WriteColorTable(imageBase, stream, quality, bitDepth);
this.WriteGraphicalControlExtension(imageBase, stream, quantized.TransparentIndex);
this.WriteImageDescriptor(quantized, quality, stream);
if (image.Frames.Any())
{
this.WriteApplicationExtension(stream, image.RepeatCount, image.Frames.Count);
foreach (ImageFrame frame in image.Frames)
{
this.WriteGraphicalControlExtension(frame, stream, quantized.TransparentIndex);
this.WriteFrameImageDescriptor(frame, stream);
}
}
// TODO: Write Comments extension etc
this.WriteByte(stream, GifConstants.EndIntroducer);
}
/// <summary>
/// Writes the logical screen descriptor to the stream.
/// </summary>
/// <param name="image">The image to encode.</param>
/// <param name="stream">The stream to write to.</param>
/// <param name="bitDepth">The bit depth.</param>
/// <returns>The <see cref="GifLogicalScreenDescriptor"/></returns>
private bool WriteGlobalLogicalScreenDescriptor(Image image, Stream stream, int bitDepth)
{
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
GifLogicalScreenDescriptor descriptor = new GifLogicalScreenDescriptor
{
Width = (short)image.Width,
Height = (short)image.Height,
GlobalColorTableFlag = true,
GlobalColorTableSize = bitDepth
};
this.WriteShort(stream, descriptor.Width);
this.WriteShort(stream, descriptor.Height);
int packed = 0x80 | // 1 : Global color table flag = 1 (GCT used)
bitDepth - 1 | // 2-4 : color resolution
0x00 | // 5 : GCT sort flag = 0
bitDepth - 1; // 6-8 : GCT size TODO: Check this.
this.WriteByte(stream, packed);
this.WriteByte(stream, descriptor.BackgroundColorIndex); // Background Color Index
this.WriteByte(stream, descriptor.PixelAspectRatio); // Pixel aspect ratio. Assume 1:1
return descriptor.GlobalColorTableFlag;
}
/// <summary>
/// Writes the color table to the stream.
/// </summary>
/// <param name="image">The <see cref="ImageBase"/> to encode.</param>
/// <param name="stream">The stream to write to.</param>
/// <param name="quality">The quality (number of colors) to encode the image to.</param>
/// <param name="bitDepth">The bit depth.</param>
/// <returns>The <see cref="QuantizedImage"/></returns>
private QuantizedImage WriteColorTable(ImageBase image, Stream stream, int quality, int bitDepth)
{
// Quantize the image returning a palette.
QuantizedImage quantizedImage = this.Quantizer.Quantize(image, quality);
// Grab the palette and write it to the stream.
Bgra32[] palette = quantizedImage.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;
Bgra32 color = palette[i];
colorTable[offset] = color.R;
colorTable[offset + 1] = color.G;
colorTable[offset + 2] = color.B;
});
stream.Write(colorTable, 0, colorTableLength);
return quantizedImage;
}
/// <summary>
/// Writes the graphics control extension to the stream.
/// </summary>
/// <param name="image">The <see cref="ImageBase"/> to encode.</param>
/// <param name="stream">The stream to write to.</param>
/// <param name="transparencyIndex">The index of the color in the color palette to make transparent.</param>
private void WriteGraphicalControlExtension(ImageBase image, Stream stream, int transparencyIndex)
{
// TODO: Check transparency logic.
bool hasTransparent = transparencyIndex > -1;
DisposalMethod disposalMethod = hasTransparent
? DisposalMethod.RestoreToBackground
: DisposalMethod.Unspecified;
GifGraphicsControlExtension extension = new GifGraphicsControlExtension()
GifEncoderCore encoder = new GifEncoderCore
{
DisposalMethod = disposalMethod,
TransparencyFlag = hasTransparent,
TransparencyIndex = transparencyIndex,
DelayTime = image.FrameDelay
Quality = this.Quality,
Quantizer = this.Quantizer,
Threshold = this.Threshold
};
this.WriteByte(stream, GifConstants.ExtensionIntroducer);
this.WriteByte(stream, GifConstants.GraphicControlLabel);
this.WriteByte(stream, 4); // Size
int packed = 0 | // 1-3 : Reserved
(int)extension.DisposalMethod << 2 | // 4-6 : Disposal
0 | // 7 : User input - 0 = none
(extension.TransparencyFlag ? 1 : 0); // 8: Has transparent.
this.WriteByte(stream, packed);
this.WriteShort(stream, extension.DelayTime);
this.WriteByte(stream, extension.TransparencyIndex == -1 ? 255 : extension.TransparencyIndex);
this.WriteByte(stream, GifConstants.Terminator);
}
/// <summary>
/// Writes the application exstension to the stream.
/// </summary>
/// <param name="stream">The stream to write to.</param>
/// <param name="repeatCount">The animated image repeat count.</param>
/// <param name="frames">Th number of image frames.</param>
private void WriteApplicationExtension(Stream stream, ushort repeatCount, int frames)
{
// Application Extension Header
if (repeatCount != 1 && frames > 0)
{
// 0 means loop indefinitely. count is set as play n + 1 times.
// TODO: Check this as the correct value might be pulled from the decoder.
repeatCount = (ushort)Math.Max(0, repeatCount - 1);
this.WriteByte(stream, GifConstants.ExtensionIntroducer); // NETSCAPE2.0
this.WriteByte(stream, GifConstants.ApplicationExtensionLabel);
this.WriteByte(stream, GifConstants.ApplicationBlockSize);
this.WriteString(stream, GifConstants.ApplicationIdentification);
this.WriteByte(stream, 3); // Application block length
this.WriteByte(stream, 1); // Data sub-block index (always 1)
this.WriteShort(stream, repeatCount); // Repeat count for images.
this.WriteByte(stream, GifConstants.Terminator); // Terminator
}
}
/// <summary>
/// Writes the image descriptor to the stream.
/// </summary>
/// <param name="image">The <see cref="QuantizedImage"/> containing indexed pixels.</param>
/// <param name="quality">The quality (number of colors) to encode the image to.</param>
/// <param name="stream">The stream to write to.</param>
private void WriteImageDescriptor(QuantizedImage image, int quality, Stream stream)
{
this.WriteByte(stream, GifConstants.ImageDescriptorLabel); // 2c
// TODO: Can we capture this?
this.WriteShort(stream, 0); // Left position
this.WriteShort(stream, 0); // Top position
this.WriteShort(stream, image.Width);
this.WriteShort(stream, image.Height);
// Calculate the quality.
int bitDepth = this.GetBitsNeededForColorDepth(quality);
// No LCT use GCT.
this.WriteByte(stream, 0);
// Write the image data.
this.WriteImageData(image, stream, bitDepth);
}
/// <summary>
/// Writes the image descriptor to the stream.
/// </summary>
/// <param name="image">The <see cref="ImageBase"/> to be encoded.</param>
/// <param name="stream">The stream to write to.</param>
private void WriteFrameImageDescriptor(ImageBase image, Stream stream)
{
this.WriteByte(stream, GifConstants.ImageDescriptorLabel); // 2c
// TODO: Can we capture this?
this.WriteShort(stream, 0); // Left position
this.WriteShort(stream, 0); // Top position
this.WriteShort(stream, image.Width);
this.WriteShort(stream, image.Height);
// Calculate the quality.
int quality = this.Quality > 0 ? this.Quality : image.Quality;
quality = quality > 0 ? quality.Clamp(1, 256) : 256;
int bitDepth = this.GetBitsNeededForColorDepth(quality);
int packed = 0x80 | // 1: Local color table flag = 1 (LCT used)
0x00 | // 2: Interlace flag 0
0x00 | // 3: Sort flag 0
0 | // 4-5: Reserved
bitDepth - 1;
this.WriteByte(stream, packed);
// Now immediately follow with the color table.
QuantizedImage quantized = this.WriteColorTable(image, stream, quality, bitDepth);
this.WriteImageData(quantized, stream, bitDepth);
}
/// <summary>
/// Writes the image pixel data to the stream.
/// </summary>
/// <param name="image">The <see cref="QuantizedImage"/> containing indexed pixels.</param>
/// <param name="stream">The stream to write to.</param>
/// <param name="bitDepth">The bit depth of the image.</param>
private void WriteImageData(QuantizedImage image, Stream stream, int bitDepth)
{
byte[] indexedPixels = image.Pixels;
LzwEncoder encoder = new LzwEncoder(indexedPixels, (byte)bitDepth);
encoder.Encode(stream);
this.WriteByte(stream, GifConstants.Terminator);
}
/// <summary>
/// Writes a short to the given stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="value">The value to write.</param>
private void WriteShort(Stream stream, int value)
{
// Leave only one significant byte.
stream.WriteByte(Convert.ToByte(value & 0xff));
stream.WriteByte(Convert.ToByte((value >> 8) & 0xff));
}
/// <summary>
/// Writes a byte to the given stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="value">The value to write.</param>
private void WriteByte(Stream stream, int value)
{
stream.WriteByte(Convert.ToByte(value));
}
/// <summary>
/// Writes a string to the given stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="value">The value to write.</param>
private void WriteString(Stream stream, string value)
{
char[] chars = value.ToCharArray();
foreach (char c in chars)
{
stream.WriteByte((byte)c);
}
}
/// <summary>
/// Returns how many bits are required to store the specified number of colors.
/// Performs a Log2() on the value.
/// </summary>
/// <param name="colors">The number of colors.</param>
/// <returns>
/// The <see cref="int"/>
/// </returns>
private int GetBitsNeededForColorDepth(int colors)
{
return (int)Math.Ceiling(Math.Log(colors, 2));
encoder.Encode(image, stream);
}
}
}

343
src/ImageProcessorCore/Formats/Gif/GifEncoderCore.cs

@ -0,0 +1,343 @@
// <copyright file="GifEncoderCore.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Formats
{
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using ImageProcessorCore.Quantizers;
/// <summary>
/// Performs the gif encoding operation.
/// </summary>
internal sealed class GifEncoderCore
{
/// <summary>
/// The number of bits requires to store the image palette.
/// </summary>
private int bitDepth;
/// <summary>
/// Gets or sets the quality of output for images.
/// </summary>
/// <remarks>For gifs the value ranges from 1 to 256.</remarks>
public int Quality { get; set; }
/// <summary>
/// Gets or sets the transparency threshold.
/// </summary>
public byte Threshold { get; set; } = 128;
/// <summary>
/// The quantizer for reducing the color count.
/// </summary>
public IQuantizer Quantizer { get; set; }
/// <summary>
/// Encodes the image to the specified stream from the <see cref="ImageBase"/>.
/// </summary>
/// <param name="imageBase">The <see cref="ImageBase"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
public void Encode(ImageBase imageBase, Stream stream)
{
Guard.NotNull(imageBase, nameof(imageBase));
Guard.NotNull(stream, nameof(stream));
Image image = (Image)imageBase;
if (this.Quantizer == null)
{
this.Quantizer = new OctreeQuantizer { Threshold = this.Threshold };
}
// Write the header.
// File Header signature and version.
this.WriteString(stream, GifConstants.FileType);
this.WriteString(stream, GifConstants.FileVersion);
// Ensure that quality can be set but has a fallback.
int quality = this.Quality > 0 ? this.Quality : imageBase.Quality;
quality = quality > 0 ? quality.Clamp(1, 256) : 256;
// Get the number of bits.
this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quality);
// Write the LSD and check to see if we need a global color table.
// Always true just now.
this.WriteGlobalLogicalScreenDescriptor(image, stream);
QuantizedImage quantized = this.WriteColorTable(imageBase, stream, quality);
this.WriteGraphicalControlExtension(imageBase, stream, quantized.TransparentIndex);
this.WriteImageDescriptor(quantized, quality, stream);
if (image.Frames.Any())
{
this.WriteApplicationExtension(stream, image.RepeatCount, image.Frames.Count);
foreach (ImageFrame frame in image.Frames)
{
this.WriteGraphicalControlExtension(frame, stream, quantized.TransparentIndex);
this.WriteFrameImageDescriptor(frame, stream);
}
}
// TODO: Write Comments extension etc
this.WriteByte(stream, GifConstants.EndIntroducer);
}
/// <summary>
/// Writes the logical screen descriptor to the stream.
/// </summary>
/// <param name="image">The image to encode.</param>
/// <param name="stream">The stream to write to.</param>
/// <returns>The <see cref="GifLogicalScreenDescriptor"/></returns>
private bool WriteGlobalLogicalScreenDescriptor(Image image, Stream stream)
{
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
GifLogicalScreenDescriptor descriptor = new GifLogicalScreenDescriptor
{
Width = (short)image.Width,
Height = (short)image.Height,
GlobalColorTableFlag = true,
GlobalColorTableSize = this.bitDepth
};
this.WriteShort(stream, descriptor.Width);
this.WriteShort(stream, descriptor.Height);
int packed = 0x80 | // 1 : Global color table flag = 1 (GCT used)
this.bitDepth - 1 | // 2-4 : color resolution
0x00 | // 5 : GCT sort flag = 0
this.bitDepth - 1; // 6-8 : GCT size TODO: Check this.
this.WriteByte(stream, packed);
this.WriteByte(stream, descriptor.BackgroundColorIndex); // Background Color Index
this.WriteByte(stream, descriptor.PixelAspectRatio); // Pixel aspect ratio. Assume 1:1
return descriptor.GlobalColorTableFlag;
}
/// <summary>
/// Writes the color table to the stream.
/// </summary>
/// <param name="image">The <see cref="ImageBase"/> to encode.</param>
/// <param name="stream">The stream to write to.</param>
/// <param name="quality">The quality (number of colors) to encode the image to.</param>
/// <returns>The <see cref="QuantizedImage"/></returns>
private QuantizedImage WriteColorTable(ImageBase image, Stream stream, int quality)
{
// Quantize the image returning a palette.
QuantizedImage quantizedImage = this.Quantizer.Quantize(image, quality);
// Grab the palette and write it to the stream.
Bgra32[] palette = quantizedImage.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;
Bgra32 color = palette[i];
colorTable[offset] = color.R;
colorTable[offset + 1] = color.G;
colorTable[offset + 2] = color.B;
});
stream.Write(colorTable, 0, colorTableLength);
return quantizedImage;
}
/// <summary>
/// Writes the graphics control extension to the stream.
/// </summary>
/// <param name="image">The <see cref="ImageBase"/> to encode.</param>
/// <param name="stream">The stream to write to.</param>
/// <param name="transparencyIndex">The index of the color in the color palette to make transparent.</param>
private void WriteGraphicalControlExtension(ImageBase image, Stream stream, int transparencyIndex)
{
// TODO: Check transparency logic.
bool hasTransparent = transparencyIndex > -1;
DisposalMethod disposalMethod = hasTransparent
? DisposalMethod.RestoreToBackground
: DisposalMethod.Unspecified;
GifGraphicsControlExtension extension = new GifGraphicsControlExtension()
{
DisposalMethod = disposalMethod,
TransparencyFlag = hasTransparent,
TransparencyIndex = transparencyIndex,
DelayTime = image.FrameDelay
};
this.WriteByte(stream, GifConstants.ExtensionIntroducer);
this.WriteByte(stream, GifConstants.GraphicControlLabel);
this.WriteByte(stream, 4); // Size
int packed = 0 | // 1-3 : Reserved
(int)extension.DisposalMethod << 2 | // 4-6 : Disposal
0 | // 7 : User input - 0 = none
(extension.TransparencyFlag ? 1 : 0); // 8: Has transparent.
this.WriteByte(stream, packed);
this.WriteShort(stream, extension.DelayTime);
this.WriteByte(stream, extension.TransparencyIndex == -1 ? 255 : extension.TransparencyIndex);
this.WriteByte(stream, GifConstants.Terminator);
}
/// <summary>
/// Writes the application exstension to the stream.
/// </summary>
/// <param name="stream">The stream to write to.</param>
/// <param name="repeatCount">The animated image repeat count.</param>
/// <param name="frames">Th number of image frames.</param>
private void WriteApplicationExtension(Stream stream, ushort repeatCount, int frames)
{
// Application Extension Header
if (repeatCount != 1 && frames > 0)
{
// 0 means loop indefinitely. count is set as play n + 1 times.
// TODO: Check this as the correct value might be pulled from the decoder.
repeatCount = (ushort)Math.Max(0, repeatCount - 1);
this.WriteByte(stream, GifConstants.ExtensionIntroducer); // NETSCAPE2.0
this.WriteByte(stream, GifConstants.ApplicationExtensionLabel);
this.WriteByte(stream, GifConstants.ApplicationBlockSize);
this.WriteString(stream, GifConstants.ApplicationIdentification);
this.WriteByte(stream, 3); // Application block length
this.WriteByte(stream, 1); // Data sub-block index (always 1)
this.WriteShort(stream, repeatCount); // Repeat count for images.
this.WriteByte(stream, GifConstants.Terminator); // Terminator
}
}
/// <summary>
/// Writes the image descriptor to the stream.
/// </summary>
/// <param name="image">The <see cref="QuantizedImage"/> containing indexed pixels.</param>
/// <param name="quality">The quality (number of colors) to encode the image to.</param>
/// <param name="stream">The stream to write to.</param>
private void WriteImageDescriptor(QuantizedImage image, int quality, Stream stream)
{
this.WriteByte(stream, GifConstants.ImageDescriptorLabel); // 2c
// TODO: Can we capture this?
this.WriteShort(stream, 0); // Left position
this.WriteShort(stream, 0); // Top position
this.WriteShort(stream, image.Width);
this.WriteShort(stream, image.Height);
// No LCT use GCT.
this.WriteByte(stream, 0);
// Write the image data.
this.WriteImageData(image, stream);
}
/// <summary>
/// Writes the image descriptor to the stream.
/// </summary>
/// <param name="image">The <see cref="ImageBase"/> to be encoded.</param>
/// <param name="stream">The stream to write to.</param>
private void WriteFrameImageDescriptor(ImageBase image, Stream stream)
{
this.WriteByte(stream, GifConstants.ImageDescriptorLabel); // 2c
// TODO: Can we capture this?
this.WriteShort(stream, 0); // Left position
this.WriteShort(stream, 0); // Top position
this.WriteShort(stream, image.Width);
this.WriteShort(stream, image.Height);
// Calculate the quality.
int quality = this.Quality > 0 ? this.Quality : image.Quality;
quality = quality > 0 ? quality.Clamp(1, 256) : 256;
int packed = 0x80 | // 1: Local color table flag = 1 (LCT used)
0x00 | // 2: Interlace flag 0
0x00 | // 3: Sort flag 0
0 | // 4-5: Reserved
this.bitDepth - 1;
this.WriteByte(stream, packed);
// Now immediately follow with the color table.
QuantizedImage quantized = this.WriteColorTable(image, stream, quality);
this.WriteImageData(quantized, stream);
}
/// <summary>
/// Writes the image pixel data to the stream.
/// </summary>
/// <param name="image">The <see cref="QuantizedImage"/> containing indexed pixels.</param>
/// <param name="stream">The stream to write to.</param>
private void WriteImageData(QuantizedImage image, Stream stream)
{
byte[] indexedPixels = image.Pixels;
LzwEncoder encoder = new LzwEncoder(indexedPixels, (byte)this.bitDepth);
encoder.Encode(stream);
this.WriteByte(stream, GifConstants.Terminator);
}
/// <summary>
/// Writes a short to the given stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="value">The value to write.</param>
private void WriteShort(Stream stream, int value)
{
// Leave only one significant byte.
stream.WriteByte(Convert.ToByte(value & 0xff));
stream.WriteByte(Convert.ToByte((value >> 8) & 0xff));
}
/// <summary>
/// Writes a byte to the given stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="value">The value to write.</param>
private void WriteByte(Stream stream, int value)
{
stream.WriteByte(Convert.ToByte(value));
}
/// <summary>
/// Writes a string to the given stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="value">The value to write.</param>
private void WriteString(Stream stream, string value)
{
char[] chars = value.ToCharArray();
foreach (char c in chars)
{
stream.WriteByte((byte)c);
}
}
/// <summary>
/// Returns how many bits are required to store the specified number of colors.
/// Performs a Log2() on the value.
/// </summary>
/// <param name="colors">The number of colors.</param>
/// <returns>
/// The <see cref="int"/>
/// </returns>
private int GetBitsNeededForColorDepth(int colors)
{
return (int)Math.Ceiling(Math.Log(colors, 2));
}
}
}

25
src/ImageProcessorCore/Formats/Png/PngEncoderCore.cs

@ -15,7 +15,7 @@ namespace ImageProcessorCore.Formats
/// Performs the png encoding operation.
/// TODO: Perf. There's lots of array parsing going on here. This should be unmanaged.
/// </summary>
internal class PngEncoderCore
internal sealed class PngEncoderCore
{
/// <summary>
/// The maximum block size, defaults at 64k for uncompressed blocks.
@ -89,14 +89,12 @@ namespace ImageProcessorCore.Formats
0,
8);
if (image.Quality > 0)
{
this.Quality = image.Quality.Clamp(1, int.MaxValue);
}
// 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, int.MaxValue) : int.MaxValue;
this.bitDepth = this.Quality <= 256
? (byte)(this.GetBitsNeededForColorDepth(this.Quality).Clamp(1, 8))
? (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
@ -463,18 +461,5 @@ namespace ImageProcessorCore.Formats
WriteInteger(stream, (uint)crc32.Value);
}
/// <summary>
/// Returns how many bits are required to store the specified number of colors.
/// Performs a Log2() on the value.
/// </summary>
/// <param name="colors">The number of colors.</param>
/// <returns>
/// The <see cref="int"/>
/// </returns>
private int GetBitsNeededForColorDepth(int colors)
{
return (int)Math.Ceiling(Math.Log(colors, 2));
}
}
}

Loading…
Cancel
Save