1 module polyplex.core.content.textures; 2 import bindbc.sdl; 3 import polyplex.core.color; 4 import polyplex.utils.logging; 5 import polyplex.utils.strutils; 6 import polyplex.core.content.gl; 7 import polyplex.core.render; 8 import polyplex.core.color; 9 import polyplex.math; 10 11 public class TextureImg { 12 private int width; 13 private int height; 14 private ubyte[] pixels; 15 private string i_name; 16 17 public @property int Width() { return width; } 18 public @property int Height() { return height; } 19 public @property ubyte[] Pixels() { return pixels; } 20 public @property string InternalName() { return i_name; } 21 22 this(int width, int height, ubyte[] pixels, string iname = "unnamed") { 23 this.width = width; 24 this.height = height; 25 this.pixels = pixels; 26 this.i_name = iname; 27 } 28 } 29 30 public class Texture2DEffectors { 31 /** 32 Very simple upscaler, width and height gets scaled * <scale> parameter. 33 */ 34 public static Color[][] SimpleUpscale(Color[][] input, uint scale) { 35 Color[][] c = input; 36 37 Color[][] oc; 38 Color[] lookup; 39 40 oc.length = c.length*scale; 41 for (int y = 0; y < oc.length; y++) { 42 43 oc[y].length = c[y/scale].length*scale; 44 lookup = c[y/scale][0..c[y/scale].length]; 45 46 int lx = 0; 47 for (int x = 0; x < oc[y].length; x += scale) { 48 for (int i = 0; i < scale; i++) { 49 50 oc[y][x+i] = lookup[lx]; 51 } 52 lx++; 53 } 54 } 55 return oc; 56 } 57 58 /** 59 Creates a new texture. 60 */ 61 public static Color[][] NewCanvas(uint width, uint height, Color backgroundcolor = Color.Transparent) { 62 Color[][] to_color; 63 to_color.length = height; 64 Logger.Debug("Creating an {0}x{1} empty square texture...", width, height); 65 for (int y = 0; y < height; y++) { 66 to_color[y].length = width; 67 for (int x = 0; x < width; x++) { 68 to_color[y][x] = backgroundcolor; 69 } 70 } 71 return to_color; 72 } 73 74 /** 75 Resizes the canvas that the texture is on. 76 */ 77 public static Color[][] ResizeCanvas(Color[][] from, uint width, uint height) { 78 Color[][] from_pixels = from; 79 Color[][] to_pixels = []; 80 81 int from_height = cast(int)from.length; 82 if (from_height == 0) throw new Exception("Invalid height of 0"); 83 84 int from_width = cast(int)from[0].length; 85 if (from_width == 0) throw new Exception("Invalid width of 0"); 86 87 // Set height. 88 to_pixels.length = height; 89 90 for (int py = 0; py < height; py++) { 91 92 // Make sure that we don't add pixels not supposed to be there. 93 if (py < 0) continue; 94 95 // Set width. 96 to_pixels[py].length = width; 97 98 for (int px = 0; px < width; px++) { 99 100 // Make sure that we don't add pixels not supposed to be there. 101 if (px < 0) continue; 102 103 // Replace out-of-bounds stuff with transparent pixels. 104 if (py >= from_height || px >= from_width) { 105 to_pixels[py][px] = new Color(0, 0, 0, 0); 106 continue; 107 } 108 109 // superimpose the pixels from (start x + current x) and (start y + current y). 110 // (reverse cause arrays are reversed like that.) 111 to_pixels[py][px] = from_pixels[py][px]; 112 } 113 } 114 return to_pixels; 115 } 116 117 /** 118 Returns a sub-image from the image starting at the coordinates extenting to the width and height. 119 */ 120 public static Color[][] GetSubImage(Color[][] from, int x, int y, int width, int height) { 121 Color[][] from_pixels = from; 122 Color[][] to_pixels = []; 123 124 int from_height = cast(int)from.length; 125 if (from_height == 0) throw new Exception("Invalid height of 0"); 126 127 int from_width = cast(int)from[0].length; 128 if (from_width == 0) throw new Exception("Invalid width of 0"); 129 130 // Set height. 131 to_pixels.length = height; 132 133 for (int py = 0; py < height; py++) { 134 135 // Make sure that we don't add pixels not supposed to be there. 136 if (y+py < 0) continue; 137 if (y+py >= from_height) continue; 138 139 // Set width. 140 to_pixels[py].length = width; 141 142 for (int px = 0; px < width; px++) { 143 144 // Make sure that we don't add pixels not supposed to be there. 145 if (x+px < 0) continue; 146 if (x+px >= from_width) continue; 147 148 // superimpose the pixels from (start x + current x) and (start y + current y). 149 // (reverse cause arrays are reversed like that.) 150 to_pixels[py][px] = from_pixels[y+py][x+px]; 151 } 152 } 153 return to_pixels; 154 } 155 156 /** 157 Superimposes <from> to texture <to> and returns the result. 158 */ 159 public static Color[][] Superimpose(Color[][] from, Color[][] to, int x, int y) { 160 Color[][] from_pixels = from; 161 Color[][] to_pixels = to; 162 163 int from_height = cast(int)from.length; 164 if (from_height == 0) throw new Exception("Invalid height of 0"); 165 166 int from_width = cast(int)from[0].length; 167 if (from_width == 0) throw new Exception("Invalid width of 0"); 168 169 int height = cast(int)to.length; 170 if (height == 0) throw new Exception("Invalid height of 0"); 171 172 int width = cast(int)to[0].length; 173 if (width == 0) throw new Exception("Invalid width of 0"); 174 175 176 for (int py = 0; py < from_height; py++) { 177 178 // Make sure that we don't add pixels not supposed to be there. 179 if (y+py < 0) continue; 180 if (y+py >= height) continue; 181 182 for (int px = 0; px < from_width; px++) { 183 184 // Make sure that we don't add pixels not supposed to be there. 185 if (x+px < 0) continue; 186 if (x+px >= width) continue; 187 188 // superimpose the pixels from (start x + current x) and (start y + current y). 189 // (reverse cause arrays are reversed like that.) 190 to_pixels[y+py][x+px] = to_pixels[y+py][x+px].PreMultAlphaBlend(from_pixels[py][px]); 191 } 192 } 193 return to_pixels; 194 } 195 196 /** 197 Very simple upscaler, width and height gets scaled * <scale> parameter. 198 T = the backend to return for. 199 Currently supported: 200 - Gl (default) 201 - Vk 202 */ 203 public static Texture2D SimpleUpscale(string T = "Gl")(Texture2D input, uint scale) { 204 mixin(q{return new {0}Texture2D(SimpleUpscale(input.Pixels, scale));}.Format(T)); 205 } 206 207 208 /** 209 Creates a new texture. 210 T = the backend to return for. 211 Currently supported: 212 - Gl (default) 213 - Vk 214 */ 215 public static Texture2D NewCanvasTex(string T = "Gl")(uint width, uint height, Color backgroundcolor = Color.Transparent) { 216 mixin(q{return new {0}Texture2D(NewCanvas(width, height, backgroundcolor));}.Format(T)); 217 } 218 219 /** 220 Resizes the canvas that the texture is on. 221 T = the backend to return for. 222 Currently supported: 223 - Gl (default) 224 - Vk 225 */ 226 public static Texture2D ResizeCanvas(string T = "Gl")(Texture2D input, uint width, uint height) { 227 mixin(q{return new {0}Texture2D(ResizeCanvas(input.Pixels, width, height));}.Format(T)); 228 } 229 230 /** 231 Returns a sub-image from the image starting at the coordinates extenting to the width and height. 232 T = the backend to return for. 233 Currently supported: 234 - Gl (default) 235 - Vk 236 */ 237 public static Texture2D GetSubImage(string T = "Gl")(Texture2D from, int x, int y, int width, int height) { 238 mixin(q{return new {0}Texture2D(GetSubImage(from.Pixels, x, y, width, height));}.Format(T)); 239 } 240 241 /** 242 Superimposes <from> to texture <to> and returns the result. 243 T = the backend to return for. 244 Currently supported: 245 - Gl (default) 246 - Vk 247 */ 248 public static Texture2D Superimpose(string T = "Gl")(Texture2D from, Texture2D to, int x, int y) { 249 mixin(q{return new {0}Texture2D(Superimpose(from.Pixels, to.Pixels, x, y));}.Format(T)); 250 } 251 252 /** 253 Superimposes <from> to texture <to> and returns the result. 254 T = the backend to return for. 255 Currently supported: 256 - Gl (default) 257 - Vk 258 */ 259 public static Texture2D Superimpose(string T = "Gl")(Color[][] from, Texture2D to, int x, int y) { 260 mixin(q{return new {0}Texture2D(Superimpose(from, to.Pixels, x, y));}.Format(T)); 261 } 262 263 /** 264 Superimposes <from> to texture <to> and returns the result. 265 T = the backend to return for. 266 Currently supported: 267 - Gl (default) 268 - Vk 269 */ 270 public static Texture2D Superimpose(string T = "Gl")(Texture2D from, Color[][] to, int x, int y) { 271 mixin(q{return new {0}Texture2D(Superimpose(from.Pixels, to, x, y));}.Format(T)); 272 } 273 } 274 275 public abstract class Texture2D { 276 protected TextureImg image; 277 public abstract uint Id(); 278 public @property int Width() { return image.Width; } 279 public @property int Height() { return image.Height; } 280 public @property Color[][] Pixels() { 281 Logger.VerboseDebug("Returning image-pixel-color of dimensions {0}x{1}...", image.Width, image.Height); 282 int i = 0; 283 Color[][] o; 284 o.length = image.Height; 285 286 for (int y = 0; y < image.Height; y++) { 287 o[y].length = image.Width; 288 289 for (int x = 0; x < image.Width; x++) { 290 291 o[y][x] = new Color(image.Pixels[i], image.Pixels[i+1], image.Pixels[i+2], image.Pixels[i+3]); 292 i += 4; 293 } 294 } 295 return o; 296 } 297 298 public @property void Pixels(Color[][] col) { 299 ubyte[] colors = []; 300 301 ulong h = col.length; 302 if (h == 0) throw new Exception("Invalid height of 0"); 303 304 ulong w = col[0].length; 305 if (w == 0) throw new Exception("Invalid width of 0"); 306 307 for (int y = 0; y < h; y++) { 308 if (col[y].length != w) throw new Exception("Non square textures not supported!"); 309 for (int x = 0; x < w; x++) { 310 colors ~= col[y][x].ColorBytes; 311 } 312 } 313 314 this.image = new TextureImg(cast(int)w, cast(int)h, colors); 315 this.rebuffer(); 316 } 317 318 this(TextureImg input) { 319 this.image = input; 320 321 // Buffer in to the appropriate graphics API. 322 rebuffer(); 323 } 324 325 this(Color[][] colors) { 326 327 // rebuffer is called from this, just apply the colors as a texture. 328 this.Pixels(colors); 329 } 330 331 this() { } 332 333 public Rectangle Size() { 334 return new Rectangle(0, 0, Width, Height); 335 } 336 337 /** 338 Binds the texture. 339 */ 340 public abstract void Bind(int attach_pos = 0, Shader s = null); 341 342 /** 343 Unbinds the texture. 344 */ 345 public abstract void Unbind(); 346 protected abstract void rebuffer(); 347 }