--- /dev/null
+/* m17n-gd.c -- implementation of the GUI API on GD Library.
+ Copyright (C) 2004
+ National Institute of Advanced Industrial Science and Technology (AIST)
+ Registration Number H15PRO112
+
+ This file is part of the m17n library.
+
+ The m17n library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public License
+ as published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ The m17n library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the m17n library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE)
+/*** @addtogroup m17nInternal
+ @{ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <gd.h>
+
+#include "m17n-gui.h"
+#include "m17n-misc.h"
+#include "internal.h"
+#include "internal-gui.h"
+#include "symbol.h"
+#include "font.h"
+#include "fontset.h"
+#include "face.h"
+
+#if defined (HAVE_FREETYPE) && defined (HAVE_GD)
+
+static MPlist *realized_fontset_list;
+static MPlist *realized_font_list;
+static MPlist *realized_face_list;
+
+/* The first element is for 256 color, the second for true color. */
+static gdImagePtr scratch_images[2];
+
+enum ColorIndex
+ {
+ COLOR_NORMAL,
+ COLOR_INVERSE,
+ COLOR_HLINE,
+ COLOR_BOX_TOP,
+ COLOR_BOX_BOTTOM,
+ COLOR_BOX_LEFT,
+ COLOR_BOX_RIGHT,
+ COLOR_MAX
+ };
+
+static MSymbol M_rgb;
+
+static void
+read_rgb_txt ()
+{
+ FILE *fp = fopen ("/usr/lib/X11/rgb.txt", "r");
+ int r, g, b;
+
+ if (! fp)
+ fp = fopen ("/usr/X11R6/lib/X11/rgb.txt", "r");
+ if (! fp)
+ return;
+ while (1)
+ {
+ char buf[256];
+ int c, len;
+
+ if ((c = getc (fp)) == EOF)
+ break;
+ if (c == '!')
+ {
+ while ((c = getc (fp)) != EOF && c != '\n');
+ continue;
+ }
+ ungetc (c, fp);
+ if (fscanf (fp, "%d %d %d", &r, &g, &b) != 3)
+ break;
+ while ((c = getc (fp)) != EOF && isspace (c));
+ if (c == EOF)
+ break;
+ buf[0] = c;
+ fgets (buf + 1, 255, fp);
+ len = strlen (buf);
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+ b |= (r << 16) | (g << 8);
+ msymbol_put (msymbol (buf), M_rgb, (void *) b);
+ }
+ fclose (fp);
+}
+
+
+static int
+parse_color (MSymbol sym)
+{
+ char *name = MSYMBOL_NAME (sym);
+ unsigned r = 0x80, g = 0x80, b = 0x80;
+ int i;
+
+ do {
+ if (strncmp (name , "rgb:", 4) == 0)
+ {
+ name += 4;
+ if (sscanf (name, "%x", &r) < 1)
+ break;
+ for (i = 0; *name != '/'; i++, name++);
+ r = (i == 1 ? ((r << 1) | r) : (r >> (i - 2)));
+ name++;
+ if (sscanf (name, "%x", &g) < 1)
+ break;
+ for (i = 0; *name != '/'; i++, name++);
+ g = (i == 1 ? ((g << 1) | g) : (g >> (i - 2)));
+ name += 4;
+ if (sscanf (name, "%x", &b) < 1)
+ break;
+ for (i = 0; *name; i++, name++);
+ b = (i == 1 ? ((b << 1) | b) : (b >> (i - 2)));
+ }
+ else if (*name == '#')
+ {
+ name++;
+ i = strlen (name);
+ if (i == 3)
+ {
+ if (sscanf (name, "%1x%1x%1x", &r, &g, &b) < 3)
+ break;
+ r <<= 4, g <<= 4, b <<= 4;
+ }
+ else if (i == 6)
+ {
+ if (sscanf (name, "%2x%2x%2x", &r, &g, &b) < 3)
+ break;
+ }
+ else if (i == 9)
+ {
+ if (sscanf (name, "%3x%3x%3x", &r, &g, &b) < 3)
+ break;
+ r >>= 1, g >>= 1, b >>= 1;
+ }
+ else if (i == 12)
+ {
+ if (sscanf (name, "%4x%4x%4x", &r, &g, &b) < 3)
+ break;
+ r >>= 2, g >>= 2, b >>= 2;
+ }
+ }
+ else
+ return (int) msymbol_get (sym, M_rgb);
+ } while (0);
+
+ return ((r << 16) | (g << 8) | b);
+}
+
+
+static gdImagePtr
+get_scrach_image (gdImagePtr img, int width, int height)
+{
+ int index;
+ gdImagePtr scratch;
+
+#if HAVE_GD == 1
+ index = 0;
+#else
+ index = img->trueColor ? 1 : 0;
+#endif
+ scratch = scratch_images[index];
+
+ if (scratch)
+ {
+ if (scratch->sx <= width && scratch->sy <= height)
+ return scratch;
+ gdImageDestroy (scratch);
+ }
+#if HAVE_GD > 1
+ if (img->trueColor)
+ scratch = scratch_images[1] = gdImageCreateTrueColor (width, height);
+ else
+#endif
+ scratch = scratch_images[0] = gdImageCreate (width, height);
+ return scratch;
+}
+
+static int
+intersect_rectangle (MDrawMetric *r1, MDrawMetric *r2, MDrawMetric *rect)
+{
+ *rect = *r1;
+ if (rect->x < r2->x)
+ rect->width -= (r2->x - rect->x), rect->x = r2->x;
+ if (rect->x + rect->width > r2->x + r2->width)
+ rect->width -= (r2->x + r2->width - rect->x - rect->width);
+ if (rect->y < r2->y)
+ rect->height -= (r2->y - rect->y), rect->y = r2->y;
+ if (rect->y + rect->height > r2->y + r2->height)
+ rect->height -= (r2->y + r2->height - rect->y - rect->height);
+ return 1;
+}
+
+
+#define INTERSECT_RECTANGLE(r1, r2, rect) \
+ (((r1)->x + (r1->width) <= (r2)->x \
+ || (r2)->x + (r2)->width <= (r1)->x \
+ || (r1)->y + (r1->height) <= (r2)->y \
+ || (r2)->y + (r2)->height <= (r1)->y) \
+ ? 0 \
+ : intersect_rectangle (r1, r2, rect))
+
+
+#define RESOLVE_COLOR(img, color) \
+ gdImageColorResolve ((img), (color) >> 16, ((color) >> 8) & 0xFF, \
+ (color) & 0xFF)
+
+
+static void
+gd_close (MFrame *frame)
+{
+}
+
+static void *
+gd_get_prop (MFrame *frame, MSymbol key)
+{
+ return NULL;
+}
+
+static void
+gd_realize_face (MRealizedFace *rface)
+{
+ int *colors;
+ MFaceHLineProp *hline;
+ MFaceBoxProp *box;
+ MSymbol *props = (MSymbol *) rface->face.property;
+
+ if (rface != rface->ascii_rface)
+ {
+ rface->info = rface->ascii_rface->info;
+ return;
+ }
+ colors = malloc (sizeof (int) * COLOR_MAX);
+ colors[COLOR_NORMAL] = parse_color (props[MFACE_FOREGROUND]);
+ colors[COLOR_INVERSE] = parse_color (props[MFACE_BACKGROUND]);
+ if (rface->face.property[MFACE_VIDEOMODE] == Mreverse)
+ {
+ colors[COLOR_HLINE] = colors[COLOR_NORMAL];
+ colors[COLOR_NORMAL] = colors[COLOR_INVERSE];
+ colors[COLOR_INVERSE] = colors[COLOR_HLINE];
+ }
+ colors[COLOR_HLINE] = 0;
+
+ hline = rface->hline;
+ if (hline)
+ {
+ if (hline->color)
+ colors[COLOR_HLINE] = parse_color (hline->color);
+ else
+ colors[COLOR_HLINE] = colors[COLOR_NORMAL];
+ }
+
+ box = rface->box;
+ if (box)
+ {
+ if (box->color_top)
+ colors[COLOR_BOX_TOP] = parse_color (box->color_top);
+ else
+ colors[COLOR_BOX_TOP] = colors[COLOR_NORMAL];
+
+ if (box->color_left && box->color_left != box->color_top)
+ colors[COLOR_BOX_LEFT] = parse_color (box->color_left);
+ else
+ colors[COLOR_BOX_LEFT] = colors[COLOR_BOX_TOP];
+
+ if (box->color_bottom && box->color_bottom != box->color_top)
+ colors[COLOR_BOX_BOTTOM] = parse_color (box->color_bottom);
+ else
+ colors[COLOR_BOX_BOTTOM] = colors[COLOR_BOX_TOP];
+
+ if (box->color_right && box->color_right != box->color_bottom)
+ colors[COLOR_BOX_RIGHT] = parse_color (box->color_right);
+ else
+ colors[COLOR_BOX_RIGHT] = colors[COLOR_BOX_BOTTOM];
+ }
+
+ rface->info = colors;
+}
+
+static void
+gd_free_realized_face (MRealizedFace *rface)
+{
+ free (rface->info);
+}
+
+static void
+gd_fill_space (MFrame *frame, MDrawWindow win, MRealizedFace *rface,
+ int reverse,
+ int x, int y, int width, int height, MDrawRegion region)
+{
+ gdImagePtr img = (gdImagePtr) win;
+ int *colors = rface->info;
+ int color = colors[reverse ? COLOR_NORMAL : COLOR_INVERSE];
+ MPlist *region_list = region, *plist;
+
+ color = RESOLVE_COLOR (img, color);
+ if (! region)
+ gdImageFilledRectangle (img, x, y, x + width - 1, y + height - 1, color);
+ else
+ {
+ MDrawMetric rect;;
+
+ rect.x = x, rect.y = y, rect.width = width, rect.height = height;
+ MPLIST_DO (plist, region_list)
+ {
+ MDrawMetric *r = MPLIST_VAL (plist), new;
+
+ if (INTERSECT_RECTANGLE (r, &rect, &new))
+ gdImageFilledRectangle (img, new.x, new.y, new.x + new.width - 1,
+ new.y + new.height - 1, color);
+ }
+ }
+}
+
+static void
+gd_draw_empty_boxes (MDrawWindow win, int x, int y,
+ MGlyphString *gstring, MGlyph *from, MGlyph *to,
+ int reverse, MDrawRegion region)
+{
+ gdImagePtr img = (gdImagePtr) win;
+ int *colors = from->rface->info;
+ int color = colors[reverse ? COLOR_INVERSE : COLOR_NORMAL];
+ MPlist *region_list = region, *plist;
+ int height;
+
+ if (from == to)
+ return;
+
+ color = RESOLVE_COLOR (img, color);
+ y -= gstring->ascent - 1;
+ height = gstring->ascent + gstring->descent - 2;
+ if (! region)
+ for (; from < to; x += from++->width)
+ gdImageRectangle (img, x, y, x + from->width - 2, y + height - 1, color);
+ else
+ {
+ gdImagePtr cpy;
+ MGlyph *g;
+ int width, x1;
+
+ for (g = from, width = 0; g < to; width += g++->width);
+ cpy = get_scrach_image (img, width, height);
+ MPLIST_DO (plist, region_list)
+ {
+ MDrawMetric *rect = MPLIST_VAL (plist);
+ gdImageCopy (cpy, img, rect->x - x, rect->y - y, rect->x, rect->y,
+ rect->x + rect->width, rect->y + rect->height);
+ }
+ for (x1 = 0; from < to; x1 += from++->width)
+ gdImageRectangle (cpy, x1, 0, x1 + from->width - 2, height - 1, color);
+ MPLIST_DO (plist, region_list)
+ {
+ MDrawMetric *rect = MPLIST_VAL (plist);
+ gdImageCopy (img, cpy, rect->x, rect->y, rect->x - x, rect->y - y,
+ rect->x + rect->width, rect->y + rect->height);
+ }
+ }
+}
+
+
+static void
+gd_draw_hline (MFrame *frame, MDrawWindow win, MGlyphString *gstring,
+ MRealizedFace *rface, int reverse,
+ int x, int y, int width, MDrawRegion region)
+{
+ enum MFaceHLineType type = rface->hline->type;
+ int height = rface->hline->width;
+ gdImagePtr img = (gdImagePtr) win;
+ int *colors = rface->info;
+ int color = colors[COLOR_HLINE];
+ MPlist *region_list = region, *plist;
+
+ color = RESOLVE_COLOR (img, color);
+ y = (type == MFACE_HLINE_BOTTOM
+ ? y + gstring->text_descent - height
+ : type == MFACE_HLINE_UNDER
+ ? y + 1
+ : type == MFACE_HLINE_STRIKE_THROUGH
+ ? y - ((gstring->ascent + gstring->descent) / 2)
+ : y - gstring->text_ascent);
+ if (! region)
+ gdImageFilledRectangle (img, x, y, x + width - 1, y + height - 1, color);
+ else
+ {
+ MDrawMetric rect;
+
+ rect.x = x, rect.y = y, rect.width = width, rect.height = height;
+ MPLIST_DO (plist, region_list)
+ {
+ MDrawMetric *r = MPLIST_VAL (plist), new;
+
+ if (INTERSECT_RECTANGLE (r, &rect, &new))
+ gdImageFilledRectangle (img, new.x, new.y, new.x + new.width - 1,
+ new.y + new.height - 1, color);
+ }
+ }
+}
+
+
+static void
+gd_draw_box (MFrame *frame, MDrawWindow win, MGlyphString *gstring,
+ MGlyph *g, int x, int y, int width, MDrawRegion region)
+{
+ gdImagePtr img = (gdImagePtr) win;
+ int *colors = g->rface->info;
+ int color;
+ MRealizedFace *rface = g->rface;
+ MFaceBoxProp *box = rface->box;
+ MPlist *region_list = region, *plist;
+ int y0, y1;
+ int i;
+
+ y0 = y - (gstring->text_ascent
+ + rface->box->inner_vmargin + rface->box->width);
+ y1 = y + (gstring->text_descent
+ + rface->box->inner_vmargin + rface->box->width - 1);
+
+ if (region)
+ {
+ int height = y1 - y0;
+ gdImagePtr cpy;
+
+ if (g->type == GLYPH_BOX)
+ width = g->width;
+ cpy = get_scrach_image (img, width, height);
+ MPLIST_DO (plist, region_list)
+ {
+ MDrawMetric *rect = MPLIST_VAL (plist);
+ gdImageCopy (cpy, img, rect->x - x, rect->y - y, rect->x, rect->y,
+ rect->x + rect->width, rect->y + rect->height);
+ }
+ gd_draw_box (frame, win, gstring, g, 0, y - y0, width, NULL);
+ MPLIST_DO (plist, region_list)
+ {
+ MDrawMetric *rect = MPLIST_VAL (plist);
+ gdImageCopy (img, cpy, rect->x, rect->y, rect->x - x, rect->y - y,
+ rect->x + rect->width, rect->y + rect->height);
+ }
+ return;
+ }
+
+ if (g->type == GLYPH_BOX)
+ {
+ int x0, x1;
+
+ if (g->left_padding)
+ x0 = x + box->outer_hmargin, x1 = x + g->width - 1;
+ else
+ x0 = x, x1 = x + g->width - box->outer_hmargin - 1;
+
+ /* Draw the top side. */
+ color = RESOLVE_COLOR (img, colors[COLOR_BOX_TOP]);
+ for (i = 0; i < box->width; i++)
+ gdImageLine (img, x0, y0 + i, x1, y0 + i, color);
+
+ /* Draw the bottom side. */
+ color = RESOLVE_COLOR (img, colors[COLOR_BOX_BOTTOM]);
+ for (i = 0; i < box->width; i++)
+ gdImageLine (img, x0, y1 - i, x1, y1 - i, color);
+
+ if (g->left_padding > 0)
+ {
+ /* Draw the left side. */
+ color = RESOLVE_COLOR (img, colors[COLOR_BOX_LEFT]);
+ for (i = 0; i < rface->box->width; i++)
+ gdImageLine (img, x0 + i, y0 + i, x0 + i, y1 - i, color);
+ }
+ else
+ {
+ /* Draw the right side. */
+ color = RESOLVE_COLOR (img, colors[COLOR_BOX_RIGHT]);
+ for (i = 0; i < rface->box->width; i++)
+ gdImageLine (img, x1 - i, y0 + i, x1 - i, y1 - i, color);
+ }
+
+ }
+ else
+ {
+ /* Draw the top side. */
+ color = RESOLVE_COLOR (img, colors[COLOR_BOX_TOP]);
+ for (i = 0; i < box->width; i++)
+ gdImageLine (img, x, y0 + i, x + width - 1, y0 + i, color);
+
+ /* Draw the bottom side. */
+ color = RESOLVE_COLOR (img, colors[COLOR_BOX_BOTTOM]);
+ for (i = 0; i < box->width; i++)
+ gdImageLine (img, x, y1 - i, x + width - 1, y1 - i, color);
+ }
+}
+
+
+static MDrawRegion
+gd_region_from_rect (MDrawMetric *rect)
+{
+ MDrawMetric *new;
+ MPlist *plist = mplist ();
+
+ MSTRUCT_MALLOC (new, MERROR_GD);
+ *new = *rect;
+ mplist_add (plist, Mt, new);
+ return (MDrawRegion) plist;
+}
+
+static void
+gd_union_rect_with_region (MDrawRegion region, MDrawMetric *rect)
+{
+ MPlist *plist = (MPlist *) region;
+ MDrawMetric *r;
+
+ MSTRUCT_MALLOC (r, MERROR_GD);
+ *r = *rect;
+ mplist_push (plist, Mt, r);
+}
+
+static void
+gd_intersect_region (MDrawRegion region1, MDrawRegion region2)
+{
+ MPlist *plist1 = (MPlist *) region1, *p1 = plist1;
+ MPlist *plist2 = (MPlist *) region2;
+ MPlist *p2;
+ MDrawMetric rect, *rect1, *rect2, *r;
+
+ while (! MPLIST_TAIL_P (p1))
+ {
+ rect1 = mplist_pop (p1);
+ MPLIST_DO (p2, plist2)
+ {
+ rect2 = MPLIST_VAL (p2);
+ if (INTERSECT_RECTANGLE (rect1, rect2, &rect))
+ {
+ MSTRUCT_MALLOC (r, MERROR_GD);
+ *r = rect;
+ mplist_push (p1, Mt, r);
+ p1 = MPLIST_NEXT (p1);
+ }
+ }
+ free (rect1);
+ }
+}
+
+static void
+gd_region_add_rect (MDrawRegion region, MDrawMetric *rect)
+{
+ MPlist *plist = (MPlist *) region;
+ MDrawMetric *new;
+
+ MSTRUCT_MALLOC (new, MERROR_GD);
+ *new = *rect;
+ mplist_push (plist, Mt, new);
+}
+
+static void
+gd_region_to_rect (MDrawRegion region, MDrawMetric *rect)
+{
+ MPlist *plist = (MPlist *) region;
+ MDrawMetric *r = MPLIST_VAL (plist);
+ int min_x = r->x, max_x = min_x + r->width;
+ int min_y = r->y, max_y = min_y + r->height;
+
+ MPLIST_DO (plist, MPLIST_NEXT (plist))
+ {
+ r = MPLIST_VAL (plist);
+ if (r->x < min_x)
+ min_x = r->x;
+ if (r->x + r->width > max_x)
+ max_x = r->x + r->width;
+ if (r->y < min_y)
+ min_y = r->y;
+ if (r->y + r->height > max_y)
+ max_y = r->y + r->height;
+ }
+ rect->x = min_x;
+ rect->y = min_y;
+ rect->width = max_x - min_x;
+ rect->height =max_y - min_y;
+}
+
+static void
+gd_free_region (MDrawRegion region)
+{
+ MPlist *plist = (MPlist *) region;
+
+ MPLIST_DO (plist, plist)
+ free (MPLIST_VAL (plist));
+ M17N_OBJECT_UNREF (region);
+}
+
+static void
+gd_dump_region (MDrawRegion region)
+{
+ MDrawMetric rect;
+
+ gd_region_to_rect (region, &rect);
+ fprintf (stderr, "(%d %d %d %d)\n", rect.x, rect.y, rect.width, rect.height);
+}
+
+static MDeviceDriver gd_driver =
+ {
+ gd_close,
+ gd_get_prop,
+ gd_realize_face,
+ gd_free_realized_face,
+ gd_fill_space,
+ gd_draw_empty_boxes,
+ gd_draw_hline,
+ gd_draw_box,
+ NULL,
+ gd_region_from_rect,
+ gd_union_rect_with_region,
+ gd_intersect_region,
+ gd_region_add_rect,
+ gd_region_to_rect,
+ gd_free_region,
+ gd_dump_region,
+ };
+
+
+static void
+gd_render (MDrawWindow win, int x, int y,
+ MGlyphString *gstring, MGlyph *from, MGlyph *to,
+ int reverse, MDrawRegion region)
+{
+ gdImagePtr img = (gdImagePtr) win;
+ MFTInfo *ft_info;
+ FT_Face ft_face;
+ MRealizedFace *rface = from->rface;
+ FT_Int32 load_flags = FT_LOAD_RENDER;
+ int i, j;
+ int color, pixel;
+ int r, g, b;
+
+ pixel = RESOLVE_COLOR (img, color);
+
+ if (from == to)
+ return;
+
+ /* It is assured that the all glyphs in the current range use the
+ same realized face. */
+ ft_info = (MFTInfo *) rface->rfont->info;
+ ft_face = ft_info->ft_face;
+ color = ((int *) rface->info)[reverse ? COLOR_INVERSE : COLOR_NORMAL];
+ pixel = RESOLVE_COLOR (img, color);
+
+ if (gstring->anti_alias)
+ r = color >> 16, g = (color >> 8) & 0xFF, b = color & 0xFF;
+ else
+ {
+#ifdef FT_LOAD_TARGET_MONO
+ load_flags |= FT_LOAD_TARGET_MONO;
+#else
+ load_flags |= FT_LOAD_MONOCHROME;
+#endif
+ }
+
+ for (; from < to; x += from++->width)
+ {
+ FT_UInt code;
+ unsigned char *bmp;
+ int xoff, yoff;
+ int width, pitch;
+
+ if (from->otf_encoded)
+ code = from->code;
+ else
+ code = FT_Get_Char_Index (ft_face, (FT_ULong) from->code);
+ FT_Load_Glyph (ft_face, code, load_flags);
+ yoff = y - ft_face->glyph->bitmap_top + from->yoff;
+ bmp = ft_face->glyph->bitmap.buffer;
+ width = ft_face->glyph->bitmap.width;
+ pitch = ft_face->glyph->bitmap.pitch;
+ if (! gstring->anti_alias)
+ pitch *= 8;
+ if (width > pitch)
+ width = pitch;
+
+ if (gstring->anti_alias)
+ for (i = 0; i < ft_face->glyph->bitmap.rows;
+ i++, bmp += ft_face->glyph->bitmap.pitch, yoff++)
+ {
+ xoff = x + ft_face->glyph->bitmap_left + from->xoff;
+ for (j = 0; j < width; j++, xoff++)
+ if (bmp[j])
+ {
+ int f = bmp[j] >> 5;
+ int pixel1 = pixel;
+
+ if (f < 7)
+ {
+ int r1, g1, b1, color1;
+
+ pixel1 = gdImageGetPixel (img, xoff, yoff);
+ r1 = gdImageRed (img, pixel1);
+ g1 = gdImageGreen (img, pixel1);
+ b1 = gdImageBlue (img, pixel1);
+ color1 = ((((r * f + r1 * (7 - f)) / 7) << 16)
+ | (((g * f + g1 * (7 - f)) / 7) << 8)
+ | ((b * f + b1 * (7 - f)) / 7));
+ pixel1 = RESOLVE_COLOR (img, color1);
+ }
+ gdImageSetPixel (img, xoff, yoff, pixel1);
+ }
+ }
+ else
+ for (i = 0; i < ft_face->glyph->bitmap.rows;
+ i++, bmp += ft_face->glyph->bitmap.pitch, yoff++)
+ {
+ xoff = x + ft_face->glyph->bitmap_left + from->xoff;
+ for (j = 0; j < width; j++, xoff++)
+ if (bmp[j / 8] & (1 << (7 - (j % 8))))
+ gdImageSetPixel (img, xoff, yoff, pixel);
+ }
+ }
+}
+
+
+static MFontDriver gd_font_driver =
+ { NULL, NULL, NULL, NULL, gd_render };
+
+int
+device_init ()
+{
+ M_rgb = msymbol (" rgb");
+ read_rgb_txt ();
+ realized_fontset_list = mplist ();
+ realized_font_list = mplist ();
+ realized_face_list = mplist ();
+ scratch_images[0] = scratch_images[1] = NULL;
+
+ gd_font_driver.select = mfont__ft_driver.select;
+ gd_font_driver.open = mfont__ft_driver.open;
+ gd_font_driver.find_metric = mfont__ft_driver.find_metric;
+ gd_font_driver.encode_char = mfont__ft_driver.encode_char;
+
+ return 0;
+}
+
+int
+device_fini ()
+{
+ MPlist *plist;
+ int i;
+
+ MPLIST_DO (plist, realized_fontset_list)
+ mfont__free_realized_fontset ((MRealizedFontset *) MPLIST_VAL (plist));
+ M17N_OBJECT_UNREF (realized_fontset_list);
+
+ MPLIST_DO (plist, realized_face_list)
+ {
+ MRealizedFace *rface = MPLIST_VAL (plist);
+
+ free (rface->info);
+ mface__free_realized (rface);
+ }
+ M17N_OBJECT_UNREF (realized_face_list);
+
+ MPLIST_DO (plist, realized_font_list)
+ mfont__free_realized ((MRealizedFont *) MPLIST_VAL (plist));
+ M17N_OBJECT_UNREF (realized_font_list);
+
+ for (i = 0; i < 2; i++)
+ if (scratch_images[i])
+ gdImageDestroy (scratch_images[i]);
+ return 0;
+}
+
+void *
+device_open (MFrame *frame, MPlist *param)
+{
+ MFace *face;
+
+ frame->device_type = MDEVICE_SUPPORT_OUTPUT;
+ frame->driver = &gd_driver;
+ frame->font_driver_list = mplist ();
+ mplist_add (frame->font_driver_list, Mfreetype, &gd_font_driver);
+ frame->realized_font_list = realized_font_list;
+ frame->realized_face_list = realized_face_list;
+ frame->realized_fontset_list = realized_fontset_list;
+ face = mface_copy (mface__default);
+ mplist_push (param, Mface, face);
+ M17N_OBJECT_UNREF (face);
+ return Mt;
+}
+
+#endif /* not HAVE_GD nor HAVE_FREETYPE */
+
+/*** @} */
+#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */