Browse Source

Begin port

af/merge-core
James Jackson-South 9 years ago
parent
commit
7bda1a997d
  1. 2
      src/ImageSharp/Common/Extensions/StreamExtensions.cs
  2. 10
      src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
  3. 53
      src/ImageSharp/Formats/Jpeg/Port/Components/Component.cs
  4. 73
      src/ImageSharp/Formats/Jpeg/Port/Components/Frame.cs
  5. 15
      src/ImageSharp/Formats/Jpeg/Port/Components/HuffmanTables.cs
  6. 43
      src/ImageSharp/Formats/Jpeg/Port/Components/QuantizationTables.cs
  7. 6
      src/ImageSharp/Formats/Jpeg/Port/Huffman.cs
  8. 185
      src/ImageSharp/Formats/Jpeg/Port/JpegConstants.cs
  9. 522
      src/ImageSharp/Formats/Jpeg/Port/JpegDecoderCore.cs
  10. 33
      src/ImageSharp/Formats/Jpeg/Port/JpegFrame.cs
  11. 32
      src/ImageSharp/Memory/Fast2DArray{T}.cs

2
src/ImageSharp/Common/Extensions/StreamExtensions.cs

@ -27,7 +27,7 @@ namespace ImageSharp
if (stream.CanSeek)
{
stream.Position += count;
stream.Seek(count, SeekOrigin.Current); // Position += count;
}
else
{

10
src/ImageSharp/Formats/Jpeg/JpegDecoder.cs

@ -21,10 +21,12 @@ namespace ImageSharp.Formats
{
Guard.NotNull(stream, "stream");
using (JpegDecoderCore decoder = new JpegDecoderCore(options, configuration))
{
return decoder.Decode<TPixel>(stream);
}
// using (JpegDecoderCore decoder = new JpegDecoderCore(options, configuration))
// {
// return decoder.Decode<TPixel>(stream);
// }
var decoder = new Jpeg.Port.JpegDecoderCore(options, configuration);
return decoder.Decode<TPixel>(stream);
}
}
}

53
src/ImageSharp/Formats/Jpeg/Port/Components/Component.cs

@ -0,0 +1,53 @@
// <copyright file="Component.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats.Jpeg.Port.Components
{
/// <summary>
/// Represents a single color component
/// </summary>
internal struct Component
{
/// <summary>
/// Gets or sets the component Id
/// </summary>
public byte Id;
/// <summary>
/// Gets or sets the horizontal sampling factor.
/// </summary>
public int HorizontalFactor;
/// <summary>
/// Gets or sets the vertical sampling factor.
/// </summary>
public int VerticalFactor;
/// <summary>
/// Gets or sets the identifier
/// </summary>
public byte QuantizationIdentifier;
/// <summary>
/// Gets or sets the quantization table
/// </summary>
public short[] QuantizationTable;
/// <summary>
/// Gets or sets the block data
/// </summary>
public short[] BlockData;
/// <summary>
/// Gets or sets the number of blocks per line
/// </summary>
public int BlocksPerLine;
/// <summary>
/// Gets or sets the number of blocks per column
/// </summary>
public int BlocksPerColumn;
}
}

73
src/ImageSharp/Formats/Jpeg/Port/Components/Frame.cs

@ -0,0 +1,73 @@
// <copyright file="QuantizationTables.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats.Jpeg.Port.Components
{
/// <summary>
/// Represent a single jpeg frame
/// </summary>
internal class Frame
{
/// <summary>
/// Gets or sets a value indicating whether the frame uses the extended specification
/// </summary>
public bool Extended { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the frame uses the progressive specification
/// </summary>
public bool Progressive { get; set; }
/// <summary>
/// Gets or sets the precision
/// </summary>
public byte Precision { get; set; }
/// <summary>
/// Gets or sets the number of scanlines within the frame
/// </summary>
public short Scanlines { get; set; }
/// <summary>
/// Gets or sets the number of samples per scanline
/// </summary>
public short SamplesPerLine { get; set; }
/// <summary>
/// Gets or sets the number of components within a frame. In progressive frames this value can range from only 1 to 4
/// </summary>
public byte ComponentCount { get; set; }
/// <summary>
/// Gets or sets the component id collection
/// </summary>
public byte[] ComponentIds { get; set; }
/// <summary>
/// Gets or sets the frame component collection
/// </summary>
public Component[] Components { get; set; }
/// <summary>
/// Gets or sets the maximum horizontal sampling factor
/// </summary>
public int MaxHorizontalFactor { get; set; }
/// <summary>
/// Gets or sets the maximum vertical sampling factor
/// </summary>
public int MaxVerticalFactor { get; set; }
/// <summary>
/// Gets or sets the number of MCU's per line
/// </summary>
public int McusPerLine { get; set; }
/// <summary>
/// Gets or sets the number of MCU's per column
/// </summary>
public int McusPerColumn { get; set; }
}
}

15
src/ImageSharp/Formats/Jpeg/Port/Components/HuffmanTables.cs

@ -0,0 +1,15 @@
namespace ImageSharp.Formats.Jpeg.Port.Components
{
using ImageSharp.Memory;
/// <summary>
/// Defines a pair of huffman tables
/// </summary>
internal class HuffmanTables
{
/// <summary>
/// Gets or sets the quantization tables.
/// </summary>
public Fast2DArray<short> Tables { get; set; } = new Fast2DArray<short>(256, 2);
}
}

43
src/ImageSharp/Formats/Jpeg/Port/Components/QuantizationTables.cs

@ -0,0 +1,43 @@
// <copyright file="QuantizationTables.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats.Jpeg.Port.Components
{
using ImageSharp.Memory;
/// <summary>
/// Contains the quantization tables.
/// TODO: This all needs optimizing for memory. I'm just stubbing out functionality for now.
/// </summary>
internal class QuantizationTables
{
/// <summary>
/// Gets the ZigZag scan table
/// </summary>
public static byte[] DctZigZag { get; } =
{
0,
1, 8,
16, 9, 2,
3, 10, 17, 24,
32, 25, 18, 11, 4,
5, 12, 19, 26, 33, 40,
48, 41, 34, 27, 20, 13, 6,
7, 14, 21, 28, 35, 42, 49, 56,
57, 50, 43, 36, 29, 22, 15,
23, 30, 37, 44, 51, 58,
59, 52, 45, 38, 31,
39, 46, 53, 60,
61, 54, 47,
55, 62,
63
};
/// <summary>
/// Gets or sets the quantization tables.
/// </summary>
public Fast2DArray<short> Tables { get; set; } = new Fast2DArray<short>(64, 4);
}
}

6
src/ImageSharp/Formats/Jpeg/Port/Huffman.cs

@ -0,0 +1,6 @@
namespace ImageSharp.Formats.Jpeg.Port
{
class Huffman
{
}
}

185
src/ImageSharp/Formats/Jpeg/Port/JpegConstants.cs

@ -0,0 +1,185 @@
// ReSharper disable InconsistentNaming
namespace ImageSharp.Formats.Jpeg.Port
{
/// <summary>
/// Contains jpeg constant values
/// </summary>
internal static class JpegConstants
{
/// <summary>
/// Contains marker specific constants
/// </summary>
public static class Markers
{
/// <summary>
/// The prefix used for all markers.
/// </summary>
public const byte Prefix = 0xFF;
/// <summary>
/// The Start of Image marker
/// </summary>
public const ushort SOI = 0xFFD8;
/// <summary>
/// The End of Image marker
/// </summary>
public const ushort EOI = 0xFFD9;
/// <summary>
/// Application specific marker for marking the jpeg format.
/// <see href="http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html"/>
/// </summary>
public const ushort APP0 = 0xFFE0;
/// <summary>
/// Application specific marker for marking where to store metadata.
/// </summary>
public const ushort APP1 = 0xFFE1;
/// <summary>
/// Application specific marker for marking where to store ICC profile information.
/// </summary>
public const ushort APP2 = 0xFFE2;
/// <summary>
/// Application specific marker.
/// </summary>
public const ushort APP3 = 0xFFE3;
/// <summary>
/// Application specific marker.
/// </summary>
public const ushort APP4 = 0xFFE4;
/// <summary>
/// Application specific marker.
/// </summary>
public const ushort APP5 = 0xFFE5;
/// <summary>
/// Application specific marker.
/// </summary>
public const ushort APP6 = 0xFFE6;
/// <summary>
/// Application specific marker.
/// </summary>
public const ushort APP7 = 0xFFE7;
/// <summary>
/// Application specific marker.
/// </summary>
public const ushort APP8 = 0xFFE8;
/// <summary>
/// Application specific marker.
/// </summary>
public const ushort APP9 = 0xFFE9;
/// <summary>
/// Application specific marker.
/// </summary>
public const ushort APP10 = 0xFFEA;
/// <summary>
/// Application specific marker.
/// </summary>
public const ushort APP11 = 0xFFEB;
/// <summary>
/// Application specific marker.
/// </summary>
public const ushort APP12 = 0xFFEC;
/// <summary>
/// Application specific marker.
/// </summary>
public const ushort APP13 = 0xFFED;
/// <summary>
/// Application specific marker used by Adobe for storing encoding information for DCT filters.
/// </summary>
public const ushort APP14 = 0xFFEE;
/// <summary>
/// Application specific marker used by GraphicConverter to store JPEG quality.
/// </summary>
public const ushort APP15 = 0xFFEF;
/// <summary>
/// The text comment marker
/// </summary>
public const ushort COM = 0xFFFE;
/// <summary>
/// Define Quantization Table(s) marker
/// <remarks>
/// Specifies one or more quantization tables.
/// </remarks>
/// </summary>
public const ushort DQT = 0xFFDB;
/// <summary>
/// Start of Frame (baseline DCT)
/// <remarks>
/// Indicates that this is a baseline DCT-based JPEG, and specifies the width, height, number of components,
/// and component subsampling (e.g., 4:2:0).
/// </remarks>
/// </summary>
public const ushort SOF0 = 0xFFC0;
/// <summary>
/// Start Of Frame (Extended Sequential DCT)
/// <remarks>
/// Indicates that this is a progressive DCT-based JPEG, and specifies the width, height, number of components,
/// and component subsampling (e.g., 4:2:0).
/// </remarks>
/// </summary>
public const ushort SOF1 = 0xFFC1;
/// <summary>
/// Start Of Frame (progressive DCT)
/// <remarks>
/// Indicates that this is a progressive DCT-based JPEG, and specifies the width, height, number of components,
/// and component subsampling (e.g., 4:2:0).
/// </remarks>
/// </summary>
public const ushort SOF2 = 0xFFC2;
/// <summary>
/// Define Huffman Table(s)
/// <remarks>
/// Specifies one or more Huffman tables.
/// </remarks>
/// </summary>
public const ushort DHT = 0xFFC4;
/// <summary>
/// Contains JFIF specific markers
/// </summary>
public static class JFif
{
/// <summary>
/// Represents J in ASCII
/// </summary>
public const byte J = 0x4A;
/// <summary>
/// Represents F in ASCII
/// </summary>
public const byte F = 0x46;
/// <summary>
/// Represents I in ASCII
/// </summary>
public const byte I = 0x49;
/// <summary>
/// Represents the null "0" marker
/// </summary>
public const byte Null = 0;
}
}
}
}

522
src/ImageSharp/Formats/Jpeg/Port/JpegDecoderCore.cs

@ -0,0 +1,522 @@
// <copyright file="JpegDecoderCore.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats.Jpeg.Port
{
using System;
using System.Buffers;
using System.IO;
using ImageSharp.Formats.Jpeg.Port.Components;
using ImageSharp.PixelFormats;
/// <summary>
/// Performs the jpeg decoding operation.
/// </summary>
internal class JpegDecoderCore
{
/// <summary>
/// The decoder options.
/// </summary>
private readonly IDecoderOptions options;
/// <summary>
/// The global configuration
/// </summary>
private readonly Configuration configuration;
/// <summary>
/// Gets the temporary buffer used to store bytes read from the stream.
/// </summary>
private readonly byte[] temp = new byte[2 * 16 * 4];
private readonly byte[] uint16Buffer = new byte[2];
private QuantizationTables quantizationTables;
private HuffmanTables dcHuffmanTables;
private HuffmanTables acHuffmanTables;
private Frame frame;
/// <summary>
/// COntains information about the jFIF marker
/// </summary>
private JFif jFif;
/// <summary>
/// Whether the image has a EXIF header
/// </summary>
private bool isExif;
private int offset;
/// <summary>
/// Initializes a new instance of the <see cref="JpegDecoderCore" /> class.
/// </summary>
/// <param name="options">The decoder options.</param>
/// <param name="configuration">The configuration.</param>
public JpegDecoderCore(IDecoderOptions options, Configuration configuration)
{
this.configuration = configuration ?? Configuration.Default;
this.options = options ?? new DecoderOptions();
}
/// <summary>
/// Gets the input stream.
/// </summary>
public Stream InputStream { get; private set; }
/// <summary>
/// Decodes the image from the specified <see cref="Stream"/> and sets
/// the data to image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="stream">The stream, where the image should be.</param>
/// <returns>The decoded image.</returns>
public Image<TPixel> Decode<TPixel>(Stream stream)
where TPixel : struct, IPixel<TPixel>
{
this.InputStream = stream;
this.ParseStream();
var image = new Image<TPixel>(1, 1);
return image;
}
private void ParseStream()
{
// Check for the Start Of Image marker.
ushort fileMarker = this.ReadUint16();
if (fileMarker != JpegConstants.Markers.SOI)
{
throw new ImageFormatException("Missing SOI marker.");
}
fileMarker = this.ReadUint16();
this.quantizationTables = new QuantizationTables();
while (fileMarker != JpegConstants.Markers.EOI)
{
// Get the marker length
int remaining = this.ReadUint16() - 2;
switch (fileMarker)
{
case JpegConstants.Markers.APP0:
this.ProcessApplicationHeaderMarker(remaining);
break;
case JpegConstants.Markers.APP1:
case JpegConstants.Markers.APP2:
case JpegConstants.Markers.APP3:
case JpegConstants.Markers.APP4:
case JpegConstants.Markers.APP5:
case JpegConstants.Markers.APP6:
case JpegConstants.Markers.APP7:
case JpegConstants.Markers.APP8:
case JpegConstants.Markers.APP9:
case JpegConstants.Markers.APP10:
case JpegConstants.Markers.APP11:
case JpegConstants.Markers.APP12:
case JpegConstants.Markers.APP13:
case JpegConstants.Markers.APP14:
case JpegConstants.Markers.APP15:
case JpegConstants.Markers.COM:
break;
case JpegConstants.Markers.DQT:
this.ProcessDqtMarker(remaining);
break;
case JpegConstants.Markers.SOF0:
case JpegConstants.Markers.SOF1:
case JpegConstants.Markers.SOF2:
this.ProcessStartOfFrameMarker(remaining, fileMarker);
break;
case JpegConstants.Markers.DHT:
this.ProcessDefineHuffmanTablesMarker(remaining);
break;
}
// Read on
fileMarker = this.FindNextFileMarker();
}
}
/// <summary>
/// Processes the application header containing the JFIF identifier plus extra data.
/// </summary>
/// <param name="remaining">The remaining bytes in the segment block.</param>
private void ProcessApplicationHeaderMarker(int remaining)
{
if (remaining < 5)
{
// Skip the application header length
this.InputStream.Skip(remaining);
return;
}
this.InputStream.Read(this.temp, 0, 13);
remaining -= 13;
bool isJfif = this.temp[0] == JpegConstants.Markers.JFif.J &&
this.temp[1] == JpegConstants.Markers.JFif.F &&
this.temp[2] == JpegConstants.Markers.JFif.I &&
this.temp[3] == JpegConstants.Markers.JFif.F &&
this.temp[4] == JpegConstants.Markers.JFif.Null;
if (isJfif)
{
this.jFif = new JFif
{
MajorVersion = this.temp[5],
MinorVersion = this.temp[6],
DensityUnits = this.temp[7],
XDensity = (short)((this.temp[8] << 8) | this.temp[9]),
YDensity = (short)((this.temp[10] << 8) | this.temp[11])
};
}
// Skip thumbnails for now.
if (remaining > 0)
{
this.InputStream.Skip(remaining);
}
}
/// <summary>
/// Processes the Define Quantization Marker and tables. Specified in section B.2.4.1.
/// </summary>
/// <param name="remaining">The remaining bytes in the segment block.</param>
/// <exception cref="ImageFormatException">
/// Thrown if the tables do not match the header
/// </exception>
private void ProcessDqtMarker(int remaining)
{
while (remaining > 0)
{
bool done = false;
remaining--;
int quantizationTableSpec = this.InputStream.ReadByte();
if (quantizationTableSpec > 3)
{
throw new ImageFormatException("Bad Tq index value");
}
switch (quantizationTableSpec >> 4)
{
case 0:
{
// 8 bit values
if (remaining < 64)
{
done = true;
break;
}
this.InputStream.Read(this.temp, 0, 64);
remaining -= 64;
Span<short> tableSpan = this.quantizationTables.Tables.GetRowSpan(quantizationTableSpec);
for (int j = 0; j < 64; j++)
{
tableSpan[QuantizationTables.DctZigZag[j]] = this.temp[j];
}
}
break;
case 1:
{
// 16 bit values
if (remaining < 128)
{
done = true;
break;
}
this.InputStream.Read(this.temp, 0, 128);
remaining -= 128;
Span<short> tableSpan = this.quantizationTables.Tables.GetRowSpan(quantizationTableSpec);
for (int j = 0; j < 64; j++)
{
tableSpan[QuantizationTables.DctZigZag[j]] = (short)((this.temp[2 * j] << 8) | this.temp[(2 * j) + 1]);
}
}
break;
default:
throw new ImageFormatException("Bad Tq index value");
}
if (done)
{
break;
}
}
if (remaining != 0)
{
throw new ImageFormatException("DQT has wrong length");
}
}
/// <summary>
/// Processes the Start of Frame marker. Specified in section B.2.2.
/// </summary>
/// <param name="remaining">The remaining bytes in the segment block.</param>
/// <param name="frameMarker">The current frame marker.</param>
private void ProcessStartOfFrameMarker(int remaining, ushort frameMarker)
{
if (this.frame != null)
{
throw new ImageFormatException("Multiple SOF markers. Only single frame jpegs supported.");
}
this.InputStream.Read(this.temp, 0, remaining);
this.frame = new Frame
{
Extended = frameMarker == JpegConstants.Markers.SOF1,
Progressive = frameMarker == JpegConstants.Markers.SOF1,
Precision = this.temp[0],
Scanlines = (short)((this.temp[1] << 8) | this.temp[2]),
SamplesPerLine = (short)((this.temp[3] << 8) | this.temp[4]),
ComponentCount = this.temp[5]
};
int maxH = 0;
int maxV = 0;
int index = 6;
// TODO: Pool this.
this.frame.ComponentIds = new byte[this.frame.ComponentCount];
this.frame.Components = new Component[this.frame.ComponentCount];
for (int i = 0; i < this.frame.ComponentCount; i++)
{
int h = this.temp[index + 1] >> 4;
int v = this.temp[index + 1] & 15;
if (maxH < h)
{
maxH = h;
}
if (maxV < v)
{
maxV = v;
}
ref var component = ref this.frame.Components[i];
component.Id = this.temp[index];
component.HorizontalFactor = h;
component.VerticalFactor = v;
component.QuantizationIdentifier = this.temp[index + 2];
// Don't assign the table yet.
index += 3;
}
this.frame.MaxHorizontalFactor = maxH;
this.frame.MaxVerticalFactor = maxV;
this.PrepareComponents();
}
/// <summary>
/// Processes a Define Huffman Table marker, and initializes a huffman
/// struct from its contents. Specified in section B.2.4.2.
/// </summary>
/// <param name="remaining">The remaining bytes in the segment block.</param>
private void ProcessDefineHuffmanTablesMarker(int remaining)
{
if (remaining < 17)
{
throw new ImageFormatException("DHT has wrong length");
}
// TODO: Pool this
byte[] huffmanData = new byte[remaining];
this.InputStream.Read(huffmanData, 0, remaining);
int o = 0;
for (int i = 0; i < remaining;)
{
byte huffmanTableSpec = huffmanData[i];
byte[] codeLengths = new byte[16];
int codeLengthSum = 0;
for (int j = 0; j < 16; j++)
{
codeLengthSum += codeLengths[j] = huffmanData[o++];
}
short[] huffmanValues = new short[codeLengthSum];
byte[] values = null;
try
{
values = ArrayPool<byte>.Shared.Rent(256);
this.InputStream.Read(values, 0, codeLengthSum);
for (int j = 0; j < codeLengthSum; j++)
{
huffmanValues[j] = values[o++];
}
}
finally
{
if (values != null)
{
ArrayPool<byte>.Shared.Return(values);
}
}
i += 17 + codeLengthSum;
this.BuildHuffmanTable(
huffmanTableSpec >> 4 == 0 ? this.dcHuffmanTables : this.acHuffmanTables,
huffmanTableSpec & 15,
codeLengths,
huffmanValues);
}
}
private void BuildHuffmanTable(HuffmanTables tables, int index, byte[] codeLengths, short[] values)
{
Span<short> tableSpan = tables.Tables.GetRowSpan(index);
}
/// <summary>
/// Allocates the frame component blocks
/// </summary>
private void PrepareComponents()
{
int mcusPerLine = this.frame.SamplesPerLine / 8 / this.frame.MaxHorizontalFactor;
int mcusPerColumn = this.frame.Scanlines / 8 / this.frame.MaxVerticalFactor;
for (int i = 0; i < this.frame.ComponentCount; i++)
{
ref var component = ref this.frame.Components[i];
int blocksPerLine = this.frame.SamplesPerLine / 8 * component.HorizontalFactor / this.frame.MaxHorizontalFactor;
int blocksPerColumn = this.frame.Scanlines / 8 * component.VerticalFactor / this.frame.MaxVerticalFactor;
int blocksPerLineForMcu = mcusPerLine * component.HorizontalFactor;
int blocksPerColumnForMcu = mcusPerColumn * component.VerticalFactor;
int blocksBufferSize = 64 * blocksPerColumnForMcu * (blocksPerLineForMcu + 1);
// TODO: Pool this
component.BlockData = new short[blocksBufferSize];
component.BlocksPerLine = blocksPerLine;
component.BlocksPerColumn = blocksPerColumn;
}
this.frame.McusPerLine = mcusPerLine;
this.frame.McusPerColumn = mcusPerColumn;
}
/// <summary>
/// Finds the next file marker within the byte stream
/// </summary>
/// <returns>The <see cref="ushort"/></returns>
private ushort FindNextFileMarker()
{
while (true)
{
int value = this.InputStream.Read(this.uint16Buffer, 0, 2);
if (value <= 0)
{
return JpegConstants.Markers.EOI;
}
while (this.uint16Buffer[0] != JpegConstants.Markers.Prefix)
{
// Strictly speaking, this is a format error. However, libjpeg is
// liberal in what it accepts. As of version 9, next_marker in
// jdmarker.c treats this as a warning (JWRN_EXTRANEOUS_DATA) and
// continues to decode the stream. Even before next_marker sees
// extraneous data, jpeg_fill_bit_buffer in jdhuff.c reads as many
// bytes as it can, possibly past the end of a scan's data. It
// effectively puts back any markers that it overscanned (e.g. an
// "\xff\xd9" EOI marker), but it does not put back non-marker data,
// and thus it can silently ignore a small number of extraneous
// non-marker bytes before next_marker has a chance to see them (and
// print a warning).
// We are therefore also liberal in what we accept. Extraneous data
// is silently ignore
// This is similar to, but not exactly the same as, the restart
// mechanism within a scan (the RST[0-7] markers).
// Note that extraneous 0xff bytes in e.g. SOS data are escaped as
// "\xff\x00", and so are detected a little further down below.
this.uint16Buffer[0] = this.uint16Buffer[1];
value = this.InputStream.ReadByte();
if (value <= 0)
{
return JpegConstants.Markers.EOI;
}
this.uint16Buffer[1] = (byte)value;
}
return (ushort)((this.uint16Buffer[0] << 8) | this.uint16Buffer[1]);
}
}
/// <summary>
/// Reads a <see cref="ushort"/> from the stream advancing it by two bytes
/// </summary>
/// <returns>The <see cref="ushort"/></returns>
private ushort ReadUint16()
{
this.InputStream.Read(this.uint16Buffer, 0, 2);
ushort value = (ushort)((this.uint16Buffer[0] << 8) | this.uint16Buffer[1]);
this.offset += 2;
return value;
}
/// <summary>
/// Provides information about the JFIF marker segment
/// </summary>
internal struct JFif
{
/// <summary>
/// The major version
/// </summary>
public byte MajorVersion;
/// <summary>
/// The minor version
/// </summary>
public byte MinorVersion;
/// <summary>
/// Units for the following pixel density fields
/// 00 : No units; width:height pixel aspect ratio = Ydensity:Xdensity
/// 01 : Pixels per inch (2.54 cm)
/// 02 : Pixels per centimeter
/// </summary>
public byte DensityUnits;
/// <summary>
/// Horizontal pixel density. Must not be zero.
/// </summary>
public short XDensity;
/// <summary>
/// Vertical pixel density. Must not be zero.
/// </summary>
public short YDensity;
// TODO: Thumbnail?
}
}
}

33
src/ImageSharp/Formats/Jpeg/Port/JpegFrame.cs

@ -0,0 +1,33 @@
namespace ImageSharp.Formats.Jpeg.Port
{
/// <summary>
/// Represents a jpeg frame
/// </summary>
internal class JpegFrame
{
/// <summary>
/// Gets or sets a value indicating whether the fame is extended
/// </summary>
public bool Extended { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the fame is progressive
/// </summary>
public bool Progressive { get; set; }
/// <summary>
/// Gets or sets the precision
/// </summary>
public byte Precision { get; set; }
/// <summary>
/// Gets or sets the number of scanlines within the frame
/// </summary>
public short Scanlines { get; set; }
/// <summary>
/// Gets or sets the number of samples per scanline
/// </summary>
public short SamplesPerLine { get; set; }
}
}

32
src/ImageSharp/Memory/Fast2DArray{T}.cs

@ -106,11 +106,23 @@ namespace ImageSharp.Memory
return new Fast2DArray<T>(data);
}
/// <summary>
/// Gets a <see cref="Span{T}"/> representing the row beginning from the the first item on that row.
/// </summary>
/// <param name="row">The y-coordinate of the row. Must be greater than or equal to zero and less than the height of the 2D array.</param>
/// <returns>The <see cref="Span{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> GetRowSpan(int row)
{
this.CheckCoordinates(row);
return new Span<T>(this.Data, row * this.Width, this.Width);
}
/// <summary>
/// Checks the coordinates to ensure they are within bounds.
/// </summary>
/// <param name="row">The row-coordinate of the item. Must be greater than zero and smaller than the height of the array.</param>
/// <param name="column">The column-coordinate of the item. Must be greater than zero and smaller than the width of the array.</param>
/// <param name="row">The y-coordinate of the item. Must be greater than zero and smaller than the height of the array.</param>
/// <param name="column">The x-coordinate of the item. Must be greater than zero and smaller than the width of the array.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if the coordinates are not within the bounds of the array.
/// </exception>
@ -127,5 +139,21 @@ namespace ImageSharp.Memory
throw new ArgumentOutOfRangeException(nameof(column), column, $"{column} is outwith the array bounds.");
}
}
/// <summary>
/// Checks the coordinates to ensure they are within bounds.
/// </summary>
/// <param name="row">The y-coordinate of the item. Must be greater than zero and smaller than the height of the array.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown if the coordinates are not within the bounds of the image.
/// </exception>
[Conditional("DEBUG")]
private void CheckCoordinates(int row)
{
if (row < 0 || row >= this.Height)
{
throw new ArgumentOutOfRangeException(nameof(row), row, $"{row} is outwith the array bounds.");
}
}
}
}
Loading…
Cancel
Save