Blame src/drawing.c

07b70f
3c0f7f
#include <ft2build.h></ft2build.h>
3c0f7f
#include FT_FREETYPE_H
3c0f7f
07b70f
#include <cairo.h></cairo.h>
3c0f7f
#include <cairo-ft.h></cairo-ft.h>
07b70f
07b70f
#include "private.h"
07b70f
#include "drawing.h"
07b70f
3c0f7f
3c0f7f
typedef struct _HeliFontInstance {
3c0f7f
	char *path;
3c0f7f
	cairo_font_face_t *font_face;
3c0f7f
	int refcount;
3c0f7f
} HeliFontInstance;
3c0f7f
3c0f7f
struct _Font {
3c0f7f
	HeliFontInstance *instance;
3c0f7f
};
3c0f7f
3c0f7f
3c0f7f
static int ftInitialized;
3c0f7f
static FT_Library ftLibrary;
3c0f7f
static HeliArray fontCache;
3c0f7f
07b70f
static double colorBack[4] = {1, 1, 1, 1};
07b70f
static double colorFill[4] = {0.5, 0.5, 0.5, 1};
07b70f
static double colorStroke[4] = {0, 0, 0, 1};
07b70f
static double lineWidth = 1;
07b70f
static double *path;
07b70f
static size_t pathSize;
07b70f
static size_t pathAllocated;
07b70f
07b70f
static HAlign horAlign = HALIGN_LEFT;
07b70f
static VAlign vertAlign = VALIGN_TOP;
3c0f7f
static char *fontFamily;
3c0f7f
static Font font;
07b70f
static double fontSize = 24;
07b70f
07b70f
07b70f
void background(const char *color)
07b70f
	{ heliParseColor(color, colorBack); }
07b70f
void fill(const char *color)
07b70f
	{ heliParseColor(color, colorFill); }
07b70f
void noFill()
07b70f
	{ fill("transparent"); }
07b70f
void stroke(const char *color)
07b70f
	{ heliParseColor(color, colorStroke); }
07b70f
void noStroke()
07b70f
	{ stroke("transparent"); }
07b70f
07b70f
void strokeWeight(double weight)
07b70f
	{ lineWidth = weight; }
07b70f
546e3a
const char* rgba(double r, double g, double b, double a) {
07b70f
	static char buf[1024];
07b70f
	snprintf(buf, sizeof(buf) - 1, "%f %f %f %f", r, g, b, a);
07b70f
	return buf;
07b70f
}
07b70f
546e3a
const char* rgb(double r, double g, double b)
07b70f
	{ return rgba(r, g, b, 1); }
07b70f
07b70f
void rect(double x, double y, double width, double height) {
07b70f
	resetPath();
07b70f
	moveTo(x, y);
07b70f
	lineTo(x + width, y);
07b70f
	lineTo(x + width, y + height);
07b70f
	lineTo(x, y + height);
07b70f
	closePath();
07b70f
}
07b70f
07b70f
void line(double x1, double y1, double x2, double y2) {
07b70f
	resetPath();
07b70f
	moveTo(x1, y1);
07b70f
	lineTo(x2, y2);
07b70f
	strokePath();
07b70f
}
07b70f
07b70f
void ellipse(double x, double y, double width, double height) {
07b70f
	resetPath();
07b70f
	arcPath(x, y, width, height, 0, 360);
07b70f
	closePath();
07b70f
}
07b70f
07b70f
void point(double x, double y)
07b70f
	{ ellipse(x - lineWidth*0.25, y - lineWidth*0.25, lineWidth*0.5, lineWidth*0.5); }
07b70f
07b70f
void arcPath(double x, double y, double w, double h, double start, double stop) {
07b70f
	double step = PI/180;
07b70f
	start *= PI/180;
07b70f
	stop *= PI/180;
07b70f
	if (start < stop) { double s = start; stop = start; start = s; }
07b70f
	for(double a = start; a < stop; a += step) {
07b70f
		double lx = x + cos(a)*w;
07b70f
		double ly = y + sin(a)*h;
07b70f
		lineTo(lx, ly);
07b70f
	}
07b70f
}
07b70f
07b70f
void arc(double x, double y, double w, double h, double start, double stop) {
07b70f
	resetPath();
07b70f
	arcPath(x, y, w, h, start, stop);
07b70f
	strokePath();
07b70f
}
07b70f
07b70f
void regularPolygon(double x, double y, int sides, double size) {
07b70f
	resetPath();
07b70f
	size *= 0.5;
07b70f
	moveTo(x + size, y);
07b70f
	for(int i = 1; i < sides; ++i) {
07b70f
		double a = i*PI/sides;
07b70f
		lineTo(x + size*cos(a), y + size*sin(a));
07b70f
	}
07b70f
	closePath();
07b70f
}
07b70f
07b70f
07b70f
void resetPath()
07b70f
	{ pathSize = 0; }
07b70f
07b70f
static void endPath(int close, int stroke, int fill) {
07b70f
	cairo_t *cr = heliCairo;
07b70f
	if (cr && pathSize >= 8) {
07b70f
		cairo_save(cr);
07b70f
		cairo_move_to(cr, path[0], path[1]);
07b70f
		for(int i = 2; i < pathSize; i += 2)
07b70f
			cairo_line_to(cr, path[i], path[i+1]);
07b70f
		if (close)
07b70f
			cairo_close_path(cr);
07b70f
		if (fill) {
07b70f
			cairo_set_source_rgba(cr, colorFill[0], colorFill[1], colorFill[2], colorFill[3]);
07b70f
			if (stroke) cairo_fill_preserve(cr); else cairo_fill(cr);
07b70f
		}
07b70f
		if (stroke) {
07b70f
			cairo_set_line_width(cr, lineWidth);
07b70f
			cairo_set_source_rgba(cr, colorStroke[0], colorStroke[1], colorStroke[2], colorStroke[3]);
07b70f
			cairo_stroke(cr);
07b70f
		}
07b70f
		cairo_restore(cr);
07b70f
	}
07b70f
	resetPath();
07b70f
}
07b70f
07b70f
static void pushPathPoint(double x, double y) {
07b70f
	if (pathAllocated < pathSize + 2) {
07b70f
		pathAllocated += pathAllocated/4 + 32;
a2530e
		path = realloc(path, pathAllocated*sizeof(*path));
07b70f
		memset(&path[pathSize], 0, (pathAllocated - pathSize)*sizeof(*path));
07b70f
	}
07b70f
	path[pathSize++] = x;
07b70f
	path[pathSize++] = y;
07b70f
}
07b70f
07b70f
void closePath()
07b70f
	{ endPath(TRUE, TRUE, TRUE); }
07b70f
void strokePath()
07b70f
	{ endPath(FALSE, TRUE, FALSE); }
07b70f
void lineTo(double x, double y)
07b70f
	{ pushPathPoint(x, y); }
07b70f
void moveTo(double x, double y)
07b70f
	{ resetPath(); lineTo(x, y); }
07b70f
07b70f
3c0f7f
static HeliFontInstance* loadFont(const char *path) {
3c0f7f
	static cairo_user_data_key_t key = {};
3c0f7f
	
3c0f7f
	HeliFontInstance *fi = calloc(1, sizeof(*fi));
3c0f7f
	fi->path = heliStringCopy(path);
3c0f7f
	
3c0f7f
	if (!ftInitialized) {
3c0f7f
		if (FT_Init_FreeType(&ftLibrary))
3c0f7f
			printf("Cannot initialize FreeType library");
3c0f7f
		ftInitialized = TRUE;
3c0f7f
	}
3c0f7f
	if (ftLibrary) {
3c0f7f
		FT_Face face;
3c0f7f
		if (FT_New_Face(ftLibrary, path, 0, &face)) {
3c0f7f
			printf("Cannot load font from file: %s", path);
3c0f7f
		} else {
3c0f7f
			fi->font_face = cairo_ft_font_face_create_for_ft_face(face, 0);
3c0f7f
			cairo_font_face_set_user_data(fi->font_face, &key, face, (cairo_destroy_func_t)FT_Done_Face);
3c0f7f
		}
3c0f7f
	}
3c0f7f
	
3c0f7f
	return fi;
3c0f7f
}
3c0f7f
3c0f7f
static void unloadFont(HeliFontInstance *fi) {
3c0f7f
	assert(!fi->refcount);
3c0f7f
	free(fi->path);
3c0f7f
	if (fi->font_face) cairo_font_face_destroy(fi->font_face);
3c0f7f
	free(fi);
3c0f7f
}
3c0f7f
3c0f7f
Font createFont(const char *path) {
546e3a
	if (!heliInitialized) return NULL;
3c0f7f
	Font f = calloc(1, sizeof(*f));
3c0f7f
	f->instance = (HeliFontInstance*)heliStringmapGet(&fontCache, path);
3c0f7f
	if (!f->instance) {
3c0f7f
		f->instance = loadFont(path);
3c0f7f
		heliStringmapAdd(&fontCache, path, f->instance, (HeliFreeCallback)&unloadFont);
3c0f7f
	}
3c0f7f
	++f->instance->refcount;
3c0f7f
	heliObjectRegister(f, (HeliFreeCallback)&fontDestroy);
3c0f7f
	return f;
3c0f7f
}
3c0f7f
3c0f7f
void fontDestroy(Font f) {
3c0f7f
	heliObjectUnregister(f);
3c0f7f
	if (font == f) font = NULL;
3c0f7f
	--f->instance->refcount;
3c0f7f
	if (f->instance->refcount <= 0)
3c0f7f
		heliStringmapRemove(&fontCache, f->instance->path);
3c0f7f
	free(f);
3c0f7f
}
3c0f7f
07b70f
void textAlign(HAlign hor, VAlign vert)
07b70f
	{ horAlign = hor; vertAlign = vert; }
3c0f7f
void textFontFamily(const char *family)
3c0f7f
	{ font = NULL; free(fontFamily); fontFamily = heliStringCopy(family); }
3c0f7f
void textFontDefault()
3c0f7f
	{ font = NULL; free(fontFamily); fontFamily = NULL; }
3c0f7f
void textFont(Font f)
3c0f7f
	{ font = f; }
07b70f
void textSize(double size)
07b70f
	{ fontSize = size; }
07b70f
07b70f
void text(const char *text, double x, double y) {
07b70f
	resetPath();
3c0f7f
	
07b70f
	cairo_t *cr = heliCairo;
07b70f
	if (!cr) return;
07b70f
	cairo_save(cr);
3c0f7f
	
3c0f7f
	if (font && font->instance->font_face) {
3c0f7f
		cairo_set_font_face(cr, font->instance->font_face);
3c0f7f
	} else
3c0f7f
	if (fontFamily) {
3c0f7f
		cairo_select_font_face(cr, fontFamily, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
3c0f7f
	} else {
3c0f7f
		cairo_set_font_face(cr, NULL);
3c0f7f
	}
07b70f
	cairo_set_font_size(cr, fontSize);
07b70f
	
07b70f
	cairo_text_extents_t extents;
07b70f
	cairo_text_extents(cr, text, &extents);
1015c5
	double w = extents.width;
1015c5
	double h = extents.height;
07b70f
	if (horAlign  == HALIGN_CENTER) x -= w*0.5;
07b70f
	if (horAlign  == HALIGN_RIGHT ) x -= w;
07b70f
	if (vertAlign == VALIGN_CENTER) y -= h*0.5;
07b70f
	if (vertAlign == VALIGN_BOTTOM) y -= h;
07b70f
	
07b70f
	cairo_set_source_rgba(cr, colorStroke[0], colorStroke[1], colorStroke[2], colorStroke[3]);
1015c5
	cairo_move_to(cr, x - extents.x_bearing, y - extents.y_bearing);
07b70f
	cairo_show_text(cr, text);
07b70f
	
3c0f7f
	if (font && font->instance->font_face)
3c0f7f
		cairo_set_font_face(cr, NULL);
3c0f7f
	
07b70f
	cairo_restore(cr);
07b70f
}
07b70f
650f35
void heliDrawingClearFrame(cairo_t *cr) {
07b70f
	cairo_save(cr);
07b70f
	cairo_set_source_rgba(cr, colorBack[0], colorBack[1], colorBack[2], 1);
07b70f
	cairo_paint(cr);
07b70f
	cairo_restore(cr);
07b70f
}
07b70f
650f35
void heliDrawingPrepareFrame() {
650f35
	resetPath();
650f35
	if (heliCairo) heliDrawingClearFrame(heliCairo);
650f35
}
650f35
07b70f
void heliDrawingFinish() {
07b70f
	resetPath();
07b70f
	free(path);
07b70f
	path = NULL;
07b70f
	pathAllocated = 0;
3c0f7f
	free(fontFamily);
3c0f7f
	fontFamily = NULL;
3c0f7f
	heliArrayDestroy(&fontCache);
3c0f7f
	if (ftInitialized && ftLibrary) FT_Done_FreeType(ftLibrary);
3c0f7f
	ftLibrary = NULL;
3c0f7f
	ftInitialized = FALSE;
07b70f
}