wip sdf font renderer

This commit is contained in:
K. Lange 2018-04-18 13:27:37 +09:00 committed by Kevin Lange
parent 9f14275bbc
commit 4ebeee76db
6 changed files with 346 additions and 0 deletions

157
apps/sdf-demo.c Normal file
View File

@ -0,0 +1,157 @@
/* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2013-2014 Kevin Lange
*/
/*
* Julia Fractal Generator
*
* This is the updated windowed version of the
* julia fractal generator demo.
*/
#include <stdio.h>
#include <stdint.h>
#include <syscall.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <getopt.h>
#include <toaru/yutani.h>
#include <toaru/graphics.h>
#include <toaru/decorations.h>
#include <toaru/sdf.h>
#define DIRECT_OFFSET(x,y) ((x) + (y) * window->width)
/*
* Macros make verything easier.
*/
#define SPRITE(sprite,x,y) sprite->bitmap[sprite->width * (y) + (x)]
#define GFX_(xpt, ypt) ((uint32_t *)window->buffer)[DIRECT_OFFSET(xpt+decor_left_width,ypt+decor_top_height)]
/* Pointer to graphics memory */
static yutani_t * yctx;
static yutani_window_t * window = NULL;
static gfx_context_t * ctx = NULL;
static int width = 500;
static int height = 500;
static int left = 200;
static int top = 200;
static int size = 30;
static void decors() {
render_decorations(window, ctx, "SDF Demo");
}
void redraw() {
draw_fill(ctx, rgb(255,255,255));
decors();
draw_sdf_string(ctx, 30, 30, "Hello, world!", size, rgb(0,0,0));
}
void resize_finish(int w, int h) {
yutani_window_resize_accept(yctx, window, w, h);
reinit_graphics_yutani(ctx, window);
width = w - decor_left_width - decor_right_width;
height = h - decor_top_height - decor_bottom_height;
redraw();
yutani_window_resize_done(yctx, window);
yutani_flip(yctx, window);
}
int main(int argc, char * argv[]) {
yctx = yutani_init();
init_decorations();
window = yutani_window_create(yctx, width + decor_width(), height + decor_height());
yutani_window_move(yctx, window, left, top);
yutani_window_advertise_icon(yctx, window, "SDF Demo", "sdf");
ctx = init_graphics_yutani(window);
redraw();
yutani_flip(yctx, window);
int playing = 1;
while (playing) {
yutani_msg_t * m = yutani_poll(yctx);
if (m) {
switch (m->type) {
case YUTANI_MSG_KEY_EVENT:
{
struct yutani_msg_key_event * ke = (void*)m->data;
if (ke->event.action == KEY_ACTION_DOWN && ke->event.keycode == 'q') {
playing = 0;
} else if (ke->event.action == KEY_ACTION_DOWN) {
if (size <= 20) {
size += 1;
} else if (size > 20) {
size += 5;
}
if (size > 100) {
size = 1;
}
redraw();
yutani_flip(yctx,window);
}
}
break;
case YUTANI_MSG_WINDOW_FOCUS_CHANGE:
{
struct yutani_msg_window_focus_change * wf = (void*)m->data;
yutani_window_t * win = hashmap_get(yctx->windows, (void*)wf->wid);
if (win) {
win->focused = wf->focused;
decors();
yutani_flip(yctx, window);
}
}
break;
case YUTANI_MSG_RESIZE_OFFER:
{
struct yutani_msg_window_resize * wr = (void*)m->data;
resize_finish(wr->width, wr->height);
}
break;
case YUTANI_MSG_WINDOW_MOUSE_EVENT:
{
int result = decor_handle_event(yctx, m);
switch (result) {
case DECOR_CLOSE:
playing = 0;
break;
default:
/* Other actions */
break;
}
}
break;
case YUTANI_MSG_SESSION_END:
playing = 0;
break;
default:
break;
}
}
free(m);
}
yutani_close(yctx, window);
return 0;
}

View File

@ -88,3 +88,5 @@ extern uint32_t premultiply(uint32_t color);
extern void gfx_add_clip(gfx_context_t * ctx, int32_t x, int32_t y, int32_t w, int32_t h);
extern void gfx_clear_clip(gfx_context_t * ctx);
extern uint32_t getBilinearFilteredPixelColor(sprite_t * tex, double u, double v);

View File

@ -0,0 +1,4 @@
#pragma once
extern int draw_sdf_character(gfx_context_t * ctx, int32_t x, int32_t y, int ch, int size, uint32_t color);
extern int draw_sdf_string(gfx_context_t * ctx, int32_t x, int32_t y, char * str, int size, uint32_t color);

BIN
base/usr/share/sdf.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 732 KiB

182
lib/sdf.c Normal file
View File

@ -0,0 +1,182 @@
/* vim: ts=4 sw=4 noexpandtab
*
* Signed Distance Field Text Library
*/
#include <stdio.h>
#include <toaru/graphics.h>
#define BASE_WIDTH 50
#define BASE_HEIGHT 50
static sprite_t _font_data;
struct {
char code;
size_t width;
} _char_data[] = {
{'!', 20},
{'"', 35},
{'#', 40},
{'$', 35},
{'%', 35},
{'&', 35},
{'\'', 35},
{'(', 35},
{')', 35},
{'*', 35},
{'+', 35},
{',', 35},
{'-', 35},
{'.', 35},
{'/', 35},
{'0', 35},
{'1', 35},
{'2', 35},
{'3', 35},
{'4', 35},
{'5', 35},
{'6', 35},
{'7', 35},
{'8', 35},
{'9', 35},
{':', 35},
{';', 35},
{'<', 35},
{'=', 35},
{'>', 35},
{'?', 35},
{'@', 50},
{'A', 35},
{'B', 35},
{'C', 35},
{'D', 35},
{'E', 35},
{'F', 35},
{'G', 35},
{'H', 35},
{'I', 35},
{'J', 35},
{'K', 35},
{'L', 35},
{'M', 40},
{'N', 35},
{'O', 35},
{'P', 35},
{'Q', 35},
{'R', 35},
{'S', 35},
{'T', 35},
{'U', 35},
{'V', 35},
{'W', 35},
{'X', 35},
{'Y', 35},
{'Z', 35},
{'[', 35},
{'\\', 35},
{']', 35},
{'^', 35},
{'_', 35},
{'`', 35},
{'a', 35},
{'b', 35},
{'c', 35},
{'d', 32},
{'e', 32},
{'f', 35},
{'g', 35},
{'h', 35},
{'i', 16},
{'j', 16},
{'k', 30},
{'l', 16},
{'m', 50},
{'n', 50},
{'o', 32},
{'p', 35},
{'q', 35},
{'r', 25},
{'s', 35},
{'t', 35},
{'u', 35},
{'v', 35},
{'w', 42},
{'x', 35},
{'y', 35},
{'z', 35},
{'{', 35},
{'|', 35},
{'}', 35},
{'~', 35},
{' ', 20},
{0,0},
};
__attribute__((constructor))
static void _init_sdf(void) {
/* Load the font. */
load_sprite(&_font_data, "/usr/share/sdf.bmp");
}
int draw_sdf_character(gfx_context_t * ctx, int32_t x, int32_t y, int ch, int size, uint32_t color) {
if (ch != ' ' && ch < '!' || ch > '~') {
/* TODO: Draw missing symbol? */
return 0;
}
/* Calculate offset into table above */
if (ch == ' ') {
ch = '~' + 1 - '!';
} else {
ch -= '!';
}
double scale = (double)size / 50.0;
size_t width = _char_data[ch].width * scale;
int fx = ((BASE_WIDTH * ch) % _font_data.width) * scale;
int fy = (((BASE_WIDTH * ch) / _font_data.width) * BASE_HEIGHT) * scale;
int height = BASE_HEIGHT * ((double)size / 50.0);
sprite_t * tmp = create_sprite(scale * _font_data.width, scale * _font_data.height, ALPHA_OPAQUE);
gfx_context_t * t = init_graphics_sprite(tmp);
draw_sprite_scaled(t, &_font_data, 0, 0, tmp->width, tmp->height);
/* ignore size */
for (int j = 0; j < height; ++j) {
for (int i = 0; i < width; ++i) {
/* TODO needs to do bilinear filter */
if (fx+i > tmp->width) continue;
if (fy+j > tmp->height) continue;
uint32_t c = SPRITE((tmp), fx+i, fy+j);
double dist = (double)_RED(c) / 255.0;
double edge0 = 0.75 - 2.0 * 1.4142 / (double)size;
double edge1 = 0.75 + 2.0 * 1.4142 / (double)size;
double a = (dist - edge0) / (edge1 - edge0);
if (a < 0.0) a = 0.0;
if (a > 1.0) a = 1.0;
a = a * a * (3 - 2 * a);
GFX(ctx,x+i,y+j) = alpha_blend(GFX(ctx,x+i,y+j), color, rgb(255*a,0,0));
}
}
sprite_free(tmp);
free(t);
return width;
}
int draw_sdf_string(gfx_context_t * ctx, int32_t x, int32_t y, char * str, int size, uint32_t color) {
int32_t out_width = 0;
while (*str) {
int w = draw_sdf_character(ctx,x,y,*str,size,color);
out_width += w;
x += w;
str++;
}
return out_width;
}

View File

@ -34,6 +34,7 @@ class Classifier(object):
'<toaru/yutani.h>': (None, '-ltoaru_yutani', ['<toaru/kbd.h>', '<toaru/list.h>', '<toaru/pex.h>', '<toaru/graphics.h>', '<toaru/hashmap.h>']),
'<toaru/decorations.h>': (None, '-ltoaru_decorations', ['<toaru/graphics.h>', '<toaru/yutani.h>','<toaru/dlfcn.h>']),
'<toaru/termemu.h>': (None, '-ltoaru_termemu', ['<toaru/graphics.h>']),
'<toaru/sdf.h>': (None, '-ltoaru_sdf', ['<toaru/graphics.h>']),
}
def __init__(self, filename):