Browse Source

Port Tga and Png

pull/2180/head
James Jackson-South 4 years ago
parent
commit
c9beb02c0e
  1. 16
      src/ImageSharp/Formats/Png/IPngDecoderOptions.cs
  2. 59
      src/ImageSharp/Formats/Png/PngDecoder.cs
  3. 57
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  4. 14
      src/ImageSharp/Formats/Png/PngDecoderOptions.cs
  5. 12
      src/ImageSharp/Formats/Tga/ITgaDecoderOptions.cs
  6. 23
      src/ImageSharp/Formats/Tga/TgaDecoder.cs
  7. 43
      src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
  8. 14
      src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs

16
src/ImageSharp/Formats/Png/IPngDecoderOptions.cs

@ -1,16 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Png
{
/// <summary>
/// The options for decoding png images
/// </summary>
internal interface IPngDecoderOptions
{
/// <summary>
/// Gets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
bool IgnoreMetadata { get; }
}
}

59
src/ImageSharp/Formats/Png/PngDecoder.cs

@ -10,24 +10,24 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Decoder for generating an image out of a png encoded stream.
/// </summary>
public sealed class PngDecoder : IImageDecoder, IPngDecoderOptions, IImageInfoDetector
public sealed class PngDecoder : ImageDecoder<PngDecoderOptions>
{
/// <inheritdoc/>
public bool IgnoreMetadata { get; set; }
/// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
public override Image<TPixel> DecodeSpecialized<TPixel>(PngDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
PngDecoderCore decoder = new(configuration, this);
return decoder.Decode<TPixel>(configuration, stream, cancellationToken);
PngDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<PngDecoderOptions, TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken);
Resize(options.GeneralOptions, image);
return image;
}
/// <inheritdoc />
public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken)
/// <inheritdoc/>
public override Image DecodeSpecialized(PngDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
PngDecoderCore decoder = new(configuration, true);
IImageInfo info = decoder.Identify(configuration, stream, cancellationToken);
PngDecoderCore decoder = new(options, true);
IImageInfo info = decoder.Identify(options.GeneralOptions.Configuration, stream, cancellationToken);
stream.Position = 0;
PngMetadata meta = info.Metadata.GetPngMetadata();
@ -39,49 +39,50 @@ namespace SixLabors.ImageSharp.Formats.Png
if (bits == PngBitDepth.Bit16)
{
return !meta.HasTransparency
? this.Decode<L16>(configuration, stream, cancellationToken)
: this.Decode<La32>(configuration, stream, cancellationToken);
? this.DecodeSpecialized<L16>(options, stream, cancellationToken)
: this.DecodeSpecialized<La32>(options, stream, cancellationToken);
}
return !meta.HasTransparency
? this.Decode<L8>(configuration, stream, cancellationToken)
: this.Decode<La16>(configuration, stream, cancellationToken);
? this.DecodeSpecialized<L8>(options, stream, cancellationToken)
: this.DecodeSpecialized<La16>(options, stream, cancellationToken);
case PngColorType.Rgb:
if (bits == PngBitDepth.Bit16)
{
return !meta.HasTransparency
? this.Decode<Rgb48>(configuration, stream, cancellationToken)
: this.Decode<Rgba64>(configuration, stream, cancellationToken);
? this.DecodeSpecialized<Rgb48>(options, stream, cancellationToken)
: this.DecodeSpecialized<Rgba64>(options, stream, cancellationToken);
}
return !meta.HasTransparency
? this.Decode<Rgb24>(configuration, stream, cancellationToken)
: this.Decode<Rgba32>(configuration, stream, cancellationToken);
? this.DecodeSpecialized<Rgb24>(options, stream, cancellationToken)
: this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
case PngColorType.Palette:
return this.Decode<Rgba32>(configuration, stream, cancellationToken);
return this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
case PngColorType.GrayscaleWithAlpha:
return (bits == PngBitDepth.Bit16)
? this.Decode<La32>(configuration, stream, cancellationToken)
: this.Decode<La16>(configuration, stream, cancellationToken);
? this.DecodeSpecialized<La32>(options, stream, cancellationToken)
: this.DecodeSpecialized<La16>(options, stream, cancellationToken);
case PngColorType.RgbWithAlpha:
return (bits == PngBitDepth.Bit16)
? this.Decode<Rgba64>(configuration, stream, cancellationToken)
: this.Decode<Rgba32>(configuration, stream, cancellationToken);
? this.DecodeSpecialized<Rgba64>(options, stream, cancellationToken)
: this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
default:
return this.Decode<Rgba32>(configuration, stream, cancellationToken);
return this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
}
}
/// <inheritdoc/>
public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken)
public override IImageInfo IdentifySpecialized(PngDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
PngDecoderCore decoder = new(configuration, this);
return decoder.Identify(configuration, stream, cancellationToken);
Guard.NotNull(stream, nameof(stream));
return new PngDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken);
}
}
}

57
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -4,7 +4,6 @@
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Runtime.CompilerServices;
@ -28,17 +27,22 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Performs the png decoding operation.
/// </summary>
internal sealed class PngDecoderCore : IImageDecoderInternals
internal sealed class PngDecoderCore : IImageDecoderInternals<PngDecoderOptions>
{
/// <summary>
/// Reusable buffer.
/// </summary>
private readonly byte[] buffer = new byte[4];
/// <summary>
/// The general decoder options.
/// </summary>
private readonly Configuration configuration;
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
private readonly bool ignoreMetadata;
private readonly bool skipMetadata;
/// <summary>
/// Gets or sets a value indicating whether to read the IHDR and tRNS chunks only.
@ -118,29 +122,26 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Initializes a new instance of the <see cref="PngDecoderCore"/> class.
/// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="options">The decoder options.</param>
public PngDecoderCore(Configuration configuration, IPngDecoderOptions options)
public PngDecoderCore(PngDecoderOptions options)
{
this.Configuration = configuration ?? Configuration.Default;
this.memoryAllocator = this.Configuration.MemoryAllocator;
this.ignoreMetadata = options.IgnoreMetadata;
this.configuration = options.GeneralOptions.Configuration;
this.memoryAllocator = this.configuration.MemoryAllocator;
this.skipMetadata = options.GeneralOptions.SkipMetadata;
}
internal PngDecoderCore(Configuration configuration, bool colorMetadataOnly)
internal PngDecoderCore(PngDecoderOptions options, bool colorMetadataOnly)
{
this.Configuration = configuration ?? Configuration.Default;
this.memoryAllocator = this.Configuration.MemoryAllocator;
this.configuration = options.GeneralOptions.Configuration;
this.memoryAllocator = this.configuration.MemoryAllocator;
this.colorMetadataOnly = colorMetadataOnly;
this.ignoreMetadata = true;
this.skipMetadata = true;
}
/// <inheritdoc/>
public Configuration Configuration { get; }
public PngDecoderOptions Options { get; }
/// <summary>
/// Gets the dimensions of the image.
/// </summary>
/// <inheritdoc/>
public Size Dimensions => new(this.header.Width, this.header.Height);
/// <inheritdoc/>
@ -199,7 +200,7 @@ namespace SixLabors.ImageSharp.Formats.Png
this.ReadInternationalTextChunk(metadata, chunk.Data.GetSpan());
break;
case PngChunkType.Exif:
if (!this.ignoreMetadata)
if (!this.skipMetadata)
{
byte[] exifData = new byte[chunk.Length];
chunk.Data.GetSpan().CopyTo(exifData);
@ -336,7 +337,7 @@ namespace SixLabors.ImageSharp.Formats.Png
break;
}
if (!this.ignoreMetadata)
if (!this.skipMetadata)
{
byte[] exifData = new byte[chunk.Length];
chunk.Data.GetSpan().CopyTo(exifData);
@ -469,7 +470,7 @@ namespace SixLabors.ImageSharp.Formats.Png
where TPixel : unmanaged, IPixel<TPixel>
{
image = Image.CreateUninitialized<TPixel>(
this.Configuration,
this.configuration,
this.header.Width,
this.header.Height,
metadata);
@ -485,7 +486,7 @@ namespace SixLabors.ImageSharp.Formats.Png
this.previousScanline?.Dispose();
this.scanline?.Dispose();
this.previousScanline = this.memoryAllocator.Allocate<byte>(this.bytesPerScanline, AllocationOptions.Clean);
this.scanline = this.Configuration.MemoryAllocator.Allocate<byte>(this.bytesPerScanline, AllocationOptions.Clean);
this.scanline = this.configuration.MemoryAllocator.Allocate<byte>(this.bytesPerScanline, AllocationOptions.Clean);
}
/// <summary>
@ -798,7 +799,7 @@ namespace SixLabors.ImageSharp.Formats.Png
case PngColorType.Rgb:
PngScanlineProcessor.ProcessRgbScanline(
this.Configuration,
this.configuration,
this.header,
scanlineSpan,
rowSpan,
@ -812,7 +813,7 @@ namespace SixLabors.ImageSharp.Formats.Png
case PngColorType.RgbWithAlpha:
PngScanlineProcessor.ProcessRgbaScanline(
this.Configuration,
this.configuration,
this.header,
scanlineSpan,
rowSpan,
@ -1001,7 +1002,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="data">The <see cref="T:Span"/> containing the data.</param>
private void ReadTextChunk(ImageMetadata baseMetadata, PngMetadata metadata, ReadOnlySpan<byte> data)
{
if (this.ignoreMetadata)
if (this.skipMetadata)
{
return;
}
@ -1036,7 +1037,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="data">The <see cref="T:Span"/> containing the data.</param>
private void ReadCompressedTextChunk(ImageMetadata baseMetadata, PngMetadata metadata, ReadOnlySpan<byte> data)
{
if (this.ignoreMetadata)
if (this.skipMetadata)
{
return;
}
@ -1222,10 +1223,10 @@ namespace SixLabors.ImageSharp.Formats.Png
{
fixed (byte* compressedDataBase = compressedData)
{
using (IMemoryOwner<byte> destBuffer = this.memoryAllocator.Allocate<byte>(this.Configuration.StreamProcessingBufferSize))
using (IMemoryOwner<byte> destBuffer = this.memoryAllocator.Allocate<byte>(this.configuration.StreamProcessingBufferSize))
using (var memoryStreamOutput = new MemoryStream(compressedData.Length))
using (var memoryStreamInput = new UnmanagedMemoryStream(compressedDataBase, compressedData.Length))
using (var bufferedStream = new BufferedReadStream(this.Configuration, memoryStreamInput))
using (var bufferedStream = new BufferedReadStream(this.configuration, memoryStreamInput))
using (var inflateStream = new ZlibInflateStream(bufferedStream))
{
Span<byte> destUncompressedData = destBuffer.GetSpan();
@ -1324,7 +1325,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="data">The <see cref="T:Span"/> containing the data.</param>
private void ReadInternationalTextChunk(ImageMetadata metadata, ReadOnlySpan<byte> data)
{
if (this.ignoreMetadata)
if (this.skipMetadata)
{
return;
}
@ -1563,7 +1564,7 @@ namespace SixLabors.ImageSharp.Formats.Png
private IMemoryOwner<byte> ReadChunkData(int length)
{
// We rent the buffer here to return it afterwards in Decode()
IMemoryOwner<byte> buffer = this.Configuration.MemoryAllocator.Allocate<byte>(length, AllocationOptions.Clean);
IMemoryOwner<byte> buffer = this.configuration.MemoryAllocator.Allocate<byte>(length, AllocationOptions.Clean);
this.currentStream.Read(buffer.GetSpan(), 0, length);

14
src/ImageSharp/Formats/Png/PngDecoderOptions.cs

@ -0,0 +1,14 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Png
{
/// <summary>
/// Configuration options for decoding Png images.
/// </summary>
public class PngDecoderOptions : ISpecializedDecoderOptions
{
/// <inheritdoc/>
public DecoderOptions GeneralOptions { get; set; } = new();
}
}

12
src/ImageSharp/Formats/Tga/ITgaDecoderOptions.cs

@ -1,12 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Tga
{
/// <summary>
/// The options for decoding tga images. Currently empty, but this may change in the future.
/// </summary>
internal interface ITgaDecoderOptions
{
}
}

23
src/ImageSharp/Formats/Tga/TgaDecoder.cs

@ -10,28 +10,29 @@ namespace SixLabors.ImageSharp.Formats.Tga
/// <summary>
/// Image decoder for Truevision TGA images.
/// </summary>
public sealed class TgaDecoder : IImageDecoder, ITgaDecoderOptions, IImageInfoDetector
public sealed class TgaDecoder : ImageDecoder<TgaDecoderOptions>
{
/// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
public override Image<TPixel> DecodeSpecialized<TPixel>(TgaDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(stream, nameof(stream));
TgaDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<TgaDecoderOptions, TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken);
Resize(options.GeneralOptions, image);
var decoder = new TgaDecoderCore(configuration, this);
return decoder.Decode<TPixel>(configuration, stream, cancellationToken);
return image;
}
/// <inheritdoc />
public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>(configuration, stream, cancellationToken);
/// <inheritdoc/>
public override Image DecodeSpecialized(TgaDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.DecodeSpecialized<Rgba32>(options, stream, cancellationToken);
/// <inheritdoc/>
public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken)
public override IImageInfo IdentifySpecialized(TgaDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(stream, nameof(stream));
return new TgaDecoderCore(configuration, this).Identify(configuration, stream, cancellationToken);
return new TgaDecoderCore(options).Identify(options.GeneralOptions.Configuration, stream, cancellationToken);
}
}
}

43
src/ImageSharp/Formats/Tga/TgaDecoderCore.cs

@ -15,13 +15,18 @@ namespace SixLabors.ImageSharp.Formats.Tga
/// <summary>
/// Performs the tga decoding operation.
/// </summary>
internal sealed class TgaDecoderCore : IImageDecoderInternals
internal sealed class TgaDecoderCore : IImageDecoderInternals<TgaDecoderOptions>
{
/// <summary>
/// A scratch buffer to reduce allocations.
/// </summary>
private readonly byte[] scratchBuffer = new byte[4];
/// <summary>
/// General configuration options.
/// </summary>
private readonly Configuration configuration;
/// <summary>
/// The metadata.
/// </summary>
@ -47,11 +52,6 @@ namespace SixLabors.ImageSharp.Formats.Tga
/// </summary>
private BufferedReadStream currentStream;
/// <summary>
/// The bitmap decoder options.
/// </summary>
private readonly ITgaDecoderOptions options;
/// <summary>
/// Indicates whether there is a alpha channel present.
/// </summary>
@ -60,22 +60,19 @@ namespace SixLabors.ImageSharp.Formats.Tga
/// <summary>
/// Initializes a new instance of the <see cref="TgaDecoderCore"/> class.
/// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="options">The options.</param>
public TgaDecoderCore(Configuration configuration, ITgaDecoderOptions options)
public TgaDecoderCore(TgaDecoderOptions options)
{
this.Configuration = configuration;
this.memoryAllocator = configuration.MemoryAllocator;
this.options = options;
this.Options = options;
this.configuration = options.GeneralOptions.Configuration;
this.memoryAllocator = this.configuration.MemoryAllocator;
}
/// <inheritdoc />
public Configuration Configuration { get; }
public TgaDecoderOptions Options { get; }
/// <summary>
/// Gets the dimensions of the image.
/// </summary>
public Size Dimensions => new Size(this.fileHeader.Width, this.fileHeader.Height);
/// <inheritdoc />
public Size Dimensions => new(this.fileHeader.Width, this.fileHeader.Height);
/// <inheritdoc />
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
@ -87,7 +84,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
this.currentStream.Skip(this.fileHeader.IdLength);
// Parse the color map, if present.
if (this.fileHeader.ColorMapType != 0 && this.fileHeader.ColorMapType != 1)
if (this.fileHeader.ColorMapType is not 0 and not 1)
{
TgaThrowHelper.ThrowNotSupportedException($"Unknown tga colormap type {this.fileHeader.ColorMapType} found");
}
@ -97,7 +94,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
throw new UnknownImageFormatException("Width or height cannot be 0");
}
var image = Image.CreateUninitialized<TPixel>(this.Configuration, this.fileHeader.Width, this.fileHeader.Height, this.metadata);
var image = Image.CreateUninitialized<TPixel>(this.configuration, this.fileHeader.Width, this.fileHeader.Height, this.metadata);
Buffer2D<TPixel> pixels = image.GetRootFramePixelBuffer();
if (this.fileHeader.ColorMapType == 1)
@ -451,11 +448,11 @@ namespace SixLabors.ImageSharp.Formats.Tga
if (this.fileHeader.ImageType == TgaImageType.BlackAndWhite)
{
PixelOperations<TPixel>.Instance.FromLa16Bytes(this.Configuration, rowSpan, pixelSpan, width);
PixelOperations<TPixel>.Instance.FromLa16Bytes(this.configuration, rowSpan, pixelSpan, width);
}
else
{
PixelOperations<TPixel>.Instance.FromBgra5551Bytes(this.Configuration, rowSpan, pixelSpan, width);
PixelOperations<TPixel>.Instance.FromBgra5551Bytes(this.configuration, rowSpan, pixelSpan, width);
}
}
}
@ -655,7 +652,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
{
this.currentStream.Read(row);
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.FromL8Bytes(this.Configuration, row, pixelSpan, width);
PixelOperations<TPixel>.Instance.FromL8Bytes(this.configuration, row, pixelSpan, width);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -682,7 +679,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
{
this.currentStream.Read(row);
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.FromBgr24Bytes(this.Configuration, row, pixelSpan, width);
PixelOperations<TPixel>.Instance.FromBgr24Bytes(this.configuration, row, pixelSpan, width);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -701,7 +698,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
{
this.currentStream.Read(row);
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(y);
PixelOperations<TPixel>.Instance.FromBgra32Bytes(this.Configuration, row, pixelSpan, width);
PixelOperations<TPixel>.Instance.FromBgra32Bytes(this.configuration, row, pixelSpan, width);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

14
src/ImageSharp/Formats/Tga/TgaDecoderOptions.cs

@ -0,0 +1,14 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Tga
{
/// <summary>
/// Configuration options for decoding Png images.
/// </summary>
public class TgaDecoderOptions : ISpecializedDecoderOptions
{
/// <inheritdoc/>
public DecoderOptions GeneralOptions { get; set; } = new();
}
}
Loading…
Cancel
Save