Skip to content

Commit

Permalink
Png Component count.
Browse files Browse the repository at this point in the history
  • Loading branch information
juliusfriedman committed Oct 22, 2024
1 parent 3d62ccf commit d25f350
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 18 deletions.
4 changes: 2 additions & 2 deletions Codecs/Image/Png/Codec.Png/Chunk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ public uint RawType
set => Header.Type = value;
}

public ChunkNames ChunkName
public ChunkName ChunkName
{
get => (ChunkNames)RawType;
get => (ChunkName)RawType;
set => RawType = (uint)value;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Media.Codec.Png;

public enum ChunkNames : uint
public enum ChunkName : uint
{
/// <summary>
/// This chunk contains the actual image data. The image can contains more
Expand Down
41 changes: 30 additions & 11 deletions Codecs/Image/Png/Codec.Png/PngImage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Numerics;
using Media.Codecs.Image;
using Media.Common;
using Media.Common.Collections.Generic;

namespace Media.Codec.Png;

Expand All @@ -14,21 +15,22 @@ private static ImageFormat CreateImageFormat(byte bitDepth, byte colorType)
switch (colorType)
{
case 0: // Grayscale
case 3: // Indexed-color
case 4: // Grayscale with alpha
case 3: // Indexed-color
return ImageFormat.Monochrome(bitDepth);
case 4: // Grayscale with alpha
return ImageFormat.WithPreceedingAlphaComponent(ImageFormat.Monochrome(bitDepth / 2), bitDepth / 2);
case 2: // Truecolor (RGB)
return ImageFormat.RGB(bitDepth);
return ImageFormat.RGB(bitDepth/ 3);
case 6: // Truecolor with alpha (RGBA)
return ImageFormat.RGBA(bitDepth);
return ImageFormat.RGBA(bitDepth / 4);
default:
throw new NotSupportedException($"Color type {colorType} is not supported.");
}
}

private static byte[] CalculateCrc(IEnumerable<byte> bytes)
{
uint checksum = 0;
uint checksum = 0; //Should be seed value...
foreach (var b in bytes)
checksum = (checksum >> 8) ^ BitOperations.Crc32C(checksum, b);
return Binary.GetBytes(checksum, BitConverter.IsLittleEndian);
Expand All @@ -37,20 +39,25 @@ private static byte[] CalculateCrc(IEnumerable<byte> bytes)

public readonly byte ColorType;

public readonly ConcurrentThesaurus<ChunkName, Chunk> Chunks;

public PngImage(ImageFormat imageFormat, int width, int height)
: base(imageFormat, width, height, new PngCodec())
{
Chunks = new ConcurrentThesaurus<ChunkName, Chunk>();
}

private PngImage(ImageFormat imageFormat, int width, int height, MemorySegment data)
: base(imageFormat, width, height, data, new PngCodec())
{
Chunks = new ConcurrentThesaurus<ChunkName, Chunk>();
}

private PngImage(ImageFormat imageFormat, int width, int height, MemorySegment data, byte colorType)
private PngImage(ImageFormat imageFormat, int width, int height, MemorySegment data, byte colorType, ConcurrentThesaurus<ChunkName, Chunk> chunks)
: this(imageFormat, width, height, data)
{
ColorType = colorType;
Chunks = chunks;
}

public static PngImage FromStream(Stream stream)
Expand All @@ -60,12 +67,14 @@ public static PngImage FromStream(Stream stream)
ImageFormat? imageFormat = default;
MemorySegment? dataSegment = default;
byte colorType = default;
ConcurrentThesaurus<ChunkName, Chunk> chunks = new ConcurrentThesaurus<ChunkName, Chunk>();

foreach(var chunk in PngCodec.ReadChunks(stream))
{
switch (chunk.ChunkName)
var chunkName = chunk.ChunkName;
switch (chunkName)
{
case ChunkNames.Header:
case ChunkName.Header:
var offset = chunk.DataOffset;
width = Binary.Read32(chunk.Array, ref offset, Binary.IsLittleEndian);
height = Binary.Read32(chunk.Array, ref offset, Binary.IsLittleEndian);
Expand All @@ -77,15 +86,17 @@ public static PngImage FromStream(Stream stream)

// Create the image format based on the IHDR data
imageFormat = CreateImageFormat(bitDepth, colorType);

//ToDo, Crc is in data segment, so we need to read it and validate it.
continue;
case ChunkNames.Data:
case ChunkName.Data:
//ToDo, Crc is in data segment, so we need to read it and validate it.
dataSegment = chunk.Data.Slice(0);
continue;
case ChunkNames.End:
case ChunkName.End:
continue;
default:
chunks.Add(chunkName, chunk);
continue;
}
}
Expand All @@ -94,7 +105,7 @@ public static PngImage FromStream(Stream stream)
throw new InvalidDataException("The provided stream does not contain valid PNG image data.");

// Create and return the PngImage
return new PngImage(imageFormat, width, height, dataSegment, colorType);
return new PngImage(imageFormat, width, height, dataSegment, colorType, chunks);
}

public void Save(Stream stream)
Expand All @@ -111,6 +122,14 @@ public void Save(Stream stream)
// Write the IDAT chunk
WriteIDATChunk(stream);

if (Chunks != null)
{
foreach (var chunk in Chunks.Values)
{
stream.Write(chunk.Array, chunk.Offset, chunk.Count);
}
}

// Write the IEND chunk
WriteIENDChunk(stream);
}
Expand Down
23 changes: 19 additions & 4 deletions Codecs/Image/Png/Codec.Png/PngUnitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,12 +209,27 @@ public static void TestLoad()

pngStream.Seek(0, SeekOrigin.Begin);

using var pngImage = PngImage.FromStream(pngStream);
using (var pngImage = PngImage.FromStream(pngStream))
{
var saveFileName = Path.Combine(outputDir, Path.GetFileName(filePath));

var saveFileName = Path.Combine(outputDir, Path.GetFileName(filePath));
using (var outputNew = new FileStream(saveFileName, FileMode.OpenOrCreate, FileAccess.Write))
{
pngImage.Save(outputNew);
}

using var outputNew = new FileStream(saveFileName, FileMode.OpenOrCreate, FileAccess.Write);
pngImage.Save(outputNew);
using (var inputNew = new FileStream(saveFileName, FileMode.OpenOrCreate, FileAccess.Read))
{
using (var newPngImage = PngImage.FromStream(inputNew))
{
if (newPngImage.Width != pngImage.Width ||
newPngImage.Height != pngImage.Height ||
newPngImage.ImageFormat.Components.Length != pngImage.ImageFormat.Components.Length ||
newPngImage.ImageFormat.Size != pngImage.ImageFormat.Size)
throw new InvalidDataException();
}
}
}
}
}
}

0 comments on commit d25f350

Please sign in to comment.