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