/* vim: tabstop=4 shiftwidth=4 noexpandtab * * Graphics library */ #include #include #include #include "graphics.h" #include "window.h" DEFN_SYSCALL0(getgraphicsaddress, 11); DEFN_SYSCALL1(kbd_mode, 12, int); DEFN_SYSCALL0(kbd_get, 13); DEFN_SYSCALL1(setgraphicsoffset, 16, int); DEFN_SYSCALL0(getgraphicswidth, 18); DEFN_SYSCALL0(getgraphicsheight, 19); DEFN_SYSCALL0(getgraphicsdepth, 20); /* Pointer to graphics memory */ void flip(gfx_context_t * ctx) { memcpy(ctx->buffer, ctx->backbuffer, ctx->size); } void clearbuffer(gfx_context_t * ctx) { memset(ctx->backbuffer, 0, ctx->size); } /* Deprecated */ gfx_context_t * init_graphics_fullscreen() { gfx_context_t * out = malloc(sizeof(gfx_context_t)); out->width = syscall_getgraphicswidth(); out->height = syscall_getgraphicsheight(); out->depth = syscall_getgraphicsdepth(); out->size = GFX_H(out) * GFX_W(out) * GFX_B(out); out->buffer = (void *)syscall_getgraphicsaddress(); out->backbuffer = out->buffer; return out; } gfx_context_t * init_graphics_fullscreen_double_buffer() { gfx_context_t * out = init_graphics_fullscreen(); out->backbuffer = malloc(sizeof(uint32_t) * GFX_W(out) * GFX_H(out)); return out; } gfx_context_t * init_graphics_window(window_t * window) { gfx_context_t * out = malloc(sizeof(gfx_context_t)); out->width = window->width; out->height = window->height; out->depth = 32; out->size = GFX_H(out) * GFX_W(out) * GFX_B(out); out->buffer = window->buffer; out->backbuffer = out->buffer; return out; } gfx_context_t * init_graphics_window_double_buffer(window_t * window) { gfx_context_t * out = init_graphics_window(window); out->backbuffer = malloc(sizeof(uint32_t) * GFX_W(out) * GFX_H(out)); return out; } uint32_t rgb(uint8_t r, uint8_t g, uint8_t b) { return 0xFF000000 + (r * 0x10000) + (g * 0x100) + (b * 0x1); } uint32_t alpha_blend(uint32_t bottom, uint32_t top, uint32_t mask) { uint8_t a = _RED(mask); uint8_t red = (_RED(bottom) * (256 - a) + _RED(top) * a) / 256; uint8_t gre = (_GRE(bottom) * (256 - a) + _GRE(top) * a) / 256; uint8_t blu = (_BLU(bottom) * (256 - a) + _BLU(top) * a) / 256; return rgb(red,gre,blu); } void load_sprite(sprite_t * sprite, char * filename) { /* Open the requested binary */ FILE * image = fopen(filename, "r"); size_t image_size= 0; fseek(image, 0, SEEK_END); image_size = ftell(image); fseek(image, 0, SEEK_SET); /* Alright, we have the length */ char * bufferb = malloc(image_size); fread(bufferb, image_size, 1, image); uint16_t x = 0; /* -> 212 */ uint16_t y = 0; /* -> 68 */ /* Get the width / height of the image */ signed int *bufferi = (signed int *)((uintptr_t)bufferb + 2); uint32_t width = bufferi[4]; uint32_t height = bufferi[5]; uint16_t bpp = bufferi[6] / 0x10000; uint32_t row_width = (bpp * width + 31) / 32 * 4; /* Skip right to the important part */ size_t i = bufferi[2]; sprite->width = width; sprite->height = height; sprite->bitmap = malloc(sizeof(uint32_t) * width * height); for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { if (i > image_size) return; /* Extract the color */ uint32_t color; if (bpp == 24) { color = (bufferb[i + 3 * x] & 0xFF) + (bufferb[i+1 + 3 * x] & 0xFF) * 0x100 + (bufferb[i+2 + 3 * x] & 0xFF) * 0x10000; } else if (bpp == 32) { color = (bufferb[i + 4 * x] & 0xFF) * 0x1000000 + (bufferb[i+1 + 4 * x] & 0xFF) * 0x100 + (bufferb[i+2 + 4 * x] & 0xFF) * 0x10000 + (bufferb[i+3 + 4 * x] & 0xFF) * 0x1; } /* Set our point */ sprite->bitmap[(height - y - 1) * width + x] = color; } i += row_width; } free(bufferb); } static inline int32_t min(int32_t a, int32_t b) { return (a < b) ? a : b; } static inline int32_t max(int32_t a, int32_t b) { return (a > b) ? a : b; } void draw_sprite(gfx_context_t * ctx, sprite_t * sprite, int32_t x, int32_t y) { int32_t _left = max(x, 0); int32_t _top = max(y, 0); int32_t _right = min(x + sprite->width, ctx->width - 1); int32_t _bottom = min(y + sprite->height, ctx->height - 1); for (uint16_t _y = 0; _y < sprite->height; ++_y) { for (uint16_t _x = 0; _x < sprite->width; ++_x) { if (x + _x < _left || x + _x > _right || y + _y < _top || y + _y > _bottom) continue; if (sprite->alpha) { GFX(ctx, x + _x, y + _y) = alpha_blend(GFX(ctx, x + _x, y + _y), SPRITE(sprite, _x, _y), SMASKS(sprite, _x, _y)); } else { if (SPRITE(sprite,_x,_y) != sprite->blank) { GFX(ctx, x + _x, y + _y) = SPRITE(sprite, _x, _y); } } } } } void draw_line(gfx_context_t * ctx, int32_t x0, int32_t x1, int32_t y0, int32_t y1, uint32_t color) { int deltax = abs(x1 - x0); int deltay = abs(y1 - y0); int sx = (x0 < x1) ? 1 : -1; int sy = (y0 < y1) ? 1 : -1; int error = deltax - deltay; while (1) { if (x0 >= 0 && y0 >= 0 && x0 < ctx->width && y0 < ctx->height) { GFX(ctx, x0, y0) = color; } if (x0 == x1 && y0 == y1) break; int e2 = 2 * error; if (e2 > -deltay) { error -= deltay; x0 += sx; } if (e2 < deltax) { error += deltax; y0 += sy; } } } void draw_line_thick(gfx_context_t * ctx, int32_t x0, int32_t x1, int32_t y0, int32_t y1, uint32_t color, char thickness) { int deltax = abs(x1 - x0); int deltay = abs(y1 - y0); int sx = (x0 < x1) ? 1 : -1; int sy = (y0 < y1) ? 1 : -1; int error = deltax - deltay; while (1) { for (char j = -thickness; j <= thickness; ++j) { for (char i = -thickness; i <= thickness; ++i) { if (x0 + i >= 0 && x0 + i < ctx->width && y0 + j >= 0 && y0 + j < ctx->height) { GFX(ctx, x0 + i, y0 + j) = color; } } } if (x0 == x1 && y0 == y1) break; int e2 = 2 * error; if (e2 > -deltay) { error -= deltay; x0 += sx; } if (e2 < deltax) { error += deltax; y0 += sy; } } } void draw_fill(gfx_context_t * ctx, uint32_t color) { for (uint16_t y = 0; y < ctx->height; ++y) { for (uint16_t x = 0; x < ctx->width; ++x) { GFX(ctx, x, y) = color; } } } /* 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 = 256; if (tex->alpha) { 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; } 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); } void draw_sprite_scaled(gfx_context_t * ctx, sprite_t * sprite, uint16_t x, uint16_t y, uint16_t width, uint16_t height) { int32_t _left = max(x, 0); int32_t _top = max(y, 0); int32_t _right = min(x + width, ctx->width - 1); int32_t _bottom = min(y + height, ctx->height - 1); for (uint16_t _y = 0; _y < height; ++_y) { for (uint16_t _x = 0; _x < width; ++_x) { if (x + _x < _left || x + _x > _right || y + _y < _top || y + _y > _bottom) continue; if (sprite->alpha) { uint32_t n_color = getBilinearFilteredPixelColor(sprite, (double)_x / (double)width, (double)_y/(double)height); uint32_t f_color = rgb(_ALP(n_color), 0, 0); GFX(ctx, x + _x, y + _y) = alpha_blend(GFX(ctx, x + _x, y + _y), n_color, f_color); } else { GFX(ctx, x + _x, y + _y) = getBilinearFilteredPixelColor(sprite, (double)_x / (double)width, (double)_y/(double)height); } } } }