LCOV - code coverage report
Current view: top level - gdraw - gxcdraw.c (source / functions) Hit Total Coverage
Test: FontForge coverage report 2017-08-04 01:21:11+02:00 (commit d35f7e4107a9e1db65cce47c468fcc914cecb8fd) Lines: 3 661 0.5 %
Date: 2017-08-04 Functions: 1 57 1.8 %

          Line data    Source code
       1             : /* Copyright (C) 2000-2012 by George Williams */
       2             : /*
       3             :  * Redistribution and use in source and binary forms, with or without
       4             :  * modification, are permitted provided that the following conditions are met:
       5             : 
       6             :  * Redistributions of source code must retain the above copyright notice, this
       7             :  * list of conditions and the following disclaimer.
       8             : 
       9             :  * Redistributions in binary form must reproduce the above copyright notice,
      10             :  * this list of conditions and the following disclaimer in the documentation
      11             :  * and/or other materials provided with the distribution.
      12             : 
      13             :  * The name of the author may not be used to endorse or promote products
      14             :  * derived from this software without specific prior written permission.
      15             : 
      16             :  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
      17             :  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
      18             :  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
      19             :  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      20             :  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
      21             :  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
      22             :  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
      23             :  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      24             :  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
      25             :  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      26             :  */
      27             : 
      28             : #include "gxdrawP.h"
      29             : #include "gxcdrawP.h"
      30             : 
      31             : #include <stdlib.h>
      32             : #include <math.h>
      33             : 
      34             : #include <ustring.h>
      35             : #include <utype.h>
      36             : #include "fontP.h"
      37             : 
      38             : #ifdef __Mac
      39             : # include <sys/utsname.h>
      40             : #endif
      41             : 
      42             : #ifdef _NO_LIBCAIRO
      43             : static int usecairo = false;
      44             : #else
      45             : static int usecairo = true;
      46             : #endif
      47             : 
      48             : 
      49             : 
      50           1 : void GDrawEnableCairo(int on) {
      51           1 :     usecairo=on;
      52             :     /* Obviously, if we have no library, enabling it will do nothing */
      53           1 : }
      54             : 
      55             : #ifndef _NO_LIBCAIRO
      56             : /* ************************************************************************** */
      57             : /* ***************************** Cairo Library ****************************** */
      58             : /* ************************************************************************** */
      59             : 
      60           0 : int _GXCDraw_hasCairo(void) {
      61           0 :     return ( usecairo );
      62             : }
      63             : 
      64             : /* ************************************************************************** */
      65             : /* ****************************** Cairo Window ****************************** */
      66             : /* ************************************************************************** */
      67           0 : void _GXCDraw_NewWindow(GXWindow nw) {
      68           0 :     GXDisplay *gdisp = nw->display;
      69           0 :     Display *display = gdisp->display;
      70             : 
      71           0 :     if ( !usecairo || !_GXCDraw_hasCairo())
      72           0 : return;
      73             : 
      74           0 :     nw->cs = cairo_xlib_surface_create(display,nw->w,gdisp->visual,
      75             :             nw->pos.width, nw->pos.height );
      76           0 :     if ( nw->cs!=NULL ) {
      77           0 :         nw->cc = cairo_create(nw->cs);
      78           0 :         if ( nw->cc!=NULL )
      79           0 :             nw->usecairo = true;
      80             :         else {
      81           0 :             cairo_surface_destroy(nw->cs);
      82           0 :             nw->cs=NULL;
      83             :         }
      84             :     }
      85             : }
      86             : 
      87           0 : void _GXCDraw_ResizeWindow(GXWindow gw,GRect *rect) {
      88           0 :     cairo_xlib_surface_set_size( gw->cs, rect->width,rect->height);
      89           0 : }
      90             : 
      91           0 : void _GXCDraw_DestroyWindow(GXWindow gw) {
      92           0 :     cairo_destroy(gw->cc);
      93           0 :     cairo_surface_destroy(gw->cs);
      94           0 :     gw->usecairo = false;
      95           0 : }
      96             : 
      97             : /* ************************************************************************** */
      98             : /* ******************************* Cairo State ****************************** */
      99             : /* ************************************************************************** */
     100           0 : static void GXCDraw_StippleMePink(GXWindow gw,int ts, Color fg) {
     101             :     static unsigned char grey_init[8] = { 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa };
     102             :     static unsigned char fence_init[8] = { 0x55, 0x22, 0x55, 0x88, 0x55, 0x22, 0x55, 0x88};
     103             :     uint8 *spt;
     104             :     int bit,i,j;
     105             :     uint32 *data;
     106             :     static uint32 space[8*8];
     107             :     static cairo_surface_t *is = NULL;
     108             :     static cairo_pattern_t *pat = NULL;
     109             : 
     110           0 :     if ( (fg>>24)!=0xff ) {
     111           0 :         int alpha = fg>>24, r = COLOR_RED(fg), g=COLOR_GREEN(fg), b=COLOR_BLUE(fg);
     112           0 :         r = (alpha*r+128)/255; g = (alpha*g+128)/255; b=(alpha*b+128)/255;
     113           0 :         fg = (alpha<<24) | (r<<16) | (g<<8) | b;
     114             :     }
     115             : 
     116           0 :     spt = ts==2 ? fence_init : grey_init;
     117           0 :     for ( i=0; i<8; ++i ) {
     118           0 :         data = space+8*i;
     119           0 :         for ( j=0, bit=0x80; bit!=0; ++j, bit>>=1 ) {
     120           0 :             if ( spt[i]&bit )
     121           0 :                 data[j] = fg;
     122             :             else
     123           0 :                 data[j] = 0;
     124             :         }
     125             :     }
     126           0 :     if ( is==NULL ) {
     127           0 :         is = cairo_image_surface_create_for_data((uint8 *) space,CAIRO_FORMAT_ARGB32,
     128             :                 8,8,8*4);
     129           0 :         pat = cairo_pattern_create_for_surface(is);
     130           0 :         cairo_pattern_set_extend(pat,CAIRO_EXTEND_REPEAT);
     131             :     }
     132           0 :     cairo_set_source(gw->cc,pat);
     133           0 : }
     134             : 
     135           0 : static int GXCDrawSetcolfunc(GXWindow gw, GGC *mine) {
     136             :     /*GCState *gcs = &gw->cairo_state;*/
     137           0 :     Color fg = mine->fg;
     138             : 
     139           0 :     if ( (fg>>24 ) == 0 )
     140           0 :         fg |= 0xff000000;
     141             : 
     142           0 :     if ( mine->ts != 0 ) {
     143           0 :         GXCDraw_StippleMePink(gw,mine->ts,fg);
     144             :     } else {
     145           0 :         cairo_set_source_rgba(gw->cc,COLOR_RED(fg)/255.0,COLOR_GREEN(fg)/255.0,COLOR_BLUE(fg)/255.0,
     146           0 :                 (fg>>24)/255.);
     147             :     }
     148           0 : return( true );
     149             : }
     150             : 
     151           0 : static int GXCDrawSetline(GXWindow gw, GGC *mine) {
     152           0 :     GCState *gcs = &gw->cairo_state;
     153           0 :     Color fg = mine->fg;
     154             : 
     155           0 :     if ( ( fg>>24 ) == 0 )
     156           0 :         fg |= 0xff000000;
     157             : 
     158           0 :     if ( mine->line_width<=0 ) mine->line_width = 1;
     159           0 :     if ( mine->line_width!=gcs->line_width || mine->line_width!=2 ) {
     160           0 :         cairo_set_line_width(gw->cc,mine->line_width);
     161           0 :         gcs->line_width = mine->line_width;
     162             :     }
     163           0 :     if ( mine->dash_len != gcs->dash_len || mine->skip_len != gcs->skip_len ||
     164           0 :             mine->dash_offset != gcs->dash_offset ) {
     165             :         double dashes[2];
     166           0 :         dashes[0] = mine->dash_len; dashes[1] = mine->skip_len;
     167           0 :         cairo_set_dash(gw->cc,dashes,0,mine->dash_offset);
     168           0 :         gcs->dash_offset = mine->dash_offset;
     169           0 :         gcs->dash_len = mine->dash_len;
     170           0 :         gcs->skip_len = mine->skip_len;
     171             :     }
     172             :     /* I don't use line join/cap. On a screen with small line_width they are irrelevant */
     173             : 
     174           0 :     if ( mine->ts != 0 ) {
     175           0 :         GXCDraw_StippleMePink(gw,mine->ts,fg);
     176             :     } else {
     177           0 :         cairo_set_source_rgba(gw->cc,COLOR_RED(fg)/255.0,COLOR_GREEN(fg)/255.0,COLOR_BLUE(fg)/255.0,
     178           0 :                     (fg>>24)/255.0);
     179             :     }
     180           0 : return( mine->line_width );
     181             : }
     182             : 
     183           0 : void _GXCDraw_PushClip(GXWindow gw) {
     184           0 :     cairo_save(gw->cc);
     185           0 :     cairo_new_path(gw->cc);
     186           0 :     cairo_rectangle(gw->cc,gw->ggc->clip.x,gw->ggc->clip.y,gw->ggc->clip.width,gw->ggc->clip.height);
     187           0 :     cairo_clip(gw->cc);
     188           0 : }
     189             : 
     190           0 : void _GXCDraw_PopClip(GXWindow gw) {
     191           0 :     cairo_restore(gw->cc);
     192           0 : }
     193             : 
     194           0 : void _GXCDraw_PushClipOnly(GXWindow gw) {
     195           0 :     cairo_save( gw->cc );
     196           0 : }
     197             : 
     198           0 : void _GXCDraw_ClipPreserve(GXWindow gw) {
     199           0 :     cairo_clip_preserve( gw->cc );
     200           0 : }
     201             : 
     202             : 
     203             : 
     204             : /* ************************************************************************** */
     205             : /* ***************************** Cairo Drawing ****************************** */
     206             : /* ************************************************************************** */
     207           0 : void _GXCDraw_Clear(GXWindow gw, GRect *rect) {
     208           0 :     GRect *r = rect, temp;
     209           0 :     if ( r==NULL ) {
     210           0 :         temp = gw->pos;
     211           0 :         temp.x = temp.y = 0;
     212           0 :         r = &temp;
     213             :     }
     214           0 :     cairo_new_path(gw->cc);
     215           0 :     cairo_rectangle(gw->cc,r->x,r->y,r->width,r->height);
     216           0 :     cairo_set_source_rgba(gw->cc,COLOR_RED(gw->ggc->bg)/255.0,COLOR_GREEN(gw->ggc->bg)/255.0,COLOR_BLUE(gw->ggc->bg)/255.0,
     217             :             1.0);
     218           0 :     cairo_fill(gw->cc);
     219           0 : }
     220             : 
     221           0 : void _GXCDraw_DrawLine(GXWindow gw, int32 x,int32 y, int32 xend,int32 yend) {
     222           0 :     int width = GXCDrawSetline(gw,gw->ggc);
     223             : 
     224           0 :     cairo_new_path(gw->cc);
     225           0 :     if ( width&1 ) {
     226           0 :         cairo_move_to(gw->cc,x+.5,y+.5);
     227           0 :         cairo_line_to(gw->cc,xend+.5,yend+.5);
     228             :     } else {
     229           0 :         cairo_move_to(gw->cc,x,y);
     230           0 :         cairo_line_to(gw->cc,xend,yend);
     231             :     }
     232           0 :     cairo_stroke(gw->cc);
     233           0 : }
     234             : 
     235           0 : void _GXCDraw_DrawRect(GXWindow gw, GRect *rect) {
     236           0 :     int width = GXCDrawSetline(gw,gw->ggc);
     237             : 
     238           0 :     cairo_new_path(gw->cc);
     239           0 :     if ( width&1 ) {
     240           0 :         cairo_rectangle(gw->cc,rect->x+.5,rect->y+.5,rect->width,rect->height);
     241             :     } else {
     242           0 :         cairo_rectangle(gw->cc,rect->x,rect->y,rect->width,rect->height);
     243             :     }
     244           0 :     cairo_stroke(gw->cc);
     245           0 : }
     246             : 
     247           0 : void _GXCDraw_FillRect(GXWindow gw, GRect *rect) {
     248           0 :     GXCDrawSetcolfunc(gw,gw->ggc);
     249             : 
     250           0 :     cairo_new_path(gw->cc);
     251           0 :     cairo_rectangle(gw->cc,rect->x,rect->y,rect->width,rect->height);
     252           0 :     cairo_fill(gw->cc);
     253           0 : }
     254             : 
     255           0 : void _GXCDraw_FillRoundRect(GXWindow gw, GRect *rect, int radius) {
     256           0 :     double degrees = M_PI / 180.0;
     257             : 
     258           0 :     GXCDrawSetcolfunc(gw,gw->ggc);
     259             : 
     260           0 :     cairo_new_path(gw->cc);
     261           0 :     cairo_arc(gw->cc, rect->x + rect->width - radius, rect->y + radius, radius, -90 * degrees, 0 * degrees);
     262           0 :     cairo_arc(gw->cc, rect->x + rect->width - radius, rect->y + rect->height - radius, radius, 0 * degrees, 90 * degrees);
     263           0 :     cairo_arc(gw->cc, rect->x + radius, rect->y + rect->height - radius, radius, 90 * degrees, 180 * degrees);
     264           0 :     cairo_arc(gw->cc, rect->x + radius, rect->y + radius, radius, 180 * degrees, 270 * degrees);
     265           0 :     cairo_close_path(gw->cc);
     266           0 :     cairo_fill(gw->cc);
     267             : 
     268           0 : }
     269             : 
     270           0 : static void GXCDraw_EllipsePath(cairo_t *cc,double cx,double cy,double width,double height) {
     271           0 :     cairo_new_path(cc);
     272           0 :     cairo_move_to(cc,cx,cy+height);
     273           0 :     cairo_curve_to(cc,
     274           0 :             cx+.552*width,cy+height,
     275           0 :             cx+width,cy+.552*height,
     276             :             cx+width,cy);
     277           0 :     cairo_curve_to(cc,
     278           0 :             cx+width,cy-.552*height,
     279           0 :             cx+.552*width,cy-height,
     280             :             cx,cy-height);
     281           0 :     cairo_curve_to(cc,
     282           0 :             cx-.552*width,cy-height,
     283           0 :             cx-width,cy-.552*height,
     284             :             cx-width,cy);
     285           0 :     cairo_curve_to(cc,
     286           0 :             cx-width,cy+.552*height,
     287           0 :             cx-.552*width,cy+height,
     288             :             cx,cy+height);
     289           0 :     cairo_close_path(cc);
     290           0 : }
     291             : 
     292           0 : void _GXCDraw_DrawEllipse(GXWindow gw, GRect *rect) {
     293             :     /* It is tempting to use the cairo arc command and scale the */
     294             :     /*  coordinates to get an elipse, but that distorts the stroke width */
     295           0 :     int lwidth = GXCDrawSetline(gw,gw->ggc);
     296             :     double cx, cy, width, height;
     297             : 
     298           0 :     width = rect->width/2.0; height = rect->height/2.0;
     299           0 :     cx = rect->x + width;
     300           0 :     cy = rect->y + height;
     301           0 :     if ( lwidth&1 ) {
     302           0 :         if ( rint(width)==width )
     303           0 :             cx += .5;
     304           0 :         if ( rint(height)==height )
     305           0 :             cy += .5;
     306             :     }
     307           0 :     GXCDraw_EllipsePath(gw->cc,cx,cy,width,height);
     308           0 :     cairo_stroke(gw->cc);
     309           0 : }
     310             : 
     311           0 : void _GXCDraw_FillEllipse(GXWindow gw, GRect *rect) {
     312             :     /* It is tempting to use the cairo arc command and scale the */
     313             :     /*  coordinates to get an elipse, but that distorts the stroke width */
     314             :     double cx, cy, width, height;
     315             : 
     316           0 :     GXCDrawSetcolfunc(gw,gw->ggc);
     317             : 
     318           0 :     width = rect->width/2.0; height = rect->height/2.0;
     319           0 :     cx = rect->x + width;
     320           0 :     cy = rect->y + height;
     321           0 :     GXCDraw_EllipsePath(gw->cc,cx,cy,width,height);
     322           0 :     cairo_fill(gw->cc);
     323           0 : }
     324             : 
     325           0 : void _GXCDraw_DrawPoly(GXWindow gw, GPoint *pts, int16 cnt) {
     326           0 :     int width = GXCDrawSetline(gw,gw->ggc);
     327           0 :     double off = width&1 ? .5 : 0;
     328             :     int i;
     329             : 
     330           0 :     cairo_new_path(gw->cc);
     331           0 :     cairo_move_to(gw->cc,pts[0].x+off,pts[0].y+off);
     332           0 :     for ( i=1; i<cnt; ++i )
     333           0 :         cairo_line_to(gw->cc,pts[i].x+off,pts[i].y+off);
     334           0 :     cairo_stroke(gw->cc);
     335           0 : }
     336             : 
     337           0 : void _GXCDraw_FillPoly(GXWindow gw, GPoint *pts, int16 cnt) {
     338           0 :     GXCDrawSetcolfunc(gw,gw->ggc);
     339             :     int i;
     340             : 
     341           0 :     cairo_new_path(gw->cc);
     342           0 :     cairo_move_to(gw->cc,pts[0].x,pts[0].y);
     343           0 :     for ( i=1; i<cnt; ++i )
     344           0 :         cairo_line_to(gw->cc,pts[i].x,pts[i].y);
     345           0 :     cairo_close_path(gw->cc);
     346           0 :     cairo_fill(gw->cc);
     347             : 
     348           0 :     cairo_set_line_width(gw->cc,1);
     349           0 :     cairo_new_path(gw->cc);
     350           0 :     cairo_move_to(gw->cc,pts[0].x+.5,pts[0].y+.5);
     351           0 :     for ( i=1; i<cnt; ++i )
     352           0 :         cairo_line_to(gw->cc,pts[i].x+.5,pts[i].y+.5);
     353           0 :     cairo_close_path(gw->cc);
     354           0 :     cairo_stroke(gw->cc);
     355           0 : }
     356             : 
     357             : /* ************************************************************************** */
     358             : /* ****************************** Cairo Paths ******************************* */
     359             : /* ************************************************************************** */
     360           0 : void _GXCDraw_PathStartNew(GWindow w) {
     361           0 :     cairo_new_path( ((GXWindow) w)->cc );
     362           0 : }
     363             : 
     364           0 : void _GXCDraw_PathStartSubNew(GWindow w) {
     365           0 :     cairo_new_sub_path( ((GXWindow) w)->cc );
     366           0 : }
     367             : 
     368           0 : int _GXCDraw_FillRuleSetWinding(GWindow w) {
     369           0 :     cairo_set_fill_rule(((GXWindow) w)->cc,CAIRO_FILL_RULE_WINDING);
     370           0 :     return 1;
     371             : }
     372             : 
     373           0 : void _GXCDraw_PathClose(GWindow w) {
     374           0 :     cairo_close_path( ((GXWindow) w)->cc );
     375           0 : }
     376             : 
     377           0 : void _GXCDraw_PathMoveTo(GWindow w,double x, double y) {
     378           0 :     cairo_move_to( ((GXWindow) w)->cc,x,y );
     379           0 : }
     380             : 
     381           0 : void _GXCDraw_PathLineTo(GWindow w,double x, double y) {
     382           0 :     cairo_line_to( ((GXWindow) w)->cc,x,y );
     383           0 : }
     384             : 
     385           0 : void _GXCDraw_PathCurveTo(GWindow w,
     386             :                     double cx1, double cy1,
     387             :                     double cx2, double cy2,
     388             :                     double x, double y) {
     389           0 :     cairo_curve_to( ((GXWindow) w)->cc,cx1,cy1,cx2,cy2,x,y );
     390           0 : }
     391             : 
     392           0 : void _GXCDraw_PathStroke(GWindow w,Color col) {
     393           0 :     w->ggc->fg = col;
     394           0 :     GXCDrawSetline((GXWindow) w,w->ggc);
     395           0 :     cairo_stroke( ((GXWindow) w)->cc );
     396           0 : }
     397             : 
     398           0 : void _GXCDraw_PathFill(GWindow w,Color col) {
     399           0 :     cairo_set_source_rgba(((GXWindow) w)->cc,COLOR_RED(col)/255.0,COLOR_GREEN(col)/255.0,COLOR_BLUE(col)/255.0,
     400           0 :             (col>>24)/255.0);
     401           0 :     cairo_fill( ((GXWindow) w)->cc );
     402           0 : }
     403             : 
     404           0 : void _GXCDraw_PathFillAndStroke(GWindow w,Color fillcol, Color strokecol) {
     405           0 :     GXWindow gw = (GXWindow) w;
     406             : 
     407           0 :     cairo_save(gw->cc);
     408           0 :     cairo_set_source_rgba(gw->cc,COLOR_RED(fillcol)/255.0,COLOR_GREEN(fillcol)/255.0,COLOR_BLUE(fillcol)/255.0,
     409           0 :             (fillcol>>24)/255.0);
     410           0 :     cairo_fill( gw->cc );
     411           0 :     cairo_restore(gw->cc);
     412           0 :     w->ggc->fg = strokecol;
     413           0 :     GXCDrawSetline(gw,gw->ggc);
     414           0 :     cairo_fill( gw->cc );
     415           0 : }
     416             : 
     417             : /* ************************************************************************** */
     418             : /* ****************************** Cairo Images ****************************** */
     419             : /* ************************************************************************** */
     420           0 : static cairo_surface_t *GImage2Surface(GImage *image, GRect *src, uint8 **_data) {
     421           0 :     struct _GImage *base = image->list_len==0?image->u.image:image->u.images[0];
     422             :     cairo_format_t type;
     423             :     uint8 *data, *pt;
     424             :     uint32 *idata, *ipt, *ito;
     425             :     int i,j,jj,tjj,stride;
     426             :     int bit, tobit;
     427             :     cairo_surface_t *cs;
     428             : 
     429           0 :     if ( base->image_type == it_rgba )
     430           0 :         type = CAIRO_FORMAT_ARGB32;
     431           0 :     else if ( base->image_type == it_true && base->trans!=COLOR_UNKNOWN )
     432           0 :         type = CAIRO_FORMAT_ARGB32;
     433           0 :     else if ( base->image_type == it_index && base->clut->trans_index!=COLOR_UNKNOWN )
     434           0 :         type = CAIRO_FORMAT_ARGB32;
     435           0 :     else if ( base->image_type == it_true )
     436           0 :         type = CAIRO_FORMAT_RGB24;
     437           0 :     else if ( base->image_type == it_index )
     438           0 :         type = CAIRO_FORMAT_RGB24;
     439           0 :     else if ( base->image_type == it_mono && base->clut!=NULL &&
     440           0 :             base->clut->trans_index!=COLOR_UNKNOWN )
     441           0 :         type = CAIRO_FORMAT_A1;
     442             :     else
     443           0 :         type = CAIRO_FORMAT_RGB24;
     444             : 
     445             :     /* We can't reuse the image's data for alpha images because we must */
     446             :     /*  premultiply each channel by alpha. We can reuse it for non-transparent*/
     447             :     /*  rgb images */
     448           0 :     if ( base->image_type == it_true && type == CAIRO_FORMAT_RGB24 ) {
     449           0 :         idata = ((uint32 *) (base->data)) + src->y*base->bytes_per_line + src->x;
     450           0 :         *_data = NULL;          /* We can reuse the image's own data, don't need a copy */
     451           0 : return( cairo_image_surface_create_for_data((uint8 *) idata,type,
     452             :                 src->width, src->height,
     453             :                 base->bytes_per_line));
     454             :     }
     455             : 
     456           0 :     stride = cairo_format_stride_for_width(type,src->width);
     457           0 :     *_data = data = malloc(stride * src->height);
     458           0 :     cs = cairo_image_surface_create_for_data(data,type,
     459             :                 src->width, src->height,   stride);
     460           0 :     idata = (uint32 *) data;
     461             : 
     462           0 :     if ( base->image_type == it_rgba ) {
     463           0 :         ipt = ((uint32 *) (base->data + src->y*base->bytes_per_line)) + src->x;
     464           0 :         ito = idata;
     465           0 :         for ( i=0; i<src->height; ++i ) {
     466           0 :            for ( j=0; j<src->width; ++j ) {
     467           0 :                uint32 orig = ipt[j];
     468           0 :                int alpha = orig>>24;
     469           0 :                if ( alpha==0xff )
     470           0 :                    ito[j] = orig;
     471           0 :                else if ( alpha==0 )
     472           0 :                    ito[j] = 0x00000000;
     473             :                else
     474           0 :                    ito[j] = (alpha<<24) |
     475           0 :                            ((COLOR_RED  (orig)*alpha/255)<<16)|
     476           0 :                            ((COLOR_GREEN(orig)*alpha/255)<<8 )|
     477           0 :                            ((COLOR_BLUE (orig)*alpha/255));
     478             :            }
     479           0 :            ipt = (uint32 *) (((uint8 *) ipt) + base->bytes_per_line);
     480           0 :            ito = (uint32 *) (((uint8 *) ito) +stride);
     481             :        }
     482           0 :     } else if ( base->image_type == it_true && base->trans!=COLOR_UNKNOWN ) {
     483           0 :         Color trans = base->trans;
     484           0 :         ipt = ((uint32 *) (base->data + src->y*base->bytes_per_line)) + src->x;
     485           0 :         ito = idata;
     486           0 :         for ( i=0; i<src->height; ++i ) {
     487           0 :            for ( j=0; j<src->width; ++j ) {
     488           0 :                if ( ipt[j]==trans )
     489           0 :                    ito[j] = 0x00000000;
     490             :                else
     491           0 :                    ito[j] = ipt[j]|0xff000000;
     492             :            }
     493           0 :            ipt = (uint32 *) (((uint8 *) ipt) + base->bytes_per_line);
     494           0 :            ito = (uint32 *) (((uint8 *) ito) +stride);
     495             :        }
     496           0 :     } else if ( base->image_type == it_true ) {
     497           0 :         ipt = ((uint32 *) (base->data + src->y*base->bytes_per_line)) + src->x;
     498           0 :         ito = idata;
     499           0 :         for ( i=0; i<src->height; ++i ) {
     500           0 :            for ( j=0; j<src->width; ++j ) {
     501           0 :                ito[j] = ipt[j]|0xff000000;
     502             :            }
     503           0 :            ipt = (uint32 *) (((uint8 *) ipt) + base->bytes_per_line);
     504           0 :            ito = (uint32 *) (((uint8 *) ito) +stride);
     505             :        }
     506           0 :     } else if ( base->image_type == it_index && base->clut->trans_index!=COLOR_UNKNOWN ) {
     507           0 :         int trans = base->clut->trans_index;
     508           0 :         Color *clut = base->clut->clut;
     509           0 :         pt = base->data + src->y*base->bytes_per_line + src->x;
     510           0 :         ito = idata;
     511           0 :         for ( i=0; i<src->height; ++i ) {
     512           0 :            for ( j=0; j<src->width; ++j ) {
     513           0 :                int index = pt[j];
     514           0 :                if ( index==trans )
     515           0 :                    ito[j] = 0x00000000;
     516             :                else
     517             :                    /* In theory RGB24 images don't need the alpha channel set*/
     518             :                    /*  but there is a bug in Cairo 1.2, and they do. */
     519           0 :                    ito[j] = clut[index]|0xff000000;
     520             :            }
     521           0 :            pt += base->bytes_per_line;
     522           0 :            ito = (uint32 *) (((uint8 *) ito) +stride);
     523             :        }
     524           0 :     } else if ( base->image_type == it_index ) {
     525           0 :         Color *clut = base->clut->clut;
     526           0 :         pt = base->data + src->y*base->bytes_per_line + src->x;
     527           0 :         ito = idata;
     528           0 :         for ( i=0; i<src->height; ++i ) {
     529           0 :            for ( j=0; j<src->width; ++j ) {
     530           0 :                int index = pt[j];
     531           0 :                ito[j] = clut[index] | 0xff000000;
     532             :            }
     533           0 :            pt += base->bytes_per_line;
     534           0 :            ito = (uint32 *) (((uint8 *) ito) +stride);
     535             :        }
     536             : #ifdef WORDS_BIGENDIAN
     537             :     } else if ( base->image_type == it_mono && base->clut!=NULL &&
     538             :             base->clut->trans_index!=COLOR_UNKNOWN ) {
     539             :         pt = base->data + src->y*base->bytes_per_line + (src->x>>3);
     540             :         ito = idata;
     541             :         memset(data,0,src->height*stride);
     542             :         if ( base->clut->trans_index==0 ) {
     543             :             for ( i=0; i<src->height; ++i ) {
     544             :                 bit = (0x80>>(src->x&0x7));
     545             :                 tobit = 0x80000000;
     546             :                 for ( j=jj=tjj=0; j<src->width; ++j ) {
     547             :                     if ( pt[jj]&bit )
     548             :                         ito[tjj] |= tobit;
     549             :                     if ( (bit>>=1)==0 ) {
     550             :                         bit = 0x80;
     551             :                         ++jj;
     552             :                     }
     553             :                     if ( (tobit>>=1)==0 ) {
     554             :                         tobit = 0x80000000;
     555             :                         ++tjj;
     556             :                     }
     557             :                 }
     558             :                 pt += base->bytes_per_line;
     559             :                 ito = (uint32 *) (((uint8 *) ito) +stride);
     560             :             }
     561             :         } else {
     562             :             for ( i=0; i<src->height; ++i ) {
     563             :                 bit = (0x80>>(src->x&0x7));
     564             :                 tobit = 0x80000000;
     565             :                 for ( j=jj=tjj=0; j<src->width; ++j ) {
     566             :                     if ( !(pt[jj]&bit) )
     567             :                         ito[tjj] |= tobit;
     568             :                     if ( (bit>>=1)==0 ) {
     569             :                         bit = 0x80;
     570             :                         ++jj;
     571             :                     }
     572             :                     if ( (tobit>>=1)==0 ) {
     573             :                         tobit = 0x80000000;
     574             :                         ++tjj;
     575             :                     }
     576             :                 }
     577             :                 pt += base->bytes_per_line;
     578             :                 ito = (uint32 *) (((uint8 *) ito) +stride);
     579             :             }
     580             :         }
     581             : #else
     582           0 :     } else if ( base->image_type == it_mono && base->clut!=NULL &&
     583           0 :             base->clut->trans_index!=COLOR_UNKNOWN ) {
     584           0 :         pt = base->data + src->y*base->bytes_per_line + (src->x>>3);
     585           0 :         ito = idata;
     586           0 :         memset(data,0,src->height*stride);
     587           0 :         if ( base->clut->trans_index==0 ) {
     588           0 :             for ( i=0; i<src->height; ++i ) {
     589           0 :                 bit = (0x80>>(src->x&0x7));
     590           0 :                 tobit = 1;
     591           0 :                 for ( j=jj=tjj=0; j<src->width; ++j ) {
     592           0 :                     if ( pt[jj]&bit )
     593           0 :                         ito[tjj] |= tobit;
     594           0 :                     if ( (bit>>=1)==0 ) {
     595           0 :                         bit = 0x80;
     596           0 :                         ++jj;
     597             :                     }
     598           0 :                     if ( (tobit<<=1)==0 ) {
     599           0 :                         tobit = 0x1;
     600           0 :                         ++tjj;
     601             :                     }
     602             :                 }
     603           0 :                 pt += base->bytes_per_line;
     604           0 :                 ito = (uint32 *) (((uint8 *) ito) +stride);
     605             :             }
     606             :         } else {
     607           0 :             for ( i=0; i<src->height; ++i ) {
     608           0 :                 bit = (0x80>>(src->x&0x7));
     609           0 :                 tobit = 1;
     610           0 :                 for ( j=jj=tjj=0; j<src->width; ++j ) {
     611           0 :                     if ( !(pt[jj]&bit) )
     612           0 :                         ito[tjj] |= tobit;
     613           0 :                     if ( (bit>>=1)==0 ) {
     614           0 :                         bit = 0x80;
     615           0 :                         ++jj;
     616             :                     }
     617           0 :                     if ( (tobit<<=1)==0 ) {
     618           0 :                         tobit = 0x1;
     619           0 :                         ++tjj;
     620             :                     }
     621             :                 }
     622           0 :                 pt += base->bytes_per_line;
     623           0 :                 ito = (uint32 *) (((uint8 *) ito) +stride);
     624             :             }
     625             :         }
     626             : #endif
     627             :     } else {
     628           0 :         Color fg = base->clut==NULL ? 0xffffff : base->clut->clut[1];
     629           0 :         Color bg = base->clut==NULL ? 0x000000 : base->clut->clut[0];
     630             :        /* In theory RGB24 images don't need the alpha channel set*/
     631             :        /*  but there is a bug in Cairo 1.2, and they do. */
     632           0 :         fg |= 0xff000000; bg |= 0xff000000;
     633           0 :         pt = base->data + src->y*base->bytes_per_line + (src->x>>3);
     634           0 :         ito = idata;
     635           0 :         for ( i=0; i<src->height; ++i ) {
     636           0 :             bit = (0x80>>(src->x&0x7));
     637           0 :             for ( j=jj=0; j<src->width; ++j ) {
     638           0 :                 ito[j] = (pt[jj]&bit) ? fg : bg;
     639           0 :                 if ( (bit>>=1)==0 ) {
     640           0 :                     bit = 0x80;
     641           0 :                     ++jj;
     642             :                 }
     643             :             }
     644           0 :             pt += base->bytes_per_line;
     645           0 :             ito = (uint32 *) (((uint8 *) ito) +stride);
     646             :         }
     647             :     }
     648           0 : return( cs );
     649             : }
     650             : 
     651           0 : void _GXCDraw_Image( GXWindow gw, GImage *image, GRect *src, int32 x, int32 y) {
     652             :     uint8 *data;
     653           0 :     cairo_surface_t *is = GImage2Surface(image,src,&data);
     654           0 :     struct _GImage *base = image->list_len==0?image->u.image:image->u.images[0];
     655             : 
     656           0 :     if ( cairo_image_surface_get_format(is)==CAIRO_FORMAT_A1 ) {
     657             :         /* No color info, just alpha channel */
     658           0 :         Color fg = base->clut->trans_index==0 ? base->clut->clut[1] : base->clut->clut[0];
     659           0 :         cairo_set_source_rgba(gw->cc,COLOR_RED(fg)/255.0,COLOR_GREEN(fg)/255.0,COLOR_BLUE(fg)/255.0,1.0);
     660           0 :         cairo_mask_surface(gw->cc,is,x,y);
     661             :     } else {
     662           0 :         cairo_set_source_surface(gw->cc,is,x,y);
     663           0 :         cairo_rectangle(gw->cc,x,y,src->width,src->height);
     664           0 :         cairo_fill(gw->cc);
     665             :     }
     666             :     /* Clear source and mask, in case we need to */
     667           0 :     cairo_new_path(gw->cc);
     668           0 :     cairo_set_source_rgba(gw->cc,0,0,0,0);
     669             : 
     670           0 :     cairo_surface_destroy(is);
     671           0 :     free(data);
     672           0 :     gw->cairo_state.fore_col = COLOR_UNKNOWN;
     673           0 : }
     674             : 
     675           0 : void _GXCDraw_TileImage( GXWindow gw, GImage *image, GRect *src, int32 x, int32 y) {
     676           0 : }
     677             : 
     678             : /* What we really want to do is use the grey levels as an alpha channel */
     679           0 : void _GXCDraw_Glyph( GXWindow gw, GImage *image, GRect *src, int32 x, int32 y) {
     680           0 :     struct _GImage *base = image->list_len==0?image->u.image:image->u.images[0];
     681             :     cairo_surface_t *is;
     682             : 
     683           0 :     if ( base->image_type!=it_index )
     684           0 :         _GXCDraw_Image(gw,image,src,x,y);
     685             :     else {
     686           0 :         int stride = cairo_format_stride_for_width(CAIRO_FORMAT_A8,src->width);
     687           0 :         uint8 *basedata = malloc(stride*src->height),
     688           0 :                *data = basedata,
     689           0 :                 *srcd = base->data + src->y*base->bytes_per_line + src->x;
     690           0 :         int factor = base->clut->clut_len==256 ? 1 :
     691           0 :                      base->clut->clut_len==16 ? 17 :
     692           0 :                      base->clut->clut_len==4 ? 85 : 255;
     693             :         int i,j;
     694           0 :         Color fg = base->clut->clut[base->clut->clut_len-1];
     695             : 
     696           0 :         for ( i=0; i<src->height; ++i ) {
     697           0 :             for ( j=0; j<src->width; ++j )
     698           0 :                 data[j] = factor*srcd[j];
     699           0 :             srcd += base->bytes_per_line;
     700           0 :             data += stride;
     701             :         }
     702           0 :         is = cairo_image_surface_create_for_data(basedata,CAIRO_FORMAT_A8,
     703             :                 src->width,src->height,stride);
     704           0 :         cairo_set_source_rgba(gw->cc,COLOR_RED(fg)/255.0,COLOR_GREEN(fg)/255.0,COLOR_BLUE(fg)/255.0,1.0);
     705           0 :         cairo_mask_surface(gw->cc,is,x,y);
     706             :         /* I think the mask is sufficient, setting a rectangle would provide */
     707             :         /*  a new mask? */
     708             :         /*cairo_rectangle(gw->cc,x,y,src->width,src->height);*/
     709             :         /* I think setting the mask also draws... at least so the tutorial implies */
     710             :         /* cairo_fill(gw->cc);*/
     711             :         /* Presumably that doesn't leave the mask surface pattern lying around */
     712             :         /* but dereferences it so we can free it */
     713           0 :         cairo_surface_destroy(is);
     714           0 :         free(basedata);
     715             :     }
     716           0 :     gw->cairo_state.fore_col = COLOR_UNKNOWN;
     717           0 : }
     718             : 
     719           0 : void _GXCDraw_ImageMagnified(GXWindow gw, GImage *image, GRect *magsrc,
     720             :         int32 x, int32 y, int32 width, int32 height) {
     721           0 :     struct _GImage *base = image->list_len==0?image->u.image:image->u.images[0];
     722             :     GRect full;
     723             :     double xscale, yscale;
     724             :     GRect viewable;
     725             : 
     726           0 :     viewable = gw->ggc->clip;
     727           0 :     if ( viewable.width > gw->pos.width-viewable.x )
     728           0 :         viewable.width = gw->pos.width-viewable.x;
     729           0 :     if ( viewable.height > gw->pos.height-viewable.y )
     730           0 :         viewable.height = gw->pos.height-viewable.y;
     731             : 
     732           0 :     xscale = (base->width>=1) ? ((double) (width))/(base->width) : 1;
     733           0 :     yscale = (base->height>=1) ? ((double) (height))/(base->height) : 1;
     734             :     /* Intersect the clip rectangle with the scaled image to find the */
     735             :     /*  portion of screen that we want to draw */
     736           0 :     if ( viewable.x<x ) {
     737           0 :         viewable.width -= (x-viewable.x);
     738           0 :         viewable.x = x;
     739             :     }
     740           0 :     if ( viewable.y<y ) {
     741           0 :         viewable.height -= (y-viewable.y);
     742           0 :         viewable.y = y;
     743             :     }
     744           0 :     if ( viewable.x+viewable.width > x+width ) viewable.width = x+width - viewable.x;
     745           0 :     if ( viewable.y+viewable.height > y+height ) viewable.height = y+height - viewable.y;
     746           0 :     if ( viewable.height<0 || viewable.width<0 )
     747           0 : return;
     748             : 
     749             :     /* Now find that same rectangle in the coordinates of the unscaled image */
     750             :     /* (translation & scale) */
     751           0 :     viewable.x -= x; viewable.y -= y;
     752           0 :     full.x = viewable.x/xscale; full.y = viewable.y/yscale;
     753           0 :     full.width = viewable.width/xscale; full.height = viewable.height/yscale;
     754           0 :     if ( full.x+full.width>base->width ) full.width = base->width-full.x;      /* Rounding errors */
     755           0 :     if ( full.y+full.height>base->height ) full.height = base->height-full.y;  /* Rounding errors */
     756             :                 /* Rounding errors */
     757             :   {
     758           0 :     GImage *temp = _GImageExtract(base,&full,&viewable,xscale,yscale);
     759             :     GRect src;
     760           0 :     src.x = src.y = 0; src.width = viewable.width; src.height = viewable.height;
     761           0 :     _GXCDraw_Image( gw, temp, &src, x+viewable.x, y+viewable.y);
     762             :   }
     763             : }
     764             : 
     765             : /* ************************************************************************** */
     766             : /* ******************************** Copy Area ******************************* */
     767             : /* ************************************************************************** */
     768             : 
     769           0 : void _GXCDraw_CopyArea( GXWindow from, GXWindow into, GRect *src, int32 x, int32 y) {
     770             : 
     771           0 :     if ( !into->usecairo || !from->usecairo ) {
     772           0 :         fprintf( stderr, "Cairo CopyArea called from something not cairo enabled\n" );
     773           0 : return;
     774             :     }
     775             : 
     776             :     int width, height;
     777             : 
     778           0 :     width = cairo_xlib_surface_get_width(into->cs);
     779           0 :     height = cairo_xlib_surface_get_height(into->cs);
     780             : 
     781             :     /* make sure the destination surface is big enough for the copied area */
     782           0 :     cairo_xlib_surface_set_size(into->cs, imax(width, src->width), imax(height, src->height));
     783             : 
     784           0 :     cairo_set_source_surface(into->cc,from->cs,x-src->x,y-src->y);
     785           0 :     cairo_rectangle(into->cc,x,y,src->width,src->height);
     786           0 :     cairo_fill(into->cc);
     787             : 
     788             :     /* Clear source and mask, in case we need to */
     789           0 :     cairo_set_source_rgba(into->cc,0,0,0,0);
     790             : 
     791           0 :     into->cairo_state.fore_col = COLOR_UNKNOWN;
     792             : }
     793             : 
     794             : /* ************************************************************************** */
     795             : /* **************************** Memory Buffering **************************** */
     796             : /* ************************************************************************** */
     797             : /* We can't draw with XOR in cairo. We can do all the cairo processing, copy */
     798             : /*  cairo's data to the x window, and then do the xor drawing. But if the X */
     799             : /*  window isn't available (if we are buffering cairo) then we must save the */
     800             : /*  XOR drawing operations until we've popped the buffering */
     801             : /* Mmm. Now we use pixmaps rather than groups and the issue isn't relevant -- I think */
     802             : 
     803           0 : enum gcairo_flags _GXCDraw_CairoCapabilities( GXWindow gw) {
     804           0 :     enum gcairo_flags flags = gc_all;
     805             : 
     806           0 : return( flags|gc_xor ); /* If not buffered, we can emulate xor by having X11 do it in the X layer */
     807             : }
     808             : /* ************************************************************************** */
     809             : /* **************************** Synchronization ***************************** */
     810             : /* ************************************************************************** */
     811           0 : void _GXCDraw_Flush(GXWindow gw) {
     812           0 :     cairo_surface_flush(gw->cs);
     813           0 : }
     814             : 
     815           0 : void _GXCDraw_DirtyRect(GXWindow gw,double x, double y, double width, double height) {
     816           0 :     cairo_surface_mark_dirty_rectangle(gw->cs,x,y,width,height);
     817           0 : }
     818             : #else
     819             : int _GXCDraw_hasCairo(void) {
     820             : return(false);
     821             : }
     822             : 
     823             : #endif  /* ! _NO_LIBCAIRO */
     824             : 
     825             : /* ************************************************************************** */
     826             : /* ***************************** Pango Library ****************************** */
     827             : /* ************************************************************************** */
     828             : 
     829             : #  define GTimer GTimer_GTK
     830             : #  include <pango/pangoxft.h>
     831             : #  if !defined(_NO_LIBCAIRO)
     832             : #   include <pango/pangocairo.h>
     833             : #  endif
     834             : #  undef GTimer
     835             : 
     836             : /* ************************************************************************** */
     837             : /* ****************************** Pango Render ****************************** */
     838             : /* ************************************************************************** */
     839             : 
     840             : /* This is not a drop-in replacement for pango_xft_render_layout as as both */
     841             : /* expect x and y in different ways */
     842           0 : static void my_xft_render_layout(XftDraw *xftw,XftColor *fgcol,
     843             :         PangoLayout *layout,int x,int y) {
     844             :     PangoRectangle rect, r2;
     845             :     PangoLayoutIter *iter;
     846             : 
     847           0 :     iter = pango_layout_get_iter(layout);
     848             :     do {
     849           0 :         PangoLayoutRun *run = pango_layout_iter_get_run(iter);
     850           0 :         if ( run!=NULL ) {      /* NULL runs mark end of line */
     851           0 :             pango_layout_iter_get_run_extents(iter,&r2,&rect);
     852           0 :             pango_xft_render(xftw,fgcol,run->item->analysis.font,run->glyphs,
     853           0 :                     x+(rect.x+PANGO_SCALE/2)/PANGO_SCALE, y+(rect.y+PANGO_SCALE/2)/PANGO_SCALE);
     854             :             /* I doubt I'm supposed to free (or unref) the run? */
     855             :         }
     856           0 :     } while ( pango_layout_iter_next_run(iter));
     857           0 :     pango_layout_iter_free(iter);
     858           0 : }
     859             : 
     860             : # if !defined(_NO_LIBCAIRO)
     861             : /* Strangely the equivalent routine was not part of the pangocairo library */
     862             : /* Oh there's pango_cairo_layout_path but that's more restrictive and probably*/
     863             : /*  less efficient */
     864             : 
     865           0 : static void my_cairo_render_layout(cairo_t *cc, Color fg,
     866             :         PangoLayout *layout,int x,int y) {
     867             :     PangoRectangle rect, r2;
     868             :     PangoLayoutIter *iter;
     869             : 
     870           0 :     iter = pango_layout_get_iter(layout);
     871             :     do {
     872           0 :         PangoLayoutRun *run = pango_layout_iter_get_run(iter);
     873           0 :         if ( run!=NULL ) {      /* NULL runs mark end of line */
     874           0 :             pango_layout_iter_get_run_extents(iter,&r2,&rect);
     875           0 :             cairo_move_to(cc,x+(rect.x+PANGO_SCALE/2)/PANGO_SCALE, y+(rect.y+PANGO_SCALE/2)/PANGO_SCALE);
     876           0 :             if ( COLOR_ALPHA(fg)==0 )
     877           0 :                 cairo_set_source_rgba(cc,COLOR_RED(fg)/255.0,COLOR_GREEN(fg)/255.0,COLOR_BLUE(fg)/255.0,
     878             :                         1.0);
     879             :             else
     880           0 :                 cairo_set_source_rgba(cc,COLOR_RED(fg)/255.0,COLOR_GREEN(fg)/255.0,COLOR_BLUE(fg)/255.0,
     881           0 :                         COLOR_ALPHA(fg)/255.);
     882           0 :             pango_cairo_show_glyph_string(cc,run->item->analysis.font,run->glyphs);
     883             :         }
     884           0 :     } while ( pango_layout_iter_next_run(iter));
     885           0 :     pango_layout_iter_free(iter);
     886           0 : }
     887             : #endif
     888             : 
     889             : /* ************************************************************************** */
     890             : /* ****************************** Pango Window ****************************** */
     891             : /* ************************************************************************** */
     892           0 : void _GXPDraw_NewWindow(GXWindow nw) {
     893           0 :     GXDisplay *gdisp = nw->display;
     894             : 
     895             : # if !defined(_NO_LIBCAIRO)
     896           0 :     if ( nw->usecairo ) {
     897             :         /* using pango through cairo is different from using it on bare X */
     898           0 :         if ( gdisp->pangoc_context==NULL ) {
     899           0 :             gdisp->pangoc_fontmap = pango_cairo_font_map_get_default();
     900           0 :             gdisp->pangoc_context = pango_font_map_create_context(
     901           0 :                     PANGO_FONT_MAP (gdisp->pangoc_fontmap));
     902           0 :             pango_cairo_context_set_resolution(gdisp->pangoc_context,
     903           0 :                     gdisp->res);
     904             :         }
     905           0 :         if (nw->pango_layout==NULL)
     906           0 :             nw->pango_layout = pango_layout_new(gdisp->pangoc_context);
     907             :     } else
     908             : # endif
     909             :     {
     910           0 :         if ( gdisp->pango_context==NULL ) {
     911           0 :             gdisp->pango_fontmap = pango_xft_get_font_map(gdisp->display,gdisp->screen);
     912           0 :             gdisp->pango_context = pango_font_map_create_context(gdisp->pango_fontmap);
     913             :             /* No obvious way to get or set the resolution of pango_xft */
     914             :         }
     915           0 :         nw->xft_w = XftDrawCreate(gdisp->display,nw->w,gdisp->visual,gdisp->cmap);
     916           0 :         if ( nw->pango_layout==NULL )
     917           0 :             nw->pango_layout = pango_layout_new(gdisp->pango_context);
     918             :     }
     919           0 : return;
     920             : }
     921             : 
     922           0 : void _GXPDraw_DestroyWindow(GXWindow nw) {
     923             :     /* And why doesn't the man page mention this essential function? */
     924           0 :     if ( XftDrawDestroy!=NULL && nw->xft_w!=NULL ) {
     925           0 :         XftDrawDestroy(nw->xft_w);
     926           0 :         nw->xft_w = NULL;
     927             :     }
     928           0 :     if ( nw->pango_layout!=NULL )
     929           0 :         g_object_unref(nw->pango_layout);
     930           0 : }
     931             : 
     932             : /* ************************************************************************** */
     933             : /* ******************************* Pango Text ******************************* */
     934             : /* ************************************************************************** */
     935           0 : PangoFontDescription *_GXPDraw_configfont(GWindow w, GFont *font) {
     936           0 :     GXWindow gw = (GXWindow) w;
     937             :     PangoFontDescription *fd;
     938             : 
     939             :     /* initialize cairo and pango if not initialized, e.g. root window */
     940           0 :     if (gw->pango_layout == NULL){
     941             : #ifndef _NO_LIBCAIRO
     942           0 :         _GXCDraw_NewWindow(gw);
     943             : #endif
     944           0 :         _GXPDraw_NewWindow(gw);
     945             :     }
     946             : 
     947             : #ifdef _NO_LIBCAIRO
     948             :     PangoFontDescription **fdbase = &font->pango_fd;
     949             : #else
     950           0 :     PangoFontDescription **fdbase = gw->usecairo ? &font->pangoc_fd : &font->pango_fd;
     951             : #endif
     952             : 
     953           0 :     if ( *fdbase!=NULL )
     954           0 : return( *fdbase );
     955           0 :     *fdbase = fd = pango_font_description_new();
     956             : 
     957           0 :     if ( font->rq.utf8_family_name != NULL )
     958           0 :         pango_font_description_set_family(fd,font->rq.utf8_family_name);
     959             :     else {
     960           0 :         char *temp = u2utf8_copy(font->rq.family_name);
     961           0 :         pango_font_description_set_family(fd,temp);
     962           0 :         free(temp);
     963             :     }
     964           0 :     pango_font_description_set_style(fd,(font->rq.style&fs_italic)?
     965             :             PANGO_STYLE_ITALIC:
     966             :             PANGO_STYLE_NORMAL);
     967           0 :     pango_font_description_set_variant(fd,(font->rq.style&fs_smallcaps)?
     968             :             PANGO_VARIANT_SMALL_CAPS:
     969             :             PANGO_VARIANT_NORMAL);
     970           0 :     pango_font_description_set_weight(fd,font->rq.weight);
     971           0 :     pango_font_description_set_stretch(fd,
     972           0 :             (font->rq.style&fs_condensed)?  PANGO_STRETCH_CONDENSED :
     973           0 :             (font->rq.style&fs_extended )?  PANGO_STRETCH_EXPANDED  :
     974             :                                             PANGO_STRETCH_NORMAL);
     975             : 
     976           0 :     if (font->rq.style&fs_vertical)
     977             :         /* FIXME: not sure this is the right thing */
     978           0 :         pango_font_description_set_gravity(fd, PANGO_GRAVITY_WEST);
     979             : 
     980           0 :     if ( font->rq.point_size<=0 )
     981           0 :         GDrawIError( "Bad point size for pango" );    /* any negative (pixel) values should be converted when font opened */
     982             : 
     983             :     /* Pango doesn't give me any control over the resolution on X, so I do my */
     984             :     /*  own conversion from points to pixels */
     985             :     /* But under pangocairo I can set the resolution, so behavior is different*/
     986           0 :     pango_font_description_set_absolute_size(fd,
     987           0 :                     GDrawPointsToPixels(NULL,font->rq.point_size*PANGO_SCALE));
     988           0 : return( fd );
     989             : }
     990             : 
     991           0 : int32 _GXPDraw_DoText8(GWindow w, int32 x, int32 y,
     992             :         const char *text, int32 cnt, Color col,
     993             :         enum text_funcs drawit, struct tf_arg *arg) {
     994           0 :     GXWindow gw = (GXWindow) w;
     995           0 :     GXDisplay *gdisp = gw->display;
     996           0 :     struct font_instance *fi = gw->ggc->fi;
     997             :     PangoRectangle rect, ink;
     998             :     PangoFontDescription *fd;
     999             : 
    1000           0 :     if (fi == NULL)
    1001           0 :         return(0);
    1002             : 
    1003           0 :     fd = _GXPDraw_configfont(w, fi);
    1004           0 :     pango_layout_set_font_description(gw->pango_layout,fd);
    1005           0 :     pango_layout_set_text(gw->pango_layout,(char *) text,cnt);
    1006           0 :     pango_layout_get_pixel_extents(gw->pango_layout,NULL,&rect);
    1007           0 :     if ( drawit==tf_drawit ) {
    1008             : # if !defined(_NO_LIBCAIRO)
    1009           0 :         if ( gw->usecairo ) {
    1010           0 :             my_cairo_render_layout(gw->cc,col,gw->pango_layout,x,y);
    1011             :         } else
    1012             : #endif
    1013             :         {
    1014             :             XftColor fg;
    1015             :             XRenderColor fgcol;
    1016             :             XRectangle clip;
    1017           0 :             fgcol.red = COLOR_RED(col)<<8; fgcol.green = COLOR_GREEN(col)<<8; fgcol.blue = COLOR_BLUE(col)<<8;
    1018           0 :             if ( COLOR_ALPHA(col)!=0 )
    1019           0 :                 fgcol.alpha = COLOR_ALPHA(col)*0x101;
    1020             :             else
    1021           0 :                 fgcol.alpha = 0xffff;
    1022           0 :             XftColorAllocValue(gdisp->display,gdisp->visual,gdisp->cmap,&fgcol,&fg);
    1023           0 :             clip.x = gw->ggc->clip.x;
    1024           0 :             clip.y = gw->ggc->clip.y;
    1025           0 :             clip.width = gw->ggc->clip.width;
    1026           0 :             clip.height = gw->ggc->clip.height;
    1027           0 :             XftDrawSetClipRectangles(gw->xft_w,0,0,&clip,1);
    1028           0 :             my_xft_render_layout(gw->xft_w,&fg,gw->pango_layout,x,y);
    1029             :         }
    1030           0 :     } else if ( drawit==tf_rect ) {
    1031             :         PangoLayoutIter *iter;
    1032             :         PangoLayoutRun *run;
    1033             :         PangoFontMetrics *fm;
    1034             : 
    1035           0 :         pango_layout_get_pixel_extents(gw->pango_layout,&ink,&rect);
    1036           0 :         arg->size.lbearing = ink.x - rect.x;
    1037           0 :         arg->size.rbearing = ink.x+ink.width - rect.x;
    1038           0 :         arg->size.width = rect.width;
    1039           0 :         if ( *text=='\0' ) {
    1040             :             /* There are no runs if there are no characters */
    1041           0 :             memset(&arg->size,0,sizeof(arg->size));
    1042             :         } else {
    1043           0 :             iter = pango_layout_get_iter(gw->pango_layout);
    1044           0 :             run = pango_layout_iter_get_run(iter);
    1045           0 :             if ( run==NULL ) {
    1046             :                 /* Pango doesn't give us runs in a couple of other places */
    1047             :                 /* surrogates, not unicode (0xfffe, 0xffff), etc. */
    1048           0 :                 memset(&arg->size,0,sizeof(arg->size));
    1049             :             } else {
    1050           0 :                 fm = pango_font_get_metrics(run->item->analysis.font,NULL);
    1051           0 :                 arg->size.fas = pango_font_metrics_get_ascent(fm)/PANGO_SCALE;
    1052           0 :                 arg->size.fds = pango_font_metrics_get_descent(fm)/PANGO_SCALE;
    1053           0 :                 arg->size.as = ink.y + ink.height - arg->size.fds;
    1054           0 :                 arg->size.ds = arg->size.fds - ink.y;
    1055           0 :                 if ( arg->size.ds<0 ) {
    1056           0 :                     --arg->size.as;
    1057           0 :                     arg->size.ds = 0;
    1058             :                 }
    1059             :                 /* In the one case I've looked at fds is one pixel off from rect.y */
    1060             :                 /*  I don't know what to make of that */
    1061           0 :                 pango_font_metrics_unref(fm);
    1062             :             }
    1063           0 :             pango_layout_iter_free(iter);
    1064             :         }
    1065             :     }
    1066           0 : return( rect.width );
    1067             : }
    1068             : 
    1069           0 : int32 _GXPDraw_DoText(GWindow w, int32 x, int32 y,
    1070             :         const unichar_t *text, int32 cnt, Color col,
    1071             :         enum text_funcs drawit, struct tf_arg *arg) {
    1072           0 :     char *temp = cnt>=0 ? u2utf8_copyn(text,cnt) : u2utf8_copy(text);
    1073           0 :     if (temp == NULL) return 0;
    1074           0 :     int width = _GXPDraw_DoText8(w,x,y,temp,-1,col,drawit,arg);
    1075           0 :     free(temp);
    1076           0 : return(width);
    1077             : }
    1078             : 
    1079           0 : void _GXPDraw_FontMetrics(GWindow gw, GFont *fi, int *as, int *ds, int *ld) {
    1080           0 :     GXDisplay *gdisp = ((GXWindow) gw)->display;
    1081             :     PangoFont *pfont;
    1082             :     PangoFontMetrics *fm;
    1083             : 
    1084           0 :     _GXPDraw_configfont(gw, fi);
    1085             : # if !defined(_NO_LIBCAIRO)
    1086           0 :     if ( gw->usecairo )
    1087           0 :         pfont = pango_font_map_load_font(gdisp->pangoc_fontmap,gdisp->pangoc_context,
    1088           0 :                 fi->pangoc_fd);
    1089             :     else
    1090             : #endif
    1091           0 :         pfont = pango_font_map_load_font(gdisp->pango_fontmap,gdisp->pango_context,
    1092           0 :                 fi->pango_fd);
    1093           0 :     fm = pango_font_get_metrics(pfont,NULL);
    1094           0 :     *as = pango_font_metrics_get_ascent(fm)/PANGO_SCALE;
    1095           0 :     *ds = pango_font_metrics_get_descent(fm)/PANGO_SCALE;
    1096           0 :     *ld = 0;
    1097           0 :     pango_font_metrics_unref(fm);
    1098             :     // pango_font_unref(pfont);
    1099             :     // This function has disappeared from Pango with no explanation.
    1100             :     // But we still leak memory here.
    1101           0 : }
    1102             : 
    1103             : /* ************************************************************************** */
    1104             : /* ****************************** Pango Layout ****************************** */
    1105             : /* ************************************************************************** */
    1106           0 : void _GXPDraw_LayoutInit(GWindow w, char *text, int cnt, GFont *fi) {
    1107           0 :     GXWindow gw = (GXWindow) w;
    1108             :     PangoFontDescription *fd;
    1109             : 
    1110           0 :     if ( fi==NULL )
    1111           0 :         fi = gw->ggc->fi;
    1112             : 
    1113           0 :     fd = _GXPDraw_configfont(w, fi);
    1114           0 :     pango_layout_set_font_description(gw->pango_layout,fd);
    1115           0 :     pango_layout_set_text(gw->pango_layout,(char *) text,cnt);
    1116           0 : }
    1117             : 
    1118           0 : void _GXPDraw_LayoutDraw(GWindow w, int32 x, int32 y, Color col) {
    1119           0 :     GXWindow gw = (GXWindow) w;
    1120           0 :     GXDisplay *gdisp = gw->display;
    1121             : 
    1122             : # if !defined(_NO_LIBCAIRO)
    1123           0 :     if ( gw->usecairo ) {
    1124           0 :         my_cairo_render_layout(gw->cc,col,gw->pango_layout,x,y);
    1125             :     } else
    1126             : #endif
    1127             :     {
    1128             :         XftColor fg;
    1129             :         XRenderColor fgcol;
    1130             :         XRectangle clip;
    1131           0 :         fgcol.red = COLOR_RED(col)<<8; fgcol.green = COLOR_GREEN(col)<<8; fgcol.blue = COLOR_BLUE(col)<<8;
    1132           0 :         if ( COLOR_ALPHA(col)!=0 )
    1133           0 :             fgcol.alpha = COLOR_ALPHA(col)*0x101;
    1134             :         else
    1135           0 :             fgcol.alpha = 0xffff;
    1136           0 :         XftColorAllocValue(gdisp->display,gdisp->visual,gdisp->cmap,&fgcol,&fg);
    1137           0 :         clip.x = gw->ggc->clip.x;
    1138           0 :         clip.y = gw->ggc->clip.y;
    1139           0 :         clip.width = gw->ggc->clip.width;
    1140           0 :         clip.height = gw->ggc->clip.height;
    1141           0 :         XftDrawSetClipRectangles(gw->xft_w,0,0,&clip,1);
    1142           0 :         my_xft_render_layout(gw->xft_w,&fg,gw->pango_layout,x,y);
    1143             :     }
    1144           0 : }
    1145             : 
    1146           0 : void _GXPDraw_LayoutIndexToPos(GWindow w, int index, GRect *pos) {
    1147           0 :     GXWindow gw = (GXWindow) w;
    1148             :     PangoRectangle rect;
    1149             : 
    1150           0 :     pango_layout_index_to_pos(gw->pango_layout,index,&rect);
    1151           0 :     pos->x = rect.x/PANGO_SCALE; pos->y = rect.y/PANGO_SCALE; pos->width = rect.width/PANGO_SCALE; pos->height = rect.height/PANGO_SCALE;
    1152           0 : }
    1153             : 
    1154           0 : int _GXPDraw_LayoutXYToIndex(GWindow w, int x, int y) {
    1155           0 :     GXWindow gw = (GXWindow) w;
    1156             :     int trailing, index;
    1157             : 
    1158             :     /* Pango retuns the last character if x is negative, not the first */
    1159           0 :     if ( x<0 ) x=0;
    1160           0 :     pango_layout_xy_to_index(gw->pango_layout,x*PANGO_SCALE,y*PANGO_SCALE,&index,&trailing);
    1161             :     /* If I give pango a position after the last character on a line, it */
    1162             :     /*  returns to me the first character. Strange. And annoying -- you click */
    1163             :     /*  at the end of a line and the cursor moves to the start */
    1164             :     /* Of course in right to left text an initial position is correct... */
    1165           0 :     if ( index+trailing==0 && x>0 ) {
    1166             :         PangoRectangle rect;
    1167           0 :         pango_layout_get_pixel_extents(gw->pango_layout,&rect,NULL);
    1168           0 :         if ( x>=rect.width ) {
    1169           0 :             x = rect.width-1;
    1170           0 :             pango_layout_xy_to_index(gw->pango_layout,x*PANGO_SCALE,y*PANGO_SCALE,&index,&trailing);
    1171             :         }
    1172             :     }
    1173           0 : return( index+trailing );
    1174             : }
    1175             : 
    1176           0 : void _GXPDraw_LayoutExtents(GWindow w, GRect *size) {
    1177           0 :     GXWindow gw = (GXWindow) w;
    1178             :     PangoRectangle rect;
    1179             : 
    1180           0 :     pango_layout_get_pixel_extents(gw->pango_layout,NULL,&rect);
    1181           0 :     size->x = rect.x; size->y = rect.y; size->width = rect.width; size->height = rect.height;
    1182           0 : }
    1183             : 
    1184           0 : void _GXPDraw_LayoutSetWidth(GWindow w, int width) {
    1185           0 :     GXWindow gw = (GXWindow) w;
    1186             : 
    1187           0 :     pango_layout_set_width(gw->pango_layout,width==-1? -1 : width*PANGO_SCALE);
    1188           0 : }
    1189             : 
    1190           0 : int _GXPDraw_LayoutLineCount(GWindow w) {
    1191           0 :     GXWindow gw = (GXWindow) w;
    1192             : 
    1193           0 : return( pango_layout_get_line_count(gw->pango_layout));
    1194             : }
    1195             : 
    1196           0 : int _GXPDraw_LayoutLineStart(GWindow w, int l) {
    1197           0 :     GXWindow gw = (GXWindow) w;
    1198             :     PangoLayoutLine *line;
    1199             : 
    1200           0 :     line = pango_layout_get_line(gw->pango_layout,l);
    1201           0 :     if ( line==NULL )
    1202           0 : return( -1 );
    1203             : 
    1204           0 : return( line->start_index );
    1205             : }

Generated by: LCOV version 1.10