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