Initial commit.
45
demo/Makefile
Normal 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
@@ -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.
|
||||
48
demo/allegro5/AtoMorph-GUI.cbp
Normal 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
@@ -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
@@ -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
561
demo/allegro5/main.cpp
Normal 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
@@ -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
@@ -0,0 +1,4 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
||||
6260
demo/lodepng.cpp
Normal file
1716
demo/lodepng.h
Normal file
299
demo/main.cpp
Normal 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
@@ -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
@@ -0,0 +1,4 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
||||
226
demo/options.h
Normal 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
@@ -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
|
After Width: | Height: | Size: 628 B |
BIN
demo/tests/data/RGB_2.png
Normal file
|
After Width: | Height: | Size: 402 B |
BIN
demo/tests/data/RGB_3.png
Normal file
|
After Width: | Height: | Size: 385 B |
BIN
demo/tests/data/RGB_4.png
Normal file
|
After Width: | Height: | Size: 417 B |
BIN
demo/tests/data/armors/a-0.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
demo/tests/data/armors/a-1.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
demo/tests/data/armors/a-10.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
demo/tests/data/armors/a-11.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
demo/tests/data/armors/a-12.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
demo/tests/data/armors/a-13.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
demo/tests/data/armors/a-14.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
demo/tests/data/armors/a-15.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
demo/tests/data/armors/a-16.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
demo/tests/data/armors/a-17.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
demo/tests/data/armors/a-18.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
demo/tests/data/armors/a-19.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
demo/tests/data/armors/a-2.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
demo/tests/data/armors/a-3.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
demo/tests/data/armors/a-4.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
demo/tests/data/armors/a-5.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
demo/tests/data/armors/a-6.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
demo/tests/data/armors/a-7.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
demo/tests/data/armors/a-8.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
demo/tests/data/armors/a-9.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
demo/tests/data/battlelord/body_1.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
demo/tests/data/battlelord/body_2.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
demo/tests/data/battlelord/body_3.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
demo/tests/data/battlelord/body_4.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
demo/tests/data/battlelord/body_5.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
demo/tests/data/battlelord/body_6.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
demo/tests/data/battlelord/head_1.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
demo/tests/data/battlelord/head_2.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
demo/tests/data/battlelord/head_3.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
demo/tests/data/battlelord/head_4.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
demo/tests/data/battlelord/head_5.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
demo/tests/data/battlelord/head_6.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
demo/tests/data/battlelord/lfoot_1.png
Normal file
|
After Width: | Height: | Size: 942 B |
BIN
demo/tests/data/battlelord/lfoot_2.png
Normal file
|
After Width: | Height: | Size: 925 B |
BIN
demo/tests/data/battlelord/lfoot_3.png
Normal file
|
After Width: | Height: | Size: 932 B |
BIN
demo/tests/data/battlelord/lfoot_4.png
Normal file
|
After Width: | Height: | Size: 999 B |
BIN
demo/tests/data/battlelord/lfoot_5.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
demo/tests/data/battlelord/lfoot_6.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
demo/tests/data/battlelord/lleg_1.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
demo/tests/data/battlelord/lleg_2.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
demo/tests/data/battlelord/lleg_3.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
demo/tests/data/battlelord/lleg_4.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
demo/tests/data/battlelord/lleg_5.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
demo/tests/data/battlelord/lleg_6.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
demo/tests/data/battlelord/rarm_1.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
demo/tests/data/battlelord/rarm_2.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
demo/tests/data/battlelord/rarm_3.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
demo/tests/data/battlelord/rarm_4.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
demo/tests/data/battlelord/rarm_5.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
demo/tests/data/battlelord/rarm_6.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
demo/tests/data/battlelord/rfoot_1.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
demo/tests/data/battlelord/rfoot_2.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
demo/tests/data/battlelord/rfoot_3.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
demo/tests/data/battlelord/rfoot_4.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
demo/tests/data/battlelord/rfoot_5.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
demo/tests/data/battlelord/rfoot_6.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
demo/tests/data/battlelord/rhand_1.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
demo/tests/data/battlelord/rhand_2.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
demo/tests/data/battlelord/rhand_3.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
demo/tests/data/battlelord/rhand_4.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
demo/tests/data/battlelord/rhand_5.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
demo/tests/data/battlelord/rhand_6.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
demo/tests/data/battlelord/rleg_1.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
demo/tests/data/battlelord/rleg_2.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
demo/tests/data/battlelord/rleg_3.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
demo/tests/data/battlelord/rleg_4.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
demo/tests/data/battlelord/rleg_5.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
demo/tests/data/battlelord/rleg_6.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
demo/tests/data/battlelord_1.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
demo/tests/data/battlelord_2.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
demo/tests/data/battlelord_3.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
demo/tests/data/battlelord_4.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
demo/tests/data/battlelord_5.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
demo/tests/data/battlelord_6.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
demo/tests/data/cat_1.png
Normal file
|
After Width: | Height: | Size: 721 B |
BIN
demo/tests/data/cat_2.png
Normal file
|
After Width: | Height: | Size: 730 B |
BIN
demo/tests/data/cat_3.png
Normal file
|
After Width: | Height: | Size: 710 B |
BIN
demo/tests/data/cat_4.png
Normal file
|
After Width: | Height: | Size: 725 B |
BIN
demo/tests/data/cat_5.png
Normal file
|
After Width: | Height: | Size: 713 B |
BIN
demo/tests/data/cat_6.png
Normal file
|
After Width: | Height: | Size: 704 B |