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