/* Bilinear filtering from Wikipedia */ uint32_t getBilinearFilteredPixelColor(sprite_t * tex, double u, double v) { u *= tex->width; v *= tex->height; int x = floor(u); int y = floor(v); if (x >= tex->width) return 0; if (y >= tex->height) return 0; double u_ratio = u - x; double v_ratio = v - y; double u_o = 1 - u_ratio; double v_o = 1 - v_ratio; double r_ALP = 255; if (tex->alpha == ALPHA_MASK) { if (x == tex->width - 1 || y == tex->height - 1) return (SPRITE(tex,x,y) | 0xFF000000) & (0xFFFFFF + _RED(SMASKS(tex,x,y)) * 0x1000000); r_ALP = (_RED(SMASKS(tex,x,y)) * u_o + _RED(SMASKS(tex,x+1,y)) * u_ratio) * v_o + (_RED(SMASKS(tex,x,y+1)) * u_o + _RED(SMASKS(tex,x+1,y+1)) * u_ratio) * v_ratio; } else if (tex->alpha == ALPHA_EMBEDDED) { if (x == tex->width - 1 || y == tex->height - 1) return (SPRITE(tex,x,y) | 0xFF000000) & (0xFFFFFF + _ALP(SPRITE(tex,x,y)) * 0x1000000); r_ALP = (_ALP(SPRITE(tex,x,y)) * u_o + _ALP(SPRITE(tex,x+1,y)) * u_ratio) * v_o + (_ALP(SPRITE(tex,x,y+1)) * u_o + _ALP(SPRITE(tex,x+1,y+1)) * u_ratio) * v_ratio; } if (x == tex->width - 1 || y == tex->height - 1) return SPRITE(tex,x,y); double r_RED = (_RED(SPRITE(tex,x,y)) * u_o + _RED(SPRITE(tex,x+1,y)) * u_ratio) * v_o + (_RED(SPRITE(tex,x,y+1)) * u_o + _RED(SPRITE(tex,x+1,y+1)) * u_ratio) * v_ratio; double r_BLU = (_BLU(SPRITE(tex,x,y)) * u_o + _BLU(SPRITE(tex,x+1,y)) * u_ratio) * v_o + (_BLU(SPRITE(tex,x,y+1)) * u_o + _BLU(SPRITE(tex,x+1,y+1)) * u_ratio) * v_ratio; double r_GRE = (_GRE(SPRITE(tex,x,y)) * u_o + _GRE(SPRITE(tex,x+1,y)) * u_ratio) * v_o + (_GRE(SPRITE(tex,x,y+1)) * u_o + _GRE(SPRITE(tex,x+1,y+1)) * u_ratio) * v_ratio; return rgb(r_RED,r_GRE,r_BLU) & (0xFFFFFF + (int)r_ALP * 0x1000000); }
uint32_t alpha_blend(uint32_t bottom, uint32_t top, uint32_t mask) { uint8_t a = _RED(mask); uint8_t red = (_RED(bottom) * (255 - a) + _RED(top) * a) / 255; uint8_t gre = (_GRE(bottom) * (255 - a) + _GRE(top) * a) / 255; uint8_t blu = (_BLU(bottom) * (255 - a) + _BLU(top) * a) / 255; uint8_t alp = (int)a + (int)_ALP(bottom) > 255 ? 255 : a + _ALP(bottom); return rgba(red,gre,blu, alp); }
static void _box_blur_vertical(gfx_context_t * _src, int radius) { uint32_t * p = (uint32_t *)_src->backbuffer; int w = _src->width; int h = _src->height; int half_radius = radius / 2; uint32_t * out_color = calloc(sizeof(uint32_t), h); int old_offset = -(half_radius + 1) * w; int new_offset = (half_radius) * w; for (int x = 0; x < w; x++) { int hits = 0; int r = 0; int g = 0; int b = 0; int a = 0; int index = -half_radius * w + x; for (int y = -half_radius; y < h; y++) { int old_p = y - half_radius - 1; if (old_p >= 0) { uint32_t col = p[clamp(index + old_offset, 0, w*h-1)]; if (col != 0) { r -= _RED(col); g -= _GRE(col); b -= _BLU(col); a -= _ALP(col); } hits--; } int newPixel = y + half_radius; if (newPixel < h) { uint32_t col = p[clamp(index + new_offset, 0, w*h-1)]; if (col != 0) { r += _RED(col); g += _GRE(col); b += _BLU(col); a += _ALP(col); } hits++; } if (y >= 0) { out_color[y] = rgba(r / hits, g / hits, b / hits, a / hits); } index += w; } for (int y = 0; y < h; y++) { p[y * w + x] = out_color[y]; } } free(out_color); }
uint32_t BlendPixels(uint32_t bottom, uint32_t top) { uint8_t a = _ALP(top); uint8_t b = (uint8_t)(((uint32_t)_ALP(bottom) * (255 - a)) / 255); uint8_t alp = a + b; uint8_t red = alp ? (uint8_t)(uint32_t)((_RED(bottom) * b + _RED(top) * a) / alp) : 0; uint8_t gre = alp ? (uint8_t)(uint32_t)((_GRE(bottom) * b + _GRE(top) * a) / alp) : 0; uint8_t blu = alp ? (uint8_t)(uint32_t)((_BLU(bottom) * b + _BLU(top) * a) / alp) : 0; return GetRGBA(red, gre, blu, alp); }
static void _box_blur_horizontal(gfx_context_t * _src, int radius) { uint32_t * p = (uint32_t *)_src->backbuffer; int w = _src->width; int h = _src->height; int half_radius = radius / 2; int index = 0; uint32_t * out_color = calloc(sizeof(uint32_t), w); for (int y = 0; y < h; y++) { int hits = 0; int r = 0; int g = 0; int b = 0; int a = 0; for (int x = -half_radius; x < w; x++) { int old_p = x - half_radius - 1; if (old_p >= 0) { uint32_t col = p[clamp(index + old_p, 0, w*h-1)]; if (col) { r -= _RED(col); g -= _GRE(col); b -= _BLU(col); a -= _ALP(col); } hits--; } int newPixel = x + half_radius; if (newPixel < w) { int col = p[clamp(index + newPixel, 0, w*h-1)]; if (col != 0) { r += _RED(col); g += _GRE(col); b += _BLU(col); a += _ALP(col); } hits++; } if (x >= 0) { out_color[x] = rgba(r / hits, g / hits, b / hits, a / hits); } } for (int x = 0; x < w; x++) { p[index + x] = out_color[x]; } index += w; } free(out_color); }
static void inplace_reorder(char * samples, int size) { for (int i = 0; i < size; ++i) { uint32_t c = ((uint32_t *)samples)[i]; char b = _RED(c); char g = _GRE(c); char r = _BLU(c); char a = _ALP(c); ((uint32_t *)samples)[i] = rgba(r,g,b,a); } }
uint32_t premultiply(uint32_t color) { uint16_t a = _ALP(color); uint16_t r = _RED(color); uint16_t g = _GRE(color); uint16_t b = _BLU(color); r = r * a / 255; g = g * a / 255; b = b * a / 255; return rgba(r,g,b,a); }
/* * Draw a character to a context. */ static void draw_char(FT_Bitmap * bitmap, int x, int y, uint32_t fg, gfx_context_t * ctx) { int i, j, p, q; int x_max = x + bitmap->width; int y_max = y + bitmap->rows; for (j = y, q = 0; j < y_max; j++, q++) { for ( i = x, p = 0; i < x_max; i++, p++) { uint32_t a = _ALP(fg); a = (a * bitmap->buffer[q * bitmap->width + p]) / 255; uint32_t tmp = premultiply(rgba(_RED(fg),_GRE(fg),_BLU(fg),a)); SGFX(ctx->backbuffer,i,j,ctx->width) = alpha_blend_rgba(SGFX(ctx->backbuffer,i,j,ctx->width),tmp); } } }
void context_to_png(FILE * file, gfx_context_t * ctx) { png_structp png_ptr = NULL; png_infop info_ptr = NULL; int32_t x, y; png_byte ** row_pointers = NULL; int status = -1; int depth = 8; png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); info_ptr = png_create_info_struct(png_ptr); if (setjmp(png_jmpbuf(png_ptr))) { goto png_write_failure; } png_set_IHDR(png_ptr, info_ptr, ctx->width, ctx->height, depth, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); row_pointers = png_malloc(png_ptr, ctx->height * sizeof(png_byte *)); for (y = 0; y < ctx->height; ++y) { png_byte * row = png_malloc(png_ptr, sizeof(uint8_t) * ctx->width * sizeof(uint32_t)); row_pointers[y] = row; for (x = 0; x < ctx->width; ++x) { uint32_t pixel = GFX(ctx, x, y); *row++ = _RED(pixel); *row++ = _GRE(pixel); *row++ = _BLU(pixel); *row++ = _ALP(pixel); } } png_init_io(png_ptr, file); png_set_rows(png_ptr, info_ptr, row_pointers); png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); for (y = 0; y < ctx->height; y++) { png_free(png_ptr, row_pointers[y]); } png_free(png_ptr, row_pointers); fprintf(stderr, "Done writing PNG.\n"); return; png_write_failure: fprintf(stderr, "There was an exception while trying to write out a PNG file :(\n"); return; }
uint32_t alpha_blend_rgba(uint32_t bottom, uint32_t top) { if (_ALP(bottom) == 0) return top; if (_ALP(top) == 255) return top; if (_ALP(top) == 0) return bottom; #if DONT_USE_FLOAT_FOR_ALPHA uint16_t a = _ALP(top); uint16_t c = 255 - a; uint16_t b = ((int)_ALP(bottom) * c) / 255; uint16_t alp = min16(a + b, 255); uint16_t red = min16((uint32_t)(_RED(bottom) * c + _RED(top) * 255) / 255, 255); uint16_t gre = min16((uint32_t)(_GRE(bottom) * c + _GRE(top) * 255) / 255, 255); uint16_t blu = min16((uint32_t)(_BLU(bottom) * c + _BLU(top) * 255) / 255, 255); return rgba(red,gre,blu,alp); #else double a = _ALP(top) / 255.0; double c = 1.0 - a; double b = (_ALP(bottom) / 255.0) * c; double alp = a + b; if (alp > 1.0) alp = 1.0; double red = (_RED(bottom) / 255.0) * c + (_RED(top) / 255.0); if (red > 1.0) red = 1.0; double gre = (_GRE(bottom) / 255.0) * c + (_GRE(top) / 255.0); if (gre > 1.0) gre = 1.0; double blu = (_BLU(bottom) / 255.0) * c + (_BLU(top) / 255.0); if (blu > 1.0) blu = 1.0; return rgba(red * 255, gre * 255, blu * 255, alp * 255); #endif }