blob: dd2a82945d7e086bc8603d2bf2e13e7109b87cc0 [file] [log] [blame]
/*
* R : A Computer Language for Statistical Data Analysis
* Copyright (C) 2001-3 Paul Murrell
* 2003-2014 The R 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
* https://www.R-project.org/Licenses/
*/
#include "grid.h"
#include <string.h>
/* Some access methods for gpars */
SEXP gpFontSizeSXP(SEXP gp) {
return VECTOR_ELT(gp, GP_FONTSIZE);
}
double gpFontSize(SEXP gp, int i) {
SEXP fontsize = gpFontSizeSXP(gp);
return REAL(fontsize)[i % LENGTH(fontsize)];
}
double gpFontSize2(SEXP gp, int i, int* gpIsScalar) {
SEXP fontsize = gpFontSizeSXP(gp);
gpIsScalar[GP_FONTSIZE] = LENGTH(fontsize) == 1;
return REAL(fontsize)[i % LENGTH(fontsize)];
}
SEXP gpLineHeightSXP(SEXP gp) {
return VECTOR_ELT(gp, GP_LINEHEIGHT);
}
double gpLineHeight(SEXP gp, int i) {
SEXP lineheight = gpLineHeightSXP(gp);
return REAL(lineheight)[i % LENGTH(lineheight)];
}
double gpLineHeight2(SEXP gp, int i, int* gpIsScalar) {
SEXP lineheight = gpLineHeightSXP(gp);
gpIsScalar[GP_LINEHEIGHT] = LENGTH(lineheight) == 1;
return REAL(lineheight)[i % LENGTH(lineheight)];
}
SEXP gpColSXP(SEXP gp) {
return VECTOR_ELT(gp, GP_COL);
}
/* grid has no concept of 'colour 0' (bg in base) */
int gpCol(SEXP gp, int i) {
SEXP col = gpColSXP(gp);
int result;
if (isNull(col))
result = R_TRANWHITE;
else
result = RGBpar3(col, i % LENGTH(col), R_TRANWHITE);
return result;
}
int gpCol2(SEXP gp, int i, int* gpIsScalar) {
SEXP col = gpColSXP(gp);
gpIsScalar[GP_COL] = LENGTH(col) == 1;
int result;
if (isNull(col))
result = R_TRANWHITE;
else
result = RGBpar3(col, i % LENGTH(col), R_TRANWHITE);
return result;
}
SEXP gpFillSXP(SEXP gp) {
return VECTOR_ELT(gp, GP_FILL);
}
int gpFill(SEXP gp, int i) {
SEXP fill = gpFillSXP(gp);
int result;
if (isNull(fill))
result = R_TRANWHITE;
else
result = RGBpar3(fill, i % LENGTH(fill), R_TRANWHITE);
return result;
}
int gpFill2(SEXP gp, int i, int* gpIsScalar) {
SEXP fill = gpFillSXP(gp);
gpIsScalar[GP_FILL] = LENGTH(fill) == 1;
int result;
if (isNull(fill))
result = R_TRANWHITE;
else
result = RGBpar3(fill, i % LENGTH(fill), R_TRANWHITE);
return result;
}
SEXP gpGammaSXP(SEXP gp) {
return VECTOR_ELT(gp, GP_GAMMA);
}
double gpGamma(SEXP gp, int i) {
SEXP gamma = gpGammaSXP(gp);
return REAL(gamma)[i % LENGTH(gamma)];
}
double gpGamma2(SEXP gp, int i, int* gpIsScalar) {
SEXP gamma = gpGammaSXP(gp);
gpIsScalar[GP_GAMMA] = LENGTH(gamma) == 1;
return REAL(gamma)[i % LENGTH(gamma)];
}
SEXP gpLineTypeSXP(SEXP gp) {
return VECTOR_ELT(gp, GP_LTY);
}
int gpLineType(SEXP gp, int i) {
SEXP linetype = gpLineTypeSXP(gp);
return GE_LTYpar(linetype, i % LENGTH(linetype));
}
int gpLineType2(SEXP gp, int i, int* gpIsScalar) {
SEXP linetype = gpLineTypeSXP(gp);
gpIsScalar[GP_LTY] = LENGTH(linetype) == 1;
return GE_LTYpar(linetype, i % LENGTH(linetype));
}
SEXP gpLineWidthSXP(SEXP gp) {
return VECTOR_ELT(gp, GP_LWD);
}
double gpLineWidth(SEXP gp, int i) {
SEXP linewidth = gpLineWidthSXP(gp);
return REAL(linewidth)[i % LENGTH(linewidth)];
}
double gpLineWidth2(SEXP gp, int i, int* gpIsScalar) {
SEXP linewidth = gpLineWidthSXP(gp);
gpIsScalar[GP_LWD] = LENGTH(linewidth) == 1;
return REAL(linewidth)[i % LENGTH(linewidth)];
}
SEXP gpCexSXP(SEXP gp) {
return VECTOR_ELT(gp, GP_CEX);
}
double gpCex(SEXP gp, int i) {
SEXP cex = gpCexSXP(gp);
return REAL(cex)[i % LENGTH(cex)];
}
double gpCex2(SEXP gp, int i, int* gpIsScalar) {
SEXP cex = gpCexSXP(gp);
gpIsScalar[GP_CEX] = LENGTH(cex) == 1;
return REAL(cex)[i % LENGTH(cex)];
}
SEXP gpFontSXP(SEXP gp) {
return VECTOR_ELT(gp, GP_FONT);
}
int gpFont(SEXP gp, int i) {
SEXP font = gpFontSXP(gp);
return INTEGER(font)[i % LENGTH(font)];
}
int gpFont2(SEXP gp, int i, int* gpIsScalar) {
SEXP font = gpFontSXP(gp);
gpIsScalar[GP_FONT] = LENGTH(font) == 1;
return INTEGER(font)[i % LENGTH(font)];
}
SEXP gpFontFamilySXP(SEXP gp) {
return VECTOR_ELT(gp, GP_FONTFAMILY);
}
const char* gpFontFamily(SEXP gp, int i) {
SEXP fontfamily = gpFontFamilySXP(gp);
return CHAR(STRING_ELT(fontfamily, i % LENGTH(fontfamily)));
}
const char* gpFontFamily2(SEXP gp, int i, int* gpIsScalar) {
SEXP fontfamily = gpFontFamilySXP(gp);
gpIsScalar[GP_FONTFAMILY] = LENGTH(fontfamily) == 1;
return CHAR(STRING_ELT(fontfamily, i % LENGTH(fontfamily)));
}
SEXP gpAlphaSXP(SEXP gp) {
return VECTOR_ELT(gp, GP_ALPHA);
}
double gpAlpha(SEXP gp, int i) {
SEXP alpha = gpAlphaSXP(gp);
return REAL(alpha)[i % LENGTH(alpha)];
}
double gpAlpha2(SEXP gp, int i, int* gpIsScalar) {
SEXP alpha = gpAlphaSXP(gp);
gpIsScalar[GP_ALPHA] = LENGTH(alpha) == 1;
return REAL(alpha)[i % LENGTH(alpha)];
}
SEXP gpLineEndSXP(SEXP gp) {
return VECTOR_ELT(gp, GP_LINEEND);
}
R_GE_lineend gpLineEnd(SEXP gp, int i) {
SEXP lineend = gpLineEndSXP(gp);
return GE_LENDpar(lineend, i % LENGTH(lineend));
}
R_GE_lineend gpLineEnd2(SEXP gp, int i, int* gpIsScalar) {
SEXP lineend = gpLineEndSXP(gp);
gpIsScalar[GP_LINEEND] = LENGTH(lineend) == 1;
return GE_LENDpar(lineend, i % LENGTH(lineend));
}
SEXP gpLineJoinSXP(SEXP gp) {
return VECTOR_ELT(gp, GP_LINEJOIN);
}
R_GE_linejoin gpLineJoin(SEXP gp, int i) {
SEXP linejoin = gpLineJoinSXP(gp);
return GE_LJOINpar(linejoin, i % LENGTH(linejoin));
}
R_GE_linejoin gpLineJoin2(SEXP gp, int i, int* gpIsScalar) {
SEXP linejoin = gpLineJoinSXP(gp);
gpIsScalar[GP_LINEJOIN] = LENGTH(linejoin) == 1;
return GE_LJOINpar(linejoin, i % LENGTH(linejoin));
}
SEXP gpLineMitreSXP(SEXP gp) {
return VECTOR_ELT(gp, GP_LINEMITRE);
}
double gpLineMitre(SEXP gp, int i) {
SEXP linemitre = gpLineMitreSXP(gp);
return REAL(linemitre)[i % LENGTH(linemitre)];
}
double gpLineMitre2(SEXP gp, int i, int* gpIsScalar) {
SEXP linemitre = gpLineMitreSXP(gp);
gpIsScalar[GP_LINEMITRE] = LENGTH(linemitre) == 1;
return REAL(linemitre)[i % LENGTH(linemitre)];
}
SEXP gpLexSXP(SEXP gp) {
return VECTOR_ELT(gp, GP_LEX);
}
double gpLex(SEXP gp, int i) {
SEXP lex = gpLexSXP(gp);
return REAL(lex)[i % LENGTH(lex)];
}
double gpLex2(SEXP gp, int i, int* gpIsScalar) {
SEXP lex = gpLexSXP(gp);
gpIsScalar[GP_LEX] = LENGTH(lex) == 1;
return REAL(lex)[i % LENGTH(lex)];
}
/*
* Never access fontface because fontface values are stored in font
* Historical reasons ...
*/
/*
* Combine gpar alpha with alpha level stored in colour
*
* finalAlpha = gpAlpha*(R_ALPHA(col)/255)
*
* Based on my reading of how group alpha and individual
* object alphas are combined in the SVG 1.0 docs
*
* Also has nice properties:
* (i) range of finalAlpha is 0 to 1.
* (ii) if either of gpAlpha or R_ALPHA(col) are 0 then finalAlpha = 0
* (i.e., can never make fully transparent colour less transparent).
* (iii) in order to get finalAlpha = 1, both gpAlpha and R_ALPHA(col)
* must be 1 (i.e., only way to get fully opaque is if both
* alpha levels are fully opaque).
*/
static unsigned int combineAlpha(double alpha, int col)
{
unsigned int newAlpha = (unsigned int)((alpha*(R_ALPHA(col)/255.0))*255);
return R_RGBA(R_RED(col), R_GREEN(col), R_BLUE(col), newAlpha);
}
/*
* Generate an R_GE_gcontext from a gpar
*/
void gcontextFromgpar(SEXP gp, int i, const pGEcontext gc, pGEDevDesc dd)
{
/*
* Combine gpAlpha with col and fill
*/
gc->col = combineAlpha(gpAlpha(gp, i), gpCol(gp, i));
gc->fill = combineAlpha(gpAlpha(gp, i), gpFill(gp, i));
gc->gamma = gpGamma(gp, i);
/*
* Combine gpLex with lwd
* Also scale by GSS_SCALE (a "zoom" factor)
*/
gc->lwd = gpLineWidth(gp, i) * gpLex(gp, i) *
REAL(gridStateElement(dd, GSS_SCALE))[0];
gc->lty = gpLineType(gp, i);
gc->lend = gpLineEnd(gp, i);
gc->ljoin = gpLineJoin(gp, i);
gc->lmitre = gpLineMitre(gp, i);
gc->cex = gpCex(gp, i);
/*
* Scale by GSS_SCALE (a "zoom" factor)
*/
gc->ps = gpFontSize(gp, i) * REAL(gridStateElement(dd, GSS_SCALE))[0];
gc->lineheight = gpLineHeight(gp, i);
gc->fontface = gpFont(gp, i);
strcpy(gc->fontfamily, gpFontFamily(gp, i));
}
SEXP L_setGPar(SEXP gpars)
{
/* Set the value of the current gpars on the current device
* Need to do this in here so that redrawing via R BASE display
* list works
*/
/* Get the current device
*/
pGEDevDesc dd = getDevice();
setGridStateElement(dd, GSS_GPAR, gpars);
return R_NilValue;
}
SEXP L_getGPar(void)
{
/* Get the value of the current gpars on the current device
* Need to do this in here so that redrawing via R BASE display
* list works
*/
/* Get the current device
*/
pGEDevDesc dd = getDevice();
return gridStateElement(dd, GSS_GPAR);
}
SEXP L_getGPsaved()
{
/* Get the current device
*/
pGEDevDesc dd = getDevice();
return gridStateElement(dd, GSS_GPSAVED);
}
SEXP L_setGPsaved(SEXP gpars)
{
/* Get the current device
*/
pGEDevDesc dd = getDevice();
setGridStateElement(dd, GSS_GPSAVED, gpars);
return R_NilValue;
}
void initGPar(pGEDevDesc dd)
{
pDevDesc dev = dd->dev;
SEXP gpar, gparnames, class;
SEXP gpfill, gpcol, gpgamma, gplty, gplwd, gpcex, gpfs, gplh, gpfont;
SEXP gpfontfamily, gpalpha, gplineend, gplinejoin, gplinemitre, gplex;
SEXP gsd = (SEXP) dd->gesd[gridRegisterIndex]->systemSpecific;
PROTECT(gpar = allocVector(VECSXP, 15));
PROTECT(gparnames = allocVector(STRSXP, 15));
SET_STRING_ELT(gparnames, GP_FILL, mkChar("fill"));
SET_STRING_ELT(gparnames, GP_COL, mkChar("col"));
SET_STRING_ELT(gparnames, GP_GAMMA, mkChar("gamma"));
SET_STRING_ELT(gparnames, GP_LTY, mkChar("lty"));
SET_STRING_ELT(gparnames, GP_LWD, mkChar("lwd"));
SET_STRING_ELT(gparnames, GP_CEX, mkChar("cex"));
SET_STRING_ELT(gparnames, GP_FONTSIZE, mkChar("fontsize"));
SET_STRING_ELT(gparnames, GP_LINEHEIGHT, mkChar("lineheight"));
SET_STRING_ELT(gparnames, GP_FONT, mkChar("font"));
SET_STRING_ELT(gparnames, GP_FONTFAMILY, mkChar("fontfamily"));
SET_STRING_ELT(gparnames, GP_ALPHA, mkChar("alpha"));
SET_STRING_ELT(gparnames, GP_LINEEND, mkChar("lineend"));
SET_STRING_ELT(gparnames, GP_LINEJOIN, mkChar("linejoin"));
SET_STRING_ELT(gparnames, GP_LINEMITRE, mkChar("linemitre"));
SET_STRING_ELT(gparnames, GP_LEX, mkChar("lex"));
setAttrib(gpar, R_NamesSymbol, gparnames);
PROTECT(gpfill = allocVector(STRSXP, 1));
SET_STRING_ELT(gpfill, 0, mkChar(col2name(dev->startfill)));
SET_VECTOR_ELT(gpar, GP_FILL, gpfill);
PROTECT(gpcol = allocVector(STRSXP, 1));
SET_STRING_ELT(gpcol, 0, mkChar(col2name(dev->startcol)));
SET_VECTOR_ELT(gpar, GP_COL, gpcol);
PROTECT(gpgamma = allocVector(REALSXP, 1));
REAL(gpgamma)[0] = dev->startgamma;
SET_VECTOR_ELT(gpar, GP_GAMMA, gpgamma);
PROTECT(gplty = GE_LTYget(dev->startlty));
SET_VECTOR_ELT(gpar, GP_LTY, gplty);
PROTECT(gplwd = allocVector(REALSXP, 1));
REAL(gplwd)[0] = 1;
SET_VECTOR_ELT(gpar, GP_LWD, gplwd);
PROTECT(gpcex = allocVector(REALSXP, 1));
REAL(gpcex)[0] = 1;
SET_VECTOR_ELT(gpar, GP_CEX, gpcex);
PROTECT(gpfs = allocVector(REALSXP, 1));
REAL(gpfs)[0] = dev->startps;
SET_VECTOR_ELT(gpar, GP_FONTSIZE, gpfs);
PROTECT(gplh = allocVector(REALSXP, 1));
REAL(gplh)[0] = 1.2;
SET_VECTOR_ELT(gpar, GP_LINEHEIGHT, gplh);
PROTECT(gpfont = allocVector(INTSXP, 1));
INTEGER(gpfont)[0] = dev->startfont;
SET_VECTOR_ELT(gpar, GP_FONT, gpfont);
PROTECT(gpfontfamily = allocVector(STRSXP, 1));
/*
* A font family of "" means that the default font
* set up by the device will be used.
*/
SET_STRING_ELT(gpfontfamily, 0, mkChar(""));
SET_VECTOR_ELT(gpar, GP_FONTFAMILY, gpfontfamily);
PROTECT(gpalpha = allocVector(REALSXP, 1));
REAL(gpalpha)[0] = 1;
SET_VECTOR_ELT(gpar, GP_ALPHA, gpalpha);
PROTECT(gplineend = allocVector(STRSXP, 1));
SET_STRING_ELT(gplineend, 0, mkChar("round"));
SET_VECTOR_ELT(gpar, GP_LINEEND, gplineend);
PROTECT(gplinejoin = allocVector(STRSXP, 1));
SET_STRING_ELT(gplinejoin, 0, mkChar("round"));
SET_VECTOR_ELT(gpar, GP_LINEJOIN, gplinejoin);
PROTECT(gplinemitre = allocVector(REALSXP, 1));
REAL(gplinemitre)[0] = 10;
SET_VECTOR_ELT(gpar, GP_LINEMITRE, gplinemitre);
PROTECT(gplex = allocVector(REALSXP, 1));
REAL(gplex)[0] = 1;
SET_VECTOR_ELT(gpar, GP_LEX, gplex);
PROTECT(class = allocVector(STRSXP, 1));
SET_STRING_ELT(class, 0, mkChar("gpar"));
classgets(gpar, class);
SET_VECTOR_ELT(gsd, GSS_GPAR, gpar);
UNPROTECT(18);
}
// Minimal primitive gc setters
// These should only be called after gcontextFromgpar has been called once
void initGContext(SEXP gp, const pGEcontext gc, pGEDevDesc dd, int* gpIsScalar,
const pGEcontext gcCache)
{
int i = 0;
/*
* Combine gpAlpha with col and fill
*/
gcCache->col = gc->col =
combineAlpha(gpAlpha2(gp, i, gpIsScalar), gpCol2(gp, i, gpIsScalar));
gcCache->fill = gc->fill =
combineAlpha(gpAlpha(gp, i), gpFill2(gp, i, gpIsScalar));
gcCache->gamma = gc->gamma = gpGamma2(gp, i, gpIsScalar);
/*
* Combine gpLex with lwd
* Also scale by GSS_SCALE (a "zoom" factor)
*/
gcCache->lwd = gc->lwd = gpLineWidth2(gp, i, gpIsScalar) *
gpLex2(gp, i, gpIsScalar) * REAL(gridStateElement(dd, GSS_SCALE))[0];
gcCache->lty = gc->lty = gpLineType2(gp, i, gpIsScalar);
gcCache->lend = gc->lend = gpLineEnd2(gp, i, gpIsScalar);
gcCache->ljoin = gc->ljoin = gpLineJoin2(gp, i, gpIsScalar);
gcCache->lmitre = gc->lmitre = gpLineMitre2(gp, i, gpIsScalar);
gcCache->cex = gc->cex = gpCex2(gp, i, gpIsScalar);
/*
* Scale by GSS_SCALE (a "zoom" factor)
*/
gcCache->ps = gc->ps = gpFontSize2(gp, i, gpIsScalar) *
REAL(gridStateElement(dd, GSS_SCALE))[0];
gcCache->lineheight = gc->lineheight = gpLineHeight2(gp, i, gpIsScalar);
gcCache->fontface = gc->fontface = gpFont2(gp, i, gpIsScalar);
strcpy(gc->fontfamily, gpFontFamily2(gp, i, gpIsScalar));
strcpy(gcCache->fontfamily, gc->fontfamily);
}
void updateGContext(SEXP gp, int i, const pGEcontext gc, pGEDevDesc dd,
int* gpIsScalar, const pGEcontext gcCache)
{
if (gpIsScalar[0] == -1) {
error(_("updateGContext must only be called after initGContext"));
}
if (!(gpIsScalar[GP_ALPHA] && gpIsScalar[GP_COL])) {
double alpha = gpAlpha(gp, i);
if (alpha == 1.0) gc->col = gpCol(gp, i);
else gc->col = combineAlpha(alpha, gpCol(gp, i));
} else {
gc->col = gcCache->col;
}
if (!(gpIsScalar[GP_ALPHA] && gpIsScalar[GP_FILL])) {
double alpha = gpAlpha(gp, i);
if (alpha == 1.0) gc->fill = gpFill(gp, i);
else gc->fill = combineAlpha(alpha, gpFill(gp, i));
} else {
gc->fill = gcCache->fill;
}
gc->gamma = gpIsScalar[GP_GAMMA] ? gcCache->gamma : gpGamma(gp, i);
gc->lwd = (gpIsScalar[GP_LWD] && gpIsScalar[GP_LEX]) ? gcCache->lwd :
gpLineWidth(gp, i) * gpLex(gp, i) *
REAL(gridStateElement(dd, GSS_SCALE))[0];
gc->lty = gpIsScalar[GP_LTY] ? gcCache->lty : gpLineType(gp, i);
gc->lend = gpIsScalar[GP_LINEEND] ? gcCache->lend : gpLineEnd(gp, i);
gc->ljoin = gpIsScalar[GP_LINEJOIN] ? gcCache->ljoin : gpLineJoin(gp, i);
gc->lmitre = gpIsScalar[GP_LINEMITRE] ? gcCache->lmitre : gpLineMitre(gp, i);
gc->cex = gpIsScalar[GP_CEX] ? gcCache->cex : gpCex(gp, i);
gc->ps = gpIsScalar[GP_FONTSIZE] ? gcCache->ps : gpFontSize(gp, i) *
REAL(gridStateElement(dd, GSS_SCALE))[0];
gc->lineheight = gpIsScalar[GP_LINEHEIGHT] ? gcCache->lineheight :
gpLineHeight(gp, i);
gc->fontface = gpIsScalar[GP_FONT] ? gcCache->fontface : gpFont(gp, i);
if (gpIsScalar[GP_FONTFAMILY]) {
strcpy(gc->fontfamily, gcCache->fontfamily);
} else {
strcpy(gc->fontfamily, gpFontFamily(gp, i));
}
}