#include "ofCairoRenderer.h"
#include "ofConstants.h"
#include "ofMesh.h"
#include "ofImage.h"
#include "ofTrueTypeFont.h"
#include "ofGraphics.h"
#include "ofVideoBaseTypes.h"
#include "cairo-features.h"
#include "cairo-pdf.h"
#include "cairo-svg.h"
using namespace std;
const string ofCairoRenderer::TYPE="cairo";
_cairo_status ofCairoRenderer::stream_function(void *closure,const unsigned char *data, unsigned int length){
((ofCairoRenderer*)closure)->streamBuffer.append((const char*)data,length);
return CAIRO_STATUS_SUCCESS;
}
ofCairoRenderer::ofCairoRenderer()
:graphics3d(this)
,projection(1)
,modelView(1)
{
type = PDF;
surface = nullptr;
cr = nullptr;
bBackgroundAuto = true;
page = 0;
multiPage = false;
b3D = false;
currentMatrixMode=OF_MATRIX_MODELVIEW;
}
ofCairoRenderer::~ofCairoRenderer(){
close();
}
void ofCairoRenderer::setup(string _filename, Type _type, bool multiPage_, bool b3D_, ofRectangle outputsize){
if( outputsize.width == 0 || outputsize.height == 0 ){
outputsize.set(0, 0, ofGetViewportWidth(), ofGetViewportHeight());
}
filename = _filename;
type = _type;
streamBuffer.clear();
if(type == FROM_FILE_EXTENSION){
string ext = ofFilePath::getFileExt(filename);
if(ofToLower(ext)=="svg"){
type = SVG;
}else if(ofToLower(ext)=="pdf"){
type = PDF;
}else{
type = IMAGE;
}
}
if(filename != "") {
switch(type) {
case PDF:
case SVG:
case IMAGE:
ofFilePath::createEnclosingDirectory(filename);
case FROM_FILE_EXTENSION:
break;
}
}
switch(type){
case PDF:
if(filename==""){
surface = cairo_pdf_surface_create_for_stream(&ofCairoRenderer::stream_function,this,outputsize.width, outputsize.height);
}else{
surface = cairo_pdf_surface_create(ofToDataPath(filename).c_str(),outputsize.width, outputsize.height);
}
break;
case SVG:
if(filename==""){
surface = cairo_svg_surface_create_for_stream(&ofCairoRenderer::stream_function,this,outputsize.width, outputsize.height);
}else{
surface = cairo_svg_surface_create(ofToDataPath(filename).c_str(),outputsize.width, outputsize.height);
}
break;
case IMAGE:
imageBuffer.allocate(outputsize.width, outputsize.height, OF_PIXELS_BGRA);
imageBuffer.set(0);
surface = cairo_image_surface_create_for_data(imageBuffer.getData(),CAIRO_FORMAT_ARGB32,outputsize.width, outputsize.height,outputsize.width*4);
break;
case FROM_FILE_EXTENSION:
ofLogFatalError("ofCairoRenderer") << "setup(): couldn't determine type from extension for filename: \"" << _filename << "\"!";
break;
default:
ofLogError("ofCairoRenderer") << "setup(): encountered unknown type for filename \"" << _filename << "\"";
break;
}
cr = cairo_create(surface);
cairo_set_antialias(cr,CAIRO_ANTIALIAS_SUBPIXEL);
viewportRect = outputsize;
originalViewport = outputsize;
viewport(viewportRect);
page = 0;
b3D = b3D_;
multiPage = multiPage_;
}
void ofCairoRenderer::setupMemoryOnly(Type _type, bool multiPage_, bool b3D_, ofRectangle outputsize){
setup("",_type,multiPage_,b3D_,outputsize);
}
void ofCairoRenderer::flush(){
if(surface){
cairo_surface_flush(surface);
}
}
void ofCairoRenderer::close(){
if(surface){
cairo_surface_flush(surface);
if(type==IMAGE && filename!=""){
ofSaveImage(imageBuffer,filename);
}
cairo_surface_finish(surface);
cairo_surface_destroy(surface);
surface = nullptr;
}
if(cr){
cairo_destroy(cr);
cr = nullptr;
}
}
void ofCairoRenderer::startRender(){
setStyle(currentStyle);
if(page==0 || !multiPage){
page=1;
}else{
page++;
if(getBackgroundAuto()){
cairo_show_page(cr);
clear();
}else{
cairo_copy_page(cr);
}
}
}
void ofCairoRenderer::finishRender(){
cairo_surface_flush(surface);
}
void ofCairoRenderer::setStyle(const ofStyle & style){
setColor((int)style.color.r, (int)style.color.g, (int)style.color.b, (int)style.color.a);
setBackgroundColor(style.bgColor);
setCircleResolution(style.circleResolution);
setSphereResolution(style.sphereResolution);
setCurveResolution(style.curveResolution);
setLineWidth(style.lineWidth);
setRectMode(style.rectMode);
setPolyMode(style.polyMode);
setFillMode(style.bFill?OF_FILLED:OF_OUTLINE);
setBlendMode(style.blendingMode);
currentStyle = style;
}
void ofCairoRenderer::setCurveResolution(int resolution){
currentStyle.curveResolution = resolution;
path.setCurveResolution(resolution);
}
void ofCairoRenderer::draw(const ofPath & shape) const{
cairo_new_path(cr);
const vector<ofPath::Command> & commands = shape.getCommands();
for(int i=0;i<(int)commands.size();i++){
draw(commands[i]);
}
cairo_fill_rule_t cairo_poly_mode;
if(shape.getWindingMode()==OF_POLY_WINDING_ODD) cairo_poly_mode=CAIRO_FILL_RULE_EVEN_ODD;
else cairo_poly_mode=CAIRO_FILL_RULE_WINDING;
cairo_set_fill_rule(cr,cairo_poly_mode);
ofColor prevColor;
if(shape.getUseShapeColor()){
prevColor = currentStyle.color;
}
if(shape.isFilled()){
if(shape.getUseShapeColor()){
ofColor c = shape.getFillColor();
c.a = shape.getFillColor().a;
cairo_set_source_rgba(cr, (float)c.r/255.0, (float)c.g/255.0, (float)c.b/255.0, (float)c.a/255.0);
}
if(shape.hasOutline()){
cairo_fill_preserve( cr );
}else{
cairo_fill(cr);
}
}
if(shape.hasOutline()){
float lineWidth = currentStyle.lineWidth;
if(shape.getUseShapeColor()){
ofColor c = shape.getStrokeColor();
c.a = shape.getStrokeColor().a;
cairo_set_source_rgba(cr, (float)c.r/255.0, (float)c.g/255.0, (float)c.b/255.0, (float)c.a/255.0);
}
cairo_set_line_width( cr, shape.getStrokeWidth() );
cairo_stroke( cr );
cairo_set_line_width( cr, lineWidth );
}
if(shape.getUseShapeColor()){
const_cast<ofCairoRenderer*>(this)->setColor(prevColor);
}
}
void ofCairoRenderer::draw(const ofPolyline & poly) const{
cairo_new_path(cr);
for(int i=0;i<(int)poly.size();i++){
cairo_line_to(cr,poly.getVertices()[i].x,poly.getVertices()[i].y);
}
if(poly.isClosed())
cairo_close_path(cr);
cairo_stroke( cr );
}
void ofCairoRenderer::draw(const vector<glm::vec3> & vertexData, ofPrimitiveMode drawMode) const{
if(vertexData.size()==0) return;
ofCairoRenderer * mut_this = const_cast<ofCairoRenderer*>(this);
mut_this->pushMatrix();
cairo_matrix_t matrix;
cairo_matrix_init_identity(&matrix);
cairo_new_path(cr);
int i = 1;
auto v = transform(vertexData[0]);
glm::vec3 v2;
cairo_move_to(cr,v.x,v.y);
if(drawMode==OF_PRIMITIVE_TRIANGLE_STRIP){
v = transform(vertexData[1]);
cairo_line_to(cr,v.x,v.y);
v = transform(vertexData[2]);
cairo_line_to(cr,v.x,v.y);
i=2;
}
for(; i<(int)vertexData.size(); i++){
v = transform(vertexData[i]);
switch(drawMode){
case(OF_PRIMITIVE_TRIANGLES):
if((i+1)%3==0){
cairo_line_to(cr,v.x,v.y);
v2 = transform(vertexData[i-2]);
cairo_line_to(cr,v2.x,v2.y);
cairo_move_to(cr,v.x,v.y);
}else if((i+3)%3==0){
cairo_move_to(cr,v.x,v.y);
}else{
cairo_line_to(cr,v.x,v.y);
}
break;
case(OF_PRIMITIVE_TRIANGLE_STRIP):
v2 = transform(vertexData[i-2]);
cairo_line_to(cr,v.x,v.y);
cairo_line_to(cr,v2.x,v2.y);
cairo_move_to(cr,v.x,v.y);
break;
case(OF_PRIMITIVE_TRIANGLE_FAN):
break;
default:break;
}
}
cairo_move_to(cr,vertexData[vertexData.size()-1].x,vertexData[vertexData.size()-1].y);
cairo_stroke( cr );
mut_this->popMatrix();
}
glm::vec3 ofCairoRenderer::transform(glm::vec3 vec) const{
if(!b3D) return vec;
auto vec4 = projection * modelView * glm::vec4(vec, 1.0);
vec = glm::vec3(vec4) / vec4.w;
vec = {vec.x/vec.z*viewportRect.width*0.5, vec.y/vec.z*viewportRect.height*0.5, 0.f};
return vec;
}
void ofCairoRenderer::draw(const ofMesh & primitive, ofPolyRenderMode mode, bool useColors, bool useTextures, bool useNormals) const{
if(useColors || useTextures || useNormals){
ofLogWarning("ofCairoRenderer") << "draw(): cairo mesh rendering doesn't support colors, textures, or normals. drawing wireframe ...";
}
if(primitive.getNumVertices() == 0){
return;
}
if(primitive.getNumIndices() == 0){
ofMesh indexedMesh = primitive;
indexedMesh.setupIndicesAuto();
draw(indexedMesh, mode, useColors, useTextures, useNormals);
return;
}
cairo_new_path(cr);
cairo_matrix_t matrix;
cairo_matrix_init_identity(&matrix);
cairo_new_path(cr);
std::size_t i = 1;
auto v = transform(primitive.getVertex(primitive.getIndex(0)));
glm::vec3 v2;
cairo_move_to(cr,v.x,v.y);
if(primitive.getMode()==OF_PRIMITIVE_TRIANGLE_STRIP){
v = transform(primitive.getVertex(primitive.getIndex(1)));
cairo_line_to(cr,v.x,v.y);
v = transform(primitive.getVertex(primitive.getIndex(2)));
cairo_line_to(cr,v.x,v.y);
i=2;
}
for(; i<primitive.getNumIndices(); i++){
v = transform(primitive.getVertex(primitive.getIndex(i)));
switch(primitive.getMode()){
case(OF_PRIMITIVE_TRIANGLES):
if((i+1)%3==0){
cairo_line_to(cr,v.x,v.y);
v2 = transform(primitive.getVertex(primitive.getIndex(i-2)));
cairo_line_to(cr,v2.x,v2.y);
cairo_move_to(cr,v.x,v.y);
}else if((i+3)%3==0){
cairo_move_to(cr,v.x,v.y);
}else{
cairo_line_to(cr,v.x,v.y);
}
break;
case(OF_PRIMITIVE_TRIANGLE_STRIP):
v2 = transform(primitive.getVertex(primitive.getIndex(i-2)));
cairo_line_to(cr,v.x,v.y);
cairo_line_to(cr,v2.x,v2.y);
cairo_move_to(cr,v.x,v.y);
break;
case(OF_PRIMITIVE_TRIANGLE_FAN):
break;
default:break;
}
}
cairo_move_to(cr,primitive.getVertex(primitive.getIndex(primitive.getNumIndices()-1)).x,primitive.getVertex(primitive.getIndex(primitive.getNumIndices()-1)).y);
if(currentStyle.lineWidth>0){
cairo_stroke( cr );
}
}
void ofCairoRenderer::draw( const of3dPrimitive& model, ofPolyRenderMode renderType ) const{
const_cast<ofCairoRenderer*>(this)->pushMatrix();
const_cast<ofCairoRenderer*>(this)->multMatrix(model.getGlobalTransformMatrix());
const ofMesh& mesh = model.getMesh();
draw( mesh, renderType );
const_cast<ofCairoRenderer*>(this)->popMatrix();
}
void ofCairoRenderer::draw(const ofNode& node) const{
const_cast<ofCairoRenderer*>(this)->pushMatrix();
const_cast<ofCairoRenderer*>(this)->multMatrix(node.getGlobalTransformMatrix());
node.customDraw(this);
const_cast<ofCairoRenderer*>(this)->popMatrix();
}
void ofCairoRenderer::draw(const ofPath::Command & command) const{
if(!surface || !cr) return;
ofCairoRenderer * mut_this = const_cast<ofCairoRenderer*>(this);
switch(command.type){
case ofPath::Command::moveTo:
curvePoints.clear();
cairo_move_to(cr,command.to.x,command.to.y);
break;
case ofPath::Command::lineTo:
curvePoints.clear();
cairo_line_to(cr,command.to.x,command.to.y);
break;
case ofPath::Command::curveTo:
curvePoints.push_back(command.to);
if(curvePoints.size()==4){
auto p1=curvePoints[0];
auto p2=curvePoints[1];
auto p3=curvePoints[2];
auto p4=curvePoints[3];
auto cp1 = p2 + ( p3 - p1 ) * (1.0f/6.f);
auto cp2 = p3 + ( p2 - p4 ) * (1.0f/6.f);
cairo_curve_to( cr, cp1.x, cp1.y, cp2.x, cp2.y, p3.x, p3.y );
curvePoints.pop_front();
}
break;
case ofPath::Command::bezierTo:
curvePoints.clear();
cairo_curve_to(cr,command.cp1.x,command.cp1.y,command.cp2.x,command.cp2.y,command.to.x,command.to.y);
break;
case ofPath::Command::quadBezierTo:
curvePoints.clear();
cairo_curve_to(cr,command.cp1.x,command.cp1.y,command.cp2.x,command.cp2.y,command.to.x,command.to.y);
break;
case ofPath::Command::arc:
curvePoints.clear();
if(command.radiusX!=command.radiusY){
float ellipse_ratio = command.radiusY/command.radiusX;
mut_this->pushMatrix();
mut_this->translate(0,-command.to.y*ellipse_ratio);
mut_this->scale(1,ellipse_ratio);
mut_this->translate(0,command.to.y/ellipse_ratio);
cairo_arc(cr,command.to.x,command.to.y,command.radiusX,command.angleBegin*DEG_TO_RAD,command.angleEnd*DEG_TO_RAD);
mut_this->popMatrix();
}else{
cairo_arc(cr,command.to.x,command.to.y,command.radiusX,command.angleBegin*DEG_TO_RAD,command.angleEnd*DEG_TO_RAD);
}
break;
case ofPath::Command::arcNegative:
curvePoints.clear();
if(command.radiusX!=command.radiusY){
float ellipse_ratio = command.radiusY/command.radiusX;
mut_this->pushMatrix();
mut_this->translate(0,-command.to.y*ellipse_ratio);
mut_this->scale(1,ellipse_ratio);
mut_this->translate(0,command.to.y/ellipse_ratio);
cairo_arc_negative(cr,command.to.x,command.to.y,command.radiusX,command.angleBegin*DEG_TO_RAD,command.angleEnd*DEG_TO_RAD);
mut_this->popMatrix();
}else{
cairo_arc_negative(cr,command.to.x,command.to.y,command.radiusX,command.angleBegin*DEG_TO_RAD,command.angleEnd*DEG_TO_RAD);
}
break;
case ofPath::Command::close:
cairo_close_path(cr);
break;
}
}
void ofCairoRenderer::draw(const ofPixels & raw, float x, float y, float z, float w, float h, float sx, float sy, float sw, float sh) const{
bool shouldCrop = sx != 0 || sy != 0 || sw != w || sh != h;
ofPixels cropped;
if(shouldCrop) {
cropped.allocate(sw, sh, raw.getPixelFormat());
raw.cropTo(cropped, sx, sy, sw, sh);
}
const ofPixels & pix = shouldCrop ? cropped : raw;
ofCairoRenderer * mut_this = const_cast<ofCairoRenderer*>(this);
mut_this->pushMatrix();
mut_this->translate(x,y,z);
mut_this->scale(w/pix.getWidth(),h/pix.getHeight());
cairo_surface_t *image;
int stride=0;
int picsize = pix.getWidth()* pix.getHeight();
const unsigned char *imgPix = pix.getData();
vector<unsigned char> swapPixels;
switch(pix.getImageType()){
case OF_IMAGE_COLOR:
#ifdef TARGET_LITTLE_ENDIAN
swapPixels.resize(picsize * 4);
for(int p= 0; p<picsize; p++) {
swapPixels[p*4] = imgPix[p*3 +2];
swapPixels[p*4 +1] = imgPix[p*3 +1];
swapPixels[p*4 +2] = imgPix[p*3];
}
#else
swapPixels.resize(picsize * 4);
for(int p= 0; p<picsize; p++) {
swapPixels[p*4] = imgPix[p*3];
swapPixels[p*4 +1] = imgPix[p*3 +1];
swapPixels[p*4 +2] = imgPix[p*3 +2];
}
#endif
stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, pix.getWidth());
image = cairo_image_surface_create_for_data(&swapPixels[0], CAIRO_FORMAT_RGB24, pix.getWidth(), pix.getHeight(), stride);
break;
case OF_IMAGE_COLOR_ALPHA:
#ifdef TARGET_LITTLE_ENDIAN
swapPixels.resize(picsize * 4);
for(int p= 0; p<picsize; p++) {
swapPixels[p*4] = imgPix[p*4+2];
swapPixels[p*4 +1] = imgPix[p*4+1];
swapPixels[p*4 +2] = imgPix[p*4];
swapPixels[p*4 +3] = imgPix[p*4+3];
}
stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, pix.getWidth());
image = cairo_image_surface_create_for_data(&swapPixels[0], CAIRO_FORMAT_ARGB32, pix.getWidth(), pix.getHeight(), stride);
#else
stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, pix.getWidth());
image = cairo_image_surface_create_for_data(pix.getData(), CAIRO_FORMAT_ARGB32, pix.getWidth(), pix.getHeight(), stride);
#endif
break;
case OF_IMAGE_GRAYSCALE:
swapPixels.resize(picsize * 4);
for(int p= 0; p<picsize; p++) {
swapPixels[p*4] = imgPix[p];
swapPixels[p*4 +1] = imgPix[p];
swapPixels[p*4 +2] = imgPix[p];
}
stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, pix.getWidth());
image = cairo_image_surface_create_for_data(&swapPixels[0], CAIRO_FORMAT_RGB24, pix.getWidth(), pix.getHeight(), stride);
break;
case OF_IMAGE_UNDEFINED:
default:
ofLogError("ofCairoRenderer") << "draw(): trying to draw undefined image type "
<< ofToString(pix.getImageType());
mut_this->popMatrix();
return;
break;
}
cairo_set_source_surface (cr, image, 0,0);
cairo_paint (cr);
cairo_surface_flush(image);
cairo_surface_destroy (image);
mut_this->popMatrix();
}
void ofCairoRenderer::draw(const ofImage & img, float x, float y, float z, float w, float h, float sx, float sy, float sw, float sh) const{
draw(img.getPixels(),x,y,z,w,h,sx,sy,sw,sh);
}
void ofCairoRenderer::draw(const ofFloatImage & image, float x, float y, float z, float w, float h, float sx, float sy, float sw, float sh) const{
ofPixels tmp = image.getPixels();
draw(tmp,x,y,z,w,h,sx,sy,sw,sh);
}
void ofCairoRenderer::draw(const ofShortImage & image, float x, float y, float z, float w, float h, float sx, float sy, float sw, float sh) const{
ofPixels tmp = image.getPixels();
draw(tmp,x,y,z,w,h,sx,sy,sw,sh);
}
void ofCairoRenderer::draw(const ofBaseVideoDraws & video, float x, float y, float w, float h) const{
draw(video.getPixels(),x,y,0,w,h,x,y,w,h);
}
ofPath & ofCairoRenderer::getPath(){
return path;
}
void ofCairoRenderer::setRectMode(ofRectMode mode){
currentStyle.rectMode = mode;
}
ofRectMode ofCairoRenderer::getRectMode(){
return currentStyle.rectMode;
}
void ofCairoRenderer::setFillMode(ofFillFlag fill){
currentStyle.bFill = fill;
if(currentStyle.bFill){
path.setFilled(true);
path.setStrokeWidth(0);
}else{
path.setFilled(false);
path.setStrokeWidth(currentStyle.lineWidth);
}
}
ofFillFlag ofCairoRenderer::getFillMode(){
if(currentStyle.bFill){
return OF_FILLED;
}else{
return OF_OUTLINE;
}
}
void ofCairoRenderer::setLineWidth(float lineWidth){
currentStyle.lineWidth = lineWidth;
if(!currentStyle.bFill){
path.setStrokeWidth(lineWidth);
}
cairo_set_line_width( cr, lineWidth );
}
void ofCairoRenderer::setDepthTest(bool depthTest) {
}
void ofCairoRenderer::setBlendMode(ofBlendMode blendMode){
switch (blendMode){
case OF_BLENDMODE_ALPHA:{
cairo_set_operator(cr,CAIRO_OPERATOR_OVER);
break;
}
case OF_BLENDMODE_ADD:{
cairo_set_operator(cr,CAIRO_OPERATOR_ADD);
break;
}
#if (CAIRO_VERSION_MAJOR==1 && CAIRO_VERSION_MINOR>=10) || CAIRO_VERSION_MAJOR>1
case OF_BLENDMODE_MULTIPLY:{
cairo_set_operator(cr,CAIRO_OPERATOR_MULTIPLY);
break;
}
case OF_BLENDMODE_SCREEN:{
cairo_set_operator(cr,CAIRO_OPERATOR_SCREEN);
break;
}
case OF_BLENDMODE_SUBTRACT:{
cairo_set_operator(cr,CAIRO_OPERATOR_DIFFERENCE);
break;
}
#endif
default:
break;
}
}
void ofCairoRenderer::setLineSmoothing(bool smooth){
}
void ofCairoRenderer::setColor(int r, int g, int b){
setColor(r,g,b,255);
};
void ofCairoRenderer::setColor(int r, int g, int b, int a){
cairo_set_source_rgba(cr, (float)r/255.0, (float)g/255.0, (float)b/255.0, (float)a/255.0);
currentStyle.color.set(r,g,b,a);
};
void ofCairoRenderer::setColor(const ofColor & c){
setColor(c.r,c.g,c.b,c.a);
};
void ofCairoRenderer::setColor(const ofColor & c, int _a){
setColor(c.r,c.g,c.b,_a);
};
void ofCairoRenderer::setColor(int gray){
setColor(gray,gray,gray,255);
};
void ofCairoRenderer::setHexColor( int hexColor ){
int r = (hexColor >> 16) & 0xff;
int g = (hexColor >> 8) & 0xff;
int b = (hexColor >> 0) & 0xff;
setColor(r,g,b);
};
glm::mat4 ofCairoRenderer::getCurrentMatrix(ofMatrixMode matrixMode_) const{
ofLogWarning() << "getCurrentMatrix not yet implemented for Cairo Renderer.";
return glm::mat4(1.0);
}
void ofCairoRenderer::pushMatrix(){
if(!surface || !cr) return;
cairo_matrix_t matrix;
cairo_get_matrix(cr,&matrix);
matrixStack.push(matrix);
if(!b3D) return;
modelViewStack.push(modelView);
}
void ofCairoRenderer::popMatrix(){
if(!surface || !cr) return;
cairo_set_matrix(cr,&matrixStack.top());
matrixStack.pop();
if(!b3D) return;
modelView = modelViewStack.top();
modelViewStack.pop();
}
void ofCairoRenderer::translate(float x, float y, float z ){
if(!surface || !cr) return;
cairo_matrix_t matrix;
cairo_get_matrix(cr,&matrix);
cairo_matrix_translate(&matrix,x,y);
cairo_set_matrix(cr,&matrix);
if(!b3D) return;
modelView = glm::translate(modelView, {x,y,z});
}
void ofCairoRenderer::translate(const glm::vec3 & p){
translate(p.x,p.y,p.z);
}
void ofCairoRenderer::scale(float xAmnt, float yAmnt, float zAmnt ){
if(!surface || !cr) return;
cairo_matrix_t matrix;
cairo_get_matrix(cr,&matrix);
cairo_matrix_scale(&matrix,xAmnt,yAmnt);
cairo_set_matrix(cr,&matrix);
if(!b3D) return;
modelView = glm::scale(modelView, {xAmnt,yAmnt,zAmnt});
}
void ofCairoRenderer::matrixMode(ofMatrixMode mode){
currentMatrixMode = mode;
}
void ofCairoRenderer::loadIdentityMatrix (void){
if(!surface || !cr) return;
if(currentMatrixMode==OF_MATRIX_MODELVIEW){
cairo_matrix_t matrix;
cairo_matrix_init_identity(&matrix);
cairo_set_matrix(cr,&matrix);
}
if(!b3D) return;
if(currentMatrixMode==OF_MATRIX_MODELVIEW){
modelView = glm::mat4(1.0);
}else if(currentMatrixMode==OF_MATRIX_PROJECTION){
projection = glm::mat4(1.0);
}
}
void ofCairoRenderer::loadMatrix (const glm::mat4 & m){
if(!surface || !cr) return;
if(!b3D) return;
if(currentMatrixMode==OF_MATRIX_MODELVIEW){
modelView = m;
}else if(currentMatrixMode==OF_MATRIX_PROJECTION){
projection = m;
}
}
void ofCairoRenderer::loadMatrix (const float * m){
loadMatrix(glm::make_mat4(m));
}
void ofCairoRenderer::multMatrix (const glm::mat4 & m){
if(!surface || !cr) return;
if(!b3D) return;
if(currentMatrixMode==OF_MATRIX_MODELVIEW){
modelView = m * modelView;
}else if(currentMatrixMode==OF_MATRIX_PROJECTION){
projection = m * projection;
}
}
void ofCairoRenderer::multMatrix (const float * m){
multMatrix(glm::make_mat4(m));
}
void ofCairoRenderer::rotateRad(float radians, float vecX, float vecY, float vecZ){
if(!surface || !cr) return;
if(vecZ == 1.0f) {
cairo_matrix_t matrix;
cairo_get_matrix(cr,&matrix);
cairo_matrix_rotate(&matrix,radians);
cairo_set_matrix(cr,&matrix);
}
if(!b3D) return;
modelView = glm::rotate(modelView, radians, glm::vec3(vecX,vecY,vecZ));
}
void ofCairoRenderer::rotateXRad(float radians){
rotateRad(radians,1,0,0);
}
void ofCairoRenderer::rotateYRad(float radians){
rotateRad(radians,0,1,0);
}
void ofCairoRenderer::rotateZRad(float radians){
rotateRad(radians,0,0,1);
}
void ofCairoRenderer::rotateRad(float radians){
rotateZRad(radians);
}
void ofCairoRenderer::setupScreen(){
if(!surface || !cr) return;
setupScreenPerspective();
}
void ofCairoRenderer::pushView(){
viewportStack.push(viewportRect);
}
void ofCairoRenderer::popView(){
viewportRect = viewportStack.top();
viewportStack.pop();
};
void ofCairoRenderer::viewport(ofRectangle v){
viewport(v.x,v.y,v.width,v.height);
}
void ofCairoRenderer::viewport(float x, float y, float width, float height, bool invertY){
if(width < 0) width = originalViewport.width;
if(height < 0) height = originalViewport.height;
ofLogVerbose("ofCairoRenderer::viewport") << "Setting viewport to:" << width << ", " << height;
if (invertY){
y = -y;
}
viewportRect.set(x, y, width, height);
cairo_reset_clip(cr);
cairo_new_path(cr);
cairo_move_to(cr,viewportRect.x,viewportRect.y);
cairo_line_to(cr,viewportRect.x+viewportRect.width,viewportRect.y);
cairo_line_to(cr,viewportRect.x+viewportRect.width,viewportRect.y+viewportRect.height);
cairo_line_to(cr,viewportRect.x,viewportRect.y+viewportRect.height);
cairo_clip(cr);
};
void ofCairoRenderer::setupScreenPerspective(float width, float height, float fov, float nearDist, float farDist){
if(!b3D) return;
if(width < 0) width = originalViewport.width;
if(height < 0) height = originalViewport.height;
ofOrientation orientation = ofGetOrientation();
float viewW = originalViewport.width;
float viewH = originalViewport.height;
float eyeX = viewW / 2;
float eyeY = viewH / 2;
float halfFov = PI * fov / 360;
float theTan = tanf(halfFov);
float dist = eyeY / theTan;
float aspect = (float) viewW / viewH;
if(nearDist == 0) nearDist = dist / 10.0f;
if(farDist == 0) farDist = dist * 10.0f;
projection = glm::perspective(ofDegToRad(fov),aspect,nearDist,farDist);
modelView = glm::lookAt(glm::vec3(eyeX,eyeY,dist),glm::vec3(eyeX,eyeY,0),glm::vec3(0,1,0));
switch(orientation) {
case OF_ORIENTATION_180:
modelView = glm::rotate(modelView,-glm::pi<float>(),glm::vec3(0,0,1));
if(isVFlipped()){
modelView = glm::scale(modelView, glm::vec3(-1,1,1));
modelView = glm::translate(modelView, glm::vec3(width,0,0));
}else{
modelView = glm::translate(modelView, glm::vec3(width, -height, 0));
}
break;
case OF_ORIENTATION_90_RIGHT:
modelView = glm::rotate(modelView,-glm::half_pi<float>(),glm::vec3(0,0,1));
if(!isVFlipped()){
modelView = glm::scale(modelView, glm::vec3(1,-1,1));
modelView = glm::translate(modelView, glm::vec3(-width,-height,0));
}
break;
case OF_ORIENTATION_90_LEFT:
modelView = glm::rotate(modelView,glm::half_pi<float>(),glm::vec3(0,0,1));
if(isVFlipped()){
modelView = glm::translate(modelView, glm::vec3(0,-height,0));
}else{
modelView = glm::scale(modelView, glm::vec3(1,-1,1));
}
break;
case OF_ORIENTATION_DEFAULT:
default:
if(isVFlipped()){
modelView = glm::scale(modelView, glm::vec3(-1,-1,1));
modelView = glm::translate(modelView, glm::vec3(-width,-height,0));
}
break;
}
};
void ofCairoRenderer::setupScreenOrtho(float width, float height, float nearDist, float farDist){
if(!b3D) return;
if(width < 0) width = viewportRect.width;
if(height < 0) height = viewportRect.height;
ofOrientation orientation = ofGetOrientation();
float viewW = viewportRect.width;
float viewH = viewportRect.height;
ofSetCoordHandedness(OF_RIGHT_HANDED);
if(isVFlipped()) {
ofSetCoordHandedness(OF_LEFT_HANDED);
}
projection = glm::ortho(0.f, viewW, 0.f, viewH, nearDist, farDist);
modelView = glm::mat4(1.0f);
switch(orientation) {
case OF_ORIENTATION_180:
modelView = glm::rotate(modelView,-glm::pi<float>(),glm::vec3(0,0,1));
if(isVFlipped()){
modelView = glm::scale(modelView, glm::vec3(-1,1,1));
modelView = glm::translate(modelView, glm::vec3(width,0,0));
}else{
modelView = glm::translate(modelView, glm::vec3(width, -height, 0));
}
break;
case OF_ORIENTATION_90_RIGHT:
modelView = glm::rotate(modelView,-glm::half_pi<float>(),glm::vec3(0,0,1));
if(!isVFlipped()){
modelView = glm::scale(modelView, glm::vec3(1,-1,1));
modelView = glm::translate(modelView, glm::vec3(-width,-height,0));
}
break;
case OF_ORIENTATION_90_LEFT:
modelView = glm::rotate(modelView,glm::half_pi<float>(),glm::vec3(0,0,1));
if(isVFlipped()){
modelView = glm::translate(modelView, glm::vec3(0,-height,0));
}else{
modelView = glm::scale(modelView, glm::vec3(1,-1,1));
}
break;
case OF_ORIENTATION_DEFAULT:
default:
if(isVFlipped()){
modelView = glm::scale(modelView, glm::vec3(-1,-1,1));
modelView = glm::translate(modelView, glm::vec3(-width,-height,0));
}
break;
}
};
ofRectangle ofCairoRenderer::getCurrentViewport() const{
return viewportRect;
};
ofRectangle ofCairoRenderer::getNativeViewport() const{
return viewportRect;
};
int ofCairoRenderer::getViewportWidth() const{
return viewportRect.width;
};
int ofCairoRenderer::getViewportHeight() const{
return viewportRect.height;
};
void ofCairoRenderer::setOrientation(ofOrientation orientation, bool vFlip){
ofLogError("ofCairoRenderer") << "orientation not supported yet";
}
bool ofCairoRenderer::isVFlipped() const{
return true;
}
void ofCairoRenderer::loadViewMatrix(const glm::mat4 & m){
ofLogError("ofCairoRenderer") << "view matrix not supported yet";
}
void ofCairoRenderer::multViewMatrix(const glm::mat4 & m){
ofLogError("ofCairoRenderer") << "view matrix not supported yet";
}
glm::mat4 ofCairoRenderer::getCurrentViewMatrix() const{
ofLogError("ofCairoRenderer") << "view matrix not supported yet";
return glm::mat4(1.0);
}
glm::mat4 ofCairoRenderer::getCurrentNormalMatrix() const{
ofLogError("ofCairoRenderer") << "normal matrix not supported yet";
return glm::mat4(1.0);
}
glm::mat4 ofCairoRenderer::getCurrentOrientationMatrix() const{
ofLogError("ofCairoRenderer") << "orientation matrix not supported yet";
return glm::mat4(1.0);
}
void ofCairoRenderer::setCircleResolution(int){
}
void ofCairoRenderer::setPolyMode(ofPolyWindingMode mode){
currentStyle.polyMode = mode;
path.setPolyWindingMode(mode);
}
void ofCairoRenderer::setCoordHandedness(ofHandednessType handedness){
};
ofHandednessType ofCairoRenderer::getCoordHandedness() const{
return OF_LEFT_HANDED;
};
void ofCairoRenderer::setupGraphicDefaults(){
setStyle(ofStyle());
path.setMode(ofPath::COMMANDS);
path.setUseShapeColor(false);
clear();
};
void ofCairoRenderer::clear(){
if(!surface || ! cr) return;
cairo_set_source_rgba(cr,currentStyle.bgColor.r/255., currentStyle.bgColor.g/255., currentStyle.bgColor.b/255., currentStyle.bgColor.a/255.);
cairo_paint(cr);
setColor(currentStyle.color);
}
void ofCairoRenderer::clear(float r, float g, float b, float a) {
if(!surface || ! cr) return;
cairo_set_source_rgba(cr,r/255., g/255., b/255., a/255.);
cairo_paint(cr);
setColor(currentStyle.color);
}
void ofCairoRenderer::clear(float brightness, float a) {
clear(brightness, brightness, brightness, a);
}
void ofCairoRenderer::clearAlpha() {
}
void ofCairoRenderer::setBitmapTextMode(ofDrawBitmapMode mode){
currentStyle.drawBitmapMode = mode;
}
ofStyle ofCairoRenderer::getStyle() const{
return currentStyle;
}
void ofCairoRenderer::pushStyle(){
styleHistory.push_back(currentStyle);
if( styleHistory.size() > OF_MAX_STYLE_HISTORY ){
styleHistory.pop_front();
ofLogWarning("ofGraphics") << "ofPushStyle(): maximum number of style pushes << " << OF_MAX_STYLE_HISTORY << " reached, did you forget to pop somewhere?";
}
}
void ofCairoRenderer::popStyle(){
if( styleHistory.size() ){
setStyle(styleHistory.back());
styleHistory.pop_back();
}
}
void ofCairoRenderer::setBackgroundAuto(bool bAuto){
bBackgroundAuto = bAuto;
}
bool ofCairoRenderer::getBackgroundAuto(){
return bBackgroundAuto;
}
void ofCairoRenderer::setBackgroundColor(const ofColor & c){
currentStyle.bgColor = c;
}
ofColor ofCairoRenderer::getBackgroundColor(){
return currentStyle.bgColor;
}
void ofCairoRenderer::background(const ofColor & c){
setBackgroundColor(c);
clear(c.r,c.g,c.b,c.a);
}
void ofCairoRenderer::background(float brightness) {
background(ofColor(brightness));
}
void ofCairoRenderer::background(int hexColor, float _a){
background ( (hexColor >> 16) & 0xff, (hexColor >> 8) & 0xff, (hexColor >> 0) & 0xff, _a);
}
void ofCairoRenderer::background(int r, int g, int b, int a){
background(ofColor(r,g,b,a));
}
void ofCairoRenderer::drawLine(float x1, float y1, float z1, float x2, float y2, float z2) const{
cairo_new_path(cr);
cairo_move_to(cr,x1,y1);
cairo_line_to(cr,x2,y2);
cairo_stroke( cr );
}
void ofCairoRenderer::drawRectangle(float x, float y, float z, float w, float h) const{
cairo_new_path(cr);
if (currentStyle.rectMode == OF_RECTMODE_CORNER){
cairo_move_to(cr,x,y);
cairo_line_to(cr,x+w, y);
cairo_line_to(cr,x+w, y+h);
cairo_line_to(cr,x, y+h);
}else{
cairo_move_to(cr,x-w/2.0f, y-h/2.0f);
cairo_line_to(cr,x+w/2.0f, y-h/2.0f);
cairo_line_to(cr,x+w/2.0f, y+h/2.0f);
cairo_line_to(cr,x-w/2.0f, y+h/2.0f);
}
cairo_close_path(cr);
if(currentStyle.bFill==OF_FILLED){
cairo_fill( cr );
}else{
cairo_stroke( cr );
}
}
void ofCairoRenderer::drawTriangle(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3) const{
cairo_new_path(cr);
cairo_move_to(cr, x1, y1);
cairo_line_to(cr, x2, y2);
cairo_line_to(cr, x3, y3);
cairo_close_path(cr);
if(currentStyle.bFill==OF_FILLED){
cairo_fill( cr );
}else{
cairo_stroke( cr );
}
}
void ofCairoRenderer::drawCircle(float x, float y, float z, float radius) const{
cairo_new_path(cr);
cairo_arc(cr, x,y,radius,0,2*PI);
cairo_close_path(cr);
if(currentStyle.bFill==OF_FILLED){
cairo_fill( cr );
}else{
cairo_stroke( cr );
}
}
void ofCairoRenderer::enableAntiAliasing(){
cairo_set_antialias(cr,CAIRO_ANTIALIAS_SUBPIXEL);
}
void ofCairoRenderer::disableAntiAliasing(){
cairo_set_antialias(cr,CAIRO_ANTIALIAS_NONE);
}
void ofCairoRenderer::drawEllipse(float x, float y, float z, float width, float height) const{
ofCairoRenderer * mutThis = const_cast<ofCairoRenderer*>(this);
cairo_new_path(cr);
float ellipse_ratio = height/width;
mutThis->pushMatrix();
mutThis->translate(0,-y*ellipse_ratio);
mutThis->scale(1,ellipse_ratio);
mutThis->translate(0,y/ellipse_ratio);
cairo_arc(cr,x,y,width*0.5,0,2*PI);
mutThis->popMatrix();
cairo_close_path(cr);
if(currentStyle.bFill==OF_FILLED){
cairo_fill( cr );
}else{
cairo_stroke( cr );
}
}
void ofCairoRenderer::drawString(string text, float x, float y, float z) const{
cairo_select_font_face (cr, "Mono", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size (cr, 10);
vector<string> lines = ofSplitString(text, "\n");
for(int i=0;i<(int)lines.size();i++){
cairo_move_to (cr, x, y+i*14.3);
cairo_show_text (cr, lines[i].c_str() );
}
}
void ofCairoRenderer::drawString(const ofTrueTypeFont & font, string text, float x, float y) const{
font.drawStringAsShapes(text,x,y);
}
cairo_t * ofCairoRenderer::getCairoContext(){
return cr;
}
cairo_surface_t * ofCairoRenderer::getCairoSurface(){
return surface;
}
ofPixels & ofCairoRenderer::getImageSurfacePixels(){
if(type!=IMAGE){
ofLogError("ofCairoRenderer") << "getImageSurfacePixels(): can only get pixels from image surface";
}
return imageBuffer;
}
ofBuffer & ofCairoRenderer::getContentBuffer(){
if(filename!="" || (type!=SVG && type!=PDF)){
ofLogError("ofCairoRenderer") << "getContentBuffer(): can only get buffer from memory allocated renderer for svg or pdf";
}
return streamBuffer;
}
const of3dGraphics & ofCairoRenderer::get3dGraphics() const{
return graphics3d;
}
of3dGraphics & ofCairoRenderer::get3dGraphics(){
return graphics3d;
}
Comments