2015-09-06 19:02:01 -05:00
// 6 september 2015
2015-10-08 17:24:09 -05:00
# include "uipriv_unix.h"
2015-09-06 19:02:01 -05:00
2015-10-08 07:47:36 -05:00
struct uiDrawPath {
GArray * pieces ;
uiDrawFillMode fillMode ;
gboolean ended ;
2015-09-06 19:02:01 -05:00
} ;
2015-10-08 07:47:36 -05:00
struct piece {
int type ;
double d [ 8 ] ;
2015-10-11 20:18:39 -05:00
int b ;
2015-10-08 07:47:36 -05:00
} ;
enum {
newFigure ,
newFigureArc ,
lineTo ,
arcTo ,
bezierTo ,
closeFigure ,
addRect ,
} ;
uiDrawPath * uiDrawNewPath ( uiDrawFillMode mode )
2015-09-06 19:02:01 -05:00
{
2015-10-08 07:47:36 -05:00
uiDrawPath * p ;
2015-09-06 19:02:01 -05:00
2015-10-09 02:43:29 -05:00
p = uiNew ( uiDrawPath ) ;
2015-10-08 07:47:36 -05:00
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 ) ;
2015-10-09 02:43:29 -05:00
uiFree ( p ) ;
2015-09-06 19:02:01 -05:00
}
2015-10-08 07:47:36 -05:00
static void add ( uiDrawPath * p , struct piece * piece )
2015-09-06 19:02:01 -05:00
{
2015-10-08 07:47:36 -05:00
if ( p - > ended )
2015-10-09 02:43:29 -05:00
complain ( " path ended in add() " ) ;
2015-10-08 07:47:36 -05:00
g_array_append_vals ( p - > pieces , piece , 1 ) ;
2015-09-06 19:02:01 -05:00
}
2015-10-08 07:47:36 -05:00
void uiDrawPathNewFigure ( uiDrawPath * p , double x , double y )
2015-09-07 09:25:59 -05:00
{
2015-10-08 07:47:36 -05:00
struct piece piece ;
piece . type = newFigure ;
piece . d [ 0 ] = x ;
piece . d [ 1 ] = y ;
add ( p , & piece ) ;
2015-09-07 09:25:59 -05:00
}
2015-10-11 20:18:39 -05:00
void uiDrawPathNewFigureWithArc ( uiDrawPath * p , double xCenter , double yCenter , double radius , double startAngle , double sweep , int negative )
2015-09-06 19:02:01 -05:00
{
2015-10-08 07:47:36 -05:00
struct piece piece ;
2015-10-09 21:00:41 -05:00
if ( sweep > 2 * M_PI )
sweep = 2 * M_PI ;
2015-10-08 07:47:36 -05:00
piece . type = newFigureArc ;
piece . d [ 0 ] = xCenter ;
piece . d [ 1 ] = yCenter ;
piece . d [ 2 ] = radius ;
piece . d [ 3 ] = startAngle ;
2015-10-09 20:51:43 -05:00
piece . d [ 4 ] = sweep ;
2015-10-11 20:18:39 -05:00
piece . b = negative ;
2015-10-08 07:47:36 -05:00
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 ) ;
2015-09-06 19:02:01 -05:00
}
2015-10-11 20:18:39 -05:00
void uiDrawPathArcTo ( uiDrawPath * p , double xCenter , double yCenter , double radius , double startAngle , double sweep , int negative )
2015-09-06 19:02:01 -05:00
{
2015-10-08 07:47:36 -05:00
struct piece piece ;
2015-10-09 21:00:41 -05:00
if ( sweep > 2 * M_PI )
sweep = 2 * M_PI ;
2015-10-08 07:47:36 -05:00
piece . type = arcTo ;
piece . d [ 0 ] = xCenter ;
piece . d [ 1 ] = yCenter ;
piece . d [ 2 ] = radius ;
piece . d [ 3 ] = startAngle ;
2015-10-09 20:51:43 -05:00
piece . d [ 4 ] = sweep ;
2015-10-11 20:18:39 -05:00
piece . b = negative ;
2015-10-08 07:47:36 -05:00
add ( p , & piece ) ;
2015-09-06 19:02:01 -05:00
}
2015-10-08 07:47:36 -05:00
void uiDrawPathBezierTo ( uiDrawPath * p , double c1x , double c1y , double c2x , double c2y , double endX , double endY )
2015-09-07 09:25:59 -05:00
{
2015-10-08 07:47:36 -05:00
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 ) ;
2015-09-07 09:25:59 -05:00
}
2015-10-08 07:47:36 -05:00
void uiDrawPathCloseFigure ( uiDrawPath * p )
2015-09-07 14:34:14 -05:00
{
2015-10-08 07:47:36 -05:00
struct piece piece ;
piece . type = closeFigure ;
add ( p , & piece ) ;
2015-09-07 14:34:14 -05:00
}
2015-10-08 07:47:36 -05:00
void uiDrawPathAddRectangle ( uiDrawPath * p , double x , double y , double width , double height )
2015-09-07 18:29:42 -05:00
{
2015-10-08 07:47:36 -05:00
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 ) ;
2015-09-07 18:29:42 -05:00
}
2015-10-08 07:47:36 -05:00
void uiDrawPathEnd ( uiDrawPath * p )
2015-09-06 21:48:25 -05:00
{
2015-10-08 07:47:36 -05:00
p - > ended = TRUE ;
2015-09-06 21:48:25 -05:00
}
2015-10-08 07:47:36 -05:00
static void runPath ( uiDrawPath * p , cairo_t * cr )
2015-09-06 19:02:01 -05:00
{
2015-10-08 07:47:36 -05:00
guint i ;
struct piece * piece ;
2015-10-11 20:18:39 -05:00
void ( * arc ) ( cairo_t * , double , double , double , double , double ) ;
2015-10-08 07:47:36 -05:00
if ( ! p - > ended )
2015-10-09 02:43:29 -05:00
complain ( " path not ended in runPath() " ) ;
2015-10-08 07:47:36 -05:00
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 :
2015-10-11 20:18:39 -05:00
arc = cairo_arc ;
if ( piece - > b )
arc = cairo_arc_negative ;
( * arc ) ( cr ,
2015-10-08 07:47:36 -05:00
piece - > d [ 0 ] ,
piece - > d [ 1 ] ,
piece - > d [ 2 ] ,
2015-10-11 19:24:06 -05:00
piece - > d [ 3 ] ,
piece - > d [ 3 ] + piece - > d [ 4 ] ) ;
2015-10-08 07:47:36 -05:00
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 * c ;
2015-10-09 02:43:29 -05:00
c = uiNew ( uiDrawContext ) ;
2015-10-08 07:47:36 -05:00
c - > cr = cr ;
return c ;
}
2015-10-09 02:43:29 -05:00
void freeContext ( uiDrawContext * c )
{
uiFree ( c ) ;
}
2015-10-08 07:47:36 -05:00
static cairo_pattern_t * mkbrush ( uiDrawBrush * b )
{
cairo_pattern_t * pat ;
size_t i ;
switch ( b - > Type ) {
case uiDrawBrushTypeSolid :
pat = cairo_pattern_create_rgba ( b - > R , b - > G , b - > B , b - > A ) ;
break ;
case uiDrawBrushTypeLinearGradient :
pat = cairo_pattern_create_linear ( b - > X0 , b - > Y0 , b - > X1 , b - > Y1 ) ;
break ;
case uiDrawBrushTypeRadialGradient :
// make the start circle radius 0 to make it a point
pat = cairo_pattern_create_radial (
b - > X0 , b - > Y0 , 0 ,
b - > X1 , b - > Y1 , b - > OuterRadius ) ;
break ;
// case uiDrawBrushTypeImage:
}
if ( cairo_pattern_status ( pat ) ! = CAIRO_STATUS_SUCCESS )
2015-10-09 02:43:29 -05:00
complain ( " error creating pattern in mkbrush(): %s " ,
2015-10-08 07:47:36 -05:00
cairo_status_to_string ( cairo_pattern_status ( pat ) ) ) ;
switch ( b - > Type ) {
case uiDrawBrushTypeLinearGradient :
case uiDrawBrushTypeRadialGradient :
for ( i = 0 ; i < b - > NumStops ; i + + )
cairo_pattern_add_color_stop_rgba ( pat ,
b - > Stops [ i ] . Pos ,
b - > Stops [ i ] . R ,
b - > Stops [ i ] . G ,
b - > Stops [ i ] . B ,
b - > Stops [ i ] . A ) ;
}
return pat ;
}
void uiDrawStroke ( uiDrawContext * c , uiDrawPath * path , uiDrawBrush * b , uiDrawStrokeParams * p )
{
cairo_pattern_t * pat ;
runPath ( path , c - > cr ) ;
pat = mkbrush ( b ) ;
cairo_set_source ( c - > cr , pat ) ;
2015-09-06 19:02:01 -05:00
switch ( p - > Cap ) {
case uiDrawLineCapFlat :
cairo_set_line_cap ( c - > cr , CAIRO_LINE_CAP_BUTT ) ;
break ;
case uiDrawLineCapRound :
cairo_set_line_cap ( c - > cr , CAIRO_LINE_CAP_ROUND ) ;
break ;
case uiDrawLineCapSquare :
cairo_set_line_cap ( c - > cr , CAIRO_LINE_CAP_SQUARE ) ;
break ;
}
switch ( p - > Join ) {
case uiDrawLineJoinMiter :
cairo_set_line_join ( c - > cr , CAIRO_LINE_JOIN_MITER ) ;
cairo_set_miter_limit ( c - > cr , p - > MiterLimit ) ;
break ;
case uiDrawLineJoinRound :
cairo_set_line_join ( c - > cr , CAIRO_LINE_JOIN_ROUND ) ;
break ;
case uiDrawLineJoinBevel :
cairo_set_line_join ( c - > cr , CAIRO_LINE_JOIN_BEVEL ) ;
break ;
}
2015-09-10 22:54:54 -05:00
cairo_set_line_width ( c - > cr , p - > Thickness ) ;
2015-10-16 09:09:41 -05:00
cairo_set_dash ( c - > cr , p - > Dashes , p - > NumDashes , p - > DashPhase ) ;
2015-09-06 19:02:01 -05:00
cairo_stroke ( c - > cr ) ;
2015-10-08 07:47:36 -05:00
cairo_pattern_destroy ( pat ) ;
2015-09-06 19:02:01 -05:00
}
2015-09-07 09:25:59 -05:00
2015-10-08 07:47:36 -05:00
void uiDrawFill ( uiDrawContext * c , uiDrawPath * path , uiDrawBrush * b )
2015-09-07 09:25:59 -05:00
{
2015-10-08 07:47:36 -05:00
cairo_pattern_t * pat ;
runPath ( path , c - > cr ) ;
pat = mkbrush ( b ) ;
cairo_set_source ( c - > cr , pat ) ;
switch ( path - > fillMode ) {
2015-09-07 09:25:59 -05:00
case uiDrawFillModeWinding :
cairo_set_fill_rule ( c - > cr , CAIRO_FILL_RULE_WINDING ) ;
break ;
case uiDrawFillModeAlternate :
cairo_set_fill_rule ( c - > cr , CAIRO_FILL_RULE_EVEN_ODD ) ;
break ;
}
cairo_fill ( c - > cr ) ;
2015-10-08 07:47:36 -05:00
cairo_pattern_destroy ( pat ) ;
2015-09-07 09:25:59 -05:00
}
2015-10-11 10:13:01 -05:00
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 ) ;
}
2015-10-12 00:43:12 -05:00
void uiDrawMatrixScale ( uiDrawMatrix * m , double xCenter , double yCenter , double x , double y )
2015-10-11 10:13:01 -05:00
{
cairo_matrix_t c ;
2015-10-12 00:43:12 -05:00
double xt , yt ;
2015-10-11 10:13:01 -05:00
m2c ( m , & c ) ;
2015-10-12 00:43:12 -05:00
// TODO explain why the translation must come first
xt = x ;
yt = y ;
scaleCenter ( xCenter , yCenter , & xt , & yt ) ;
cairo_matrix_translate ( & c , xt , yt ) ;
2015-10-11 10:13:01 -05:00
cairo_matrix_scale ( & c , x , y ) ;
2015-10-12 00:43:12 -05:00
// TODO undo the translation?
2015-10-11 10:13:01 -05:00
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 ) ;
2015-10-12 00:43:12 -05:00
cairo_matrix_rotate ( & c , amount ) ;
2015-10-12 08:08:50 -05:00
// TODO undo the translation? also cocoa backend
2015-10-11 10:13:01 -05:00
cairo_matrix_translate ( & c , - x , - y ) ;
c2m ( & c , m ) ;
}
2015-10-12 06:58:07 -05:00
void uiDrawMatrixSkew ( uiDrawMatrix * m , double x , double y , double xamount , double yamount )
2015-10-11 10:13:01 -05:00
{
2015-10-12 06:58:07 -05:00
fallbackSkew ( m , x , y , xamount , yamount ) ;
2015-10-11 10:13:01 -05:00
}
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 ) ;
}
2015-10-12 20:11:42 -05:00
void uiDrawTransform ( uiDrawContext * c , uiDrawMatrix * m )
{
cairo_matrix_t cm ;
m2c ( m , & cm ) ;
cairo_transform ( c - > cr , & cm ) ;
}
2015-10-13 10:16:06 -05:00
void uiDrawClip ( uiDrawContext * c , uiDrawPath * path )
{
runPath ( path , c - > cr ) ;
switch ( path - > fillMode ) {
case uiDrawFillModeWinding :
cairo_set_fill_rule ( c - > cr , CAIRO_FILL_RULE_WINDING ) ;
break ;
case uiDrawFillModeAlternate :
cairo_set_fill_rule ( c - > cr , CAIRO_FILL_RULE_EVEN_ODD ) ;
break ;
}
cairo_clip ( c - > cr ) ;
}
2015-10-11 10:13:01 -05:00
void uiDrawSave ( uiDrawContext * c )
{
cairo_save ( c - > cr ) ;
}
void uiDrawRestore ( uiDrawContext * c )
{
cairo_restore ( c - > cr ) ;
}
2015-12-22 18:17:27 -06:00
2016-04-19 22:52:44 -05:00
// TODO split everything after this into a drawtext.c
2015-12-22 18:17:27 -06:00
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 ) ;
}
2015-12-24 18:48:57 -06:00
2016-01-12 15:59:13 -06:00
struct uiDrawTextFont {
PangoFont * f ;
} ;
2016-04-19 10:23:05 -05:00
uiDrawTextFont * mkTextFont ( PangoFont * f , gboolean ref )
{
uiDrawTextFont * font ;
font = uiNew ( uiDrawTextFont ) ;
font - > f = f ;
if ( ref )
g_object_ref ( font - > f ) ;
return font ;
}
2015-12-24 18:48:57 -06:00
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 ,
} ;
2016-01-16 14:59:34 -06:00
// 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()))
2016-04-20 12:59:59 -05:00
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 ;
}
2016-01-12 15:59:13 -06:00
uiDrawTextFont * uiDrawLoadClosestFont ( const uiDrawTextFontDescriptor * desc )
{
2016-04-19 10:23:05 -05:00
PangoFont * f ;
2016-01-12 15:59:13 -06:00
PangoFontDescription * pdesc ;
2016-04-15 13:23:16 -05:00
//TODO PangoVariant variant;
2016-01-12 15:59:13 -06:00
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 ] ) ;
2016-04-15 13:23:16 -05:00
#if 0
TODO
2016-01-12 15:59:13 -06:00
variant = PANGO_VARIANT_NORMAL ;
if ( desc - > SmallCaps )
variant = PANGO_VARIANT_SMALL_CAPS ;
pango_font_description_set_variant ( pdesc , variant ) ;
2016-04-15 13:23:16 -05:00
# endif
2016-01-12 15:59:13 -06:00
pango_font_description_set_stretch ( pdesc ,
pangoStretches [ desc - > Stretch ] ) ;
2016-04-20 12:59:59 -05:00
f = pangoDescToPangoFont ( pdesc ) ;
pango_font_description_free ( pdesc ) ;
return mkTextFont ( f , FALSE ) ; // we hold the initial reference; no need to ref
2016-01-12 15:59:13 -06:00
}
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 ) ;
}
2016-01-12 22:33:50 -06:00
// 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)
2016-01-16 14:59:34 -06:00
# define cairoToPango(cairo) ((gint) ((cairo) * PANGO_SCALE))
2016-01-12 22:33:50 -06:00
// 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 ) ;
}
2016-01-07 17:37:43 -06:00
// note: PangoCairoLayouts are tied to a given cairo_t, so we can't store one in this device-independent structure
struct uiDrawTextLayout {
char * s ;
2016-04-19 13:57:15 -05:00
ptrdiff_t * charsToBytes ;
2016-01-12 15:59:13 -06:00
PangoFont * defaultFont ;
2016-01-16 14:59:34 -06:00
double width ;
2016-04-19 22:52:44 -05:00
PangoAttrList * attrs ;
2016-01-07 17:37:43 -06:00
} ;
2016-04-19 13:57:15 -05:00
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 ;
}
2016-01-16 14:59:34 -06:00
uiDrawTextLayout * uiDrawNewTextLayout ( const char * text , uiDrawTextFont * defaultFont , double width )
2016-01-07 17:37:43 -06:00
{
uiDrawTextLayout * layout ;
layout = uiNew ( uiDrawTextLayout ) ;
layout - > s = g_strdup ( text ) ;
2016-04-19 13:57:15 -05:00
layout - > charsToBytes = computeCharsToBytes ( layout - > s ) ;
2016-01-12 15:59:13 -06:00
layout - > defaultFont = defaultFont - > f ;
g_object_ref ( layout - > defaultFont ) ; // retain a copy
2016-01-16 14:59:34 -06:00
uiDrawTextLayoutSetWidth ( layout , width ) ;
2016-04-19 22:52:44 -05:00
layout - > attrs = pango_attr_list_new ( ) ;
2016-01-07 17:37:43 -06:00
return layout ;
}
void uiDrawFreeTextLayout ( uiDrawTextLayout * layout )
{
2016-04-19 22:52:44 -05:00
pango_attr_list_unref ( layout - > attrs ) ;
2016-01-12 15:59:13 -06:00
g_object_unref ( layout - > defaultFont ) ;
2016-04-19 13:57:15 -05:00
uiFree ( layout - > charsToBytes ) ;
2016-01-07 17:37:43 -06:00
g_free ( layout - > s ) ;
uiFree ( layout ) ;
}
2016-01-16 14:59:34 -06:00
void uiDrawTextLayoutSetWidth ( uiDrawTextLayout * layout , double width )
2016-01-07 17:37:43 -06:00
{
2016-01-16 14:59:34 -06:00
layout - > width = width ;
}
2016-01-07 17:37:43 -06:00
2016-01-16 14:59:34 -06:00
static void prepareLayout ( uiDrawTextLayout * layout , PangoLayout * pl )
{
PangoFontDescription * desc ;
int width ;
2016-01-12 15:59:13 -06:00
2016-01-07 17:37:43 -06:00
pango_layout_set_text ( pl , layout - > s , - 1 ) ;
2016-01-12 15:59:13 -06:00
// again, this makes a copy
desc = pango_font_describe ( layout - > defaultFont ) ;
2016-01-07 17:37:43 -06:00
// this is safe; the description is copied
2016-01-12 15:59:13 -06:00
pango_layout_set_font_description ( pl , desc ) ;
pango_font_description_free ( desc ) ;
2016-01-16 14:59:34 -06:00
width = cairoToPango ( layout - > width ) ;
if ( layout - > width < 0 )
width = - 1 ;
pango_layout_set_width ( pl , width ) ;
2016-04-19 22:52:44 -05:00
2016-04-20 00:14:46 -05:00
pango_layout_set_attributes ( pl , layout - > attrs ) ;
2016-01-16 14:59:34 -06:00
}
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 ) ;
2016-01-07 17:37:43 -06:00
cairo_move_to ( c - > cr , x , y ) ;
pango_cairo_show_layout ( c - > cr , pl ) ;
2016-01-12 15:59:13 -06:00
2016-01-07 17:37:43 -06:00
g_object_unref ( pl ) ;
2015-12-24 18:48:57 -06:00
}
2016-04-19 22:52:44 -05:00
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
2016-04-19 22:58:36 -05:00
static PangoAttribute * ( * newFGAlphaAttr ) ( guint16 alpha ) = NULL ;
2016-04-19 22:52:44 -05:00
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 ;
2016-04-20 00:14:46 -05:00
// dlsym() walks the dependency chain, so opening the current process should be sufficient
2016-04-19 22:52:44 -05:00
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 ) ;
}
}