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 }