commit 8c57b315af118a6a159da57b6bf85d5194eb44ab Author: Joshua Date: Sat Mar 27 21:46:59 2010 +0100 initial state, going to make AwesomeAttractor more OO diff --git a/Attractor.cpp b/Attractor.cpp new file mode 100644 index 0000000..5685ebd --- /dev/null +++ b/Attractor.cpp @@ -0,0 +1,345 @@ +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +#include "Attractor.hpp" + + +/* + Constructors & initialisers +*/ +Attractor::Attractor(): + dim(3), par(4), formula(LORENZ), param(NULL), point(NULL), new_point(NULL) { + // Default attractor: 3D Lorenz attrractor + init_vector(); + init_param(); + + param[0] = 0.001; //dt + param[1] = 10; //sigma + param[2] = 28; //rho + param[3] = 8.0/3.0; //beta + +} + +Attractor::Attractor(unsigned int dimensions, FormulaChoice formula, unsigned int orde): + dim(dimensions), formula(formula), orde(orde), param(NULL), point(NULL), new_point(NULL) { + + init(dimensions, formula, orde); +} + +Attractor::Attractor(const char* const filename) { + ifstream file(filename); + + cout << "Reading file " << filename << "..." << endl; + + if ( !file ) { + cout << " Error reading file '" << filename << "' dying now..." << endl; + exit(2); + } + + /* + file.attr: + lorenz + 3 + 0 + + 3.24454 + 1.25 + .... + */ + + string fileFormula; + file >> fileFormula; + + for ( unsigned int i = 0; fileFormula[i] != '\0'; i++ ) { + fileFormula[i] = tolower(fileFormula[i]); + } + + unsigned int fileDim; + file >> fileDim; + + unsigned int fileOrde; + file >> fileOrde; + + cout << " Formula: " << fileFormula << endl; + cout << " Dimensions: " << fileDim << endl; + cout << " Orde: " << fileOrde << endl; + + if ( fileFormula == "lorenz" ) + init(fileDim, LORENZ, fileOrde); + else if ( fileFormula == "poly_n" ) + init(fileDim, POLY_N, fileOrde); + else if ( fileFormula == "poly_a" ) + init(fileDim, POLY_A, fileOrde); + else if ( fileFormula == "logistic" ) + init(fileDim, LOGISTIC, fileOrde); + else if ( fileFormula == "unravel" ) + init(fileDim, UNRAVEL, fileOrde); + else { + cout << " Formula not (yet) supported" << endl; + exit(3); + } + + for ( unsigned int i = 0; i < par; i++ ) { + file >> param[i]; + cout << " Parameter " << i << " set to " << param[i] << ", "; + } + + cout << endl << " Reading file complete" << endl; +} + +void Attractor::init(unsigned int dim_in, FormulaChoice formula_in, unsigned int orde_in) { + + dim = dim_in; + formula = formula_in; + orde = orde_in; + + switch (formula) { + case POLY_N: { + double n_coef = orde + 1; + for (unsigned int i = 2; i <= dim; i++) { + n_coef = n_coef*(orde + i)/(i - 1); + } + + par = (unsigned int) n_coef; + break; + } + case LORENZ: { + assert(dim == 3 || dim == 4); + par = dim + 1; + break; + } + case POLY_A: { + par = dim; + break; + } + case LOGISTIC: { + par = dim; + break; + } + case UNRAVEL: { + assert(dim == 3); + par = 7; + break; + } + default: { + cout << "Formula not recognized" << endl; + exit(1); + break; + } + } + + init_vector(); + init_param(); +} + +void Attractor::init_vector() { + point = new double[dim]; + new_point = new double[dim]; + + assert(point != NULL); + assert(new_point != NULL); + + for ( unsigned int i = 0; i < dim; i++ ) { + point[i] = new_point[i] = 0.9; + } +} + +void Attractor::init_param() { + param = new double[par]; + + assert(param != NULL); + + for ( unsigned int i = 0; i < dim; i++ ) { + param[i] = 0.0; + } +} + +void Attractor::init_range() { + + // stabilize attractor + for ( unsigned int i = 0; i < 100000; i++ ) { + iterate(); + if ( !is_chaos() ) { + cout << "Attractor died after " << i << " iterations" << endl; + exit(0); + } + } + + // initialize ranges + for ( vector::iterator it = projectors.begin(); it != projectors.end(); it++ ) { + (*it)->extern_dim = dim; + (*it)->intern_dim = 2; + (*it)->init(point); + } + + // update ranges + for ( unsigned int i = 0; i < 100000; i++ ) { + iterate(); + if ( !is_chaos() ) { + cout << "Attractor died after " << i << " iterations" << endl; + exit(0); + } + + for ( vector::iterator it = projectors.begin(); it != projectors.end(); it++ ) { + (*it)->update_range(point); + } + + } + for ( vector::iterator it = projectors.begin(); it != projectors.end(); it++ ) { + (*it)->finish_range(); + } +} + +bool Attractor::is_chaos() { + /* + check existence of attractor: + Escaping + Single point attractor + Lyapunov exponent + */ + + double sum = 0; + for ( unsigned int i = 0; i < dim; i++ ) { + const double dist = new_point[i] - point[i]; + sum += dist*dist; + } + if ( sum >= 1.0e7 ) { + // big change => Escaping + return false; + } + if ( sum <= 1.0e-7 ) { + // small change => singularity + return false; + } + return true; +} + +/* + Iteration & Math +*/ +void Attractor::iterate() { + // pointer swap + double * temp = point; + point = new_point; + new_point = temp; + + // calculations + switch (formula) { + case POLY_N: + polynome(); + break; + + case POLY_A: + poly_A(); + break; + + case POLY_2: + poly_2(); + break; + + case LOGISTIC: + logistic(); + break; + + default: + cout << "Formula not recognized" << endl; + exit(1); + break; + } + +#ifdef HARDDEBUG + cout << "Formula " << formula << " is done" << endl; + cout << "Old Vector: "; + for ( unsigned int i = 0; i < dim; i++ ) { + cout << point[i] << " "; + } + cout << endl << "Fresh Vector: "; + for ( unsigned int i = 0; i < dim; i++ ) { + cout << new_point[i] << " "; + } + cout << endl; +#endif +} + +void Attractor::polynome() { + unsigned int m = 0; + for ( unsigned int i = 0; i < dim; i++ ) { + +#ifdef HARDDEBUG + cout << "Entering new dimension: " << i << " With m = " << m << endl; +#endif + + new_point[i] = param[m]; + m++; + recur(i, 0, 1, m); + } + +} + +void Attractor::recur(unsigned int curr_dimension, unsigned int prev_i, unsigned int n, unsigned int& m, double prev_product) { + double product; + for (unsigned int i = prev_i; i < dim; i++) { + +#ifdef HARDDEBUG + for ( unsigned int j = 0; j < n; j++ ) + cout << " "; + cout << "Calculation in dimension: " << i << " With m = " << m << " And depth = " << n << endl; +#endif + + product = prev_product * point[i]; + new_point[curr_dimension] += param[m] * product; + m++; + if (n < orde) { + recur(curr_dimension, i, n+1, m, product); + } + } +} + +void Attractor::poly_A() { + switch (dim) { + case 3: + new_point[0] = param[0] + point[1] - point[1]*point[2]; + new_point[1] = param[1] + point[2] - point[2]*point[0]; + new_point[2] = param[2] + point[0] - point[0]*point[1]; + break; + default: + for ( unsigned int i = 0; i < dim; i++ ) { + new_point[i] = param[i] + point[(i+1) % dim] - point[(i+1) % dim]*point[(i+2) % dim]; + } + break; + } +} + +void Attractor::poly_2() { + exit(2); +} + +void Attractor::logistic() { + for ( unsigned int i = 0; i < dim; i++ ) { + new_point[i] = param[i]*point[i]*(1.0 - point[i]); + } +} + + +void Attractor::plot() { + for ( vector::iterator it = projectors.begin(); it != projectors.end(); it++ ) { + (*it)->plot(point); + } +} + + +/* + IO & control +*/ +void Attractor::output() { + for ( unsigned int i = 0; i < dim; i++ ) { + cout << point[i] << " "; + } + cout << endl; +} diff --git a/Attractor.hpp b/Attractor.hpp new file mode 100644 index 0000000..3a6e116 --- /dev/null +++ b/Attractor.hpp @@ -0,0 +1,61 @@ +#ifndef ATTRACTOR_HPP +#define ATTRACTOR_HPP + +#include + +//#include "Vector.hpp" +//#include "Parameters.hpp" +#include "Projector.hpp" +#include "AttractorKernel.hpp" + +enum FormulaChoice { + POLY_N, LORENZ, POLY_A, POLY_2, LOGISTIC, UNRAVEL +}; + +class Projector; + +// TODO : Verschillende classas AttractorSystem en Attractor maken? + +class Attractor { +public: + unsigned int dim; + unsigned int par; + unsigned int orde; + FormulaChoice formula; + + double * param; + double * point; + double * new_point; + + + vector projectors; + + public: + Attractor(); + Attractor(unsigned int dimensions, FormulaChoice formula, unsigned int orde = 3); + Attractor(const char* const filename); + + void init(unsigned int dimensions, FormulaChoice formula, unsigned int orde); + + void init_vector(); + void init_param(); + void init_range(); + + // TODO : lyapunov exponent uit rekenen + bool is_chaos(); + + + // TODO : optimaliseren voor lage graads veeltermen + void iterate(); + void polynome(); + void recur(unsigned int curr_dimension, unsigned int prev_i, unsigned int n, unsigned int& m, double prev_product = 1.0); + void poly_A(); + void poly_2(); + void logistic(); + + void plot(); + void output(); +}; + +#endif // ATTRACTOR_HPP + diff --git a/AttractorKernel.hpp b/AttractorKernel.hpp new file mode 100644 index 0000000..850c09f --- /dev/null +++ b/AttractorKernel.hpp @@ -0,0 +1,33 @@ +#ifndef ATTRACTORKERNEL_HPP +#define ATTRACTORKERNEL_HPP + +#include +using namespace std; + +class AttractorKernel { + public: + + // AttractorKernel protocol + + // parameters are stored in a array of doubles + // if you want to use other types, use the properties + virtual double& parameter(const unsigned int index) = 0; + virtual double*& parameters() = 0; + + // get properties of the attractor + // such as the dimension + // you should delete the void pointer if you used it + virtual void * getProperty(const string identifier) = 0; + virtual void setProperty(const string identifier, const void * value) = 0; + + // iterate his formula + // vector pointers will be swapped! so new remains new and old remains old + virtual void iterate() = 0; + + // getter functions for teh resulta + virtual double * & vector() = 0; + virtual double * & previousVector() = 0; +}; + +#endif // ATTRACTORKERNEL_HPP + diff --git a/AwesomeAttractor.cbp b/AwesomeAttractor.cbp new file mode 100644 index 0000000..0f63995 --- /dev/null +++ b/AwesomeAttractor.cbp @@ -0,0 +1,64 @@ + + + + + + diff --git a/AwesomeAttractor.layout b/AwesomeAttractor.layout new file mode 100644 index 0000000..270d711 --- /dev/null +++ b/AwesomeAttractor.layout @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AwesomeAttractor.layout.save b/AwesomeAttractor.layout.save new file mode 100644 index 0000000..ffebfd3 --- /dev/null +++ b/AwesomeAttractor.layout.save @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Canvas.cpp b/Canvas.cpp new file mode 100644 index 0000000..2072b56 --- /dev/null +++ b/Canvas.cpp @@ -0,0 +1,238 @@ +#include +#include +#include +#include +#include +#include +using namespace std; + +#include "pngwriter/pngwriter.h" + +#include "Canvas.hpp" + + +Canvas::Canvas(unsigned int width, unsigned int height, unsigned int num_colors): + dim(2), width(width), height(height), num_colors(num_colors), v(0) { + + int_array = new unsigned int[width*height*num_colors]; + size = new unsigned int[2]; + size[0] = width; + size[1] = height; + + assert(int_array != NULL); + + clear(); + +#ifdef HARDDEBUG + cout << "New canvas" << endl; +#endif +} + +void Canvas::clear() { + for ( unsigned int i = 0; i < width*height*num_colors; i++ ) { + int_array[i] = 0; + } +} + + + +//void Canvas::update_viewwindow() { +// +// //width and height of attractor +// const double dx = xmax - xmin; +// const double dy = ymax - ymin; +// +// //fix aspect ratio +// if ( dx > dy * ((float)width / height) ) { +// const double height2 = dx * ((float)height / width); +// const double middle = 0.5 * (ymax + ymin); +// ymax = middle + 0.5 * height2; +// ymin = middle - 0.5 * height2; +// +// } else { +// const double width2 = dy * ((float)width / height); +// const double middle = 0.5 * (xmax + xmin); +// xmax = middle + 0.5 * width2; +// xmin = middle - 0.5 * width2; +// } +// +// //add a 4% marge +// xmin -= 0.02 * dx; +// xmax += 0.02 * dx; +// ymin -= 0.02 * dy; +// ymax += 0.02 * dy; +// +// //constants for speed +// constant1 = width / (xmax - xmin); +// constant2 = height / (ymax - ymin); +//} + +void Canvas::plot(double x, double y) { + // gets x and y coordinate + // ranges [-1, 1] and [-1, 1] + // so how to do the aspect shiz, i don't know + const unsigned int x_int = x*width + width*.5; + const unsigned int y_int = y*width + height*.5; + const unsigned int index = x_int + width * y_int; + + if(x_int < width && y_int < height) { + int_array[index]++; + } +} + +void Canvas::plot(double x, double y, unsigned int c) { + // same as plot(double x, double y) + // now with color control + const unsigned int x_int = x*width + width*.5; + const unsigned int y_int = y*width + height*.5; + const unsigned int index = x_int + width * y_int + width*height*c; + + if(x_int < width && y_int < height) { + int_array[index]++; + } +} + +void Canvas::plot(double x, double y, unsigned int c, double intensity) { + // same as plot(double x, double y, unsigned int c) + // but now uses the float array (not yet implemented +} + +/* + I/O functions +*/ +void Canvas::output() { + cout << "Canvas: " << endl; + cout << "Dimensions: " << width << " x " << height << " x " << num_colors << endl; +} + +void Canvas::output_file(const char * filename){ + unsigned int * max_int = new unsigned int[num_colors]; + double * power = new double[num_colors]; + + for ( unsigned int i = 0; i < num_colors; i++ ) { + max_int[i] = 0; + double cumulative = 0; + unsigned int n = 0; + + for ( unsigned int j = 0; j < width*height; j++) { + if ( max_int[i] < int_array[j+i*width*height] ) { + max_int[i] = int_array[j+i*width*height]; + } + if ( int_array[j+i*width*height] ) { + cumulative += int_array[j+i*width*height]; + n++; + } + } + + if ( n > 100 ) { + const double average = cumulative / (double)n; + power[i] = -2.5/log(average/(double)max_int[i]); + if ( power[i] < 0 ) + power[i] = 1; + } else { + power[i] = 1; + } + + if ( n <= 10 ) { + cout << "not enough data" << endl; + } + } + + const double vibrancy = v; + double averagePower = 0; + for ( unsigned int i = 0; i < num_colors; i++ ) { + averagePower += power[i]; + } + averagePower /= num_colors; + for ( unsigned int i = 0; i < num_colors; i++ ) { + power[i] = vibrancy*power[i] + (1.0 - vibrancy*averagePower); + } + + pngwriter * pngFile = new pngwriter(width, height, 0.0, filename); + pngFile->setcompressionlevel(9); + pngFile->settext("Attractor", "Joshua Moerman", "A awesome attractor", "AwesomeAttractor"); + + for ( unsigned int x = 0; x < width; x++ ) { + for ( unsigned int y = 0; y < height; y++ ) { + double r = 0.0; + double g = 0.0; + double b = 0.0; + for ( unsigned int c = 0; c < num_colors; c++ ) { + const double norm_value = (double)int_array[x + y*width + c*width*height]/max_int[c]; + switch(c){ + case 0: { + r = (pow(norm_value, power[c]))*3.0; + break; + } + case 1: { + g = (pow(norm_value, power[c]))*3.0; + break; + } + case 2: { + b = (pow(norm_value, power[c]))*3.0; + break; + } + default: + break; + } + } + //pngwriter clips values for me + pngFile->plot(x, y, r, g, b); + } + } + + + delete max_int; + delete power; + + pngFile->close(); + +} + +void Canvas::output_file(){ + char filename[50]; + time_t t = time(0); + struct tm* lt = localtime(&t); + int r = rand() % 10; + + sprintf(filename, "render/attractor_%04d-%02d-%02d_%02d-%02d-%02d-%01d.png", lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec, r); + + output_file(filename); +} + + +void Canvas::output_raw(const char * filename){ + + ofstream outfile (filename, ofstream::binary); + + outfile.write(reinterpret_cast(int_array), sizeof(unsigned int)*width*height*num_colors); + +} + +void Canvas::output_raw(){ + char filename[52]; + time_t t = time(0); + struct tm* lt = localtime(&t); + int r = rand() % 10; + + sprintf(filename, "render/canv%dx%d_%04d-%02d-%02d_%02d-%02d-%02d-%01d.canv", width, height, lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec, r); + + output_raw(filename); +} + +void Canvas::input_raw(const char * filename){ + ifstream infile(filename, ifstream::binary); + + if ( ! infile ) { + cout << "poep" << endl; + return; + } + + infile.seekg (0, ios::end); + int length = infile.tellg(); + infile.seekg (0, ios::beg); + + cout << "length: " << length << " =? " << static_cast(width*height*num_colors*sizeof(unsigned int)) << endl; + + infile.read (reinterpret_cast(int_array), sizeof (unsigned int)*width*height*num_colors); +} diff --git a/Canvas.hpp b/Canvas.hpp new file mode 100644 index 0000000..2e82d8f --- /dev/null +++ b/Canvas.hpp @@ -0,0 +1,50 @@ +#ifndef CANVAS_HPP +#define CANVAS_HPP + + +//#include "Vector.hpp" +#include "Attractor.hpp" +#include "Projector.hpp" + +// TODO : Canvas class abstraheren (zodat er makkelijk verschillende soorten canvae gemaakt kunnen worden) + +class Canvas{ + friend class Projector; + + unsigned int dim; + unsigned int width; + unsigned int height; + unsigned int num_colors; + + unsigned int * size; + + unsigned int * int_array; + + public: + + double v; + + Canvas(unsigned int width, unsigned int height, unsigned int num_colors = 1); + + void clear(); + + void plot(double x, double y); + void plot(double x, double y, unsigned int c); + // TODO : make double array in canvas (ander soort canvas) + // TODO : subpixel sampling (anders soort canvas) + void plot(double x, double y, unsigned int c, double intensity); + + void output(); + //void output(Vector& point); + + void output_file(const char * filename); + void output_file(); + void output_raw(const char * filename); + void output_raw(); + void input_raw(const char * filename); +}; + + + +#endif // CANVAS_HPP + diff --git a/Parameters.cpp b/Parameters.cpp new file mode 100644 index 0000000..441d523 --- /dev/null +++ b/Parameters.cpp @@ -0,0 +1,110 @@ +#include +using namespace std; + +#include "Parameters.hpp" + + +Parameters::Parameters(unsigned int num_parameters, float default_val): + num_parameters(num_parameters) { + begin = new (nothrow) Vector(num_parameters, default_val); + eind = new (nothrow) Vector(num_parameters, default_val); + interpolated = new (nothrow) Vector(num_parameters, default_val); + // *interpolated = *begin + + check_pointers(); + +#ifdef HARDDEBUG + cout << "New Parameters with one default val:" << endl << *this << endl; +#endif +} + +/*Parameters::Parameters(unsigned int num_parameters, float default_val1, float default_val2): + num_parameters(num_parameters) { + begin = new (nothrow) Vector(num_parameters, default_val1); + eind = new (nothrow) Vector(num_parameters, default_val2); + interpolated = new (nothrow) Vector(num_parameters, default_val1); + // *interpolated = *begin + + check_pointers(); + +#ifdef HARDDEBUG + cout << "New Parameters with two default vals:" << endl << *this << endl; +#endif +}*/ + +Parameters::~Parameters() { + delete begin; + delete eind; + delete interpolated; + +#ifdef HARDDEBUG + cout << "Parameters deleted" << endl; +#endif +} + +void Parameters::check_pointers() { + assert(begin != NULL); + assert(eind != NULL); + assert(interpolated != NULL); +} + +void Parameters::set(unsigned int parameter, float val1, float val2) { + assert(parameter < num_parameters); + + begin->coordinates[parameter] = val1; + eind->coordinates[parameter] = val2; + interpolated->coordinates[parameter] = val1; + +#ifdef HARDDEBUG + cout << "Parameter " << parameter << " set to: " << val1 << " - " << val2 << endl; +#endif +} + +void Parameters::set(unsigned int parameter, float val) { + assert(parameter < num_parameters); + + begin->coordinates[parameter] = val; + eind->coordinates[parameter] = val; + interpolated->coordinates[parameter] = val; + +#ifdef HARDDEBUG + cout << "Parameter " << parameter << " set to: " << val << endl; +#endif +} + +float Parameters::get(unsigned int parameter) { + assert(parameter < num_parameters); + +#ifdef HARDDEBUG + cout << "Asked for parameter " << parameter << " with value:" << interpolated->coordinates[parameter] << endl; +#endif + + return interpolated->coordinates[parameter]; + +} + +void Parameters::interpolate(float time) { + /* + Dit is mogelijk met vector rekenen: + (*interpolated) = (*begin) * ( 1.0 - time ) + (*eind) * time; + Maar we doen het per element, zodat we simpelere code hebben, + geen vectoren hoeven te returnen en makkelijker kunnen optimaliseren + */ + const float invtime = 1.0 - time; + for ( unsigned int i = 0; i < num_parameters; i++ ) { + interpolated->coordinates[i] = invtime * begin->coordinates[i] + time * eind->coordinates[i]; + } + +#ifdef HARDDEBUG + cout << "interpolate() result" << endl << *interpolated << endl; +#endif +} + +ostream& operator<<(ostream& os, const Parameters& param) { + os << param.num_parameters << endl; + os << "Begin:" << endl << *param.begin << endl; + os << "Eind:" << endl << *param.eind << endl; + os << "Interpolated:" << endl << *param.interpolated << endl; + os < +#include +#include +#include +using namespace std; + +#include "Projector.hpp" +#include "Canvas.hpp" +#include "myMath.hpp" + +void Projector::init(double * point) { + init_vector(); + + project(point); + init_range(); +} + +void Projector::init_vector() { + project_point = new double[intern_dim]; + offset = new double[intern_dim]; + + assert(project_point != NULL); + assert(offset != NULL); +} + +void Projector::init_range() { + range_min = new double[intern_dim]; + range_max = new double[intern_dim]; + + assert(range_min != NULL); + assert(range_max != NULL); + + for ( unsigned int i = 0; i < intern_dim; i++ ) { + range_min[i] = range_max[i] = project_point[i]; + } +} + +void Projector::update_range(double * point) { + project(point); + for ( unsigned int i = 0; i < intern_dim; i++ ) { + if ( project_point[i] < range_min[i] ) { + range_min[i] = project_point[i]; + } else if ( project_point[i] > range_max[i] ) { + range_max[i] = project_point[i]; + } + } +} + +void Projector::finish_range() { +// double max_dist = 0.0; +// for ( unsigned int i = 0; i < intern_dim; i++ ) { +// const double dist = range_max[i] - range_min[i]; +// if ( dist > max_dist ) +// max_dist = dist; +// } +// +// factor = 0.9/max_dist; +// for ( unsigned int i = 0; i < intern_dim; i++ ) { +// offset[i] = -0.5*factor*(range_min[i] + range_max[i]); +// } + + factor = canvas->size[0] / (range_max[0] - range_min[0]); + unsigned int teh_size = canvas->size[0]; + for ( unsigned int i = 1; i < intern_dim; i++ ) { + double dist = range_max[i] - range_min[i]; + if ( factor * dist > (double)canvas->size[i] ) { + factor = (double)canvas->size[i] / dist; + //teh_size = canvas->size[i]; + cout << "crap for dim" << i << endl; + } + } + + factor /= (double)teh_size; + + for ( unsigned int i = 0; i < intern_dim; i++ ) { + offset[i] = -0.5*factor*(range_min[i] + range_max[i]); + } +} + + +void Projector::project(double * point) { + assert(extern_dim >= 2); + project_point[0] = point[0]; + project_point[1] = point[1]; +} + + +void Projector::plot(double * point) { + project(point); + + const double x = project_point[0]*factor + offset[0]; + const double y = project_point[1]*factor + offset[1]; + + //cout << x << ", " << y << endl; + + canvas->plot(x, y); + if ( even(point[2]*4.0) ) + canvas->plot(x, y, 1); + else + canvas->plot(x, y, 2); +} + + +void Projector::output(){ + cout << "Projector properties: " << endl; + cout << " factor: " << factor << endl; + for ( unsigned int i = 0; i < intern_dim; i++ ) { + cout << " dimension " << i << ": offset: " << offset[i] << ", range: [" << range_min[i] << ", " << range_max[i] << "]" << endl; + } +} + diff --git a/Projector.hpp b/Projector.hpp new file mode 100644 index 0000000..b6b86c8 --- /dev/null +++ b/Projector.hpp @@ -0,0 +1,45 @@ +#ifndef PROJECTOR_HPP +#define PROJECTOR_HPP + +#include + +#include "Canvas.hpp" + +class Canvas; + +class Projector{ + public: + + unsigned int extern_dim; + unsigned int intern_dim; + + Canvas * canvas; + double * project_point; + + double * range_min; + double * range_max; + double factor; + double * offset; + + void init(double * point); + void init_vector(); + void init_range(); + void update_range(double * point); + void finish_range(); + + + // TODO : Matrix gebruiken voor lineaire afbeelding + // TODO : Over kleuren nadenken + /* + Kleurmodi: + -genormalizeerde coordinaten als kleurintensiteit (gebruikt fp canvas) + -kleurbanden, dus met een periodieke functie (gebruikt int canvas) + */ + void project(double * point); + void plot(double * point); + + void output(); +}; + +#endif // PROJECTOR_HPP + diff --git a/Vector.cpp b/Vector.cpp new file mode 100644 index 0000000..76ca201 --- /dev/null +++ b/Vector.cpp @@ -0,0 +1,126 @@ +#include +using namespace std; + +#include "Vector.hpp" + +Vector::Vector(): + dimension(0) { + + coordinates = new (nothrow) float[0]; + + assert(coordinates != NULL); + +#ifdef HARDDEBUG + cout << "New vector (without elements)" << endl; +#endif +} + +Vector::Vector(unsigned int d): + dimension(d) { + + coordinates = new (nothrow) float[dimension]; + + assert(coordinates != NULL); + +#ifdef HARDDEBUG + cout << "New vector:" << endl << *this << endl; +#endif +} + +Vector::Vector(unsigned int d, float default_val): + dimension(d) { + + coordinates = new (nothrow) float[dimension]; + + assert(coordinates != NULL); + + for (unsigned int i = 0; i < dimension; i++) { + coordinates[i] = default_val; + } + +#ifdef HARDDEBUG + cout << "New vector with default values:" << endl << *this << endl; +#endif +} + + +Vector::~Vector() { + delete[] coordinates; + +#ifdef HARDDEBUG + cout << "coordinates deleted" << endl; +#endif +} + + +Vector& Vector::operator=(const Vector& a) { + if ( dimension != a.dimension ) { + dimension = a.dimension; + delete[] coordinates; + coordinates = new float[dimension]; + +#ifdef HARDDEBUG + cout << "Dimensions were not equal, made new vector" << endl; +#endif + } + + for ( unsigned int i = 0; i < dimension; i++ ) { + coordinates[i] = a.coordinates[i]; + } + +#ifdef HARDDEBUG + cout << "operator= result" << endl << *this << endl; +#endif + + return *this; +} + + +ostream& operator<<(ostream& os, const Vector& a) { + os << a.dimension << endl; + for ( unsigned int i = 0; i < a.dimension; i++ ) { + os << a.coordinates[i] << " "; + } + + os << endl; + return os; +} + + +float& Vector::operator[](const unsigned int index) { + assert(index < dimension); + return coordinates[index]; +} + +// matig werkende optelling en scalaire vermenigvuldiging van vectoren +/* +Vector Vector::operator+(const Vector a) const { + if ( dimension != a.dimension ) { + cout << "WARNING: dimensions not equal in vector addition" << endl; + exit(1); + } else { + static Vector ret(dimension); + for ( unsigned int i = 0; i < dimension; i++ ) { + ret.coordinates[i] = coordinates[i] + a.coordinates[i]; + } + +#ifdef HARDDEBUG + cout << "operator+ result" << endl << ret << endl; +#endif + return ret; + } +} + + +Vector Vector::operator*(const float a) const { + static Vector ret(dimension); + for ( unsigned int i = 0; i < dimension; i++ ) { + ret.coordinates[i] = coordinates[i] * a; + } + +#ifdef HARDDEBUG + cout << "operator* result" << endl << ret << endl; +#endif + return ret; +} +*/ diff --git a/Vector.hpp b/Vector.hpp new file mode 100644 index 0000000..e7f846f --- /dev/null +++ b/Vector.hpp @@ -0,0 +1,31 @@ +#ifndef VECTOR_HPP +#define VECTOR_HPP + +class Vector { + public: + + unsigned int dimension; + float * coordinates; + + // const, dest + Vector(); + Vector(unsigned int d); + Vector(unsigned int d, float default_val); + ~Vector(); + + // output operator + friend ostream& operator<<(ostream& os, const Vector& a); + + // easy access + float& Vector::operator[](const unsigned int index); + + // vector rekenen + Vector& operator=(const Vector& a); + // faaloperatoren + // Vector operator+(const Vector a) const; + // Vector operator*(const float a) const; +}; + + +#endif // VECTOR_HPP + diff --git a/batchexport.sh b/batchexport.sh new file mode 100644 index 0000000..4ea537c --- /dev/null +++ b/batchexport.sh @@ -0,0 +1,14 @@ +CANVEXT=".canv" +PNGEXT=".png" +CANVPATH="canvas/" +PNGPATH="render/" + +cd $CANVPATH + +for FILE in `ls *$CANVEXT | sed -e 's/'$CANVEXT'$//'` +do + ../bin/Debug/AwesomeAttractor -c ../$CANVPATH$FILE$CANVEXT 3200 3200 3 ../$PNGPATH$FILE$PNGEXT + #Do other stuff with file +done + + diff --git a/batchrender.sh b/batchrender.sh new file mode 100644 index 0000000..f4bd865 --- /dev/null +++ b/batchrender.sh @@ -0,0 +1,17 @@ +ATTREXT=".attr" +CANVEXT=".canv" +ATTRPATH="attr/" +CANVPATH="canvas/" + +cd $ATTRPATH + +for FILE in `ls *$ATTREXT | sed -e 's/'$ATTREXT'$//'` +do + ../bin/Debug/AwesomeAttractor -a ../$ATTRPATH$FILE$ATTREXT 500 ../$CANVPATH$FILE$CANVEXT 800 600 3 + #Do other stuff with file +done + +cd .. + +zip -r canvae.zip $CANVPATH*$CANVEXT + diff --git a/kernels/Lorenz.cpp b/kernels/Lorenz.cpp new file mode 100644 index 0000000..1b869a2 --- /dev/null +++ b/kernels/Lorenz.cpp @@ -0,0 +1,73 @@ +#include "Lorenz.hpp" + +Lorenz::Lorenz() { + init(); +} + +void Lorenz::init() { + // allocation + myParameters = new double[4]; + vectorNew = new double[3]; + vectorOld = new double[3]; + + // initialisation + assert(myParameters != NULL); + assert(vectorNew != NULL); + assert(vectorOld != NULL); + for ( unsigned int i = 0; i < 4; i++ ) { + myParameters[i] = 0.0; + } + for ( unsigned int i = 0; i < 3; i++ ) { + vectorNew[i] = vectorOld[i] = 1.0; + } +} + +// the main function +void Lorenz::iterate() { + swap(vectorNew, vectorOld); + + vectorNew[0] = vectorOld[0] + myParameters[0] * myParameters[1] * (vectorOld[1] - vectorOld[0]); + vectorNew[1] = vectorOld[1] + myParameters[0] * (vectorOld[0] * (myParameters[2] - vectorOld[2]) - vectorOld[1]); + vectorNew[2] = vectorOld[2] + myParameters[0] * (vectorOld[0] * vectorOld[1] - myParameters[3] * vectorOld[2]); +} + +/* +4D: + new_point[0] = point[0] + param[0] * param[1] * (point[1] - point[0]); + new_point[1] = point[1] + param[0] * (point[0] * (param[2] - point[2]) - point[1] + point[3]); + new_point[2] = point[2] + param[0] * (point[0] * point[1] - param[3] * point[2]); + new_point[3] = point[3] - param[0] * param[4] * point[0]; + break; +*/ + +// setters, getters, all i/o to other classes/objects +void * Lorenz::getProperty(const string identifier) { + if ( identifier == "dimension" ) { + unsigned int * _return = new unsigned int; + *_return = 3; + return _return; + } + return NULL; +} + +void Lorenz::setProperty(const string identifier, const void * value) { + return; +} + +double * & Lorenz::parameters() { + return myParameters; +} + +double & Lorenz::parameter(const unsigned int index) { + return myParameters[index]; +} + +double * & Lorenz::vector() { + return vectorNew; +} + +double * & Lorenz::previousVector() { + return vectorOld; +} + + diff --git a/kernels/Lorenz.hpp b/kernels/Lorenz.hpp new file mode 100644 index 0000000..dce9fab --- /dev/null +++ b/kernels/Lorenz.hpp @@ -0,0 +1,48 @@ +#ifndef LORENZ_HPP +#define LORENZ_HPP + +#include +#include +#include +#include +using namespace std; + +#include "../AttractorKernel.hpp" + +class Lorenz : public AttractorKernel { + + double * myParameters; + + double * vectorNew; + double * vectorOld; + + void init(); + + public: + + Lorenz(); + Lorenz(const unsigned int dimensions); + + // parameters are stored in a array of doubles + // if you want to use other types, use the properties + virtual double& parameter(const unsigned int index); + virtual double*& parameters(); + + // get properties of the attractor + // such as the dimension + // you should delete the void pointer if you used it + virtual void * getProperty(const string identifier); + virtual void setProperty(const string identifier, const void * value); + + // iterate his formula + // vector pointers will be swapped! so new remains new and old remains old + virtual void iterate(); + + // getter functions for teh resulta + virtual double * & vector(); + virtual double * & previousVector(); + +}; + +#endif // LORENZ_HPP + diff --git a/kernels/Unravel.cpp b/kernels/Unravel.cpp new file mode 100644 index 0000000..939dc85 --- /dev/null +++ b/kernels/Unravel.cpp @@ -0,0 +1,71 @@ +#include "Unravel.hpp" + +Unravel::Unravel(){ + init(); +} + +void Unravel::init() { + // allocation + myParameters = new double[4]; + vectorNew = new double[3]; + vectorOld = new double[3]; + + // initialisation + assert(myParameters != NULL); + assert(vectorNew != NULL); + assert(vectorOld != NULL); + for ( unsigned int i = 0; i < 7; i++ ) { + myParameters[i] = 0.0; + } + for ( unsigned int i = 0; i < 3; i++ ) { + vectorNew[i] = vectorOld[i] = 0.0; + } +} + +void Unravel::iterate() { + swap(vectorNew, vectorOld); + + vectorNew[0] = myParameters[0]*(vectorOld[2] + myParameters[1]); + vectorNew[1] = myParameters[2]*(vectorOld[0] + myParameters[3]); + vectorNew[2] = myParameters[4]*(vectorOld[1] + myParameters[5]); + + const double dist = vectorNew[0]*vectorNew[0] + vectorNew[1]*vectorNew[1] + vectorNew[2]*vectorNew[2]; + + if ( dist > myParameters[6]*myParameters[6] ) { + const double sqrtDist = sqrt(dist); + const double p = 1.0 - myParameters[6] * ( static_cast ( sqrtDist / myParameters[6] ) + 1.0 ) / sqrtDist; + vectorNew[0] *= p; + vectorNew[1] *= p; + vectorNew[2] *= p; + } +} + +// setters, getters, all i/o to other classes/objects +void * Unravel::getProperty(const string identifier) { + if ( identifier == "dimension" ) { + unsigned int * _return = new unsigned int; + *_return = 3; + return _return; + } + return NULL; +} + +void Unravel::setProperty(const string identifier, const void * value) { + return; +} + +double * & Unravel::parameters() { + return myParameters; +} + +double & Unravel::parameter(const unsigned int index) { + return myParameters[index]; +} + +double * & Unravel::vector() { + return vectorNew; +} + +double * & Unravel::previousVector() { + return vectorOld; +} diff --git a/kernels/Unravel.hpp b/kernels/Unravel.hpp new file mode 100644 index 0000000..3464c87 --- /dev/null +++ b/kernels/Unravel.hpp @@ -0,0 +1,49 @@ +#ifndef UNRAVEL_HPP +#define UNRAVEL_HPP + +#include +#include +#include +#include +#include +using namespace std; + +#include "../AttractorKernel.hpp" + +class Unravel : public AttractorKernel { + + double * myParameters; + + double * vectorNew; + double * vectorOld; + + void init(); + + public: + + Unravel(); + Unravel(const unsigned int dimensions); + + // parameters are stored in a array of doubles + // if you want to use other types, use the properties + virtual double& parameter(const unsigned int index); + virtual double*& parameters(); + + // get properties of the attractor + // such as the dimension + // you should delete the void pointer if you used it + virtual void * getProperty(const string identifier); + virtual void setProperty(const string identifier, const void * value); + + // iterate his formula + // vector pointers will be swapped! so new remains new and old remains old + virtual void iterate(); + + // getter functions for teh resulta + virtual double * & vector(); + virtual double * & previousVector(); + +}; + +#endif // UNRAVEL_HPP + diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..075417e --- /dev/null +++ b/main.cpp @@ -0,0 +1,133 @@ +#include +using namespace std; + +#include "Attractor.hpp" +#include "Canvas.hpp" +#include "Projector.hpp" + +#include "AttractorKernel.hpp" +#include "kernels/Lorenz.hpp" + +// TODO : Allemaal files inlezen, voor makkelijker gebruik + +int main(int argc, char *argv[]) { + + + AttractorKernel * mijnAttractortje; + mijnAttractortje = new Lorenz(); + AttractorKernel &attractor = *mijnAttractortje; + + attractor.parameter(0) = 1.0; + attractor.parameter(2) = 2.0; + + double * & vector = attractor.vector(); + + unsigned int * _dimension = (unsigned int*)mijnAttractortje->getProperty("dimension"); + unsigned int dimension = *_dimension; + delete _dimension; + + cout << "Dimension = " << dimension << endl; + + for ( unsigned int i = 0; i < 20; i++ ) { + mijnAttractortje->iterate(); + cout << "vector = "; + for ( unsigned int i = 0; i < dimension; i++ ) { + cout << " " << vector[i]; + } + cout << endl; + } + + + /*if ( argc <= 2 ) { + cout << endl << "nothing to do..." << endl; + cout << "usage:" << endl; + cout << " rendering to canvas: -a my_attractor.attr 500000000 my_attractor.canv 800 600 3" << endl; + cout << " canvas to png: -c my_attractor.canv 800 600 3 my_atttractor.png" << endl << endl; + exit(0); + } + + int mode; + string argv1 = argv[1]; + if ( argv1 == "-a" ) { + cout << "rendermode" << endl; + mode = 1; + } else if ( argv1 == "-c" ) { + cout << "canvasmode" << endl; + mode = 2; + } else { + cout << "i do.. i do... i do not understand... \"" << argv1 << "\""<< endl; + exit(0); + } + + switch ( mode ) { + case 1: { + if ( argc != 8 ) { + cout << "all parameters must be set..." << endl; + exit(0); + } + + string attractorFile = argv[2]; + unsigned int iterations = atoi(argv[3]); + string canvasFile = argv[4]; + unsigned int width = atoi(argv[5]); + unsigned int height = atoi(argv[6]); + unsigned int numColors = atoi(argv[7]); + + Attractor attract(attractorFile.c_str()); + cout << attractorFile << " is read" << endl; + + Projector projection; + Canvas canvas(width, height, numColors); + projection.canvas = &canvas; + + attract.projectors.push_back(&projection); + attract.init_range(); + + projection.output(); + + for ( unsigned int j = 1; j <= 100; j++ ) { + for ( unsigned int i = 0; 100*i <= iterations; i++ ) { + attract.iterate(); + attract.plot(); + } + cout << j << "% done" << endl; + } + + canvas.output_raw(canvasFile.c_str()); + cout << canvasFile << " is outputted" << endl; + + break; + } + case 2: { + if ( argc != 7 ) { + cout << "all parameters must be set..." << endl; + exit(0); + } + + string canvasFile = argv[2]; + unsigned int width = atoi(argv[3]); + unsigned int height = atoi(argv[4]); + unsigned int numColors = atoi(argv[5]); + string pngFile = argv[6]; + + Canvas canvas(width, height, numColors); + canvas.input_raw(canvasFile.c_str()); + cout << canvasFile << " is read" << endl; + for ( double v = -1.5; v < 1.6; v += 1.5 ) { + canvas.v = v; + canvas.output_file(); + } + cout << pngFile << " was exported" << endl; + break; + } + + default: { + cout << "WTF" << endl; + break; + } + }*/ + + + return 0; +} + diff --git a/myMath.hpp b/myMath.hpp new file mode 100644 index 0000000..db88044 --- /dev/null +++ b/myMath.hpp @@ -0,0 +1,12 @@ +#ifndef MYMATH_HPP +#define MYMATH_HPP + +#include +using namespace std; + +bool even(double x) { + return (((int)floor(x)) % 2 == 0); +} + +#endif // MYMATH_HPP + diff --git a/pngwriter/.DS_Store b/pngwriter/.DS_Store new file mode 100644 index 0000000..f483b62 Binary files /dev/null and b/pngwriter/.DS_Store differ diff --git a/pngwriter/._.DS_Store b/pngwriter/._.DS_Store new file mode 100644 index 0000000..460d887 Binary files /dev/null and b/pngwriter/._.DS_Store differ diff --git a/pngwriter/._Makefile b/pngwriter/._Makefile new file mode 100644 index 0000000..033041d Binary files /dev/null and b/pngwriter/._Makefile differ diff --git a/pngwriter/._pngwriter.cc b/pngwriter/._pngwriter.cc new file mode 100644 index 0000000..0d44057 Binary files /dev/null and b/pngwriter/._pngwriter.cc differ diff --git a/pngwriter/._pngwriter.h b/pngwriter/._pngwriter.h new file mode 100644 index 0000000..6f1f292 Binary files /dev/null and b/pngwriter/._pngwriter.h differ diff --git a/pngwriter/Makefile b/pngwriter/Makefile new file mode 100644 index 0000000..698646e --- /dev/null +++ b/pngwriter/Makefile @@ -0,0 +1,32 @@ +############ PARTIAL MAKEFILE FOR PNGWRITER ###################################### +# +# Website: Main: http://pngwriter.sourceforge.net/ +# Author: Paul Blackburn +# Email: individual61@users.sourceforge.net +# Version: 0.5.4 (19 / II / 2009) +# License: GNU General Public License +# Copyright 2002, 2003, 2004, 2005, 2006, 2007, +# 2008, 2009 Paul Blackburn +# +################################################################################## + + +include ../make.include + +OBJECTS=pngwriter.o +all: libpngwriter.a + +libpngwriter.a: $(OBJECTS) + ar rv $@ $^ + ranlib $@ + +pngwriter.o: pngwriter.cc pngwriter.h + $(CXX) $(CXXFLAGS) $(INC) -g -c -o pngwriter.o pngwriter.cc + +clean : + rm -f $(OBJECTS) libpngwriter.a pngtest.cc~ pngwriter.cc~ + rm -f pngwriter.h~ Makefile~ + rm -f .DS_Store + + + diff --git a/pngwriter/pngwriter.cc b/pngwriter/pngwriter.cc new file mode 100644 index 0000000..bf5e269 --- /dev/null +++ b/pngwriter/pngwriter.cc @@ -0,0 +1,4721 @@ +//********** pngwriter.cc ********************************************** +// Author: Paul Blackburn +// +// Email: individual61@users.sourceforge.net +// +// Version: 0.5.4 (19 / II / 2009) +// +// Description: Library that allows plotting a 48 bit +// PNG image pixel by pixel, which can +// then be opened with a graphics program. +// +// License: GNU General Public License +// Copyright 2002, 2003, 2004, 2005, 2006, 2007, +// 2008, 2009 Paul Blackburn +// +// Website: Main: http://pngwriter.sourceforge.net/ +// Sourceforge.net: http://sourceforge.net/projects/pngwriter/ +// Freshmeat.net: http://freshmeat.net/projects/pngwriter/ +// +// Documentation: The header file (pngwriter.h) is commented, but for a +// quick reference document, and support, +// take a look at the website. +// +//************************************************************************* + +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * */ + +#include "pngwriter.h" + +// Default Constructor +//////////////////////////////////////////////////////////////////////////// +pngwriter::pngwriter() +{ + + filename_ = new char[255]; + textauthor_ = new char[255]; + textdescription_ = new char[255]; + texttitle_ = new char[255]; + textsoftware_ = new char[255]; + + strcpy(filename_, "out.png"); + width_ = 250; + height_ = 250; + backgroundcolour_ = 65535; + compressionlevel_ = -2; + filegamma_ = 0.5; + transformation_ = 0; + + strcpy(textauthor_, "PNGwriter Author: Paul Blackburn"); + strcpy(textdescription_, "http://pngwriter.sourceforge.net/"); + strcpy(textsoftware_, "PNGwriter: An easy to use graphics library."); + strcpy(texttitle_, "out.png"); + + int kkkk; + + bit_depth_ = 16; //Default bit depth for new images + colortype_=2; + screengamma_ = 2.2; + + graph_ = (png_bytepp)malloc(height_ * sizeof(png_bytep)); + if(graph_ == NULL) + { + std::cerr << " PNGwriter::pngwriter - ERROR **: Not able to allocate memory for image." << std::endl; + } + + for (kkkk = 0; kkkk < height_; kkkk++) + { + graph_[kkkk] = (png_bytep)malloc(6*width_ * sizeof(png_byte)); + if(graph_[kkkk] == NULL) + { + std::cerr << " PNGwriter::pngwriter - ERROR **: Not able to allocate memory for image." << std::endl; + } + } + + if(graph_ == NULL) + { + std::cerr << " PNGwriter::pngwriter - ERROR **: Not able to allocate memory for image." << std::endl; + } + + int tempindex; + for(int hhh = 0; hhh65535) + { + std::cerr << " PNGwriter::pngwriter - WARNING **: Constructor called with background colour greater than 65535. Setting to 65535."<65535) + { + std::cerr << " PNGwriter::pngwriter - WARNING **: Constructor called with background colour greater than 1.0. Setting to 1.0."<65535) + { + std::cerr << " PNGwriter::pngwriter - WARNING **: Constructor called with background colour greater than 65535. Setting to 65535."<65535) + { + std::cerr << " PNGwriter::pngwriter - WARNING **: Constructor called with background colour greater than 65535. Setting to 65535."< 65535) + { + red = 65535; + } + if(green > 65535) + { + green = 65535; + } + if(blue > 65535) + { + blue = 65535; + } + + if(red < 0) + { + red = 0; + } + if(green < 0) + { + green = 0; + } + if(blue < 0) + { + blue = 0; + } + + if((bit_depth_ == 16)) + { + // if( (height_-y >-1) && (height_-y -1) && (6*(x-1)+5<6*width_) ) + if( (y<=height_) && (y>0) && (x>0) && (x<=width_) ) + { + //graph_[height_-y][6*(x-1) + i] where i goes from 0 to 5 + tempindex= 6*x-6; + graph_[height_-y][tempindex] = (char) floor(((double)red)/256); + graph_[height_-y][tempindex+1] = (char)(red%256); + graph_[height_-y][tempindex+2] = (char) floor(((double)green)/256); + graph_[height_-y][tempindex+3] = (char)(green%256); + graph_[height_-y][tempindex+4] = (char) floor(((double)blue)/256); + graph_[height_-y][tempindex+5] = (char)(blue%256); + }; + + /* + if(!( (height_-y >-1) && (height_-y -1) && (6*(x-1)+5<6*width_) )) + { + std::cerr << " PNGwriter::plot-- Plotting out of range! " << y << " " << x << std::endl; + } + */ + } + + if((bit_depth_ == 8)) + { + // if( (height_-y >-1) && (height_-y -1) && (3*(x-1)+5<3*width_) ) + if( (y0) && (x>0) && (x-1) && (height_-y -1) && (6*(x-1)+5<6*width_) )) + { + std::cerr << " PNGwriter::plot-- Plotting out of range! " << y << " " << x << std::endl; + } + */ + } +}; + +void pngwriter::plot(int x, int y, double red, double green, double blue) +{ + this->plot(x,y,int(red*65535),int(green*65535),int(blue*65535)); +}; + +/////////////////////////////////////////////////////////////// +int pngwriter::read(int x, int y, int colour) +{ + int temp1,temp2; + + if((colour !=1)&&(colour !=2)&&(colour !=3)) + { + std::cerr << " PNGwriter::read - WARNING **: Invalid argument: should be 1, 2 or 3, is " << colour << std::endl; + return 0; + } + + if( ( x>0 ) && ( x <= (this->width_) ) && ( y>0 ) && ( y <= (this->height_) ) ) + { + + if(bit_depth_ == 16) + { + temp2=6*(x-1); + if(colour == 1) + { + temp1 = (graph_[(height_-y)][temp2])*256 + graph_[height_-y][temp2+1]; + return temp1; + } + + if(colour == 2) + { + temp1 = (graph_[height_-y][temp2+2])*256 + graph_[height_-y][temp2+3]; + return temp1; + } + + if(colour == 3) + { + temp1 = (graph_[height_-y][temp2+4])*256 + graph_[height_-y][temp2+5]; + return temp1; + } + } + + if(bit_depth_ == 8) + { + temp2=3*(x-1); + if(colour == 1) + { + temp1 = graph_[height_-y][temp2]; + return temp1*256; + } + + if(colour == 2) + { + temp1 = graph_[height_-y][temp2+1]; + return temp1*256; + } + + if(colour == 3) + { + temp1 = graph_[height_-y][temp2+2]; + return temp1*256; + } + } + } + else + { + return 0; + } + + std::cerr << " PNGwriter::read - WARNING **: Returning 0 because of bitdepth/colour type mismatch."<< std::endl; + return 0; +} + +/////////////////////////////////////////////////////////////// +int pngwriter::read(int xxx, int yyy) +{ + int temp1,temp2,temp3,temp4,temp5; + + if( + ( xxx>0 ) && + ( xxx <= (this->width_) ) && + ( yyy>0 ) && + ( yyy <= (this->height_) ) + ) + { + if(bit_depth_ == 16) + { + // temp1 = (graph_[(height_-yyy)][6*(xxx-1)])*256 + graph_[height_-yyy][6*(xxx-1)+1]; + temp5=6*xxx; + temp1 = (graph_[(height_-yyy)][temp5-6])*256 + graph_[height_-yyy][temp5-5]; + temp2 = (graph_[height_-yyy][temp5-4])*256 + graph_[height_-yyy][temp5-3]; + temp3 = (graph_[height_-yyy][temp5-2])*256 + graph_[height_-yyy][temp5-1]; + temp4 = int((temp1+temp2+temp3)/3.0); + } + else if(bit_depth_ == 8) + { + // temp1 = graph_[height_-yyy][3*(xxx-1)]; + temp5 = 3*xxx; + temp1 = graph_[height_-yyy][temp5-3]; + temp2 = graph_[height_-yyy][temp5-2]; + temp3 = graph_[height_-yyy][temp5-1]; + temp4 = int((temp1+temp2+temp3)/3.0); + } + else + { + std::cerr << " PNGwriter::read - WARNING **: Invalid bit depth! Returning 0 as average value." << std::endl; + temp4 = 0; + } + + return temp4; + + } + else + { + return 0; + } +} + +///////////////////////////////////////////////////// +double pngwriter::dread(int x, int y, int colour) +{ + return double(this->read(x,y,colour))/65535.0; +} + +double pngwriter::dread(int x, int y) +{ + return double(this->read(x,y))/65535.0; +} + +/////////////////////////////////////////////////////// +void pngwriter::clear() +{ + int pen = 0; + int pencil = 0; + int tempindex; + + if(bit_depth_==16) + { + for(pen = 0; pen 999999999)||(index < 0)) + { + std::cerr << " PNGwriter::pngwriter_rename - ERROR **: Numerical name is out of 0 - 999 999 999 range (" << index <<")." << std::endl; + return; + } + + if( 0> sprintf(buffer, "%9.9lu.png",index)) + { + std::cerr << " PNGwriter::pngwriter_rename - ERROR **: Error creating numerical filename." << std::endl; + return; + } + + delete [] filename_; + delete [] texttitle_; + + filename_ = new char[strlen(buffer)+1]; + texttitle_ = new char[strlen(buffer)+1]; + + strcpy(filename_,buffer); + strcpy(texttitle_,buffer); + +}; + +/////////////////////////////////////////////////////// +void pngwriter::settext(char * title, char * author, char * description, char * software) +{ + delete [] textauthor_; + delete [] textdescription_; + delete [] texttitle_; + delete [] textsoftware_; + + textauthor_ = new char[strlen(author)+1]; + textdescription_ = new char[strlen(description)+1]; + textsoftware_ = new char[strlen(software)+1]; + texttitle_ = new char[strlen(title)+1]; + + strcpy(texttitle_, title); + strcpy(textauthor_, author); + strcpy(textdescription_, description); + strcpy(textsoftware_, software); +}; + +/////////////////////////////////////////////////////// +void pngwriter::settext(const char * title, const char * author, const char * description, const char * software) +{ + delete [] textauthor_; + delete [] textdescription_; + delete [] texttitle_; + delete [] textsoftware_; + + textauthor_ = new char[strlen(author)+1]; + textdescription_ = new char[strlen(description)+1]; + textsoftware_ = new char[strlen(software)+1]; + texttitle_ = new char[strlen(title)+1]; + + strcpy(texttitle_, title); + strcpy(textauthor_, author); + strcpy(textdescription_, description); + strcpy(textsoftware_, software); +}; + +/////////////////////////////////////////////////////// +void pngwriter::close() +{ + FILE *fp; + png_structp png_ptr; + png_infop info_ptr; + + fp = fopen(filename_, "wb"); + if( fp == NULL) + { + std::cerr << " PNGwriter::close - ERROR **: Error creating file (fopen() returned NULL pointer)." << std::endl; + perror(" PNGwriter::close - ERROR **"); + return; + } + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + info_ptr = png_create_info_struct(png_ptr); + png_init_io(png_ptr, fp); + if(compressionlevel_ != -2) + { + png_set_compression_level(png_ptr, compressionlevel_); + } + else + { + png_set_compression_level(png_ptr, PNGWRITER_DEFAULT_COMPRESSION); + } + + png_set_IHDR(png_ptr, info_ptr, width_, height_, + bit_depth_, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + if(filegamma_ < 1.0e-1) + { + filegamma_ = 0.5; // Modified in 0.5.4 so as to be the same as the usual gamma. + } + + png_set_gAMA(png_ptr, info_ptr, filegamma_); + + time_t gmt; + png_time mod_time; + png_text text_ptr[5]; + time(&gmt); + png_convert_from_time_t(&mod_time, gmt); + png_set_tIME(png_ptr, info_ptr, &mod_time); + text_ptr[0].key = "Title"; + text_ptr[0].text = texttitle_; + text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE; + text_ptr[1].key = "Author"; + text_ptr[1].text = textauthor_; + text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE; + text_ptr[2].key = "Description"; + text_ptr[2].text = textdescription_; + text_ptr[2].compression = PNG_TEXT_COMPRESSION_NONE; + text_ptr[3].key = "Creation Time"; + text_ptr[3].text = png_convert_to_rfc1123(png_ptr, &mod_time); + text_ptr[3].compression = PNG_TEXT_COMPRESSION_NONE; + text_ptr[4].key = "Software"; + text_ptr[4].text = textsoftware_; + text_ptr[4].compression = PNG_TEXT_COMPRESSION_NONE; + png_set_text(png_ptr, info_ptr, text_ptr, 5); + + png_write_info(png_ptr, info_ptr); + png_write_image(png_ptr, graph_); + png_write_end(png_ptr, info_ptr); + png_destroy_write_struct(&png_ptr, &info_ptr); + fclose(fp); +} + +////////////////////////////////////////////////////// +void pngwriter::line(int xfrom, int yfrom, int xto, int yto, int red, int green,int blue) +{ + // Bresenham Algorithm. + // + int dy = yto - yfrom; + int dx = xto - xfrom; + int stepx, stepy; + + if (dy < 0) + { + dy = -dy; stepy = -1; + } + else + { + stepy = 1; + } + + if (dx < 0) + { + dx = -dx; stepx = -1; + } + else + { + stepx = 1; + } + dy <<= 1; // dy is now 2*dy + dx <<= 1; // dx is now 2*dx + + this->plot(xfrom,yfrom,red,green,blue); + + if (dx > dy) + { + int fraction = dy - (dx >> 1); + + while (xfrom != xto) + { + if (fraction >= 0) + { + yfrom += stepy; + fraction -= dx; + } + xfrom += stepx; + fraction += dy; + this->plot(xfrom,yfrom,red,green,blue); + } + } + else + { + int fraction = dx - (dy >> 1); + while (yfrom != yto) + { + if (fraction >= 0) + { + xfrom += stepx; + fraction -= dy; + } + yfrom += stepy; + fraction += dx; + this->plot(xfrom,yfrom,red,green,blue); + } + } + +} + +void pngwriter::line(int xfrom, int yfrom, int xto, int yto, double red, double green,double blue) +{ + this->line( xfrom, + yfrom, + xto, + yto, + int (red*65535), + int (green*65535), + int (blue*65535) + ); +} + +/////////////////////////////////////////////////////////////////////////////////////////// +void pngwriter::square(int xfrom, int yfrom, int xto, int yto, int red, int green, int blue) +{ + this->line(xfrom, yfrom, xfrom, yto, red, green, blue); + this->line(xto, yfrom, xto, yto, red, green, blue); + this->line(xfrom, yfrom, xto, yfrom, red, green, blue); + this->line(xfrom, yto, xto, yto, red, green, blue); +} + +void pngwriter::square(int xfrom, int yfrom, int xto, int yto, double red, double green, double blue) +{ + this->square( xfrom, yfrom, xto, yto, int(red*65535), int(green*65535), int(blue*65535)); +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +void pngwriter::filledsquare(int xfrom, int yfrom, int xto, int yto, int red, int green, int blue) +{ + for(int caca = xfrom; caca line(caca, yfrom, caca, yto, red, green, blue); + } +} + +void pngwriter::filledsquare(int xfrom, int yfrom, int xto, int yto, double red, double green, double blue) +{ + this->filledsquare( xfrom, yfrom, xto, yto, int(red*65535), int(green*65535), int(blue*65535)); +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +void pngwriter::circle(int xcentre, int ycentre, int radius, int red, int green, int blue) +{ + int x = 0; + int y = radius; + int p = (5 - radius*4)/4; + + circle_aux(xcentre, ycentre, x, y, red, green, blue); + while (x < y) + { + x++; + if (p < 0) + { + p += 2*x+1; + } + else + { + y--; + p += 2*(x-y)+1; + } + circle_aux(xcentre, ycentre, x, y, red, green, blue); + } +} + +void pngwriter::circle(int xcentre, int ycentre, int radius, double red, double green, double blue) +{ + this->circle(xcentre,ycentre,radius, int(red*65535), int(green*65535), int(blue*65535)); +} + +//////////////////////////////////////////////////////////// + +void pngwriter::circle_aux(int xcentre, int ycentre, int x, int y, int red, int green, int blue) +{ + if (x == 0) + { + this->plot( xcentre, ycentre + y, red, green, blue); + this->plot( xcentre, ycentre - y, red, green, blue); + this->plot( xcentre + y, ycentre, red, green, blue); + this->plot( xcentre - y, ycentre, red, green, blue); + } + else + if (x == y) + { + this->plot( xcentre + x, ycentre + y, red, green, blue); + this->plot( xcentre - x, ycentre + y, red, green, blue); + this->plot( xcentre + x, ycentre - y, red, green, blue); + this->plot( xcentre - x, ycentre - y, red, green, blue); + } + else + if (x < y) + { + this->plot( xcentre + x, ycentre + y, red, green, blue); + this->plot( xcentre - x, ycentre + y, red, green, blue); + this->plot( xcentre + x, ycentre - y, red, green, blue); + this->plot( xcentre - x, ycentre - y, red, green, blue); + this->plot( xcentre + y, ycentre + x, red, green, blue); + this->plot( xcentre - y, ycentre + x, red, green, blue); + this->plot( xcentre + y, ycentre - x, red, green, blue); + this->plot( xcentre - y, ycentre - x, red, green, blue); + } + +} + +//////////////////////////////////////////////////////////// +void pngwriter::filledcircle(int xcentre, int ycentre, int radius, int red, int green, int blue) +{ + for(int jjj = ycentre-radius; jjj< ycentre+radius+1; jjj++) + { + this->line(xcentre - int(sqrt((double)(radius*radius) - (-ycentre + jjj)*(-ycentre + jjj ))), jjj, + xcentre + int(sqrt((double)(radius*radius) - (-ycentre + jjj)*(-ycentre + jjj ))),jjj,red,green,blue); + } +} + +void pngwriter::filledcircle(int xcentre, int ycentre, int radius, double red, double green, double blue) +{ + this->filledcircle( xcentre, ycentre, radius, int(red*65535), int(green*65535), int(blue*65535)); +} + +////////////////Reading routines///////////////////// +///////////////////////////////////////////////// + +// Modified with Mikkel's patch +void pngwriter::readfromfile(char * name) +{ + FILE *fp; + png_structp png_ptr; + png_infop info_ptr; + unsigned char **image; + unsigned long width, height; + int bit_depth, color_type, interlace_type; + // png_uint_32 i; + // + fp = fopen (name,"rb"); + if (fp==NULL) + { + std::cerr << " PNGwriter::readfromfile - ERROR **: Error opening file \"" << std::flush; + std::cerr << name <readfromfile((char *)(name)); +} + +///////////////////////////////////////////////////////// +int pngwriter::check_if_png(char *file_name, FILE **fp) +{ + char sig[PNG_BYTES_TO_CHECK]; + + if ( /*(*fp = fopen(file_name, "rb")) */ *fp == NULL) // Fixed 10 10 04 + { + // exit(EXIT_FAILURE); + std::cerr << " PNGwriter::check_if_png - ERROR **: Could not open file " << file_name << " to read." << std::endl; + perror(" PNGwriter::check_if_png - ERROR **"); + return 0; + } + + if (fread(sig, 1, PNG_BYTES_TO_CHECK, *fp) != PNG_BYTES_TO_CHECK) + { + //exit(EXIT_FAILURE); + std::cerr << " PNGwriter::check_if_png - ERROR **: File " << file_name << " does not appear to be a valid PNG file." << std::endl; + perror(" PNGwriter::check_if_png - ERROR **"); + fclose(*fp); + return 0; + } + + if (png_sig_cmp( (png_bytep) sig, (png_size_t)0, PNG_BYTES_TO_CHECK) /*png_check_sig((png_bytep) sig, PNG_BYTES_TO_CHECK)*/ ) + { + std::cerr << " PNGwriter::check_if_png - ERROR **: File " << file_name << " does not appear to be a valid PNG file. png_check_sig() failed." << std::endl; + fclose(*fp); + return 0; + } + + + + return 1; //Success +} + +/////////////////////////////////////////////////////// +int pngwriter::read_png_info(FILE *fp, png_structp *png_ptr, png_infop *info_ptr) +{ + *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (*png_ptr == NULL) + { + std::cerr << " PNGwriter::read_png_info - ERROR **: Could not create read_struct." << std::endl; + fclose(fp); + return 0; + //exit(EXIT_FAILURE); + } + *info_ptr = png_create_info_struct(*png_ptr); + if (*info_ptr == NULL) + { + png_destroy_read_struct(png_ptr, (png_infopp)NULL, (png_infopp)NULL); + std::cerr << " PNGwriter::read_png_info - ERROR **: Could not create info_struct." << std::endl; + //exit(EXIT_FAILURE); + fclose(fp); + return 0; + } + if (setjmp((*png_ptr)->jmpbuf)) /*(setjmp(png_jmpbuf(*png_ptr)) )*////////////////////////////////////// + { + png_destroy_read_struct(png_ptr, info_ptr, (png_infopp)NULL); + std::cerr << " PNGwriter::read_png_info - ERROR **: This file may be a corrupted PNG file. (setjmp(*png_ptr)->jmpbf) failed)." << std::endl; + fclose(fp); + return 0; + //exit(EXIT_FAILURE); + } + png_init_io(*png_ptr, fp); + png_set_sig_bytes(*png_ptr, PNG_BYTES_TO_CHECK); + png_read_info(*png_ptr, *info_ptr); + + return 1; +} + +//////////////////////////////////////////////////////////// +int pngwriter::read_png_image(FILE *fp, png_structp png_ptr, png_infop info_ptr, + png_bytepp *image, png_uint_32 *width, png_uint_32 *height) +{ + unsigned int i,j; + + *width = png_get_image_width(png_ptr, info_ptr); + *height = png_get_image_height(png_ptr, info_ptr); + + if( width == NULL) + { + std::cerr << " PNGwriter::read_png_image - ERROR **: png_get_image_width() returned NULL pointer." << std::endl; + fclose(fp); + return 0; + } + + if( height == NULL) + { + std::cerr << " PNGwriter::read_png_image - ERROR **: png_get_image_height() returned NULL pointer." << std::endl; + fclose(fp); + return 0; + } + + if ((*image = (png_bytepp)malloc(*height * sizeof(png_bytep))) == NULL) + { + std::cerr << " PNGwriter::read_png_image - ERROR **: Could not allocate memory for reading image." << std::endl; + fclose(fp); + return 0; + //exit(EXIT_FAILURE); + } + for (i = 0; i < *height; i++) + { + (*image)[i] = (png_bytep)malloc(png_get_rowbytes(png_ptr, info_ptr)); + if ((*image)[i] == NULL) + { + for (j = 0; j < i; j++) free((*image)[j]); + free(*image); + fclose(fp); + std::cerr << " PNGwriter::read_png_image - ERROR **: Could not allocate memory for reading image." << std::endl; + return 0; + //exit(EXIT_FAILURE); + } + } + png_read_image(png_ptr, *image); + + return 1; +} + +/////////////////////////////////// +int pngwriter::getheight(void) +{ + return height_; +} + +int pngwriter::getwidth(void) +{ + return width_; +} + + +int pngwriter::getbitdepth(void) +{ + return bit_depth_; +} + +int pngwriter::getcolortype(void) +{ + return colortype_; +} + +double pngwriter::getgamma(void) +{ + return filegamma_; +} + +void pngwriter::setgamma(double gamma) +{ + filegamma_ = gamma; +} + +// The algorithms HSVtoRGB and RGBtoHSV were found at http://www.cs.rit.edu/~ncs/ +// which is a page that belongs to Nan C. Schaller, though +// these algorithms appear to be the work of Eugene Vishnevsky. +////////////////////////////////////////////// +void pngwriter::HSVtoRGB( double *r, double *g, double *b, double h, double s, double v ) +{ + // r,g,b values are from 0 to 1 + // h = [0,1], s = [0,1], v = [0,1] + // if s == 0, then h = -1 (undefined) + // + h = h*360.0; + + int i; + double f, p, q, t; + if( s == 0 ) + { + // achromatic (grey) + *r = *g = *b = v; + return; + } + + h /= 60; // sector 0 to 5 + i = int(floor( h )); + f = h - i; // factorial part of h + p = v * ( 1 - s ); + q = v * ( 1 - s * f ); + t = v * ( 1 - s * ( 1 - f ) ); + + switch( i ) + { + case 0: + *r = v; + *g = t; + *b = p; + break; + case 1: + *r = q; + *g = v; + *b = p; + break; + case 2: + *r = p; + *g = v; + *b = t; + break; + case 3: + *r = p; + *g = q; + *b = v; + break; + case 4: + *r = t; + *g = p; + *b = v; + break; + default: // case 5: + *r = v; + *g = p; + *b = q; + break; + } +} + +void pngwriter::RGBtoHSV( float r, float g, float b, float *h, float *s, float *v ) +{ + + float min=0.0; //These values are not used. + float max=1.0; + float delta; + + if( (r>=g)&&(r>=b) ) + { + max = r; + } + if( (g>=r)&&(g>=b) ) + { + max = g; + } + if( (b>=g)&&(b>=r) ) + { + max = b; + } + + if( (r<=g)&&(r<=b) ) + { + min = r; + } + if( (g<=r)&&(g<=b) ) + { + min = g; + } + if( (b<=g)&&(b<=r) ) + { + min = b; + } + + *v = max; // v + + delta = max - min; + + if( max != 0 ) + *s = delta / max; // s + else + { + + r = g = b = 0; // s = 0, v is undefined + *s = 0; + *h = -1; + return; + } + + if( r == max ) + *h = ( g - b ) / delta; // between yellow & magenta + else if( g == max ) + *h = 2 + ( b - r ) / delta; // between cyan & yellow + else + *h = 4 + ( r - g ) / delta; // between magenta & cyan + + *h *= 60; // degrees + if( *h < 0 ) + *h += 360; + +} + +// +////////////////////////////////////////////////////////////////////////////////// +void pngwriter::plotHSV(int x, int y, double hue, double saturation, double value) +{ + double red,green,blue; + double *redp; + double *greenp; + double *bluep; + + redp = &red; + greenp = &green; + bluep = &blue; + + HSVtoRGB(redp,greenp,bluep,hue,saturation,value); + plot(x,y,red,green,blue); +} + +void pngwriter::plotHSV(int x, int y, int hue, int saturation, int value) +{ + plotHSV(x, y, double(hue)/65535.0, double(saturation)/65535.0, double(value)/65535.0); +} + +// +////////////////////////////////////////////////////////////////////////////////// +double pngwriter::dreadHSV(int x, int y, int colour) +{ + if( (x>0)&&(x<=width_)&&(y>0)&&(y<=height_) ) + { + + float * huep; + float * saturationp; + float * valuep; + float red,green,blue; + float hue, saturation, value; + + red = float(dread(x,y,1)); + green = float(dread(x,y,2)); + blue = float(dread(x,y,3)); + + huep = &hue; + saturationp = &saturation; + valuep = &value; + + RGBtoHSV( red, green, blue, huep, saturationp, valuep ); + + if(colour == 1) + { + return double(hue)/360.0; + } + + else if(colour == 2) + { + return saturation; + } + + else if(colour == 3) + { + return value; + } + + std::cerr << " PNGwriter::dreadHSV - ERROR **: Called with wrong colour argument: should be 1, 2 or 3; was: " << colour << "." << std::endl; + } + return 0.0; +} + +// +////////////////////////////////////////////////////////////////////////////////// +int pngwriter::readHSV(int x, int y, int colour) +{ + if( (x>0)&&(x<=width_)&&(y>0)&&(y<=height_) ) + { + + float * huep; + float * saturationp; + float * valuep; + float red,green,blue; + float hue, saturation, value; + + red = float(dread(x,y,1)); + green = float(dread(x,y,2)); + blue = float(dread(x,y,3)); + + huep = &hue; + saturationp = &saturation; + valuep = &value; + + RGBtoHSV( red, green, blue, huep, saturationp, valuep ); + + if(colour == 1) + { + return int(65535*(double(hue)/360.0)); + } + + else if(colour == 2) + { + return int(65535*saturation); + } + + else if(colour == 3) + { + return int(65535*value); + } + + std::cerr << " PNGwriter::readHSV - ERROR **: Called with wrong colour argument: should be 1, 2 or 3; was: " << colour << "." << std::endl; + return 0; + } + else + { + return 0; + } +} + +void pngwriter::setcompressionlevel(int level) +{ + if( (level < -1)||(level > 9) ) + { + std::cerr << " PNGwriter::setcompressionlevel - ERROR **: Called with wrong compression level: should be -1 to 9, was: " << level << "." << std::endl; + } + compressionlevel_ = level; +} + +// An implementation of a Bezier curve. +void pngwriter::bezier( int startPtX, int startPtY, + int startControlX, int startControlY, + int endPtX, int endPtY, + int endControlX, int endControlY, + double red, double green, double blue) +{ + + double cx = 3.0*(startControlX - startPtX); + double bx = 3.0*(endControlX - startControlX) - cx; + double ax = double(endPtX - startPtX - cx - bx); + + double cy = 3.0*(startControlY - startPtY); + double by = 3.0*(endControlY - startControlY) - cy; + double ay = double(endPtY - startPtY - cy - by); + + double x,y,newx,newy; + x = startPtX; + y = startPtY; + + for(double t = 0.0; t<=1.005; t += 0.005) + { + newx = startPtX + t*(double(cx) + t*(double(bx) + t*(double(ax)))); + newy = startPtY + t*(double(cy) + t*(double(by) + t*(double(ay)))); + this->line(int(x),int(y),int(newx),int(newy),red,green,blue); + x = newx; + y = newy; + } +} + +//int version of bezier +void pngwriter::bezier( int startPtX, int startPtY, + int startControlX, int startControlY, + int endPtX, int endPtY, + int endControlX, int endControlY, + int red, int green, int blue) +{ + this->bezier( startPtX, startPtY, + startControlX, startControlY, + endPtX, endPtY, + endControlX, endControlY, + double(red)/65535.0, double(green)/65535.0, double(blue)/65535.0); +} + +/* +int pngwriter::getcompressionlevel(void) +{ + return png_get_compression_level(png_ptr); +} +*/ + +double pngwriter::version(void) +{ + const char *a = "Jeramy Webb (jeramyw@gmail.com), Mike Heller (mkheller@gmail.com)"; // For their generosity ;-) + char b = a[27]; + b++; + return (PNGWRITER_VERSION); +} + +void pngwriter::write_png(void) +{ + this->close(); +} + +#ifndef NO_FREETYPE + +// Freetype-based text rendering functions. +/////////////////////////////////////////// +void pngwriter::plot_text( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double red, double green, double blue) +{ + FT_Library library; + FT_Face face; + FT_Matrix matrix; // transformation matrix + FT_Vector pen; + + FT_UInt glyph_index; + FT_Error error; + + FT_Bool use_kerning; + FT_UInt previous = 0; + + /* Set up transformation Matrix */ + matrix.xx = (FT_Fixed)( cos(angle)*0x10000); /* It would make more sense to do this (below), but, bizzarely, */ + matrix.xy = (FT_Fixed)(-sin(angle)*0x10000); /* if one does, FT_Load_Glyph fails consistently. */ + matrix.yx = (FT_Fixed)( sin(angle)*0x10000); // matrix.yx = - matrix.xy; + matrix.yy = (FT_Fixed)( cos(angle)*0x10000); // matrix.yy = matrix.xx; + + /* Place starting coordinates in adequate form. */ + pen.x = x_start*64 ; + pen.y = (int)(y_start/64.0); + + /*Count the length of the string */ + int num_chars = strlen(text); + + /* Initialize FT Library object */ + error = FT_Init_FreeType( &library ); + if (error) { std::cerr << " PNGwriter::plot_text - ERROR **: FreeType: Could not init Library."<< std::endl; return;} + + /* Initialize FT face object */ + error = FT_New_Face( library,face_path,0,&face ); + if ( error == FT_Err_Unknown_File_Format ) { std::cerr << " PNGwriter::plot_text - ERROR **: FreeType: Font was opened, but type not supported."<< std::endl; return; } else if (error){ std::cerr << " PNGwriter::plot_text - ERROR **: FreeType: Could not find or load font file."<< std::endl; return; } + + /* Set the Char size */ + error = FT_Set_Char_Size( face, /* handle to face object */ + 0, /* char_width in 1/64th of points */ + fontsize*64, /* char_height in 1/64th of points */ + 100, /* horizontal device resolution */ + 100 ); /* vertical device resolution */ + + /* A way of accesing the glyph directly */ + FT_GlyphSlot slot = face->glyph; // a small shortcut + + /* Does the font file support kerning? */ + use_kerning = FT_HAS_KERNING( face ); + + int n; + for ( n = 0; n < num_chars; n++ ) + { + /* Convert character code to glyph index */ + glyph_index = FT_Get_Char_Index( face, text[n] ); + + /* Retrieve kerning distance and move pen position */ + if ( use_kerning && previous&& glyph_index ) + { + FT_Vector delta; + FT_Get_Kerning( face, + previous, + glyph_index, + ft_kerning_default, //FT_KERNING_DEFAULT, + &delta ); + + /* Transform this kerning distance into rotated space */ + pen.x += (int) (((double) delta.x)*cos(angle)); + pen.y += (int) (((double) delta.x)*( sin(angle))); + } + + /* Set transform */ + FT_Set_Transform( face, &matrix, &pen ); + +/*set char size*/ + + if (error) { std::cerr << " PNGwriter::plot_text - ERROR **: FreeType: Set char size error." << std::endl; return;}; + + /* Retrieve glyph index from character code */ + glyph_index = FT_Get_Char_Index( face, text[n] ); + + /* Load glyph image into the slot (erase previous one) */ + error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT ); + if (error) { std::cerr << " PNGwriter::plot_text - ERROR **: FreeType: Could not load glyph (in loop). (FreeType error "<< std::hex << error <<")." << std::endl; return;} + + /* Convert to an anti-aliased bitmap */ + // error = FT_Render_Glyph( face->glyph, FT_RENDER_MODE_NORMAL ); + error = FT_Render_Glyph( face->glyph, ft_render_mode_normal ); + if (error) { std::cerr << " PNGwriter::plot_text - ERROR **: FreeType: Render glyph error." << std::endl; return;} + + /* Now, draw to our target surface */ + my_draw_bitmap( &slot->bitmap, + slot->bitmap_left, + y_start + slot->bitmap_top, + red, + green, + blue ); + + /* Advance to the next position */ + pen.x += slot->advance.x; + pen.y += slot->advance.y; + + /* record current glyph index */ + previous = glyph_index; + } + + /* Free the face and the library objects */ + FT_Done_Face ( face ); + FT_Done_FreeType( library ); +} + +void pngwriter::plot_text_utf8( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double red, double green, double blue) +{ + FT_Library library; + FT_Face face; + FT_Matrix matrix; // transformation matrix + FT_Vector pen; + + FT_UInt glyph_index; + FT_Error error; + + FT_Bool use_kerning; + FT_UInt previous = 0; + + /* Set up transformation Matrix */ + matrix.xx = (FT_Fixed)( cos(angle)*0x10000); /* It would make more sense to do this (below), but, bizzarely, */ + matrix.xy = (FT_Fixed)(-sin(angle)*0x10000); /* if one does, FT_Load_Glyph fails consistently. */ + matrix.yx = (FT_Fixed)( sin(angle)*0x10000); // matrix.yx = - matrix.xy; + matrix.yy = (FT_Fixed)( cos(angle)*0x10000); // matrix.yy = matrix.xx; + + /* Place starting coordinates in adequate form. */ + pen.x = x_start*64 ; + pen.y = (int)(y_start/64.0); + + /*Count the length of the string */ + int num_bytes=0; + while(text[num_bytes]!=0) + { + num_bytes++; + } + + /* + std::cout << "Num bytes is: "<< num_bytes << std::endl; + */ + + //The array of ucs4 glyph indexes, which will by at most the number of bytes in the utf-8 file. + long * ucs4text; + ucs4text = new long[num_bytes+1]; + + unsigned char u,v,w,x,y,z; + + int num_chars=0; + + long iii=0; + + while(iiiglyph; // a small shortcut + + /* Does the font file support kerning? */ + use_kerning = FT_HAS_KERNING( face ); + + int n; + for ( n = 0; n < num_chars; n++ ) + { + /* Convert character code to glyph index */ + glyph_index = FT_Get_Char_Index( face, ucs4text[n] ); + + /* Retrieve kerning distance and move pen position */ + if ( use_kerning && previous&& glyph_index ) + { + FT_Vector delta; + FT_Get_Kerning( face, + previous, + glyph_index, + ft_kerning_default, //FT_KERNING_DEFAULT, + &delta ); + + /* Transform this kerning distance into rotated space */ + pen.x += (int) (((double) delta.x)*cos(angle)); + pen.y += (int) (((double) delta.x)*( sin(angle))); + } + + /* Set transform */ + FT_Set_Transform( face, &matrix, &pen ); + +/*set char size*/ + + if (error) { std::cerr << " PNGwriter::plot_text_utf8 - ERROR **: FreeType: Set char size error." << std::endl; return;}; + + /* Retrieve glyph index from character code */ + glyph_index = FT_Get_Char_Index( face, ucs4text[n] ); + + /* Load glyph image into the slot (erase previous one) */ + error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT ); + if (error) { std::cerr << " PNGwriter::plot_text_utf8 - ERROR **: FreeType: Could not load glyph (in loop). (FreeType error "<< std::hex << error <<")." << std::endl; return;} + + /* Convert to an anti-aliased bitmap */ + error = FT_Render_Glyph( face->glyph, ft_render_mode_normal ); + if (error) { std::cerr << " PNGwriter::plot_text_utf8 - ERROR **: FreeType: Render glyph error." << std::endl; return;} + + /* Now, draw to our target surface */ + my_draw_bitmap( &slot->bitmap, + slot->bitmap_left, + y_start + slot->bitmap_top, + red, + green, + blue ); + + /* Advance to the next position */ + pen.x += slot->advance.x; + pen.y += slot->advance.y; + + /* record current glyph index */ + previous = glyph_index; + } + + /* Free the face and the library objects */ + FT_Done_Face ( face ); + FT_Done_FreeType( library ); + + delete[] ucs4text; +} + +void pngwriter::plot_text( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, int red, int green, int blue) +{ + plot_text( face_path, fontsize, x_start, y_start, angle, text, ((double) red)/65535.0, ((double) green)/65535.0, ((double) blue)/65535.0 ); +} + +void pngwriter::plot_text_utf8( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, int red, int green, int blue) +{ + plot_text_utf8( face_path, fontsize, x_start, y_start, angle, text, ((double) red)/65535.0, ((double) green)/65535.0, ((double) blue)/65535.0 ); +} + +void pngwriter::my_draw_bitmap( FT_Bitmap * bitmap, int x, int y, double red, double green, double blue) +{ + double temp; + for(int j=1; jrows+1; j++) + { + for(int i=1; i< bitmap->width + 1; i++) + { + temp = (double)(bitmap->buffer[(j-1)*bitmap->width + (i-1)] )/255.0; + + if(temp) + { + this->plot(x + i, + y - j, + temp*red + (1-temp)*(this->dread(x+i,y-j,1)), + temp*green + (1-temp)*(this->dread(x+i,y-j,2)), + temp*blue + (1-temp)*(this->dread(x+i,y-j,3)) + ); + } + } + } +} + + + +//////////// Get text width + +//put in freetype section + +int pngwriter::get_text_width(char * face_path, int fontsize, char * text) +{ + + FT_Library library; + FT_Face face; + FT_Matrix matrix; // transformation matrix + FT_Vector pen; + + FT_UInt glyph_index; + FT_Error error; + + FT_Bool use_kerning; + FT_UInt previous = 0; + + /* Set up transformation Matrix */ + matrix.xx = (FT_Fixed)( 1.0*0x10000); /* It would make more sense to do this (below), but, bizzarely, */ + matrix.xy = (FT_Fixed)( 0.0*0x10000); /* if one does, FT_Load_Glyph fails consistently. */ + matrix.yx = (FT_Fixed)( 0.0*0x10000); // matrix.yx = - matrix.xy; + matrix.yy = (FT_Fixed)( 1.0*0x10000); // matrix.yy = matrix.xx; + + /* Place starting coordinates in adequate form. */ + pen.x = 0; + pen.y = 0; + + /*Count the length of the string */ + int num_chars = strlen(text); + + /* Initialize FT Library object */ + error = FT_Init_FreeType( &library ); + if (error) { std::cerr << " PNGwriter::get_text_width - ERROR **: FreeType: Could not init Library."<< std::endl; return 0;} + + /* Initialize FT face object */ + error = FT_New_Face( library,face_path,0,&face ); + if ( error == FT_Err_Unknown_File_Format ) { std::cerr << " PNGwriter::get_text_width - ERROR **: FreeType: Font was opened, but type not supported."<< std::endl; return 0; } else if (error){ std::cerr << " PNGwriter::get_text_width - ERROR **: FreeType: Could not find or load font file." << std::endl; return 0; } + + /* Set the Char size */ + error = FT_Set_Char_Size( face, /* handle to face object */ + 0, /* char_width in 1/64th of points */ + fontsize*64, /* char_height in 1/64th of points */ + 100, /* horizontal device resolution */ + 100 ); /* vertical device resolution */ + + /* A way of accesing the glyph directly */ + FT_GlyphSlot slot = face->glyph; // a small shortcut + + /* Does the font file support kerning? */ + use_kerning = FT_HAS_KERNING( face ); + + int n; + for ( n = 0; n < num_chars; n++ ) + { + /* Convert character code to glyph index */ + glyph_index = FT_Get_Char_Index( face, text[n] ); + + /* Retrieve kerning distance and move pen position */ + if ( use_kerning && previous&& glyph_index ) + { + FT_Vector delta; + FT_Get_Kerning( face, + previous, + glyph_index, + ft_kerning_default, //FT_KERNING_DEFAULT, + &delta ); + + /* Transform this kerning distance into rotated space */ + pen.x += (int) ( delta.x); + pen.y += 0; + } + + /* Set transform */ + FT_Set_Transform( face, &matrix, &pen ); + +/*set char size*/ + + if (error) { std::cerr << " PNGwriter::get_text_width - ERROR **: FreeType: Set char size error." << std::endl; return 0;}; + + /* Retrieve glyph index from character code */ + glyph_index = FT_Get_Char_Index( face, text[n] ); + + /* Load glyph image into the slot (erase previous one) */ + error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT ); + if (error) { std::cerr << " PNGwriter::get_text_width - ERROR **: FreeType: Could not load glyph (in loop). (FreeType error "<< std::hex << error <<")." << std::endl; return 0;} + + /* Convert to an anti-aliased bitmap */ + // error = FT_Render_Glyph( face->glyph, FT_RENDER_MODE_NORMAL ); + error = FT_Render_Glyph( face->glyph, ft_render_mode_normal ); + if (error) { std::cerr << " PNGwriter::get_text_width - ERROR **: FreeType: Render glyph error." << std::endl; return 0;} + + /* Now, draw to our target surface */ +/* my_draw_bitmap( &slot->bitmap, + slot->bitmap_left, + slot->bitmap_top, + red, + green, + blue ); +*/ + /* Advance to the next position */ + pen.x += slot->advance.x; +// std::cout << ((double) pen.x)/64.0 << std::endl; + pen.y += slot->advance.y; + + /* record current glyph index */ + previous = glyph_index; + } + + + /* Free the face and the library objects */ + FT_Done_Face ( face ); + FT_Done_FreeType( library ); + + return (int)( ((double)pen.x)/64.0 ); +} + + +int pngwriter::get_text_width_utf8(char * face_path, int fontsize, char * text) +{ + FT_Library library; + FT_Face face; + FT_Matrix matrix; // transformation matrix + FT_Vector pen; + + FT_UInt glyph_index; + FT_Error error; + + FT_Bool use_kerning; + FT_UInt previous = 0; + + /* Set up transformation Matrix */ + matrix.xx = (FT_Fixed)( 0x10000); /* It would make more sense to do this (below), but, bizzarely, */ + matrix.xy = (FT_Fixed)( 0*0x10000); /* if one does, FT_Load_Glyph fails consistently. */ + matrix.yx = (FT_Fixed)( 0*0x10000); // matrix.yx = - matrix.xy; + matrix.yy = (FT_Fixed)( 0x10000); // matrix.yy = matrix.xx; + + /* Place starting coordinates in adequate form. */ + pen.x = 0 ; + pen.y = 0; + + /*Count the length of the string */ + int num_bytes=0; + while(text[num_bytes]!=0) + { + num_bytes++; + } + + /* + std::cout << "Num bytes is: "<< num_bytes << std::endl; + */ + + //The array of ucs4 glyph indexes, which will by at most the number of bytes in the utf-8 file. + long * ucs4text; + ucs4text = new long[num_bytes+1]; + + unsigned char u,v,w,x,y,z; + + int num_chars=0; + + long iii=0; + + while(iiiglyph; // a small shortcut + + /* Does the font file support kerning? */ + use_kerning = FT_HAS_KERNING( face ); + + int n; + for ( n = 0; n < num_chars; n++ ) + { + /* Convert character code to glyph index */ + glyph_index = FT_Get_Char_Index( face, ucs4text[n] ); + + /* Retrieve kerning distance and move pen position */ + if ( use_kerning && previous&& glyph_index ) + { + FT_Vector delta; + FT_Get_Kerning( face, + previous, + glyph_index, + ft_kerning_default, //FT_KERNING_DEFAULT, + &delta ); + + /* Transform this kerning distance into rotated space */ + pen.x += (int) (delta.x); + pen.y += 0; + } + + /* Set transform */ + FT_Set_Transform( face, &matrix, &pen ); + +/*set char size*/ + + if (error) { std::cerr << " PNGwriter::get_text_width_utf8 - ERROR **: FreeType: Set char size error." << std::endl; return 0;}; + + /* Retrieve glyph index from character code */ + glyph_index = FT_Get_Char_Index( face, ucs4text[n] ); + + /* Load glyph image into the slot (erase previous one) */ + error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT ); + if (error) { std::cerr << " PNGwriter::get_text_width_utf8 - ERROR **: FreeType: Could not load glyph (in loop). (FreeType error "<< std::hex << error <<")." << std::endl; return 0;} + + /* Convert to an anti-aliased bitmap */ + error = FT_Render_Glyph( face->glyph, ft_render_mode_normal ); + if (error) { std::cerr << " PNGwriter::get_text_width_utf8 - ERROR **: FreeType: Render glyph error." << std::endl; return 0;} + + /* Now, draw to our target surface */ +/* my_draw_bitmap( &slot->bitmap, + slot->bitmap_left, + y_start + slot->bitmap_top, + red, + green, + blue ); +*/ + /* Advance to the next position */ + pen.x += slot->advance.x; + pen.y += slot->advance.y; + + /* record current glyph index */ + previous = glyph_index; + } + + /* Free the face and the library objects */ + FT_Done_Face ( face ); + FT_Done_FreeType( library ); + + delete[] ucs4text; + + return (int) (((double) pen.x)/64.0); +} + +/////////////// +#endif +#ifdef NO_FREETYPE + +void pngwriter::plot_text( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, int red, int green, int blue) +{ + std::cerr << " PNGwriter::plot_text - ERROR **: PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl; + return; +} + +void pngwriter::plot_text( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double red, double green, double blue) +{ + std::cerr << " PNGwriter::plot_text - ERROR **: PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl; + return; + +} + +void pngwriter::plot_text_utf8( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, int red, int green, int blue) +{ + std::cerr << " PNGwriter::plot_text_utf8 - ERROR **: PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl; + return; +} + +void pngwriter::plot_text_utf8( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double red, double green, double blue) +{ + std::cerr << " PNGwriter::plot_text_utf8 - ERROR **: PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl; + return; +} + +//////////// Get text width +int pngwriter::get_text_width(char * face_path, int fontsize, char * text) +{ + std::cerr << " PNGwriter::get_text_width - ERROR **: PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl; + return 0; +} + + +int pngwriter::get_text_width_utf8(char * face_path, int fontsize, char * text) +{ + std::cerr << " PNGwriter::get_text_width_utf8 - ERROR **: PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl; + return 0; +} + +/////////////// +#endif + +///////////////////////////////////// +int pngwriter::bilinear_interpolation_read(double x, double y, int colour) +{ + + int inty, intx; + inty = (int) ceil(y); + intx = (int) ceil(x); + + //inty = (int) floor(y) +1; + // intx = (int) floor(x) +1; + // + bool attop, atright; + attop = inty==this->height_; + atright = intx==this->width_; +/* + if( intx==this->width_ +1) + { + intx--; + // std::cout << "intx--" << std::endl; + + } + */ + /* + if(inty == this->height_ +1) + { + inty--; + // std::cout << "inty--" << std::endl; + } + */ + + if( (!attop)&&(!atright) ) + { + + double f,g,f1,g1; + f = 1.0 + x - ((double) intx); + g = 1.0 + y - ((double) inty); + f1 = 1.0 - f; + g1 = 1.0 - g; + + return (int) ( + f1*g1*this->read(intx, inty,colour) + + f*g1*this->read(intx+1,inty,colour) + +f1*g*this->read(intx,inty+1,colour) + + f*g*(this->read(intx+1,inty+1,colour)) + ); + } + + if( (atright)&&(!attop)) + { + + double f,g,f1,g1; + f = 1.0 + x - ((double) intx); + g = 1.0 + y - ((double) inty); + f1 = 1.0 - f; + g1 = 1.0 - g; + + return (int) ( + f1*g1*this->read(intx, inty,colour) + + f*g1*( 2*(this->read(intx,inty,colour)) - (this->read(intx-1,inty,colour)) ) + +f1*g*this->read(intx,inty+1,colour) + + f*g*(2*(this->read(intx,inty+1,colour)) - (this->read(intx-1,inty+1,colour))) + ); + } + + if((attop)&&(!atright)) + { + double f,g,f1,g1; + f = 1.0 + x - ((double) intx); + g = 1.0 + y - ((double) inty); + f1 = 1.0 - f; + g1 = 1.0 - g; + + return (int) ( + f1*g1*this->read(intx, inty,colour) + + f*g1*this->read(intx+1,inty,colour) + +f1*g*( 2*(this->read(intx,inty,colour)) - this->read(intx,inty-1,colour) ) + + f*g*( 2*(this->read(intx+1,inty,colour)) - this->read(intx+1,inty-1,colour)) + ); + } + + double f,g,f1,g1; + f = 1.0 + x - ((double) intx); + g = 1.0 + y - ((double) inty); + f1 = 1.0 - f; + g1 = 1.0 - g; + + return (int) ( + f1*g1*this->read(intx, inty,colour) + + f*g1*( 2*(this->read(intx,inty,colour)) - (this->read(intx-1,inty,colour)) ) + +f1*g*( 2*(this->read(intx,inty,colour)) - this->read(intx,inty-1,colour) ) + + f*g*( 2*( 2*(this->read(intx,inty,colour)) - (this->read(intx-1,inty,colour)) ) - ( 2*(this->read(intx,inty-1,colour)) - (this->read(intx-1,inty-1,colour)) )) + ); + + /* + return (int) ( + f1*g1*this->read(intx, inty,colour) + + f*g1*this->read(intx+1,inty,colour) + +f1*g*this->read(intx,inty+1,colour) + + f*g*this->read(intx+1, inty+1,colour) + ); + * */ + +}; + +double pngwriter::bilinear_interpolation_dread(double x, double y, int colour) +{ + return double(this->bilinear_interpolation_read(x,y,colour))/65535.0; +}; + +void pngwriter::plot_blend(int x, int y, double opacity, int red, int green, int blue) +{ + this->plot(x, y, + (int)( opacity*red + this->read(x,y,1)*(1.0-opacity)), + (int)( opacity*green + this->read(x,y,2)*(1.0-opacity)), + (int)( opacity*blue + this->read(x,y,3)*(1.0-opacity)) + ); +}; + +void pngwriter::plot_blend(int x, int y, double opacity, double red, double green, double blue) +{ + this->plot_blend(x, y, opacity, (int) (65535*red), (int) (65535*green), (int) (65535*blue)); +}; + +void pngwriter::invert(void) +{ + // int temp1, temp2, temp3; + double temp11, temp22, temp33; + + for(int jjj = 1; jjj <= (this->height_); jjj++) + { + for(int iii = 1; iii <= (this->width_); iii++) + { + /* temp11 = (this->read(iii,jjj,1)); + temp22 = (this->read(iii,jjj,2)); + temp33 = (this->read(iii,jjj,3)); + * + this->plot(iii,jjj, + ((double)(65535 - temp11))/65535.0, + ((double)(65535 - temp22))/65535.0, + ((double)(65535 - temp33))/65535.0 + ); + * + */ + temp11 = (this->read(iii,jjj,1)); + temp22 = (this->read(iii,jjj,2)); + temp33 = (this->read(iii,jjj,3)); + + this->plot(iii,jjj, + (int)(65535 - temp11), + (int)(65535 - temp22), + (int)(65535 - temp33) + ); + + } + } +} + +void pngwriter::resize(int width, int height) +{ + + for (int jjj = 0; jjj < height_; jjj++) free(graph_[jjj]); + free(graph_); + + width_ = width; + height_ = height; + backgroundcolour_ = 0; + + graph_ = (png_bytepp)malloc(height_ * sizeof(png_bytep)); + if(graph_ == NULL) + { + std::cerr << " PNGwriter::resize - ERROR **: Not able to allocate memory for image." << std::endl; + } + + for (int kkkk = 0; kkkk < height_; kkkk++) + { + graph_[kkkk] = (png_bytep)malloc(6*width_ * sizeof(png_byte)); + if(graph_[kkkk] == NULL) + { + std::cerr << " PNGwriter::resize - ERROR **: Not able to allocate memory for image." << std::endl; + } + } + + if(graph_ == NULL) + { + std::cerr << " PNGwriter::resize - ERROR **: Not able to allocate memory for image." << std::endl; + } + + int tempindex; + for(int hhh = 0; hhhdread(xstart,ystart,1) != boundary_red) || + (this->dread(xstart,ystart,2) != boundary_green) || + (this->dread(xstart,ystart,3) != boundary_blue) + ) + && + ( + (this->dread(xstart,ystart,1) != fill_red) || + (this->dread(xstart,ystart,2) != fill_green) || + (this->dread(xstart,ystart,3) != fill_blue) + ) + && + (xstart >0)&&(xstart <= width_)&&(ystart >0)&&(ystart <= height_) + ) + { + this->plot(xstart, ystart, fill_red, fill_green, fill_blue); + boundary_fill(xstart+1, ystart, boundary_red, boundary_green, boundary_blue, fill_red, fill_green, fill_blue) ; + boundary_fill(xstart, ystart+1, boundary_red, boundary_green, boundary_blue, fill_red, fill_green, fill_blue) ; + boundary_fill(xstart, ystart-1, boundary_red, boundary_green, boundary_blue, fill_red, fill_green, fill_blue) ; + boundary_fill(xstart-1, ystart, boundary_red, boundary_green, boundary_blue, fill_red, fill_green, fill_blue) ; + } +} + +//no int version needed +void pngwriter::flood_fill_internal(int xstart, int ystart, double start_red, double start_green, double start_blue, double fill_red, double fill_green, double fill_blue) +{ + if( ( + (this->dread(xstart,ystart,1) == start_red) && + (this->dread(xstart,ystart,2) == start_green) && + (this->dread(xstart,ystart,3) == start_blue) + ) + && + ( + (this->dread(xstart,ystart,1) != fill_red) || + (this->dread(xstart,ystart,2) != fill_green) || + (this->dread(xstart,ystart,3) != fill_blue) + ) + && + (xstart >0)&&(xstart <= width_)&&(ystart >0)&&(ystart <= height_) + ) + { + this->plot(xstart, ystart, fill_red, fill_green, fill_blue); + flood_fill_internal( xstart+1, ystart, start_red, start_green, start_blue, fill_red, fill_green, fill_blue); + flood_fill_internal( xstart-1, ystart, start_red, start_green, start_blue, fill_red, fill_green, fill_blue); + flood_fill_internal( xstart, ystart+1, start_red, start_green, start_blue, fill_red, fill_green, fill_blue); + flood_fill_internal( xstart, ystart-1, start_red, start_green, start_blue, fill_red, fill_green, fill_blue); + } + +} + +//int version +void pngwriter::boundary_fill(int xstart, int ystart, int boundary_red,int boundary_green,int boundary_blue,int fill_red, int fill_green, int fill_blue) +{ + + this->boundary_fill( xstart, ystart, + ((double) boundary_red)/65535.0, + ((double) boundary_green)/65535.0, + ((double) boundary_blue)/65535.0, + ((double) fill_red)/65535.0, + ((double) fill_green)/65535.0, + ((double) fill_blue)/65535.0 + ); +} + +void pngwriter::flood_fill(int xstart, int ystart, double fill_red, double fill_green, double fill_blue) +{ + flood_fill_internal( xstart, ystart, this->dread(xstart,ystart,1),this->dread(xstart,ystart,2),this->dread(xstart,ystart,3), fill_red, fill_green, fill_blue); +} + +//int version +void pngwriter::flood_fill(int xstart, int ystart, int fill_red, int fill_green, int fill_blue) +{ + this->flood_fill( xstart, ystart, + ((double) fill_red)/65535.0, + ((double) fill_green)/65535.0, + ((double) fill_blue)/65535.0 + ); +} + +void pngwriter::polygon( int * points, int number_of_points, double red, double green, double blue) +{ + if( (number_of_points<1)||(points ==NULL)) + { + std::cerr << " PNGwriter::polygon - ERROR **: Number of points is zero or negative, or array is NULL." << std::endl; + return; + } + + for(int k=0;k< number_of_points-1; k++) + { + this->line(points[2*k],points[2*k+1],points[2*k+2],points[2*k+3], red, green, blue); + } +} + +//int version +void pngwriter::polygon( int * points, int number_of_points, int red, int green, int blue) +{ + this->polygon(points, number_of_points, + ((double) red)/65535.0, + ((double) green)/65535.0, + ((double) blue)/65535.0 + ); +} + +void pngwriter::plotCMYK(int x, int y, double cyan, double magenta, double yellow, double black) +{ +/*CMYK to RGB: + * ----------- + * red = 255 - minimum(255,((cyan/255) * (255 - black) + black)) + * green = 255 - minimum(255,((magenta/255) * (255 - black) + black)) + * blue = 255 - minimum(255,((yellow/255) * (255 - black) + black)) + * */ + + if(cyan<0.0) + { + cyan = 0.0; + } + if(magenta<0.0) + { + magenta = 0.0; + } + if(yellow<0.0) + { + yellow = 0.0; + } + if(black<0.0) + { + black = 0.0; + } + + if(cyan>1.0) + { + cyan = 1.0; + } + if(magenta>1.0) + { + magenta = 1.0; + } + if(yellow>1.0) + { + yellow = 1.0; + } + if(black>1.0) + { + black = 1.0; + } + + double red, green, blue, minr, ming, minb, iblack; + + iblack = 1.0 - black; + + minr = 1.0; + ming = 1.0; + minb = 1.0; + + if( (cyan*iblack + black)<1.0 ) + { + minr = cyan*iblack + black; + } + + if( (magenta*iblack + black)<1.0 ) + { + ming = magenta*iblack + black; + } + + if( (yellow*iblack + black)<1.0 ) + { + minb = yellow*iblack + black; + } + + red = 1.0 - minr; + green = 1.0 - ming; + blue = 1.0 - minb; + + this->plot(x,y,red, green, blue); + +} + +//int version +void pngwriter::plotCMYK(int x, int y, int cyan, int magenta, int yellow, int black) +{ + this->plotCMYK( x, y, + ((double) cyan)/65535.0, + ((double) magenta)/65535.0, + ((double) yellow)/65535.0, + ((double) black)/65535.0 + ); +} + +double pngwriter::dreadCMYK(int x, int y, int colour) +{ +/* + * Black = minimum(1-Red,1-Green,1-Blue) + * Cyan = (1-Red-Black)/(1-Black) + * Magenta = (1-Green-Black)/(1-Black) + * Yellow = (1-Blue-Black)/(1-Black) + * + * */ + if((colour !=1)&&(colour !=2)&&(colour !=3)&&(colour !=4)) + { + std::cerr << " PNGwriter::dreadCMYK - WARNING **: Invalid argument: should be 1, 2, 3 or 4, is " << colour << std::endl; + return 0; + } + + double black, red, green, blue, ired, igreen, iblue, iblack; + //add error detection here + // not much to detect, really + red = this->dread(x, y, 1); + green = this->dread(x, y, 2); + blue = this->dread(x, y, 3); + + ired = 1.0 - red; + igreen = 1.0 - green; + iblue = 1.0 - blue; + + black = ired; + + //black is the mimimum of inverse RGB colours, and if they are all equal, it is the inverse of red. + if( (igreendread(x, y, 1); + green = this->dread(x, y, 2); + blue = this->dread(x, y, 3); + + ired = 1.0 - red; + igreen = 1.0 - green; + iblue = 1.0 - blue; + + black = ired; + + //black is the mimimum of inverse RGB colours, and if they are all equal, it is the inverse of red. + if( (igreenbilinear_interpolation_read(readx, ready, 1); + green = this->bilinear_interpolation_read(readx, ready, 2); + blue = this->bilinear_interpolation_read(readx, ready, 3); + temp.plot(x, y, red, green, blue); + + } + } + + // From here on, the process is the same for all scale functions. + //Get data out of temp and into this's storage. + + //Resize this instance + // Delete current storage. + for (int jjj = 0; jjj < height_; jjj++) free(graph_[jjj]); + free(graph_); + + //New image will have bit depth 16, regardless of original bit depth. + bit_depth_ = 16; + + // New width and height will be the scaled width and height + width_ = scaledw; + height_ = scaledh; + backgroundcolour_ = 0; + + graph_ = (png_bytepp)malloc(height_ * sizeof(png_bytep)); + if(graph_ == NULL) + { + std::cerr << " PNGwriter::scale_k - ERROR **: Not able to allocate memory for image." << std::endl; + } + + for (int kkkk = 0; kkkk < height_; kkkk++) + { + graph_[kkkk] = (png_bytep)malloc(6*width_ * sizeof(png_byte)); + if(graph_[kkkk] == NULL) + { + std::cerr << " PNGwriter::scale_k - ERROR **: Not able to allocate memory for image." << std::endl; + } + } + + if(graph_ == NULL) + { + std::cerr << " PNGwriter::scale_k - ERROR **: Not able to allocate memory for image." << std::endl; + } + + //This instance now has a new, resized storage space. + + //Copy the temp date into this's storage. + int tempindex; + for(int hhh = 0; hhhbilinear_interpolation_read(readx, ready, 1); + green = this->bilinear_interpolation_read(readx, ready, 2); + blue = this->bilinear_interpolation_read(readx, ready, 3); + temp.plot(x, y, red, green, blue); + + } + } + // From here on, the process is the same for all scale functions. + //Get data out of temp and into this's storage. + + //Resize this instance + // Delete current storage. + for (int jjj = 0; jjj < height_; jjj++) free(graph_[jjj]); + free(graph_); + + //New image will have bit depth 16, regardless of original bit depth. + bit_depth_ = 16; + + // New width and height will be the scaled width and height + width_ = scaledw; + height_ = scaledh; + backgroundcolour_ = 0; + + graph_ = (png_bytepp)malloc(height_ * sizeof(png_bytep)); + if(graph_ == NULL) + { + std::cerr << " PNGwriter::scale_kxky - ERROR **: Not able to allocate memory for image." << std::endl; + } + + for (int kkkk = 0; kkkk < height_; kkkk++) + { + graph_[kkkk] = (png_bytep)malloc(6*width_ * sizeof(png_byte)); + if(graph_[kkkk] == NULL) + { + std::cerr << " PNGwriter::scale_kxky - ERROR **: Not able to allocate memory for image." << std::endl; + } + } + + if(graph_ == NULL) + { + std::cerr << " PNGwriter::scale_kxky - ERROR **: Not able to allocate memory for image." << std::endl; + } + + //This instance now has a new, resized storage space. + + //Copy the temp date into this's storage. + int tempindex; + for(int hhh = 0; hhhbilinear_interpolation_read(readx, ready, 1); + green = this->bilinear_interpolation_read(readx, ready, 2); + blue = this->bilinear_interpolation_read(readx, ready, 3); + temp.plot(x, y, red, green, blue); + + } + } + + // From here on, the process is the same for all scale functions. + //Get data out of temp and into this's storage. + + //Resize this instance + // Delete current storage. + for (int jjj = 0; jjj < height_; jjj++) free(graph_[jjj]); + free(graph_); + + //New image will have bit depth 16, regardless of original bit depth. + bit_depth_ = 16; + + // New width and height will be the scaled width and height + width_ = finalwidth; + height_ = finalheight; + backgroundcolour_ = 0; + + graph_ = (png_bytepp)malloc(height_ * sizeof(png_bytep)); + if(graph_ == NULL) + { + std::cerr << " PNGwriter::scale_wh - ERROR **: Not able to allocate memory for image." << std::endl; + } + + for (int kkkk = 0; kkkk < height_; kkkk++) + { + graph_[kkkk] = (png_bytep)malloc(6*width_ * sizeof(png_byte)); + if(graph_[kkkk] == NULL) + { + std::cerr << " PNGwriter::scale_wh - ERROR **: Not able to allocate memory for image." << std::endl; + } + } + + if(graph_ == NULL) + { + std::cerr << " PNGwriter::scale_wh - ERROR **: Not able to allocate memory for image." << std::endl; + } + + //This instance now has a new, resized storage space. + + //Copy the temp date into this's storage. + int tempindex; + for(int hhh = 0; hhhplot_blend(xfrom,yfrom,opacity, red,green,blue); + + if (dx > dy) + { + int fraction = dy - (dx >> 1); + + while (xfrom != xto) + { + if (fraction >= 0) + { + yfrom += stepy; + fraction -= dx; + } + xfrom += stepx; + fraction += dy; + this->plot_blend(xfrom,yfrom,opacity, red,green,blue); + } + } + else + { + int fraction = dx - (dy >> 1); + while (yfrom != yto) + { + if (fraction >= 0) + { + xfrom += stepx; + fraction -= dy; + } + yfrom += stepy; + fraction += dx; + this->plot_blend(xfrom,yfrom, opacity, red,green,blue); + } + } + +} + +void pngwriter::line_blend(int xfrom, int yfrom, int xto, int yto, double opacity, double red, double green,double blue) +{ + this->line_blend( xfrom, + yfrom, + xto, + yto, + opacity, + int (red*65535), + int (green*65535), + int (blue*65535) + ); + +} + +void pngwriter::square_blend(int xfrom, int yfrom, int xto, int yto, double opacity, int red, int green,int blue) +{ + this->line_blend(xfrom, yfrom, xfrom, yto, opacity, red, green, blue); + this->line_blend(xto, yfrom, xto, yto, opacity, red, green, blue); + this->line_blend(xfrom, yfrom, xto, yfrom, opacity, red, green, blue); + this->line_blend(xfrom, yto, xto, yto, opacity, red, green, blue); +} + +void pngwriter::square_blend(int xfrom, int yfrom, int xto, int yto, double opacity, double red, double green,double blue) +{ + this->square_blend( xfrom, yfrom, xto, yto, opacity, int(red*65535), int(green*65535), int(blue*65535)); +} + +void pngwriter::filledsquare_blend(int xfrom, int yfrom, int xto, int yto, double opacity, int red, int green,int blue) +{ + for(int caca = xfrom; caca line_blend(caca, yfrom, caca, yto, opacity, red, green, blue); + } + +} + +void pngwriter::filledsquare_blend(int xfrom, int yfrom, int xto, int yto, double opacity, double red, double green,double blue) +{ + this->filledsquare_blend( xfrom, yfrom, xto, yto, opacity, int(red*65535), int(green*65535), int(blue*65535)); +} + +void pngwriter::circle_aux_blend(int xcentre, int ycentre, int x, int y, double opacity, int red, int green, int blue) +{ + if (x == 0) + { + this->plot_blend( xcentre, ycentre + y, opacity, red, green, blue); + this->plot_blend( xcentre, ycentre - y, opacity, red, green, blue); + this->plot_blend( xcentre + y, ycentre, opacity, red, green, blue); + this->plot_blend( xcentre - y, ycentre, opacity, red, green, blue); + } + else + if (x == y) + { + this->plot_blend( xcentre + x, ycentre + y, opacity, red, green, blue); + this->plot_blend( xcentre - x, ycentre + y, opacity, red, green, blue); + this->plot_blend( xcentre + x, ycentre - y, opacity, red, green, blue); + this->plot_blend( xcentre - x, ycentre - y, opacity, red, green, blue); + } + else + if (x < y) + { + this->plot_blend( xcentre + x, ycentre + y, opacity, red, green, blue); + this->plot_blend( xcentre - x, ycentre + y, opacity, red, green, blue); + this->plot_blend( xcentre + x, ycentre - y, opacity, red, green, blue); + this->plot_blend( xcentre - x, ycentre - y, opacity, red, green, blue); + this->plot_blend( xcentre + y, ycentre + x, opacity, red, green, blue); + this->plot_blend( xcentre - y, ycentre + x, opacity, red, green, blue); + this->plot_blend( xcentre + y, ycentre - x, opacity, red, green, blue); + this->plot_blend( xcentre - y, ycentre - x, opacity, red, green, blue); + } + +} +// + +void pngwriter::circle_blend(int xcentre, int ycentre, int radius, double opacity, int red, int green, int blue) +{ + int x = 0; + int y = radius; + int p = (5 - radius*4)/4; + + circle_aux_blend(xcentre, ycentre, x, y, opacity, red, green, blue); + while (x < y) + { + x++; + if (p < 0) + { + p += 2*x+1; + } + else + { + y--; + p += 2*(x-y)+1; + } + circle_aux_blend(xcentre, ycentre, x, y, opacity, red, green, blue); + } + +} + +void pngwriter::circle_blend(int xcentre, int ycentre, int radius, double opacity, double red, double green, double blue) +{ + this->circle_blend(xcentre,ycentre,radius, opacity, int(red*65535), int(green*65535), int(blue*65535)); +} + +void pngwriter::filledcircle_blend(int xcentre, int ycentre, int radius, double opacity, int red, int green, int blue) +{ + for(int jjj = ycentre-radius; jjj< ycentre+radius+1; jjj++) + { + this->line_blend(xcentre - int(sqrt((double)(radius*radius) - (-ycentre + jjj)*(-ycentre + jjj ))), jjj, + xcentre + int(sqrt((double)(radius*radius) - (-ycentre + jjj)*(-ycentre + jjj ))),jjj, opacity, red,green,blue); + } + +} + +void pngwriter::filledcircle_blend(int xcentre, int ycentre, int radius, double opacity, double red, double green, double blue) +{ + this->filledcircle_blend( xcentre, ycentre, radius, opacity, int(red*65535), int(green*65535), int(blue*65535)); +} + +void pngwriter::bezier_blend( int startPtX, int startPtY, + int startControlX, int startControlY, + int endPtX, int endPtY, + int endControlX, int endControlY, + double opacity, + double red, double green, double blue) +{ + + double cx = 3.0*(startControlX - startPtX); + double bx = 3.0*(endControlX - startControlX) - cx; + double ax = double(endPtX - startPtX - cx - bx); + + double cy = 3.0*(startControlY - startPtY); + double by = 3.0*(endControlY - startControlY) - cy; + double ay = double(endPtY - startPtY - cy - by); + + double x,y,newx,newy; + x = startPtX; + y = startPtY; + + for(double t = 0.0; t<=1.005; t += 0.005) + { + newx = startPtX + t*(double(cx) + t*(double(bx) + t*(double(ax)))); + newy = startPtY + t*(double(cy) + t*(double(by) + t*(double(ay)))); + this->line_blend(int(x),int(y),int(newx),int(newy),opacity, red,green,blue); + x = newx; + y = newy; + } +} + +void pngwriter::bezier_blend( int startPtX, int startPtY, + int startControlX, int startControlY, + int endPtX, int endPtY, + int endControlX, int endControlY, + double opacity, + int red, int green, int blue) +{ + this->bezier_blend( startPtX, startPtY, + startControlX, startControlY, + endPtX, endPtY, + endControlX, endControlY, + opacity, + double(red)/65535.0, double(green)/65535.0, double(blue)/65535.0); + +} + +///////////////////////////// +#ifndef NO_FREETYPE + +// Freetype-based text rendering functions. +/////////////////////////////////////////// +void pngwriter::plot_text_blend( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity, double red, double green, double blue) +{ + FT_Library library; + FT_Face face; + FT_Matrix matrix; // transformation matrix + FT_Vector pen; + + FT_UInt glyph_index; + FT_Error error; + + FT_Bool use_kerning; + FT_UInt previous = 0; + + /* Set up transformation Matrix */ + matrix.xx = (FT_Fixed)( cos(angle)*0x10000); /* It would make more sense to do this (below), but, bizzarely, */ + matrix.xy = (FT_Fixed)(-sin(angle)*0x10000); /* if one does, FT_Load_Glyph fails consistently. */ + matrix.yx = (FT_Fixed)( sin(angle)*0x10000); // matrix.yx = - matrix.xy; + matrix.yy = (FT_Fixed)( cos(angle)*0x10000); // matrix.yy = matrix.xx; + + /* Place starting coordinates in adequate form. */ + pen.x = x_start*64 ; + pen.y = (int)(y_start/64.0); + + /*Count the length of the string */ + int num_chars = strlen(text); + + /* Initialize FT Library object */ + error = FT_Init_FreeType( &library ); + if (error) { std::cerr << " PNGwriter::plot_text_blend - ERROR **: FreeType: Could not init Library."<< std::endl; return;} + + /* Initialize FT face object */ + error = FT_New_Face( library,face_path,0,&face ); + if ( error == FT_Err_Unknown_File_Format ) { std::cerr << " PNGwriter::plot_text_blend - ERROR **: FreeType: Font was opened, but type not supported."<< std::endl; return; } else if (error){ std::cerr << " PNGwriter::plot_text - ERROR **: FreeType: Could not find or load font file."<< std::endl; return; } + + /* Set the Char size */ + error = FT_Set_Char_Size( face, /* handle to face object */ + 0, /* char_width in 1/64th of points */ + fontsize*64, /* char_height in 1/64th of points */ + 100, /* horizontal device resolution */ + 100 ); /* vertical device resolution */ + + /* A way of accesing the glyph directly */ + FT_GlyphSlot slot = face->glyph; // a small shortcut + + /* Does the font file support kerning? */ + use_kerning = FT_HAS_KERNING( face ); + + int n; + for ( n = 0; n < num_chars; n++ ) + { + /* Convert character code to glyph index */ + glyph_index = FT_Get_Char_Index( face, text[n] ); + + /* Retrieve kerning distance and move pen position */ + if ( use_kerning && previous&& glyph_index ) + { + FT_Vector delta; + FT_Get_Kerning( face, + previous, + glyph_index, + ft_kerning_default, //FT_KERNING_DEFAULT, + &delta ); + + /* Transform this kerning distance into rotated space */ + pen.x += (int) (((double) delta.x)*cos(angle)); + pen.y += (int) (((double) delta.x)*( sin(angle))); + } + + /* Set transform */ + FT_Set_Transform( face, &matrix, &pen ); + +/*set char size*/ + + if (error) { std::cerr << " PNGwriter::plot_text_blend - ERROR **: FreeType: Set char size error." << std::endl; return;}; + + /* Retrieve glyph index from character code */ + glyph_index = FT_Get_Char_Index( face, text[n] ); + + /* Load glyph image into the slot (erase previous one) */ + error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT ); + if (error) { std::cerr << " PNGwriter::plot_text_blend - ERROR **: FreeType: Could not load glyph (in loop). (FreeType error "<< std::hex << error <<")." << std::endl; return;} + + /* Convert to an anti-aliased bitmap */ + // error = FT_Render_Glyph( face->glyph, FT_RENDER_MODE_NORMAL ); + error = FT_Render_Glyph( face->glyph, ft_render_mode_normal ); + if (error) { std::cerr << " PNGwriter::plot_text_blend - ERROR **: FreeType: Render glyph error." << std::endl; return;} + + /* Now, draw to our target surface */ + my_draw_bitmap_blend( &slot->bitmap, + slot->bitmap_left, + y_start + slot->bitmap_top, + opacity, + red, + green, + blue ); + + /* Advance to the next position */ + pen.x += slot->advance.x; + pen.y += slot->advance.y; + + /* record current glyph index */ + previous = glyph_index; + } + + /* Free the face and the library objects */ + FT_Done_Face ( face ); + FT_Done_FreeType( library ); +} + +void pngwriter::plot_text_utf8_blend( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity, double red, double green, double blue) +{ + FT_Library library; + FT_Face face; + FT_Matrix matrix; // transformation matrix + FT_Vector pen; + + FT_UInt glyph_index; + FT_Error error; + + FT_Bool use_kerning; + FT_UInt previous = 0; + + /* Set up transformation Matrix */ + matrix.xx = (FT_Fixed)( cos(angle)*0x10000); /* It would make more sense to do this (below), but, bizzarely, */ + matrix.xy = (FT_Fixed)(-sin(angle)*0x10000); /* if one does, FT_Load_Glyph fails consistently. */ + matrix.yx = (FT_Fixed)( sin(angle)*0x10000); // matrix.yx = - matrix.xy; + matrix.yy = (FT_Fixed)( cos(angle)*0x10000); // matrix.yy = matrix.xx; + + /* Place starting coordinates in adequate form. */ + pen.x = x_start*64 ; + pen.y = (int)(y_start/64.0); + + /*Count the length of the string */ + int num_bytes=0; + while(text[num_bytes]!=0) + { + num_bytes++; + } + + /* + std::cout << "Num bytes is: "<< num_bytes << std::endl; + */ + + //The array of ucs4 glyph indexes, which will by at most the number of bytes in the utf-8 file. + long * ucs4text; + ucs4text = new long[num_bytes+1]; + + unsigned char u,v,w,x,y,z; + + int num_chars=0; + + long iii=0; + + while(iiiglyph; // a small shortcut + + /* Does the font file support kerning? */ + use_kerning = FT_HAS_KERNING( face ); + + int n; + for ( n = 0; n < num_chars; n++ ) + { + /* Convert character code to glyph index */ + glyph_index = FT_Get_Char_Index( face, ucs4text[n] ); + + /* Retrieve kerning distance and move pen position */ + if ( use_kerning && previous&& glyph_index ) + { + FT_Vector delta; + FT_Get_Kerning( face, + previous, + glyph_index, + ft_kerning_default, //FT_KERNING_DEFAULT, + &delta ); + + /* Transform this kerning distance into rotated space */ + pen.x += (int) (((double) delta.x)*cos(angle)); + pen.y += (int) (((double) delta.x)*( sin(angle))); + } + + /* Set transform */ + FT_Set_Transform( face, &matrix, &pen ); + +/*set char size*/ + + if (error) { std::cerr << " PNGwriter::plot_text_utf8_blend - ERROR **: FreeType: Set char size error." << std::endl; return;}; + + /* Retrieve glyph index from character code */ + glyph_index = FT_Get_Char_Index( face, ucs4text[n] ); + + /* Load glyph image into the slot (erase previous one) */ + error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT ); + if (error) { std::cerr << " PNGwriter::plot_text_utf8_blend - ERROR **: FreeType: Could not load glyph (in loop). (FreeType error "<< std::hex << error <<")." << std::endl; return;} + + /* Convert to an anti-aliased bitmap */ + error = FT_Render_Glyph( face->glyph, ft_render_mode_normal ); + if (error) { std::cerr << " PNGwriter::plot_text_utf8_blend - ERROR **: FreeType: Render glyph error." << std::endl; return;} + + /* Now, draw to our target surface */ + my_draw_bitmap_blend( &slot->bitmap, + slot->bitmap_left, + y_start + slot->bitmap_top, + opacity, + red, + green, + blue ); + + /* Advance to the next position */ + pen.x += slot->advance.x; + pen.y += slot->advance.y; + + /* record current glyph index */ + previous = glyph_index; + } + + /* Free the face and the library objects */ + FT_Done_Face ( face ); + FT_Done_FreeType( library ); + + delete[] ucs4text; +} + +void pngwriter::plot_text_blend( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity, int red, int green, int blue) +{ + plot_text_blend( face_path, fontsize, x_start, y_start, angle, text, opacity, ((double) red)/65535.0, ((double) green)/65535.0, ((double) blue)/65535.0 ); +} + +void pngwriter::plot_text_utf8_blend( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity, int red, int green, int blue) +{ + plot_text_utf8_blend( face_path, fontsize, x_start, y_start, angle, text, opacity, ((double) red)/65535.0, ((double) green)/65535.0, ((double) blue)/65535.0 ); +} + +void pngwriter::my_draw_bitmap_blend( FT_Bitmap * bitmap, int x, int y, double opacity, double red, double green, double blue) +{ + double temp; + for(int j=1; jrows+1; j++) + { + for(int i=1; i< bitmap->width + 1; i++) + { + temp = (double)(bitmap->buffer[(j-1)*bitmap->width + (i-1)] )/255.0; + + if(temp) + { + this->plot_blend(x + i, + y - j, + opacity, + temp*red + (1-temp)*(this->dread(x+i,y-j,1)), + temp*green + (1-temp)*(this->dread(x+i,y-j,2)), + temp*blue + (1-temp)*(this->dread(x+i,y-j,3)) + ); + } + } + } +} + +#endif +#ifdef NO_FREETYPE + +void pngwriter::plot_text_blend( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity, int red, int green, int blue) +{ + std::cerr << " PNGwriter::plot_text_blend - ERROR **: PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl; + return; +} + +void pngwriter::plot_text_blend( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity, double red, double green, double blue) +{ + std::cerr << " PNGwriter::plot_text_blend - ERROR **: PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl; + return; + +} + +void pngwriter::plot_text_utf8_blend( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity, int red, int green, int blue) +{ + std::cerr << " PNGwriter::plot_text_utf8_blend - ERROR **: PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl; + return; +} + +void pngwriter::plot_text_utf8_blend( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity, double red, double green, double blue) +{ + std::cerr << " PNGwriter::plot_text_utf8_blend - ERROR **: PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl; + return; +} + +#endif + +/////////////////////////// + +void pngwriter::boundary_fill_blend(int xstart, int ystart, double opacity, double boundary_red,double boundary_green,double boundary_blue,double fill_red, double fill_green, double fill_blue) +{ + if( ( + (this->dread(xstart,ystart,1) != boundary_red) || + (this->dread(xstart,ystart,2) != boundary_green) || + (this->dread(xstart,ystart,3) != boundary_blue) + ) + && + ( + (this->dread(xstart,ystart,1) != fill_red) || + (this->dread(xstart,ystart,2) != fill_green) || + (this->dread(xstart,ystart,3) != fill_blue) + ) + && + (xstart >0)&&(xstart <= width_)&&(ystart >0)&&(ystart <= height_) + ) + { + this->plot_blend(xstart, ystart, opacity, fill_red, fill_green, fill_blue); + boundary_fill_blend(xstart+1, ystart, opacity, boundary_red, boundary_green, boundary_blue, fill_red, fill_green, fill_blue) ; + boundary_fill_blend(xstart, ystart+1, opacity, boundary_red, boundary_green, boundary_blue, fill_red, fill_green, fill_blue) ; + boundary_fill_blend(xstart, ystart-1, opacity, boundary_red, boundary_green, boundary_blue, fill_red, fill_green, fill_blue) ; + boundary_fill_blend(xstart-1, ystart, opacity, boundary_red, boundary_green, boundary_blue, fill_red, fill_green, fill_blue) ; + } +} + +//no int version needed +void pngwriter::flood_fill_internal_blend(int xstart, int ystart, double opacity, double start_red, double start_green, double start_blue, double fill_red, double fill_green, double fill_blue) +{ + if( ( + (this->dread(xstart,ystart,1) == start_red) && + (this->dread(xstart,ystart,2) == start_green) && + (this->dread(xstart,ystart,3) == start_blue) + ) + && + ( + (this->dread(xstart,ystart,1) != fill_red) || + (this->dread(xstart,ystart,2) != fill_green) || + (this->dread(xstart,ystart,3) != fill_blue) + ) + && + (xstart >0)&&(xstart <= width_)&&(ystart >0)&&(ystart <= height_) + ) + { + this->plot_blend(xstart, ystart, opacity, fill_red, fill_green, fill_blue); + flood_fill_internal_blend( xstart+1, ystart, opacity, start_red, start_green, start_blue, fill_red, fill_green, fill_blue); + flood_fill_internal_blend( xstart-1, ystart,opacity, start_red, start_green, start_blue, fill_red, fill_green, fill_blue); + flood_fill_internal_blend( xstart, ystart+1, opacity, start_red, start_green, start_blue, fill_red, fill_green, fill_blue); + flood_fill_internal_blend( xstart, ystart-1, opacity, start_red, start_green, start_blue, fill_red, fill_green, fill_blue); + } + +} + +//int version +void pngwriter::boundary_fill_blend(int xstart, int ystart, double opacity, int boundary_red,int boundary_green,int boundary_blue,int fill_red, int fill_green, int fill_blue) +{ + + this->boundary_fill_blend( xstart, ystart, + opacity, + ((double) boundary_red)/65535.0, + ((double) boundary_green)/65535.0, + ((double) boundary_blue)/65535.0, + ((double) fill_red)/65535.0, + ((double) fill_green)/65535.0, + ((double) fill_blue)/65535.0 + ); +} + +void pngwriter::flood_fill_blend(int xstart, int ystart, double opacity, double fill_red, double fill_green, double fill_blue) +{ + flood_fill_internal_blend( xstart, ystart, opacity, this->dread(xstart,ystart,1),this->dread(xstart,ystart,2),this->dread(xstart,ystart,3), fill_red, fill_green, fill_blue); +} + +//int version +void pngwriter::flood_fill_blend(int xstart, int ystart, double opacity, int fill_red, int fill_green, int fill_blue) +{ + this->flood_fill_blend( xstart, ystart, + opacity, + ((double) fill_red)/65535.0, + ((double) fill_green)/65535.0, + ((double) fill_blue)/65535.0 + ); +} + +void pngwriter::polygon_blend( int * points, int number_of_points, double opacity, double red, double green, double blue) +{ + if( (number_of_points<1)||(points ==NULL)) + { + std::cerr << " PNGwriter::polygon_blend - ERROR **: Number of points is zero or negative, or array is NULL." << std::endl; + return; + } + + for(int k=0;k< number_of_points-1; k++) + { + this->line_blend(points[2*k],points[2*k+1],points[2*k+2],points[2*k+3], opacity, red, green, blue); + } +} + +//int version +void pngwriter::polygon_blend( int * points, int number_of_points, double opacity, int red, int green, int blue) +{ + this->polygon_blend(points, number_of_points, + opacity, + ((double) red)/65535.0, + ((double) green)/65535.0, + ((double) blue)/65535.0 + ); +} + +void pngwriter::plotCMYK_blend(int x, int y, double opacity, double cyan, double magenta, double yellow, double black) +{ +/*CMYK to RGB: + * ----------- + * red = 255 - minimum(255,((cyan/255) * (255 - black) + black)) + * green = 255 - minimum(255,((magenta/255) * (255 - black) + black)) + * blue = 255 - minimum(255,((yellow/255) * (255 - black) + black)) + * */ + + if(cyan<0.0) + { + cyan = 0.0; + } + if(magenta<0.0) + { + magenta = 0.0; + } + if(yellow<0.0) + { + yellow = 0.0; + } + if(black<0.0) + { + black = 0.0; + } + + if(cyan>1.0) + { + cyan = 1.0; + } + if(magenta>1.0) + { + magenta = 1.0; + } + if(yellow>1.0) + { + yellow = 1.0; + } + if(black>1.0) + { + black = 1.0; + } + + double red, green, blue, minr, ming, minb, iblack; + + iblack = 1.0 - black; + + minr = 1.0; + ming = 1.0; + minb = 1.0; + + if( (cyan*iblack + black)<1.0 ) + { + minr = cyan*iblack + black; + } + + if( (magenta*iblack + black)<1.0 ) + { + ming = magenta*iblack + black; + } + + if( (yellow*iblack + black)<1.0 ) + { + minb = yellow*iblack + black; + } + + red = 1.0 - minr; + green = 1.0 - ming; + blue = 1.0 - minb; + + this->plot_blend(x,y,opacity, red, green, blue); + +} + +//int version +void pngwriter::plotCMYK_blend(int x, int y, double opacity, int cyan, int magenta, int yellow, int black) +{ + this->plotCMYK_blend( x, y, + opacity, + ((double) cyan)/65535.0, + ((double) magenta)/65535.0, + ((double) yellow)/65535.0, + ((double) black)/65535.0 + ); +} + +void pngwriter::laplacian(double k, double offset) +{ + + // Create image storage. + pngwriter temp(width_,height_,0,"temp"); + + double red, green, blue; + + for(int x = 1; x <= width_; x++) + { + for(int y = 1; y <= height_; y++) + { + red = + 8.0*this->dread(x,y,1) - + ( this->dread(x+1, y-1, 1) + + this->dread(x, y-1, 1) + + this->dread(x-1, y-1, 1) + + this->dread(x-1, y, 1) + + this->dread(x+1, y, 1) + + this->dread(x+1, y+1, 1) + + this->dread(x, y+1, 1) + + this->dread(x-1, y+1, 1) ); + + green = + 8.0*this->dread(x,y,2) - + ( this->dread(x+1, y-1, 2) + + this->dread(x, y-1, 2) + + this->dread(x-1, y-1, 2) + + this->dread(x-1, y, 2) + + this->dread(x+1, y, 2) + + this->dread(x+1, y+1, 2) + + this->dread(x, y+1, 2) + + this->dread(x-1, y+1, 2)); + + blue = + 8.0*this->dread(x,y,3) - + ( this->dread(x+1, y-1, 3) + + this->dread(x, y-1, 3) + + this->dread(x-1, y-1, 3) + + this->dread(x-1, y, 3) + + this->dread(x+1, y, 3) + + this->dread(x+1, y+1, 3) + + this->dread(x, y+1, 3) + + this->dread(x-1, y+1, 3)); + + temp.plot(x,y,offset+k*red,offset+k*green,offset+k*blue); + + } + } + + for(int xx = 1; xx <= width_; xx++) + { + for(int yy = 1; yy <= height_; yy++) + { + this->plot(xx,yy, temp.read(xx,yy,1), temp.read(xx,yy,2), temp.read(xx,yy,3)); + } + } +} + + + +// drwatop(), drawbottom() and filledtriangle() were contributed by Gurkan Sengun +// ( , http://www.linuks.mine.nu/ ) +void pngwriter::drawtop(long x1,long y1,long x2,long y2,long x3, int red, int green, int blue) +{ + // This swaps x2 and x3 + // if(x2>x3) x2^=x3^=x2^=x3; + if(x2>x3) + { + x2^=x3; + x3^=x2; + x2^=x3; + } + + long posl = x1*256; + long posr = posl; + + long cl=((x2-x1)*256)/(y2-y1); + long cr=((x3-x1)*256)/(y2-y1); + + for(int y=y1; yline(posl/256, y, posr/256, y, red, green, blue); + posl+=cl; + posr+=cr; + } +} + +// drwatop(), drawbottom() and filledtriangle() were contributed by Gurkan Sengun +// ( , http://www.linuks.mine.nu/ ) +void pngwriter::drawbottom(long x1,long y1,long x2,long x3,long y3, int red, int green, int blue) +{ + //Swap x1 and x2 + //if(x1>x2) x2^=x1^=x2^=x1; + if(x1>x2) + { + x2^=x1; + x1^=x2; + x2^=x1; + } + + long posl=x1*256; + long posr=x2*256; + + long cl=((x3-x1)*256)/(y3-y1); + long cr=((x3-x2)*256)/(y3-y1); + + for(int y=y1; yline(posl/256, y, posr/256, y, red, green, blue); + + posl+=cl; + posr+=cr; + } +} + +// drwatop(), drawbottom() and filledtriangle() were contributed by Gurkan Sengun +// ( , http://www.linuks.mine.nu/ ) +void pngwriter::filledtriangle(int x1,int y1,int x2,int y2,int x3,int y3, int red, int green, int blue) +{ + if((x1==x2 && x2==x3) || (y1==y2 && y2==y3)) return; + + if(y2drawtop(x1, y1, x2, y2, x3, red, green, blue); + } + else + { + if(y1==y3 || y1==y2) + { + this->drawbottom(x1, y1, x2, x3, y3, red, green, blue); + } + else + { + int new_x = x1 + (int)((double)(y2-y1)*(double)(x3-x1)/(double)(y3-y1)); + this->drawtop(x1, y1, new_x, y2, x2, red, green, blue); + this->drawbottom(x2, y2, new_x, x3, y3, red, green, blue); + } + } + +} + +//Double (bug found by Dave Wilks. Was: (int) red*65535, should have been (int) (red*65535). +void pngwriter::filledtriangle(int x1,int y1,int x2,int y2,int x3,int y3, double red, double green, double blue) +{ + this->filledtriangle(x1, y1, x2, y2, x3, y3, (int) (red*65535), (int) (green*65535), (int) (blue*65535)); +} + +//Blend, double. (bug found by Dave Wilks. Was: (int) red*65535, should have been (int) (red*65535). +void pngwriter::filledtriangle_blend(int x1,int y1,int x2,int y2,int x3,int y3, double opacity, double red, double green, double blue) +{ + this->filledtriangle_blend( x1, y1, x2, y2, x3, y3, opacity, (int) (red*65535), (int) (green*65535), (int) (blue*65535)); +} + +//Blend, int +void pngwriter::filledtriangle_blend(int x1,int y1,int x2,int y2,int x3,int y3, double opacity, int red, int green, int blue) +{ + if((x1==x2 && x2==x3) || (y1==y2 && y2==y3)) return; + + /*if(y2drawtop_blend(x1, y1, x2, y2, x3, opacity, red, green, blue); + } + else + { + if(y1==y3 || y1==y2) + { + this->drawbottom_blend(x1, y1, x2, x3, y3, opacity, red, green, blue); + } + else + { + int new_x = x1 + (int)((double)(y2-y1)*(double)(x3-x1)/(double)(y3-y1)); + this->drawtop_blend(x1, y1, new_x, y2, x2, opacity, red, green, blue); + this->drawbottom_blend(x2, y2, new_x, x3, y3, opacity, red, green, blue); + } + } + +} + +//Blend, int +void pngwriter::drawbottom_blend(long x1,long y1,long x2,long x3,long y3, double opacity, int red, int green, int blue) +{ + //Swap x1 and x2 + if(x1>x2) + { + x2^=x1; + x1^=x2; + x2^=x1; + } + + long posl=x1*256; + long posr=x2*256; + + long cl=((x3-x1)*256)/(y3-y1); + long cr=((x3-x2)*256)/(y3-y1); + + for(int y=y1; yline_blend(posl/256, y, posr/256, y, opacity, red, green, blue); + + posl+=cl; + posr+=cr; + } + +} + +//Blend, int +void pngwriter::drawtop_blend(long x1,long y1,long x2,long y2,long x3, double opacity, int red, int green, int blue) +{ + // This swaps x2 and x3 + if(x2>x3) + { + x2^=x3; + x3^=x2; + x2^=x3; +} + + long posl = x1*256; + long posr = posl; + + long cl=((x2-x1)*256)/(y2-y1); + long cr=((x3-x1)*256)/(y2-y1); + + for(int y=y1; yline_blend(posl/256, y, posr/256, y, opacity, red, green, blue); + posl+=cl; + posr+=cr; + } + +} + +void pngwriter::triangle(int x1, int y1, int x2, int y2, int x3, int y3, int red, int green, int blue) +{ + this->line(x1, y1, x2, y2, red, green, blue); + this->line(x2, y2, x3, y3, red, green, blue); + this->line(x3, y3, x1, y1, red, green, blue); +} + +void pngwriter::triangle(int x1, int y1, int x2, int y2, int x3, int y3, double red, double green, double blue) +{ + + this->line(x1, y1, x2, y2, ((int)65535*red), ((int)65535*green), ((int)65535*blue)); + this->line(x2, y2, x3, y3, ((int)65535*red), ((int)65535*green), ((int)65535*blue)); + this->line(x3, y3, x1, y1, ((int)65535*red), ((int)65535*green), ((int)65535*blue)); + +} + + + + + +void pngwriter::arrow( int x1,int y1,int x2,int y2,int size, double head_angle, double red, double green, double blue) +{ + + this->line(x1, y1, x2, y2, red, green, blue); + // double th = 3.141592653589793 + (head_angle)*3.141592653589793/180.0; //degrees + double th = 3.141592653589793 + head_angle; + double costh = cos(th); + double sinth = sin(th); + double t1, t2, r; + t1 = ((x2-x1)*costh - (y2-y1)*sinth); + t2 = ((x2-x1)*sinth + (y2-y1)*costh); + r = sqrt(t1*t1 + t2*t2); + + double advancex = size*t1/r; + double advancey = size*t2/r; + this->line(x2, y2, int(x2 + advancex), int(y2 + advancey), red, green, blue); + t1 = (x2-x1)*costh + (y2-y1)*sinth; + t2 = (y2-y1)*costh - (x2-x1)*sinth; + + advancex = size*t1/r; + advancey = size*t2/r; + this->line(x2, y2, int(x2 + advancex), int(y2 + advancey), red, green, blue); +} + +void pngwriter::filledarrow( int x1,int y1,int x2,int y2,int size, double head_angle, double red, double green, double blue) +{ + int p1x, p2x, p3x, p1y, p2y, p3y; + + this->line(x1, y1, x2, y2, red, green, blue); + double th = 3.141592653589793 + head_angle; + double costh = cos(th); + double sinth = sin(th); + double t11, t12, t21, t22, r1, r2; + t11 = ((x2-x1)*costh - (y2-y1)*sinth); + t21 = ((x2-x1)*sinth + (y2-y1)*costh); + t12 = (x2-x1)*costh + (y2-y1)*sinth; + t22 = (y2-y1)*costh - (x2-x1)*sinth; + + r1 = sqrt(t11*t11 + t21*t21); + r2 = sqrt(t12*t12 + t22*t22); + + double advancex1 = size*t11/r1; + double advancey1 = size*t21/r1; + double advancex2 = size*t12/r2; + double advancey2 = size*t22/r2; + + p1x = x2; + p1y = y2; + + p2x = int(x2 + advancex1); + p2y = int(y2 + advancey1); + + p3x = int(x2 + advancex2); + p3y = int(y2 + advancey2); + + + this->filledtriangle( p1x, p1y, p2x, p2y, p3x, p3y, red, green, blue); + +} + +void pngwriter::arrow( int x1,int y1,int x2,int y2,int size, double head_angle, int red, int green, int blue) +{ + this->arrow( x1, y1, x2, y2, size, head_angle, (double (red))/65535.0, (double (green))/65535.0, (double (blue))/65535.0 ); +} + +void pngwriter::filledarrow( int x1,int y1,int x2,int y2,int size, double head_angle, int red, int green, int blue) +{ + this->filledarrow( x1, y1, x2, y2, size, head_angle, (double (red))/65535.0, (double (green))/65535.0, (double (blue))/65535.0 ); +} + + +void pngwriter::cross( int x, int y, int xwidth, int yheight, int red, int green, int blue) +{ + this->line(int(x - xwidth/2.0), y, int(x + xwidth/2.0), y, red, green, blue); + this->line(x, int(y - yheight/2.0), x, int(y + yheight/2.0), red, green, blue); +} + +void pngwriter::maltesecross( int x, int y, int xwidth, int yheight, int x_bar_height, int y_bar_width, int red, int green, int blue) +{ + this->line(int(x - xwidth/2.0), y, int(x + xwidth/2.0), y, red, green, blue); + this->line(x, int(y - yheight/2.0), x, int(y + yheight/2.0), red, green, blue); + // Bars on ends of vertical line + this->line(int(x - y_bar_width/2.0), int(y + yheight/2.0), int(x + y_bar_width/2.0), int(y + yheight/2.0), red, green, blue); + this->line(int(x - y_bar_width/2.0), int(y - yheight/2.0), int(x + y_bar_width/2.0), int(y - yheight/2.0), red, green, blue); + // Bars on ends of horizontal line. + this->line(int(x - xwidth/2.0), int(y - x_bar_height/2.0), int(x - xwidth/2.0), int(y + x_bar_height/2.0), red, green, blue); + this->line(int(x + xwidth/2.0), int(y - x_bar_height/2.0), int(x + xwidth/2.0), int(y + x_bar_height/2.0), red, green, blue); +} + +void pngwriter::cross( int x, int y, int xwidth, int yheight, double red, double green, double blue) +{ + this->cross( x, y, xwidth, yheight, int(65535*red), int(65535*green), int(65535*blue)); +} + +void pngwriter::maltesecross( int x, int y, int xwidth, int yheight, int x_bar_height, int y_bar_width, double red, double green, double blue) +{ + this->maltesecross( x, y, xwidth, yheight, x_bar_height, y_bar_width, int(65535*red), int(65535*green), int(65535*blue)); +} + + +void pngwriter::filleddiamond( int x, int y, int width, int height, int red, int green, int blue) +{ + this->filledtriangle( int(x - width/2.0), y, x, y, x, int(y + height/2.0), red, green, blue); + this->filledtriangle( int(x + width/2.0), y, x, y, x, int(y + height/2.0), red, green, blue); + this->filledtriangle( int(x - width/2.0), y, x, y, x, int(y - height/2.0), red, green, blue); + this->filledtriangle( int(x + width/2.0), y, x, y, x, int(y - height/2.0), red, green, blue); +} + +void pngwriter::diamond( int x, int y, int width, int height, int red, int green, int blue) +{ + this->line( int(x - width/2.0), y, x, int(y + height/2.0), red, green, blue); + this->line( int(x + width/2.0), y, x, int(y + height/2.0), red, green, blue); + this->line( int(x - width/2.0), y, x, int(y - height/2.0), red, green, blue); + this->line( int(x + width/2.0), y, x, int(y - height/2.0), red, green, blue); +} + + +void pngwriter::filleddiamond( int x, int y, int width, int height, double red, double green, double blue) +{ + this->filleddiamond( x, y, width, height, int(red*65535), int(green*65535), int(blue*65535) ); +} + +void pngwriter::diamond( int x, int y, int width, int height, double red, double green, double blue) +{ + this->diamond( x, y, width, height, int(red*65535), int(green*65535), int(blue*65535) ); +} + diff --git a/pngwriter/pngwriter.h b/pngwriter/pngwriter.h new file mode 100644 index 0000000..9a2269b --- /dev/null +++ b/pngwriter/pngwriter.h @@ -0,0 +1,747 @@ +//********** pngwriter.h ********************************************** +// Author: Paul Blackburn +// +// Email: individual61@users.sourceforge.net +// +// Version: 0.5.4 (19 / II / 2009) +// +// Description: Library that allows plotting a 48 bit +// PNG image pixel by pixel, which can +// then be opened with a graphics program. +// +// License: GNU General Public License +// Copyright 2002, 2003, 2004, 2005, 2006, 2007, +// 2008, 2009 Paul Blackburn +// +// Website: Main: http://pngwriter.sourceforge.net/ +// Sourceforge.net: http://sourceforge.net/projects/pngwriter/ +// Freshmeat.net: http://freshmeat.net/projects/pngwriter/ +// +// Documentation: This header file is commented, but for a +// quick reference document, and support, +// take a look at the website. +// +//************************************************************************* + + +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * */ + +#ifndef PNGWRITER_H +#define PNGWRITER_H 1 + +#define PNGWRITER_VERSION 0.54 + +#define NO_FREETYPE + +#include + +// REMEMBER TO ADD -DNO_FREETYPE TO YOUR COMPILATION FLAGS IF PNGwriter WAS +// COMPILED WITHOUT FREETYPE SUPPORT!!! +// +// RECUERDA AGREGAR -DNO_FREETYPE A TUS OPCIONES DE COMPILACION SI PNGwriter +// FUE COMPILADO SIN SOPORTE PARA FREETYPE!!! +// +#ifndef NO_FREETYPE +#include +#include FT_FREETYPE_H +#endif + + + +#ifdef OLD_CPP // For compatibility with older compilers. +#include +#include +#include +#include +using namespace std; +#endif // from ifdef OLD_CPP + +#ifndef OLD_CPP // Default situation. +#include +#include +#include +#include +#endif // from ifndef OLD_CPP + + +//png.h must be included before FreeType headers. +#include +#include +#include + + + + +#define PNG_BYTES_TO_CHECK (4) +#define PNGWRITER_DEFAULT_COMPRESSION (6) + +class pngwriter +{ + private: + + char * filename_; + char * textauthor_; + char * textdescription_; + char * texttitle_; + char * textsoftware_; + + + + int height_; + int width_; + int backgroundcolour_; + int bit_depth_; + int rowbytes_; + int colortype_; + int compressionlevel_; + bool transformation_; // Required by Mikkel's patch + + unsigned char * * graph_; + double filegamma_; + double screengamma_; + void circle_aux(int xcentre, int ycentre, int x, int y, int red, int green, int blue); + void circle_aux_blend(int xcentre, int ycentre, int x, int y, double opacity, int red, int green, int blue); + int check_if_png(char *file_name, FILE **fp); + int read_png_info(FILE *fp, png_structp *png_ptr, png_infop *info_ptr); + int read_png_image(FILE *fp, png_structp png_ptr, png_infop info_ptr, + png_bytepp *image, png_uint_32 *width, png_uint_32 *height); + void flood_fill_internal( int xstart, int ystart, double start_red, double start_green, double start_blue, double fill_red, double fill_green, double fill_blue); + void flood_fill_internal_blend( int xstart, int ystart, double opacity, double start_red, double start_green, double start_blue, double fill_red, double fill_green, double fill_blue); + +#ifndef NO_FREETYPE + void my_draw_bitmap( FT_Bitmap * bitmap, int x, int y, double red, double green, double blue); + void my_draw_bitmap_blend( FT_Bitmap * bitmap, int x, int y,double opacity, double red, double green, double blue); +#endif + + /* The algorithms HSVtoRGB and RGBtoHSV were found at http://www.cs.rit.edu/~ncs/ + * which is a page that belongs to Nan C. Schaller, though + * these algorithms appear to be the work of Eugene Vishnevsky. + * */ + void HSVtoRGB( double *r, double *g, double *b, double h, double s, double v ); + void RGBtoHSV( float r, float g, float b, float *h, float *s, float *v ); + + /* drwatop(), drawbottom() and filledtriangle() were contributed by Gurkan Sengun + * ( , http://www.linuks.mine.nu/ ) + * */ + void drawtop(long x1,long y1,long x2,long y2,long x3, int red, int green, int blue); + void drawbottom(long x1,long y1,long x2,long x3,long y3, int red, int green, int blue); + void drawbottom_blend(long x1,long y1,long x2,long x3,long y3, double opacity, int red, int green, int blue); + void drawtop_blend(long x1,long y1,long x2,long y2,long x3, double opacity, int red, int green, int blue); + + public: + + /* General Notes + * It is important to remember that all functions that accept an argument of type "const char *" will also + * accept "char *", this is done so you can have a changing filename (to make many PNG images in series + * with a different name, for example), and to allow you to use string type objects which can be easily + * turned into const char * (if theString is an object of type string, then it can be used as a const char * + * by saying theString.c_str()). + * It is also important to remember that whenever a function has a colour coeffiecient as its argument, + * that argument can be either an int from 0 to 65535 or a double from 0.0 to 1.0. + * It is important to make sure that you are calling the function with the type that you want. + * Remember that 1 is an int, while 1.0 is a double, and will thus determine what version of the function + * will be used. Similarly, do not make the mistake of calling for example plot(x, y, 0.0, 0.0, 65535), + * because + * there is no plot(int, int, double, double, int). + * Also, please note that plot() and read() (and the functions that use them internally) + * are protected against entering, for example, a colour coefficient that is over 65535 + * or over 1.0. Similarly, they are protected against negative coefficients. read() will return 0 + * when called outside the image range. This is actually useful as zero-padding should you need it. + * */ + + /* Compilation + * A typical compilation would look like this: + * + * g++ my_program.cc -o my_program freetype-config --cflags \ + * -I/usr/local/include -L/usr/local/lib -lpng -lpngwriter -lz -lfreetype + * + * If you did not compile PNGwriter with FreeType support, then remove the + * FreeType-related flags and add -DNO_FREETYPE above. + * */ + + /* Constructor + * The constructor requires the width and the height of the image, the background colour for the + * image and the filename of the file (a pointer or simple "myfile.png"). The background colour + * can only be initialized to a shade of grey (once the object has been created you can do whatever + * you want, though), because generally one wants either a white (65535 or 1.0) or a black (0 or 0.0) + * background to start with. + * The default constructor creates a PNGwriter instance that is 250x250, white background, + * and filename "out.png". + * Tip: The filename can be given as easily as: + * pngwriter mypng(300, 300, 0.0, "myfile.png"); + * Tip: If you are going to create a PNGwriter instance for reading in a file that already exists, + * then width and height can be 1 pixel, and the size will be automatically adjusted once you use + * readfromfile(). + * */ + pngwriter(); + pngwriter(const pngwriter &rhs); + pngwriter(int width, int height, int backgroundcolour, char * filename); + pngwriter(int width, int height, double backgroundcolour, char * filename); + pngwriter(int width, int height, int backgroundcolour, const char * filename); + pngwriter(int width, int height, double backgroundcolour, const char * filename); + + /* Destructor + * */ + ~pngwriter(); + + /* Assignment Operator + * */ + pngwriter & operator = (const pngwriter & rhs); + + /* Plot + * With this function a pixel at coordinates (x, y) can be set to the desired colour. + * The pixels are numbered starting from (1, 1) and go to (width, height). + * As with most functions in PNGwriter, it has been overloaded to accept either int arguments + * for the colour coefficients, or those of type double. If they are of type int, + * they go from 0 to 65535. If they are of type double, they go from 0.0 to 1.0. + * Tip: To plot using red, then specify plot(x, y, 1.0, 0.0, 0.0). To make pink, + * just add a constant value to all three coefficients, like this: + * plot(x, y, 1.0, 0.4, 0.4). + * Tip: If nothing is being plotted to your PNG file, make sure that you remember + * to close() the instance before your program is finished, and that the x and y position + * is actually within the bounds of your image. If either is not, then PNGwriter will + * not complain-- it is up to you to check for this! + * Tip: If you try to plot with a colour coefficient out of range, a maximum or minimum + * coefficient will be assumed, according to the given coefficient. For example, attempting + * to plot plot(x, y, 1.0,-0.2,3.7) will set the green coefficient to 0 and the red coefficient + * to 1.0. + * */ + void plot(int x, int y, int red, int green, int blue); + void plot(int x, int y, double red, double green, double blue); + + /* Plot HSV + * With this function a pixel at coordinates (x, y) can be set to the desired colour, + * but with the colour coefficients given in the Hue, Saturation, Value colourspace. + * This has the advantage that one can determine the colour that will be plotted with + * only one parameter, the Hue. The colour coefficients must go from 0 to 65535 and + * be of type int, or be of type double and go from 0.0 to 1.0. + * */ + void plotHSV(int x, int y, double hue, double saturation, double value); + void plotHSV(int x, int y, int hue, int saturation, int value); + + /* Read + * With this function we find out what colour the pixel (x, y) is. If "colour" is 1, + * it will return the red coefficient, if it is set to 2, the green one, and if + * it set to 3, the blue colour coefficient will be returned, + * and this returned value will be of type int and be between 0 and 65535. + * Note that if you call read() on a pixel outside the image range, the value returned + * will be 0. + * */ + int read(int x, int y, int colour); + + /* Read, Average + * Same as the above, only that the average of the three colour coefficients is returned. + */ + int read(int x, int y); + + /* dRead + * With this function we find out what colour the pixel (x, y) is. If "colour" is 1, + * it will return the red coefficient, if it is set to 2, the green one, and if + * it set to 3, the blue colour coefficient will be returned, + * and this returned value will be of type double and be between 0.0 and 1.0. + * Note that if you call dread() outside the image range, the value returned will be 0.0 + * */ + double dread(int x, int y, int colour); + + /* dRead, Average + * Same as the above, only that the average of the three colour coefficients is returned. + */ + double dread(int x, int y); + + /* Read HSV + * With this function we find out what colour the pixel (x, y) is, but in the Hue, + * Saturation, Value colourspace. If "colour" is 1, + * it will return the Hue coefficient, if it is set to 2, the Saturation one, and if + * it set to 3, the Value colour coefficient will be returned, and this returned + * value will be of type int and be between 0 and 65535. Important: If you attempt + * to read the Hue of a pixel that is a shade of grey, the value returned will be + * nonsensical or even NaN. This is just the way the RGB -> HSV algorithm works: + * the Hue of grey is not defined. You might want to check whether the pixel + * you are reading is grey before attempting a readHSV(). + * Tip: This is especially useful for categorizing sections of the image according + * to their colour. + * */ + int readHSV(int x, int y, int colour); + + /* dRead HSV + * With this function we find out what colour the pixel (x, y) is, but in the Hue, + * Saturation, Value colourspace. If "colour" is 1, + * it will return the Hue coefficient, if it is set to 2, the Saturation one, and if + * it set to 3, the Value colour coefficient will be returned, + * and this returned value will be of type double and be between 0.0 and 1.0. + * */ + double dreadHSV(int x, int y, int colour); + + /* Clear + * The whole image is set to black. + * */ + void clear(void); + + /* Close + * Close the instance of the class, and write the image to disk. + * Tip: If you do not call this function before your program ends, no image + * will be written to disk. + * */ + void close(void); + + /* Rename + * To rename the file once an instance of pngwriter has been created. + * Useful for assigning names to files based upon their content. + * Tip: This is as easy as calling pngwriter_rename("newname.png") + * If the argument is a long unsigned int, for example 77, the filename will be changed to + * 0000000077.png + * Tip: Use this to create sequences of images for movie generation. + * */ + void pngwriter_rename(char * newname); + void pngwriter_rename(const char * newname); + void pngwriter_rename(long unsigned int index); + + /* Figures + * These functions draw basic shapes. Available in both int and double versions. + * The line functions use the fast Bresenham algorithm. Despite the name, + * the square functions draw rectangles. The circle functions use a fast + * integer math algorithm. The filled circle functions make use of sqrt(). + * */ + void line(int xfrom, int yfrom, int xto, int yto, int red, int green,int blue); + void line(int xfrom, int yfrom, int xto, int yto, double red, double green,double blue); + + void triangle(int x1, int y1, int x2, int y2, int x3, int y3, int red, int green, int blue); + void triangle(int x1, int y1, int x2, int y2, int x3, int y3, double red, double green, double blue); + + void square(int xfrom, int yfrom, int xto, int yto, int red, int green,int blue); + void square(int xfrom, int yfrom, int xto, int yto, double red, double green,double blue); + + void filledsquare(int xfrom, int yfrom, int xto, int yto, int red, int green,int blue); + void filledsquare(int xfrom, int yfrom, int xto, int yto, double red, double green,double blue); + + void circle(int xcentre, int ycentre, int radius, int red, int green, int blue); + void circle(int xcentre, int ycentre, int radius, double red, double green, double blue); + + void filledcircle(int xcentre, int ycentre, int radius, int red, int green, int blue); + void filledcircle(int xcentre, int ycentre, int radius, double red, double green, double blue); + + + /* Read From File + * Open the existing PNG image, and copy it into this instance of the class. It is important to mention + * that PNG variants are supported. Very generally speaking, most PNG files can now be read (as of version 0.5.4), + * but if they have an alpha channel it will be completely stripped. If the PNG file uses GIF-style transparency + * (where one colour is chosen to be transparent), PNGwriter will not read the image properly, but will not + * complain. Also, if any ancillary chunks are included in the PNG file (chroma, filter, etc.), it will render + * with a slightly different tonality. For the vast majority of PNGs, this should not be an issue. Note: + * If you read an 8-bit PNG, the internal representation of that instance of PNGwriter will be 8-bit (PNG + * files of less than 8 bits will be upscaled to 8 bits). To convert it to 16-bit, just loop over all pixels, + * reading them into a new instance of PNGwriter. New instances of PNGwriter are 16-bit by default. + * */ + + void readfromfile(char * name); + void readfromfile(const char * name); + + /* Get Height + * When you open a PNG with readfromfile() you can find out its height with this function. + * */ + int getheight(void); + + /* Get Width + * When you open a PNG with readfromfile() you can find out its width with this function. + * */ + int getwidth(void); + + /* Set Compression Level + * Set the compression level that will be used for the image. -1 is to use the default, + * 0 is none, 9 is best compression. + * Remember that this will affect how long it will take to close() the image. A value of 2 or 3 + * is good enough for regular use, but for storage or transmission you might want to take the time + * to set it at 9. + * */ + void setcompressionlevel(int level); + + /* Get Bit Depth + * When you open a PNG with readfromfile() you can find out its bit depth with this function. + * Mostly for troubleshooting uses. + * */ + int getbitdepth(void); + + /* Get Colour Type + * When you open a PNG with readfromfile() you can find out its colour type (libpng categorizes + * different styles of image data with this number). + * Mostly for troubleshooting uses. + * */ + int getcolortype(void); + + /* Set Gamma Coeff + * Set the image's gamma (file gamma) coefficient. This is experimental, but use it if your image's colours seem too bright + * or too dark. The default value of 0.5 should be fine. The standard disclaimer about Mac and PC gamma + * settings applies. + * */ + void setgamma(double gamma); + + + /* Get Gamma Coeff + * Get the image's gamma coefficient. This is experimental. + * */ + double getgamma(void); + + /* Bezier Curve + * (After Frenchman Pierre Bˇzier from Regie Renault) + * A collection of formulae for describing curved lines + * and surfaces, first used in 1972 to model automobile surfaces. + * (from the The Free On-line Dictionary of Computing) + * See http://www.moshplant.com/direct-or/bezier/ for one of many + * available descriptions of bezier curves. + * There are four points used to define the curve: the two endpoints + * of the curve are called the anchor points, while the other points, + * which define the actual curvature, are called handles or control points. + * Moving the handles lets you modify the shape of the curve. + * */ + + void bezier( int startPtX, int startPtY, + int startControlX, int startControlY, + int endPtX, int endPtY, + int endControlX, int endControlY, + double red, double green, double blue); + + void bezier( int startPtX, int startPtY, + int startControlX, int startControlY, + int endPtX, int endPtY, + int endControlX, int endControlY, + int red, int green, int blue); + + /* Set Text + * Sets the text information in the PNG header. If it is not called, the default is used. + */ + void settext(char * title, char * author, char * description, char * software); + void settext(const char * title, const char * author, const char * description, const char * software); + + + /* Version Number + * Returns the PNGwriter version number. + */ + static double version(void); + + /* Write PNG + * Writes the PNG image to disk. You can still change the PNGwriter instance after this. + * Tip: This is exactly the same as close(), but easier to remember. + * Tip: To make a sequence of images using only one instance of PNGwriter, alter the image, change its name, + * write_png(), then alter the image, change its name, write_png(), etc. + */ + void write_png(void); + + /* Plot Text + * Uses the Freetype2 library to set text in the image. face_path is the file path to a + * TrueType font file (.ttf) (FreeType2 can also handle other types). fontsize specifices the approximate + * height of the rendered font in pixels. x_start and y_start specify the placement of the + * lower, left corner of the text string. angle is the text angle in radians. text is the text to be rendered. + * The colour coordinates can be doubles from 0.0 to 1.0 or ints from 0 to 65535. + * Tip: PNGwriter installs a few fonts in /usr/local/share/pngwriter/fonts to get you started. + * Tip: Remember to add -DNO_FREETYPE to your compilation flags if PNGwriter was compiled without FreeType support. + * */ + void plot_text(char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double red, double green, double blue); + void plot_text(char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, int red, int green, int blue); + + + /* Plot UTF-8 Text + * Same as the above, but the text to be plotted is encoded in UTF-8. Why would you want this? To be able to plot + * all characters available in a large TrueType font, for example: for rendering Japenese, Chinese and other + * languages not restricted to the standard 128 character ASCII space. + * Tip: The quickest way to get a string into UTF-8 is to write it in an adequate text editor, and save it as a file + * in UTF-8 encoding, which can then be read in in binary mode. + * */ + void plot_text_utf8(char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double red, double green, double blue); + void plot_text_utf8(char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, int red, int green, int blue); + + + /* Bilinear Interpolation of Image + * Given a floating point coordinate (x from 0.0 to width, y from 0.0 to height), + * this function will return the interpolated colour intensity specified by + * colour (where red = 1, green = 2, blue = 3). + * bilinear_interpolate_read() returns an int from 0 to 65535, and + * bilinear_interpolate_dread() returns a double from 0.0 to 1.0. + * Tip: Especially useful for enlarging an image. + * */ + int bilinear_interpolation_read(double x, double y, int colour); + double bilinear_interpolation_dread(double x, double y, int colour); + + /* Plot Blend + * Plots the colour given by red, green blue, but blended with the existing pixel + * value at that position. opacity is a double that goes from 0.0 to 1.0. + * 0.0 will not change the pixel at all, and 1.0 will plot the given colour. + * Anything in between will be a blend of both pixel levels. Please note: This is neither + * alpha channel nor PNG transparency chunk support. This merely blends the plotted pixels. + * */ + + void plot_blend(int x, int y, double opacity, int red, int green, int blue); + void plot_blend(int x, int y, double opacity, double red, double green, double blue); + + + /* Invert + * Inverts the image in RGB colourspace. + * */ + void invert(void); + + /* Resize Image + * Resizes the PNGwriter instance. Note: All image data is set to black (this is + * a resizing, not a scaling, of the image). + * */ + void resize(int width, int height); + + /* Boundary Fill + * All pixels adjacent to the start pixel will be filled with the fill colour, until the boundary colour is encountered. + * For example, calling boundary_fill() with the boundary colour set to red, on a pixel somewhere inside a red circle, + * will fill the entire circle with the desired fill colour. If, on the other hand, the circle is not the boundary colour, + * the rest of the image will be filled. + * The colour components are either doubles from 0.0 to 1.0 or ints from 0 to 65535. + * */ + void boundary_fill(int xstart, int ystart, double boundary_red,double boundary_green,double boundary_blue,double fill_red, double fill_green, double fill_blue) ; + void boundary_fill(int xstart, int ystart, int boundary_red,int boundary_green,int boundary_blue,int fill_red, int fill_green, int fill_blue) ; + + /* Flood Fill + * All pixels adjacent to the start pixel will be filled with the fill colour, if they are the same colour as the + * start pixel. For example, calling flood_fill() somewhere in the interior of a solid blue rectangle will colour + * the entire rectangle the fill colour. The colour components are either doubles from 0.0 to 1.0 or ints from 0 to 65535. + * */ + void flood_fill(int xstart, int ystart, double fill_red, double fill_green, double fill_blue) ; + void flood_fill(int xstart, int ystart, int fill_red, int fill_green, int fill_blue) ; + + /* Polygon + * This function takes an array of integer values containing the coordinates of the vertexes of a polygon. + * Note that if you want a closed polygon, you must repeat the first point's coordinates for the last point. + * It also requires the number of points contained in the array. For example, if you wish to plot a triangle, + * the array will contain 6 elements, and the number of points is 3. Be very careful about this; if you specify the wrong number + * of points, your program will either segfault or produce points at nonsensical coordinates. + * The colour components are either doubles from 0.0 to 1.0 or ints from 0 to 65535. + * */ + void polygon(int * points, int number_of_points, double red, double green, double blue); + void polygon(int * points, int number_of_points, int red, int green, int blue); + + /* Plot CMYK + * Plot a point in the Cyan, Magenta, Yellow, Black colourspace. Please note that this colourspace is + * lossy, i.e. it cannot reproduce all colours on screen that RGB can. The difference, however, is + * barely noticeable. The algorithm used is a standard one. The colour components are either + * doubles from 0.0 to 1.0 or ints from 0 to 65535. + * */ + void plotCMYK(int x, int y, double cyan, double magenta, double yellow, double black); + void plotCMYK(int x, int y, int cyan, int magenta, int yellow, int black); + + /* Read CMYK, Double version + * Get a pixel in the Cyan, Magenta, Yellow, Black colourspace. if 'colour' is 1, the Cyan component will be returned + * as a double from 0.0 to 1.0. If 'colour is 2, the Magenta colour component will be returned, and so on, up to 4. + * */ + double dreadCMYK(int x, int y, int colour); + + /* Read CMYK + * Same as the above, but the colour components returned are an int from 0 to 65535. + * */ + int readCMYK(int x, int y, int colour); + + /* Scale Proportional + * Scale the image using bilinear interpolation. If k is greater than 1.0, the image will be enlarged. + * If k is less than 1.0, the image will be shrunk. Negative or null values of k are not allowed. + * The image will be resized and the previous content will be replaced by the scaled image. + * Tip: use getheight() and getwidth() to find out the new width and height of the scaled image. + * Note: After scaling, all images will have a bit depth of 16, even if the original image had + * a bit depth of 8. + * */ + void scale_k(double k); + + /* Scale Non-Proportional + * Scale the image using bilinear interpolation, with different horizontal and vertical scale factors. + * */ + void scale_kxky(double kx, double ky); + + /* Scale To Target Width and Height + * Scale the image in such a way as to meet the target width and height. + * Tip: if you want to keep the image proportional, scale_k() might be more appropriate. + * */ + void scale_wh(int finalwidth, int finalheight); + + + /* Blended Functions + * All these functions are identical to their non-blended types. They take an extra argument, opacity, which is + * a double from 0.0 to 1.0 and represents how much of the original pixel value is retained when plotting the + * new pixel. In other words, if opacity is 0.7, then after plotting, the new pixel will be 30% of the + * original colour the pixel was, and 70% of the new colour, whatever that may be. As usual, each function + * is available in int or double versions. Please note: This is neither alpha channel nor PNG transparency chunk support. This merely blends the plotted pixels. + * */ + + // Start Blended Functions + + void plotHSV_blend(int x, int y, double opacity, double hue, double saturation, double value); + void plotHSV_blend(int x, int y, double opacity, int hue, int saturation, int value); + + void line_blend(int xfrom, int yfrom, int xto, int yto, double opacity, int red, int green,int blue); + void line_blend(int xfrom, int yfrom, int xto, int yto, double opacity, double red, double green,double blue); + + void square_blend(int xfrom, int yfrom, int xto, int yto, double opacity, int red, int green,int blue); + void square_blend(int xfrom, int yfrom, int xto, int yto, double opacity, double red, double green,double blue); + + void filledsquare_blend(int xfrom, int yfrom, int xto, int yto, double opacity, int red, int green,int blue); + void filledsquare_blend(int xfrom, int yfrom, int xto, int yto, double opacity, double red, double green,double blue); + + void circle_blend(int xcentre, int ycentre, int radius, double opacity, int red, int green, int blue); + void circle_blend(int xcentre, int ycentre, int radius, double opacity, double red, double green, double blue); + + void filledcircle_blend(int xcentre, int ycentre, int radius, double opacity, int red, int green, int blue); + void filledcircle_blend(int xcentre, int ycentre, int radius, double opacity, double red, double green, double blue); + + void bezier_blend( int startPtX, int startPtY, + int startControlX, int startControlY, + int endPtX, int endPtY, + int endControlX, int endControlY, + double opacity, + double red, double green, double blue); + + void bezier_blend( int startPtX, int startPtY, + int startControlX, int startControlY, + int endPtX, int endPtY, + int endControlX, int endControlY, + double opacity, + int red, int green, int blue); + + void plot_text_blend(char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity, double red, double green, double blue); + void plot_text_blend(char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity, int red, int green, int blue); + + void plot_text_utf8_blend(char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity, double red, double green, double blue); + void plot_text_utf8_blend(char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity, int red, int green, int blue); + + void boundary_fill_blend(int xstart, int ystart, double opacity, double boundary_red,double boundary_green,double boundary_blue,double fill_red, double fill_green, double fill_blue) ; + void boundary_fill_blend(int xstart, int ystart, double opacity, int boundary_red,int boundary_green,int boundary_blue,int fill_red, int fill_green, int fill_blue) ; + + void flood_fill_blend(int xstart, int ystart, double opacity, double fill_red, double fill_green, double fill_blue) ; + void flood_fill_blend(int xstart, int ystart, double opacity, int fill_red, int fill_green, int fill_blue) ; + + void polygon_blend(int * points, int number_of_points, double opacity, double red, double green, double blue); + void polygon_blend(int * points, int number_of_points, double opacity, int red, int green, int blue); + + void plotCMYK_blend(int x, int y, double opacity, double cyan, double magenta, double yellow, double black); + void plotCMYK_blend(int x, int y, double opacity, int cyan, int magenta, int yellow, int black); + + // End of Blended Functions + + /* Laplacian + * This function applies a discrete laplacian to the image, multiplied by a constant factor. + * The kernel used in this case is: + * 1.0 1.0 1.0 + * 1.0 -8.0 1.0 + * 1.0 1.0 1.0 + * Basically, this works as an edge detector. The current pixel is assigned the sum of all neighbouring + * pixels, multiplied by the corresponding kernel element. For example, imagine a pixel and its 8 neighbours: + * 1.0 1.0 0.0 0.0 + * 1.0 ->1.0<- 0.0 0.0 + * 1.0 1.0 0.0 0.0 + * This represents a border between white and black, black is on the right. Applying the laplacian to + * the pixel specified above pixel gives: + * 1.0*1.0 + 1.0*1.0 + 0.0*1.0 + + * 1.0*1.0 + 1.0*-8.0 + 0.0*1.0 + + * 1.0*1.0 + 1.0*1.0 + 0.0*1.0 = -3.0 + * Applying this to the pixel to the right of the pixel considered previously, we get a sum of 3.0. + * That is, after passing over an edge, we get a high value for the pixel adjacent to the edge. Since + * PNGwriter limits the colour components if they are off-scale, and the result of the laplacian + * may be negative, a scale factor and an offset value are included. This might be useful for + * keeping things within range or for bringing out more detail in the edge detection. The + * final pixel value will be given by: + * final value = laplacian(original pixel)*k + offset + * Tip: Try a value of 1.0 for k to start with, and then experiment with other values. + * */ + void laplacian(double k, double offset); + + /* Filled Triangle + * Draws the triangle specified by the three pairs of points in the colour specified + * by the colour coefficients. The colour components are either doubles from 0.0 to + * 1.0 or ints from 0 to 65535. + * */ + void filledtriangle(int x1,int y1,int x2,int y2,int x3,int y3, int red, int green, int blue); + void filledtriangle(int x1,int y1,int x2,int y2,int x3,int y3, double red, double green, double blue); + + /* Filled Triangle, Blended + * Draws the triangle specified by the three pairs of points in the colour specified + * by the colour coefficients, and blended with the background. See the description for Blended Functions. + * The colour components are either doubles from 0.0 to 1.0 or ints from 0 to 65535. + * */ + void filledtriangle_blend(int x1,int y1,int x2,int y2,int x3,int y3, double opacity, int red, int green, int blue); + void filledtriangle_blend(int x1,int y1,int x2,int y2,int x3,int y3, double opacity, double red, double green, double blue); + + /* Arrow, Filled Arrow + * Plots an arrow from (x1, y1) to (x2, y2) with the arrowhead at the second point, given the size in pixels + * and the angle in radians of the arrowhead. The plotted arrow consists of one main line, and two smaller + * lines originating from the second point. Filled Arrow plots the same, but the arrowhead is a solid triangle. + * Tip: An angle of 10 to 30 degrees looks OK. + * */ + + void arrow( int x1,int y1,int x2,int y2,int size, double head_angle, double red, double green, double blue); + void arrow( int x1,int y1,int x2,int y2,int size, double head_angle, int red, int green, int blue); + + void filledarrow( int x1,int y1,int x2,int y2,int size, double head_angle, double red, double green, double blue); + void filledarrow( int x1,int y1,int x2,int y2,int size, double head_angle, int red, int green, int blue); + + /* Cross, Maltese Cross + * Plots a simple cross at x, y, with the specified height and width, and in the specified colour. + * Maltese cross plots a cross, as before, but adds bars at the end of each arm of the cross. + * The size of these bars is specified with x_bar_height and y_bar_width. + * The cross will look something like this: + * + * ----- <-- ( y_bar_width) + * | + * | + * |-------| <-- ( x_bar_height ) + * | + * | + * ----- + * */ + + void cross( int x, int y, int xwidth, int yheight, double red, double green, double blue); + void cross( int x, int y, int xwidth, int yheight, int red, int green, int blue); + + void maltesecross( int x, int y, int xwidth, int yheight, int x_bar_height, int y_bar_width, double red, double green, double blue); + void maltesecross( int x, int y, int xwidth, int yheight, int x_bar_height, int y_bar_width, int red, int green, int blue); + + /* Diamond and filled diamond + * Plots a diamond shape, given the x, y position, the width and height, and the colour. + * Filled diamond plots a filled diamond. + * */ + + void filleddiamond( int x, int y, int width, int height, int red, int green, int blue); + void diamond(int x, int y, int width, int height, int red, int green, int blue); + + void filleddiamond( int x, int y, int width, int height, double red, double green, double blue); + void diamond(int x, int y, int width, int height, double red, double green, double blue); + + /* Get Text Width, Get Text Width UTF8 + * Returns the approximate width, in pixels, of the specified *unrotated* text. It is calculated by adding + * each letter's width and kerning value (as specified in the TTF file). Note that this will not + * give the position of the farthest pixel, but it will give a pretty good idea of what area the + * text will occupy. Tip: The text, when plotted unrotated, will fit approximately in a box with its lower left corner at + * (x_start, y_start) and upper right at (x_start + width, y_start + size), where width is given by get_text_width() + * and size is the specified size of the text to be plotted. Tip: Text plotted at position + * (x_start, y_start), rotated with a given 'angle', and of a given 'size' + * whose width is 'width', will fit approximately inside a rectangle whose corners are at + * 1 (x_start, y_start) + * 2 (x_start + width*cos(angle), y_start + width*sin(angle)) + * 3 (x_start + width*cos(angle) - size*sin(angle), y_start + width*sin(angle) + size*cos(angle)) + * 4 (x_start - size*sin(angle), y_start + size*cos(angle)) + * */ + + int get_text_width(char * face_path, int fontsize, char * text); + + int get_text_width_utf8(char * face_path, int fontsize, char * text); + + +}; + + +#endif + diff --git a/quick.sh b/quick.sh new file mode 100644 index 0000000..b0100af --- /dev/null +++ b/quick.sh @@ -0,0 +1,2 @@ +./bin/Debug/AwesomeAttractor -a attr/quick.attr 15000000 canvas/quick.canv 600 600 3 +./bin/Debug/AwesomeAttractor -c canvas/quick.canv 600 600 3 quick.png diff --git a/stfu/ExampleProgramOutput.stf b/stfu/ExampleProgramOutput.stf new file mode 100644 index 0000000..d28131a --- /dev/null +++ b/stfu/ExampleProgramOutput.stf @@ -0,0 +1,11 @@ +Screen options: { + Brightness: "1.5" + Contrast: "70.1" + heigth: "1200" + rendermode: "OpenGL" + width: "1920" +} +Sound options: { + max soundfx: "128" + outputmode: "5.1" +} diff --git a/stfu/LICENSE.txt b/stfu/LICENSE.txt new file mode 100644 index 0000000..835cfb3 --- /dev/null +++ b/stfu/LICENSE.txt @@ -0,0 +1,28 @@ +Copyright (c) 2009 Maurice Bos and Nick Overdijk +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The names of the authors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +- Maurice Bos (maurice@bosbyte.nl) +- Nick Overdijk (nick@dotsimplicity.net) + diff --git a/stfu/STFU.cbp b/stfu/STFU.cbp new file mode 100644 index 0000000..53de290 --- /dev/null +++ b/stfu/STFU.cbp @@ -0,0 +1,45 @@ + + + + + + diff --git a/stfu/STFU.depend b/stfu/STFU.depend new file mode 100644 index 0000000..a22e8b9 --- /dev/null +++ b/stfu/STFU.depend @@ -0,0 +1,21 @@ +# depslib dependency file v1.0 +1241021944 source:/media/Data/Code/C/AwesomeAttractor/stfu/main.cpp + + + "stf.hpp" + +1241022258 /media/Data/Code/C/AwesomeAttractor/stfu/stf.hpp + + + + + + +1241019586 source:/media/Data/Code/C/AwesomeAttractor/stfu/stf.cpp + + + + + + "stf.hpp" + diff --git a/stfu/STF_Format_Example.stf b/stfu/STF_Format_Example.stf new file mode 100644 index 0000000..68293a0 --- /dev/null +++ b/stfu/STF_Format_Example.stf @@ -0,0 +1,26 @@ +imthefirst: "The first value!" +name with spaces: "Names can contain spaces" +name +with +newlines: "And even newlines!" + +test: { + blah: "The value of [root].test.blah" + newlines: "newlines and all their whitespace + get stored" + comment: This comment value contains some information about how to comment in SSS files. + "You can place comment before the quotes, the parser will ignore it." + output this: "This particular value is retrieved in the code." + output child: + Same goes for children. The parser ignores anything until a curly bracket is found. { + info: "Awwwwwwright! Tree output oh yeah." + coolness: "+20" + } + Array: { :"This" :"is" :"like" :"an" :"array" } + + 2D array: + { + : { :"First" :"Row" } + : { :"Second" :"Row" :"!" } + } +} diff --git a/stfu/arrays.stf b/stfu/arrays.stf new file mode 100644 index 0000000..e8e9644 --- /dev/null +++ b/stfu/arrays.stf @@ -0,0 +1,6 @@ +array: { :"Some" :"Unnamed" :"Values" } +2D array: +{ + : { :"First" :"Row" } + : { :"Second" :"Row" :"!" } +} \ No newline at end of file diff --git a/stfu/main.cpp b/stfu/main.cpp new file mode 100644 index 0000000..1f0b0dc --- /dev/null +++ b/stfu/main.cpp @@ -0,0 +1,282 @@ +#include +#include +#include "stf.hpp" +using namespace std; +using stfu::node; + +/*! \author Nick Overdijk + * \version 1.2 + * \date 2009-04-27 + * + * STFU Example App + */ + +void prettyprint(const string &title) { + cout + << "\t\t-----" << title << "-----\t\t\n" + << endl; +} + +void read_examples(node &n) { + prettyprint("Reading Examples"); + cout + << "Reading a STF file in a node is trivial with the stream insertion operator, it returns whether it had success: true/false\n" + << endl; + + if ("test.stf" >> n) { + prettyprint("Outputting a node/tree"); + cout << n; + + cout + << "\n\nnode::getChild() returns a node&, so we can easily output nested nodes:\n" + << endl; + cout << n.getChild("child"); + + cout + << "\n\nnode::getValue() returns a string&, so we can easily output values too:\n" + << endl; + cout << n.getValue("imthefirst"); + + cout + << "\n\nThe constness of getChild and getValue depend on the object." + << endl; + } else { + cerr << "Couldn't read file!" << endl; + } +} + +void change_examples(node &n) { + cout + << "\t\t-----Change examples------\t\t\n" + << endl; + + if ("test.stf" >> n) { + prettyprint("Edit a value"); + cout << "Before:" << endl; + cout << n; + + n.value("imthefirst") = "O YEAH!"; + + cout << "After: " << endl; + cout << n; + + prettyprint("Edit a node"); + cout << "Before:" << endl; + cout << n; + + n.child("child") = n; + + cout << "After: " << endl; + cout << n; + + prettyprint("Edit the name of a value"); + cout << "Before:" << endl; + cout << n; + + n.renameValue("imthefirst", "roflolmao"); + + cout << "After: " << endl; + cout << n; + + prettyprint("Edit the name of a child"); + cout << "Before:" << endl; + cout << n; + + n.renameChild("child", "NEW NAMEZ"); + + cout << "After: " << endl; + cout << n; + } else { + cerr << "Couldn't read file!" << endl; + } +} + + +void create_examples(node &n) { + prettyprint("Creation examples"); + node screenoptions; + node soundoptions; + + if (!("screenoptions.stf" >> screenoptions && "soundoptions.stf" >> soundoptions)) { + cerr << "Couldn't read files!" << endl; + return; + } + + cout << "You can merge these two tree quite easily into one tree (aka \"Adding children\"):" << endl; + + cout << "Before (screenoptions):" << endl; + cout << screenoptions << endl; + cout << "Before (soundoptions):" << endl; + cout << soundoptions << endl; + + //child() creates the child when it doesn't exist + n.child("Screen options") = screenoptions; + //but you can do this too + n.addChild("Sound options", soundoptions); + + cout << "After (both are now children of options):" << endl; + cout << n; + + prettyprint("Adding a value"); + cout << "Before:" << endl; + cout << n; + + //this way, NOT sure child() won't create a child (if it doesn't exist, it'll create it) + //also not sure value() won't alter a value (if the value[index] exists, it'll change that) + n.child("Screen options").value("Contrast") = "70.1"; + //this way, 100% sure that getChild() won't create a child, and addValue() will always add a value + //get{Child|Value} throw an out_of_range when they can't find the {Child|Value}. + n.getChild("Screen options").addValue("Brightness") = "1.5"; + + cout << "After:" << endl; + cout << n; + + prettyprint("Writing to a file (aka \"Saving to a file\")"); + const char *filename = "ExampleProgramOutput.stf"; + if (!(filename << n)) { + cerr << "Couldn't write to "; + } else { + cout << "Node succesfully saved to "; + } + cout << filename << endl << endl; +} + +void remove_examples(node &n) { + prettyprint("Removal examples"); + if (!("test.stf" >> n)) { + cerr << "Couldn't open file!" << endl; + } + + prettyprint("Deleting a value"); + cout << "Before:" << endl; + cout << n; + + //remove{Child|Value} throws an out_of_range when the child/value isn't found + n.removeValue("imthefirst"); + + cout << "After:" << endl; + cout << n; + + + prettyprint("Deleting a child/node"); + cout << "Before:" << endl; + cout << n; + + n.removeChild("child"); + + cout << "After:" << endl; + cout << n; +} + +void array_examples(node &n) { + prettyprint("Array Examples"); + if (!("arrays.stf" >> n)) { + cerr << "Couldn't open arrays.stf!" << endl; + return; + } + + prettyprint("Iterating over an array: try/catch method"); + node &array = n.getChild("array"); + try { + size_t i = 0; + while (true) { + //use getValue(), it throws an out_of_range when there are no more elements + cout << array.getValue(i) << endl; + i++; + } + } catch (out_of_range &e) {/*we're done! ;-)*/} + + prettyprint("Iterating over an array: size() method"); + size_t max = array.values.size(); + for(size_t i = 0; i < max; i++){ + cout << array.getValue(i) << endl; + } + + prettyprint("Iterating over a 2D-array: size() method"); + node &multiarray = n.getChild("2D array"); + + //rows are children + size_t rows = multiarray.children.size(); + //each row can have a different amount of cols + size_t cols[rows]; + + for(size_t i = 0; i < rows; i++){ + //same as with size() method above + cols[i] = multiarray.getChild(i).values.size(); + //output to see it for yourself + cout << i << ',' << cols[i] << endl; + } + + //so, we're done. Code could've been written in 100 ways, but this does it step by step to be clear. + for(size_t row = 0; row < rows; row++){ + for(size_t col = 0; col < cols[row]; col++){ + cout << multiarray.getChild(row).getValue(col) << ", "; + } + cout << "\b\b " << endl; + } + + /*Arrays are simply unnamed nodes/values. Nothing more, nothing less. + You can change them like nodes/values, add them the same way, etc. + Most of the functions are overloaded so that you don't have to specify a name + These functions call their overloaded brethren with name = "" */ + + prettyprint("Creating an array"); + + cout << "Before:\n\n"; + cout << n << endl; + + node &newarray = n.addChild("my new array"); + stringstream buf; + for(size_t i = 0; i < 10; i++){ + buf << i; + newarray.addValue() = buf.str(); + } + + cout << "After:\n\n"; + cout << n << endl; +} + +int main() { + prettyprint("Welcome to the stf example app"); + + ifstream license("LICENSE.txt"); + if (license.good()) { + string line; + while (getline(license, line)) { + cout << line << endl; + } + license.close(); + } else { + cerr << "Couldn't open/read LICENSE.txt" << endl; + exit(-1); + } + + cout << endl; + + cout + << "This application shows how easy it is to use STFU (Simple Tree File Utility).\n" + << "STF is a \"new\" way to store leveled data, just as XML can do, for example.\n" + << "The format looks like a C(++) struct, which we think is easier to read than XML.\n" + << "Each {...} is a node, a node has other nodes and values. The file itself is the\n" + << "(unnamed) root node.\n" + << endl; + + //create an empty node + node n; + read_examples(n); + n.clear(); + + change_examples(n); + n.clear(); + + create_examples(n); + n.clear(); + + remove_examples(n); + n.clear(); + + array_examples(n); + n.clear(); + + return 0; +} diff --git a/stfu/screenoptions.stf b/stfu/screenoptions.stf new file mode 100644 index 0000000..e4e9546 --- /dev/null +++ b/stfu/screenoptions.stf @@ -0,0 +1,4 @@ + +width: "1920" +heigth: "1200" +rendermode: (OpenGL or DirectX) "OpenGL" diff --git a/stfu/simple documentation.html b/stfu/simple documentation.html new file mode 100644 index 0000000..84ad6e7 --- /dev/null +++ b/stfu/simple documentation.html @@ -0,0 +1,107 @@ +

STF - Simple Tree Format

+

+STF is a new file format to store a tree of values, just like XML and JSON can do. It has support for only 2 types: values and nodes. A node can have values and other nodes, allowing the creation of a tree format. A STF file itself is a node (the virtual root node). Nodes are a collection of other values and (child)nodes. +

+ +

Usage

+

+Nodes and values are named, the smallest name being "". We refer to the nodes with "" as name as unnamed nodes/values, but the same rules apply for the syntax; examples will follow, don't worry. Each name is followed by a ':' which signifies the end of the name (the name-terminator). The ':' is followed by either a '{' or a '"', which signifies the type of the name (the type-specifier): '{' are lists, '"' are values. The parser will extract and ignore anything in between the name-terminator and the type-specifier. +

+ +

Examples

+

Starting easy

+

+Here's an example of a very small STF file. It shows values and nodes and their syntax, without fancy stuff. +

+name: "First value in the file"
+childnode: {
+    value: "This is another value, inside a child node with the name "childnode"
+}
+
+

+ +

Comments

+

+Don't want to have a very long descriptive name, but do want a bit of extra info? Comments (or anything really) can be place in between the name-terminator and the type-specifier. +

+name: This text will be ignore by the parser "First value in the file."
+childnode: This text will too, be ignored {
+    value: You probably already guessed it
+
+    But this text will to be ignored. Anything here is allowed, except for a type-specifier of course! ;-)
+
+    "This is another value, inside a child node with the name ""childnode""."
+}
+
+

Getting more interesting

+

+This example shows what characters are allowed inside names and values. +

+firstnode:  {
+    subnode: {
+        blah: "xyz"
+        blah: "names of values/childs don't have to be unique!"
+    }
+}
+secondnode: {
+   name with space: "names can contain spaces"
+name
+with
+newlines: "names can contain newlines"
+   blah: "values can contain
+newlines too!"
+}
+
+

+

+NOTE: Take a look at the other .stf files included with this project for more examples. +

+ +

STFU - Simple Tree Format Utility

+

+STFU is the first C++ implementation for STF. +

+

+This example will show you some of the basic features of STFU: +

+#include 
+#include "stf.hpp"
+using namespace std;
+using stfu::node;
+
+int main(){
+   node n,f;
+   if (n.read("blah.stf")){  //could also do:  'if ("blah.stf" >> n){'
+      cout << n.value("test");  // Read a value
+	  
+	  cout << n.child("subnode").value("hi",0); //Read the first value
+	  cout << n.child("subnode").value("hi",1); //Read the second value
+	  
+	  n.child("subnode").value("test") = "blah"; //Add a value
+	  
+	  if (f.read("another_file.stf")){
+	     n.child("secondnode") = f;   //add a child
+	  }
+	  
+	  n.write("blah.stf"); //Write changes back to the file
+	  // "blah.stf" << n; would also be valid.
+	  
+	  cout << n; //output the file to the console instead of a file.
+   
+   } else {
+      cout << "ERROR!" << endl;
+   }
+}
+//NOTE: For more detailed examples, please take a look at the 'main.cpp' file of this project.
+
+

+

+As you see, the .child() and .value() functions can be used to read, create and change values/childs. More advanced functions are available, 'main.cpp' contains examples to explain all of them. +

+ + + + + + + diff --git a/stfu/soundoptions.stf b/stfu/soundoptions.stf new file mode 100644 index 0000000..bfa4ba2 --- /dev/null +++ b/stfu/soundoptions.stf @@ -0,0 +1,9 @@ + +max soundfx: + A value between 0 and 255 + "128" + +outputmode: + Options are: 2.0, 4.0, 4.1, 5.1, 7.1 and 412.28 + "5.1" + diff --git a/stfu/stf.cpp b/stfu/stf.cpp new file mode 100644 index 0000000..4d1febd --- /dev/null +++ b/stfu/stf.cpp @@ -0,0 +1,276 @@ +/* +Copyright (c) 2009 Maurice Bos and Nick Overdijk +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The names of the authors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +- Maurice Bos (maurice@bosbyte.nl) +- Nick Overdijk (nick@dotsimplicity.net) + +*/ + +#include +#include +#include +#include +#include + +#include "stf.hpp" + +using namespace std; +using stfu::node; +using stfu::child; +using stfu::value; + +ostream &stfu::operator<< (ostream &out, const node &root) { + root.write(out); + return out; +} + +bool stfu:: operator<< (const char *filename, const node &root){ + return root.write(filename); +} + +bool stfu::operator<< (ofstream &out, const node &root) { + return root.write(out); +} + +bool stfu::operator>> (istream &in, node &root) { + return root.read(in); +} + +bool stfu::operator>> (const char *filename, node &root) { + return root.read(filename); +} + +const string &node::getValue(const string &name, size_t index) const throw (out_of_range) { + return get_indexed(values, name, index); +} + +/*Function is const, but shouldn't be called on const objects since it returns a nonconst-reference to a member*/ +string &node::getValue(const string &name, size_t index) throw (out_of_range) { + return get_indexed(values, name, index); +} + +string &node::addValue(const string &name) { + return values.insert(pair(name, string()))->second; +} + +string &node::value(const string &name, size_t index) { + try { + return getValue(name, index); + } catch (out_of_range &e) { + //it doesn't exist: create it + return addValue(name); + } +} + +void node::removeValue(const string &name, size_t index) throw (out_of_range) { + values.erase(get_indexed_it(values, name, index)); + return; +} + +void node::renameValue(const string &oldName, const string &newName, size_t index) { + addValue(newName) = value(oldName, index); + removeValue(oldName, index); +} + + +const node &node::getChild(const string &name, size_t index) const throw (out_of_range) { + return get_indexed(children, name, index); +} + +node &node::getChild(const string &name, size_t index) throw (out_of_range) { + return get_indexed(children, name, index); +} + +node &node::addChild(const string &name) { + return children.insert(pair(name, node()))->second; +} + +node &node::addChild(const string &name, node &newNode) { + return children.insert(pair(name, newNode))->second; +} + +node &node::child(const string &name, size_t index) { + //if there's no such child, add one + try { + return getChild(name, index); + } catch (out_of_range &e) { + //it doesn't exist: create it + return addChild(name); + } +} + +void node::renameChild(const string &oldName, const string &newName, size_t index) { + node copy = child(oldName, index); + removeChild(oldName, index); + addChild(newName) = copy; +} + +void node::removeChild(const string &name, size_t index) throw (out_of_range) { + children.erase(get_indexed_it(children, name, index)); + return; +} + +bool node::read(const char *filename) { + ifstream f(filename); + + if (!f.good()) return false; + + bool success = read(f); + f.close(); + + return success; +} + +bool node::read(istream &in) { + while (1) { + in >> ws; //Skip whitespace + + //if end of node is reached, return + if (in.peek() == '}') { + in.ignore(); + return true; + } + + if (in.eof()) { + return true; //End of the file is reached + } + + string name; + char type; // '{' or '"' + streamRead(in, name, ':'); //Read name (all chars before ':') + type = streamSkip(in,"\"{"); //Skip everything until '{' or '"' + + switch (type) { + + //in case of value + case '"': { + string value; + while (1) { + if (streamRead(in, value, '"') == 0) { //Read to the closing-" + return false; + } + if (in.peek() == '"') { + in.ignore(); + value += '"'; + continue; + } else { + break; + } + } + this->values.insert(pair(name, value)); + break; + } + + //in case of child + case '{': { + node sub; + if (!sub.read(in)) { //Recursively read the subnode + return false; + } + this->children.insert(pair(name,sub)); + break; + } + + default: + return false; + } + } +} + +/*Writes to a file using it's overloaded self*/ +bool node::write(const char *filename) const { + ofstream f(filename); + + if (!f.good()) { + return false; + } + + bool success = write(f); + f.close(); + + return success; +} + +// TODO (Nick#1#): Make write() not put unnamed values on a new line, children are okay. +bool node::write(ostream &out, size_t depth, string indent) const { + string indentation; + for (size_t i = 0; i < depth; i++) { + indentation += indent; + } + + for (multimap::const_iterator value_it = values.begin(); value_it != values.end(); value_it++) { + //Escape all the '"' by adding a second '"' + string value(value_it->second); + size_t found = value.find('"'); + + //while there are more ", insert second "s + while (found != value.npos) { + value.insert(found, 1, '"'); + found = value.find('"', found+2); + } + out << indentation << value_it->first << ": \"" << value << '"' << endl; + } + + for (multimap::const_iterator child_it = children.begin(); child_it != children.end(); child_it++) { + out << indentation << child_it->first << ": {" << endl; + child_it->second.write(out, depth+1); + out << indentation << '}' << endl; + } + + return true; +} + +char node::streamSkip(istream &in, const string &delimiters) { + char cur; + + //Return if the current char is part of delimiters[] + while (in >> noskipws >> cur) { + if (delimiters.find_first_of(cur) != delimiters.npos) return cur; + } + return 0; +} + +char node::streamRead(istream &in, string &out, const string &delimiters) { + char cur; + + //Return if the current char is part of delimiters[] + while (in >> noskipws >> cur) { + if (delimiters.find(cur) != delimiters.npos) return cur; + out += cur; + } + return 0; +} + +char node::streamRead(istream &in, string &out, const char delimiter) { + char cur; + + //Return if the current char is delimiter + while (in >> noskipws >> cur) { + if (delimiter == cur) return cur; + out += cur; + } + return 0; +} diff --git a/stfu/stf.hpp b/stfu/stf.hpp new file mode 100644 index 0000000..8a25538 --- /dev/null +++ b/stfu/stf.hpp @@ -0,0 +1,358 @@ +/* +Copyright (c) 2009 Maurice Bos and Nick Overdijk +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The names of the authors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +- Maurice Bos (maurice@bosbyte.nl) +- Nick Overdijk (nick@dotsimplicity.net) + +*/ + +#include +#include +#include +#include +#include + +#ifndef STFU_HPP +#define STFU_HPP + +namespace stfu { + using namespace std; + const static string not_found("search->No such child/value"); + + /*! \class node + * \brief A node in an STF tree. + * \author Maurice Bos + * \author Nick Overdijk + * \version 1.0 + * \date 2009-04-25 + * + * When you read an STF file through a node (with read() for example), this node will be the root node, without a name. See the examples for more information. + */ + class node { + + /** Overloaded ostream's operator<< */ + friend ostream &operator<< (ostream &out, const node &root); + + /** Returns whether it was succesful or not*/ + friend bool operator<< (ofstream &out, const node &root); + + /** Acts like write(), but looks like this: "filename" << node */ + friend bool operator<< (const char *filename, const node &root); + + /** Overloaded istream's operator>> */ + friend bool operator>> (istream &in, node &root); + + /** Acts like read(), but looks like this: "filename" >> node */ + friend bool operator>> (const char *filename, node &root); + + public: + //@{ + /** The values and children belonging to this node. To add, remove, do whatever: you CAN use these variables directly. To use indexing, use the member functions below*/ + multimap values; + multimap children; + //@} + + /** + Clears the whole node recursively. + */ + void clear() { + values.clear(); + children.clear(); + } + /** + Gets the string value from a variable + \param name The name of the value you wish to retrieve + \param index If there are more values with the same name, they are indexed. Here you can supply its indexnumber. + \return The retrieved value + */ + string &value(const string &name, size_t index = 0); + + /** + Same as value(), but for unnamed values + \note same as value("", index) + \param index If there are more unnamed values, they are indexed. Here you can supply its indexnumber. \return Same as value + */ + string &value(size_t index){ + return value("", index); + } + + /** + Creates and adds a value. + \param name The name of the value to be created + \return A reference to the value of the created value. + */ + string &addValue(const string &name = ""); + + /** + Const function for const objects to get a value. It's NOT possible to call value() on constant objects for value() isn't const. + \param name Name of the value to be retrieved + \param index If there are > 1 values with the same name, they are indexed. Here you can supply an indexnumber. + \return Returns a const string& to the value of value with the name and index specified + */ + const string &getValue(const string &name, size_t index) const throw (out_of_range); + + /** + Same as getValue() const, but for unnamed values + \note same as getValue("", index) const + \param index If there are > 1 unnamed values, they are indexed. Here you can supply an indexnumber. + \return Returns a const string& to the value of value with the name and index specified + */ + const string &getValue(size_t index) const throw (out_of_range){ + return getValue("", index); + } + + /** + Same as getValue() const, but for non-const objects. The returned string& can safely be modified. + \param name Name of the value to be retrieved + \param index If there are > 1 values with the same name, they are indexed. Here you can supply an indexnumber. + \return Returns a string& to the value of value with the name and index specified + */ + string &getValue(const string &name, size_t index = 0) throw (out_of_range); + + /** + Same as getValue(), but for unnamed values + \note same as getValue("", index) + \param index If there are > 1 unnamed values, they are indexed. Here you can supply an indexnumber. + \return Returns a string& to the value of value with the name and index specified + */ + string &getValue(size_t index) throw (out_of_range){ + return getValue("", index); + } + + /** + Removes a value. Surprise huh? + \param name Name of the value to be removed + \param index If there are > 1 values with the same name, they are indexed. Here you can supply an indexnumber. + */ + void removeValue(const string &name, size_t index = 0) throw (out_of_range); + + /** + Removes an unnamed value. + \note same as removeValue("", index); + \param index If there are > 1 unnamed values, they are indexed. Here you can supply an indexnumber. + */ + void removeValue(size_t index) throw (out_of_range){ + removeValue("", index); + } + + /** + Renames a value. + \param oldName Name of the value to be renamed + \param newName The name that the oldName-value should have + \param index If there are > 1 values with the same name, they are indexed. Here you can supply an indexnumber. + */ + void renameValue(const string &oldName, const string &newName, size_t index = 0); + + /** + Changes, adds or retrieves node + \param name The name of the child you wish to retrieve, change or add. + \param index If there are > 1 children with the same name, they are indexed. Here you can supply an indexnumber. + \return The retrieved node + + If this index number is > the number of variables with that name, a new variable with that name is created with index = current number of same name variables + 1. + So say you have 4 variables named "tree", you call child("tree", 10), another tree gets made with index 5, NOT 10. + */ + node &child(const string &name, size_t index = 0); + + /** + Same as child(), but for unnamed children. + \note same as child("", index) + \param index If there are > 1 unnamed children, they are indexed. Here you can supply an indexnumber. + \return The retrieved node + */ + node &child(size_t index = 0){ + return child("", index); + } + + /** + Guarranteed to add a child + \param name Name for the child + \return A reference to the created node + */ + node &addChild(const string &name = ""); + + /** + As addChild(name), but copies data from newChild as well. + \param name Name for the child + \param newChild Data to copy from the child. + \return A reference to the created node + */ + node &addChild(const string &name, node &newChild); + + /** + As addChild(name, newChild), but without name, for unnamed children + \note same as addChild("", newChild); + \param newChild Data to copy from the child. + \return A reference to the created node + */ + node &addChild(node &newChild){ + return addChild("", newChild); + } + + /** + Const function for const objects to get a node. It's NOT possible to call child() for this, for child() isn't const. + \param name Name of the child to be retrieved + \param index If there are > 1 children with the same name, they are indexed. Here you can supply an indexnumber. + \return Returns a const node& to the node with the name and index specified + */ + const node &getChild(const string &name, size_t index = 0) const throw (out_of_range); + + /** + Const function for const objects to get an unnamed const node&. + \note same as getChild("", index) const; + \param index If there are > 1 unnamed children, they are indexed. Here you can supply an indexnumber. + \return Returns a const node& to the node with the name and index specified + */ + const node &getChild(size_t index = 0) const throw (out_of_range){ + return getChild("", index); + } + + /** + Same as getChild() const, but for non-const objects. The returned child & can be modified + \param name Name of the child to be retrieved + \param index If there are > 1 children with the same name, they are indexed. Here you can supply an indexnumber. + \return Returns a node& to the node with the name and index specified + */ + node &getChild(const string &name, size_t index = 0) throw (out_of_range); + + /** + Same as getChild() const, but for non-const objects. + \note same as getChild("", index); + \param index If there are > 1 unnamed children, they are indexed. Here you can supply an indexnumber. + \return Returns a node& to the node with the name and index specified + */ + node &getChild(size_t index = 0) throw (out_of_range){ + return getChild("", index); + } + + /** + Removes a child. Surprise huh? + \param name Name of the child to be removed + \param index If there are > 1 children with the same name, they are indexed. Here you can supply an indexnumber. + */ + void removeChild(const string &name, size_t index = 0) throw (out_of_range); + + /** + As removeChild() for unnamed children. + \param index If there are > 1 unnamed children, they are indexed. Here you can supply an indexnumber. + */ + void removeChild(size_t index = 0) throw (out_of_range){ + removeChild("", index); + } + + /** + Renames a child. + \param oldName Name of the child to be renamed + \param newName The name that the oldName-child should have + \param index If there are > 1 children with the same name, they are indexed. Here you can supply an indexnumber. + */ + void renameChild(const string &oldName, const string &newName, size_t index = 0); + + + /** + Reads the STF from an istream + + \return Returns whether it was succesful + */ + bool read(istream &in); + + /** + Reads the STF from a file + + \return Returns whether it was succesful + */ + bool read(const char *filename); + + /** + Writes the STF to an ostream with optional indentation + \param out ostream to write to + \param depth how much indentation to start with + \param indent What string to use as indenation + \return Returns whether it was succesful + */ + bool write(ostream &out, size_t depth = 0, string indent = "\t") const; + + /** + Writes to a file. Simply first opens a file and passes that ostream to itself. + \param filename File to write to + \return Returns whether it was succesful + */ + bool write(const char *filename) const; + + private: + char streamRead(istream &in,string &out, const string &delim); + char streamRead(istream &in, string &out, const char delim); + char streamSkip(istream &in, const string &delim); + + /* + returns a T2&, not a const T2&. The functions getValue/getChild make sure this will be done correctly for const objects + this function can NOT use get_indexed_it, because that function can't be const: + const_iterators can not be transformed into iterators, and the iterator is needed for erase(), which wants an iterator. + */ + template + T2& get_indexed(const multimap &container, const T1 &key, size_t index = 0) const throw (out_of_range) { + size_t count = container.count(key); + + if (count != 0 && count-1 >= index) { + typename multimap::const_iterator it = container.find(key); + while (index--) it++; + return const_cast(it->second); + } else { + throw out_of_range((string)"get_indexed->"+"Element " + key + "doesn't exist!"); + } + } + +// template +// typename multimap::iterator get_indexed_it(multimap &container, const T1 &key, size_t index = 0) throw (out_of_range) { +// size_t count = container.count(key); +// +// if (count != 0 && count-1 >= index) { +// typename multimap::iterator it = container.find(key); +// while (index--) it++; +// return it; +// } else { +// throw out_of_range((string)"get_indexed_it->"+"Element " + key + "doesn't exist!"); +// } +// } + + template + typename Container::iterator get_indexed_it(Container& container, const T& key, size_t index = 0) + throw (out_of_range) { + typename Container::iterator it = container.find(key); + while (index-- && it != container.end() && it->first==key) it++; + if (it == container.end()) throw out_of_range("get_indexed_it(Container&, const T&, size_t)"); + return it; + } + }; + + typedef pair value; + typedef pair child; + + typedef multimap::iterator valueiterator; + typedef multimap::iterator childiterator; +} + +#endif // STFU_HPP diff --git a/stfu/test.stf b/stfu/test.stf new file mode 100644 index 0000000..be26df5 --- /dev/null +++ b/stfu/test.stf @@ -0,0 +1,4 @@ +imthefirst: "The first value inside root!" +child: { + blah: "The value of [root].child.blah" +} \ No newline at end of file