1 /* m17n-gd.c -- implementation of the GUI API on GD Library.
2 Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010
3 National Institute of Advanced Industrial Science and Technology (AIST)
4 Registration Number H15PRO112
6 This file is part of the m17n library.
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.
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.
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,
23 #if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE)
24 /*** @addtogroup m17nInternal
29 #if defined (HAVE_FREETYPE) && defined (HAVE_GD)
38 #include "m17n-misc.h"
40 #include "internal-gui.h"
46 static MPlist *realized_fontset_list;
47 static MPlist *realized_font_list;
48 static MPlist *realized_face_list;
50 /* The first element is for 256 color, the second for true color. */
51 static gdImagePtr scratch_images[2];
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);
93 = {"/usr/lib/X11/rgb.txt", "/usr/X11R6/lib/X11/rgb.txt",
97 for (i = 0; i < (sizeof rgb_path) / (sizeof rgb_path[0]); i++)
98 if ((fp = fopen ("/usr/lib/X11/rgb.txt", "r")))
108 if ((c = getc (fp)) == EOF)
112 while ((c = getc (fp)) != EOF && c != '\n');
116 if (fscanf (fp, "%d %d %d", &r, &g, &b) != 3)
118 while ((c = getc (fp)) != EOF && isspace (c));
122 fgets (buf + 1, 255, fp);
124 for (i = 0; i < len; i++)
125 buf[i] = tolower (buf[i]);
126 if (buf[len - 1] == '\n')
128 b |= (r << 16) | (g << 8);
129 msymbol_put (msymbol (buf), M_rgb, (void *) b);
136 parse_color (MSymbol sym)
138 char *name = MSYMBOL_NAME (sym);
139 unsigned r = 0x80, g = 0x80, b = 0x80;
143 if (strncmp (name , "rgb:", 4) == 0)
146 if (sscanf (name, "%x", &r) < 1)
148 for (i = 0; *name != '/'; i++, name++);
149 r = (i == 1 ? ((r << 1) | r) : (r >> (i - 2)));
151 if (sscanf (name, "%x", &g) < 1)
153 for (i = 0; *name != '/'; i++, name++);
154 g = (i == 1 ? ((g << 1) | g) : (g >> (i - 2)));
156 if (sscanf (name, "%x", &b) < 1)
158 for (i = 0; *name; i++, name++);
159 b = (i == 1 ? ((b << 1) | b) : (b >> (i - 2)));
161 else if (*name == '#')
167 if (sscanf (name, "%1x%1x%1x", &r, &g, &b) < 3)
169 r <<= 4, g <<= 4, b <<= 4;
173 if (sscanf (name, "%2x%2x%2x", &r, &g, &b) < 3)
178 if (sscanf (name, "%3x%3x%3x", &r, &g, &b) < 3)
180 r >>= 1, g >>= 1, b >>= 1;
184 if (sscanf (name, "%4x%4x%4x", &r, &g, &b) < 3)
186 r >>= 2, g >>= 2, b >>= 2;
190 return (int) msymbol_get (sym, M_rgb);
193 return ((r << 16) | (g << 8) | b);
198 get_scrach_image (gdImagePtr img, int width, int height)
206 index = img->trueColor ? 1 : 0;
208 scratch = scratch_images[index];
212 if (scratch->sx <= width && scratch->sy <= height)
214 gdImageDestroy (scratch);
218 scratch = scratch_images[1] = gdImageCreateTrueColor (width, height);
221 scratch = scratch_images[0] = gdImageCreate (width, height);
226 intersect_rectangle (MDrawMetric *r1, MDrawMetric *r2, MDrawMetric *rect)
230 rect->width -= (r2->x - rect->x), rect->x = r2->x;
231 if (rect->x + rect->width > r2->x + r2->width)
232 rect->width -= (r2->x + r2->width - rect->x - rect->width);
234 rect->height -= (r2->y - rect->y), rect->y = r2->y;
235 if (rect->y + rect->height > r2->y + r2->height)
236 rect->height -= (r2->y + r2->height - rect->y - rect->height);
241 #define INTERSECT_RECTANGLE(r1, r2, rect) \
242 (((r1)->x + (r1->width) <= (r2)->x \
243 || (r2)->x + (r2)->width <= (r1)->x \
244 || (r1)->y + (r1->height) <= (r2)->y \
245 || (r2)->y + (r2)->height <= (r1)->y) \
247 : intersect_rectangle (r1, r2, rect))
250 #define RESOLVE_COLOR(img, color) \
251 gdImageColorResolve ((img), (color) >> 16, ((color) >> 8) & 0xFF, \
254 static MRealizedFont *gd_font_open (MFrame *, MFont *, MFont *,
256 static void gd_render (MDrawWindow, int, int, MGlyphString *,
257 MGlyph *, MGlyph *, int, MDrawRegion);
259 static MFontDriver gd_font_driver =
260 { NULL, gd_font_open, NULL, NULL, NULL, gd_render, NULL };
262 static MRealizedFont *
263 gd_font_open (MFrame *frame, MFont *font, MFont *spec, MRealizedFont *rfont)
265 double size = font->size ? font->size : spec->size;
266 int reg = spec->property[MFONT_REGISTRY];
271 MRealizedFont *save = NULL;
273 for (; rfont; rfont = rfont->next)
274 if (rfont->font == font
275 && (rfont->font->size ? rfont->font->size == size
276 : rfont->spec.size == size)
277 && rfont->spec.property[MFONT_REGISTRY] == reg)
281 if (rfont->driver == &gd_font_driver)
286 rfont = (mfont__ft_driver.open) (frame, font, spec, rfont);
289 M17N_OBJECT_REF (rfont->info);
290 MSTRUCT_CALLOC (new, MERROR_GD);
292 new->driver = &gd_font_driver;
293 new->next = MPLIST_VAL (frame->realized_font_list);
294 MPLIST_VAL (frame->realized_font_list) = new;
299 gd_render (MDrawWindow win, int x, int y,
300 MGlyphString *gstring, MGlyph *from, MGlyph *to,
301 int reverse, MDrawRegion region)
303 gdImagePtr img = (gdImagePtr) win;
305 MRealizedFace *rface = from->rface;
306 FT_Int32 load_flags = FT_LOAD_RENDER;
314 /* It is assured that the all glyphs in the current range use the
315 same realized face. */
316 ft_face = rface->rfont->fontp;
317 color = ((int *) rface->info)[reverse ? COLOR_INVERSE : COLOR_NORMAL];
318 pixel = RESOLVE_COLOR (img, color);
320 if (gstring->anti_alias)
321 r = color >> 16, g = (color >> 8) & 0xFF, b = color & 0xFF;
324 #ifdef FT_LOAD_TARGET_MONO
325 load_flags |= FT_LOAD_TARGET_MONO;
327 load_flags |= FT_LOAD_MONOCHROME;
331 for (; from < to; x += from++->g.xadv)
337 FT_Load_Glyph (ft_face, (FT_UInt) from->g.code, load_flags);
338 yoff = y - ft_face->glyph->bitmap_top + from->g.yoff;
339 bmp = ft_face->glyph->bitmap.buffer;
340 width = ft_face->glyph->bitmap.width;
341 pitch = ft_face->glyph->bitmap.pitch;
342 if (! gstring->anti_alias)
347 if (gstring->anti_alias)
348 for (i = 0; i < ft_face->glyph->bitmap.rows;
349 i++, bmp += ft_face->glyph->bitmap.pitch, yoff++)
351 xoff = x + ft_face->glyph->bitmap_left + from->g.xoff;
352 for (j = 0; j < width; j++, xoff++)
357 int alpha = gdAlphaTransparent * (255 - bmp[j]) / 255;
360 pixel1 = gdImageColorResolveAlpha (img, r, g, b, alpha);
366 int r1, g1, b1, color1;
368 pixel1 = gdImageGetPixel (img, xoff, yoff);
369 r1 = gdImageRed (img, pixel1);
370 g1 = gdImageGreen (img, pixel1);
371 b1 = gdImageBlue (img, pixel1);
372 color1 = ((((r * f + r1 * (7 - f)) / 7) << 16)
373 | (((g * f + g1 * (7 - f)) / 7) << 8)
374 | ((b * f + b1 * (7 - f)) / 7));
375 pixel1 = RESOLVE_COLOR (img, color1);
378 gdImageSetPixel (img, xoff, yoff, pixel1);
382 for (i = 0; i < ft_face->glyph->bitmap.rows;
383 i++, bmp += ft_face->glyph->bitmap.pitch, yoff++)
385 xoff = x + ft_face->glyph->bitmap_left + from->g.xoff;
386 for (j = 0; j < width; j++, xoff++)
387 if (bmp[j / 8] & (1 << (7 - (j % 8))))
388 gdImageSetPixel (img, xoff, yoff, pixel);
394 gd_close (MFrame *frame)
399 gd_get_prop (MFrame *frame, MSymbol key)
405 gd_realize_face (MRealizedFace *rface)
408 MFaceHLineProp *hline;
410 MSymbol *props = (MSymbol *) rface->face.property;
412 if (rface != rface->ascii_rface)
414 rface->info = rface->ascii_rface->info;
417 colors = malloc (sizeof (int) * COLOR_MAX);
418 colors[COLOR_NORMAL] = parse_color (props[MFACE_FOREGROUND]);
419 colors[COLOR_INVERSE] = parse_color (props[MFACE_BACKGROUND]);
420 if (rface->face.property[MFACE_VIDEOMODE] == Mreverse)
422 colors[COLOR_HLINE] = colors[COLOR_NORMAL];
423 colors[COLOR_NORMAL] = colors[COLOR_INVERSE];
424 colors[COLOR_INVERSE] = colors[COLOR_HLINE];
426 colors[COLOR_HLINE] = 0;
428 hline = rface->hline;
432 colors[COLOR_HLINE] = parse_color (hline->color);
434 colors[COLOR_HLINE] = colors[COLOR_NORMAL];
441 colors[COLOR_BOX_TOP] = parse_color (box->color_top);
443 colors[COLOR_BOX_TOP] = colors[COLOR_NORMAL];
445 if (box->color_left && box->color_left != box->color_top)
446 colors[COLOR_BOX_LEFT] = parse_color (box->color_left);
448 colors[COLOR_BOX_LEFT] = colors[COLOR_BOX_TOP];
450 if (box->color_bottom && box->color_bottom != box->color_top)
451 colors[COLOR_BOX_BOTTOM] = parse_color (box->color_bottom);
453 colors[COLOR_BOX_BOTTOM] = colors[COLOR_BOX_TOP];
455 if (box->color_right && box->color_right != box->color_bottom)
456 colors[COLOR_BOX_RIGHT] = parse_color (box->color_right);
458 colors[COLOR_BOX_RIGHT] = colors[COLOR_BOX_BOTTOM];
461 rface->info = colors;
465 gd_free_realized_face (MRealizedFace *rface)
471 gd_fill_space (MFrame *frame, MDrawWindow win, MRealizedFace *rface,
473 int x, int y, int width, int height, MDrawRegion region)
475 gdImagePtr img = (gdImagePtr) win;
476 int *colors = rface->info;
477 int color = colors[reverse ? COLOR_NORMAL : COLOR_INVERSE];
478 MPlist *region_list = region, *plist;
480 color = RESOLVE_COLOR (img, color);
482 gdImageFilledRectangle (img, x, y, x + width - 1, y + height - 1, color);
487 rect.x = x, rect.y = y, rect.width = width, rect.height = height;
488 MPLIST_DO (plist, region_list)
490 MDrawMetric *r = MPLIST_VAL (plist), new;
492 if (INTERSECT_RECTANGLE (r, &rect, &new))
493 gdImageFilledRectangle (img, new.x, new.y, new.x + new.width - 1,
494 new.y + new.height - 1, color);
500 gd_draw_empty_boxes (MDrawWindow win, int x, int y,
501 MGlyphString *gstring, MGlyph *from, MGlyph *to,
502 int reverse, MDrawRegion region)
504 gdImagePtr img = (gdImagePtr) win;
505 int *colors = from->rface->info;
506 int color = colors[reverse ? COLOR_INVERSE : COLOR_NORMAL];
507 MPlist *region_list = region, *plist;
513 color = RESOLVE_COLOR (img, color);
514 y -= gstring->ascent - 1;
515 height = gstring->ascent + gstring->descent - 2;
517 for (; from < to; x += from++->g.xadv)
518 gdImageRectangle (img, x, y, x + from->g.xadv - 2, y + height - 1, color);
525 for (g = from, width = 0; g < to; width += g++->g.xadv);
526 cpy = get_scrach_image (img, width, height);
527 MPLIST_DO (plist, region_list)
529 MDrawMetric *rect = MPLIST_VAL (plist);
530 gdImageCopy (cpy, img, rect->x - x, rect->y - y, rect->x, rect->y,
531 rect->x + rect->width, rect->y + rect->height);
533 for (x1 = 0; from < to; x1 += from++->g.xadv)
534 gdImageRectangle (cpy, x1, 0, x1 + from->g.xadv - 2, height - 1, color);
535 MPLIST_DO (plist, region_list)
537 MDrawMetric *rect = MPLIST_VAL (plist);
538 gdImageCopy (img, cpy, rect->x, rect->y, rect->x - x, rect->y - y,
539 rect->x + rect->width, rect->y + rect->height);
546 gd_draw_hline (MFrame *frame, MDrawWindow win, MGlyphString *gstring,
547 MRealizedFace *rface, int reverse,
548 int x, int y, int width, MDrawRegion region)
550 enum MFaceHLineType type = rface->hline->type;
551 int height = rface->hline->width;
552 gdImagePtr img = (gdImagePtr) win;
553 int *colors = rface->info;
554 int color = colors[COLOR_HLINE];
555 MPlist *region_list = region, *plist;
557 color = RESOLVE_COLOR (img, color);
558 y = (type == MFACE_HLINE_BOTTOM
559 ? y + gstring->text_descent - height
560 : type == MFACE_HLINE_UNDER
562 : type == MFACE_HLINE_STRIKE_THROUGH
563 ? y - ((gstring->ascent + gstring->descent) / 2)
564 : y - gstring->text_ascent);
566 gdImageFilledRectangle (img, x, y, x + width - 1, y + height - 1, color);
571 rect.x = x, rect.y = y, rect.width = width, rect.height = height;
572 MPLIST_DO (plist, region_list)
574 MDrawMetric *r = MPLIST_VAL (plist), new;
576 if (INTERSECT_RECTANGLE (r, &rect, &new))
577 gdImageFilledRectangle (img, new.x, new.y, new.x + new.width - 1,
578 new.y + new.height - 1, color);
585 gd_draw_box (MFrame *frame, MDrawWindow win, MGlyphString *gstring,
586 MGlyph *g, int x, int y, int width, MDrawRegion region)
588 gdImagePtr img = (gdImagePtr) win;
589 int *colors = g->rface->info;
591 MRealizedFace *rface = g->rface;
592 MFaceBoxProp *box = rface->box;
593 MPlist *region_list = region, *plist;
597 y0 = y - (gstring->text_ascent
598 + rface->box->inner_vmargin + rface->box->width);
599 y1 = y + (gstring->text_descent
600 + rface->box->inner_vmargin + rface->box->width - 1);
604 int height = y1 - y0;
607 if (g->type == GLYPH_BOX)
609 cpy = get_scrach_image (img, width, height);
610 MPLIST_DO (plist, region_list)
612 MDrawMetric *rect = MPLIST_VAL (plist);
613 gdImageCopy (cpy, img, rect->x - x, rect->y - y, rect->x, rect->y,
614 rect->x + rect->width, rect->y + rect->height);
616 gd_draw_box (frame, win, gstring, g, 0, y - y0, width, NULL);
617 MPLIST_DO (plist, region_list)
619 MDrawMetric *rect = MPLIST_VAL (plist);
620 gdImageCopy (img, cpy, rect->x, rect->y, rect->x - x, rect->y - y,
621 rect->x + rect->width, rect->y + rect->height);
626 if (g->type == GLYPH_BOX)
631 x0 = x + box->outer_hmargin, x1 = x + g->g.xadv - 1;
633 x0 = x, x1 = x + g->g.xadv - box->outer_hmargin - 1;
635 /* Draw the top side. */
636 color = RESOLVE_COLOR (img, colors[COLOR_BOX_TOP]);
637 for (i = 0; i < box->width; i++)
638 gdImageLine (img, x0, y0 + i, x1, y0 + i, color);
640 /* Draw the bottom side. */
641 color = RESOLVE_COLOR (img, colors[COLOR_BOX_BOTTOM]);
642 for (i = 0; i < box->width; i++)
643 gdImageLine (img, x0, y1 - i, x1, y1 - i, color);
645 if (g->left_padding > 0)
647 /* Draw the left side. */
648 color = RESOLVE_COLOR (img, colors[COLOR_BOX_LEFT]);
649 for (i = 0; i < rface->box->width; i++)
650 gdImageLine (img, x0 + i, y0 + i, x0 + i, y1 - i, color);
654 /* Draw the right side. */
655 color = RESOLVE_COLOR (img, colors[COLOR_BOX_RIGHT]);
656 for (i = 0; i < rface->box->width; i++)
657 gdImageLine (img, x1 - i, y0 + i, x1 - i, y1 - i, color);
663 /* Draw the top side. */
664 color = RESOLVE_COLOR (img, colors[COLOR_BOX_TOP]);
665 for (i = 0; i < box->width; i++)
666 gdImageLine (img, x, y0 + i, x + width - 1, y0 + i, color);
668 /* Draw the bottom side. */
669 color = RESOLVE_COLOR (img, colors[COLOR_BOX_BOTTOM]);
670 for (i = 0; i < box->width; i++)
671 gdImageLine (img, x, y1 - i, x + width - 1, y1 - i, color);
677 gd_region_from_rect (MDrawMetric *rect)
680 MPlist *plist = mplist ();
682 MSTRUCT_MALLOC (new, MERROR_GD);
684 mplist_add (plist, Mt, new);
685 return (MDrawRegion) plist;
689 gd_union_rect_with_region (MDrawRegion region, MDrawMetric *rect)
691 MPlist *plist = (MPlist *) region;
694 MSTRUCT_MALLOC (r, MERROR_GD);
696 mplist_push (plist, Mt, r);
700 gd_intersect_region (MDrawRegion region1, MDrawRegion region2)
702 MPlist *plist1 = (MPlist *) region1, *p1 = plist1;
703 MPlist *plist2 = (MPlist *) region2;
705 MDrawMetric rect, *rect1, *rect2, *r;
707 while (! MPLIST_TAIL_P (p1))
709 rect1 = mplist_pop (p1);
710 MPLIST_DO (p2, plist2)
712 rect2 = MPLIST_VAL (p2);
713 if (INTERSECT_RECTANGLE (rect1, rect2, &rect))
715 MSTRUCT_MALLOC (r, MERROR_GD);
717 mplist_push (p1, Mt, r);
718 p1 = MPLIST_NEXT (p1);
726 gd_region_add_rect (MDrawRegion region, MDrawMetric *rect)
728 MPlist *plist = (MPlist *) region;
731 MSTRUCT_MALLOC (new, MERROR_GD);
733 mplist_push (plist, Mt, new);
737 gd_region_to_rect (MDrawRegion region, MDrawMetric *rect)
739 MPlist *plist = (MPlist *) region;
740 MDrawMetric *r = MPLIST_VAL (plist);
741 int min_x = r->x, max_x = min_x + r->width;
742 int min_y = r->y, max_y = min_y + r->height;
744 MPLIST_DO (plist, MPLIST_NEXT (plist))
746 r = MPLIST_VAL (plist);
749 if (r->x + r->width > max_x)
750 max_x = r->x + r->width;
753 if (r->y + r->height > max_y)
754 max_y = r->y + r->height;
758 rect->width = max_x - min_x;
759 rect->height =max_y - min_y;
763 gd_free_region (MDrawRegion region)
765 MPlist *plist = (MPlist *) region;
767 MPLIST_DO (plist, plist)
768 free (MPLIST_VAL (plist));
769 M17N_OBJECT_UNREF (region);
773 gd_dump_region (MDrawRegion region)
777 gd_region_to_rect (region, &rect);
778 fprintf (stderr, "(%d %d %d %d)\n", rect.x, rect.y, rect.width, rect.height);
781 static MDeviceDriver gd_driver =
786 gd_free_realized_face,
793 gd_union_rect_with_region,
801 /* Functions to be stored in MDeviceLibraryInterface by dlsym (). */
806 M_rgb = msymbol (" rgb");
808 realized_fontset_list = mplist ();
809 realized_font_list = mplist ();
810 realized_face_list = mplist ();
811 scratch_images[0] = scratch_images[1] = NULL;
813 gd_font_driver.select = mfont__ft_driver.select;
814 gd_font_driver.find_metric = mfont__ft_driver.find_metric;
815 gd_font_driver.has_char = mfont__ft_driver.has_char;
816 gd_font_driver.encode_char = mfont__ft_driver.encode_char;
817 gd_font_driver.list = mfont__ft_driver.list;
818 gd_font_driver.check_otf = mfont__ft_driver.check_otf;
819 gd_font_driver.drive_otf = mfont__ft_driver.drive_otf;
830 MPLIST_DO (plist, realized_fontset_list)
831 mfont__free_realized_fontset ((MRealizedFontset *) MPLIST_VAL (plist));
832 M17N_OBJECT_UNREF (realized_fontset_list);
834 MPLIST_DO (plist, realized_face_list)
836 MRealizedFace *rface = MPLIST_VAL (plist);
839 mface__free_realized (rface);
841 M17N_OBJECT_UNREF (realized_face_list);
843 if (MPLIST_VAL (realized_font_list))
844 mfont__free_realized (MPLIST_VAL (realized_font_list));
845 M17N_OBJECT_UNREF (realized_font_list);
847 for (i = 0; i < 2; i++)
848 if (scratch_images[i])
849 gdImageDestroy (scratch_images[i]);
854 device_open (MFrame *frame, MPlist *param)
858 frame->device = NULL;
859 frame->device_type = MDEVICE_SUPPORT_OUTPUT;
860 frame->dpi = (int) mplist_get (param, Mresolution);
863 frame->driver = &gd_driver;
864 frame->font_driver_list = mplist ();
865 mplist_add (frame->font_driver_list, Mfreetype, &gd_font_driver);
866 frame->realized_font_list = realized_font_list;
867 frame->realized_face_list = realized_face_list;
868 frame->realized_fontset_list = realized_fontset_list;
869 face = mface_copy (mface__default);
870 mface_put_prop (face, Mfoundry, Mnil);
871 mface_put_prop (face, Mfamily, Mnil);
872 mplist_push (param, Mface, face);
873 M17N_OBJECT_UNREF (face);
877 #else /* not HAVE_GD nor HAVE_FREETYPE */
879 int device_open () { return -1; }
881 #endif /* not HAVE_GD nor HAVE_FREETYPE */
884 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */