mirror of https://github.com/SixLabors/ImageSharp
101 changed files with 2488 additions and 622 deletions
@ -1,21 +1,21 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
/// <summary>
|
|||
/// Enumerates the quantization tables
|
|||
/// Enumerates the quantization tables.
|
|||
/// </summary>
|
|||
internal enum QuantIndex |
|||
{ |
|||
/// <summary>
|
|||
/// The luminance quantization table index
|
|||
/// The luminance quantization table index.
|
|||
/// </summary>
|
|||
Luminance = 0, |
|||
|
|||
/// <summary>
|
|||
/// The chrominance quantization table index
|
|||
/// The chrominance quantization table index.
|
|||
/// </summary>
|
|||
Chrominance = 1, |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,114 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using SixLabors.ImageSharp.Advanced; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
/// <summary>
|
|||
/// On-stack worker struct to convert TPixel -> Rgb24 of 8x8 pixel blocks.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel type to work on.</typeparam>
|
|||
internal ref struct RgbForwardConverter<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
/// <summary>
|
|||
/// Number of pixels processed per single <see cref="Convert(int, int, ref RowOctet{TPixel})"/> call
|
|||
/// </summary>
|
|||
private const int PixelsPerSample = 8 * 8; |
|||
|
|||
/// <summary>
|
|||
/// Total byte size of processed pixels converted from TPixel to <see cref="Rgb24"/>
|
|||
/// </summary>
|
|||
private const int RgbSpanByteSize = PixelsPerSample * 3; |
|||
|
|||
/// <summary>
|
|||
/// <see cref="Size"/> of sampling area from given frame pixel buffer.
|
|||
/// </summary>
|
|||
private static readonly Size SampleSize = new Size(8, 8); |
|||
|
|||
/// <summary>
|
|||
/// The Red component.
|
|||
/// </summary>
|
|||
public Block8x8F R; |
|||
|
|||
/// <summary>
|
|||
/// The Green component.
|
|||
/// </summary>
|
|||
public Block8x8F G; |
|||
|
|||
/// <summary>
|
|||
/// The Blue component.
|
|||
/// </summary>
|
|||
public Block8x8F B; |
|||
|
|||
/// <summary>
|
|||
/// Temporal 64-byte span to hold unconverted TPixel data.
|
|||
/// </summary>
|
|||
private readonly Span<TPixel> pixelSpan; |
|||
|
|||
/// <summary>
|
|||
/// Temporal 64-byte span to hold converted Rgb24 data.
|
|||
/// </summary>
|
|||
private readonly Span<Rgb24> rgbSpan; |
|||
|
|||
/// <summary>
|
|||
/// Sampled pixel buffer size.
|
|||
/// </summary>
|
|||
private readonly Size samplingAreaSize; |
|||
|
|||
/// <summary>
|
|||
/// <see cref="Configuration"/> for internal operations.
|
|||
/// </summary>
|
|||
private readonly Configuration config; |
|||
|
|||
public RgbForwardConverter(ImageFrame<TPixel> frame) |
|||
{ |
|||
this.R = default; |
|||
this.G = default; |
|||
this.B = default; |
|||
|
|||
// temporal pixel buffers
|
|||
this.pixelSpan = new TPixel[PixelsPerSample].AsSpan(); |
|||
this.rgbSpan = MemoryMarshal.Cast<byte, Rgb24>(new byte[RgbSpanByteSize + RgbToYCbCrConverterVectorized.AvxCompatibilityPadding].AsSpan()); |
|||
|
|||
// frame data
|
|||
this.samplingAreaSize = new Size(frame.Width, frame.Height); |
|||
this.config = frame.GetConfiguration(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts a 8x8 image area inside 'pixels' at position (x, y) to Rgb24.
|
|||
/// </summary>
|
|||
public void Convert(int x, int y, ref RowOctet<TPixel> currentRows) |
|||
{ |
|||
YCbCrForwardConverter<TPixel>.LoadAndStretchEdges(currentRows, this.pixelSpan, new Point(x, y), SampleSize, this.samplingAreaSize); |
|||
|
|||
PixelOperations<TPixel>.Instance.ToRgb24(this.config, this.pixelSpan, this.rgbSpan); |
|||
|
|||
ref Block8x8F redBlock = ref this.R; |
|||
ref Block8x8F greenBlock = ref this.G; |
|||
ref Block8x8F blueBlock = ref this.B; |
|||
|
|||
CopyToBlock(this.rgbSpan, ref redBlock, ref greenBlock, ref blueBlock); |
|||
} |
|||
|
|||
private static void CopyToBlock(Span<Rgb24> rgbSpan, ref Block8x8F redBlock, ref Block8x8F greenBlock, ref Block8x8F blueBlock) |
|||
{ |
|||
ref Rgb24 rgbStart = ref MemoryMarshal.GetReference(rgbSpan); |
|||
|
|||
for (int i = 0; i < Block8x8F.Size; i++) |
|||
{ |
|||
Rgb24 c = Unsafe.Add(ref rgbStart, (nint)(uint)i); |
|||
|
|||
redBlock[i] = c.R; |
|||
greenBlock[i] = c.G; |
|||
blueBlock[i] = c.B; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,23 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg |
|||
{ |
|||
/// <summary>
|
|||
/// Enumerates the chroma subsampling method applied to the image.
|
|||
/// </summary>
|
|||
public enum JpegSubsample |
|||
{ |
|||
/// <summary>
|
|||
/// High Quality - Each of the three Y'CbCr components have the same sample rate,
|
|||
/// thus there is no chroma subsampling.
|
|||
/// </summary>
|
|||
Ratio444, |
|||
|
|||
/// <summary>
|
|||
/// Medium Quality - The horizontal sampling is halved and the Cb and Cr channels are only
|
|||
/// sampled on each alternate line.
|
|||
/// </summary>
|
|||
Ratio420 |
|||
} |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.IO; |
|||
using SixLabors.ImageSharp.Formats.Jpeg; |
|||
using SixLabors.ImageSharp.Formats.Tiff.Constants; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors |
|||
{ |
|||
internal class TiffJpegCompressor : TiffBaseCompressor |
|||
{ |
|||
public TiffJpegCompressor(Stream output, MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffPredictor predictor = TiffPredictor.None) |
|||
: base(output, memoryAllocator, width, bitsPerPixel, predictor) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override TiffCompression Method => TiffCompression.Jpeg; |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void Initialize(int rowsPerStrip) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void CompressStrip(Span<byte> rows, int height) |
|||
{ |
|||
int pixelCount = rows.Length / 3; |
|||
int width = pixelCount / height; |
|||
|
|||
using var memoryStream = new MemoryStream(); |
|||
var image = Image.LoadPixelData<Rgb24>(rows, width, height); |
|||
image.Save(memoryStream, new JpegEncoder() |
|||
{ |
|||
ColorType = JpegColorType.Rgb |
|||
}); |
|||
memoryStream.Position = 0; |
|||
memoryStream.WriteTo(this.Output); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void Dispose(bool disposing) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,154 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a reference scan line for CCITT 2D decoding.
|
|||
/// </summary>
|
|||
internal readonly ref struct CcittReferenceScanline |
|||
{ |
|||
private readonly ReadOnlySpan<byte> scanLine; |
|||
private readonly int width; |
|||
private readonly byte whiteByte; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CcittReferenceScanline"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="whiteIsZero">Indicates, if white is zero, otherwise black is zero.</param>
|
|||
/// <param name="scanLine">The scan line.</param>
|
|||
public CcittReferenceScanline(bool whiteIsZero, ReadOnlySpan<byte> scanLine) |
|||
{ |
|||
this.scanLine = scanLine; |
|||
this.width = scanLine.Length; |
|||
this.whiteByte = whiteIsZero ? (byte)0 : (byte)255; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CcittReferenceScanline"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="whiteIsZero">Indicates, if white is zero, otherwise black is zero.</param>
|
|||
/// <param name="width">The width of the scanline.</param>
|
|||
public CcittReferenceScanline(bool whiteIsZero, int width) |
|||
{ |
|||
this.scanLine = default; |
|||
this.width = width; |
|||
this.whiteByte = whiteIsZero ? (byte)0 : (byte)255; |
|||
} |
|||
|
|||
public bool IsEmpty => this.scanLine.IsEmpty; |
|||
|
|||
/// <summary>
|
|||
/// Finds b1: The first changing element on the reference line to the right of a0 and of opposite color to a0.
|
|||
/// </summary>
|
|||
/// <param name="a0">The reference or starting element om the coding line.</param>
|
|||
/// <param name="a0Byte">Fill byte.</param>
|
|||
/// <returns>Position of b1.</returns>
|
|||
public int FindB1(int a0, byte a0Byte) |
|||
{ |
|||
if (this.IsEmpty) |
|||
{ |
|||
return this.FindB1ForImaginaryWhiteLine(a0, a0Byte); |
|||
} |
|||
|
|||
return this.FindB1ForNormalLine(a0, a0Byte); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Finds b2: The next changing element to the right of b1 on the reference line.
|
|||
/// </summary>
|
|||
/// <param name="b1">The first changing element on the reference line to the right of a0 and opposite of color to a0.</param>
|
|||
/// <returns>Position of b1.</returns>
|
|||
public int FindB2(int b1) |
|||
{ |
|||
if (this.IsEmpty) |
|||
{ |
|||
return this.FindB2ForImaginaryWhiteLine(); |
|||
} |
|||
|
|||
return this.FindB2ForNormalLine(b1); |
|||
} |
|||
|
|||
private int FindB1ForImaginaryWhiteLine(int a0, byte a0Byte) |
|||
{ |
|||
if (a0 < 0) |
|||
{ |
|||
if (a0Byte != this.whiteByte) |
|||
{ |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
return this.width; |
|||
} |
|||
|
|||
private int FindB1ForNormalLine(int a0, byte a0Byte) |
|||
{ |
|||
int offset = 0; |
|||
if (a0 < 0) |
|||
{ |
|||
if (a0Byte != this.scanLine[0]) |
|||
{ |
|||
return 0; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
offset = a0; |
|||
} |
|||
|
|||
ReadOnlySpan<byte> searchSpace = this.scanLine.Slice(offset); |
|||
byte searchByte = (byte)~a0Byte; |
|||
int index = searchSpace.IndexOf(searchByte); |
|||
if (index < 0) |
|||
{ |
|||
return this.scanLine.Length; |
|||
} |
|||
|
|||
if (index != 0) |
|||
{ |
|||
return offset + index; |
|||
} |
|||
|
|||
searchByte = (byte)~searchSpace[0]; |
|||
index = searchSpace.IndexOf(searchByte); |
|||
if (index < 0) |
|||
{ |
|||
return this.scanLine.Length; |
|||
} |
|||
|
|||
searchSpace = searchSpace.Slice(index); |
|||
offset += index; |
|||
index = searchSpace.IndexOf((byte)~searchByte); |
|||
if (index < 0) |
|||
{ |
|||
return this.scanLine.Length; |
|||
} |
|||
|
|||
return index + offset; |
|||
} |
|||
|
|||
private int FindB2ForImaginaryWhiteLine() => this.width; |
|||
|
|||
private int FindB2ForNormalLine(int b1) |
|||
{ |
|||
if (b1 >= this.scanLine.Length) |
|||
{ |
|||
return this.scanLine.Length; |
|||
} |
|||
|
|||
byte searchByte = (byte)~this.scanLine[b1]; |
|||
int offset = b1 + 1; |
|||
ReadOnlySpan<byte> searchSpace = this.scanLine.Slice(offset); |
|||
int index = searchSpace.IndexOf(searchByte); |
|||
if (index == -1) |
|||
{ |
|||
return this.scanLine.Length; |
|||
} |
|||
|
|||
return offset + index; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System.Diagnostics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors |
|||
{ |
|||
[DebuggerDisplay("Type = {Type}")] |
|||
internal readonly struct CcittTwoDimensionalCode |
|||
{ |
|||
private readonly ushort value; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CcittTwoDimensionalCode"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="type">The type.</param>
|
|||
/// <param name="bitsRequired">The bits required.</param>
|
|||
/// <param name="extensionBits">The extension bits.</param>
|
|||
public CcittTwoDimensionalCode(CcittTwoDimensionalCodeType type, int bitsRequired, int extensionBits = 0) |
|||
=> this.value = (ushort)((byte)type | ((bitsRequired & 0b1111) << 8) | ((extensionBits & 0b111) << 11)); |
|||
|
|||
/// <summary>
|
|||
/// Gets the code type.
|
|||
/// </summary>
|
|||
public CcittTwoDimensionalCodeType Type => (CcittTwoDimensionalCodeType)(this.value & 0b11111111); |
|||
} |
|||
} |
|||
@ -0,0 +1,73 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors |
|||
{ |
|||
/// <summary>
|
|||
/// Enum for the different two dimensional code words for the ccitt fax compression.
|
|||
/// </summary>
|
|||
internal enum CcittTwoDimensionalCodeType |
|||
{ |
|||
/// <summary>
|
|||
/// No valid code word was read.
|
|||
/// </summary>
|
|||
None = 0, |
|||
|
|||
/// <summary>
|
|||
/// Pass mode: This mode is identified when the position of b2 lies to the left of a1.
|
|||
/// </summary>
|
|||
Pass = 1, |
|||
|
|||
/// <summary>
|
|||
/// Indicates horizontal mode.
|
|||
/// </summary>
|
|||
Horizontal = 2, |
|||
|
|||
/// <summary>
|
|||
/// Vertical 0 code word: relative distance between a1 and b1 is 0.
|
|||
/// </summary>
|
|||
Vertical0 = 3, |
|||
|
|||
/// <summary>
|
|||
/// Vertical r1 code word: relative distance between a1 and b1 is 1, a1 is to the right of b1.
|
|||
/// </summary>
|
|||
VerticalR1 = 4, |
|||
|
|||
/// <summary>
|
|||
/// Vertical r2 code word: relative distance between a1 and b1 is 2, a1 is to the right of b1.
|
|||
/// </summary>
|
|||
VerticalR2 = 5, |
|||
|
|||
/// <summary>
|
|||
/// Vertical r3 code word: relative distance between a1 and b1 is 3, a1 is to the right of b1.
|
|||
/// </summary>
|
|||
VerticalR3 = 6, |
|||
|
|||
/// <summary>
|
|||
/// Vertical l1 code word: relative distance between a1 and b1 is 1, a1 is to the left of b1.
|
|||
/// </summary>
|
|||
VerticalL1 = 7, |
|||
|
|||
/// <summary>
|
|||
/// Vertical l2 code word: relative distance between a1 and b1 is 2, a1 is to the left of b1.
|
|||
/// </summary>
|
|||
VerticalL2 = 8, |
|||
|
|||
/// <summary>
|
|||
/// Vertical l3 code word: relative distance between a1 and b1 is 3, a1 is to the left of b1.
|
|||
/// </summary>
|
|||
VerticalL3 = 9, |
|||
|
|||
/// <summary>
|
|||
/// 1d extensions code word, extension code is used to indicate the change from the current mode to another mode, e.g., another coding scheme.
|
|||
/// Not supported.
|
|||
/// </summary>
|
|||
Extensions1D = 10, |
|||
|
|||
/// <summary>
|
|||
/// 2d extensions code word, extension code is used to indicate the change from the current mode to another mode, e.g., another coding scheme.
|
|||
/// Not supported.
|
|||
/// </summary>
|
|||
Extensions2D = 11, |
|||
} |
|||
} |
|||
@ -0,0 +1,95 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Runtime.InteropServices; |
|||
using System.Threading; |
|||
using SixLabors.ImageSharp.Formats.Jpeg; |
|||
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; |
|||
using SixLabors.ImageSharp.Formats.Tiff.Constants; |
|||
using SixLabors.ImageSharp.IO; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.ImageSharp.Metadata; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors |
|||
{ |
|||
/// <summary>
|
|||
/// Class to handle cases where TIFF image data is compressed as a jpeg stream.
|
|||
/// </summary>
|
|||
internal sealed class JpegTiffCompression : TiffBaseDecompressor |
|||
{ |
|||
private readonly Configuration configuration; |
|||
|
|||
private readonly byte[] jpegTables; |
|||
|
|||
private readonly TiffPhotometricInterpretation photometricInterpretation; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="JpegTiffCompression"/> class.
|
|||
/// </summary>
|
|||
/// <param name="configuration">The configuration.</param>
|
|||
/// <param name="memoryAllocator">The memoryAllocator to use for buffer allocations.</param>
|
|||
/// <param name="width">The image width.</param>
|
|||
/// <param name="bitsPerPixel">The bits per pixel.</param>
|
|||
/// <param name="jpegTables">The JPEG tables containing the quantization and/or Huffman tables.</param>
|
|||
/// <param name="photometricInterpretation">The photometric interpretation.</param>
|
|||
public JpegTiffCompression( |
|||
Configuration configuration, |
|||
MemoryAllocator memoryAllocator, |
|||
int width, |
|||
int bitsPerPixel, |
|||
byte[] jpegTables, |
|||
TiffPhotometricInterpretation photometricInterpretation) |
|||
: base(memoryAllocator, width, bitsPerPixel) |
|||
{ |
|||
this.configuration = configuration; |
|||
this.jpegTables = jpegTables; |
|||
this.photometricInterpretation = photometricInterpretation; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span<byte> buffer) |
|||
{ |
|||
if (this.jpegTables != null) |
|||
{ |
|||
using var jpegDecoder = new JpegDecoderCore(this.configuration, new JpegDecoder()); |
|||
|
|||
// TODO: Should we pass through the CancellationToken from the tiff decoder?
|
|||
// If the PhotometricInterpretation is YCbCr we explicitly assume the JPEG data is in RGB color space.
|
|||
// There seems no other way to determine that the JPEG data is RGB colorspace (no APP14 marker, componentId's are not RGB).
|
|||
using SpectralConverter<Rgb24> spectralConverter = this.photometricInterpretation == TiffPhotometricInterpretation.YCbCr ? |
|||
new RgbJpegSpectralConverter<Rgb24>(this.configuration, CancellationToken.None) : new SpectralConverter<Rgb24>(this.configuration, CancellationToken.None); |
|||
var scanDecoder = new HuffmanScanDecoder(stream, spectralConverter, CancellationToken.None); |
|||
jpegDecoder.LoadTables(this.jpegTables, scanDecoder); |
|||
scanDecoder.ResetInterval = 0; |
|||
jpegDecoder.ParseStream(stream, scanDecoder, CancellationToken.None); |
|||
|
|||
using var image = new Image<Rgb24>(this.configuration, spectralConverter.PixelBuffer, new ImageMetadata()); |
|||
CopyImageBytesToBuffer(buffer, image); |
|||
} |
|||
else |
|||
{ |
|||
using var image = Image.Load<Rgb24>(stream); |
|||
CopyImageBytesToBuffer(buffer, image); |
|||
} |
|||
} |
|||
|
|||
private static void CopyImageBytesToBuffer(Span<byte> buffer, Image<Rgb24> image) |
|||
{ |
|||
int offset = 0; |
|||
for (int y = 0; y < image.Height; y++) |
|||
{ |
|||
Span<Rgb24> pixelRowSpan = image.GetPixelRowSpan(y); |
|||
Span<byte> rgbBytes = MemoryMarshal.AsBytes(pixelRowSpan); |
|||
rgbBytes.CopyTo(buffer.Slice(offset)); |
|||
offset += rgbBytes.Length; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void Dispose(bool disposing) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,73 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System.IO; |
|||
using SixLabors.ImageSharp.Formats.Tiff.Constants; |
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors |
|||
{ |
|||
/// <summary>
|
|||
/// Bit reader for data encoded with the modified huffman rle method.
|
|||
/// See TIFF 6.0 specification, section 10.
|
|||
/// </summary>
|
|||
internal sealed class ModifiedHuffmanBitReader : T4BitReader |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ModifiedHuffmanBitReader"/> class.
|
|||
/// </summary>
|
|||
/// <param name="input">The compressed input stream.</param>
|
|||
/// <param name="fillOrder">The logical order of bits within a byte.</param>
|
|||
/// <param name="bytesToRead">The number of bytes to read from the stream.</param>
|
|||
/// <param name="allocator">The memory allocator.</param>
|
|||
public ModifiedHuffmanBitReader(Stream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator) |
|||
: base(input, fillOrder, bytesToRead, allocator) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool HasMoreData => this.Position < (ulong)this.DataLength - 1 || ((uint)(this.BitsRead - 1) < (7 - 1)); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool IsEndOfScanLine |
|||
{ |
|||
get |
|||
{ |
|||
if (this.IsWhiteRun && this.CurValueBitsRead == 12 && this.Value == 1) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
if (this.CurValueBitsRead == 11 && this.Value == 0) |
|||
{ |
|||
// black run.
|
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void StartNewRow() |
|||
{ |
|||
base.StartNewRow(); |
|||
|
|||
int remainder = this.BitsRead & 7; // bit-hack for % 8
|
|||
if (remainder != 0) |
|||
{ |
|||
// Skip padding bits, move to next byte.
|
|||
this.Position++; |
|||
this.ResetBitsRead(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// No EOL is expected at the start of a run for the modified huffman encoding.
|
|||
/// </summary>
|
|||
protected override void ReadEolBeforeFirstData() |
|||
{ |
|||
// Nothing to do here.
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System.Threading; |
|||
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; |
|||
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors |
|||
{ |
|||
/// <summary>
|
|||
/// Spectral converter for YCbCr TIFF's which use the JPEG compression.
|
|||
/// The jpeg data should be always treated as RGB color space.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
|
|||
internal sealed class RgbJpegSpectralConverter<TPixel> : SpectralConverter<TPixel> |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="RgbJpegSpectralConverter{TPixel}"/> class.
|
|||
/// This Spectral converter will always convert the pixel data to RGB color.
|
|||
/// </summary>
|
|||
/// <param name="configuration">The configuration.</param>
|
|||
/// <param name="cancellationToken">The cancellation token.</param>
|
|||
public RgbJpegSpectralConverter(Configuration configuration, CancellationToken cancellationToken) |
|||
: base(configuration, cancellationToken) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override JpegColorConverter GetColorConverter(JpegFrame frame, IRawJpegData jpegData) => JpegColorConverter.GetConverter(JpegColorSpace.RGB, frame.Precision); |
|||
} |
|||
} |
|||
@ -0,0 +1,160 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
|
|||
using SixLabors.ImageSharp.Formats.Tiff.Constants; |
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors |
|||
{ |
|||
/// <summary>
|
|||
/// Bit reader for reading CCITT T6 compressed fax data.
|
|||
/// See: Facsimile Coding Schemes and Coding Control Functions for Group 4 Facsimile Apparatus, itu-t recommendation t.6
|
|||
/// </summary>
|
|||
internal sealed class T6BitReader : T4BitReader |
|||
{ |
|||
private readonly int maxCodeLength = 12; |
|||
|
|||
private static readonly CcittTwoDimensionalCode None = new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.None, 0); |
|||
|
|||
private static readonly Dictionary<uint, CcittTwoDimensionalCode> Len1Codes = new Dictionary<uint, CcittTwoDimensionalCode>() |
|||
{ |
|||
{ 0b1, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Vertical0, 1) } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, CcittTwoDimensionalCode> Len3Codes = new Dictionary<uint, CcittTwoDimensionalCode>() |
|||
{ |
|||
{ 0b001, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Horizontal, 3) }, |
|||
{ 0b010, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalL1, 3) }, |
|||
{ 0b011, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalR1, 3) } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, CcittTwoDimensionalCode> Len4Codes = new Dictionary<uint, CcittTwoDimensionalCode>() |
|||
{ |
|||
{ 0b0001, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Pass, 4) } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, CcittTwoDimensionalCode> Len6Codes = new Dictionary<uint, CcittTwoDimensionalCode>() |
|||
{ |
|||
{ 0b000011, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalR2, 6) }, |
|||
{ 0b000010, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalL2, 6) } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, CcittTwoDimensionalCode> Len7Codes = new Dictionary<uint, CcittTwoDimensionalCode>() |
|||
{ |
|||
{ 0b0000011, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalR3, 7) }, |
|||
{ 0b0000010, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalL3, 7) }, |
|||
{ 0b0000001, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Extensions2D, 7) }, |
|||
{ 0b0000000, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Extensions1D, 7) } |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="T6BitReader"/> class.
|
|||
/// </summary>
|
|||
/// <param name="input">The compressed input stream.</param>
|
|||
/// <param name="fillOrder">The logical order of bits within a byte.</param>
|
|||
/// <param name="bytesToRead">The number of bytes to read from the stream.</param>
|
|||
/// <param name="allocator">The memory allocator.</param>
|
|||
public T6BitReader(Stream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator) |
|||
: base(input, fillOrder, bytesToRead, allocator) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool HasMoreData => this.Position < (ulong)this.DataLength - 1 || ((uint)(this.BitsRead - 1) < (7 - 1)); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the two dimensional code.
|
|||
/// </summary>
|
|||
public CcittTwoDimensionalCode Code { get; internal set; } |
|||
|
|||
public bool ReadNextCodeWord() |
|||
{ |
|||
this.Code = None; |
|||
this.Reset(); |
|||
uint value = this.ReadValue(1); |
|||
|
|||
do |
|||
{ |
|||
if (this.CurValueBitsRead > this.maxCodeLength) |
|||
{ |
|||
TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error: invalid code length read"); |
|||
} |
|||
|
|||
switch (this.CurValueBitsRead) |
|||
{ |
|||
case 1: |
|||
if (Len1Codes.ContainsKey(value)) |
|||
{ |
|||
this.Code = Len1Codes[value]; |
|||
return false; |
|||
} |
|||
|
|||
break; |
|||
|
|||
case 3: |
|||
if (Len3Codes.ContainsKey(value)) |
|||
{ |
|||
this.Code = Len3Codes[value]; |
|||
return false; |
|||
} |
|||
|
|||
break; |
|||
|
|||
case 4: |
|||
if (Len4Codes.ContainsKey(value)) |
|||
{ |
|||
this.Code = Len4Codes[value]; |
|||
return false; |
|||
} |
|||
|
|||
break; |
|||
|
|||
case 6: |
|||
if (Len6Codes.ContainsKey(value)) |
|||
{ |
|||
this.Code = Len6Codes[value]; |
|||
return false; |
|||
} |
|||
|
|||
break; |
|||
|
|||
case 7: |
|||
if (Len7Codes.ContainsKey(value)) |
|||
{ |
|||
this.Code = Len7Codes[value]; |
|||
return false; |
|||
} |
|||
|
|||
break; |
|||
} |
|||
|
|||
uint currBit = this.ReadValue(1); |
|||
value = (value << 1) | currBit; |
|||
} |
|||
while (!this.IsEndOfScanLine); |
|||
|
|||
if (this.IsEndOfScanLine) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// No EOL is expected at the start of a run.
|
|||
/// </summary>
|
|||
protected override void ReadEolBeforeFirstData() |
|||
{ |
|||
// Nothing to do here.
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Swaps the white run to black run an vise versa.
|
|||
/// </summary>
|
|||
public void SwapColor() => this.IsWhiteRun = !this.IsWhiteRun; |
|||
} |
|||
} |
|||
@ -0,0 +1,254 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using SixLabors.ImageSharp.Formats.Tiff.Constants; |
|||
using SixLabors.ImageSharp.IO; |
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors |
|||
{ |
|||
/// <summary>
|
|||
/// Class to handle cases where TIFF image data is compressed using CCITT T6 compression.
|
|||
/// </summary>
|
|||
internal sealed class T6TiffCompression : TiffBaseDecompressor |
|||
{ |
|||
private readonly bool isWhiteZero; |
|||
|
|||
private readonly byte whiteValue; |
|||
|
|||
private readonly byte blackValue; |
|||
|
|||
private readonly int width; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="T6TiffCompression" /> class.
|
|||
/// </summary>
|
|||
/// <param name="allocator">The memory allocator.</param>
|
|||
/// <param name="fillOrder">The logical order of bits within a byte.</param>
|
|||
/// <param name="width">The image width.</param>
|
|||
/// <param name="bitsPerPixel">The number of bits per pixel.</param>
|
|||
/// <param name="photometricInterpretation">The photometric interpretation.</param>
|
|||
public T6TiffCompression( |
|||
MemoryAllocator allocator, |
|||
TiffFillOrder fillOrder, |
|||
int width, |
|||
int bitsPerPixel, |
|||
TiffPhotometricInterpretation photometricInterpretation) |
|||
: base(allocator, width, bitsPerPixel) |
|||
{ |
|||
this.FillOrder = fillOrder; |
|||
this.width = width; |
|||
this.isWhiteZero = photometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero; |
|||
this.whiteValue = (byte)(this.isWhiteZero ? 0 : 1); |
|||
this.blackValue = (byte)(this.isWhiteZero ? 1 : 0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the logical order of bits within a byte.
|
|||
/// </summary>
|
|||
private TiffFillOrder FillOrder { get; } |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span<byte> buffer) |
|||
{ |
|||
int height = stripHeight; |
|||
|
|||
using System.Buffers.IMemoryOwner<byte> scanLineBuffer = this.Allocator.Allocate<byte>(this.width * 2); |
|||
Span<byte> scanLine = scanLineBuffer.GetSpan().Slice(0, this.width); |
|||
Span<byte> referenceScanLineSpan = scanLineBuffer.GetSpan().Slice(this.width, this.width); |
|||
|
|||
using var bitReader = new T6BitReader(stream, this.FillOrder, byteCount, this.Allocator); |
|||
|
|||
var referenceScanLine = new CcittReferenceScanline(this.isWhiteZero, this.width); |
|||
uint bitsWritten = 0; |
|||
for (int y = 0; y < height; y++) |
|||
{ |
|||
scanLine.Fill(0); |
|||
Decode2DScanline(bitReader, this.isWhiteZero, referenceScanLine, scanLine); |
|||
|
|||
bitsWritten = this.WriteScanLine(buffer, scanLine, bitsWritten); |
|||
|
|||
scanLine.CopyTo(referenceScanLineSpan); |
|||
referenceScanLine = new CcittReferenceScanline(this.isWhiteZero, referenceScanLineSpan); |
|||
} |
|||
} |
|||
|
|||
private uint WriteScanLine(Span<byte> buffer, Span<byte> scanLine, uint bitsWritten) |
|||
{ |
|||
byte white = (byte)(this.isWhiteZero ? 0 : 255); |
|||
for (int i = 0; i < scanLine.Length; i++) |
|||
{ |
|||
BitWriterUtils.WriteBits(buffer, (int)bitsWritten, 1, scanLine[i] == white ? this.whiteValue : this.blackValue); |
|||
bitsWritten++; |
|||
} |
|||
|
|||
// Write padding bytes, if necessary.
|
|||
uint remainder = bitsWritten % 8; |
|||
if (remainder != 0) |
|||
{ |
|||
uint padding = 8 - remainder; |
|||
BitWriterUtils.WriteBits(buffer, (int)bitsWritten, padding, 0); |
|||
bitsWritten += padding; |
|||
} |
|||
|
|||
return bitsWritten; |
|||
} |
|||
|
|||
private static void Decode2DScanline(T6BitReader bitReader, bool whiteIsZero, CcittReferenceScanline referenceScanline, Span<byte> scanline) |
|||
{ |
|||
int width = scanline.Length; |
|||
bitReader.StartNewRow(); |
|||
|
|||
// 2D Encoding variables.
|
|||
int a0 = -1; |
|||
byte fillByte = whiteIsZero ? (byte)0 : (byte)255; |
|||
|
|||
// Process every code word in this scanline.
|
|||
int unpacked = 0; |
|||
while (true) |
|||
{ |
|||
// Read next code word and advance pass it.
|
|||
bool isEol = bitReader.ReadNextCodeWord(); |
|||
|
|||
// Special case handling for EOL.
|
|||
if (isEol) |
|||
{ |
|||
// If a TIFF reader encounters EOFB before the expected number of lines has been extracted,
|
|||
// it is appropriate to assume that the missing rows consist entirely of white pixels.
|
|||
scanline.Fill(whiteIsZero ? (byte)0 : (byte)255); |
|||
break; |
|||
} |
|||
|
|||
// Update 2D Encoding variables.
|
|||
int b1 = referenceScanline.FindB1(a0, fillByte); |
|||
|
|||
// Switch on the code word.
|
|||
int a1; |
|||
switch (bitReader.Code.Type) |
|||
{ |
|||
case CcittTwoDimensionalCodeType.None: |
|||
TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error, could not read a valid code word."); |
|||
break; |
|||
|
|||
case CcittTwoDimensionalCodeType.Pass: |
|||
int b2 = referenceScanline.FindB2(b1); |
|||
scanline.Slice(unpacked, b2 - unpacked).Fill(fillByte); |
|||
unpacked = b2; |
|||
a0 = b2; |
|||
break; |
|||
case CcittTwoDimensionalCodeType.Horizontal: |
|||
// Decode M(a0a1)
|
|||
bitReader.ReadNextRun(); |
|||
int runLength = (int)bitReader.RunLength; |
|||
if (runLength > (uint)(scanline.Length - unpacked)) |
|||
{ |
|||
TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error"); |
|||
} |
|||
|
|||
scanline.Slice(unpacked, runLength).Fill(fillByte); |
|||
unpacked += runLength; |
|||
fillByte = (byte)~fillByte; |
|||
|
|||
// Decode M(a1a2)
|
|||
bitReader.ReadNextRun(); |
|||
runLength = (int)bitReader.RunLength; |
|||
if (runLength > (uint)(scanline.Length - unpacked)) |
|||
{ |
|||
TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error"); |
|||
} |
|||
|
|||
scanline.Slice(unpacked, runLength).Fill(fillByte); |
|||
unpacked += runLength; |
|||
fillByte = (byte)~fillByte; |
|||
|
|||
// Prepare next a0
|
|||
a0 = unpacked; |
|||
break; |
|||
|
|||
case CcittTwoDimensionalCodeType.Vertical0: |
|||
a1 = b1; |
|||
scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte); |
|||
unpacked = a1; |
|||
a0 = a1; |
|||
fillByte = (byte)~fillByte; |
|||
bitReader.SwapColor(); |
|||
break; |
|||
|
|||
case CcittTwoDimensionalCodeType.VerticalR1: |
|||
a1 = b1 + 1; |
|||
scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte); |
|||
unpacked = a1; |
|||
a0 = a1; |
|||
fillByte = (byte)~fillByte; |
|||
bitReader.SwapColor(); |
|||
break; |
|||
|
|||
case CcittTwoDimensionalCodeType.VerticalR2: |
|||
a1 = b1 + 2; |
|||
scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte); |
|||
unpacked = a1; |
|||
a0 = a1; |
|||
fillByte = (byte)~fillByte; |
|||
bitReader.SwapColor(); |
|||
break; |
|||
|
|||
case CcittTwoDimensionalCodeType.VerticalR3: |
|||
a1 = b1 + 3; |
|||
scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte); |
|||
unpacked = a1; |
|||
a0 = a1; |
|||
fillByte = (byte)~fillByte; |
|||
bitReader.SwapColor(); |
|||
break; |
|||
|
|||
case CcittTwoDimensionalCodeType.VerticalL1: |
|||
a1 = b1 - 1; |
|||
scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte); |
|||
unpacked = a1; |
|||
a0 = a1; |
|||
fillByte = (byte)~fillByte; |
|||
bitReader.SwapColor(); |
|||
break; |
|||
|
|||
case CcittTwoDimensionalCodeType.VerticalL2: |
|||
a1 = b1 - 2; |
|||
scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte); |
|||
unpacked = a1; |
|||
a0 = a1; |
|||
fillByte = (byte)~fillByte; |
|||
bitReader.SwapColor(); |
|||
break; |
|||
|
|||
case CcittTwoDimensionalCodeType.VerticalL3: |
|||
a1 = b1 - 3; |
|||
scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte); |
|||
unpacked = a1; |
|||
a0 = a1; |
|||
fillByte = (byte)~fillByte; |
|||
bitReader.SwapColor(); |
|||
break; |
|||
|
|||
default: |
|||
throw new NotSupportedException("ccitt extensions are not supported."); |
|||
} |
|||
|
|||
// This line is fully unpacked. Should exit and process next line.
|
|||
if (unpacked == width) |
|||
{ |
|||
break; |
|||
} |
|||
|
|||
if (unpacked > width) |
|||
{ |
|||
TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error, unpacked data > width"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void Dispose(bool disposing) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
Binary file not shown.
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:fec7012d9ae52a12c4617fd7526e506feee812fc67e923a76b2ca88c95f7a538 |
|||
size 3111 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:f4630c33d722a89de5cb1834bffa43c729f204c0f8c95d4ec2127ddfcd433f60 |
|||
size 10100 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:318338c1a541227632a99cc47b04fc9f6d19c3e8300a76e71136edec2d17a5f5 |
|||
size 9073 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:70315936e36bb1edf38fc950b7b321f20be5a2592db59bfd28d79dbc391a5aaf |
|||
size 4465 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:be21207ecb96bcb0925706649678c522c68bf08ee26e6e8878456f4f7772dd31 |
|||
size 3951 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:8937e245885e1f280e1843ad48a4349ec1a3f71f86c954229cd44160aeeaaac4 |
|||
size 209584 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:3af237c172248d39c7b82c879de3d162e4dafaf36dc298add210740843edd33f |
|||
size 3129 |
|||
@ -1,3 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:ed3b08730e5c34eb8d268f58d1e09efe2605398899bfd726cc3b35de21baa6ff |
|||
size 121196 |
|||
oid sha256:c35902ca485fba441230efa88f794ee5aafa9f75ad5e4a14cb3d592a0a98c538 |
|||
size 7760 |
|||
|
|||
@ -1,3 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:bba35f1e43c8425f3bcfab682efae4d2c00c62f0d8a4b411e646d32047469526 |
|||
size 125802 |
|||
oid sha256:da7d98823c284d92982a88c4a51434bbc140dceac245a8a054c6e41a489d0cc7 |
|||
size 5986 |
|||
|
|||
@ -1,3 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:7a2c95aec08b96bca30af344f7d9952a603a951802ce534a5f2c5f563795cbd2 |
|||
size 117704 |
|||
oid sha256:118e55fe0f0dbb5d2712337ec9456a27407f11c0eb9f7e7e81700d4d84b7db09 |
|||
size 4378 |
|||
|
|||
@ -1,3 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:2314b31ca9938fa8b11cbabda0b118a90025a45d2931fca9afa131c0d6919aca |
|||
size 557717 |
|||
oid sha256:d4d3541db6b7751d3225599aa822c3376fb27bb9dc2dd28674ef4f78bf426191 |
|||
size 83356 |
|||
|
|||
@ -1,3 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:b9576b3a49b84e26938a7e9ded5f43a1a3c3390bf4824803f5aaab8e00c1afb4 |
|||
size 630947 |
|||
oid sha256:d007429701cc20154e84909af6988414001e6e60fba5c995f67b2a04cad6e57b |
|||
size 41135 |
|||
|
|||
@ -1,3 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:3f24fd8f36a4847fcb84a317de4fd2eacd5eb0c58ef4436d33919f0a6658d0d9 |
|||
size 698309 |
|||
oid sha256:649f0b8ad50a9465fdb447c411e33f20a8b367600e7c3af83c8f686f3ab3e6dc |
|||
size 47143 |
|||
|
|||
@ -1,3 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:0283f2be39a151ca3ed19be97ebe4a6b17978ed251dd4d0d568895865fec24c7 |
|||
size 964588 |
|||
oid sha256:5925388b374b75161ef49273e8c85c4f99713e5e3be380ea13a03895f47809ba |
|||
size 60001 |
|||
|
|||
@ -1,3 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:1a4f687de9925863b1c9f32f53b6c05fb121f9d7b02ff5869113c4745433f10d |
|||
size 124644 |
|||
oid sha256:f27e758bb72d5e03fdcf0b1c58c417e4a7928cbdf8d432dc5b9a7d8d7ee4d06b |
|||
size 5668 |
|||
|
|||
@ -1,3 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:b70500348b1af7828c15e7782eaca105ff749136d7c45eb4cab8c5cd5269c3f6 |
|||
size 966134 |
|||
oid sha256:29d4b30265158a7cc651d75130843a7f5a7ebf8b2f0f4bb0cf86c82cbec7f6ec |
|||
size 61549 |
|||
|
|||
@ -1,3 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:da6e6a35a0bb0f5f2d49e3c5f0eb2deb7118718dd08844f66a6cb72f48b5c489 |
|||
size 1476294 |
|||
oid sha256:392e1269220e7e3feb9e2b256e82cce6a2394a5cabef042fdb10def6b24ff165 |
|||
size 111819 |
|||
|
|||
@ -1,3 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:ba79ffac35e16208406e073492d770d3a77e51a47112aa02ab8fd98b5a8487b2 |
|||
size 198564 |
|||
oid sha256:4baf0f4c462e5bef71ab36f505dfff87a31bd1d25deabccd822a359c1075e08e |
|||
size 65748 |
|||
|
|||
@ -1,3 +0,0 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:53006876fcdc655a794462de57eb6b56f4d0cdd3cb8b752c63328db0eb4aa3c1 |
|||
size 725085 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:d83d8a81ebb7337f00b319a8c37cfdef07423d6a61006411130e386238dd00dd |
|||
size 121907 |
|||
@ -1,3 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:ecb529e5e3e0eca6f5e407b034fa8ba67bb4b9068af9e9b30425b08d30a249c0 |
|||
size 1756355 |
|||
oid sha256:993207c34358165af5fac0d0b955b56cd79e9707c1c46344863e01cbc9c7707d |
|||
size 126695 |
|||
|
|||
@ -1,3 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:59dbb48f10c40cbbd4f5617a9f57536790ce0b9a4cc241dc8d6257095598cb76 |
|||
size 2891292 |
|||
oid sha256:81e7456578510c85e5bebe8bc7c5796da6e2cd61f5bbe0a1f6bb46b8aee3d695 |
|||
size 179949 |
|||
|
|||
@ -1,3 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:acb46c990af78fcb0e63849f0f26acffe26833d5cfd2fc77a728baf92166eea3 |
|||
size 2893218 |
|||
oid sha256:bb25349a7f803aeafc47d8a05deca1a8afdc4bdc5a53e2916f68d5c3e7d8cad3 |
|||
size 179207 |
|||
|
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:185ae3c4174b323adcf811d125cd77b71768406845923f50395c0baebff57b7c |
|||
size 282 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:a19eb117f194718575681a81a4fbe7fe4a1b82b99113707295194090fb935784 |
|||
size 282 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:af0d8f3c18f96228aa369bc295201a1bfe1b044c23991ff168401adc5402ebb6 |
|||
size 308 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:6b81013d7b0a29ed1ac9c33e175e0c0e69494b93b2b65b692f16d9ea042b9d5d |
|||
size 7759 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:1f027d8c2ab0b244f04e51b9bf724eac0123e104a2446324496a08bdf5881922 |
|||
size 10550 |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue