blob: edeb5173fe77a455275568c78c383c592845f79a [file] [log] [blame]
/*
* Twin - A Tiny Window System
* Copyright © 2004 Keith Packard <keithp@keithp.com>
* All rights reserved.
*
* This Library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with the Twin Library; see the file COPYING. If not,
* write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "twinint.h"
static int
_twin_current_subpath_len (twin_path_t *path)
{
int start;
if (path->nsublen)
start = path->sublen[path->nsublen-1];
else
start = 0;
return path->npoints - start;
}
twin_spoint_t
_twin_path_current_spoint (twin_path_t *path)
{
if (!path->npoints)
twin_path_move (path, 0, 0);
return path->points[path->npoints - 1];
}
twin_spoint_t
_twin_path_subpath_first_spoint (twin_path_t *path)
{
int start;
if (!path->npoints)
twin_path_move (path, 0, 0);
if (path->nsublen)
start = path->sublen[path->nsublen-1];
else
start = 0;
return path->points[start];
}
void
_twin_path_sfinish (twin_path_t *path)
{
switch (_twin_current_subpath_len(path)) {
case 1:
path->npoints--;
case 0:
return;
}
if (path->nsublen == path->size_sublen)
{
int size_sublen;
int *sublen;
if (path->size_sublen > 0)
size_sublen = path->size_sublen * 2;
else
size_sublen = 1;
if (path->sublen)
sublen = realloc (path->sublen, size_sublen * sizeof (int));
else
sublen = malloc (size_sublen * sizeof (int));
if (!sublen)
return;
path->sublen = sublen;
path->size_sublen = size_sublen;
}
path->sublen[path->nsublen] = path->npoints;
path->nsublen++;
}
void
_twin_path_smove (twin_path_t *path, twin_sfixed_t x, twin_sfixed_t y)
{
switch (_twin_current_subpath_len (path)) {
default:
_twin_path_sfinish (path);
case 0:
_twin_path_sdraw (path, x, y);
break;
case 1:
path->points[path->npoints-1].x = x;
path->points[path->npoints-1].y = y;
break;
}
}
void
_twin_path_sdraw (twin_path_t *path, twin_sfixed_t x, twin_sfixed_t y)
{
if (_twin_current_subpath_len(path) > 0 &&
path->points[path->npoints-1].x == x &&
path->points[path->npoints-1].y == y)
return;
if (path->npoints == path->size_points)
{
int size_points;
twin_spoint_t *points;
if (path->size_points > 0)
size_points = path->size_points * 2;
else
size_points = 16;
if (path->points)
points = realloc (path->points, size_points * sizeof (twin_spoint_t));
else
points = malloc (size_points * sizeof (twin_spoint_t));
if (!points)
return;
path->points = points;
path->size_points = size_points;
}
path->points[path->npoints].x = x;
path->points[path->npoints].y = y;
path->npoints++;
}
void
twin_path_move (twin_path_t *path, twin_fixed_t x, twin_fixed_t y)
{
_twin_path_smove (path,
_twin_matrix_x (&path->state.matrix, x, y),
_twin_matrix_y (&path->state.matrix, x, y));
}
void
twin_path_rmove (twin_path_t *path, twin_fixed_t dx, twin_fixed_t dy)
{
twin_spoint_t here = _twin_path_current_spoint (path);
_twin_path_smove (path,
here.x +
_twin_matrix_dx (&path->state.matrix, dx, dy),
here.y +
_twin_matrix_dy (&path->state.matrix, dx, dy));
}
void
twin_path_draw (twin_path_t *path, twin_fixed_t x, twin_fixed_t y)
{
_twin_path_sdraw (path,
_twin_matrix_x (&path->state.matrix, x, y),
_twin_matrix_y (&path->state.matrix, x, y));
}
void
twin_path_rdraw (twin_path_t *path, twin_fixed_t dx, twin_fixed_t dy)
{
twin_spoint_t here = _twin_path_current_spoint (path);
_twin_path_sdraw (path,
here.x +
_twin_matrix_dx (&path->state.matrix, dx, dy),
here.y +
_twin_matrix_dy (&path->state.matrix, dx, dy));
}
void
twin_path_close (twin_path_t *path)
{
twin_spoint_t f;
switch (_twin_current_subpath_len(path)) {
case 0:
case 1:
break;
default:
f = _twin_path_subpath_first_spoint (path);
_twin_path_sdraw (path, f.x, f.y);
break;
}
}
void
twin_path_circle (twin_path_t *path,
twin_fixed_t x,
twin_fixed_t y,
twin_fixed_t radius)
{
twin_path_ellipse (path, x, y, radius, radius);
}
void
twin_path_ellipse (twin_path_t *path,
twin_fixed_t x,
twin_fixed_t y,
twin_fixed_t x_radius,
twin_fixed_t y_radius)
{
twin_path_move (path, x + x_radius, y);
twin_path_arc (path, x, y, x_radius, y_radius, 0, TWIN_ANGLE_360);
twin_path_close (path);
}
#define twin_fixed_abs(f) ((f) < 0 ? -(f) : (f))
static twin_fixed_t
_twin_matrix_max_radius (twin_matrix_t *m)
{
return (twin_fixed_abs (m->m[0][0]) + twin_fixed_abs (m->m[0][1]) +
twin_fixed_abs (m->m[1][0]) + twin_fixed_abs (m->m[1][1]));
}
void
twin_path_arc (twin_path_t *path,
twin_fixed_t x,
twin_fixed_t y,
twin_fixed_t x_radius,
twin_fixed_t y_radius,
twin_angle_t start,
twin_angle_t extent)
{
twin_matrix_t save = twin_path_current_matrix (path);
twin_fixed_t max_radius;
int32_t sides;
int32_t n;
twin_angle_t a;
twin_angle_t first, last, step, inc;
twin_angle_t epsilon;
twin_path_translate (path, x, y);
twin_path_scale (path, x_radius, y_radius);
max_radius = _twin_matrix_max_radius (&path->state.matrix);
sides = max_radius / twin_sfixed_to_fixed (TWIN_SFIXED_TOLERANCE);
if (sides > 1024) sides = 1024;
n = 2;
while ((1 << n) < sides)
n++;
sides = (1 << n);
step = TWIN_ANGLE_360 >> n;
inc = step;
epsilon = 1;
if (extent < 0)
{
inc = -inc;
epsilon = -1;
}
first = (start + inc - epsilon) & ~(step - 1);
last = (start + extent - inc + epsilon) & ~(step - 1);
if (first != start)
twin_path_draw (path, twin_cos(start), twin_sin(start));
for (a = first; a != last; a += inc)
twin_path_draw (path, twin_cos (a), twin_sin (a));
if (last != start + extent)
twin_path_draw (path, twin_cos (start+extent), twin_sin(start+extent));
twin_path_set_matrix (path, save);
}
void
twin_path_rectangle (twin_path_t *path,
twin_fixed_t x,
twin_fixed_t y,
twin_fixed_t w,
twin_fixed_t h)
{
twin_path_move (path, x, y);
twin_path_draw (path, x+w, y);
twin_path_draw (path, x+w, y+h);
twin_path_draw (path, x, y+h);
twin_path_close (path);
}
void
twin_path_rounded_rectangle (twin_path_t *path,
twin_fixed_t x,
twin_fixed_t y,
twin_fixed_t w,
twin_fixed_t h,
twin_fixed_t x_radius,
twin_fixed_t y_radius)
{
twin_matrix_t save = twin_path_current_matrix (path);
twin_path_translate (path, x, y);
twin_path_move (path,
0, y_radius);
twin_path_arc (path, x_radius, y_radius, x_radius, y_radius,
TWIN_ANGLE_180, TWIN_ANGLE_90);
twin_path_draw (path,
w - x_radius, 0);
twin_path_arc (path, w - x_radius, y_radius, x_radius, y_radius,
TWIN_ANGLE_270, TWIN_ANGLE_90);
twin_path_draw (path,
w, h - y_radius);
twin_path_arc (path, w - x_radius, h - y_radius, x_radius, y_radius,
TWIN_ANGLE_0, TWIN_ANGLE_90);
twin_path_draw (path,
x_radius, h);
twin_path_arc (path, x_radius, h - y_radius, x_radius, y_radius,
TWIN_ANGLE_90, TWIN_ANGLE_90);
twin_path_close (path);
twin_path_set_matrix (path, save);
}
void
twin_path_lozenge (twin_path_t *path,
twin_fixed_t x,
twin_fixed_t y,
twin_fixed_t w,
twin_fixed_t h)
{
twin_fixed_t radius;
if (w > h)
radius = h / 2;
else
radius = w / 2;
twin_path_rounded_rectangle (path, x, y, w, h, radius, radius);
}
void
twin_path_tab (twin_path_t *path,
twin_fixed_t x,
twin_fixed_t y,
twin_fixed_t w,
twin_fixed_t h,
twin_fixed_t x_radius,
twin_fixed_t y_radius)
{
twin_matrix_t save = twin_path_current_matrix (path);
twin_path_translate (path, x, y);
twin_path_move (path,
0, y_radius);
twin_path_arc (path, x_radius, y_radius, x_radius, y_radius,
TWIN_ANGLE_180, TWIN_ANGLE_90);
twin_path_draw (path,
w - x_radius, 0);
twin_path_arc (path, w - x_radius, y_radius, x_radius, y_radius,
TWIN_ANGLE_270, TWIN_ANGLE_90);
twin_path_draw (path,
w, h);
twin_path_draw (path,
0, h);
twin_path_close (path);
twin_path_set_matrix (path, save);
}
void
twin_path_set_matrix (twin_path_t *path, twin_matrix_t matrix)
{
path->state.matrix = matrix;
}
twin_matrix_t
twin_path_current_matrix (twin_path_t *path)
{
return path->state.matrix;
}
void
twin_path_identity (twin_path_t *path)
{
twin_matrix_identity (&path->state.matrix);
}
void
twin_path_translate (twin_path_t *path, twin_fixed_t tx, twin_fixed_t ty)
{
twin_matrix_translate (&path->state.matrix, tx, ty);
}
void
twin_path_scale (twin_path_t *path, twin_fixed_t sx, twin_fixed_t sy)
{
twin_matrix_scale (&path->state.matrix, sx, sy);
}
void
twin_path_rotate (twin_path_t *path, twin_angle_t a)
{
twin_matrix_rotate (&path->state.matrix, a);
}
void
twin_path_set_font_size (twin_path_t *path, twin_fixed_t font_size)
{
path->state.font_size = font_size;
}
twin_fixed_t
twin_path_current_font_size (twin_path_t *path)
{
return path->state.font_size;
}
void
twin_path_set_font_style (twin_path_t *path, twin_style_t font_style)
{
path->state.font_style = font_style;
}
twin_style_t
twin_path_current_font_style (twin_path_t *path)
{
return path->state.font_style;
}
void
twin_path_set_cap_style (twin_path_t *path, twin_cap_t cap_style)
{
path->state.cap_style = cap_style;
}
twin_cap_t
twin_path_current_cap_style (twin_path_t *path)
{
return path->state.cap_style;
}
void
twin_path_empty (twin_path_t *path)
{
path->npoints = 0;
path->nsublen = 0;
}
void
twin_path_bounds (twin_path_t *path, twin_rect_t *rect)
{
twin_sfixed_t left = TWIN_SFIXED_MAX;
twin_sfixed_t top = TWIN_SFIXED_MAX;
twin_sfixed_t right = TWIN_SFIXED_MIN;
twin_sfixed_t bottom = TWIN_SFIXED_MIN;
int i;
for (i = 0; i < path->npoints; i++)
{
twin_sfixed_t x = path->points[i].x;
twin_sfixed_t y = path->points[i].y;
if (x < left) left = x;
if (x > right) right = x;
if (y < top) top = y;
if (y > bottom) bottom = y;
}
if (left >= right || top >= bottom)
left = right = top = bottom = 0;
rect->left = twin_sfixed_trunc (left);
rect->top = twin_sfixed_trunc (top);
rect->right = twin_sfixed_trunc (twin_sfixed_ceil (right));
rect->bottom = twin_sfixed_trunc (twin_sfixed_ceil (bottom));
}
void
twin_path_append (twin_path_t *dst, twin_path_t *src)
{
int p;
int s = 0;
for (p = 0; p < src->npoints; p++)
{
if (s < src->nsublen && p == src->sublen[s])
{
_twin_path_sfinish (dst);
s++;
}
_twin_path_sdraw (dst, src->points[p].x, src->points[p].y);
}
}
twin_state_t
twin_path_save (twin_path_t *path)
{
return path->state;
}
void
twin_path_restore (twin_path_t *path, twin_state_t *state)
{
path->state = *state;
}
twin_path_t *
twin_path_create (void)
{
twin_path_t *path;
path = malloc (sizeof (twin_path_t));
path->npoints = path->size_points = 0;
path->nsublen = path->size_sublen = 0;
path->points = 0;
path->sublen = 0;
twin_matrix_identity (&path->state.matrix);
path->state.font_size = TWIN_FIXED_ONE * 15;
path->state.font_style = TWIN_TEXT_ROMAN;
path->state.cap_style = TwinCapRound;
return path;
}
void
twin_path_destroy (twin_path_t *path)
{
if (path->points)
free (path->points);
if (path->sublen)
free (path->sublen);
free (path);
}
void
twin_composite_path (twin_pixmap_t *dst,
twin_operand_t *src,
twin_coord_t src_x,
twin_coord_t src_y,
twin_path_t *path,
twin_operator_t operator)
{
twin_rect_t bounds;
twin_pixmap_t *mask;
twin_operand_t msk;
twin_coord_t width, height;
twin_path_bounds (path, &bounds);
if (bounds.left >= bounds.right || bounds.top >= bounds.bottom)
return;
width = bounds.right - bounds.left;
height = bounds.bottom - bounds.top;
mask = twin_pixmap_create (TWIN_A8, width, height);
if (!mask)
return;
twin_fill_path (mask, path, -bounds.left, -bounds.top);
msk.source_kind = TWIN_PIXMAP;
msk.u.pixmap = mask;
twin_composite (dst, bounds.left, bounds.top,
src, src_x + bounds.left, src_y + bounds.top,
&msk, 0, 0, operator, width, height);
twin_pixmap_destroy (mask);
}
void
twin_paint_path (twin_pixmap_t *dst,
twin_argb32_t argb,
twin_path_t *path)
{
twin_operand_t src;
src.source_kind = TWIN_SOLID;
src.u.argb = argb;
twin_composite_path (dst, &src, 0, 0, path, TWIN_OVER);
}
void
twin_composite_stroke (twin_pixmap_t *dst,
twin_operand_t *src,
twin_coord_t src_x,
twin_coord_t src_y,
twin_path_t *stroke,
twin_fixed_t pen_width,
twin_operator_t operator)
{
twin_path_t *pen = twin_path_create ();
twin_path_t *path = twin_path_create ();
twin_matrix_t m = twin_path_current_matrix (stroke);
m.m[2][0] = 0;
m.m[2][1] = 0;
twin_path_set_matrix (pen, m);
twin_path_set_cap_style (path, twin_path_current_cap_style (stroke));
twin_path_circle (pen, 0, 0, pen_width / 2);
twin_path_convolve (path, stroke, pen);
twin_composite_path (dst, src, src_x, src_y, path, operator);
twin_path_destroy (path);
twin_path_destroy (pen);
}
void
twin_paint_stroke (twin_pixmap_t *dst,
twin_argb32_t argb,
twin_path_t *stroke,
twin_fixed_t pen_width)
{
twin_operand_t src;
src.source_kind = TWIN_SOLID;
src.u.argb = argb;
twin_composite_stroke (dst, &src, 0, 0, stroke, pen_width, TWIN_OVER);
}