Source

watches / arcs / draw.c

Full commit
/** \file
 * Drawing functions.
 */
#include "draw.h"


void
draw_pixel(
	color24_t c,
	int32_t x,
	int32_t y
)
{
	//printf("pix %d %d @ %d %d %d\n", x, y, c.red, c.blue, c.green);
	if (x >= VSCREEN_WIDTH || y >= VSCREEN_HEIGHT)
		return;
	if (x < 0 || y < 0)
		return;

	x >>= VSCREEN_SHIFT;
	y >>= VSCREEN_SHIFT;
	pulse_set_draw_window(x, y, x, y);
	pulse_draw_point24(c);
}



static inline int32_t
vscreen_ipart(
	int32_t x
)
{
	return x & ~(VSCREEN_PIXEL - 1);
}


static inline int32_t
vscreen_fpart(
	int32_t x
)
{
	return x & (VSCREEN_PIXEL - 1);
}


static inline int32_t
vscreen_rfpart(
	int32_t x
)
{
	return VSCREEN_PIXEL - vscreen_fpart(x);
}


static inline int32_t
vscreen_round(
	int32_t x
)
{
	return vscreen_ipart(x + (VSCREEN_PIXEL / 2));
}


static void
draw_pixel_aa(
	color24_t c,
	int32_t x,
	int32_t y,
	uint32_t frac
)
{
	if (c.red)
		c.red = (c.red * frac) >> VSCREEN_SHIFT;
	if (c.blue)
		c.blue = (c.blue * frac) >> VSCREEN_SHIFT;
	if (c.green)
		c.green = (c.green * frac) >> VSCREEN_SHIFT;

	draw_pixel(c, x, y);
}


/** Draw using Xiaolin Wu's line algorithm */
#define GRAD_SCALE 256

void
draw_line_horiz(
	color24_t c,
	int32_t x1,
	int32_t y1,
	int32_t x2,
	int32_t y2
)
{
	const int32_t dx = x2 - x1;
	const int32_t dy = y2 - y1;

	if (x2 < x1)
	{
		int32_t temp = x1;
		x1 = x2;
		x2 = temp;
		temp = y1;
		y1 = y2;
		y2 = temp;
	}

	// The gradient is scaled by another shift factor since
	// otherwise we won't hit the end points correctly.
	const int32_t grad = (dy * GRAD_SCALE * VSCREEN_PIXEL) / dx;
	const int32_t half_pixel = VSCREEN_PIXEL / 2;

	int32_t xend, yend, xgap;

	// Handle first endpoint
	xend = vscreen_round(x1);
	yend = y1 + (grad * (xend - x1)) / GRAD_SCALE / VSCREEN_PIXEL;
	xgap = vscreen_rfpart(x1 + half_pixel);
	const int32_t xpxl1 = xend;
	const int32_t ypxl1 = vscreen_ipart(yend);
	draw_pixel_aa(c, xpxl1, ypxl1, (vscreen_rfpart(yend) * xgap) / VSCREEN_PIXEL);
	draw_pixel_aa(c, xpxl1, ypxl1 + VSCREEN_PIXEL, (vscreen_fpart(yend) * xgap) / VSCREEN_PIXEL);
	int32_t intery = yend * GRAD_SCALE + grad; // first y-intersection

	//printf("%d %d %d => %d %d grad %d\n", x1, xend, y1, x2, y2, grad);


	// Handle second endpoint
	xend = vscreen_round(x2);
	yend = y2 + (grad * (xend - x2)) / GRAD_SCALE / VSCREEN_PIXEL;
	xgap = vscreen_fpart(x2 + half_pixel);
	const int32_t xpxl2 = xend;
	const int32_t ypxl2 = vscreen_ipart(yend);
	draw_pixel_aa(c, xpxl2, ypxl2, (vscreen_rfpart(yend) * xgap) / VSCREEN_PIXEL);
	draw_pixel_aa(c, xpxl2, ypxl2 + VSCREEN_PIXEL, (vscreen_fpart(yend) * xgap) / VSCREEN_PIXEL);

	//printf("x: %d %d\n", xpxl1 + 1, xpxl2);

	// Loop (this could make a single window draw)
	for (int32_t x = xpxl1 + VSCREEN_PIXEL ; x < xpxl2 ; x += VSCREEN_PIXEL)
	{
		int32_t y = intery / GRAD_SCALE;
		intery += grad;
		draw_pixel_aa(c, x, vscreen_ipart(y), vscreen_rfpart(y));
		draw_pixel_aa(c, x, vscreen_ipart(y)+VSCREEN_PIXEL, vscreen_fpart(y));
	}
}


static void
draw_line_vert(
	color24_t c,
	int32_t x1,
	int32_t y1,
	int32_t x2,
	int32_t y2
)
{
	const int32_t dx = x2 - x1;
	const int32_t dy = y2 - y1;

	if (y2 < y1)
	{
		int32_t temp = x1;
		x1 = x2;
		x2 = temp;
		temp = y1;
		y1 = y2;
		y2 = temp;
	}

	const int32_t grad = (dx * VSCREEN_PIXEL * GRAD_SCALE) / dy;
	const int32_t half_pixel = VSCREEN_PIXEL / 2;

	int32_t xend, yend, ygap;

	// Handle first endpoint
	yend = vscreen_round(y1);
	xend = x1 + (grad * (yend - y1)) / GRAD_SCALE / VSCREEN_PIXEL;
	ygap = vscreen_rfpart(y1 + half_pixel);
	const int32_t ypxl1 = yend;
	const int32_t xpxl1 = vscreen_ipart(xend);
	draw_pixel_aa(c, xpxl1, ypxl1, (vscreen_rfpart(xend) * ygap) / VSCREEN_PIXEL);
	draw_pixel_aa(c, xpxl1 + VSCREEN_PIXEL, ypxl1, (vscreen_fpart(xend) * ygap) / VSCREEN_PIXEL);
	int32_t interx = xend * GRAD_SCALE + grad; // first x-intersection


	// Handle second endpoint
	yend = vscreen_round(y2);
	xend = x2 + (grad * (yend - y2)) / GRAD_SCALE / VSCREEN_PIXEL;
	ygap = vscreen_fpart(y2 + half_pixel);
	const int32_t ypxl2 = yend;
	const int32_t xpxl2 = vscreen_ipart(xend);
	draw_pixel_aa(c, xpxl2, ypxl2, (vscreen_rfpart(xend) * ygap) / VSCREEN_PIXEL);
	draw_pixel_aa(c, xpxl2 + VSCREEN_PIXEL, ypxl2, (vscreen_fpart(xend) * ygap) / VSCREEN_PIXEL);

	//printf("x: %d %d\n", xpxl1 + 1, xpxl2);

	// Loop (this could make a single window draw)
	for (int32_t y = ypxl1 + VSCREEN_PIXEL ; y < ypxl2 ; y += VSCREEN_PIXEL)
	{
		int32_t x = interx / GRAD_SCALE;
		interx += grad;

		draw_pixel_aa(c, vscreen_ipart(x), y, vscreen_rfpart(x));
		draw_pixel_aa(c, vscreen_ipart(x)+VSCREEN_PIXEL, y, vscreen_fpart(x));
	}
}


void
draw_line(
	color24_t c,
	int32_t x0,
	int32_t y0,
	int32_t x1,
	int32_t y1
)
{
	const int32_t dx = x1 > x0 ? x1 - x0 : x0 - x1;
	const int32_t dy = y1 > y0 ? y1 - y0 : y0 - y1;
	if (dx == 0 && dy == 0)
		draw_pixel(c, x0, y0);
	else
	if (dx > dy)
		draw_line_horiz(c, x0, y0, x1, y1);
	else
		draw_line_vert(c, x0, y0, x1, y1);

/*
	// Mark where we should start and end
	draw_pixel(COLOR_WHITE, x0, y0);
	draw_pixel(COLOR_BLUE, x1, y1);
*/
}




static char buf[2];
struct PWidgetTextDynamic font;

void
draw_monostring(
	int32_t x,
	int32_t y,
	color24_t color,
	PulseResource font_name,
	int width,
	const char * s
)
{
	pulse_init_dynamic_text_widget(
		&font,
		buf,
		font_name,
		color,
		PWTS_TRUNCATE
	);

	uint32_t orig_x = x >> VSCREEN_SHIFT;

	struct PWTextBox box = {
		.left		= orig_x,
		.top		= y >> VSCREEN_SHIFT,
		.right		= SCREEN_WIDTH - 1,
		.bottom		= SCREEN_HEIGHT - 1,
	};

	buf[1] = '\0';

	while ((buf[0] = *s++))
	{
		pulse_render_text(&box, &font);
		box.left += width;
	}
}