1 /* Copyright (C) 2000-2012 by George Williams */
2 /* Copyright (C) 2016 by Jeremy Tan */
3 /*
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6 
7  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9 
10  * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13 
14  * The name of the author may not be used to endorse or promote products
15  * derived from this software without specific prior written permission.
16 
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
18  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
20  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
26  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <fontforge-config.h>
30 
31 /**
32  *  \file ggdkcdraw.c
33  *  \brief Cairo drawing functionality
34  *  This is based on gxcdraw.c, but modified to suit GDK.
35  */
36 
37 #include "ggdkdrawP.h"
38 
39 #ifdef FONTFORGE_CAN_USE_GDK
40 
41 #include "ustring.h"
42 
43 #include <assert.h>
44 #include <math.h>
45 
46 // Private member functions
47 
_GGDKDraw_CheckAutoPaint(GGDKWindow gw)48 static void _GGDKDraw_CheckAutoPaint(GGDKWindow gw) {
49     if (gw->cc == NULL) {
50         assert(!gw->is_pixmap);
51 
52         //Log(LOGWARN, "Dirty dirty window! 0x%p", gw);
53         if (gw->display->dirty_window != gw) {
54             _GGDKDraw_CleanupAutoPaint(gw->display);
55             gw->display->dirty_window = gw;
56         }
57 
58         // Shut up! I know what I'm doing.
59 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
60         gw->cc = gdk_cairo_create(gw->w);
61 G_GNUC_END_IGNORE_DEPRECATIONS
62 
63         // Unlike GDK2, it turns out you can draw over child windows
64         // But we don't want that. Must be something to do with alpha transparency.
65         if (!gdk_window_has_native(gw->w)) {
66             cairo_region_t *r = _GGDKDraw_ExcludeChildRegions(gw, NULL, false);
67             if (r != NULL) {
68                 gdk_cairo_region(gw->cc, r);
69                 cairo_clip(gw->cc);
70                 cairo_region_destroy(r);
71             }
72         }
73     }
74 }
75 
GGDKDraw_StippleMePink(GGDKWindow gw,int ts,Color fg)76 static void GGDKDraw_StippleMePink(GGDKWindow gw, int ts, Color fg) {
77     static unsigned char grey_init[8] = {0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa};
78     static unsigned char fence_init[8] = {0x55, 0x22, 0x55, 0x88, 0x55, 0x22, 0x55, 0x88};
79     uint8 *spt;
80     int bit, i, j;
81     uint32 *data;
82     static uint32 space[8 * 8];
83     static cairo_pattern_t *pat = NULL;
84 
85     if ((fg >> 24) != 0xff) {
86         int alpha = fg >> 24, r = COLOR_RED(fg), g = COLOR_GREEN(fg), b = COLOR_BLUE(fg);
87         r = (alpha * r + 128) / 255;
88         g = (alpha * g + 128) / 255;
89         b = (alpha * b + 128) / 255;
90         fg = (alpha << 24) | (r << 16) | (g << 8) | b;
91     }
92 
93     spt = (ts == 2) ? fence_init : grey_init;
94     for (i = 0; i < 8; ++i) {
95         data = space + 8 * i;
96         for (j = 0, bit = 0x80; bit != 0; ++j, bit >>= 1) {
97             if (spt[i]&bit) {
98                 data[j] = fg;
99             } else {
100                 data[j] = 0;
101             }
102         }
103     }
104     if (pat == NULL) {
105         cairo_surface_t *is = cairo_image_surface_create_for_data((uint8 *) space,
106                               CAIRO_FORMAT_ARGB32, 8, 8, 8 * 4);
107         pat = cairo_pattern_create_for_surface(is);
108         cairo_surface_destroy(is);
109         cairo_pattern_set_extend(pat, CAIRO_EXTEND_REPEAT);
110     }
111     cairo_set_source(gw->cc, pat);
112 }
113 
GGDKDrawSetcolfunc(GGDKWindow gw,GGC * mine)114 static int GGDKDrawSetcolfunc(GGDKWindow gw, GGC *mine) {
115     Color fg = mine->fg;
116 
117     if ((fg >> 24) == 0) {
118         fg |= 0xff000000;
119     }
120 
121     if (mine->ts != 0) {
122         GGDKDraw_StippleMePink(gw, mine->ts, fg);
123     } else {
124         cairo_set_source_rgba(gw->cc, COLOR_RED(fg) / 255.0,
125                               COLOR_GREEN(fg) / 255.0, COLOR_BLUE(fg) / 255.0, (fg >> 24) / 255.);
126     }
127     return true;
128 }
129 
GGDKDrawSetline(GGDKWindow gw,GGC * mine)130 static int GGDKDrawSetline(GGDKWindow gw, GGC *mine) {
131     Color fg = mine->fg;
132     double dashes[2] = {mine->dash_len, mine->skip_len};
133 
134     if ((fg >> 24) == 0) {
135         fg |= 0xff000000;
136     }
137 
138     if (mine->line_width <= 0) {
139         mine->line_width = 1;
140     }
141     cairo_set_line_width(gw->cc, mine->line_width);
142     cairo_set_dash(gw->cc, dashes, (mine->dash_len == 0) ? 0 : 2, mine->dash_offset);
143 
144     // I don't use line join/cap. On a screen with small line_width they are irrelevant
145 
146     if (mine->ts != 0) {
147         GGDKDraw_StippleMePink(gw, mine->ts, fg);
148     } else {
149         cairo_set_source_rgba(gw->cc, COLOR_RED(fg) / 255.0, COLOR_GREEN(fg) / 255.0, COLOR_BLUE(fg) / 255.0,
150                               (fg >> 24) / 255.0);
151     }
152     return mine->line_width;
153 }
154 
155 // Pango text
_GGDKDraw_configfont(GWindow w,GFont * font)156 static PangoFontDescription *_GGDKDraw_configfont(GWindow w, GFont *font) {
157     GGDKWindow gw = (GGDKWindow) w;
158     PangoFontDescription *fd;
159 
160     // Initialize Cairo and Pango if not initialized, e.g. root window
161     if (gw->pango_layout == NULL && !_GGDKDraw_InitPangoCairo(gw)) {
162         return NULL;
163     }
164 
165     PangoFontDescription **fdbase = &font->pangoc_fd;
166     if (*fdbase != NULL) {
167         return *fdbase;
168     }
169     *fdbase = fd = pango_font_description_new();
170     if (*fdbase == NULL) {
171         return NULL;
172     }
173 
174     if (font->rq.utf8_family_name != NULL) {
175         pango_font_description_set_family(fd, font->rq.utf8_family_name);
176     } else {
177         char *temp = u2utf8_copy(font->rq.family_name);
178         pango_font_description_set_family(fd, temp);
179         free(temp);
180     }
181 
182     pango_font_description_set_style(fd, (font->rq.style & fs_italic) ?
183                                      PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
184     pango_font_description_set_variant(fd, (font->rq.style & fs_smallcaps) ?
185                                        PANGO_VARIANT_SMALL_CAPS : PANGO_VARIANT_NORMAL);
186     pango_font_description_set_weight(fd, font->rq.weight);
187     pango_font_description_set_stretch(fd,
188                                        (font->rq.style & fs_condensed) ? PANGO_STRETCH_CONDENSED :
189                                        (font->rq.style & fs_extended) ? PANGO_STRETCH_EXPANDED  :
190                                        PANGO_STRETCH_NORMAL);
191 
192     if (font->rq.style & fs_vertical) {
193         //FIXME: not sure this is the right thing
194         pango_font_description_set_gravity(fd, PANGO_GRAVITY_WEST);
195     }
196 
197     if (font->rq.point_size <= 0) {
198         // Any negative (pixel) values should be converted when font opened
199         GDrawIError("Bad point size for Pango");
200     }
201 
202     // Pango doesn't give me any control over the resolution on X, so I do my
203     //  own conversion from points to pixels
204     // But under pangocairo I can set the resolution, so behavior is different
205     pango_font_description_set_absolute_size(fd,
206             GDrawPointsToPixels(w, font->rq.point_size * PANGO_SCALE));
207     return fd;
208 }
209 
210 // Strangely the equivalent routine was not part of the pangocairo library
211 // Oh there's pango_cairo_layout_path but that's more restrictive and probably
212 // less efficient
_GGDKDraw_MyCairoRenderLayout(cairo_t * cc,Color fg,PangoLayout * layout,int x,int y)213 static void _GGDKDraw_MyCairoRenderLayout(cairo_t *cc, Color fg, PangoLayout *layout, int x, int y) {
214     PangoRectangle rect, r2;
215     PangoLayoutIter *iter;
216 
217     iter = pango_layout_get_iter(layout);
218     do {
219         PangoLayoutRun *run = pango_layout_iter_get_run(iter);
220         if (run != NULL) { // NULL runs mark end of line
221             pango_layout_iter_get_run_extents(iter, &r2, &rect);
222             cairo_move_to(cc, x + (rect.x + PANGO_SCALE / 2) / PANGO_SCALE, y + (rect.y + PANGO_SCALE / 2) / PANGO_SCALE);
223             if (COLOR_ALPHA(fg) == 0) {
224                 cairo_set_source_rgba(cc, COLOR_RED(fg) / 255.0,
225                                       COLOR_GREEN(fg) / 255.0, COLOR_BLUE(fg) / 255.0, 1.0);
226             } else {
227                 cairo_set_source_rgba(cc, COLOR_RED(fg) / 255.0,
228                                       COLOR_GREEN(fg) / 255.0, COLOR_BLUE(fg) / 255.0, COLOR_ALPHA(fg) / 255.);
229             }
230             pango_cairo_show_glyph_string(cc, run->item->analysis.font, run->glyphs);
231         }
232     } while (pango_layout_iter_next_run(iter));
233     pango_layout_iter_free(iter);
234 }
235 
_GGDKDraw_EllipsePath(cairo_t * cc,double cx,double cy,double width,double height)236 static void _GGDKDraw_EllipsePath(cairo_t *cc, double cx, double cy, double width, double height) {
237     cairo_new_path(cc);
238     cairo_move_to(cc, cx, cy + height);
239     cairo_curve_to(cc,
240                    cx + .552 * width, cy + height,
241                    cx + width, cy + .552 * height,
242                    cx + width, cy);
243     cairo_curve_to(cc,
244                    cx + width, cy - .552 * height,
245                    cx + .552 * width, cy - height,
246                    cx, cy - height);
247     cairo_curve_to(cc,
248                    cx - .552 * width, cy - height,
249                    cx - width, cy - .552 * height,
250                    cx - width, cy);
251     cairo_curve_to(cc,
252                    cx - width, cy + .552 * height,
253                    cx - .552 * width, cy + height,
254                    cx, cy + height);
255     cairo_close_path(cc);
256 }
257 
_GGDKDraw_GImage2Surface(GImage * image,GRect * src)258 static cairo_surface_t *_GGDKDraw_GImage2Surface(GImage *image, GRect *src) {
259     struct _GImage *base = (image->list_len == 0) ? image->u.image : image->u.images[0];
260     cairo_format_t type;
261     uint8 *pt;
262     uint32 *idata, *ipt, *ito;
263     int i, j, jj, tjj, stride;
264     int bit, tobit;
265     cairo_surface_t *cs;
266 
267     if (base->image_type == it_rgba) {
268         type = CAIRO_FORMAT_ARGB32;
269     } else if (base->image_type == it_true && base->trans != COLOR_UNKNOWN) {
270         type = CAIRO_FORMAT_ARGB32;
271     } else if (base->image_type == it_index && base->clut->trans_index != COLOR_UNKNOWN) {
272         type = CAIRO_FORMAT_ARGB32;
273     } else if (base->image_type == it_true) {
274         type = CAIRO_FORMAT_RGB24;
275     } else if (base->image_type == it_index) {
276         type = CAIRO_FORMAT_RGB24;
277     } else if (base->image_type == it_mono && base->clut != NULL &&
278                base->clut->trans_index != COLOR_UNKNOWN) {
279         type = CAIRO_FORMAT_A1;
280     } else {
281         type = CAIRO_FORMAT_RGB24;
282     }
283 
284     /* We can't reuse the image's data for alpha images because we must */
285     /*  premultiply each channel by alpha. We can reuse it for non-transparent*/
286     /*  rgb images */
287     if (base->image_type == it_true && type == CAIRO_FORMAT_RGB24) {
288         idata = ((uint32 *)(base->data + src->y * base->bytes_per_line)) + src->x;
289         return cairo_image_surface_create_for_data((uint8 *) idata, type,
290                 src->width, src->height,
291                 base->bytes_per_line);
292     }
293 
294     cs = cairo_image_surface_create(type, src->width, src->height);
295     stride = cairo_image_surface_get_stride(cs);
296     cairo_surface_flush(cs);
297     idata = (uint32 *)cairo_image_surface_get_data(cs);
298 
299     if (base->image_type == it_rgba) {
300         ipt = ((uint32 *)(base->data + src->y * base->bytes_per_line)) + src->x;
301         ito = idata;
302         for (i = 0; i < src->height; ++i) {
303             for (j = 0; j < src->width; ++j) {
304                 uint32 orig = ipt[j];
305                 int alpha = orig >> 24;
306                 if (alpha == 0xff) {
307                     ito[j] = orig;
308                 } else if (alpha == 0) {
309                     ito[j] = 0x00000000;
310                 } else
311                     ito[j] = (alpha << 24) |
312                              ((COLOR_RED(orig) * alpha / 255) << 16) |
313                              ((COLOR_GREEN(orig) * alpha / 255) << 8) |
314                              ((COLOR_BLUE(orig) * alpha / 255));
315             }
316             ipt = (uint32 *)(((uint8 *) ipt) + base->bytes_per_line);
317             ito = (uint32 *)(((uint8 *) ito) + stride);
318         }
319     } else if (base->image_type == it_true && base->trans != COLOR_UNKNOWN) {
320         Color trans = base->trans;
321         ipt = ((uint32 *)(base->data + src->y * base->bytes_per_line)) + src->x;
322         ito = idata;
323         for (i = 0; i < src->height; ++i) {
324             for (j = 0; j < src->width; ++j) {
325                 if (ipt[j] == trans) {
326                     ito[j] = 0x00000000;
327                 } else {
328                     ito[j] = ipt[j] | 0xff000000;
329                 }
330             }
331             ipt = (uint32 *)(((uint8 *) ipt) + base->bytes_per_line);
332             ito = (uint32 *)(((uint8 *) ito) + stride);
333         }
334     } else if (base->image_type == it_true) {
335         ipt = ((uint32 *)(base->data + src->y * base->bytes_per_line)) + src->x;
336         ito = idata;
337         for (i = 0; i < src->height; ++i) {
338             for (j = 0; j < src->width; ++j) {
339                 ito[j] = ipt[j] | 0xff000000;
340             }
341             ipt = (uint32 *)(((uint8 *) ipt) + base->bytes_per_line);
342             ito = (uint32 *)(((uint8 *) ito) + stride);
343         }
344     } else if (base->image_type == it_index && base->clut->trans_index != COLOR_UNKNOWN) {
345         int trans = base->clut->trans_index;
346         Color *clut = base->clut->clut;
347         pt = base->data + src->y * base->bytes_per_line + src->x;
348         ito = idata;
349         for (i = 0; i < src->height; ++i) {
350             for (j = 0; j < src->width; ++j) {
351                 int index = pt[j];
352                 if (index == trans) {
353                     ito[j] = 0x00000000;
354                 } else
355                     /* In theory RGB24 images don't need the alpha channel set*/
356                     /*  but there is a bug in Cairo 1.2, and they do. */
357                 {
358                     ito[j] = clut[index] | 0xff000000;
359                 }
360             }
361             pt += base->bytes_per_line;
362             ito = (uint32 *)(((uint8 *) ito) + stride);
363         }
364     } else if (base->image_type == it_index) {
365         Color *clut = base->clut->clut;
366         pt = base->data + src->y * base->bytes_per_line + src->x;
367         ito = idata;
368         for (i = 0; i < src->height; ++i) {
369             for (j = 0; j < src->width; ++j) {
370                 int index = pt[j];
371                 ito[j] = clut[index] | 0xff000000;
372             }
373             pt += base->bytes_per_line;
374             ito = (uint32 *)(((uint8 *) ito) + stride);
375         }
376 #ifdef WORDS_BIGENDIAN
377     } else if (base->image_type == it_mono && base->clut != NULL &&
378                base->clut->trans_index != COLOR_UNKNOWN) {
379         pt = base->data + src->y * base->bytes_per_line + (src->x >> 3);
380         ito = idata;
381         if (base->clut->trans_index == 0) {
382             for (i = 0; i < src->height; ++i) {
383                 bit = (0x80 >> (src->x & 0x7));
384                 tobit = 0x80000000;
385                 for (j = jj = tjj = 0; j < src->width; ++j) {
386                     if (pt[jj]&bit) {
387                         ito[tjj] |= tobit;
388                     }
389                     if ((bit >>= 1) == 0) {
390                         bit = 0x80;
391                         ++jj;
392                     }
393                     if ((tobit >>= 1) == 0) {
394                         tobit = 0x80000000;
395                         ++tjj;
396                     }
397                 }
398                 pt += base->bytes_per_line;
399                 ito = (uint32 *)(((uint8 *) ito) + stride);
400             }
401         } else {
402             for (i = 0; i < src->height; ++i) {
403                 bit = (0x80 >> (src->x & 0x7));
404                 tobit = 0x80000000;
405                 for (j = jj = tjj = 0; j < src->width; ++j) {
406                     if (!(pt[jj]&bit)) {
407                         ito[tjj] |= tobit;
408                     }
409                     if ((bit >>= 1) == 0) {
410                         bit = 0x80;
411                         ++jj;
412                     }
413                     if ((tobit >>= 1) == 0) {
414                         tobit = 0x80000000;
415                         ++tjj;
416                     }
417                 }
418                 pt += base->bytes_per_line;
419                 ito = (uint32 *)(((uint8 *) ito) + stride);
420             }
421         }
422 #else
423     } else if (base->image_type == it_mono && base->clut != NULL &&
424                base->clut->trans_index != COLOR_UNKNOWN) {
425         pt = base->data + src->y * base->bytes_per_line + (src->x >> 3);
426         ito = idata;
427         if (base->clut->trans_index == 0) {
428             for (i = 0; i < src->height; ++i) {
429                 bit = (0x80 >> (src->x & 0x7));
430                 tobit = 1;
431                 for (j = jj = tjj = 0; j < src->width; ++j) {
432                     if (pt[jj]&bit) {
433                         ito[tjj] |= tobit;
434                     }
435                     if ((bit >>= 1) == 0) {
436                         bit = 0x80;
437                         ++jj;
438                     }
439                     if ((tobit <<= 1) == 0) {
440                         tobit = 0x1;
441                         ++tjj;
442                     }
443                 }
444                 pt += base->bytes_per_line;
445                 ito = (uint32 *)(((uint8 *) ito) + stride);
446             }
447         } else {
448             for (i = 0; i < src->height; ++i) {
449                 bit = (0x80 >> (src->x & 0x7));
450                 tobit = 1;
451                 for (j = jj = tjj = 0; j < src->width; ++j) {
452                     if (!(pt[jj]&bit)) {
453                         ito[tjj] |= tobit;
454                     }
455                     if ((bit >>= 1) == 0) {
456                         bit = 0x80;
457                         ++jj;
458                     }
459                     if ((tobit <<= 1) == 0) {
460                         tobit = 0x1;
461                         ++tjj;
462                     }
463                 }
464                 pt += base->bytes_per_line;
465                 ito = (uint32 *)(((uint8 *) ito) + stride);
466             }
467         }
468 #endif
469     } else {
470         Color fg = base->clut == NULL ? 0xffffff : base->clut->clut[1];
471         Color bg = base->clut == NULL ? 0x000000 : base->clut->clut[0];
472         /* In theory RGB24 images don't need the alpha channel set*/
473         /*  but there is a bug in Cairo 1.2, and they do. */
474         fg |= 0xff000000;
475         bg |= 0xff000000;
476         pt = base->data + src->y * base->bytes_per_line + (src->x >> 3);
477         ito = idata;
478         for (i = 0; i < src->height; ++i) {
479             bit = (0x80 >> (src->x & 0x7));
480             for (j = jj = 0; j < src->width; ++j) {
481                 ito[j] = (pt[jj] & bit) ? fg : bg;
482                 if ((bit >>= 1) == 0) {
483                     bit = 0x80;
484                     ++jj;
485                 }
486             }
487             pt += base->bytes_per_line;
488             ito = (uint32 *)(((uint8 *) ito) + stride);
489         }
490     }
491     cairo_surface_mark_dirty(cs);
492     return cs;
493 }
494 
_GGDKDraw_GImageExtract(struct _GImage * base,GRect * src,GRect * size,double xscale,double yscale)495 static GImage *_GGDKDraw_GImageExtract(struct _GImage *base, GRect *src, GRect *size,
496                                        double xscale, double yscale) {
497     static GImage temp;
498     static struct _GImage tbase;
499     static uint8 *data;
500     static int dlen;
501     int r, c;
502 
503     memset(&temp, 0, sizeof(temp));
504     tbase = *base;
505     temp.u.image = &tbase;
506     tbase.width = size->width;
507     tbase.height = size->height;
508     if (base->image_type == it_mono) {
509         tbase.bytes_per_line = (size->width + 7) / 8;
510     } else if (base->image_type == it_index) {
511         tbase.bytes_per_line = size->width;
512     } else {
513         tbase.bytes_per_line = 4 * size->width;
514     }
515     if (tbase.bytes_per_line * size->height > dlen) {
516         data = realloc(data, dlen = tbase.bytes_per_line * size->height);
517     }
518     tbase.data = data;
519 
520     /* I used to use rint(x). Now I use floor(x). For normal images rint */
521     /*  might be better, but for text we need floor */
522 
523     if (base->image_type == it_mono) {
524         memset(data, 0, tbase.height * tbase.bytes_per_line);
525         for (r = 0; r < size->height; ++r) {
526             int or = ((int) floor((r + size->y) / yscale));
527             uint8 *pt = data + r * tbase.bytes_per_line;
528             uint8 *opt = base->data + or * base->bytes_per_line;
529             for (c = 0; c < size->width; ++c) {
530                 int oc = ((int) floor((c + size->x) / xscale));
531                 if (opt[oc >> 3] & (0x80 >> (oc & 7))) {
532                     pt[c >> 3] |= (0x80 >> (c & 7));
533                 }
534             }
535         }
536     } else if (base->image_type == it_index) {
537         for (r = 0; r < size->height; ++r) {
538             int or = ((int) floor((r + size->y) / yscale));
539             uint8 *pt = data + r * tbase.bytes_per_line;
540             uint8 *opt = base->data + or * base->bytes_per_line;
541             for (c = 0; c < size->width; ++c) {
542                 int oc = ((int) floor((c + size->x) / xscale));
543                 *pt++ = opt[oc];
544             }
545         }
546     } else {
547         for (r = 0; r < size->height; ++r) {
548             int or = ((int) floor((r + size->y) / yscale));
549             uint32 *pt = (uint32 *)(data + r * tbase.bytes_per_line);
550             uint32 *opt = (uint32 *)(base->data + or * base->bytes_per_line);
551             for (c = 0; c < size->width; ++c) {
552                 int oc = ((int) floor((c + size->x) / xscale));
553                 *pt++ = opt[oc];
554             }
555         }
556     }
557     return (&temp);
558 }
559 
560 // Protected member functions
561 
_GGDKDraw_InitPangoCairo(GGDKWindow gw)562 bool _GGDKDraw_InitPangoCairo(GGDKWindow gw) {
563     if (gw->is_pixmap) {
564         gw->cc = cairo_create(gw->cs);
565         if (gw->cc == NULL) {
566             Log(LOGDEBUG, "GGDKDRAW: Cairo context creation failed!");
567             return false;
568         }
569     }
570 
571     // Establish Pango layout context
572     gw->pango_layout = pango_layout_new(gw->display->pangoc_context);
573     if (gw->pango_layout == NULL) {
574         Log(LOGDEBUG, "GGDKDRAW: Pango layout creation failed!");
575         if (gw->cc != NULL) {
576             cairo_destroy(gw->cc);
577             gw->cc = NULL;
578         }
579         return false;
580     }
581 
582     return true;
583 }
584 
_GGDKDraw_ExcludeChildRegions(GGDKWindow gw,cairo_region_t * r,bool force)585 cairo_region_t *_GGDKDraw_ExcludeChildRegions(GGDKWindow gw, cairo_region_t *r, bool force) {
586     GList_Glib *children = gdk_window_peek_children(gw->w);
587     cairo_region_t *reg = NULL;
588 
589     if (children) {
590         if (r == NULL) {
591             reg = gdk_window_get_clip_region(gw->w);
592         } else {
593             reg = cairo_region_copy(r);
594         }
595 
596         while (children != NULL) {
597             cairo_region_t *chr = gdk_window_get_clip_region((GdkWindow *)children->data);
598             int dx, dy;
599 
600             gdk_window_get_position((GdkWindow *)children->data, &dx, &dy);
601             cairo_region_translate(chr, dx, dy);
602             cairo_region_subtract(reg, chr);
603             cairo_region_destroy(chr);
604             children = children->next;
605         }
606     } else if (force) {
607         reg = gdk_window_get_clip_region(gw->w);
608     }
609 
610     return reg;
611 }
612 
_GGDKDraw_CleanupAutoPaint(GGDKDisplay * gdisp)613 void _GGDKDraw_CleanupAutoPaint(GGDKDisplay *gdisp) {
614     if (gdisp->dirty_window != NULL) {
615         GGDKWindow gw = gdisp->dirty_window;
616 
617         if (gw->cc != NULL) {
618             cairo_destroy(gw->cc);
619             gw->cc = NULL;
620         }
621         if (gw->is_in_paint) {
622             //Log(LOGDEBUG, "ENDED PAINT %p", gw);
623 #if !defined(GDK_WINDOWING_WIN32) && !defined(GDK_WINDOWING_QUARTZ)
624             if (gw->cs != NULL) {
625                 assert(gw->expose_region != NULL);
626 #ifdef GGDKDRAW_GDK_3_22
627                 cairo_t *cc = cairo_reference(gdk_drawing_context_get_cairo_context(gw->drawing_ctx));
628 #else
629                 cairo_t *cc = gdk_cairo_create(gw->w);
630 #endif
631 
632                 gdk_cairo_region(cc, gw->expose_region);
633                 cairo_clip(cc);
634 
635                 cairo_set_source_surface(cc, gw->cs, 0, 0);
636                 cairo_set_operator(cc, CAIRO_OPERATOR_SOURCE);
637                 cairo_paint(cc);
638 
639                 cairo_destroy(cc);
640                 cairo_region_destroy(gw->expose_region);
641                 gw->expose_region = NULL;
642                 cairo_surface_destroy(gw->cs);
643                 gw->cs = NULL;
644             }
645 #endif
646 #ifdef GGDKDRAW_GDK_3_22
647             gdk_window_end_draw_frame(gw->w, gw->drawing_ctx);
648 #else
649             gdk_window_end_paint(gw->w);
650 #endif
651             gw->is_in_paint = false;
652         }
653         gdisp->dirty_window = NULL;
654     }
655 }
656 
GGDKDrawPushClip(GWindow w,GRect * rct,GRect * old)657 void GGDKDrawPushClip(GWindow w, GRect *rct, GRect *old) {
658     //Log(LOGDEBUG, " ");
659 
660     // Return the current clip, and intersect the current clip with the desired
661     // clip to get the new clip.
662     _GGDKDraw_CheckAutoPaint((GGDKWindow)w);
663 
664     *old = w->ggc->clip;
665     w->ggc->clip = *rct;
666     if (w->ggc->clip.x + w->ggc->clip.width > old->x + old->width) {
667         w->ggc->clip.width = old->x + old->width - w->ggc->clip.x;
668     }
669     if (w->ggc->clip.y + w->ggc->clip.height > old->y + old->height) {
670         w->ggc->clip.height = old->y + old->height - w->ggc->clip.y;
671     }
672     if (w->ggc->clip.x < old->x) {
673         if (w->ggc->clip.width > (old->x - w->ggc->clip.x)) {
674             w->ggc->clip.width -= (old->x - w->ggc->clip.x);
675         } else {
676             w->ggc->clip.width = 0;
677         }
678         w->ggc->clip.x = old->x;
679     }
680     if (w->ggc->clip.y < old->y) {
681         if (w->ggc->clip.height > (old->y - w->ggc->clip.y)) {
682             w->ggc->clip.height -= (old->y - w->ggc->clip.y);
683         } else {
684             w->ggc->clip.height = 0;
685         }
686         w->ggc->clip.y = old->y;
687     }
688     if (w->ggc->clip.height < 0 || w->ggc->clip.width < 0) {
689         // Negative values mean large positive values, so if we want to clip
690         //  to nothing force clip outside window
691         w->ggc->clip.x = w->ggc->clip.y = -100;
692         w->ggc->clip.height = w->ggc->clip.width = 1;
693     }
694 
695     GGDKWindow gw = (GGDKWindow)w;
696 
697     cairo_save(gw->cc);
698     cairo_new_path(gw->cc);
699     cairo_rectangle(gw->cc, gw->ggc->clip.x, gw->ggc->clip.y,
700                     gw->ggc->clip.width, gw->ggc->clip.height);
701     cairo_clip(gw->cc);
702 }
703 
GGDKDrawPopClip(GWindow w,GRect * old)704 void GGDKDrawPopClip(GWindow w, GRect *old) {
705     //Log(LOGDEBUG, " ");
706     GGDKWindow gw = (GGDKWindow)w;
707     if (old) {
708         gw->ggc->clip = *old;
709     }
710     // cc can be NULL if the autopaint cleanup had to run because
711     // a raise/lower/move/resize function was called.
712     if (gw->cc != NULL) {
713         cairo_restore(gw->cc);
714     }
715 }
716 
717 
GGDKDrawSetDifferenceMode(GWindow w)718 void GGDKDrawSetDifferenceMode(GWindow w) {
719     //Log(LOGDEBUG, " ");
720     GGDKWindow gw = (GGDKWindow)w;
721     _GGDKDraw_CheckAutoPaint(gw);
722     cairo_set_operator(gw->cc, CAIRO_OPERATOR_DIFFERENCE);
723     cairo_set_antialias(gw->cc, CAIRO_ANTIALIAS_NONE);
724 }
725 
726 
GGDKDrawClear(GWindow w,GRect * rect)727 void GGDKDrawClear(GWindow w, GRect *rect) {
728     //Log(LOGDEBUG, " ");
729     GGDKWindow gw = (GGDKWindow)w;
730     _GGDKDraw_CheckAutoPaint(gw);
731     GRect temp, *r = rect, old;
732     if (r == NULL) {
733         temp = gw->pos;
734         temp.x = temp.y = 0;
735         r = &temp;
736     }
737     GGDKDrawPushClip((GWindow)gw, r, &old);
738     cairo_set_source_rgba(gw->cc, COLOR_RED(gw->ggc->bg) / 255.,
739                           COLOR_GREEN(gw->ggc->bg) / 255.,
740                           COLOR_BLUE(gw->ggc->bg) / 255., 1.0);
741     cairo_paint(gw->cc);
742     GGDKDrawPopClip((GWindow)gw, &old);
743 }
744 
GGDKDrawDrawLine(GWindow w,int32 x,int32 y,int32 xend,int32 yend,Color col)745 void GGDKDrawDrawLine(GWindow w, int32 x, int32 y, int32 xend, int32 yend, Color col) {
746     //Log(LOGDEBUG, " ");
747     GGDKWindow gw = (GGDKWindow)w;
748     _GGDKDraw_CheckAutoPaint(gw);
749 
750     w->ggc->fg = col;
751 
752     int width = GGDKDrawSetline(gw, gw->ggc);
753     cairo_new_path(gw->cc);
754     if (width & 1) {
755         cairo_move_to(gw->cc, x + .5, y + .5);
756         cairo_line_to(gw->cc, xend + .5, yend + .5);
757     } else {
758         cairo_move_to(gw->cc, x, y);
759         cairo_line_to(gw->cc, xend, yend);
760     }
761     cairo_stroke(gw->cc);
762 
763 }
764 
GGDKDrawDrawArrow(GWindow w,int32 x,int32 y,int32 xend,int32 yend,int16 arrows,Color col)765 void GGDKDrawDrawArrow(GWindow w, int32 x, int32 y, int32 xend, int32 yend, int16 arrows, Color col) {
766     //Log(LOGDEBUG, " ");
767     GGDKWindow gw = (GGDKWindow)w;
768     _GGDKDraw_CheckAutoPaint(gw);
769 
770     gw->ggc->fg = col;
771 
772     int width = GGDKDrawSetline(gw, gw->ggc);
773     if (width & 1) {
774         x += .5;
775         y += .5;
776         xend += .5;
777         yend += .5;
778     }
779 
780     const double head_angle = 0.5;
781     double angle = atan2(yend - y, xend - x) + FF_PI;
782     double length = sqrt((x - xend) * (x - xend) + (y - yend) * (y - yend));
783 
784     cairo_new_path(gw->cc);
785     cairo_move_to(gw->cc, x, y);
786     cairo_line_to(gw->cc, xend, yend);
787     cairo_stroke(gw->cc);
788 
789     if (length < 2) { //No point arrowing something so small
790 
791         return;
792     } else if (length > 20) {
793         length = 10;
794     } else {
795         length *= 2. / 3.;
796     }
797     cairo_new_path(gw->cc);
798     cairo_move_to(gw->cc, xend, yend);
799     cairo_line_to(gw->cc, xend + length * cos(angle - head_angle), yend + length * sin(angle - head_angle));
800     cairo_line_to(gw->cc, xend + length * cos(angle + head_angle), yend + length * sin(angle + head_angle));
801     cairo_close_path(gw->cc);
802     cairo_fill(gw->cc);
803 
804 }
805 
GGDKDrawDrawRect(GWindow w,GRect * rect,Color col)806 void GGDKDrawDrawRect(GWindow w, GRect *rect, Color col) {
807     //Log(LOGDEBUG, " ");
808     GGDKWindow gw = (GGDKWindow)w;
809     _GGDKDraw_CheckAutoPaint(gw);
810 
811     gw->ggc->fg = col;
812 
813     int width = GGDKDrawSetline(gw, gw->ggc);
814     cairo_new_path(gw->cc);
815     if (width & 1) {
816         cairo_rectangle(gw->cc, rect->x + .5, rect->y + .5, rect->width, rect->height);
817     } else {
818         cairo_rectangle(gw->cc, rect->x, rect->y, rect->width, rect->height);
819     }
820     cairo_stroke(gw->cc);
821 
822 }
823 
GGDKDrawFillRect(GWindow w,GRect * rect,Color col)824 void GGDKDrawFillRect(GWindow w, GRect *rect, Color col) {
825     //Log(LOGDEBUG, " ");
826     GGDKWindow gw = (GGDKWindow)w;
827     _GGDKDraw_CheckAutoPaint(gw);
828 
829     gw->ggc->fg = col;
830 
831     GGDKDrawSetcolfunc(gw, gw->ggc);
832 
833     cairo_new_path(gw->cc);
834     cairo_rectangle(gw->cc, rect->x, rect->y, rect->width, rect->height);
835     cairo_fill(gw->cc);
836 
837 }
838 
GGDKDrawFillRoundRect(GWindow w,GRect * rect,int radius,Color col)839 void GGDKDrawFillRoundRect(GWindow w, GRect *rect, int radius, Color col) {
840     //Log(LOGDEBUG, " ");
841     GGDKWindow gw = (GGDKWindow)w;
842     _GGDKDraw_CheckAutoPaint(gw);
843 
844     gw->ggc->fg = col;
845 
846     GGDKDrawSetcolfunc(gw, gw->ggc);
847 
848     double degrees = FF_PI / 180.0;
849     int rr = (radius <= (rect->height + 1) / 2) ? (radius > 0 ? radius : 0) : (rect->height + 1) / 2;
850     cairo_new_path(gw->cc);
851     cairo_arc(gw->cc, rect->x + rect->width - rr, rect->y + rr, rr, -90 * degrees, 0 * degrees);
852     cairo_arc(gw->cc, rect->x + rect->width - rr, rect->y + rect->height - rr, rr, 0 * degrees, 90 * degrees);
853     cairo_arc(gw->cc, rect->x + rr, rect->y + rect->height - rr, rr, 90 * degrees, 180 * degrees);
854     cairo_arc(gw->cc, rect->x + rr, rect->y + rr, rr, 180 * degrees, 270 * degrees);
855     cairo_close_path(gw->cc);
856     cairo_fill(gw->cc);
857 
858 }
859 
GGDKDrawDrawEllipse(GWindow w,GRect * rect,Color col)860 void GGDKDrawDrawEllipse(GWindow w, GRect *rect, Color col) {
861     //Log(LOGDEBUG, " ");
862     GGDKWindow gw = (GGDKWindow)w;
863     _GGDKDraw_CheckAutoPaint(gw);
864 
865     gw->ggc->fg = col;
866 
867     // It is tempting to use the cairo arc command and scale the
868     //  coordinates to get an ellipse, but that distorts the stroke width.
869     int lwidth = GGDKDrawSetline(gw, gw->ggc);
870     double cx, cy, width, height;
871 
872     width = rect->width / 2.0;
873     height = rect->height / 2.0;
874     cx = rect->x + width;
875     cy = rect->y + height;
876     if (lwidth & 1) {
877         if (rint(width) == width) {
878             cx += .5;
879         }
880         if (rint(height) == height) {
881             cy += .5;
882         }
883     }
884     _GGDKDraw_EllipsePath(gw->cc, cx, cy, width, height);
885     cairo_stroke(gw->cc);
886 
887 }
888 
GGDKDrawFillEllipse(GWindow w,GRect * rect,Color col)889 void GGDKDrawFillEllipse(GWindow w, GRect *rect, Color col) {
890     //Log(LOGDEBUG, " ");
891     GGDKWindow gw = (GGDKWindow)w;
892     _GGDKDraw_CheckAutoPaint(gw);
893 
894 
895     gw->ggc->fg = col;
896     GGDKDrawSetcolfunc(gw, gw->ggc);
897 
898     // It is tempting to use the cairo arc command and scale the
899     //  coordinates to get an ellipse, but that distorts the stroke width.
900     double cx, cy, width, height;
901     width = rect->width / 2.0;
902     height = rect->height / 2.0;
903     cx = rect->x + width;
904     cy = rect->y + height;
905     _GGDKDraw_EllipsePath(gw->cc, cx, cy, width, height);
906     cairo_fill(gw->cc);
907 
908 }
909 
910 /**
911  *  \brief Draws an arc (circular and elliptical).
912  *
913  *  \param [in] gw The window to draw on.
914  *  \param [in] rect The bounding box of the arc. If width!=height, then
915  *                   an elliptical arc will be drawn.
916  *  \param [in] sangle The start angle in degrees * 64 (Cartesian)
917  *  \param [in] eangle The angle offset from the start in degrees * 64 (positive CCW)
918  */
GGDKDrawDrawArc(GWindow w,GRect * rect,int32 sangle,int32 eangle,Color col)919 void GGDKDrawDrawArc(GWindow w, GRect *rect, int32 sangle, int32 eangle, Color col) {
920     //Log(LOGDEBUG, " ");
921     GGDKWindow gw = (GGDKWindow)w;
922     _GGDKDraw_CheckAutoPaint(gw);
923 
924     gw->ggc->fg = col;
925 
926     // Leftover from XDrawArc: sangle/eangle in degrees*64.
927     double start = -(sangle + eangle) * FF_PI / 11520., end = -sangle * FF_PI / 11520.;
928     int width = GGDKDrawSetline(gw, gw->ggc);
929 
930     cairo_new_path(gw->cc);
931     cairo_save(gw->cc);
932     if (width & 1) {
933         cairo_translate(gw->cc, rect->x + .5 + rect->width / 2., rect->y + .5 + rect->height / 2.);
934     } else {
935         cairo_translate(gw->cc, rect->x + rect->width / 2., rect->y + rect->height / 2.);
936     }
937     cairo_scale(gw->cc, rect->width / 2., rect->height / 2.);
938     cairo_arc(gw->cc, 0., 0., 1., start, end);
939     cairo_restore(gw->cc);
940     cairo_stroke(gw->cc);
941 
942 }
943 
GGDKDrawDrawPoly(GWindow w,GPoint * pts,int16 cnt,Color col)944 void GGDKDrawDrawPoly(GWindow w, GPoint *pts, int16 cnt, Color col) {
945     //Log(LOGDEBUG, " ");
946     GGDKWindow gw = (GGDKWindow)w;
947     _GGDKDraw_CheckAutoPaint(gw);
948 
949     gw->ggc->fg = col;
950 
951     int width = GGDKDrawSetline(gw, gw->ggc);
952     double off = (width & 1) ? .5 : 0;
953 
954     cairo_new_path(gw->cc);
955     cairo_move_to(gw->cc, pts[0].x + off, pts[0].y + off);
956     for (int i = 1; i < cnt; ++i) {
957         cairo_line_to(gw->cc, pts[i].x + off, pts[i].y + off);
958     }
959     cairo_stroke(gw->cc);
960 
961 }
962 
GGDKDrawFillPoly(GWindow w,GPoint * pts,int16 cnt,Color col)963 void GGDKDrawFillPoly(GWindow w, GPoint *pts, int16 cnt, Color col) {
964     //Log(LOGDEBUG, " ");
965     GGDKWindow gw = (GGDKWindow)w;
966     _GGDKDraw_CheckAutoPaint(gw);
967 
968     gw->ggc->fg = col;
969 
970     GGDKDrawSetcolfunc(gw, gw->ggc);
971 
972     cairo_new_path(gw->cc);
973     cairo_move_to(gw->cc, pts[0].x, pts[0].y);
974     for (int i = 1; i < cnt; ++i) {
975         cairo_line_to(gw->cc, pts[i].x, pts[i].y);
976     }
977     cairo_close_path(gw->cc);
978     cairo_fill(gw->cc);
979 
980     cairo_set_line_width(gw->cc, 1);
981     cairo_new_path(gw->cc);
982     cairo_move_to(gw->cc, pts[0].x + .5, pts[0].y + .5);
983     for (int i = 1; i < cnt; ++i) {
984         cairo_line_to(gw->cc, pts[i].x + .5, pts[i].y + .5);
985     }
986     cairo_close_path(gw->cc);
987     cairo_stroke(gw->cc);
988 
989 }
990 
GGDKDrawDrawImage(GWindow w,GImage * image,GRect * src,int32 x,int32 y)991 void GGDKDrawDrawImage(GWindow w, GImage *image, GRect *src, int32 x, int32 y) {
992     //Log(LOGDEBUG, " ");
993     GGDKWindow gw = (GGDKWindow)w;
994     _GGDKDraw_CheckAutoPaint(gw);
995 
996     cairo_surface_t *is = _GGDKDraw_GImage2Surface(image, src), *cs = is;
997     struct _GImage *base = (image->list_len == 0) ? image->u.image : image->u.images[0];
998 
999     if (cairo_image_surface_get_format(is) == CAIRO_FORMAT_A1) {
1000         /* No color info, just alpha channel */
1001         Color fg = base->clut->trans_index == 0 ? base->clut->clut[1] : base->clut->clut[0];
1002 #ifdef GDK_WINDOWING_QUARTZ
1003         // The quartz backend cannot mask/render A1 surfaces directly
1004         // So render to intermediate ARGB32 surface first, then render that to screen
1005         cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, src->width, src->height);
1006         cairo_t *cc = cairo_create(cs);
1007         cairo_set_source_rgba(cc, COLOR_RED(fg) / 255.0, COLOR_GREEN(fg) / 255.0, COLOR_BLUE(fg) / 255.0, 1.0);
1008         cairo_mask_surface(cc, is, 0, 0);
1009         cairo_destroy(cc);
1010 #else
1011         cairo_set_source_rgba(gw->cc, COLOR_RED(fg) / 255.0, COLOR_GREEN(fg) / 255.0, COLOR_BLUE(fg) / 255.0, 1.0);
1012         cairo_mask_surface(gw->cc, cs, x, y);
1013         cs = NULL;
1014 #endif
1015     }
1016 
1017     if (cs != NULL) {
1018         cairo_set_source_surface(gw->cc, cs, x, y);
1019         cairo_rectangle(gw->cc, x, y, src->width, src->height);
1020         cairo_fill(gw->cc);
1021 
1022         if (cs != is) {
1023             cairo_surface_destroy(cs);
1024         }
1025     }
1026     /* Clear source and mask, in case we need to */
1027     cairo_new_path(gw->cc);
1028     cairo_set_source_rgba(gw->cc, 0, 0, 0, 0);
1029 
1030     cairo_surface_destroy(is);
1031 }
1032 
1033 // What we really want to do is use the grey levels as an alpha channel
GGDKDrawDrawGlyph(GWindow w,GImage * image,GRect * src,int32 x,int32 y)1034 void GGDKDrawDrawGlyph(GWindow w, GImage *image, GRect *src, int32 x, int32 y) {
1035     //Log(LOGDEBUG, " ");
1036     GGDKWindow gw = (GGDKWindow)w;
1037     _GGDKDraw_CheckAutoPaint(gw);
1038 
1039     struct _GImage *base = (image->list_len) == 0 ? image->u.image : image->u.images[0];
1040     cairo_surface_t *is;
1041 
1042     if (base->image_type != it_index) {
1043         GGDKDrawDrawImage(w, image, src, x, y);
1044     } else {
1045         int stride = cairo_format_stride_for_width(CAIRO_FORMAT_A8, src->width);
1046         uint8 *basedata = malloc(stride * src->height),
1047                *data = basedata,
1048                 *srcd = base->data + src->y * base->bytes_per_line + src->x;
1049         int factor = base->clut->clut_len == 256 ? 1 :
1050                      base->clut->clut_len == 16 ? 17 :
1051                      base->clut->clut_len == 4 ? 85 : 255;
1052         int i, j;
1053         Color fg = base->clut->clut[base->clut->clut_len - 1];
1054 
1055         for (i = 0; i < src->height; ++i) {
1056             for (j = 0; j < src->width; ++j) {
1057                 data[j] = factor * srcd[j];
1058             }
1059             srcd += base->bytes_per_line;
1060             data += stride;
1061         }
1062         is = cairo_image_surface_create_for_data(basedata, CAIRO_FORMAT_A8,
1063                 src->width, src->height, stride);
1064         cairo_set_source_rgba(gw->cc, COLOR_RED(fg) / 255.0, COLOR_GREEN(fg) / 255.0, COLOR_BLUE(fg) / 255.0, 1.0);
1065         cairo_mask_surface(gw->cc, is, x, y);
1066         /* I think the mask is sufficient, setting a rectangle would provide */
1067         /*  a new mask? */
1068         /*cairo_rectangle(gw->cc,x,y,src->width,src->height);*/
1069         /* I think setting the mask also draws... at least so the tutorial implies */
1070         /* cairo_fill(gw->cc);*/
1071         /* Presumably that doesn't leave the mask surface pattern lying around */
1072         /* but dereferences it so we can free it */
1073         cairo_surface_destroy(is);
1074         free(basedata);
1075     }
1076 
1077 }
1078 
GGDKDrawDrawImageMagnified(GWindow w,GImage * image,GRect * src,int32 x,int32 y,int32 width,int32 height)1079 void GGDKDrawDrawImageMagnified(GWindow w, GImage *image, GRect *src, int32 x, int32 y, int32 width, int32 height) {
1080     //Log(LOGDEBUG, " ");
1081     GGDKWindow gw = (GGDKWindow)w;
1082     _GGDKDraw_CheckAutoPaint(gw);
1083 
1084     struct _GImage *base = (image->list_len == 0) ? image->u.image : image->u.images[0];
1085     GRect full;
1086     double xscale, yscale;
1087     GRect viewable;
1088 
1089     viewable = gw->ggc->clip;
1090     if (viewable.width > gw->pos.width - viewable.x) {
1091         viewable.width = gw->pos.width - viewable.x;
1092     }
1093     if (viewable.height > gw->pos.height - viewable.y) {
1094         viewable.height = gw->pos.height - viewable.y;
1095     }
1096 
1097     xscale = (base->width >= 1) ? ((double)(width)) / (base->width) : 1;
1098     yscale = (base->height >= 1) ? ((double)(height)) / (base->height) : 1;
1099     /* Intersect the clip rectangle with the scaled image to find the */
1100     /*  portion of screen that we want to draw */
1101     if (viewable.x < x) {
1102         viewable.width -= (x - viewable.x);
1103         viewable.x = x;
1104     }
1105     if (viewable.y < y) {
1106         viewable.height -= (y - viewable.y);
1107         viewable.y = y;
1108     }
1109     if (viewable.x + viewable.width > x + width) {
1110         viewable.width = x + width - viewable.x;
1111     }
1112     if (viewable.y + viewable.height > y + height) {
1113         viewable.height = y + height - viewable.y;
1114     }
1115     if (viewable.height < 0 || viewable.width < 0) {
1116 
1117         return;
1118     }
1119 
1120     /* Now find that same rectangle in the coordinates of the unscaled image */
1121     /* (translation & scale) */
1122     viewable.x -= x;
1123     viewable.y -= y;
1124     full.x = viewable.x / xscale;
1125     full.y = viewable.y / yscale;
1126     full.width = viewable.width / xscale;
1127     full.height = viewable.height / yscale;
1128     if (full.x + full.width > base->width) {
1129         full.width = base->width - full.x;    /* Rounding errors */
1130     }
1131     if (full.y + full.height > base->height) {
1132         full.height = base->height - full.y;    /* Rounding errors */
1133     }
1134     /* Rounding errors */
1135     {
1136         GImage *temp = _GGDKDraw_GImageExtract(base, &full, &viewable, xscale, yscale);
1137         GRect src;
1138         src.x = src.y = 0;
1139         src.width = viewable.width;
1140         src.height = viewable.height;
1141         GGDKDrawDrawImage(w, temp, &src, x + viewable.x, y + viewable.y);
1142     }
1143 
1144 }
1145 
GGDKDrawDrawPixmap(GWindow w,GWindow pixmap,GRect * src,int32 x,int32 y)1146 void GGDKDrawDrawPixmap(GWindow w, GWindow pixmap, GRect *src, int32 x, int32 y) {
1147     //Log(LOGDEBUG, " ");
1148     GGDKWindow gw = (GGDKWindow)w, gpixmap = (GGDKWindow)pixmap;
1149     _GGDKDraw_CheckAutoPaint(gw);
1150 
1151     if (!gpixmap->is_pixmap) {
1152         return;
1153     }
1154 
1155     cairo_set_source_surface(gw->cc, gpixmap->cs, x - src->x, y - src->y);
1156     cairo_rectangle(gw->cc, x, y, src->width, src->height);
1157     cairo_fill(gw->cc);
1158 }
1159 
GGDKDrawHasCairo(GWindow UNUSED (w))1160 enum gcairo_flags GGDKDrawHasCairo(GWindow UNUSED(w)) {
1161     //Log(LOGDEBUG, " ");
1162     return gc_all;
1163 }
1164 
GGDKDrawPathStartNew(GWindow w)1165 void GGDKDrawPathStartNew(GWindow w) {
1166     //Log(LOGDEBUG, " ");
1167     _GGDKDraw_CheckAutoPaint((GGDKWindow)w);
1168     cairo_new_path(((GGDKWindow)w)->cc);
1169 }
1170 
GGDKDrawPathClose(GWindow w)1171 void GGDKDrawPathClose(GWindow w) {
1172     //Log(LOGDEBUG, " ");
1173     cairo_close_path(((GGDKWindow)w)->cc);
1174 }
1175 
GGDKDrawPathMoveTo(GWindow w,double x,double y)1176 void GGDKDrawPathMoveTo(GWindow w, double x, double y) {
1177     //Log(LOGDEBUG, " ");
1178     _GGDKDraw_CheckAutoPaint((GGDKWindow)w);
1179     cairo_move_to(((GGDKWindow)w)->cc, x, y);
1180 }
1181 
GGDKDrawPathLineTo(GWindow w,double x,double y)1182 void GGDKDrawPathLineTo(GWindow w, double x, double y) {
1183     //Log(LOGDEBUG, " ");
1184     cairo_line_to(((GGDKWindow)w)->cc, x, y);
1185 }
1186 
GGDKDrawPathCurveTo(GWindow w,double cx1,double cy1,double cx2,double cy2,double x,double y)1187 void GGDKDrawPathCurveTo(GWindow w, double cx1, double cy1, double cx2, double cy2, double x, double y) {
1188     //Log(LOGDEBUG, " ");
1189     cairo_curve_to(((GGDKWindow)w)->cc, cx1, cy1, cx2, cy2, x, y);
1190 }
1191 
GGDKDrawPathStroke(GWindow w,Color col)1192 void GGDKDrawPathStroke(GWindow w, Color col) {
1193     //Log(LOGDEBUG, " ");
1194     w->ggc->fg = col;
1195     GGDKDrawSetline((GGDKWindow) w, w->ggc);
1196     cairo_stroke(((GGDKWindow)w)->cc);
1197 }
1198 
GGDKDrawPathFill(GWindow w,Color col)1199 void GGDKDrawPathFill(GWindow w, Color col) {
1200     //Log(LOGDEBUG, " ");
1201     cairo_set_source_rgba(((GGDKWindow) w)->cc, COLOR_RED(col) / 255.0, COLOR_GREEN(col) / 255.0, COLOR_BLUE(col) / 255.0,
1202                           (col >> 24) / 255.0);
1203     cairo_fill(((GGDKWindow) w)->cc);
1204 }
1205 
GGDKDrawPathFillAndStroke(GWindow w,Color fillcol,Color strokecol)1206 void GGDKDrawPathFillAndStroke(GWindow w, Color fillcol, Color strokecol) {
1207     //Log(LOGDEBUG, " ");
1208     // This function is unused, so it's unclear if it's implemented correctly.
1209     GGDKWindow gw = (GGDKWindow) w;
1210 
1211     cairo_save(gw->cc);
1212     cairo_set_source_rgba(gw->cc, COLOR_RED(fillcol) / 255.0, COLOR_GREEN(fillcol) / 255.0, COLOR_BLUE(fillcol) / 255.0,
1213                           (fillcol >> 24) / 255.0);
1214     cairo_fill(gw->cc);
1215     cairo_restore(gw->cc);
1216     w->ggc->fg = strokecol;
1217     GGDKDrawSetline(gw, gw->ggc);
1218     cairo_fill_preserve(gw->cc);
1219     cairo_stroke(gw->cc);
1220 }
1221 
GGDKDrawStartNewSubPath(GWindow w)1222 void GGDKDrawStartNewSubPath(GWindow w) {
1223     //Log(LOGDEBUG, " ");
1224     _GGDKDraw_CheckAutoPaint((GGDKWindow)w);
1225     cairo_new_sub_path(((GGDKWindow)w)->cc);
1226 }
1227 
GGDKDrawFillRuleSetWinding(GWindow w)1228 int GGDKDrawFillRuleSetWinding(GWindow w) {
1229     //Log(LOGDEBUG, " ");
1230     _GGDKDraw_CheckAutoPaint((GGDKWindow)w);
1231     cairo_set_fill_rule(((GGDKWindow)w)->cc, CAIRO_FILL_RULE_WINDING);
1232     return 1;
1233 }
1234 
GGDKDrawDoText8(GWindow w,int32 x,int32 y,const char * text,int32 cnt,Color col,enum text_funcs drawit,struct tf_arg * arg)1235 int GGDKDrawDoText8(GWindow w, int32 x, int32 y, const char *text, int32 cnt, Color col, enum text_funcs drawit,
1236                     struct tf_arg *arg) {
1237     //Log(LOGDEBUG, " ");
1238 
1239     GGDKWindow gw = (GGDKWindow) w;
1240     struct font_instance *fi = gw->ggc->fi;
1241     PangoRectangle rect, ink;
1242     PangoFontDescription *fd;
1243 
1244     if (fi == NULL) {
1245         return 0;
1246     }
1247 
1248     fd = _GGDKDraw_configfont(w, fi);
1249     if (fd == NULL) {
1250         return 0;
1251     }
1252 
1253     pango_layout_set_font_description(gw->pango_layout, fd);
1254     pango_layout_set_text(gw->pango_layout, (char *)text, cnt);
1255     pango_layout_get_pixel_extents(gw->pango_layout, &ink, &rect);
1256 
1257     if (drawit == tf_drawit) {
1258         _GGDKDraw_CheckAutoPaint(gw);
1259         _GGDKDraw_MyCairoRenderLayout(gw->cc, col, gw->pango_layout, x, y);
1260     } else if (drawit == tf_rect) {
1261         PangoLayoutIter *iter;
1262         PangoLayoutRun *run;
1263         PangoFontMetrics *fm;
1264 
1265         arg->size.lbearing = ink.x - rect.x;
1266         arg->size.rbearing = ink.x + ink.width - rect.x;
1267         arg->size.width = rect.width;
1268         if (*text == '\0') {
1269             // There are no runs if there are no characters
1270             memset(&arg->size, 0, sizeof(arg->size));
1271         } else {
1272             iter = pango_layout_get_iter(gw->pango_layout);
1273             run = pango_layout_iter_get_run(iter);
1274             if (run == NULL) {
1275                 // Pango doesn't give us runs in a couple of other places
1276                 // surrogates, not unicode (0xfffe, 0xffff), etc.
1277                 memset(&arg->size, 0, sizeof(arg->size));
1278             } else {
1279                 fm = pango_font_get_metrics(run->item->analysis.font, NULL);
1280                 arg->size.fas = pango_font_metrics_get_ascent(fm) / PANGO_SCALE;
1281                 arg->size.fds = pango_font_metrics_get_descent(fm) / PANGO_SCALE;
1282                 arg->size.as = ink.y + ink.height - arg->size.fds;
1283                 arg->size.ds = arg->size.fds - ink.y;
1284                 if (arg->size.ds < 0) {
1285                     --arg->size.as;
1286                     arg->size.ds = 0;
1287                 }
1288                 // In the one case I've looked at fds is one pixel off from rect.y
1289                 //  I don't know what to make of that
1290                 pango_font_metrics_unref(fm);
1291             }
1292             pango_layout_iter_free(iter);
1293         }
1294     }
1295     return rect.width;
1296 }
1297 
GGDKDrawPushClipOnly(GWindow w)1298 void GGDKDrawPushClipOnly(GWindow w) {
1299     //Log(LOGDEBUG, " ");
1300     _GGDKDraw_CheckAutoPaint((GGDKWindow)w);
1301     cairo_save(((GGDKWindow)w)->cc);
1302 }
1303 
GGDKDrawClipPreserve(GWindow w)1304 void GGDKDrawClipPreserve(GWindow w) {
1305     //Log(LOGDEBUG, " ");
1306     _GGDKDraw_CheckAutoPaint((GGDKWindow)w);
1307     cairo_clip_preserve(((GGDKWindow)w)->cc);
1308 }
1309 
1310 // PANGO LAYOUT
1311 
GGDKDrawGetFontMetrics(GWindow gw,GFont * fi,int * as,int * ds,int * ld)1312 void GGDKDrawGetFontMetrics(GWindow gw, GFont *fi, int *as, int *ds, int *ld) {
1313     //Log(LOGDEBUG, " ");
1314 
1315     GGDKDisplay *gdisp = ((GGDKWindow) gw)->display;
1316     PangoFont *pfont;
1317     PangoFontMetrics *fm;
1318     PangoFontMap *pfm;
1319 
1320     _GGDKDraw_configfont(gw, fi);
1321     pfm = pango_context_get_font_map(gdisp->pangoc_context);
1322     pfont = pango_font_map_load_font(pfm, gdisp->pangoc_context, fi->pangoc_fd);
1323     fm = pango_font_get_metrics(pfont, NULL);
1324     *as = pango_font_metrics_get_ascent(fm) / PANGO_SCALE;
1325     *ds = pango_font_metrics_get_descent(fm) / PANGO_SCALE;
1326     *ld = 0;
1327     pango_font_metrics_unref(fm);
1328     if (pfont != NULL) {
1329         g_object_unref(pfont);
1330     }
1331 }
1332 
GGDKDrawLayoutInit(GWindow w,char * text,int cnt,GFont * fi)1333 void GGDKDrawLayoutInit(GWindow w, char *text, int cnt, GFont *fi) {
1334     //Log(LOGDEBUG, " ");
1335     GGDKWindow gw = (GGDKWindow) w;
1336     PangoFontDescription *fd;
1337 
1338     if (fi == NULL) {
1339         fi = gw->ggc->fi;
1340     }
1341 
1342     fd = _GGDKDraw_configfont(w, fi);
1343     pango_layout_set_font_description(gw->pango_layout, fd);
1344     pango_layout_set_text(gw->pango_layout, text, cnt);
1345 }
1346 
GGDKDrawLayoutDraw(GWindow w,int32 x,int32 y,Color fg)1347 void GGDKDrawLayoutDraw(GWindow w, int32 x, int32 y, Color fg) {
1348     //Log(LOGDEBUG, " ");
1349     GGDKWindow gw = (GGDKWindow)w;
1350 
1351     _GGDKDraw_MyCairoRenderLayout(gw->cc, fg, gw->pango_layout, x, y);
1352 }
1353 
GGDKDrawLayoutIndexToPos(GWindow w,int index,GRect * pos)1354 void GGDKDrawLayoutIndexToPos(GWindow w, int index, GRect *pos) {
1355     //Log(LOGDEBUG, " ");
1356     GGDKWindow gw = (GGDKWindow)w;
1357     PangoRectangle rect;
1358 
1359     pango_layout_index_to_pos(gw->pango_layout, index, &rect);
1360     pos->x = rect.x / PANGO_SCALE;
1361     pos->y = rect.y / PANGO_SCALE;
1362     pos->width  = rect.width / PANGO_SCALE;
1363     pos->height = rect.height / PANGO_SCALE;
1364 }
1365 
GGDKDrawLayoutXYToIndex(GWindow w,int x,int y)1366 int GGDKDrawLayoutXYToIndex(GWindow w, int x, int y) {
1367     //Log(LOGDEBUG, " ");
1368     GGDKWindow gw = (GGDKWindow)w;
1369     int trailing, index;
1370 
1371     // Pango retuns the last character if x is negative, not the first.
1372     if (x < 0) {
1373         x = 0;
1374     }
1375     pango_layout_xy_to_index(gw->pango_layout, x * PANGO_SCALE, y * PANGO_SCALE, &index, &trailing);
1376 
1377     // If I give Pango a position after the last character on a line, it
1378     // returns to me the first character. Strange. And annoying -- you click
1379     // at the end of a line and the cursor moves to the start
1380     // Of course in right to left text an initial position is correct...
1381     if ((index + trailing) == 0 && x > 0) {
1382         PangoRectangle rect;
1383         pango_layout_get_pixel_extents(gw->pango_layout, &rect, NULL);
1384         if (x >= rect.width) {
1385             x = rect.width - 1;
1386             pango_layout_xy_to_index(gw->pango_layout, x * PANGO_SCALE, y * PANGO_SCALE, &index, &trailing);
1387         }
1388     }
1389     return index + trailing;
1390 }
1391 
GGDKDrawLayoutExtents(GWindow w,GRect * size)1392 void GGDKDrawLayoutExtents(GWindow w, GRect *size) {
1393     //Log(LOGDEBUG, " ");
1394     GGDKWindow gw = (GGDKWindow)w;
1395     PangoRectangle rect;
1396 
1397     pango_layout_get_pixel_extents(gw->pango_layout, NULL, &rect);
1398     size->x = rect.x;
1399     size->y = rect.y;
1400     size->width  = rect.width;
1401     size->height = rect.height;
1402 }
1403 
GGDKDrawLayoutSetWidth(GWindow w,int width)1404 void GGDKDrawLayoutSetWidth(GWindow w, int width) {
1405     //Log(LOGDEBUG, " ");
1406     GGDKWindow gw = (GGDKWindow)w;
1407 
1408     pango_layout_set_width(gw->pango_layout, (width == -1) ? -1 : width * PANGO_SCALE);
1409 }
1410 
GGDKDrawLayoutLineCount(GWindow w)1411 int GGDKDrawLayoutLineCount(GWindow w) {
1412     //Log(LOGDEBUG, " ");
1413     GGDKWindow gw = (GGDKWindow)w;
1414 
1415     return pango_layout_get_line_count(gw->pango_layout);
1416 }
1417 
GGDKDrawLayoutLineStart(GWindow w,int l)1418 int GGDKDrawLayoutLineStart(GWindow w, int l) {
1419     //Log(LOGDEBUG, " ");
1420     GGDKWindow gw = (GGDKWindow)w;
1421     PangoLayoutLine *line = pango_layout_get_line(gw->pango_layout, l);
1422 
1423     if (line == NULL) {
1424         return -1;
1425     }
1426 
1427     return line->start_index;
1428 }
1429 
1430 #endif // FONTFORGE_CAN_USE_GDK
1431