New file.
authorhanda <handa>
Thu, 27 May 2004 10:57:20 +0000 (10:57 +0000)
committerhanda <handa>
Thu, 27 May 2004 10:57:20 +0000 (10:57 +0000)
src/m17n-gd.c [new file with mode: 0644]

diff --git a/src/m17n-gd.c b/src/m17n-gd.c
new file mode 100644 (file)
index 0000000..bd6f72d
--- /dev/null
@@ -0,0 +1,805 @@
+/* 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 */