Split unix/draw.c into a bunch of smaller files.
This commit is contained in:
parent
45dd9416d3
commit
4e53551e06
|
@ -11,6 +11,9 @@ CFILES += \
|
||||||
unix/control.c \
|
unix/control.c \
|
||||||
unix/datetimepicker.c \
|
unix/datetimepicker.c \
|
||||||
unix/draw.c \
|
unix/draw.c \
|
||||||
|
unix/drawmatrix.c \
|
||||||
|
unix/drawpath.c \
|
||||||
|
unix/drawtext.c \
|
||||||
unix/entry.c \
|
unix/entry.c \
|
||||||
unix/fontbutton.c \
|
unix/fontbutton.c \
|
||||||
unix/group.c \
|
unix/group.c \
|
||||||
|
@ -30,6 +33,7 @@ CFILES += \
|
||||||
unix/window.c
|
unix/window.c
|
||||||
|
|
||||||
HFILES += \
|
HFILES += \
|
||||||
|
unix/draw.h \
|
||||||
unix/uipriv_unix.h
|
unix/uipriv_unix.h
|
||||||
|
|
||||||
# TODO split into a separate file or put in GNUmakefile.libui somehow?
|
# TODO split into a separate file or put in GNUmakefile.libui somehow?
|
||||||
|
|
666
unix/draw.c
666
unix/draw.c
|
@ -1,200 +1,6 @@
|
||||||
// 6 september 2015
|
// 6 september 2015
|
||||||
#include "uipriv_unix.h"
|
#include "uipriv_unix.h"
|
||||||
|
#include "draw.h"
|
||||||
struct uiDrawPath {
|
|
||||||
GArray *pieces;
|
|
||||||
uiDrawFillMode fillMode;
|
|
||||||
gboolean ended;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct piece {
|
|
||||||
int type;
|
|
||||||
double d[8];
|
|
||||||
int b;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum {
|
|
||||||
newFigure,
|
|
||||||
newFigureArc,
|
|
||||||
lineTo,
|
|
||||||
arcTo,
|
|
||||||
bezierTo,
|
|
||||||
closeFigure,
|
|
||||||
addRect,
|
|
||||||
};
|
|
||||||
|
|
||||||
uiDrawPath *uiDrawNewPath(uiDrawFillMode mode)
|
|
||||||
{
|
|
||||||
uiDrawPath *p;
|
|
||||||
|
|
||||||
p = uiNew(uiDrawPath);
|
|
||||||
p->pieces = g_array_new(FALSE, TRUE, sizeof (struct piece));
|
|
||||||
p->fillMode = mode;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawFreePath(uiDrawPath *p)
|
|
||||||
{
|
|
||||||
g_array_free(p->pieces, TRUE);
|
|
||||||
uiFree(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void add(uiDrawPath *p, struct piece *piece)
|
|
||||||
{
|
|
||||||
if (p->ended)
|
|
||||||
complain("path ended in add()");
|
|
||||||
g_array_append_vals(p->pieces, piece, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawPathNewFigure(uiDrawPath *p, double x, double y)
|
|
||||||
{
|
|
||||||
struct piece piece;
|
|
||||||
|
|
||||||
piece.type = newFigure;
|
|
||||||
piece.d[0] = x;
|
|
||||||
piece.d[1] = y;
|
|
||||||
add(p, &piece);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawPathNewFigureWithArc(uiDrawPath *p, double xCenter, double yCenter, double radius, double startAngle, double sweep, int negative)
|
|
||||||
{
|
|
||||||
struct piece piece;
|
|
||||||
|
|
||||||
if (sweep > 2 * M_PI)
|
|
||||||
sweep = 2 * M_PI;
|
|
||||||
piece.type = newFigureArc;
|
|
||||||
piece.d[0] = xCenter;
|
|
||||||
piece.d[1] = yCenter;
|
|
||||||
piece.d[2] = radius;
|
|
||||||
piece.d[3] = startAngle;
|
|
||||||
piece.d[4] = sweep;
|
|
||||||
piece.b = negative;
|
|
||||||
add(p, &piece);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawPathLineTo(uiDrawPath *p, double x, double y)
|
|
||||||
{
|
|
||||||
struct piece piece;
|
|
||||||
|
|
||||||
piece.type = lineTo;
|
|
||||||
piece.d[0] = x;
|
|
||||||
piece.d[1] = y;
|
|
||||||
add(p, &piece);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawPathArcTo(uiDrawPath *p, double xCenter, double yCenter, double radius, double startAngle, double sweep, int negative)
|
|
||||||
{
|
|
||||||
struct piece piece;
|
|
||||||
|
|
||||||
if (sweep > 2 * M_PI)
|
|
||||||
sweep = 2 * M_PI;
|
|
||||||
piece.type = arcTo;
|
|
||||||
piece.d[0] = xCenter;
|
|
||||||
piece.d[1] = yCenter;
|
|
||||||
piece.d[2] = radius;
|
|
||||||
piece.d[3] = startAngle;
|
|
||||||
piece.d[4] = sweep;
|
|
||||||
piece.b = negative;
|
|
||||||
add(p, &piece);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawPathBezierTo(uiDrawPath *p, double c1x, double c1y, double c2x, double c2y, double endX, double endY)
|
|
||||||
{
|
|
||||||
struct piece piece;
|
|
||||||
|
|
||||||
piece.type = bezierTo;
|
|
||||||
piece.d[0] = c1x;
|
|
||||||
piece.d[1] = c1y;
|
|
||||||
piece.d[2] = c2x;
|
|
||||||
piece.d[3] = c2y;
|
|
||||||
piece.d[4] = endX;
|
|
||||||
piece.d[5] = endY;
|
|
||||||
add(p, &piece);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawPathCloseFigure(uiDrawPath *p)
|
|
||||||
{
|
|
||||||
struct piece piece;
|
|
||||||
|
|
||||||
piece.type = closeFigure;
|
|
||||||
add(p, &piece);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawPathAddRectangle(uiDrawPath *p, double x, double y, double width, double height)
|
|
||||||
{
|
|
||||||
struct piece piece;
|
|
||||||
|
|
||||||
piece.type = addRect;
|
|
||||||
piece.d[0] = x;
|
|
||||||
piece.d[1] = y;
|
|
||||||
piece.d[2] = width;
|
|
||||||
piece.d[3] = height;
|
|
||||||
add(p, &piece);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawPathEnd(uiDrawPath *p)
|
|
||||||
{
|
|
||||||
p->ended = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void runPath(uiDrawPath *p, cairo_t *cr)
|
|
||||||
{
|
|
||||||
guint i;
|
|
||||||
struct piece *piece;
|
|
||||||
void (*arc)(cairo_t *, double, double, double, double, double);
|
|
||||||
|
|
||||||
if (!p->ended)
|
|
||||||
complain("path not ended in runPath()");
|
|
||||||
cairo_new_path(cr);
|
|
||||||
for (i = 0; i < p->pieces->len; i++) {
|
|
||||||
piece = &g_array_index(p->pieces, struct piece, i);
|
|
||||||
switch (piece->type) {
|
|
||||||
case newFigure:
|
|
||||||
cairo_move_to(cr, piece->d[0], piece->d[1]);
|
|
||||||
break;
|
|
||||||
case newFigureArc:
|
|
||||||
cairo_new_sub_path(cr);
|
|
||||||
// fall through
|
|
||||||
case arcTo:
|
|
||||||
arc = cairo_arc;
|
|
||||||
if (piece->b)
|
|
||||||
arc = cairo_arc_negative;
|
|
||||||
(*arc)(cr,
|
|
||||||
piece->d[0],
|
|
||||||
piece->d[1],
|
|
||||||
piece->d[2],
|
|
||||||
piece->d[3],
|
|
||||||
piece->d[3] + piece->d[4]);
|
|
||||||
break;
|
|
||||||
case lineTo:
|
|
||||||
cairo_line_to(cr, piece->d[0], piece->d[1]);
|
|
||||||
break;
|
|
||||||
case bezierTo:
|
|
||||||
cairo_curve_to(cr,
|
|
||||||
piece->d[0],
|
|
||||||
piece->d[1],
|
|
||||||
piece->d[2],
|
|
||||||
piece->d[3],
|
|
||||||
piece->d[4],
|
|
||||||
piece->d[5]);
|
|
||||||
break;
|
|
||||||
case closeFigure:
|
|
||||||
cairo_close_path(cr);
|
|
||||||
break;
|
|
||||||
case addRect:
|
|
||||||
cairo_rectangle(cr,
|
|
||||||
piece->d[0],
|
|
||||||
piece->d[1],
|
|
||||||
piece->d[2],
|
|
||||||
piece->d[3]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct uiDrawContext {
|
|
||||||
cairo_t *cr;
|
|
||||||
};
|
|
||||||
|
|
||||||
uiDrawContext *newContext(cairo_t *cr)
|
uiDrawContext *newContext(cairo_t *cr)
|
||||||
{
|
{
|
||||||
|
@ -290,7 +96,7 @@ void uiDrawFill(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b)
|
||||||
runPath(path, c->cr);
|
runPath(path, c->cr);
|
||||||
pat = mkbrush(b);
|
pat = mkbrush(b);
|
||||||
cairo_set_source(c->cr, pat);
|
cairo_set_source(c->cr, pat);
|
||||||
switch (path->fillMode) {
|
switch (pathFillMode(path)) {
|
||||||
case uiDrawFillModeWinding:
|
case uiDrawFillModeWinding:
|
||||||
cairo_set_fill_rule(c->cr, CAIRO_FILL_RULE_WINDING);
|
cairo_set_fill_rule(c->cr, CAIRO_FILL_RULE_WINDING);
|
||||||
break;
|
break;
|
||||||
|
@ -302,131 +108,10 @@ void uiDrawFill(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b)
|
||||||
cairo_pattern_destroy(pat);
|
cairo_pattern_destroy(pat);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void m2c(uiDrawMatrix *m, cairo_matrix_t *c)
|
|
||||||
{
|
|
||||||
c->xx = m->M11;
|
|
||||||
c->yx = m->M12;
|
|
||||||
c->xy = m->M21;
|
|
||||||
c->yy = m->M22;
|
|
||||||
c->x0 = m->M31;
|
|
||||||
c->y0 = m->M32;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void c2m(cairo_matrix_t *c, uiDrawMatrix *m)
|
|
||||||
{
|
|
||||||
m->M11 = c->xx;
|
|
||||||
m->M12 = c->yx;
|
|
||||||
m->M21 = c->xy;
|
|
||||||
m->M22 = c->yy;
|
|
||||||
m->M31 = c->x0;
|
|
||||||
m->M32 = c->y0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawMatrixSetIdentity(uiDrawMatrix *m)
|
|
||||||
{
|
|
||||||
setIdentity(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawMatrixTranslate(uiDrawMatrix *m, double x, double y)
|
|
||||||
{
|
|
||||||
cairo_matrix_t c;
|
|
||||||
|
|
||||||
m2c(m, &c);
|
|
||||||
cairo_matrix_translate(&c, x, y);
|
|
||||||
c2m(&c, m);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawMatrixScale(uiDrawMatrix *m, double xCenter, double yCenter, double x, double y)
|
|
||||||
{
|
|
||||||
cairo_matrix_t c;
|
|
||||||
double xt, yt;
|
|
||||||
|
|
||||||
m2c(m, &c);
|
|
||||||
// TODO explain why the translation must come first
|
|
||||||
xt = x;
|
|
||||||
yt = y;
|
|
||||||
scaleCenter(xCenter, yCenter, &xt, &yt);
|
|
||||||
cairo_matrix_translate(&c, xt, yt);
|
|
||||||
cairo_matrix_scale(&c, x, y);
|
|
||||||
// TODO undo the translation?
|
|
||||||
c2m(&c, m);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawMatrixRotate(uiDrawMatrix *m, double x, double y, double amount)
|
|
||||||
{
|
|
||||||
cairo_matrix_t c;
|
|
||||||
|
|
||||||
m2c(m, &c);
|
|
||||||
cairo_matrix_translate(&c, x, y);
|
|
||||||
cairo_matrix_rotate(&c, amount);
|
|
||||||
// TODO undo the translation? also cocoa backend
|
|
||||||
cairo_matrix_translate(&c, -x, -y);
|
|
||||||
c2m(&c, m);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawMatrixSkew(uiDrawMatrix *m, double x, double y, double xamount, double yamount)
|
|
||||||
{
|
|
||||||
fallbackSkew(m, x, y, xamount, yamount);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawMatrixMultiply(uiDrawMatrix *dest, uiDrawMatrix *src)
|
|
||||||
{
|
|
||||||
cairo_matrix_t c;
|
|
||||||
cairo_matrix_t d;
|
|
||||||
|
|
||||||
m2c(dest, &c);
|
|
||||||
m2c(src, &d);
|
|
||||||
cairo_matrix_multiply(&c, &c, &d);
|
|
||||||
c2m(&c, dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
int uiDrawMatrixInvertible(uiDrawMatrix *m)
|
|
||||||
{
|
|
||||||
cairo_matrix_t c;
|
|
||||||
|
|
||||||
m2c(m, &c);
|
|
||||||
return cairo_matrix_invert(&c) == CAIRO_STATUS_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
int uiDrawMatrixInvert(uiDrawMatrix *m)
|
|
||||||
{
|
|
||||||
cairo_matrix_t c;
|
|
||||||
|
|
||||||
m2c(m, &c);
|
|
||||||
if (cairo_matrix_invert(&c) != CAIRO_STATUS_SUCCESS)
|
|
||||||
return 0;
|
|
||||||
c2m(&c, m);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawMatrixTransformPoint(uiDrawMatrix *m, double *x, double *y)
|
|
||||||
{
|
|
||||||
cairo_matrix_t c;
|
|
||||||
|
|
||||||
m2c(m, &c);
|
|
||||||
cairo_matrix_transform_point(&c, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawMatrixTransformSize(uiDrawMatrix *m, double *x, double *y)
|
|
||||||
{
|
|
||||||
cairo_matrix_t c;
|
|
||||||
|
|
||||||
m2c(m, &c);
|
|
||||||
cairo_matrix_transform_distance(&c, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawTransform(uiDrawContext *c, uiDrawMatrix *m)
|
|
||||||
{
|
|
||||||
cairo_matrix_t cm;
|
|
||||||
|
|
||||||
m2c(m, &cm);
|
|
||||||
cairo_transform(c->cr, &cm);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawClip(uiDrawContext *c, uiDrawPath *path)
|
void uiDrawClip(uiDrawContext *c, uiDrawPath *path)
|
||||||
{
|
{
|
||||||
runPath(path, c->cr);
|
runPath(path, c->cr);
|
||||||
switch (path->fillMode) {
|
switch (pathFillMode(path)) {
|
||||||
case uiDrawFillModeWinding:
|
case uiDrawFillModeWinding:
|
||||||
cairo_set_fill_rule(c->cr, CAIRO_FILL_RULE_WINDING);
|
cairo_set_fill_rule(c->cr, CAIRO_FILL_RULE_WINDING);
|
||||||
break;
|
break;
|
||||||
|
@ -436,348 +121,3 @@ void uiDrawClip(uiDrawContext *c, uiDrawPath *path)
|
||||||
}
|
}
|
||||||
cairo_clip(c->cr);
|
cairo_clip(c->cr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void uiDrawSave(uiDrawContext *c)
|
|
||||||
{
|
|
||||||
cairo_save(c->cr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawRestore(uiDrawContext *c)
|
|
||||||
{
|
|
||||||
cairo_restore(c->cr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO split everything after this into a drawtext.c
|
|
||||||
|
|
||||||
struct uiDrawFontFamilies {
|
|
||||||
PangoFontFamily **f;
|
|
||||||
int n;
|
|
||||||
};
|
|
||||||
|
|
||||||
uiDrawFontFamilies *uiDrawListFontFamilies(void)
|
|
||||||
{
|
|
||||||
uiDrawFontFamilies *ff;
|
|
||||||
PangoFontMap *map;
|
|
||||||
|
|
||||||
ff = uiNew(uiDrawFontFamilies);
|
|
||||||
map = pango_cairo_font_map_get_default();
|
|
||||||
pango_font_map_list_families(map, &(ff->f), &(ff->n));
|
|
||||||
// do not free map; it's a shared resource
|
|
||||||
return ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
uintmax_t uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff)
|
|
||||||
{
|
|
||||||
return ff->n;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, uintmax_t n)
|
|
||||||
{
|
|
||||||
PangoFontFamily *f;
|
|
||||||
|
|
||||||
f = ff->f[n];
|
|
||||||
return uiUnixStrdupText(pango_font_family_get_name(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff)
|
|
||||||
{
|
|
||||||
g_free(ff->f);
|
|
||||||
uiFree(ff);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct uiDrawTextFont {
|
|
||||||
PangoFont *f;
|
|
||||||
};
|
|
||||||
|
|
||||||
uiDrawTextFont *mkTextFont(PangoFont *f, gboolean ref)
|
|
||||||
{
|
|
||||||
uiDrawTextFont *font;
|
|
||||||
|
|
||||||
font = uiNew(uiDrawTextFont);
|
|
||||||
font->f = f;
|
|
||||||
if (ref)
|
|
||||||
g_object_ref(font->f);
|
|
||||||
return font;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const PangoWeight pangoWeights[] = {
|
|
||||||
[uiDrawTextWeightThin] = PANGO_WEIGHT_THIN,
|
|
||||||
[uiDrawTextWeightUltraLight] = PANGO_WEIGHT_ULTRALIGHT,
|
|
||||||
[uiDrawTextWeightLight] = PANGO_WEIGHT_LIGHT,
|
|
||||||
[uiDrawTextWeightBook] = PANGO_WEIGHT_BOOK,
|
|
||||||
[uiDrawTextWeightNormal] = PANGO_WEIGHT_NORMAL,
|
|
||||||
[uiDrawTextWeightMedium] = PANGO_WEIGHT_MEDIUM,
|
|
||||||
[uiDrawTextWeightSemiBold] = PANGO_WEIGHT_SEMIBOLD,
|
|
||||||
[uiDrawTextWeightBold] = PANGO_WEIGHT_BOLD,
|
|
||||||
[uiDrawTextWeightUtraBold] = PANGO_WEIGHT_ULTRABOLD,
|
|
||||||
[uiDrawTextWeightHeavy] = PANGO_WEIGHT_HEAVY,
|
|
||||||
[uiDrawTextWeightUltraHeavy] = PANGO_WEIGHT_ULTRAHEAVY,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const PangoStyle pangoItalics[] = {
|
|
||||||
[uiDrawTextItalicNormal] = PANGO_STYLE_NORMAL,
|
|
||||||
[uiDrawTextItalicOblique] = PANGO_STYLE_OBLIQUE,
|
|
||||||
[uiDrawTextItalicItalic] = PANGO_STYLE_ITALIC,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const PangoStretch pangoStretches[] = {
|
|
||||||
[uiDrawTextStretchUltraCondensed] = PANGO_STRETCH_ULTRA_CONDENSED,
|
|
||||||
[uiDrawTextStretchExtraCondensed] = PANGO_STRETCH_EXTRA_CONDENSED,
|
|
||||||
[uiDrawTextStretchCondensed] = PANGO_STRETCH_CONDENSED,
|
|
||||||
[uiDrawTextStretchSemiCondensed] = PANGO_STRETCH_SEMI_CONDENSED,
|
|
||||||
[uiDrawTextStretchNormal] = PANGO_STRETCH_NORMAL,
|
|
||||||
[uiDrawTextStretchSemiExpanded] = PANGO_STRETCH_SEMI_EXPANDED,
|
|
||||||
[uiDrawTextStretchExpanded] = PANGO_STRETCH_EXPANDED,
|
|
||||||
[uiDrawTextStretchExtraExpanded] = PANGO_STRETCH_EXTRA_EXPANDED,
|
|
||||||
[uiDrawTextStretchUltraExpanded] = PANGO_STRETCH_ULTRA_EXPANDED,
|
|
||||||
};
|
|
||||||
|
|
||||||
// we need a context for a few things
|
|
||||||
// the documentation suggests creating cairo_t-specific, GdkScreen-specific, or even GtkWidget-specific contexts, but we can't really do that because we want our uiDrawTextFonts and uiDrawTextLayouts to be context-independent
|
|
||||||
// so this will have to do
|
|
||||||
// TODO really see if there's a better way instead; what do GDK and GTK+ do internally? gdk_pango_context_get()?
|
|
||||||
#define mkGenericPangoCairoContext() (pango_font_map_create_context(pango_cairo_font_map_get_default()))
|
|
||||||
|
|
||||||
PangoFont *pangoDescToPangoFont(PangoFontDescription *pdesc)
|
|
||||||
{
|
|
||||||
PangoFont *f;
|
|
||||||
PangoContext *context;
|
|
||||||
|
|
||||||
// in this case, the context is necessary for the metrics to be correct
|
|
||||||
context = mkGenericPangoCairoContext();
|
|
||||||
f = pango_font_map_load_font(pango_cairo_font_map_get_default(), context, pdesc);
|
|
||||||
if (f == NULL) {
|
|
||||||
// TODO
|
|
||||||
g_error("[libui] no match in pangoDescToPangoFont(); report to andlabs");
|
|
||||||
}
|
|
||||||
g_object_unref(context);
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc)
|
|
||||||
{
|
|
||||||
PangoFont *f;
|
|
||||||
PangoFontDescription *pdesc;
|
|
||||||
//TODO PangoVariant variant;
|
|
||||||
|
|
||||||
pdesc = pango_font_description_new();
|
|
||||||
pango_font_description_set_family(pdesc,
|
|
||||||
desc->Family);
|
|
||||||
pango_font_description_set_size(pdesc,
|
|
||||||
(gint) (desc->Size * PANGO_SCALE));
|
|
||||||
pango_font_description_set_weight(pdesc,
|
|
||||||
pangoWeights[desc->Weight]);
|
|
||||||
pango_font_description_set_style(pdesc,
|
|
||||||
pangoItalics[desc->Italic]);
|
|
||||||
#if 0
|
|
||||||
TODO
|
|
||||||
variant = PANGO_VARIANT_NORMAL;
|
|
||||||
if (desc->SmallCaps)
|
|
||||||
variant = PANGO_VARIANT_SMALL_CAPS;
|
|
||||||
pango_font_description_set_variant(pdesc, variant);
|
|
||||||
#endif
|
|
||||||
pango_font_description_set_stretch(pdesc,
|
|
||||||
pangoStretches[desc->Stretch]);
|
|
||||||
f = pangoDescToPangoFont(pdesc);
|
|
||||||
pango_font_description_free(pdesc);
|
|
||||||
return mkTextFont(f, FALSE); // we hold the initial reference; no need to ref
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawFreeTextFont(uiDrawTextFont *font)
|
|
||||||
{
|
|
||||||
g_object_unref(font->f);
|
|
||||||
uiFree(font);
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font)
|
|
||||||
{
|
|
||||||
return (uintptr_t) (font->f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc)
|
|
||||||
{
|
|
||||||
PangoFontDescription *pdesc;
|
|
||||||
|
|
||||||
// this creates a copy; we free it later
|
|
||||||
pdesc = pango_font_describe(font->f);
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
pango_font_description_free(pdesc);
|
|
||||||
}
|
|
||||||
|
|
||||||
// See https://developer.gnome.org/pango/1.30/pango-Cairo-Rendering.html#pango-Cairo-Rendering.description
|
|
||||||
// Note that we convert to double before dividing to make sure the floating-point stuff is right
|
|
||||||
#define pangoToCairo(pango) (((double) (pango)) / PANGO_SCALE)
|
|
||||||
#define cairoToPango(cairo) ((gint) ((cairo) * PANGO_SCALE))
|
|
||||||
|
|
||||||
// TODO this isn't enough; pango adds extra space to each layout
|
|
||||||
void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics)
|
|
||||||
{
|
|
||||||
PangoFontMetrics *pm;
|
|
||||||
|
|
||||||
pm = pango_font_get_metrics(font->f, NULL);
|
|
||||||
metrics->Ascent = pangoToCairo(pango_font_metrics_get_ascent(pm));
|
|
||||||
metrics->Descent = pangoToCairo(pango_font_metrics_get_descent(pm));
|
|
||||||
// Pango doesn't seem to expose this :( Use 0 and hope for the best.
|
|
||||||
metrics->Leading = 0;
|
|
||||||
metrics->UnderlinePos = pangoToCairo(pango_font_metrics_get_underline_position(pm));
|
|
||||||
metrics->UnderlineThickness = pangoToCairo(pango_font_metrics_get_underline_thickness(pm));
|
|
||||||
pango_font_metrics_unref(pm);
|
|
||||||
}
|
|
||||||
|
|
||||||
// note: PangoCairoLayouts are tied to a given cairo_t, so we can't store one in this device-independent structure
|
|
||||||
struct uiDrawTextLayout {
|
|
||||||
char *s;
|
|
||||||
ptrdiff_t *charsToBytes;
|
|
||||||
PangoFont *defaultFont;
|
|
||||||
double width;
|
|
||||||
PangoAttrList *attrs;
|
|
||||||
};
|
|
||||||
|
|
||||||
static ptrdiff_t *computeCharsToBytes(const char *s)
|
|
||||||
{
|
|
||||||
ptrdiff_t *charsToBytes;
|
|
||||||
glong i, charlen;
|
|
||||||
|
|
||||||
// we INCLUDE the null terminator as a character in the string
|
|
||||||
// g_utf8_offset_to_pointer() doesn't stop on null terminator so this should work
|
|
||||||
charlen = g_utf8_strlen(s, -1) + 1;
|
|
||||||
charsToBytes = (ptrdiff_t *) uiAlloc(charlen * sizeof (ptrdiff_t), "ptrdiff_t[]");
|
|
||||||
// TODO speed this up by not needing to scan the whole string again
|
|
||||||
for (i = 0; i < charlen; i++)
|
|
||||||
charsToBytes[i] = g_utf8_offset_to_pointer(s, i) - s;
|
|
||||||
return charsToBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
uiDrawTextLayout *uiDrawNewTextLayout(const char *text, uiDrawTextFont *defaultFont, double width)
|
|
||||||
{
|
|
||||||
uiDrawTextLayout *layout;
|
|
||||||
|
|
||||||
layout = uiNew(uiDrawTextLayout);
|
|
||||||
layout->s = g_strdup(text);
|
|
||||||
layout->charsToBytes = computeCharsToBytes(layout->s);
|
|
||||||
layout->defaultFont = defaultFont->f;
|
|
||||||
g_object_ref(layout->defaultFont); // retain a copy
|
|
||||||
uiDrawTextLayoutSetWidth(layout, width);
|
|
||||||
layout->attrs = pango_attr_list_new();
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawFreeTextLayout(uiDrawTextLayout *layout)
|
|
||||||
{
|
|
||||||
pango_attr_list_unref(layout->attrs);
|
|
||||||
g_object_unref(layout->defaultFont);
|
|
||||||
uiFree(layout->charsToBytes);
|
|
||||||
g_free(layout->s);
|
|
||||||
uiFree(layout);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width)
|
|
||||||
{
|
|
||||||
layout->width = width;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void prepareLayout(uiDrawTextLayout *layout, PangoLayout *pl)
|
|
||||||
{
|
|
||||||
PangoFontDescription *desc;
|
|
||||||
int width;
|
|
||||||
|
|
||||||
pango_layout_set_text(pl, layout->s, -1);
|
|
||||||
|
|
||||||
// again, this makes a copy
|
|
||||||
desc = pango_font_describe(layout->defaultFont);
|
|
||||||
// this is safe; the description is copied
|
|
||||||
pango_layout_set_font_description(pl, desc);
|
|
||||||
pango_font_description_free(desc);
|
|
||||||
|
|
||||||
width = cairoToPango(layout->width);
|
|
||||||
if (layout->width < 0)
|
|
||||||
width = -1;
|
|
||||||
pango_layout_set_width(pl, width);
|
|
||||||
|
|
||||||
pango_layout_set_attributes(pl, layout->attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height)
|
|
||||||
{
|
|
||||||
PangoContext *context;
|
|
||||||
PangoLayout *pl;
|
|
||||||
PangoRectangle logical;
|
|
||||||
|
|
||||||
// in this case, the context is necessary to create the layout
|
|
||||||
context = mkGenericPangoCairoContext();
|
|
||||||
pl = pango_layout_new(context);
|
|
||||||
// TODO g_object_unref() context?
|
|
||||||
prepareLayout(layout, pl);
|
|
||||||
|
|
||||||
pango_layout_get_extents(pl, NULL, &logical);
|
|
||||||
|
|
||||||
g_object_unref(pl);
|
|
||||||
|
|
||||||
*width = pangoToCairo(logical.width);
|
|
||||||
*height = pangoToCairo(logical.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout)
|
|
||||||
{
|
|
||||||
PangoLayout *pl;
|
|
||||||
|
|
||||||
pl = pango_cairo_create_layout(c->cr);
|
|
||||||
prepareLayout(layout, pl);
|
|
||||||
|
|
||||||
cairo_move_to(c->cr, x, y);
|
|
||||||
pango_cairo_show_layout(c->cr, pl);
|
|
||||||
|
|
||||||
g_object_unref(pl);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void addAttr(uiDrawTextLayout *layout, PangoAttribute *attr, intmax_t startChar, intmax_t endChar)
|
|
||||||
{
|
|
||||||
attr->start_index = layout->charsToBytes[startChar];
|
|
||||||
attr->end_index = layout->charsToBytes[endChar];
|
|
||||||
pango_attr_list_insert(layout->attrs, attr);
|
|
||||||
// pango_attr_list_insert() takes attr; we don't free it
|
|
||||||
}
|
|
||||||
|
|
||||||
// these attributes are only supported on 1.38 and higher; we need to support 1.36
|
|
||||||
// use dynamic linking to make them work at least on newer systems
|
|
||||||
// TODO warn programmers
|
|
||||||
static PangoAttribute *(*newFGAlphaAttr)(guint16 alpha) = NULL;
|
|
||||||
static gboolean tried138 = FALSE;
|
|
||||||
|
|
||||||
// note that we treat any error as "the 1.38 symbols aren't there" (and don't care if dlclose() failed)
|
|
||||||
static void try138(void)
|
|
||||||
{
|
|
||||||
void *handle;
|
|
||||||
|
|
||||||
tried138 = TRUE;
|
|
||||||
// dlsym() walks the dependency chain, so opening the current process should be sufficient
|
|
||||||
handle = dlopen(NULL, RTLD_LAZY);
|
|
||||||
if (handle == NULL)
|
|
||||||
return;
|
|
||||||
*((void **) (&newFGAlphaAttr)) = dlsym(handle, "pango_attr_foreground_alpha_new");
|
|
||||||
dlclose(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, intmax_t startChar, intmax_t endChar, double r, double g, double b, double a)
|
|
||||||
{
|
|
||||||
PangoAttribute *attr;
|
|
||||||
guint16 rr, gg, bb, aa;
|
|
||||||
|
|
||||||
rr = (guint16) (r * 65535);
|
|
||||||
gg = (guint16) (g * 65535);
|
|
||||||
bb = (guint16) (b * 65535);
|
|
||||||
aa = (guint16) (a * 65535);
|
|
||||||
|
|
||||||
attr = pango_attr_foreground_new(rr, gg, bb);
|
|
||||||
addAttr(layout, attr, startChar, endChar);
|
|
||||||
|
|
||||||
if (!tried138)
|
|
||||||
try138();
|
|
||||||
// TODO what if aa == 0?
|
|
||||||
if (newFGAlphaAttr != NULL) {
|
|
||||||
attr = (*newFGAlphaAttr)(aa);
|
|
||||||
addAttr(layout, attr, startChar, endChar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
// 5 may 2016
|
||||||
|
|
||||||
|
// draw.c
|
||||||
|
struct uiDrawContext {
|
||||||
|
cairo_t *cr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// drawpath.c
|
||||||
|
extern void runPath(uiDrawPath *p, cairo_t *cr);
|
||||||
|
extern uiDrawFillMode pathFillMode(uiDrawPath *path);
|
|
@ -0,0 +1,134 @@
|
||||||
|
// 6 september 2015
|
||||||
|
#include "uipriv_unix.h"
|
||||||
|
#include "draw.h"
|
||||||
|
|
||||||
|
static void m2c(uiDrawMatrix *m, cairo_matrix_t *c)
|
||||||
|
{
|
||||||
|
c->xx = m->M11;
|
||||||
|
c->yx = m->M12;
|
||||||
|
c->xy = m->M21;
|
||||||
|
c->yy = m->M22;
|
||||||
|
c->x0 = m->M31;
|
||||||
|
c->y0 = m->M32;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void c2m(cairo_matrix_t *c, uiDrawMatrix *m)
|
||||||
|
{
|
||||||
|
m->M11 = c->xx;
|
||||||
|
m->M12 = c->yx;
|
||||||
|
m->M21 = c->xy;
|
||||||
|
m->M22 = c->yy;
|
||||||
|
m->M31 = c->x0;
|
||||||
|
m->M32 = c->y0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawMatrixSetIdentity(uiDrawMatrix *m)
|
||||||
|
{
|
||||||
|
setIdentity(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawMatrixTranslate(uiDrawMatrix *m, double x, double y)
|
||||||
|
{
|
||||||
|
cairo_matrix_t c;
|
||||||
|
|
||||||
|
m2c(m, &c);
|
||||||
|
cairo_matrix_translate(&c, x, y);
|
||||||
|
c2m(&c, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawMatrixScale(uiDrawMatrix *m, double xCenter, double yCenter, double x, double y)
|
||||||
|
{
|
||||||
|
cairo_matrix_t c;
|
||||||
|
double xt, yt;
|
||||||
|
|
||||||
|
m2c(m, &c);
|
||||||
|
// TODO explain why the translation must come first
|
||||||
|
xt = x;
|
||||||
|
yt = y;
|
||||||
|
scaleCenter(xCenter, yCenter, &xt, &yt);
|
||||||
|
cairo_matrix_translate(&c, xt, yt);
|
||||||
|
cairo_matrix_scale(&c, x, y);
|
||||||
|
// TODO undo the translation?
|
||||||
|
c2m(&c, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawMatrixRotate(uiDrawMatrix *m, double x, double y, double amount)
|
||||||
|
{
|
||||||
|
cairo_matrix_t c;
|
||||||
|
|
||||||
|
m2c(m, &c);
|
||||||
|
cairo_matrix_translate(&c, x, y);
|
||||||
|
cairo_matrix_rotate(&c, amount);
|
||||||
|
// TODO undo the translation? also cocoa backend
|
||||||
|
cairo_matrix_translate(&c, -x, -y);
|
||||||
|
c2m(&c, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawMatrixSkew(uiDrawMatrix *m, double x, double y, double xamount, double yamount)
|
||||||
|
{
|
||||||
|
fallbackSkew(m, x, y, xamount, yamount);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawMatrixMultiply(uiDrawMatrix *dest, uiDrawMatrix *src)
|
||||||
|
{
|
||||||
|
cairo_matrix_t c;
|
||||||
|
cairo_matrix_t d;
|
||||||
|
|
||||||
|
m2c(dest, &c);
|
||||||
|
m2c(src, &d);
|
||||||
|
cairo_matrix_multiply(&c, &c, &d);
|
||||||
|
c2m(&c, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
int uiDrawMatrixInvertible(uiDrawMatrix *m)
|
||||||
|
{
|
||||||
|
cairo_matrix_t c;
|
||||||
|
|
||||||
|
m2c(m, &c);
|
||||||
|
return cairo_matrix_invert(&c) == CAIRO_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int uiDrawMatrixInvert(uiDrawMatrix *m)
|
||||||
|
{
|
||||||
|
cairo_matrix_t c;
|
||||||
|
|
||||||
|
m2c(m, &c);
|
||||||
|
if (cairo_matrix_invert(&c) != CAIRO_STATUS_SUCCESS)
|
||||||
|
return 0;
|
||||||
|
c2m(&c, m);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawMatrixTransformPoint(uiDrawMatrix *m, double *x, double *y)
|
||||||
|
{
|
||||||
|
cairo_matrix_t c;
|
||||||
|
|
||||||
|
m2c(m, &c);
|
||||||
|
cairo_matrix_transform_point(&c, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawMatrixTransformSize(uiDrawMatrix *m, double *x, double *y)
|
||||||
|
{
|
||||||
|
cairo_matrix_t c;
|
||||||
|
|
||||||
|
m2c(m, &c);
|
||||||
|
cairo_matrix_transform_distance(&c, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawTransform(uiDrawContext *c, uiDrawMatrix *m)
|
||||||
|
{
|
||||||
|
cairo_matrix_t cm;
|
||||||
|
|
||||||
|
m2c(m, &cm);
|
||||||
|
cairo_transform(c->cr, &cm);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawSave(uiDrawContext *c)
|
||||||
|
{
|
||||||
|
cairo_save(c->cr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawRestore(uiDrawContext *c)
|
||||||
|
{
|
||||||
|
cairo_restore(c->cr);
|
||||||
|
}
|
|
@ -0,0 +1,199 @@
|
||||||
|
// 6 september 2015
|
||||||
|
#include "uipriv_unix.h"
|
||||||
|
#include "draw.h"
|
||||||
|
|
||||||
|
struct uiDrawPath {
|
||||||
|
GArray *pieces;
|
||||||
|
uiDrawFillMode fillMode;
|
||||||
|
gboolean ended;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct piece {
|
||||||
|
int type;
|
||||||
|
double d[8];
|
||||||
|
int b;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
newFigure,
|
||||||
|
newFigureArc,
|
||||||
|
lineTo,
|
||||||
|
arcTo,
|
||||||
|
bezierTo,
|
||||||
|
closeFigure,
|
||||||
|
addRect,
|
||||||
|
};
|
||||||
|
|
||||||
|
uiDrawPath *uiDrawNewPath(uiDrawFillMode mode)
|
||||||
|
{
|
||||||
|
uiDrawPath *p;
|
||||||
|
|
||||||
|
p = uiNew(uiDrawPath);
|
||||||
|
p->pieces = g_array_new(FALSE, TRUE, sizeof (struct piece));
|
||||||
|
p->fillMode = mode;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawFreePath(uiDrawPath *p)
|
||||||
|
{
|
||||||
|
g_array_free(p->pieces, TRUE);
|
||||||
|
uiFree(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add(uiDrawPath *p, struct piece *piece)
|
||||||
|
{
|
||||||
|
if (p->ended)
|
||||||
|
complain("path ended in add()");
|
||||||
|
g_array_append_vals(p->pieces, piece, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawPathNewFigure(uiDrawPath *p, double x, double y)
|
||||||
|
{
|
||||||
|
struct piece piece;
|
||||||
|
|
||||||
|
piece.type = newFigure;
|
||||||
|
piece.d[0] = x;
|
||||||
|
piece.d[1] = y;
|
||||||
|
add(p, &piece);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawPathNewFigureWithArc(uiDrawPath *p, double xCenter, double yCenter, double radius, double startAngle, double sweep, int negative)
|
||||||
|
{
|
||||||
|
struct piece piece;
|
||||||
|
|
||||||
|
if (sweep > 2 * M_PI)
|
||||||
|
sweep = 2 * M_PI;
|
||||||
|
piece.type = newFigureArc;
|
||||||
|
piece.d[0] = xCenter;
|
||||||
|
piece.d[1] = yCenter;
|
||||||
|
piece.d[2] = radius;
|
||||||
|
piece.d[3] = startAngle;
|
||||||
|
piece.d[4] = sweep;
|
||||||
|
piece.b = negative;
|
||||||
|
add(p, &piece);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawPathLineTo(uiDrawPath *p, double x, double y)
|
||||||
|
{
|
||||||
|
struct piece piece;
|
||||||
|
|
||||||
|
piece.type = lineTo;
|
||||||
|
piece.d[0] = x;
|
||||||
|
piece.d[1] = y;
|
||||||
|
add(p, &piece);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawPathArcTo(uiDrawPath *p, double xCenter, double yCenter, double radius, double startAngle, double sweep, int negative)
|
||||||
|
{
|
||||||
|
struct piece piece;
|
||||||
|
|
||||||
|
if (sweep > 2 * M_PI)
|
||||||
|
sweep = 2 * M_PI;
|
||||||
|
piece.type = arcTo;
|
||||||
|
piece.d[0] = xCenter;
|
||||||
|
piece.d[1] = yCenter;
|
||||||
|
piece.d[2] = radius;
|
||||||
|
piece.d[3] = startAngle;
|
||||||
|
piece.d[4] = sweep;
|
||||||
|
piece.b = negative;
|
||||||
|
add(p, &piece);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawPathBezierTo(uiDrawPath *p, double c1x, double c1y, double c2x, double c2y, double endX, double endY)
|
||||||
|
{
|
||||||
|
struct piece piece;
|
||||||
|
|
||||||
|
piece.type = bezierTo;
|
||||||
|
piece.d[0] = c1x;
|
||||||
|
piece.d[1] = c1y;
|
||||||
|
piece.d[2] = c2x;
|
||||||
|
piece.d[3] = c2y;
|
||||||
|
piece.d[4] = endX;
|
||||||
|
piece.d[5] = endY;
|
||||||
|
add(p, &piece);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawPathCloseFigure(uiDrawPath *p)
|
||||||
|
{
|
||||||
|
struct piece piece;
|
||||||
|
|
||||||
|
piece.type = closeFigure;
|
||||||
|
add(p, &piece);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawPathAddRectangle(uiDrawPath *p, double x, double y, double width, double height)
|
||||||
|
{
|
||||||
|
struct piece piece;
|
||||||
|
|
||||||
|
piece.type = addRect;
|
||||||
|
piece.d[0] = x;
|
||||||
|
piece.d[1] = y;
|
||||||
|
piece.d[2] = width;
|
||||||
|
piece.d[3] = height;
|
||||||
|
add(p, &piece);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawPathEnd(uiDrawPath *p)
|
||||||
|
{
|
||||||
|
p->ended = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void runPath(uiDrawPath *p, cairo_t *cr)
|
||||||
|
{
|
||||||
|
guint i;
|
||||||
|
struct piece *piece;
|
||||||
|
void (*arc)(cairo_t *, double, double, double, double, double);
|
||||||
|
|
||||||
|
if (!p->ended)
|
||||||
|
complain("path not ended in runPath()");
|
||||||
|
cairo_new_path(cr);
|
||||||
|
for (i = 0; i < p->pieces->len; i++) {
|
||||||
|
piece = &g_array_index(p->pieces, struct piece, i);
|
||||||
|
switch (piece->type) {
|
||||||
|
case newFigure:
|
||||||
|
cairo_move_to(cr, piece->d[0], piece->d[1]);
|
||||||
|
break;
|
||||||
|
case newFigureArc:
|
||||||
|
cairo_new_sub_path(cr);
|
||||||
|
// fall through
|
||||||
|
case arcTo:
|
||||||
|
arc = cairo_arc;
|
||||||
|
if (piece->b)
|
||||||
|
arc = cairo_arc_negative;
|
||||||
|
(*arc)(cr,
|
||||||
|
piece->d[0],
|
||||||
|
piece->d[1],
|
||||||
|
piece->d[2],
|
||||||
|
piece->d[3],
|
||||||
|
piece->d[3] + piece->d[4]);
|
||||||
|
break;
|
||||||
|
case lineTo:
|
||||||
|
cairo_line_to(cr, piece->d[0], piece->d[1]);
|
||||||
|
break;
|
||||||
|
case bezierTo:
|
||||||
|
cairo_curve_to(cr,
|
||||||
|
piece->d[0],
|
||||||
|
piece->d[1],
|
||||||
|
piece->d[2],
|
||||||
|
piece->d[3],
|
||||||
|
piece->d[4],
|
||||||
|
piece->d[5]);
|
||||||
|
break;
|
||||||
|
case closeFigure:
|
||||||
|
cairo_close_path(cr);
|
||||||
|
break;
|
||||||
|
case addRect:
|
||||||
|
cairo_rectangle(cr,
|
||||||
|
piece->d[0],
|
||||||
|
piece->d[1],
|
||||||
|
piece->d[2],
|
||||||
|
piece->d[3]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uiDrawFillMode pathFillMode(uiDrawPath *path)
|
||||||
|
{
|
||||||
|
return path->fillMode;
|
||||||
|
}
|
|
@ -0,0 +1,328 @@
|
||||||
|
// 6 september 2015
|
||||||
|
#include "uipriv_unix.h"
|
||||||
|
#include "draw.h"
|
||||||
|
|
||||||
|
struct uiDrawFontFamilies {
|
||||||
|
PangoFontFamily **f;
|
||||||
|
int n;
|
||||||
|
};
|
||||||
|
|
||||||
|
uiDrawFontFamilies *uiDrawListFontFamilies(void)
|
||||||
|
{
|
||||||
|
uiDrawFontFamilies *ff;
|
||||||
|
PangoFontMap *map;
|
||||||
|
|
||||||
|
ff = uiNew(uiDrawFontFamilies);
|
||||||
|
map = pango_cairo_font_map_get_default();
|
||||||
|
pango_font_map_list_families(map, &(ff->f), &(ff->n));
|
||||||
|
// do not free map; it's a shared resource
|
||||||
|
return ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
uintmax_t uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff)
|
||||||
|
{
|
||||||
|
return ff->n;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, uintmax_t n)
|
||||||
|
{
|
||||||
|
PangoFontFamily *f;
|
||||||
|
|
||||||
|
f = ff->f[n];
|
||||||
|
return uiUnixStrdupText(pango_font_family_get_name(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff)
|
||||||
|
{
|
||||||
|
g_free(ff->f);
|
||||||
|
uiFree(ff);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct uiDrawTextFont {
|
||||||
|
PangoFont *f;
|
||||||
|
};
|
||||||
|
|
||||||
|
uiDrawTextFont *mkTextFont(PangoFont *f, gboolean ref)
|
||||||
|
{
|
||||||
|
uiDrawTextFont *font;
|
||||||
|
|
||||||
|
font = uiNew(uiDrawTextFont);
|
||||||
|
font->f = f;
|
||||||
|
if (ref)
|
||||||
|
g_object_ref(font->f);
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const PangoWeight pangoWeights[] = {
|
||||||
|
[uiDrawTextWeightThin] = PANGO_WEIGHT_THIN,
|
||||||
|
[uiDrawTextWeightUltraLight] = PANGO_WEIGHT_ULTRALIGHT,
|
||||||
|
[uiDrawTextWeightLight] = PANGO_WEIGHT_LIGHT,
|
||||||
|
[uiDrawTextWeightBook] = PANGO_WEIGHT_BOOK,
|
||||||
|
[uiDrawTextWeightNormal] = PANGO_WEIGHT_NORMAL,
|
||||||
|
[uiDrawTextWeightMedium] = PANGO_WEIGHT_MEDIUM,
|
||||||
|
[uiDrawTextWeightSemiBold] = PANGO_WEIGHT_SEMIBOLD,
|
||||||
|
[uiDrawTextWeightBold] = PANGO_WEIGHT_BOLD,
|
||||||
|
[uiDrawTextWeightUtraBold] = PANGO_WEIGHT_ULTRABOLD,
|
||||||
|
[uiDrawTextWeightHeavy] = PANGO_WEIGHT_HEAVY,
|
||||||
|
[uiDrawTextWeightUltraHeavy] = PANGO_WEIGHT_ULTRAHEAVY,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const PangoStyle pangoItalics[] = {
|
||||||
|
[uiDrawTextItalicNormal] = PANGO_STYLE_NORMAL,
|
||||||
|
[uiDrawTextItalicOblique] = PANGO_STYLE_OBLIQUE,
|
||||||
|
[uiDrawTextItalicItalic] = PANGO_STYLE_ITALIC,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const PangoStretch pangoStretches[] = {
|
||||||
|
[uiDrawTextStretchUltraCondensed] = PANGO_STRETCH_ULTRA_CONDENSED,
|
||||||
|
[uiDrawTextStretchExtraCondensed] = PANGO_STRETCH_EXTRA_CONDENSED,
|
||||||
|
[uiDrawTextStretchCondensed] = PANGO_STRETCH_CONDENSED,
|
||||||
|
[uiDrawTextStretchSemiCondensed] = PANGO_STRETCH_SEMI_CONDENSED,
|
||||||
|
[uiDrawTextStretchNormal] = PANGO_STRETCH_NORMAL,
|
||||||
|
[uiDrawTextStretchSemiExpanded] = PANGO_STRETCH_SEMI_EXPANDED,
|
||||||
|
[uiDrawTextStretchExpanded] = PANGO_STRETCH_EXPANDED,
|
||||||
|
[uiDrawTextStretchExtraExpanded] = PANGO_STRETCH_EXTRA_EXPANDED,
|
||||||
|
[uiDrawTextStretchUltraExpanded] = PANGO_STRETCH_ULTRA_EXPANDED,
|
||||||
|
};
|
||||||
|
|
||||||
|
// we need a context for a few things
|
||||||
|
// the documentation suggests creating cairo_t-specific, GdkScreen-specific, or even GtkWidget-specific contexts, but we can't really do that because we want our uiDrawTextFonts and uiDrawTextLayouts to be context-independent
|
||||||
|
// so this will have to do
|
||||||
|
// TODO really see if there's a better way instead; what do GDK and GTK+ do internally? gdk_pango_context_get()?
|
||||||
|
#define mkGenericPangoCairoContext() (pango_font_map_create_context(pango_cairo_font_map_get_default()))
|
||||||
|
|
||||||
|
PangoFont *pangoDescToPangoFont(PangoFontDescription *pdesc)
|
||||||
|
{
|
||||||
|
PangoFont *f;
|
||||||
|
PangoContext *context;
|
||||||
|
|
||||||
|
// in this case, the context is necessary for the metrics to be correct
|
||||||
|
context = mkGenericPangoCairoContext();
|
||||||
|
f = pango_font_map_load_font(pango_cairo_font_map_get_default(), context, pdesc);
|
||||||
|
if (f == NULL) {
|
||||||
|
// TODO
|
||||||
|
g_error("[libui] no match in pangoDescToPangoFont(); report to andlabs");
|
||||||
|
}
|
||||||
|
g_object_unref(context);
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc)
|
||||||
|
{
|
||||||
|
PangoFont *f;
|
||||||
|
PangoFontDescription *pdesc;
|
||||||
|
|
||||||
|
pdesc = pango_font_description_new();
|
||||||
|
pango_font_description_set_family(pdesc,
|
||||||
|
desc->Family);
|
||||||
|
pango_font_description_set_size(pdesc,
|
||||||
|
(gint) (desc->Size * PANGO_SCALE));
|
||||||
|
pango_font_description_set_weight(pdesc,
|
||||||
|
pangoWeights[desc->Weight]);
|
||||||
|
pango_font_description_set_style(pdesc,
|
||||||
|
pangoItalics[desc->Italic]);
|
||||||
|
pango_font_description_set_stretch(pdesc,
|
||||||
|
pangoStretches[desc->Stretch]);
|
||||||
|
f = pangoDescToPangoFont(pdesc);
|
||||||
|
pango_font_description_free(pdesc);
|
||||||
|
return mkTextFont(f, FALSE); // we hold the initial reference; no need to ref
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawFreeTextFont(uiDrawTextFont *font)
|
||||||
|
{
|
||||||
|
g_object_unref(font->f);
|
||||||
|
uiFree(font);
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font)
|
||||||
|
{
|
||||||
|
return (uintptr_t) (font->f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc)
|
||||||
|
{
|
||||||
|
PangoFontDescription *pdesc;
|
||||||
|
|
||||||
|
// this creates a copy; we free it later
|
||||||
|
pdesc = pango_font_describe(font->f);
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
pango_font_description_free(pdesc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// See https://developer.gnome.org/pango/1.30/pango-Cairo-Rendering.html#pango-Cairo-Rendering.description
|
||||||
|
// Note that we convert to double before dividing to make sure the floating-point stuff is right
|
||||||
|
#define pangoToCairo(pango) (((double) (pango)) / PANGO_SCALE)
|
||||||
|
#define cairoToPango(cairo) ((gint) ((cairo) * PANGO_SCALE))
|
||||||
|
|
||||||
|
// TODO this isn't enough; pango adds extra space to each layout
|
||||||
|
void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics)
|
||||||
|
{
|
||||||
|
PangoFontMetrics *pm;
|
||||||
|
|
||||||
|
pm = pango_font_get_metrics(font->f, NULL);
|
||||||
|
metrics->Ascent = pangoToCairo(pango_font_metrics_get_ascent(pm));
|
||||||
|
metrics->Descent = pangoToCairo(pango_font_metrics_get_descent(pm));
|
||||||
|
// Pango doesn't seem to expose this :( Use 0 and hope for the best.
|
||||||
|
metrics->Leading = 0;
|
||||||
|
metrics->UnderlinePos = pangoToCairo(pango_font_metrics_get_underline_position(pm));
|
||||||
|
metrics->UnderlineThickness = pangoToCairo(pango_font_metrics_get_underline_thickness(pm));
|
||||||
|
pango_font_metrics_unref(pm);
|
||||||
|
}
|
||||||
|
|
||||||
|
// note: PangoCairoLayouts are tied to a given cairo_t, so we can't store one in this device-independent structure
|
||||||
|
struct uiDrawTextLayout {
|
||||||
|
char *s;
|
||||||
|
ptrdiff_t *charsToBytes;
|
||||||
|
PangoFont *defaultFont;
|
||||||
|
double width;
|
||||||
|
PangoAttrList *attrs;
|
||||||
|
};
|
||||||
|
|
||||||
|
static ptrdiff_t *computeCharsToBytes(const char *s)
|
||||||
|
{
|
||||||
|
ptrdiff_t *charsToBytes;
|
||||||
|
glong i, charlen;
|
||||||
|
|
||||||
|
// we INCLUDE the null terminator as a character in the string
|
||||||
|
// g_utf8_offset_to_pointer() doesn't stop on null terminator so this should work
|
||||||
|
charlen = g_utf8_strlen(s, -1) + 1;
|
||||||
|
charsToBytes = (ptrdiff_t *) uiAlloc(charlen * sizeof (ptrdiff_t), "ptrdiff_t[]");
|
||||||
|
// TODO speed this up by not needing to scan the whole string again
|
||||||
|
for (i = 0; i < charlen; i++)
|
||||||
|
charsToBytes[i] = g_utf8_offset_to_pointer(s, i) - s;
|
||||||
|
return charsToBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
uiDrawTextLayout *uiDrawNewTextLayout(const char *text, uiDrawTextFont *defaultFont, double width)
|
||||||
|
{
|
||||||
|
uiDrawTextLayout *layout;
|
||||||
|
|
||||||
|
layout = uiNew(uiDrawTextLayout);
|
||||||
|
layout->s = g_strdup(text);
|
||||||
|
layout->charsToBytes = computeCharsToBytes(layout->s);
|
||||||
|
layout->defaultFont = defaultFont->f;
|
||||||
|
g_object_ref(layout->defaultFont); // retain a copy
|
||||||
|
uiDrawTextLayoutSetWidth(layout, width);
|
||||||
|
layout->attrs = pango_attr_list_new();
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawFreeTextLayout(uiDrawTextLayout *layout)
|
||||||
|
{
|
||||||
|
pango_attr_list_unref(layout->attrs);
|
||||||
|
g_object_unref(layout->defaultFont);
|
||||||
|
uiFree(layout->charsToBytes);
|
||||||
|
g_free(layout->s);
|
||||||
|
uiFree(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width)
|
||||||
|
{
|
||||||
|
layout->width = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void prepareLayout(uiDrawTextLayout *layout, PangoLayout *pl)
|
||||||
|
{
|
||||||
|
PangoFontDescription *desc;
|
||||||
|
int width;
|
||||||
|
|
||||||
|
pango_layout_set_text(pl, layout->s, -1);
|
||||||
|
|
||||||
|
// again, this makes a copy
|
||||||
|
desc = pango_font_describe(layout->defaultFont);
|
||||||
|
// this is safe; the description is copied
|
||||||
|
pango_layout_set_font_description(pl, desc);
|
||||||
|
pango_font_description_free(desc);
|
||||||
|
|
||||||
|
width = cairoToPango(layout->width);
|
||||||
|
if (layout->width < 0)
|
||||||
|
width = -1;
|
||||||
|
pango_layout_set_width(pl, width);
|
||||||
|
|
||||||
|
pango_layout_set_attributes(pl, layout->attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height)
|
||||||
|
{
|
||||||
|
PangoContext *context;
|
||||||
|
PangoLayout *pl;
|
||||||
|
PangoRectangle logical;
|
||||||
|
|
||||||
|
// in this case, the context is necessary to create the layout
|
||||||
|
context = mkGenericPangoCairoContext();
|
||||||
|
pl = pango_layout_new(context);
|
||||||
|
// TODO g_object_unref() context?
|
||||||
|
prepareLayout(layout, pl);
|
||||||
|
|
||||||
|
pango_layout_get_extents(pl, NULL, &logical);
|
||||||
|
|
||||||
|
g_object_unref(pl);
|
||||||
|
|
||||||
|
*width = pangoToCairo(logical.width);
|
||||||
|
*height = pangoToCairo(logical.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout)
|
||||||
|
{
|
||||||
|
PangoLayout *pl;
|
||||||
|
|
||||||
|
pl = pango_cairo_create_layout(c->cr);
|
||||||
|
prepareLayout(layout, pl);
|
||||||
|
|
||||||
|
cairo_move_to(c->cr, x, y);
|
||||||
|
pango_cairo_show_layout(c->cr, pl);
|
||||||
|
|
||||||
|
g_object_unref(pl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void addAttr(uiDrawTextLayout *layout, PangoAttribute *attr, intmax_t startChar, intmax_t endChar)
|
||||||
|
{
|
||||||
|
attr->start_index = layout->charsToBytes[startChar];
|
||||||
|
attr->end_index = layout->charsToBytes[endChar];
|
||||||
|
pango_attr_list_insert(layout->attrs, attr);
|
||||||
|
// pango_attr_list_insert() takes attr; we don't free it
|
||||||
|
}
|
||||||
|
|
||||||
|
// these attributes are only supported on 1.38 and higher; we need to support 1.36
|
||||||
|
// use dynamic linking to make them work at least on newer systems
|
||||||
|
// TODO warn programmers
|
||||||
|
static PangoAttribute *(*newFGAlphaAttr)(guint16 alpha) = NULL;
|
||||||
|
static gboolean tried138 = FALSE;
|
||||||
|
|
||||||
|
// note that we treat any error as "the 1.38 symbols aren't there" (and don't care if dlclose() failed)
|
||||||
|
static void try138(void)
|
||||||
|
{
|
||||||
|
void *handle;
|
||||||
|
|
||||||
|
tried138 = TRUE;
|
||||||
|
// dlsym() walks the dependency chain, so opening the current process should be sufficient
|
||||||
|
handle = dlopen(NULL, RTLD_LAZY);
|
||||||
|
if (handle == NULL)
|
||||||
|
return;
|
||||||
|
*((void **) (&newFGAlphaAttr)) = dlsym(handle, "pango_attr_foreground_alpha_new");
|
||||||
|
dlclose(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, intmax_t startChar, intmax_t endChar, double r, double g, double b, double a)
|
||||||
|
{
|
||||||
|
PangoAttribute *attr;
|
||||||
|
guint16 rr, gg, bb, aa;
|
||||||
|
|
||||||
|
rr = (guint16) (r * 65535);
|
||||||
|
gg = (guint16) (g * 65535);
|
||||||
|
bb = (guint16) (b * 65535);
|
||||||
|
aa = (guint16) (a * 65535);
|
||||||
|
|
||||||
|
attr = pango_attr_foreground_new(rr, gg, bb);
|
||||||
|
addAttr(layout, attr, startChar, endChar);
|
||||||
|
|
||||||
|
if (!tried138)
|
||||||
|
try138();
|
||||||
|
// TODO what if aa == 0?
|
||||||
|
if (newFGAlphaAttr != NULL) {
|
||||||
|
attr = (*newFGAlphaAttr)(aa);
|
||||||
|
addAttr(layout, attr, startChar, endChar);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue