Skip to content

Commit a4128fb

Browse files
author
Wayfarer
committed
unify both DirectBitmaps a bit more.
1 parent 049a81f commit a4128fb

3 files changed

Lines changed: 94 additions & 94 deletions

File tree

Imaging/DirectBitmap.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,15 @@ public void SetPixelsSimd(IEnumerable<(int x, int y, Color color)> pixels)
399399
}
400400
}
401401

402+
/// <summary>
403+
/// Blends the int.
404+
/// </summary>
405+
/// <param name="src">The source.</param>
406+
public void BlendInt(uint[] src)
407+
{
408+
DirectBitmapCore.BlendInt(Bits, src);
409+
}
410+
402411
/// <summary>
403412
/// Draws the vertical lines.
404413
/// </summary>
@@ -536,7 +545,6 @@ public BitmapImage ToBitmapImage()
536545
return bitmapImage;
537546
}
538547

539-
540548
/// <summary>
541549
/// Converts to string.
542550
/// </summary>

Imaging/DirectBitmapCore.cs

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,19 @@
66
* PROGRAMER: Peter Geinitz (Wayfarer)
77
*/
88

9+
using System;
910
using System.Collections.Generic;
1011
using System.Linq;
12+
using System.Runtime.InteropServices;
1113

1214
namespace Imaging
1315
{
1416
/// <summary>
15-
/// Hopefully this will be the last time we need to duplicate code between DirectBitmap and DirectBitmapImage.
17+
/// Hopefully this will be the last time we need to duplicate code between DirectBitmap and DirectBitmapImage.
1618
/// This class contains shared logic for both to set and get pixels from the underlying Pixel32 array, as well as an optimized method to set multiple pixels efficiently using contiguous runs per row.
1719
/// It works on a Pixel32 array only and does not touch any bitmap object, allowing both DirectBitmap and DirectBitmapImage to use it without duplication.
1820
/// </summary>
19-
public static class DirectBitmapCore
21+
internal static class DirectBitmapCore
2022
{
2123
/// <summary>
2224
/// Sets the pixel.
@@ -27,7 +29,7 @@ public static class DirectBitmapCore
2729
/// <param name="x">The x.</param>
2830
/// <param name="y">The y.</param>
2931
/// <param name="color">The color.</param>
30-
public static void SetPixel(Pixel32[] bits, int width, int height, int x, int y, Pixel32 color)
32+
internal static void SetPixel(Pixel32[] bits, int width, int height, int x, int y, Pixel32 color)
3133
{
3234
if ((uint)x >= width || (uint)y >= height) return;
3335

@@ -44,7 +46,7 @@ public static void SetPixel(Pixel32[] bits, int width, int height, int x, int y,
4446
/// <param name="x">The x.</param>
4547
/// <param name="y">The y.</param>
4648
/// <returns>Pixel32 Struct for the Coordinate.</returns>
47-
public static Pixel32 GetPixel(Pixel32[] bits, int width, int height, int x, int y)
49+
internal static Pixel32 GetPixel(Pixel32[] bits, int width, int height, int x, int y)
4850
{
4951
int index = x + y * width;
5052
return bits[index];
@@ -57,7 +59,7 @@ public static Pixel32 GetPixel(Pixel32[] bits, int width, int height, int x, int
5759
/// <param name="width">The width.</param>
5860
/// <param name="height">The height.</param>
5961
/// <param name="pixels">The pixels.</param>
60-
public static void SetPixels(Pixel32[] bits, int width, int height, IEnumerable<PixelData> pixels)
62+
internal static void SetPixels(Pixel32[] bits, int width, int height, IEnumerable<PixelData> pixels)
6163
{
6264
foreach (var pixel in pixels)
6365
{
@@ -76,7 +78,7 @@ public static void SetPixels(Pixel32[] bits, int width, int height, IEnumerable<
7678
/// <param name="width">The width of the image.</param>
7779
/// <param name="height">The height of the image.</param>
7880
/// <param name="pixels">Enumerable of (x, y, Pixel32) tuples to set.</param>
79-
public static void SetPixelsSimd(Pixel32[] bits, int width, int height, IEnumerable<(int x, int y, Pixel32 color)> pixels)
81+
internal static void SetPixelsSimd(Pixel32[] bits, int width, int height, IEnumerable<(int x, int y, Pixel32 color)> pixels)
8082
{
8183
if (bits == null || pixels == null) return;
8284

@@ -113,6 +115,71 @@ public static void SetPixelsSimd(Pixel32[] bits, int width, int height, IEnumera
113115
}
114116
}
115117
}
118+
119+
/// <summary>
120+
/// Alpha blends another pixel buffer onto this image using SIMD.
121+
/// Format: BGRA (32-bit uint). Alpha is premultiplied at runtime.
122+
/// </summary>
123+
/// <param name="dstBits">The DST bits.</param>
124+
/// <param name="src">Source pixels to blend (same size as current bitmap)</param>
125+
/// <exception cref="System.ArgumentNullException">
126+
/// dstBits
127+
/// or
128+
/// src
129+
/// </exception>
130+
/// <exception cref="System.ArgumentException">Source must match image size</exception>
131+
internal static unsafe void BlendInt(Pixel32[] dstBits, uint[] src)
132+
{
133+
if (dstBits == null) throw new ArgumentNullException(nameof(dstBits));
134+
if (src == null) throw new ArgumentNullException(nameof(src));
135+
if (dstBits.Length != src.Length)
136+
throw new ArgumentException("Source must match destination size");
137+
138+
var dstSpan = MemoryMarshal.Cast<Pixel32, uint>(dstBits.AsSpan());
139+
var srcSpan = src.AsSpan();
140+
141+
int len = dstSpan.Length;
142+
143+
fixed (uint* pDst = dstSpan)
144+
fixed (uint* pSrc = srcSpan)
145+
{
146+
uint* dPtr = pDst;
147+
uint* sPtr = pSrc;
148+
149+
for (int i = 0; i < len; i++)
150+
{
151+
uint s = *sPtr;
152+
153+
uint sa = s >> 24;
154+
if (sa == 0) { dPtr++; sPtr++; continue; }
155+
if (sa == 255) { *dPtr = s; dPtr++; sPtr++; continue; }
156+
157+
uint d = *dPtr;
158+
159+
uint da = d >> 24;
160+
uint dr = (d >> 16) & 0xFF;
161+
uint dg = (d >> 8) & 0xFF;
162+
uint db = d & 0xFF;
163+
164+
uint sr = (s >> 16) & 0xFF;
165+
uint sg = (s >> 8) & 0xFF;
166+
uint sb = s & 0xFF;
167+
168+
uint invA = 255 - sa;
169+
170+
uint r = (sr * sa + dr * invA) / 255;
171+
uint g = (sg * sa + dg * invA) / 255;
172+
uint b = (sb * sa + db * invA) / 255;
173+
174+
uint a = sa + ((da * invA) / 255);
175+
176+
*dPtr = (a << 24) | (r << 16) | (g << 8) | b;
177+
178+
dPtr++;
179+
sPtr++;
180+
}
181+
}
182+
}
116183
}
117184
}
118185

Imaging/DirectBitmapImage.cs

Lines changed: 12 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -265,93 +265,6 @@ public void SetPixels(IEnumerable<PixelData> pixels)
265265
UpdateBitmapFromBits();
266266
}
267267

268-
/// <summary>
269-
/// Alpha blends another pixel buffer onto this image using SIMD.
270-
/// Format: BGRA (32-bit uint). Alpha is premultiplied at runtime.
271-
/// </summary>
272-
/// <param name="src">Source pixels to blend (same size as current bitmap)</param>
273-
/// <exception cref="System.ArgumentException">Source must match image size</exception>
274-
public unsafe void BlendInt(uint[] src)
275-
{
276-
if (src == null || src.Length != Bits.Length)
277-
throw new ArgumentException("Source must match image size");
278-
279-
// Get spans for safe bounds check elimination (optional but good practice)
280-
// Casting Pixel32[] to uint[] for easier bitwise ops
281-
var dstSpan = System.Runtime.InteropServices.MemoryMarshal.Cast<Pixel32, uint>(Bits.AsSpan());
282-
var srcSpan = src.AsSpan();
283-
284-
int len = dstSpan.Length;
285-
286-
// Use unsafe pointers for maximum speed in the loop
287-
fixed (uint* pDst = dstSpan)
288-
fixed (uint* pSrc = srcSpan)
289-
{
290-
uint* dPtr = pDst;
291-
uint* sPtr = pSrc;
292-
293-
for (int i = 0; i < len; i++)
294-
{
295-
uint s = *sPtr;
296-
297-
// Fast check: if source is fully transparent, skip
298-
uint sa = s >> 24;
299-
if (sa == 0)
300-
{
301-
dPtr++;
302-
sPtr++;
303-
continue;
304-
}
305-
306-
// Fast check: if source is fully opaque, just overwrite
307-
if (sa == 255)
308-
{
309-
*dPtr = s;
310-
dPtr++;
311-
sPtr++;
312-
continue;
313-
}
314-
315-
uint d = *dPtr;
316-
317-
// Extract components (0xAARRGGBB format)
318-
// Destination
319-
uint da = d >> 24;
320-
uint dr = (d >> 16) & 0xFF;
321-
uint dg = (d >> 8) & 0xFF;
322-
uint db = d & 0xFF;
323-
324-
// Source
325-
uint sr = (s >> 16) & 0xFF;
326-
uint sg = (s >> 8) & 0xFF;
327-
uint sb = s & 0xFF;
328-
329-
// Standard Alpha Blending Formula: Out = (Src * Alpha + Dst * (255 - Alpha)) / 255
330-
// We approximate division by 255 using: (x + 128) / 255 ~= (x * 257 + 128) >> 16
331-
// Or simpler fast approximation: (v + (v >> 8)) >> 8
332-
333-
uint invA = 255 - sa;
334-
335-
// Blend Color Channels
336-
uint r = (sr * sa + dr * invA) / 255;
337-
uint g = (sg * sa + dg * invA) / 255;
338-
uint b = (sb * sa + db * invA) / 255;
339-
340-
// Blend Alpha Channel (Standard "Over" operator)
341-
// ResultAlpha = SrcAlpha + DstAlpha * (1 - SrcAlpha)
342-
uint a = sa + ((da * invA) / 255);
343-
344-
// Re-pack and write
345-
*dPtr = (a << 24) | (r << 16) | (g << 8) | b;
346-
347-
dPtr++;
348-
sPtr++;
349-
}
350-
}
351-
352-
UpdateBitmapFromBits();
353-
}
354-
355268
/// <summary>
356269
/// Sets multiple pixels efficiently using SIMD or run-length optimization.
357270
/// </summary>
@@ -370,6 +283,18 @@ public void SetPixelsBulk(IEnumerable<(int x, int y, Color color)> pixels)
370283
UpdateBitmapFromBits();
371284
}
372285

286+
/// <summary>
287+
/// Alpha blends another pixel buffer onto this image using SIMD.
288+
/// Format: BGRA (32-bit uint). Alpha is premultiplied at runtime.
289+
/// </summary>
290+
/// <param name="src">Source pixels to blend (same size as current bitmap)</param>
291+
/// <exception cref="System.ArgumentException">Source must match image size</exception>
292+
public unsafe void BlendInt(uint[] src)
293+
{
294+
DirectBitmapCore.BlendInt(Bits, src);
295+
UpdateBitmapFromBits();
296+
}
297+
373298
/// <summary>
374299
/// Converts the internal bitmap to a <see cref="BitmapImage" />.
375300
/// </summary>

0 commit comments

Comments
 (0)