mirror of https://github.com/SixLabors/ImageSharp
committed by
GitHub
20 changed files with 337 additions and 107 deletions
@ -0,0 +1,33 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.CompilerServices; |
||||
|
using SixLabors.ImageSharp.Memory; |
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors; |
||||
|
|
||||
|
internal static class JpegCompressionUtils |
||||
|
{ |
||||
|
public static void CopyImageBytesToBuffer(Configuration configuration, Span<byte> buffer, Buffer2D<Rgb24> pixelBuffer) |
||||
|
{ |
||||
|
int offset = 0; |
||||
|
for (int y = 0; y < pixelBuffer.Height; y++) |
||||
|
{ |
||||
|
Span<Rgb24> pixelRowSpan = pixelBuffer.DangerousGetRowSpan(y); |
||||
|
PixelOperations<Rgb24>.Instance.ToRgb24Bytes(configuration, pixelRowSpan, buffer[offset..], pixelRowSpan.Length); |
||||
|
offset += Unsafe.SizeOf<Rgb24>() * pixelRowSpan.Length; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static void CopyImageBytesToBuffer(Configuration configuration, Span<byte> buffer, Buffer2D<L8> pixelBuffer) |
||||
|
{ |
||||
|
int offset = 0; |
||||
|
for (int y = 0; y < pixelBuffer.Height; y++) |
||||
|
{ |
||||
|
Span<L8> pixelRowSpan = pixelBuffer.DangerousGetRowSpan(y); |
||||
|
PixelOperations<L8>.Instance.ToL8Bytes(configuration, pixelRowSpan, buffer[offset..], pixelRowSpan.Length); |
||||
|
offset += Unsafe.SizeOf<L8>() * pixelRowSpan.Length; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,87 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
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.PixelFormats; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors; |
||||
|
|
||||
|
internal sealed class OldJpegTiffCompression : TiffBaseDecompressor |
||||
|
{ |
||||
|
private readonly JpegDecoderOptions options; |
||||
|
|
||||
|
private readonly uint startOfImageMarker; |
||||
|
|
||||
|
private readonly TiffPhotometricInterpretation photometricInterpretation; |
||||
|
|
||||
|
public OldJpegTiffCompression( |
||||
|
JpegDecoderOptions options, |
||||
|
MemoryAllocator memoryAllocator, |
||||
|
int width, |
||||
|
int bitsPerPixel, |
||||
|
uint startOfImageMarker, |
||||
|
TiffPhotometricInterpretation photometricInterpretation) |
||||
|
: base(memoryAllocator, width, bitsPerPixel) |
||||
|
{ |
||||
|
this.options = options; |
||||
|
this.startOfImageMarker = startOfImageMarker; |
||||
|
this.photometricInterpretation = photometricInterpretation; |
||||
|
} |
||||
|
|
||||
|
protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span<byte> buffer, CancellationToken cancellationToken) |
||||
|
{ |
||||
|
long stripOffset = stream.Position; |
||||
|
stream.Position = this.startOfImageMarker; |
||||
|
|
||||
|
this.DecodeJpegData(stream, buffer, cancellationToken); |
||||
|
|
||||
|
// Setting the stream position to the expected position.
|
||||
|
// This is a workaround for some images having set the stripBytesCount not equal to the compressed jpeg data.
|
||||
|
stream.Position = stripOffset + byteCount; |
||||
|
} |
||||
|
|
||||
|
private void DecodeJpegData(BufferedReadStream stream, Span<byte> buffer, CancellationToken cancellationToken) |
||||
|
{ |
||||
|
using JpegDecoderCore jpegDecoder = new(this.options); |
||||
|
Configuration configuration = this.options.GeneralOptions.Configuration; |
||||
|
switch (this.photometricInterpretation) |
||||
|
{ |
||||
|
case TiffPhotometricInterpretation.BlackIsZero: |
||||
|
case TiffPhotometricInterpretation.WhiteIsZero: |
||||
|
{ |
||||
|
using SpectralConverter<L8> spectralConverterGray = new GrayJpegSpectralConverter<L8>(configuration); |
||||
|
|
||||
|
jpegDecoder.ParseStream(stream, spectralConverterGray, cancellationToken); |
||||
|
|
||||
|
using Buffer2D<L8> decompressedBuffer = spectralConverterGray.GetPixelBuffer(cancellationToken); |
||||
|
JpegCompressionUtils.CopyImageBytesToBuffer(spectralConverterGray.Configuration, buffer, decompressedBuffer); |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
case TiffPhotometricInterpretation.YCbCr: |
||||
|
case TiffPhotometricInterpretation.Rgb: |
||||
|
{ |
||||
|
using SpectralConverter<Rgb24> spectralConverter = new TiffOldJpegSpectralConverter<Rgb24>(configuration, this.photometricInterpretation); |
||||
|
|
||||
|
jpegDecoder.ParseStream(stream, spectralConverter, cancellationToken); |
||||
|
|
||||
|
using Buffer2D<Rgb24> decompressedBuffer = spectralConverter.GetPixelBuffer(cancellationToken); |
||||
|
JpegCompressionUtils.CopyImageBytesToBuffer(spectralConverter.Configuration, buffer, decompressedBuffer); |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
default: |
||||
|
TiffThrowHelper.ThrowNotSupported($"Jpeg compressed tiff with photometric interpretation {this.photometricInterpretation} is not supported"); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void Dispose(bool disposing) |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,45 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using SixLabors.ImageSharp.Formats.Jpeg.Components; |
||||
|
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; |
||||
|
using SixLabors.ImageSharp.Formats.Tiff.Constants; |
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Spectral converter for YCbCr TIFF's which use the OldJPEG compression.
|
||||
|
/// The jpeg data should be always treated as YCbCr color space.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
|
||||
|
internal sealed class TiffOldJpegSpectralConverter<TPixel> : SpectralConverter<TPixel> |
||||
|
where TPixel : unmanaged, IPixel<TPixel> |
||||
|
{ |
||||
|
private readonly TiffPhotometricInterpretation photometricInterpretation; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="TiffOldJpegSpectralConverter{TPixel}"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="configuration">The configuration.</param>
|
||||
|
/// <param name="photometricInterpretation">Tiff photometric interpretation.</param>
|
||||
|
public TiffOldJpegSpectralConverter(Configuration configuration, TiffPhotometricInterpretation photometricInterpretation) |
||||
|
: base(configuration) |
||||
|
=> this.photometricInterpretation = photometricInterpretation; |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override JpegColorConverterBase GetColorConverter(JpegFrame frame, IRawJpegData jpegData) |
||||
|
{ |
||||
|
JpegColorSpace colorSpace = GetJpegColorSpaceFromPhotometricInterpretation(this.photometricInterpretation); |
||||
|
return JpegColorConverterBase.GetConverter(colorSpace, frame.Precision); |
||||
|
} |
||||
|
|
||||
|
private static JpegColorSpace GetJpegColorSpaceFromPhotometricInterpretation(TiffPhotometricInterpretation interpretation) |
||||
|
=> interpretation switch |
||||
|
{ |
||||
|
// Like libtiff: Always treat the pixel data as YCbCr when the data is compressed with old jpeg compression.
|
||||
|
TiffPhotometricInterpretation.Rgb => JpegColorSpace.YCbCr, |
||||
|
TiffPhotometricInterpretation.YCbCr => JpegColorSpace.YCbCr, |
||||
|
_ => throw new InvalidImageContentException($"Invalid tiff photometric interpretation for jpeg encoding: {interpretation}"), |
||||
|
}; |
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:0a5a02fb888a47ed51aa6d72122f9d1996311e943be62bef4829bc1aa9f457e8 |
||||
|
size 214023 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:e8f722bc322b9a7621d1d858bec36ea78d31b85221f314a2b9872d3af91d600a |
||||
|
size 5052 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:4b2b73290a40c49a4a29e51ad53dadea65c1ceafc6a5384430f7092d613830db |
||||
|
size 1390339 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:790662b0fc48d5604e80a9689eb28cfa231be3eadc33f34c9bf43c211cce5c16 |
||||
|
size 440613 |
||||
@ -0,0 +1,3 @@ |
|||||
|
version https://git-lfs.github.com/spec/v1 |
||||
|
oid sha256:b6527e69a3cce3e6d425c35695319711ad36d939f934902faa47f6ee1f1c7e08 |
||||
|
size 10076700 |
||||
Loading…
Reference in new issue