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