/* * Copyright 2010 Mario Zechner (contact@badlogicgames.com), Nathan Sweet (admin@esotericsoftware.com) * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ #include "gdx2d.h" #include #define STB_IMAGE_IMPLEMENTATION #define STBI_NO_FAILURE_STRINGS #include "stb_image.h" #include "jpgd_c.h" #include #define APP_LOG "GDX" static uint32_t gdx2d_blend = GDX2D_BLEND_NONE; static uint32_t gdx2d_scale = GDX2D_SCALE_NEAREST; static uint32_t* lu4 = 0; static uint32_t* lu5 = 0; static uint32_t* lu6 = 0; typedef void(*set_pixel_func)(unsigned char* pixel_addr, uint32_t color); typedef uint32_t(*get_pixel_func)(unsigned char* pixel_addr); static inline void generate_look_ups() { uint32_t i = 0; lu4 = malloc(sizeof(uint32_t) * 16); lu5 = malloc(sizeof(uint32_t) * 32); lu6 = malloc(sizeof(uint32_t) * 64); for(i = 0; i < 16; i++) { lu4[i] = (uint32_t) i / 15.0f * 255; lu5[i] = (uint32_t) i / 31.0f * 255; lu6[i] = (uint32_t) i / 63.0f * 255; } for(i = 16; i < 32; i++) { lu5[i] = (uint32_t) i / 31.0f * 255; lu6[i] = (uint32_t) i / 63.0f * 255; } for(i = 32; i < 64; i++) { lu6[i] = (uint32_t) i / 63.0f * 255; } } static inline uint32_t to_format(uint32_t format, uint32_t color) { uint32_t r, g, b, a, l; switch(format) { case GDX2D_FORMAT_ALPHA: return color & 0xff; case GDX2D_FORMAT_LUMINANCE_ALPHA: r = (color & 0xff000000) >> 24; g = (color & 0xff0000) >> 16; b = (color & 0xff00) >> 8; a = (color & 0xff); l = ((uint32_t)(0.2126f * r + 0.7152 * g + 0.0722 * b) & 0xff) << 8; return (l & 0xffffff00) | a; case GDX2D_FORMAT_RGB888: return color >> 8; case GDX2D_FORMAT_RGBA8888: return color; case GDX2D_FORMAT_RGB565: r = (((color & 0xff000000) >> 27) << 11) & 0xf800; g = (((color & 0xff0000) >> 18) << 5) & 0x7e0; b = ((color & 0xff00) >> 11) & 0x1f; return r | g | b; case GDX2D_FORMAT_RGBA4444: r = (((color & 0xff000000) >> 28) << 12) & 0xf000; g = (((color & 0xff0000) >> 20) << 8) & 0xf00; b = (((color & 0xff00) >> 12) << 4) & 0xf0; a = ((color & 0xff) >> 4) & 0xf; return r | g | b | a; default: return 0; } } #define min(a, b) (a > b?b:a) static inline uint32_t weight_RGBA8888(uint32_t color, float weight) { uint32_t r, g, b, a; r = min((uint32_t)(((color & 0xff000000) >> 24) * weight), 255); g = min((uint32_t)(((color & 0xff0000) >> 16) * weight), 255); b = min((uint32_t)(((color & 0xff00) >> 8) * weight), 255); a = min((uint32_t)(((color & 0xff)) * weight), 255); return (r << 24) | (g << 16) | (b << 8) | a; } static inline uint32_t to_RGBA8888(uint32_t format, uint32_t color) { uint32_t r, g, b, a; if(!lu5) generate_look_ups(); switch(format) { case GDX2D_FORMAT_ALPHA: return (color & 0xff) | 0xffffff00; case GDX2D_FORMAT_LUMINANCE_ALPHA: return ((color & 0xff00) << 16) | ((color & 0xff00) << 8) | (color & 0xffff); case GDX2D_FORMAT_RGB888: return (color << 8) | 0x000000ff; case GDX2D_FORMAT_RGBA8888: return color; case GDX2D_FORMAT_RGB565: r = lu5[(color & 0xf800) >> 11] << 24; g = lu6[(color & 0x7e0) >> 5] << 16; b = lu5[(color & 0x1f)] << 8; return r | g | b | 0xff; case GDX2D_FORMAT_RGBA4444: r = lu4[(color & 0xf000) >> 12] << 24; g = lu4[(color & 0xf00) >> 8] << 16; b = lu4[(color & 0xf0) >> 4] << 8; a = lu4[(color & 0xf)]; return r | g | b | a; default: return 0; } } static inline void set_pixel_alpha(unsigned char *pixel_addr, uint32_t color) { *pixel_addr = (unsigned char)(color & 0xff); } static inline void set_pixel_luminance_alpha(unsigned char *pixel_addr, uint32_t color) { *(unsigned short*)pixel_addr = (unsigned short)color; } static inline void set_pixel_RGB888(unsigned char *pixel_addr, uint32_t color) { //*(unsigned short*)pixel_addr = (unsigned short)(((color & 0xff0000) >> 16) | (color & 0xff00)); pixel_addr[0] = (color & 0xff0000) >> 16; pixel_addr[1] = (color & 0xff00) >> 8; pixel_addr[2] = (color & 0xff); } static inline void set_pixel_RGBA8888(unsigned char *pixel_addr, uint32_t color) { *(uint32_t*)pixel_addr = ((color & 0xff000000) >> 24) | ((color & 0xff0000) >> 8) | ((color & 0xff00) << 8) | ((color & 0xff) << 24); } static inline void set_pixel_RGB565(unsigned char *pixel_addr, uint32_t color) { *(uint16_t*)pixel_addr = (uint16_t)(color); } static inline void set_pixel_RGBA4444(unsigned char *pixel_addr, uint32_t color) { *(uint16_t*)pixel_addr = (uint16_t)(color); } static inline set_pixel_func set_pixel_func_ptr(uint32_t format) { switch(format) { case GDX2D_FORMAT_ALPHA: return &set_pixel_alpha; case GDX2D_FORMAT_LUMINANCE_ALPHA: return &set_pixel_luminance_alpha; case GDX2D_FORMAT_RGB888: return &set_pixel_RGB888; case GDX2D_FORMAT_RGBA8888: return &set_pixel_RGBA8888; case GDX2D_FORMAT_RGB565: return &set_pixel_RGB565; case GDX2D_FORMAT_RGBA4444: return &set_pixel_RGBA4444; default: return &set_pixel_alpha; // better idea for a default? } } static inline uint32_t blend(uint32_t src, uint32_t dst) { int32_t src_r = (src & 0xff000000) >> 24; int32_t src_g = (src & 0xff0000) >> 16; int32_t src_b = (src & 0xff00) >> 8; int32_t src_a = (src & 0xff); int32_t dst_r = (dst & 0xff000000) >> 24; int32_t dst_g = (dst & 0xff0000) >> 16; int32_t dst_b = (dst & 0xff00) >> 8; int32_t dst_a = (dst & 0xff); dst_r = dst_r + src_a * (src_r - dst_r) / 255; dst_g = dst_g + src_a * (src_g - dst_g) / 255; dst_b = dst_b + src_a * (src_b - dst_b) / 255; dst_a = (int32_t)((1.0f - (1.0f - src_a / 255.0f) * (1.0f - dst_a / 255.0f)) * 255); return (uint32_t)((dst_r << 24) | (dst_g << 16) | (dst_b << 8) | dst_a); } static inline uint32_t get_pixel_alpha(unsigned char *pixel_addr) { return *pixel_addr; } static inline uint32_t get_pixel_luminance_alpha(unsigned char *pixel_addr) { return (((uint32_t)pixel_addr[0]) << 8) | pixel_addr[1]; } static inline uint32_t get_pixel_RGB888(unsigned char *pixel_addr) { return (((uint32_t)pixel_addr[0]) << 16) | (((uint32_t)pixel_addr[1]) << 8) | (pixel_addr[2]); } static inline uint32_t get_pixel_RGBA8888(unsigned char *pixel_addr) { return (((uint32_t)pixel_addr[0]) << 24) | (((uint32_t)pixel_addr[1]) << 16) | (((uint32_t)pixel_addr[2]) << 8) | pixel_addr[3]; } static inline uint32_t get_pixel_RGB565(unsigned char *pixel_addr) { return *(uint16_t*)pixel_addr; } static inline uint32_t get_pixel_RGBA4444(unsigned char *pixel_addr) { return *(uint16_t*)pixel_addr; } static inline get_pixel_func get_pixel_func_ptr(uint32_t format) { switch(format) { case GDX2D_FORMAT_ALPHA: return &get_pixel_alpha; case GDX2D_FORMAT_LUMINANCE_ALPHA: return &get_pixel_luminance_alpha; case GDX2D_FORMAT_RGB888: return &get_pixel_RGB888; case GDX2D_FORMAT_RGBA8888: return &get_pixel_RGBA8888; case GDX2D_FORMAT_RGB565: return &get_pixel_RGB565; case GDX2D_FORMAT_RGBA4444: return &get_pixel_RGBA4444; default: return &get_pixel_alpha; // better idea for a default? } } gdx2d_pixmap* gdx2d_load(const unsigned char *buffer, uint32_t len) { int32_t width, height, format; const unsigned char* pixels = stbi_load_from_memory(buffer, len, &width, &height, &format, 0); if (pixels == NULL) { pixels = jpgd_decompress_jpeg_image_from_memory(buffer, len, &width, &height, &format, 3); } if (pixels == NULL) return NULL; gdx2d_pixmap* pixmap = (gdx2d_pixmap*)malloc(sizeof(gdx2d_pixmap)); if (!pixmap) return 0; pixmap->width = (uint32_t)width; pixmap->height = (uint32_t)height; pixmap->format = (uint32_t)format; pixmap->pixels = pixels; return pixmap; } uint32_t gdx2d_bytes_per_pixel(uint32_t format) { switch(format) { case GDX2D_FORMAT_ALPHA: return 1; case GDX2D_FORMAT_LUMINANCE_ALPHA: case GDX2D_FORMAT_RGB565: case GDX2D_FORMAT_RGBA4444: return 2; case GDX2D_FORMAT_RGB888: return 3; case GDX2D_FORMAT_RGBA8888: return 4; default: return 4; } } gdx2d_pixmap* gdx2d_new(uint32_t width, uint32_t height, uint32_t format) { gdx2d_pixmap* pixmap = (gdx2d_pixmap*)malloc(sizeof(gdx2d_pixmap)); if (!pixmap) return 0; pixmap->width = width; pixmap->height = height; pixmap->format = format; pixmap->pixels = (unsigned char*)malloc(width * height * gdx2d_bytes_per_pixel(format)); if (!pixmap->pixels) { free((void*)pixmap); return 0; } return pixmap; } void gdx2d_free(const gdx2d_pixmap* pixmap) { free((void*)pixmap->pixels); free((void*)pixmap); } void gdx2d_set_blend (uint32_t blend) { gdx2d_blend = blend; } void gdx2d_set_scale (uint32_t scale) { gdx2d_scale = scale; } const char *gdx2d_get_failure_reason(void) { if (stbi_failure_reason()) return stbi_failure_reason(); return jpgd_failure_reason(); } static inline void clear_alpha(const gdx2d_pixmap* pixmap, uint32_t col) { int pixels = pixmap->width * pixmap->height; memset((void*)pixmap->pixels, col, pixels); } static inline void clear_luminance_alpha(const gdx2d_pixmap* pixmap, uint32_t col) { int pixels = pixmap->width * pixmap->height; unsigned short* ptr = (unsigned short*)pixmap->pixels; unsigned short l = (col & 0xff) << 8 | (col >> 8); for(; pixels > 0; pixels--) { *ptr = l; ptr++; } } static inline void clear_RGB888(const gdx2d_pixmap* pixmap, uint32_t col) { int pixels = pixmap->width * pixmap->height; unsigned char* ptr = (unsigned char*)pixmap->pixels; unsigned char r = (col & 0xff0000) >> 16; unsigned char g = (col & 0xff00) >> 8; unsigned char b = (col & 0xff); for(; pixels > 0; pixels--) { *ptr = r; ptr++; *ptr = g; ptr++; *ptr = b; ptr++; } } static inline void clear_RGBA8888(const gdx2d_pixmap* pixmap, uint32_t col) { int pixels = pixmap->width * pixmap->height; uint32_t* ptr = (uint32_t*)pixmap->pixels; unsigned char r = (col & 0xff000000) >> 24; unsigned char g = (col & 0xff0000) >> 16; unsigned char b = (col & 0xff00) >> 8; unsigned char a = (col & 0xff); col = (a << 24) | (b << 16) | (g << 8) | r; for(; pixels > 0; pixels--) { *ptr = col; ptr++; } } static inline void clear_RGB565(const gdx2d_pixmap* pixmap, uint32_t col) { int pixels = pixmap->width * pixmap->height; unsigned short* ptr = (unsigned short*)pixmap->pixels; unsigned short l = col & 0xffff; for(; pixels > 0; pixels--) { *ptr = l; ptr++; } } static inline void clear_RGBA4444(const gdx2d_pixmap* pixmap, uint32_t col) { int pixels = pixmap->width * pixmap->height; unsigned short* ptr = (unsigned short*)pixmap->pixels; unsigned short l = col & 0xffff; for(; pixels > 0; pixels--) { *ptr = l; ptr++; } } void gdx2d_clear(const gdx2d_pixmap* pixmap, uint32_t col) { if (pixmap == 0) return; col = to_format(pixmap->format, col); // Check for malformed Pixmap size_t requestedSize = pixmap->width * pixmap->height * sizeof(col); size_t pixelsSize = sizeof(pixmap->pixels); if (requestedSize > pixelsSize) { __android_log_print(ANDROID_LOG_VERBOSE, APP_LOG, "Invalid pixmap. %ix%i - Size should be %u but found %u", pixmap->width, pixmap->height, requestedSize, pixelsSize); return; } switch(pixmap->format) { case GDX2D_FORMAT_ALPHA: clear_alpha(pixmap, col); break; case GDX2D_FORMAT_LUMINANCE_ALPHA: clear_luminance_alpha(pixmap, col); break; case GDX2D_FORMAT_RGB888: clear_RGB888(pixmap, col); break; case GDX2D_FORMAT_RGBA8888: clear_RGBA8888(pixmap, col); break; case GDX2D_FORMAT_RGB565: clear_RGB565(pixmap, col); break; case GDX2D_FORMAT_RGBA4444: clear_RGBA4444(pixmap, col); break; default: break; } } static inline int32_t in_pixmap(const gdx2d_pixmap* pixmap, int32_t x, int32_t y) { if(x < 0 || y < 0) return 0; if(x >= pixmap->width || y >= pixmap->height) return 0; return -1; } 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) { if(x < 0 || y < 0) return; if(x >= (int32_t)width || y >= (int32_t)height) return; pixels = pixels + (x + width * y) * bpp; pixel_func(pixels, col); } uint32_t gdx2d_get_pixel(const gdx2d_pixmap* pixmap, int32_t x, int32_t y) { if(!in_pixmap(pixmap, x, y)) return 0; unsigned char* ptr = (unsigned char*)pixmap->pixels + (x + pixmap->width * y) * gdx2d_bytes_per_pixel(pixmap->format); return to_RGBA8888(pixmap->format, get_pixel_func_ptr(pixmap->format)(ptr)); } void gdx2d_set_pixel(const gdx2d_pixmap* pixmap, int32_t x, int32_t y, uint32_t col) { if(gdx2d_blend) { uint32_t dst = gdx2d_get_pixel(pixmap, x, y); col = blend(col, dst); col = to_format(pixmap->format, col); 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); } else { col = to_format(pixmap->format, col); 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); } } void gdx2d_draw_line(const gdx2d_pixmap* pixmap, int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t col) { int32_t dy = y1 - y0; int32_t dx = x1 - x0; int32_t fraction = 0; int32_t stepx, stepy; unsigned char* ptr = (unsigned char*)pixmap->pixels; uint32_t bpp = gdx2d_bytes_per_pixel(pixmap->format); set_pixel_func pset = set_pixel_func_ptr(pixmap->format); get_pixel_func pget = get_pixel_func_ptr(pixmap->format); uint32_t col_format = to_format(pixmap->format, col); void* addr = ptr + (x0 + y0 * pixmap->width) * bpp; if (dy < 0) { dy = -dy; stepy = -1; } else { stepy = 1; } if (dx < 0) { dx = -dx; stepx = -1; } else { stepx = 1; } dy <<= 1; dx <<= 1; if(in_pixmap(pixmap, x0, y0)) { if(gdx2d_blend) { col_format = to_format(pixmap->format, blend(col, to_RGBA8888(pixmap->format, pget(addr)))); } pset(addr, col_format); } if (dx > dy) { fraction = dy - (dx >> 1); while (x0 != x1) { if (fraction >= 0) { y0 += stepy; fraction -= dx; } x0 += stepx; fraction += dy; if(in_pixmap(pixmap, x0, y0)) { addr = ptr + (x0 + y0 * pixmap->width) * bpp; if(gdx2d_blend) { col_format = to_format(pixmap->format, blend(col, to_RGBA8888(pixmap->format, pget(addr)))); } pset(addr, col_format); } } } else { fraction = dx - (dy >> 1); while (y0 != y1) { if (fraction >= 0) { x0 += stepx; fraction -= dy; } y0 += stepy; fraction += dx; if(in_pixmap(pixmap, x0, y0)) { addr = ptr + (x0 + y0 * pixmap->width) * bpp; if(gdx2d_blend) { col_format = to_format(pixmap->format, blend(col, to_RGBA8888(pixmap->format, pget(addr)))); } pset(addr, col_format); } } } } static inline void hline(const gdx2d_pixmap* pixmap, int32_t x1, int32_t x2, int32_t y, uint32_t col) { int32_t tmp = 0; set_pixel_func pset = set_pixel_func_ptr(pixmap->format); get_pixel_func pget = get_pixel_func_ptr(pixmap->format); unsigned char* ptr = (unsigned char*)pixmap->pixels; uint32_t bpp = gdx2d_bytes_per_pixel(pixmap->format); uint32_t col_format = to_format(pixmap->format, col); if(y < 0 || y >= (int32_t)pixmap->height) return; if(x1 > x2) { tmp = x1; x1 = x2; x2 = tmp; } if(x1 >= (int32_t)pixmap->width) return; if(x2 < 0) return; if(x1 < 0) x1 = 0; if(x2 >= (int32_t)pixmap->width) x2 = pixmap->width - 1; x2 += 1; ptr += (x1 + y * pixmap->width) * bpp; while(x1 != x2) { if(gdx2d_blend) { col_format = to_format(pixmap->format, blend(col, to_RGBA8888(pixmap->format, pget(ptr)))); } pset(ptr, col_format); x1++; ptr += bpp; } } static inline void vline(const gdx2d_pixmap* pixmap, int32_t y1, int32_t y2, int32_t x, uint32_t col) { int32_t tmp = 0; set_pixel_func pset = set_pixel_func_ptr(pixmap->format); get_pixel_func pget = get_pixel_func_ptr(pixmap->format); unsigned char* ptr = (unsigned char*)pixmap->pixels; uint32_t bpp = gdx2d_bytes_per_pixel(pixmap->format); uint32_t stride = bpp * pixmap->width; uint32_t col_format = to_format(pixmap->format, col); if(x < 0 || x >= pixmap->width) return; if(y1 > y2) { tmp = y1; y1 = y2; y2 = tmp; } if(y1 >= (int32_t)pixmap->height) return; if(y2 < 0) return; if(y1 < 0) y1 = 0; if(y2 >= (int32_t)pixmap->height) y2 = pixmap->height - 1; y2 += 1; ptr += (x + y1 * pixmap->width) * bpp; while(y1 != y2) { if(gdx2d_blend) { col_format = to_format(pixmap->format, blend(col, to_RGBA8888(pixmap->format, pget(ptr)))); } pset(ptr, col_format); y1++; ptr += stride; } } void gdx2d_draw_rect(const gdx2d_pixmap* pixmap, int32_t x, int32_t y, uint32_t width, uint32_t height, uint32_t col) { hline(pixmap, x, x + width - 1, y, col); hline(pixmap, x, x + width - 1, y + height - 1, col); vline(pixmap, y, y + height - 1, x, col); vline(pixmap, y, y + height - 1, x + width - 1, col); } 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) { if (x == 0) { set_pixel(pixels, width, height, bpp, pixel_func, cx, cy + y, col); set_pixel(pixels, width, height, bpp, pixel_func, cx, cy - y, col); set_pixel(pixels, width, height, bpp, pixel_func, cx + y, cy, col); set_pixel(pixels, width, height, bpp, pixel_func, cx - y, cy, col); } else if (x == y) { set_pixel(pixels, width, height, bpp, pixel_func, cx + x, cy + y, col); set_pixel(pixels, width, height, bpp, pixel_func, cx - x, cy + y, col); set_pixel(pixels, width, height, bpp, pixel_func, cx + x, cy - y, col); set_pixel(pixels, width, height, bpp, pixel_func, cx - x, cy - y, col); } else if (x < y) { set_pixel(pixels, width, height, bpp, pixel_func, cx + x, cy + y, col); set_pixel(pixels, width, height, bpp, pixel_func, cx - x, cy + y, col); set_pixel(pixels, width, height, bpp, pixel_func, cx + x, cy - y, col); set_pixel(pixels, width, height, bpp, pixel_func, cx - x, cy - y, col); set_pixel(pixels, width, height, bpp, pixel_func, cx + y, cy + x, col); set_pixel(pixels, width, height, bpp, pixel_func, cx - y, cy + x, col); set_pixel(pixels, width, height, bpp, pixel_func, cx + y, cy - x, col); set_pixel(pixels, width, height, bpp, pixel_func, cx - y, cy - x, col); } } void gdx2d_draw_circle(const gdx2d_pixmap* pixmap, int32_t x, int32_t y, uint32_t radius, uint32_t col) { int32_t px = 0; int32_t py = radius; int32_t p = (5 - (int32_t)radius*4)/4; unsigned char* pixels = (unsigned char*)pixmap->pixels; uint32_t width = pixmap->width; uint32_t height = pixmap->height; uint32_t bpp = gdx2d_bytes_per_pixel(pixmap->format); set_pixel_func pixel_func = set_pixel_func_ptr(pixmap->format); col = to_format(pixmap->format, col); circle_points(pixels, width, height, bpp, pixel_func, x, y, px, py, col); while (px < py) { px++; if (p < 0) { p += 2*px+1; } else { py--; p += 2*(px-py)+1; } circle_points(pixels, width, height, bpp, pixel_func, x, y, px, py, col); } } void gdx2d_fill_rect(const gdx2d_pixmap* pixmap, int32_t x, int32_t y, uint32_t width, uint32_t height, uint32_t col) { int32_t x2 = x + width - 1; int32_t y2 = y + height - 1; if(x >= (int32_t)pixmap->width) return; if(y >= (int32_t)pixmap->height) return; if(x2 < 0) return; if(y2 < 0) return; if(x < 0) x = 0; if(y < 0) y = 0; if(x2 >= (int32_t)pixmap->width) x2 = pixmap->width - 1; if(y2 >= (int32_t)pixmap->height) y2 = pixmap->height - 1; y2++; while(y!=y2) { hline(pixmap, x, x2, y, col); y++; } } void gdx2d_fill_circle(const gdx2d_pixmap* pixmap, int32_t x0, int32_t y0, uint32_t radius, uint32_t col) { int32_t f = 1 - (int32_t)radius; int32_t ddF_x = 1; int32_t ddF_y = -2 * (int32_t)radius; int32_t px = 0; int32_t py = (int32_t)radius; hline(pixmap, x0, x0, y0 + (int32_t)radius, col); hline(pixmap, x0, x0, y0 - (int32_t)radius, col); hline(pixmap, x0 - (int32_t)radius, x0 + (int32_t)radius, y0, col); while(px < py) { if(f >= 0) { py--; ddF_y += 2; f += ddF_y; } px++; ddF_x += 2; f += ddF_x; hline(pixmap, x0 - px, x0 + px, y0 + py, col); hline(pixmap, x0 - px, x0 + px, y0 - py, col); hline(pixmap, x0 - py, x0 + py, y0 + px, col); hline(pixmap, x0 - py, x0 + py, y0 - px, col); } } #define max(a, b) (a < b?b:a) #define EDGE_ASSIGN(edge,_x1,_y1,_x2,_y2) \ { if (_y2 > _y1) { edge.y1 = _y1; edge.y2 = _y2; edge.x1 = _x1; edge.x2 = _x2; } \ else { edge.y2 = _y1; edge.y1 = _y2; edge.x2 = _x1; edge.x1 = _x2; } } 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) { // this structure is used to sort edges according to y-component. struct edge { int32_t x1; int32_t y1; int32_t x2; int32_t y2; }; struct edge edges[3], edge_tmp; float slope0, slope1, slope2; int32_t edge0_len, edge1_len, edge2_len, edge_len_tmp; int32_t y, bound_y1, bound_y2, calc_x1, calc_x2; // do nothing when points are colinear -- we draw the fill not the line. if ((x2 - x1) * (y3 - y1) == (x3 - x1) * (y2 - y1)) { return; } // asign input vertices into internally-sorted edge structures. EDGE_ASSIGN(edges[0], x1, y1, x2, y2); EDGE_ASSIGN(edges[1], x1, y1, x3, y3); EDGE_ASSIGN(edges[2], x2, y2, x3, y3); // order edges according to descending length. edge0_len = edges[0].y2 - edges[0].y1; edge1_len = edges[1].y2 - edges[1].y1; edge2_len = edges[2].y2 - edges[2].y1; if (edge1_len >= edge0_len && edge1_len >= edge2_len) { // swap edge0 and edge1 with respective lengths. edge_tmp = edges[0]; edges[0] = edges[1]; edges[1] = edge_tmp; edge_len_tmp = edge0_len; edge0_len = edge1_len; edge1_len = edge_len_tmp; } else if (edge2_len >= edge0_len && edge2_len >= edge1_len) { // swap edge0 and edge2 with respective lengths. edge_tmp = edges[0]; edges[0] = edges[2]; edges[2] = edge_tmp; edge_len_tmp = edge0_len; edge0_len = edge2_len; edge2_len = edge_len_tmp; } if (edge2_len > edge1_len) { // swap edge1 and edge2 - edge len no longer necessary. edge_tmp = edges[1]; edges[1] = edges[2]; edges[2] = edge_tmp; } // y-component of the two longest y-component edges is provably > 0. slope0 = ((float) (edges[0].x1 - edges[0].x2)) / ((float) (edges[0].y2 - edges[0].y1)); slope1 = ((float) (edges[1].x1 - edges[1].x2)) / ((float) (edges[1].y2 - edges[1].y1)); // avoid iterating on y values out of bounds. bound_y1 = max(edges[1].y1, 0); bound_y2 = min(edges[1].y2, pixmap->height-1); for ( y=bound_y1; y <= bound_y2; y++ ) { // calculate the x values for this y value. calc_x1 = (int32_t) ((float) edges[0].x2 + slope0 * (float) (edges[0].y2 - y) + 0.5); calc_x2 = (int32_t) ((float) edges[1].x2 + slope1 * (float) (edges[1].y2 - y) + 0.5); // do not duplicate hline() swap and boundary checking. hline(pixmap, calc_x1, calc_x2, y, col); } // if there are still values of y which remain, keep calculating. if (edges[2].y2 - edges[2].y1 > 0) { slope2 = ((float) (edges[2].x1 - edges[2].x2)) / ((float) (edges[2].y2 - edges[2].y1)); bound_y1 = max(edges[2].y1, 0); bound_y2 = min(edges[2].y2, pixmap->height-1); for ( y=bound_y1; y <= bound_y2; y++ ) { calc_x1 = (int32_t) ((float) edges[0].x2 + slope0 * (float) (edges[0].y2 - y) + 0.5); calc_x2 = (int32_t) ((float) edges[2].x2 + slope2 * (float) (edges[2].y2 - y) + 0.5); hline(pixmap, calc_x1, calc_x2, y, col); } } return; } static inline void blit_same_size(const gdx2d_pixmap* src_pixmap, const gdx2d_pixmap* dst_pixmap, int32_t src_x, int32_t src_y, int32_t dst_x, int32_t dst_y, uint32_t width, uint32_t height) { set_pixel_func pset = set_pixel_func_ptr(dst_pixmap->format); get_pixel_func pget = get_pixel_func_ptr(src_pixmap->format); get_pixel_func dpget = get_pixel_func_ptr(dst_pixmap->format); uint32_t sbpp = gdx2d_bytes_per_pixel(src_pixmap->format); uint32_t dbpp = gdx2d_bytes_per_pixel(dst_pixmap->format); uint32_t spitch = sbpp * src_pixmap->width; uint32_t dpitch = dbpp * dst_pixmap->width; int sx = src_x; int sy = src_y; int dx = dst_x; int dy = dst_y; for(;sy < src_y + height; sy++, dy++) { if(sy < 0 || dy < 0) continue; if(sy >= src_pixmap->height || dy >= dst_pixmap->height) break; for(sx = src_x, dx = dst_x; sx < src_x + width; sx++, dx++) { if(sx < 0 || dx < 0) continue; if(sx >= src_pixmap->width || dx >= dst_pixmap->width) break; const void* src_ptr = src_pixmap->pixels + sx * sbpp + sy * spitch; const void* dst_ptr = dst_pixmap->pixels + dx * dbpp + dy * dpitch; uint32_t src_col = to_RGBA8888(src_pixmap->format, pget((void*)src_ptr)); if(gdx2d_blend) { uint32_t dst_col = to_RGBA8888(dst_pixmap->format, dpget((void*)dst_ptr)); src_col = to_format(dst_pixmap->format, blend(src_col, dst_col)); } else { src_col = to_format(dst_pixmap->format, src_col); } pset((void*)dst_ptr, src_col); } } } static inline void blit_bilinear(const gdx2d_pixmap* src_pixmap, const gdx2d_pixmap* dst_pixmap, int32_t src_x, int32_t src_y, uint32_t src_width, uint32_t src_height, int32_t dst_x, int32_t dst_y, uint32_t dst_width, uint32_t dst_height) { set_pixel_func pset = set_pixel_func_ptr(dst_pixmap->format); get_pixel_func pget = get_pixel_func_ptr(src_pixmap->format); get_pixel_func dpget = get_pixel_func_ptr(dst_pixmap->format); uint32_t sbpp = gdx2d_bytes_per_pixel(src_pixmap->format); uint32_t dbpp = gdx2d_bytes_per_pixel(dst_pixmap->format); uint32_t spitch = sbpp * src_pixmap->width; uint32_t dpitch = dbpp * dst_pixmap->width; float x_ratio = ((float)src_width - 1)/ dst_width; float y_ratio = ((float)src_height - 1) / dst_height; float x_diff = 0; float y_diff = 0; int dx = dst_x; int dy = dst_y; int sx = src_x; int sy = src_y; int i = 0; int j = 0; for(;i < dst_height; i++) { sy = (int)(i * y_ratio) + src_y; dy = i + dst_y; y_diff = (y_ratio * i + src_y) - sy; if(sy < 0 || dy < 0) continue; if(sy >= src_pixmap->height || dy >= dst_pixmap->height) break; for(j = 0 ;j < dst_width; j++) { sx = (int)(j * x_ratio) + src_x; dx = j + dst_x; x_diff = (x_ratio * j + src_x) - sx; if(sx < 0 || dx < 0) continue; if(sx >= src_pixmap->width || dx >= dst_pixmap->width) break; const void* dst_ptr = dst_pixmap->pixels + dx * dbpp + dy * dpitch; const void* src_ptr = src_pixmap->pixels + sx * sbpp + sy * spitch; uint32_t c1 = 0, c2 = 0, c3 = 0, c4 = 0; c1 = to_RGBA8888(src_pixmap->format, pget((void*)src_ptr)); if(sx + 1 < src_width) c2 = to_RGBA8888(src_pixmap->format, pget((void*)(src_ptr + sbpp))); else c2 = c1; if(sy + 1< src_height) c3 = to_RGBA8888(src_pixmap->format, pget((void*)(src_ptr + spitch))); else c3 = c1; if(sx + 1< src_width && sy + 1 < src_height) c4 = to_RGBA8888(src_pixmap->format, pget((void*)(src_ptr + spitch + sbpp))); else c4 = c1; float ta = (1 - x_diff) * (1 - y_diff); float tb = (x_diff) * (1 - y_diff); float tc = (1 - x_diff) * (y_diff); float td = (x_diff) * (y_diff); uint32_t r = (uint32_t)(((c1 & 0xff000000) >> 24) * ta + ((c2 & 0xff000000) >> 24) * tb + ((c3 & 0xff000000) >> 24) * tc + ((c4 & 0xff000000) >> 24) * td) & 0xff; uint32_t g = (uint32_t)(((c1 & 0xff0000) >> 16) * ta + ((c2 & 0xff0000) >> 16) * tb + ((c3 & 0xff0000) >> 16) * tc + ((c4 & 0xff0000) >> 16) * td) & 0xff; uint32_t b = (uint32_t)(((c1 & 0xff00) >> 8) * ta + ((c2 & 0xff00) >> 8) * tb + ((c3 & 0xff00) >> 8) * tc + ((c4 & 0xff00) >> 8) * td) & 0xff; uint32_t a = (uint32_t)((c1 & 0xff) * ta + (c2 & 0xff) * tb + (c3 & 0xff) * tc + (c4 & 0xff) * td) & 0xff; uint32_t src_col = (r << 24) | (g << 16) | (b << 8) | a; if(gdx2d_blend) { uint32_t dst_col = to_RGBA8888(dst_pixmap->format, dpget((void*)dst_ptr)); src_col = to_format(dst_pixmap->format, blend(src_col, dst_col)); } else { src_col = to_format(dst_pixmap->format, src_col); } pset((void*)dst_ptr, src_col); } } } static inline void blit_linear(const gdx2d_pixmap* src_pixmap, const gdx2d_pixmap* dst_pixmap, int32_t src_x, int32_t src_y, uint32_t src_width, uint32_t src_height, int32_t dst_x, int32_t dst_y, uint32_t dst_width, uint32_t dst_height) { set_pixel_func pset = set_pixel_func_ptr(dst_pixmap->format); get_pixel_func pget = get_pixel_func_ptr(src_pixmap->format); get_pixel_func dpget = get_pixel_func_ptr(dst_pixmap->format); uint32_t sbpp = gdx2d_bytes_per_pixel(src_pixmap->format); uint32_t dbpp = gdx2d_bytes_per_pixel(dst_pixmap->format); uint32_t spitch = sbpp * src_pixmap->width; uint32_t dpitch = dbpp * dst_pixmap->width; uint32_t x_ratio = (src_width << 16) / dst_width + 1; uint32_t y_ratio = (src_height << 16) / dst_height + 1; int dx = dst_x; int dy = dst_y; int sx = src_x; int sy = src_y; int i = 0; int j = 0; for(;i < dst_height; i++) { sy = ((i * y_ratio) >> 16) + src_y; dy = i + dst_y; if(sy < 0 || dy < 0) continue; if(sy >= src_pixmap->height || dy >= dst_pixmap->height) break; for(j = 0 ;j < dst_width; j++) { sx = ((j * x_ratio) >> 16) + src_x; dx = j + dst_x; if(sx < 0 || dx < 0) continue; if(sx >= src_pixmap->width || dx >= dst_pixmap->width) break; const void* src_ptr = src_pixmap->pixels + sx * sbpp + sy * spitch; const void* dst_ptr = dst_pixmap->pixels + dx * dbpp + dy * dpitch; uint32_t src_col = to_RGBA8888(src_pixmap->format, pget((void*)src_ptr)); if(gdx2d_blend) { uint32_t dst_col = to_RGBA8888(dst_pixmap->format, dpget((void*)dst_ptr)); src_col = to_format(dst_pixmap->format, blend(src_col, dst_col)); } else { src_col = to_format(dst_pixmap->format, src_col); } pset((void*)dst_ptr, src_col); } } } static inline void blit(const gdx2d_pixmap* src_pixmap, const gdx2d_pixmap* dst_pixmap, int32_t src_x, int32_t src_y, uint32_t src_width, uint32_t src_height, int32_t dst_x, int32_t dst_y, uint32_t dst_width, uint32_t dst_height) { if(gdx2d_scale == GDX2D_SCALE_NEAREST) blit_linear(src_pixmap, dst_pixmap, src_x, src_y, src_width, src_height, dst_x, dst_y, dst_width, dst_height); if(gdx2d_scale == GDX2D_SCALE_BILINEAR) blit_bilinear(src_pixmap, dst_pixmap, src_x, src_y, src_width, src_height, dst_x, dst_y, dst_width, dst_height); } void gdx2d_draw_pixmap(const gdx2d_pixmap* src_pixmap, const gdx2d_pixmap* dst_pixmap, int32_t src_x, int32_t src_y, uint32_t src_width, uint32_t src_height, int32_t dst_x, int32_t dst_y, uint32_t dst_width, uint32_t dst_height) { if(src_width == dst_width && src_height == dst_height) { blit_same_size(src_pixmap, dst_pixmap, src_x, src_y, dst_x, dst_y, src_width, src_height); } else { blit(src_pixmap, dst_pixmap, src_x, src_y, src_width, src_height, dst_x, dst_y, dst_width, dst_height); } }