mirror of https://github.com/SixLabors/ImageSharp
85 changed files with 3625 additions and 2961 deletions
@ -0,0 +1,6 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<RuleSet Name="ImageSharp" ToolsVersion="14.0"> |
||||
|
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers"> |
||||
|
<Rule Id="SA1413" Action="None" /> |
||||
|
</Rules> |
||||
|
</RuleSet> |
||||
@ -1,6 +1,6 @@ |
|||||
{ |
{ |
||||
"projects": [ "src" ], |
"projects": [ "src" ], |
||||
"sdk": { |
"sdk": { |
||||
"version": "1.0.0-preview2-003121" |
"version": "1.0.0-preview2-003131" |
||||
} |
} |
||||
} |
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
// <copyright file="Constants.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Common constants used throughout the project
|
||||
|
/// </summary>
|
||||
|
internal static class Constants |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The epsilon for comparing floating point numbers.
|
||||
|
/// </summary>
|
||||
|
public static readonly float Epsilon = 0.001f; |
||||
|
} |
||||
|
} |
||||
@ -1,101 +0,0 @@ |
|||||
// <copyright file="GrayImage.cs" company="James Jackson-South">
|
|
||||
// Copyright (c) James Jackson-South and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
// </copyright>
|
|
||||
|
|
||||
namespace ImageSharp.Formats.Jpg |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Represents a grayscale image
|
|
||||
/// </summary>
|
|
||||
internal class GrayImage |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="GrayImage"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="width">The width.</param>
|
|
||||
/// <param name="height">The height.</param>
|
|
||||
public GrayImage(int width, int height) |
|
||||
{ |
|
||||
this.Width = width; |
|
||||
this.Height = height; |
|
||||
this.Pixels = new byte[width * height]; |
|
||||
this.Stride = width; |
|
||||
this.Offset = 0; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Prevents a default instance of the <see cref="GrayImage"/> class from being created.
|
|
||||
/// </summary>
|
|
||||
private GrayImage() |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the pixels.
|
|
||||
/// </summary>
|
|
||||
public byte[] Pixels { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the stride.
|
|
||||
/// </summary>
|
|
||||
public int Stride { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the horizontal position.
|
|
||||
/// </summary>
|
|
||||
public int X { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the vertical position.
|
|
||||
/// </summary>
|
|
||||
public int Y { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the width.
|
|
||||
/// </summary>
|
|
||||
public int Width { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the height.
|
|
||||
/// </summary>
|
|
||||
public int Height { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the offset
|
|
||||
/// </summary>
|
|
||||
public int Offset { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets an image made up of a subset of the originals pixels.
|
|
||||
/// </summary>
|
|
||||
/// <param name="x">The x-coordinate of the image.</param>
|
|
||||
/// <param name="y">The y-coordinate of the image.</param>
|
|
||||
/// <param name="width">The width.</param>
|
|
||||
/// <param name="height">The height.</param>
|
|
||||
/// <returns>
|
|
||||
/// The <see cref="GrayImage"/>.
|
|
||||
/// </returns>
|
|
||||
public GrayImage Subimage(int x, int y, int width, int height) |
|
||||
{ |
|
||||
return new GrayImage |
|
||||
{ |
|
||||
Width = width, |
|
||||
Height = height, |
|
||||
Pixels = this.Pixels, |
|
||||
Stride = this.Stride, |
|
||||
Offset = (y * this.Stride) + x |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the row offset at the given position
|
|
||||
/// </summary>
|
|
||||
/// <param name="y">The y-coordinate of the image.</param>
|
|
||||
/// <returns>The <see cref="int"/></returns>
|
|
||||
public int GetRowOffset(int y) |
|
||||
{ |
|
||||
return this.Offset + (y * this.Stride); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,132 @@ |
|||||
|
// <copyright file="JpegPixelArea.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
namespace ImageSharp.Formats.Jpg |
||||
|
{ |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents an area of a Jpeg subimage (channel)
|
||||
|
/// </summary>
|
||||
|
internal struct JpegPixelArea |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="JpegPixelArea" /> struct from existing data.
|
||||
|
/// </summary>
|
||||
|
/// <param name="pixels">The pixel array</param>
|
||||
|
/// <param name="striede">The stride</param>
|
||||
|
/// <param name="offset">The offset</param>
|
||||
|
public JpegPixelArea(byte[] pixels, int striede, int offset) |
||||
|
{ |
||||
|
this.Stride = striede; |
||||
|
this.Pixels = pixels; |
||||
|
this.Offset = offset; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the pixels.
|
||||
|
/// </summary>
|
||||
|
public byte[] Pixels { get; private set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value indicating whether the instance has been initalized. (Is not default(JpegPixelArea))
|
||||
|
/// </summary>
|
||||
|
public bool IsInitialized => this.Pixels != null; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or the stride.
|
||||
|
/// </summary>
|
||||
|
public int Stride { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or the offset.
|
||||
|
/// </summary>
|
||||
|
public int Offset { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a <see cref="MutableSpan{T}" /> of bytes to the pixel area
|
||||
|
/// </summary>
|
||||
|
public MutableSpan<byte> Span => new MutableSpan<byte>(this.Pixels, this.Offset); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns the pixel at (x, y)
|
||||
|
/// </summary>
|
||||
|
/// <param name="x">The x index</param>
|
||||
|
/// <param name="y">The y index</param>
|
||||
|
/// <returns>The pixel value</returns>
|
||||
|
public byte this[int x, int y] |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get |
||||
|
{ |
||||
|
return this.Pixels[(y * this.Stride) + x]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates a new instance of the <see cref="JpegPixelArea" /> struct.
|
||||
|
/// Pixel array will be taken from a pool, this instance will be the owner of it's pixel data, therefore
|
||||
|
/// <see cref="ReturnPooled" /> should be called when the instance is no longer needed.
|
||||
|
/// </summary>
|
||||
|
/// <param name="width">The width.</param>
|
||||
|
/// <param name="height">The height.</param>
|
||||
|
/// <returns>A <see cref="JpegPixelArea" /> with pooled data</returns>
|
||||
|
public static JpegPixelArea CreatePooled(int width, int height) |
||||
|
{ |
||||
|
int size = width * height; |
||||
|
var pixels = CleanPooler<byte>.RentCleanArray(size); |
||||
|
return new JpegPixelArea(pixels, width, 0); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns <see cref="Pixels" /> to the pool
|
||||
|
/// </summary>
|
||||
|
public void ReturnPooled() |
||||
|
{ |
||||
|
if (this.Pixels == null) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
CleanPooler<byte>.ReturnArray(this.Pixels); |
||||
|
this.Pixels = null; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the subarea that belongs to the Block8x8 defined by block indices
|
||||
|
/// </summary>
|
||||
|
/// <param name="bx">The block X index</param>
|
||||
|
/// <param name="by">The block Y index</param>
|
||||
|
/// <returns>The subarea offseted by block indices</returns>
|
||||
|
public JpegPixelArea GetOffsetedSubAreaForBlock(int bx, int by) |
||||
|
{ |
||||
|
int offset = this.Offset + (8 * ((by * this.Stride) + bx)); |
||||
|
return new JpegPixelArea(this.Pixels, this.Stride, offset); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the row offset at the given position
|
||||
|
/// </summary>
|
||||
|
/// <param name="y">The y-coordinate of the image.</param>
|
||||
|
/// <returns>The <see cref="int" /></returns>
|
||||
|
public int GetRowOffset(int y) |
||||
|
{ |
||||
|
return this.Offset + (y * this.Stride); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Load values to the pixel area from the given <see cref="Block8x8F" />.
|
||||
|
/// Level shift [-128.0, 128.0] floating point color values by +128, clip them to [0, 255], and convert them to
|
||||
|
/// <see cref="byte" /> values
|
||||
|
/// </summary>
|
||||
|
/// <param name="block">The block holding the color values</param>
|
||||
|
/// <param name="temp">Temporal block provided by the caller</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public unsafe void LoadColorsFrom(Block8x8F* block, Block8x8F* temp) |
||||
|
{ |
||||
|
// Level shift by +128, clip to [0, 255], and write to dst.
|
||||
|
block->CopyColorsTo(new MutableSpan<byte>(this.Pixels, this.Offset), this.Stride, temp); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,653 @@ |
|||||
|
// ReSharper disable InconsistentNaming
|
||||
|
namespace ImageSharp.Formats.Jpg |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Encapsulates the impementation of Jpeg SOS decoder.
|
||||
|
/// See JpegScanDecoder.md!
|
||||
|
/// </summary>
|
||||
|
internal unsafe struct JpegScanDecoder |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The AC table index
|
||||
|
/// </summary>
|
||||
|
internal const int AcTableIndex = 1; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The DC table index
|
||||
|
/// </summary>
|
||||
|
internal const int DcTableIndex = 0; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Holds the "large" data blocks needed for computations
|
||||
|
/// </summary>
|
||||
|
[StructLayout(LayoutKind.Sequential)] |
||||
|
public struct ComponentData |
||||
|
{ |
||||
|
public Block8x8F Block; |
||||
|
|
||||
|
public Block8x8F Temp1; |
||||
|
|
||||
|
public Block8x8F Temp2; |
||||
|
|
||||
|
public Block8x8F QuantiazationTable; |
||||
|
|
||||
|
public UnzigData Unzig; |
||||
|
|
||||
|
public fixed byte ScanData [3 * JpegDecoderCore.MaxComponents]; |
||||
|
|
||||
|
public fixed int Dc [JpegDecoderCore.MaxComponents]; |
||||
|
|
||||
|
public static ComponentData Create() |
||||
|
{ |
||||
|
ComponentData data = default(ComponentData); |
||||
|
data.Unzig = UnzigData.Create(); |
||||
|
return data; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Contains pointers to the memory regions of <see cref="ComponentData"/> so they can be easily passed around to pointer based utility methods of <see cref="Block8x8F"/>
|
||||
|
/// </summary>
|
||||
|
public struct ComponentPointers |
||||
|
{ |
||||
|
public Block8x8F* Block; |
||||
|
|
||||
|
public Block8x8F* Temp1; |
||||
|
|
||||
|
public Block8x8F* Temp2; |
||||
|
|
||||
|
public Block8x8F* QuantiazationTable; |
||||
|
|
||||
|
public int* Unzig; |
||||
|
|
||||
|
public Scan* Scan; |
||||
|
|
||||
|
public int* Dc; |
||||
|
|
||||
|
public ComponentPointers(ComponentData* basePtr) |
||||
|
{ |
||||
|
this.Block = &basePtr->Block; |
||||
|
this.Temp1 = &basePtr->Temp1; |
||||
|
this.Temp2 = &basePtr->Temp2; |
||||
|
this.QuantiazationTable = &basePtr->QuantiazationTable; |
||||
|
this.Unzig = basePtr->Unzig.Data; |
||||
|
this.Scan = (Scan*)basePtr->ScanData; |
||||
|
this.Dc = basePtr->Dc; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void ResetDc() |
||||
|
{ |
||||
|
Unsafe.InitBlock(this.Pointers.Dc, default(byte), sizeof(int) * JpegDecoderCore.MaxComponents); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// bx and by are the
|
||||
|
// blocks: the third block in the first row has (bx, by) = (2, 0).
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// X coordinate of the current block, in units of 8x8. (The third block in the first row has (bx, by) = (2, 0))
|
||||
|
/// </summary>
|
||||
|
private int bx; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Y coordinate of the current block, in units of 8x8. (The third block in the first row has (bx, by) = (2, 0))
|
||||
|
/// </summary>
|
||||
|
private int by; |
||||
|
|
||||
|
// zigStart and zigEnd are the spectral selection bounds.
|
||||
|
// ah and al are the successive approximation high and low values.
|
||||
|
// The spec calls these values Ss, Se, Ah and Al.
|
||||
|
// For progressive JPEGs, these are the two more-or-less independent
|
||||
|
// aspects of progression. Spectral selection progression is when not
|
||||
|
// all of a block's 64 DCT coefficients are transmitted in one pass.
|
||||
|
// For example, three passes could transmit coefficient 0 (the DC
|
||||
|
// component), coefficients 1-5, and coefficients 6-63, in zig-zag
|
||||
|
// order. Successive approximation is when not all of the bits of a
|
||||
|
// band of coefficients are transmitted in one pass. For example,
|
||||
|
// three passes could transmit the 6 most significant bits, followed
|
||||
|
// by the second-least significant bit, followed by the least
|
||||
|
// significant bit.
|
||||
|
// For baseline JPEGs, these parameters are hard-coded to 0/63/0/0.
|
||||
|
|
||||
|
private int zigStart; |
||||
|
|
||||
|
private int zigEnd; |
||||
|
|
||||
|
private int ah; |
||||
|
|
||||
|
private int al; |
||||
|
|
||||
|
// XNumberOfMCUs and YNumberOfMCUs are the number of MCUs (Minimum Coded Units) in the image.
|
||||
|
|
||||
|
public int XNumberOfMCUs; |
||||
|
|
||||
|
public int YNumberOfMCUs; |
||||
|
|
||||
|
private int scanComponentCount; |
||||
|
|
||||
|
private ComponentData Data; |
||||
|
|
||||
|
private ComponentPointers Pointers; |
||||
|
|
||||
|
public static void Init(JpegScanDecoder* p, JpegDecoderCore decoder, int remaining) |
||||
|
{ |
||||
|
p->Data = ComponentData.Create(); |
||||
|
p->Pointers = new ComponentPointers(&p->Data); |
||||
|
p->InitImpl(decoder, remaining); |
||||
|
} |
||||
|
|
||||
|
private void InitImpl(JpegDecoderCore decoder, int remaining) |
||||
|
{ |
||||
|
if (decoder.ComponentCount == 0) |
||||
|
{ |
||||
|
throw new ImageFormatException("Missing SOF marker"); |
||||
|
} |
||||
|
|
||||
|
if (remaining < 6 || 4 + (2 * decoder.ComponentCount) < remaining || remaining % 2 != 0) |
||||
|
{ |
||||
|
throw new ImageFormatException("SOS has wrong length"); |
||||
|
} |
||||
|
|
||||
|
decoder.ReadFull(decoder.Temp, 0, remaining); |
||||
|
this.scanComponentCount = decoder.Temp[0]; |
||||
|
|
||||
|
int scanComponentCountX2 = 2 * this.scanComponentCount; |
||||
|
if (remaining != 4 + scanComponentCountX2) |
||||
|
{ |
||||
|
throw new ImageFormatException("SOS length inconsistent with number of components"); |
||||
|
} |
||||
|
|
||||
|
int totalHv = 0; |
||||
|
|
||||
|
for (int i = 0; i < this.scanComponentCount; i++) |
||||
|
{ |
||||
|
this.ProcessScanImpl(decoder, i, ref this.Pointers.Scan[i], ref totalHv); |
||||
|
} |
||||
|
// Section B.2.3 states that if there is more than one component then the
|
||||
|
// total H*V values in a scan must be <= 10.
|
||||
|
if (decoder.ComponentCount > 1 && totalHv > 10) |
||||
|
{ |
||||
|
throw new ImageFormatException("Total sampling factors too large."); |
||||
|
} |
||||
|
|
||||
|
this.zigEnd = Block8x8F.ScalarCount - 1; |
||||
|
|
||||
|
if (decoder.IsProgressive) |
||||
|
{ |
||||
|
this.zigStart = decoder.Temp[1 + scanComponentCountX2]; |
||||
|
this.zigEnd = decoder.Temp[2 + scanComponentCountX2]; |
||||
|
this.ah = decoder.Temp[3 + scanComponentCountX2] >> 4; |
||||
|
this.al = decoder.Temp[3 + scanComponentCountX2] & 0x0f; |
||||
|
|
||||
|
if ((this.zigStart == 0 && this.zigEnd != 0) || this.zigStart > this.zigEnd |
||||
|
|| this.zigEnd >= Block8x8F.ScalarCount) |
||||
|
{ |
||||
|
throw new ImageFormatException("Bad spectral selection bounds"); |
||||
|
} |
||||
|
|
||||
|
if (this.zigStart != 0 && this.scanComponentCount != 1) |
||||
|
{ |
||||
|
throw new ImageFormatException("Progressive AC coefficients for more than one component"); |
||||
|
} |
||||
|
|
||||
|
if (this.ah != 0 && this.ah != this.al + 1) |
||||
|
{ |
||||
|
throw new ImageFormatException("Bad successive approximation values"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// XNumberOfMCUs and YNumberOfMCUs are the number of MCUs (Minimum Coded Units) in the image.
|
||||
|
int h0 = decoder.ComponentArray[0].HorizontalFactor; |
||||
|
int v0 = decoder.ComponentArray[0].VerticalFactor; |
||||
|
this.XNumberOfMCUs = (decoder.ImageWidth + (8 * h0) - 1) / (8 * h0); |
||||
|
this.YNumberOfMCUs = (decoder.ImageHeight + (8 * v0) - 1) / (8 * v0); |
||||
|
|
||||
|
if (decoder.IsProgressive) |
||||
|
{ |
||||
|
for (int i = 0; i < this.scanComponentCount; i++) |
||||
|
{ |
||||
|
int compIndex = this.Pointers.Scan[i].Index; |
||||
|
if (decoder.ProgCoeffs[compIndex] == null) |
||||
|
{ |
||||
|
int size = this.XNumberOfMCUs * this.YNumberOfMCUs * decoder.ComponentArray[compIndex].HorizontalFactor |
||||
|
* decoder.ComponentArray[compIndex].VerticalFactor; |
||||
|
|
||||
|
decoder.ProgCoeffs[compIndex] = new Block8x8F[size]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void ProcessScanImpl(JpegDecoderCore decoder, int i, ref Scan currentScan, ref int totalHv) |
||||
|
{ |
||||
|
// Component selector.
|
||||
|
int cs = decoder.Temp[1 + (2 * i)]; |
||||
|
int compIndex = -1; |
||||
|
for (int j = 0; j < decoder.ComponentCount; j++) |
||||
|
{ |
||||
|
// Component compv = ;
|
||||
|
if (cs == decoder.ComponentArray[j].Identifier) |
||||
|
{ |
||||
|
compIndex = j; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (compIndex < 0) |
||||
|
{ |
||||
|
throw new ImageFormatException("Unknown component selector"); |
||||
|
} |
||||
|
|
||||
|
currentScan.Index = (byte)compIndex; |
||||
|
|
||||
|
this.ProcessComponentImpl(decoder, i, ref currentScan, ref totalHv, ref decoder.ComponentArray[compIndex]); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
private void ProcessComponentImpl( |
||||
|
JpegDecoderCore decoder, |
||||
|
int i, |
||||
|
ref Scan currentScan, |
||||
|
ref int totalHv, |
||||
|
ref Component currentComponent) |
||||
|
{ |
||||
|
// Section B.2.3 states that "the value of Cs_j shall be different from
|
||||
|
// the values of Cs_1 through Cs_(j-1)". Since we have previously
|
||||
|
// verified that a frame's component identifiers (C_i values in section
|
||||
|
// B.2.2) are unique, it suffices to check that the implicit indexes
|
||||
|
// into comp are unique.
|
||||
|
for (int j = 0; j < i; j++) |
||||
|
{ |
||||
|
if (currentScan.Index == this.Pointers.Scan[j].Index) |
||||
|
{ |
||||
|
throw new ImageFormatException("Repeated component selector"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
totalHv += currentComponent.HorizontalFactor * currentComponent.VerticalFactor; |
||||
|
|
||||
|
currentScan.DcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] >> 4); |
||||
|
if (currentScan.DcTableSelector > HuffmanTree.MaxTh) |
||||
|
{ |
||||
|
throw new ImageFormatException("Bad DC table selector value"); |
||||
|
} |
||||
|
|
||||
|
currentScan.AcTableSelector = (byte)(decoder.Temp[2 + (2 * i)] & 0x0f); |
||||
|
if (currentScan.AcTableSelector > HuffmanTree.MaxTh) |
||||
|
{ |
||||
|
throw new ImageFormatException("Bad AC table selector value"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void ProcessBlocks(JpegDecoderCore decoder) |
||||
|
{ |
||||
|
int blockCount = 0; |
||||
|
int mcu = 0; |
||||
|
byte expectedRst = JpegConstants.Markers.RST0; |
||||
|
|
||||
|
for (int my = 0; my < this.YNumberOfMCUs; my++) |
||||
|
{ |
||||
|
for (int mx = 0; mx < this.XNumberOfMCUs; mx++) |
||||
|
{ |
||||
|
for (int i = 0; i < this.scanComponentCount; i++) |
||||
|
{ |
||||
|
int compIndex = this.Pointers.Scan[i].Index; |
||||
|
int hi = decoder.ComponentArray[compIndex].HorizontalFactor; |
||||
|
int vi = decoder.ComponentArray[compIndex].VerticalFactor; |
||||
|
|
||||
|
for (int j = 0; j < hi * vi; j++) |
||||
|
{ |
||||
|
// The blocks are traversed one MCU at a time. For 4:2:0 chroma
|
||||
|
// subsampling, there are four Y 8x8 blocks in every 16x16 MCU.
|
||||
|
// For a baseline 32x16 pixel image, the Y blocks visiting order is:
|
||||
|
// 0 1 4 5
|
||||
|
// 2 3 6 7
|
||||
|
// For progressive images, the interleaved scans (those with component count > 1)
|
||||
|
// are traversed as above, but non-interleaved scans are traversed left
|
||||
|
// to right, top to bottom:
|
||||
|
// 0 1 2 3
|
||||
|
// 4 5 6 7
|
||||
|
// Only DC scans (zigStart == 0) can be interleave AC scans must have
|
||||
|
// only one component.
|
||||
|
// To further complicate matters, for non-interleaved scans, there is no
|
||||
|
// data for any blocks that are inside the image at the MCU level but
|
||||
|
// outside the image at the pixel level. For example, a 24x16 pixel 4:2:0
|
||||
|
// progressive image consists of two 16x16 MCUs. The interleaved scans
|
||||
|
// will process 8 Y blocks:
|
||||
|
// 0 1 4 5
|
||||
|
// 2 3 6 7
|
||||
|
// The non-interleaved scans will process only 6 Y blocks:
|
||||
|
// 0 1 2
|
||||
|
// 3 4 5
|
||||
|
if (this.scanComponentCount != 1) |
||||
|
{ |
||||
|
this.bx = (hi * mx) + (j % hi); |
||||
|
this.by = (vi * my) + (j / hi); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
int q = this.XNumberOfMCUs * hi; |
||||
|
this.bx = blockCount % q; |
||||
|
this.by = blockCount / q; |
||||
|
blockCount++; |
||||
|
if (this.bx * 8 >= decoder.ImageWidth || this.by * 8 >= decoder.ImageHeight) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
int qtIndex = decoder.ComponentArray[compIndex].Selector; |
||||
|
|
||||
|
// TODO: Reading & processing blocks should be done in 2 separate loops. The second one could be parallelized. The first one could be async.
|
||||
|
|
||||
|
this.Data.QuantiazationTable = decoder.QuantizationTables[qtIndex]; |
||||
|
|
||||
|
//Load the previous partially decoded coefficients, if applicable.
|
||||
|
if (decoder.IsProgressive) |
||||
|
{ |
||||
|
int blockIndex = ((this.by * this.XNumberOfMCUs) * hi) + this.bx; |
||||
|
this.Data.Block = decoder.ProgCoeffs[compIndex][blockIndex]; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
this.Data.Block.Clear(); |
||||
|
} |
||||
|
|
||||
|
this.ProcessBlockImpl(decoder, i, compIndex, hi); |
||||
|
} |
||||
|
|
||||
|
// for j
|
||||
|
} |
||||
|
|
||||
|
// for i
|
||||
|
mcu++; |
||||
|
|
||||
|
if (decoder.RestartInterval > 0 && mcu % decoder.RestartInterval == 0 && mcu < this.XNumberOfMCUs * this.YNumberOfMCUs) |
||||
|
{ |
||||
|
// A more sophisticated decoder could use RST[0-7] markers to resynchronize from corrupt input,
|
||||
|
// but this one assumes well-formed input, and hence the restart marker follows immediately.
|
||||
|
decoder.ReadFull(decoder.Temp, 0, 2); |
||||
|
if (decoder.Temp[0] != 0xff || decoder.Temp[1] != expectedRst) |
||||
|
{ |
||||
|
throw new ImageFormatException("Bad RST marker"); |
||||
|
} |
||||
|
|
||||
|
expectedRst++; |
||||
|
if (expectedRst == JpegConstants.Markers.RST7 + 1) |
||||
|
{ |
||||
|
expectedRst = JpegConstants.Markers.RST0; |
||||
|
} |
||||
|
|
||||
|
// Reset the Huffman decoder.
|
||||
|
decoder.Bits = default(Bits); |
||||
|
|
||||
|
// Reset the DC components, as per section F.2.1.3.1.
|
||||
|
this.ResetDc(); |
||||
|
|
||||
|
// Reset the progressive decoder state, as per section G.1.2.2.
|
||||
|
decoder.EobRun = 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// for mx
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Decodes a successive approximation refinement block, as specified in section G.1.2.
|
||||
|
/// </summary>
|
||||
|
/// <param name="h">The Huffman tree</param>
|
||||
|
/// <param name="delta">The low transform offset</param>
|
||||
|
/// <param name="decoder">The decoder instance</param>
|
||||
|
private void Refine(JpegDecoderCore decoder, ref HuffmanTree h, int delta) |
||||
|
{ |
||||
|
Block8x8F* b = this.Pointers.Block; |
||||
|
// Refining a DC component is trivial.
|
||||
|
if (this.zigStart == 0) |
||||
|
{ |
||||
|
if (this.zigEnd != 0) |
||||
|
{ |
||||
|
throw new ImageFormatException("Invalid state for zig DC component"); |
||||
|
} |
||||
|
|
||||
|
bool bit = decoder.DecodeBit(); |
||||
|
if (bit) |
||||
|
{ |
||||
|
int stuff = (int)Block8x8F.GetScalarAt(b, 0); |
||||
|
|
||||
|
// int stuff = (int)b[0];
|
||||
|
stuff |= delta; |
||||
|
|
||||
|
// b[0] = stuff;
|
||||
|
Block8x8F.SetScalarAt(b, 0, stuff); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Refining AC components is more complicated; see sections G.1.2.2 and G.1.2.3.
|
||||
|
int zig = this.zigStart; |
||||
|
if (decoder.EobRun == 0) |
||||
|
{ |
||||
|
for (; zig <= this.zigEnd; zig++) |
||||
|
{ |
||||
|
bool done = false; |
||||
|
int z = 0; |
||||
|
byte val = decoder.DecodeHuffman(ref h); |
||||
|
int val0 = val >> 4; |
||||
|
int val1 = val & 0x0f; |
||||
|
|
||||
|
switch (val1) |
||||
|
{ |
||||
|
case 0: |
||||
|
if (val0 != 0x0f) |
||||
|
{ |
||||
|
decoder.EobRun = (ushort)(1 << val0); |
||||
|
if (val0 != 0) |
||||
|
{ |
||||
|
decoder.EobRun |= (ushort)decoder.DecodeBits(val0); |
||||
|
} |
||||
|
|
||||
|
done = true; |
||||
|
} |
||||
|
|
||||
|
break; |
||||
|
case 1: |
||||
|
z = delta; |
||||
|
bool bit = decoder.DecodeBit(); |
||||
|
if (!bit) |
||||
|
{ |
||||
|
z = -z; |
||||
|
} |
||||
|
|
||||
|
break; |
||||
|
default: |
||||
|
throw new ImageFormatException("Unexpected Huffman code"); |
||||
|
} |
||||
|
|
||||
|
if (done) |
||||
|
{ |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
zig = this.RefineNonZeroes(decoder, zig, val0, delta); |
||||
|
if (zig > this.zigEnd) |
||||
|
{ |
||||
|
throw new ImageFormatException($"Too many coefficients {zig} > {this.zigEnd}"); |
||||
|
} |
||||
|
|
||||
|
if (z != 0) |
||||
|
{ |
||||
|
// b[Unzig[zig]] = z;
|
||||
|
Block8x8F.SetScalarAt(b, this.Pointers.Unzig[zig], z); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (decoder.EobRun > 0) |
||||
|
{ |
||||
|
decoder.EobRun--; |
||||
|
this.RefineNonZeroes(decoder, zig,-1, delta); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Refines non-zero entries of b in zig-zag order.
|
||||
|
/// If <paramref name="nz" /> >= 0, the first <paramref name="nz" /> zero entries are skipped over.
|
||||
|
/// </summary>
|
||||
|
/// <param name="decoder">The decoder</param>
|
||||
|
/// <param name="zig">The zig-zag start index</param>
|
||||
|
/// <param name="nz">The non-zero entry</param>
|
||||
|
/// <param name="delta">The low transform offset</param>
|
||||
|
/// <returns>The <see cref="int" /></returns>
|
||||
|
private int RefineNonZeroes(JpegDecoderCore decoder, int zig, int nz, int delta) |
||||
|
{ |
||||
|
var b = this.Pointers.Block; |
||||
|
for (; zig <= this.zigEnd; zig++) |
||||
|
{ |
||||
|
int u = this.Pointers.Unzig[zig]; |
||||
|
float bu = Block8x8F.GetScalarAt(b, u); |
||||
|
|
||||
|
// TODO: Are the equality comparsions OK with floating point values? Isn't an epsilon value necessary?
|
||||
|
if (bu == 0) |
||||
|
{ |
||||
|
if (nz == 0) |
||||
|
{ |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
nz--; |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
bool bit = decoder.DecodeBit(); |
||||
|
if (!bit) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if (bu >= 0) |
||||
|
{ |
||||
|
// b[u] += delta;
|
||||
|
Block8x8F.SetScalarAt(b, u, bu + delta); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// b[u] -= delta;
|
||||
|
Block8x8F.SetScalarAt(b, u, bu - delta); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return zig; |
||||
|
} |
||||
|
|
||||
|
private void ProcessBlockImpl(JpegDecoderCore decoder, int i, int compIndex, int hi) |
||||
|
{ |
||||
|
var b = this.Pointers.Block; |
||||
|
//var dc = this.Pointers.Dc;
|
||||
|
int huffmannIdx = (AcTableIndex * HuffmanTree.ThRowSize) + this.Pointers.Scan[i].AcTableSelector; |
||||
|
if (this.ah != 0) |
||||
|
{ |
||||
|
this.Refine(decoder, ref decoder.HuffmanTrees[huffmannIdx], 1 << this.al); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
int zig = this.zigStart; |
||||
|
if (zig == 0) |
||||
|
{ |
||||
|
zig++; |
||||
|
|
||||
|
// Decode the DC coefficient, as specified in section F.2.2.1.
|
||||
|
byte value = |
||||
|
decoder.DecodeHuffman( |
||||
|
ref decoder.HuffmanTrees[(DcTableIndex * HuffmanTree.ThRowSize) + this.Pointers.Scan[i].DcTableSelector]); |
||||
|
if (value > 16) |
||||
|
{ |
||||
|
throw new ImageFormatException("Excessive DC component"); |
||||
|
} |
||||
|
|
||||
|
int deltaDC = decoder.Bits.ReceiveExtend(value, decoder); |
||||
|
this.Pointers.Dc[compIndex] += deltaDC; |
||||
|
|
||||
|
// b[0] = dc[compIndex] << al;
|
||||
|
Block8x8F.SetScalarAt(b, 0, this.Pointers.Dc[compIndex] << al); |
||||
|
} |
||||
|
|
||||
|
if (zig <= this.zigEnd && decoder.EobRun > 0) |
||||
|
{ |
||||
|
decoder.EobRun--; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// Decode the AC coefficients, as specified in section F.2.2.2.
|
||||
|
for (; zig <= this.zigEnd; zig++) |
||||
|
{ |
||||
|
byte value = decoder.DecodeHuffman(ref decoder.HuffmanTrees[huffmannIdx]); |
||||
|
byte val0 = (byte)(value >> 4); |
||||
|
byte val1 = (byte)(value & 0x0f); |
||||
|
if (val1 != 0) |
||||
|
{ |
||||
|
zig += val0; |
||||
|
if (zig > this.zigEnd) |
||||
|
{ |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
int ac = decoder.Bits.ReceiveExtend(val1, decoder); |
||||
|
|
||||
|
// b[Unzig[zig]] = ac << al;
|
||||
|
Block8x8F.SetScalarAt(b, this.Pointers.Unzig[zig], ac << this.al); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
if (val0 != 0x0f) |
||||
|
{ |
||||
|
decoder.EobRun = (ushort)(1 << val0); |
||||
|
if (val0 != 0) |
||||
|
{ |
||||
|
decoder.EobRun |= (ushort)decoder.DecodeBits(val0); |
||||
|
} |
||||
|
|
||||
|
decoder.EobRun--; |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
zig += 0x0f; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (decoder.IsProgressive) |
||||
|
{ |
||||
|
if (this.zigEnd != Block8x8F.ScalarCount - 1 || this.al != 0) |
||||
|
{ |
||||
|
// We haven't completely decoded this 8x8 block. Save the coefficients.
|
||||
|
// this.ProgCoeffs[compIndex][((@by * XNumberOfMCUs) * hi) + bx] = b.Clone();
|
||||
|
decoder.ProgCoeffs[compIndex][((this.by * this.XNumberOfMCUs) * hi) + this.bx] = *b; |
||||
|
|
||||
|
// At this point, we could execute the rest of the loop body to dequantize and
|
||||
|
// perform the inverse DCT, to save early stages of a progressive image to the
|
||||
|
// *image.YCbCr buffers (the whole point of progressive encoding), but in Go,
|
||||
|
// the jpeg.Decode function does not return until the entire image is decoded,
|
||||
|
// so we "continue" here to avoid wasted computation.
|
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Dequantize, perform the inverse DCT and store the block to the image.
|
||||
|
Block8x8F.UnZig(b, this.Pointers.QuantiazationTable, this.Pointers.Unzig); |
||||
|
|
||||
|
DCT.TransformIDCT(ref *b, ref *this.Pointers.Temp1, ref *this.Pointers.Temp2); |
||||
|
|
||||
|
var destChannel = decoder.GetDestinationChannel(compIndex); |
||||
|
var destArea = destChannel.GetOffsetedSubAreaForBlock(this.bx, this.by); |
||||
|
destArea.LoadColorsFrom(this.Pointers.Temp1, this.Pointers.Temp2); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
## JpegScanDecoder |
||||
|
Encapsulates the impementation of the Jpeg top-to bottom scan decoder triggered by the `SOS` marker. |
||||
|
The implementation is optimized to hold most of the necessary data in a single value type, which is intended to be used as an on-stack object. |
||||
|
|
||||
|
#### Benefits: |
||||
|
- Maximized locality of reference by keeping most of the operation data on the stack |
||||
|
- Reaching this without long parameter lists, most of the values describing the state of the decoder algorithm |
||||
|
are members of the `JpegScanDecoder` struct |
||||
|
- Most of the logic related to Scan decoding is refactored & simplified now to live in the methods of `JpegScanDecoder` |
||||
|
- The first step is done towards separating the stream reading from block processing. They can be refactored later to be executed in two disctinct loops. |
||||
|
- The input processing loop can be `async` |
||||
|
- The block processing loop can be parallelized |
||||
|
|
||||
|
#### Data layout |
||||
|
|
||||
|
|JpegScanDecoder | |
||||
|
|-------------------| |
||||
|
|Variables | |
||||
|
|ComponentData | |
||||
|
|ComponentPointers | |
||||
|
|
||||
|
- **ComponentData** holds the "large" data blocks needed for computations (Mostly `Block8x8F`-s) |
||||
|
- **ComponentPointers** contains pointers to the memory regions of `ComponentData` so they can be easily passed around to pointer based utility methods of `Block8x8F` |
||||
|
|
||||
|
|
||||
@ -0,0 +1,26 @@ |
|||||
|
namespace ImageSharp.Formats.Jpg |
||||
|
{ |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents a component scan
|
||||
|
/// </summary>
|
||||
|
[StructLayout(LayoutKind.Sequential)] |
||||
|
internal struct Scan |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets or sets the component index.
|
||||
|
/// </summary>
|
||||
|
public byte Index; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the DC table selector
|
||||
|
/// </summary>
|
||||
|
public byte DcTableSelector; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the AC table selector
|
||||
|
/// </summary>
|
||||
|
public byte AcTableSelector; |
||||
|
} |
||||
|
} |
||||
File diff suppressed because it is too large
@ -0,0 +1,30 @@ |
|||||
|
// <copyright file="CleanPooler.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
namespace ImageSharp.Formats |
||||
|
{ |
||||
|
using System.Buffers; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Wraps <see cref="ArrayPool{T}"/> to always provide arrays initialized with default(T)
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The element type</typeparam>
|
||||
|
internal class CleanPooler<T> |
||||
|
{ |
||||
|
private static readonly ArrayPool<T> Pool = ArrayPool<T>.Create(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Rents a clean array
|
||||
|
/// </summary>
|
||||
|
/// <param name="minimumLength">The minimum array length</param>
|
||||
|
/// <returns>A clean array of T</returns>
|
||||
|
public static T[] RentCleanArray(int minimumLength) => Pool.Rent(minimumLength); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Retursn array to the pool
|
||||
|
/// </summary>
|
||||
|
/// <param name="array">The array</param>
|
||||
|
public static void ReturnArray(T[] array) => Pool.Return(array, true); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
// <copyright file="ConstantsTests.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageSharp.Tests.Common |
||||
|
{ |
||||
|
using Xunit; |
||||
|
|
||||
|
public class ConstantsTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void Epsilon() |
||||
|
{ |
||||
|
Assert.Equal(Constants.Epsilon, 0.001f); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace ImageSharp.Tests |
||||
|
{ |
||||
|
using Xunit; |
||||
|
using Xunit.Abstractions; |
||||
|
|
||||
|
public class HelloTest |
||||
|
{ |
||||
|
private ITestOutputHelper output; |
||||
|
|
||||
|
public HelloTest(ITestOutputHelper output) |
||||
|
{ |
||||
|
this.output = output; |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void HelloFoo() |
||||
|
{ |
||||
|
TestFile file = TestFile.Create(TestImages.Jpeg.Calliphora); |
||||
|
var img = file.CreateImage(); |
||||
|
this.output.WriteLine(img.Width.ToString()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,179 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> |
||||
|
<PropertyGroup> |
||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
||||
|
<ProjectGuid>{88C5FB74-5845-4CC0-8F1E-A25EBABC95C2}</ProjectGuid> |
||||
|
<OutputType>Library</OutputType> |
||||
|
<AppDesignerFolder>Properties</AppDesignerFolder> |
||||
|
<RootNamespace>ImageSharp.Tests</RootNamespace> |
||||
|
<AssemblyName>ImageSharp.Tests46</AssemblyName> |
||||
|
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> |
||||
|
<FileAlignment>512</FileAlignment> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
||||
|
<DebugSymbols>true</DebugSymbols> |
||||
|
<DebugType>full</DebugType> |
||||
|
<Optimize>false</Optimize> |
||||
|
<OutputPath>bin\Debug\</OutputPath> |
||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants> |
||||
|
<ErrorReport>prompt</ErrorReport> |
||||
|
<WarningLevel>4</WarningLevel> |
||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
||||
|
<DebugType>pdbonly</DebugType> |
||||
|
<Optimize>true</Optimize> |
||||
|
<OutputPath>bin\Release\</OutputPath> |
||||
|
<DefineConstants>TRACE</DefineConstants> |
||||
|
<ErrorReport>prompt</ErrorReport> |
||||
|
<WarningLevel>4</WarningLevel> |
||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
||||
|
</PropertyGroup> |
||||
|
<ItemGroup> |
||||
|
<Reference Include="ImageSharp" Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
||||
|
<HintPath>..\..\src\ImageSharp\bin\Debug\net45\ImageSharp.dll</HintPath> |
||||
|
</Reference> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<Reference Include="ImageSharp" Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
||||
|
<HintPath>..\..\src\ImageSharp\bin\Release\net45\ImageSharp.dll</HintPath> |
||||
|
</Reference> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<Reference Include="System" /> |
||||
|
<Reference Include="System.Core" /> |
||||
|
<Reference Include="System.Numerics" /> |
||||
|
<Reference Include="System.Numerics.Vectors, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> |
||||
|
<HintPath>..\..\packages\System.Numerics.Vectors.4.1.1\lib\net46\System.Numerics.Vectors.dll</HintPath> |
||||
|
<Private>True</Private> |
||||
|
</Reference> |
||||
|
<Reference Include="System.Xml.Linq" /> |
||||
|
<Reference Include="System.Data.DataSetExtensions" /> |
||||
|
<Reference Include="Microsoft.CSharp" /> |
||||
|
<Reference Include="System.Data" /> |
||||
|
<Reference Include="System.Net.Http" /> |
||||
|
<Reference Include="System.Xml" /> |
||||
|
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL"> |
||||
|
<HintPath>..\..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll</HintPath> |
||||
|
<Private>True</Private> |
||||
|
</Reference> |
||||
|
<Reference Include="xunit.assert, Version=2.2.0.3444, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL"> |
||||
|
<HintPath>..\..\packages\xunit.assert.2.2.0-beta4-build3444\lib\netstandard1.0\xunit.assert.dll</HintPath> |
||||
|
<Private>True</Private> |
||||
|
</Reference> |
||||
|
<Reference Include="xunit.core, Version=2.2.0.3444, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL"> |
||||
|
<HintPath>..\..\packages\xunit.extensibility.core.2.2.0-beta4-build3444\lib\net45\xunit.core.dll</HintPath> |
||||
|
<Private>True</Private> |
||||
|
</Reference> |
||||
|
<Reference Include="xunit.execution.desktop, Version=2.2.0.3444, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL"> |
||||
|
<HintPath>..\..\packages\xunit.extensibility.execution.2.2.0-beta4-build3444\lib\net45\xunit.execution.desktop.dll</HintPath> |
||||
|
<Private>True</Private> |
||||
|
</Reference> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<Compile Include="..\ImageSharp.Tests\Formats\Jpg\Block8x8FTests.cs"> |
||||
|
<Link>Formats\Jpg\Block8x8FTests.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\Formats\Jpg\JpegTests.cs"> |
||||
|
<Link>Formats\Jpg\JpegTests.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\Formats\Jpg\ReferenceImplementations.cs"> |
||||
|
<Link>Formats\Jpg\ReferenceImplementations.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\Formats\Jpg\ReferenceImplementationsTests.cs"> |
||||
|
<Link>Formats\Jpg\ReferenceImplementationsTests.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\Formats\Jpg\UtilityTestClassBase.cs"> |
||||
|
<Link>Formats\Jpg\UtilityTestClassBase.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\Image\ImagePropertyTests.cs"> |
||||
|
<Link>Image\ImagePropertyTests.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\Image\ImageTests.cs"> |
||||
|
<Link>Image\ImageTests.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\Image\PixelAccessorTests.cs"> |
||||
|
<Link>Image\PixelAccessorTests.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\TestBase.cs"> |
||||
|
<Link>TestBase.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\TestImages.cs"> |
||||
|
<Link>TestImages.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\TestUtilities\Attributes\ImageDataAttributeBase.cs"> |
||||
|
<Link>TestUtilities\ImageDataAttributeBase.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\TestUtilities\Attributes\WithBlankImageAttribute.cs"> |
||||
|
<Link>TestUtilities\WithBlankImageAttribute.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\TestUtilities\Attributes\WithFileAttribute.cs"> |
||||
|
<Link>TestUtilities\WithFileAttribute.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\TestUtilities\Attributes\WithFileCollectionAttribute.cs"> |
||||
|
<Link>TestUtilities\WithFileCollectionAttribute.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\TestUtilities\Attributes\WithMemberFactoryAttribute.cs"> |
||||
|
<Link>TestUtilities\WithMemberFactoryAttribute.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\TestUtilities\Attributes\WithSolidFilledImagesAttribute.cs"> |
||||
|
<Link>TestUtilities\WithSolidFilledImagesAttribute.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\TestUtilities\EnumHelper.cs"> |
||||
|
<Link>TestUtilities\EnumHelper.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\TestUtilities\Factories\GenericFactory.cs"> |
||||
|
<Link>TestUtilities\GenericFactory.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\TestUtilities\Factories\ImageFactory.cs"> |
||||
|
<Link>TestUtilities\ImageFactory.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\TestUtilities\ImageProviders\BlankProvider.cs"> |
||||
|
<Link>TestUtilities\BlankProvider.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\TestUtilities\ImageProviders\FileProvider.cs"> |
||||
|
<Link>TestUtilities\FileProvider.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\TestUtilities\ImageProviders\LambdaProvider.cs"> |
||||
|
<Link>TestUtilities\LambdaProvider.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\TestUtilities\ImageProviders\SolidProvider.cs"> |
||||
|
<Link>TestUtilities\SolidProvider.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\TestUtilities\ImageProviders\TestImageProvider.cs"> |
||||
|
<Link>TestUtilities\TestImageProvider.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\TestUtilities\ImagingTestCaseUtility.cs"> |
||||
|
<Link>TestUtilities\ImagingTestCaseUtility.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\TestUtilities\PixelTypes.cs"> |
||||
|
<Link>TestUtilities\PixelTypes.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\TestUtilities\Tests\TestImageProviderTests.cs"> |
||||
|
<Link>TestUtilities\TestImageProviderTests.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\TestUtilities\Tests\TestUtilityExtensionsTests.cs"> |
||||
|
<Link>TestUtilities\TestUtilityExtensionsTests.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="..\ImageSharp.Tests\TestUtilities\TestUtilityExtensions.cs"> |
||||
|
<Link>TestUtilities\TestUtilityExtensions.cs</Link> |
||||
|
</Compile> |
||||
|
<Compile Include="HelloTest.cs" /> |
||||
|
<Compile Include="Properties\AssemblyInfo.cs" /> |
||||
|
<Compile Include="TestFile.cs" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<None Include="packages.config" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup /> |
||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> |
||||
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. |
||||
|
Other similar extension points exist, see Microsoft.Common.targets. |
||||
|
<Target Name="BeforeBuild"> |
||||
|
</Target> |
||||
|
<Target Name="AfterBuild"> |
||||
|
</Target> |
||||
|
--> |
||||
|
</Project> |
||||
@ -0,0 +1,36 @@ |
|||||
|
using System.Reflection; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
// General Information about an assembly is controlled through the following
|
||||
|
// set of attributes. Change these attribute values to modify the information
|
||||
|
// associated with an assembly.
|
||||
|
[assembly: AssemblyTitle("ImageSharp.Tests46")] |
||||
|
[assembly: AssemblyDescription("")] |
||||
|
[assembly: AssemblyConfiguration("")] |
||||
|
[assembly: AssemblyCompany("Sapa")] |
||||
|
[assembly: AssemblyProduct("ImageSharp.Tests46")] |
||||
|
[assembly: AssemblyCopyright("Copyright © Sapa 2016")] |
||||
|
[assembly: AssemblyTrademark("")] |
||||
|
[assembly: AssemblyCulture("")] |
||||
|
|
||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
|
// to COM components. If you need to access a type in this assembly from
|
||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||
|
[assembly: ComVisible(false)] |
||||
|
|
||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
|
[assembly: Guid("88c5fb74-5845-4cc0-8f1e-a25ebabc95c2")] |
||||
|
|
||||
|
// Version information for an assembly consists of the following four values:
|
||||
|
//
|
||||
|
// Major Version
|
||||
|
// Minor Version
|
||||
|
// Build Number
|
||||
|
// Revision
|
||||
|
//
|
||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
|
// by using the '*' as shown below:
|
||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||
|
[assembly: AssemblyVersion("1.0.0.0")] |
||||
|
[assembly: AssemblyFileVersion("1.0.0.0")] |
||||
@ -0,0 +1,78 @@ |
|||||
|
// <copyright file="TestImage.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
namespace ImageSharp.Tests |
||||
|
{ |
||||
|
using System; |
||||
|
using System.Collections.Concurrent; |
||||
|
using System.IO; |
||||
|
|
||||
|
public class TestFile |
||||
|
{ |
||||
|
private static readonly ConcurrentDictionary<string, TestFile> cache = new ConcurrentDictionary<string, TestFile>(); |
||||
|
private static readonly string FormatsDirectory = GetFormatsDirectory(); |
||||
|
|
||||
|
private static string GetFormatsDirectory() |
||||
|
{ |
||||
|
return "../../../ImageSharp.Tests/TestImages/Formats/"; |
||||
|
} |
||||
|
|
||||
|
private readonly Image image; |
||||
|
private readonly string file; |
||||
|
|
||||
|
private TestFile(string file) |
||||
|
{ |
||||
|
this.file = file; |
||||
|
|
||||
|
this.Bytes = File.ReadAllBytes(file); |
||||
|
this.image = new Image(this.Bytes); |
||||
|
} |
||||
|
|
||||
|
public static string GetPath(string file) |
||||
|
{ |
||||
|
return Path.Combine(FormatsDirectory, file); |
||||
|
} |
||||
|
|
||||
|
public static TestFile Create(string file) |
||||
|
{ |
||||
|
return cache.GetOrAdd(file, (string fileName) => |
||||
|
{ |
||||
|
return new TestFile(FormatsDirectory + fileName); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public byte[] Bytes { get; } |
||||
|
|
||||
|
public string FileName |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return Path.GetFileName(this.file); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public string FileNameWithoutExtension |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return Path.GetFileNameWithoutExtension(this.file); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public string GetFileName(object value) |
||||
|
{ |
||||
|
return this.FileNameWithoutExtension + "-" + value + Path.GetExtension(this.file); |
||||
|
} |
||||
|
|
||||
|
public string GetFileNameWithoutExtension(object value) |
||||
|
{ |
||||
|
return this.FileNameWithoutExtension + "-" + value; |
||||
|
} |
||||
|
|
||||
|
public Image CreateImage() |
||||
|
{ |
||||
|
return new Image(this.image); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<packages> |
||||
|
<package id="System.Numerics.Vectors" version="4.1.1" targetFramework="net461" /> |
||||
|
<package id="xunit" version="2.2.0-beta4-build3444" targetFramework="net461" /> |
||||
|
<package id="xunit.abstractions" version="2.0.1" targetFramework="net461" /> |
||||
|
<package id="xunit.assert" version="2.2.0-beta4-build3444" targetFramework="net461" /> |
||||
|
<package id="xunit.core" version="2.2.0-beta4-build3444" targetFramework="net461" /> |
||||
|
<package id="xunit.extensibility.core" version="2.2.0-beta4-build3444" targetFramework="net461" /> |
||||
|
<package id="xunit.extensibility.execution" version="2.2.0-beta4-build3444" targetFramework="net461" /> |
||||
|
</packages> |
||||
Loading…
Reference in new issue