Home | History | Annotate | Download | only in gdx2d
      1 /*
      2  * Copyright 2010 Mario Zechner (contact (at) badlogicgames.com), Nathan Sweet (admin (at) 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 
     20 static uint32_t gdx2d_blend = GDX2D_BLEND_NONE;
     21 static uint32_t gdx2d_scale = GDX2D_SCALE_NEAREST;
     22 
     23 static uint32_t* lu4 = 0;
     24 static uint32_t* lu5 = 0;
     25 static uint32_t* lu6 = 0;
     26 
     27 typedef void(*set_pixel_func)(unsigned char* pixel_addr, uint32_t color);
     28 typedef uint32_t(*get_pixel_func)(unsigned char* pixel_addr);
     29 
     30 static 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 
     52 static 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 
     87 static 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 
     97 static 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 
    127 static inline void set_pixel_alpha(unsigned char *pixel_addr, uint32_t color) {
    128 	*pixel_addr = (unsigned char)(color & 0xff);
    129 }
    130 
    131 static inline void set_pixel_luminance_alpha(unsigned char *pixel_addr, uint32_t color) {
    132 	*(unsigned short*)pixel_addr = (unsigned short)color;
    133 }
    134 
    135 static 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 
    142 static 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 
    149 static inline void set_pixel_RGB565(unsigned char *pixel_addr, uint32_t color) {
    150 	*(uint16_t*)pixel_addr = (uint16_t)(color);
    151 }
    152 
    153 static inline void set_pixel_RGBA4444(unsigned char *pixel_addr, uint32_t color) {
    154 	*(uint16_t*)pixel_addr = (uint16_t)(color);
    155 }
    156 
    157 static 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 
    169 static 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 
    187 static inline uint32_t get_pixel_alpha(unsigned char *pixel_addr) {
    188 	return *pixel_addr;
    189 }
    190 
    191 static inline uint32_t get_pixel_luminance_alpha(unsigned char *pixel_addr) {
    192 	return (((uint32_t)pixel_addr[0]) << 8) | pixel_addr[1];
    193 }
    194 
    195 static 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 
    199 static 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 
    203 static inline uint32_t get_pixel_RGB565(unsigned char *pixel_addr) {
    204 	return *(uint16_t*)pixel_addr;
    205 }
    206 
    207 static inline uint32_t get_pixel_RGBA4444(unsigned char *pixel_addr) {
    208 	return *(uint16_t*)pixel_addr;
    209 }
    210 
    211 static 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 
    223 gdx2d_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 
    242 uint32_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 
    259 gdx2d_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 }
    272 void gdx2d_free(const gdx2d_pixmap* pixmap) {
    273 	free((void*)pixmap->pixels);
    274 	free((void*)pixmap);
    275 }
    276 
    277 void gdx2d_set_blend (uint32_t blend) {
    278 	gdx2d_blend = blend;
    279 }
    280 
    281 void gdx2d_set_scale (uint32_t scale) {
    282 	gdx2d_scale = scale;
    283 }
    284 
    285 const char *gdx2d_get_failure_reason(void) {
    286 	if (stbi_failure_reason())
    287 		return stbi_failure_reason();
    288     return jpgd_failure_reason();
    289 }
    290 
    291 static 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 
    296 static 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 
    307 static 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 
    324 static 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 
    339 static 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 
    350 static 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 
    361 void 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 
    388 static 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 
    396 static 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 
    403 uint32_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 
    410 void 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 
    422 void 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 
    482 static 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 
    517 static 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 
    553 void 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 
    560 static 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 
    585 void 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 
    609 void 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 
    630 void 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 
    666 void 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 
    767 static 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 
    808 static 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 
    889 static 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 
    938 static 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 
    947 void 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