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