Skip to content

Commit

Permalink
More work on Jpeg and Png.
Browse files Browse the repository at this point in the history
Jpeg saving is almost working, then need to move to decoding.
  • Loading branch information
juliusfriedman committed Oct 20, 2024
1 parent 33625ce commit cbf2608
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 20 deletions.
12 changes: 6 additions & 6 deletions Codecs/Image/Image.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
using Codecs.Image;
using Media.Codec;
using Media.Codec;
using Media.Codec.Interfaces;
using Media.Codecs.Image;
using Media.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Text;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace Media.Codecs.Image
{
Expand Down Expand Up @@ -103,8 +101,8 @@ public Image(ImageFormat format, int width, int height, byte[] data)
Height = height;
}

public Image(ImageFormat format, int width, int height, MemorySegment data)
: base(format, data)
public Image(ImageFormat format, int width, int height, MemorySegment data, ICodec codec = null)
: base(format, data, codec)
{
Width = width;

Expand All @@ -115,6 +113,8 @@ public Image(ImageFormat format, int width, int height, MemorySegment data)

#region Properties

public double AspectRatio => Width / Height;

//Should be Vector<byte>?

//Assumes component order
Expand Down
38 changes: 31 additions & 7 deletions Codecs/Image/Jpeg/JpegImage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Media.Codecs.Image;
using Media.Common;
using System.Collections.Generic;
using System.Linq;

namespace Media.Codec.Jpeg;

Expand All @@ -17,8 +18,13 @@ public JpegImage(ImageFormat imageFormat, int width, int height)
{
}

public JpegImage(ImageFormat imageFormat, int width, int height, MemorySegment data)
: base(imageFormat, width, height, new JpegCodec())
{
}

private JpegImage(ImageFormat imageFormat, int width, int height, MemorySegment data, bool progressive, List<Marker> markers)
: base(imageFormat, width, height, data)
: this(imageFormat, width, height, data)
{
Progressive = progressive;
Markers = markers;
Expand All @@ -30,8 +36,8 @@ public static JpegImage FromStream(Stream stream)

// Read the SOF0 (Start of Frame) marker
int width = 0, height = 0;
ImageFormat? imageFormat = default;
MemorySegment? dataSegment = default;
ImageFormat imageFormat = default;
MemorySegment dataSegment = default;
bool progressive = false;
List<Marker> markers = new List<Marker>();
foreach (var marker in markerReader.ReadMarkers())
Expand Down Expand Up @@ -60,7 +66,9 @@ public static JpegImage FromStream(Stream stream)
var samplingFactors = marker.Data[offset++];
widths[componentIndex] = samplingFactors & 0x0F;
heights[componentIndex] = samplingFactors >> 4;
var quantizationTableNumber = marker.Data[offset++];

//TODO CMYK image throws this off?
//var quantizationTableNumber = marker.Data[offset++];

var mediaComponent = new MediaComponent(componentId, bitDepth);

Expand All @@ -78,7 +86,7 @@ public static JpegImage FromStream(Stream stream)
dataSegment = marker.Data;
break;
}
else
else if (marker.FunctionCode != Jpeg.Markers.StartOfInformation)
{
markers.Add(marker);
}
Expand All @@ -98,7 +106,15 @@ public void Save(Stream stream)

if (Markers != null)
{
foreach (var marker in Markers)
foreach (var marker in Markers.Where(marker => marker.FunctionCode == Jpeg.Markers.TextComment))
{
WriteMarker(stream, marker.FunctionCode, (s) => s.Write(marker.Data.Array, marker.Data.Offset, marker.Data.Count));
}
}

if (Markers != null)
{
foreach (var marker in Markers.Where(marker => marker.FunctionCode == Jpeg.Markers.QuantizationTable))
{
WriteMarker(stream, marker.FunctionCode, (s) => s.Write(marker.Data.Array, marker.Data.Offset, marker.Data.Count));
}
Expand All @@ -107,6 +123,14 @@ public void Save(Stream stream)
// Write the SOF0 marker
WriteMarker(stream, Progressive ? Jpeg.Markers.StartOfProgressiveFrame : Jpeg.Markers.StartOfBaselineFrame, WriteSOF0Marker);

if (Markers != null)
{
foreach (var marker in Markers.Where(marker => marker.FunctionCode == Jpeg.Markers.HuffmanTable))
{
WriteMarker(stream, marker.FunctionCode, (s) => s.Write(marker.Data.Array, marker.Data.Offset, marker.Data.Count));
}
}

// Write the SOS marker
WriteMarker(stream, Jpeg.Markers.StartOfScan, WriteSOSMarker);

Expand Down Expand Up @@ -163,7 +187,7 @@ private void WriteMarker(Stream writer, byte functionCode, Action<Stream> writeM
{
writeMarkerData(ms);
ms.TryGetBuffer(out var markerData);
Marker marker = new Marker(functionCode, markerData.Count + Binary.BytesPerShort);
Marker marker = new Marker(functionCode, markerData.Count > 0 ? markerData.Count + Binary.BytesPerShort : 0);
markerData.CopyTo(marker.Data.Array, marker.Data.Offset);
writer.Write(marker.Array, marker.Offset, marker.Count);
}
Expand Down
15 changes: 13 additions & 2 deletions Codecs/Image/Jpeg/JpegUnitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,19 @@ public static void TestLoad()

var saveFileName = Path.Combine(outputDir, Path.GetFileName(filePath));

using var outputNew = new FileStream(saveFileName, FileMode.OpenOrCreate, FileAccess.Write);
jpgImage.Save(outputNew);
using (var outputNew = new FileStream(saveFileName, FileMode.OpenOrCreate, FileAccess.Write))
{
jpgImage.Save(outputNew);
}

using var inputNew = new FileStream(saveFileName, FileMode.OpenOrCreate, FileAccess.Read);

using var newJpgImage = JpegImage.FromStream(inputNew);

if (newJpgImage.Width != jpgImage.Width ||
newJpgImage.Height != jpgImage.Height ||
newJpgImage.Progressive != jpgImage.Progressive)
throw new InvalidDataException();
}
}
}
7 changes: 4 additions & 3 deletions Codecs/Image/Jpeg/Marker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@ public int Length

public int DataSize => Binary.Max(0, Length - 2);

public MemorySegment Data => new MemorySegment(Array, Offset + Binary.BytesPerInteger, DataSize);
public MemorySegment Data => DataSize > 0 ? new MemorySegment(Array, Offset + Binary.BytesPerInteger, DataSize) : Common.MemorySegment.Empty;

public Marker(byte functionCode, int size)
: base(new byte[size + Binary.BytesPerInteger])
: base(new byte[size > 0 ? size + Binary.BytesPerInteger : size + Binary.BytesPerShort])
{
Prefix = Markers.Prefix;
FunctionCode = functionCode;
Length = size;
if (size > 0)
Length = size;
}
}
2 changes: 2 additions & 0 deletions Codecs/Image/Png/Codec.Png/ChunkHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public uint Length
set => Binary.Write32(Array, Offset, Binary.IsLittleEndian, value);
}

public int TotalLength => (int)(Length + Binary.BytesPerInteger);

public uint Type
{
get => Binary.ReadU32(Array, Offset + Binary.BytesPerInteger, Binary.IsLittleEndian);
Expand Down
4 changes: 2 additions & 2 deletions Codecs/Image/Png/Codec.Png/PngImage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public PngImage(ImageFormat imageFormat, int width, int height)
}

private PngImage(ImageFormat imageFormat, int width, int height, MemorySegment data)
: base(imageFormat, width, height, data)
: base(imageFormat, width, height, data, new PngCodec())
{
}

Expand Down Expand Up @@ -129,7 +129,7 @@ public static PngImage FromStream(Stream stream)
else
{
// Skip the chunk data and CRC
stream.Seek(chunkHeader.Length + Binary.BytesPerInteger, SeekOrigin.Current);
stream.Seek(chunkHeader.TotalLength, SeekOrigin.Current);
}
}

Expand Down

0 comments on commit cbf2608

Please sign in to comment.