# HG changeset patch # User John Tsiombikas # Date 1349564640 -10800 # Node ID 2560a7ab024356731182ca0fd29494cef0838bc3 # Parent fc2b3d06d07c00daefcc39f77e8b4d9fa17d9922 internalized libanim, libimago2, and libpsys diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/Makefile.in --- a/prototype/Makefile.in Tue Oct 02 13:42:18 2012 +0300 +++ b/prototype/Makefile.in Sun Oct 07 02:04:00 2012 +0300 @@ -1,5 +1,8 @@ csrc = $(wildcard src/*.c) \ $(wildcard vmath/*.c) \ + $(wildcard imago2/*.c) \ + $(wildcard anim/*.c) \ + $(wildcard psys/*.c) \ $(wildcard drawtext/*.c) \ $(wildcard kdtree/*.c) @@ -13,13 +16,16 @@ warn = -Wall -Wno-format-extra-args -Wno-char-subscripts -inc = -I. -Isrc -Ivmath -Idrawtext -Ikdtree `pkg-config --cflags freetype2` +inc = -I. -Isrc -Ivmath -Iimago2 -Idrawtext -Ikdtree `pkg-config --cflags freetype2` CFLAGS = -pedantic -fno-strict-aliasing $(warn) $(dbg) $(prof) $(opt) $(inc) CXXFLAGS = $(CFLAGS) $(cxx11_cflags) -LDFLAGS = $(cxx11_ldflags) $(prof) $(libgl) $(libal) -lvorbisfile -lm -lassimp -limago -lpsys `pkg-config --libs freetype2` +LDFLAGS = $(cxx11_ldflags) $(prof) $(libgl) $(libal) -lvorbisfile -lm -lassimp \ + -ljpeg -lpng -lz `pkg-config --libs freetype2` ifeq ($(shell uname -s), Darwin) + CC = clang + CXX = clang++ libgl = -framework OpenGL -framework GLUT -lglew libal = -framework OpenAL else diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/anim/anim.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/anim/anim.c Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,394 @@ +#include +#include +#include "anim.h" +#include "dynarr.h" + +int anm_init_node(struct anm_node *node) +{ + int i, j; + static const float defaults[] = { + 0.0f, 0.0f, 0.0f, /* default position */ + 0.0f, 0.0f, 0.0f, 1.0f, /* default rotation quat */ + 1.0f, 1.0f, 1.0f /* default scale factor */ + }; + + memset(node, 0, sizeof *node); + + for(i=0; itracks + i) == -1) { + for(j=0; jtracks + i); + } + } + anm_set_track_default(node->tracks + i, defaults[i]); + } + return 0; +} + +void anm_destroy_node(struct anm_node *node) +{ + int i; + free(node->name); + + for(i=0; itracks + i); + } +} + +void anm_destroy_node_tree(struct anm_node *tree) +{ + struct anm_node *c, *tmp; + + if(!tree) return; + + c = tree->child; + while(c) { + tmp = c; + c = c->next; + + anm_destroy_node_tree(tmp); + } + anm_destroy_node(tree); +} + +struct anm_node *anm_create_node(void) +{ + struct anm_node *n; + + if((n = malloc(sizeof *n))) { + if(anm_init_node(n) == -1) { + free(n); + return 0; + } + } + return n; +} + +void anm_free_node(struct anm_node *node) +{ + anm_destroy_node(node); + free(node); +} + +void anm_free_node_tree(struct anm_node *tree) +{ + struct anm_node *c, *tmp; + + if(!tree) return; + + c = tree->child; + while(c) { + tmp = c; + c = c->next; + + anm_free_node_tree(tmp); + } + + anm_free_node(tree); +} + +int anm_set_node_name(struct anm_node *node, const char *name) +{ + char *str; + + if(!(str = malloc(strlen(name) + 1))) { + return -1; + } + strcpy(str, name); + free(node->name); + node->name = str; + return 0; +} + +const char *anm_get_node_name(struct anm_node *node) +{ + return node->name ? node->name : ""; +} + +void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in) +{ + int i; + + for(i=0; itracks + i, in); + } +} + +void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex) +{ + int i; + + for(i=0; itracks + i, ex); + } +} + +void anm_link_node(struct anm_node *p, struct anm_node *c) +{ + c->next = p->child; + p->child = c; + + c->parent = p; +} + +int anm_unlink_node(struct anm_node *p, struct anm_node *c) +{ + struct anm_node *iter; + + if(p->child == c) { + p->child = c->next; + c->next = 0; + return 0; + } + + iter = p->child; + while(iter->next) { + if(iter->next == c) { + iter->next = c->next; + c->next = 0; + return 0; + } + } + return -1; +} + +void anm_set_position(struct anm_node *node, vec3_t pos, anm_time_t tm) +{ + anm_set_value(node->tracks + ANM_TRACK_POS_X, tm, pos.x); + anm_set_value(node->tracks + ANM_TRACK_POS_Y, tm, pos.y); + anm_set_value(node->tracks + ANM_TRACK_POS_Z, tm, pos.z); +} + +vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm) +{ + vec3_t v; + v.x = anm_get_value(node->tracks + ANM_TRACK_POS_X, tm); + v.y = anm_get_value(node->tracks + ANM_TRACK_POS_Y, tm); + v.z = anm_get_value(node->tracks + ANM_TRACK_POS_Z, tm); + return v; +} + +void anm_set_rotation(struct anm_node *node, quat_t rot, anm_time_t tm) +{ + anm_set_value(node->tracks + ANM_TRACK_ROT_X, tm, rot.x); + anm_set_value(node->tracks + ANM_TRACK_ROT_Y, tm, rot.y); + anm_set_value(node->tracks + ANM_TRACK_ROT_Z, tm, rot.z); + anm_set_value(node->tracks + ANM_TRACK_ROT_W, tm, rot.w); +} + +quat_t anm_get_node_rotation(struct anm_node *node, anm_time_t tm) +{ + int idx0, idx1, last_idx; + anm_time_t tstart, tend; + float t, dt; + struct anm_track *track_x, *track_y, *track_z, *track_w; + quat_t q, q1, q2; + + track_x = node->tracks + ANM_TRACK_ROT_X; + track_y = node->tracks + ANM_TRACK_ROT_Y; + track_z = node->tracks + ANM_TRACK_ROT_Z; + track_w = node->tracks + ANM_TRACK_ROT_W; + + if(!track_x->count) { + q.x = track_x->def_val; + q.y = track_y->def_val; + q.z = track_z->def_val; + q.w = track_w->def_val; + return q; + } + + last_idx = track_x->count - 1; + + tstart = track_x->keys[0].time; + tend = track_x->keys[last_idx].time; + tm = anm_remap_time(track_x, tm, tstart, tend); + + idx0 = anm_get_key_interval(track_x, tm); + assert(idx0 >= 0 && idx0 < track_x->count); + idx1 = idx0 + 1; + + dt = (float)(track_x->keys[idx1].time - track_x->keys[idx0].time); + t = (float)(tm - track_x->keys[idx0].time) / dt; + + q1.x = track_x->keys[idx0].val; + q1.y = track_y->keys[idx0].val; + q1.z = track_z->keys[idx0].val; + q1.w = track_w->keys[idx0].val; + + q2.x = track_x->keys[idx1].val; + q2.y = track_y->keys[idx1].val; + q2.z = track_z->keys[idx1].val; + q2.w = track_w->keys[idx1].val; + + return quat_slerp(q1, q2, t); +} + +void anm_set_scaling(struct anm_node *node, vec3_t scl, anm_time_t tm) +{ + anm_set_value(node->tracks + ANM_TRACK_SCL_X, tm, scl.x); + anm_set_value(node->tracks + ANM_TRACK_SCL_Y, tm, scl.y); + anm_set_value(node->tracks + ANM_TRACK_SCL_Z, tm, scl.z); +} + +vec3_t anm_get_node_scaling(struct anm_node *node, anm_time_t tm) +{ + vec3_t v; + v.x = anm_get_value(node->tracks + ANM_TRACK_SCL_X, tm); + v.y = anm_get_value(node->tracks + ANM_TRACK_SCL_Y, tm); + v.z = anm_get_value(node->tracks + ANM_TRACK_SCL_Z, tm); + return v; +} + + +vec3_t anm_get_position(struct anm_node *node, anm_time_t tm) +{ + mat4_t xform; + vec3_t pos = {0.0, 0.0, 0.0}; + + if(!node->parent) { + return anm_get_node_position(node, tm); + } + + anm_get_matrix(node, xform, tm); + return v3_transform(pos, xform); +} + +quat_t anm_get_rotation(struct anm_node *node, anm_time_t tm) +{ + quat_t rot, prot; + rot = anm_get_node_rotation(node, tm); + + if(!node->parent) { + return rot; + } + + prot = anm_get_rotation(node->parent, tm); + return quat_mul(prot, rot); +} + +vec3_t anm_get_scaling(struct anm_node *node, anm_time_t tm) +{ + vec3_t s, ps; + s = anm_get_node_scaling(node, tm); + + if(!node->parent) { + return s; + } + + ps = anm_get_scaling(node->parent, tm); + return v3_mul(s, ps); +} + +void anm_set_pivot(struct anm_node *node, vec3_t piv) +{ + node->pivot = piv; +} + +vec3_t anm_get_pivot(struct anm_node *node) +{ + return node->pivot; +} + +void anm_get_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm) +{ +#ifdef ANIM_THREAD_SAFE + /* XXX we're holding the mutex for way too anm_time_t... but it looks like the + * alternative would be to lock/unlock twice which might be worse. + */ + pthread_mutex_lock(node->cache_mutex); +#endif + + if(node->cache_time != tm) { + mat4_t tmat, rmat, smat, pivmat, neg_pivmat; + vec3_t pos, scale; + quat_t rot; + + m4_identity(tmat); + /*no need to m4_identity(rmat); quat_to_mat4 sets this properly */ + m4_identity(smat); + m4_identity(pivmat); + m4_identity(neg_pivmat); + + pos = anm_get_node_position(node, tm); + rot = anm_get_node_rotation(node, tm); + scale = anm_get_node_scaling(node, tm); + + m4_translate(pivmat, node->pivot.x, node->pivot.y, node->pivot.z); + m4_translate(neg_pivmat, -node->pivot.x, -node->pivot.y, -node->pivot.z); + + m4_translate(tmat, pos.x, pos.y, pos.z); + quat_to_mat4(rmat, rot); + m4_translate(smat, scale.x, scale.y, scale.z); + + /* ok this would look nicer in C++ */ + m4_mult(node->cache_matrix, pivmat, tmat); + m4_mult(node->cache_matrix, node->cache_matrix, rmat); + m4_mult(node->cache_matrix, node->cache_matrix, smat); + m4_mult(node->cache_matrix, node->cache_matrix, neg_pivmat); + + if(node->parent) { + mat4_t parent_mat; + + anm_get_matrix(node->parent, mat, tm); + m4_mult(node->cache_matrix, parent_mat, node->cache_matrix); + } + node->cache_time = tm; + } + m4_copy(mat, node->cache_matrix); + +#ifdef ANIM_THREAD_SAFE + pthread_mutex_unlock(node->cache_mutex); +#endif +} + +anm_time_t anm_get_start_time(struct anm_node *node) +{ + int i; + struct anm_node *c; + anm_time_t res = LONG_MAX; + + for(i=0; itracks[i].count) { + anm_time_t tm = node->tracks[i].keys[0].time; + if(tm < res) { + res = tm; + } + } + } + + c = node->child; + while(c) { + anm_time_t tm = anm_get_start_time(c); + if(tm < res) { + res = tm; + } + c = c->next; + } + return res; +} + +anm_time_t anm_get_end_time(struct anm_node *node) +{ + int i; + struct anm_node *c; + anm_time_t res = LONG_MIN; + + for(i=0; itracks[i].count) { + anm_time_t tm = node->tracks[i].keys[node->tracks[i].count - 1].time; + if(tm > res) { + res = tm; + } + } + } + + c = node->child; + while(c) { + anm_time_t tm = anm_get_end_time(c); + if(tm > res) { + res = tm; + } + c = c->next; + } + return res; +} diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/anim/anim.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/anim/anim.h Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,99 @@ +#ifndef LIBANIM_H_ +#define LIBANIM_H_ + +#include "config.h" + +#ifdef ANIM_THREAD_SAFE +#include +#endif + +#include +#include "track.h" + +enum { + ANM_TRACK_POS_X, + ANM_TRACK_POS_Y, + ANM_TRACK_POS_Z, + + ANM_TRACK_ROT_X, + ANM_TRACK_ROT_Y, + ANM_TRACK_ROT_Z, + ANM_TRACK_ROT_W, + + ANM_TRACK_SCL_X, + ANM_TRACK_SCL_Y, + ANM_TRACK_SCL_Z, + + ANM_NUM_TRACKS +}; + +struct anm_node { + char *name; + + struct anm_track tracks[ANM_NUM_TRACKS]; + vec3_t pivot; + + /* matrix cache */ + mat4_t cache_matrix; + anm_time_t cache_time; +#ifdef ANIM_THREAD_SAFE + pthread_mutex_t cache_mutex; +#endif + + struct anm_node *parent; + struct anm_node *child; + struct anm_node *next; +}; + + +/* node constructor and destructor */ +int anm_init_node(struct anm_node *node); +void anm_destroy_node(struct anm_node *node); + +/* recursively destroy an animation node tree */ +void anm_destroy_node_tree(struct anm_node *tree); + +/* helper functions to allocate/construct and destroy/free with + * a single call. They call anm_init_node and anm_destroy_node + * internally. + */ +struct anm_node *anm_create_node(void); +void anm_free_node(struct anm_node *node); + +/* recursively destroy and free the nodes of a node tree */ +void anm_free_node_tree(struct anm_node *tree); + +int anm_set_node_name(struct anm_node *node, const char *name); +const char *anm_get_node_name(struct anm_node *node); + +void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in); +void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex); + +/* link and unlink nodes with parent/child relations */ +void anm_link_node(struct anm_node *parent, struct anm_node *child); +int anm_unlink_node(struct anm_node *parent, struct anm_node *child); + +void anm_set_position(struct anm_node *node, vec3_t pos, anm_time_t tm); +vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm); + +void anm_set_rotation(struct anm_node *node, quat_t rot, anm_time_t tm); +quat_t anm_get_node_rotation(struct anm_node *node, anm_time_t tm); + +void anm_set_scaling(struct anm_node *node, vec3_t scl, anm_time_t tm); +vec3_t anm_get_node_scaling(struct anm_node *node, anm_time_t tm); + +/* these three return the full p/r/s taking hierarchy into account */ +vec3_t anm_get_position(struct anm_node *node, anm_time_t tm); +quat_t anm_get_rotation(struct anm_node *node, anm_time_t tm); +vec3_t anm_get_scaling(struct anm_node *node, anm_time_t tm); + +void anm_set_pivot(struct anm_node *node, vec3_t pivot); +vec3_t anm_get_pivot(struct anm_node *node); + +void anm_get_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm); + +/* those return the start and end times of the whole tree */ +anm_time_t anm_get_start_time(struct anm_node *node); +anm_time_t anm_get_end_time(struct anm_node *node); + +#endif /* LIBANIM_H_ */ diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/anim/config.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/anim/config.h Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,6 @@ +#ifndef ANIM_CONFIG_H_ +#define ANIM_CONFIG_H_ + +#undef ANIM_THREAD_SAFE + +#endif /* ANIM_CONFIG_H_ */ diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/anim/dynarr.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/anim/dynarr.c Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,122 @@ +#include +#include +#include +#include "dynarr.h" + +/* The array descriptor keeps auxilliary information needed to manipulate + * the dynamic array. It's allocated adjacent to the array buffer. + */ +struct arrdesc { + int nelem, szelem; + int max_elem; + int bufsz; /* not including the descriptor */ +}; + +#define DESC(x) ((struct arrdesc*)((char*)(x) - sizeof(struct arrdesc))) + +void *dynarr_alloc(int elem, int szelem) +{ + struct arrdesc *desc; + + if(!(desc = malloc(elem * szelem + sizeof *desc))) { + return 0; + } + desc->nelem = desc->max_elem = elem; + desc->szelem = szelem; + desc->bufsz = elem * szelem; + return (char*)desc + sizeof *desc; +} + +void dynarr_free(void *da) +{ + if(da) { + free(DESC(da)); + } +} + +void *dynarr_resize(void *da, int elem) +{ + int newsz; + void *tmp; + struct arrdesc *desc; + + if(!da) return 0; + desc = DESC(da); + + newsz = desc->szelem * elem; + + if(!(tmp = realloc(desc, newsz + sizeof *desc))) { + return 0; + } + desc = tmp; + + desc->nelem = desc->max_elem = elem; + desc->bufsz = newsz; + return (char*)desc + sizeof *desc; +} + +int dynarr_empty(void *da) +{ + return DESC(da)->nelem ? 0 : 1; +} + +int dynarr_size(void *da) +{ + return DESC(da)->nelem; +} + + +/* stack semantics */ +void *dynarr_push(void *da, void *item) +{ + struct arrdesc *desc; + int nelem; + + desc = DESC(da); + nelem = desc->nelem; + + if(nelem >= desc->max_elem) { + /* need to resize */ + struct arrdesc *tmp; + int newsz = desc->max_elem ? desc->max_elem * 2 : 1; + + if(!(tmp = dynarr_resize(da, newsz))) { + fprintf(stderr, "failed to resize\n"); + return da; + } + da = tmp; + desc = DESC(da); + desc->nelem = nelem; + } + + memcpy((char*)da + desc->nelem++ * desc->szelem, item, desc->szelem); + return da; +} + +void *dynarr_pop(void *da) +{ + struct arrdesc *desc; + int nelem; + + desc = DESC(da); + nelem = desc->nelem; + + if(!nelem) return da; + + if(nelem <= desc->max_elem / 3) { + /* reclaim space */ + struct arrdesc *tmp; + int newsz = desc->max_elem / 2; + + if(!(tmp = dynarr_resize(da, newsz))) { + fprintf(stderr, "failed to resize\n"); + return da; + } + da = tmp; + desc = DESC(da); + desc->nelem = nelem; + } + desc->nelem--; + + return da; +} diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/anim/dynarr.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/anim/dynarr.h Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,16 @@ +#ifndef DYNARR_H_ +#define DYNARR_H_ + +void *dynarr_alloc(int elem, int szelem); +void dynarr_free(void *da); +void *dynarr_resize(void *da, int elem); + +int dynarr_empty(void *da); +int dynarr_size(void *da); + +/* stack semantics */ +void *dynarr_push(void *da, void *item); +void *dynarr_pop(void *da); + + +#endif /* DYNARR_H_ */ diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/anim/track.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/anim/track.c Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,272 @@ +#include +#include +#include +#include "track.h" +#include "dynarr.h" + +static int keycmp(const void *a, const void *b); +static int find_prev_key(struct anm_keyframe *arr, int start, int end, anm_time_t tm); + +static float interp_step(float v0, float v1, float v2, float v3, float t); +static float interp_linear(float v0, float v1, float v2, float v3, float t); +static float interp_cubic(float v0, float v1, float v2, float v3, float t); + +static anm_time_t remap_extend(anm_time_t tm, anm_time_t start, anm_time_t end); +static anm_time_t remap_clamp(anm_time_t tm, anm_time_t start, anm_time_t end); +static anm_time_t remap_repeat(anm_time_t tm, anm_time_t start, anm_time_t end); + +/* XXX keep this in sync with enum anm_interpolator at track.h */ +static float (*interp[])(float, float, float, float, float) = { + interp_step, + interp_linear, + interp_cubic, + 0 +}; + +/* XXX keep this in sync with enum anm_extrapolator at track.h */ +static anm_time_t (*remap_time[])(anm_time_t, anm_time_t, anm_time_t) = { + remap_extend, + remap_clamp, + remap_repeat, + 0 +}; + +int anm_init_track(struct anm_track *track) +{ + memset(track, 0, sizeof *track); + + if(!(track->keys = dynarr_alloc(0, sizeof *track->keys))) { + return -1; + } + track->interp = ANM_INTERP_LINEAR; + track->extrap = ANM_EXTRAP_CLAMP; + return 0; +} + +void anm_destroy_track(struct anm_track *track) +{ + dynarr_free(track->keys); +} + +struct anm_track *anm_create_track(void) +{ + struct anm_track *track; + + if((track = malloc(sizeof *track))) { + if(anm_init_track(track) == -1) { + free(track); + return 0; + } + } + return track; +} + +void anm_free_track(struct anm_track *track) +{ + anm_destroy_track(track); + free(track); +} + +int anm_set_track_name(struct anm_track *track, const char *name) +{ + char *tmp; + + if(!(tmp = malloc(strlen(name) + 1))) { + return -1; + } + free(track->name); + track->name = tmp; + return 0; +} + +const char *anm_get_track_name(struct anm_track *track) +{ + return track->name; +} + +void anm_set_track_interpolator(struct anm_track *track, enum anm_interpolator in) +{ + track->interp = in; +} + +void anm_set_track_extrapolator(struct anm_track *track, enum anm_extrapolator ex) +{ + track->extrap = ex; +} + +anm_time_t anm_remap_time(struct anm_track *track, anm_time_t tm, anm_time_t start, anm_time_t end) +{ + return remap_time[track->extrap](tm, start, end); +} + +void anm_set_track_default(struct anm_track *track, float def) +{ + track->def_val = def; +} + +int anm_set_keyframe(struct anm_track *track, struct anm_keyframe *key) +{ + int idx = anm_get_key_interval(track, key->time); + + /* if we got a valid keyframe index, compare them... */ + if(idx >= 0 && idx < track->count && keycmp(key, track->keys + idx) == 0) { + /* ... it's the same key, just update the value */ + track->keys[idx].val = key->val; + } else { + /* ... it's a new key, add it and re-sort them */ + void *tmp; + if(!(tmp = dynarr_push(track->keys, key))) { + return -1; + } + track->keys = tmp; + /* TODO lazy qsort */ + qsort(track->keys, ++track->count, sizeof *track->keys, keycmp); + } + return 0; +} + +static int keycmp(const void *a, const void *b) +{ + return ((struct anm_keyframe*)a)->time - ((struct anm_keyframe*)b)->time; +} + +struct anm_keyframe *anm_get_keyframe(struct anm_track *track, int idx) +{ + if(idx < 0 || idx >= track->count) { + return 0; + } + return track->keys + idx; +} + +int anm_get_key_interval(struct anm_track *track, anm_time_t tm) +{ + int last; + + if(!track->count || tm < track->keys[0].time) { + return -1; + } + + last = track->count - 1; + if(tm > track->keys[last].time) { + return last; + } + + return find_prev_key(track->keys, 0, last, tm); +} + +static int find_prev_key(struct anm_keyframe *arr, int start, int end, anm_time_t tm) +{ + int mid; + + if(end - start <= 1) { + return start; + } + + mid = (start + end) / 2; + if(tm < arr[mid].time) { + return find_prev_key(arr, start, mid, tm); + } + if(tm > arr[mid].time) { + return find_prev_key(arr, mid, end, tm); + } + return mid; +} + +int anm_set_value(struct anm_track *track, anm_time_t tm, float val) +{ + struct anm_keyframe key; + key.time = tm; + key.val = val; + + return anm_set_keyframe(track, &key); +} + +float anm_get_value(struct anm_track *track, anm_time_t tm) +{ + int idx0, idx1, last_idx; + anm_time_t tstart, tend; + float t, dt; + float v0, v1, v2, v3; + + if(!track->count) { + return track->def_val; + } + + last_idx = track->count - 1; + + tstart = track->keys[0].time; + tend = track->keys[last_idx].time; + + if(tstart == tend) { + return track->keys[0].val; + } + + tm = remap_time[track->extrap](tm, tstart, tend); + + idx0 = anm_get_key_interval(track, tm); + assert(idx0 >= 0 && idx0 < track->count); + idx1 = idx0 + 1; + + if(idx0 == last_idx) { + return track->keys[idx0].val; + } + + dt = (float)(track->keys[idx1].time - track->keys[idx0].time); + t = (float)(tm - track->keys[idx0].time) / dt; + + v1 = track->keys[idx0].val; + v2 = track->keys[idx1].val; + + /* get the neigboring values to allow for cubic interpolation */ + v0 = idx0 > 0 ? track->keys[idx0 - 1].val : v1; + v3 = idx1 < last_idx ? track->keys[idx1 + 1].val : v2; + + return interp[track->interp](v0, v1, v2, v3, t); +} + + +static float interp_step(float v0, float v1, float v2, float v3, float t) +{ + return v1; +} + +static float interp_linear(float v0, float v1, float v2, float v3, float t) +{ + return v1 + (v2 - v1) * t; +} + +static float interp_cubic(float a, float b, float c, float d, float t) +{ + float x, y, z, w; + float tsq = t * t; + + x = -a + 3.0 * b - 3.0 * c + d; + y = 2.0 * a - 5.0 * b + 4.0 * c - d; + z = c - a; + w = 2.0 * b; + + return 0.5 * (x * tsq * t + y * tsq + z * t + w); +} + +static anm_time_t remap_extend(anm_time_t tm, anm_time_t start, anm_time_t end) +{ + return remap_repeat(tm, start, end); +} + +static anm_time_t remap_clamp(anm_time_t tm, anm_time_t start, anm_time_t end) +{ + return tm < start ? start : (tm >= end ? end - 1 : tm); +} + +static anm_time_t remap_repeat(anm_time_t tm, anm_time_t start, anm_time_t end) +{ + anm_time_t interv = end - start; + + if(tm < start) { + while(tm < start) { + tm += interv; + } + return tm; + } + return (tm - start) % interv + start; +} diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/anim/track.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/anim/track.h Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,88 @@ +/* An animation track defines the values of a single scalar over time + * and supports various interpolation and extrapolation modes. + */ +#ifndef LIBANIM_TRACK_H_ +#define LIBANIM_TRACK_H_ + +#include +#include "config.h" + +enum anm_interpolator { + ANM_INTERP_STEP, + ANM_INTERP_LINEAR, + ANM_INTERP_CUBIC +}; + +enum anm_extrapolator { + ANM_EXTRAP_EXTEND, /* extend to infinity */ + ANM_EXTRAP_CLAMP, /* clamp to last value */ + ANM_EXTRAP_REPEAT /* repeat motion */ +}; + +typedef long anm_time_t; +#define ANM_TIME_INVAL LONG_MIN + +#define ANM_SEC2TM(x) ((anm_time_t)((x) * 1000)) +#define ANM_MSEC2TM(x) ((anm_time_t)(x)) +#define ANM_TM2SEC(x) ((x) / 1000.0) +#define ANM_TM2MSEC(x) (x) + +struct anm_keyframe { + anm_time_t time; + float val; +}; + +struct anm_track { + char *name; + int count; + struct anm_keyframe *keys; + + float def_val; + + enum anm_interpolator interp; + enum anm_extrapolator extrap; +}; + +/* track constructor and destructor */ +int anm_init_track(struct anm_track *track); +void anm_destroy_track(struct anm_track *track); + +/* helper functions that use anm_init_track and anm_destroy_track internally */ +struct anm_track *anm_create_track(void); +void anm_free_track(struct anm_track *track); + +int anm_set_track_name(struct anm_track *track, const char *name); +const char *anm_get_track_name(struct anm_track *track); + +void anm_set_track_interpolator(struct anm_track *track, enum anm_interpolator in); +void anm_set_track_extrapolator(struct anm_track *track, enum anm_extrapolator ex); + +anm_time_t anm_remap_time(struct anm_track *track, anm_time_t tm, anm_time_t start, anm_time_t end); + +void anm_set_track_default(struct anm_track *track, float def); + +/* set or update a keyframe */ +int anm_set_keyframe(struct anm_track *track, struct anm_keyframe *key); + +/* get the idx-th keyframe, returns null if it doesn't exist */ +struct anm_keyframe *anm_get_keyframe(struct anm_track *track, int idx); + +/* Finds the 0-based index of the intra-keyframe interval which corresponds + * to the specified time. If the time falls exactly onto the N-th keyframe + * the function returns N. + * + * Special cases: + * - if the time is before the first keyframe -1 is returned. + * - if the time is after the last keyframe, the index of the last keyframe + * is returned. + */ +int anm_get_key_interval(struct anm_track *track, anm_time_t tm); + +int anm_set_value(struct anm_track *track, anm_time_t tm, float val); + +/* evaluates and returns the value of the track for a particular time */ +float anm_get_value(struct anm_track *track, anm_time_t tm); + + + +#endif /* LIBANIM_TRACK_H_ */ diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/data/fire.psys --- a/prototype/data/fire.psys Tue Oct 02 13:42:18 2012 +0300 +++ b/prototype/data/fire.psys Sun Oct 07 02:04:00 2012 +0300 @@ -11,13 +11,13 @@ # center ~ range. If the range is missing, it's assumed to be 0. texture = "fire_particle.png" -rate = 50 +rate = 40 life = 0.9 ~ 0.1 grav = [0 0.3 0] spawn_range = 0.025 -size(0) = 0.2 -size(1s) = 0.075 +size(0) = 0.25 +size(1s) = 0.08 pcolor(0) = [1.0 0.7 0.1] pcolor(1s) = [1.0 0.27 0.15] diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/imago2/conv.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/imago2/conv.c Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,251 @@ +/* +libimago - a multi-format image file input/output library. +Copyright (C) 2010 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include +#include "imago2.h" + +/* pixel-format conversions are sub-optimal at the moment to avoid + * writing a lot of code. optimize at some point ? + */ + +#define CLAMP(x, a, b) ((x) < (a) ? (a) : ((x) > (b) ? (b) : (x))) + +struct pixel { + float r, g, b, a; +}; + +static void unpack_grey8(struct pixel *unp, void *pptr, int count); +static void unpack_rgb24(struct pixel *unp, void *pptr, int count); +static void unpack_rgba32(struct pixel *unp, void *pptr, int count); +static void unpack_greyf(struct pixel *unp, void *pptr, int count); +static void unpack_rgbf(struct pixel *unp, void *pptr, int count); +static void unpack_rgbaf(struct pixel *unp, void *pptr, int count); + +static void pack_grey8(void *pptr, struct pixel *unp, int count); +static void pack_rgb24(void *pptr, struct pixel *unp, int count); +static void pack_rgba32(void *pptr, struct pixel *unp, int count); +static void pack_greyf(void *pptr, struct pixel *unp, int count); +static void pack_rgbf(void *pptr, struct pixel *unp, int count); +static void pack_rgbaf(void *pptr, struct pixel *unp, int count); + +/* XXX keep in sync with enum img_fmt at imago2.h */ +static void (*unpack[])(struct pixel*, void*, int) = { + unpack_grey8, + unpack_rgb24, + unpack_rgba32, + unpack_greyf, + unpack_rgbf, + unpack_rgbaf +}; + +/* XXX keep in sync with enum img_fmt at imago2.h */ +static void (*pack[])(void*, struct pixel*, int) = { + pack_grey8, + pack_rgb24, + pack_rgba32, + pack_greyf, + pack_rgbf, + pack_rgbaf +}; + + +int img_convert(struct img_pixmap *img, enum img_fmt tofmt) +{ + struct pixel pbuf[8]; + int bufsz = (img->width & 7) == 0 ? 8 : ((img->width & 3) == 0 ? 4 : 1); + int i, num_pix = img->width * img->height; + int num_iter = num_pix / bufsz; + char *sptr, *dptr; + struct img_pixmap nimg; + + if(img->fmt == tofmt) { + return 0; /* nothing to do */ + } + + img_init(&nimg); + if(img_set_pixels(&nimg, img->width, img->height, tofmt, 0) == -1) { + img_destroy(&nimg); + return -1; + } + + sptr = img->pixels; + dptr = nimg.pixels; + + for(i=0; ifmt](pbuf, sptr, bufsz); + pack[tofmt](dptr, pbuf, bufsz); + + sptr += bufsz * img->pixelsz; + dptr += bufsz * nimg.pixelsz; + } + + img_copy(img, &nimg); + img_destroy(&nimg); + return 0; +} + +/* the following functions *could* benefit from SIMD */ + +static void unpack_grey8(struct pixel *unp, void *pptr, int count) +{ + int i; + unsigned char *pix = pptr; + + for(i=0; ir = unp->g = unp->b = (float)*pix++ / 255.0; + unp->a = 1.0; + unp++; + } +} + +static void unpack_rgb24(struct pixel *unp, void *pptr, int count) +{ + int i; + unsigned char *pix = pptr; + + for(i=0; ir = (float)*pix++ / 255.0; + unp->g = (float)*pix++ / 255.0; + unp->b = (float)*pix++ / 255.0; + unp->a = 1.0; + unp++; + } +} + +static void unpack_rgba32(struct pixel *unp, void *pptr, int count) +{ + memcpy(unp, pptr, count * sizeof *unp); +} + +static void unpack_greyf(struct pixel *unp, void *pptr, int count) +{ + int i; + float *pix = pptr; + + for(i=0; ir = unp->g = unp->b = *pix++; + unp->a = 1.0; + unp++; + } +} + +static void unpack_rgbf(struct pixel *unp, void *pptr, int count) +{ + int i; + float *pix = pptr; + + for(i=0; ir = *pix++; + unp->g = *pix++; + unp->b = *pix++; + unp->a = 1.0; + unp++; + } +} + +static void unpack_rgbaf(struct pixel *unp, void *pptr, int count) +{ + int i; + float *pix = pptr; + + for(i=0; ir = *pix++; + unp->g = *pix++; + unp->b = *pix++; + unp->a = *pix++; + unp++; + } +} + + +static void pack_grey8(void *pptr, struct pixel *unp, int count) +{ + int i; + unsigned char *pix = pptr; + + for(i=0; ir + unp->g + unp->b) / 3.0); + *pix++ = CLAMP(lum, 0, 255); + unp++; + } +} + +static void pack_rgb24(void *pptr, struct pixel *unp, int count) +{ + int i; + unsigned char *pix = pptr; + + for(i=0; ir * 255.0); + int g = (int)(unp->g * 255.0); + int b = (int)(unp->b * 255.0); + + *pix++ = CLAMP(r, 0, 255); + *pix++ = CLAMP(g, 0, 255); + *pix++ = CLAMP(b, 0, 255); + unp++; + } +} + +static void pack_rgba32(void *pptr, struct pixel *unp, int count) +{ + int i; + unsigned char *pix = pptr; + + for(i=0; ir * 255.0); + int g = (int)(unp->g * 255.0); + int b = (int)(unp->b * 255.0); + int a = (int)(unp->a * 255.0); + + *pix++ = CLAMP(r, 0, 255); + *pix++ = CLAMP(g, 0, 255); + *pix++ = CLAMP(b, 0, 255); + *pix++ = CLAMP(a, 0, 255); + unp++; + } +} + +static void pack_greyf(void *pptr, struct pixel *unp, int count) +{ + int i; + float *pix = pptr; + + for(i=0; ir + unp->g + unp->b) / 3.0; + unp++; + } +} + +static void pack_rgbf(void *pptr, struct pixel *unp, int count) +{ + int i; + float *pix = pptr; + + for(i=0; ir; + *pix++ = unp->g; + *pix++ = unp->b; + unp++; + } +} + +static void pack_rgbaf(void *pptr, struct pixel *unp, int count) +{ + memcpy(pptr, unp, count * sizeof *unp); +} + diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/imago2/file_jpeg.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/imago2/file_jpeg.c Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,293 @@ +/* +libimago - a multi-format image file input/output library. +Copyright (C) 2010 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ + +/* -- JPEG module -- */ + +#include +#include +#include + +#ifdef WIN32 +#include +#define HAVE_BOOLEAN +#endif + +#include +#include "imago2.h" +#include "ftype_module.h" + +#define INPUT_BUF_SIZE 512 +#define OUTPUT_BUF_SIZE 512 + +/* data source manager: adapted from jdatasrc.c */ +struct src_mgr { + struct jpeg_source_mgr pub; + + struct img_io *io; + unsigned char buffer[INPUT_BUF_SIZE]; + int start_of_file; +}; + +/* data destination manager: adapted from jdatadst.c */ +struct dst_mgr { + struct jpeg_destination_mgr pub; + + struct img_io *io; + unsigned char buffer[OUTPUT_BUF_SIZE]; +}; + +static int check(struct img_io *io); +static int read(struct img_pixmap *img, struct img_io *io); +static int write(struct img_pixmap *img, struct img_io *io); + +/* read source functions */ +static void init_source(j_decompress_ptr jd); +static int fill_input_buffer(j_decompress_ptr jd); +static void skip_input_data(j_decompress_ptr jd, long num_bytes); +static void term_source(j_decompress_ptr jd); + +/* write destination functions */ +static void init_destination(j_compress_ptr jc); +static int empty_output_buffer(j_compress_ptr jc); +static void term_destination(j_compress_ptr jc); + +int img_register_jpeg(void) +{ + static struct ftype_module mod = {".jpg", check, read, write}; + return img_register_module(&mod); +} + + +static int check(struct img_io *io) +{ + unsigned char sig[10]; + + long pos = io->seek(0, SEEK_CUR, io->uptr); + + if(io->read(sig, 10, io->uptr) < 10) { + io->seek(pos, SEEK_SET, io->uptr); + return -1; + } + + if(memcmp(sig, "\xff\xd8\xff\xe0", 4) != 0 || memcmp(sig + 6, "JFIF", 4) != 0) { + io->seek(pos, SEEK_SET, io->uptr); + return -1; + } + io->seek(pos, SEEK_SET, io->uptr); + return 0; +} + +static int read(struct img_pixmap *img, struct img_io *io) +{ + int i, nlines = 0; + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + struct src_mgr src; + unsigned char **scanlines; + + io->seek(0, SEEK_CUR, io->uptr); + + cinfo.err = jpeg_std_error(&jerr); /* XXX change... */ + jpeg_create_decompress(&cinfo); + + src.pub.init_source = init_source; + src.pub.fill_input_buffer = fill_input_buffer; + src.pub.skip_input_data = skip_input_data; + src.pub.resync_to_restart = jpeg_resync_to_restart; + src.pub.term_source = term_source; + src.pub.next_input_byte = 0; + src.pub.bytes_in_buffer = 0; + src.io = io; + cinfo.src = (struct jpeg_source_mgr*)&src; + + jpeg_read_header(&cinfo, 1); + cinfo.out_color_space = JCS_RGB; + + if(img_set_pixels(img, cinfo.image_width, cinfo.image_height, IMG_FMT_RGB24, 0) == -1) { + jpeg_destroy_decompress(&cinfo); + return -1; + } + + if(!(scanlines = malloc(img->height * sizeof *scanlines))) { + jpeg_destroy_decompress(&cinfo); + return -1; + } + scanlines[0] = img->pixels; + for(i=1; iheight; i++) { + scanlines[i] = scanlines[i - 1] + img->width * img->pixelsz; + } + + jpeg_start_decompress(&cinfo); + while(nlines < img->height) { + int res = jpeg_read_scanlines(&cinfo, scanlines + nlines, img->height - nlines); + nlines += res; + } + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + + free(scanlines); + return 0; +} + +static int write(struct img_pixmap *img, struct img_io *io) +{ + int i, nlines = 0; + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + struct dst_mgr dest; + struct img_pixmap tmpimg; + unsigned char **scanlines; + + img_init(&tmpimg); + + if(img->fmt != IMG_FMT_RGB24) { + if(img_copy(&tmpimg, img) == -1) { + return -1; + } + if(img_convert(&tmpimg, IMG_FMT_RGB24) == -1) { + img_destroy(&tmpimg); + return -1; + } + img = &tmpimg; + } + + if(!(scanlines = malloc(img->height * sizeof *scanlines))) { + img_destroy(&tmpimg); + return -1; + } + scanlines[0] = img->pixels; + for(i=1; iheight; i++) { + scanlines[i] = scanlines[i - 1] + img->width * img->pixelsz; + } + + cinfo.err = jpeg_std_error(&jerr); /* XXX */ + jpeg_create_compress(&cinfo); + + dest.pub.init_destination = init_destination; + dest.pub.empty_output_buffer = empty_output_buffer; + dest.pub.term_destination = term_destination; + dest.io = io; + cinfo.dest = (struct jpeg_destination_mgr*)&dest; + + cinfo.image_width = img->width; + cinfo.image_height = img->height; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + + jpeg_set_defaults(&cinfo); + + jpeg_start_compress(&cinfo, 1); + while(nlines < img->height) { + int res = jpeg_write_scanlines(&cinfo, scanlines + nlines, img->height - nlines); + nlines += res; + } + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + + free(scanlines); + img_destroy(&tmpimg); + return 0; +} + +/* -- read source functions -- + * the following functions are adapted from jdatasrc.c in jpeglib + */ +static void init_source(j_decompress_ptr jd) +{ + struct src_mgr *src = (struct src_mgr*)jd->src; + src->start_of_file = 1; +} + +static int fill_input_buffer(j_decompress_ptr jd) +{ + struct src_mgr *src = (struct src_mgr*)jd->src; + size_t nbytes; + + nbytes = src->io->read(src->buffer, INPUT_BUF_SIZE, src->io->uptr); + + if(nbytes <= 0) { + if(src->start_of_file) { + return 0; + } + /* insert a fake EOI marker */ + src->buffer[0] = 0xff; + src->buffer[1] = JPEG_EOI; + nbytes = 2; + } + + src->pub.next_input_byte = src->buffer; + src->pub.bytes_in_buffer = nbytes; + src->start_of_file = 0; + return 1; +} + +static void skip_input_data(j_decompress_ptr jd, long num_bytes) +{ + struct src_mgr *src = (struct src_mgr*)jd->src; + + if(num_bytes > 0) { + while(num_bytes > (long)src->pub.bytes_in_buffer) { + num_bytes -= (long)src->pub.bytes_in_buffer; + fill_input_buffer(jd); + } + src->pub.next_input_byte += (size_t)num_bytes; + src->pub.bytes_in_buffer -= (size_t)num_bytes; + } +} + +static void term_source(j_decompress_ptr jd) +{ + /* nothing to see here, move along */ +} + + +/* -- write destination functions -- + * the following functions are adapted from jdatadst.c in jpeglib + */ +static void init_destination(j_compress_ptr jc) +{ + struct dst_mgr *dest = (struct dst_mgr*)jc->dest; + + dest->pub.next_output_byte = dest->buffer; + dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; +} + +static int empty_output_buffer(j_compress_ptr jc) +{ + struct dst_mgr *dest = (struct dst_mgr*)jc->dest; + + if(dest->io->write(dest->buffer, OUTPUT_BUF_SIZE, dest->io->uptr) != OUTPUT_BUF_SIZE) { + return 0; + } + + dest->pub.next_output_byte = dest->buffer; + dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; + return 1; +} + +static void term_destination(j_compress_ptr jc) +{ + struct dst_mgr *dest = (struct dst_mgr*)jc->dest; + size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer; + + /* write any remaining data in the buffer */ + if(datacount > 0) { + dest->io->write(dest->buffer, datacount, dest->io->uptr); + } + /* XXX flush? ... */ +} diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/imago2/file_png.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/imago2/file_png.c Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,244 @@ +/* +libimago - a multi-format image file input/output library. +Copyright (C) 2010 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ + +/* -- PNG module -- */ + +#include +#include +#include "imago2.h" +#include "ftype_module.h" + +static int check_file(struct img_io *io); +static int read_file(struct img_pixmap *img, struct img_io *io); +static int write_file(struct img_pixmap *img, struct img_io *io); + +static void read_func(png_struct *png, unsigned char *data, size_t len); +static void write_func(png_struct *png, unsigned char *data, size_t len); +static void flush_func(png_struct *png); + +static int png_type_to_fmt(int color_type, int channel_bits); +static int fmt_to_png_type(enum img_fmt fmt); + + +int img_register_png(void) +{ + static struct ftype_module mod = {".png", check_file, read_file, write_file}; + return img_register_module(&mod); +} + +static int check_file(struct img_io *io) +{ + unsigned char sig[8]; + int res; + long pos = io->seek(0, SEEK_CUR, io->uptr); + + if(io->read(sig, 8, io->uptr) < 8) { + io->seek(pos, SEEK_SET, io->uptr); + return -1; + } + + res = png_sig_cmp(sig, 0, 8) == 0 ? 0 : -1; + io->seek(pos, SEEK_SET, io->uptr); + return res; +} + +static int read_file(struct img_pixmap *img, struct img_io *io) +{ + png_struct *png; + png_info *info; + unsigned char **lineptr, *dest; + int i, channel_bits, color_type, ilace_type, compression, filtering, fmt; + png_uint_32 xsz, ysz; + + if(!(png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0))) { + return -1; + } + + if(!(info = png_create_info_struct(png))) { + png_destroy_read_struct(&png, 0, 0); + return -1; + } + + if(setjmp(png_jmpbuf(png))) { + png_destroy_read_struct(&png, &info, 0); + return -1; + } + + png_set_read_fn(png, io, read_func); + png_set_sig_bytes(png, 0); + png_read_png(png, info, 0, 0); + + png_get_IHDR(png, info, &xsz, &ysz, &channel_bits, &color_type, &ilace_type, + &compression, &filtering); + if((fmt = png_type_to_fmt(color_type, channel_bits)) == -1) { + png_destroy_read_struct(&png, &info, 0); + return -1; + } + + if(img_set_pixels(img, xsz, ysz, fmt, 0) == -1) { + png_destroy_read_struct(&png, &info, 0); + return -1; + } + + lineptr = (unsigned char**)png_get_rows(png, info); + + dest = img->pixels; + for(i=0; ipixelsz); + dest += xsz * img->pixelsz; + } + png_destroy_read_struct(&png, &info, 0); + return 0; +} + + +static int write_file(struct img_pixmap *img, struct img_io *io) +{ + png_struct *png; + png_info *info; + png_text txt; + struct img_pixmap tmpimg; + unsigned char **rows; + unsigned char *pixptr; + int i, coltype; + + img_init(&tmpimg); + + if(!(png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0))) { + return -1; + } + if(!(info = png_create_info_struct(png))) { + png_destroy_write_struct(&png, 0); + return -1; + } + + /* if the input image is floating-point, we need to convert it to integer */ + if(img_is_float(img)) { + if(img_copy(&tmpimg, img) == -1) { + return -1; + } + if(img_to_integer(&tmpimg) == -1) { + img_destroy(&tmpimg); + return -1; + } + img = &tmpimg; + } + + txt.compression = PNG_TEXT_COMPRESSION_NONE; + txt.key = "Software"; + txt.text = "libimago2"; + txt.text_length = 0; + + if(setjmp(png_jmpbuf(png))) { + png_destroy_write_struct(&png, &info); + img_destroy(&tmpimg); + return -1; + } + png_set_write_fn(png, io, write_func, flush_func); + + coltype = fmt_to_png_type(img->fmt); + png_set_IHDR(png, info, img->width, img->height, 8, coltype, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + png_set_text(png, info, &txt, 1); + + if(!(rows = malloc(img->height * sizeof *rows))) { + png_destroy_write_struct(&png, &info); + img_destroy(&tmpimg); + return -1; + } + + pixptr = img->pixels; + for(i=0; iheight; i++) { + rows[i] = pixptr; + pixptr += img->width * img->pixelsz; + } + png_set_rows(png, info, rows); + + png_write_png(png, info, 0, 0); + png_write_end(png, info); + png_destroy_write_struct(&png, &info); + + free(rows); + + img_destroy(&tmpimg); + return 0; +} + +static void read_func(png_struct *png, unsigned char *data, size_t len) +{ + struct img_io *io = (struct img_io*)png_get_io_ptr(png); + + if(io->read(data, len, io->uptr) == -1) { + longjmp(png_jmpbuf(png), 1); + } +} + +static void write_func(png_struct *png, unsigned char *data, size_t len) +{ + struct img_io *io = (struct img_io*)png_get_io_ptr(png); + + if(io->write(data, len, io->uptr) == -1) { + longjmp(png_jmpbuf(png), 1); + } +} + +static void flush_func(png_struct *png) +{ + /* XXX does it matter that we can't flush? */ +} + +static int png_type_to_fmt(int color_type, int channel_bits) +{ + /* we don't support non-8bit-per-channel images yet */ + if(channel_bits > 8) { + return -1; + } + + switch(color_type) { + case PNG_COLOR_TYPE_RGB: + return IMG_FMT_RGB24; + + case PNG_COLOR_TYPE_RGB_ALPHA: + return IMG_FMT_RGBA32; + + case PNG_COLOR_TYPE_GRAY: + return IMG_FMT_GREY8; + + default: + break; + } + return -1; +} + +static int fmt_to_png_type(enum img_fmt fmt) +{ + switch(fmt) { + case IMG_FMT_GREY8: + return PNG_COLOR_TYPE_GRAY; + + case IMG_FMT_RGB24: + return PNG_COLOR_TYPE_RGB; + + case IMG_FMT_RGBA32: + return PNG_COLOR_TYPE_RGBA; + + default: + break; + } + return -1; +} diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/imago2/file_ppm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/imago2/file_ppm.c Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,153 @@ +/* +libimago - a multi-format image file input/output library. +Copyright (C) 2010 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ + +/* -- Portable Pixmap (PPM) module -- */ + +#include +#include "imago2.h" +#include "ftype_module.h" + +static int check(struct img_io *io); +static int read(struct img_pixmap *img, struct img_io *io); +static int write(struct img_pixmap *img, struct img_io *io); + +int img_register_ppm(void) +{ + static struct ftype_module mod = {".ppm", check, read, write}; + return img_register_module(&mod); +} + + +static int check(struct img_io *io) +{ + char id[2]; + int res = -1; + long pos = io->seek(0, SEEK_CUR, io->uptr); + + if(io->read(id, 2, io->uptr) < 2) { + io->seek(pos, SEEK_SET, io->uptr); + return -1; + } + + if(id[0] == 'P' && (id[1] == '6' || id[1] == '3')) { + res = 0; + } + io->seek(pos, SEEK_SET, io->uptr); + return res; +} + +static int iofgetc(struct img_io *io) +{ + char c; + return io->read(&c, 1, io->uptr) < 1 ? -1 : c; +} + +static char *iofgets(char *buf, int size, struct img_io *io) +{ + int c; + char *ptr = buf; + + while(--size > 0 && (c = iofgetc(io)) != -1) { + *ptr++ = c; + if(c == '\n') break; + } + *ptr = 0; + + return ptr == buf ? 0 : buf; +} + +/* TODO: implement P3 reading */ +static int read(struct img_pixmap *img, struct img_io *io) +{ + char buf[256]; + int xsz, ysz, maxval, got_hdrlines = 1; + + if(!iofgets(buf, sizeof buf, io)) { + return -1; + } + if(!(buf[0] == 'P' && (buf[1] == '6' || buf[1] == '3'))) { + return -1; + } + + while(got_hdrlines < 3 && iofgets(buf, sizeof buf, io)) { + if(buf[0] == '#') continue; + + switch(got_hdrlines) { + case 1: + if(sscanf(buf, "%d %d\n", &xsz, &ysz) < 2) { + return -1; + } + break; + + case 2: + if(sscanf(buf, "%d\n", &maxval) < 1) { + return -1; + } + default: + break; + } + got_hdrlines++; + } + + if(xsz < 1 || ysz < 1 || maxval != 255) { + return -1; + } + + if(img_set_pixels(img, xsz, ysz, IMG_FMT_RGB24, 0) == -1) { + return -1; + } + + if(io->read(img->pixels, xsz * ysz * 3, io->uptr) < xsz * ysz * 3) { + return -1; + } + return 0; +} + +static int write(struct img_pixmap *img, struct img_io *io) +{ + int sz; + char buf[256]; + struct img_pixmap tmpimg; + + img_init(&tmpimg); + + if(img->fmt != IMG_FMT_RGB24) { + if(img_copy(&tmpimg, img) == -1) { + return -1; + } + if(img_convert(&tmpimg, IMG_FMT_RGB24) == -1) { + return -1; + } + img = &tmpimg; + } + + sprintf(buf, "P6\n#written by libimago2\n%d %d\n255\n", img->width, img->height); + if(io->write(buf, strlen(buf), io->uptr) < strlen(buf)) { + img_destroy(&tmpimg); + return -1; + } + + sz = img->width * img->height * 3; + if(io->write(img->pixels, sz, io->uptr) < sz) { + img_destroy(&tmpimg); + return -1; + } + + img_destroy(&tmpimg); + return 0; +} diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/imago2/file_rgbe.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/imago2/file_rgbe.c Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,501 @@ +/* This file contains code to read and write four byte rgbe file format + * developed by Greg Ward. It handles the conversions between rgbe and + * pixels consisting of floats. The data is assumed to be an array of floats. + * By default there are three floats per pixel in the order red, green, blue. + * (RGBE_DATA_??? values control this.) + * + * written by Bruce Walter (bjw@graphics.cornell.edu) 5/26/95 + * based on code written by Greg Ward + * minor modifications by John Tsiombikas (nuclear@member.fsf.org) apr.9 2007 + */ + +#include +#include +#include +#include +#include +#include +#include "imago2.h" +#include "ftype_module.h" + + +typedef struct { + int valid; /* indicate which fields are valid */ + char programtype[16]; /* listed at beginning of file to identify it + * after "#?". defaults to "RGBE" */ + float gamma; /* image has already been gamma corrected with + * given gamma. defaults to 1.0 (no correction) */ + float exposure; /* a value of 1.0 in an image corresponds to + * watts/steradian/m^2. + * defaults to 1.0 */ +} rgbe_header_info; + + +static int check(struct img_io *io); +static int read(struct img_pixmap *img, struct img_io *io); +static int write(struct img_pixmap *img, struct img_io *io); + +static int rgbe_read_header(struct img_io *io, int *width, int *height, rgbe_header_info * info); +static int rgbe_read_pixels_rle(struct img_io *io, float *data, int scanline_width, int num_scanlines); + + +int img_register_rgbe(void) +{ + static struct ftype_module mod = {".rgbe", check, read, write}; + return img_register_module(&mod); +} + + +static int check(struct img_io *io) +{ + int xsz, ysz, res; + long pos = io->seek(0, SEEK_CUR, io->uptr); + + rgbe_header_info hdr; + res = rgbe_read_header(io, &xsz, &ysz, &hdr); + + io->seek(pos, SEEK_SET, io->uptr); + return res; +} + +static int read(struct img_pixmap *img, struct img_io *io) +{ + int xsz, ysz; + rgbe_header_info hdr; + + if(rgbe_read_header(io, &xsz, &ysz, &hdr) == -1) { + return -1; + } + + if(img_set_pixels(img, xsz, ysz, IMG_FMT_RGBF, 0) == -1) { + return -1; + } + if(rgbe_read_pixels_rle(io, img->pixels, xsz, ysz) == -1) { + return -1; + } + return 0; +} + +static int write(struct img_pixmap *img, struct img_io *io) +{ + return -1; /* TODO */ +} + + +static int iofgetc(struct img_io *io) +{ + char c; + return io->read(&c, 1, io->uptr) < 1 ? -1 : c; +} + +static char *iofgets(char *buf, int size, struct img_io *io) +{ + int c; + char *ptr = buf; + + while(--size > 0 && (c = iofgetc(io)) != -1) { + *ptr++ = c; + if(c == '\n') break; + } + *ptr = 0; + + return ptr == buf ? 0 : buf; +} + + +/* flags indicating which fields in an rgbe_header_info are valid */ +#define RGBE_VALID_PROGRAMTYPE 0x01 +#define RGBE_VALID_GAMMA 0x02 +#define RGBE_VALID_EXPOSURE 0x04 + +/* return codes for rgbe routines */ +#define RGBE_RETURN_SUCCESS 0 +#define RGBE_RETURN_FAILURE -1 + + +#if defined(__cplusplus) || defined(GNUC) || __STDC_VERSION >= 199901L +#define INLINE inline +#else +#define INLINE +#endif + +/* offsets to red, green, and blue components in a data (float) pixel */ +#define RGBE_DATA_RED 0 +#define RGBE_DATA_GREEN 1 +#define RGBE_DATA_BLUE 2 + +/* number of floats per pixel */ +#define RGBE_DATA_SIZE 3 + +enum rgbe_error_codes { + rgbe_read_error, + rgbe_write_error, + rgbe_format_error, + rgbe_memory_error +}; + + +/* default error routine. change this to change error handling */ +static int rgbe_error(int rgbe_error_code, char *msg) +{ + switch (rgbe_error_code) { + case rgbe_read_error: + fprintf(stderr, "RGBE read error: %s\n", strerror(errno)); + break; + + case rgbe_write_error: + fprintf(stderr, "RGBE write error: %s\n", strerror(errno)); + break; + + case rgbe_format_error: + fprintf(stderr, "RGBE bad file format: %s\n", msg); + break; + + default: + case rgbe_memory_error: + fprintf(stderr, "RGBE error: %s\n", msg); + } + return RGBE_RETURN_FAILURE; +} + +/* standard conversion from float pixels to rgbe pixels */ +/*static INLINE void float2rgbe(unsigned char rgbe[4], float red, float green, float blue) +{ + float v; + int e; + + v = red; + if(green > v) + v = green; + if(blue > v) + v = blue; + if(v < 1e-32) { + rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; + } else { + v = frexp(v, &e) * 256.0 / v; + rgbe[0] = (unsigned char)(red * v); + rgbe[1] = (unsigned char)(green * v); + rgbe[2] = (unsigned char)(blue * v); + rgbe[3] = (unsigned char)(e + 128); + } +}*/ + +/* standard conversion from rgbe to float pixels */ +/* note: Ward uses ldexp(col+0.5,exp-(128+8)). However we wanted pixels */ +/* in the range [0,1] to map back into the range [0,1]. */ +static INLINE void rgbe2float(float *red, float *green, float *blue, unsigned char rgbe[4]) +{ + float f; + + if(rgbe[3]) { /*nonzero pixel */ + f = ldexp(1.0, rgbe[3] - (int)(128 + 8)); + *red = rgbe[0] * f; + *green = rgbe[1] * f; + *blue = rgbe[2] * f; + } else + *red = *green = *blue = 0.0; +} + +#if 0 +/* default minimal header. modify if you want more information in header */ +static int rgbe_write_header(FILE * fp, int width, int height, rgbe_header_info * info) +{ + char *programtype = "RGBE"; + + if(info && (info->valid & RGBE_VALID_PROGRAMTYPE)) + programtype = info->programtype; + if(fprintf(fp, "#?%s\n", programtype) < 0) + return rgbe_error(rgbe_write_error, NULL); + /* The #? is to identify file type, the programtype is optional. */ + if(info && (info->valid & RGBE_VALID_GAMMA)) { + if(fprintf(fp, "GAMMA=%g\n", info->gamma) < 0) + return rgbe_error(rgbe_write_error, NULL); + } + if(info && (info->valid & RGBE_VALID_EXPOSURE)) { + if(fprintf(fp, "EXPOSURE=%g\n", info->exposure) < 0) + return rgbe_error(rgbe_write_error, NULL); + } + if(fprintf(fp, "FORMAT=32-bit_rle_rgbe\n\n") < 0) + return rgbe_error(rgbe_write_error, NULL); + if(fprintf(fp, "-Y %d +X %d\n", height, width) < 0) + return rgbe_error(rgbe_write_error, NULL); + return RGBE_RETURN_SUCCESS; +} +#endif + +/* minimal header reading. modify if you want to parse more information */ +static int rgbe_read_header(struct img_io *io, int *width, int *height, rgbe_header_info * info) +{ + char buf[128]; + float tempf; + int i; + + if(info) { + info->valid = 0; + info->programtype[0] = 0; + info->gamma = info->exposure = 1.0; + } + if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == NULL) + return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_read_error, NULL);*/ + if((buf[0] != '#') || (buf[1] != '?')) { + /* if you want to require the magic token then uncomment the next line */ + /*return rgbe_error(rgbe_format_error,"bad initial token"); */ + } else if(info) { + info->valid |= RGBE_VALID_PROGRAMTYPE; + for(i = 0; i < sizeof(info->programtype) - 1; i++) { + if((buf[i + 2] == 0) || isspace(buf[i + 2])) + break; + info->programtype[i] = buf[i + 2]; + } + info->programtype[i] = 0; + if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0) + return rgbe_error(rgbe_read_error, NULL); + } + for(;;) { + if((buf[0] == 0) || (buf[0] == '\n')) + return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_format_error, "no FORMAT specifier found");*/ + else if(strcmp(buf, "FORMAT=32-bit_rle_rgbe\n") == 0) + break; /* format found so break out of loop */ + else if(info && (sscanf(buf, "GAMMA=%g", &tempf) == 1)) { + info->gamma = tempf; + info->valid |= RGBE_VALID_GAMMA; + } else if(info && (sscanf(buf, "EXPOSURE=%g", &tempf) == 1)) { + info->exposure = tempf; + info->valid |= RGBE_VALID_EXPOSURE; + } + if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0) + return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_read_error, NULL);*/ + } + if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0) + return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_read_error, NULL);*/ + if(strcmp(buf, "\n") != 0) + return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_format_error, "missing blank line after FORMAT specifier");*/ + if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0) + return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_read_error, NULL);*/ + if(sscanf(buf, "-Y %d +X %d", height, width) < 2) + return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_format_error, "missing image size specifier");*/ + return RGBE_RETURN_SUCCESS; +} + +#if 0 +/* simple write routine that does not use run length encoding */ + +/* These routines can be made faster by allocating a larger buffer and + fread-ing and fwrite-ing the data in larger chunks */ +static int rgbe_write_pixels(FILE * fp, float *data, int numpixels) +{ + unsigned char rgbe[4]; + + while(numpixels-- > 0) { + float2rgbe(rgbe, data[RGBE_DATA_RED], data[RGBE_DATA_GREEN], data[RGBE_DATA_BLUE]); + data += RGBE_DATA_SIZE; + if(fwrite(rgbe, sizeof(rgbe), 1, fp) < 1) + return rgbe_error(rgbe_write_error, NULL); + } + return RGBE_RETURN_SUCCESS; +} +#endif + +/* simple read routine. will not correctly handle run length encoding */ +static int rgbe_read_pixels(struct img_io *io, float *data, int numpixels) +{ + unsigned char rgbe[4]; + + while(numpixels-- > 0) { + if(io->read(rgbe, sizeof(rgbe), io->uptr) < 1) + return rgbe_error(rgbe_read_error, NULL); + rgbe2float(&data[RGBE_DATA_RED], &data[RGBE_DATA_GREEN], &data[RGBE_DATA_BLUE], rgbe); + data += RGBE_DATA_SIZE; + } + return RGBE_RETURN_SUCCESS; +} + +#if 0 +/* The code below is only needed for the run-length encoded files. */ + +/* Run length encoding adds considerable complexity but does */ + +/* save some space. For each scanline, each channel (r,g,b,e) is */ + +/* encoded separately for better compression. */ + +static int rgbe_write_bytes_rle(struct img_io *io, unsigned char *data, int numbytes) +{ +#define MINRUNLENGTH 4 + int cur, beg_run, run_count, old_run_count, nonrun_count; + unsigned char buf[2]; + + cur = 0; + while(cur < numbytes) { + beg_run = cur; + /* find next run of length at least 4 if one exists */ + run_count = old_run_count = 0; + while((run_count < MINRUNLENGTH) && (beg_run < numbytes)) { + beg_run += run_count; + old_run_count = run_count; + run_count = 1; + while((beg_run + run_count < numbytes) && (run_count < 127) + && (data[beg_run] == data[beg_run + run_count])) + run_count++; + } + /* if data before next big run is a short run then write it as such */ + if((old_run_count > 1) && (old_run_count == beg_run - cur)) { + buf[0] = 128 + old_run_count; /*write short run */ + buf[1] = data[cur]; + if(fwrite(buf, sizeof(buf[0]) * 2, 1, fp) < 1) + return rgbe_error(rgbe_write_error, NULL); + cur = beg_run; + } + /* write out bytes until we reach the start of the next run */ + while(cur < beg_run) { + nonrun_count = beg_run - cur; + if(nonrun_count > 128) + nonrun_count = 128; + buf[0] = nonrun_count; + if(fwrite(buf, sizeof(buf[0]), 1, fp) < 1) + return rgbe_error(rgbe_write_error, NULL); + if(fwrite(&data[cur], sizeof(data[0]) * nonrun_count, 1, fp) < 1) + return rgbe_error(rgbe_write_error, NULL); + cur += nonrun_count; + } + /* write out next run if one was found */ + if(run_count >= MINRUNLENGTH) { + buf[0] = 128 + run_count; + buf[1] = data[beg_run]; + if(fwrite(buf, sizeof(buf[0]) * 2, 1, fp) < 1) + return rgbe_error(rgbe_write_error, NULL); + cur += run_count; + } + } + return RGBE_RETURN_SUCCESS; +#undef MINRUNLENGTH +} + +static int rgbe_write_pixels_rle(struct img_io *io, float *data, int scanline_width, int num_scanlines) +{ + unsigned char rgbe[4]; + unsigned char *buffer; + int i, err; + + if((scanline_width < 8) || (scanline_width > 0x7fff)) + /* run length encoding is not allowed so write flat */ + return rgbe_write_pixels(io, data, scanline_width * num_scanlines); + buffer = (unsigned char *)malloc(sizeof(unsigned char) * 4 * scanline_width); + if(buffer == NULL) + /* no buffer space so write flat */ + return rgbe_write_pixels(fp, data, scanline_width * num_scanlines); + while(num_scanlines-- > 0) { + rgbe[0] = 2; + rgbe[1] = 2; + rgbe[2] = scanline_width >> 8; + rgbe[3] = scanline_width & 0xFF; + if(fwrite(rgbe, sizeof(rgbe), 1, fp) < 1) { + free(buffer); + return rgbe_error(rgbe_write_error, NULL); + } + for(i = 0; i < scanline_width; i++) { + float2rgbe(rgbe, data[RGBE_DATA_RED], data[RGBE_DATA_GREEN], data[RGBE_DATA_BLUE]); + buffer[i] = rgbe[0]; + buffer[i + scanline_width] = rgbe[1]; + buffer[i + 2 * scanline_width] = rgbe[2]; + buffer[i + 3 * scanline_width] = rgbe[3]; + data += RGBE_DATA_SIZE; + } + /* write out each of the four channels separately run length encoded */ + /* first red, then green, then blue, then exponent */ + for(i = 0; i < 4; i++) { + if((err = rgbe_write_bytes_rle(fp, &buffer[i * scanline_width], + scanline_width)) != RGBE_RETURN_SUCCESS) { + free(buffer); + return err; + } + } + } + free(buffer); + return RGBE_RETURN_SUCCESS; +} +#endif + +static int rgbe_read_pixels_rle(struct img_io *io, float *data, int scanline_width, int num_scanlines) +{ + unsigned char rgbe[4], *scanline_buffer, *ptr, *ptr_end; + int i, count; + unsigned char buf[2]; + + if((scanline_width < 8) || (scanline_width > 0x7fff)) + /* run length encoding is not allowed so read flat */ + return rgbe_read_pixels(io, data, scanline_width * num_scanlines); + scanline_buffer = NULL; + /* read in each successive scanline */ + while(num_scanlines > 0) { + if(io->read(rgbe, sizeof(rgbe), io->uptr) < 1) { + free(scanline_buffer); + return rgbe_error(rgbe_read_error, NULL); + } + if((rgbe[0] != 2) || (rgbe[1] != 2) || (rgbe[2] & 0x80)) { + /* this file is not run length encoded */ + rgbe2float(&data[0], &data[1], &data[2], rgbe); + data += RGBE_DATA_SIZE; + free(scanline_buffer); + return rgbe_read_pixels(io, data, scanline_width * num_scanlines - 1); + } + if((((int)rgbe[2]) << 8 | rgbe[3]) != scanline_width) { + free(scanline_buffer); + return rgbe_error(rgbe_format_error, "wrong scanline width"); + } + if(scanline_buffer == NULL) + scanline_buffer = (unsigned char *) + malloc(sizeof(unsigned char) * 4 * scanline_width); + if(scanline_buffer == NULL) + return rgbe_error(rgbe_memory_error, "unable to allocate buffer space"); + + ptr = &scanline_buffer[0]; + /* read each of the four channels for the scanline into the buffer */ + for(i = 0; i < 4; i++) { + ptr_end = &scanline_buffer[(i + 1) * scanline_width]; + while(ptr < ptr_end) { + if(io->read(buf, sizeof(buf[0]) * 2, io->uptr) < 1) { + free(scanline_buffer); + return rgbe_error(rgbe_read_error, NULL); + } + if(buf[0] > 128) { + /* a run of the same value */ + count = buf[0] - 128; + if((count == 0) || (count > ptr_end - ptr)) { + free(scanline_buffer); + return rgbe_error(rgbe_format_error, "bad scanline data"); + } + while(count-- > 0) + *ptr++ = buf[1]; + } else { + /* a non-run */ + count = buf[0]; + if((count == 0) || (count > ptr_end - ptr)) { + free(scanline_buffer); + return rgbe_error(rgbe_format_error, "bad scanline data"); + } + *ptr++ = buf[1]; + if(--count > 0) { + if(io->read(ptr, sizeof(*ptr) * count, io->uptr) < 1) { + free(scanline_buffer); + return rgbe_error(rgbe_read_error, NULL); + } + ptr += count; + } + } + } + } + /* now convert data from buffer into floats */ + for(i = 0; i < scanline_width; i++) { + rgbe[0] = scanline_buffer[i]; + rgbe[1] = scanline_buffer[i + scanline_width]; + rgbe[2] = scanline_buffer[i + 2 * scanline_width]; + rgbe[3] = scanline_buffer[i + 3 * scanline_width]; + rgbe2float(&data[RGBE_DATA_RED], &data[RGBE_DATA_GREEN], &data[RGBE_DATA_BLUE], rgbe); + data += RGBE_DATA_SIZE; + } + num_scanlines--; + } + free(scanline_buffer); + return RGBE_RETURN_SUCCESS; +} diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/imago2/ftype_module.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/imago2/ftype_module.c Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,118 @@ +/* +libimago - a multi-format image file input/output library. +Copyright (C) 2010 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ + +#include +#include +#include "ftype_module.h" + +static struct list_node { + struct ftype_module *module; + struct list_node *next; +} *modules; + +/* defined in modules.c which is generated by configure */ +void img_modules_init(); + +static int done_init; + +int img_register_module(struct ftype_module *mod) +{ + struct list_node *node; + + if(!(node = malloc(sizeof *node))) { + return -1; + } + + node->module = mod; + node->next = modules; + modules = node; + return 0; +} + +struct ftype_module *img_find_format_module(struct img_io *io) +{ + struct list_node *node; + + if(!done_init) { + img_modules_init(); + done_init = 1; + } + + node = modules; + while(node) { + if(node->module->check(io) != -1) { + return node->module; + } + node = node->next; + } + return 0; +} + +struct ftype_module *img_guess_format(const char *fname) +{ + struct list_node *node; + char *suffix; + int suffix_len; + + if(!done_init) { + img_modules_init(); + done_init = 1; + } + + if(!(suffix = strrchr(fname, '.'))) { + return 0; /* no suffix, can't guess ... */ + } + suffix_len = strlen(suffix); + + node = modules; + while(node) { + char *suflist = node->module->suffix; + char *start, *end; + + while(*suflist) { + if(!(start = strstr(suflist, suffix))) { + break; + } + end = start + suffix_len; + + if(*end == ':' || *end == 0) { + return node->module; /* found it */ + } + suflist = end; + } + + node = node->next; + } + return 0; +} + +struct ftype_module *img_get_module(int idx) +{ + struct list_node *node; + + if(!done_init) { + img_modules_init(); + done_init = 1; + } + + node = modules; + while(node && idx--) { + node = node->next; + } + return node->module; +} diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/imago2/ftype_module.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/imago2/ftype_module.h Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,39 @@ +/* +libimago - a multi-format image file input/output library. +Copyright (C) 2010 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ + +#ifndef FTYPE_MODULE_H_ +#define FTYPE_MODULE_H_ + +#include "imago2.h" + +struct ftype_module { + char *suffix; /* used for format autodetection during saving only */ + + int (*check)(struct img_io *io); + int (*read)(struct img_pixmap *img, struct img_io *io); + int (*write)(struct img_pixmap *img, struct img_io *io); +}; + +int img_register_module(struct ftype_module *mod); + +struct ftype_module *img_find_format_module(struct img_io *io); +struct ftype_module *img_guess_format(const char *fname); +struct ftype_module *img_get_module(int idx); + + +#endif /* FTYPE_MODULE_H_ */ diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/imago2/imago2.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/imago2/imago2.c Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,449 @@ +/* +libimago - a multi-format image file input/output library. +Copyright (C) 2010 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ + +#include +#include +#include +#include "imago2.h" +#include "ftype_module.h" + +static int pixel_size(enum img_fmt fmt); +static size_t def_read(void *buf, size_t bytes, void *uptr); +static size_t def_write(void *buf, size_t bytes, void *uptr); +static long def_seek(long offset, int whence, void *uptr); + + +void img_init(struct img_pixmap *img) +{ + img->pixels = 0; + img->width = img->height = 0; + img->fmt = IMG_FMT_RGBA32; + img->pixelsz = pixel_size(img->fmt); + img->name = 0; +} + + +void img_destroy(struct img_pixmap *img) +{ + free(img->pixels); + img->pixels = 0; /* just in case... */ + img->width = img->height = 0xbadbeef; + free(img->name); +} + +struct img_pixmap *img_create(void) +{ + struct img_pixmap *p; + + if(!(p = malloc(sizeof *p))) { + return 0; + } + img_init(p); + return p; +} + +void img_free(struct img_pixmap *img) +{ + img_destroy(img); + free(img); +} + +int img_set_name(struct img_pixmap *img, const char *name) +{ + char *tmp; + + if(!(tmp = malloc(strlen(name) + 1))) { + return -1; + } + strcpy(tmp, name); + img->name = tmp; + return 0; +} + +int img_set_format(struct img_pixmap *img, enum img_fmt fmt) +{ + if(img->pixels) { + return img_convert(img, fmt); + } + img->fmt = fmt; + return 0; +} + +int img_copy(struct img_pixmap *dest, struct img_pixmap *src) +{ + return img_set_pixels(dest, src->width, src->height, src->fmt, src->pixels); +} + +int img_set_pixels(struct img_pixmap *img, int w, int h, enum img_fmt fmt, void *pix) +{ + void *newpix; + int pixsz = pixel_size(fmt); + + if(!(newpix = malloc(w * h * pixsz))) { + return -1; + } + + if(pix) { + memcpy(newpix, pix, w * h * pixsz); + } else { + memset(newpix, 0, w * h * pixsz); + } + + free(img->pixels); + img->pixels = newpix; + img->width = w; + img->height = h; + img->pixelsz = pixsz; + img->fmt = fmt; + return 0; +} + +void *img_load_pixels(const char *fname, int *xsz, int *ysz, enum img_fmt fmt) +{ + struct img_pixmap img; + + img_init(&img); + + if(img_load(&img, fname) == -1) { + return 0; + } + if(img.fmt != fmt) { + if(img_convert(&img, fmt) == -1) { + img_destroy(&img); + return 0; + } + } + + *xsz = img.width; + *ysz = img.height; + return img.pixels; +} + +int img_save_pixels(const char *fname, void *pix, int xsz, int ysz, enum img_fmt fmt) +{ + struct img_pixmap img; + + img_init(&img); + img.fmt = fmt; + img.name = (char*)fname; + img.width = xsz; + img.height = ysz; + img.pixels = pix; + + return img_save(&img, fname); +} + +void img_free_pixels(void *pix) +{ + free(pix); +} + +int img_load(struct img_pixmap *img, const char *fname) +{ + int res; + FILE *fp; + + if(!(fp = fopen(fname, "rb"))) { + return -1; + } + res = img_read_file(img, fp); + fclose(fp); + return res; +} + +/* TODO implement filetype selection */ +int img_save(struct img_pixmap *img, const char *fname) +{ + int res; + FILE *fp; + + img_set_name(img, fname); + + if(!(fp = fopen(fname, "wb"))) { + return -1; + } + res = img_write_file(img, fp); + fclose(fp); + return res; +} + +int img_read_file(struct img_pixmap *img, FILE *fp) +{ + struct img_io io = {0, def_read, def_write, def_seek}; + + io.uptr = fp; + return img_read(img, &io); +} + +int img_write_file(struct img_pixmap *img, FILE *fp) +{ + struct img_io io = {0, def_read, def_write, def_seek}; + + io.uptr = fp; + return img_write(img, &io); +} + +int img_read(struct img_pixmap *img, struct img_io *io) +{ + struct ftype_module *mod; + + if((mod = img_find_format_module(io))) { + return mod->read(img, io); + } + return -1; +} + +int img_write(struct img_pixmap *img, struct img_io *io) +{ + struct ftype_module *mod; + + if(!img->name || !(mod = img_guess_format(img->name))) { + /* TODO throw some sort of warning? */ + /* TODO implement some sort of module priority or let the user specify? */ + if(!(mod = img_get_module(0))) { + return -1; + } + } + + return mod->write(img, io); +} + +int img_to_float(struct img_pixmap *img) +{ + enum img_fmt targ_fmt; + + switch(img->fmt) { + case IMG_FMT_GREY8: + targ_fmt = IMG_FMT_GREYF; + break; + + case IMG_FMT_RGB24: + targ_fmt = IMG_FMT_RGBF; + break; + + case IMG_FMT_RGBA32: + targ_fmt = IMG_FMT_RGBAF; + break; + + default: + return 0; /* already float */ + } + + return img_convert(img, targ_fmt); +} + +int img_to_integer(struct img_pixmap *img) +{ + enum img_fmt targ_fmt; + + switch(img->fmt) { + case IMG_FMT_GREYF: + targ_fmt = IMG_FMT_GREY8; + break; + + case IMG_FMT_RGBF: + targ_fmt = IMG_FMT_RGB24; + break; + + case IMG_FMT_RGBAF: + targ_fmt = IMG_FMT_RGBA32; + break; + + default: + return 0; /* already integer */ + } + + return img_convert(img, targ_fmt); +} + +int img_is_float(struct img_pixmap *img) +{ + return img->fmt >= IMG_FMT_GREYF && img->fmt <= IMG_FMT_RGBAF; +} + +int img_has_alpha(struct img_pixmap *img) +{ + if(img->fmt == IMG_FMT_RGBA32 || img->fmt == IMG_FMT_RGBAF) { + return 1; + } + return 0; +} + + +void img_setpixel(struct img_pixmap *img, int x, int y, void *pixel) +{ + char *dest = (char*)img->pixels + (y * img->width + x) * img->pixelsz; + memcpy(dest, pixel, img->pixelsz); +} + +void img_getpixel(struct img_pixmap *img, int x, int y, void *pixel) +{ + char *dest = (char*)img->pixels + (y * img->width + x) * img->pixelsz; + memcpy(pixel, dest, img->pixelsz); +} + +void img_setpixel1i(struct img_pixmap *img, int x, int y, int pix) +{ + img_setpixel4i(img, x, y, pix, pix, pix, pix); +} + +void img_setpixel1f(struct img_pixmap *img, int x, int y, float pix) +{ + img_setpixel4f(img, x, y, pix, pix, pix, pix); +} + +void img_setpixel4i(struct img_pixmap *img, int x, int y, int r, int g, int b, int a) +{ + if(img_is_float(img)) { + img_setpixel4f(img, x, y, r / 255.0, g / 255.0, b / 255.0, a / 255.0); + } else { + unsigned char pixel[4]; + pixel[0] = r; + pixel[1] = g; + pixel[2] = b; + pixel[3] = a; + + img_setpixel(img, x, y, pixel); + } +} + +void img_setpixel4f(struct img_pixmap *img, int x, int y, float r, float g, float b, float a) +{ + if(img_is_float(img)) { + float pixel[4]; + pixel[0] = r; + pixel[1] = g; + pixel[2] = b; + pixel[3] = a; + + img_setpixel(img, x, y, pixel); + } else { + img_setpixel4i(img, x, y, (int)(r * 255.0), (int)(g * 255.0), (int)(b * 255.0), (int)(a * 255.0)); + } +} + +void img_getpixel1i(struct img_pixmap *img, int x, int y, int *pix) +{ + int junk[3]; + img_getpixel4i(img, x, y, pix, junk, junk + 1, junk + 2); +} + +void img_getpixel1f(struct img_pixmap *img, int x, int y, float *pix) +{ + float junk[3]; + img_getpixel4f(img, x, y, pix, junk, junk + 1, junk + 2); +} + +void img_getpixel4i(struct img_pixmap *img, int x, int y, int *r, int *g, int *b, int *a) +{ + if(img_is_float(img)) { + float pixel[4] = {0, 0, 0, 0}; + img_getpixel(img, x, y, pixel); + *r = pixel[0] * 255.0; + *g = pixel[1] * 255.0; + *b = pixel[2] * 255.0; + *a = pixel[3] * 255.0; + } else { + unsigned char pixel[4]; + img_getpixel(img, x, y, pixel); + *r = pixel[0]; + *g = pixel[1]; + *b = pixel[2]; + *a = pixel[3]; + } +} + +void img_getpixel4f(struct img_pixmap *img, int x, int y, float *r, float *g, float *b, float *a) +{ + if(img_is_float(img)) { + float pixel[4] = {0, 0, 0, 0}; + img_getpixel(img, x, y, pixel); + *r = pixel[0]; + *g = pixel[1]; + *b = pixel[2]; + *a = pixel[3]; + } else { + unsigned char pixel[4]; + img_getpixel(img, x, y, pixel); + *r = pixel[0] / 255.0; + *g = pixel[1] / 255.0; + *b = pixel[2] / 255.0; + *a = pixel[3] / 255.0; + } +} + +void img_io_set_user_data(struct img_io *io, void *uptr) +{ + io->uptr = uptr; +} + +void img_io_set_read_func(struct img_io *io, size_t (*read)(void*, size_t, void*)) +{ + io->read = read; +} + +void img_io_set_write_func(struct img_io *io, size_t (*write)(void*, size_t, void*)) +{ + io->write = write; +} + +void img_io_set_seek_func(struct img_io *io, long (*seek)(long, int, void*)) +{ + io->seek = seek; +} + + +static int pixel_size(enum img_fmt fmt) +{ + switch(fmt) { + case IMG_FMT_GREY8: + return 1; + case IMG_FMT_RGB24: + return 3; + case IMG_FMT_RGBA32: + return 4; + case IMG_FMT_GREYF: + return sizeof(float); + case IMG_FMT_RGBF: + return 3 * sizeof(float); + case IMG_FMT_RGBAF: + return 4 * sizeof(float); + default: + break; + } + return 0; +} + +static size_t def_read(void *buf, size_t bytes, void *uptr) +{ + return uptr ? fread(buf, 1, bytes, uptr) : 0; +} + +static size_t def_write(void *buf, size_t bytes, void *uptr) +{ + return uptr ? fwrite(buf, 1, bytes, uptr) : 0; +} + +static long def_seek(long offset, int whence, void *uptr) +{ + if(!uptr || fseek(uptr, offset, whence) == -1) { + return -1; + } + return ftell(uptr); +} + diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/imago2/imago2.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/imago2/imago2.h Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,222 @@ +/* +libimago - a multi-format image file input/output library. +Copyright (C) 2010-2012 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ + +#ifndef IMAGO2_H_ +#define IMAGO2_H_ + +#include + +#ifdef __cplusplus +#define IMG_OPTARG(arg, val) arg = val +#else +#define IMG_OPTARG(arg, val) arg +#endif + +/* XXX if you change this make sure to also change pack/unpack arrays in conv.c */ +enum img_fmt { + IMG_FMT_GREY8, + IMG_FMT_RGB24, + IMG_FMT_RGBA32, + IMG_FMT_GREYF, + IMG_FMT_RGBF, + IMG_FMT_RGBAF, + + NUM_IMG_FMT +}; + +struct img_pixmap { + void *pixels; + int width, height; + enum img_fmt fmt; + int pixelsz; + char *name; +}; + +struct img_io { + void *uptr; /* user-data */ + + size_t (*read)(void *buf, size_t bytes, void *uptr); + size_t (*write)(void *buf, size_t bytes, void *uptr); + long (*seek)(long offs, int whence, void *uptr); +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/* initialize the img_pixmap structure */ +void img_init(struct img_pixmap *img); +/* destroys the img_pixmap structure, freeing the pixel buffer (if available) + * and any other memory held by the pixmap. + */ +void img_destroy(struct img_pixmap *img); + +/* convenience function that allocates an img_pixmap struct and then initializes it. + * returns null if the malloc fails. + */ +struct img_pixmap *img_create(void); +/* frees a pixmap previously allocated with img_create (free followed by img_destroy) */ +void img_free(struct img_pixmap *img); + +int img_set_name(struct img_pixmap *img, const char *name); + +/* set the image pixel format */ +int img_set_format(struct img_pixmap *img, enum img_fmt fmt); + +/* copies one pixmap to another. + * equivalent to: img_set_pixels(dest, src->width, src->height, src->fmt, src->pixels) + */ +int img_copy(struct img_pixmap *dest, struct img_pixmap *src); + +/* allocates a pixel buffer of the specified dimensions and format, and copies the + * pixels given through the pix pointer into it. + * the pix pointer can be null, in which case there's no copy, just allocation. + * + * C++: fmt and pix have default parameters IMG_FMT_RGBA32 and null respectively. + */ +int img_set_pixels(struct img_pixmap *img, int w, int h, IMG_OPTARG(enum img_fmt fmt, IMG_FMT_RGBA32), IMG_OPTARG(void *pix, 0)); + +/* Simplified image loading + * Loads the specified file, and returns a pointer to an array of pixels of the + * requested pixel format. The width and height of the image are returned through + * the xsz and ysz pointers. + * If the image cannot be loaded, the function returns null. + * + * C++: the format argument is optional and defaults to IMG_FMT_RGBA32 + */ +void *img_load_pixels(const char *fname, int *xsz, int *ysz, IMG_OPTARG(enum img_fmt fmt, IMG_FMT_RGBA32)); + +/* Simplified image saving + * Reads an array of pixels supplied through the pix pointer, of dimensions xsz + * and ysz, and pixel-format fmt, and saves it to a file. + * The output filetype is guessed by the filename suffix. + * + * C++: the format argument is optional and defaults to IMG_FMT_RGBA32 + */ +int img_save_pixels(const char *fname, void *pix, int xsz, int ysz, IMG_OPTARG(enum img_fmt fmt, IMG_FMT_RGBA32)); + +/* Frees the memory allocated by img_load_pixels */ +void img_free_pixels(void *pix); + +/* Loads an image file into the supplied pixmap */ +int img_load(struct img_pixmap *img, const char *fname); +/* Saves the supplied pixmap to a file. The output filetype is guessed by the filename suffix */ +int img_save(struct img_pixmap *img, const char *fname); + +/* Reads an image from an open FILE* into the supplied pixmap */ +int img_read_file(struct img_pixmap *img, FILE *fp); +/* Writes the supplied pixmap to an open FILE* */ +int img_write_file(struct img_pixmap *img, FILE *fp); + +/* Reads an image using user-defined file-i/o functions (see img_io_set_*) */ +int img_read(struct img_pixmap *img, struct img_io *io); +/* Writes an image using user-defined file-i/o functions (see img_io_set_*) */ +int img_write(struct img_pixmap *img, struct img_io *io); + +/* Converts an image to the specified pixel format */ +int img_convert(struct img_pixmap *img, enum img_fmt tofmt); + +/* Converts an image from an integer pixel format to the corresponding floating point one */ +int img_to_float(struct img_pixmap *img); +/* Converts an image from a floating point pixel format to the corresponding integer one */ +int img_to_integer(struct img_pixmap *img); + +/* Returns non-zero (true) if the supplied image is in a floating point pixel format */ +int img_is_float(struct img_pixmap *img); +/* Returns non-zero (true) if the supplied image has an alpha channel */ +int img_has_alpha(struct img_pixmap *img); + + +/* don't use these for anything performance-critical */ +void img_setpixel(struct img_pixmap *img, int x, int y, void *pixel); +void img_getpixel(struct img_pixmap *img, int x, int y, void *pixel); + +void img_setpixel1i(struct img_pixmap *img, int x, int y, int pix); +void img_setpixel1f(struct img_pixmap *img, int x, int y, float pix); +void img_setpixel4i(struct img_pixmap *img, int x, int y, int r, int g, int b, int a); +void img_setpixel4f(struct img_pixmap *img, int x, int y, float r, float g, float b, float a); + +void img_getpixel1i(struct img_pixmap *img, int x, int y, int *pix); +void img_getpixel1f(struct img_pixmap *img, int x, int y, float *pix); +void img_getpixel4i(struct img_pixmap *img, int x, int y, int *r, int *g, int *b, int *a); +void img_getpixel4f(struct img_pixmap *img, int x, int y, float *r, float *g, float *b, float *a); + + +/* OpenGL helper functions */ + +/* Returns the equivalent OpenGL "format" as expected by the 7th argument of glTexImage2D */ +unsigned int img_fmt_glfmt(enum img_fmt fmt); +/* Returns the equivalent OpenGL "type" as expected by the 8th argument of glTexImage2D */ +unsigned int img_fmt_gltype(enum img_fmt fmt); +/* Returns the equivalent OpenGL "internal format" as expected by the 3rd argument of glTexImage2D */ +unsigned int img_fmt_glintfmt(enum img_fmt fmt); + +/* Same as above, based on the pixel format of the supplied image */ +unsigned int img_glfmt(struct img_pixmap *img); +unsigned int img_gltype(struct img_pixmap *img); +unsigned int img_glintfmt(struct img_pixmap *img); + +/* Creates an OpenGL texture from the image, and returns the texture id, or 0 for failure */ +unsigned int img_gltexture(struct img_pixmap *img); + +/* Load an image and create an OpenGL texture out of it */ +unsigned int img_gltexture_load(const char *fname); +unsigned int img_gltexture_read_file(FILE *fp); +unsigned int img_gltexture_read(struct img_io *io); + +/* These functions can be used to fill an img_io struct before it's passed to + * one of the user-defined i/o image reading/writing functions (img_read/img_write). + * + * User-defined i/o functions: + * + * - size_t read_func(void *buffer, size_t bytes, void *user_ptr) + * Must try to fill the buffer with the specified number of bytes, and return + * the number of bytes actually read. + * + * - size_t write_func(void *buffer, size_t bytes, void *user_ptr) + * Must write the specified number of bytes from the supplied buffer and return + * the number of bytes actually written. + * + * - long seek_func(long offset, int whence, void *user_ptr) + * Must seek offset bytes from: the beginning of the file if whence is SEEK_SET, + * the current position if whence is SEEK_CUR, or the end of the file if whence is + * SEEK_END, and return the resulting file offset from the beginning of the file. + * (i.e. seek_func(0, SEEK_CUR, user_ptr); must be equivalent to an ftell). + * + * All three functions get the user-data pointer set through img_io_set_user_data + * as their last argument. + * + * Note: obviously you don't need to set a write function if you're only going + * to call img_read, or the read and seek function if you're only going to call + * img_write. + * + * Note: if the user-supplied write function is buffered, make sure to flush + * (or close the file) after img_write returns. + */ +void img_io_set_user_data(struct img_io *io, void *uptr); +void img_io_set_read_func(struct img_io *io, size_t (*read)(void*, size_t, void*)); +void img_io_set_write_func(struct img_io *io, size_t (*write)(void*, size_t, void*)); +void img_io_set_seek_func(struct img_io *io, long (*seek)(long, int, void*)); + + +#ifdef __cplusplus +} +#endif + + +#endif /* IMAGO_H_ */ diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/imago2/imago_gl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/imago2/imago_gl.c Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,223 @@ +#include "imago2.h" + + +/* to avoid dependency to OpenGL, I'll define all the relevant GL macros manually */ +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_FLOAT 0x1406 + +#define GL_LUMINANCE 0x1909 +#define GL_RGB 0x1907 +#define GL_RGBA 0x1908 + +#define GL_RGBA32F 0x8814 +#define GL_RGB32F 0x8815 +#define GL_LUMINANCE32F 0x8818 + +#define GL_TEXTURE_2D 0x0de1 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_LINEAR 0x2601 +#define GL_REPEAT 0x2901 + + +typedef unsigned int GLenum; +typedef unsigned int GLuint; +typedef int GLint; +typedef int GLsizei; +typedef void GLvoid; + +/* for the same reason I'll load GL functions dynamically */ +typedef void (*gl_gen_textures_func)(GLsizei, GLuint*); +typedef void (*gl_bind_texture_func)(GLenum, GLuint); +typedef void (*gl_tex_parameteri_func)(GLenum, GLenum, GLint); +typedef void (*gl_tex_image2d_func)(GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid*); + +static gl_gen_textures_func gl_gen_textures; +static gl_bind_texture_func gl_bind_texture; +static gl_tex_parameteri_func gl_tex_parameteri; +static gl_tex_image2d_func gl_tex_image2d; + +static int load_glfunc(void); + +unsigned int img_fmt_glfmt(enum img_fmt fmt) +{ + switch(fmt) { + case IMG_FMT_GREY8: + case IMG_FMT_GREYF: + return GL_LUMINANCE; + + case IMG_FMT_RGB24: + case IMG_FMT_RGBF: + return GL_RGB; + + case IMG_FMT_RGBA32: + case IMG_FMT_RGBAF: + return GL_RGBA; + + default: + break; + } + return 0; +} + +unsigned int img_fmt_gltype(enum img_fmt fmt) +{ + switch(fmt) { + case IMG_FMT_GREY8: + case IMG_FMT_RGB24: + case IMG_FMT_RGBA32: + return GL_UNSIGNED_BYTE; + + case IMG_FMT_GREYF: + case IMG_FMT_RGBF: + case IMG_FMT_RGBAF: + return GL_FLOAT; + + default: + break; + } + return 0; +} + +unsigned int img_fmt_glintfmt(enum img_fmt fmt) +{ + switch(fmt) { + case IMG_FMT_GREY8: + return GL_LUMINANCE; + case IMG_FMT_RGB24: + return GL_RGB; + case IMG_FMT_RGBA32: + return GL_RGBA; + case IMG_FMT_GREYF: + return GL_LUMINANCE32F; + case IMG_FMT_RGBF: + return GL_RGB32F; + case IMG_FMT_RGBAF: + return GL_RGBA32F; + default: + break; + } + return 0; +} + +unsigned int img_glfmt(struct img_pixmap *img) +{ + return img_fmt_glfmt(img->fmt); +} + +unsigned int img_gltype(struct img_pixmap *img) +{ + return img_fmt_gltype(img->fmt); +} + +unsigned int img_glintfmt(struct img_pixmap *img) +{ + return img_fmt_glintfmt(img->fmt); +} + +unsigned int img_gltexture(struct img_pixmap *img) +{ + unsigned int tex; + unsigned int intfmt, fmt, type; + + if(!gl_gen_textures) { + if(load_glfunc() == -1) { + fprintf(stderr, "imago: failed to initialize the OpenGL helpers\n"); + return 0; + } + } + + intfmt = img_glintfmt(img); + fmt = img_glfmt(img); + type = img_gltype(img); + + gl_gen_textures(1, &tex); + gl_bind_texture(GL_TEXTURE_2D, tex); + gl_tex_parameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl_tex_parameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl_tex_parameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + gl_tex_parameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + gl_tex_image2d(GL_TEXTURE_2D, 0, intfmt, img->width, img->height, 0, fmt, type, img->pixels); + return tex; +} + +unsigned int img_gltexture_load(const char *fname) +{ + struct img_pixmap img; + unsigned int tex; + + img_init(&img); + if(img_load(&img, fname) == -1) { + img_destroy(&img); + return 0; + } + + tex = img_gltexture(&img); + img_destroy(&img); + return tex; +} + +unsigned int img_gltexture_read_file(FILE *fp) +{ + struct img_pixmap img; + unsigned int tex; + + img_init(&img); + if(img_read_file(&img, fp) == -1) { + img_destroy(&img); + return 0; + } + + tex = img_gltexture(&img); + img_destroy(&img); + return tex; +} + +unsigned int img_gltexture_read(struct img_io *io) +{ + struct img_pixmap img; + unsigned int tex; + + img_init(&img); + if(img_read(&img, io) == -1) { + img_destroy(&img); + return 0; + } + + tex = img_gltexture(&img); + img_destroy(&img); + return tex; +} + +#if defined(__unix__) || defined(__APPLE__) +#ifndef __USE_GNU +#define __USE_GNU +#endif + +#include +#endif +#ifdef WIN32 +#include +#endif + +static int load_glfunc(void) +{ +#if defined(__unix__) || defined(__APPLE__) + gl_gen_textures = (gl_gen_textures_func)dlsym(RTLD_DEFAULT, "glGenTextures"); + gl_bind_texture = (gl_bind_texture_func)dlsym(RTLD_DEFAULT, "glBindTexture"); + gl_tex_parameteri = (gl_tex_parameteri_func)dlsym(RTLD_DEFAULT, "glTexParameteri"); + gl_tex_image2d = (gl_tex_image2d_func)dlsym(RTLD_DEFAULT, "glTexImage2D"); +#endif + +#ifdef WIN32 + HMODULE handle = GetModuleHandle(0); + gl_gen_textures = (gl_gen_textures_func)GetProcAddress(handle, "glGenTextures"); + gl_bind_texture = (gl_bind_texture_func)GetProcAddress(handle, "glBindTexture"); + gl_tex_parameteri = (gl_tex_parameteri_func)GetProcAddress(handle, "glTexParameteri"); + gl_tex_image2d = (gl_tex_image2d_func)GetProcAddress(handle, "glTexImage2D"); +#endif + + return (gl_gen_textures && gl_bind_texture && gl_tex_parameteri && gl_tex_image2d) ? 0 : -1; +} diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/imago2/modules.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/imago2/modules.c Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,13 @@ +/* this file is generated by ./configure, do not edit */ +int img_register_jpeg(); +int img_register_png(); +int img_register_ppm(); +int img_register_rgbe(); + +void img_modules_init(void) +{ + img_register_jpeg(); + img_register_png(); + img_register_ppm(); + img_register_rgbe(); +} diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/psys/pattr.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/psys/pattr.c Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,371 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#include +#else +#include +#endif + +#include "pattr.h" +#include "psys_gl.h" + +enum { + OPT_STR, + OPT_NUM, + OPT_NUM_RANGE, + OPT_VEC, + OPT_VEC_RANGE +}; + +struct cfgopt { + char *name; + int type; + long tm; + char *valstr; + vec3_t val, valrng; +}; + +static int init_particle_attr(struct psys_particle_attributes *pattr); +static void destroy_particle_attr(struct psys_particle_attributes *pattr); +static struct cfgopt *get_cfg_opt(const char *line); +static void release_cfg_opt(struct cfgopt *opt); +static char *stripspace(char *str); + +static void *tex_cls; +static unsigned int (*load_texture)(const char*, void*) = psys_gl_load_texture; +static void (*unload_texture)(unsigned int, void*) = psys_gl_unload_texture; + + +void psys_texture_loader(unsigned int (*load)(const char*, void*), void (*unload)(unsigned int, void*), void *cls) +{ + load_texture = load; + unload_texture = unload; + tex_cls = cls; +} + +struct psys_attributes *psys_create_attr(void) +{ + struct psys_attributes *attr = malloc(sizeof *attr); + if(attr) { + if(psys_init_attr(attr) == -1) { + free(attr); + attr = 0; + } + } + return attr; +} + +void psys_free_attr(struct psys_attributes *attr) +{ + psys_destroy_attr(attr); + free(attr); +} + +int psys_init_attr(struct psys_attributes *attr) +{ + memset(attr, 0, sizeof *attr); + + if(psys_init_track3(&attr->spawn_range) == -1) + goto err; + if(psys_init_track(&attr->rate) == -1) + goto err; + if(psys_init_anm_rnd(&attr->life) == -1) + goto err; + if(psys_init_anm_rnd(&attr->size) == -1) + goto err; + if(psys_init_anm_rnd3(&attr->dir) == -1) + goto err; + if(psys_init_track3(&attr->grav) == -1) + goto err; + + if(init_particle_attr(&attr->part_attr) == -1) + goto err; + + attr->max_particles = -1; + + anm_set_track_default(&attr->size.value.trk, 1.0); + anm_set_track_default(&attr->life.value.trk, 1.0); + + return 0; + +err: + psys_destroy_attr(attr); + return -1; +} + + +static int init_particle_attr(struct psys_particle_attributes *pattr) +{ + if(psys_init_track3(&pattr->color) == -1) { + return -1; + } + if(psys_init_track(&pattr->alpha) == -1) { + psys_destroy_track3(&pattr->color); + return -1; + } + if(psys_init_track(&pattr->size) == -1) { + psys_destroy_track3(&pattr->color); + psys_destroy_track(&pattr->alpha); + return -1; + } + + anm_set_track_default(&pattr->color.x, 1.0); + anm_set_track_default(&pattr->color.y, 1.0); + anm_set_track_default(&pattr->color.z, 1.0); + anm_set_track_default(&pattr->alpha.trk, 1.0); + anm_set_track_default(&pattr->size.trk, 1.0); + return 0; +} + + +void psys_destroy_attr(struct psys_attributes *attr) +{ + psys_destroy_track3(&attr->spawn_range); + psys_destroy_track(&attr->rate); + psys_destroy_anm_rnd(&attr->life); + psys_destroy_anm_rnd(&attr->size); + psys_destroy_anm_rnd3(&attr->dir); + psys_destroy_track3(&attr->grav); + + destroy_particle_attr(&attr->part_attr); + + if(attr->tex && unload_texture) { + unload_texture(attr->tex, tex_cls); + } +} + +static void destroy_particle_attr(struct psys_particle_attributes *pattr) +{ + psys_destroy_track3(&pattr->color); + psys_destroy_track(&pattr->alpha); + psys_destroy_track(&pattr->size); +} + +void psys_eval_attr(struct psys_attributes *attr, anm_time_t tm) +{ + psys_eval_track3(&attr->spawn_range, tm); + psys_eval_track(&attr->rate, tm); + psys_eval_anm_rnd(&attr->life, tm); + psys_eval_anm_rnd(&attr->size, tm); + psys_eval_anm_rnd3(&attr->dir, tm); + psys_eval_track3(&attr->grav, tm); +} + +int psys_load_attr(struct psys_attributes *attr, const char *fname) +{ + FILE *fp; + int res; + + if(!fname) { + return -1; + } + + if(!(fp = fopen(fname, "r"))) { + fprintf(stderr, "%s: failed to read file: %s: %s\n", __func__, fname, strerror(errno)); + return -1; + } + res = psys_load_attr_stream(attr, fp); + fclose(fp); + return res; +} + +int psys_load_attr_stream(struct psys_attributes *attr, FILE *fp) +{ + int lineno = 0; + char buf[512]; + struct cfgopt *opt = 0; + + psys_init_attr(attr); + + while(fgets(buf, sizeof buf, fp)) { + + lineno++; + + if(!(opt = get_cfg_opt(buf))) { + continue; + } + + if(strcmp(opt->name, "texture") == 0) { + if(opt->type != OPT_STR) { + goto err; + } + if(!(attr->tex = load_texture(opt->valstr, tex_cls))) { + fprintf(stderr, "failed to load texture: %s\n", opt->valstr); + goto err; + } + + release_cfg_opt(opt); + continue; + } else if(opt->type == OPT_STR) { + fprintf(stderr, "invalid particle config: '%s'\n", opt->name); + goto err; + } + + if(strcmp(opt->name, "spawn_range") == 0) { + psys_set_value3(&attr->spawn_range, opt->tm, opt->val); + } else if(strcmp(opt->name, "rate") == 0) { + psys_set_value(&attr->rate, opt->tm, opt->val.x); + } else if(strcmp(opt->name, "life") == 0) { + psys_set_anm_rnd(&attr->life, opt->tm, opt->val.x, opt->valrng.x); + } else if(strcmp(opt->name, "size") == 0) { + psys_set_anm_rnd(&attr->size, opt->tm, opt->val.x, opt->valrng.x); + } else if(strcmp(opt->name, "dir") == 0) { + psys_set_anm_rnd3(&attr->dir, opt->tm, opt->val, opt->valrng); + } else if(strcmp(opt->name, "grav") == 0) { + psys_set_value3(&attr->grav, opt->tm, opt->val); + } else if(strcmp(opt->name, "drag") == 0) { + attr->drag = opt->val.x; + } else if(strcmp(opt->name, "pcolor") == 0) { + psys_set_value3(&attr->part_attr.color, opt->tm, opt->val); + } else if(strcmp(opt->name, "palpha") == 0) { + psys_set_value(&attr->part_attr.alpha, opt->tm, opt->val.x); + } else if(strcmp(opt->name, "psize") == 0) { + psys_set_value(&attr->part_attr.size, opt->tm, opt->val.x); + } else { + fprintf(stderr, "unrecognized particle config option: %s\n", opt->name); + goto err; + } + + release_cfg_opt(opt); + } + + return 0; + +err: + fprintf(stderr, "Line %d: error parsing particle definition\n", lineno); + release_cfg_opt(opt); + return -1; +} + +static struct cfgopt *get_cfg_opt(const char *line) +{ + char *buf, *tmp; + struct cfgopt *opt; + + line = stripspace((char*)line); + if(line[0] == '#' || !line[0]) { + return 0; /* skip empty lines and comments */ + } + + if(!(opt = malloc(sizeof *opt))) { + return 0; + } + + if(!(opt->valstr = strchr(line, '='))) { + release_cfg_opt(opt); + return 0; + } + *opt->valstr++ = 0; + opt->valstr = stripspace(opt->valstr); + + /* allocate a working buffer on the stack that could fit the current line */ + buf = alloca(strlen(line) + 1); + strcpy(buf, line); + buf = stripspace(buf); + + /* parse the keyframe time specifier if it exists */ + if((tmp = strchr(buf, '('))) { + char *endp; + float tval; + + *tmp++ = 0; + opt->name = strdup(buf); + + tval = strtod(tmp, &endp); + if(endp == tmp) { /* nada ... */ + opt->tm = 0; + } else if(*endp == 's') { /* seconds suffix */ + opt->tm = (long)(tval * 1000.0f); + } else { + opt->tm = (long)tval; + } + } else { + opt->name = strdup(buf); + opt->tm = 0; + } + + if(sscanf(opt->valstr, "[%f %f %f] ~ [%f %f %f]", &opt->val.x, &opt->val.y, &opt->val.z, + &opt->valrng.x, &opt->valrng.y, &opt->valrng.z) == 6) { + /* value is a vector range */ + opt->type = OPT_VEC_RANGE; + + } else if(sscanf(opt->valstr, "%f ~ %f", &opt->val.x, &opt->valrng.x) == 2) { + /* value is a number range */ + opt->type = OPT_NUM_RANGE; + opt->val.y = opt->val.z = opt->val.x; + opt->valrng.y = opt->valrng.z = opt->valrng.x; + + } else if(sscanf(opt->valstr, "[%f %f %f]", &opt->val.x, &opt->val.y, &opt->val.z) == 3) { + /* value is a vector */ + opt->type = OPT_VEC; + opt->valrng.x = opt->valrng.y = opt->valrng.z = 0.0f; + + } else if(sscanf(opt->valstr, "%f", &opt->val.x) == 1) { + /* value is a number */ + opt->type = OPT_NUM; + opt->val.y = opt->val.z = opt->val.x; + opt->valrng.x = opt->valrng.y = opt->valrng.z = 0.0f; + + } else if(sscanf(opt->valstr, "\"%s\"", buf) == 1) { + /* just a string... strip the quotes */ + if(buf[strlen(buf) - 1] == '\"') { + buf[strlen(buf) - 1] = 0; + } + opt->type = OPT_STR; + opt->valstr = strdup(buf); + } else { + /* fuck it ... */ + release_cfg_opt(opt); + return 0; + } + + return opt; +} + +static void release_cfg_opt(struct cfgopt *opt) +{ + if(opt) { + free(opt->name); + } +} + + +int psys_save_attr(struct psys_attributes *attr, const char *fname) +{ + FILE *fp; + int res; + + if(!(fp = fopen(fname, "w"))) { + fprintf(stderr, "%s: failed to write file: %s: %s\n", __func__, fname, strerror(errno)); + return -1; + } + res = psys_save_attr_stream(attr, fp); + fclose(fp); + return res; +} + +int psys_save_attr_stream(struct psys_attributes *attr, FILE *fp) +{ + return -1; /* TODO */ +} + + +static char *stripspace(char *str) +{ + char *end; + + while(*str && isspace(*str)) { + str++; + } + + end = str + strlen(str) - 1; + while(end >= str && isspace(*end)) { + *end-- = 0; + } + return str; +} diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/psys/pattr.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/psys/pattr.h Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,57 @@ +#ifndef PATTR_H_ +#define PATTR_H_ + +#include "pstrack.h" +#include "rndval.h" + +/* the particle attributes vary from 0 to 1 during its lifetime */ +struct psys_particle_attributes { + struct psys_track3 color; + struct psys_track alpha; + struct psys_track size; +}; + +struct psys_attributes { + unsigned int tex; /* OpenGL texture to use for the billboard */ + + struct psys_track3 spawn_range; /* radius of emmiter */ + struct psys_track rate; /* spawn rate particles per sec */ + struct psys_anm_rnd life; /* particle life in seconds */ + struct psys_anm_rnd size; /* base particle size */ + struct psys_anm_rnd3 dir; /* particle shoot direction */ + + struct psys_track3 grav; /* external force (usually gravity) */ + float drag; /* I don't think this needs to animate */ + + /* particle attributes */ + struct psys_particle_attributes part_attr; + + /* limits */ + int max_particles; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +void psys_texture_loader(unsigned int (*load)(const char*, void*), void (*unload)(unsigned int, void*), void *cls); + +struct psys_attributes *psys_create_attr(void); +void psys_free_attr(struct psys_attributes *attr); + +int psys_init_attr(struct psys_attributes *attr); +void psys_destroy_attr(struct psys_attributes *attr); + +void psys_eval_attr(struct psys_attributes *attr, anm_time_t tm); + +int psys_load_attr(struct psys_attributes *attr, const char *fname); +int psys_load_attr_stream(struct psys_attributes *attr, FILE *fp); + +int psys_save_attr(struct psys_attributes *attr, const char *fname); +int psys_save_attr_stream(struct psys_attributes *attr, FILE *fp); + +#ifdef __cplusplus +} +#endif + +#endif /* PATTR_H_ */ diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/psys/pstrack.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/psys/pstrack.c Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,97 @@ +#include "pstrack.h" + +int psys_init_track(struct psys_track *track) +{ + track->cache_tm = ANM_TIME_INVAL; + + if(anm_init_track(&track->trk) == -1) { + return -1; + } + return 0; +} + +void psys_destroy_track(struct psys_track *track) +{ + anm_destroy_track(&track->trk); +} + +int psys_init_track3(struct psys_track3 *track) +{ + track->cache_tm = ANM_TIME_INVAL; + + if(anm_init_track(&track->x) == -1) { + return -1; + } + if(anm_init_track(&track->y) == -1) { + anm_destroy_track(&track->x); + return -1; + } + if(anm_init_track(&track->z) == -1) { + anm_destroy_track(&track->x); + anm_destroy_track(&track->z); + return -1; + } + return 0; +} + +void psys_destroy_track3(struct psys_track3 *track) +{ + anm_destroy_track(&track->x); + anm_destroy_track(&track->y); + anm_destroy_track(&track->z); +} + +void psys_eval_track(struct psys_track *track, anm_time_t tm) +{ + if(track->cache_tm != tm) { + track->cache_tm = tm; + track->cache_val = anm_get_value(&track->trk, tm); + } +} + +void psys_set_value(struct psys_track *track, anm_time_t tm, float v) +{ + anm_set_value(&track->trk, tm, v); + track->cache_tm = ANM_TIME_INVAL; +} + +float psys_get_value(struct psys_track *track, anm_time_t tm) +{ + psys_eval_track(track, tm); + return track->cache_val; +} + +float psys_get_cur_value(struct psys_track *track) +{ + return track->cache_val; +} + + +void psys_eval_track3(struct psys_track3 *track, anm_time_t tm) +{ + if(track->cache_tm != tm) { + track->cache_tm = tm; + track->cache_vec.x = anm_get_value(&track->x, tm); + track->cache_vec.y = anm_get_value(&track->y, tm); + track->cache_vec.z = anm_get_value(&track->z, tm); + } +} + +void psys_set_value3(struct psys_track3 *track, anm_time_t tm, vec3_t v) +{ + anm_set_value(&track->x, tm, v.x); + anm_set_value(&track->y, tm, v.y); + anm_set_value(&track->z, tm, v.z); + track->cache_tm = ANM_TIME_INVAL; +} + +vec3_t psys_get_value3(struct psys_track3 *track, anm_time_t tm) +{ + psys_eval_track3(track, tm); + return track->cache_vec; +} + +vec3_t psys_get_cur_value3(struct psys_track3 *track) +{ + return track->cache_vec; +} diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/psys/pstrack.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/psys/pstrack.h Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,37 @@ +#ifndef PSTRACK_H_ +#define PSTRACK_H_ + +#include +#include + +struct psys_track { + struct anm_track trk; + + anm_time_t cache_tm; + float cache_val; +}; + +struct psys_track3 { + struct anm_track x, y, z; + + anm_time_t cache_tm; + vec3_t cache_vec; +}; + +int psys_init_track(struct psys_track *track); +void psys_destroy_track(struct psys_track *track); + +int psys_init_track3(struct psys_track3 *track); +void psys_destroy_track3(struct psys_track3 *track); + +void psys_eval_track(struct psys_track *track, anm_time_t tm); +void psys_set_value(struct psys_track *track, anm_time_t tm, float v); +float psys_get_value(struct psys_track *track, anm_time_t tm); +float psys_get_cur_value(struct psys_track *track); + +void psys_eval_track3(struct psys_track3 *track, anm_time_t tm); +void psys_set_value3(struct psys_track3 *track, anm_time_t tm, vec3_t v); +vec3_t psys_get_value3(struct psys_track3 *track, anm_time_t tm); +vec3_t psys_get_cur_value3(struct psys_track3 *track); + +#endif /* PSTRACK_H_ */ diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/psys/psys.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/psys/psys.c Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,325 @@ +#include +#include +#include +#include +#include "psys.h" +#include "psys_gl.h" + +static int spawn(struct psys_emitter *em, struct psys_particle *p, void *cls); +static void update_particle(struct psys_emitter *em, struct psys_particle *p, float tm, float dt, void *cls); + +/* particle pool */ +static struct psys_particle *ppool; +static int ppool_size; +static pthread_mutex_t pool_lock = PTHREAD_MUTEX_INITIALIZER; + +static struct psys_particle *palloc(void); +static void pfree(struct psys_particle *p); + +/* --- constructors and shit --- */ + +struct psys_emitter *psys_create(void) +{ + struct psys_emitter *em; + + if(!(em = malloc(sizeof *em))) { + return 0; + } + if(psys_init(em) == -1) { + free(em); + return 0; + } + return em; +} + +void psys_free(struct psys_emitter *em) +{ + psys_destroy(em); + free(em); +} + +int psys_init(struct psys_emitter *em) +{ + memset(em, 0, sizeof *em); + + if(anm_init_node(&em->prs) == -1) { + return -1; + } + if(psys_init_attr(&em->attr) == -1) { + anm_destroy_node(&em->prs); + return -1; + } + + em->spawn = spawn; + em->update = update_particle; + + em->draw = psys_gl_draw; + em->draw_start = psys_gl_draw_start; + em->draw_end = psys_gl_draw_end; + return 0; +} + +void psys_destroy(struct psys_emitter *em) +{ + struct psys_particle *part; + + part = em->plist; + while(part) { + struct psys_particle *tmp = part; + part = part->next; + pfree(tmp); + } + + psys_destroy_attr(&em->attr); +} + +void psys_set_pos(struct psys_emitter *em, vec3_t pos, float tm) +{ + anm_set_position(&em->prs, pos, ANM_SEC2TM(tm)); +} + +void psys_set_rot(struct psys_emitter *em, quat_t rot, float tm) +{ + anm_set_rotation(&em->prs, rot, ANM_SEC2TM(tm)); +} + +void psys_set_pivot(struct psys_emitter *em, vec3_t pivot) +{ + anm_set_pivot(&em->prs, pivot); +} + +vec3_t psys_get_pos(struct psys_emitter *em, float tm) +{ + return anm_get_node_position(&em->prs, ANM_SEC2TM(tm)); +} + +quat_t psys_get_rot(struct psys_emitter *em, float tm) +{ + return anm_get_node_rotation(&em->prs, ANM_SEC2TM(tm)); +} + +vec3_t psys_get_pivot(struct psys_emitter *em) +{ + return anm_get_pivot(&em->prs); +} + +void psys_clear_collision_planes(struct psys_emitter *em) +{ + struct psys_plane *plane; + + plane = em->planes; + while(plane) { + struct psys_plane *tmp = plane; + plane = plane->next; + free(tmp); + } +} + +int psys_add_collision_plane(struct psys_emitter *em, plane_t plane, float elast) +{ + struct psys_plane *node; + + if(!(node = malloc(sizeof *node))) { + return -1; + } + node->p = plane; + node->elasticity = elast; + node->next = em->planes; + em->planes = node; + return 0; +} + +void psys_add_particle(struct psys_emitter *em, struct psys_particle *p) +{ + p->next = em->plist; + em->plist = p; + + em->pcount++; +} + +void psys_spawn_func(struct psys_emitter *em, psys_spawn_func_t func, void *cls) +{ + em->spawn = func; + em->spawn_cls = cls; +} + +void psys_update_func(struct psys_emitter *em, psys_update_func_t func, void *cls) +{ + em->update = func; + em->upd_cls = cls; +} + +void psys_draw_func(struct psys_emitter *em, psys_draw_func_t draw, + psys_draw_start_func_t start, psys_draw_end_func_t end, void *cls) +{ + em->draw = draw; + em->draw_start = start; + em->draw_end = end; + em->draw_cls = cls; +} + +/* --- update and render --- */ + +void psys_update(struct psys_emitter *em, float tm) +{ + float dt, spawn_dt, spawn_tm; + int i, spawn_count; + struct psys_particle *p, pdummy; + anm_time_t atm = ANM_SEC2TM(tm); + + assert(em->spawn && em->update); + + dt = tm - em->last_update; + if(dt <= 0.0) { + return; + } + + psys_eval_attr(&em->attr, atm); + + /* how many particles to spawn for this interval ? */ + em->spawn_acc += psys_get_cur_value(&em->attr.rate) * dt; + if(em->spawn_acc >= 1.0) { + spawn_count = em->spawn_acc; + em->spawn_acc = fmod(em->spawn_acc, 1.0); + } else { + spawn_count = 0; + } + + spawn_dt = dt / (float)spawn_count; + spawn_tm = em->last_update; + for(i=0; iattr.max_particles >= 0 && em->pcount >= em->attr.max_particles) { + break; + } + + /* update emitter position for this spawning */ + em->cur_pos = anm_get_position(&em->prs, ANM_SEC2TM(spawn_tm)); + + if(!(p = palloc())) { + return; + } + if(em->spawn(em, p, em->spawn_cls) == -1) { + pfree(p); + } + spawn_tm += spawn_dt; + } + + /* update all particles */ + p = em->plist; + while(p) { + em->update(em, p, tm, dt, em->upd_cls); + p = p->next; + } + + /* cleanup dead particles */ + pdummy.next = em->plist; + p = &pdummy; + while(p->next) { + if(p->next->life <= 0) { + struct psys_particle *tmp = p->next; + p->next = p->next->next; + pfree(tmp); + em->pcount--; + } else { + p = p->next; + } + } + em->plist = pdummy.next; + + em->last_update = tm; + + /*printf("particles: %5d\r", em->pcount);*/ +} + +void psys_draw(struct psys_emitter *em) +{ + struct psys_particle *p; + + if(em->draw_start) { + em->draw_start(em, em->draw_cls); + } + + p = em->plist; + while(p) { + em->draw(em, p, em->draw_cls); + p = p->next; + } + + if(em->draw_end) { + em->draw_end(em, em->draw_cls); + } +} + +static int spawn(struct psys_emitter *em, struct psys_particle *p, void *cls) +{ + struct psys_rnd3 rpos; + rpos.value = em->cur_pos; + rpos.range = psys_get_cur_value3(&em->attr.spawn_range); + + p->pos = psys_eval_rnd3(&rpos); + p->vel = psys_eval_anm_rnd3(&em->attr.dir, PSYS_EVAL_CUR); + p->base_size = psys_eval_anm_rnd(&em->attr.size, PSYS_EVAL_CUR); + p->max_life = p->life = psys_eval_anm_rnd(&em->attr.life, PSYS_EVAL_CUR); + + p->pattr = &em->attr.part_attr; + + psys_add_particle(em, p); + return 0; +} + +static void update_particle(struct psys_emitter *em, struct psys_particle *p, float tm, float dt, void *cls) +{ + vec3_t accel, grav; + anm_time_t t; + + grav = psys_get_cur_value3(&em->attr.grav); + + accel.x = grav.x - p->vel.x * em->attr.drag; + accel.y = grav.y - p->vel.y * em->attr.drag; + accel.z = grav.z - p->vel.z * em->attr.drag; + + p->vel.x += accel.x * dt; + p->vel.y += accel.y * dt; + p->vel.z += accel.z * dt; + + p->pos.x += p->vel.x * dt; + p->pos.y += p->vel.y * dt; + p->pos.z += p->vel.z * dt; + + /* update particle attributes */ + t = (anm_time_t)(1000.0 * (p->max_life - p->life) / p->max_life); + + p->color = psys_get_value3(&p->pattr->color, t); + p->alpha = psys_get_value(&p->pattr->alpha, t); + p->size = p->base_size * psys_get_value(&p->pattr->size, t); + + p->life -= dt; +} + +/* --- particle allocation pool --- */ + +static struct psys_particle *palloc(void) +{ + struct psys_particle *p; + + pthread_mutex_lock(&pool_lock); + if(ppool) { + p = ppool; + ppool = ppool->next; + ppool_size--; + } else { + p = malloc(sizeof *p); + } + pthread_mutex_unlock(&pool_lock); + + return p; +} + +static void pfree(struct psys_particle *p) +{ + pthread_mutex_lock(&pool_lock); + p->next = ppool; + ppool = p; + ppool_size++; + pthread_mutex_unlock(&pool_lock); +} diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/psys/psys.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/psys/psys.h Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,111 @@ +#ifndef LIBPSYS_H_ +#define LIBPSYS_H_ + +#include +#include +#include "rndval.h" +#include "pattr.h" + +struct psys_particle; +struct psys_emitter; + +typedef int (*psys_spawn_func_t)(struct psys_emitter*, struct psys_particle*, void*); +typedef void (*psys_update_func_t)(struct psys_emitter*, struct psys_particle*, float, float, void*); + +typedef void (*psys_draw_func_t)(struct psys_emitter*, struct psys_particle*, void*); +typedef void (*psys_draw_start_func_t)(struct psys_emitter*, void*); +typedef void (*psys_draw_end_func_t)(struct psys_emitter*, void*); + + +struct psys_plane { + plane_t p; + float elasticity; + struct psys_plane *next; +}; + + +struct psys_emitter { + struct anm_node prs; + vec3_t cur_pos; + + struct psys_attributes attr; + + /* list of active particles */ + struct psys_particle *plist; + int pcount; /* number of active particles */ + + /* list of collision planes */ + struct psys_plane *planes; + + /* custom spawn closure */ + void *spawn_cls; + psys_spawn_func_t spawn; + + /* custom particle update closure */ + void *upd_cls; + psys_update_func_t update; + + /* custom draw closure */ + void *draw_cls; + psys_draw_func_t draw; + psys_draw_start_func_t draw_start; + psys_draw_end_func_t draw_end; + + float spawn_acc; /* partial spawn accumulator */ + float last_update; /* last update time (to calc dt) */ +}; + + +struct psys_particle { + vec3_t pos, vel; + float life, max_life; + float base_size; + + struct psys_particle_attributes *pattr; + + /* current particle attr values calculated during update */ + vec3_t color; + float alpha, size; + + struct psys_particle *next; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +struct psys_emitter *psys_create(void); +void psys_free(struct psys_emitter *em); + +int psys_init(struct psys_emitter *em); +void psys_destroy(struct psys_emitter *em); + +/* set properties */ +void psys_set_pos(struct psys_emitter *em, vec3_t pos, float tm); +void psys_set_rot(struct psys_emitter *em, quat_t rot, float tm); +void psys_set_pivot(struct psys_emitter *em, vec3_t pivot); + +vec3_t psys_get_pos(struct psys_emitter *em, float tm); +quat_t psys_get_rot(struct psys_emitter *em, float tm); +vec3_t psys_get_pivot(struct psys_emitter *em); + +void psys_clear_collision_planes(struct psys_emitter *em); +int psys_add_collision_plane(struct psys_emitter *em, plane_t plane, float elast); + +void psys_add_particle(struct psys_emitter *em, struct psys_particle *p); + +void psys_spawn_func(struct psys_emitter *em, psys_spawn_func_t func, void *cls); +void psys_update_func(struct psys_emitter *em, psys_update_func_t func, void *cls); +void psys_draw_func(struct psys_emitter *em, psys_draw_func_t draw, + psys_draw_start_func_t start, psys_draw_end_func_t end, void *cls); + +/* update and render */ + +void psys_update(struct psys_emitter *em, float tm); +void psys_draw(struct psys_emitter *em); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBPSYS_H_ */ diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/psys/psys_gl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/psys/psys_gl.c Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,106 @@ +#include +#include +#include + +#ifndef __APPLE__ +#ifdef WIN32 +#include +#endif + +#include +#else +#include +#endif + +#include +#include "psys.h" +#include "psys_gl.h" + +void psys_gl_draw_start(struct psys_emitter *em, void *cls) +{ + glPushAttrib(GL_ENABLE_BIT); + glDisable(GL_LIGHTING); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + + if(em->attr.tex) { + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, em->attr.tex); + } + + glDepthMask(0); +} + +void psys_gl_draw(struct psys_emitter *em, struct psys_particle *p, void *cls) +{ + float hsz = p->size / 2.0; + + float xform[16]; + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + + glTranslatef(p->pos.x, p->pos.y, p->pos.z); + + glGetFloatv(GL_MODELVIEW_MATRIX, xform); + xform[0] = xform[5] = xform[10] = 1.0; + xform[1] = xform[2] = xform[4] = xform[6] = xform[8] = xform[9] = 0.0; + glLoadMatrixf(xform); + + glBegin(GL_QUADS); + glColor4f(p->color.x, p->color.y, p->color.z, p->alpha); + + glTexCoord2f(0, 0); + glVertex2f(-hsz, -hsz); + + glTexCoord2f(1, 0); + glVertex2f(hsz, -hsz); + + glTexCoord2f(1, 1); + glVertex2f(hsz, hsz); + + glTexCoord2f(0, 1); + glVertex2f(-hsz, hsz); + glEnd(); + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); +} + +void psys_gl_draw_end(struct psys_emitter *em, void *cls) +{ + glDepthMask(1); + glPopAttrib(); +} + + +unsigned int psys_gl_load_texture(const char *fname, void *cls) +{ + unsigned int tex; + void *pixels; + int xsz, ysz; + + if(!(pixels = img_load_pixels(fname, &xsz, &ysz, IMG_FMT_RGBA32))) { + return 0; + } + printf("%s: creating texture %s (%dx%d)\n", __func__, fname, xsz, ysz); + + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, xsz, ysz, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + assert(glGetError() == GL_NO_ERROR); + + img_free_pixels(pixels); + return tex; +} + +void psys_gl_unload_texture(unsigned int tex, void *cls) +{ + glDeleteTextures(1, &tex); +} diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/psys/psys_gl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/psys/psys_gl.h Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,14 @@ +#ifndef PSYS_GL_H_ +#define PSYS_GL_H_ + +struct psys_particle; +struct psys_emitter; + +void psys_gl_draw_start(struct psys_emitter *em, void *cls); +void psys_gl_draw(struct psys_emitter *em, struct psys_particle *p, void *cls); +void psys_gl_draw_end(struct psys_emitter *em, void *cls); + +unsigned int psys_gl_load_texture(const char *fname, void *cls); +void psys_gl_unload_texture(unsigned int tex, void *cls); + +#endif /* PSYS_GL_H_ */ diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/psys/rndval.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/psys/rndval.c Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,105 @@ +#include +#include "rndval.h" + +int psys_init_anm_rnd(struct psys_anm_rnd *r) +{ + if(psys_init_track(&r->value) == -1) { + return -1; + } + if(psys_init_track(&r->range) == -1) { + psys_destroy_track(&r->value); + return -1; + } + return 0; +} + +void psys_destroy_anm_rnd(struct psys_anm_rnd *r) +{ + psys_destroy_track(&r->value); + psys_destroy_track(&r->range); +} + +int psys_init_anm_rnd3(struct psys_anm_rnd3 *r) +{ + if(psys_init_track3(&r->value) == -1) { + return -1; + } + if(psys_init_track3(&r->range) == -1) { + psys_destroy_track3(&r->value); + return -1; + } + return 0; +} + +void psys_destroy_anm_rnd3(struct psys_anm_rnd3 *r) +{ + psys_destroy_track3(&r->value); + psys_destroy_track3(&r->range); +} + + +void psys_set_rnd(struct psys_rnd *r, float val, float range) +{ + r->value = val; + r->range = range; +} + +void psys_set_rnd3(struct psys_rnd3 *r, vec3_t val, vec3_t range) +{ + r->value = val; + r->range = range; +} + +void psys_set_anm_rnd(struct psys_anm_rnd *r, anm_time_t tm, float val, float range) +{ + psys_set_value(&r->value, tm, val); + psys_set_value(&r->range, tm, range); +} + +void psys_set_anm_rnd3(struct psys_anm_rnd3 *r, anm_time_t tm, vec3_t val, vec3_t range) +{ + psys_set_value3(&r->value, tm, val); + psys_set_value3(&r->range, tm, range); +} + + +float psys_eval_rnd(struct psys_rnd *r) +{ + return r->value + r->range * (float)rand() / (float)RAND_MAX - 0.5 * r->range; +} + +vec3_t psys_eval_rnd3(struct psys_rnd3 *r) +{ + vec3_t res; + res.x = r->value.x + r->range.x * (float)rand() / (float)RAND_MAX - 0.5 * r->range.x; + res.y = r->value.y + r->range.y * (float)rand() / (float)RAND_MAX - 0.5 * r->range.y; + res.z = r->value.z + r->range.z * (float)rand() / (float)RAND_MAX - 0.5 * r->range.z; + return res; +} + + +float psys_eval_anm_rnd(struct psys_anm_rnd *r, anm_time_t tm) +{ + struct psys_rnd tmp; + if(tm == ANM_TIME_INVAL) { + tmp.value = psys_get_cur_value(&r->value); + tmp.range = psys_get_cur_value(&r->range); + } else { + tmp.value = psys_get_value(&r->value, tm); + tmp.range = psys_get_value(&r->range, tm); + } + return psys_eval_rnd(&tmp); +} + +vec3_t psys_eval_anm_rnd3(struct psys_anm_rnd3 *r, anm_time_t tm) +{ + struct psys_rnd3 tmp; + if(tm == ANM_TIME_INVAL) { + tmp.value = psys_get_cur_value3(&r->value); + tmp.range = psys_get_cur_value3(&r->range); + } else { + tmp.value = psys_get_value3(&r->value, tm); + tmp.range = psys_get_value3(&r->range, tm); + } + return psys_eval_rnd3(&tmp); +} diff -r fc2b3d06d07c -r 2560a7ab0243 prototype/psys/rndval.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/prototype/psys/rndval.h Sun Oct 07 02:04:00 2012 +0300 @@ -0,0 +1,43 @@ +#ifndef RNDVAL_H_ +#define RNDVAL_H_ + +#include +#include "pstrack.h" + +struct psys_rnd { + float value, range; +}; + +struct psys_rnd3 { + vec3_t value, range; +}; + +struct psys_anm_rnd { + struct psys_track value, range; +}; + +struct psys_anm_rnd3 { + struct psys_track3 value, range; +}; + +#define PSYS_EVAL_CUR ANM_TIME_INVAL + +int psys_init_anm_rnd(struct psys_anm_rnd *v); +void psys_destroy_anm_rnd(struct psys_anm_rnd *v); +int psys_init_anm_rnd3(struct psys_anm_rnd3 *v); +void psys_destroy_anm_rnd3(struct psys_anm_rnd3 *v); + +void psys_set_rnd(struct psys_rnd *r, float val, float range); +void psys_set_rnd3(struct psys_rnd3 *r, vec3_t val, vec3_t range); + +void psys_set_anm_rnd(struct psys_anm_rnd *r, anm_time_t tm, float val, float range); +void psys_set_anm_rnd3(struct psys_anm_rnd3 *r, anm_time_t tm, vec3_t val, vec3_t range); + +float psys_eval_rnd(struct psys_rnd *r); +vec3_t psys_eval_rnd3(struct psys_rnd3 *r); + +float psys_eval_anm_rnd(struct psys_anm_rnd *r, anm_time_t tm); +vec3_t psys_eval_anm_rnd3(struct psys_anm_rnd3 *r, anm_time_t tm); + + +#endif /* RNDVAL_H_ */