diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs
index ecebe9480..bf2f64b34 100644
--- a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoder.cs
@@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
///
public IImageInfo Identify(Configuration configuration, Stream stream)
{
- Guard.NotNull(stream, "stream");
+ Guard.NotNull(stream, nameof(stream));
using (var decoder = new OrigJpegDecoderCore(configuration, this))
{
diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs
index 37ce0151f..e12278cc7 100644
--- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoder.cs
@@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
///
/// Image decoder for generating an image out of a jpg stream.
///
- internal sealed class PdfJsJpegDecoder : IImageDecoder, IJpegDecoderOptions
+ internal sealed class PdfJsJpegDecoder : IImageDecoder, IJpegDecoderOptions, IImageInfoDetector
{
///
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
@@ -27,5 +27,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
return decoder.Decode(stream);
}
}
+
+ ///
+ public IImageInfo Identify(Configuration configuration, Stream stream)
+ {
+ Guard.NotNull(stream, nameof(stream));
+
+ using (var decoder = new PdfJsJpegDecoderCore(configuration, this))
+ {
+ return decoder.Identify(stream);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
index 5d18ec78f..c1e89dc0e 100644
--- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
@@ -24,6 +24,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
///
internal sealed class PdfJsJpegDecoderCore : IDisposable
{
+ ///
+ /// The only supported precision
+ ///
+ public const int SupportedPrecision = 8;
+
#pragma warning disable SA1401 // Fields should be private
///
/// The global configuration
@@ -103,6 +108,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
///
public int NumberOfComponents { get; private set; }
+ ///
+ /// Gets the color depth, in number of bits per pixel.
+ ///
+ public int BitsPerPixel => this.NumberOfComponents * SupportedPrecision;
+
///
/// Gets the input stream.
///
@@ -113,6 +123,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
///
public bool IgnoreMetadata { get; }
+ ///
+ /// Gets the decoded by this decoder instance.
+ ///
+ public ImageMetaData MetaData { get; private set; }
+
///
/// Finds the next file marker within the byte stream.
///
@@ -158,55 +173,36 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
public Image Decode(Stream stream)
where TPixel : struct, IPixel
{
- ImageMetaData metadata = this.ParseStream(stream);
-
+ this.ParseStream(stream);
this.QuantizeAndInverseAllComponents();
- var image = new Image(this.configuration, this.ImageWidth, this.ImageHeight, metadata);
+ var image = new Image(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData);
this.FillPixelData(image.Frames.RootFrame);
- this.AssignResolution(image);
+ this.AssignResolution();
return image;
}
- ///
- public void Dispose()
- {
- this.Frame?.Dispose();
- this.components?.Dispose();
- this.quantizationTables?.Dispose();
- this.pixelArea.Dispose();
-
- // Set large fields to null.
- this.Frame = null;
- this.components = null;
- this.quantizationTables = null;
- this.dcHuffmanTables = null;
- this.acHuffmanTables = null;
- }
-
- internal ImageMetaData ParseStream(Stream stream)
- {
- this.InputStream = stream;
-
- var metadata = new ImageMetaData();
- this.ParseStream(metadata, false);
- return metadata;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static int GetBlockBufferOffset(ref PdfJsComponent component, int row, int col)
+ ///
+ /// Reads the raw image information from the specified stream.
+ ///
+ /// The containing image data.
+ public IImageInfo Identify(Stream stream)
{
- return 64 * (((component.BlocksPerLine + 1) * row) + col);
+ this.ParseStream(stream, true);
+ this.AssignResolution();
+ return new ImageInfo(new PixelTypeInfo(this.BitsPerPixel), this.ImageWidth, this.ImageHeight, this.MetaData);
}
///
/// Parses the input stream for file markers
///
- /// Contains the metadata for an image
+ /// The input stream
/// Whether to decode metadata only.
- private void ParseStream(ImageMetaData metaData, bool metadataOnly)
+ public void ParseStream(Stream stream, bool metadataOnly = false)
{
- // TODO: metadata only logic
+ this.MetaData = new ImageMetaData();
+ this.InputStream = stream;
+
// Check for the Start Of Image marker.
var fileMarker = new PdfJsFileMarker(this.ReadUint16(), 0);
if (fileMarker.Marker != PdfJsJpegConstants.Markers.SOI)
@@ -233,11 +229,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
break;
case PdfJsJpegConstants.Markers.APP1:
- this.ProcessApp1Marker(remaining, metaData);
+ this.ProcessApp1Marker(remaining);
break;
case PdfJsJpegConstants.Markers.APP2:
- this.ProcessApp2Marker(remaining, metaData);
+ this.ProcessApp2Marker(remaining);
break;
case PdfJsJpegConstants.Markers.APP3:
case PdfJsJpegConstants.Markers.APP4:
@@ -263,24 +259,58 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
break;
case PdfJsJpegConstants.Markers.DQT:
- this.ProcessDefineQuantizationTablesMarker(remaining);
+ if (metadataOnly)
+ {
+ this.InputStream.Skip(remaining);
+ }
+ else
+ {
+ this.ProcessDefineQuantizationTablesMarker(remaining);
+ }
+
break;
case PdfJsJpegConstants.Markers.SOF0:
case PdfJsJpegConstants.Markers.SOF1:
case PdfJsJpegConstants.Markers.SOF2:
this.ProcessStartOfFrameMarker(remaining, fileMarker);
+ if (metadataOnly && !this.jFif.Equals(default))
+ {
+ this.InputStream.Skip(remaining);
+ }
+
break;
case PdfJsJpegConstants.Markers.DHT:
- this.ProcessDefineHuffmanTablesMarker(remaining);
+ if (metadataOnly)
+ {
+ this.InputStream.Skip(remaining);
+ }
+ else
+ {
+ this.ProcessDefineHuffmanTablesMarker(remaining);
+ }
+
break;
case PdfJsJpegConstants.Markers.DRI:
- this.ProcessDefineRestartIntervalMarker(remaining);
+ if (metadataOnly)
+ {
+ this.InputStream.Skip(remaining);
+ }
+ else
+ {
+ this.ProcessDefineRestartIntervalMarker(remaining);
+ }
+
break;
case PdfJsJpegConstants.Markers.SOS:
+ if (metadataOnly)
+ {
+ return;
+ }
+
this.ProcessStartOfScanMarker();
break;
}
@@ -312,6 +342,28 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
this.NumberOfComponents = this.components.Components.Length;
}
+ ///
+ public void Dispose()
+ {
+ this.Frame?.Dispose();
+ this.components?.Dispose();
+ this.quantizationTables?.Dispose();
+ this.pixelArea.Dispose();
+
+ // Set large fields to null.
+ this.Frame = null;
+ this.components = null;
+ this.quantizationTables = null;
+ this.dcHuffmanTables = null;
+ this.acHuffmanTables = null;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int GetBlockBufferOffset(ref PdfJsComponent component, int row, int col)
+ {
+ return 64 * (((component.BlocksPerLine + 1) * row) + col);
+ }
+
internal void QuantizeAndInverseAllComponents()
{
for (int i = 0; i < this.components.Components.Length; i++)
@@ -373,31 +425,28 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
///
/// Assigns the horizontal and vertical resolution to the image if it has a JFIF header or EXIF metadata.
///
- /// The pixel format.
- /// The image to assign the resolution to.
- private void AssignResolution(Image image)
- where TPixel : struct, IPixel
+ private void AssignResolution()
{
if (this.isExif)
{
- double horizontalValue = image.MetaData.ExifProfile.TryGetValue(ExifTag.XResolution, out ExifValue horizontalTag)
+ double horizontalValue = this.MetaData.ExifProfile.TryGetValue(ExifTag.XResolution, out ExifValue horizontalTag)
? ((Rational)horizontalTag.Value).ToDouble()
: 0;
- double verticalValue = image.MetaData.ExifProfile.TryGetValue(ExifTag.YResolution, out ExifValue verticalTag)
+ double verticalValue = this.MetaData.ExifProfile.TryGetValue(ExifTag.YResolution, out ExifValue verticalTag)
? ((Rational)verticalTag.Value).ToDouble()
: 0;
if (horizontalValue > 0 && verticalValue > 0)
{
- image.MetaData.HorizontalResolution = horizontalValue;
- image.MetaData.VerticalResolution = verticalValue;
+ this.MetaData.HorizontalResolution = horizontalValue;
+ this.MetaData.VerticalResolution = verticalValue;
}
}
else if (this.jFif.XDensity > 0 && this.jFif.YDensity > 0)
{
- image.MetaData.HorizontalResolution = this.jFif.XDensity;
- image.MetaData.VerticalResolution = this.jFif.YDensity;
+ this.MetaData.HorizontalResolution = this.jFif.XDensity;
+ this.MetaData.VerticalResolution = this.jFif.YDensity;
}
}
@@ -430,8 +479,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
/// Processes the App1 marker retrieving any stored metadata
///
/// The remaining bytes in the segment block.
- /// The image.
- private void ProcessApp1Marker(int remaining, ImageMetaData metadata)
+ private void ProcessApp1Marker(int remaining)
{
if (remaining < 6 || this.IgnoreMetadata)
{
@@ -451,7 +499,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
profile[5] == PdfJsJpegConstants.Markers.Exif.Null)
{
this.isExif = true;
- metadata.ExifProfile = new ExifProfile(profile);
+ this.MetaData.ExifProfile = new ExifProfile(profile);
}
}
@@ -459,8 +507,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
/// Processes the App2 marker retrieving any stored ICC profile information
///
/// The remaining bytes in the segment block.
- /// The image.
- private void ProcessApp2Marker(int remaining, ImageMetaData metadata)
+ private void ProcessApp2Marker(int remaining)
{
// Length is 14 though we only need to check 12.
const int Icclength = 14;
@@ -490,13 +537,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
byte[] profile = new byte[remaining];
this.InputStream.Read(profile, 0, remaining);
- if (metadata.IccProfile == null)
+ if (this.MetaData.IccProfile == null)
{
- metadata.IccProfile = new IccProfile(profile);
+ this.MetaData.IccProfile = new IccProfile(profile);
}
else
{
- metadata.IccProfile.Extend(profile);
+ this.MetaData.IccProfile.Extend(profile);
}
}
else
@@ -619,6 +666,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
this.InputStream.Read(this.temp, 0, remaining);
+ // We only support 8-bit precision.
+ if (this.temp[0] != SupportedPrecision)
+ {
+ throw new ImageFormatException("Only 8-Bit precision supported.");
+ }
+
this.Frame = new PdfJsFrame
{
Extended = frameMarker.Marker == PdfJsJpegConstants.Markers.SOF1,
@@ -791,7 +844,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
int blocksPerLine = component.BlocksPerLine;
int blocksPerColumn = component.BlocksPerColumn;
using (IBuffer computationBuffer = this.configuration.MemoryManager.Allocate(64, true))
- using (IBuffer multiplicationBuffer = this.configuration.MemoryManager.Allocate(64, true))
{
ref short quantizationTableRef = ref MemoryMarshal.GetReference(this.quantizationTables.Tables.GetRowSpan(frameComponent.QuantizationTableIndex));
ref short computationBufferSpan = ref MemoryMarshal.GetReference(computationBuffer.Span);