1/*
2 * Copyright 2010 Mario Zechner (contact@badlogicgames.com), Nathan Sweet (admin@esotericsoftware.com)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
5 * License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
10 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
11 * governing permissions and limitations under the License.
12 */
13#include "gdx2d.h"
14#include <stdlib.h>
15#define STB_IMAGE_IMPLEMENTATION
16#define STBI_NO_FAILURE_STRINGS
17#include "stb_image.h"
18#include "jpgd_c.h"
19
20static uint32_t gdx2d_blend = GDX2D_BLEND_NONE;
21static uint32_t gdx2d_scale = GDX2D_SCALE_NEAREST;
22
23static uint32_t* lu4 = 0;
24static uint32_t* lu5 = 0;
25static uint32_t* lu6 = 0;
26
27typedef void(*set_pixel_func)(unsigned char* pixel_addr, uint32_t color);
28typedef uint32_t(*get_pixel_func)(unsigned char* pixel_addr);
29
30static inline void generate_look_ups() {
31	uint32_t i = 0;
32	lu4 = malloc(sizeof(uint32_t) * 16);
33	lu5 = malloc(sizeof(uint32_t) * 32);
34	lu6 = malloc(sizeof(uint32_t) * 64);
35
36	for(i = 0; i < 16; i++) {
37		lu4[i] = (uint32_t) i / 15.0f * 255;
38		lu5[i] = (uint32_t) i / 31.0f * 255;
39		lu6[i] = (uint32_t) i / 63.0f * 255;
40	}
41
42	for(i = 16; i < 32; i++) {
43		lu5[i] = (uint32_t) i / 31.0f * 255;
44		lu6[i] = (uint32_t) i / 63.0f * 255;
45	}
46
47	for(i = 32; i < 64; i++) {
48		lu6[i] = (uint32_t) i / 63.0f * 255;
49	}
50}
51
52static inline uint32_t to_format(uint32_t format, uint32_t color) {
53	uint32_t r, g, b, a, l;
54
55	switch(format) {
56		case GDX2D_FORMAT_ALPHA:
57			return color & 0xff;
58		case GDX2D_FORMAT_LUMINANCE_ALPHA:
59			r = (color & 0xff000000) >> 24;
60			g = (color & 0xff0000) >> 16;
61			b = (color & 0xff00) >> 8;
62			a = (color & 0xff);
63			l = ((uint32_t)(0.2126f * r + 0.7152 * g + 0.0722 * b) & 0xff) << 8;
64			return (l & 0xffffff00) | a;
65		case GDX2D_FORMAT_RGB888:
66			return color >> 8;
67		case GDX2D_FORMAT_RGBA8888:
68			return color;
69		case GDX2D_FORMAT_RGB565:
70			r = (((color & 0xff000000) >> 27) << 11) & 0xf800;
71			g = (((color & 0xff0000) >> 18) << 5) & 0x7e0;
72			b = ((color & 0xff00) >> 11) & 0x1f;
73			return r | g | b;
74		case GDX2D_FORMAT_RGBA4444:
75			r = (((color & 0xff000000) >> 28) << 12) & 0xf000;
76			g = (((color & 0xff0000) >> 20) << 8) & 0xf00;
77			b = (((color & 0xff00) >> 12) << 4) & 0xf0;
78			a = ((color & 0xff) >> 4) & 0xf;
79			return r | g | b | a;
80		default:
81			return 0;
82	}
83}
84
85#define min(a, b) (a > b?b:a)
86
87static inline uint32_t weight_RGBA8888(uint32_t color, float weight) {
88	uint32_t r, g, b, a;
89	r = min((uint32_t)(((color & 0xff000000) >> 24) * weight), 255);
90	g = min((uint32_t)(((color & 0xff0000) >> 16) * weight), 255);
91	b = min((uint32_t)(((color & 0xff00) >> 8) * weight), 255);
92	a = min((uint32_t)(((color & 0xff)) * weight), 255);
93
94	return (r << 24) | (g << 16) | (b << 8) | a;
95}
96
97static inline uint32_t to_RGBA8888(uint32_t format, uint32_t color) {
98	uint32_t r, g, b, a;
99
100	if(!lu5) generate_look_ups();
101
102	switch(format) {
103		case GDX2D_FORMAT_ALPHA:
104			return (color & 0xff) | 0xffffff00;
105		case GDX2D_FORMAT_LUMINANCE_ALPHA:
106			return ((color & 0xff00) << 16) | ((color & 0xff00) << 8) | (color & 0xffff);
107		case GDX2D_FORMAT_RGB888:
108			return (color << 8) | 0x000000ff;
109		case GDX2D_FORMAT_RGBA8888:
110			return color;
111		case GDX2D_FORMAT_RGB565:
112			r = lu5[(color & 0xf800) >> 11] << 24;
113			g = lu6[(color & 0x7e0) >> 5] << 16;
114			b = lu5[(color & 0x1f)] << 8;
115			return r | g | b | 0xff;
116		case GDX2D_FORMAT_RGBA4444:
117			r = lu4[(color & 0xf000) >> 12] << 24;
118			g = lu4[(color & 0xf00) >> 8] << 16;
119			b = lu4[(color & 0xf0) >> 4] << 8;
120			a = lu4[(color & 0xf)];
121			return r | g | b | a;
122		default:
123			return 0;
124	}
125}
126
127static inline void set_pixel_alpha(unsigned char *pixel_addr, uint32_t color) {
128	*pixel_addr = (unsigned char)(color & 0xff);
129}
130
131static inline void set_pixel_luminance_alpha(unsigned char *pixel_addr, uint32_t color) {
132	*(unsigned short*)pixel_addr = (unsigned short)color;
133}
134
135static inline void set_pixel_RGB888(unsigned char *pixel_addr, uint32_t color) {
136	//*(unsigned short*)pixel_addr = (unsigned short)(((color & 0xff0000) >> 16) | (color & 0xff00));
137	pixel_addr[0] = (color & 0xff0000) >> 16;
138	pixel_addr[1] = (color & 0xff00) >> 8;
139	pixel_addr[2] = (color & 0xff);
140}
141
142static inline void set_pixel_RGBA8888(unsigned char *pixel_addr, uint32_t color) {
143	*(uint32_t*)pixel_addr = ((color & 0xff000000) >> 24) |
144							((color & 0xff0000) >> 8) |
145							((color & 0xff00) << 8) |
146							((color & 0xff) << 24);
147}
148
149static inline void set_pixel_RGB565(unsigned char *pixel_addr, uint32_t color) {
150	*(uint16_t*)pixel_addr = (uint16_t)(color);
151}
152
153static inline void set_pixel_RGBA4444(unsigned char *pixel_addr, uint32_t color) {
154	*(uint16_t*)pixel_addr = (uint16_t)(color);
155}
156
157static inline set_pixel_func set_pixel_func_ptr(uint32_t format) {
158	switch(format) {
159		case GDX2D_FORMAT_ALPHA:			return &set_pixel_alpha;
160		case GDX2D_FORMAT_LUMINANCE_ALPHA:	return &set_pixel_luminance_alpha;
161		case GDX2D_FORMAT_RGB888:			return &set_pixel_RGB888;
162		case GDX2D_FORMAT_RGBA8888:			return &set_pixel_RGBA8888;
163		case GDX2D_FORMAT_RGB565:			return &set_pixel_RGB565;
164		case GDX2D_FORMAT_RGBA4444:			return &set_pixel_RGBA4444;
165		default: return &set_pixel_alpha; // better idea for a default?
166	}
167}
168
169static inline uint32_t blend(uint32_t src, uint32_t dst) {
170	int32_t src_r = (src & 0xff000000) >> 24;
171	int32_t src_g = (src & 0xff0000) >> 16;
172	int32_t src_b = (src & 0xff00) >> 8;
173	int32_t src_a = (src & 0xff);
174
175	int32_t dst_r = (dst & 0xff000000) >> 24;
176	int32_t dst_g = (dst & 0xff0000) >> 16;
177	int32_t dst_b = (dst & 0xff00) >> 8;
178	int32_t dst_a = (dst & 0xff);
179
180	dst_r = dst_r + src_a * (src_r - dst_r) / 255;
181	dst_g = dst_g + src_a * (src_g - dst_g) / 255;
182	dst_b = dst_b + src_a * (src_b - dst_b) / 255;
183	dst_a = (int32_t)((1.0f - (1.0f - src_a / 255.0f) * (1.0f - dst_a / 255.0f)) * 255);
184	return (uint32_t)((dst_r << 24) | (dst_g << 16) | (dst_b << 8) | dst_a);
185}
186
187static inline uint32_t get_pixel_alpha(unsigned char *pixel_addr) {
188	return *pixel_addr;
189}
190
191static inline uint32_t get_pixel_luminance_alpha(unsigned char *pixel_addr) {
192	return (((uint32_t)pixel_addr[0]) << 8) | pixel_addr[1];
193}
194
195static inline uint32_t get_pixel_RGB888(unsigned char *pixel_addr) {
196	return (((uint32_t)pixel_addr[0]) << 16) | (((uint32_t)pixel_addr[1]) << 8) | (pixel_addr[2]);
197}
198
199static inline uint32_t get_pixel_RGBA8888(unsigned char *pixel_addr) {
200	return (((uint32_t)pixel_addr[0]) << 24) | (((uint32_t)pixel_addr[1]) << 16) | (((uint32_t)pixel_addr[2]) << 8) | pixel_addr[3];
201}
202
203static inline uint32_t get_pixel_RGB565(unsigned char *pixel_addr) {
204	return *(uint16_t*)pixel_addr;
205}
206
207static inline uint32_t get_pixel_RGBA4444(unsigned char *pixel_addr) {
208	return *(uint16_t*)pixel_addr;
209}
210
211static inline get_pixel_func get_pixel_func_ptr(uint32_t format) {
212	switch(format) {
213		case GDX2D_FORMAT_ALPHA:			return &get_pixel_alpha;
214		case GDX2D_FORMAT_LUMINANCE_ALPHA:	return &get_pixel_luminance_alpha;
215		case GDX2D_FORMAT_RGB888:			return &get_pixel_RGB888;
216		case GDX2D_FORMAT_RGBA8888:			return &get_pixel_RGBA8888;
217		case GDX2D_FORMAT_RGB565:			return &get_pixel_RGB565;
218		case GDX2D_FORMAT_RGBA4444:			return &get_pixel_RGBA4444;
219		default: return &get_pixel_alpha; // better idea for a default?
220	}
221}
222
223gdx2d_pixmap* gdx2d_load(const unsigned char *buffer, uint32_t len) {
224	int32_t width, height, format;
225
226	const unsigned char* pixels = stbi_load_from_memory(buffer, len, &width, &height, &format, 0);
227	if (pixels == NULL) {
228		pixels = jpgd_decompress_jpeg_image_from_memory(buffer, len, &width, &height, &format, 3);
229	}
230	if (pixels == NULL)
231		return NULL;
232
233	gdx2d_pixmap* pixmap = (gdx2d_pixmap*)malloc(sizeof(gdx2d_pixmap));
234	if (!pixmap) return 0;
235	pixmap->width = (uint32_t)width;
236	pixmap->height = (uint32_t)height;
237	pixmap->format = (uint32_t)format;
238	pixmap->pixels = pixels;
239	return pixmap;
240}
241
242uint32_t gdx2d_bytes_per_pixel(uint32_t format) {
243	switch(format) {
244		case GDX2D_FORMAT_ALPHA:
245			return 1;
246		case GDX2D_FORMAT_LUMINANCE_ALPHA:
247		case GDX2D_FORMAT_RGB565:
248		case GDX2D_FORMAT_RGBA4444:
249			return 2;
250		case GDX2D_FORMAT_RGB888:
251			return 3;
252		case GDX2D_FORMAT_RGBA8888:
253			return 4;
254		default:
255			return 4;
256	}
257}
258
259gdx2d_pixmap* gdx2d_new(uint32_t width, uint32_t height, uint32_t format) {
260	gdx2d_pixmap* pixmap = (gdx2d_pixmap*)malloc(sizeof(gdx2d_pixmap));
261	if (!pixmap) return 0;
262	pixmap->width = width;
263	pixmap->height = height;
264	pixmap->format = format;
265	pixmap->pixels = (unsigned char*)malloc(width * height * gdx2d_bytes_per_pixel(format));
266	if (!pixmap->pixels) {
267		free((void*)pixmap);
268		return 0;
269	}
270	return pixmap;
271}
272void gdx2d_free(const gdx2d_pixmap* pixmap) {
273	free((void*)pixmap->pixels);
274	free((void*)pixmap);
275}
276
277void gdx2d_set_blend (uint32_t blend) {
278	gdx2d_blend = blend;
279}
280
281void gdx2d_set_scale (uint32_t scale) {
282	gdx2d_scale = scale;
283}
284
285const char *gdx2d_get_failure_reason(void) {
286	if (stbi_failure_reason())
287		return stbi_failure_reason();
288    return jpgd_failure_reason();
289}
290
291static inline void clear_alpha(const gdx2d_pixmap* pixmap, uint32_t col) {
292	int pixels = pixmap->width * pixmap->height;
293	memset((void*)pixmap->pixels, col, pixels);
294}
295
296static inline void clear_luminance_alpha(const gdx2d_pixmap* pixmap, uint32_t col) {
297	int pixels = pixmap->width * pixmap->height;
298	unsigned short* ptr = (unsigned short*)pixmap->pixels;
299	unsigned short l = (col & 0xff) << 8 | (col >> 8);
300
301	for(; pixels > 0; pixels--) {
302		*ptr = l;
303		ptr++;
304	}
305}
306
307static inline void clear_RGB888(const gdx2d_pixmap* pixmap, uint32_t col) {
308	int pixels = pixmap->width * pixmap->height;
309	unsigned char* ptr = (unsigned char*)pixmap->pixels;
310	unsigned char r = (col & 0xff0000) >> 16;
311	unsigned char g = (col & 0xff00) >> 8;
312	unsigned char b = (col & 0xff);
313
314	for(; pixels > 0; pixels--) {
315		*ptr = r;
316		ptr++;
317		*ptr = g;
318		ptr++;
319		*ptr = b;
320		ptr++;
321	}
322}
323
324static inline void clear_RGBA8888(const gdx2d_pixmap* pixmap, uint32_t col) {
325	int pixels = pixmap->width * pixmap->height;
326	uint32_t* ptr = (uint32_t*)pixmap->pixels;
327	unsigned char r = (col & 0xff000000) >> 24;
328	unsigned char g = (col & 0xff0000) >> 16;
329	unsigned char b = (col & 0xff00) >> 8;
330	unsigned char a = (col & 0xff);
331	col = (a << 24) | (b << 16) | (g << 8) | r;
332
333	for(; pixels > 0; pixels--) {
334		*ptr = col;
335		ptr++;
336	}
337}
338
339static inline void clear_RGB565(const gdx2d_pixmap* pixmap, uint32_t col) {
340	int pixels = pixmap->width * pixmap->height;
341	unsigned short* ptr = (unsigned short*)pixmap->pixels;
342	unsigned short l = col & 0xffff;
343
344	for(; pixels > 0; pixels--) {
345		*ptr = l;
346		ptr++;
347	}
348}
349
350static inline void clear_RGBA4444(const gdx2d_pixmap* pixmap, uint32_t col) {
351	int pixels = pixmap->width * pixmap->height;
352	unsigned short* ptr = (unsigned short*)pixmap->pixels;
353	unsigned short l = col & 0xffff;
354
355	for(; pixels > 0; pixels--) {
356		*ptr = l;
357		ptr++;
358	}
359}
360
361void gdx2d_clear(const gdx2d_pixmap* pixmap, uint32_t col) {
362	col = to_format(pixmap->format, col);
363
364	switch(pixmap->format) {
365		case GDX2D_FORMAT_ALPHA:
366			clear_alpha(pixmap, col);
367			break;
368		case GDX2D_FORMAT_LUMINANCE_ALPHA:
369			clear_luminance_alpha(pixmap, col);
370			break;
371		case GDX2D_FORMAT_RGB888:
372			clear_RGB888(pixmap, col);
373			break;
374		case GDX2D_FORMAT_RGBA8888:
375			clear_RGBA8888(pixmap, col);
376			break;
377		case GDX2D_FORMAT_RGB565:
378			clear_RGB565(pixmap, col);
379			break;
380		case GDX2D_FORMAT_RGBA4444:
381			clear_RGBA4444(pixmap, col);
382			break;
383		default:
384			break;
385	}
386}
387
388static inline int32_t in_pixmap(const gdx2d_pixmap* pixmap, int32_t x, int32_t y) {
389	if(x < 0 || y < 0)
390		return 0;
391	if(x >= pixmap->width || y >= pixmap->height)
392		return 0;
393	return -1;
394}
395
396static inline void set_pixel(unsigned char* pixels, uint32_t width, uint32_t height, uint32_t bpp, set_pixel_func pixel_func, int32_t x, int32_t y, uint32_t col) {
397	if(x < 0 || y < 0) return;
398	if(x >= (int32_t)width || y >= (int32_t)height) return;
399	pixels = pixels + (x + width * y) * bpp;
400	pixel_func(pixels, col);
401}
402
403uint32_t gdx2d_get_pixel(const gdx2d_pixmap* pixmap, int32_t x, int32_t y) {
404	if(!in_pixmap(pixmap, x, y))
405		return 0;
406	unsigned char* ptr = (unsigned char*)pixmap->pixels + (x + pixmap->width * y) * gdx2d_bytes_per_pixel(pixmap->format);
407	return to_RGBA8888(pixmap->format, get_pixel_func_ptr(pixmap->format)(ptr));
408}
409
410void gdx2d_set_pixel(const gdx2d_pixmap* pixmap, int32_t x, int32_t y, uint32_t col) {
411	if(gdx2d_blend) {
412		uint32_t dst = gdx2d_get_pixel(pixmap, x, y);
413		col = blend(col, dst);
414		col = to_format(pixmap->format, col);
415		set_pixel((unsigned char*)pixmap->pixels, pixmap->width, pixmap->height, gdx2d_bytes_per_pixel(pixmap->format), set_pixel_func_ptr(pixmap->format), x, y, col);
416	} else {
417		col = to_format(pixmap->format, col);
418		set_pixel((unsigned char*)pixmap->pixels, pixmap->width, pixmap->height, gdx2d_bytes_per_pixel(pixmap->format), set_pixel_func_ptr(pixmap->format), x, y, col);
419	}
420}
421
422void gdx2d_draw_line(const gdx2d_pixmap* pixmap, int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t col) {
423    int32_t dy = y1 - y0;
424    int32_t dx = x1 - x0;
425	int32_t fraction = 0;
426    int32_t stepx, stepy;
427	unsigned char* ptr = (unsigned char*)pixmap->pixels;
428	uint32_t bpp = gdx2d_bytes_per_pixel(pixmap->format);
429	set_pixel_func pset = set_pixel_func_ptr(pixmap->format);
430	get_pixel_func pget = get_pixel_func_ptr(pixmap->format);
431	uint32_t col_format = to_format(pixmap->format, col);
432	void* addr = ptr + (x0 + y0 * pixmap->width) * bpp;
433
434    if (dy < 0) { dy = -dy;  stepy = -1; } else { stepy = 1; }
435    if (dx < 0) { dx = -dx;  stepx = -1; } else { stepx = 1; }
436    dy <<= 1;
437    dx <<= 1;
438
439    if(in_pixmap(pixmap, x0, y0)) {
440    	if(gdx2d_blend) {
441    		col_format = to_format(pixmap->format, blend(col, to_RGBA8888(pixmap->format, pget(addr))));
442    	}
443    	pset(addr, col_format);
444    }
445    if (dx > dy) {
446        fraction = dy - (dx >> 1);
447        while (x0 != x1) {
448            if (fraction >= 0) {
449                y0 += stepy;
450                fraction -= dx;
451            }
452            x0 += stepx;
453            fraction += dy;
454			if(in_pixmap(pixmap, x0, y0)) {
455				addr = ptr + (x0 + y0 * pixmap->width) * bpp;
456				if(gdx2d_blend) {
457					col_format = to_format(pixmap->format, blend(col, to_RGBA8888(pixmap->format, pget(addr))));
458				}
459				pset(addr, col_format);
460			}
461        }
462    } else {
463		fraction = dx - (dy >> 1);
464		while (y0 != y1) {
465			if (fraction >= 0) {
466				x0 += stepx;
467				fraction -= dy;
468			}
469			y0 += stepy;
470			fraction += dx;
471			if(in_pixmap(pixmap, x0, y0)) {
472				addr = ptr + (x0 + y0 * pixmap->width) * bpp;
473				if(gdx2d_blend) {
474					col_format = to_format(pixmap->format, blend(col, to_RGBA8888(pixmap->format, pget(addr))));
475				}
476				pset(addr, col_format);
477			}
478		}
479	}
480}
481
482static inline void hline(const gdx2d_pixmap* pixmap, int32_t x1, int32_t x2, int32_t y, uint32_t col) {
483	int32_t tmp = 0;
484	set_pixel_func pset = set_pixel_func_ptr(pixmap->format);
485	get_pixel_func pget = get_pixel_func_ptr(pixmap->format);
486	unsigned char* ptr = (unsigned char*)pixmap->pixels;
487	uint32_t bpp = gdx2d_bytes_per_pixel(pixmap->format);
488	uint32_t col_format = to_format(pixmap->format, col);
489
490	if(y < 0 || y >= (int32_t)pixmap->height) return;
491
492	if(x1 > x2) {
493		tmp = x1;
494		x1 = x2;
495		x2 = tmp;
496	}
497
498	if(x1 >= (int32_t)pixmap->width) return;
499	if(x2 < 0)  return;
500
501	if(x1 < 0) x1 = 0;
502	if(x2 >= (int32_t)pixmap->width) x2 = pixmap->width - 1;
503	x2 += 1;
504
505	ptr += (x1 + y * pixmap->width) * bpp;
506
507	while(x1 != x2) {
508		if(gdx2d_blend) {
509			col_format = to_format(pixmap->format, blend(col, to_RGBA8888(pixmap->format, pget(ptr))));
510		}
511		pset(ptr, col_format);
512		x1++;
513		ptr += bpp;
514	}
515}
516
517static inline void vline(const gdx2d_pixmap* pixmap, int32_t y1, int32_t y2, int32_t x, uint32_t col) {
518	int32_t tmp = 0;
519	set_pixel_func pset = set_pixel_func_ptr(pixmap->format);
520	get_pixel_func pget = get_pixel_func_ptr(pixmap->format);
521	unsigned char* ptr = (unsigned char*)pixmap->pixels;
522	uint32_t bpp = gdx2d_bytes_per_pixel(pixmap->format);
523	uint32_t stride = bpp * pixmap->width;
524	uint32_t col_format = to_format(pixmap->format, col);
525
526	if(x < 0 || x >= pixmap->width) return;
527
528	if(y1 > y2) {
529		tmp = y1;
530		y1 = y2;
531		y2 = tmp;
532	}
533
534	if(y1 >= (int32_t)pixmap->height) return;
535	if(y2 < 0) return;
536
537	if(y1 < 0) y1 = 0;
538	if(y2 >= (int32_t)pixmap->height) y2 = pixmap->height - 1;
539	y2 += 1;
540
541	ptr += (x + y1 * pixmap->width) * bpp;
542
543	while(y1 != y2) {
544		if(gdx2d_blend) {
545			col_format = to_format(pixmap->format, blend(col, to_RGBA8888(pixmap->format, pget(ptr))));
546		}
547		pset(ptr, col_format);
548		y1++;
549		ptr += stride;
550	}
551}
552
553void gdx2d_draw_rect(const gdx2d_pixmap* pixmap, int32_t x, int32_t y, uint32_t width, uint32_t height, uint32_t col) {
554	hline(pixmap, x, x + width - 1, y, col);
555	hline(pixmap, x, x + width - 1, y + height - 1, col);
556	vline(pixmap, y, y + height - 1, x, col);
557	vline(pixmap, y, y + height - 1, x + width - 1, col);
558}
559
560static inline void circle_points(unsigned char* pixels, uint32_t width, uint32_t height, uint32_t bpp, set_pixel_func pixel_func, int32_t cx, int32_t cy, int32_t x, int32_t y, uint32_t col) {
561    if (x == 0) {
562        set_pixel(pixels, width, height, bpp, pixel_func, cx, cy + y, col);
563        set_pixel(pixels, width, height, bpp, pixel_func, cx, cy - y, col);
564        set_pixel(pixels, width, height, bpp, pixel_func, cx + y, cy, col);
565        set_pixel(pixels, width, height, bpp, pixel_func, cx - y, cy, col);
566    } else
567    if (x == y) {
568        set_pixel(pixels, width, height, bpp, pixel_func, cx + x, cy + y, col);
569        set_pixel(pixels, width, height, bpp, pixel_func, cx - x, cy + y, col);
570        set_pixel(pixels, width, height, bpp, pixel_func, cx + x, cy - y, col);
571        set_pixel(pixels, width, height, bpp, pixel_func, cx - x, cy - y, col);
572    } else
573    if (x < y) {
574        set_pixel(pixels, width, height, bpp, pixel_func, cx + x, cy + y, col);
575        set_pixel(pixels, width, height, bpp, pixel_func, cx - x, cy + y, col);
576        set_pixel(pixels, width, height, bpp, pixel_func, cx + x, cy - y, col);
577        set_pixel(pixels, width, height, bpp, pixel_func, cx - x, cy - y, col);
578        set_pixel(pixels, width, height, bpp, pixel_func, cx + y, cy + x, col);
579        set_pixel(pixels, width, height, bpp, pixel_func, cx - y, cy + x, col);
580        set_pixel(pixels, width, height, bpp, pixel_func, cx + y, cy - x, col);
581        set_pixel(pixels, width, height, bpp, pixel_func, cx - y, cy - x, col);
582    }
583}
584
585void gdx2d_draw_circle(const gdx2d_pixmap* pixmap, int32_t x, int32_t y, uint32_t radius, uint32_t col) {
586    int32_t px = 0;
587    int32_t py = radius;
588    int32_t p = (5 - (int32_t)radius*4)/4;
589	unsigned char* pixels = (unsigned char*)pixmap->pixels;
590	uint32_t width = pixmap->width;
591	uint32_t height = pixmap->height;
592	uint32_t bpp = gdx2d_bytes_per_pixel(pixmap->format);
593	set_pixel_func pixel_func = set_pixel_func_ptr(pixmap->format);
594	col = to_format(pixmap->format, col);
595
596    circle_points(pixels, width, height, bpp, pixel_func, x, y, px, py, col);
597    while (px < py) {
598        px++;
599        if (p < 0) {
600            p += 2*px+1;
601        } else {
602            py--;
603            p += 2*(px-py)+1;
604        }
605        circle_points(pixels, width, height, bpp, pixel_func, x, y, px, py, col);
606    }
607}
608
609void gdx2d_fill_rect(const gdx2d_pixmap* pixmap, int32_t x, int32_t y, uint32_t width, uint32_t height, uint32_t col) {
610	int32_t x2 = x + width - 1;
611	int32_t y2 = y + height - 1;
612
613	if(x >= (int32_t)pixmap->width) return;
614	if(y >= (int32_t)pixmap->height) return;
615	if(x2 < 0) return;
616	if(y2 < 0) return;
617
618	if(x < 0) x = 0;
619	if(y < 0) y = 0;
620	if(x2 >= (int32_t)pixmap->width) x2 = pixmap->width - 1;
621	if(y2 >= (int32_t)pixmap->height) y2 = pixmap->height - 1;
622
623	y2++;
624	while(y!=y2) {
625		hline(pixmap, x, x2, y, col);
626		y++;
627	}
628}
629
630void gdx2d_fill_circle(const gdx2d_pixmap* pixmap, int32_t x0, int32_t y0, uint32_t radius, uint32_t col) {
631	int32_t f = 1 - (int32_t)radius;
632	int32_t ddF_x = 1;
633	int32_t ddF_y = -2 * (int32_t)radius;
634	int32_t px = 0;
635	int32_t py = (int32_t)radius;
636
637	hline(pixmap, x0, x0, y0 + (int32_t)radius, col);
638	hline(pixmap, x0, x0, y0 - (int32_t)radius, col);
639	hline(pixmap, x0 - (int32_t)radius, x0 + (int32_t)radius, y0, col);
640
641
642	while(px < py)
643	{
644		if(f >= 0)
645		{
646			py--;
647			ddF_y += 2;
648			f += ddF_y;
649		}
650		px++;
651		ddF_x += 2;
652		f += ddF_x;
653		hline(pixmap, x0 - px, x0 + px, y0 + py, col);
654		hline(pixmap, x0 - px, x0 + px, y0 - py, col);
655		hline(pixmap, x0 - py, x0 + py, y0 + px, col);
656		hline(pixmap, x0 - py, x0 + py, y0 - px, col);
657	}
658}
659
660#define max(a, b) (a < b?b:a)
661
662#define EDGE_ASSIGN(edge,_x1,_y1,_x2,_y2) \
663  { if (_y2 > _y1) { edge.y1 = _y1; edge.y2 = _y2; edge.x1 = _x1; edge.x2 = _x2; } \
664    else { edge.y2 = _y1; edge.y1 = _y2; edge.x2 = _x1; edge.x1 = _x2; } }
665
666void gdx2d_fill_triangle(const gdx2d_pixmap* pixmap, int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, uint32_t col) {
667
668	// this structure is used to sort edges according to y-component.
669	struct edge {
670		int32_t x1;
671		int32_t y1;
672		int32_t x2;
673		int32_t y2;
674	};
675	struct edge edges[3], edge_tmp;
676	float slope0, slope1, slope2;
677	int32_t edge0_len, edge1_len, edge2_len, edge_len_tmp;
678	int32_t y, bound_y1, bound_y2, calc_x1, calc_x2;
679
680	// do nothing when points are colinear -- we draw the fill not the line.
681	if ((x2 - x1) * (y3 - y1) == (x3 - x1) * (y2 - y1)) {
682		return;
683	}
684
685	// asign input vertices into internally-sorted edge structures.
686	EDGE_ASSIGN(edges[0], x1, y1, x2, y2);
687	EDGE_ASSIGN(edges[1], x1, y1, x3, y3);
688	EDGE_ASSIGN(edges[2], x2, y2, x3, y3);
689
690	// order edges according to descending length.
691	edge0_len = edges[0].y2 - edges[0].y1;
692	edge1_len = edges[1].y2 - edges[1].y1;
693	edge2_len = edges[2].y2 - edges[2].y1;
694
695	if (edge1_len >= edge0_len && edge1_len >= edge2_len) {
696		// swap edge0 and edge1 with respective lengths.
697		edge_tmp = edges[0];
698		edges[0] = edges[1];
699		edges[1] = edge_tmp;
700		edge_len_tmp = edge0_len;
701		edge0_len = edge1_len;
702		edge1_len = edge_len_tmp;
703	} else if (edge2_len >= edge0_len && edge2_len >= edge1_len) {
704		// swap edge0 and edge2 with respective lengths.
705		edge_tmp = edges[0];
706		edges[0] = edges[2];
707		edges[2] = edge_tmp;
708		edge_len_tmp = edge0_len;
709		edge0_len = edge2_len;
710		edge2_len = edge_len_tmp;
711	}
712
713	if (edge2_len > edge1_len) {
714		// swap edge1 and edge2 - edge len no longer necessary.
715		edge_tmp = edges[1];
716		edges[1] = edges[2];
717		edges[2] = edge_tmp;
718	}
719
720	// y-component of the two longest y-component edges is provably > 0.
721
722	slope0 = ((float) (edges[0].x1 - edges[0].x2)) /
723		((float) (edges[0].y2 - edges[0].y1));
724	slope1 = ((float) (edges[1].x1 - edges[1].x2)) /
725		((float) (edges[1].y2 - edges[1].y1));
726
727	// avoid iterating on y values out of bounds.
728	bound_y1 = max(edges[1].y1, 0);
729	bound_y2 = min(edges[1].y2, pixmap->height-1);
730
731	for ( y=bound_y1; y <= bound_y2; y++ ) {
732
733		// calculate the x values for this y value.
734		calc_x1 = (int32_t) ((float) edges[0].x2 +
735			slope0 * (float) (edges[0].y2 - y) + 0.5);
736		calc_x2 = (int32_t) ((float) edges[1].x2 +
737			slope1 * (float) (edges[1].y2 - y) + 0.5);
738
739		// do not duplicate hline() swap and boundary checking.
740		hline(pixmap, calc_x1, calc_x2, y, col);
741	}
742
743	// if there are still values of y which remain, keep calculating.
744
745	if (edges[2].y2 - edges[2].y1 > 0) {
746
747		slope2 = ((float) (edges[2].x1 - edges[2].x2)) /
748			((float) (edges[2].y2 - edges[2].y1));
749
750		bound_y1 = max(edges[2].y1, 0);
751		bound_y2 = min(edges[2].y2, pixmap->height-1);
752
753		for ( y=bound_y1; y <= bound_y2; y++ ) {
754
755			calc_x1 = (int32_t) ((float) edges[0].x2 +
756				slope0 * (float) (edges[0].y2 - y) + 0.5);
757			calc_x2 = (int32_t) ((float) edges[2].x2 +
758				slope2 * (float) (edges[2].y2 - y) + 0.5);
759
760			hline(pixmap, calc_x1, calc_x2, y, col);
761		}
762	}
763
764	return;
765}
766
767static inline void blit_same_size(const gdx2d_pixmap* src_pixmap, const gdx2d_pixmap* dst_pixmap,
768						 			 int32_t src_x, int32_t src_y,
769									 int32_t dst_x, int32_t dst_y,
770									 uint32_t width, uint32_t height) {
771	set_pixel_func pset = set_pixel_func_ptr(dst_pixmap->format);
772	get_pixel_func pget = get_pixel_func_ptr(src_pixmap->format);
773	get_pixel_func dpget = get_pixel_func_ptr(dst_pixmap->format);
774	uint32_t sbpp = gdx2d_bytes_per_pixel(src_pixmap->format);
775	uint32_t dbpp = gdx2d_bytes_per_pixel(dst_pixmap->format);
776	uint32_t spitch = sbpp * src_pixmap->width;
777	uint32_t dpitch = dbpp * dst_pixmap->width;
778
779	int sx = src_x;
780	int sy = src_y;
781	int dx = dst_x;
782	int dy = dst_y;
783
784	for(;sy < src_y + height; sy++, dy++) {
785		if(sy < 0 || dy < 0) continue;
786		if(sy >= src_pixmap->height || dy >= dst_pixmap->height) break;
787
788		for(sx = src_x, dx = dst_x; sx < src_x + width; sx++, dx++) {
789			if(sx < 0 || dx < 0) continue;
790			if(sx >= src_pixmap->width || dx >= dst_pixmap->width) break;
791
792			const void* src_ptr = src_pixmap->pixels + sx * sbpp + sy * spitch;
793			const void* dst_ptr = dst_pixmap->pixels + dx * dbpp + dy * dpitch;
794			uint32_t src_col = to_RGBA8888(src_pixmap->format, pget((void*)src_ptr));
795
796			if(gdx2d_blend) {
797				uint32_t dst_col = to_RGBA8888(dst_pixmap->format, dpget((void*)dst_ptr));
798				src_col = to_format(dst_pixmap->format, blend(src_col, dst_col));
799			} else {
800				src_col = to_format(dst_pixmap->format, src_col);
801			}
802
803			pset((void*)dst_ptr, src_col);
804		}
805	}
806}
807
808static inline void blit_bilinear(const gdx2d_pixmap* src_pixmap, const gdx2d_pixmap* dst_pixmap,
809		   int32_t src_x, int32_t src_y, uint32_t src_width, uint32_t src_height,
810		   int32_t dst_x, int32_t dst_y, uint32_t dst_width, uint32_t dst_height) {
811	set_pixel_func pset = set_pixel_func_ptr(dst_pixmap->format);
812	get_pixel_func pget = get_pixel_func_ptr(src_pixmap->format);
813	get_pixel_func dpget = get_pixel_func_ptr(dst_pixmap->format);
814	uint32_t sbpp = gdx2d_bytes_per_pixel(src_pixmap->format);
815	uint32_t dbpp = gdx2d_bytes_per_pixel(dst_pixmap->format);
816	uint32_t spitch = sbpp * src_pixmap->width;
817	uint32_t dpitch = dbpp * dst_pixmap->width;
818
819	float x_ratio = ((float)src_width - 1)/ dst_width;
820	float y_ratio = ((float)src_height - 1) / dst_height;
821	float x_diff = 0;
822	float y_diff = 0;
823
824	int dx = dst_x;
825	int dy = dst_y;
826	int sx = src_x;
827	int sy = src_y;
828	int i = 0;
829	int j = 0;
830
831	for(;i < dst_height; i++) {
832		sy = (int)(i * y_ratio) + src_y;
833		dy = i + dst_y;
834		y_diff = (y_ratio * i + src_y) - sy;
835		if(sy < 0 || dy < 0) continue;
836		if(sy >= src_pixmap->height || dy >= dst_pixmap->height) break;
837
838		for(j = 0 ;j < dst_width; j++) {
839			sx = (int)(j * x_ratio) + src_x;
840			dx = j + dst_x;
841			x_diff = (x_ratio * j + src_x) - sx;
842			if(sx < 0 || dx < 0) continue;
843			if(sx >= src_pixmap->width || dx >= dst_pixmap->width) break;
844
845			const void* dst_ptr = dst_pixmap->pixels + dx * dbpp + dy * dpitch;
846			const void* src_ptr = src_pixmap->pixels + sx * sbpp + sy * spitch;
847			uint32_t c1 = 0, c2 = 0, c3 = 0, c4 = 0;
848			c1 = to_RGBA8888(src_pixmap->format, pget((void*)src_ptr));
849			if(sx + 1 < src_width) c2 = to_RGBA8888(src_pixmap->format, pget((void*)(src_ptr + sbpp))); else c2 = c1;
850			if(sy + 1< src_height) c3 = to_RGBA8888(src_pixmap->format, pget((void*)(src_ptr + spitch))); else c3 = c1;
851			if(sx + 1< src_width && sy + 1 < src_height) c4 = to_RGBA8888(src_pixmap->format, pget((void*)(src_ptr + spitch + sbpp))); else c4 = c1;
852
853			float ta = (1 - x_diff) * (1 - y_diff);
854			float tb = (x_diff) * (1 - y_diff);
855			float tc = (1 - x_diff) * (y_diff);
856			float td = (x_diff) * (y_diff);
857
858			uint32_t r = (uint32_t)(((c1 & 0xff000000) >> 24) * ta +
859									((c2 & 0xff000000) >> 24) * tb +
860									((c3 & 0xff000000) >> 24) * tc +
861									((c4 & 0xff000000) >> 24) * td) & 0xff;
862			uint32_t g = (uint32_t)(((c1 & 0xff0000) >> 16) * ta +
863									((c2 & 0xff0000) >> 16) * tb +
864									((c3 & 0xff0000) >> 16) * tc +
865									((c4 & 0xff0000) >> 16) * td) & 0xff;
866			uint32_t b = (uint32_t)(((c1 & 0xff00) >> 8) * ta +
867									((c2 & 0xff00) >> 8) * tb +
868									((c3 & 0xff00) >> 8) * tc +
869									((c4 & 0xff00) >> 8) * td) & 0xff;
870			uint32_t a = (uint32_t)((c1 & 0xff) * ta +
871									(c2 & 0xff) * tb +
872									(c3 & 0xff) * tc +
873									(c4 & 0xff) * td) & 0xff;
874
875			uint32_t src_col = (r << 24) | (g << 16) | (b << 8) | a;
876
877			if(gdx2d_blend) {
878				uint32_t dst_col = to_RGBA8888(dst_pixmap->format, dpget((void*)dst_ptr));
879				src_col = to_format(dst_pixmap->format, blend(src_col, dst_col));
880			} else {
881				src_col = to_format(dst_pixmap->format, src_col);
882			}
883
884			pset((void*)dst_ptr, src_col);
885		}
886	}
887}
888
889static inline void blit_linear(const gdx2d_pixmap* src_pixmap, const gdx2d_pixmap* dst_pixmap,
890		   int32_t src_x, int32_t src_y, uint32_t src_width, uint32_t src_height,
891		   int32_t dst_x, int32_t dst_y, uint32_t dst_width, uint32_t dst_height) {
892	set_pixel_func pset = set_pixel_func_ptr(dst_pixmap->format);
893	get_pixel_func pget = get_pixel_func_ptr(src_pixmap->format);
894	get_pixel_func dpget = get_pixel_func_ptr(dst_pixmap->format);
895	uint32_t sbpp = gdx2d_bytes_per_pixel(src_pixmap->format);
896	uint32_t dbpp = gdx2d_bytes_per_pixel(dst_pixmap->format);
897	uint32_t spitch = sbpp * src_pixmap->width;
898	uint32_t dpitch = dbpp * dst_pixmap->width;
899
900	uint32_t x_ratio = (src_width << 16) / dst_width + 1;
901	uint32_t y_ratio = (src_height << 16) / dst_height + 1;
902
903	int dx = dst_x;
904	int dy = dst_y;
905	int sx = src_x;
906	int sy = src_y;
907	int i = 0;
908	int j = 0;
909
910	for(;i < dst_height; i++) {
911		sy = ((i * y_ratio) >> 16) + src_y;
912		dy = i + dst_y;
913		if(sy < 0 || dy < 0) continue;
914		if(sy >= src_pixmap->height || dy >= dst_pixmap->height) break;
915
916		for(j = 0 ;j < dst_width; j++) {
917			sx = ((j * x_ratio) >> 16) + src_x;
918			dx = j + dst_x;
919			if(sx < 0 || dx < 0) continue;
920			if(sx >= src_pixmap->width || dx >= dst_pixmap->width) break;
921
922			const void* src_ptr = src_pixmap->pixels + sx * sbpp + sy * spitch;
923			const void* dst_ptr = dst_pixmap->pixels + dx * dbpp + dy * dpitch;
924			uint32_t src_col = to_RGBA8888(src_pixmap->format, pget((void*)src_ptr));
925
926			if(gdx2d_blend) {
927				uint32_t dst_col = to_RGBA8888(dst_pixmap->format, dpget((void*)dst_ptr));
928				src_col = to_format(dst_pixmap->format, blend(src_col, dst_col));
929			} else {
930				src_col = to_format(dst_pixmap->format, src_col);
931			}
932
933			pset((void*)dst_ptr, src_col);
934		}
935	}
936}
937
938static inline void blit(const gdx2d_pixmap* src_pixmap, const gdx2d_pixmap* dst_pixmap,
939					   int32_t src_x, int32_t src_y, uint32_t src_width, uint32_t src_height,
940					   int32_t dst_x, int32_t dst_y, uint32_t dst_width, uint32_t dst_height) {
941	if(gdx2d_scale == GDX2D_SCALE_NEAREST)
942		blit_linear(src_pixmap, dst_pixmap, src_x, src_y, src_width, src_height, dst_x, dst_y, dst_width, dst_height);
943	if(gdx2d_scale == GDX2D_SCALE_BILINEAR)
944		blit_bilinear(src_pixmap, dst_pixmap, src_x, src_y, src_width, src_height, dst_x, dst_y, dst_width, dst_height);
945}
946
947void gdx2d_draw_pixmap(const gdx2d_pixmap* src_pixmap, const gdx2d_pixmap* dst_pixmap,
948					   int32_t src_x, int32_t src_y, uint32_t src_width, uint32_t src_height,
949					   int32_t dst_x, int32_t dst_y, uint32_t dst_width, uint32_t dst_height) {
950	if(src_width == dst_width && src_height == dst_height) {
951		blit_same_size(src_pixmap, dst_pixmap, src_x, src_y, dst_x, dst_y, src_width, src_height);
952	} else {
953		blit(src_pixmap, dst_pixmap, src_x, src_y, src_width, src_height, dst_x, dst_y, dst_width, dst_height);
954	}
955}
956