Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V3 : Decode Chunky Tile Rows Directly. #2874

Merged
merged 12 commits into from
Feb 2, 2025
14 changes: 4 additions & 10 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ jobs:

steps:
- name: Install libgdi+, which is required for tests running on ubuntu
if: ${{ matrix.options.os == 'buildjet-4vcpu-ubuntu-2204-arm' }}
if: ${{ contains(matrix.options.os, 'ubuntu') }}
run: sudo apt-get -y install libgdiplus libgif-dev libglib2.0-dev libcairo2-dev libtiff-dev libexif-dev

- name: Git Config
Expand Down Expand Up @@ -108,18 +108,12 @@ jobs:
restore-keys: ${{ runner.os }}-nuget-

- name: DotNet Setup
if: ${{ matrix.options.sdk-preview != true }}
uses: actions/setup-dotnet@v3
with:
dotnet-version: |
6.0.x

- name: DotNet Setup Preview
if: ${{ matrix.options.sdk-preview == true }}
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
7.0.x
6.0.x

- name: DotNet Build
if: ${{ matrix.options.sdk-preview != true }}
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/code-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ jobs:
runs-on: ${{matrix.options.os}}

steps:
- name: Install libgdi+, which is required for tests running on ubuntu
run: sudo apt-get -y install libgdiplus libgif-dev libglib2.0-dev libcairo2-dev libtiff-dev libexif-dev

- name: Git Config
shell: bash
run: |
Expand Down Expand Up @@ -55,9 +58,11 @@ jobs:
restore-keys: ${{ runner.os }}-nuget-

- name: DotNet Setup
uses: actions/setup-dotnet@v3
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
7.0.x
6.0.x

- name: DotNet Build
Expand Down
2 changes: 1 addition & 1 deletion src/ImageSharp/Formats/Png/PngThrowHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public static void ThrowInvalidParameter(object value, string message, [CallerAr
=> throw new NotSupportedException($"Invalid {name}. {message}. Was '{value}'.");

[DoesNotReturn]
public static void ThrowInvalidParameter(object value1, object value2, string message, [CallerArgumentExpression(nameof(value1))] string name1 = "", [CallerArgumentExpression(nameof(value1))] string name2 = "")
public static void ThrowInvalidParameter(object value1, object value2, string message, [CallerArgumentExpression(nameof(value1))] string name1 = "", [CallerArgumentExpression(nameof(value2))] string name2 = "")
=> throw new NotSupportedException($"Invalid {name1} or {name2}. {message}. Was '{value1}' and '{value2}'.");

[DoesNotReturn]
Expand Down
31 changes: 14 additions & 17 deletions src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,7 @@ private void DecodeTilesPlanar<TPixel>(
}

/// <summary>
/// Decodes the image data for Tiff's which arrange the pixel data in tiles and the chunky configuration.
/// Decodes the image data for TIFFs which arrange the pixel data in tiles and the chunky configuration.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="frame">The image frame to decode into.</param>
Expand All @@ -634,28 +634,26 @@ private void DecodeTilesChunky<TPixel>(
int width = pixels.Width;
int height = pixels.Height;
int bitsPerPixel = this.BitsPerPixel;

int bytesPerRow = RoundUpToMultipleOfEight(width * bitsPerPixel);
int bytesPerTileRow = RoundUpToMultipleOfEight(tileWidth * bitsPerPixel);
int uncompressedTilesSize = bytesPerTileRow * tileLength;
using IMemoryOwner<byte> tileBuffer = this.memoryAllocator.Allocate<byte>(uncompressedTilesSize, AllocationOptions.Clean);
using IMemoryOwner<byte> uncompressedPixelBuffer = this.memoryAllocator.Allocate<byte>(tilesDown * tileLength * bytesPerRow, AllocationOptions.Clean);

using IMemoryOwner<byte> tileBuffer = this.memoryAllocator.Allocate<byte>(bytesPerTileRow * tileLength, AllocationOptions.Clean);
Span<byte> tileBufferSpan = tileBuffer.GetSpan();
Span<byte> uncompressedPixelBufferSpan = uncompressedPixelBuffer.GetSpan();

using TiffBaseDecompressor decompressor = this.CreateDecompressor<TPixel>(frame.Width, bitsPerPixel);
TiffBaseColorDecoder<TPixel> colorDecoder = this.CreateChunkyColorDecoder<TPixel>();

int tileIndex = 0;
for (int tileY = 0; tileY < tilesDown; tileY++)
{
int remainingPixelsInRow = width;
int rowStartY = tileY * tileLength;
int rowEndY = Math.Min(rowStartY + tileLength, height);

for (int tileX = 0; tileX < tilesAcross; tileX++)
{
cancellationToken.ThrowIfCancellationRequested();

int uncompressedPixelBufferOffset = tileY * tileLength * bytesPerRow;
bool isLastHorizontalTile = tileX == tilesAcross - 1;
int remainingPixelsInRow = width - (tileX * tileWidth);

decompressor.Decompress(
this.inputStream,
Expand All @@ -666,22 +664,21 @@ private void DecodeTilesChunky<TPixel>(
cancellationToken);

int tileBufferOffset = 0;
uncompressedPixelBufferOffset += bytesPerTileRow * tileX;
int bytesToCopy = isLastHorizontalTile ? RoundUpToMultipleOfEight(bitsPerPixel * remainingPixelsInRow) : bytesPerTileRow;
for (int y = 0; y < tileLength; y++)
int rowWidth = Math.Min(tileWidth, remainingPixelsInRow);
int left = tileX * tileWidth;

for (int y = rowStartY; y < rowEndY; y++)
{
Span<byte> uncompressedPixelRow = uncompressedPixelBufferSpan.Slice(uncompressedPixelBufferOffset, bytesToCopy);
tileBufferSpan.Slice(tileBufferOffset, bytesToCopy).CopyTo(uncompressedPixelRow);
// Decode the tile row directly into the pixel buffer.
ReadOnlySpan<byte> tileRowSpan = tileBufferSpan.Slice(tileBufferOffset, bytesToCopy);
colorDecoder.Decode(tileRowSpan, pixels, left, y, rowWidth, 1);
tileBufferOffset += bytesPerTileRow;
uncompressedPixelBufferOffset += bytesPerRow;
}

remainingPixelsInRow -= tileWidth;
tileIndex++;
}
}

colorDecoder.Decode(uncompressedPixelBufferSpan, pixels, 0, 0, width, height);
}

private TiffBaseColorDecoder<TPixel> CreateChunkyColorDecoder<TPixel>()
Expand Down
Loading