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