4f2c122af5
All graphics library commands now take a gfx_context_t pointer, which points to a simple datastructure describing a rendering context (width, height, depth, total size, front buffer, backbuffer; where backbuffer = front buffer when not in double-buffering mode, thus we always render to backbuffer except on a flip). This may have caused a minor speed reduction, but I don't really care as it's far more important that we support multiple graphics contexts. TODO: - Shared Memory Fonts library (there are a couple of apps that use these so-called "shmem fonts" on their own; we need a dedicated library for them) - Break off "TTK" GUI toolkit into its own library. Since it's just a callback-based button framework, this shouldn't be too hard right now. Also, with the previous tick, I'll be able to put labels on controls and start using text in more places.
254 lines
7.9 KiB
C
254 lines
7.9 KiB
C
/* vim: tabstop=4 shiftwidth=4 noexpandtab
|
|
*
|
|
* Graphics library
|
|
*/
|
|
|
|
#include <syscall.h>
|
|
#include <stdint.h>
|
|
#include <math.h>
|
|
#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) {
|
|
float a = _RED(mask) / 256.0;
|
|
uint8_t red = _RED(bottom) * (1.0 - a) + _RED(top) * a;
|
|
uint8_t gre = _GRE(bottom) * (1.0 - a) + _GRE(top) * a;
|
|
uint8_t blu = _BLU(bottom) * (1.0 - a) + _BLU(top) * a;
|
|
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, uint16_t x0, uint16_t x1, uint16_t y0, uint16_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) {
|
|
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_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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void draw_line_thick(gfx_context_t * ctx, uint16_t x0, uint16_t x1, uint16_t y0, uint16_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) {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|