mirror of https://github.com/SixLabors/ImageSharp
381 changed files with 4632 additions and 6151 deletions
@ -1,193 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Gif |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Represents a byte of data in a GIF data stream which contains a number
|
|
||||
/// of data items.
|
|
||||
/// </summary>
|
|
||||
internal readonly struct PackedField : IEquatable<PackedField> |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The individual bits representing the packed byte.
|
|
||||
/// </summary>
|
|
||||
private static readonly bool[] Bits = new bool[8]; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the byte which represents the data items held in this instance.
|
|
||||
/// </summary>
|
|
||||
public byte Byte |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
int returnValue = 0; |
|
||||
int bitShift = 7; |
|
||||
foreach (bool bit in Bits) |
|
||||
{ |
|
||||
int bitValue; |
|
||||
if (bit) |
|
||||
{ |
|
||||
bitValue = 1 << bitShift; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
bitValue = 0; |
|
||||
} |
|
||||
|
|
||||
returnValue |= bitValue; |
|
||||
bitShift--; |
|
||||
} |
|
||||
|
|
||||
return Convert.ToByte(returnValue & 0xFF); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns a new <see cref="PackedField"/> with the bits in the packed fields to
|
|
||||
/// the corresponding bits from the supplied byte.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value to pack.</param>
|
|
||||
/// <returns>The <see cref="PackedField"/></returns>
|
|
||||
public static PackedField FromInt(byte value) |
|
||||
{ |
|
||||
PackedField packed = default(PackedField); |
|
||||
packed.SetBits(0, 8, value); |
|
||||
return packed; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Sets the specified bit within the packed fields to the supplied
|
|
||||
/// value.
|
|
||||
/// </summary>
|
|
||||
/// <param name="index">
|
|
||||
/// The zero-based index within the packed fields of the bit to set.
|
|
||||
/// </param>
|
|
||||
/// <param name="valueToSet">
|
|
||||
/// The value to set the bit to.
|
|
||||
/// </param>
|
|
||||
public void SetBit(int index, bool valueToSet) |
|
||||
{ |
|
||||
if (index < 0 || index > 7) |
|
||||
{ |
|
||||
string message = $"Index must be between 0 and 7. Supplied index: {index}"; |
|
||||
throw new ArgumentOutOfRangeException(nameof(index), message); |
|
||||
} |
|
||||
|
|
||||
Bits[index] = valueToSet; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Sets the specified bits within the packed fields to the supplied
|
|
||||
/// value.
|
|
||||
/// </summary>
|
|
||||
/// <param name="startIndex">The zero-based index within the packed fields of the first bit to set.</param>
|
|
||||
/// <param name="length">The number of bits to set.</param>
|
|
||||
/// <param name="valueToSet">The value to set the bits to.</param>
|
|
||||
public void SetBits(int startIndex, int length, int valueToSet) |
|
||||
{ |
|
||||
if (startIndex < 0 || startIndex > 7) |
|
||||
{ |
|
||||
string message = $"Start index must be between 0 and 7. Supplied index: {startIndex}"; |
|
||||
throw new ArgumentOutOfRangeException(nameof(startIndex), message); |
|
||||
} |
|
||||
|
|
||||
if (length < 1 || startIndex + length > 8) |
|
||||
{ |
|
||||
string message = "Length must be greater than zero and the sum of length and start index must be less than 8. " |
|
||||
+ $"Supplied length: {length}. Supplied start index: {startIndex}"; |
|
||||
throw new ArgumentOutOfRangeException(nameof(length), message); |
|
||||
} |
|
||||
|
|
||||
int bitShift = length - 1; |
|
||||
for (int i = startIndex; i < startIndex + length; i++) |
|
||||
{ |
|
||||
int bitValueIfSet = 1 << bitShift; |
|
||||
int bitValue = valueToSet & bitValueIfSet; |
|
||||
int bitIsSet = bitValue >> bitShift; |
|
||||
Bits[i] = bitIsSet == 1; |
|
||||
bitShift--; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the value of the specified bit within the byte.
|
|
||||
/// </summary>
|
|
||||
/// <param name="index">The zero-based index of the bit to get.</param>
|
|
||||
/// <returns>
|
|
||||
/// The value of the specified bit within the byte.
|
|
||||
/// </returns>
|
|
||||
public bool GetBit(int index) |
|
||||
{ |
|
||||
if (index < 0 || index > 7) |
|
||||
{ |
|
||||
string message = $"Index must be between 0 and 7. Supplied index: {index}"; |
|
||||
throw new ArgumentOutOfRangeException(nameof(index), message); |
|
||||
} |
|
||||
|
|
||||
return Bits[index]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the value of the specified bits within the byte.
|
|
||||
/// </summary>
|
|
||||
/// <param name="startIndex">The zero-based index of the first bit to get.</param>
|
|
||||
/// <param name="length">The number of bits to get.</param>
|
|
||||
/// <returns>
|
|
||||
/// The value of the specified bits within the byte.
|
|
||||
/// </returns>
|
|
||||
public int GetBits(int startIndex, int length) |
|
||||
{ |
|
||||
if (startIndex < 0 || startIndex > 7) |
|
||||
{ |
|
||||
string message = $"Start index must be between 0 and 7. Supplied index: {startIndex}"; |
|
||||
throw new ArgumentOutOfRangeException(nameof(startIndex), message); |
|
||||
} |
|
||||
|
|
||||
if (length < 1 || startIndex + length > 8) |
|
||||
{ |
|
||||
string message = "Length must be greater than zero and the sum of length and start index must be less than 8. " |
|
||||
+ $"Supplied length: {length}. Supplied start index: {startIndex}"; |
|
||||
|
|
||||
throw new ArgumentOutOfRangeException(nameof(length), message); |
|
||||
} |
|
||||
|
|
||||
int returnValue = 0; |
|
||||
int bitShift = length - 1; |
|
||||
for (int i = startIndex; i < startIndex + length; i++) |
|
||||
{ |
|
||||
int bitValue = (Bits[i] ? 1 : 0) << bitShift; |
|
||||
returnValue += bitValue; |
|
||||
bitShift--; |
|
||||
} |
|
||||
|
|
||||
return returnValue; |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override bool Equals(object obj) |
|
||||
{ |
|
||||
return obj is PackedField other && this.Equals(other); |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public bool Equals(PackedField other) |
|
||||
{ |
|
||||
return this.Byte.Equals(other.Byte); |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override string ToString() |
|
||||
{ |
|
||||
return $"PackedField [ Byte={this.Byte} ]"; |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override int GetHashCode() |
|
||||
{ |
|
||||
return this.Byte.GetHashCode(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,106 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Gif |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The Graphic Control Extension contains parameters used when
|
||||
|
/// processing a graphic rendering block.
|
||||
|
/// </summary>
|
||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)] |
||||
|
internal readonly struct GifGraphicControlExtension : IGifExtension |
||||
|
{ |
||||
|
public GifGraphicControlExtension( |
||||
|
byte packed, |
||||
|
ushort delayTime, |
||||
|
byte transparencyIndex) |
||||
|
{ |
||||
|
this.BlockSize = 4; |
||||
|
this.Packed = packed; |
||||
|
this.DelayTime = delayTime; |
||||
|
this.TransparencyIndex = transparencyIndex; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the size of the block.
|
||||
|
/// </summary>
|
||||
|
public byte BlockSize { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the packed disposalMethod and transparencyFlag value.
|
||||
|
/// </summary>
|
||||
|
public byte Packed { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the delay time in of hundredths (1/100) of a second
|
||||
|
/// to wait before continuing with the processing of the Data Stream.
|
||||
|
/// The clock starts ticking immediately after the graphic is rendered.
|
||||
|
/// </summary>
|
||||
|
public ushort DelayTime { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the transparency index.
|
||||
|
/// The Transparency Index is such that when encountered, the corresponding pixel
|
||||
|
/// of the display device is not modified and processing goes on to the next pixel.
|
||||
|
/// </summary>
|
||||
|
public byte TransparencyIndex { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the disposal method which indicates the way in which the
|
||||
|
/// graphic is to be treated after being displayed.
|
||||
|
/// </summary>
|
||||
|
public DisposalMethod DisposalMethod => (DisposalMethod)((this.Packed & 0x1C) >> 2); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value indicating whether transparency flag is to be set.
|
||||
|
/// This indicates whether a transparency index is given in the Transparent Index field.
|
||||
|
/// </summary>
|
||||
|
public bool TransparencyFlag => (this.Packed & 0x01) == 1; |
||||
|
|
||||
|
byte IGifExtension.Label => GifConstants.GraphicControlLabel; |
||||
|
|
||||
|
public int WriteTo(Span<byte> buffer) |
||||
|
{ |
||||
|
ref GifGraphicControlExtension dest = ref Unsafe.As<byte, GifGraphicControlExtension>(ref MemoryMarshal.GetReference(buffer)); |
||||
|
|
||||
|
dest = this; |
||||
|
|
||||
|
return 5; |
||||
|
} |
||||
|
|
||||
|
public static GifGraphicControlExtension Parse(ReadOnlySpan<byte> buffer) |
||||
|
{ |
||||
|
return MemoryMarshal.Cast<byte, GifGraphicControlExtension>(buffer)[0]; |
||||
|
} |
||||
|
|
||||
|
public static byte GetPackedValue(DisposalMethod disposalMethod, bool userInputFlag = false, bool transparencyFlag = false) |
||||
|
{ |
||||
|
/* |
||||
|
Reserved | 3 Bits |
||||
|
Disposal Method | 3 Bits |
||||
|
User Input Flag | 1 Bit |
||||
|
Transparent Color Flag | 1 Bit |
||||
|
*/ |
||||
|
|
||||
|
byte value = 0; |
||||
|
|
||||
|
value |= (byte)((int)disposalMethod << 2); |
||||
|
|
||||
|
if (userInputFlag) |
||||
|
{ |
||||
|
value |= 1 << 1; |
||||
|
} |
||||
|
|
||||
|
if (transparencyFlag) |
||||
|
{ |
||||
|
value |= 1; |
||||
|
} |
||||
|
|
||||
|
return value; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,40 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Gif |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The Graphic Control Extension contains parameters used when
|
|
||||
/// processing a graphic rendering block.
|
|
||||
/// </summary>
|
|
||||
internal sealed class GifGraphicsControlExtension |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets or sets the disposal method which indicates the way in which the
|
|
||||
/// graphic is to be treated after being displayed.
|
|
||||
/// </summary>
|
|
||||
public DisposalMethod DisposalMethod { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets a value indicating whether transparency flag is to be set.
|
|
||||
/// This indicates whether a transparency index is given in the Transparent Index field.
|
|
||||
/// (This field is the least significant bit of the byte.)
|
|
||||
/// </summary>
|
|
||||
public bool TransparencyFlag { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the transparency index.
|
|
||||
/// The Transparency Index is such that when encountered, the corresponding pixel
|
|
||||
/// of the display device is not modified and processing goes on to the next pixel.
|
|
||||
/// </summary>
|
|
||||
public byte TransparencyIndex { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the delay time.
|
|
||||
/// If not 0, this field specifies the number of hundredths (1/100) of a second to
|
|
||||
/// wait before continuing with the processing of the Data Stream.
|
|
||||
/// The clock starts ticking immediately after the graphic is rendered.
|
|
||||
/// </summary>
|
|
||||
public int DelayTime { get; set; } |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,22 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Gif |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// A base interface for GIF extensions.
|
||||
|
/// </summary>
|
||||
|
public interface IGifExtension |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets the label identifying the extensions.
|
||||
|
/// </summary>
|
||||
|
byte Label { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes the extension data to the buffer.
|
||||
|
/// </summary>
|
||||
|
/// <param name="buffer">The buffer to write the extension to.</param>
|
||||
|
/// <returns>The number of bytes written to the buffer.</returns>
|
||||
|
int WriteTo(Span<byte> buffer); |
||||
|
} |
||||
|
} |
||||
@ -1,16 +0,0 @@ |
|||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Various utilities for <see cref="IJpegComponent"/>.
|
|
||||
/// </summary>
|
|
||||
internal static class ComponentUtils |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets a reference to the <see cref="Block8x8"/> at the given row and column index from <see cref="IJpegComponent.SpectralBlocks"/>
|
|
||||
/// </summary>
|
|
||||
public static ref Block8x8 GetBlockReference(this IJpegComponent component, int bx, int by) |
|
||||
{ |
|
||||
return ref component.SpectralBlocks[bx, by]; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,24 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components |
||||
|
{ |
||||
|
[StructLayout(LayoutKind.Sequential)] |
||||
|
internal unsafe struct FixedByteBuffer256 |
||||
|
{ |
||||
|
public fixed byte Data[256]; |
||||
|
|
||||
|
public byte this[int idx] |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get |
||||
|
{ |
||||
|
ref byte self = ref Unsafe.As<FixedByteBuffer256, byte>(ref this); |
||||
|
return Unsafe.Add(ref self, idx); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components |
||||
|
{ |
||||
|
[StructLayout(LayoutKind.Sequential)] |
||||
|
internal unsafe struct FixedInt16Buffer18 |
||||
|
{ |
||||
|
public fixed short Data[18]; |
||||
|
|
||||
|
public short this[int idx] |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get |
||||
|
{ |
||||
|
ref short self = ref Unsafe.As<FixedInt16Buffer18, short>(ref this); |
||||
|
return Unsafe.Add(ref self, idx); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components |
||||
|
{ |
||||
|
[StructLayout(LayoutKind.Sequential)] |
||||
|
internal unsafe struct FixedInt16Buffer256 |
||||
|
{ |
||||
|
public fixed short Data[256]; |
||||
|
|
||||
|
public short this[int idx] |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get |
||||
|
{ |
||||
|
ref short self = ref Unsafe.As<FixedInt16Buffer256, short>(ref this); |
||||
|
return Unsafe.Add(ref self, idx); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components |
||||
|
{ |
||||
|
[StructLayout(LayoutKind.Sequential)] |
||||
|
internal unsafe struct FixedInt64Buffer18 |
||||
|
{ |
||||
|
public fixed long Data[18]; |
||||
|
|
||||
|
public long this[int idx] |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
get |
||||
|
{ |
||||
|
ref long self = ref Unsafe.As<FixedInt64Buffer18, long>(ref this); |
||||
|
return Unsafe.Add(ref self, idx); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,43 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Numerics; |
|
||||
using SixLabors.ImageSharp.Memory; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Represents a component block
|
|
||||
/// </summary>
|
|
||||
internal class PdfJsComponent : IDisposable |
|
||||
{ |
|
||||
#pragma warning disable SA1401
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the output
|
|
||||
/// </summary>
|
|
||||
public IBuffer<short> Output; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the scaling factors
|
|
||||
/// </summary>
|
|
||||
public Vector2 Scale; |
|
||||
|
|
||||
/// <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; |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public void Dispose() |
|
||||
{ |
|
||||
this.Output?.Dispose(); |
|
||||
this.Output = null; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,32 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Contains all the decoded component blocks
|
|
||||
/// </summary>
|
|
||||
internal sealed class PdfJsComponentBlocks : IDisposable |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Gets or sets the component blocks
|
|
||||
/// </summary>
|
|
||||
public PdfJsComponent[] Components { get; set; } |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public void Dispose() |
|
||||
{ |
|
||||
if (this.Components != null) |
|
||||
{ |
|
||||
for (int i = 0; i < this.Components.Length; i++) |
|
||||
{ |
|
||||
this.Components[i].Dispose(); |
|
||||
} |
|
||||
|
|
||||
this.Components = null; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,513 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using SixLabors.ImageSharp.Memory; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Performs the inverse Descrete Cosine Transform on each frame component.
|
|
||||
/// </summary>
|
|
||||
internal static class PdfJsIDCT |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Precomputed values scaled up by 14 bits
|
|
||||
/// </summary>
|
|
||||
public static readonly short[] Aanscales = |
|
||||
{ |
|
||||
16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, 22725, 31521, 29692, 26722, 22725, 17855, |
|
||||
12299, 6270, 21407, 29692, 27969, 25172, 21407, 16819, 11585, |
|
||||
5906, 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, |
|
||||
16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, 12873, |
|
||||
17855, 16819, 15137, 12873, 10114, 6967, 3552, 8867, 12299, |
|
||||
11585, 10426, 8867, 6967, 4799, 2446, 4520, 6270, 5906, 5315, |
|
||||
4520, 3552, 2446, 1247 |
|
||||
}; |
|
||||
|
|
||||
private const int DctCos1 = 4017; // cos(pi/16)
|
|
||||
private const int DctSin1 = 799; // sin(pi/16)
|
|
||||
private const int DctCos3 = 3406; // cos(3*pi/16)
|
|
||||
private const int DctSin3 = 2276; // sin(3*pi/16)
|
|
||||
private const int DctCos6 = 1567; // cos(6*pi/16)
|
|
||||
private const int DctSin6 = 3784; // sin(6*pi/16)
|
|
||||
private const int DctSqrt2 = 5793; // sqrt(2)
|
|
||||
private const int DctSqrt1D2 = 2896; // sqrt(2) / 2
|
|
||||
|
|
||||
#pragma warning disable SA1310 // Field names must not contain underscore
|
|
||||
private const int FIX_1_082392200 = 277; // FIX(1.082392200)
|
|
||||
private const int FIX_1_414213562 = 362; // FIX(1.414213562)
|
|
||||
private const int FIX_1_847759065 = 473; // FIX(1.847759065)
|
|
||||
private const int FIX_2_613125930 = 669; // FIX(2.613125930)
|
|
||||
#pragma warning restore SA1310 // Field names must not contain underscore
|
|
||||
|
|
||||
private const int ConstBits = 8; |
|
||||
private const int Pass1Bits = 2; // Factional bits in scale factors
|
|
||||
private const int MaxJSample = 255; |
|
||||
private const int CenterJSample = 128; |
|
||||
private const int RangeCenter = (MaxJSample * 2) + 2; |
|
||||
|
|
||||
// First segment of range limit table: limit[x] = 0 for x < 0
|
|
||||
// allow negative subscripts of simple table
|
|
||||
private const int TableOffset = 2 * (MaxJSample + 1); |
|
||||
private const int LimitOffset = TableOffset - (RangeCenter - CenterJSample); |
|
||||
|
|
||||
// Each IDCT routine is responsible for range-limiting its results and
|
|
||||
// converting them to unsigned form (0..MaxJSample). The raw outputs could
|
|
||||
// be quite far out of range if the input data is corrupt, so a bulletproof
|
|
||||
// range-limiting step is required. We use a mask-and-table-lookup method
|
|
||||
// to do the combined operations quickly, assuming that MaxJSample+1
|
|
||||
// is a power of 2.
|
|
||||
private const int RangeMask = (MaxJSample * 4) + 3; // 2 bits wider than legal samples
|
|
||||
|
|
||||
private static readonly byte[] Limit = new byte[5 * (MaxJSample + 1)]; |
|
||||
|
|
||||
static PdfJsIDCT() |
|
||||
{ |
|
||||
// Main part of range limit table: limit[x] = x
|
|
||||
int i; |
|
||||
for (i = 0; i <= MaxJSample; i++) |
|
||||
{ |
|
||||
Limit[TableOffset + i] = (byte)i; |
|
||||
} |
|
||||
|
|
||||
// End of range limit table: Limit[x] = MaxJSample for x > MaxJSample
|
|
||||
for (; i < 3 * (MaxJSample + 1); i++) |
|
||||
{ |
|
||||
Limit[TableOffset + i] = MaxJSample; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// A port of Poppler's IDCT method which in turn is taken from:
|
|
||||
/// Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz,
|
|
||||
/// 'Practical Fast 1-D DCT Algorithms with 11 Multiplications',
|
|
||||
/// IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989, 988-991.
|
|
||||
/// </summary>
|
|
||||
/// <param name="component">The fram component</param>
|
|
||||
/// <param name="blockBufferOffset">The block buffer offset</param>
|
|
||||
/// <param name="computationBuffer">The computational buffer for holding temp values</param>
|
|
||||
/// <param name="quantizationTable">The quantization table</param>
|
|
||||
public static void QuantizeAndInverse(PdfJsFrameComponent component, int blockBufferOffset, ref Span<short> computationBuffer, ref Span<short> quantizationTable) |
|
||||
{ |
|
||||
Span<short> blockData = component.BlockData.Slice(blockBufferOffset); |
|
||||
int v0, v1, v2, v3, v4, v5, v6, v7; |
|
||||
int p0, p1, p2, p3, p4, p5, p6, p7; |
|
||||
int t; |
|
||||
|
|
||||
// inverse DCT on rows
|
|
||||
for (int row = 0; row < 64; row += 8) |
|
||||
{ |
|
||||
// gather block data
|
|
||||
p0 = blockData[row]; |
|
||||
p1 = blockData[row + 1]; |
|
||||
p2 = blockData[row + 2]; |
|
||||
p3 = blockData[row + 3]; |
|
||||
p4 = blockData[row + 4]; |
|
||||
p5 = blockData[row + 5]; |
|
||||
p6 = blockData[row + 6]; |
|
||||
p7 = blockData[row + 7]; |
|
||||
|
|
||||
// dequant p0
|
|
||||
p0 *= quantizationTable[row]; |
|
||||
|
|
||||
// check for all-zero AC coefficients
|
|
||||
if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) == 0) |
|
||||
{ |
|
||||
t = ((DctSqrt2 * p0) + 512) >> 10; |
|
||||
short st = (short)t; |
|
||||
computationBuffer[row] = st; |
|
||||
computationBuffer[row + 1] = st; |
|
||||
computationBuffer[row + 2] = st; |
|
||||
computationBuffer[row + 3] = st; |
|
||||
computationBuffer[row + 4] = st; |
|
||||
computationBuffer[row + 5] = st; |
|
||||
computationBuffer[row + 6] = st; |
|
||||
computationBuffer[row + 7] = st; |
|
||||
continue; |
|
||||
} |
|
||||
|
|
||||
// dequant p1 ... p7
|
|
||||
p1 *= quantizationTable[row + 1]; |
|
||||
p2 *= quantizationTable[row + 2]; |
|
||||
p3 *= quantizationTable[row + 3]; |
|
||||
p4 *= quantizationTable[row + 4]; |
|
||||
p5 *= quantizationTable[row + 5]; |
|
||||
p6 *= quantizationTable[row + 6]; |
|
||||
p7 *= quantizationTable[row + 7]; |
|
||||
|
|
||||
// stage 4
|
|
||||
v0 = ((DctSqrt2 * p0) + 128) >> 8; |
|
||||
v1 = ((DctSqrt2 * p4) + 128) >> 8; |
|
||||
v2 = p2; |
|
||||
v3 = p6; |
|
||||
v4 = ((DctSqrt1D2 * (p1 - p7)) + 128) >> 8; |
|
||||
v7 = ((DctSqrt1D2 * (p1 + p7)) + 128) >> 8; |
|
||||
v5 = p3 << 4; |
|
||||
v6 = p5 << 4; |
|
||||
|
|
||||
// stage 3
|
|
||||
v0 = (v0 + v1 + 1) >> 1; |
|
||||
v1 = v0 - v1; |
|
||||
t = ((v2 * DctSin6) + (v3 * DctCos6) + 128) >> 8; |
|
||||
v2 = ((v2 * DctCos6) - (v3 * DctSin6) + 128) >> 8; |
|
||||
v3 = t; |
|
||||
v4 = (v4 + v6 + 1) >> 1; |
|
||||
v6 = v4 - v6; |
|
||||
v7 = (v7 + v5 + 1) >> 1; |
|
||||
v5 = v7 - v5; |
|
||||
|
|
||||
// stage 2
|
|
||||
v0 = (v0 + v3 + 1) >> 1; |
|
||||
v3 = v0 - v3; |
|
||||
v1 = (v1 + v2 + 1) >> 1; |
|
||||
v2 = v1 - v2; |
|
||||
t = ((v4 * DctSin3) + (v7 * DctCos3) + 2048) >> 12; |
|
||||
v4 = ((v4 * DctCos3) - (v7 * DctSin3) + 2048) >> 12; |
|
||||
v7 = t; |
|
||||
t = ((v5 * DctSin1) + (v6 * DctCos1) + 2048) >> 12; |
|
||||
v5 = ((v5 * DctCos1) - (v6 * DctSin1) + 2048) >> 12; |
|
||||
v6 = t; |
|
||||
|
|
||||
// stage 1
|
|
||||
computationBuffer[row] = (short)(v0 + v7); |
|
||||
computationBuffer[row + 7] = (short)(v0 - v7); |
|
||||
computationBuffer[row + 1] = (short)(v1 + v6); |
|
||||
computationBuffer[row + 6] = (short)(v1 - v6); |
|
||||
computationBuffer[row + 2] = (short)(v2 + v5); |
|
||||
computationBuffer[row + 5] = (short)(v2 - v5); |
|
||||
computationBuffer[row + 3] = (short)(v3 + v4); |
|
||||
computationBuffer[row + 4] = (short)(v3 - v4); |
|
||||
} |
|
||||
|
|
||||
// inverse DCT on columns
|
|
||||
for (int col = 0; col < 8; ++col) |
|
||||
{ |
|
||||
p0 = computationBuffer[col]; |
|
||||
p1 = computationBuffer[col + 8]; |
|
||||
p2 = computationBuffer[col + 16]; |
|
||||
p3 = computationBuffer[col + 24]; |
|
||||
p4 = computationBuffer[col + 32]; |
|
||||
p5 = computationBuffer[col + 40]; |
|
||||
p6 = computationBuffer[col + 48]; |
|
||||
p7 = computationBuffer[col + 56]; |
|
||||
|
|
||||
// check for all-zero AC coefficients
|
|
||||
if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) == 0) |
|
||||
{ |
|
||||
t = ((DctSqrt2 * p0) + 8192) >> 14; |
|
||||
|
|
||||
// convert to 8 bit
|
|
||||
t = (t < -2040) ? 0 : (t >= 2024) ? MaxJSample : (t + 2056) >> 4; |
|
||||
short st = (short)t; |
|
||||
|
|
||||
blockData[col] = st; |
|
||||
blockData[col + 8] = st; |
|
||||
blockData[col + 16] = st; |
|
||||
blockData[col + 24] = st; |
|
||||
blockData[col + 32] = st; |
|
||||
blockData[col + 40] = st; |
|
||||
blockData[col + 48] = st; |
|
||||
blockData[col + 56] = st; |
|
||||
continue; |
|
||||
} |
|
||||
|
|
||||
// stage 4
|
|
||||
v0 = ((DctSqrt2 * p0) + 2048) >> 12; |
|
||||
v1 = ((DctSqrt2 * p4) + 2048) >> 12; |
|
||||
v2 = p2; |
|
||||
v3 = p6; |
|
||||
v4 = ((DctSqrt1D2 * (p1 - p7)) + 2048) >> 12; |
|
||||
v7 = ((DctSqrt1D2 * (p1 + p7)) + 2048) >> 12; |
|
||||
v5 = p3; |
|
||||
v6 = p5; |
|
||||
|
|
||||
// stage 3
|
|
||||
// Shift v0 by 128.5 << 5 here, so we don't need to shift p0...p7 when
|
|
||||
// converting to UInt8 range later.
|
|
||||
v0 = ((v0 + v1 + 1) >> 1) + 4112; |
|
||||
v1 = v0 - v1; |
|
||||
t = ((v2 * DctSin6) + (v3 * DctCos6) + 2048) >> 12; |
|
||||
v2 = ((v2 * DctCos6) - (v3 * DctSin6) + 2048) >> 12; |
|
||||
v3 = t; |
|
||||
v4 = (v4 + v6 + 1) >> 1; |
|
||||
v6 = v4 - v6; |
|
||||
v7 = (v7 + v5 + 1) >> 1; |
|
||||
v5 = v7 - v5; |
|
||||
|
|
||||
// stage 2
|
|
||||
v0 = (v0 + v3 + 1) >> 1; |
|
||||
v3 = v0 - v3; |
|
||||
v1 = (v1 + v2 + 1) >> 1; |
|
||||
v2 = v1 - v2; |
|
||||
t = ((v4 * DctSin3) + (v7 * DctCos3) + 2048) >> 12; |
|
||||
v4 = ((v4 * DctCos3) - (v7 * DctSin3) + 2048) >> 12; |
|
||||
v7 = t; |
|
||||
t = ((v5 * DctSin1) + (v6 * DctCos1) + 2048) >> 12; |
|
||||
v5 = ((v5 * DctCos1) - (v6 * DctSin1) + 2048) >> 12; |
|
||||
v6 = t; |
|
||||
|
|
||||
// stage 1
|
|
||||
p0 = v0 + v7; |
|
||||
p7 = v0 - v7; |
|
||||
p1 = v1 + v6; |
|
||||
p6 = v1 - v6; |
|
||||
p2 = v2 + v5; |
|
||||
p5 = v2 - v5; |
|
||||
p3 = v3 + v4; |
|
||||
p4 = v3 - v4; |
|
||||
|
|
||||
// convert to 8-bit integers
|
|
||||
p0 = (p0 < 16) ? 0 : (p0 >= 4080) ? MaxJSample : p0 >> 4; |
|
||||
p1 = (p1 < 16) ? 0 : (p1 >= 4080) ? MaxJSample : p1 >> 4; |
|
||||
p2 = (p2 < 16) ? 0 : (p2 >= 4080) ? MaxJSample : p2 >> 4; |
|
||||
p3 = (p3 < 16) ? 0 : (p3 >= 4080) ? MaxJSample : p3 >> 4; |
|
||||
p4 = (p4 < 16) ? 0 : (p4 >= 4080) ? MaxJSample : p4 >> 4; |
|
||||
p5 = (p5 < 16) ? 0 : (p5 >= 4080) ? MaxJSample : p5 >> 4; |
|
||||
p6 = (p6 < 16) ? 0 : (p6 >= 4080) ? MaxJSample : p6 >> 4; |
|
||||
p7 = (p7 < 16) ? 0 : (p7 >= 4080) ? MaxJSample : p7 >> 4; |
|
||||
|
|
||||
// store block data
|
|
||||
blockData[col] = (short)p0; |
|
||||
blockData[col + 8] = (short)p1; |
|
||||
blockData[col + 16] = (short)p2; |
|
||||
blockData[col + 24] = (short)p3; |
|
||||
blockData[col + 32] = (short)p4; |
|
||||
blockData[col + 40] = (short)p5; |
|
||||
blockData[col + 48] = (short)p6; |
|
||||
blockData[col + 56] = (short)p7; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// A port of <see href="https://github.com/libjpeg-turbo/libjpeg-turbo/blob/master/jidctfst.c#L171"/>
|
|
||||
/// A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT
|
|
||||
/// on each row(or vice versa, but it's more convenient to emit a row at
|
|
||||
/// a time). Direct algorithms are also available, but they are much more
|
|
||||
/// complex and seem not to be any faster when reduced to code.
|
|
||||
///
|
|
||||
/// This implementation is based on Arai, Agui, and Nakajima's algorithm for
|
|
||||
/// scaled DCT.Their original paper (Trans.IEICE E-71(11):1095) is in
|
|
||||
/// Japanese, but the algorithm is described in the Pennebaker & Mitchell
|
|
||||
/// JPEG textbook(see REFERENCES section in file README.ijg). The following
|
|
||||
/// code is based directly on figure 4-8 in P&M.
|
|
||||
/// While an 8-point DCT cannot be done in less than 11 multiplies, it is
|
|
||||
/// possible to arrange the computation so that many of the multiplies are
|
|
||||
/// simple scalings of the final outputs.These multiplies can then be
|
|
||||
/// folded into the multiplications or divisions by the JPEG quantization
|
|
||||
/// table entries. The AA&N method leaves only 5 multiplies and 29 adds
|
|
||||
/// to be done in the DCT itself.
|
|
||||
/// The primary disadvantage of this method is that with fixed-point math,
|
|
||||
/// accuracy is lost due to imprecise representation of the scaled
|
|
||||
/// quantization values.The smaller the quantization table entry, the less
|
|
||||
/// precise the scaled value, so this implementation does worse with high -
|
|
||||
/// quality - setting files than with low - quality ones.
|
|
||||
/// </summary>
|
|
||||
/// <param name="component">The frame component</param>
|
|
||||
/// <param name="blockBufferOffset">The block buffer offset</param>
|
|
||||
/// <param name="computationBuffer">The computational buffer for holding temp values</param>
|
|
||||
/// <param name="multiplierTable">The multiplier table</param>
|
|
||||
public static void QuantizeAndInverseFast(PdfJsFrameComponent component, int blockBufferOffset, ref Span<short> computationBuffer, ref Span<short> multiplierTable) |
|
||||
{ |
|
||||
Span<short> blockData = component.BlockData.Slice(blockBufferOffset); |
|
||||
int p0, p1, p2, p3, p4, p5, p6, p7; |
|
||||
|
|
||||
for (int col = 0; col < 8; col++) |
|
||||
{ |
|
||||
// Gather block data
|
|
||||
p0 = blockData[col]; |
|
||||
p1 = blockData[col + 8]; |
|
||||
p2 = blockData[col + 16]; |
|
||||
p3 = blockData[col + 24]; |
|
||||
p4 = blockData[col + 32]; |
|
||||
p5 = blockData[col + 40]; |
|
||||
p6 = blockData[col + 48]; |
|
||||
p7 = blockData[col + 56]; |
|
||||
|
|
||||
int tmp0 = p0 * multiplierTable[col]; |
|
||||
|
|
||||
// Due to quantization, we will usually find that many of the input
|
|
||||
// coefficients are zero, especially the AC terms. We can exploit this
|
|
||||
// by short-circuiting the IDCT calculation for any column in which all
|
|
||||
// the AC terms are zero. In that case each output is equal to the
|
|
||||
// DC coefficient (with scale factor as needed).
|
|
||||
// With typical images and quantization tables, half or more of the
|
|
||||
// column DCT calculations can be simplified this way.
|
|
||||
if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) == 0) |
|
||||
{ |
|
||||
short dcval = (short)tmp0; |
|
||||
|
|
||||
computationBuffer[col] = dcval; |
|
||||
computationBuffer[col + 8] = dcval; |
|
||||
computationBuffer[col + 16] = dcval; |
|
||||
computationBuffer[col + 24] = dcval; |
|
||||
computationBuffer[col + 32] = dcval; |
|
||||
computationBuffer[col + 40] = dcval; |
|
||||
computationBuffer[col + 48] = dcval; |
|
||||
computationBuffer[col + 56] = dcval; |
|
||||
|
|
||||
continue; |
|
||||
} |
|
||||
|
|
||||
// Even part
|
|
||||
int tmp1 = p2 * multiplierTable[col + 16]; |
|
||||
int tmp2 = p4 * multiplierTable[col + 32]; |
|
||||
int tmp3 = p6 * multiplierTable[col + 48]; |
|
||||
|
|
||||
int tmp10 = tmp0 + tmp2; // Phase 3
|
|
||||
int tmp11 = tmp0 - tmp2; |
|
||||
|
|
||||
int tmp13 = tmp1 + tmp3; // Phases 5-3
|
|
||||
int tmp12 = Multiply(tmp1 - tmp3, FIX_1_414213562) - tmp13; // 2*c4
|
|
||||
|
|
||||
tmp0 = tmp10 + tmp13; // Phase 2
|
|
||||
tmp3 = tmp10 - tmp13; |
|
||||
tmp1 = tmp11 + tmp12; |
|
||||
tmp2 = tmp11 - tmp12; |
|
||||
|
|
||||
// Odd Part
|
|
||||
int tmp4 = p1 * multiplierTable[col + 8]; |
|
||||
int tmp5 = p3 * multiplierTable[col + 24]; |
|
||||
int tmp6 = p5 * multiplierTable[col + 40]; |
|
||||
int tmp7 = p7 * multiplierTable[col + 56]; |
|
||||
|
|
||||
int z13 = tmp6 + tmp5; // Phase 6
|
|
||||
int z10 = tmp6 - tmp5; |
|
||||
int z11 = tmp4 + tmp7; |
|
||||
int z12 = tmp4 - tmp7; |
|
||||
|
|
||||
tmp7 = z11 + z13; // Phase 5
|
|
||||
tmp11 = Multiply(z11 - z13, FIX_1_414213562); // 2*c4
|
|
||||
|
|
||||
int z5 = Multiply(z10 + z12, FIX_1_847759065); // 2*c2
|
|
||||
tmp10 = z5 - Multiply(z12, FIX_1_082392200); // 2*(c2-c6)
|
|
||||
tmp12 = z5 - Multiply(z10, FIX_2_613125930); // 2*(c2+c6)
|
|
||||
|
|
||||
tmp6 = tmp12 - tmp7; // Phase 2
|
|
||||
tmp5 = tmp11 - tmp6; |
|
||||
tmp4 = tmp10 - tmp5; |
|
||||
|
|
||||
computationBuffer[col] = (short)(tmp0 + tmp7); |
|
||||
computationBuffer[col + 56] = (short)(tmp0 - tmp7); |
|
||||
computationBuffer[col + 8] = (short)(tmp1 + tmp6); |
|
||||
computationBuffer[col + 48] = (short)(tmp1 - tmp6); |
|
||||
computationBuffer[col + 16] = (short)(tmp2 + tmp5); |
|
||||
computationBuffer[col + 40] = (short)(tmp2 - tmp5); |
|
||||
computationBuffer[col + 24] = (short)(tmp3 + tmp4); |
|
||||
computationBuffer[col + 32] = (short)(tmp3 - tmp4); |
|
||||
} |
|
||||
|
|
||||
// Pass 2: process rows from work array, store into output array.
|
|
||||
// Note that we must descale the results by a factor of 8 == 2**3,
|
|
||||
// and also undo the pass 1 bits scaling.
|
|
||||
for (int row = 0; row < 64; row += 8) |
|
||||
{ |
|
||||
p1 = computationBuffer[row + 1]; |
|
||||
p2 = computationBuffer[row + 2]; |
|
||||
p3 = computationBuffer[row + 3]; |
|
||||
p4 = computationBuffer[row + 4]; |
|
||||
p5 = computationBuffer[row + 5]; |
|
||||
p6 = computationBuffer[row + 6]; |
|
||||
p7 = computationBuffer[row + 7]; |
|
||||
|
|
||||
// Add range center and fudge factor for final descale and range-limit.
|
|
||||
int z5 = computationBuffer[row] + (RangeCenter << (Pass1Bits + 3)) + (1 << (Pass1Bits + 2)); |
|
||||
|
|
||||
// Check for all-zero AC coefficients
|
|
||||
if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) == 0) |
|
||||
{ |
|
||||
byte dcval = Limit[LimitOffset + (RightShift(z5, Pass1Bits + 3) & RangeMask)]; |
|
||||
|
|
||||
blockData[row] = dcval; |
|
||||
blockData[row + 1] = dcval; |
|
||||
blockData[row + 2] = dcval; |
|
||||
blockData[row + 3] = dcval; |
|
||||
blockData[row + 4] = dcval; |
|
||||
blockData[row + 5] = dcval; |
|
||||
blockData[row + 6] = dcval; |
|
||||
blockData[row + 7] = dcval; |
|
||||
|
|
||||
continue; |
|
||||
} |
|
||||
|
|
||||
// Even part
|
|
||||
int tmp10 = z5 + p4; |
|
||||
int tmp11 = z5 - p4; |
|
||||
|
|
||||
int tmp13 = p2 + p6; |
|
||||
int tmp12 = Multiply(p2 - p6, FIX_1_414213562) - tmp13; // 2*c4
|
|
||||
|
|
||||
int tmp0 = tmp10 + tmp13; |
|
||||
int tmp3 = tmp10 - tmp13; |
|
||||
int tmp1 = tmp11 + tmp12; |
|
||||
int tmp2 = tmp11 - tmp12; |
|
||||
|
|
||||
// Odd part
|
|
||||
int z13 = p5 + p3; |
|
||||
int z10 = p5 - p3; |
|
||||
int z11 = p1 + p7; |
|
||||
int z12 = p1 - p7; |
|
||||
|
|
||||
int tmp7 = z11 + z13; // Phase 5
|
|
||||
tmp11 = Multiply(z11 - z13, FIX_1_414213562); // 2*c4
|
|
||||
|
|
||||
z5 = Multiply(z10 + z12, FIX_1_847759065); // 2*c2
|
|
||||
tmp10 = z5 - Multiply(z12, FIX_1_082392200); // 2*(c2-c6)
|
|
||||
tmp12 = z5 - Multiply(z10, FIX_2_613125930); // 2*(c2+c6)
|
|
||||
|
|
||||
int tmp6 = tmp12 - tmp7; // Phase 2
|
|
||||
int tmp5 = tmp11 - tmp6; |
|
||||
int tmp4 = tmp10 - tmp5; |
|
||||
|
|
||||
// Final output stage: scale down by a factor of 8, offset, and range-limit
|
|
||||
blockData[row] = Limit[LimitOffset + (RightShift(tmp0 + tmp7, Pass1Bits + 3) & RangeMask)]; |
|
||||
blockData[row + 7] = Limit[LimitOffset + (RightShift(tmp0 - tmp7, Pass1Bits + 3) & RangeMask)]; |
|
||||
blockData[row + 1] = Limit[LimitOffset + (RightShift(tmp1 + tmp6, Pass1Bits + 3) & RangeMask)]; |
|
||||
blockData[row + 6] = Limit[LimitOffset + (RightShift(tmp1 - tmp6, Pass1Bits + 3) & RangeMask)]; |
|
||||
blockData[row + 2] = Limit[LimitOffset + (RightShift(tmp2 + tmp5, Pass1Bits + 3) & RangeMask)]; |
|
||||
blockData[row + 5] = Limit[LimitOffset + (RightShift(tmp2 - tmp5, Pass1Bits + 3) & RangeMask)]; |
|
||||
blockData[row + 3] = Limit[LimitOffset + (RightShift(tmp3 + tmp4, Pass1Bits + 3) & RangeMask)]; |
|
||||
blockData[row + 4] = Limit[LimitOffset + (RightShift(tmp3 - tmp4, Pass1Bits + 3) & RangeMask)]; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Descale and correctly round an int value that's scaled by <paramref name="n"/> bits.
|
|
||||
/// We assume <see cref="RightShift"/> rounds towards minus infinity, so adding
|
|
||||
/// the fudge factor is correct for either sign of <paramref name="value"/>.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value</param>
|
|
||||
/// <param name="n">The number of bits</param>
|
|
||||
/// <returns>The <see cref="int"/></returns>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
public static int Descale(int value, int n) |
|
||||
{ |
|
||||
return RightShift(value + (1 << (n - 1)), n); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Multiply a variable by an int constant, and immediately descale.
|
|
||||
/// </summary>
|
|
||||
/// <param name="val">The value</param>
|
|
||||
/// <param name="c">The multiplier</param>
|
|
||||
/// <returns>The <see cref="int"/></returns>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private static int Multiply(int val, int c) |
|
||||
{ |
|
||||
return Descale(val * c, ConstBits); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Right-shifts the value by the given amount
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value</param>
|
|
||||
/// <param name="shift">The amount to shift by</param>
|
|
||||
/// <returns>The <see cref="int"/></returns>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private static int RightShift(int value, int shift) |
|
||||
{ |
|
||||
return value >> shift; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,149 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Diagnostics; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using SixLabors.ImageSharp.Memory; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Represents a section of the jpeg component data laid out in pixel order.
|
|
||||
/// </summary>
|
|
||||
internal struct PdfJsJpegPixelArea : IDisposable |
|
||||
{ |
|
||||
private readonly MemoryManager memoryManager; |
|
||||
|
|
||||
private readonly int imageWidth; |
|
||||
|
|
||||
private readonly int imageHeight; |
|
||||
|
|
||||
private IBuffer<byte> componentData; |
|
||||
|
|
||||
private int rowStride; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="PdfJsJpegPixelArea"/> struct.
|
|
||||
/// </summary>
|
|
||||
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
|
|
||||
/// <param name="imageWidth">The image width</param>
|
|
||||
/// <param name="imageHeight">The image height</param>
|
|
||||
/// <param name="numberOfComponents">The number of components</param>
|
|
||||
public PdfJsJpegPixelArea(MemoryManager memoryManager, int imageWidth, int imageHeight, int numberOfComponents) |
|
||||
{ |
|
||||
this.memoryManager = memoryManager; |
|
||||
this.imageWidth = imageWidth; |
|
||||
this.imageHeight = imageHeight; |
|
||||
this.Width = 0; |
|
||||
this.Height = 0; |
|
||||
this.NumberOfComponents = numberOfComponents; |
|
||||
this.componentData = null; |
|
||||
this.rowStride = 0; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the number of components
|
|
||||
/// </summary>
|
|
||||
public int NumberOfComponents { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the width
|
|
||||
/// </summary>
|
|
||||
public int Width { get; private set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the height
|
|
||||
/// </summary>
|
|
||||
public int Height { get; private set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Organsizes the decoded jpeg components into a linear array ordered by component.
|
|
||||
/// This must be called before attempting to retrieve the data.
|
|
||||
/// </summary>
|
|
||||
/// <param name="components">The jpeg component blocks</param>
|
|
||||
/// <param name="width">The pixel area width</param>
|
|
||||
/// <param name="height">The pixel area height</param>
|
|
||||
public void LinearizeBlockData(PdfJsComponentBlocks components, int width, int height) |
|
||||
{ |
|
||||
this.Width = width; |
|
||||
this.Height = height; |
|
||||
int numberOfComponents = this.NumberOfComponents; |
|
||||
this.rowStride = width * numberOfComponents; |
|
||||
var scale = new Vector2(this.imageWidth / (float)width, this.imageHeight / (float)height); |
|
||||
|
|
||||
this.componentData = this.memoryManager.Allocate<byte>(width * height * numberOfComponents); |
|
||||
Span<byte> componentDataSpan = this.componentData.Span; |
|
||||
const uint Mask3Lsb = 0xFFFFFFF8; // Used to clear the 3 LSBs
|
|
||||
|
|
||||
using (IBuffer<int> xScaleBlockOffset = this.memoryManager.Allocate<int>(width)) |
|
||||
{ |
|
||||
Span<int> xScaleBlockOffsetSpan = xScaleBlockOffset.Span; |
|
||||
for (int i = 0; i < numberOfComponents; i++) |
|
||||
{ |
|
||||
ref PdfJsComponent component = ref components.Components[i]; |
|
||||
Vector2 componentScale = component.Scale * scale; |
|
||||
int offset = i; |
|
||||
Span<short> output = component.Output.Span; |
|
||||
int blocksPerScanline = (component.BlocksPerLine + 1) << 3; |
|
||||
|
|
||||
// Precalculate the xScaleBlockOffset
|
|
||||
int j; |
|
||||
for (int x = 0; x < width; x++) |
|
||||
{ |
|
||||
j = (int)(x * componentScale.X); |
|
||||
xScaleBlockOffsetSpan[x] = (int)((j & Mask3Lsb) << 3) | (j & 7); |
|
||||
} |
|
||||
|
|
||||
// Linearize the blocks of the component
|
|
||||
for (int y = 0; y < height; y++) |
|
||||
{ |
|
||||
j = (int)(y * componentScale.Y); |
|
||||
int index = blocksPerScanline * (int)(j & Mask3Lsb) | ((j & 7) << 3); |
|
||||
for (int x = 0; x < width; x++) |
|
||||
{ |
|
||||
componentDataSpan[offset] = (byte)output[index + xScaleBlockOffsetSpan[x]]; |
|
||||
offset += numberOfComponents; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a <see cref="Span{Byte}"/> representing the row 'y' beginning from the the first byte on that row.
|
|
||||
/// </summary>
|
|
||||
/// <param name="y">The y-coordinate of the pixel row. Must be greater than or equal to zero and less than the height of the pixel area.</param>
|
|
||||
/// <returns>The <see cref="Span{TPixel}"/></returns>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
public Span<byte> GetRowSpan(int y) |
|
||||
{ |
|
||||
this.CheckCoordinates(y); |
|
||||
return this.componentData.Slice(y * this.rowStride, this.rowStride); |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public void Dispose() |
|
||||
{ |
|
||||
this.componentData?.Dispose(); |
|
||||
this.componentData = null; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Checks the coordinates to ensure they are within bounds.
|
|
||||
/// </summary>
|
|
||||
/// <param name="y">The y-coordinate of the row. Must be greater than zero and less than the height of the area.</param>
|
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
|
||||
/// Thrown if the coordinates are not within the bounds of the image.
|
|
||||
/// </exception>
|
|
||||
[Conditional("DEBUG")] |
|
||||
private void CheckCoordinates(int y) |
|
||||
{ |
|
||||
if (y < 0 || y >= this.Height) |
|
||||
{ |
|
||||
throw new ArgumentOutOfRangeException(nameof(y), y, $"{y} is outwith the area bounds."); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,67 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using SixLabors.ImageSharp.Memory; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Contains the quantization tables.
|
|
||||
/// </summary>
|
|
||||
internal sealed class PdfJsQuantizationTables : IDisposable |
|
||||
{ |
|
||||
public PdfJsQuantizationTables(MemoryManager memoryManager) |
|
||||
{ |
|
||||
this.Tables = memoryManager.Allocate2D<short>(64, 4); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the ZigZag scan table
|
|
||||
/// </summary>
|
|
||||
public static byte[] DctZigZag |
|
||||
{ |
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
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 Buffer2D<short> Tables |
|
||||
{ |
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
get; set; |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public void Dispose() |
|
||||
{ |
|
||||
if (this.Tables != null) |
|
||||
{ |
|
||||
this.Tables.Dispose(); |
|
||||
this.Tables = null; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,131 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System.Runtime.CompilerServices; |
|
||||
using SixLabors.ImageSharp.PixelFormats; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Provides 8-bit lookup tables for converting from YCbCr to Rgb colorspace.
|
|
||||
/// Methods to build the tables are based on libjpeg implementation.
|
|
||||
/// </summary>
|
|
||||
internal readonly struct PdfJsYCbCrToRgbTables |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The red red-chrominance table
|
|
||||
/// </summary>
|
|
||||
public static int[] CrRTable = new int[256]; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The blue blue-chrominance table
|
|
||||
/// </summary>
|
|
||||
public static int[] CbBTable = new int[256]; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The green red-chrominance table
|
|
||||
/// </summary>
|
|
||||
public static int[] CrGTable = new int[256]; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The green blue-chrominance table
|
|
||||
/// </summary>
|
|
||||
public static int[] CbGTable = new int[256]; |
|
||||
|
|
||||
// Speediest right-shift on some machines and gives us enough accuracy at 4 decimal places.
|
|
||||
private const int ScaleBits = 16; |
|
||||
|
|
||||
private const int Half = 1 << (ScaleBits - 1); |
|
||||
|
|
||||
private const int MinSample = 0; |
|
||||
|
|
||||
private const int HalfSample = 128; |
|
||||
|
|
||||
private const int MaxSample = 255; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes the YCbCr tables
|
|
||||
/// </summary>
|
|
||||
public static void Create() |
|
||||
{ |
|
||||
for (int i = 0, x = -128; i <= 255; i++, x++) |
|
||||
{ |
|
||||
// i is the actual input pixel value, in the range 0..255
|
|
||||
// The Cb or Cr value we are thinking of is x = i - 128
|
|
||||
// Cr=>R value is nearest int to 1.402 * x
|
|
||||
CrRTable[i] = RightShift((Fix(1.402F) * x) + Half); |
|
||||
|
|
||||
// Cb=>B value is nearest int to 1.772 * x
|
|
||||
CbBTable[i] = RightShift((Fix(1.772F) * x) + Half); |
|
||||
|
|
||||
// Cr=>G value is scaled-up -0.714136286
|
|
||||
CrGTable[i] = (-Fix(0.714136286F)) * x; |
|
||||
|
|
||||
// Cb => G value is scaled - up - 0.344136286 * x
|
|
||||
// We also add in Half so that need not do it in inner loop
|
|
||||
CbGTable[i] = ((-Fix(0.344136286F)) * x) + Half; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Optimized method to pack bytes to the image from the YCbCr color space.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|
||||
/// <param name="packed">The packed pixel.</param>
|
|
||||
/// <param name="y">The y luminance component.</param>
|
|
||||
/// <param name="cb">The cb chroma component.</param>
|
|
||||
/// <param name="cr">The cr chroma component.</param>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
public static void PackYCbCr<TPixel>(ref TPixel packed, byte y, byte cb, byte cr) |
|
||||
where TPixel : struct, IPixel<TPixel> |
|
||||
{ |
|
||||
byte r = (byte)(y + CrRTable[cr]).Clamp(0, 255); |
|
||||
|
|
||||
// The values for the G calculation are left scaled up, since we must add them together before rounding.
|
|
||||
byte g = (byte)(y + RightShift(CbGTable[cb] + CrGTable[cr])).Clamp(0, 255); |
|
||||
|
|
||||
byte b = (byte)(y + CbBTable[cb]).Clamp(0, 255); |
|
||||
|
|
||||
packed.PackFromRgba32(new Rgba32(r, g, b, 255)); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Optimized method to pack bytes to the image from the YccK color space.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|
||||
/// <param name="packed">The packed pixel.</param>
|
|
||||
/// <param name="y">The y luminance component.</param>
|
|
||||
/// <param name="cb">The cb chroma component.</param>
|
|
||||
/// <param name="cr">The cr chroma component.</param>
|
|
||||
/// <param name="k">The keyline component.</param>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
public static void PackYccK<TPixel>(ref TPixel packed, byte y, byte cb, byte cr, byte k) |
|
||||
where TPixel : struct, IPixel<TPixel> |
|
||||
{ |
|
||||
int c = (MaxSample - (y + CrRTable[cr])).Clamp(0, 255); |
|
||||
|
|
||||
// The values for the G calculation are left scaled up, since we must add them together before rounding.
|
|
||||
int m = (MaxSample - (y + RightShift(CbGTable[cb] + CrGTable[cr]))).Clamp(0, 255); |
|
||||
|
|
||||
int cy = (MaxSample - (y + CbBTable[cb])).Clamp(0, 255); |
|
||||
|
|
||||
byte r = (byte)((c * k) / MaxSample); |
|
||||
byte g = (byte)((m * k) / MaxSample); |
|
||||
byte b = (byte)((cy * k) / MaxSample); |
|
||||
|
|
||||
packed.PackFromRgba32(new Rgba32(r, g, b, MaxSample)); |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private static int Fix(float x) |
|
||||
{ |
|
||||
return (int)((x * (1L << ScaleBits)) + 0.5F); |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private static int RightShift(int x) |
|
||||
{ |
|
||||
return x >> ScaleBits; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue