Initial commit.

This commit is contained in:
1Hyena 2016-09-09 16:06:08 +03:00
parent 7774c0ce2e
commit ad627b132f
207 changed files with 15756 additions and 1 deletions

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2016 Hyena
Copyright (c) 2016 Erich Erstu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

39
Makefile Normal file
View File

@ -0,0 +1,39 @@
NAME = atomorph
CC = gcc
PROF = -O2
C_FLAGS = -std=c++11 -Wall -pedantic $(PROF)
OBJ_DIR = obj
SRC_FILES := $(wildcard *.cpp)
O_FILES := $(patsubst %.cpp,$(OBJ_DIR)/%.o,$(SRC_FILES))
OUT = ./lib$(NAME).a
all:
@printf "\033[0mHINT: On errors, try \033[1;33m-std=gnu++11 -stdlib=libc++\033[0m compiler flags.\033[0m\n"
@printf "\033[0mHINT: Use \033[1;33mmake opencv\033[0m for OpenCV optimizations (experimental).\033[0m\n"
@printf "\033[0mHINT: Use \033[1;33mmake deprecated\033[0m to compile the old version.\033[0m\n"
@$(MAKE) $(OUT) -s
opencv: DEFINES = -D ATOMORPH_OPENCV
opencv: $(O_FILES)
@ar rcs $(OUT) $(O_FILES)
@printf "\033[1;32mOpenCV dependent lib$(NAME).a DONE!\033[0m\n"
deprecated: DEFINES = -D ATOMORPH_DEPRECATED
deprecated: $(O_FILES)
@ar rcs $(OUT) $(O_FILES)
@printf "\033[1;32mDeprecated lib$(NAME).a DONE!\033[0m\n"
$(OUT): $(O_FILES)
@ar rcs $(OUT) $(O_FILES)
@printf "\033[1;32mlib$(NAME).a DONE!\033[0m\n"
$(OBJ_DIR)/%.o: %.cpp
@printf "\033[1m\033[31mCompiling \033[37m....\033[34m %-20s\t\033[33m%6s\033[31m lines\033[0m \n" $*.cpp "`wc -l $*.cpp | cut -f1 -d' '`"
@$(CC) $(INCLUDE) $< $(DEFINES) $(C_FLAGS) -c -o $@
clean:
@printf "\033[1;36mCleaning \033[37m ...."
@rm -f $(O_FILES) $(OUT) *~ *.bak *.orig *.rej
@printf "\033[1;37m lib$(NAME).a cleaned!\033[0m\n"

View File

@ -1,2 +1,19 @@
# atomorph
Image morphing library that uses fluid dynamics and optimal transport to produce intuitive morphs.
http://atomorph.org
AtoMorph Library uses the C++11 standard. The included Makefiles
have been tested on Linux operating systems. iOS users may encounter
some problems due to poor support of the C++11 standard on their
system. One should try modifying the Makefile by adding -std=gnu++11
and -stdlib=libc++ compiler flags.
Build instructions:
Run make in this folder.
Example usage:
See README in demo folder.
If you like this software, please consider making a donation
by sending Bitcoins to 1Erich1YUdkUAp9ynf4Rfw2ug8nBtuUmMu.

1161
atomorph.cpp Normal file

File diff suppressed because it is too large Load Diff

389
atomorph.h Normal file
View File

@ -0,0 +1,389 @@
/*
* AtoMorph - Simple Library for Morphing 2D Particle Clouds
* See Copyright Notice at the end of this file.
*/
#ifndef _ATOMORPH_H_
#define _ATOMORPH_H_
#include <stdlib.h>
#include <algorithm>
#include <vector>
#include <future>
#include <random>
#include <map>
#include <set>
#include <limits>
#include <assert.h>
#ifdef ATOMORPH_OPENCV
#include <opencv/cv.h>
#endif
#include "spline.h"
#include "perlin.h"
#include "fluidmodel.h"
#include "color.h"
#ifdef ATOMORPH_DEPRECATED
#define AM_NONE 0
#define AM_LINEAR 1
#define AM_COSINE 2
#define AM_PERLIN 3
#define AM_SPLINE 4
#define AM_WARN_POINTER_SIZE 1 // Set when sizeof(void *) is less than 64 bits.
#define AM_WARN_ATOM_SIZE 2 // Set when sizeof(AM_ATOM) is more than 64 bits.
typedef struct AM_COLOR {
unsigned char r;
unsigned char g;
unsigned char b;
unsigned char a;
} AM_COLOR;
typedef struct AM_ATOM {
uint16_t x;
uint16_t y;
unsigned char r;
unsigned char g;
unsigned char b;
unsigned char a;
} AM_ATOM;
class AM_SCENE {
public:
AM_SCENE();
~AM_SCENE();
void clear();
bool init(size_t atoms, size_t frames);
bool push_atom(size_t frame, AM_ATOM atom);
void renew_splines();
double get_certainty(double t) const;
void get_xy (size_t atom, double t, double *x, double *y, unsigned method) const;
AM_COLOR get_rgba(size_t atom, double t, double lag, double slope, unsigned interpolation) const;
double get_current_path_length(size_t atom, double t) const;
AM_ATOM get_atom(size_t atom, size_t frame) const;
size_t atom_count() const {return atoms;}
size_t frame_count() const {return frames;}
size_t get_sorted_atom_at(size_t position);
size_t get_current_frame(double t) const;
bool copy_map_from(const AM_SCENE *scene);
bool copy_candidates_from(const AM_SCENE *scene);
void shuffle();
void sort_atoms();
double get_cost() const;
double get_path_length(size_t atom) const;
double get_path_color(size_t atom) const;
bool elect_atoms();
bool swap_atoms(size_t frame, size_t atom1, size_t atom2);
const std::vector<AM_ATOM> *get_candidates(size_t frame) const;
private:
size_t atoms;
size_t frames;
std::vector<size_t> sorted_atoms;
std::vector<AM_ATOM> *candidates;
glnemo::CRSpline *splines;
AM_ATOM **map;
};
class AM_THREAD {
public:
AM_THREAD();
~AM_THREAD();
bool clear();
bool init(const AM_SCENE *scene);
void set_seed(unsigned seed);
void set_step_size(int step_size);
void set_gradient_importance(double weight);
void set_magic_exponent(double exponent);
bool is_running() const {return running;}
bool is_paused() const {return paused;}
void stop() { signal_stop = true; while(running) std::this_thread::sleep_for(std::chrono::milliseconds(0)); step_thread.join();}
void start() { running = true; step_thread = std::thread(&AM_THREAD::run, this);}
void pause() { signal_pause = true; while(!paused) std::this_thread::sleep_for(std::chrono::milliseconds(0)); signal_pause = false;}
void resume() { paused = false;}
double get_cost() const {return cost;}
bool fetch_scene(AM_SCENE *target) const;
private:
void run();
void step();
double chain_length(AM_ATOM a1, AM_ATOM a2, AM_ATOM a3);
double chain_gradient(AM_ATOM a1, AM_ATOM a2, AM_ATOM a3);
AM_SCENE scene;
double *subcost;
double cost;
std::default_random_engine e1;
int step_size;
double magic_exponent;
double gradient_importance;
std::atomic<bool> signal_stop;
std::atomic<bool> running;
std::atomic<bool> signal_pause;
std::atomic<bool> paused;
std::thread step_thread;
};
class AM_IMAGE {
public:
AM_IMAGE();
~AM_IMAGE();
bool set_scene(const AM_SCENE *scene);
bool set_resolution(size_t width, size_t height);
bool set_time(double time);
bool set_seed(unsigned seed);
bool set_color_interpolation(unsigned method);
bool set_path_interpolation(unsigned method);
bool is_running() const {return running;}
bool is_paused() const {return paused;}
size_t pixel_count() const {return atoms.size();}
bool get_xy (size_t pixel, int *x, int *y) const;
bool get_rgba(size_t pixel, unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a) const;
size_t get_pixel_count() const {return atoms.size();}
bool fetch_pixels(std::vector<AM_ATOM> *to) const;
void stop() { signal_stop = true; while(running) std::this_thread::sleep_for(std::chrono::milliseconds(0)); step_thread.join();}
void start() { running = true; step_thread = std::thread(&AM_IMAGE::run, this);}
void pause() { signal_pause = true; while(!paused) std::this_thread::sleep_for(std::chrono::milliseconds(0)); signal_pause = false;}
void resume() { paused = false;}
private:
void run();
void render();
std::vector<AM_ATOM> atoms;
size_t w;
size_t h;
double t;
AM_SCENE scene;
bool done;
unsigned seed;
unsigned color_interpolation;
unsigned path_interpolation;
PerlinNoise lag_map;
PerlinNoise slope_map;
std::atomic<bool> signal_stop;
std::atomic<bool> running;
std::atomic<bool> signal_pause;
std::atomic<bool> paused;
std::thread step_thread;
};
class AM_BLENDER {
public:
AM_BLENDER();
~AM_BLENDER();
bool set_resolution(size_t width, size_t height);
bool set_median_combining(bool value);
bool clear();
bool add_image(const AM_IMAGE *img);
bool get_xy (size_t pixel, int *x, int *y) const;
bool get_rgba(size_t pixel, unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a) const;
size_t pixel_count() const {return atoms.size();}
bool is_running() const {return running;}
bool is_paused() const {return paused;}
void stop() { signal_stop = true; while(running) std::this_thread::sleep_for(std::chrono::milliseconds(0)); step_thread.join();}
void start() { running = true; step_thread = std::thread(&AM_BLENDER::run, this);}
void pause() { signal_pause = true; while(!paused) std::this_thread::sleep_for(std::chrono::milliseconds(0)); signal_pause = false;}
void resume() { paused = false;}
private:
void run();
void render();
std::vector<AM_ATOM> atoms;
std::vector<size_t> layers;
size_t w;
size_t h;
bool done;
bool median_combining;
std::atomic<bool> signal_stop;
std::atomic<bool> running;
std::atomic<bool> signal_pause;
std::atomic<bool> paused;
std::thread step_thread;
};
AM_ATOM am_create_atom(double x, double y, unsigned char r, unsigned char g, unsigned char b, unsigned char a);
double am_atom_distance(AM_ATOM a1, AM_ATOM a2);
double am_atom_gradient(AM_ATOM a1, AM_ATOM a2);
const char * am_get_version();
size_t am_get_warning();
#endif
namespace am {
const unsigned RGB = 0; // Don't change the color space.
const unsigned HSP = 1; // Convert from RGB to HSP.
const unsigned NONE = 2; // Color/motion interpolation is off.
const unsigned LINEAR = 3; // Uses linear interpolation.
const unsigned SPLINE = 4; // Spline interpolation (motion only).
const unsigned COSINE = 5; // Cosine interpolation (colors only).
const unsigned PERLIN = 6; // Perlin noise dependent interpolation (colors only).
const unsigned STATE_BLOB_DETECTION = 0;
const unsigned STATE_BLOB_UNIFICATION = 1;
const unsigned STATE_BLOB_MATCHING = 2;
const unsigned STATE_ATOM_MORPHING = 3;
const unsigned STATE_DONE = 4;
const unsigned char HAS_PIXEL = 1;
const unsigned char HAS_FLUID = 2;
typedef struct pixel {
uint16_t x;
uint16_t y;
color c;
} pixel;
typedef struct blob {
size_t index; // Position in the nest vector.
std::set<size_t> surface; // Pixel positions for surface.
std::set<size_t> border; // Pixel positions around surface.
size_t group =0;
bool unified=false;
double x,y,r,g,b,a;
} blob;
typedef struct key_frame {
std::vector<blob*> blobs; // Vector of blobs.
std::map<size_t, pixel> pixels; // Map of pixels by position.
std::map<size_t, blob*> owners; // Map of blobs by position.
double x,y,r,g,b,a;
size_t index =0; // Position in the nest container.
size_t first_expansion=0; // Blobs before this index cannot expand.
size_t first_dust =0; // First under-sized blob to be unified.
} frame;
typedef union key_point {
struct {
uint16_t x;
uint16_t y;
uint8_t x_fract;
uint8_t y_fract;
uint8_t flags;
} s;
uint64_t word;
} point;
typedef struct blob_chain {
size_t width =0;
size_t height=0;
point **points=nullptr;
size_t **places=nullptr;
double energy=0.0;
size_t max_surface=0;
glnemo::CRSpline *splines=nullptr;
#ifdef ATOMORPH_OPENCV
void **kdtrees=nullptr;
void **feature=nullptr;
#endif
} chain;
const size_t WARN_POINTER_SIZE = 1; // Set when sizeof(void *) is less than 64 bits.
const size_t WARN_PIXEL_SIZE = 2; // Set when sizeof(pixel) is more than 64 bits.
const size_t WARN_POINT_SIZE = 3; // Set when sizeof(point) is more than 64 bits.
const unsigned TEXTURE = 0;
const unsigned AVERAGE = 1;
const unsigned DISTINCT = 2;
void clear_chain(chain *c);
bool renew_chain(chain *c, size_t width, size_t height);
pixel create_pixel(uint16_t x, uint16_t y, unsigned char r, unsigned char g, unsigned char b, unsigned char a);
pixel create_pixel(uint16_t x, uint16_t y, color c);
inline size_t xy2pos (uint16_t x, uint16_t y) {return (y*(UINT16_MAX+1)+x);}
inline void point2xy(point pt, float *x, float *y) {
*x = pt.s.x + pt.s.x_fract/float(UINT8_MAX+1);
*y = pt.s.y + pt.s.y_fract/float(UINT8_MAX+1);
}
inline uint32_t pixel_distance (pixel p1, pixel p2 ) {
int32_t xd = p1.x-p2.x;
int32_t yd = p1.y-p2.y;
return xd*xd+yd*yd;
}
inline uint32_t approx_point_distance (point p1, point p2) {
int32_t xd = p1.s.x-p2.s.x;
int32_t yd = p1.s.y-p2.s.y;
return xd*xd + yd*yd;
}
inline uint64_t point_distance (point p1, point p2) {
uint64_t xd = std::abs(((UINT8_MAX+1)*p1.s.x+p1.s.x_fract) - ((UINT8_MAX+1)*p2.s.x+p2.s.x_fract));
uint64_t yd = std::abs(((UINT8_MAX+1)*p1.s.y+p1.s.y_fract) - ((UINT8_MAX+1)*p2.s.y+p2.s.y_fract));
return (xd*xd + yd*yd);
}
inline double distance(double x1, double y1, double x2, double y2) {
double xd = x1-x2;
double yd = y1-y2;
return xd*xd+yd*yd;
}
inline bool point_has_pixel(point p) {
return (p.s.flags & HAS_PIXEL);
}
inline bool point_has_fluid(point p) {
return (p.s.flags & HAS_FLUID);
}
const char * get_version();
size_t get_warning();
bool uses_opencv();
}
#include "thread.h"
#include "morph.h"
#endif
/*
The MIT License (MIT)
Copyright (c) 2013-2014 Erich Erstu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

181
color.cpp Normal file
View File

@ -0,0 +1,181 @@
/*
* See Copyright Notice in atomorph.h
*/
#include <math.h>
#include <stdlib.h>
#include <algorithm>
#include "color.h"
namespace am {
color create_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
color c;
c.r = r;
c.g = g;
c.b = b;
c.a = a;
return c;
}
color create_color(double r, double g, double b, double a) {
color c;
c.r = std::round(r*255.0);
c.g = std::round(g*255.0);
c.b = std::round(b*255.0);
c.a = std::round(a*255.0);
return c;
}
color rgb_to_hsp(color c) {
color hsp = c;
double r,g,b,h,s,p;
r = c.r/255.0;
g = c.g/255.0;
b = c.b/255.0;
RGBtoHSP(r,g,b,&h,&s,&p);
hsp.r = std::round(h*255.0);
hsp.g = std::round(s*255.0);
hsp.b = std::round(p*255.0);
return hsp;
}
color hsp_to_rgb(color c) {
color rgb = c;
double r,g,b,h,s,p;
h = c.r/255.0;
s = c.g/255.0;
p = c.b/255.0;
HSPtoRGB(h,s,p,&r,&g,&b);
rgb.r = std::min(std::round(r*255.0), 255.0);
rgb.g = std::min(std::round(g*255.0), 255.0);
rgb.b = std::min(std::round(b*255.0), 255.0);
return rgb;
}
const double Pr = 0.299;
const double Pg = 0.587;
const double Pb = 0.114;
// public domain function by Darel Rex Finley, 2006
//
// This function expects the passed-in values to be on a scale
// of 0 to 1, and uses that same scale for the return values.
//
// See description/examples at alienryderflex.com/hsp.html
void RGBtoHSP(double R, double G, double B, double *H, double *S, double *P) {
// Calculate the Perceived brightness.
*P=sqrt(R*R*Pr+G*G*Pg+B*B*Pb);
// Calculate the Hue and Saturation. (This part works
// the same way as in the HSV/B and HSL systems???.)
if (R==G && R==B) {
*H=0.;
*S=0.;
return;
}
if (R>=G && R>=B) {// R is largest
if (B>=G) {
*H=6./6.-1./6.*(B-G)/(R-G);
*S=1.-G/R;
}
else {
*H=0./6.+1./6.*(G-B)/(R-B);
*S=1.-B/R;
}
}
else if (G>=R && G>=B) {// G is largest
if (R>=B) {
*H=2./6.-1./6.*(R-B)/(G-B); *S=1.-B/G;
}
else {
*H=2./6.+1./6.*(B-R)/(G-R); *S=1.-R/G;
}
}
else {// B is largest
if (G>=R) {
*H=4./6.-1./6.*(G-R)/(B-R); *S=1.-R/B;
}
else {
*H=4./6.+1./6.*(R-G)/(B-G); *S=1.-G/B;
}
}
}
// public domain function by Darel Rex Finley, 2006
//
// This function expects the passed-in values to be on a scale
// of 0 to 1, and uses that same scale for the return values.
//
// Note that some combinations of HSP, even if in the scale
// 0-1, may return RGB values that exceed a value of 1. For
// example, if you pass in the HSP color 0,1,1, the result
// will be the RGB color 2.037,0,0.
//
// See description/examples at alienryderflex.com/hsp.html
void HSPtoRGB(double H, double S, double P, double *R, double *G, double *B) {
double part, minOverMax=1.-S ;
if (minOverMax>0.) {
if ( H<1./6.) { // R>G>B
H= 6.*( H-0./6.); part=1.+H*(1./minOverMax-1.);
*B=P/sqrt(Pr/minOverMax/minOverMax+Pg*part*part+Pb);
*R=(*B)/minOverMax; *G=(*B)+H*((*R)-(*B));
}
else if ( H<2./6.) { // G>R>B
H= 6.*(-H+2./6.); part=1.+H*(1./minOverMax-1.);
*B=P/sqrt(Pg/minOverMax/minOverMax+Pr*part*part+Pb);
*G=(*B)/minOverMax; *R=(*B)+H*((*G)-(*B));
}
else if ( H<3./6.) { // G>B>R
H= 6.*( H-2./6.); part=1.+H*(1./minOverMax-1.);
*R=P/sqrt(Pg/minOverMax/minOverMax+Pb*part*part+Pr);
*G=(*R)/minOverMax; *B=(*R)+H*((*G)-(*R));
}
else if ( H<4./6.) { // B>G>R
H= 6.*(-H+4./6.); part=1.+H*(1./minOverMax-1.);
*R=P/sqrt(Pb/minOverMax/minOverMax+Pg*part*part+Pr);
*B=(*R)/minOverMax; *G=(*R)+H*((*B)-(*R));
}
else if ( H<5./6.) { // B>R>G
H= 6.*( H-4./6.); part=1.+H*(1./minOverMax-1.);
*G=P/sqrt(Pb/minOverMax/minOverMax+Pr*part*part+Pg);
*B=(*G)/minOverMax; *R=(*G)+H*((*B)-(*G)); }
else { // R>B>G
H= 6.*(-H+6./6.); part=1.+H*(1./minOverMax-1.);
*G=P/sqrt(Pr/minOverMax/minOverMax+Pb*part*part+Pg);
*R=(*G)/minOverMax; *B=(*G)+H*((*R)-(*G));
}
}
else {
if ( H<1./6.) { // R>G>B
H= 6.*( H-0./6.); *R=sqrt(P*P/(Pr+Pg*H*H)); *G=(*R)*H; *B=0.;
}
else if ( H<2./6.) { // G>R>B
H= 6.*(-H+2./6.); *G=sqrt(P*P/(Pg+Pr*H*H)); *R=(*G)*H; *B=0.;
}
else if ( H<3./6.) { // G>B>R
H= 6.*( H-2./6.); *G=sqrt(P*P/(Pg+Pb*H*H)); *B=(*G)*H; *R=0.;
}
else if ( H<4./6.) { // B>G>R
H= 6.*(-H+4./6.); *B=sqrt(P*P/(Pb+Pg*H*H)); *G=(*B)*H; *R=0.;
}
else if ( H<5./6.) { // B>R>G
H= 6.*( H-4./6.); *B=sqrt(P*P/(Pb+Pr*H*H)); *R=(*B)*H; *G=0.;
}
else { // R>B>G
H= 6.*(-H+6./6.); *R=sqrt(P*P/(Pr+Pb*H*H)); *B=(*R)*H; *G=0.;
}
}
}
}

33
color.h Normal file
View File

@ -0,0 +1,33 @@
/*
* See Copyright Notice in atomorph.h
*/
namespace am {
typedef struct color {
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
} color;
color create_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
color create_color(double r, double g, double b, double a);
inline double color_distance(color c1, color c2) {
int16_t rd = c1.r-c2.r;
int16_t gd = c1.g-c2.g;
int16_t bd = c1.b-c2.b;
int16_t ad = c1.a-c2.a;
return sqrt(rd*rd+gd*gd+bd*bd+ad*ad)/510.0;
}
color rgb_to_hsp(color c);
color hsp_to_rgb(color c);
void RGBtoHSP(double R, double G, double B, double *H, double *S, double *P);
void HSPtoRGB(double H, double S, double P, double *R, double *G, double *B);
}

45
demo/Makefile Normal file
View File

@ -0,0 +1,45 @@
NAME = atomorph
CC = gcc
PROF = -O2
C_FLAGS = -std=c++11 -Wall $(PROF)
L_FLAGS = -lm -lstdc++ -pthread $(PROF)
OBJ_DIR = obj
OPENCV = 0
#Change the LIB_DIR to the path where the MODULES are located:
LIB_DIR = ../
MODULES = atomorph.a
SRC_FILES := $(wildcard *.cpp)
LIB_FILES := $(patsubst %,$(LIB_DIR)/lib%,$(MODULES))
O_FILES := $(patsubst %.cpp,$(OBJ_DIR)/%.o,$(SRC_FILES))
OUT = ./$(NAME)
all:
@printf "\033[0mHINT: On errors, try \033[1;33m-std=gnu++11 -stdlib=libc++\033[0m compiler flags.\033[0m\n"
@printf "\033[0mHINT: On linker errors, try \033[1;33mmake opencv\033[0m to link with OpenCV.\033[0m\n"
@$(MAKE) independently_linked -s
opencv:
@$(MAKE) opencv_linked -s
opencv_linked: L_FLAGS+= -lopencv_core -lopencv_flann
opencv_linked: $(O_FILES)
@printf "\033[1;33mMaking \033[37m ...."
$(CC) -o ./$(NAME) $(O_FILES) $(LIB_FILES) $(L_FLAGS)
@printf "\033[1;32m OpenCV dependent %s DONE!\033[0m\n" $(NAME)
independently_linked: $(O_FILES)
@printf "\033[1;33mMaking \033[37m ...."
$(CC) -o ./$(NAME) $(O_FILES) $(LIB_FILES) $(L_FLAGS)
@printf "\033[1;32m %s DONE!\033[0m\n" $(NAME)
$(OBJ_DIR)/%.o: %.cpp
@printf "\033[1m\033[31mCompiling \033[37m....\033[34m %-20s\t\033[33m%6s\033[31m lines\033[0m \n" $*.cpp "`wc -l $*.cpp | cut -f1 -d' '`"
@$(CC) $< $(DEFINES) $(C_FLAGS) -c -o $@
clean:
@printf "\033[1;36mCleaning \033[37m ...."
@rm -f $(O_FILES) $(OUT) *~ *.bak *.orig *.rej
@printf "\033[1;37m $(NAME) cleaned!\033[0m\n"

12
demo/README Normal file
View File

@ -0,0 +1,12 @@
Build instructions:
Run make in this folder.
Edit the Makefile if needed.
Testing out AtoMorph:
See README in tests folder.
For a graphical version of AtoMorph:
See README in allegro5 folder.
If you like this software, please consider making a donation
by sending Bitcoins to 1Erich1YUdkUAp9ynf4Rfw2ug8nBtuUmMu.

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<CodeBlocks_project_file>
<FileVersion major="1" minor="6" />
<Project>
<Option title="AtoMorph GUI" />
<Option pch_mode="2" />
<Option compiler="gcc" />
<Build>
<Target title="Debug">
<Option output="atomorph-gui" prefix_auto="1" extension_auto="1" />
<Option object_output="obj/" />
<Option type="0" />
<Option compiler="gcc" />
<Compiler>
<Add option="-g" />
</Compiler>
</Target>
<Target title="Release">
<Option output="atomorph-gui" prefix_auto="1" extension_auto="1" />
<Option object_output="obj/" />
<Option type="0" />
<Option compiler="gcc" />
<Compiler>
<Add option="-O2" />
</Compiler>
<Linker>
<Add option="-s" />
</Linker>
</Target>
</Build>
<Compiler>
<Add option="-std=c++11" />
</Compiler>
<Linker>
<Add library="liballegro" />
<Add library="liballegro_image" />
<Add library="liballegro_font" />
<Add library="liballegro_primitives" />
<Add library="../../libatomorph.a" />
</Linker>
<Unit filename="main.cpp" />
<Unit filename="main.h" />
<Extensions>
<code_completion />
<debugger />
</Extensions>
</Project>
</CodeBlocks_project_file>

44
demo/allegro5/Makefile Normal file
View File

@ -0,0 +1,44 @@
NAME = atomorph-gui
CC = gcc
PROF = -O2
C_FLAGS = -std=c++11 -Wall $(PROF)
L_FLAGS = ../../libatomorph.a -lm -lstdc++ $(PROF)
OBJ_DIR = obj
DEFINES = -D ATOMORPH_DEPRECATED
#Change the LIB_DIR to the path where the MODULES are located:
LIB_DIR = /usr/lib/
MODULES = allegro.so allegro_image.so allegro_font.so allegro_primitives.so
SRC_FILES := $(wildcard *.cpp)
LIB_FILES := $(patsubst %,$(LIB_DIR)/lib%,$(MODULES))
O_FILES := $(patsubst %.cpp,$(OBJ_DIR)/%.o,$(SRC_FILES))
OUT = ./$(NAME)
all:
@printf "\033[0mHINT: libatomorph.a has to be compiled using the \033[1;33mmake deprecated\033[0m command.\033[0m\n"
@printf "\033[0mHINT: Use \033[1;33mmake static\033[0m to link statically.\033[0m\n"
@$(MAKE) dynamically_linked -s
static:
@$(MAKE) statically_linked -s
statically_linked: $(O_FILES)
@printf "\033[1;33mMaking \033[37m ...."
$(CC) -static -o ./$(NAME) $(O_FILES) $(LIB_FILES) $(L_FLAGS)
@printf "\033[1;32m Statically linked %s DONE!\033[0m\n" $(NAME)
dynamically_linked: $(O_FILES)
@printf "\033[1;33mMaking \033[37m ...."
$(CC) -o ./$(NAME) $(O_FILES) $(LIB_FILES) $(L_FLAGS)
@printf "\033[1;32m Dynamically linked %s DONE!\033[0m\n" $(NAME)
$(OBJ_DIR)/%.o: %.cpp
@printf "\033[1m\033[31mCompiling \033[37m....\033[34m %-20s\t\033[33m%6s\033[31m lines\033[0m \n" $*.cpp "`wc -l $*.cpp | cut -f1 -d' '`"
@$(CC) $< $(DEFINES) $(C_FLAGS) -c -o $@
clean:
@printf "\033[1;36mCleaning \033[37m ...."
@rm -f $(O_FILES) $(OUT) *~ *.bak *.orig *.rej
@printf "\033[1;37m $(NAME) cleaned!\033[0m\n"

15
demo/allegro5/README Normal file
View File

@ -0,0 +1,15 @@
This AtoMorph-GUI implementation is deprecated because it uses
an older version of the AtoMorph Library. The latter has to be
compiled using the `deprecated` build target.
AtoMorph-GUI requires Allegro5.1 installed on your system.
Build instructions:
Run make in this folder.
Edit the Makefile if needed.
Alternative build instructions:
Use CodeBlocks and open AtoMorph-GUI.cbp.
If you like this software, please consider making a donation
by sending Bitcoins to 1Erich1YUdkUAp9ynf4Rfw2ug8nBtuUmMu.

BIN
demo/allegro5/data/fixed_font.tga Executable file

Binary file not shown.

561
demo/allegro5/main.cpp Normal file
View File

@ -0,0 +1,561 @@
/*
* See Copyright Notice in main.h
*/
#include <stdio.h>
#include <math.h>
#include "main.h"
const float FPS = 60.0; // Maximum FPS.
const int SCREEN_W = 800;
const int SCREEN_H = 600;
const int MORPH_W = 128; // Width of the morph. Should be at most the width of the input image.
const int MORPH_H = 128; // Height of the morph. Should be at most the height of the input image.
const int ATOMS = 10000; // Number of atoms used in one thread.
const size_t THREAD_N = 5; // Number of threads to use to find a perfect morph.
const size_t SLOWNESS = 50; // How many frames to render per animation cycle.
size_t morph_time = 0;
int view_frame = 0;
bool pressed_keys[ALLEGRO_KEY_MAX];
size_t active_thread = 0; // When render is ON, morph only one thread at a time.
int color_fade = AM_NONE; // Color interpolation method.
int trajectory = AM_NONE; // Atom trajectory interpolation method.
bool median_combining = false; // Noise reduction method. FALSE for averaging.
bool stop_morphing = false; // To halt the morph time temporarily.
bool no_render = false; // When TRUE no blending is done, just atom morphing.
ALLEGRO_DISPLAY *display = NULL;
ALLEGRO_EVENT_QUEUE *event_queue = NULL;
ALLEGRO_TIMER *timer = NULL;
ALLEGRO_FONT *font = NULL;
ALLEGRO_BITMAP *morph_bmp = NULL; // Holds the final morph as a bitmap.
ALLEGRO_BITMAP *thread_bmp[THREAD_N]; // Holds the results of the morphing threads.
// Helper function to initially populate the AM_SCENE object according to the provided
// image file.
bool fill_scene(AM_SCENE *scene, size_t frame, const char *png_file) {
std::random_device rd;
std::default_random_engine re(rd());
std::uniform_real_distribution<double> uniform_dist(0.0, 1.0);
ALLEGRO_BITMAP * bmp = al_load_bitmap(png_file);
int bmp_w = al_get_bitmap_width(bmp);
int bmp_h = al_get_bitmap_height(bmp);
al_lock_bitmap(bmp, ALLEGRO_PIXEL_FORMAT_ANY, ALLEGRO_LOCK_READONLY);
for (int j=0; j<bmp_h; j++) {
for (int i=0; i<bmp_w; i++) {
ALLEGRO_COLOR c = al_get_pixel(bmp, i, j);
unsigned char r,g,b,a;
al_unmap_rgba(c, &r, &g, &b, &a);
if (a == 0) continue;
double px,py;
px = double(i) / double(bmp_w);
py = double(j) / double(bmp_h);
scene->push_atom(frame, am_create_atom(px,py,r,g,b,a));
}
}
al_unlock_bitmap(bmp);
al_destroy_bitmap(bmp);
return true;
}
int main(int argc, char **argv) {
if (!init(argc, argv)) {
fprintf(stderr, "Failed to initialize!\n");
return -1;
}
std::random_device rd;
std::default_random_engine seed_engine(rd());
std::uniform_int_distribution<unsigned> uniform_dist(1, std::numeric_limits<unsigned>::max());
morph_bmp = al_create_bitmap(MORPH_W, MORPH_H);
al_set_target_bitmap(morph_bmp);
al_clear_to_color(al_map_rgba(0,0,0,0));
AM_BLENDER blender; // Used to combine the thread results into the final morph.
blender.set_resolution(MORPH_W, MORPH_H);
blender.set_median_combining(median_combining);
blender.start();
AM_THREAD scene_thread[THREAD_N]; // Each of these will morph its own version of the animation.
AM_SCENE scene_buf [THREAD_N]; // Temporarily holds the last results of the morphing threads.
AM_IMAGE image_buf [THREAD_N]; // Used to render the final image of the provided scene.
{
AM_SCENE scene; // Needed temporarily to store the raw input data.
scene.init(ATOMS, 6); // Reserve 6 frames for this scene.
fill_scene(&scene, 0, "../tests/data/battlelord_1.png");
fill_scene(&scene, 1, "../tests/data/battlelord_2.png");
fill_scene(&scene, 2, "../tests/data/battlelord_3.png");
fill_scene(&scene, 3, "../tests/data/battlelord_4.png");
fill_scene(&scene, 4, "../tests/data/battlelord_5.png");
fill_scene(&scene, 5, "../tests/data/battlelord_6.png");
for (size_t i=0; i<THREAD_N; ++i) {
scene_buf [i].init(scene.atom_count(), scene.frame_count());
scene_thread[i].init(&scene); // Give the initial work to the worker thread.
scene_thread[i].set_seed(uniform_dist(seed_engine)); // Seed for its RNG.
scene_thread[i].set_step_size(100); // Number of iterations to make per step.
scene_thread[i].set_magic_exponent(3.0); // Affects the generated trajectories.
scene_thread[i].set_gradient_importance(0.0); // Importance of the color values.
scene_thread[i].start();
thread_bmp[i] = al_create_bitmap(MORPH_W, MORPH_H);
al_set_target_bitmap(thread_bmp[i]);
al_clear_to_color(al_map_rgba(0,0,0,0));
image_buf[i].set_scene(&scene); // Prepares the image rendering object.
image_buf[i].set_resolution(MORPH_W, MORPH_H);
image_buf[i].set_seed(i); // Seed for its RNG (used in Perlin noise).
image_buf[i].start();
}
}
// Helper variables:
bool redraw = true;
bool doexit = false;
bool started = false;
bool debug = false;
int frame = 0;
double scene_cost = 0.0; // Lower cost means better quality. Cost decreases over time.
int old_morph_time = 0;
bool refresh = false;
while(!doexit) {
ALLEGRO_EVENT ev;
al_wait_for_event(event_queue, &ev);
if(ev.type == ALLEGRO_EVENT_TIMER) {
redraw = true;
}
else if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
break;
}
else if(ev.type == ALLEGRO_EVENT_KEY_DOWN) {
pressed_keys[ev.keyboard.keycode] = true;
switch(ev.keyboard.keycode) {
case ALLEGRO_KEY_PAD_PLUS: view_frame++; break;
case ALLEGRO_KEY_PAD_MINUS: view_frame--; break;
case ALLEGRO_KEY_D: debug = !debug; break;
case ALLEGRO_KEY_M: median_combining = !median_combining; break;
case ALLEGRO_KEY_S: stop_morphing = !stop_morphing; break;
case ALLEGRO_KEY_R: no_render = !no_render;
if (no_render) old_morph_time = morph_time;
else {
morph_time = old_morph_time;
refresh = true;
}
break;
case ALLEGRO_KEY_C:
if (color_fade == AM_NONE) color_fade = AM_LINEAR;
else if (color_fade == AM_LINEAR) color_fade = AM_COSINE;
else if (color_fade == AM_COSINE) color_fade = AM_PERLIN;
else color_fade = AM_NONE;
break;
case ALLEGRO_KEY_T:
if (trajectory == AM_NONE) trajectory = AM_LINEAR;
else if (trajectory == AM_LINEAR) trajectory = AM_SPLINE;
else trajectory = AM_NONE;
break;
default: break;
}
}
else if(ev.type == ALLEGRO_EVENT_KEY_UP) {
pressed_keys[ev.keyboard.keycode] = false;
switch(ev.keyboard.keycode) {
case ALLEGRO_KEY_ESCAPE:
doexit = true;
break;
case ALLEGRO_KEY_SPACE:
started = !started;
for (size_t i=0; i<THREAD_N; ++i) {
if (!scene_thread[i].is_running()) continue;
if (!started) scene_thread[i].pause();
else scene_thread[i].resume();
}
break;
default: break;
}
}
else if(ev.type == ALLEGRO_EVENT_MOUSE_AXES ||
ev.type == ALLEGRO_EVENT_MOUSE_ENTER_DISPLAY);
else if(ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN) ;
else if(ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP) ;
if(redraw && al_is_event_queue_empty(event_queue)) {
int fps;
if ( (fps = calculate_fps()) == -1) continue;
frame++;
if (started) {
size_t i;
bool skip_render = false;
if (morph_time%SLOWNESS == 0 || refresh) {
double cost =0.0;
active_thread = (active_thread + 1) % THREAD_N;
for (i=0; i<THREAD_N; ++i) {
if (!scene_thread[i].is_paused()) {
// Before thread results can be read it must be paused.
scene_thread[i].pause();
cost += scene_thread[i].get_cost();
if (!no_render) {
scene_thread[i].fetch_scene(&(scene_buf[i]));
}
}
// When rendering is disabled all morphing threads will work,
// othwerwise only the active morphing thread works.
if (active_thread == i || no_render) scene_thread[i].resume();
}
scene_cost = cost / THREAD_N;
skip_render = true;
refresh = false;
}
else if (no_render) active_thread = (active_thread + 1) % THREAD_N;
if (no_render) skip_render = true;
bool slow_down = false; // Is set to TRUE when image rendering in not yet finished.
// This is to slow down the animation rather than skip frames.
if (!skip_render) {
bool all_images_done = true;
for (i=0; i<THREAD_N; ++i) {
if (!image_buf[i].is_paused()) {
all_images_done = false;
break;
}
}
if (all_images_done && blender.is_paused()) {
double t = (morph_time % SLOWNESS)/(double(SLOWNESS));
// Render blender image:
blend_morphs(&blender, morph_bmp);
blender.clear();
blender.set_median_combining(median_combining);
for (i=0; i<THREAD_N; ++i) {
// Render thread images:
render_morph(&image_buf[i], thread_bmp[i]);
// Give a new job to image blender thread:
blender.add_image(&image_buf[i]);
// Give new job to image thread:
image_buf[i].set_scene(&scene_buf[i]);
image_buf[i].set_time(t);
image_buf[i].set_color_interpolation(color_fade);
image_buf[i].set_path_interpolation(trajectory);
image_buf[i].resume();
}
blender.resume();
}
else slow_down = true; // Image rendering is lagging behind!
}
if (!stop_morphing) {
if (!slow_down && ++morph_time == SLOWNESS) morph_time = 0;
}
}
redraw = false;
double k = double(MORPH_H) /double(MORPH_W); // Aspect ratio.
int merged_h = SCREEN_H - SCREEN_H/8;
int merged_w = merged_h * k;
al_set_target_bitmap(al_get_backbuffer(display));
al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_ZERO);
al_clear_to_color(al_map_rgb(128,128,128));
al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_INVERSE_ALPHA);
int mh = SCREEN_H / 8;
int mw = mh * k;
double s = double(SCREEN_W)/double(THREAD_N);
// Draw the thread images to the upper side of the screen:
for (size_t i=0; i<THREAD_N; ++i) {
if (active_thread == i) {
al_draw_rectangle(s*i+s/2.0 - mw/2.0, 0.0,
s*i+s/2.0 + mw/2.0, mh,
al_map_rgb(192,0,0), 4.0);
}
al_draw_scaled_bitmap(thread_bmp[i], 0.0, 0.0, MORPH_W, MORPH_H,
s*i+s/2.0 - mw/2.0, 0.0, mw, mh, 0);
}
// Draw the final morph to the center of the screen:
al_draw_scaled_bitmap(morph_bmp, 0.0, 0.0, MORPH_W, MORPH_H,
SCREEN_W/2 - merged_w/2, SCREEN_H - merged_h,
merged_w, merged_h, 0);
// Draw textual information:
if (font!=NULL) {
al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_INVERSE_ALPHA);
al_draw_filled_rectangle(0, 0, SCREEN_W-SCREEN_W/4, 14, al_map_rgba(0,0,0,128));
al_draw_textf(font, al_map_rgb(0,255,0), 0, 0, 0,
"FPS: %3d; Atoms: %d; Cost: %2.3f; Thread: %lu/%lu; Morph time: %lu;",
fps, ATOMS, scene_cost, active_thread+1, THREAD_N, morph_time
);
if (!started) {
al_draw_filled_rectangle(0, 0, SCREEN_W, SCREEN_H/4, al_map_rgba(0,0,0,128));
al_draw_textf(font, al_map_rgb(0,255,0),
SCREEN_W/2, SCREEN_H/8, ALLEGRO_ALIGN_CENTRE,
"AtoMorph v%s Demo by Erich Erstu, 2013",
am_get_version()
);
al_draw_filled_rectangle(0, SCREEN_H - SCREEN_H/4,
SCREEN_W, SCREEN_H, al_map_rgba(0,0,0,128)
);
al_draw_textf(font, al_map_rgb(0,255,0), SCREEN_W/2,
SCREEN_H - SCREEN_H/8, ALLEGRO_ALIGN_CENTRE, "Press SPACE to start!"
);
}
else {
al_draw_filled_rectangle(0, 14, SCREEN_W/3, SCREEN_H/5, al_map_rgba(0,0,0,128));
al_draw_textf(font, al_map_rgb(0,255,0), 0, 12, 0,
"[M]edian combining: %s", median_combining ? "ON" : "OFF"
);
al_draw_textf(font, al_map_rgb(0,255,0), 0, 24, 0,
"[S]top morph time.%s", stop_morphing ? " (Stopped)" : ""
);
al_draw_textf(font, al_map_rgb(0,255,0), 0, 36, 0,
"[C]olor interpolation: %s",
color_fade == AM_NONE ? "NONE" :
color_fade == AM_LINEAR ? "LINEAR" :
color_fade == AM_COSINE ? "COSINE" :
color_fade == AM_PERLIN ? "PERLIN" : ""
);
al_draw_textf(font, al_map_rgb(0,255,0), 0, 48, 0,
"[T]rajectory interpolation: %s",
trajectory == AM_NONE ? "NONE" :
trajectory == AM_LINEAR ? "LINEAR" :
trajectory == AM_SPLINE ? "SPLINE" : ""
);
al_draw_textf(font, al_map_rgb(0,255,0), 0, 60, 0,
"[R]endering: %s", !no_render ? "ON" : "OFF");
al_draw_text (font, al_map_rgb(0,255,0), 0, 72, 0, "SPACE to pause.");
al_draw_text (font, al_map_rgb(0,255,0), 0, 84, 0, "ESC to exit.");
}
}
al_flip_display();
}
}
for (size_t t=0; t<THREAD_N; ++t) {
scene_thread[t].stop();
image_buf[t].stop();
al_destroy_bitmap(thread_bmp[t]);
}
blender.stop();
al_destroy_bitmap(morph_bmp);
al_destroy_font(font);
al_destroy_timer(timer);
al_destroy_display(display);
al_destroy_event_queue(event_queue);
return 0;
}
void render_morph(AM_IMAGE *img, ALLEGRO_BITMAP *to) {
// Clear old bitmap:
al_set_target_bitmap(to);
al_set_blender(ALLEGRO_DEST_MINUS_SRC, ALLEGRO_ALPHA, ALLEGRO_INVERSE_ALPHA);
al_draw_filled_rectangle(0.0, 0.0, MORPH_W, MORPH_H, al_map_rgba(0,0,0,255));
// Prepare to render:
al_set_blender(ALLEGRO_ADD, ALLEGRO_ALPHA, ALLEGRO_INVERSE_ALPHA);
ALLEGRO_LOCKED_REGION * lock = al_lock_bitmap(to,ALLEGRO_PIXEL_FORMAT_ANY,ALLEGRO_LOCK_READWRITE);
if (lock == NULL) return;
// Put the pixels:
size_t pixels = img->pixel_count();
for (size_t i=0; i<pixels; ++i) {
int x,y;
unsigned char r,g,b,a;
img->get_xy(i, &x, &y);
img->get_rgba(i, &r, &g, &b, &a);
al_put_pixel(x, y, al_map_rgba(r,g,b,a));
}
// Finally unlock the bitmap:
if (lock) al_unlock_bitmap(to);
}
void blend_morphs(AM_BLENDER *blender, ALLEGRO_BITMAP *to) {
// Clear old bitmap:
al_set_target_bitmap(to);
al_set_blender(ALLEGRO_DEST_MINUS_SRC, ALLEGRO_ALPHA, ALLEGRO_INVERSE_ALPHA);
al_draw_filled_rectangle(0.0, 0.0, MORPH_W, MORPH_H, al_map_rgba(0,0,0,255));
// Prepare to render:
al_set_blender(ALLEGRO_ADD, ALLEGRO_ALPHA, ALLEGRO_INVERSE_ALPHA);
ALLEGRO_LOCKED_REGION * lock = al_lock_bitmap(to,ALLEGRO_PIXEL_FORMAT_ANY,ALLEGRO_LOCK_READWRITE);
if (lock == NULL) return;
// Put the pixels:
size_t pixels = blender->pixel_count();
for (size_t i=0; i<pixels; ++i) {
int x,y;
unsigned char r,g,b,a;
blender->get_xy(i, &x, &y);
blender->get_rgba(i, &r, &g, &b, &a);
al_put_pixel(x, y, al_map_rgba(r,g,b,a));
}
// Finally unlock the bitmap:
if (lock) al_unlock_bitmap(to);
}
bool init(int argc, char **argv) {
if (true == (am_get_warning()&AM_WARN_POINTER_SIZE)) {
fprintf(stderr, "Pointer size is insufficiently small.\n");
}
if (true == (am_get_warning()&AM_WARN_ATOM_SIZE)) {
fprintf(stderr, "Atom size (%lu) is larger than optimal (%lu).\n",
sizeof(AM_ATOM),
sizeof(void *)
);
}
if(!al_init()) {
fprintf(stderr, "failed to initialize allegro!\n");
return false;
}
if(!al_install_keyboard()) {
fprintf(stderr, "failed to initialize the keyboard!\n");
return false;
}
al_install_mouse();
al_init_image_addon();
al_init_font_addon();
al_init_primitives_addon();
timer = al_create_timer(1.0 / FPS);
if(!timer) {
fprintf(stderr, "failed to create timer!\n");
return false;
}
display = al_create_display(SCREEN_W, SCREEN_H);
if(!display) {
fprintf(stderr, "failed to create display!\n");
al_destroy_timer(timer);
return false;
}
al_set_new_bitmap_flags(ALLEGRO_MAG_LINEAR|ALLEGRO_MIN_LINEAR);
font = al_load_font("data/fixed_font.tga", 0, 0);
if (font==NULL) {
fprintf(stderr, "failed to load font!\n");
al_destroy_display(display);
al_destroy_timer(timer);
return false;
}
al_set_target_bitmap(al_get_backbuffer(display));
event_queue = al_create_event_queue();
if(!event_queue) {
fprintf(stderr, "failed to create event_queue!\n");
al_destroy_display(display);
al_destroy_timer(timer);
al_destroy_font(font);
return false;
}
al_register_event_source(event_queue, al_get_display_event_source(display));
al_register_event_source(event_queue, al_get_timer_event_source(timer));
al_register_event_source(event_queue, al_get_keyboard_event_source());
al_register_event_source(event_queue, al_get_mouse_event_source());
al_clear_to_color(al_map_rgb(0,0,0));
al_draw_textf(font, al_map_rgb(0,255,0), SCREEN_W/2, SCREEN_H/2,
ALLEGRO_ALIGN_CENTRE,
"LOADING...");
al_flip_display();
al_start_timer(timer);
calculate_fps();
return true;
}
int round_int( double r ) {
return (r > 0.0) ? (r + 0.5) : (r - 0.5);
}
int calculate_fps() {
static int times = 0;
static double old_time = 0.0;
static double delta_sum = 0.0;
static int old_fps = -1;
static bool first = true;
if (first) {
first = false;
old_time = al_get_time();
return -1;
}
int rec_times = 0;
int max_times = round_int(FPS);
double new_time = al_get_time();
double delta = new_time - old_time;
delta_sum += delta;
old_time = new_time;
double p = delta_sum * max_times;
rec_times = round_int(p);
if (times > rec_times) {
return -1;
}
times++;
int fps = 0;
if (delta_sum >= 1.0 || times>=max_times) {
fps = times;
old_fps = fps;
times=0;
delta_sum=0.0;
}
else {
if (old_fps == -1) fps = times;
else fps = old_fps;
}
return fps;
}

50
demo/allegro5/main.h Normal file
View File

@ -0,0 +1,50 @@
/*
* AtoMorph Demo - Simple Demo showing what AtoMorph is capable of doing.
* See Copyright Notice at the end of this file.
*/
#include "../../atomorph.h"
#include <allegro5/allegro.h>
#include <allegro5/allegro_font.h>
#include <allegro5/allegro_image.h>
#include <allegro5/allegro_color.h>
#include <allegro5/allegro_primitives.h>
extern const float FPS;
extern const int SCREEN_W;
extern const int SCREEN_H;
extern ALLEGRO_DISPLAY *display ;
extern ALLEGRO_EVENT_QUEUE *event_queue ;
extern ALLEGRO_TIMER *timer ;
extern ALLEGRO_FONT *font ;
bool init(int argc, char **argv);
int calculate_fps();
void draw(AM_SCENE *scene, ALLEGRO_BITMAP *to, double t, double weight);
void render_morph(AM_IMAGE *img, ALLEGRO_BITMAP *to);
void blend_morphs(AM_BLENDER *b, ALLEGRO_BITMAP *to);
/*
The MIT License (MIT)
Copyright (c) 2013-2014 Erich Erstu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

4
demo/allegro5/obj/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

6260
demo/lodepng.cpp Normal file

File diff suppressed because it is too large Load Diff

1716
demo/lodepng.h Normal file

File diff suppressed because it is too large Load Diff

299
demo/main.cpp Normal file
View File

@ -0,0 +1,299 @@
/*
* See Copyright Notice in main.h
*/
#include <stdio.h>
#include <math.h>
#include <chrono>
#include <thread>
#include "main.h"
#include "lodepng.h"
MORPH_OPTIONS options;
int main(int argc, char **argv) {
if (!init(argc, argv)) {
fprintf(stderr, "Failed to initialize!\n");
return -1;
}
if (options.exit_flag) return 0;
std::chrono::steady_clock::time_point program_start;
std::chrono::steady_clock::time_point program_end;
program_start = std::chrono::steady_clock::now();
{
am::morph morph;
// Options should be set before any operations with
// the morph instance. However, they can be changed
// during the run time too.
morph.set_blob_delimiter (options.differ_blobs);
morph.set_blob_max_size (options.blob_max_size);
morph.set_blob_min_size (options.blob_min_size);
morph.set_blob_box_grip (options.blob_box_grip);
morph.set_blob_box_samples(options.blob_box_samples);
morph.set_blob_threshold (options.blob_threshold);
morph.set_blob_number (options.blob_number);
morph.set_seed (options.seed);
morph.set_blob_rgba_weight(options.blob_rgba_weight);
morph.set_blob_size_weight(options.blob_size_weight);
morph.set_blob_xy_weight (options.blob_xy_weight);
morph.set_degeneration (options.degenerate);
morph.set_density (options.density); // Higher than 1 sets fluid to 0.
morph.set_motion (options.motion);
morph.set_fading (options.fading);
morph.set_threads (options.threads);
morph.set_cycle_length (options.cycle_length);
morph.set_feather (options.feather);
morph.set_keep_background (options.keep_background);
morph.set_finite (options.finite);
morph.set_show_blobs (options.show_blobs);
morph.set_fluid (options.fluid); // Higher than 0 sets density to 1.
if (!load_files(&morph)) return -1;
main_loop (&morph);
save_files(&morph);
}
program_end = std::chrono::steady_clock::now();
if (options.verbose) {
size_t duration = std::chrono::duration_cast<std::chrono::microseconds>(program_end - program_start).count();
if (duration < 1000) printf("Process took %lu microseconds to finish.\n", duration);
else if (duration < 1000000) printf("Process took %lu milliseconds to finish.\n", duration/1000);
else printf("Process took %lu seconds to finish.\n", duration/1000000);
}
return 0;
}
bool init(int argc, char **argv) {
if (true == (am::get_warning()&am::WARN_POINTER_SIZE)) {
fprintf(stderr, "Pointer size is insufficiently small.\n");
}
if (true == (am::get_warning()&am::WARN_PIXEL_SIZE)) {
fprintf(stderr, "Pixel size (%lu) is larger than optimal (%lu).\n",
sizeof(am::pixel),
sizeof(void *)
);
}
if (true == (am::get_warning()&am::WARN_POINT_SIZE)) {
fprintf(stderr, "Point size (%lu) is larger than optimal (%lu).\n",
sizeof(am::point),
sizeof(void *)
);
}
if (am::uses_opencv()) {
fprintf(stderr, "Experimental OpenCV optimizations are enabled.\n");
}
if (argc == 1) {
fprintf(stderr, "No arguments specified, try \"\e[1;33m%s --help\e[0m\".\n", argv[0]);
}
return options.parse(argc, argv);
}
bool fill_morph(am::morph *morph, size_t frame, std::vector<unsigned char> *image, unsigned width) {
size_t sz = image->size();
unsigned char r=0,g=0,b=0,a=0;
size_t pixel = 0;
bool empty = true;
if (width > UINT16_MAX) return false;
for (size_t j=0; j<sz; ++j) {
switch (j%4) {
case 0: r=image->at(j); break;
case 1: g=image->at(j); break;
case 2: b=image->at(j); break;
default: {
a=image->at(j);
if (a == 0) {
pixel++;
continue;
}
morph->add_pixel(frame, am::create_pixel(pixel%width, pixel/width, r, g, b, a));
pixel++;
empty = false;
break;
}
}
}
if (empty) morph->add_frame(frame);
return true;
}
void save_files(am::morph *morph) {
for (unsigned f = 0; f<options.frames_out; ++f) {
write_image(morph, f, morph->get_width(), morph->get_height());
}
}
bool load_files(am::morph *morph) {
std::string buf;
std::vector<unsigned char> image;
unsigned width, height, error;
unsigned max_width=0, max_height=0;
image.reserve(262144);
size_t i;
// Load input image files:
for (i=0; i<options.files.size(); ++i) {
buf=options.indir; buf.append("/"); buf.append(options.files[i]);
if (options.verbose) printf("Loading %-30s ... ", buf.c_str());
error = lodepng::decode(image, width, height, buf.c_str());
if (error) {
std::cerr << lodepng_error_text(error) << "." << std::endl;
return false;
}
max_width = std::max(max_width, width);
max_height = std::max(max_height, height);
if (options.verbose) printf("%ux%u image decoded.\n", width, height);
if (!fill_morph(morph, i, &image, width) && options.verbose) {
printf("Unable to fill %lu. frame.\n", i);
}
image.clear();
}
if (morph->get_frame_count()==0) {
fprintf(stderr, "Error. Morph does not contain any key frames.\n");
return false;
}
width = max_width;
height= max_height;
morph->set_resolution(width, height);
return true;
}
void write_image(am::morph *morph, size_t frame_out, unsigned width, unsigned height) {
double t = morph->get_time(frame_out, options.frames_out);
char buf[1024];
sprintf(buf, "%s/%s_%04lu.png", options.outdir.c_str(), options.file.c_str(), frame_out+1);
if (options.verbose) printf("Rendering %4lu. frame ... ", frame_out+1);
std::vector<unsigned char> image;
image.reserve(4*width*height);
image.resize (4*width*height, 0);
std::vector<am::pixel> pixels;
morph->get_pixels(t, &pixels);
while (!pixels.empty()) {
am::pixel px = pixels.back();
pixels.pop_back();
size_t pos = (px.y*width + px.x)*4;
if (pos >= image.size()) continue;
image[pos + 0] = px.c.r;
image[pos + 1] = px.c.g;
image[pos + 2] = px.c.b;
image[pos + 3] = px.c.a;
}
if (options.verbose) printf("writing %s ... ", buf);
unsigned error = lodepng::encode(buf, image, width, height);
if (error) {
if (options.verbose) printf("[\e[1;31mFAIL\e[0m]\n");
std::cerr << "encoder error " << error << ": "<< lodepng_error_text(error) << std::endl;
}
else if (options.verbose) printf("[\e[1;32mDONE\e[0m]\n");
}
void main_loop(am::morph *morph) {
if (options.verbose) {
printf("Blobifying %ux%u morph.\n", morph->get_width(), morph->get_height());
}
std::chrono::steady_clock::time_point start,end;
start = std::chrono::steady_clock::now();
size_t blob_count = 0;
size_t largest_frame = 0;
double last_energy = 0.0;
size_t match_blobs = 0;
size_t morph_atoms = 0;
size_t fs = options.files.size();
while (1) {
morph->suspend();
morph->synchronize();
morph->compute();
blob_count = 0;
for (size_t i=0; i<fs; ++i) {
size_t bc = morph->get_blob_count(i);
if (bc >= blob_count) {
blob_count = bc;
largest_frame = i;
}
}
unsigned morph_state = morph->get_state();
end = std::chrono::steady_clock::now();
if (std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() > 1000) {
if (morph_state == am::STATE_BLOB_DETECTION
|| morph_state == am::STATE_BLOB_UNIFICATION) {
if (options.verbose) {
printf("%lu/%lu blob%s remaining on frame %lu.\n",
blob_count, morph->get_blob_count(),
blob_count == 1 ? "" : "s", largest_frame
);
}
}
else if (morph_state == am::STATE_BLOB_MATCHING) {
if (options.verbose) {
double e = morph->get_energy();
if (last_energy < e) printf("Matching blobs, absolute energy was %30.10f.\n", e);
else printf("Matching blobs, energy decreased by %30.10f.\n", last_energy - e);
last_energy = e;
}
if (++match_blobs >= options.match_time) {
morph->next_state();
last_energy = 0.0;
}
}
else if (morph_state == am::STATE_ATOM_MORPHING) {
if (options.verbose) {
double e = morph->get_energy();
if (last_energy < e) printf("Matching atoms, absolute energy was %30.2f.\n", e);
else printf("Matching atoms, energy decreased by %30.2f.\n", last_energy - e);
last_energy = e;
}
if (++morph_atoms >= options.morph_time) {
morph->next_state();
}
}
else if (morph_state == am::STATE_DONE) {
if (options.verbose) printf("All done!\n");
break;
}
else {
if (options.verbose) printf("Unknown state!\n");
break;
}
start = std::chrono::steady_clock::now();
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
if (options.verbose) {
printf("Frame %lu had the most blobs (%lu).\n", largest_frame, blob_count);
}
return;
}

38
demo/main.h Normal file
View File

@ -0,0 +1,38 @@
/*
* AtoMorph - Command line program for morphing images with AtoMorph Library.
* See Copyright Notice at the end of this file.
*/
#include "../atomorph.h"
#include "options.h"
bool init (int argc, char **argv);
bool fill_morph (am::morph *morph, size_t frame, std::vector<unsigned char> *image, unsigned width);
void write_image (am::morph *morph, size_t frame, unsigned width, unsigned height);
bool load_files (am::morph *morph);
void save_files (am::morph *morph);
void main_loop (am::morph *morph);
/*
The MIT License (MIT)
Copyright (c) 2013-2014 Erich Erstu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

4
demo/obj/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

226
demo/options.h Normal file
View File

@ -0,0 +1,226 @@
/*
* See Copyright Notice in main.h
*/
#include <getopt.h>
class MORPH_OPTIONS {
public:
MORPH_OPTIONS() {}
~MORPH_OPTIONS() {}
const int AS_TEXTURE = am::TEXTURE;
const int AS_AVERAGE = am::AVERAGE;
const int AS_DISTINCT= am::DISTINCT;
const int BY_RGB = am::RGB;
const int BY_HSP = am::HSP;
const int MOTION_NONE = am::NONE;
const int MOTION_LINEAR = am::LINEAR;
const int MOTION_SPLINE = am::SPLINE;
const int FADING_NONE = am::NONE;
const int FADING_LINEAR = am::LINEAR;
const int FADING_COSINE = am::COSINE;
const int FADING_PERLIN = am::PERLIN;
int verbose = 0;
int blend_blobs = 0;
int finite = 0;
int keep_background= 0;
int show_blobs = AS_TEXTURE;
int differ_blobs = BY_HSP;
int motion = MOTION_SPLINE;
int fading = FADING_PERLIN;
int exit_flag = 0;
std::string name = "";
std::string indir = ".";
std::string outdir = ".";
std::string file = "untitled_morph";
int height = 0;
int width = 0;
unsigned seed = 0;
unsigned frames_out= 0;
unsigned match_time= 1;
unsigned morph_time= 3;
unsigned feather = 0;
unsigned fluid = 0;
unsigned cycle_length=100000;
unsigned threads =8;
size_t blob_number = 1;
size_t blob_max_size = SIZE_MAX;
size_t blob_min_size = 1;
uint16_t blob_box_grip = UINT16_MAX;
size_t blob_box_samples = 100;
double blob_threshold = 1.0;
unsigned char blob_rgba_weight = 1;
unsigned char blob_size_weight = 1;
unsigned char blob_xy_weight = 1;
size_t degenerate = 10000;
uint16_t density = 2;
bool frames_out_defined = false;
std::vector<std::string> files;
inline void print_usage (FILE* stream) {
fprintf (stream, "Usage: %s [options] <input files...>\n", name.c_str());
fprintf (stream, "Example: %s data/otu_1.png data/otu_2.png data/otu_3.png --verbose\n", name.c_str());
fprintf (stream, "Options:\n");
fprintf (stream,
" --blend-blobs Blob intersections are blended together.\n"
" -E --blob-feather INT Number of gradually transparent outer layers.\n"
" -c --blob-rgba-weight[0-255] Importance of a blob's color when matching.\n"
" -z --blob-size-weight[0-255] Importance of a blob's size when matching.\n"
" -p --blob-xy-weight [0-255] Importance of a blob's location when matching.\n"
" -b --blobs INT Preferred number of blobs to keep.\n"
" --blobs-as-texture A blob is textured by its pixels (default).\n"
" --blobs-as-average A blob has the average color of its pixels.\n"
" --blobs-as-distinct A blob has a random color.\n"
" --blobs-by-rgb Differentiate blobs by RGB distance.\n"
" --blobs-by-hsp Differentiate blobs by HSP distance (default).\n"
" -B --blobs-max-size INT Maximum size of a single blob in pixels.\n"
" -m --blobs-min-size INT Minimum size of a single blob in pixels.\n"
" -g --blobs-box-grip INT Unifying grip bounds for undersized blobs.\n"
" -S --blobs-box-samples INT Number of samples to take for each dust box.\n"
" -t --blobs-threshold [0-255] Maximum color difference for merging blobs.\n"
" --brief Print brief messages (default).\n"
" -C --cycle-length INT Number of iterations per morph cycle.\n"
" -d --degenerate INT Degeneration period for energy minimzation.\n"
" -D --density INT Number of atoms per pixel at minimum.\n"
" --fading-none Colours are not interpolated.\n"
" --fading-linear Colours are interpolated linearly.\n"
" --fading-cosine Colours are cosine interpolated.\n"
" --fading-perlin Use Perlin noise colour transition (default).\n"
" -f --file STR Output files with prefix.\n"
" --finite Morph will not repeat itself seamlessly.\n"
" -L --fluid INT Number of fluid simulation steps per frame.\n"
" -F --frames INT Number of frames to generate.\n"
" -h --help Display this usage information.\n"
" -i --indir STR Read input from this directory.\n"
" --keep-background Morph background is cross-dissolved.\n"
" -M --match-time INT Time in seconds given for blob matching.\n"
" -O --morph-time INT Time in seconds given for atom morphing.\n"
" --motion-none Positions are not interpolated.\n"
" --motion-linear Positions are linearly interpolated.\n"
" --motion-spline Uses Catmull-Rom splines (default).\n"
" -o --outdir STR Write output to this directory.\n"
" -s --seed INT Seed for the random number generator.\n"
" -T --threads INT Number of worker threads to spawn.\n"
" --verbose Print verbose messages.\n"
" -v --version Show version information.\n"
);
}
inline bool parse(int argc, char **argv) {
int c;
name = argv[0];
while (1) {
static struct option long_options[] = {
// These options set a flag:
{"verbose", no_argument, &verbose, 1 },
{"blend-blobs", no_argument, &blend_blobs, 1 },
{"finite", no_argument, &finite, 1 },
{"blobs-as-texture", no_argument, &show_blobs, AS_TEXTURE },
{"blobs-as-average", no_argument, &show_blobs, AS_AVERAGE },
{"blobs-as-distinct", no_argument, &show_blobs, AS_DISTINCT },
{"blobs-by-rgb", no_argument, &differ_blobs, BY_RGB },
{"blobs-by-hsp", no_argument, &differ_blobs, BY_HSP },
{"brief", no_argument, &verbose, 0 },
{"keep-background", no_argument, &keep_background, 1 },
// These options don't set a flag. We distinguish them by their indices:
{"blob-feather", required_argument, 0, 'E'},
{"blob-rgba-weight", required_argument, 0, 'c'},
{"blob-size-weight", required_argument, 0, 'z'},
{"blob-xy-weight", required_argument, 0, 'p'},
{"blobs", required_argument, 0, 'b'},
{"blobs-max-size", required_argument, 0, 'B'},
{"blobs-min-size", required_argument, 0, 'm'},
{"blobs-box-grip", required_argument, 0, 'g'},
{"blobs-box-samples", required_argument, 0, 'S'},
{"blobs-threshold", required_argument, 0, 't'},
{"cycle-length", required_argument, 0, 'C'},
{"degenerate", required_argument, 0, 'd'},
{"density", required_argument, 0, 'D'},
{"fading-none", no_argument, &fading, FADING_NONE },
{"fading-linear", no_argument, &fading, FADING_LINEAR },
{"fading-cosine", no_argument, &fading, FADING_COSINE },
{"fading-perlin", no_argument, &fading, FADING_PERLIN },
{"file", required_argument, 0, 'f'},
{"fluid", required_argument, 0, 'L'},
{"frames", required_argument, 0, 'F'},
{"help", no_argument, 0, 'h'},
{"indir", required_argument, 0, 'i'},
{"match-time", required_argument, 0, 'M'},
{"morph-time", required_argument, 0, 'O'},
{"motion-none", no_argument, &motion, MOTION_NONE },
{"motion-linear", no_argument, &motion, MOTION_LINEAR },
{"motion-spline", no_argument, &motion, MOTION_SPLINE },
{"outdir", required_argument, 0, 'o'},
{"seed", required_argument, 0, 's'},
{"threads", required_argument, 0, 'T'},
{"version", no_argument, 0, 'v'},
{0, 0, 0, 0 }
};
// getopt_long stores the option index here.
int option_index = 0;
c = getopt_long(argc, argv, "b:B:c:C:d:D:E:m:M:O:g:p:S:t:T:f:L:F:hi:o:s:vz:", long_options, &option_index);
/* Detect the end of the options. */
if (c == -1) break;
switch (c) {
case 0: // If this option set a flag, do nothing else now.
if (long_options[option_index].flag != 0) break;
printf ("option %s", long_options[option_index].name);
if (optarg) printf(" with arg %s", optarg); printf ("\n");
break;
case 'E': feather = atoi(optarg); break;
case 'c': blob_rgba_weight= std::min(atoi(optarg),255); break;
case 'z': blob_size_weight= std::min(atoi(optarg),255); break;
case 'p': blob_xy_weight = std::min(atoi(optarg),255); break;
case 'b': blob_number = (size_t) atoi(optarg); break;
case 'B': blob_max_size = atoi(optarg); break;
case 'C': cycle_length = atoi(optarg); break;
case 'd': degenerate = (size_t) atoi(optarg); break;
case 'D': density = (uint16_t) atoi(optarg); break;
case 'm': blob_min_size = atoi(optarg); break;
case 'M': match_time = atoi(optarg); break;
case 'O': morph_time = atoi(optarg); break;
case 'g': blob_box_grip = atoi(optarg); break;
case 'S': blob_box_samples= atoi(optarg); break;
case 't': blob_threshold = std::min(atoi(optarg),255)/255.0; break;
case 's': seed = atoi(optarg); break;
case 'T': threads = std::min(atoi(optarg),1024); break;
case 'L': fluid = atoi(optarg); break;
case 'F': frames_out = atoi(optarg); break;
case 'i': indir = optarg; break;
case 'o': outdir = optarg; break;
case 'f': file = optarg; break;
case 'h': print_usage(stdout); exit_flag = 1; break;
case 'v':
printf("AtoMorph %s Copyright (C) 2013-2014 Erich Erstu\n", am::get_version());
exit_flag = 1;
break;
case '?':
/* getopt_long already printed an error message. */
break;
default: return false;
}
}
while (optind < argc) {
files.push_back(argv[optind++]);
}
if (frames_out == 0) {
if (files.size() > 1) frames_out = files.size()*2;
else frames_out = 1;
}
return true;
}
};

9
demo/tests/README Normal file
View File

@ -0,0 +1,9 @@
ImageMagick is needed for these tests to run properly:
sudo apt-get update
sudo apt-get install imagemagick --fix-missing
The completion of all of the tests requires approximately 10 minutes.
However, some of the individual tests finish within seconds. Feel free
to examine the test scripts and modify the parameters.

BIN
demo/tests/data/RGB_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

BIN
demo/tests/data/RGB_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B

BIN
demo/tests/data/RGB_3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 B

BIN
demo/tests/data/RGB_4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 417 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 942 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 925 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 932 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 999 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Some files were not shown because too many files have changed in this diff Show More