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 }