1 module CPUblit.draw; 2 3 import CPUblit.colorspaces; 4 /** 5 * Draws a line using a fixed point method. Is capable of drawing lines diagonally. 6 */ 7 public void drawLine(T)(int x0, int y0, int x1, int y1, T color, T* dest, size_t destWidth) @nogc nothrow pure { 8 if(x1 < x0){ 9 const int k = x1; 10 x1 = x0; 11 x0 = k; 12 } 13 if(y1 < y0){ 14 const int k = y1; 15 y1 = y0; 16 y0 = k; 17 } 18 const int dx = x1 - x0; 19 const int dy = y1 - y0; 20 if(!dx || !dy){ 21 if(!dy){ 22 sizediff_t offset = destWidth * y1; 23 for(int x = x0; x <= x1; x++){ 24 dest[offset + x] = color; 25 } 26 }else{ 27 sizediff_t offset = destWidth * y0 + x0; 28 for(int y = y0; y <= y1; y++){ 29 dest[offset] = color; 30 offset += destWidth; 31 } 32 } 33 }else if(dx>=dy){ 34 int D = 2*dy - dx; 35 int y = y0; 36 for(int x = x0 ; x <= x1 ; x++){ 37 dest[destWidth * y + x] = color; 38 if(D > 0){ 39 y += 1; 40 D -= 2 * dx; 41 } 42 D += 2 * dx; 43 } 44 }else{ 45 int D = 2 * dx - dy; 46 int x = x0; 47 for(int y = y0 ; y <= y1 ; y++){ 48 dest[destWidth * y + x] = color; 49 if(D > 0){ 50 x += 1; 51 D -= 2 * dy; 52 } 53 D += 2 * dy; 54 } 55 } 56 } 57 /** 58 * Draws a line with the given pattern. 59 */ 60 public void drawLinePattern(T)(int x0, int y0, int x1, int y1, T[] pattern, T* dest, size_t destWidth) @nogc nothrow pure { 61 if(x1 < x0){ 62 const int k = x1; 63 x1 = x0; 64 x0 = k; 65 } 66 if(y1 < y0){ 67 const int k = y1; 68 y1 = y0; 69 y0 = k; 70 } 71 const int dx = x1 - x0; 72 const int dy = y1 - y0; 73 size_t patternPos; 74 if(!dx || !dy){ 75 if(!dy){ 76 sizediff_t offset = destWidth * y1; 77 for(int x = x0 ; x <= x1 ; x++){ 78 dest[offset + x] = pattern[(patternPos++) % pattern.length]; 79 80 } 81 }else{ 82 sizediff_t offset = destWidth * y0 + x0; 83 for(int y = y0 ; y <= y1 ; y++){ 84 dest[offset] = pattern[(patternPos++) % pattern.length]; 85 86 offset += destWidth; 87 } 88 } 89 }else if(dx>=dy){ 90 int D = 2 * dy - dx; 91 int y = y0; 92 for(int x = x0 ; x <= x1 ; x++){ 93 dest[destWidth * y + x] = pattern[(patternPos++) % pattern.length]; 94 //patternPos = patternPos + 1 < pattern.length ? patternPos + 1 : 0; 95 if(D > 0){ 96 y += 1; 97 D -= 2 * dx; 98 } 99 D += 2 * dx; 100 } 101 }else{ 102 int D = 2 * dx - dy; 103 int x = x0; 104 for(int y = y0 ; y <= y1 ; y++){ 105 dest[destWidth * y + x] = pattern[(patternPos++) % pattern.length]; 106 //patternPos = patternPos + 1 < pattern.length ? patternPos + 1 : 0; 107 if(D > 0){ 108 x += 1; 109 D -= 2 * dy; 110 } 111 D += 2 * dy; 112 } 113 } 114 } 115 /** 116 * Draws a rectangle. 117 */ 118 public void drawRectangle(T)(int x0, int y0, int x1, int y1, T color, T*dest, size_t destWidth) @nogc nothrow pure { 119 /+static if(!(T.stringof == "ubyte" || T.stringof == "ushort" || T.stringof == "uint" || T.stringof == "Color32Bit")) { 120 static assert(0, "Template parameter '" ~ T.stringof ~ "' is not supported!"); 121 }+/ 122 drawLine(x0,y0,x0,y1,color,dest,destWidth); 123 drawLine(x0,y0,x1,y0,color,dest,destWidth); 124 drawLine(x1,y0,x1,y1,color,dest,destWidth); 125 drawLine(x0,y1,x1,y1,color,dest,destWidth); 126 } 127 /** 128 * Draws a filled rectangle. 129 */ 130 public void drawFilledRectangle(T) (int x0, int y0, int x1, int y1, T color, T* dest, size_t destWidth) @nogc nothrow 131 pure { 132 import core.stdc..string : memset; 133 import core.stdc.wchar_ : wmemset; 134 if(x1 < x0){ 135 const int k = x1; 136 x1 = x0; 137 x0 = k; 138 } 139 if(y1 < y0){ 140 const int k = y1; 141 y1 = y0; 142 y0 = k; 143 } 144 int width = x1 - x0; 145 dest += x0; 146 for (int y = y0 ; y <= y1 ; y++) { 147 static if (is(T == ubyte)) { 148 memset(dest + (y * destWidth), color, width); 149 } else static if (is(T == ushort)) { 150 T* dest0 = dest + (y * destWidth); 151 for (int x ; x < width ; x++) { 152 dest0[x] = color; 153 } 154 } else static if (is(T == uint)) { 155 wmemset(dest + (y * destWidth), color, width); 156 } 157 } 158 } 159 /** 160 * Flood fills a bitmap at the given point. 161 */ 162 public void floodFill(T)(int x0, int y0, T color, T* dest, size_t destWidth, size_t destLength, 163 T transparencyIndex = T.init) @nogc nothrow pure { 164 //check for boundaries of the bitmap 165 if(x0 > 0 && y0 > 0){ 166 const size_t yOffset = y0 * destWidth; 167 if(x0 < destWidth && yOffset < destLength){ 168 //check if the current pixel is "transparent" 169 T* p = dest + yOffset + x0; 170 if(transparencyIndex == *(p)){ 171 *p = color; 172 floodFill(x0 + 1, y0, color, dest, destWidth, destLength, transparencyIndex); 173 floodFill(x0 - 1, y0, color, dest, destWidth, destLength, transparencyIndex); 174 floodFill(x0, y0 + 1, color, dest, destWidth, destLength, transparencyIndex); 175 floodFill(x0, y0 - 1, color, dest, destWidth, destLength, transparencyIndex); 176 } 177 } 178 } 179 } 180 unittest{ 181 import std.conv : to; 182 { //test if only the first line is being drawn. 183 ubyte[256*256] virtualImage; 184 drawLine(0, 0, 255, 0, 0xFF, virtualImage.ptr, 256); 185 for(int x ; x < 256 ; x++){ 186 assert(virtualImage[x] == 0xFF); 187 } 188 for(int x ; x < 256 ; x++){ 189 assert(virtualImage[256 + x] == 0x00); 190 } 191 } 192 { //test if only the first row is being drawn. 193 ubyte[256*256] virtualImage; 194 drawLine(0, 0, 0, 255, 0xFF, virtualImage.ptr, 256); 195 for(int y ; y < 256 ; y++){ 196 assert(virtualImage[256 * y] == 0xFF); 197 } 198 for(int y ; y < 256 ; y++){ 199 assert(virtualImage[(256 * y) + 1] == 0x00); 200 } 201 } 202 { //test if only the first line is being drawn. 203 ubyte[256*256] virtualImage; 204 ubyte[3] pattern = [0x20,0xFF,0x33]; 205 drawLinePattern(0, 0, 255, 0, pattern, virtualImage.ptr, 256); 206 for(int x ; x < 256 ; x++){ 207 assert(virtualImage[x] == pattern[x%3], "Error at position " ~ to!string(x)); 208 } 209 for(int x ; x < 256 ; x++){ 210 assert(virtualImage[256 + x] == 0x00); 211 } 212 } 213 214 }