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 : }
|