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 }