|
| 1 | +/* |
| 2 | + * COPYRIGHT: See COPYING in the top level directory |
| 3 | + * PROJECT: Imaging |
| 4 | + * FILE: DireDirectBitmapCorectBitmap.cs |
| 5 | + * PURPOSE: Shared logic for DirectBitmap and DirectBitmapImage to set and get pixels from the underlying Pixel32 array. |
| 6 | + * PROGRAMER: Peter Geinitz (Wayfarer) |
| 7 | + */ |
| 8 | + |
| 9 | +using System.Collections.Generic; |
| 10 | +using System.Linq; |
| 11 | + |
| 12 | +namespace Imaging |
| 13 | +{ |
| 14 | + /// <summary> |
| 15 | + /// Hopefully this will be the last time we need to duplicate code between DirectBitmap and DirectBitmapImage. |
| 16 | + /// 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. |
| 17 | + /// It works on a Pixel32 array only and does not touch any bitmap object, allowing both DirectBitmap and DirectBitmapImage to use it without duplication. |
| 18 | + /// </summary> |
| 19 | + public static class DirectBitmapCore |
| 20 | + { |
| 21 | + /// <summary> |
| 22 | + /// Sets the pixel. |
| 23 | + /// </summary> |
| 24 | + /// <param name="bits">The bits.</param> |
| 25 | + /// <param name="width">The width.</param> |
| 26 | + /// <param name="height">The height.</param> |
| 27 | + /// <param name="x">The x.</param> |
| 28 | + /// <param name="y">The y.</param> |
| 29 | + /// <param name="color">The color.</param> |
| 30 | + public static void SetPixel(Pixel32[] bits, int width, int height, int x, int y, Pixel32 color) |
| 31 | + { |
| 32 | + if ((uint)x >= width || (uint)y >= height) return; |
| 33 | + |
| 34 | + int index = x + y * width; |
| 35 | + bits[index] = color; |
| 36 | + } |
| 37 | + |
| 38 | + /// <summary> |
| 39 | + /// Gets the pixel. |
| 40 | + /// </summary> |
| 41 | + /// <param name="bits">The bits.</param> |
| 42 | + /// <param name="width">The width.</param> |
| 43 | + /// <param name="height">The height.</param> |
| 44 | + /// <param name="x">The x.</param> |
| 45 | + /// <param name="y">The y.</param> |
| 46 | + /// <returns>Pixel32 Struct for the Coordinate.</returns> |
| 47 | + public static Pixel32 GetPixel(Pixel32[] bits, int width, int height, int x, int y) |
| 48 | + { |
| 49 | + int index = x + y * width; |
| 50 | + return bits[index]; |
| 51 | + } |
| 52 | + |
| 53 | + /// <summary> |
| 54 | + /// Sets the pixels. |
| 55 | + /// </summary> |
| 56 | + /// <param name="bits">The bits.</param> |
| 57 | + /// <param name="width">The width.</param> |
| 58 | + /// <param name="height">The height.</param> |
| 59 | + /// <param name="pixels">The pixels.</param> |
| 60 | + public static void SetPixels(Pixel32[] bits, int width, int height, IEnumerable<PixelData> pixels) |
| 61 | + { |
| 62 | + foreach (var pixel in pixels) |
| 63 | + { |
| 64 | + if ((uint)pixel.X >= width || (uint)pixel.Y >= height) continue; |
| 65 | + |
| 66 | + bits[pixel.Y * width + pixel.X] = |
| 67 | + new Pixel32(pixel.R, pixel.G, pixel.B, pixel.A); |
| 68 | + } |
| 69 | + } |
| 70 | + |
| 71 | + /// <summary> |
| 72 | + /// Sets multiple pixels efficiently using contiguous runs per row. |
| 73 | + /// Works on a Pixel32 array only; does not touch any bitmap object. |
| 74 | + /// </summary> |
| 75 | + /// <param name="bits">The pixel buffer.</param> |
| 76 | + /// <param name="width">The width of the image.</param> |
| 77 | + /// <param name="height">The height of the image.</param> |
| 78 | + /// <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) |
| 80 | + { |
| 81 | + if (bits == null || pixels == null) return; |
| 82 | + |
| 83 | + // Group pixels by row and color to make contiguous writes cache-friendly |
| 84 | + var grouped = pixels |
| 85 | + .Where(p => (uint)p.x < width && (uint)p.y < height) // bounds check |
| 86 | + .GroupBy(p => (p.y, p.color)); |
| 87 | + |
| 88 | + foreach (var group in grouped) |
| 89 | + { |
| 90 | + int y = group.Key.y; |
| 91 | + Pixel32 color = group.Key.color; |
| 92 | + |
| 93 | + // Sort X positions to detect contiguous runs |
| 94 | + var xs = group.Select(p => p.x).Order().ToArray(); |
| 95 | + |
| 96 | + int i = 0; |
| 97 | + while (i < xs.Length) |
| 98 | + { |
| 99 | + int runStart = xs[i]; |
| 100 | + int runLength = 1; |
| 101 | + |
| 102 | + // Detect contiguous sequence |
| 103 | + while (i + runLength < xs.Length && xs[i + runLength] == runStart + runLength) |
| 104 | + runLength++; |
| 105 | + |
| 106 | + int startIndex = runStart + (y * width); |
| 107 | + |
| 108 | + // Scalar write for the run |
| 109 | + for (int offset = 0; offset < runLength; offset++) |
| 110 | + bits[startIndex + offset] = color; |
| 111 | + |
| 112 | + i += runLength; |
| 113 | + } |
| 114 | + } |
| 115 | + } |
| 116 | + } |
| 117 | +} |
| 118 | + |
0 commit comments