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