/* | |
* 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 <stdlib.h> | |
#define STB_IMAGE_IMPLEMENTATION | |
#define STBI_NO_FAILURE_STRINGS | |
#include "stb_image.h" | |
#include "jpgd_c.h" | |
#include <android/log.h> | |
#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); | |
} | |
} |