void ofCairoRenderer::setup(string _filename, Type _type, bool multiPage_, bool b3D_, ofRectangle _viewport){
	if( _viewport.width == 0 || _viewport.height == 0 ){
		_viewport.set(0, 0, ofGetWidth(), ofGetHeight());
	}
	
	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{ // default to image
			type = IMAGE;
		}
	}

	switch(type){
	case PDF:
		if(filename==""){
			surface = cairo_pdf_surface_create_for_stream(&ofCairoRenderer::stream_function,this,_viewport.width, _viewport.height);
		}else{
			surface = cairo_pdf_surface_create(ofToDataPath(filename).c_str(),_viewport.width, _viewport.height);
		}
		break;
	case SVG:
		if(filename==""){
			surface = cairo_svg_surface_create_for_stream(&ofCairoRenderer::stream_function,this,_viewport.width, _viewport.height);
		}else{
			surface = cairo_svg_surface_create(ofToDataPath(filename).c_str(),_viewport.width, _viewport.height);
		}
		break;
	case IMAGE:
		imageBuffer.allocate(_viewport.width, _viewport.height, 4);
		surface = cairo_image_surface_create_for_data(imageBuffer.getPixels(),CAIRO_FORMAT_ARGB32,_viewport.width, _viewport.height,_viewport.width*4);
		break;
	case FROM_FILE_EXTENSION:
		ofLogFatalError("ofCairoRenderer") << "Type not determined from file extension!";
		break;
	default:
		ofLogError("ofCairoRenderer") << "Unknown type encountered!";
		break;
	}

	cr = cairo_create(surface);
	cairo_set_antialias(cr,CAIRO_ANTIALIAS_SUBPIXEL);
	viewportRect = _viewport;
	viewport(viewportRect);
	page = 0;
	b3D = b3D_;
	multiPage = multiPage_;
	setStyle(ofGetStyle());
}
//-----------------------------------------------------------------------------------
void ofxCairoTexture::setup(bool b3D_, ofRectangle viewport_){

	if( viewport_.width == 0 || viewport_.height == 0 ){
		viewport_.set(0, 0, ofGetWidth(), ofGetHeight());
	}
	
	//printf("ofxCairoTexture::setup %f %f\n", viewport_.width, viewport_.height);
	
	surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, viewport_.width, viewport_.height);
	cr = cairo_create (surface);
	
	viewportRect = viewport_;
	viewport(viewportRect);
	
	b3D = b3D_;
}
void ofCairoRenderer::setup(string filename, Type type, bool multiPage_, bool b3D_, ofRectangle _viewport){
	if( _viewport.width == 0 || _viewport.height == 0 ){
		_viewport.set(0, 0, ofGetWidth(), ofGetHeight());
	}
	
	switch(type){
	case PDF:
		surface = cairo_pdf_surface_create(ofToDataPath(filename).c_str(),_viewport.width, _viewport.height);
		break;
	case SVG:
		surface = cairo_svg_surface_create(ofToDataPath(filename).c_str(),_viewport.width, _viewport.height);
		break;
	}

	cr = cairo_create(surface);
	viewportRect = _viewport;
	viewport(viewportRect);
	page = 0;
	b3D = b3D_;
	multiPage = multiPage_;
}
void ofCairoRenderer::setup(string _filename, Type _type, bool multiPage_, bool b3D_, ofRectangle _viewport){
	if( _viewport.width == 0 || _viewport.height == 0 ){
		_viewport.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{ // default to image
			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,_viewport.width, _viewport.height);
		}else{
			surface = cairo_pdf_surface_create(ofToDataPath(filename).c_str(),_viewport.width, _viewport.height);
		}
		break;
	case SVG:
		if(filename==""){
			surface = cairo_svg_surface_create_for_stream(&ofCairoRenderer::stream_function,this,_viewport.width, _viewport.height);
		}else{
			surface = cairo_svg_surface_create(ofToDataPath(filename).c_str(),_viewport.width, _viewport.height);
		}
		break;
	case IMAGE:
		imageBuffer.allocate(_viewport.width, _viewport.height, OF_PIXELS_BGRA);
		imageBuffer.set(0);
		surface = cairo_image_surface_create_for_data(imageBuffer.getData(),CAIRO_FORMAT_ARGB32,_viewport.width, _viewport.height,_viewport.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 = _viewport;
	originalViewport = _viewport;
	viewport(viewportRect);
	page = 0;
	b3D = b3D_;
	multiPage = multiPage_;
}
void Controller::setup(ofTexture * texture, ofVec2f originalSize, ofRectangle originalCoordinates, ofPoint originalPerspective[4], string name, float guiWidth, float guiHeight ){
    // Arguments
    this->texture = texture;
	this->name = name;
    this->guiWidth = guiWidth;
    this->guiHeight = guiHeight;
    if(originalSize.x==0.0
       && originalSize.y==0.0){
        originalSize.set(texture->getWidth(), texture->getHeight());
    }
    this->originalSize = originalSize;
    if(originalCoordinates.x==0.0
       && originalCoordinates.y==0.0
       && originalCoordinates.width==0.0
       && originalCoordinates.height==0.0){
        if(ofGetUsingNormalizedTexCoords())originalCoordinates.set(0.0, 0.0, 1.0, 1.0);
        else originalCoordinates.set(0.0, 0.0, texture->getWidth(), texture->getHeight());
    }
    this->originalCoordinates = originalCoordinates;
    if(originalPerspective[0].x == 0 && originalPerspective[0].y == 0
       && originalPerspective[1].x == 0 && originalPerspective[1].y == 0
       && originalPerspective[2].x == 0 && originalPerspective[2].y == 0
       && originalPerspective[3].x == 0 && originalPerspective[3].y == 0){
        originalPerspective[0].x = ofGetWidth()/2 - texture->getWidth()/2;
        originalPerspective[0].y = ofGetHeight()/2 - texture->getHeight()/2;
        originalPerspective[1].x = ofGetWidth()/2 + texture->getWidth()/2;
        originalPerspective[1].y = ofGetHeight()/2 - texture->getHeight()/2;
        originalPerspective[2].x = ofGetWidth()/2 + texture->getWidth()/2;
        originalPerspective[2].y = ofGetHeight()/2 + texture->getHeight()/2;
        originalPerspective[3].x = ofGetWidth()/2 - texture->getWidth()/2;
        originalPerspective[3].y = ofGetHeight()/2 + texture->getHeight()/2;
    }
    memcpy(this->originalPerspective,originalPerspective,sizeof(ofPoint)*4);
    
	// Register draw event for the enable/disable magic
	ofAddListener(ofEvents().draw, this, &Controller::drawEvent);
	drawing = false;
    drawn = true;
	
	// Some intial setup
	guiHelperFbo.allocate(150, 60);
	blendB = blendL = blendR = blendT = 0;
	lastClickTime = ofGetElapsedTimeMillis();
	historyIndex = -1;
    guiHasChanged = false;
    perspectiveHasChanged = false;

	// Generate filenames
	safename = safe_string(name);
	stringstream ss_guiFile;
	ss_guiFile << safename << "/gui.xml";	
	guiFile = ss_guiFile.str();
	
	stringstream ss_perspectiveFile;
	ss_perspectiveFile << safename << "/perspective.xml";
	perspectiveFile = ss_perspectiveFile.str();
	
	stringstream ss_meshFile;
	ss_meshFile << safename << "/mesh";
	meshFile = ss_meshFile.str();
	
	// Perspective
    perspective.setup(0, 0, getWindowWidth(), getWindowHeight());
    perspective.setCornerSensibility(0.03);
    perspective.activate();
    ofAddListener(perspective.changeEvent, this, &Controller::onPerspectiveChange);
    
	// Meshes
    controlMesh.setMode(OF_PRIMITIVE_POINTS);
	internalMesh.setMode(OF_PRIMITIVE_TRIANGLES);
	
	// GUI
    gui.setup(name, guiFile,guiWidth, guiHeight);
	gui.setPosition(originalPerspective[0].x, originalPerspective[0].y);
	
	ofxButton * load = new ofxButton();
    load->setup("Load", guiWidth, guiHeight);
    load->addListener(this, &Controller::onLoad);
    gui.add(load);
	
	ofxButton * save = new ofxButton();
    save->setup("Save", guiWidth, guiHeight);
    save->addListener(this, &Controller::onSave);
    gui.add(save);
    
    ofxToggle * enablePerspective = new ofxToggle();
    enablePerspective->setup("Perspective Warp", true, guiWidth, guiHeight);
    enablePerspective->addListener(this, &Controller::onEnablePerspective);
    gui.add(enablePerspective);
    
    ofxButton * resetPerspective = new ofxButton();
    resetPerspective->setup("Reset Perspective", guiWidth, guiHeight);
    resetPerspective->addListener(this, &Controller::onResetPerspective);
    gui.add(resetPerspective);
    
    ofxButton * resetMesh = new ofxButton();
    resetMesh->setup("Reset Mesh", guiWidth, guiHeight);
    resetMesh->addListener(this, &Controller::onResetMesh);
    gui.add(resetMesh);
    
    ofxIntSlider * resolutionX = new ofxIntSlider();
    resolutionX->setup("Horizontal Resolution", 4, 1, 20, guiWidth, guiHeight);
    resolutionX->addListener(this, &Controller::onGridChange);
    gui.add(resolutionX);
	
	ofxIntSlider * resolutionY = new ofxIntSlider();
    resolutionY->setup("Vertical Resolution", 4, 1, 20, guiWidth, guiHeight);
    resolutionY->addListener(this, &Controller::onGridChange);
    gui.add(resolutionY);
    
    ofxIntSlider * columns = new ofxIntSlider();
    columns->setup("Grid Columns", 4, 1, 20, guiWidth, guiHeight);
    columns->addListener(this, &Controller::onGridChange);
    gui.add(columns);
    
    ofxIntSlider * rows = new ofxIntSlider();
    rows->setup("Grid Rows", 4, 1, 20, guiWidth, guiHeight);
    rows->addListener(this, &Controller::onGridChange);
    gui.add(rows);
    
    ofxFloatSlider * startX = new ofxFloatSlider();
	float startXmin = originalCoordinates.x;
	float startXmax = originalCoordinates.x+originalCoordinates.width;
	float startXmargin = (startXmax - startXmin) * 0.3333;
    startX->setup("UV Start X", startXmin, startXmin - startXmargin, startXmax + startXmargin, guiWidth, guiHeight);
    startX->addListener(this, &Controller::onCoordinatesChange);
    gui.add(startX);
    
    ofxFloatSlider * startY = new ofxFloatSlider();
	float startYmin = originalCoordinates.y;
	float startYmax = originalCoordinates.y+originalCoordinates.height;
	float startYmargin = (startYmax - startYmin) * 0.3333;
    startY->setup("UV Start Y", startYmin, startYmin - startYmargin, startYmax + startYmargin, guiWidth, guiHeight);
    startY->addListener(this, &Controller::onCoordinatesChange);
    gui.add(startY);
    
    ofxFloatSlider * endX = new ofxFloatSlider();
	float endXmin = originalCoordinates.x;
	float endXmax = originalCoordinates.x+originalCoordinates.width;
	float endXmargin = (endXmax - endXmin) * 0.3333;
    endX->setup("UV End X", endXmax, endXmin - endXmargin, endXmax + endXmargin, guiWidth, guiHeight);
    endX->addListener(this, &Controller::onCoordinatesChange);
    gui.add(endX);
    
    ofxFloatSlider * endY = new ofxFloatSlider();
	float endYmin = originalCoordinates.y;
	float endYmax = originalCoordinates.y+originalCoordinates.height;
	float endYmargin = (endYmax - endYmin) * 0.3333;
    endY->setup("UV End Y", endYmax, endYmin - endYmargin, endYmax + endYmargin, guiWidth, guiHeight);
    endY->addListener(this, &Controller::onCoordinatesChange);
    gui.add(endY);
	
	ofxFloatSlider * blendTSlider = new ofxFloatSlider();
    blendTSlider->setup("Blend Top", 0, 0, 1, guiWidth, guiHeight);
    blendTSlider->addListener(this, &Controller::onBlendChange);
    gui.add(blendTSlider);
	
	ofxFloatSlider * blendDSlider = new ofxFloatSlider();
    blendDSlider->setup("Blend Down", 0, 0, 1, guiWidth, guiHeight);
    blendDSlider->addListener(this, &Controller::onBlendChange);
    gui.add(blendDSlider);
	
	ofxFloatSlider * blendLSlider = new ofxFloatSlider();
    blendLSlider->setup("Blend Left", 0, 0, 1, guiWidth, guiHeight);
    blendLSlider->addListener(this, &Controller::onBlendChange);
    gui.add(blendLSlider);
	
	ofxFloatSlider * blendRSlider = new ofxFloatSlider();
    blendRSlider->setup("Blend Right", 0, 0, 1, guiWidth, guiHeight);
    blendRSlider->addListener(this, &Controller::onBlendChange);
    gui.add(blendRSlider);
    
    ofxToggle * scissorEnable = new ofxToggle();
    scissorEnable->setup("Scissor Active", false, guiWidth, guiHeight);
    scissorEnable->addListener(this, &Controller::onScissorEnabled);
    gui.add(scissorEnable);
    
    ofxIntSlider * scissorX = new ofxIntSlider();
    scissorX->setup("Scissor Start X", 0, 0, ofGetScreenWidth(), guiWidth, guiHeight);
    scissorX->addListener(this, &Controller::onScissorChange);
    gui.add(scissorX);
    
    ofxIntSlider * scissorY = new ofxIntSlider();
    scissorY->setup("Scissor Start Y", 0, 0, ofGetScreenHeight(), guiWidth, guiHeight);
    scissorY->addListener(this, &Controller::onScissorChange);
    gui.add(scissorY);
    
    ofxIntSlider * scissorWidth = new ofxIntSlider();
    scissorWidth->setup("Scissor End X", ofGetScreenWidth(), 0, ofGetScreenWidth(), guiWidth, guiHeight);
    scissorWidth->addListener(this, &Controller::onScissorChange);
    gui.add(scissorWidth);
    
    ofxIntSlider * scissorHeight = new ofxIntSlider();
    scissorHeight->setup("Scissor End Y", ofGetScreenHeight(), 0, ofGetScreenHeight(), guiWidth, guiHeight);
    scissorHeight->addListener(this, &Controller::onScissorChange);
    gui.add(scissorHeight);
	
	
	ofxFloatSlider * postBrtSlider = new ofxFloatSlider();
    postBrtSlider->setup("Brighteness", 1, 0, 5, guiWidth, guiHeight);
    postBrtSlider->addListener(this, &Controller::onPostProcessingValueChanged);
    gui.add(postBrtSlider);
	
	ofxFloatSlider * postConSlider = new ofxFloatSlider();
    postConSlider->setup("Contrast", 1, 0, 5, guiWidth, guiHeight);
    postConSlider->addListener(this, &Controller::onPostProcessingValueChanged);
    gui.add(postConSlider);
	
	ofxFloatSlider * postSatSlider = new ofxFloatSlider();
    postSatSlider->setup("Saturation", 1, 0, 5, guiWidth, guiHeight);
    postSatSlider->addListener(this, &Controller::onPostProcessingValueChanged);
    gui.add(postSatSlider);
	
	ofxFloatSlider * postRMultSlider = new ofxFloatSlider();
    postRMultSlider->setup("Red Multiplier", 1, 0, 5, guiWidth, guiHeight);
    postRMultSlider->addListener(this, &Controller::onPostProcessingValueChanged);
    gui.add(postRMultSlider);
	
	ofxFloatSlider * postGmultSlider = new ofxFloatSlider();
    postGmultSlider->setup("Green Multiplier", 1, 0, 5, guiWidth, guiHeight);
    postGmultSlider->addListener(this, &Controller::onPostProcessingValueChanged);
    gui.add(postGmultSlider);
	
	ofxFloatSlider * postBMultSlider = new ofxFloatSlider();
    postBMultSlider->setup("Blue Multipler", 1, 0, 5, guiWidth, guiHeight);
    postBMultSlider->addListener(this, &Controller::onPostProcessingValueChanged);
    gui.add(postBMultSlider);
	
	ofxIntSlider * lineThickness = new ofxIntSlider();
    lineThickness->setup("GUI Lines Thickness", 1, 1, 10, guiWidth, guiHeight);
    lineThickness->addListener(this, &Controller::onGuiLinesThicknessChange);
    gui.add(lineThickness);
    
	
	// Post processing
    if(ofIsGLProgrammableRenderer()){
	shader.setupShaderFromSource(GL_VERTEX_SHADER, VertShaderProgrammable);
	if(ofGetUsingNormalizedTexCoords()) shader.setupShaderFromSource(GL_FRAGMENT_SHADER, NormalizedFragShaderProgrammable);
        else shader.setupShaderFromSource(GL_FRAGMENT_SHADER, UnnormalizedFragShaderProgrammable);
        shader.bindDefaults();
	shader.linkProgram();
    }
    else {
        shader.setupShaderFromSource(GL_VERTEX_SHADER, VertShader);
	if(ofGetUsingNormalizedTexCoords()) shader.setupShaderFromSource(GL_FRAGMENT_SHADER, NormalizedFragShader);
	else shader.setupShaderFromSource(GL_FRAGMENT_SHADER, UnnormalizedFragShader);
	shader.linkProgram();
    }
	
	// load settings
	onLoad();
	
	
}