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