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