Skip to content

Commit 7c152bb

Browse files
Merge pull request #1158 from SixLabors/js/handle-large-gif
Gif. Use Buffer2D throughout.
2 parents 323ddb5 + d915f48 commit 7c152bb

File tree

10 files changed

+174
-179
lines changed

10 files changed

+174
-179
lines changed

src/ImageSharp/Advanced/IPixelSource.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
1-
// Copyright (c) Six Labors and contributors.
1+
// Copyright (c) Six Labors and contributors.
22
// Licensed under the Apache License, Version 2.0.
33

44
using SixLabors.ImageSharp.Memory;
55
using SixLabors.ImageSharp.PixelFormats;
66

77
namespace SixLabors.ImageSharp.Advanced
88
{
9+
/// <summary>
10+
/// Encapsulates the basic properties and methods required to manipulate images.
11+
/// </summary>
12+
internal interface IPixelSource
13+
{
14+
/// <summary>
15+
/// Gets the pixel buffer.
16+
/// </summary>
17+
Buffer2D<byte> PixelBuffer { get; }
18+
}
19+
920
/// <summary>
1021
/// Encapsulates the basic properties and methods required to manipulate images.
1122
/// </summary>
@@ -18,4 +29,4 @@ internal interface IPixelSource<TPixel>
1829
/// </summary>
1930
Buffer2D<TPixel> PixelBuffer { get; }
2031
}
21-
}
32+
}

src/ImageSharp/Common/Extensions/ComparableExtensions.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Six Labors and contributors.
1+
// Copyright (c) Six Labors and contributors.
22
// Licensed under the Apache License, Version 2.0.
33

44
using System;
@@ -14,7 +14,7 @@ internal static class ComparableExtensions
1414
/// <summary>
1515
/// Restricts a <see cref="byte"/> to be within a specified range.
1616
/// </summary>
17-
/// <param name="value">The The value to clamp.</param>
17+
/// <param name="value">The value to clamp.</param>
1818
/// <param name="min">The minimum value. If value is less than min, min will be returned.</param>
1919
/// <param name="max">The maximum value. If value is greater than max, max will be returned.</param>
2020
/// <returns>
@@ -137,4 +137,4 @@ public static double Clamp(this double value, double min, double max)
137137
return value;
138138
}
139139
}
140-
}
140+
}

src/ImageSharp/Formats/Gif/GifDecoderCore.cs

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ private void ReadFrame<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPixel> p
353353
this.ReadImageDescriptor();
354354

355355
IManagedByteBuffer localColorTable = null;
356-
IManagedByteBuffer indices = null;
356+
Buffer2D<byte> indices = null;
357357
try
358358
{
359359
// Determine the color table for this frame. If there is a local one, use it otherwise use the global color table.
@@ -364,11 +364,11 @@ private void ReadFrame<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPixel> p
364364
this.stream.Read(localColorTable.Array, 0, length);
365365
}
366366

367-
indices = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(this.imageDescriptor.Width * this.imageDescriptor.Height, AllocationOptions.Clean);
367+
indices = this.configuration.MemoryAllocator.Allocate2D<byte>(this.imageDescriptor.Width, this.imageDescriptor.Height, AllocationOptions.Clean);
368368

369-
this.ReadFrameIndices(this.imageDescriptor, indices.GetSpan());
369+
this.ReadFrameIndices(indices);
370370
ReadOnlySpan<Rgb24> colorTable = MemoryMarshal.Cast<byte, Rgb24>((localColorTable ?? this.globalColorTable).GetSpan());
371-
this.ReadFrameColors(ref image, ref previousFrame, indices.GetSpan(), colorTable, this.imageDescriptor);
371+
this.ReadFrameColors(ref image, ref previousFrame, indices, colorTable, this.imageDescriptor);
372372

373373
// Skip any remaining blocks
374374
this.SkipBlock();
@@ -383,16 +383,13 @@ private void ReadFrame<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPixel> p
383383
/// <summary>
384384
/// Reads the frame indices marking the color to use for each pixel.
385385
/// </summary>
386-
/// <param name="imageDescriptor">The <see cref="GifImageDescriptor"/>.</param>
387-
/// <param name="indices">The pixel array to write to.</param>
386+
/// <param name="indices">The 2D pixel buffer to write to.</param>
388387
[MethodImpl(MethodImplOptions.AggressiveInlining)]
389-
private void ReadFrameIndices(in GifImageDescriptor imageDescriptor, Span<byte> indices)
388+
private void ReadFrameIndices(Buffer2D<byte> indices)
390389
{
391390
int dataSize = this.stream.ReadByte();
392-
using (var lzwDecoder = new LzwDecoder(this.configuration.MemoryAllocator, this.stream))
393-
{
394-
lzwDecoder.DecodePixels(imageDescriptor.Width, imageDescriptor.Height, dataSize, indices);
395-
}
391+
using var lzwDecoder = new LzwDecoder(this.configuration.MemoryAllocator, this.stream);
392+
lzwDecoder.DecodePixels(dataSize, indices);
396393
}
397394

398395
/// <summary>
@@ -404,10 +401,9 @@ private void ReadFrameIndices(in GifImageDescriptor imageDescriptor, Span<byte>
404401
/// <param name="indices">The indexed pixels.</param>
405402
/// <param name="colorTable">The color table containing the available colors.</param>
406403
/// <param name="descriptor">The <see cref="GifImageDescriptor"/></param>
407-
private void ReadFrameColors<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPixel> previousFrame, Span<byte> indices, ReadOnlySpan<Rgb24> colorTable, in GifImageDescriptor descriptor)
404+
private void ReadFrameColors<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPixel> previousFrame, Buffer2D<byte> indices, ReadOnlySpan<Rgb24> colorTable, in GifImageDescriptor descriptor)
408405
where TPixel : unmanaged, IPixel<TPixel>
409406
{
410-
ref byte indicesRef = ref MemoryMarshal.GetReference(indices);
411407
int imageWidth = this.logicalScreenDescriptor.Width;
412408
int imageHeight = this.logicalScreenDescriptor.Height;
413409

@@ -440,13 +436,20 @@ private void ReadFrameColors<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPi
440436
this.RestoreToBackground(imageFrame);
441437
}
442438

443-
int i = 0;
444439
int interlacePass = 0; // The interlace pass
445440
int interlaceIncrement = 8; // The interlacing line increment
446441
int interlaceY = 0; // The current interlaced line
447-
448-
for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++)
442+
int descriptorTop = descriptor.Top;
443+
int descriptorBottom = descriptorTop + descriptor.Height;
444+
int descriptorLeft = descriptor.Left;
445+
int descriptorRight = descriptorLeft + descriptor.Width;
446+
bool transFlag = this.graphicsControlExtension.TransparencyFlag;
447+
byte transIndex = this.graphicsControlExtension.TransparencyIndex;
448+
449+
for (int y = descriptorTop; y < descriptorBottom && y < imageHeight; y++)
449450
{
451+
ref byte indicesRowRef = ref MemoryMarshal.GetReference(indices.GetRowSpan(y - descriptorTop));
452+
450453
// Check if this image is interlaced.
451454
int writeY; // the target y offset to write to
452455
if (descriptor.InterlaceFlag)
@@ -482,35 +485,29 @@ private void ReadFrameColors<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPi
482485
}
483486

484487
ref TPixel rowRef = ref MemoryMarshal.GetReference(imageFrame.GetPixelRowSpan(writeY));
485-
bool transFlag = this.graphicsControlExtension.TransparencyFlag;
486488

487489
if (!transFlag)
488490
{
489491
// #403 The left + width value can be larger than the image width
490-
for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width && x < imageWidth; x++)
492+
for (int x = descriptorLeft; x < descriptorRight && x < imageWidth; x++)
491493
{
492-
int index = Unsafe.Add(ref indicesRef, i);
494+
int index = Unsafe.Add(ref indicesRowRef, x - descriptorLeft);
493495
ref TPixel pixel = ref Unsafe.Add(ref rowRef, x);
494496
Rgb24 rgb = colorTable[index];
495497
pixel.FromRgb24(rgb);
496-
497-
i++;
498498
}
499499
}
500500
else
501501
{
502-
byte transIndex = this.graphicsControlExtension.TransparencyIndex;
503-
for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width && x < imageWidth; x++)
502+
for (int x = descriptorLeft; x < descriptorRight && x < imageWidth; x++)
504503
{
505-
int index = Unsafe.Add(ref indicesRef, i);
504+
int index = Unsafe.Add(ref indicesRowRef, x - descriptorLeft);
506505
if (transIndex != index)
507506
{
508507
ref TPixel pixel = ref Unsafe.Add(ref rowRef, x);
509508
Rgb24 rgb = colorTable[index];
510509
pixel.FromRgb24(rgb);
511510
}
512-
513-
i++;
514511
}
515512
}
516513
}

src/ImageSharp/Formats/Gif/GifEncoderCore.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.IO;
77
using System.Runtime.CompilerServices;
88
using System.Runtime.InteropServices;
9+
using SixLabors.ImageSharp.Advanced;
910
using SixLabors.ImageSharp.Memory;
1011
using SixLabors.ImageSharp.Metadata;
1112
using SixLabors.ImageSharp.PixelFormats;
@@ -471,7 +472,7 @@ private void WriteImageData<TPixel>(IndexedImageFrame<TPixel> image, Stream stre
471472
where TPixel : unmanaged, IPixel<TPixel>
472473
{
473474
using var encoder = new LzwEncoder(this.memoryAllocator, (byte)this.bitDepth);
474-
encoder.Encode(image.GetPixelBufferSpan(), stream);
475+
encoder.Encode(((IPixelSource)image).PixelBuffer, stream);
475476
}
476477
}
477478
}

src/ImageSharp/Formats/Gif/LzwDecoder.cs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,15 @@ public LzwDecoder(MemoryAllocator memoryAllocator, Stream stream)
6565
/// <summary>
6666
/// Decodes and decompresses all pixel indices from the stream.
6767
/// </summary>
68-
/// <param name="width">The width of the pixel index array.</param>
69-
/// <param name="height">The height of the pixel index array.</param>
7068
/// <param name="dataSize">Size of the data.</param>
7169
/// <param name="pixels">The pixel array to decode to.</param>
72-
public void DecodePixels(int width, int height, int dataSize, Span<byte> pixels)
70+
public void DecodePixels(int dataSize, Buffer2D<byte> pixels)
7371
{
7472
Guard.MustBeLessThan(dataSize, int.MaxValue, nameof(dataSize));
7573

7674
// The resulting index table length.
75+
int width = pixels.Width;
76+
int height = pixels.Height;
7777
int length = width * height;
7878

7979
// Calculate the clear code. The value of the clear code is 2 ^ dataSize
@@ -105,17 +105,28 @@ public void DecodePixels(int width, int height, int dataSize, Span<byte> pixels)
105105
ref int prefixRef = ref MemoryMarshal.GetReference(this.prefix.GetSpan());
106106
ref int suffixRef = ref MemoryMarshal.GetReference(this.suffix.GetSpan());
107107
ref int pixelStackRef = ref MemoryMarshal.GetReference(this.pixelStack.GetSpan());
108-
ref byte pixelsRef = ref MemoryMarshal.GetReference(pixels);
109108

110109
for (code = 0; code < clearCode; code++)
111110
{
112111
Unsafe.Add(ref suffixRef, code) = (byte)code;
113112
}
114113

115-
Span<byte> buffer = stackalloc byte[255];
114+
Span<byte> buffer = stackalloc byte[byte.MaxValue];
116115

116+
int y = 0;
117+
int x = 0;
118+
int rowMax = width;
119+
ref byte pixelsRowRef = ref MemoryMarshal.GetReference(pixels.GetRowSpan(y));
117120
while (xyz < length)
118121
{
122+
// Reset row reference.
123+
if (xyz == rowMax)
124+
{
125+
x = 0;
126+
pixelsRowRef = ref MemoryMarshal.GetReference(pixels.GetRowSpan(++y));
127+
rowMax = (y * width) + width;
128+
}
129+
119130
if (top == 0)
120131
{
121132
if (bits < codeSize)
@@ -209,7 +220,8 @@ public void DecodePixels(int width, int height, int dataSize, Span<byte> pixels)
209220
top--;
210221

211222
// Clear missing pixels
212-
Unsafe.Add(ref pixelsRef, xyz++) = (byte)Unsafe.Add(ref pixelStackRef, top);
223+
xyz++;
224+
Unsafe.Add(ref pixelsRowRef, x++) = (byte)Unsafe.Add(ref pixelStackRef, top);
213225
}
214226
}
215227

0 commit comments

Comments
 (0)