Skip to content

Commit a5bb2ce

Browse files
author
Wayfarer
committed
Cleanup DirectBitmap a bit more.
1 parent a4128fb commit a5bb2ce

2 files changed

Lines changed: 88 additions & 37 deletions

File tree

Imaging/DirectBitmapCore.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,49 @@ internal static void SetPixelsSimd(Pixel32[] bits, int width, int height, IEnume
116116
}
117117
}
118118

119+
/// <summary>
120+
/// Sets pixels directly using unsafe pointer arithmetic for speed.
121+
/// Updates both the WPF back buffer and the Pixel32 array.
122+
/// </summary>
123+
/// <param name="bits">The Pixel32 array.</param>
124+
/// <param name="width">Width of the image.</param>
125+
/// <param name="height">Height of the image.</param>
126+
/// <param name="pixels">Pixels to set.</param>
127+
/// <param name="backBuffer">Pointer to the back buffer (optional, for WPF WriteableBitmap).</param>
128+
/// <param name="stride">Stride of the back buffer (required if backBuffer is not null).</param>
129+
internal static unsafe void SetPixelsUnsafe(
130+
Pixel32[] bits,
131+
int width,
132+
int height,
133+
IEnumerable<PixelData> pixels,
134+
byte* backBuffer = null,
135+
int stride = 0)
136+
{
137+
if (bits == null || pixels == null) return;
138+
139+
if (backBuffer != null && stride <= 0)
140+
throw new ArgumentException("Stride must be positive when backBuffer is provided.");
141+
142+
foreach (var pixel in pixels)
143+
{
144+
if ((uint)pixel.X >= width || (uint)pixel.Y >= height)
145+
continue;
146+
147+
// Update Pixel32 array
148+
bits[pixel.Y * width + pixel.X] = new Pixel32(pixel.R, pixel.G, pixel.B, pixel.A);
149+
150+
// Update back buffer if provided
151+
if (backBuffer != null)
152+
{
153+
var offset = pixel.Y * stride + pixel.X * 4;
154+
backBuffer[offset + 0] = pixel.B;
155+
backBuffer[offset + 1] = pixel.G;
156+
backBuffer[offset + 2] = pixel.R;
157+
backBuffer[offset + 3] = pixel.A;
158+
}
159+
}
160+
}
161+
119162
/// <summary>
120163
/// Alpha blends another pixel buffer onto this image using SIMD.
121164
/// Format: BGRA (32-bit uint). Alpha is premultiplied at runtime.

Imaging/DirectBitmapImage.cs

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -161,38 +161,6 @@ public void SetPixel(int x, int y, Color color)
161161
}
162162
}
163163

164-
/// <summary>
165-
/// Sets pixels from a collection of <see cref="PixelData" />.
166-
/// Uses unsafe pointer arithmetic for speed.
167-
/// </summary>
168-
/// <param name="pixels">The pixels to set.</param>
169-
public void SetPixelsUnsafe(IEnumerable<PixelData> pixels)
170-
{
171-
_bitmap.Lock();
172-
unsafe
173-
{
174-
var buffer = (byte*)_bitmap.BackBuffer.ToPointer();
175-
var stride = _bitmap.BackBufferStride;
176-
177-
foreach (var pixel in pixels)
178-
{
179-
if ((uint)pixel.X >= Width || (uint)pixel.Y >= Height)
180-
continue;
181-
182-
var offset = pixel.Y * stride + pixel.X * 4;
183-
buffer[offset + 0] = pixel.B;
184-
buffer[offset + 1] = pixel.G;
185-
buffer[offset + 2] = pixel.R;
186-
buffer[offset + 3] = pixel.A;
187-
188-
// still update the shared Bits array
189-
DirectBitmapCore.SetPixels(Bits, Width, Height, new[] { pixel });
190-
}
191-
}
192-
_bitmap.AddDirtyRect(new Int32Rect(0, 0, Width, Height));
193-
_bitmap.Unlock();
194-
}
195-
196164
/// <summary>
197165
/// Fills the bitmap with a uniform color using SIMD.
198166
/// </summary>
@@ -251,14 +219,54 @@ public void ApplyColorMatrix(float[][] matrix)
251219
/// Sets pixels from PixelData collection efficiently.
252220
/// </summary>
253221
/// <param name="pixels">Collection of PixelData (x, y, RGBA).</param>
254-
public void SetPixels(IEnumerable<PixelData> pixels)
222+
public void SetPixels(IEnumerable<PixelData> pixels, int threshold = 16)
255223
{
256-
lock (_syncLock)
224+
if (pixels == null) return;
225+
226+
IEnumerable<PixelData> pixelCollection = pixels;
227+
228+
// Materialize if not ICollection to count
229+
int count;
230+
if (pixels is ICollection<PixelData> col)
231+
{
232+
count = col.Count;
233+
}
234+
else
235+
{
236+
var list = pixels.ToList();
237+
count = list.Count;
238+
pixelCollection = list;
239+
}
240+
241+
// Small batch -> unsafe direct write
242+
if (count <= threshold)
257243
{
258-
// Convert PixelData to (x, y, Pixel32)
259-
var pixelData = pixels.Select(p => (p.X, p.Y, new Pixel32(p.R, p.G, p.B, p.A)));
244+
if (_bitmap != null)
245+
{
246+
_bitmap.Lock();
247+
unsafe
248+
{
249+
var buffer = (byte*)_bitmap.BackBuffer.ToPointer();
250+
var stride = _bitmap.BackBufferStride;
251+
DirectBitmapCore.SetPixelsUnsafe(Bits, Width, Height, pixelCollection, buffer, stride);
252+
}
253+
_bitmap.AddDirtyRect(new Int32Rect(0, 0, Width, Height));
254+
_bitmap.Unlock();
255+
}
256+
else
257+
{
258+
unsafe
259+
{
260+
DirectBitmapCore.SetPixelsUnsafe(Bits, Width, Height, pixelCollection);
261+
}
262+
}
263+
return;
264+
}
260265

261-
// SIMD batch update
266+
// Large batch -> SIMD batch
267+
lock (_syncLock)
268+
{
269+
var pixelData = pixelCollection.Select(p => (p.X, p.Y, new Pixel32(p.R, p.G, p.B, p.A)));
262270
DirectBitmapCore.SetPixelsSimd(Bits, Width, Height, pixelData);
263271
}
264272

0 commit comments

Comments
 (0)