562 lines
21 KiB
C++
562 lines
21 KiB
C++
/*
|
|
* 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;
|
|
}
|