Browse Source

Add JxlToJpegDecoder static methods

pull/3153/head
winscripter 1 month ago
parent
commit
5de2701e1c
  1. 13
      src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlDecoderCore.cs
  2. 150
      src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlToJpegDecoder.cs

13
src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlDecoderCore.cs

@ -363,11 +363,6 @@ internal sealed class JxlDecoderCore : ImageDecoderCore, IDisposable
/// </summary>
private readonly JxlBoxContentDecoder? boxContentDecoder;
/// <summary>
/// Decodes JPEG XL to JPEG.
/// </summary>
private JxlToJpegDecoder? jpegDecoder;
private readonly JxlBoxContentDecoder? metadataDecoder;
/// <summary>
@ -2246,12 +2241,12 @@ internal sealed class JxlDecoderCore : ImageDecoderCore, IDisposable
if (this.reconstructionExifSize > 0)
{
JxlToJpegDecoder.SetExif(this.exifMetadata!.Memory, jpegData);
JxlToJpegDecoder.TrySetExif(this.exifMetadata!.Memory.Span, jpegData);
}
if (this.reconstructionXmpSize > 0)
{
JxlToJpegDecoder.SetXmp(this.xmpMetadata!.Memory, jpegData);
JxlToJpegDecoder.TrySetXmp(this.xmpMetadata!.Memory.Span, jpegData);
}
this.reconstructionOutputJpeg = JpegReconstructionStage.Output;
@ -2592,7 +2587,7 @@ internal sealed class JxlDecoderCore : ImageDecoderCore, IDisposable
throw new InvalidOperationException("Only one EXIF marker for JPEG reconstruction can be present");
}
if (JxlToJpegDecoder.ExifBoxContentSize(jpegData, ref this.reconstructionExifSize) != Success)
if (!JxlToJpegDecoder.ExifBoxContentSize(jpegData, ref this.reconstructionExifSize))
{
throw new InvalidOperationException("Invalid jbrd EXIF size");
}
@ -2605,7 +2600,7 @@ internal sealed class JxlDecoderCore : ImageDecoderCore, IDisposable
throw new InvalidOperationException("Only one XMP marker for JPEG reconstruction can be present");
}
if (JxlToJpegDecoder.XmlBoxContentSize(jpegData, ref this.reconstructionXmpSize) != Success)
if (!JxlToJpegDecoder.XmpBoxContentSize(jpegData, ref this.reconstructionXmpSize))
{
throw new InvalidOperationException("Invalid jbrd XMP size");
}

150
src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlToJpegDecoder.cs

@ -0,0 +1,150 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Jxl.IO.Jpeg.Data;
namespace SixLabors.ImageSharp.Formats.Jxl.Processing.Decoder;
internal class JxlToJpegDecoder
{
/// <summary>
/// Returns the number of EXIF markers in the JPEG file.
/// </summary>
/// <param name="jpegData">Input parsed JPEG data</param>
/// <returns>Number of EXIFs in the JPEG</returns>
public static int NumExifMarkers(JpegData jpegData) => jpegData.AppMarkerTypes.Count(x => x == JpegAppMarkerType.Exif);
/// <summary>
/// Returns the number of XMP markers in the JPEG file.
/// </summary>
/// <param name="jpegData">Input parsed JPEG data</param>
/// <returns>Number of XMPs in the JPEG</returns>
public static int NumXmpMarkers(JpegData jpegData) => jpegData.AppMarkerTypes.Count(x => x == JpegAppMarkerType.Xmp);
/// <summary>
/// Attempts to set EXIF data in the JPEG file.
/// </summary>
/// <param name="data">EXIF data</param>
/// <param name="jpegData">JPEG file for EXIF data</param>
/// <returns>If EXIF data was set, true; returns false if no EXIF marker is present, or is present but not enough data</returns>
public static bool TrySetExif(Span<byte> data, JpegData jpegData)
{
int size = data.Length;
ReadOnlySpan<byte> exifTag = JpegDataConstants.ExifTag;
int exifTagSize = exifTag.Length;
for (int i = 0; i < jpegData.AppData.Count; ++i)
{
if (jpegData.AppMarkerTypes[i] == JpegAppMarkerType.Exif)
{
Span<byte> dataSpan = CollectionsMarshal.AsSpan(jpegData.AppData[i]);
if (dataSpan.Length != size + 3 + exifTagSize - 4)
{
return false;
}
// The first 9 bytes are used for JPEG marker header.
dataSpan[0] = 0xE1;
// The second and third byte are already filled in correctly
exifTag.CopyTo(dataSpan[3..]);
// The first 4 bytes are the TIFF header from the box contents, and are
// not included in the JPEG
data[4..].CopyTo(dataSpan[(3 + exifTagSize)..]);
return true;
}
}
return false;
}
/// <summary>
/// Attempts to set XMP data in the JPEG file.
/// </summary>
/// <param name="data">XMP data</param>
/// <param name="jpegData">JPEG file for XMP data</param>
/// <returns>If XMP data was set, true; returns false if no XMP marker is present, or is present but not enough data</returns>
public static bool TrySetXmp(Span<byte> data, JpegData jpegData)
{
int size = data.Length;
ReadOnlySpan<byte> xmpTag = JpegDataConstants.XmpTag;
int xmpTagSize = xmpTag.Length;
for (int i = 0; i < jpegData.AppData.Count; ++i)
{
if (jpegData.AppMarkerTypes[i] == JpegAppMarkerType.Xmp)
{
Span<byte> dataSpan = CollectionsMarshal.AsSpan(jpegData.AppData[i]);
if (dataSpan.Length != size + 3 + xmpTagSize)
{
return false;
}
// The first 9 bytes are used for JPEG marker header.
dataSpan[0] = 0xE1;
// The second and third byte are already filled in correctly
xmpTag.CopyTo(dataSpan[3..]);
data.CopyTo(dataSpan[(3 + xmpTagSize)..]);
return true;
}
}
return false;
}
public static bool ExifBoxContentSize(JpegData jpegData, ref long size)
{
size = 0;
int exifTagLength = JpegDataConstants.ExifTag.Length;
for (int i = 0; i < jpegData.AppData.Count; ++i)
{
if (jpegData.AppMarkerTypes[i] == JpegAppMarkerType.Exif)
{
if (jpegData.AppData[i].Count < 3 + exifTagLength)
{
// too small for app marker header
return false;
}
// The first 4 bytes are the TIFF header from the box contents, and are
// not included in the JPEG
size = jpegData.AppData[i].Count + 4 - 3 - exifTagLength;
return true;
}
}
return false;
}
public static bool XmpBoxContentSize(JpegData jpegData, ref long size)
{
size = 0;
int xmpTagLength = JpegDataConstants.XmpTag.Length;
for (int i = 0; i < jpegData.AppData.Count; ++i)
{
if (jpegData.AppMarkerTypes[i] == JpegAppMarkerType.Xmp)
{
if (jpegData.AppData[i].Count < 3 + xmpTagLength)
{
// too small for app marker header
return false;
}
size = jpegData.AppData[i].Count - 3 - xmpTagLength;
return true;
}
}
return false;
}
}
Loading…
Cancel
Save