Browse Source

Merge pull request #701 from SixLabors/js/public-formats

Make formats public + minor cleanup.
pull/705/head
James Jackson-South 8 years ago
committed by GitHub
parent
commit
e185e91663
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 22
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  2. 2
      src/ImageSharp/Formats/Bmp/BmpFormat.cs
  3. 5
      src/ImageSharp/Formats/Gif/GifEncoder.cs
  4. 59
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  5. 2
      src/ImageSharp/Formats/Gif/GifFormat.cs
  6. 5
      src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs
  7. 29
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
  8. 2
      src/ImageSharp/Formats/Jpeg/JpegFormat.cs
  9. 6
      src/ImageSharp/Formats/Png/PngEncoder.cs
  10. 26
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  11. 2
      src/ImageSharp/Formats/Png/PngFormat.cs
  12. 23
      src/ImageSharp/ImageFrame{TPixel}.cs
  13. 16
      tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs

22
src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

@ -48,7 +48,8 @@ namespace SixLabors.ImageSharp.Formats.Bmp
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
BmpMetaData bmpMetaData = image.MetaData.GetFormatMetaData(BmpFormat.Instance);
ImageMetaData metaData = image.MetaData;
BmpMetaData bmpMetaData = metaData.GetFormatMetaData(BmpFormat.Instance);
this.bitsPerPixel = this.bitsPerPixel ?? bmpMetaData.BitsPerPixel;
short bpp = (short)this.bitsPerPixel;
@ -56,31 +57,30 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.padding = bytesPerLine - (int)(image.Width * (bpp / 8F));
// Set Resolution.
ImageMetaData meta = image.MetaData;
int hResolution = 0;
int vResolution = 0;
if (meta.ResolutionUnits != PixelResolutionUnit.AspectRatio)
if (metaData.ResolutionUnits != PixelResolutionUnit.AspectRatio)
{
if (meta.HorizontalResolution > 0 && meta.VerticalResolution > 0)
if (metaData.HorizontalResolution > 0 && metaData.VerticalResolution > 0)
{
switch (meta.ResolutionUnits)
switch (metaData.ResolutionUnits)
{
case PixelResolutionUnit.PixelsPerInch:
hResolution = (int)Math.Round(UnitConverter.InchToMeter(meta.HorizontalResolution));
vResolution = (int)Math.Round(UnitConverter.InchToMeter(meta.VerticalResolution));
hResolution = (int)Math.Round(UnitConverter.InchToMeter(metaData.HorizontalResolution));
vResolution = (int)Math.Round(UnitConverter.InchToMeter(metaData.VerticalResolution));
break;
case PixelResolutionUnit.PixelsPerCentimeter:
hResolution = (int)Math.Round(UnitConverter.CmToMeter(meta.HorizontalResolution));
vResolution = (int)Math.Round(UnitConverter.CmToMeter(meta.VerticalResolution));
hResolution = (int)Math.Round(UnitConverter.CmToMeter(metaData.HorizontalResolution));
vResolution = (int)Math.Round(UnitConverter.CmToMeter(metaData.VerticalResolution));
break;
case PixelResolutionUnit.PixelsPerMeter:
hResolution = (int)Math.Round(meta.HorizontalResolution);
vResolution = (int)Math.Round(meta.VerticalResolution);
hResolution = (int)Math.Round(metaData.HorizontalResolution);
vResolution = (int)Math.Round(metaData.VerticalResolution);
break;
}

2
src/ImageSharp/Formats/Bmp/BmpFormat.cs

@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the bmp format.
/// </summary>
internal sealed class BmpFormat : IImageFormat<BmpMetaData>
public sealed class BmpFormat : IImageFormat<BmpMetaData>
{
private BmpFormat()
{

5
src/ImageSharp/Formats/Gif/GifEncoder.cs

@ -14,11 +14,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
public sealed class GifEncoder : IImageEncoder, IGifEncoderOptions
{
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being encoded.
/// </summary>
public bool IgnoreMetadata { get; set; } = false;
/// <summary>
/// Gets or sets the encoding that should be used when writing comments.
/// </summary>

59
src/ImageSharp/Formats/Gif/GifEncoderCore.cs

@ -45,11 +45,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
private GifColorTableMode? colorTableMode;
/// <summary>
/// A flag indicating whether to ingore the metadata when writing the image.
/// </summary>
private readonly bool ignoreMetadata;
/// <summary>
/// The number of bits requires to store the color palette.
/// </summary>
@ -70,7 +65,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.memoryAllocator = memoryAllocator;
this.textEncoding = options.TextEncoding ?? GifConstants.DefaultEncoding;
this.quantizer = options.Quantizer;
this.ignoreMetadata = options.IgnoreMetadata;
this.colorTableMode = options.ColorTableMode;
}
@ -86,7 +80,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
this.gifMetaData = image.MetaData.GetFormatMetaData(GifFormat.Instance);
ImageMetaData metaData = image.MetaData;
this.gifMetaData = metaData.GetFormatMetaData(GifFormat.Instance);
this.colorTableMode = this.colorTableMode ?? this.gifMetaData.ColorTableMode;
bool useGlobalTable = this.colorTableMode.Equals(GifColorTableMode.Global);
@ -102,7 +97,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Write the LSD.
int index = this.GetTransparentIndex(quantized);
this.WriteLogicalScreenDescriptor(image, index, useGlobalTable, stream);
this.WriteLogicalScreenDescriptor(metaData, image.Width, image.Height, index, useGlobalTable, stream);
if (useGlobalTable)
{
@ -110,7 +105,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
// Write the comments.
this.WriteComments(image.MetaData, stream);
this.WriteComments(metaData, stream);
// Write application extension to allow additional frames.
if (image.Frames.Count > 1)
@ -143,7 +138,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
for (int i = 0; i < image.Frames.Count; i++)
{
ImageFrame<TPixel> frame = image.Frames[i];
GifFrameMetaData frameMetaData = frame.MetaData.GetFormatMetaData(GifFormat.Instance);
ImageFrameMetaData metaData = frame.MetaData;
GifFrameMetaData frameMetaData = metaData.GetFormatMetaData(GifFormat.Instance);
this.WriteGraphicalControlExtension(frameMetaData, transparencyIndex, stream);
this.WriteImageDescriptor(frame, false, stream);
@ -169,15 +165,16 @@ namespace SixLabors.ImageSharp.Formats.Gif
GifFrameMetaData previousMeta = null;
foreach (ImageFrame<TPixel> frame in image.Frames)
{
GifFrameMetaData meta = frame.MetaData.GetFormatMetaData(GifFormat.Instance);
ImageFrameMetaData metaData = frame.MetaData;
GifFrameMetaData frameMetaData = metaData.GetFormatMetaData(GifFormat.Instance);
if (quantized is null)
{
// Allow each frame to be encoded at whatever color depth the frame designates if set.
if (previousFrame != null
&& previousMeta.ColorTableLength != meta.ColorTableLength
&& meta.ColorTableLength > 0)
&& previousMeta.ColorTableLength != frameMetaData.ColorTableLength
&& frameMetaData.ColorTableLength > 0)
{
quantized = this.quantizer.CreateFrameQuantizer<TPixel>(meta.ColorTableLength).QuantizeFrame(frame);
quantized = this.quantizer.CreateFrameQuantizer<TPixel>(frameMetaData.ColorTableLength).QuantizeFrame(frame);
}
else
{
@ -186,7 +183,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8);
this.WriteGraphicalControlExtension(meta, this.GetTransparentIndex(quantized), stream);
this.WriteGraphicalControlExtension(frameMetaData, this.GetTransparentIndex(quantized), stream);
this.WriteImageDescriptor(frame, true, stream);
this.WriteColorTable(quantized, stream);
this.WriteImageData(quantized, stream);
@ -194,7 +191,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
quantized?.Dispose();
quantized = null; // So next frame can regenerate it
previousFrame = frame;
previousMeta = meta;
previousMeta = frameMetaData;
}
}
@ -239,13 +236,19 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Writes the logical screen descriptor to the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The image to encode.</param>
/// <param name="metaData">The image metadata.</param>
/// <param name="width">The image width.</param>
/// <param name="height">The image height.</param>
/// <param name="transparencyIndex">The transparency index to set the default background index to.</param>
/// <param name="useGlobalTable">Whether to use a global or local color table.</param>
/// <param name="stream">The stream to write to.</param>
private void WriteLogicalScreenDescriptor<TPixel>(Image<TPixel> image, int transparencyIndex, bool useGlobalTable, Stream stream)
where TPixel : struct, IPixel<TPixel>
private void WriteLogicalScreenDescriptor(
ImageMetaData metaData,
int width,
int height,
int transparencyIndex,
bool useGlobalTable,
Stream stream)
{
byte packedValue = GifLogicalScreenDescriptor.GetPackedValue(useGlobalTable, this.bitDepth - 1, false, this.bitDepth - 1);
@ -258,13 +261,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
// 1..255 - Value used in the computation.
//
// Aspect Ratio = (Pixel Aspect Ratio + 15) / 64
ImageMetaData meta = image.MetaData;
byte ratio = 0;
if (meta.ResolutionUnits == PixelResolutionUnit.AspectRatio)
if (metaData.ResolutionUnits == PixelResolutionUnit.AspectRatio)
{
double hr = meta.HorizontalResolution;
double vr = meta.VerticalResolution;
double hr = metaData.HorizontalResolution;
double vr = metaData.VerticalResolution;
if (hr != vr)
{
if (hr > vr)
@ -279,8 +281,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
var descriptor = new GifLogicalScreenDescriptor(
width: (ushort)image.Width,
height: (ushort)image.Height,
width: (ushort)width,
height: (ushort)height,
packed: packedValue,
backgroundColorIndex: unchecked((byte)transparencyIndex),
ratio);
@ -312,11 +314,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <param name="stream">The stream to write to.</param>
private void WriteComments(ImageMetaData metadata, Stream stream)
{
if (this.ignoreMetadata)
{
return;
}
if (!metadata.TryGetProperty(GifConstants.Comments, out ImageProperty property) || string.IsNullOrEmpty(property.Value))
{
return;

2
src/ImageSharp/Formats/Gif/GifFormat.cs

@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the gif format.
/// </summary>
internal sealed class GifFormat : IImageFormat<GifMetaData, GifFrameMetaData>
public sealed class GifFormat : IImageFormat<GifMetaData, GifFrameMetaData>
{
private GifFormat()
{

5
src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs

@ -11,11 +11,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
internal interface IGifEncoderOptions
{
/// <summary>
/// Gets a value indicating whether the metadata should be ignored when the image is being encoded.
/// </summary>
bool IgnoreMetadata { get; }
/// <summary>
/// Gets the text encoding used to write comments.
/// </summary>

29
src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs

@ -186,9 +186,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
this.outputStream = stream;
ImageMetaData metaData = image.MetaData;
// System.Drawing produces identical output for jpegs with a quality parameter of 0 and 1.
int qlty = (this.quality ?? image.MetaData.GetFormatMetaData(JpegFormat.Instance).Quality).Clamp(1, 100);
int qlty = (this.quality ?? metaData.GetFormatMetaData(JpegFormat.Instance).Quality).Clamp(1, 100);
this.subsample = this.subsample ?? (qlty >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420);
// Convert from a quality rating to a scaling factor.
@ -210,10 +211,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
int componentCount = 3;
// Write the Start Of Image marker.
this.WriteApplicationHeader(image.MetaData);
this.WriteApplicationHeader(metaData);
// Write Exif and ICC profiles
this.WriteProfiles(image);
this.WriteProfiles(metaData);
// Write the quantization tables.
this.WriteDefineQuantizationTables();
@ -620,6 +621,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// </exception>
private void WriteExifProfile(ExifProfile exifProfile)
{
if (exifProfile is null)
{
return;
}
const int MaxBytesApp1 = 65533;
const int MaxBytesWithExifId = 65527;
@ -758,14 +764,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <summary>
/// Writes the metadata profiles to the image.
/// </summary>
/// <param name="image">The image.</param>
/// <typeparam name="TPixel">The pixel format.</typeparam>
private void WriteProfiles<TPixel>(Image<TPixel> image)
where TPixel : struct, IPixel<TPixel>
/// <param name="metaData">The image meta data.</param>
private void WriteProfiles(ImageMetaData metaData)
{
image.MetaData.SyncProfiles();
this.WriteExifProfile(image.MetaData.ExifProfile);
this.WriteIccProfile(image.MetaData.IccProfile);
if (metaData is null)
{
return;
}
metaData.SyncProfiles();
this.WriteExifProfile(metaData.ExifProfile);
this.WriteIccProfile(metaData.IccProfile);
}
/// <summary>

2
src/ImageSharp/Formats/Jpeg/JpegFormat.cs

@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the jpeg format.
/// </summary>
internal sealed class JpegFormat : IImageFormat<JpegMetaData>
public sealed class JpegFormat : IImageFormat<JpegMetaData>
{
private JpegFormat()
{

6
src/ImageSharp/Formats/Png/PngEncoder.cs

@ -51,12 +51,6 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
public byte Threshold { get; set; } = 255;
/// <summary>
/// Gets or sets a value indicating whether this instance should write
/// gamma information to the stream. The default value is false.
/// </summary>
public bool WriteGamma { get; set; }
/// <summary>
/// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>.
/// </summary>

26
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -181,7 +181,8 @@ namespace SixLabors.ImageSharp.Formats.Png
this.height = image.Height;
// Always take the encoder options over the metadata values.
PngMetaData pngMetaData = image.MetaData.GetFormatMetaData(PngFormat.Instance);
ImageMetaData metaData = image.MetaData;
PngMetaData pngMetaData = metaData.GetFormatMetaData(PngFormat.Instance);
this.gamma = this.gamma ?? pngMetaData.Gamma;
this.writeGamma = this.gamma > 0;
this.pngColorType = this.pngColorType ?? pngMetaData.ColorType;
@ -245,9 +246,9 @@ namespace SixLabors.ImageSharp.Formats.Png
this.WritePaletteChunk(stream, header, quantized);
}
this.WritePhysicalChunk(stream, image);
this.WritePhysicalChunk(stream, metaData);
this.WriteGammaChunk(stream);
this.WriteExifChunk(stream, image);
this.WriteExifChunk(stream, metaData);
this.WriteDataChunks(image.Frames.RootFrame, quantizedPixelsSpan, stream);
this.WriteEndChunk(stream);
stream.Flush();
@ -611,11 +612,9 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Writes the physical dimension information to the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="image">The image.</param>
private void WritePhysicalChunk<TPixel>(Stream stream, Image<TPixel> image)
where TPixel : struct, IPixel<TPixel>
/// <param name="meta">The image meta data.</param>
private void WritePhysicalChunk(Stream stream, ImageMetaData meta)
{
// The pHYs chunk specifies the intended pixel size or aspect ratio for display of the image. It contains:
// Pixels per unit, X axis: 4 bytes (unsigned integer)
@ -627,7 +626,6 @@ namespace SixLabors.ImageSharp.Formats.Png
// 1: unit is the meter
//
// When the unit specifier is 0, the pHYs chunk defines pixel aspect ratio only; the actual size of the pixels remains unspecified.
ImageMetaData meta = image.MetaData;
Span<byte> hResolution = this.chunkDataBuffer.AsSpan(0, 4);
Span<byte> vResolution = this.chunkDataBuffer.AsSpan(4, 4);
@ -668,16 +666,14 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Writes the eXIf chunk to the stream, if any EXIF Profile values are present in the meta data.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="image">The image.</param>
private void WriteExifChunk<TPixel>(Stream stream, Image<TPixel> image)
where TPixel : struct, IPixel<TPixel>
/// <param name="meta">The image meta data.</param>
private void WriteExifChunk(Stream stream, ImageMetaData meta)
{
if (image.MetaData.ExifProfile?.Values.Count > 0)
if (meta.ExifProfile?.Values.Count > 0)
{
image.MetaData.SyncProfiles();
this.WriteChunk(stream, PngChunkType.Exif, image.MetaData.ExifProfile.ToByteArray());
meta.SyncProfiles();
this.WriteChunk(stream, PngChunkType.Exif, meta.ExifProfile.ToByteArray());
}
}

2
src/ImageSharp/Formats/Png/PngFormat.cs

@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the png format.
/// </summary>
internal sealed class PngFormat : IImageFormat<PngMetaData>
public sealed class PngFormat : IImageFormat<PngMetaData>
{
private PngFormat()
{

23
src/ImageSharp/ImageFrame{TPixel}.cs

@ -84,12 +84,11 @@ namespace SixLabors.ImageSharp
Guard.NotNull(configuration, nameof(configuration));
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
Guard.NotNull(metaData, nameof(metaData));
this.configuration = configuration;
this.MemoryAllocator = configuration.MemoryAllocator;
this.PixelBuffer = this.MemoryAllocator.Allocate2D<TPixel>(width, height);
this.MetaData = metaData;
this.MetaData = metaData ?? new ImageFrameMetaData();
this.Clear(configuration.GetParallelOptions(), backgroundColor);
}
@ -201,10 +200,7 @@ namespace SixLabors.ImageSharp
/// <param name="y">The y-coordinate of the pixel. Must be greater than or equal to zero and less than the height of the image.</param>
/// <returns>The <see typeparam="TPixel"/> at the specified position.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ref TPixel GetPixelReference(int x, int y)
{
return ref this.PixelBuffer[x, y];
}
internal ref TPixel GetPixelReference(int x, int y) => ref this.PixelBuffer[x, y];
/// <summary>
/// Copies the pixels to a <see cref="Buffer2D{TPixel}"/> of the same size.
@ -249,10 +245,7 @@ namespace SixLabors.ImageSharp
}
/// <inheritdoc/>
public override string ToString()
{
return $"ImageFrame<{typeof(TPixel).Name}>: {this.Width}x{this.Height}";
}
public override string ToString() => $"ImageFrame<{typeof(TPixel).Name}>: {this.Width}x{this.Height}";
/// <summary>
/// Returns a copy of the image frame in the given pixel format.
@ -309,15 +302,9 @@ namespace SixLabors.ImageSharp
/// Clones the current instance.
/// </summary>
/// <returns>The <see cref="ImageFrame{TPixel}"/></returns>
internal ImageFrame<TPixel> Clone()
{
return new ImageFrame<TPixel>(this.configuration, this);
}
internal ImageFrame<TPixel> Clone() => new ImageFrame<TPixel>(this.configuration, this);
/// <inheritdoc/>
void IDisposable.Dispose()
{
this.Dispose();
}
void IDisposable.Dispose() => this.Dispose();
}
}

16
tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs

@ -55,10 +55,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
[MemberData(nameof(RatioFiles))]
public void Encode_PreserveRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit)
{
var options = new GifEncoder()
{
IgnoreMetadata = false
};
var options = new GifEncoder();
var testFile = TestFile.Create(imagePath);
using (Image<Rgba32> input = testFile.CreateImage())
@ -82,10 +79,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
[Fact]
public void Encode_IgnoreMetadataIsFalse_CommentsAreWritten()
{
var options = new GifEncoder()
{
IgnoreMetadata = false
};
var options = new GifEncoder();
var testFile = TestFile.Create(TestImages.Gif.Rings);
@ -109,15 +103,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
[Fact]
public void Encode_IgnoreMetadataIsTrue_CommentsAreNotWritten()
{
var options = new GifEncoder()
{
IgnoreMetadata = true
};
var options = new GifEncoder();
var testFile = TestFile.Create(TestImages.Gif.Rings);
using (Image<Rgba32> input = testFile.CreateImage())
{
input.MetaData.Properties.Clear();
using (var memStream = new MemoryStream())
{
input.SaveAsGif(memStream, options);

Loading…
Cancel
Save