#include "teensy_renderer.h" #include #include #include #include #include "teensy_context.h" #include "teensy_list.h" struct ty_renderer r; static bool is_renderer_init(void) { return r.screen.data != NULL && r.screen.width * r.screen.height != 0; } static void image_bounds_check(struct ty_image img, struct ty_vec2i pos) { #ifdef TEENSY_DEBUG if ( pos.x < 0 || pos.x >= img.width || pos.y < 0 || pos.y >= img.height ) { ty_log_fatal( TY_ERR_RENDER_OOB, "OOB (%d, %d). bounds are (%d, %d)", pos.x, pos.y, img.width, img.height ); } #else (void)img; (void)pos; #endif } void ty_init_renderer(void) { r.screen = ty_create_image( ctx.hints.scr_width, ctx.hints.scr_height, NULL ); } void ty_deinit_renderer(void) { assert(is_renderer_init()); ty_free_image(r.screen); } struct ty_image ty_create_image(int w, int h, const struct ty_color* data) { struct ty_image img; img.width = w; img.height = h; size_t size = sizeof(struct ty_color) * w * h; img.data = ty_alloc(size); if (data) memcpy(img.data, data, size); else memset(img.data, 0, size); return img; } void ty_free_image(struct ty_image img) { ty_free(img.data); } struct ty_color ty_img_get_pixel(struct ty_image img, struct ty_vec2i pos) { image_bounds_check(img, pos); return img.data[pos.y * img.width + pos.x]; } void ty_img_set_pixel( struct ty_image img, struct ty_vec2i pos, struct ty_color color ) { image_bounds_check(img, pos); img.data[pos.y * img.width + pos.x] = color; } // This function technically is not needed, but I may want unicode support in // the future, when an abstraction like this is needed. void ty_font_add_glyph(struct ty_font *font, uint8_t c, struct ty_image img) { font->glyphs[c] = img; } int ty_font_width(struct ty_font *font, const char *fmt, ...) { va_list args; va_start(args, fmt); int len = vsnprintf(NULL, 0, fmt, args); char *text = ty_talloc(sizeof(char) * (len + 1)); va_end(args); va_start(args, fmt); vsnprintf(text, len + 1, fmt, args); va_end(args); int width = 0; for (uint8_t *c = (uint8_t*)text; *c != '\0'; c++) width += font->glyphs[*c].width; return width; } void ty_draw_clear(struct ty_color col) { assert(is_renderer_init()); for (int i = 0; i < r.screen.width * r.screen.height; i++) { r.screen.data[i] = col; } } void ty_draw_image(struct ty_image img, struct ty_vec2i pos) { int x1 = fmin(fmax(floor(pos.x), 0), r.screen.width); int y1 = fmin(fmax(floor(pos.y), 0), r.screen.height); int x2 = fmin(fmax(floor(pos.x + img.width), 0), r.screen.width); int y2 = fmin(fmax(floor(pos.y + img.height), 0), r.screen.height); for (int dy = y1; dy < y2; dy++) { for (int dx = x1; dx < x2; dx++) { ty_img_set_pixel( r.screen, ty_vec2i(dx, dy), ty_img_get_pixel(img, ty_vec2i(dx - x1, dy - y1)) ); } } } void ty_draw_image_ex( struct ty_image img, struct ty_recti src, struct ty_recti dst ) { int x1 = fmin(fmax(floor(dst.x), 0), r.screen.width); int y1 = fmin(fmax(floor(dst.y), 0), r.screen.height); int x2 = fmin(fmax(floor(dst.x + dst.w), 0), r.screen.width); int y2 = fmin(fmax(floor(dst.y + dst.h), 0), r.screen.height); for (int dy = y1; dy < y2; dy++) { for (int dx = x1; dx < x2; dx++) { int img_x = ((dx - x1) * src.w) / dst.w + src.x; int img_y = ((dy - y1) * src.h) / dst.h + src.y; ty_img_set_pixel( r.screen, ty_vec2i(dx, dy), ty_img_get_pixel(img, ty_vec2i(img_x, img_y)) ); } } } void ty_draw_rect(struct ty_recti rect, struct ty_color color) { int x1 = fmin(fmax(floor(rect.x), 0), r.screen.width); int y1 = fmin(fmax(floor(rect.y), 0), r.screen.height); int x2 = fmin(fmax(floor(rect.x + rect.w), 0), r.screen.width); int y2 = fmin(fmax(floor(rect.y + rect.h), 0), r.screen.height); for (int dy = y1; dy < y2; dy++) { for (int dx = x1; dx < x2; dx++) { ty_img_set_pixel( r.screen, ty_vec2i(dx, dy), color ); } } } void ty_draw_line( struct ty_vec2i start, struct ty_vec2i end, struct ty_color color ) { int dx = end.x - start.x; int dy = end.y - start.y; if (abs(dx) > abs(dy)) { if (start.x > end.x) { int tmp = start.x; start.x = end.x; end.x = tmp; tmp = start.y; start.y = end.y; end.y = tmp; } for (int x = start.x; x < end.x; x++) { int y = start.y + dy * (x - start.x) / dx; ty_img_set_pixel( r.screen, ty_vec2i(x, y), color ); } } else { if (start.y > end.y) { int tmp = start.x; start.x = end.x; end.x = tmp; tmp = start.y; start.y = end.y; end.y = tmp; } for (int y = start.y; y < end.y; y++) { int x = start.x + dx * (y - start.y) / dy; ty_img_set_pixel( r.screen, ty_vec2i(x, y), color ); } } } void ty_draw_text(struct ty_font *font, struct ty_vec2i pos, const char *text) { for (const uint8_t *c = (const uint8_t*)text; *c != '\0'; c++) { struct ty_image glyph = font->glyphs[*c]; ty_draw_image(glyph, pos); pos.x += glyph.width; } } void ty_draw_text_fmt( struct ty_font *font, struct ty_vec2i pos, const char *fmt, ... ) { va_list args; va_start(args, fmt); int len = vsnprintf(NULL, 0, fmt, args); char *text = ty_talloc(sizeof(char) * (len + 1)); va_end(args); va_start(args, fmt); vsnprintf(text, len + 1, fmt, args); va_end(args); ty_draw_text(font, pos, text); } void ty_draw_end(void) { assert(is_renderer_init()); }