(gd_render): Use gdImageColorResolveAlpha instead of
[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 static MRealizedFont *gd_font_open (MFrame *, MFont *, MFont *,
227                                     MRealizedFont *);
228 static void gd_render (MDrawWindow, int, int, MGlyphString *,
229                        MGlyph *, MGlyph *, int, MDrawRegion);
230
231 static MFontDriver gd_font_driver =
232   { NULL, gd_font_open, NULL, NULL, NULL, gd_render, NULL };
233
234 static MRealizedFont *
235 gd_font_open (MFrame *frame, MFont *font, MFont *spec, MRealizedFont *rfont)
236 {
237   double size = font->size ? font->size : spec->size;
238   int reg = spec->property[MFONT_REGISTRY];
239   MRealizedFont *new;
240
241   if (rfont)
242     {
243       MRealizedFont *save = NULL;
244
245       for (; rfont; rfont = rfont->next)
246         if (rfont->font == font
247             && (rfont->font->size ? rfont->font->size == size
248                 : rfont->spec.size == size)
249             && rfont->spec.property[MFONT_REGISTRY] == reg)
250           {
251             if (! save)
252               save = rfont;
253             if (rfont->driver == &gd_font_driver)
254               return rfont;
255           }
256       rfont = save;
257     }
258   rfont = (mfont__ft_driver.open) (frame, font, spec, rfont);
259   if (! rfont)
260     return NULL;
261   M17N_OBJECT_REF (rfont->info);
262   MSTRUCT_CALLOC (new, MERROR_GD);
263   *new = *rfont;
264   new->driver = &gd_font_driver;
265   new->next = MPLIST_VAL (frame->realized_font_list);
266   MPLIST_VAL (frame->realized_font_list) = new;
267   return new;
268 }
269
270 static void
271 gd_render (MDrawWindow win, int x, int y,
272            MGlyphString *gstring, MGlyph *from, MGlyph *to,
273            int reverse, MDrawRegion region)
274 {
275   gdImagePtr img = (gdImagePtr) win;
276   FT_Face ft_face;
277   MRealizedFace *rface = from->rface;
278   FT_Int32 load_flags = FT_LOAD_RENDER;
279   int i, j;
280   int color, pixel;
281   int r, g, b;
282   
283   pixel = RESOLVE_COLOR (img, color);
284
285   if (from == to)
286     return;
287
288   /* It is assured that the all glyphs in the current range use the
289      same realized face.  */
290   ft_face = rface->rfont->fontp;
291   color = ((int *) rface->info)[reverse ? COLOR_INVERSE : COLOR_NORMAL];
292   pixel = RESOLVE_COLOR (img, color);
293
294   if (gstring->anti_alias)
295     r = color >> 16, g = (color >> 8) & 0xFF, b = color & 0xFF;
296   else
297     {
298 #ifdef FT_LOAD_TARGET_MONO
299       load_flags |= FT_LOAD_TARGET_MONO;
300 #else
301       load_flags |= FT_LOAD_MONOCHROME;
302 #endif
303     }
304
305   for (; from < to; x += from++->width)
306     {
307       unsigned char *bmp;
308       int xoff, yoff;
309       int width, pitch;
310
311       FT_Load_Glyph (ft_face, (FT_UInt) from->code, load_flags);
312       yoff = y - ft_face->glyph->bitmap_top + from->yoff;
313       bmp = ft_face->glyph->bitmap.buffer;
314       width = ft_face->glyph->bitmap.width;
315       pitch = ft_face->glyph->bitmap.pitch;
316       if (! gstring->anti_alias)
317         pitch *= 8;
318       if (width > pitch)
319         width = pitch;
320
321       if (gstring->anti_alias)
322         for (i = 0; i < ft_face->glyph->bitmap.rows;
323              i++, bmp += ft_face->glyph->bitmap.pitch, yoff++)
324           {
325             xoff = x + ft_face->glyph->bitmap_left + from->xoff;
326             for (j = 0; j < width; j++, xoff++)
327               if (bmp[j] > 0)
328                 {
329                   int pixel1 = pixel;
330                   int alpha = gdAlphaTransparent * (255 - bmp[j]) / 255;
331
332                   if (alpha > 0)
333                     pixel1 = gdImageColorResolveAlpha (img, r, g, b, alpha);
334                   gdImageSetPixel (img, xoff, yoff, pixel1);
335                 }
336           }
337       else
338         for (i = 0; i < ft_face->glyph->bitmap.rows;
339              i++, bmp += ft_face->glyph->bitmap.pitch, yoff++)
340           {
341             xoff = x + ft_face->glyph->bitmap_left + from->xoff;
342             for (j = 0; j < width; j++, xoff++)
343               if (bmp[j / 8] & (1 << (7 - (j % 8))))
344                 gdImageSetPixel (img, xoff, yoff, pixel);
345           }
346     }
347 }
348
349 static void
350 gd_close (MFrame *frame)
351 {
352 }
353
354 static void *
355 gd_get_prop (MFrame *frame, MSymbol key)
356 {
357   return NULL;
358 }
359
360 static void
361 gd_realize_face (MRealizedFace *rface)
362 {
363   int *colors;
364   MFaceHLineProp *hline;
365   MFaceBoxProp *box;
366   MSymbol *props = (MSymbol *) rface->face.property;
367
368   if (rface != rface->ascii_rface)
369     {
370       rface->info = rface->ascii_rface->info;
371       return;
372     }
373   colors = malloc (sizeof (int) * COLOR_MAX);
374   colors[COLOR_NORMAL] = parse_color (props[MFACE_FOREGROUND]);
375   colors[COLOR_INVERSE] = parse_color (props[MFACE_BACKGROUND]);
376   if (rface->face.property[MFACE_VIDEOMODE] == Mreverse)
377     {
378       colors[COLOR_HLINE] = colors[COLOR_NORMAL];
379       colors[COLOR_NORMAL] = colors[COLOR_INVERSE];
380       colors[COLOR_INVERSE] = colors[COLOR_HLINE];
381     }
382   colors[COLOR_HLINE] = 0;
383
384   hline = rface->hline;
385   if (hline)
386     {
387       if (hline->color)
388         colors[COLOR_HLINE] = parse_color (hline->color);
389       else
390         colors[COLOR_HLINE] = colors[COLOR_NORMAL];
391     }
392
393   box = rface->box;
394   if (box)
395     {
396       if (box->color_top)
397         colors[COLOR_BOX_TOP] = parse_color (box->color_top);
398       else
399         colors[COLOR_BOX_TOP] = colors[COLOR_NORMAL];
400
401       if (box->color_left && box->color_left != box->color_top)
402         colors[COLOR_BOX_LEFT] = parse_color (box->color_left);
403       else
404         colors[COLOR_BOX_LEFT] = colors[COLOR_BOX_TOP];
405
406       if (box->color_bottom && box->color_bottom != box->color_top)
407         colors[COLOR_BOX_BOTTOM] = parse_color (box->color_bottom);
408       else
409         colors[COLOR_BOX_BOTTOM] = colors[COLOR_BOX_TOP];
410
411       if (box->color_right && box->color_right != box->color_bottom)
412         colors[COLOR_BOX_RIGHT] = parse_color (box->color_right);
413       else
414         colors[COLOR_BOX_RIGHT] = colors[COLOR_BOX_BOTTOM];
415     }
416
417   rface->info = colors;
418 }
419
420 static void
421 gd_free_realized_face (MRealizedFace *rface)
422 {
423   free (rface->info);
424 }
425
426 static void
427 gd_fill_space (MFrame *frame, MDrawWindow win, MRealizedFace *rface,
428                int reverse,
429                int x, int y, int width, int height, MDrawRegion region)
430 {
431   gdImagePtr img = (gdImagePtr) win;
432   int *colors = rface->info;
433   int color = colors[reverse ? COLOR_NORMAL : COLOR_INVERSE];
434   MPlist *region_list = region, *plist;
435
436   color = RESOLVE_COLOR (img, color);
437   if (! region)
438     gdImageFilledRectangle (img, x, y, x + width - 1, y + height - 1, color);
439   else
440     {
441       MDrawMetric rect;;
442
443       rect.x = x, rect.y = y, rect.width = width, rect.height = height;
444       MPLIST_DO (plist, region_list)
445         {
446           MDrawMetric *r = MPLIST_VAL (plist), new;
447
448           if (INTERSECT_RECTANGLE (r, &rect, &new))
449             gdImageFilledRectangle (img, new.x, new.y, new.x + new.width - 1,
450                                     new.y + new.height - 1, color);
451         }
452     }
453 }
454
455 static void
456 gd_draw_empty_boxes (MDrawWindow win, int x, int y,
457                      MGlyphString *gstring, MGlyph *from, MGlyph *to,
458                      int reverse, MDrawRegion region)
459 {
460   gdImagePtr img = (gdImagePtr) win;
461   int *colors = from->rface->info;
462   int color = colors[reverse ? COLOR_INVERSE : COLOR_NORMAL];
463   MPlist *region_list = region, *plist;
464   int height;
465
466   if (from == to)
467     return;
468
469   color = RESOLVE_COLOR (img, color);
470   y -= gstring->ascent - 1;
471   height = gstring->ascent + gstring->descent - 2;
472   if (! region)
473     for (; from < to; x += from++->width)
474       gdImageRectangle (img, x, y, x + from->width - 2, y + height - 1, color);
475   else
476     {
477       gdImagePtr cpy;
478       MGlyph *g;
479       int width, x1;
480
481       for (g = from, width = 0; g < to; width += g++->width);
482       cpy = get_scrach_image (img, width, height);
483       MPLIST_DO (plist, region_list)
484         {
485           MDrawMetric *rect = MPLIST_VAL (plist);
486           gdImageCopy (cpy, img, rect->x - x, rect->y - y, rect->x, rect->y,
487                        rect->x + rect->width, rect->y + rect->height);
488         }
489       for (x1 = 0; from < to; x1 += from++->width)
490         gdImageRectangle (cpy, x1, 0, x1 + from->width - 2, height - 1, color);
491       MPLIST_DO (plist, region_list)
492         {
493           MDrawMetric *rect = MPLIST_VAL (plist);
494           gdImageCopy (img, cpy, rect->x, rect->y, rect->x - x, rect->y - y,
495                        rect->x + rect->width, rect->y + rect->height);
496         }
497     }
498 }
499
500
501 static void
502 gd_draw_hline (MFrame *frame, MDrawWindow win, MGlyphString *gstring,
503                MRealizedFace *rface, int reverse,
504                int x, int y, int width, MDrawRegion region)
505 {
506   enum MFaceHLineType type = rface->hline->type;
507   int height = rface->hline->width;
508   gdImagePtr img = (gdImagePtr) win;
509   int *colors = rface->info;
510   int color = colors[COLOR_HLINE];
511   MPlist *region_list = region, *plist;
512
513   color = RESOLVE_COLOR (img, color);
514   y = (type == MFACE_HLINE_BOTTOM
515        ? y + gstring->text_descent - height
516        : type == MFACE_HLINE_UNDER
517        ? y + 1
518        : type == MFACE_HLINE_STRIKE_THROUGH
519        ? y - ((gstring->ascent + gstring->descent) / 2)
520        : y - gstring->text_ascent);
521   if (! region)
522     gdImageFilledRectangle (img, x, y, x + width - 1, y + height - 1, color);
523   else
524     {
525       MDrawMetric rect;
526
527       rect.x = x, rect.y = y, rect.width = width, rect.height = height;
528       MPLIST_DO (plist, region_list)
529         {
530           MDrawMetric *r = MPLIST_VAL (plist), new;
531
532           if (INTERSECT_RECTANGLE (r, &rect, &new))
533             gdImageFilledRectangle (img, new.x, new.y, new.x + new.width - 1,
534                                     new.y + new.height - 1, color);
535         }
536     }
537 }
538
539
540 static void
541 gd_draw_box (MFrame *frame, MDrawWindow win, MGlyphString *gstring,
542              MGlyph *g, int x, int y, int width, MDrawRegion region)
543 {
544   gdImagePtr img = (gdImagePtr) win;
545   int *colors = g->rface->info;
546   int color;
547   MRealizedFace *rface = g->rface;
548   MFaceBoxProp *box = rface->box;
549   MPlist *region_list = region, *plist;
550   int y0, y1;
551   int i;
552
553   y0 = y - (gstring->text_ascent
554             + rface->box->inner_vmargin + rface->box->width);
555   y1 = y + (gstring->text_descent
556             + rface->box->inner_vmargin + rface->box->width - 1);
557
558   if (region)
559     {
560       int height = y1 - y0;
561       gdImagePtr cpy;
562
563       if (g->type == GLYPH_BOX)
564         width = g->width;
565       cpy = get_scrach_image (img, width, height);
566       MPLIST_DO (plist, region_list)
567         {
568           MDrawMetric *rect = MPLIST_VAL (plist);
569           gdImageCopy (cpy, img, rect->x - x, rect->y - y, rect->x, rect->y,
570                        rect->x + rect->width, rect->y + rect->height);
571         }
572       gd_draw_box (frame, win, gstring, g, 0, y - y0, width, NULL);
573       MPLIST_DO (plist, region_list)
574         {
575           MDrawMetric *rect = MPLIST_VAL (plist);
576           gdImageCopy (img, cpy, rect->x, rect->y, rect->x - x, rect->y - y,
577                        rect->x + rect->width, rect->y + rect->height);
578         }
579       return;
580     }
581
582   if (g->type == GLYPH_BOX)
583     {
584       int x0, x1;
585
586       if (g->left_padding)
587         x0 = x + box->outer_hmargin, x1 = x + g->width - 1;
588       else
589         x0 = x, x1 = x + g->width - box->outer_hmargin - 1;
590
591       /* Draw the top side.  */
592       color = RESOLVE_COLOR (img, colors[COLOR_BOX_TOP]);
593       for (i = 0; i < box->width; i++)
594         gdImageLine (img, x0, y0 + i, x1, y0 + i, color);
595
596       /* Draw the bottom side.  */
597       color = RESOLVE_COLOR (img, colors[COLOR_BOX_BOTTOM]);
598       for (i = 0; i < box->width; i++)
599         gdImageLine (img, x0, y1 - i, x1, y1 - i, color);
600
601       if (g->left_padding > 0)
602         {
603           /* Draw the left side.  */
604           color = RESOLVE_COLOR (img, colors[COLOR_BOX_LEFT]);
605           for (i = 0; i < rface->box->width; i++)
606             gdImageLine (img, x0 + i, y0 + i, x0 + i, y1 - i, color);
607         }
608       else
609         {
610           /* Draw the right side.  */
611           color = RESOLVE_COLOR (img, colors[COLOR_BOX_RIGHT]);
612           for (i = 0; i < rface->box->width; i++)
613             gdImageLine (img, x1 - i, y0 + i, x1 - i, y1 - i, color);
614         }
615
616     }
617   else
618     {
619       /* Draw the top side.  */
620       color = RESOLVE_COLOR (img, colors[COLOR_BOX_TOP]);
621       for (i = 0; i < box->width; i++)
622         gdImageLine (img, x, y0 + i, x + width - 1, y0 + i, color);
623
624       /* Draw the bottom side.  */
625       color = RESOLVE_COLOR (img, colors[COLOR_BOX_BOTTOM]);
626       for (i = 0; i < box->width; i++)
627         gdImageLine (img, x, y1 - i, x + width - 1, y1 - i, color);
628     }
629 }
630
631
632 static MDrawRegion
633 gd_region_from_rect (MDrawMetric *rect)
634 {
635   MDrawMetric *new;
636   MPlist *plist = mplist ();
637
638   MSTRUCT_MALLOC (new, MERROR_GD);
639   *new = *rect;
640   mplist_add (plist, Mt, new);
641   return (MDrawRegion) plist;
642 }
643
644 static void
645 gd_union_rect_with_region (MDrawRegion region, MDrawMetric *rect)
646 {
647   MPlist *plist = (MPlist *) region;
648   MDrawMetric *r;
649
650   MSTRUCT_MALLOC (r, MERROR_GD);
651   *r = *rect;
652   mplist_push (plist, Mt, r);
653 }
654
655 static void
656 gd_intersect_region (MDrawRegion region1, MDrawRegion region2)
657 {
658   MPlist *plist1 = (MPlist *) region1, *p1 = plist1;
659   MPlist *plist2 = (MPlist *) region2;
660   MPlist *p2;
661   MDrawMetric rect, *rect1, *rect2, *r;
662
663   while (! MPLIST_TAIL_P (p1))
664     {
665       rect1 = mplist_pop (p1);
666       MPLIST_DO (p2, plist2)
667         {
668           rect2 = MPLIST_VAL (p2);
669           if (INTERSECT_RECTANGLE (rect1, rect2, &rect))
670             {
671               MSTRUCT_MALLOC (r, MERROR_GD);
672               *r = rect;
673               mplist_push (p1, Mt, r);
674               p1 = MPLIST_NEXT (p1);
675             }
676         }
677       free (rect1);
678     }
679 }
680
681 static void
682 gd_region_add_rect (MDrawRegion region, MDrawMetric *rect)
683 {
684   MPlist *plist = (MPlist *) region;
685   MDrawMetric *new;
686
687   MSTRUCT_MALLOC (new, MERROR_GD);
688   *new = *rect;
689   mplist_push (plist, Mt, new);
690 }
691
692 static void
693 gd_region_to_rect (MDrawRegion region, MDrawMetric *rect)
694 {
695   MPlist *plist = (MPlist *) region;
696   MDrawMetric *r = MPLIST_VAL (plist);
697   int min_x = r->x, max_x = min_x + r->width;
698   int min_y = r->y, max_y = min_y + r->height;
699   
700   MPLIST_DO (plist, MPLIST_NEXT (plist))
701     {
702       r = MPLIST_VAL (plist);
703       if (r->x < min_x)
704         min_x = r->x;
705       if (r->x + r->width > max_x)
706         max_x = r->x + r->width;
707       if (r->y < min_y)
708         min_y = r->y;
709       if (r->y + r->height > max_y)
710         max_y = r->y + r->height;
711     }
712   rect->x = min_x;
713   rect->y = min_y;
714   rect->width = max_x - min_x;
715   rect->height =max_y - min_y;
716 }
717
718 static void
719 gd_free_region (MDrawRegion region)
720 {
721   MPlist *plist = (MPlist *) region;
722
723   MPLIST_DO (plist, plist)
724     free (MPLIST_VAL (plist));
725   M17N_OBJECT_UNREF (region);
726 }
727
728 static void
729 gd_dump_region (MDrawRegion region)
730 {
731   MDrawMetric rect;
732
733   gd_region_to_rect (region, &rect);
734   fprintf (stderr, "(%d %d %d %d)\n", rect.x, rect.y, rect.width, rect.height);
735 }
736
737 static MDeviceDriver gd_driver =
738   {
739     gd_close,
740     gd_get_prop,
741     gd_realize_face,
742     gd_free_realized_face,
743     gd_fill_space,
744     gd_draw_empty_boxes,
745     gd_draw_hline,
746     gd_draw_box,
747     NULL,
748     gd_region_from_rect,
749     gd_union_rect_with_region,
750     gd_intersect_region,
751     gd_region_add_rect,
752     gd_region_to_rect,
753     gd_free_region,
754     gd_dump_region,
755   };
756
757 /* Functions to be stored in MDeviceLibraryInterface by dlsym ().  */
758
759 int
760 device_init ()
761 {
762   M_rgb = msymbol ("  rgb");
763   read_rgb_txt ();
764   realized_fontset_list = mplist ();
765   realized_font_list = mplist ();
766   realized_face_list = mplist ();  
767   scratch_images[0] = scratch_images[1] = NULL;
768
769   gd_font_driver.select = mfont__ft_driver.select;
770   gd_font_driver.find_metric = mfont__ft_driver.find_metric;
771   gd_font_driver.has_char = mfont__ft_driver.has_char;
772   gd_font_driver.encode_char = mfont__ft_driver.encode_char;
773   gd_font_driver.list = mfont__ft_driver.list;
774
775   return 0;
776 }
777
778 int
779 device_fini ()
780 {
781   MPlist *plist;
782   int i;
783
784   MPLIST_DO (plist, realized_fontset_list)
785     mfont__free_realized_fontset ((MRealizedFontset *) MPLIST_VAL (plist));
786   M17N_OBJECT_UNREF (realized_fontset_list);
787
788   MPLIST_DO (plist, realized_face_list)
789     {
790       MRealizedFace *rface = MPLIST_VAL (plist);
791
792       free (rface->info);
793       mface__free_realized (rface);
794     }
795   M17N_OBJECT_UNREF (realized_face_list);
796
797   if (MPLIST_VAL (realized_font_list))
798     mfont__free_realized (MPLIST_VAL (realized_font_list));
799   M17N_OBJECT_UNREF (realized_font_list);
800
801   for (i = 0; i < 2; i++)
802     if (scratch_images[i])
803       gdImageDestroy (scratch_images[i]);
804   return 0;
805 }
806
807 int
808 device_open (MFrame *frame, MPlist *param)
809 {
810   MFace *face;
811
812   frame->device = NULL;
813   frame->device_type = MDEVICE_SUPPORT_OUTPUT;
814   frame->dpi = (int) mplist_get (param, Mresolution);
815   if (frame->dpi == 0)
816     frame->dpi = 100;
817   frame->driver = &gd_driver;
818   frame->font_driver_list = mplist ();
819   mplist_add (frame->font_driver_list, Mfreetype, &gd_font_driver);
820   frame->realized_font_list = realized_font_list;
821   frame->realized_face_list = realized_face_list;
822   frame->realized_fontset_list = realized_fontset_list;
823   face = mface_copy (mface__default);
824   mplist_push (param, Mface, face);
825   M17N_OBJECT_UNREF (face);
826   return 0;
827 }
828
829 #else   /* not HAVE_GD nor HAVE_FREETYPE */
830
831 int device_open () { return -1; }
832
833 #endif  /* not HAVE_GD nor HAVE_FREETYPE */
834
835 /*** @} */
836 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */