Skip to content

Commit 1d99ec2

Browse files
authored
Merge branch 'main' into bp/decodePxr24
2 parents c1de5b6 + d7c7eb6 commit 1d99ec2

16 files changed

Lines changed: 139 additions & 109 deletions

File tree

src/ImageSharp/Advanced/AdvancedImageExtensions.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,15 @@ public static void AcceptVisitor(this Image source, IImageVisitor visitor)
7676
public static Task AcceptVisitorAsync(this Image source, IImageVisitorAsync visitor, CancellationToken cancellationToken = default)
7777
=> source.AcceptAsync(visitor, cancellationToken);
7878

79+
/// <summary>
80+
/// Accepts a <see cref="IImageVisitor"/> to implement a double-dispatch pattern in order to
81+
/// apply pixel-specific operations on non-generic <see cref="Image"/> instances
82+
/// </summary>
83+
/// <param name="source">The source image frame.</param>
84+
/// <param name="visitor">The image visitor.</param>
85+
public static void AcceptVisitor(this ImageFrame source, IImageFrameVisitor visitor)
86+
=> source.Accept(visitor);
87+
7988
/// <summary>
8089
/// Gets the representation of the pixels as a <see cref="IMemoryGroup{T}"/> containing the backing pixel data of the image
8190
/// stored in row major order, as a list of contiguous <see cref="Memory{T}"/> blocks in the source image's pixel format.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using SixLabors.ImageSharp.PixelFormats;
5+
6+
namespace SixLabors.ImageSharp.Advanced;
7+
8+
/// <summary>
9+
/// A visitor to implement a double-dispatch pattern in order to apply pixel-specific operations
10+
/// on non-generic <see cref="ImageFrame"/> instances.
11+
/// </summary>
12+
public interface IImageFrameVisitor
13+
{
14+
/// <summary>
15+
/// Provides a pixel-specific implementation for a given operation.
16+
/// </summary>
17+
/// <param name="frame">The image frame.</param>
18+
/// <typeparam name="TPixel">The pixel type.</typeparam>
19+
public void Visit<TPixel>(ImageFrame<TPixel> frame)
20+
where TPixel : unmanaged, IPixel<TPixel>;
21+
}

src/ImageSharp/Advanced/IImageVisitor.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public interface IImageVisitor
1616
/// </summary>
1717
/// <param name="image">The image.</param>
1818
/// <typeparam name="TPixel">The pixel type.</typeparam>
19-
void Visit<TPixel>(Image<TPixel> image)
19+
public void Visit<TPixel>(Image<TPixel> image)
2020
where TPixel : unmanaged, IPixel<TPixel>;
2121
}
2222

@@ -33,6 +33,6 @@ public interface IImageVisitorAsync
3333
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
3434
/// <typeparam name="TPixel">The pixel type.</typeparam>
3535
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
36-
Task VisitAsync<TPixel>(Image<TPixel> image, CancellationToken cancellationToken)
36+
public Task VisitAsync<TPixel>(Image<TPixel> image, CancellationToken cancellationToken)
3737
where TPixel : unmanaged, IPixel<TPixel>;
3838
}

src/ImageSharp/Formats/Gif/GifEncoderCore.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ private void EncodeFirstFrame<TPixel>(
303303
this.WriteGraphicalControlExtension(metadata, stream);
304304

305305
Buffer2D<byte> indices = ((IPixelSource)quantized).PixelBuffer;
306-
Rectangle interest = indices.FullRectangle();
306+
Rectangle interest = indices.Bounds;
307307
bool useLocal = this.colorTableMode == FrameColorTableMode.Local || (metadata.ColorTableMode == FrameColorTableMode.Local);
308308
int bitDepth = ColorNumerics.GetBitsNeededForColorDepth(quantized.Palette.Length);
309309

src/ImageSharp/ImageFrame.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,19 @@ public void Dispose()
7171
/// <param name="disposing">Whether to dispose of managed and unmanaged objects.</param>
7272
protected abstract void Dispose(bool disposing);
7373

74+
/// <summary>
75+
/// Accepts a <see cref="IImageFrameVisitor"/>.
76+
/// Implemented by <see cref="ImageFrame{TPixel}"/> invoking <see cref="IImageFrameVisitor.Visit{TPixel}"/>
77+
/// with the pixel type of the image.
78+
/// </summary>
79+
/// <param name="visitor">The visitor.</param>
80+
internal abstract void Accept(IImageFrameVisitor visitor);
81+
82+
/// <summary>
83+
/// Copies the pixel data of the image frame to a <see cref="Buffer2D{TDestinationPixel}"/> of a specific pixel type.
84+
/// </summary>
85+
/// <typeparam name="TDestinationPixel">The pixel type of the destination buffer.</typeparam>
86+
/// <param name="destination">The buffer to copy the pixel data to.</param>
7487
internal abstract void CopyPixelsTo<TDestinationPixel>(Buffer2D<TDestinationPixel> destination)
7588
where TDestinationPixel : unmanaged, IPixel<TDestinationPixel>;
7689

src/ImageSharp/ImageFrame{TPixel}.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -339,14 +339,18 @@ public bool DangerousTryGetSinglePixelMemory(out Memory<TPixel> memory)
339339
[MethodImpl(MethodImplOptions.AggressiveInlining)]
340340
internal ref TPixel GetPixelReference(int x, int y) => ref this.PixelBuffer[x, y];
341341

342+
/// <inheritdoc />
343+
internal override void Accept(IImageFrameVisitor visitor)
344+
=> visitor.Visit(this);
345+
342346
/// <summary>
343347
/// Copies the pixels to a <see cref="Buffer2D{TPixel}"/> of the same size.
344348
/// </summary>
345349
/// <param name="target">The target pixel buffer accessor.</param>
346350
/// <exception cref="ArgumentException">ImageFrame{TPixel}.CopyTo(): target must be of the same size!</exception>
347351
internal void CopyTo(Buffer2D<TPixel> target)
348352
{
349-
if (this.Size != target.Size())
353+
if (this.Size != target.Size)
350354
{
351355
throw new ArgumentException("ImageFrame<TPixel>.CopyTo(): target must be of the same size!", nameof(target));
352356
}
@@ -363,8 +367,8 @@ internal void SwapOrCopyPixelsBufferFrom(ImageFrame<TPixel> source)
363367
{
364368
Guard.NotNull(source, nameof(source));
365369

366-
Buffer2D<TPixel>.SwapOrCopyContent(this.PixelBuffer, source.PixelBuffer);
367-
this.UpdateSize(this.PixelBuffer.Size());
370+
_ = Buffer2D<TPixel>.SwapOrCopyContent(this.PixelBuffer, source.PixelBuffer);
371+
this.UpdateSize(this.PixelBuffer.Size);
368372
}
369373

370374
/// <summary>
@@ -475,10 +479,7 @@ internal ImageFrame<TPixel2> CloneAs<TPixel2>(Configuration configuration)
475479
/// Clears the bitmap.
476480
/// </summary>
477481
/// <param name="value">The value to initialize the bitmap with.</param>
478-
internal void Clear(TPixel value)
479-
{
480-
this.PixelBuffer.Clear(value);
481-
}
482+
internal void Clear(TPixel value) => this.PixelBuffer.Clear(value);
482483

483484
[MethodImpl(InliningOptions.ShortMethod)]
484485
private void VerifyCoords(int x, int y)

src/ImageSharp/Image{TPixel}.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Licensed under the Six Labors Split License.
33

44
using System.Runtime.CompilerServices;
5-
using System.Runtime.InteropServices;
65
using SixLabors.ImageSharp.Advanced;
76
using SixLabors.ImageSharp.Memory;
87
using SixLabors.ImageSharp.Metadata;

src/ImageSharp/Memory/Buffer2DExtensions.cs

Lines changed: 15 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -104,60 +104,40 @@ internal static unsafe void DangerousCopyColumns<T>(
104104
}
105105

106106
/// <summary>
107-
/// Returns a <see cref="Rectangle"/> representing the full area of the buffer.
108-
/// </summary>
109-
/// <typeparam name="T">The element type</typeparam>
110-
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
111-
/// <returns>The <see cref="Rectangle"/></returns>
112-
internal static Rectangle FullRectangle<T>(this Buffer2D<T> buffer)
113-
where T : struct
114-
=> new(0, 0, buffer.Width, buffer.Height);
115-
116-
/// <summary>
117-
/// Return a <see cref="Buffer2DRegion{T}"/> to the subregion represented by 'rectangle'
107+
/// Return a <see cref="Buffer2DRegion{T}"/> to the subregion represented by <paramref name="rectangle"/>.
118108
/// </summary>
119109
/// <typeparam name="T">The element type</typeparam>
120110
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
121111
/// <param name="rectangle">The rectangle subregion</param>
122112
/// <returns>The <see cref="Buffer2DRegion{T}"/></returns>
123-
internal static Buffer2DRegion<T> GetRegion<T>(this Buffer2D<T> buffer, Rectangle rectangle)
113+
public static Buffer2DRegion<T> GetRegion<T>(this Buffer2D<T> buffer, Rectangle rectangle)
124114
where T : unmanaged =>
125115
new(buffer, rectangle);
126116

127-
internal static Buffer2DRegion<T> GetRegion<T>(this Buffer2D<T> buffer, int x, int y, int width, int height)
117+
/// <summary>
118+
/// Return a <see cref="Buffer2DRegion{T}"/> to the specified area of <paramref name="buffer"/>.
119+
/// </summary>
120+
/// <typeparam name="T">The element type.</typeparam>
121+
/// <param name="buffer">The <see cref="Buffer2D{T}"/>.</param>
122+
/// <param name="x">The X coordinate of the region.</param>
123+
/// <param name="y">The Y coordinate of the region.</param>
124+
/// <param name="width">The region width.</param>
125+
/// <param name="height">The region height.</param>
126+
/// <returns>The <see cref="Buffer2DRegion{T}"/>.</returns>
127+
public static Buffer2DRegion<T> GetRegion<T>(this Buffer2D<T> buffer, int x, int y, int width, int height)
128128
where T : unmanaged =>
129129
new(buffer, new Rectangle(x, y, width, height));
130130

131131
/// <summary>
132-
/// Return a <see cref="Buffer2DRegion{T}"/> to the whole area of 'buffer'
132+
/// Return a <see cref="Buffer2DRegion{T}"/> to the whole area of <paramref name="buffer"/>.
133133
/// </summary>
134134
/// <typeparam name="T">The element type</typeparam>
135135
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
136136
/// <returns>The <see cref="Buffer2DRegion{T}"/></returns>
137-
internal static Buffer2DRegion<T> GetRegion<T>(this Buffer2D<T> buffer)
137+
public static Buffer2DRegion<T> GetRegion<T>(this Buffer2D<T> buffer)
138138
where T : unmanaged =>
139139
new(buffer);
140140

141-
/// <summary>
142-
/// Returns the size of the buffer.
143-
/// </summary>
144-
/// <typeparam name="T">The element type</typeparam>
145-
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
146-
/// <returns>The <see cref="Size{T}"/> of the buffer</returns>
147-
internal static Size Size<T>(this Buffer2D<T> buffer)
148-
where T : struct =>
149-
new(buffer.Width, buffer.Height);
150-
151-
/// <summary>
152-
/// Gets the bounds of the buffer.
153-
/// </summary>
154-
/// <typeparam name="T">The element type</typeparam>
155-
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
156-
/// <returns>The <see cref="Rectangle"/></returns>
157-
internal static Rectangle Bounds<T>(this Buffer2D<T> buffer)
158-
where T : struct =>
159-
new(0, 0, buffer.Width, buffer.Height);
160-
161141
[Conditional("DEBUG")]
162142
private static void CheckColumnRegionsDoNotOverlap<T>(
163143
Buffer2D<T> buffer,

src/ImageSharp/Memory/Buffer2DRegion{T}.cs

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,17 @@ public readonly struct Buffer2DRegion<T>
1515
/// Initializes a new instance of the <see cref="Buffer2DRegion{T}"/> struct.
1616
/// </summary>
1717
/// <param name="buffer">The <see cref="Buffer2D{T}"/>.</param>
18-
/// <param name="rectangle">The <see cref="Rectangle"/> defining a rectangular area within the buffer.</param>
18+
/// <param name="bounds">The <see cref="Bounds"/> defining a rectangular area within the buffer.</param>
1919
[MethodImpl(MethodImplOptions.AggressiveInlining)]
20-
public Buffer2DRegion(Buffer2D<T> buffer, Rectangle rectangle)
20+
public Buffer2DRegion(Buffer2D<T> buffer, Rectangle bounds)
2121
{
22-
DebugGuard.MustBeGreaterThanOrEqualTo(rectangle.X, 0, nameof(rectangle));
23-
DebugGuard.MustBeGreaterThanOrEqualTo(rectangle.Y, 0, nameof(rectangle));
24-
DebugGuard.MustBeLessThanOrEqualTo(rectangle.Width, buffer.Width, nameof(rectangle));
25-
DebugGuard.MustBeLessThanOrEqualTo(rectangle.Height, buffer.Height, nameof(rectangle));
22+
DebugGuard.MustBeGreaterThanOrEqualTo(bounds.X, 0, nameof(bounds));
23+
DebugGuard.MustBeGreaterThanOrEqualTo(bounds.Y, 0, nameof(bounds));
24+
DebugGuard.MustBeLessThanOrEqualTo(bounds.Width, buffer.Width, nameof(bounds));
25+
DebugGuard.MustBeLessThanOrEqualTo(bounds.Height, buffer.Height, nameof(bounds));
2626

2727
this.Buffer = buffer;
28-
this.Rectangle = rectangle;
28+
this.Bounds = bounds;
2929
}
3030

3131
/// <summary>
@@ -34,15 +34,10 @@ public Buffer2DRegion(Buffer2D<T> buffer, Rectangle rectangle)
3434
/// <param name="buffer">The <see cref="Buffer2D{T}"/>.</param>
3535
[MethodImpl(MethodImplOptions.AggressiveInlining)]
3636
public Buffer2DRegion(Buffer2D<T> buffer)
37-
: this(buffer, buffer.FullRectangle())
37+
: this(buffer, buffer.Bounds)
3838
{
3939
}
4040

41-
/// <summary>
42-
/// Gets the rectangle specifying the boundaries of the area in <see cref="Buffer"/>.
43-
/// </summary>
44-
public Rectangle Rectangle { get; }
45-
4641
/// <summary>
4742
/// Gets the <see cref="Buffer2D{T}"/> being pointed by this instance.
4843
/// </summary>
@@ -51,12 +46,12 @@ public Buffer2DRegion(Buffer2D<T> buffer)
5146
/// <summary>
5247
/// Gets the width
5348
/// </summary>
54-
public int Width => this.Rectangle.Width;
49+
public int Width => this.Bounds.Width;
5550

5651
/// <summary>
5752
/// Gets the height
5853
/// </summary>
59-
public int Height => this.Rectangle.Height;
54+
public int Height => this.Bounds.Height;
6055

6156
/// <summary>
6257
/// Gets the number of elements between row starts in <see cref="Buffer"/>.
@@ -66,20 +61,25 @@ public Buffer2DRegion(Buffer2D<T> buffer)
6661
/// <summary>
6762
/// Gets the size of the area.
6863
/// </summary>
69-
internal Size Size => this.Rectangle.Size;
64+
public Size Size => this.Bounds.Size;
65+
66+
/// <summary>
67+
/// Gets the rectangle specifying the boundaries of the area in <see cref="Buffer"/>.
68+
/// </summary>
69+
public Rectangle Bounds { get; }
7070

7171
/// <summary>
7272
/// Gets a value indicating whether the area refers to the entire <see cref="Buffer"/>
7373
/// </summary>
74-
internal bool IsFullBufferArea => this.Size == this.Buffer.Size();
74+
internal bool IsFullBufferArea => this.Size == this.Buffer.Size;
7575

7676
/// <summary>
7777
/// Gets or sets a value at the given index.
7878
/// </summary>
7979
/// <param name="x">The position inside a row</param>
8080
/// <param name="y">The row index</param>
8181
/// <returns>The reference to the value</returns>
82-
internal ref T this[int x, int y] => ref this.Buffer[x + this.Rectangle.X, y + this.Rectangle.Y];
82+
internal ref T this[int x, int y] => ref this.Buffer[x + this.Bounds.X, y + this.Bounds.Y];
8383

8484
/// <summary>
8585
/// Gets a span to row 'y' inside this area.
@@ -89,9 +89,9 @@ public Buffer2DRegion(Buffer2D<T> buffer)
8989
[MethodImpl(MethodImplOptions.AggressiveInlining)]
9090
public Span<T> DangerousGetRowSpan(int y)
9191
{
92-
int yy = this.Rectangle.Y + y;
93-
int xx = this.Rectangle.X;
94-
int width = this.Rectangle.Width;
92+
int yy = this.Bounds.Y + y;
93+
int xx = this.Bounds.X;
94+
int width = this.Bounds.Width;
9595

9696
return this.Buffer.DangerousGetRowSpan(yy).Slice(xx, width);
9797
}
@@ -114,16 +114,16 @@ public Buffer2DRegion<T> GetSubRegion(int x, int y, int width, int height)
114114
/// <summary>
115115
/// Returns a subregion as <see cref="Buffer2DRegion{T}"/>. (Similar to <see cref="Span{T}.Slice(int, int)"/>.)
116116
/// </summary>
117-
/// <param name="rectangle">The <see cref="Rectangle"/> specifying the boundaries of the subregion</param>
117+
/// <param name="rectangle">The <see cref="Bounds"/> specifying the boundaries of the subregion</param>
118118
/// <returns>The subregion</returns>
119119
[MethodImpl(MethodImplOptions.AggressiveInlining)]
120120
public Buffer2DRegion<T> GetSubRegion(Rectangle rectangle)
121121
{
122-
DebugGuard.MustBeLessThanOrEqualTo(rectangle.Width, this.Rectangle.Width, nameof(rectangle));
123-
DebugGuard.MustBeLessThanOrEqualTo(rectangle.Height, this.Rectangle.Height, nameof(rectangle));
122+
DebugGuard.MustBeLessThanOrEqualTo(rectangle.Width, this.Bounds.Width, nameof(rectangle));
123+
DebugGuard.MustBeLessThanOrEqualTo(rectangle.Height, this.Bounds.Height, nameof(rectangle));
124124

125-
int x = this.Rectangle.X + rectangle.X;
126-
int y = this.Rectangle.Y + rectangle.Y;
125+
int x = this.Bounds.X + rectangle.X;
126+
int y = this.Bounds.Y + rectangle.Y;
127127
rectangle = new Rectangle(x, y, rectangle.Width, rectangle.Height);
128128
return new Buffer2DRegion<T>(this.Buffer, rectangle);
129129
}
@@ -135,8 +135,8 @@ public Buffer2DRegion<T> GetSubRegion(Rectangle rectangle)
135135
[MethodImpl(MethodImplOptions.AggressiveInlining)]
136136
internal ref T GetReferenceToOrigin()
137137
{
138-
int y = this.Rectangle.Y;
139-
int x = this.Rectangle.X;
138+
int y = this.Bounds.Y;
139+
int x = this.Bounds.X;
140140
return ref this.Buffer.DangerousGetRowSpan(y)[x];
141141
}
142142

@@ -152,7 +152,7 @@ internal void Clear()
152152
return;
153153
}
154154

155-
for (int y = 0; y < this.Rectangle.Height; y++)
155+
for (int y = 0; y < this.Bounds.Height; y++)
156156
{
157157
Span<T> row = this.DangerousGetRowSpan(y);
158158
row.Clear();
@@ -172,7 +172,7 @@ internal void Fill(T value)
172172
return;
173173
}
174174

175-
for (int y = 0; y < this.Rectangle.Height; y++)
175+
for (int y = 0; y < this.Bounds.Height; y++)
176176
{
177177
Span<T> row = this.DangerousGetRowSpan(y);
178178
row.Fill(value);

0 commit comments

Comments
 (0)