@@ -250,59 +250,92 @@ public void SetPixels(IEnumerable<PixelData> pixels)
250250 /// </summary>
251251 /// <param name="src">Source pixels to blend (same size as current bitmap)</param>
252252 /// <exception cref="System.ArgumentException">Source must match image size</exception>
253- public void BlendSimd ( uint [ ] src )
253+ public unsafe void BlendInt ( uint [ ] src )
254254 {
255255 if ( src == null || src . Length != Bits . Length )
256256 throw new ArgumentException ( "Source must match image size" ) ;
257257
258- var dst = MemoryMarshal . Cast < Pixel32 , uint > ( Bits ) ;
258+ // Get spans for safe bounds check elimination (optional but good practice)
259+ // Casting Pixel32[] to uint[] for easier bitwise ops
260+ var dstSpan = System . Runtime . InteropServices . MemoryMarshal . Cast < Pixel32 , uint > ( Bits . AsSpan ( ) ) ;
261+ var srcSpan = src . AsSpan ( ) ;
259262
260- var len = dst . Length ;
263+ int len = dstSpan . Length ;
261264
262- for ( var i = 0 ; i < len ; i ++ )
265+ // Use unsafe pointers for maximum speed in the loop
266+ fixed ( uint * pDst = dstSpan )
267+ fixed ( uint * pSrc = srcSpan )
263268 {
264- var s = src [ i ] ;
265- if ( ( s >> 24 ) == 0 )
266- continue ;
269+ uint * dPtr = pDst ;
270+ uint * sPtr = pSrc ;
271+
272+ for ( int i = 0 ; i < len ; i ++ )
273+ {
274+ uint s = * sPtr ;
275+
276+ // Fast check: if source is fully transparent, skip
277+ uint sa = s >> 24 ;
278+ if ( sa == 0 )
279+ {
280+ dPtr ++ ;
281+ sPtr ++ ;
282+ continue ;
283+ }
284+
285+ // Fast check: if source is fully opaque, just overwrite
286+ if ( sa == 255 )
287+ {
288+ * dPtr = s ;
289+ dPtr ++ ;
290+ sPtr ++ ;
291+ continue ;
292+ }
267293
268- var d = dst [ i ] ;
294+ uint d = * dPtr ;
269295
270- var sf = new Vector4 (
271- ( s >> 16 ) & 255 ,
272- ( s >> 8 ) & 255 ,
273- ( s ) & 255 ,
274- ( s >> 24 ) & 255
275- ) ;
296+ // Extract components (0xAARRGGBB format)
297+ // Destination
298+ uint da = d >> 24 ;
299+ uint dr = ( d >> 16 ) & 0xFF ;
300+ uint dg = ( d >> 8 ) & 0xFF ;
301+ uint db = d & 0xFF ;
276302
277- var df = new Vector4 (
278- ( d >> 16 ) & 255 ,
279- ( d >> 8 ) & 255 ,
280- ( d ) & 255 ,
281- ( d >> 24 ) & 255
282- ) ;
303+ // Source
304+ uint sr = ( s >> 16 ) & 0xFF ;
305+ uint sg = ( s >> 8 ) & 0xFF ;
306+ uint sb = s & 0xFF ;
283307
284- var a = sf . W * ( 1f / 255f ) ;
285- if ( a <= 0.0001f )
286- continue ;
308+ // Standard Alpha Blending Formula: Out = (Src * Alpha + Dst * (255 - Alpha)) / 255
309+ // We approximate division by 255 using: (x + 128) / 255 ~= (x * 257 + 128) >> 16
310+ // Or simpler fast approximation: (v + (v >> 8)) >> 8
287311
288- var r = sf * a + df * ( 1f - a ) ;
312+ uint invA = 255 - sa ;
289313
290- dst [ i ] =
291- ( ( uint ) Math . Clamp ( r . W , 0 , 255 ) << 24 ) |
292- ( ( uint ) Math . Clamp ( r . X , 0 , 255 ) << 16 ) |
293- ( ( uint ) Math . Clamp ( r . Y , 0 , 255 ) << 8 ) |
294- ( uint ) Math . Clamp ( r . Z , 0 , 255 ) ;
314+ // Blend Color Channels
315+ uint r = ( sr * sa + dr * invA ) / 255 ;
316+ uint g = ( sg * sa + dg * invA ) / 255 ;
317+ uint b = ( sb * sa + db * invA ) / 255 ;
318+
319+ // Blend Alpha Channel (Standard "Over" operator)
320+ // ResultAlpha = SrcAlpha + DstAlpha * (1 - SrcAlpha)
321+ uint a = sa + ( ( da * invA ) / 255 ) ;
322+
323+ // Re-pack and write
324+ * dPtr = ( a << 24 ) | ( r << 16 ) | ( g << 8 ) | b ;
325+
326+ dPtr ++ ;
327+ sPtr ++ ;
328+ }
295329 }
296330
297331 UpdateBitmapFromBits ( ) ;
298332 }
299333
300-
301334 /// <summary>
302335 /// SIMD-based batch pixel update from (x,y,color) triplets.
303336 /// </summary>
304337 /// <param name="pixels">The pixels.</param>
305- public void SetPixelsSimd ( IEnumerable < ( int x , int y , Color color ) > pixels )
338+ public void SetPixelsBulk ( IEnumerable < ( int x , int y , Color color ) > pixels )
306339 {
307340 foreach ( var ( x , y , c ) in pixels )
308341 {
0 commit comments