merge FLT
[m17n/m17n-lib.git] / src / m17n-gd.c
1 /* m17n-gd.c -- implementation of the GUI API on GD Library.
2    Copyright (C) 2004, 2005, 2006
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., 51 Franklin Street, Fifth Floor,
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;
71   int r, g, b, i;
72
73   /* At first, support HTML 4.0 color names. */
74   msymbol_put (msymbol ("black"), M_rgb, (void *) 0x000000);
75   msymbol_put (msymbol ("silver"), M_rgb, (void *) 0xC0C0C0);
76   msymbol_put (msymbol ("gray"), M_rgb, (void *) 0x808080);
77   msymbol_put (msymbol ("white"), M_rgb, (void *) 0xFFFFFF);
78   msymbol_put (msymbol ("maroon"), M_rgb, (void *) 0x800000);
79   msymbol_put (msymbol ("red"), M_rgb, (void *) 0xFF0000);
80   msymbol_put (msymbol ("purple"), M_rgb, (void *) 0x800080);
81   msymbol_put (msymbol ("fuchsia"), M_rgb, (void *) 0xFF00FF);
82   msymbol_put (msymbol ("green"), M_rgb, (void *) 0x008000);
83   msymbol_put (msymbol ("lime"), M_rgb, (void *) 0x00FF00);
84   msymbol_put (msymbol ("olive"), M_rgb, (void *) 0x808000);
85   msymbol_put (msymbol ("yellow"), M_rgb, (void *) 0xFFFF00);
86   msymbol_put (msymbol ("navy"), M_rgb, (void *) 0x000080);
87   msymbol_put (msymbol ("blue"), M_rgb, (void *) 0x0000FF);
88   msymbol_put (msymbol ("teal"), M_rgb, (void *) 0x008080);
89   msymbol_put (msymbol ("aqua"), M_rgb, (void *) 0x00FFFF);
90
91   {
92     char *rgb_path[]
93       =  {"/usr/lib/X11/rgb.txt", "/usr/X11R6/lib/X11/rgb.txt",
94           "/etc/X11/rgb.txt" };
95
96     fp = NULL;
97     for (i = 0; i < (sizeof rgb_path) / (sizeof rgb_path[0]); i++)
98       if ((fp = fopen ("/usr/lib/X11/rgb.txt", "r")))
99         break;
100     if (! fp)
101       return;
102   }
103   while (1)
104     {
105       char buf[256];
106       int c, len;
107
108       if ((c = getc (fp)) == EOF) 
109         break;
110       if (c == '!')
111         {
112           while ((c = getc (fp)) != EOF && c != '\n');
113           continue;
114         }
115       ungetc (c, fp);
116       if (fscanf (fp, "%d %d %d", &r, &g, &b) != 3)
117         break;
118       while ((c = getc (fp)) != EOF && isspace (c));
119       if (c == EOF)
120         break;
121       buf[0] = c;
122       fgets (buf + 1, 255, fp);
123       len = strlen (buf);
124       for (i = 0; i < len; i++)
125         buf[i] = tolower (buf[i]);
126       if (buf[len - 1] == '\n')
127         buf[len - 1] = '\0';
128       b |= (r << 16) | (g << 8);
129       msymbol_put (msymbol (buf), M_rgb, (void *) b);
130     }
131   fclose (fp);
132 }
133    
134
135 static int
136 parse_color (MSymbol sym)
137 {
138   char *name = MSYMBOL_NAME (sym);
139   unsigned r = 0x80, g = 0x80, b = 0x80;
140   int i;
141   
142   do {
143     if (strncmp (name , "rgb:", 4) == 0)
144       {
145         name += 4;
146         if (sscanf (name, "%x", &r) < 1)
147           break;
148         for (i = 0; *name != '/'; i++, name++);
149         r = (i == 1 ? ((r << 1) | r) : (r >> (i - 2)));
150         name++;
151         if (sscanf (name, "%x", &g) < 1)
152           break;
153         for (i = 0; *name != '/'; i++, name++);
154         g = (i == 1 ? ((g << 1) | g) : (g >> (i - 2)));
155         name += 4;
156         if (sscanf (name, "%x", &b) < 1)
157           break;
158         for (i = 0; *name; i++, name++);
159         b = (i == 1 ? ((b << 1) | b) : (b >> (i - 2)));
160       }
161     else if (*name == '#')
162       {
163         name++;
164         i = strlen (name);
165         if (i == 3)
166           {
167             if (sscanf (name, "%1x%1x%1x", &r, &g, &b) < 3)
168               break;
169             r <<= 4, g <<= 4, b <<= 4;
170           }
171         else if (i == 6)
172           {
173             if (sscanf (name, "%2x%2x%2x", &r, &g, &b) < 3)
174               break;
175           }
176         else if (i == 9)
177           {
178             if (sscanf (name, "%3x%3x%3x", &r, &g, &b) < 3)
179               break;
180             r >>= 1, g >>= 1, b >>= 1;
181           }
182         else if (i == 12)
183           {
184             if (sscanf (name, "%4x%4x%4x", &r, &g, &b) < 3)
185               break;
186             r >>= 2, g >>= 2, b >>= 2;
187           }
188       }
189     else
190       return (int) msymbol_get (sym, M_rgb);
191   } while (0);
192
193   return ((r << 16) | (g << 8) | b);
194 }
195
196
197 static gdImagePtr
198 get_scrach_image (gdImagePtr img, int width, int height)
199 {
200   int index;
201   gdImagePtr scratch;
202
203 #if HAVE_GD == 1
204   index = 0;
205 #else
206   index = img->trueColor ? 1 : 0;
207 #endif
208   scratch = scratch_images[index];
209
210   if (scratch)
211     {
212       if (scratch->sx <= width && scratch->sy <= height)
213         return scratch;
214       gdImageDestroy (scratch);
215     }
216 #if HAVE_GD > 1
217   if (img->trueColor)
218     scratch = scratch_images[1] = gdImageCreateTrueColor (width, height);
219   else
220 #endif
221     scratch = scratch_images[0] = gdImageCreate (width, height);
222   return scratch;
223 }  
224
225 static int
226 intersect_rectangle (MDrawMetric *r1, MDrawMetric *r2, MDrawMetric *rect)
227 {
228   *rect = *r1;
229   if (rect->x < r2->x)
230     rect->width -= (r2->x - rect->x), rect->x = r2->x;
231   if (rect->x + rect->width > r2->x + r2->width)
232     rect->width -= (r2->x + r2->width - rect->x - rect->width);
233   if (rect->y < r2->y)
234     rect->height -= (r2->y - rect->y), rect->y = r2->y;
235   if (rect->y + rect->height > r2->y + r2->height)
236     rect->height -= (r2->y + r2->height - rect->y - rect->height);
237   return 1;
238 }
239
240
241 #define INTERSECT_RECTANGLE(r1, r2, rect)       \
242   (((r1)->x + (r1->width) <= (r2)->x            \
243     || (r2)->x + (r2)->width <= (r1)->x         \
244     || (r1)->y + (r1->height) <= (r2)->y        \
245     || (r2)->y + (r2)->height <= (r1)->y)       \
246    ? 0                                          \
247    : intersect_rectangle (r1, r2, rect))
248
249
250 #define RESOLVE_COLOR(img, color)                                       \
251   gdImageColorResolve ((img), (color) >> 16, ((color) >> 8) & 0xFF,     \
252                        (color) & 0xFF)
253
254 static MRealizedFont *gd_font_open (MFrame *, MFont *, MFont *,
255                                     MRealizedFont *);
256 static void gd_render (MDrawWindow, int, int, MGlyphString *,
257                        MGlyph *, MGlyph *, int, MDrawRegion);
258
259 static MFontDriver gd_font_driver =
260   { NULL, gd_font_open, NULL, NULL, NULL, gd_render, NULL };
261
262 static MRealizedFont *
263 gd_font_open (MFrame *frame, MFont *font, MFont *spec, MRealizedFont *rfont)
264 {
265   double size = font->size ? font->size : spec->size;
266   int reg = spec->property[MFONT_REGISTRY];
267   MRealizedFont *new;
268
269   if (rfont)
270     {
271       MRealizedFont *save = NULL;
272
273       for (; rfont; rfont = rfont->next)
274         if (rfont->font == font
275             && (rfont->font->size ? rfont->font->size == size
276                 : rfont->spec.size == size)
277             && rfont->spec.property[MFONT_REGISTRY] == reg)
278           {
279             if (! save)
280               save = rfont;
281             if (rfont->driver == &gd_font_driver)
282               return rfont;
283           }
284       rfont = save;
285     }
286   rfont = (mfont__ft_driver.open) (frame, font, spec, rfont);
287   if (! rfont)
288     return NULL;
289   M17N_OBJECT_REF (rfont->info);
290   MSTRUCT_CALLOC (new, MERROR_GD);
291   *new = *rfont;
292   new->driver = &gd_font_driver;
293   new->next = MPLIST_VAL (frame->realized_font_list);
294   MPLIST_VAL (frame->realized_font_list) = new;
295   return new;
296 }
297
298 static void
299 gd_render (MDrawWindow win, int x, int y,
300            MGlyphString *gstring, MGlyph *from, MGlyph *to,
301            int reverse, MDrawRegion region)
302 {
303   gdImagePtr img = (gdImagePtr) win;
304   FT_Face ft_face;
305   MRealizedFace *rface = from->rface;
306   FT_Int32 load_flags = FT_LOAD_RENDER;
307   int i, j;
308   int color, pixel;
309   int r, g, b;
310   
311   if (from == to)
312     return;
313
314   /* It is assured that the all glyphs in the current range use the
315      same realized face.  */
316   ft_face = rface->rfont->fontp;
317   color = ((int *) rface->info)[reverse ? COLOR_INVERSE : COLOR_NORMAL];
318   pixel = RESOLVE_COLOR (img, color);
319
320   if (gstring->anti_alias)
321     r = color >> 16, g = (color >> 8) & 0xFF, b = color & 0xFF;
322   else
323     {
324 #ifdef FT_LOAD_TARGET_MONO
325       load_flags |= FT_LOAD_TARGET_MONO;
326 #else
327       load_flags |= FT_LOAD_MONOCHROME;
328 #endif
329     }
330
331   for (; from < to; x += from++->g.xadv)
332     {
333       unsigned char *bmp;
334       int xoff, yoff;
335       int width, pitch;
336
337       FT_Load_Glyph (ft_face, (FT_UInt) from->g.code, load_flags);
338       yoff = y - ft_face->glyph->bitmap_top + from->g.yoff;
339       bmp = ft_face->glyph->bitmap.buffer;
340       width = ft_face->glyph->bitmap.width;
341       pitch = ft_face->glyph->bitmap.pitch;
342       if (! gstring->anti_alias)
343         pitch *= 8;
344       if (width > pitch)
345         width = pitch;
346
347       if (gstring->anti_alias)
348         for (i = 0; i < ft_face->glyph->bitmap.rows;
349              i++, bmp += ft_face->glyph->bitmap.pitch, yoff++)
350           {
351             xoff = x + ft_face->glyph->bitmap_left + from->g.xoff;
352             for (j = 0; j < width; j++, xoff++)
353               if (bmp[j] > 0)
354                 {
355                   int pixel1 = pixel;
356 #if HAVE_GD > 1
357                   int alpha = gdAlphaTransparent * (255 - bmp[j]) / 255;
358
359                   if (alpha > 0)
360                     pixel1 = gdImageColorResolveAlpha (img, r, g, b, alpha);
361 #else
362                   int f = bmp[j] >> 5;
363
364                   if (f < 7)
365                     {
366                       int r1, g1, b1, color1;
367
368                       pixel1 = gdImageGetPixel (img, xoff, yoff);
369                       r1 = gdImageRed (img, pixel1);
370                       g1 = gdImageGreen (img, pixel1);
371                       b1 = gdImageBlue (img, pixel1);
372                       color1 = ((((r * f + r1 * (7 - f)) / 7) << 16)
373                                 | (((g * f + g1 * (7 - f)) / 7) << 8)
374                                 | ((b * f + b1 * (7 - f)) / 7));
375                       pixel1 = RESOLVE_COLOR (img, color1);
376                     }
377 #endif
378                   gdImageSetPixel (img, xoff, yoff, pixel1);
379                 }
380           }
381       else
382         for (i = 0; i < ft_face->glyph->bitmap.rows;
383              i++, bmp += ft_face->glyph->bitmap.pitch, yoff++)
384           {
385             xoff = x + ft_face->glyph->bitmap_left + from->g.xoff;
386             for (j = 0; j < width; j++, xoff++)
387               if (bmp[j / 8] & (1 << (7 - (j % 8))))
388                 gdImageSetPixel (img, xoff, yoff, pixel);
389           }
390     }
391 }
392
393 static void
394 gd_close (MFrame *frame)
395 {
396 }
397
398 static void *
399 gd_get_prop (MFrame *frame, MSymbol key)
400 {
401   return NULL;
402 }
403
404 static void
405 gd_realize_face (MRealizedFace *rface)
406 {
407   int *colors;
408   MFaceHLineProp *hline;
409   MFaceBoxProp *box;
410   MSymbol *props = (MSymbol *) rface->face.property;
411
412   if (rface != rface->ascii_rface)
413     {
414       rface->info = rface->ascii_rface->info;
415       return;
416     }
417   colors = malloc (sizeof (int) * COLOR_MAX);
418   colors[COLOR_NORMAL] = parse_color (props[MFACE_FOREGROUND]);
419   colors[COLOR_INVERSE] = parse_color (props[MFACE_BACKGROUND]);
420   if (rface->face.property[MFACE_VIDEOMODE] == Mreverse)
421     {
422       colors[COLOR_HLINE] = colors[COLOR_NORMAL];
423       colors[COLOR_NORMAL] = colors[COLOR_INVERSE];
424       colors[COLOR_INVERSE] = colors[COLOR_HLINE];
425     }
426   colors[COLOR_HLINE] = 0;
427
428   hline = rface->hline;
429   if (hline)
430     {
431       if (hline->color)
432         colors[COLOR_HLINE] = parse_color (hline->color);
433       else
434         colors[COLOR_HLINE] = colors[COLOR_NORMAL];
435     }
436
437   box = rface->box;
438   if (box)
439     {
440       if (box->color_top)
441         colors[COLOR_BOX_TOP] = parse_color (box->color_top);
442       else
443         colors[COLOR_BOX_TOP] = colors[COLOR_NORMAL];
444
445       if (box->color_left && box->color_left != box->color_top)
446         colors[COLOR_BOX_LEFT] = parse_color (box->color_left);
447       else
448         colors[COLOR_BOX_LEFT] = colors[COLOR_BOX_TOP];
449
450       if (box->color_bottom && box->color_bottom != box->color_top)
451         colors[COLOR_BOX_BOTTOM] = parse_color (box->color_bottom);
452       else
453         colors[COLOR_BOX_BOTTOM] = colors[COLOR_BOX_TOP];
454
455       if (box->color_right && box->color_right != box->color_bottom)
456         colors[COLOR_BOX_RIGHT] = parse_color (box->color_right);
457       else
458         colors[COLOR_BOX_RIGHT] = colors[COLOR_BOX_BOTTOM];
459     }
460
461   rface->info = colors;
462 }
463
464 static void
465 gd_free_realized_face (MRealizedFace *rface)
466 {
467   free (rface->info);
468 }
469
470 static void
471 gd_fill_space (MFrame *frame, MDrawWindow win, MRealizedFace *rface,
472                int reverse,
473                int x, int y, int width, int height, MDrawRegion region)
474 {
475   gdImagePtr img = (gdImagePtr) win;
476   int *colors = rface->info;
477   int color = colors[reverse ? COLOR_NORMAL : COLOR_INVERSE];
478   MPlist *region_list = region, *plist;
479
480   color = RESOLVE_COLOR (img, color);
481   if (! region)
482     gdImageFilledRectangle (img, x, y, x + width - 1, y + height - 1, color);
483   else
484     {
485       MDrawMetric rect;;
486
487       rect.x = x, rect.y = y, rect.width = width, rect.height = height;
488       MPLIST_DO (plist, region_list)
489         {
490           MDrawMetric *r = MPLIST_VAL (plist), new;
491
492           if (INTERSECT_RECTANGLE (r, &rect, &new))
493             gdImageFilledRectangle (img, new.x, new.y, new.x + new.width - 1,
494                                     new.y + new.height - 1, color);
495         }
496     }
497 }
498
499 static void
500 gd_draw_empty_boxes (MDrawWindow win, int x, int y,
501                      MGlyphString *gstring, MGlyph *from, MGlyph *to,
502                      int reverse, MDrawRegion region)
503 {
504   gdImagePtr img = (gdImagePtr) win;
505   int *colors = from->rface->info;
506   int color = colors[reverse ? COLOR_INVERSE : COLOR_NORMAL];
507   MPlist *region_list = region, *plist;
508   int height;
509
510   if (from == to)
511     return;
512
513   color = RESOLVE_COLOR (img, color);
514   y -= gstring->ascent - 1;
515   height = gstring->ascent + gstring->descent - 2;
516   if (! region)
517     for (; from < to; x += from++->g.xadv)
518       gdImageRectangle (img, x, y, x + from->g.xadv - 2, y + height - 1, color);
519   else
520     {
521       gdImagePtr cpy;
522       MGlyph *g;
523       int width, x1;
524
525       for (g = from, width = 0; g < to; width += g++->g.xadv);
526       cpy = get_scrach_image (img, width, height);
527       MPLIST_DO (plist, region_list)
528         {
529           MDrawMetric *rect = MPLIST_VAL (plist);
530           gdImageCopy (cpy, img, rect->x - x, rect->y - y, rect->x, rect->y,
531                        rect->x + rect->width, rect->y + rect->height);
532         }
533       for (x1 = 0; from < to; x1 += from++->g.xadv)
534         gdImageRectangle (cpy, x1, 0, x1 + from->g.xadv - 2, height - 1, color);
535       MPLIST_DO (plist, region_list)
536         {
537           MDrawMetric *rect = MPLIST_VAL (plist);
538           gdImageCopy (img, cpy, rect->x, rect->y, rect->x - x, rect->y - y,
539                        rect->x + rect->width, rect->y + rect->height);
540         }
541     }
542 }
543
544
545 static void
546 gd_draw_hline (MFrame *frame, MDrawWindow win, MGlyphString *gstring,
547                MRealizedFace *rface, int reverse,
548                int x, int y, int width, MDrawRegion region)
549 {
550   enum MFaceHLineType type = rface->hline->type;
551   int height = rface->hline->width;
552   gdImagePtr img = (gdImagePtr) win;
553   int *colors = rface->info;
554   int color = colors[COLOR_HLINE];
555   MPlist *region_list = region, *plist;
556
557   color = RESOLVE_COLOR (img, color);
558   y = (type == MFACE_HLINE_BOTTOM
559        ? y + gstring->text_descent - height
560        : type == MFACE_HLINE_UNDER
561        ? y + 1
562        : type == MFACE_HLINE_STRIKE_THROUGH
563        ? y - ((gstring->ascent + gstring->descent) / 2)
564        : y - gstring->text_ascent);
565   if (! region)
566     gdImageFilledRectangle (img, x, y, x + width - 1, y + height - 1, color);
567   else
568     {
569       MDrawMetric rect;
570
571       rect.x = x, rect.y = y, rect.width = width, rect.height = height;
572       MPLIST_DO (plist, region_list)
573         {
574           MDrawMetric *r = MPLIST_VAL (plist), new;
575
576           if (INTERSECT_RECTANGLE (r, &rect, &new))
577             gdImageFilledRectangle (img, new.x, new.y, new.x + new.width - 1,
578                                     new.y + new.height - 1, color);
579         }
580     }
581 }
582
583
584 static void
585 gd_draw_box (MFrame *frame, MDrawWindow win, MGlyphString *gstring,
586              MGlyph *g, int x, int y, int width, MDrawRegion region)
587 {
588   gdImagePtr img = (gdImagePtr) win;
589   int *colors = g->rface->info;
590   int color;
591   MRealizedFace *rface = g->rface;
592   MFaceBoxProp *box = rface->box;
593   MPlist *region_list = region, *plist;
594   int y0, y1;
595   int i;
596
597   y0 = y - (gstring->text_ascent
598             + rface->box->inner_vmargin + rface->box->width);
599   y1 = y + (gstring->text_descent
600             + rface->box->inner_vmargin + rface->box->width - 1);
601
602   if (region)
603     {
604       int height = y1 - y0;
605       gdImagePtr cpy;
606
607       if (g->type == GLYPH_BOX)
608         width = g->g.xadv;
609       cpy = get_scrach_image (img, width, height);
610       MPLIST_DO (plist, region_list)
611         {
612           MDrawMetric *rect = MPLIST_VAL (plist);
613           gdImageCopy (cpy, img, rect->x - x, rect->y - y, rect->x, rect->y,
614                        rect->x + rect->width, rect->y + rect->height);
615         }
616       gd_draw_box (frame, win, gstring, g, 0, y - y0, width, NULL);
617       MPLIST_DO (plist, region_list)
618         {
619           MDrawMetric *rect = MPLIST_VAL (plist);
620           gdImageCopy (img, cpy, rect->x, rect->y, rect->x - x, rect->y - y,
621                        rect->x + rect->width, rect->y + rect->height);
622         }
623       return;
624     }
625
626   if (g->type == GLYPH_BOX)
627     {
628       int x0, x1;
629
630       if (g->left_padding)
631         x0 = x + box->outer_hmargin, x1 = x + g->g.xadv - 1;
632       else
633         x0 = x, x1 = x + g->g.xadv - box->outer_hmargin - 1;
634
635       /* Draw the top side.  */
636       color = RESOLVE_COLOR (img, colors[COLOR_BOX_TOP]);
637       for (i = 0; i < box->width; i++)
638         gdImageLine (img, x0, y0 + i, x1, y0 + i, color);
639
640       /* Draw the bottom side.  */
641       color = RESOLVE_COLOR (img, colors[COLOR_BOX_BOTTOM]);
642       for (i = 0; i < box->width; i++)
643         gdImageLine (img, x0, y1 - i, x1, y1 - i, color);
644
645       if (g->left_padding > 0)
646         {
647           /* Draw the left side.  */
648           color = RESOLVE_COLOR (img, colors[COLOR_BOX_LEFT]);
649           for (i = 0; i < rface->box->width; i++)
650             gdImageLine (img, x0 + i, y0 + i, x0 + i, y1 - i, color);
651         }
652       else
653         {
654           /* Draw the right side.  */
655           color = RESOLVE_COLOR (img, colors[COLOR_BOX_RIGHT]);
656           for (i = 0; i < rface->box->width; i++)
657             gdImageLine (img, x1 - i, y0 + i, x1 - i, y1 - i, color);
658         }
659
660     }
661   else
662     {
663       /* Draw the top side.  */
664       color = RESOLVE_COLOR (img, colors[COLOR_BOX_TOP]);
665       for (i = 0; i < box->width; i++)
666         gdImageLine (img, x, y0 + i, x + width - 1, y0 + i, color);
667
668       /* Draw the bottom side.  */
669       color = RESOLVE_COLOR (img, colors[COLOR_BOX_BOTTOM]);
670       for (i = 0; i < box->width; i++)
671         gdImageLine (img, x, y1 - i, x + width - 1, y1 - i, color);
672     }
673 }
674
675
676 static MDrawRegion
677 gd_region_from_rect (MDrawMetric *rect)
678 {
679   MDrawMetric *new;
680   MPlist *plist = mplist ();
681
682   MSTRUCT_MALLOC (new, MERROR_GD);
683   *new = *rect;
684   mplist_add (plist, Mt, new);
685   return (MDrawRegion) plist;
686 }
687
688 static void
689 gd_union_rect_with_region (MDrawRegion region, MDrawMetric *rect)
690 {
691   MPlist *plist = (MPlist *) region;
692   MDrawMetric *r;
693
694   MSTRUCT_MALLOC (r, MERROR_GD);
695   *r = *rect;
696   mplist_push (plist, Mt, r);
697 }
698
699 static void
700 gd_intersect_region (MDrawRegion region1, MDrawRegion region2)
701 {
702   MPlist *plist1 = (MPlist *) region1, *p1 = plist1;
703   MPlist *plist2 = (MPlist *) region2;
704   MPlist *p2;
705   MDrawMetric rect, *rect1, *rect2, *r;
706
707   while (! MPLIST_TAIL_P (p1))
708     {
709       rect1 = mplist_pop (p1);
710       MPLIST_DO (p2, plist2)
711         {
712           rect2 = MPLIST_VAL (p2);
713           if (INTERSECT_RECTANGLE (rect1, rect2, &rect))
714             {
715               MSTRUCT_MALLOC (r, MERROR_GD);
716               *r = rect;
717               mplist_push (p1, Mt, r);
718               p1 = MPLIST_NEXT (p1);
719             }
720         }
721       free (rect1);
722     }
723 }
724
725 static void
726 gd_region_add_rect (MDrawRegion region, MDrawMetric *rect)
727 {
728   MPlist *plist = (MPlist *) region;
729   MDrawMetric *new;
730
731   MSTRUCT_MALLOC (new, MERROR_GD);
732   *new = *rect;
733   mplist_push (plist, Mt, new);
734 }
735
736 static void
737 gd_region_to_rect (MDrawRegion region, MDrawMetric *rect)
738 {
739   MPlist *plist = (MPlist *) region;
740   MDrawMetric *r = MPLIST_VAL (plist);
741   int min_x = r->x, max_x = min_x + r->width;
742   int min_y = r->y, max_y = min_y + r->height;
743   
744   MPLIST_DO (plist, MPLIST_NEXT (plist))
745     {
746       r = MPLIST_VAL (plist);
747       if (r->x < min_x)
748         min_x = r->x;
749       if (r->x + r->width > max_x)
750         max_x = r->x + r->width;
751       if (r->y < min_y)
752         min_y = r->y;
753       if (r->y + r->height > max_y)
754         max_y = r->y + r->height;
755     }
756   rect->x = min_x;
757   rect->y = min_y;
758   rect->width = max_x - min_x;
759   rect->height =max_y - min_y;
760 }
761
762 static void
763 gd_free_region (MDrawRegion region)
764 {
765   MPlist *plist = (MPlist *) region;
766
767   MPLIST_DO (plist, plist)
768     free (MPLIST_VAL (plist));
769   M17N_OBJECT_UNREF (region);
770 }
771
772 static void
773 gd_dump_region (MDrawRegion region)
774 {
775   MDrawMetric rect;
776
777   gd_region_to_rect (region, &rect);
778   fprintf (stderr, "(%d %d %d %d)\n", rect.x, rect.y, rect.width, rect.height);
779 }
780
781 static MDeviceDriver gd_driver =
782   {
783     gd_close,
784     gd_get_prop,
785     gd_realize_face,
786     gd_free_realized_face,
787     gd_fill_space,
788     gd_draw_empty_boxes,
789     gd_draw_hline,
790     gd_draw_box,
791     NULL,
792     gd_region_from_rect,
793     gd_union_rect_with_region,
794     gd_intersect_region,
795     gd_region_add_rect,
796     gd_region_to_rect,
797     gd_free_region,
798     gd_dump_region,
799   };
800
801 /* Functions to be stored in MDeviceLibraryInterface by dlsym ().  */
802
803 int
804 device_init ()
805 {
806   M_rgb = msymbol ("  rgb");
807   read_rgb_txt ();
808   realized_fontset_list = mplist ();
809   realized_font_list = mplist ();
810   realized_face_list = mplist ();  
811   scratch_images[0] = scratch_images[1] = NULL;
812
813   gd_font_driver.select = mfont__ft_driver.select;
814   gd_font_driver.find_metric = mfont__ft_driver.find_metric;
815   gd_font_driver.has_char = mfont__ft_driver.has_char;
816   gd_font_driver.encode_char = mfont__ft_driver.encode_char;
817   gd_font_driver.list = mfont__ft_driver.list;
818   gd_font_driver.check_otf = mfont__ft_driver.check_otf;
819   gd_font_driver.drive_otf = mfont__ft_driver.drive_otf;
820
821   return 0;
822 }
823
824 int
825 device_fini ()
826 {
827   MPlist *plist;
828   int i;
829
830   MPLIST_DO (plist, realized_fontset_list)
831     mfont__free_realized_fontset ((MRealizedFontset *) MPLIST_VAL (plist));
832   M17N_OBJECT_UNREF (realized_fontset_list);
833
834   MPLIST_DO (plist, realized_face_list)
835     {
836       MRealizedFace *rface = MPLIST_VAL (plist);
837
838       free (rface->info);
839       mface__free_realized (rface);
840     }
841   M17N_OBJECT_UNREF (realized_face_list);
842
843   if (MPLIST_VAL (realized_font_list))
844     mfont__free_realized (MPLIST_VAL (realized_font_list));
845   M17N_OBJECT_UNREF (realized_font_list);
846
847   for (i = 0; i < 2; i++)
848     if (scratch_images[i])
849       gdImageDestroy (scratch_images[i]);
850   return 0;
851 }
852
853 int
854 device_open (MFrame *frame, MPlist *param)
855 {
856   MFace *face;
857
858   frame->device = NULL;
859   frame->device_type = MDEVICE_SUPPORT_OUTPUT;
860   frame->dpi = (int) mplist_get (param, Mresolution);
861   if (frame->dpi == 0)
862     frame->dpi = 100;
863   frame->driver = &gd_driver;
864   frame->font_driver_list = mplist ();
865   mplist_add (frame->font_driver_list, Mfreetype, &gd_font_driver);
866   frame->realized_font_list = realized_font_list;
867   frame->realized_face_list = realized_face_list;
868   frame->realized_fontset_list = realized_fontset_list;
869   face = mface_copy (mface__default);
870   mface_put_prop (face, Mfoundry, Mnil);
871   mface_put_prop (face, Mfamily, Mnil);
872   mplist_push (param, Mface, face);
873   M17N_OBJECT_UNREF (face);
874   return 0;
875 }
876
877 #else   /* not HAVE_GD nor HAVE_FREETYPE */
878
879 int device_open () { return -1; }
880
881 #endif  /* not HAVE_GD nor HAVE_FREETYPE */
882
883 /*** @} */
884 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */