Cancel previous changes
[m17n/m17n-lib.git] / src / m17n-gd.c
1 /* m17n-gd.c -- implementation of the GUI API on GD Library.
2    Copyright (C) 2004
3      National Institute of Advanced Industrial Science and Technology (AIST)
4      Registration Number H15PRO112
5
6    This file is part of the m17n library.
7
8    The m17n library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Lesser General Public License
10    as published by the Free Software Foundation; either version 2.1 of
11    the License, or (at your option) any later version.
12
13    The m17n library is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Lesser General Public License for more details.
17
18    You should have received a copy of the GNU Lesser General Public
19    License along with the m17n library; if not, write to the Free
20    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21    02111-1307, USA.  */
22
23 #if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE)
24 /*** @addtogroup m17nInternal
25      @{ */
26
27 #include "config.h"
28
29 #if defined (HAVE_FREETYPE) && defined (HAVE_GD)
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <ctype.h>
34 #include <string.h>
35 #include <gd.h>
36
37 #include "m17n-gui.h"
38 #include "m17n-misc.h"
39 #include "internal.h"
40 #include "internal-gui.h"
41 #include "symbol.h"
42 #include "font.h"
43 #include "fontset.h"
44 #include "face.h"
45
46 static MPlist *realized_fontset_list;
47 static MPlist *realized_font_list;
48 static MPlist *realized_face_list;
49
50 /* The first element is for 256 color, the second for true color.  */
51 static gdImagePtr scratch_images[2];
52
53 enum ColorIndex
54   {
55     COLOR_NORMAL,
56     COLOR_INVERSE,
57     COLOR_HLINE,
58     COLOR_BOX_TOP,
59     COLOR_BOX_BOTTOM,
60     COLOR_BOX_LEFT,
61     COLOR_BOX_RIGHT,
62     COLOR_MAX
63   };
64
65 static MSymbol M_rgb;
66
67 static void
68 read_rgb_txt ()
69 {
70   FILE *fp = fopen ("/usr/lib/X11/rgb.txt", "r");
71   int r, g, b;
72
73   if (! fp)
74     fp = fopen ("/usr/X11R6/lib/X11/rgb.txt", "r");
75   if (! fp)
76     return;
77   while (1)
78     {
79       char buf[256];
80       int c, len;
81
82       if ((c = getc (fp)) == EOF) 
83         break;
84       if (c == '!')
85         {
86           while ((c = getc (fp)) != EOF && c != '\n');
87           continue;
88         }
89       ungetc (c, fp);
90       if (fscanf (fp, "%d %d %d", &r, &g, &b) != 3)
91         break;
92       while ((c = getc (fp)) != EOF && isspace (c));
93       if (c == EOF)
94         break;
95       buf[0] = c;
96       fgets (buf + 1, 255, fp);
97       len = strlen (buf);
98       if (buf[len - 1] == '\n')
99         buf[len - 1] = '\0';
100       b |= (r << 16) | (g << 8);
101       msymbol_put (msymbol (buf), M_rgb, (void *) b);
102     }
103   fclose (fp);
104 }
105    
106
107 static int
108 parse_color (MSymbol sym)
109 {
110   char *name = MSYMBOL_NAME (sym);
111   unsigned r = 0x80, g = 0x80, b = 0x80;
112   int i;
113   
114   do {
115     if (strncmp (name , "rgb:", 4) == 0)
116       {
117         name += 4;
118         if (sscanf (name, "%x", &r) < 1)
119           break;
120         for (i = 0; *name != '/'; i++, name++);
121         r = (i == 1 ? ((r << 1) | r) : (r >> (i - 2)));
122         name++;
123         if (sscanf (name, "%x", &g) < 1)
124           break;
125         for (i = 0; *name != '/'; i++, name++);
126         g = (i == 1 ? ((g << 1) | g) : (g >> (i - 2)));
127         name += 4;
128         if (sscanf (name, "%x", &b) < 1)
129           break;
130         for (i = 0; *name; i++, name++);
131         b = (i == 1 ? ((b << 1) | b) : (b >> (i - 2)));
132       }
133     else if (*name == '#')
134       {
135         name++;
136         i = strlen (name);
137         if (i == 3)
138           {
139             if (sscanf (name, "%1x%1x%1x", &r, &g, &b) < 3)
140               break;
141             r <<= 4, g <<= 4, b <<= 4;
142           }
143         else if (i == 6)
144           {
145             if (sscanf (name, "%2x%2x%2x", &r, &g, &b) < 3)
146               break;
147           }
148         else if (i == 9)
149           {
150             if (sscanf (name, "%3x%3x%3x", &r, &g, &b) < 3)
151               break;
152             r >>= 1, g >>= 1, b >>= 1;
153           }
154         else if (i == 12)
155           {
156             if (sscanf (name, "%4x%4x%4x", &r, &g, &b) < 3)
157               break;
158             r >>= 2, g >>= 2, b >>= 2;
159           }
160       }
161     else
162       return (int) msymbol_get (sym, M_rgb);
163   } while (0);
164
165   return ((r << 16) | (g << 8) | b);
166 }
167
168
169 static gdImagePtr
170 get_scrach_image (gdImagePtr img, int width, int height)
171 {
172   int index;
173   gdImagePtr scratch;
174
175 #if HAVE_GD == 1
176   index = 0;
177 #else
178   index = img->trueColor ? 1 : 0;
179 #endif
180   scratch = scratch_images[index];
181
182   if (scratch)
183     {
184       if (scratch->sx <= width && scratch->sy <= height)
185         return scratch;
186       gdImageDestroy (scratch);
187     }
188 #if HAVE_GD > 1
189   if (img->trueColor)
190     scratch = scratch_images[1] = gdImageCreateTrueColor (width, height);
191   else
192 #endif
193     scratch = scratch_images[0] = gdImageCreate (width, height);
194   return scratch;
195 }  
196
197 static int
198 intersect_rectangle (MDrawMetric *r1, MDrawMetric *r2, MDrawMetric *rect)
199 {
200   *rect = *r1;
201   if (rect->x < r2->x)
202     rect->width -= (r2->x - rect->x), rect->x = r2->x;
203   if (rect->x + rect->width > r2->x + r2->width)
204     rect->width -= (r2->x + r2->width - rect->x - rect->width);
205   if (rect->y < r2->y)
206     rect->height -= (r2->y - rect->y), rect->y = r2->y;
207   if (rect->y + rect->height > r2->y + r2->height)
208     rect->height -= (r2->y + r2->height - rect->y - rect->height);
209   return 1;
210 }
211
212
213 #define INTERSECT_RECTANGLE(r1, r2, rect)       \
214   (((r1)->x + (r1->width) <= (r2)->x            \
215     || (r2)->x + (r2)->width <= (r1)->x         \
216     || (r1)->y + (r1->height) <= (r2)->y        \
217     || (r2)->y + (r2)->height <= (r1)->y)       \
218    ? 0                                          \
219    : intersect_rectangle (r1, r2, rect))
220
221
222 #define RESOLVE_COLOR(img, color)                                       \
223   gdImageColorResolve ((img), (color) >> 16, ((color) >> 8) & 0xFF,     \
224                        (color) & 0xFF)
225
226
227 static void
228 gd_render (MDrawWindow win, int x, int y,
229            MGlyphString *gstring, MGlyph *from, MGlyph *to,
230            int reverse, MDrawRegion region)
231 {
232   gdImagePtr img = (gdImagePtr) win;
233   MFTInfo *ft_info;
234   FT_Face ft_face;
235   MRealizedFace *rface = from->rface;
236   FT_Int32 load_flags = FT_LOAD_RENDER;
237   int i, j;
238   int color, pixel;
239   int r, g, b;
240   
241   pixel = RESOLVE_COLOR (img, color);
242
243   if (from == to)
244     return;
245
246   /* It is assured that the all glyphs in the current range use the
247      same realized face.  */
248   ft_info = (MFTInfo *) rface->rfont->info;
249   ft_face = ft_info->ft_face;
250   color = ((int *) rface->info)[reverse ? COLOR_INVERSE : COLOR_NORMAL];
251   pixel = RESOLVE_COLOR (img, color);
252
253   if (gstring->anti_alias)
254     r = color >> 16, g = (color >> 8) & 0xFF, b = color & 0xFF;
255   else
256     {
257 #ifdef FT_LOAD_TARGET_MONO
258       load_flags |= FT_LOAD_TARGET_MONO;
259 #else
260       load_flags |= FT_LOAD_MONOCHROME;
261 #endif
262     }
263
264   for (; from < to; x += from++->width)
265     {
266       unsigned char *bmp;
267       int xoff, yoff;
268       int width, pitch;
269
270       FT_Load_Glyph (ft_face, (FT_UInt) from->code, load_flags);
271       yoff = y - ft_face->glyph->bitmap_top + from->yoff;
272       bmp = ft_face->glyph->bitmap.buffer;
273       width = ft_face->glyph->bitmap.width;
274       pitch = ft_face->glyph->bitmap.pitch;
275       if (! gstring->anti_alias)
276         pitch *= 8;
277       if (width > pitch)
278         width = pitch;
279
280       if (gstring->anti_alias)
281         for (i = 0; i < ft_face->glyph->bitmap.rows;
282              i++, bmp += ft_face->glyph->bitmap.pitch, yoff++)
283           {
284             xoff = x + ft_face->glyph->bitmap_left + from->xoff;
285             for (j = 0; j < width; j++, xoff++)
286               if (bmp[j])
287                 {
288                   int f = bmp[j] >> 5;
289                   int pixel1 = pixel;
290
291                   if (f < 7)
292                     {
293                       int r1, g1, b1, color1;
294
295                       pixel1 = gdImageGetPixel (img, xoff, yoff);
296                       r1 = gdImageRed (img, pixel1);
297                       g1 = gdImageGreen (img, pixel1);
298                       b1 = gdImageBlue (img, pixel1);
299                       color1 = ((((r * f + r1 * (7 - f)) / 7) << 16)
300                                 | (((g * f + g1 * (7 - f)) / 7) << 8)
301                                 | ((b * f + b1 * (7 - f)) / 7));
302                       pixel1 = RESOLVE_COLOR (img, color1);
303                     }
304                   gdImageSetPixel (img, xoff, yoff, pixel1);
305                 }
306           }
307       else
308         for (i = 0; i < ft_face->glyph->bitmap.rows;
309              i++, bmp += ft_face->glyph->bitmap.pitch, yoff++)
310           {
311             xoff = x + ft_face->glyph->bitmap_left + from->xoff;
312             for (j = 0; j < width; j++, xoff++)
313               if (bmp[j / 8] & (1 << (7 - (j % 8))))
314                 gdImageSetPixel (img, xoff, yoff, pixel);
315           }
316     }
317 }
318
319 static MFontDriver gd_font_driver =
320   { NULL, NULL, NULL, NULL, gd_render };
321
322 static void
323 gd_close (MFrame *frame)
324 {
325 }
326
327 static void *
328 gd_get_prop (MFrame *frame, MSymbol key)
329 {
330   return NULL;
331 }
332
333 static void
334 gd_realize_face (MRealizedFace *rface)
335 {
336   int *colors;
337   MFaceHLineProp *hline;
338   MFaceBoxProp *box;
339   MSymbol *props = (MSymbol *) rface->face.property;
340
341   if (rface != rface->ascii_rface)
342     {
343       rface->info = rface->ascii_rface->info;
344       return;
345     }
346   colors = malloc (sizeof (int) * COLOR_MAX);
347   colors[COLOR_NORMAL] = parse_color (props[MFACE_FOREGROUND]);
348   colors[COLOR_INVERSE] = parse_color (props[MFACE_BACKGROUND]);
349   if (rface->face.property[MFACE_VIDEOMODE] == Mreverse)
350     {
351       colors[COLOR_HLINE] = colors[COLOR_NORMAL];
352       colors[COLOR_NORMAL] = colors[COLOR_INVERSE];
353       colors[COLOR_INVERSE] = colors[COLOR_HLINE];
354     }
355   colors[COLOR_HLINE] = 0;
356
357   hline = rface->hline;
358   if (hline)
359     {
360       if (hline->color)
361         colors[COLOR_HLINE] = parse_color (hline->color);
362       else
363         colors[COLOR_HLINE] = colors[COLOR_NORMAL];
364     }
365
366   box = rface->box;
367   if (box)
368     {
369       if (box->color_top)
370         colors[COLOR_BOX_TOP] = parse_color (box->color_top);
371       else
372         colors[COLOR_BOX_TOP] = colors[COLOR_NORMAL];
373
374       if (box->color_left && box->color_left != box->color_top)
375         colors[COLOR_BOX_LEFT] = parse_color (box->color_left);
376       else
377         colors[COLOR_BOX_LEFT] = colors[COLOR_BOX_TOP];
378
379       if (box->color_bottom && box->color_bottom != box->color_top)
380         colors[COLOR_BOX_BOTTOM] = parse_color (box->color_bottom);
381       else
382         colors[COLOR_BOX_BOTTOM] = colors[COLOR_BOX_TOP];
383
384       if (box->color_right && box->color_right != box->color_bottom)
385         colors[COLOR_BOX_RIGHT] = parse_color (box->color_right);
386       else
387         colors[COLOR_BOX_RIGHT] = colors[COLOR_BOX_BOTTOM];
388     }
389
390   rface->info = colors;
391 }
392
393 static void
394 gd_free_realized_face (MRealizedFace *rface)
395 {
396   free (rface->info);
397 }
398
399 static void
400 gd_fill_space (MFrame *frame, MDrawWindow win, MRealizedFace *rface,
401                int reverse,
402                int x, int y, int width, int height, MDrawRegion region)
403 {
404   gdImagePtr img = (gdImagePtr) win;
405   int *colors = rface->info;
406   int color = colors[reverse ? COLOR_NORMAL : COLOR_INVERSE];
407   MPlist *region_list = region, *plist;
408
409   color = RESOLVE_COLOR (img, color);
410   if (! region)
411     gdImageFilledRectangle (img, x, y, x + width - 1, y + height - 1, color);
412   else
413     {
414       MDrawMetric rect;;
415
416       rect.x = x, rect.y = y, rect.width = width, rect.height = height;
417       MPLIST_DO (plist, region_list)
418         {
419           MDrawMetric *r = MPLIST_VAL (plist), new;
420
421           if (INTERSECT_RECTANGLE (r, &rect, &new))
422             gdImageFilledRectangle (img, new.x, new.y, new.x + new.width - 1,
423                                     new.y + new.height - 1, color);
424         }
425     }
426 }
427
428 static void
429 gd_draw_empty_boxes (MDrawWindow win, int x, int y,
430                      MGlyphString *gstring, MGlyph *from, MGlyph *to,
431                      int reverse, MDrawRegion region)
432 {
433   gdImagePtr img = (gdImagePtr) win;
434   int *colors = from->rface->info;
435   int color = colors[reverse ? COLOR_INVERSE : COLOR_NORMAL];
436   MPlist *region_list = region, *plist;
437   int height;
438
439   if (from == to)
440     return;
441
442   color = RESOLVE_COLOR (img, color);
443   y -= gstring->ascent - 1;
444   height = gstring->ascent + gstring->descent - 2;
445   if (! region)
446     for (; from < to; x += from++->width)
447       gdImageRectangle (img, x, y, x + from->width - 2, y + height - 1, color);
448   else
449     {
450       gdImagePtr cpy;
451       MGlyph *g;
452       int width, x1;
453
454       for (g = from, width = 0; g < to; width += g++->width);
455       cpy = get_scrach_image (img, width, height);
456       MPLIST_DO (plist, region_list)
457         {
458           MDrawMetric *rect = MPLIST_VAL (plist);
459           gdImageCopy (cpy, img, rect->x - x, rect->y - y, rect->x, rect->y,
460                        rect->x + rect->width, rect->y + rect->height);
461         }
462       for (x1 = 0; from < to; x1 += from++->width)
463         gdImageRectangle (cpy, x1, 0, x1 + from->width - 2, height - 1, color);
464       MPLIST_DO (plist, region_list)
465         {
466           MDrawMetric *rect = MPLIST_VAL (plist);
467           gdImageCopy (img, cpy, rect->x, rect->y, rect->x - x, rect->y - y,
468                        rect->x + rect->width, rect->y + rect->height);
469         }
470     }
471 }
472
473
474 static void
475 gd_draw_hline (MFrame *frame, MDrawWindow win, MGlyphString *gstring,
476                MRealizedFace *rface, int reverse,
477                int x, int y, int width, MDrawRegion region)
478 {
479   enum MFaceHLineType type = rface->hline->type;
480   int height = rface->hline->width;
481   gdImagePtr img = (gdImagePtr) win;
482   int *colors = rface->info;
483   int color = colors[COLOR_HLINE];
484   MPlist *region_list = region, *plist;
485
486   color = RESOLVE_COLOR (img, color);
487   y = (type == MFACE_HLINE_BOTTOM
488        ? y + gstring->text_descent - height
489        : type == MFACE_HLINE_UNDER
490        ? y + 1
491        : type == MFACE_HLINE_STRIKE_THROUGH
492        ? y - ((gstring->ascent + gstring->descent) / 2)
493        : y - gstring->text_ascent);
494   if (! region)
495     gdImageFilledRectangle (img, x, y, x + width - 1, y + height - 1, color);
496   else
497     {
498       MDrawMetric rect;
499
500       rect.x = x, rect.y = y, rect.width = width, rect.height = height;
501       MPLIST_DO (plist, region_list)
502         {
503           MDrawMetric *r = MPLIST_VAL (plist), new;
504
505           if (INTERSECT_RECTANGLE (r, &rect, &new))
506             gdImageFilledRectangle (img, new.x, new.y, new.x + new.width - 1,
507                                     new.y + new.height - 1, color);
508         }
509     }
510 }
511
512
513 static void
514 gd_draw_box (MFrame *frame, MDrawWindow win, MGlyphString *gstring,
515              MGlyph *g, int x, int y, int width, MDrawRegion region)
516 {
517   gdImagePtr img = (gdImagePtr) win;
518   int *colors = g->rface->info;
519   int color;
520   MRealizedFace *rface = g->rface;
521   MFaceBoxProp *box = rface->box;
522   MPlist *region_list = region, *plist;
523   int y0, y1;
524   int i;
525
526   y0 = y - (gstring->text_ascent
527             + rface->box->inner_vmargin + rface->box->width);
528   y1 = y + (gstring->text_descent
529             + rface->box->inner_vmargin + rface->box->width - 1);
530
531   if (region)
532     {
533       int height = y1 - y0;
534       gdImagePtr cpy;
535
536       if (g->type == GLYPH_BOX)
537         width = g->width;
538       cpy = get_scrach_image (img, width, height);
539       MPLIST_DO (plist, region_list)
540         {
541           MDrawMetric *rect = MPLIST_VAL (plist);
542           gdImageCopy (cpy, img, rect->x - x, rect->y - y, rect->x, rect->y,
543                        rect->x + rect->width, rect->y + rect->height);
544         }
545       gd_draw_box (frame, win, gstring, g, 0, y - y0, width, NULL);
546       MPLIST_DO (plist, region_list)
547         {
548           MDrawMetric *rect = MPLIST_VAL (plist);
549           gdImageCopy (img, cpy, rect->x, rect->y, rect->x - x, rect->y - y,
550                        rect->x + rect->width, rect->y + rect->height);
551         }
552       return;
553     }
554
555   if (g->type == GLYPH_BOX)
556     {
557       int x0, x1;
558
559       if (g->left_padding)
560         x0 = x + box->outer_hmargin, x1 = x + g->width - 1;
561       else
562         x0 = x, x1 = x + g->width - box->outer_hmargin - 1;
563
564       /* Draw the top side.  */
565       color = RESOLVE_COLOR (img, colors[COLOR_BOX_TOP]);
566       for (i = 0; i < box->width; i++)
567         gdImageLine (img, x0, y0 + i, x1, y0 + i, color);
568
569       /* Draw the bottom side.  */
570       color = RESOLVE_COLOR (img, colors[COLOR_BOX_BOTTOM]);
571       for (i = 0; i < box->width; i++)
572         gdImageLine (img, x0, y1 - i, x1, y1 - i, color);
573
574       if (g->left_padding > 0)
575         {
576           /* Draw the left side.  */
577           color = RESOLVE_COLOR (img, colors[COLOR_BOX_LEFT]);
578           for (i = 0; i < rface->box->width; i++)
579             gdImageLine (img, x0 + i, y0 + i, x0 + i, y1 - i, color);
580         }
581       else
582         {
583           /* Draw the right side.  */
584           color = RESOLVE_COLOR (img, colors[COLOR_BOX_RIGHT]);
585           for (i = 0; i < rface->box->width; i++)
586             gdImageLine (img, x1 - i, y0 + i, x1 - i, y1 - i, color);
587         }
588
589     }
590   else
591     {
592       /* Draw the top side.  */
593       color = RESOLVE_COLOR (img, colors[COLOR_BOX_TOP]);
594       for (i = 0; i < box->width; i++)
595         gdImageLine (img, x, y0 + i, x + width - 1, y0 + i, color);
596
597       /* Draw the bottom side.  */
598       color = RESOLVE_COLOR (img, colors[COLOR_BOX_BOTTOM]);
599       for (i = 0; i < box->width; i++)
600         gdImageLine (img, x, y1 - i, x + width - 1, y1 - i, color);
601     }
602 }
603
604
605 static MDrawRegion
606 gd_region_from_rect (MDrawMetric *rect)
607 {
608   MDrawMetric *new;
609   MPlist *plist = mplist ();
610
611   MSTRUCT_MALLOC (new, MERROR_GD);
612   *new = *rect;
613   mplist_add (plist, Mt, new);
614   return (MDrawRegion) plist;
615 }
616
617 static void
618 gd_union_rect_with_region (MDrawRegion region, MDrawMetric *rect)
619 {
620   MPlist *plist = (MPlist *) region;
621   MDrawMetric *r;
622
623   MSTRUCT_MALLOC (r, MERROR_GD);
624   *r = *rect;
625   mplist_push (plist, Mt, r);
626 }
627
628 static void
629 gd_intersect_region (MDrawRegion region1, MDrawRegion region2)
630 {
631   MPlist *plist1 = (MPlist *) region1, *p1 = plist1;
632   MPlist *plist2 = (MPlist *) region2;
633   MPlist *p2;
634   MDrawMetric rect, *rect1, *rect2, *r;
635
636   while (! MPLIST_TAIL_P (p1))
637     {
638       rect1 = mplist_pop (p1);
639       MPLIST_DO (p2, plist2)
640         {
641           rect2 = MPLIST_VAL (p2);
642           if (INTERSECT_RECTANGLE (rect1, rect2, &rect))
643             {
644               MSTRUCT_MALLOC (r, MERROR_GD);
645               *r = rect;
646               mplist_push (p1, Mt, r);
647               p1 = MPLIST_NEXT (p1);
648             }
649         }
650       free (rect1);
651     }
652 }
653
654 static void
655 gd_region_add_rect (MDrawRegion region, MDrawMetric *rect)
656 {
657   MPlist *plist = (MPlist *) region;
658   MDrawMetric *new;
659
660   MSTRUCT_MALLOC (new, MERROR_GD);
661   *new = *rect;
662   mplist_push (plist, Mt, new);
663 }
664
665 static void
666 gd_region_to_rect (MDrawRegion region, MDrawMetric *rect)
667 {
668   MPlist *plist = (MPlist *) region;
669   MDrawMetric *r = MPLIST_VAL (plist);
670   int min_x = r->x, max_x = min_x + r->width;
671   int min_y = r->y, max_y = min_y + r->height;
672   
673   MPLIST_DO (plist, MPLIST_NEXT (plist))
674     {
675       r = MPLIST_VAL (plist);
676       if (r->x < min_x)
677         min_x = r->x;
678       if (r->x + r->width > max_x)
679         max_x = r->x + r->width;
680       if (r->y < min_y)
681         min_y = r->y;
682       if (r->y + r->height > max_y)
683         max_y = r->y + r->height;
684     }
685   rect->x = min_x;
686   rect->y = min_y;
687   rect->width = max_x - min_x;
688   rect->height =max_y - min_y;
689 }
690
691 static void
692 gd_free_region (MDrawRegion region)
693 {
694   MPlist *plist = (MPlist *) region;
695
696   MPLIST_DO (plist, plist)
697     free (MPLIST_VAL (plist));
698   M17N_OBJECT_UNREF (region);
699 }
700
701 static void
702 gd_dump_region (MDrawRegion region)
703 {
704   MDrawMetric rect;
705
706   gd_region_to_rect (region, &rect);
707   fprintf (stderr, "(%d %d %d %d)\n", rect.x, rect.y, rect.width, rect.height);
708 }
709
710 static MDeviceDriver gd_driver =
711   {
712     gd_close,
713     gd_get_prop,
714     gd_realize_face,
715     gd_free_realized_face,
716     gd_fill_space,
717     gd_draw_empty_boxes,
718     gd_draw_hline,
719     gd_draw_box,
720     NULL,
721     gd_region_from_rect,
722     gd_union_rect_with_region,
723     gd_intersect_region,
724     gd_region_add_rect,
725     gd_region_to_rect,
726     gd_free_region,
727     gd_dump_region,
728   };
729
730 /* Functions to be stored in MDeviceLibraryInterface by dlsym ().  */
731
732 int
733 device_init ()
734 {
735   M_rgb = msymbol ("  rgb");
736   read_rgb_txt ();
737   realized_fontset_list = mplist ();
738   realized_font_list = mplist ();
739   realized_face_list = mplist ();  
740   scratch_images[0] = scratch_images[1] = NULL;
741
742   gd_font_driver.select = mfont__ft_driver.select;
743   gd_font_driver.open = mfont__ft_driver.open;
744   gd_font_driver.find_metric = mfont__ft_driver.find_metric;
745   gd_font_driver.encode_char = mfont__ft_driver.encode_char;
746
747   return 0;
748 }
749
750 int
751 device_fini ()
752 {
753   MPlist *plist;
754   int i;
755
756   MPLIST_DO (plist, realized_fontset_list)
757     mfont__free_realized_fontset ((MRealizedFontset *) MPLIST_VAL (plist));
758   M17N_OBJECT_UNREF (realized_fontset_list);
759
760   MPLIST_DO (plist, realized_face_list)
761     {
762       MRealizedFace *rface = MPLIST_VAL (plist);
763
764       free (rface->info);
765       mface__free_realized (rface);
766     }
767   M17N_OBJECT_UNREF (realized_face_list);
768
769   MPLIST_DO (plist, realized_font_list)
770     mfont__free_realized ((MRealizedFont *) MPLIST_VAL (plist));
771   M17N_OBJECT_UNREF (realized_font_list);
772
773   for (i = 0; i < 2; i++)
774     if (scratch_images[i])
775       gdImageDestroy (scratch_images[i]);
776   return 0;
777 }
778
779 int
780 device_open (MFrame *frame, MPlist *param)
781 {
782   MFace *face;
783
784   frame->device = NULL;
785   frame->device_type = MDEVICE_SUPPORT_OUTPUT;
786   frame->driver = &gd_driver;
787   frame->font_driver_list = mplist ();
788   mplist_add (frame->font_driver_list, Mfreetype, &gd_font_driver);
789   frame->realized_font_list = realized_font_list;
790   frame->realized_face_list = realized_face_list;
791   frame->realized_fontset_list = realized_fontset_list;
792   face = mface_copy (mface__default);
793   mplist_push (param, Mface, face);
794   M17N_OBJECT_UNREF (face);
795   return 0;
796 }
797
798 #endif  /* not HAVE_GD nor HAVE_FREETYPE */
799
800 /*** @} */
801 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */