From 8c57b315af118a6a159da57b6bf85d5194eb44ab Mon Sep 17 00:00:00 2001 From: Joshua Date: Sat, 27 Mar 2010 21:46:59 +0100 Subject: [PATCH] initial state, going to make AwesomeAttractor more OO --- Attractor.cpp | 345 +++ Attractor.hpp | 61 + AttractorKernel.hpp | 33 + AwesomeAttractor.cbp | 64 + AwesomeAttractor.layout | 52 + AwesomeAttractor.layout.save | 40 + Canvas.cpp | 238 ++ Canvas.hpp | 50 + Parameters.cpp | 110 + Parameters.hpp | 34 + Projector.cpp | 111 + Projector.hpp | 45 + Vector.cpp | 126 + Vector.hpp | 31 + batchexport.sh | 14 + batchrender.sh | 17 + kernels/Lorenz.cpp | 73 + kernels/Lorenz.hpp | 48 + kernels/Unravel.cpp | 71 + kernels/Unravel.hpp | 49 + main.cpp | 133 + myMath.hpp | 12 + pngwriter/.DS_Store | Bin 0 -> 6148 bytes pngwriter/._.DS_Store | Bin 0 -> 82 bytes pngwriter/._Makefile | Bin 0 -> 171 bytes pngwriter/._pngwriter.cc | Bin 0 -> 171 bytes pngwriter/._pngwriter.h | Bin 0 -> 167 bytes pngwriter/Makefile | 32 + pngwriter/pngwriter.cc | 4721 ++++++++++++++++++++++++++++++++ pngwriter/pngwriter.h | 747 +++++ quick.sh | 2 + stfu/ExampleProgramOutput.stf | 11 + stfu/LICENSE.txt | 28 + stfu/STFU.cbp | 45 + stfu/STFU.depend | 21 + stfu/STF_Format_Example.stf | 26 + stfu/arrays.stf | 6 + stfu/main.cpp | 282 ++ stfu/screenoptions.stf | 4 + stfu/simple documentation.html | 107 + stfu/soundoptions.stf | 9 + stfu/stf.cpp | 276 ++ stfu/stf.hpp | 358 +++ stfu/test.stf | 4 + 44 files changed, 8436 insertions(+) create mode 100644 Attractor.cpp create mode 100644 Attractor.hpp create mode 100644 AttractorKernel.hpp create mode 100644 AwesomeAttractor.cbp create mode 100644 AwesomeAttractor.layout create mode 100644 AwesomeAttractor.layout.save create mode 100644 Canvas.cpp create mode 100644 Canvas.hpp create mode 100644 Parameters.cpp create mode 100644 Parameters.hpp create mode 100644 Projector.cpp create mode 100644 Projector.hpp create mode 100644 Vector.cpp create mode 100644 Vector.hpp create mode 100644 batchexport.sh create mode 100644 batchrender.sh create mode 100644 kernels/Lorenz.cpp create mode 100644 kernels/Lorenz.hpp create mode 100644 kernels/Unravel.cpp create mode 100644 kernels/Unravel.hpp create mode 100644 main.cpp create mode 100644 myMath.hpp create mode 100644 pngwriter/.DS_Store create mode 100644 pngwriter/._.DS_Store create mode 100644 pngwriter/._Makefile create mode 100644 pngwriter/._pngwriter.cc create mode 100644 pngwriter/._pngwriter.h create mode 100644 pngwriter/Makefile create mode 100644 pngwriter/pngwriter.cc create mode 100644 pngwriter/pngwriter.h create mode 100644 quick.sh create mode 100644 stfu/ExampleProgramOutput.stf create mode 100644 stfu/LICENSE.txt create mode 100644 stfu/STFU.cbp create mode 100644 stfu/STFU.depend create mode 100644 stfu/STF_Format_Example.stf create mode 100644 stfu/arrays.stf create mode 100644 stfu/main.cpp create mode 100644 stfu/screenoptions.stf create mode 100644 stfu/simple documentation.html create mode 100644 stfu/soundoptions.stf create mode 100644 stfu/stf.cpp create mode 100644 stfu/stf.hpp create mode 100644 stfu/test.stf 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 0000000000000000000000000000000000000000..f483b6294eabeb47327f31dc2a103b8f6d4aa3c9 GIT binary patch literal 6148 zcmeHKyH3ME5S)b+mO_z|^1c9xKR899q~ZqvCp?6epa7zE$8R(H2r{y1DPULH9eX`{ zr<>x|3qY1{>vLcMprI?`L&MngyZXqEB6Ca>TjLIo=&)J8wB3k3ozU6_m$<w$JAb1=-2dvk!y4YYsExs#eC6Ld^+G2edhNX*gIM|^HxsG9|6@REfx3;1wH}P CP$ZoI literal 0 HcmV?d00001 diff --git a/pngwriter/._.DS_Store b/pngwriter/._.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..460d887a2e21de5e7ccf222b0fdc4fbe43f67e75 GIT binary patch literal 82 ucmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}u>uf-_(4Dn2M%x+Dgyuy>I7i` literal 0 HcmV?d00001 diff --git a/pngwriter/._Makefile b/pngwriter/._Makefile new file mode 100644 index 0000000000000000000000000000000000000000..033041d9b4c51356a7b757e45d43b560e2b30285 GIT binary patch literal 171 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aU~Fg_^W}KA=uX^CD=FA2gJq>9795a z7{c@vfU+PRbAXs1h>-y!kOLJLPtMQPODrhJN!1HUttfHLOU_Tp%u5F<2n})5wXilc MHZd|Zx3n+;06KCPI{*Lx literal 0 HcmV?d00001 diff --git a/pngwriter/._pngwriter.cc b/pngwriter/._pngwriter.cc new file mode 100644 index 0000000000000000000000000000000000000000..0d44057bf8e1f366b0afef265e8c0824bce1b7d1 GIT binary patch literal 171 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aU~Fg_^W}KA=uX^CD=FA2gJq>9795a z7{c__fU+PRbAXs1O(i2jTs%2HS1+-kASYEXB($Vqox1Ojhs@R)|o50+1L3ClDI}aVZdk_{)KqA=uX^CD=FA2gJq>9795a z7{c^4fwCYSbAXr|O(i2jTs%2HS1+-kASYEXB(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 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