Browse Source

Work in progress preserving isTrans data

pull/764/head
Devedse 8 years ago
parent
commit
b798d8dc01
  1. 97
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  2. 5
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  3. 27
      src/ImageSharp/Formats/Png/PngMetaData.cs
  4. 1
      src/ImageSharp/MetaData/ImageMetaData.cs

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

@ -124,31 +124,6 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary> /// </summary>
private PngColorType pngColorType; private PngColorType pngColorType;
/// <summary>
/// Represents any color in an 8 bit Rgb24 encoded png that should be transparent
/// </summary>
private Rgb24 rgb24Trans;
/// <summary>
/// Represents any color in a 16 bit Rgb24 encoded png that should be transparent
/// </summary>
private Rgb48 rgb48Trans;
/// <summary>
/// Represents any color in an 8 bit grayscale encoded png that should be transparent
/// </summary>
private byte luminanceTrans;
/// <summary>
/// Represents any color in a 16 bit grayscale encoded png that should be transparent
/// </summary>
private ushort luminance16Trans;
/// <summary>
/// Whether the image has transparency chunk and markers were decoded
/// </summary>
private bool hasTrans;
/// <summary> /// <summary>
/// The next chunk of data to return /// The next chunk of data to return
/// </summary> /// </summary>
@ -213,7 +188,7 @@ namespace SixLabors.ImageSharp.Formats.Png
using (var deframeStream = new ZlibInflateStream(this.currentStream, this.ReadNextDataChunk)) using (var deframeStream = new ZlibInflateStream(this.currentStream, this.ReadNextDataChunk))
{ {
deframeStream.AllocateNewBytes(chunk.Length); deframeStream.AllocateNewBytes(chunk.Length);
this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame); this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame, pngMetaData);
} }
break; break;
@ -226,7 +201,7 @@ namespace SixLabors.ImageSharp.Formats.Png
byte[] alpha = new byte[chunk.Length]; byte[] alpha = new byte[chunk.Length];
Buffer.BlockCopy(chunk.Data.Array, 0, alpha, 0, chunk.Length); Buffer.BlockCopy(chunk.Data.Array, 0, alpha, 0, chunk.Length);
this.paletteAlpha = alpha; this.paletteAlpha = alpha;
this.AssignTransparentMarkers(alpha); this.AssignTransparentMarkers(alpha, pngMetaData);
break; break;
case PngChunkType.Text: case PngChunkType.Text:
this.ReadTextChunk(metaData, chunk.Data.Array.AsSpan(0, chunk.Length)); this.ReadTextChunk(metaData, chunk.Data.Array.AsSpan(0, chunk.Length));
@ -331,7 +306,9 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <returns>The <see cref="int"/></returns> /// <returns>The <see cref="int"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static byte ReadByteLittleEndian(ReadOnlySpan<byte> buffer, int offset) private static byte ReadByteLittleEndian(ReadOnlySpan<byte> buffer, int offset)
=> (byte)(((buffer[offset] & 0xFF) << 16) | (buffer[offset + 1] & 0xFF)); {
return (byte)(((buffer[offset] & 0xFF) << 16) | (buffer[offset + 1] & 0xFF));
}
/// <summary> /// <summary>
/// Attempts to convert a byte array to a new array where each value in the original array is represented by the /// Attempts to convert a byte array to a new array where each value in the original array is represented by the
@ -496,16 +473,17 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="dataStream">The <see cref="MemoryStream"/> containing data.</param> /// <param name="dataStream">The <see cref="MemoryStream"/> containing data.</param>
/// <param name="image"> The pixel data.</param> /// <param name="image"> The pixel data.</param>
private void ReadScanlines<TPixel>(Stream dataStream, ImageFrame<TPixel> image) /// <param name="pngMetaData">The png meta data</param>
private void ReadScanlines<TPixel>(Stream dataStream, ImageFrame<TPixel> image, PngMetaData pngMetaData)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
if (this.header.InterlaceMethod == PngInterlaceMode.Adam7) if (this.header.InterlaceMethod == PngInterlaceMode.Adam7)
{ {
this.DecodeInterlacedPixelData(dataStream, image); this.DecodeInterlacedPixelData(dataStream, image, pngMetaData);
} }
else else
{ {
this.DecodePixelData(dataStream, image); this.DecodePixelData(dataStream, image, pngMetaData);
} }
} }
@ -515,7 +493,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="compressedStream">The compressed pixel data stream.</param> /// <param name="compressedStream">The compressed pixel data stream.</param>
/// <param name="image">The image to decode to.</param> /// <param name="image">The image to decode to.</param>
private void DecodePixelData<TPixel>(Stream compressedStream, ImageFrame<TPixel> image) /// <param name="pngMetaData">The png meta data</param>
private void DecodePixelData<TPixel>(Stream compressedStream, ImageFrame<TPixel> image, PngMetaData pngMetaData)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
while (this.currentRow < this.header.Height) while (this.currentRow < this.header.Height)
@ -555,7 +534,7 @@ namespace SixLabors.ImageSharp.Formats.Png
throw new ImageFormatException("Unknown filter type."); throw new ImageFormatException("Unknown filter type.");
} }
this.ProcessDefilteredScanline(scanlineSpan, image); this.ProcessDefilteredScanline(scanlineSpan, image, pngMetaData);
this.SwapBuffers(); this.SwapBuffers();
this.currentRow++; this.currentRow++;
@ -569,7 +548,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="compressedStream">The compressed pixel data stream.</param> /// <param name="compressedStream">The compressed pixel data stream.</param>
/// <param name="image">The current image.</param> /// <param name="image">The current image.</param>
private void DecodeInterlacedPixelData<TPixel>(Stream compressedStream, ImageFrame<TPixel> image) /// <param name="pngMetaData">The png meta data</param>
private void DecodeInterlacedPixelData<TPixel>(Stream compressedStream, ImageFrame<TPixel> image, PngMetaData pngMetaData)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
while (true) while (true)
@ -626,7 +606,7 @@ namespace SixLabors.ImageSharp.Formats.Png
} }
Span<TPixel> rowSpan = image.GetPixelRowSpan(this.currentRow); Span<TPixel> rowSpan = image.GetPixelRowSpan(this.currentRow);
this.ProcessInterlacedDefilteredScanline(this.scanline.GetSpan(), rowSpan, Adam7.FirstColumn[this.pass], Adam7.ColumnIncrement[this.pass]); this.ProcessInterlacedDefilteredScanline(this.scanline.GetSpan(), rowSpan, pngMetaData, Adam7.FirstColumn[this.pass], Adam7.ColumnIncrement[this.pass]);
this.SwapBuffers(); this.SwapBuffers();
@ -654,7 +634,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="defilteredScanline">The de-filtered scanline</param> /// <param name="defilteredScanline">The de-filtered scanline</param>
/// <param name="pixels">The image</param> /// <param name="pixels">The image</param>
private void ProcessDefilteredScanline<TPixel>(ReadOnlySpan<byte> defilteredScanline, ImageFrame<TPixel> pixels) /// <param name="pngMetaData">The png meta data</param>
private void ProcessDefilteredScanline<TPixel>(ReadOnlySpan<byte> defilteredScanline, ImageFrame<TPixel> pixels, PngMetaData pngMetaData)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
Span<TPixel> rowSpan = pixels.GetPixelRowSpan(this.currentRow); Span<TPixel> rowSpan = pixels.GetPixelRowSpan(this.currentRow);
@ -674,9 +655,9 @@ namespace SixLabors.ImageSharp.Formats.Png
this.header, this.header,
scanlineSpan, scanlineSpan,
rowSpan, rowSpan,
this.hasTrans, pngMetaData.HasTrans,
this.luminance16Trans, pngMetaData.Luminance16Trans,
this.luminanceTrans); pngMetaData.LuminanceTrans);
break; break;
@ -708,9 +689,9 @@ namespace SixLabors.ImageSharp.Formats.Png
rowSpan, rowSpan,
this.bytesPerPixel, this.bytesPerPixel,
this.bytesPerSample, this.bytesPerSample,
this.hasTrans, pngMetaData.HasTrans,
this.rgb48Trans, pngMetaData.Rgb48Trans,
this.rgb24Trans); pngMetaData.Rgb24Trans);
break; break;
@ -735,9 +716,10 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="defilteredScanline">The de-filtered scanline</param> /// <param name="defilteredScanline">The de-filtered scanline</param>
/// <param name="rowSpan">The current image row.</param> /// <param name="rowSpan">The current image row.</param>
/// <param name="pngMetaData">The png meta data</param>
/// <param name="pixelOffset">The column start index. Always 0 for none interlaced images.</param> /// <param name="pixelOffset">The column start index. Always 0 for none interlaced images.</param>
/// <param name="increment">The column increment. Always 1 for none interlaced images.</param> /// <param name="increment">The column increment. Always 1 for none interlaced images.</param>
private void ProcessInterlacedDefilteredScanline<TPixel>(ReadOnlySpan<byte> defilteredScanline, Span<TPixel> rowSpan, int pixelOffset = 0, int increment = 1) private void ProcessInterlacedDefilteredScanline<TPixel>(ReadOnlySpan<byte> defilteredScanline, Span<TPixel> rowSpan, PngMetaData pngMetaData, int pixelOffset = 0, int increment = 1)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
// Trim the first marker byte from the buffer // Trim the first marker byte from the buffer
@ -757,9 +739,9 @@ namespace SixLabors.ImageSharp.Formats.Png
rowSpan, rowSpan,
pixelOffset, pixelOffset,
increment, increment,
this.hasTrans, pngMetaData.HasTrans,
this.luminance16Trans, pngMetaData.Luminance16Trans,
this.luminanceTrans); pngMetaData.LuminanceTrans);
break; break;
@ -796,9 +778,9 @@ namespace SixLabors.ImageSharp.Formats.Png
increment, increment,
this.bytesPerPixel, this.bytesPerPixel,
this.bytesPerSample, this.bytesPerSample,
this.hasTrans, pngMetaData.HasTrans,
this.rgb48Trans, pngMetaData.Rgb48Trans,
this.rgb24Trans); pngMetaData.Rgb24Trans);
break; break;
@ -822,7 +804,8 @@ namespace SixLabors.ImageSharp.Formats.Png
/// Decodes and assigns marker colors that identify transparent pixels in non indexed images /// Decodes and assigns marker colors that identify transparent pixels in non indexed images
/// </summary> /// </summary>
/// <param name="alpha">The alpha tRNS array</param> /// <param name="alpha">The alpha tRNS array</param>
private void AssignTransparentMarkers(ReadOnlySpan<byte> alpha) /// <param name="pngMetaData">The png meta data</param>
private void AssignTransparentMarkers(ReadOnlySpan<byte> alpha, PngMetaData pngMetaData)
{ {
if (this.pngColorType == PngColorType.Rgb) if (this.pngColorType == PngColorType.Rgb)
{ {
@ -834,16 +817,16 @@ namespace SixLabors.ImageSharp.Formats.Png
ushort gc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(2, 2)); ushort gc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(2, 2));
ushort bc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(4, 2)); ushort bc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(4, 2));
this.rgb48Trans = new Rgb48(rc, gc, bc); pngMetaData.Rgb48Trans = new Rgb48(rc, gc, bc);
this.hasTrans = true; pngMetaData.HasTrans = true;
return; return;
} }
byte r = ReadByteLittleEndian(alpha, 0); byte r = ReadByteLittleEndian(alpha, 0);
byte g = ReadByteLittleEndian(alpha, 2); byte g = ReadByteLittleEndian(alpha, 2);
byte b = ReadByteLittleEndian(alpha, 4); byte b = ReadByteLittleEndian(alpha, 4);
this.rgb24Trans = new Rgb24(r, g, b); pngMetaData.Rgb24Trans = new Rgb24(r, g, b);
this.hasTrans = true; pngMetaData.HasTrans = true;
} }
} }
else if (this.pngColorType == PngColorType.Grayscale) else if (this.pngColorType == PngColorType.Grayscale)
@ -852,14 +835,14 @@ namespace SixLabors.ImageSharp.Formats.Png
{ {
if (this.header.BitDepth == 16) if (this.header.BitDepth == 16)
{ {
this.luminance16Trans = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(0, 2)); pngMetaData.Luminance16Trans = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(0, 2));
} }
else else
{ {
this.luminanceTrans = ReadByteLittleEndian(alpha, 0); pngMetaData.LuminanceTrans = ReadByteLittleEndian(alpha, 0);
} }
this.hasTrans = true; pngMetaData.HasTrans = true;
} }
} }
} }

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

@ -290,6 +290,11 @@ namespace SixLabors.ImageSharp.Formats.Png
this.WritePaletteChunk(stream, quantized); this.WritePaletteChunk(stream, quantized);
} }
if (pngMetaData.HasTrans)
{
//Write transparency header
}
this.WritePhysicalChunk(stream, metaData); this.WritePhysicalChunk(stream, metaData);
this.WriteGammaChunk(stream); this.WriteGammaChunk(stream);
this.WriteExifChunk(stream, metaData); this.WriteExifChunk(stream, metaData);

27
src/ImageSharp/Formats/Png/PngMetaData.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Png namespace SixLabors.ImageSharp.Formats.Png
{ {
/// <summary> /// <summary>
@ -42,6 +44,31 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary> /// </summary>
public float Gamma { get; set; } public float Gamma { get; set; }
/// <summary>
/// Gets or sets the Rgb 24 transparent color. This represents any color in an 8 bit Rgb24 encoded png that should be transparent
/// </summary>
public Rgb24 Rgb24Trans { get; set; }
/// <summary>
/// Gets or sets the Rgb 48 transparent color. This represents any color in a 16 bit Rgb24 encoded png that should be transparent
/// </summary>
public Rgb48 Rgb48Trans { get; set; }
/// <summary>
/// Gets or sets the 8 bit grayscale transparent color. This represents any color in an 8 bit grayscale encoded png that should be transparent
/// </summary>
public byte LuminanceTrans { get; set; }
/// <summary>
/// Gets or sets the 16 bit grayscale transparent color. This represents any color in a 16 bit grayscale encoded png that should be transparent
/// </summary>
public ushort Luminance16Trans { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the image has transparency chunk and markers were decoded
/// </summary>
public bool HasTrans { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public IDeepCloneable DeepClone() => new PngMetaData(this); public IDeepCloneable DeepClone() => new PngMetaData(this);
} }

1
src/ImageSharp/MetaData/ImageMetaData.cs

@ -5,6 +5,7 @@ using System.Collections.Generic;
using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.MetaData.Profiles.Exif; using SixLabors.ImageSharp.MetaData.Profiles.Exif;
using SixLabors.ImageSharp.MetaData.Profiles.Icc; using SixLabors.ImageSharp.MetaData.Profiles.Icc;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.MetaData namespace SixLabors.ImageSharp.MetaData
{ {

Loading…
Cancel
Save