Rev 51404 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/** R : A Computer Language for Statistical Data Analysis* Copyright (C) 1995, 1996 Robert Gentleman and Ross Ihaka* Copyright (C) 1997--2008 R Development Core Team** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation; either version 2 of the License, or* (at your option) any later version.** This program 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 General Public License for more details.** You should have received a copy of the GNU General Public License* along with this program; if not, a copy is available at* http://www.r-project.org/Licenses/*//* Entry points usedcairo_arccairo_clipcairo_close_pathcairo_createcairo_destroycairo_fill_preservecairo_image_surface_createcairo_image_surface_get_data (1.2)cairo_line_tocairo_move_tocairo_new_pathcairo_paintcairo_rectanglecairo_rel_move_tocairo_reset_clipcairo_restorecairo_rotatecairo_savecairo_set_antialiascairo_set_dashcairo_set_line_capcairo_set_line_joincairo_set_line_widthcairo_set_miter_limitcairo_set_operatorcairo_set_source_rgbcairo_set_source_rgbacairo_set_source_surfacecairo_statuscairo_status_to_stringcairo_strokecairo_surface_destroycairo_surface_statuscairo_surface_write_to_pngcairo_xlib_surface_createcairo_xlib_surface_set_sizecairo_show_textcairo_text_extentscairo_pdf_surface_create (1.2)cairo_ps_surface_create (1.2)cairo_svg_surface_create (1.2)cairo_ft_font_face_create_for_ft_face [OSX]g_object_unref (glib)pango_cairo_create_layout (1.10)pango_cairo_show_layout (1.10)pango_font_description_freepango_font_description_newpango_font_description_set_familypango_font_description_set_sizepango_font_description_set_stylepango_font_description_set_weightpango_layout_get_linepango_layout_line_get_pixel_extentspango_layout_set_font_descriptionpango_layout_set_text*/static void Cairo_update(pX11Desc xd){/* We could first paint the canvas colour andthen the backing surface. */if(xd->xcc) {cairo_set_source_surface (xd->xcc, xd->cs, 0, 0);cairo_paint(xd->xcc);}}static void CairoColor(unsigned int col, pX11Desc xd){unsigned int alpha = R_ALPHA(col);double red, blue, green;red = R_RED(col)/255.0;green = R_GREEN(col)/255.0;blue = R_BLUE(col)/255.0;red = pow(red, RedGamma);green = pow(green, GreenGamma);blue = pow(blue, BlueGamma);/* These optimizations should not be necessary, but alpha = 1seems to cause image fallback in some backends */if (alpha == 255)cairo_set_source_rgb(xd->cc, red, green, blue);elsecairo_set_source_rgba(xd->cc, red, green, blue, alpha/255.0);}static void CairoLineType(const pGEcontext gc, pX11Desc xd){cairo_t *cc = xd->cc;double lwd = gc->lwd;cairo_line_cap_t lcap = CAIRO_LINE_CAP_SQUARE;cairo_line_join_t ljoin = CAIRO_LINE_JOIN_ROUND;switch(gc->lend){case GE_ROUND_CAP: lcap = CAIRO_LINE_CAP_ROUND; break;case GE_BUTT_CAP: lcap = CAIRO_LINE_CAP_BUTT; break;case GE_SQUARE_CAP: lcap = CAIRO_LINE_CAP_SQUARE; break;}switch(gc->ljoin){case GE_ROUND_JOIN: ljoin = CAIRO_LINE_JOIN_ROUND; break;case GE_MITRE_JOIN: ljoin = CAIRO_LINE_JOIN_MITER; break;case GE_BEVEL_JOIN: ljoin = CAIRO_LINE_JOIN_BEVEL; break;}cairo_set_line_width(cc, (lwd > 0.01 ? lwd : 0.01) * xd->lwdscale);cairo_set_line_cap(cc, lcap);cairo_set_line_join(cc, ljoin);cairo_set_miter_limit(cc, gc->lmitre);if (gc->lty == 0 || gc->lty == -1)cairo_set_dash(cc, 0, 0, 0);else {double ls[16], lwd = (gc->lwd > 1) ? gc->lwd : 1;int l, dt = gc->lty;for (l = 0; dt != 0; dt >>= 4, l++)ls[l] = (dt & 0xF) * lwd * xd->lwdscale;cairo_set_dash(cc, ls, l, 0);}}static void Cairo_Clip(double x0, double x1, double y0, double y1,pDevDesc dd){pX11Desc xd = (pX11Desc) dd->deviceSpecific;if (x1 < x0) { double h = x1; x1 = x0; x0 = h; };if (y1 < y0) { double h = y1; y1 = y0; y0 = h; };cairo_reset_clip(xd->cc);cairo_new_path(xd->cc);/* Add 1 per X11_Clip */cairo_rectangle(xd->cc, x0, y0, x1 - x0 + 1, y1 - y0 + 1);cairo_clip(xd->cc);}static void Cairo_NewPage(const pGEcontext gc, pDevDesc dd){pX11Desc xd = (pX11Desc) dd->deviceSpecific;cairo_reset_clip(xd->cc);xd->fill = R_OPAQUE(gc->fill) ? gc->fill: xd->canvas;CairoColor(xd->fill, xd);cairo_new_path(xd->cc);cairo_paint(xd->cc);Cairo_update(xd);/* Apparently needed */XSync(display, 0);}static void Cairo_Rect(double x0, double y0, double x1, double y1,const pGEcontext gc, pDevDesc dd){pX11Desc xd = (pX11Desc) dd->deviceSpecific;cairo_new_path(xd->cc);cairo_rectangle(xd->cc, x0, y0, x1 - x0, y1 - y0);if (R_ALPHA(gc->fill) > 0) {cairo_set_antialias(xd->cc, CAIRO_ANTIALIAS_NONE);CairoColor(gc->fill, xd);cairo_fill_preserve(xd->cc);cairo_set_antialias(xd->cc, xd->antialias);}if (R_ALPHA(gc->col) > 0 && gc->lty != -1) {CairoColor(gc->col, xd);CairoLineType(gc, xd);cairo_stroke(xd->cc);}}static void Cairo_Circle(double x, double y, double r,const pGEcontext gc, pDevDesc dd){pX11Desc xd = (pX11Desc) dd->deviceSpecific;cairo_new_path(xd->cc);/* radius 0.5 seems to be visible */cairo_arc(xd->cc, x, y, (r > 0.5 ? r : 0.5), 0.0, 2 * M_PI);if (R_ALPHA(gc->fill) > 0) {cairo_set_antialias(xd->cc, CAIRO_ANTIALIAS_NONE);CairoColor(gc->fill, xd);cairo_fill_preserve(xd->cc);cairo_set_antialias(xd->cc, xd->antialias);}if (R_ALPHA(gc->col) > 0 && gc->lty != -1) {CairoColor(gc->col, xd);CairoLineType(gc, xd);cairo_stroke(xd->cc);}}static void Cairo_Line(double x1, double y1, double x2, double y2,const pGEcontext gc, pDevDesc dd){pX11Desc xd = (pX11Desc) dd->deviceSpecific;if (R_ALPHA(gc->col) > 0) {CairoColor(gc->col, xd);CairoLineType(gc, xd);cairo_new_path(xd->cc);cairo_move_to(xd->cc, x1, y1);cairo_line_to(xd->cc, x2, y2);cairo_stroke(xd->cc);}}static void Cairo_Polyline(int n, double *x, double *y,const pGEcontext gc, pDevDesc dd){int i;pX11Desc xd = (pX11Desc) dd->deviceSpecific;if (R_ALPHA(gc->col) > 0) {CairoColor(gc->col, xd);CairoLineType(gc, xd);cairo_new_path(xd->cc);cairo_move_to(xd->cc, x[0], y[0]);for(i = 0; i < n; i++) cairo_line_to(xd->cc, x[i], y[i]);cairo_stroke(xd->cc);}}static void Cairo_Polygon(int n, double *x, double *y,const pGEcontext gc, pDevDesc dd){int i;pX11Desc xd = (pX11Desc) dd->deviceSpecific;cairo_new_path(xd->cc);cairo_move_to(xd->cc, x[0], y[0]);for(i = 0; i < n; i++) cairo_line_to(xd->cc, x[i], y[i]);cairo_close_path(xd->cc);if (R_ALPHA(gc->fill) > 0) {cairo_set_antialias(xd->cc, CAIRO_ANTIALIAS_NONE);CairoColor(gc->fill, xd);cairo_fill_preserve(xd->cc);cairo_set_antialias(xd->cc, xd->antialias);}if (R_ALPHA(gc->col) > 0 && gc->lty != -1) {CairoColor(gc->col, xd);CairoLineType(gc, xd);cairo_stroke(xd->cc);}}static void Cairo_Raster(unsigned int *raster, int w, int h,double x, double y,double width, double height,double rot,Rboolean interpolate,const pGEcontext gc, pDevDesc dd){const void *vmax = vmaxget();int i;cairo_surface_t *image;unsigned char *imageData;pX11Desc xd = (pX11Desc) dd->deviceSpecific;imageData = (unsigned char *) R_alloc(4*w*h, sizeof(unsigned char));/* The R ABGR needs to be converted to a Cairo ARGB* AND values need to by premultiplied by alpha*/for (i=0; i<w*h; i++) {int alpha = R_ALPHA(raster[i]);imageData[i*4 + 3] = alpha;if (alpha < 255) {imageData[i*4 + 2] = R_RED(raster[i]) * alpha / 255;imageData[i*4 + 1] = R_GREEN(raster[i]) * alpha / 255;imageData[i*4 + 0] = R_BLUE(raster[i]) * alpha / 255;} else {imageData[i*4 + 2] = R_RED(raster[i]);imageData[i*4 + 1] = R_GREEN(raster[i]);imageData[i*4 + 0] = R_BLUE(raster[i]);}}image = cairo_image_surface_create_for_data(imageData,CAIRO_FORMAT_ARGB32,w, h,4*w);cairo_save(xd->cc);cairo_translate(xd->cc, x, y);cairo_rotate(xd->cc, -rot*M_PI/180);cairo_scale(xd->cc, width/w, height/h);/* Flip vertical first */cairo_translate(xd->cc, 0, h/2.0);cairo_scale(xd->cc, 1, -1);cairo_translate(xd->cc, 0, -h/2.0);cairo_set_source_surface(xd->cc, image, 0, 0);/* Use nearest-neighbour filter so that a scaled up image* is "blocky"; alternative is some sort of linear* interpolation, which gives nasty edge-effects*/if (!interpolate) {cairo_pattern_set_filter(cairo_get_source(xd->cc),CAIRO_FILTER_NEAREST);}cairo_paint(xd->cc);cairo_restore(xd->cc);cairo_surface_destroy(image);vmaxset(vmax);}static SEXP Cairo_Cap(pDevDesc dd){int i, width, height, size;pX11Desc xd = (pX11Desc) dd->deviceSpecific;cairo_surface_t* screen;cairo_format_t format;unsigned char *screenData;SEXP dim, raster = R_NilValue;unsigned int *rint;screen = cairo_surface_reference(cairo_get_target(xd->cc));width = cairo_image_surface_get_width(screen);height = cairo_image_surface_get_height(screen);screenData = cairo_image_surface_get_data(screen);/* The type of image surface will depend on what sort* of X11 color model has been used */format = cairo_image_surface_get_format(screen);/* For now, if format is not RGB24 just bail out */if (format != CAIRO_FORMAT_RGB24)return raster;size = width*height;PROTECT(raster = allocVector(INTSXP, size));/* Copy each byte of screen to an R matrix.* The Cairo RGB24 needs to be converted to an R ABGR32 */rint = (unsigned int *) INTEGER(raster);for (i=0; i<size; i++) {rint[i] = 255u << 24 |((screenData[i*4])<<16 |(screenData[i*4 + 1])<<8 |(screenData[i*4 + 2]));}PROTECT(dim = allocVector(INTSXP, 2));INTEGER(dim)[0] = height;INTEGER(dim)[1] = width;setAttrib(raster, R_DimSymbol, dim);/* Release MY reference to the screen surface */cairo_surface_destroy(screen);UNPROTECT(2);return raster;}#ifdef HAVE_PANGOCAIRO/* ------------- pangocairo section --------------- */static PangoFontDescription *PG_getFont(const pGEcontext gc, double fs){PangoFontDescription *fontdesc;gint face = gc->fontface;double size = gc->cex * gc->ps * fs;if (face < 1 || face > 5) face = 1;fontdesc = pango_font_description_new();if (face == 5)pango_font_description_set_family(fontdesc, "symbol");else {char *fm = gc->fontfamily;if(streql(fm, "mono")) fm = "courier";else if(streql(fm, "serif")) fm = "times";else if(streql(fm, "sans")) fm = "helvetica";pango_font_description_set_family(fontdesc, fm[0] ? fm : "helvetica");if(face == 2 || face == 4)pango_font_description_set_weight(fontdesc, PANGO_WEIGHT_BOLD);if(face == 3 || face == 4)pango_font_description_set_style(fontdesc, PANGO_STYLE_OBLIQUE);}pango_font_description_set_size(fontdesc, PANGO_SCALE * size);return fontdesc;}static PangoLayout*PG_layout(PangoFontDescription *desc, cairo_t *cc, const char *str){PangoLayout *layout;layout = pango_cairo_create_layout(cc);pango_layout_set_font_description(layout, desc);pango_layout_set_text(layout, str, -1);return layout;}static voidPG_text_extents(cairo_t *cc, PangoLayout *layout,gint *lbearing, gint *rbearing,gint *width, gint *ascent, gint *descent, int ink){PangoRectangle rect, lrect;pango_layout_line_get_pixel_extents(pango_layout_get_line(layout, 0),&rect, &lrect);if(width) *width = lrect.width;if(ink) {if(ascent) *ascent = PANGO_ASCENT(rect);if(descent) *descent = PANGO_DESCENT(rect);if(lbearing) *lbearing = PANGO_LBEARING(rect);if(rbearing) *rbearing = PANGO_RBEARING(rect);} else {if(ascent) *ascent = PANGO_ASCENT(lrect);if(descent) *descent = PANGO_DESCENT(lrect);if(lbearing) *lbearing = PANGO_LBEARING(lrect);if(rbearing) *rbearing = PANGO_RBEARING(lrect);}}static voidPangoCairo_MetricInfo(int c, const pGEcontext gc,double* ascent, double* descent,double* width, pDevDesc dd){pX11Desc xd = (pX11Desc) dd->deviceSpecific;char str[16];int Unicode = mbcslocale;PangoFontDescription *desc = PG_getFont(gc, xd->fontscale);PangoLayout *layout;gint iascent, idescent, iwidth;if(c == 0) c = 77;if(c < 0) {c = -c; Unicode = 1;}if(Unicode) {Rf_ucstoutf8(str, (unsigned int) c);} else {/* Here we assume that c < 256 */str[0] = c; str[1] = 0;}layout = PG_layout(desc, xd->cc, str);PG_text_extents(xd->cc, layout, NULL, NULL, &iwidth,&iascent, &idescent, 1);g_object_unref(layout);pango_font_description_free(desc);*ascent = iascent;*descent = idescent;*width = iwidth;#if 0printf("c = %d, '%s', face %d %f %f %f\n",c, str, gc->fontface, *width, *ascent, *descent);#endif}static doublePangoCairo_StrWidth(const char *str, const pGEcontext gc, pDevDesc dd){pX11Desc xd = (pX11Desc) dd->deviceSpecific;gint width;PangoFontDescription *desc = PG_getFont(gc, xd->fontscale);PangoLayout *layout = PG_layout(desc, xd->cc, str);PG_text_extents(xd->cc, layout, NULL, NULL, &width, NULL, NULL, 0);g_object_unref(layout);pango_font_description_free(desc);return (double) width;}static voidPangoCairo_Text(double x, double y,const char *str, double rot, double hadj,const pGEcontext gc, pDevDesc dd){pX11Desc xd = (pX11Desc) dd->deviceSpecific;gint ascent, lbearing, width;PangoLayout *layout;if (R_ALPHA(gc->col) > 0) {PangoFontDescription *desc = PG_getFont(gc, xd->fontscale);cairo_save(xd->cc);layout = PG_layout(desc, xd->cc, str);PG_text_extents(xd->cc, layout, &lbearing, NULL, &width,&ascent, NULL, 0);cairo_move_to(xd->cc, x, y);if (rot != 0.0) cairo_rotate(xd->cc, -rot/180.*M_PI);/* pango has a coord system at top left */cairo_rel_move_to(xd->cc, -lbearing - width*hadj, -ascent);CairoColor(gc->col, xd);pango_cairo_show_layout(xd->cc, layout);cairo_restore(xd->cc);g_object_unref(layout);pango_font_description_free(desc);}}#else/* ------------- cairo-ft section --------------- *//* FIXME: although this should work on all platforms, I didn't get totest it (yet) anywhere else, hence the __APPLE__ condition for now [SU]r44621: now works on Linux, just finds the same fonts as before. [BDR]*/#if CAIRO_HAS_FT_FONT && __APPLE__/* FT implies FC in Cairo */#include <cairo-ft.h>/* cairo font cache - to prevent unnecessary font look ups */typedef struct Rc_font_cache_s {const char *family;int face;cairo_font_face_t *font;struct Rc_font_cache_s *next;} Rc_font_cache_t;static Rc_font_cache_t *cache, *cache_tail;static cairo_font_face_t *Rc_findFont(const char *family, int face){Rc_font_cache_t *here = cache;while (here) {if (here->face == face && streql(here->family, family))return here->font;here = here->next;}return NULL;}static void Rc_addFont(const char *family, int face, cairo_font_face_t* font){Rc_font_cache_t *fc = (Rc_font_cache_t*) malloc(sizeof(Rc_font_cache_t));if (!fc) return;fc->family = strdup(family);fc->face = face;fc->font = font;fc->next = NULL;if (cache)cache_tail = cache_tail->next = fc;elsecache = cache_tail = fc;}/* FC patterns to append to font family names */static const char *face_styles[4] = {":style=Regular",":style=Bold",":style=Italic",":style=Bold Italic,BoldItalic"};static int fc_loaded;static FT_Library ft_library;/* use FC to find a font, load it in FT and return the Cairo FT font face */static cairo_font_face_t *FC_getFont(const char *family, int style){FcFontSet *fs;FcPattern *pat, *match;FcResult result;FcChar8 *file;char fcname[250]; /* 200 for family + 50 for style *//* find candidate fonts via FontConfig */if (!fc_loaded) {if (!FcInit()) return NULL;fc_loaded = 1;}style &= 3;strcpy(fcname, family);strcat(fcname, face_styles[style]);pat = FcNameParse((FcChar8 *)fcname);if (!pat) return NULL;FcConfigSubstitute (0, pat, FcMatchPattern);FcDefaultSubstitute (pat);fs = FcFontSetCreate ();match = FcFontMatch (0, pat, &result);FcPatternDestroy (pat);if (!match) {FcFontSetDestroy (fs);return NULL;}FcFontSetAdd (fs, match);/* then try to load the font into FT */if (fs) {int j = 0, index = 0;while (j < fs->nfont) { /* find the font file + face index and use it with FreeType */if (FcPatternGetString (fs->fonts[j], FC_FILE, 0, &file)== FcResultMatch &&FcPatternGetInteger(fs->fonts[j], FC_INDEX, 0, &index)== FcResultMatch) {FT_Face face;if (!ft_library && FT_Init_FreeType(&ft_library)) {FcFontSetDestroy (fs);return NULL;}/* some FreeType versions have broken index support, fall back to index 0 */if (!FT_New_Face(ft_library, (const char *) file, index, &face) ||(index && !FT_New_Face(ft_library, (const char *) file, 0, &face))) {FcFontSetDestroy (fs);#ifdef __APPLE__ /* FreeType is broken on OS X in that face index is often wrong (unfortunatelyeven for Helvetica!) - we try to find the best match through enumeration */if (face->num_faces > 1 && (face->style_flags & 3) != style) {FT_Face alt_face;int i = 0;while (i < face->num_faces)if (!FT_New_Face(ft_library, (const char *) file, i++, &alt_face)) {if ((alt_face->style_flags & 3) == style) {FT_Done_Face(face);face = alt_face;break;} elseFT_Done_Face(alt_face);}}#endifreturn cairo_ft_font_face_create_for_ft_face(face, FT_LOAD_DEFAULT);}}j++;}FcFontSetDestroy (fs);}return NULL;}static void FT_getFont(pGEcontext gc, pDevDesc dd, double fs){pX11Desc xd = (pX11Desc) dd->deviceSpecific;int face = gc->fontface;double size = gc->cex * gc->ps *fs;cairo_font_face_t *cairo_face = NULL;const char *family;if (face < 1 || face > 5) face = 1;family = gc->fontfamily;if (face == 5) {if (!*family) family = "Symbol";} else {if (!*family || streql(family, "sans")) family = "Helvetica";else if (streql(family, "serif")) family = "Times";else if (streql(family, "mono")) family = "Courier";}cairo_face = Rc_findFont(family, face);if (!cairo_face) {cairo_face = FC_getFont(family, face - 1);if (!cairo_face) return;Rc_addFont(family, face, cairo_face);}cairo_set_font_face (xd->cc, cairo_face);/* FIXME: this should really use a matrix if pixels are non-square */cairo_set_font_size (xd->cc, size);}#elsestatic void FT_getFont(pGEcontext gc, pDevDesc dd, double fs){pX11Desc xd = (pX11Desc) dd->deviceSpecific;int face = gc->fontface;double size = gc->cex * gc->ps *fs;char *family = "Helvetica";int slant = CAIRO_FONT_SLANT_NORMAL, wt = CAIRO_FONT_WEIGHT_NORMAL;char *fm = gc->fontfamily;if(streql(fm, "mono")) family = "courier";else if(streql(fm, "serif")) family = "times";else if(streql(fm, "sans")) family = "helvetica";else if(fm[0]) family = fm;if (face < 1 || face > 5) face = 1;if (face == 5) family = "Symbol";if (face == 2 || face == 4) wt = CAIRO_FONT_WEIGHT_BOLD;if (face == 3 || face == 4) slant = CAIRO_FONT_SLANT_ITALIC;cairo_select_font_face (xd->cc, family, slant, wt);/* FIXME: this should really use a matrix if pixels are non-square */cairo_set_font_size (xd->cc, size);}#endifstatic void Cairo_MetricInfo(int c, pGEcontext gc,double* ascent, double* descent,double* width, pDevDesc dd){pX11Desc xd = (pX11Desc) dd->deviceSpecific;cairo_text_extents_t exts;char str[16];int Unicode = mbcslocale;if(c == 0) c = 77;if(c < 0) {c = -c; Unicode = 1;}if(Unicode) {Rf_ucstoutf8(str, (unsigned int) c);} else {/* Here, we assume that c < 256 */str[0] = c; str[1] = 0;}FT_getFont(gc, dd, xd->fontscale);cairo_text_extents(xd->cc, str, &exts);*ascent = -exts.y_bearing;*descent = exts.height + exts.y_bearing;*width = exts.x_advance;#if 0printf("c = %d, '%s', face %d %f %f %f\n",c, str, gc->fontface, *width, *ascent, *descent);#endif}static double Cairo_StrWidth(const char *str, pGEcontext gc, pDevDesc dd){pX11Desc xd = (pX11Desc) dd->deviceSpecific;cairo_text_extents_t exts;if (!utf8Valid(str)) error("invalid string in Cairo_StrWidth");FT_getFont(gc, dd, xd->fontscale);cairo_text_extents(xd->cc, str, &exts);return exts.x_advance;}static void Cairo_Text(double x, double y,const char *str, double rot, double hadj,pGEcontext gc, pDevDesc dd){pX11Desc xd = (pX11Desc) dd->deviceSpecific;if (!utf8Valid(str)) error("invalid string in Cairo_Text");if (R_ALPHA(gc->col) > 0) {cairo_save(xd->cc);FT_getFont(gc, dd, xd->fontscale);cairo_move_to(xd->cc, x, y);if (hadj != 0.0 || rot != 0.0) {cairo_text_extents_t te;cairo_text_extents(xd->cc, str, &te);if (rot != 0.0) cairo_rotate(xd->cc, -rot/180.*M_PI);if (hadj != 0.0)cairo_rel_move_to(xd->cc, -te.x_advance * hadj, 0);}CairoColor(gc->col, xd);cairo_show_text(xd->cc, str);cairo_restore(xd->cc);}}#endif