dungeon_crawler

changeset 67:2560a7ab0243

internalized libanim, libimago2, and libpsys
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 07 Oct 2012 02:04:00 +0300
parents fc2b3d06d07c
children 1141a325cb73
files prototype/Makefile.in prototype/anim/anim.c prototype/anim/anim.h prototype/anim/config.h prototype/anim/dynarr.c prototype/anim/dynarr.h prototype/anim/track.c prototype/anim/track.h prototype/data/fire.psys prototype/imago2/conv.c prototype/imago2/file_jpeg.c prototype/imago2/file_png.c prototype/imago2/file_ppm.c prototype/imago2/file_rgbe.c prototype/imago2/ftype_module.c prototype/imago2/ftype_module.h prototype/imago2/imago2.c prototype/imago2/imago2.h prototype/imago2/imago_gl.c prototype/imago2/modules.c prototype/psys/pattr.c prototype/psys/pattr.h prototype/psys/pstrack.c prototype/psys/pstrack.h prototype/psys/psys.c prototype/psys/psys.h prototype/psys/psys_gl.c prototype/psys/psys_gl.h prototype/psys/rndval.c prototype/psys/rndval.h
diffstat 30 files changed, 4780 insertions(+), 5 deletions(-) [+]
line diff
     1.1 --- a/prototype/Makefile.in	Tue Oct 02 13:42:18 2012 +0300
     1.2 +++ b/prototype/Makefile.in	Sun Oct 07 02:04:00 2012 +0300
     1.3 @@ -1,5 +1,8 @@
     1.4  csrc = $(wildcard src/*.c) \
     1.5  	   $(wildcard vmath/*.c) \
     1.6 +	   $(wildcard imago2/*.c) \
     1.7 +	   $(wildcard anim/*.c) \
     1.8 +	   $(wildcard psys/*.c) \
     1.9  	   $(wildcard drawtext/*.c) \
    1.10  	   $(wildcard kdtree/*.c)
    1.11  
    1.12 @@ -13,13 +16,16 @@
    1.13  
    1.14  warn = -Wall -Wno-format-extra-args -Wno-char-subscripts
    1.15  
    1.16 -inc = -I. -Isrc -Ivmath -Idrawtext -Ikdtree `pkg-config --cflags freetype2`
    1.17 +inc = -I. -Isrc -Ivmath -Iimago2 -Idrawtext -Ikdtree `pkg-config --cflags freetype2`
    1.18  
    1.19  CFLAGS = -pedantic -fno-strict-aliasing $(warn) $(dbg) $(prof) $(opt) $(inc)
    1.20  CXXFLAGS = $(CFLAGS) $(cxx11_cflags)
    1.21 -LDFLAGS = $(cxx11_ldflags) $(prof) $(libgl) $(libal) -lvorbisfile -lm -lassimp -limago -lpsys `pkg-config --libs freetype2`
    1.22 +LDFLAGS = $(cxx11_ldflags) $(prof) $(libgl) $(libal) -lvorbisfile -lm -lassimp \
    1.23 +		  -ljpeg -lpng -lz `pkg-config --libs freetype2`
    1.24  
    1.25  ifeq ($(shell uname -s), Darwin)
    1.26 +	CC = clang
    1.27 +	CXX = clang++
    1.28  	libgl = -framework OpenGL -framework GLUT -lglew
    1.29  	libal = -framework OpenAL
    1.30  else
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/prototype/anim/anim.c	Sun Oct 07 02:04:00 2012 +0300
     2.3 @@ -0,0 +1,394 @@
     2.4 +#include <limits.h>
     2.5 +#include <assert.h>
     2.6 +#include "anim.h"
     2.7 +#include "dynarr.h"
     2.8 +
     2.9 +int anm_init_node(struct anm_node *node)
    2.10 +{
    2.11 +	int i, j;
    2.12 +	static const float defaults[] = {
    2.13 +		0.0f, 0.0f, 0.0f,		/* default position */
    2.14 +		0.0f, 0.0f, 0.0f, 1.0f,	/* default rotation quat */
    2.15 +		1.0f, 1.0f, 1.0f		/* default scale factor */
    2.16 +	};
    2.17 +
    2.18 +	memset(node, 0, sizeof *node);
    2.19 +
    2.20 +	for(i=0; i<ANM_NUM_TRACKS; i++) {
    2.21 +		if(anm_init_track(node->tracks + i) == -1) {
    2.22 +			for(j=0; j<i; j++) {
    2.23 +				anm_destroy_track(node->tracks + i);
    2.24 +			}
    2.25 +		}
    2.26 +		anm_set_track_default(node->tracks + i, defaults[i]);
    2.27 +	}
    2.28 +	return 0;
    2.29 +}
    2.30 +
    2.31 +void anm_destroy_node(struct anm_node *node)
    2.32 +{
    2.33 +	int i;
    2.34 +	free(node->name);
    2.35 +
    2.36 +	for(i=0; i<ANM_NUM_TRACKS; i++) {
    2.37 +		anm_destroy_track(node->tracks + i);
    2.38 +	}
    2.39 +}
    2.40 +
    2.41 +void anm_destroy_node_tree(struct anm_node *tree)
    2.42 +{
    2.43 +	struct anm_node *c, *tmp;
    2.44 +
    2.45 +	if(!tree) return;
    2.46 +
    2.47 +	c = tree->child;
    2.48 +	while(c) {
    2.49 +		tmp = c;
    2.50 +		c = c->next;
    2.51 +
    2.52 +		anm_destroy_node_tree(tmp);
    2.53 +	}
    2.54 +	anm_destroy_node(tree);
    2.55 +}
    2.56 +
    2.57 +struct anm_node *anm_create_node(void)
    2.58 +{
    2.59 +	struct anm_node *n;
    2.60 +
    2.61 +	if((n = malloc(sizeof *n))) {
    2.62 +		if(anm_init_node(n) == -1) {
    2.63 +			free(n);
    2.64 +			return 0;
    2.65 +		}
    2.66 +	}
    2.67 +	return n;
    2.68 +}
    2.69 +
    2.70 +void anm_free_node(struct anm_node *node)
    2.71 +{
    2.72 +	anm_destroy_node(node);
    2.73 +	free(node);
    2.74 +}
    2.75 +
    2.76 +void anm_free_node_tree(struct anm_node *tree)
    2.77 +{
    2.78 +	struct anm_node *c, *tmp;
    2.79 +
    2.80 +	if(!tree) return;
    2.81 +
    2.82 +	c = tree->child;
    2.83 +	while(c) {
    2.84 +		tmp = c;
    2.85 +		c = c->next;
    2.86 +
    2.87 +		anm_free_node_tree(tmp);
    2.88 +	}
    2.89 +
    2.90 +	anm_free_node(tree);
    2.91 +}
    2.92 +
    2.93 +int anm_set_node_name(struct anm_node *node, const char *name)
    2.94 +{
    2.95 +	char *str;
    2.96 +
    2.97 +	if(!(str = malloc(strlen(name) + 1))) {
    2.98 +		return -1;
    2.99 +	}
   2.100 +	strcpy(str, name);
   2.101 +	free(node->name);
   2.102 +	node->name = str;
   2.103 +	return 0;
   2.104 +}
   2.105 +
   2.106 +const char *anm_get_node_name(struct anm_node *node)
   2.107 +{
   2.108 +	return node->name ? node->name : "";
   2.109 +}
   2.110 +
   2.111 +void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in)
   2.112 +{
   2.113 +	int i;
   2.114 +
   2.115 +	for(i=0; i<ANM_NUM_TRACKS; i++) {
   2.116 +		anm_set_track_interpolator(node->tracks + i, in);
   2.117 +	}
   2.118 +}
   2.119 +
   2.120 +void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex)
   2.121 +{
   2.122 +	int i;
   2.123 +
   2.124 +	for(i=0; i<ANM_NUM_TRACKS; i++) {
   2.125 +		anm_set_track_extrapolator(node->tracks + i, ex);
   2.126 +	}
   2.127 +}
   2.128 +
   2.129 +void anm_link_node(struct anm_node *p, struct anm_node *c)
   2.130 +{
   2.131 +	c->next = p->child;
   2.132 +	p->child = c;
   2.133 +
   2.134 +	c->parent = p;
   2.135 +}
   2.136 +
   2.137 +int anm_unlink_node(struct anm_node *p, struct anm_node *c)
   2.138 +{
   2.139 +	struct anm_node *iter;
   2.140 +
   2.141 +	if(p->child == c) {
   2.142 +		p->child = c->next;
   2.143 +		c->next = 0;
   2.144 +		return 0;
   2.145 +	}
   2.146 +
   2.147 +	iter = p->child;
   2.148 +	while(iter->next) {
   2.149 +		if(iter->next == c) {
   2.150 +			iter->next = c->next;
   2.151 +			c->next = 0;
   2.152 +			return 0;
   2.153 +		}
   2.154 +	}
   2.155 +	return -1;
   2.156 +}
   2.157 +
   2.158 +void anm_set_position(struct anm_node *node, vec3_t pos, anm_time_t tm)
   2.159 +{
   2.160 +	anm_set_value(node->tracks + ANM_TRACK_POS_X, tm, pos.x);
   2.161 +	anm_set_value(node->tracks + ANM_TRACK_POS_Y, tm, pos.y);
   2.162 +	anm_set_value(node->tracks + ANM_TRACK_POS_Z, tm, pos.z);
   2.163 +}
   2.164 +
   2.165 +vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm)
   2.166 +{
   2.167 +	vec3_t v;
   2.168 +	v.x = anm_get_value(node->tracks + ANM_TRACK_POS_X, tm);
   2.169 +	v.y = anm_get_value(node->tracks + ANM_TRACK_POS_Y, tm);
   2.170 +	v.z = anm_get_value(node->tracks + ANM_TRACK_POS_Z, tm);
   2.171 +	return v;
   2.172 +}
   2.173 +
   2.174 +void anm_set_rotation(struct anm_node *node, quat_t rot, anm_time_t tm)
   2.175 +{
   2.176 +	anm_set_value(node->tracks + ANM_TRACK_ROT_X, tm, rot.x);
   2.177 +	anm_set_value(node->tracks + ANM_TRACK_ROT_Y, tm, rot.y);
   2.178 +	anm_set_value(node->tracks + ANM_TRACK_ROT_Z, tm, rot.z);
   2.179 +	anm_set_value(node->tracks + ANM_TRACK_ROT_W, tm, rot.w);
   2.180 +}
   2.181 +
   2.182 +quat_t anm_get_node_rotation(struct anm_node *node, anm_time_t tm)
   2.183 +{
   2.184 +	int idx0, idx1, last_idx;
   2.185 +	anm_time_t tstart, tend;
   2.186 +	float t, dt;
   2.187 +	struct anm_track *track_x, *track_y, *track_z, *track_w;
   2.188 +	quat_t q, q1, q2;
   2.189 +
   2.190 +	track_x = node->tracks + ANM_TRACK_ROT_X;
   2.191 +	track_y = node->tracks + ANM_TRACK_ROT_Y;
   2.192 +	track_z = node->tracks + ANM_TRACK_ROT_Z;
   2.193 +	track_w = node->tracks + ANM_TRACK_ROT_W;
   2.194 +
   2.195 +	if(!track_x->count) {
   2.196 +		q.x = track_x->def_val;
   2.197 +		q.y = track_y->def_val;
   2.198 +		q.z = track_z->def_val;
   2.199 +		q.w = track_w->def_val;
   2.200 +		return q;
   2.201 +	}
   2.202 +
   2.203 +	last_idx = track_x->count - 1;
   2.204 +
   2.205 +	tstart = track_x->keys[0].time;
   2.206 +	tend = track_x->keys[last_idx].time;
   2.207 +	tm = anm_remap_time(track_x, tm, tstart, tend);
   2.208 +
   2.209 +	idx0 = anm_get_key_interval(track_x, tm);
   2.210 +	assert(idx0 >= 0 && idx0 < track_x->count);
   2.211 +	idx1 = idx0 + 1;
   2.212 +
   2.213 +	dt = (float)(track_x->keys[idx1].time - track_x->keys[idx0].time);
   2.214 +	t = (float)(tm - track_x->keys[idx0].time) / dt;
   2.215 +
   2.216 +	q1.x = track_x->keys[idx0].val;
   2.217 +	q1.y = track_y->keys[idx0].val;
   2.218 +	q1.z = track_z->keys[idx0].val;
   2.219 +	q1.w = track_w->keys[idx0].val;
   2.220 +
   2.221 +	q2.x = track_x->keys[idx1].val;
   2.222 +	q2.y = track_y->keys[idx1].val;
   2.223 +	q2.z = track_z->keys[idx1].val;
   2.224 +	q2.w = track_w->keys[idx1].val;
   2.225 +
   2.226 +	return quat_slerp(q1, q2, t);
   2.227 +}
   2.228 +
   2.229 +void anm_set_scaling(struct anm_node *node, vec3_t scl, anm_time_t tm)
   2.230 +{
   2.231 +	anm_set_value(node->tracks + ANM_TRACK_SCL_X, tm, scl.x);
   2.232 +	anm_set_value(node->tracks + ANM_TRACK_SCL_Y, tm, scl.y);
   2.233 +	anm_set_value(node->tracks + ANM_TRACK_SCL_Z, tm, scl.z);
   2.234 +}
   2.235 +
   2.236 +vec3_t anm_get_node_scaling(struct anm_node *node, anm_time_t tm)
   2.237 +{
   2.238 +	vec3_t v;
   2.239 +	v.x = anm_get_value(node->tracks + ANM_TRACK_SCL_X, tm);
   2.240 +	v.y = anm_get_value(node->tracks + ANM_TRACK_SCL_Y, tm);
   2.241 +	v.z = anm_get_value(node->tracks + ANM_TRACK_SCL_Z, tm);
   2.242 +	return v;
   2.243 +}
   2.244 +
   2.245 +
   2.246 +vec3_t anm_get_position(struct anm_node *node, anm_time_t tm)
   2.247 +{
   2.248 +	mat4_t xform;
   2.249 +	vec3_t pos = {0.0, 0.0, 0.0};
   2.250 +
   2.251 +	if(!node->parent) {
   2.252 +		return anm_get_node_position(node, tm);
   2.253 +	}
   2.254 +
   2.255 +	anm_get_matrix(node, xform, tm);
   2.256 +	return v3_transform(pos, xform);
   2.257 +}
   2.258 +
   2.259 +quat_t anm_get_rotation(struct anm_node *node, anm_time_t tm)
   2.260 +{
   2.261 +	quat_t rot, prot;
   2.262 +	rot = anm_get_node_rotation(node, tm);
   2.263 +
   2.264 +	if(!node->parent) {
   2.265 +		return rot;
   2.266 +	}
   2.267 +
   2.268 +	prot = anm_get_rotation(node->parent, tm);
   2.269 +	return quat_mul(prot, rot);
   2.270 +}
   2.271 +
   2.272 +vec3_t anm_get_scaling(struct anm_node *node, anm_time_t tm)
   2.273 +{
   2.274 +	vec3_t s, ps;
   2.275 +	s = anm_get_node_scaling(node, tm);
   2.276 +
   2.277 +	if(!node->parent) {
   2.278 +		return s;
   2.279 +	}
   2.280 +
   2.281 +	ps = anm_get_scaling(node->parent, tm);
   2.282 +	return v3_mul(s, ps);
   2.283 +}
   2.284 +
   2.285 +void anm_set_pivot(struct anm_node *node, vec3_t piv)
   2.286 +{
   2.287 +	node->pivot = piv;
   2.288 +}
   2.289 +
   2.290 +vec3_t anm_get_pivot(struct anm_node *node)
   2.291 +{
   2.292 +	return node->pivot;
   2.293 +}
   2.294 +
   2.295 +void anm_get_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm)
   2.296 +{
   2.297 +#ifdef ANIM_THREAD_SAFE
   2.298 +	/* XXX we're holding the mutex for way too anm_time_t... but it looks like the
   2.299 +	 * alternative would be to lock/unlock twice which might be worse.
   2.300 +	 */
   2.301 +	pthread_mutex_lock(node->cache_mutex);
   2.302 +#endif
   2.303 +
   2.304 +	if(node->cache_time != tm) {
   2.305 +		mat4_t tmat, rmat, smat, pivmat, neg_pivmat;
   2.306 +		vec3_t pos, scale;
   2.307 +		quat_t rot;
   2.308 +
   2.309 +		m4_identity(tmat);
   2.310 +		/*no need to m4_identity(rmat); quat_to_mat4 sets this properly */
   2.311 +		m4_identity(smat);
   2.312 +		m4_identity(pivmat);
   2.313 +		m4_identity(neg_pivmat);
   2.314 +
   2.315 +		pos = anm_get_node_position(node, tm);
   2.316 +		rot = anm_get_node_rotation(node, tm);
   2.317 +		scale = anm_get_node_scaling(node, tm);
   2.318 +
   2.319 +		m4_translate(pivmat, node->pivot.x, node->pivot.y, node->pivot.z);
   2.320 +		m4_translate(neg_pivmat, -node->pivot.x, -node->pivot.y, -node->pivot.z);
   2.321 +
   2.322 +		m4_translate(tmat, pos.x, pos.y, pos.z);
   2.323 +		quat_to_mat4(rmat, rot);
   2.324 +		m4_translate(smat, scale.x, scale.y, scale.z);
   2.325 +
   2.326 +		/* ok this would look nicer in C++ */
   2.327 +		m4_mult(node->cache_matrix, pivmat, tmat);
   2.328 +		m4_mult(node->cache_matrix, node->cache_matrix, rmat);
   2.329 +		m4_mult(node->cache_matrix, node->cache_matrix, smat);
   2.330 +		m4_mult(node->cache_matrix, node->cache_matrix, neg_pivmat);
   2.331 +
   2.332 +		if(node->parent) {
   2.333 +			mat4_t parent_mat;
   2.334 +
   2.335 +			anm_get_matrix(node->parent, mat, tm);
   2.336 +			m4_mult(node->cache_matrix, parent_mat, node->cache_matrix);
   2.337 +		}
   2.338 +		node->cache_time = tm;
   2.339 +	}
   2.340 +	m4_copy(mat, node->cache_matrix);
   2.341 +
   2.342 +#ifdef ANIM_THREAD_SAFE
   2.343 +	pthread_mutex_unlock(node->cache_mutex);
   2.344 +#endif
   2.345 +}
   2.346 +
   2.347 +anm_time_t anm_get_start_time(struct anm_node *node)
   2.348 +{
   2.349 +	int i;
   2.350 +	struct anm_node *c;
   2.351 +	anm_time_t res = LONG_MAX;
   2.352 +
   2.353 +	for(i=0; i<ANM_NUM_TRACKS; i++) {
   2.354 +		if(node->tracks[i].count) {
   2.355 +			anm_time_t tm = node->tracks[i].keys[0].time;
   2.356 +			if(tm < res) {
   2.357 +				res = tm;
   2.358 +			}
   2.359 +		}
   2.360 +	}
   2.361 +
   2.362 +	c = node->child;
   2.363 +	while(c) {
   2.364 +		anm_time_t tm = anm_get_start_time(c);
   2.365 +		if(tm < res) {
   2.366 +			res = tm;
   2.367 +		}
   2.368 +		c = c->next;
   2.369 +	}
   2.370 +	return res;
   2.371 +}
   2.372 +
   2.373 +anm_time_t anm_get_end_time(struct anm_node *node)
   2.374 +{
   2.375 +	int i;
   2.376 +	struct anm_node *c;
   2.377 +	anm_time_t res = LONG_MIN;
   2.378 +
   2.379 +	for(i=0; i<ANM_NUM_TRACKS; i++) {
   2.380 +		if(node->tracks[i].count) {
   2.381 +			anm_time_t tm = node->tracks[i].keys[node->tracks[i].count - 1].time;
   2.382 +			if(tm > res) {
   2.383 +				res = tm;
   2.384 +			}
   2.385 +		}
   2.386 +	}
   2.387 +
   2.388 +	c = node->child;
   2.389 +	while(c) {
   2.390 +		anm_time_t tm = anm_get_end_time(c);
   2.391 +		if(tm > res) {
   2.392 +			res = tm;
   2.393 +		}
   2.394 +		c = c->next;
   2.395 +	}
   2.396 +	return res;
   2.397 +}
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/prototype/anim/anim.h	Sun Oct 07 02:04:00 2012 +0300
     3.3 @@ -0,0 +1,99 @@
     3.4 +#ifndef LIBANIM_H_
     3.5 +#define LIBANIM_H_
     3.6 +
     3.7 +#include "config.h"
     3.8 +
     3.9 +#ifdef ANIM_THREAD_SAFE
    3.10 +#include <pthread.h>
    3.11 +#endif
    3.12 +
    3.13 +#include <vmath/vmath.h>
    3.14 +#include "track.h"
    3.15 +
    3.16 +enum {
    3.17 +	ANM_TRACK_POS_X,
    3.18 +	ANM_TRACK_POS_Y,
    3.19 +	ANM_TRACK_POS_Z,
    3.20 +
    3.21 +	ANM_TRACK_ROT_X,
    3.22 +	ANM_TRACK_ROT_Y,
    3.23 +	ANM_TRACK_ROT_Z,
    3.24 +	ANM_TRACK_ROT_W,
    3.25 +
    3.26 +	ANM_TRACK_SCL_X,
    3.27 +	ANM_TRACK_SCL_Y,
    3.28 +	ANM_TRACK_SCL_Z,
    3.29 +
    3.30 +	ANM_NUM_TRACKS
    3.31 +};
    3.32 +
    3.33 +struct anm_node {
    3.34 +	char *name;
    3.35 +
    3.36 +	struct anm_track tracks[ANM_NUM_TRACKS];
    3.37 +	vec3_t pivot;
    3.38 +
    3.39 +	/* matrix cache */
    3.40 +	mat4_t cache_matrix;
    3.41 +	anm_time_t cache_time;
    3.42 +#ifdef ANIM_THREAD_SAFE
    3.43 +	pthread_mutex_t cache_mutex;
    3.44 +#endif
    3.45 +
    3.46 +	struct anm_node *parent;
    3.47 +	struct anm_node *child;
    3.48 +	struct anm_node *next;
    3.49 +};
    3.50 +
    3.51 +
    3.52 +/* node constructor and destructor */
    3.53 +int anm_init_node(struct anm_node *node);
    3.54 +void anm_destroy_node(struct anm_node *node);
    3.55 +
    3.56 +/* recursively destroy an animation node tree */
    3.57 +void anm_destroy_node_tree(struct anm_node *tree);
    3.58 +
    3.59 +/* helper functions to allocate/construct and destroy/free with
    3.60 + * a single call. They call anm_init_node and anm_destroy_node
    3.61 + * internally.
    3.62 + */
    3.63 +struct anm_node *anm_create_node(void);
    3.64 +void anm_free_node(struct anm_node *node);
    3.65 +
    3.66 +/* recursively destroy and free the nodes of a node tree */
    3.67 +void anm_free_node_tree(struct anm_node *tree);
    3.68 +
    3.69 +int anm_set_node_name(struct anm_node *node, const char *name);
    3.70 +const char *anm_get_node_name(struct anm_node *node);
    3.71 +
    3.72 +void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in);
    3.73 +void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex);
    3.74 +
    3.75 +/* link and unlink nodes with parent/child relations */
    3.76 +void anm_link_node(struct anm_node *parent, struct anm_node *child);
    3.77 +int anm_unlink_node(struct anm_node *parent, struct anm_node *child);
    3.78 +
    3.79 +void anm_set_position(struct anm_node *node, vec3_t pos, anm_time_t tm);
    3.80 +vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm);
    3.81 +
    3.82 +void anm_set_rotation(struct anm_node *node, quat_t rot, anm_time_t tm);
    3.83 +quat_t anm_get_node_rotation(struct anm_node *node, anm_time_t tm);
    3.84 +
    3.85 +void anm_set_scaling(struct anm_node *node, vec3_t scl, anm_time_t tm);
    3.86 +vec3_t anm_get_node_scaling(struct anm_node *node, anm_time_t tm);
    3.87 +
    3.88 +/* these three return the full p/r/s taking hierarchy into account */
    3.89 +vec3_t anm_get_position(struct anm_node *node, anm_time_t tm);
    3.90 +quat_t anm_get_rotation(struct anm_node *node, anm_time_t tm);
    3.91 +vec3_t anm_get_scaling(struct anm_node *node, anm_time_t tm);
    3.92 +
    3.93 +void anm_set_pivot(struct anm_node *node, vec3_t pivot);
    3.94 +vec3_t anm_get_pivot(struct anm_node *node);
    3.95 +
    3.96 +void anm_get_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm);
    3.97 +
    3.98 +/* those return the start and end times of the whole tree */
    3.99 +anm_time_t anm_get_start_time(struct anm_node *node);
   3.100 +anm_time_t anm_get_end_time(struct anm_node *node);
   3.101 +
   3.102 +#endif	/* LIBANIM_H_ */
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/prototype/anim/config.h	Sun Oct 07 02:04:00 2012 +0300
     4.3 @@ -0,0 +1,6 @@
     4.4 +#ifndef ANIM_CONFIG_H_
     4.5 +#define ANIM_CONFIG_H_
     4.6 +
     4.7 +#undef ANIM_THREAD_SAFE
     4.8 +
     4.9 +#endif	/* ANIM_CONFIG_H_ */
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/prototype/anim/dynarr.c	Sun Oct 07 02:04:00 2012 +0300
     5.3 @@ -0,0 +1,122 @@
     5.4 +#include <stdio.h>
     5.5 +#include <stdlib.h>
     5.6 +#include <string.h>
     5.7 +#include "dynarr.h"
     5.8 +
     5.9 +/* The array descriptor keeps auxilliary information needed to manipulate
    5.10 + * the dynamic array. It's allocated adjacent to the array buffer.
    5.11 + */
    5.12 +struct arrdesc {
    5.13 +	int nelem, szelem;
    5.14 +	int max_elem;
    5.15 +	int bufsz;	/* not including the descriptor */
    5.16 +};
    5.17 +
    5.18 +#define DESC(x)		((struct arrdesc*)((char*)(x) - sizeof(struct arrdesc)))
    5.19 +
    5.20 +void *dynarr_alloc(int elem, int szelem)
    5.21 +{
    5.22 +	struct arrdesc *desc;
    5.23 +
    5.24 +	if(!(desc = malloc(elem * szelem + sizeof *desc))) {
    5.25 +		return 0;
    5.26 +	}
    5.27 +	desc->nelem = desc->max_elem = elem;
    5.28 +	desc->szelem = szelem;
    5.29 +	desc->bufsz = elem * szelem;
    5.30 +	return (char*)desc + sizeof *desc;
    5.31 +}
    5.32 +
    5.33 +void dynarr_free(void *da)
    5.34 +{
    5.35 +	if(da) {
    5.36 +		free(DESC(da));
    5.37 +	}
    5.38 +}
    5.39 +
    5.40 +void *dynarr_resize(void *da, int elem)
    5.41 +{
    5.42 +	int newsz;
    5.43 +	void *tmp;
    5.44 +	struct arrdesc *desc;
    5.45 +
    5.46 +	if(!da) return 0;
    5.47 +	desc = DESC(da);
    5.48 +
    5.49 +	newsz = desc->szelem * elem;
    5.50 +
    5.51 +	if(!(tmp = realloc(desc, newsz + sizeof *desc))) {
    5.52 +		return 0;
    5.53 +	}
    5.54 +	desc = tmp;
    5.55 +
    5.56 +	desc->nelem = desc->max_elem = elem;
    5.57 +	desc->bufsz = newsz;
    5.58 +	return (char*)desc + sizeof *desc;
    5.59 +}
    5.60 +
    5.61 +int dynarr_empty(void *da)
    5.62 +{
    5.63 +	return DESC(da)->nelem ? 0 : 1;
    5.64 +}
    5.65 +
    5.66 +int dynarr_size(void *da)
    5.67 +{
    5.68 +	return DESC(da)->nelem;
    5.69 +}
    5.70 +
    5.71 +
    5.72 +/* stack semantics */
    5.73 +void *dynarr_push(void *da, void *item)
    5.74 +{
    5.75 +	struct arrdesc *desc;
    5.76 +	int nelem;
    5.77 +
    5.78 +	desc = DESC(da);
    5.79 +	nelem = desc->nelem;
    5.80 +
    5.81 +	if(nelem >= desc->max_elem) {
    5.82 +		/* need to resize */
    5.83 +		struct arrdesc *tmp;
    5.84 +		int newsz = desc->max_elem ? desc->max_elem * 2 : 1;
    5.85 +
    5.86 +		if(!(tmp = dynarr_resize(da, newsz))) {
    5.87 +			fprintf(stderr, "failed to resize\n");
    5.88 +			return da;
    5.89 +		}
    5.90 +		da = tmp;
    5.91 +		desc = DESC(da);
    5.92 +		desc->nelem = nelem;
    5.93 +	}
    5.94 +
    5.95 +	memcpy((char*)da + desc->nelem++ * desc->szelem, item, desc->szelem);
    5.96 +	return da;
    5.97 +}
    5.98 +
    5.99 +void *dynarr_pop(void *da)
   5.100 +{
   5.101 +	struct arrdesc *desc;
   5.102 +	int nelem;
   5.103 +
   5.104 +	desc = DESC(da);
   5.105 +	nelem = desc->nelem;
   5.106 +
   5.107 +	if(!nelem) return da;
   5.108 +
   5.109 +	if(nelem <= desc->max_elem / 3) {
   5.110 +		/* reclaim space */
   5.111 +		struct arrdesc *tmp;
   5.112 +		int newsz = desc->max_elem / 2;
   5.113 +
   5.114 +		if(!(tmp = dynarr_resize(da, newsz))) {
   5.115 +			fprintf(stderr, "failed to resize\n");
   5.116 +			return da;
   5.117 +		}
   5.118 +		da = tmp;
   5.119 +		desc = DESC(da);
   5.120 +		desc->nelem = nelem;
   5.121 +	}
   5.122 +	desc->nelem--;
   5.123 +
   5.124 +	return da;
   5.125 +}
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/prototype/anim/dynarr.h	Sun Oct 07 02:04:00 2012 +0300
     6.3 @@ -0,0 +1,16 @@
     6.4 +#ifndef DYNARR_H_
     6.5 +#define DYNARR_H_
     6.6 +
     6.7 +void *dynarr_alloc(int elem, int szelem);
     6.8 +void dynarr_free(void *da);
     6.9 +void *dynarr_resize(void *da, int elem);
    6.10 +
    6.11 +int dynarr_empty(void *da);
    6.12 +int dynarr_size(void *da);
    6.13 +
    6.14 +/* stack semantics */
    6.15 +void *dynarr_push(void *da, void *item);
    6.16 +void *dynarr_pop(void *da);
    6.17 +
    6.18 +
    6.19 +#endif	/* DYNARR_H_ */
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/prototype/anim/track.c	Sun Oct 07 02:04:00 2012 +0300
     7.3 @@ -0,0 +1,272 @@
     7.4 +#include <stdlib.h>
     7.5 +#include <string.h>
     7.6 +#include <assert.h>
     7.7 +#include "track.h"
     7.8 +#include "dynarr.h"
     7.9 +
    7.10 +static int keycmp(const void *a, const void *b);
    7.11 +static int find_prev_key(struct anm_keyframe *arr, int start, int end, anm_time_t tm);
    7.12 +
    7.13 +static float interp_step(float v0, float v1, float v2, float v3, float t);
    7.14 +static float interp_linear(float v0, float v1, float v2, float v3, float t);
    7.15 +static float interp_cubic(float v0, float v1, float v2, float v3, float t);
    7.16 +
    7.17 +static anm_time_t remap_extend(anm_time_t tm, anm_time_t start, anm_time_t end);
    7.18 +static anm_time_t remap_clamp(anm_time_t tm, anm_time_t start, anm_time_t end);
    7.19 +static anm_time_t remap_repeat(anm_time_t tm, anm_time_t start, anm_time_t end);
    7.20 +
    7.21 +/* XXX keep this in sync with enum anm_interpolator at track.h */
    7.22 +static float (*interp[])(float, float, float, float, float) = {
    7.23 +	interp_step,
    7.24 +	interp_linear,
    7.25 +	interp_cubic,
    7.26 +	0
    7.27 +};
    7.28 +
    7.29 +/* XXX keep this in sync with enum anm_extrapolator at track.h */
    7.30 +static anm_time_t (*remap_time[])(anm_time_t, anm_time_t, anm_time_t) = {
    7.31 +	remap_extend,
    7.32 +	remap_clamp,
    7.33 +	remap_repeat,
    7.34 +	0
    7.35 +};
    7.36 +
    7.37 +int anm_init_track(struct anm_track *track)
    7.38 +{
    7.39 +	memset(track, 0, sizeof *track);
    7.40 +
    7.41 +	if(!(track->keys = dynarr_alloc(0, sizeof *track->keys))) {
    7.42 +		return -1;
    7.43 +	}
    7.44 +	track->interp = ANM_INTERP_LINEAR;
    7.45 +	track->extrap = ANM_EXTRAP_CLAMP;
    7.46 +	return 0;
    7.47 +}
    7.48 +
    7.49 +void anm_destroy_track(struct anm_track *track)
    7.50 +{
    7.51 +	dynarr_free(track->keys);
    7.52 +}
    7.53 +
    7.54 +struct anm_track *anm_create_track(void)
    7.55 +{
    7.56 +	struct anm_track *track;
    7.57 +
    7.58 +	if((track = malloc(sizeof *track))) {
    7.59 +		if(anm_init_track(track) == -1) {
    7.60 +			free(track);
    7.61 +			return 0;
    7.62 +		}
    7.63 +	}
    7.64 +	return track;
    7.65 +}
    7.66 +
    7.67 +void anm_free_track(struct anm_track *track)
    7.68 +{
    7.69 +	anm_destroy_track(track);
    7.70 +	free(track);
    7.71 +}
    7.72 +
    7.73 +int anm_set_track_name(struct anm_track *track, const char *name)
    7.74 +{
    7.75 +	char *tmp;
    7.76 +
    7.77 +	if(!(tmp = malloc(strlen(name) + 1))) {
    7.78 +		return -1;
    7.79 +	}
    7.80 +	free(track->name);
    7.81 +	track->name = tmp;
    7.82 +	return 0;
    7.83 +}
    7.84 +
    7.85 +const char *anm_get_track_name(struct anm_track *track)
    7.86 +{
    7.87 +	return track->name;
    7.88 +}
    7.89 +
    7.90 +void anm_set_track_interpolator(struct anm_track *track, enum anm_interpolator in)
    7.91 +{
    7.92 +	track->interp = in;
    7.93 +}
    7.94 +
    7.95 +void anm_set_track_extrapolator(struct anm_track *track, enum anm_extrapolator ex)
    7.96 +{
    7.97 +	track->extrap = ex;
    7.98 +}
    7.99 +
   7.100 +anm_time_t anm_remap_time(struct anm_track *track, anm_time_t tm, anm_time_t start, anm_time_t end)
   7.101 +{
   7.102 +	return remap_time[track->extrap](tm, start, end);
   7.103 +}
   7.104 +
   7.105 +void anm_set_track_default(struct anm_track *track, float def)
   7.106 +{
   7.107 +	track->def_val = def;
   7.108 +}
   7.109 +
   7.110 +int anm_set_keyframe(struct anm_track *track, struct anm_keyframe *key)
   7.111 +{
   7.112 +	int idx = anm_get_key_interval(track, key->time);
   7.113 +
   7.114 +	/* if we got a valid keyframe index, compare them... */
   7.115 +	if(idx >= 0 && idx < track->count && keycmp(key, track->keys + idx) == 0) {
   7.116 +		/* ... it's the same key, just update the value */
   7.117 +		track->keys[idx].val = key->val;
   7.118 +	} else {
   7.119 +		/* ... it's a new key, add it and re-sort them */
   7.120 +		void *tmp;
   7.121 +		if(!(tmp = dynarr_push(track->keys, key))) {
   7.122 +			return -1;
   7.123 +		}
   7.124 +		track->keys = tmp;
   7.125 +		/* TODO lazy qsort */
   7.126 +		qsort(track->keys, ++track->count, sizeof *track->keys, keycmp);
   7.127 +	}
   7.128 +	return 0;
   7.129 +}
   7.130 +
   7.131 +static int keycmp(const void *a, const void *b)
   7.132 +{
   7.133 +	return ((struct anm_keyframe*)a)->time - ((struct anm_keyframe*)b)->time;
   7.134 +}
   7.135 +
   7.136 +struct anm_keyframe *anm_get_keyframe(struct anm_track *track, int idx)
   7.137 +{
   7.138 +	if(idx < 0 || idx >= track->count) {
   7.139 +		return 0;
   7.140 +	}
   7.141 +	return track->keys + idx;
   7.142 +}
   7.143 +
   7.144 +int anm_get_key_interval(struct anm_track *track, anm_time_t tm)
   7.145 +{
   7.146 +	int last;
   7.147 +
   7.148 +	if(!track->count || tm < track->keys[0].time) {
   7.149 +		return -1;
   7.150 +	}
   7.151 +
   7.152 +	last = track->count - 1;
   7.153 +	if(tm > track->keys[last].time) {
   7.154 +		return last;
   7.155 +	}
   7.156 +
   7.157 +	return find_prev_key(track->keys, 0, last, tm);
   7.158 +}
   7.159 +
   7.160 +static int find_prev_key(struct anm_keyframe *arr, int start, int end, anm_time_t tm)
   7.161 +{
   7.162 +	int mid;
   7.163 +
   7.164 +	if(end - start <= 1) {
   7.165 +		return start;
   7.166 +	}
   7.167 +
   7.168 +	mid = (start + end) / 2;
   7.169 +	if(tm < arr[mid].time) {
   7.170 +		return find_prev_key(arr, start, mid, tm);
   7.171 +	}
   7.172 +	if(tm > arr[mid].time) {
   7.173 +		return find_prev_key(arr, mid, end, tm);
   7.174 +	}
   7.175 +	return mid;
   7.176 +}
   7.177 +
   7.178 +int anm_set_value(struct anm_track *track, anm_time_t tm, float val)
   7.179 +{
   7.180 +	struct anm_keyframe key;
   7.181 +	key.time = tm;
   7.182 +	key.val = val;
   7.183 +
   7.184 +	return anm_set_keyframe(track, &key);
   7.185 +}
   7.186 +
   7.187 +float anm_get_value(struct anm_track *track, anm_time_t tm)
   7.188 +{
   7.189 +	int idx0, idx1, last_idx;
   7.190 +	anm_time_t tstart, tend;
   7.191 +	float t, dt;
   7.192 +	float v0, v1, v2, v3;
   7.193 +
   7.194 +	if(!track->count) {
   7.195 +		return track->def_val;
   7.196 +	}
   7.197 +
   7.198 +	last_idx = track->count - 1;
   7.199 +
   7.200 +	tstart = track->keys[0].time;
   7.201 +	tend = track->keys[last_idx].time;
   7.202 +
   7.203 +	if(tstart == tend) {
   7.204 +		return track->keys[0].val;
   7.205 +	}
   7.206 +
   7.207 +	tm = remap_time[track->extrap](tm, tstart, tend);
   7.208 +
   7.209 +	idx0 = anm_get_key_interval(track, tm);
   7.210 +	assert(idx0 >= 0 && idx0 < track->count);
   7.211 +	idx1 = idx0 + 1;
   7.212 +
   7.213 +	if(idx0 == last_idx) {
   7.214 +		return track->keys[idx0].val;
   7.215 +	}
   7.216 +
   7.217 +	dt = (float)(track->keys[idx1].time - track->keys[idx0].time);
   7.218 +	t = (float)(tm - track->keys[idx0].time) / dt;
   7.219 +
   7.220 +	v1 = track->keys[idx0].val;
   7.221 +	v2 = track->keys[idx1].val;
   7.222 +
   7.223 +	/* get the neigboring values to allow for cubic interpolation */
   7.224 +	v0 = idx0 > 0 ? track->keys[idx0 - 1].val : v1;
   7.225 +	v3 = idx1 < last_idx ? track->keys[idx1 + 1].val : v2;
   7.226 +
   7.227 +	return interp[track->interp](v0, v1, v2, v3, t);
   7.228 +}
   7.229 +
   7.230 +
   7.231 +static float interp_step(float v0, float v1, float v2, float v3, float t)
   7.232 +{
   7.233 +	return v1;
   7.234 +}
   7.235 +
   7.236 +static float interp_linear(float v0, float v1, float v2, float v3, float t)
   7.237 +{
   7.238 +	return v1 + (v2 - v1) * t;
   7.239 +}
   7.240 +
   7.241 +static float interp_cubic(float a, float b, float c, float d, float t)
   7.242 +{
   7.243 +	float x, y, z, w;
   7.244 +	float tsq = t * t;
   7.245 +
   7.246 +	x = -a + 3.0 * b - 3.0 * c + d;
   7.247 +	y = 2.0 * a - 5.0 * b + 4.0 * c - d;
   7.248 +	z = c - a;
   7.249 +	w = 2.0 * b;
   7.250 +
   7.251 +	return 0.5 * (x * tsq * t + y * tsq + z * t + w);
   7.252 +}
   7.253 +
   7.254 +static anm_time_t remap_extend(anm_time_t tm, anm_time_t start, anm_time_t end)
   7.255 +{
   7.256 +	return remap_repeat(tm, start, end);
   7.257 +}
   7.258 +
   7.259 +static anm_time_t remap_clamp(anm_time_t tm, anm_time_t start, anm_time_t end)
   7.260 +{
   7.261 +	return tm < start ? start : (tm >= end ? end - 1 : tm);
   7.262 +}
   7.263 +
   7.264 +static anm_time_t remap_repeat(anm_time_t tm, anm_time_t start, anm_time_t end)
   7.265 +{
   7.266 +	anm_time_t interv = end - start;
   7.267 +
   7.268 +	if(tm < start) {
   7.269 +		while(tm < start) {
   7.270 +			tm += interv;
   7.271 +		}
   7.272 +		return tm;
   7.273 +	}
   7.274 +	return (tm - start) % interv + start;
   7.275 +}
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/prototype/anim/track.h	Sun Oct 07 02:04:00 2012 +0300
     8.3 @@ -0,0 +1,88 @@
     8.4 +/* An animation track defines the values of a single scalar over time
     8.5 + * and supports various interpolation and extrapolation modes.
     8.6 + */
     8.7 +#ifndef LIBANIM_TRACK_H_
     8.8 +#define LIBANIM_TRACK_H_
     8.9 +
    8.10 +#include <limits.h>
    8.11 +#include "config.h"
    8.12 +
    8.13 +enum anm_interpolator {
    8.14 +	ANM_INTERP_STEP,
    8.15 +	ANM_INTERP_LINEAR,
    8.16 +	ANM_INTERP_CUBIC
    8.17 +};
    8.18 +
    8.19 +enum anm_extrapolator {
    8.20 +	ANM_EXTRAP_EXTEND,	/* extend to infinity */
    8.21 +	ANM_EXTRAP_CLAMP,	/* clamp to last value */
    8.22 +	ANM_EXTRAP_REPEAT	/* repeat motion */
    8.23 +};
    8.24 +
    8.25 +typedef long anm_time_t;
    8.26 +#define ANM_TIME_INVAL	LONG_MIN
    8.27 +
    8.28 +#define ANM_SEC2TM(x)	((anm_time_t)((x) * 1000))
    8.29 +#define ANM_MSEC2TM(x)	((anm_time_t)(x))
    8.30 +#define ANM_TM2SEC(x)	((x) / 1000.0)
    8.31 +#define ANM_TM2MSEC(x)	(x)
    8.32 +
    8.33 +struct anm_keyframe {
    8.34 +	anm_time_t time;
    8.35 +	float val;
    8.36 +};
    8.37 +
    8.38 +struct anm_track {
    8.39 +	char *name;
    8.40 +	int count;
    8.41 +	struct anm_keyframe *keys;
    8.42 +
    8.43 +	float def_val;
    8.44 +
    8.45 +	enum anm_interpolator interp;
    8.46 +	enum anm_extrapolator extrap;
    8.47 +};
    8.48 +
    8.49 +/* track constructor and destructor */
    8.50 +int anm_init_track(struct anm_track *track);
    8.51 +void anm_destroy_track(struct anm_track *track);
    8.52 +
    8.53 +/* helper functions that use anm_init_track and anm_destroy_track internally */
    8.54 +struct anm_track *anm_create_track(void);
    8.55 +void anm_free_track(struct anm_track *track);
    8.56 +
    8.57 +int anm_set_track_name(struct anm_track *track, const char *name);
    8.58 +const char *anm_get_track_name(struct anm_track *track);
    8.59 +
    8.60 +void anm_set_track_interpolator(struct anm_track *track, enum anm_interpolator in);
    8.61 +void anm_set_track_extrapolator(struct anm_track *track, enum anm_extrapolator ex);
    8.62 +
    8.63 +anm_time_t anm_remap_time(struct anm_track *track, anm_time_t tm, anm_time_t start, anm_time_t end);
    8.64 +
    8.65 +void anm_set_track_default(struct anm_track *track, float def);
    8.66 +
    8.67 +/* set or update a keyframe */
    8.68 +int anm_set_keyframe(struct anm_track *track, struct anm_keyframe *key);
    8.69 +
    8.70 +/* get the idx-th keyframe, returns null if it doesn't exist */
    8.71 +struct anm_keyframe *anm_get_keyframe(struct anm_track *track, int idx);
    8.72 +
    8.73 +/* Finds the 0-based index of the intra-keyframe interval which corresponds
    8.74 + * to the specified time. If the time falls exactly onto the N-th keyframe
    8.75 + * the function returns N.
    8.76 + *
    8.77 + * Special cases:
    8.78 + * - if the time is before the first keyframe -1 is returned.
    8.79 + * - if the time is after the last keyframe, the index of the last keyframe
    8.80 + *   is returned.
    8.81 + */
    8.82 +int anm_get_key_interval(struct anm_track *track, anm_time_t tm);
    8.83 +
    8.84 +int anm_set_value(struct anm_track *track, anm_time_t tm, float val);
    8.85 +
    8.86 +/* evaluates and returns the value of the track for a particular time */
    8.87 +float anm_get_value(struct anm_track *track, anm_time_t tm);
    8.88 +
    8.89 +
    8.90 +
    8.91 +#endif	/* LIBANIM_TRACK_H_ */
     9.1 --- a/prototype/data/fire.psys	Tue Oct 02 13:42:18 2012 +0300
     9.2 +++ b/prototype/data/fire.psys	Sun Oct 07 02:04:00 2012 +0300
     9.3 @@ -11,13 +11,13 @@
     9.4  # center ~ range. If the range is missing, it's assumed to be 0.
     9.5  
     9.6  texture = "fire_particle.png"
     9.7 -rate = 50
     9.8 +rate = 40
     9.9  life = 0.9 ~ 0.1
    9.10  grav = [0 0.3 0]
    9.11  spawn_range = 0.025
    9.12  
    9.13 -size(0) = 0.2
    9.14 -size(1s) = 0.075
    9.15 +size(0) = 0.25
    9.16 +size(1s) = 0.08
    9.17  
    9.18  pcolor(0) = [1.0 0.7 0.1]
    9.19  pcolor(1s) = [1.0 0.27 0.15]
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/prototype/imago2/conv.c	Sun Oct 07 02:04:00 2012 +0300
    10.3 @@ -0,0 +1,251 @@
    10.4 +/*
    10.5 +libimago - a multi-format image file input/output library.
    10.6 +Copyright (C) 2010 John Tsiombikas <nuclear@member.fsf.org>
    10.7 +
    10.8 +This program is free software: you can redistribute it and/or modify
    10.9 +it under the terms of the GNU Lesser General Public License as published
   10.10 +by the Free Software Foundation, either version 3 of the License, or
   10.11 +(at your option) any later version.
   10.12 +
   10.13 +This program is distributed in the hope that it will be useful,
   10.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of
   10.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   10.16 +GNU Lesser General Public License for more details.
   10.17 +
   10.18 +You should have received a copy of the GNU Lesser General Public License
   10.19 +along with this program.  If not, see <http://www.gnu.org/licenses/>.
   10.20 +*/
   10.21 +#include <string.h>
   10.22 +#include "imago2.h"
   10.23 +
   10.24 +/* pixel-format conversions are sub-optimal at the moment to avoid
   10.25 + * writing a lot of code. optimize at some point ?
   10.26 + */
   10.27 +
   10.28 +#define CLAMP(x, a, b)	((x) < (a) ? (a) : ((x) > (b) ? (b) : (x)))
   10.29 +
   10.30 +struct pixel {
   10.31 +	float r, g, b, a;
   10.32 +};
   10.33 +
   10.34 +static void unpack_grey8(struct pixel *unp, void *pptr, int count);
   10.35 +static void unpack_rgb24(struct pixel *unp, void *pptr, int count);
   10.36 +static void unpack_rgba32(struct pixel *unp, void *pptr, int count);
   10.37 +static void unpack_greyf(struct pixel *unp, void *pptr, int count);
   10.38 +static void unpack_rgbf(struct pixel *unp, void *pptr, int count);
   10.39 +static void unpack_rgbaf(struct pixel *unp, void *pptr, int count);
   10.40 +
   10.41 +static void pack_grey8(void *pptr, struct pixel *unp, int count);
   10.42 +static void pack_rgb24(void *pptr, struct pixel *unp, int count);
   10.43 +static void pack_rgba32(void *pptr, struct pixel *unp, int count);
   10.44 +static void pack_greyf(void *pptr, struct pixel *unp, int count);
   10.45 +static void pack_rgbf(void *pptr, struct pixel *unp, int count);
   10.46 +static void pack_rgbaf(void *pptr, struct pixel *unp, int count);
   10.47 +
   10.48 +/* XXX keep in sync with enum img_fmt at imago2.h */
   10.49 +static void (*unpack[])(struct pixel*, void*, int) = {
   10.50 +	unpack_grey8,
   10.51 +	unpack_rgb24,
   10.52 +	unpack_rgba32,
   10.53 +	unpack_greyf,
   10.54 +	unpack_rgbf,
   10.55 +	unpack_rgbaf
   10.56 +};
   10.57 +
   10.58 +/* XXX keep in sync with enum img_fmt at imago2.h */
   10.59 +static void (*pack[])(void*, struct pixel*, int) = {
   10.60 +	pack_grey8,
   10.61 +	pack_rgb24,
   10.62 +	pack_rgba32,
   10.63 +	pack_greyf,
   10.64 +	pack_rgbf,
   10.65 +	pack_rgbaf
   10.66 +};
   10.67 +
   10.68 +
   10.69 +int img_convert(struct img_pixmap *img, enum img_fmt tofmt)
   10.70 +{
   10.71 +	struct pixel pbuf[8];
   10.72 +	int bufsz = (img->width & 7) == 0 ? 8 : ((img->width & 3) == 0 ? 4 : 1);
   10.73 +	int i, num_pix = img->width * img->height;
   10.74 +	int num_iter = num_pix / bufsz;
   10.75 +	char *sptr, *dptr;
   10.76 +	struct img_pixmap nimg;
   10.77 +
   10.78 +	if(img->fmt == tofmt) {
   10.79 +		return 0;	/* nothing to do */
   10.80 +	}
   10.81 +
   10.82 +	img_init(&nimg);
   10.83 +	if(img_set_pixels(&nimg, img->width, img->height, tofmt, 0) == -1) {
   10.84 +		img_destroy(&nimg);
   10.85 +		return -1;
   10.86 +	}
   10.87 +
   10.88 +	sptr = img->pixels;
   10.89 +	dptr = nimg.pixels;
   10.90 +
   10.91 +	for(i=0; i<num_iter; i++) {
   10.92 +		unpack[img->fmt](pbuf, sptr, bufsz);
   10.93 +		pack[tofmt](dptr, pbuf, bufsz);
   10.94 +
   10.95 +		sptr += bufsz * img->pixelsz;
   10.96 +		dptr += bufsz * nimg.pixelsz;
   10.97 +	}
   10.98 +
   10.99 +	img_copy(img, &nimg);
  10.100 +	img_destroy(&nimg);
  10.101 +	return 0;
  10.102 +}
  10.103 +
  10.104 +/* the following functions *could* benefit from SIMD */
  10.105 +
  10.106 +static void unpack_grey8(struct pixel *unp, void *pptr, int count)
  10.107 +{
  10.108 +	int i;
  10.109 +	unsigned char *pix = pptr;
  10.110 +
  10.111 +	for(i=0; i<count; i++) {
  10.112 +		unp->r = unp->g = unp->b = (float)*pix++ / 255.0;
  10.113 +		unp->a = 1.0;
  10.114 +		unp++;
  10.115 +	}
  10.116 +}
  10.117 +
  10.118 +static void unpack_rgb24(struct pixel *unp, void *pptr, int count)
  10.119 +{
  10.120 +	int i;
  10.121 +	unsigned char *pix = pptr;
  10.122 +
  10.123 +	for(i=0; i<count; i++) {
  10.124 +		unp->r = (float)*pix++ / 255.0;
  10.125 +		unp->g = (float)*pix++ / 255.0;
  10.126 +		unp->b = (float)*pix++ / 255.0;
  10.127 +		unp->a = 1.0;
  10.128 +		unp++;
  10.129 +	}
  10.130 +}
  10.131 +
  10.132 +static void unpack_rgba32(struct pixel *unp, void *pptr, int count)
  10.133 +{
  10.134 +	memcpy(unp, pptr, count * sizeof *unp);
  10.135 +}
  10.136 +
  10.137 +static void unpack_greyf(struct pixel *unp, void *pptr, int count)
  10.138 +{
  10.139 +	int i;
  10.140 +	float *pix = pptr;
  10.141 +
  10.142 +	for(i=0; i<count; i++) {
  10.143 +		unp->r = unp->g = unp->b = *pix++;
  10.144 +		unp->a = 1.0;
  10.145 +		unp++;
  10.146 +	}
  10.147 +}
  10.148 +
  10.149 +static void unpack_rgbf(struct pixel *unp, void *pptr, int count)
  10.150 +{
  10.151 +	int i;
  10.152 +	float *pix = pptr;
  10.153 +
  10.154 +	for(i=0; i<count; i++) {
  10.155 +		unp->r = *pix++;
  10.156 +		unp->g = *pix++;
  10.157 +		unp->b = *pix++;
  10.158 +		unp->a = 1.0;
  10.159 +		unp++;
  10.160 +	}
  10.161 +}
  10.162 +
  10.163 +static void unpack_rgbaf(struct pixel *unp, void *pptr, int count)
  10.164 +{
  10.165 +	int i;
  10.166 +	float *pix = pptr;
  10.167 +
  10.168 +	for(i=0; i<count; i++) {
  10.169 +		unp->r = *pix++;
  10.170 +		unp->g = *pix++;
  10.171 +		unp->b = *pix++;
  10.172 +		unp->a = *pix++;
  10.173 +		unp++;
  10.174 +	}
  10.175 +}
  10.176 +
  10.177 +
  10.178 +static void pack_grey8(void *pptr, struct pixel *unp, int count)
  10.179 +{
  10.180 +	int i;
  10.181 +	unsigned char *pix = pptr;
  10.182 +
  10.183 +	for(i=0; i<count; i++) {
  10.184 +		int lum = (int)(255.0 * (unp->r + unp->g + unp->b) / 3.0);
  10.185 +		*pix++ = CLAMP(lum, 0, 255);
  10.186 +		unp++;
  10.187 +	}
  10.188 +}
  10.189 +
  10.190 +static void pack_rgb24(void *pptr, struct pixel *unp, int count)
  10.191 +{
  10.192 +	int i;
  10.193 +	unsigned char *pix = pptr;
  10.194 +
  10.195 +	for(i=0; i<count; i++) {
  10.196 +		int r = (int)(unp->r * 255.0);
  10.197 +		int g = (int)(unp->g * 255.0);
  10.198 +		int b = (int)(unp->b * 255.0);
  10.199 +
  10.200 +		*pix++ = CLAMP(r, 0, 255);
  10.201 +		*pix++ = CLAMP(g, 0, 255);
  10.202 +		*pix++ = CLAMP(b, 0, 255);
  10.203 +		unp++;
  10.204 +	}
  10.205 +}
  10.206 +
  10.207 +static void pack_rgba32(void *pptr, struct pixel *unp, int count)
  10.208 +{
  10.209 +	int i;
  10.210 +	unsigned char *pix = pptr;
  10.211 +
  10.212 +	for(i=0; i<count; i++) {
  10.213 +		int r = (int)(unp->r * 255.0);
  10.214 +		int g = (int)(unp->g * 255.0);
  10.215 +		int b = (int)(unp->b * 255.0);
  10.216 +		int a = (int)(unp->a * 255.0);
  10.217 +
  10.218 +		*pix++ = CLAMP(r, 0, 255);
  10.219 +		*pix++ = CLAMP(g, 0, 255);
  10.220 +		*pix++ = CLAMP(b, 0, 255);
  10.221 +		*pix++ = CLAMP(a, 0, 255);
  10.222 +		unp++;
  10.223 +	}
  10.224 +}
  10.225 +
  10.226 +static void pack_greyf(void *pptr, struct pixel *unp, int count)
  10.227 +{
  10.228 +	int i;
  10.229 +	float *pix = pptr;
  10.230 +
  10.231 +	for(i=0; i<count; i++) {
  10.232 +		*pix++ = (unp->r + unp->g + unp->b) / 3.0;
  10.233 +		unp++;
  10.234 +	}
  10.235 +}
  10.236 +
  10.237 +static void pack_rgbf(void *pptr, struct pixel *unp, int count)
  10.238 +{
  10.239 +	int i;
  10.240 +	float *pix = pptr;
  10.241 +
  10.242 +	for(i=0; i<count; i++) {
  10.243 +		*pix++ = unp->r;
  10.244 +		*pix++ = unp->g;
  10.245 +		*pix++ = unp->b;
  10.246 +		unp++;
  10.247 +	}
  10.248 +}
  10.249 +
  10.250 +static void pack_rgbaf(void *pptr, struct pixel *unp, int count)
  10.251 +{
  10.252 +	memcpy(pptr, unp, count * sizeof *unp);
  10.253 +}
  10.254 +
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/prototype/imago2/file_jpeg.c	Sun Oct 07 02:04:00 2012 +0300
    11.3 @@ -0,0 +1,293 @@
    11.4 +/*
    11.5 +libimago - a multi-format image file input/output library.
    11.6 +Copyright (C) 2010 John Tsiombikas <nuclear@member.fsf.org>
    11.7 +
    11.8 +This program is free software: you can redistribute it and/or modify
    11.9 +it under the terms of the GNU Lesser General Public License as published
   11.10 +by the Free Software Foundation, either version 3 of the License, or
   11.11 +(at your option) any later version.
   11.12 +
   11.13 +This program is distributed in the hope that it will be useful,
   11.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of
   11.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   11.16 +GNU Lesser General Public License for more details.
   11.17 +
   11.18 +You should have received a copy of the GNU Lesser General Public License
   11.19 +along with this program.  If not, see <http://www.gnu.org/licenses/>.
   11.20 +*/
   11.21 +
   11.22 +/* -- JPEG module -- */
   11.23 +
   11.24 +#include <stdio.h>
   11.25 +#include <stdlib.h>
   11.26 +#include <string.h>
   11.27 +
   11.28 +#ifdef WIN32
   11.29 +#include <windows.h>
   11.30 +#define HAVE_BOOLEAN
   11.31 +#endif
   11.32 +
   11.33 +#include <jpeglib.h>
   11.34 +#include "imago2.h"
   11.35 +#include "ftype_module.h"
   11.36 +
   11.37 +#define INPUT_BUF_SIZE	512
   11.38 +#define OUTPUT_BUF_SIZE	512
   11.39 +
   11.40 +/* data source manager: adapted from jdatasrc.c */
   11.41 +struct src_mgr {
   11.42 +	struct jpeg_source_mgr pub;
   11.43 +
   11.44 +	struct img_io *io;
   11.45 +	unsigned char buffer[INPUT_BUF_SIZE];
   11.46 +	int start_of_file;
   11.47 +};
   11.48 +
   11.49 +/* data destination manager: adapted from jdatadst.c */
   11.50 +struct dst_mgr {
   11.51 +	struct jpeg_destination_mgr pub;
   11.52 +
   11.53 +	struct img_io *io;
   11.54 +	unsigned char buffer[OUTPUT_BUF_SIZE];
   11.55 +};
   11.56 +
   11.57 +static int check(struct img_io *io);
   11.58 +static int read(struct img_pixmap *img, struct img_io *io);
   11.59 +static int write(struct img_pixmap *img, struct img_io *io);
   11.60 +
   11.61 +/* read source functions */
   11.62 +static void init_source(j_decompress_ptr jd);
   11.63 +static int fill_input_buffer(j_decompress_ptr jd);
   11.64 +static void skip_input_data(j_decompress_ptr jd, long num_bytes);
   11.65 +static void term_source(j_decompress_ptr jd);
   11.66 +
   11.67 +/* write destination functions */
   11.68 +static void init_destination(j_compress_ptr jc);
   11.69 +static int empty_output_buffer(j_compress_ptr jc);
   11.70 +static void term_destination(j_compress_ptr jc);
   11.71 +
   11.72 +int img_register_jpeg(void)
   11.73 +{
   11.74 +	static struct ftype_module mod = {".jpg", check, read, write};
   11.75 +	return img_register_module(&mod);
   11.76 +}
   11.77 +
   11.78 +
   11.79 +static int check(struct img_io *io)
   11.80 +{
   11.81 +	unsigned char sig[10];
   11.82 +
   11.83 +	long pos = io->seek(0, SEEK_CUR, io->uptr);
   11.84 +
   11.85 +	if(io->read(sig, 10, io->uptr) < 10) {
   11.86 +		io->seek(pos, SEEK_SET, io->uptr);
   11.87 +		return -1;
   11.88 +	}
   11.89 +
   11.90 +	if(memcmp(sig, "\xff\xd8\xff\xe0", 4) != 0 || memcmp(sig + 6, "JFIF", 4) != 0) {
   11.91 +		io->seek(pos, SEEK_SET, io->uptr);
   11.92 +		return -1;
   11.93 +	}
   11.94 +	io->seek(pos, SEEK_SET, io->uptr);
   11.95 +	return 0;
   11.96 +}
   11.97 +
   11.98 +static int read(struct img_pixmap *img, struct img_io *io)
   11.99 +{
  11.100 +	int i, nlines = 0;
  11.101 +	struct jpeg_decompress_struct cinfo;
  11.102 +	struct jpeg_error_mgr jerr;
  11.103 +	struct src_mgr src;
  11.104 +	unsigned char **scanlines;
  11.105 +
  11.106 +	io->seek(0, SEEK_CUR, io->uptr);
  11.107 +
  11.108 +	cinfo.err = jpeg_std_error(&jerr);	/* XXX change... */
  11.109 +	jpeg_create_decompress(&cinfo);
  11.110 +
  11.111 +	src.pub.init_source = init_source;
  11.112 +	src.pub.fill_input_buffer = fill_input_buffer;
  11.113 +	src.pub.skip_input_data = skip_input_data;
  11.114 +	src.pub.resync_to_restart = jpeg_resync_to_restart;
  11.115 +	src.pub.term_source = term_source;
  11.116 +	src.pub.next_input_byte = 0;
  11.117 +	src.pub.bytes_in_buffer = 0;
  11.118 +	src.io = io;
  11.119 +	cinfo.src = (struct jpeg_source_mgr*)&src;
  11.120 +
  11.121 +	jpeg_read_header(&cinfo, 1);
  11.122 +	cinfo.out_color_space = JCS_RGB;
  11.123 +
  11.124 +	if(img_set_pixels(img, cinfo.image_width, cinfo.image_height, IMG_FMT_RGB24, 0) == -1) {
  11.125 +		jpeg_destroy_decompress(&cinfo);
  11.126 +		return -1;
  11.127 +	}
  11.128 +
  11.129 +	if(!(scanlines = malloc(img->height * sizeof *scanlines))) {
  11.130 +		jpeg_destroy_decompress(&cinfo);
  11.131 +		return -1;
  11.132 +	}
  11.133 +	scanlines[0] = img->pixels;
  11.134 +	for(i=1; i<img->height; i++) {
  11.135 +		scanlines[i] = scanlines[i - 1] + img->width * img->pixelsz;
  11.136 +	}
  11.137 +
  11.138 +	jpeg_start_decompress(&cinfo);
  11.139 +	while(nlines < img->height) {
  11.140 +		int res = jpeg_read_scanlines(&cinfo, scanlines + nlines, img->height - nlines);
  11.141 +		nlines += res;
  11.142 +	}
  11.143 +	jpeg_finish_decompress(&cinfo);
  11.144 +	jpeg_destroy_decompress(&cinfo);
  11.145 +
  11.146 +	free(scanlines);
  11.147 +	return 0;
  11.148 +}
  11.149 +
  11.150 +static int write(struct img_pixmap *img, struct img_io *io)
  11.151 +{
  11.152 +	int i, nlines = 0;
  11.153 +	struct jpeg_compress_struct cinfo;
  11.154 +	struct jpeg_error_mgr jerr;
  11.155 +	struct dst_mgr dest;
  11.156 +	struct img_pixmap tmpimg;
  11.157 +	unsigned char **scanlines;
  11.158 +
  11.159 +	img_init(&tmpimg);
  11.160 +
  11.161 +	if(img->fmt != IMG_FMT_RGB24) {
  11.162 +		if(img_copy(&tmpimg, img) == -1) {
  11.163 +			return -1;
  11.164 +		}
  11.165 +		if(img_convert(&tmpimg, IMG_FMT_RGB24) == -1) {
  11.166 +			img_destroy(&tmpimg);
  11.167 +			return -1;
  11.168 +		}
  11.169 +		img = &tmpimg;
  11.170 +	}
  11.171 +
  11.172 +	if(!(scanlines = malloc(img->height * sizeof *scanlines))) {
  11.173 +		img_destroy(&tmpimg);
  11.174 +		return -1;
  11.175 +	}
  11.176 +	scanlines[0] = img->pixels;
  11.177 +	for(i=1; i<img->height; i++) {
  11.178 +		scanlines[i] = scanlines[i - 1] + img->width * img->pixelsz;
  11.179 +	}
  11.180 +
  11.181 +	cinfo.err = jpeg_std_error(&jerr);	/* XXX */
  11.182 +	jpeg_create_compress(&cinfo);
  11.183 +
  11.184 +	dest.pub.init_destination = init_destination;
  11.185 +	dest.pub.empty_output_buffer = empty_output_buffer;
  11.186 +	dest.pub.term_destination = term_destination;
  11.187 +	dest.io = io;
  11.188 +	cinfo.dest = (struct jpeg_destination_mgr*)&dest;
  11.189 +
  11.190 +	cinfo.image_width = img->width;
  11.191 +	cinfo.image_height = img->height;
  11.192 +	cinfo.input_components = 3;
  11.193 +	cinfo.in_color_space = JCS_RGB;
  11.194 +
  11.195 +	jpeg_set_defaults(&cinfo);
  11.196 +
  11.197 +	jpeg_start_compress(&cinfo, 1);
  11.198 +	while(nlines < img->height) {
  11.199 +		int res = jpeg_write_scanlines(&cinfo, scanlines + nlines, img->height - nlines);
  11.200 +		nlines += res;
  11.201 +	}
  11.202 +	jpeg_finish_compress(&cinfo);
  11.203 +	jpeg_destroy_compress(&cinfo);
  11.204 +
  11.205 +	free(scanlines);
  11.206 +	img_destroy(&tmpimg);
  11.207 +	return 0;
  11.208 +}
  11.209 +
  11.210 +/* -- read source functions --
  11.211 + * the following functions are adapted from jdatasrc.c in jpeglib
  11.212 + */
  11.213 +static void init_source(j_decompress_ptr jd)
  11.214 +{
  11.215 +	struct src_mgr *src = (struct src_mgr*)jd->src;
  11.216 +	src->start_of_file = 1;
  11.217 +}
  11.218 +
  11.219 +static int fill_input_buffer(j_decompress_ptr jd)
  11.220 +{
  11.221 +	struct src_mgr *src = (struct src_mgr*)jd->src;
  11.222 +	size_t nbytes;
  11.223 +
  11.224 +	nbytes = src->io->read(src->buffer, INPUT_BUF_SIZE, src->io->uptr);
  11.225 +
  11.226 +	if(nbytes <= 0) {
  11.227 +		if(src->start_of_file) {
  11.228 +			return 0;
  11.229 +		}
  11.230 +		/* insert a fake EOI marker */
  11.231 +		src->buffer[0] = 0xff;
  11.232 +		src->buffer[1] = JPEG_EOI;
  11.233 +		nbytes = 2;
  11.234 +	}
  11.235 +
  11.236 +	src->pub.next_input_byte = src->buffer;
  11.237 +	src->pub.bytes_in_buffer = nbytes;
  11.238 +	src->start_of_file = 0;
  11.239 +	return 1;
  11.240 +}
  11.241 +
  11.242 +static void skip_input_data(j_decompress_ptr jd, long num_bytes)
  11.243 +{
  11.244 +	struct src_mgr *src = (struct src_mgr*)jd->src;
  11.245 +
  11.246 +	if(num_bytes > 0) {
  11.247 +		while(num_bytes > (long)src->pub.bytes_in_buffer) {
  11.248 +			num_bytes -= (long)src->pub.bytes_in_buffer;
  11.249 +			fill_input_buffer(jd);
  11.250 +		}
  11.251 +		src->pub.next_input_byte += (size_t)num_bytes;
  11.252 +		src->pub.bytes_in_buffer -= (size_t)num_bytes;
  11.253 +	}
  11.254 +}
  11.255 +
  11.256 +static void term_source(j_decompress_ptr jd)
  11.257 +{
  11.258 +	/* nothing to see here, move along */
  11.259 +}
  11.260 +
  11.261 +
  11.262 +/* -- write destination functions --
  11.263 + * the following functions are adapted from jdatadst.c in jpeglib
  11.264 + */
  11.265 +static void init_destination(j_compress_ptr jc)
  11.266 +{
  11.267 +	struct dst_mgr *dest = (struct dst_mgr*)jc->dest;
  11.268 +
  11.269 +	dest->pub.next_output_byte = dest->buffer;
  11.270 +	dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
  11.271 +}
  11.272 +
  11.273 +static int empty_output_buffer(j_compress_ptr jc)
  11.274 +{
  11.275 +	struct dst_mgr *dest = (struct dst_mgr*)jc->dest;
  11.276 +
  11.277 +	if(dest->io->write(dest->buffer, OUTPUT_BUF_SIZE, dest->io->uptr) != OUTPUT_BUF_SIZE) {
  11.278 +		return 0;
  11.279 +	}
  11.280 +
  11.281 +	dest->pub.next_output_byte = dest->buffer;
  11.282 +	dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
  11.283 +	return 1;
  11.284 +}
  11.285 +
  11.286 +static void term_destination(j_compress_ptr jc)
  11.287 +{
  11.288 +	struct dst_mgr *dest = (struct dst_mgr*)jc->dest;
  11.289 +	size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
  11.290 +
  11.291 +	/* write any remaining data in the buffer */
  11.292 +	if(datacount > 0) {
  11.293 +		dest->io->write(dest->buffer, datacount, dest->io->uptr);
  11.294 +	}
  11.295 +	/* XXX flush? ... */
  11.296 +}
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/prototype/imago2/file_png.c	Sun Oct 07 02:04:00 2012 +0300
    12.3 @@ -0,0 +1,244 @@
    12.4 +/*
    12.5 +libimago - a multi-format image file input/output library.
    12.6 +Copyright (C) 2010 John Tsiombikas <nuclear@member.fsf.org>
    12.7 +
    12.8 +This program is free software: you can redistribute it and/or modify
    12.9 +it under the terms of the GNU Lesser General Public License as published
   12.10 +by the Free Software Foundation, either version 3 of the License, or
   12.11 +(at your option) any later version.
   12.12 +
   12.13 +This program is distributed in the hope that it will be useful,
   12.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of
   12.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12.16 +GNU Lesser General Public License for more details.
   12.17 +
   12.18 +You should have received a copy of the GNU Lesser General Public License
   12.19 +along with this program.  If not, see <http://www.gnu.org/licenses/>.
   12.20 +*/
   12.21 +
   12.22 +/* -- PNG module -- */
   12.23 +
   12.24 +#include <stdlib.h>
   12.25 +#include <png.h>
   12.26 +#include "imago2.h"
   12.27 +#include "ftype_module.h"
   12.28 +
   12.29 +static int check_file(struct img_io *io);
   12.30 +static int read_file(struct img_pixmap *img, struct img_io *io);
   12.31 +static int write_file(struct img_pixmap *img, struct img_io *io);
   12.32 +
   12.33 +static void read_func(png_struct *png, unsigned char *data, size_t len);
   12.34 +static void write_func(png_struct *png, unsigned char *data, size_t len);
   12.35 +static void flush_func(png_struct *png);
   12.36 +
   12.37 +static int png_type_to_fmt(int color_type, int channel_bits);
   12.38 +static int fmt_to_png_type(enum img_fmt fmt);
   12.39 +
   12.40 +
   12.41 +int img_register_png(void)
   12.42 +{
   12.43 +	static struct ftype_module mod = {".png", check_file, read_file, write_file};
   12.44 +	return img_register_module(&mod);
   12.45 +}
   12.46 +
   12.47 +static int check_file(struct img_io *io)
   12.48 +{
   12.49 +	unsigned char sig[8];
   12.50 +	int res;
   12.51 +	long pos = io->seek(0, SEEK_CUR, io->uptr);
   12.52 +
   12.53 +	if(io->read(sig, 8, io->uptr) < 8) {
   12.54 +		io->seek(pos, SEEK_SET, io->uptr);
   12.55 +		return -1;
   12.56 +	}
   12.57 +
   12.58 +	res = png_sig_cmp(sig, 0, 8) == 0 ? 0 : -1;
   12.59 +	io->seek(pos, SEEK_SET, io->uptr);
   12.60 +	return res;
   12.61 +}
   12.62 +
   12.63 +static int read_file(struct img_pixmap *img, struct img_io *io)
   12.64 +{
   12.65 +	png_struct *png;
   12.66 +	png_info *info;
   12.67 +	unsigned char **lineptr, *dest;
   12.68 +	int i, channel_bits, color_type, ilace_type, compression, filtering, fmt;
   12.69 +	png_uint_32 xsz, ysz;
   12.70 +
   12.71 +	if(!(png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0))) {
   12.72 +		return -1;
   12.73 +	}
   12.74 +
   12.75 +	if(!(info = png_create_info_struct(png))) {
   12.76 +		png_destroy_read_struct(&png, 0, 0);
   12.77 +		return -1;
   12.78 +	}
   12.79 +
   12.80 +	if(setjmp(png_jmpbuf(png))) {
   12.81 +		png_destroy_read_struct(&png, &info, 0);
   12.82 +		return -1;
   12.83 +	}
   12.84 +
   12.85 +	png_set_read_fn(png, io, read_func);
   12.86 +	png_set_sig_bytes(png, 0);
   12.87 +	png_read_png(png, info, 0, 0);
   12.88 +
   12.89 +	png_get_IHDR(png, info, &xsz, &ysz, &channel_bits, &color_type, &ilace_type,
   12.90 +			&compression, &filtering);
   12.91 +	if((fmt = png_type_to_fmt(color_type, channel_bits)) == -1) {
   12.92 +		png_destroy_read_struct(&png, &info, 0);
   12.93 +		return -1;
   12.94 +	}
   12.95 +
   12.96 +	if(img_set_pixels(img, xsz, ysz, fmt, 0) == -1) {
   12.97 +		png_destroy_read_struct(&png, &info, 0);
   12.98 +		return -1;
   12.99 +	}
  12.100 +
  12.101 +	lineptr = (unsigned char**)png_get_rows(png, info);
  12.102 +
  12.103 +	dest = img->pixels;
  12.104 +	for(i=0; i<ysz; i++) {
  12.105 +		memcpy(dest, lineptr[i], xsz * img->pixelsz);
  12.106 +		dest += xsz * img->pixelsz;
  12.107 +	}
  12.108 +	png_destroy_read_struct(&png, &info, 0);
  12.109 +	return 0;
  12.110 +}
  12.111 +
  12.112 +
  12.113 +static int write_file(struct img_pixmap *img, struct img_io *io)
  12.114 +{
  12.115 +	png_struct *png;
  12.116 +	png_info *info;
  12.117 +	png_text txt;
  12.118 +	struct img_pixmap tmpimg;
  12.119 +	unsigned char **rows;
  12.120 +	unsigned char *pixptr;
  12.121 +	int i, coltype;
  12.122 +
  12.123 +	img_init(&tmpimg);
  12.124 +
  12.125 +	if(!(png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0))) {
  12.126 +		return -1;
  12.127 +	}
  12.128 +	if(!(info = png_create_info_struct(png))) {
  12.129 +		png_destroy_write_struct(&png, 0);
  12.130 +		return -1;
  12.131 +	}
  12.132 +
  12.133 +	/* if the input image is floating-point, we need to convert it to integer */
  12.134 +	if(img_is_float(img)) {
  12.135 +		if(img_copy(&tmpimg, img) == -1) {
  12.136 +			return -1;
  12.137 +		}
  12.138 +		if(img_to_integer(&tmpimg) == -1) {
  12.139 +			img_destroy(&tmpimg);
  12.140 +			return -1;
  12.141 +		}
  12.142 +		img = &tmpimg;
  12.143 +	}
  12.144 +
  12.145 +	txt.compression = PNG_TEXT_COMPRESSION_NONE;
  12.146 +	txt.key = "Software";
  12.147 +	txt.text = "libimago2";
  12.148 +	txt.text_length = 0;
  12.149 +
  12.150 +	if(setjmp(png_jmpbuf(png))) {
  12.151 +		png_destroy_write_struct(&png, &info);
  12.152 +		img_destroy(&tmpimg);
  12.153 +		return -1;
  12.154 +	}
  12.155 +	png_set_write_fn(png, io, write_func, flush_func);
  12.156 +
  12.157 +	coltype = fmt_to_png_type(img->fmt);
  12.158 +	png_set_IHDR(png, info, img->width, img->height, 8, coltype, PNG_INTERLACE_NONE,
  12.159 +			PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
  12.160 +	png_set_text(png, info, &txt, 1);
  12.161 +
  12.162 +	if(!(rows = malloc(img->height * sizeof *rows))) {
  12.163 +		png_destroy_write_struct(&png, &info);
  12.164 +		img_destroy(&tmpimg);
  12.165 +		return -1;
  12.166 +	}
  12.167 +
  12.168 +	pixptr = img->pixels;
  12.169 +	for(i=0; i<img->height; i++) {
  12.170 +		rows[i] = pixptr;
  12.171 +		pixptr += img->width * img->pixelsz;
  12.172 +	}
  12.173 +	png_set_rows(png, info, rows);
  12.174 +
  12.175 +	png_write_png(png, info, 0, 0);
  12.176 +	png_write_end(png, info);
  12.177 +	png_destroy_write_struct(&png, &info);
  12.178 +
  12.179 +	free(rows);
  12.180 +
  12.181 +	img_destroy(&tmpimg);
  12.182 +	return 0;
  12.183 +}
  12.184 +
  12.185 +static void read_func(png_struct *png, unsigned char *data, size_t len)
  12.186 +{
  12.187 +	struct img_io *io = (struct img_io*)png_get_io_ptr(png);
  12.188 +
  12.189 +	if(io->read(data, len, io->uptr) == -1) {
  12.190 +		longjmp(png_jmpbuf(png), 1);
  12.191 +	}
  12.192 +}
  12.193 +
  12.194 +static void write_func(png_struct *png, unsigned char *data, size_t len)
  12.195 +{
  12.196 +	struct img_io *io = (struct img_io*)png_get_io_ptr(png);
  12.197 +
  12.198 +	if(io->write(data, len, io->uptr) == -1) {
  12.199 +		longjmp(png_jmpbuf(png), 1);
  12.200 +	}
  12.201 +}
  12.202 +
  12.203 +static void flush_func(png_struct *png)
  12.204 +{
  12.205 +	/* XXX does it matter that we can't flush? */
  12.206 +}
  12.207 +
  12.208 +static int png_type_to_fmt(int color_type, int channel_bits)
  12.209 +{
  12.210 +	/* we don't support non-8bit-per-channel images yet */
  12.211 +	if(channel_bits > 8) {
  12.212 +		return -1;
  12.213 +	}
  12.214 +
  12.215 +	switch(color_type) {
  12.216 +	case PNG_COLOR_TYPE_RGB:
  12.217 +		return IMG_FMT_RGB24;
  12.218 +
  12.219 +	case PNG_COLOR_TYPE_RGB_ALPHA:
  12.220 +		return IMG_FMT_RGBA32;
  12.221 +
  12.222 +	case PNG_COLOR_TYPE_GRAY:
  12.223 +		return IMG_FMT_GREY8;
  12.224 +
  12.225 +	default:
  12.226 +		break;
  12.227 +	}
  12.228 +	return -1;
  12.229 +}
  12.230 +
  12.231 +static int fmt_to_png_type(enum img_fmt fmt)
  12.232 +{
  12.233 +	switch(fmt) {
  12.234 +	case IMG_FMT_GREY8:
  12.235 +		return PNG_COLOR_TYPE_GRAY;
  12.236 +
  12.237 +	case IMG_FMT_RGB24:
  12.238 +		return PNG_COLOR_TYPE_RGB;
  12.239 +
  12.240 +	case IMG_FMT_RGBA32:
  12.241 +		return PNG_COLOR_TYPE_RGBA;
  12.242 +
  12.243 +	default:
  12.244 +		break;
  12.245 +	}
  12.246 +	return -1;
  12.247 +}
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/prototype/imago2/file_ppm.c	Sun Oct 07 02:04:00 2012 +0300
    13.3 @@ -0,0 +1,153 @@
    13.4 +/*
    13.5 +libimago - a multi-format image file input/output library.
    13.6 +Copyright (C) 2010 John Tsiombikas <nuclear@member.fsf.org>
    13.7 +
    13.8 +This program is free software: you can redistribute it and/or modify
    13.9 +it under the terms of the GNU Lesser General Public License as published
   13.10 +by the Free Software Foundation, either version 3 of the License, or
   13.11 +(at your option) any later version.
   13.12 +
   13.13 +This program is distributed in the hope that it will be useful,
   13.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of
   13.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13.16 +GNU Lesser General Public License for more details.
   13.17 +
   13.18 +You should have received a copy of the GNU Lesser General Public License
   13.19 +along with this program.  If not, see <http://www.gnu.org/licenses/>.
   13.20 +*/
   13.21 +
   13.22 +/* -- Portable Pixmap (PPM) module -- */
   13.23 +
   13.24 +#include <string.h>
   13.25 +#include "imago2.h"
   13.26 +#include "ftype_module.h"
   13.27 +
   13.28 +static int check(struct img_io *io);
   13.29 +static int read(struct img_pixmap *img, struct img_io *io);
   13.30 +static int write(struct img_pixmap *img, struct img_io *io);
   13.31 +
   13.32 +int img_register_ppm(void)
   13.33 +{
   13.34 +	static struct ftype_module mod = {".ppm", check, read, write};
   13.35 +	return img_register_module(&mod);
   13.36 +}
   13.37 +
   13.38 +
   13.39 +static int check(struct img_io *io)
   13.40 +{
   13.41 +	char id[2];
   13.42 +	int res = -1;
   13.43 +	long pos = io->seek(0, SEEK_CUR, io->uptr);
   13.44 +
   13.45 +	if(io->read(id, 2, io->uptr) < 2) {
   13.46 +		io->seek(pos, SEEK_SET, io->uptr);
   13.47 +		return -1;
   13.48 +	}
   13.49 +
   13.50 +	if(id[0] == 'P' && (id[1] == '6' || id[1] == '3')) {
   13.51 +		res = 0;
   13.52 +	}
   13.53 +	io->seek(pos, SEEK_SET, io->uptr);
   13.54 +	return res;
   13.55 +}
   13.56 +
   13.57 +static int iofgetc(struct img_io *io)
   13.58 +{
   13.59 +	char c;
   13.60 +	return io->read(&c, 1, io->uptr) < 1 ? -1 : c;
   13.61 +}
   13.62 +
   13.63 +static char *iofgets(char *buf, int size, struct img_io *io)
   13.64 +{
   13.65 +	int c;
   13.66 +	char *ptr = buf;
   13.67 +
   13.68 +	while(--size > 0 && (c = iofgetc(io)) != -1) {
   13.69 +		*ptr++ = c;
   13.70 +		if(c == '\n') break;
   13.71 +	}
   13.72 +	*ptr = 0;
   13.73 +
   13.74 +	return ptr == buf ? 0 : buf;
   13.75 +}
   13.76 +
   13.77 +/* TODO: implement P3 reading */
   13.78 +static int read(struct img_pixmap *img, struct img_io *io)
   13.79 +{
   13.80 +	char buf[256];
   13.81 +	int xsz, ysz, maxval, got_hdrlines = 1;
   13.82 +
   13.83 +	if(!iofgets(buf, sizeof buf, io)) {
   13.84 +		return -1;
   13.85 +	}
   13.86 +	if(!(buf[0] == 'P' && (buf[1] == '6' || buf[1] == '3'))) {
   13.87 +		return -1;
   13.88 +	}
   13.89 +
   13.90 +	while(got_hdrlines < 3 && iofgets(buf, sizeof buf, io)) {
   13.91 +		if(buf[0] == '#') continue;
   13.92 +
   13.93 +		switch(got_hdrlines) {
   13.94 +		case 1:
   13.95 +			if(sscanf(buf, "%d %d\n", &xsz, &ysz) < 2) {
   13.96 +				return -1;
   13.97 +			}
   13.98 +			break;
   13.99 +
  13.100 +		case 2:
  13.101 +			if(sscanf(buf, "%d\n", &maxval) < 1) {
  13.102 +				return -1;
  13.103 +			}
  13.104 +		default:
  13.105 +			break;
  13.106 +		}
  13.107 +		got_hdrlines++;
  13.108 +	}
  13.109 +
  13.110 +	if(xsz < 1 || ysz < 1 || maxval != 255) {
  13.111 +		return -1;
  13.112 +	}
  13.113 +
  13.114 +	if(img_set_pixels(img, xsz, ysz, IMG_FMT_RGB24, 0) == -1) {
  13.115 +		return -1;
  13.116 +	}
  13.117 +
  13.118 +	if(io->read(img->pixels, xsz * ysz * 3, io->uptr) < xsz * ysz * 3) {
  13.119 +		return -1;
  13.120 +	}
  13.121 +	return 0;
  13.122 +}
  13.123 +
  13.124 +static int write(struct img_pixmap *img, struct img_io *io)
  13.125 +{
  13.126 +	int sz;
  13.127 +	char buf[256];
  13.128 +	struct img_pixmap tmpimg;
  13.129 +
  13.130 +	img_init(&tmpimg);
  13.131 +
  13.132 +	if(img->fmt != IMG_FMT_RGB24) {
  13.133 +		if(img_copy(&tmpimg, img) == -1) {
  13.134 +			return -1;
  13.135 +		}
  13.136 +		if(img_convert(&tmpimg, IMG_FMT_RGB24) == -1) {
  13.137 +			return -1;
  13.138 +		}
  13.139 +		img = &tmpimg;
  13.140 +	}
  13.141 +
  13.142 +	sprintf(buf, "P6\n#written by libimago2\n%d %d\n255\n", img->width, img->height);
  13.143 +	if(io->write(buf, strlen(buf), io->uptr) < strlen(buf)) {
  13.144 +		img_destroy(&tmpimg);
  13.145 +		return -1;
  13.146 +	}
  13.147 +
  13.148 +	sz = img->width * img->height * 3;
  13.149 +	if(io->write(img->pixels, sz, io->uptr) < sz) {
  13.150 +		img_destroy(&tmpimg);
  13.151 +		return -1;
  13.152 +	}
  13.153 +
  13.154 +	img_destroy(&tmpimg);
  13.155 +	return 0;
  13.156 +}
    14.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2 +++ b/prototype/imago2/file_rgbe.c	Sun Oct 07 02:04:00 2012 +0300
    14.3 @@ -0,0 +1,501 @@
    14.4 +/* This file contains code to read and write four byte rgbe file format
    14.5 + * developed by Greg Ward.  It handles the conversions between rgbe and
    14.6 + * pixels consisting of floats.  The data is assumed to be an array of floats.
    14.7 + * By default there are three floats per pixel in the order red, green, blue.
    14.8 + * (RGBE_DATA_??? values control this.)
    14.9 + *
   14.10 + * written by Bruce Walter  (bjw@graphics.cornell.edu)  5/26/95
   14.11 + * based on code written by Greg Ward
   14.12 + * minor modifications by John Tsiombikas (nuclear@member.fsf.org) apr.9 2007
   14.13 + */
   14.14 +
   14.15 +#include <stdio.h>
   14.16 +#include <stdlib.h>
   14.17 +#include <string.h>
   14.18 +#include <math.h>
   14.19 +#include <ctype.h>
   14.20 +#include <errno.h>
   14.21 +#include "imago2.h"
   14.22 +#include "ftype_module.h"
   14.23 +
   14.24 +
   14.25 +typedef struct {
   14.26 +	int valid;				/* indicate which fields are valid */
   14.27 +	char programtype[16];	/* listed at beginning of file to identify it
   14.28 +							 * after "#?".  defaults to "RGBE" */
   14.29 +	float gamma;			/* image has already been gamma corrected with
   14.30 +							 * given gamma.  defaults to 1.0 (no correction) */
   14.31 +	float exposure;			/* a value of 1.0 in an image corresponds to
   14.32 +							 * <exposure> watts/steradian/m^2.
   14.33 +							 * defaults to 1.0 */
   14.34 +} rgbe_header_info;
   14.35 +
   14.36 +
   14.37 +static int check(struct img_io *io);
   14.38 +static int read(struct img_pixmap *img, struct img_io *io);
   14.39 +static int write(struct img_pixmap *img, struct img_io *io);
   14.40 +
   14.41 +static int rgbe_read_header(struct img_io *io, int *width, int *height, rgbe_header_info * info);
   14.42 +static int rgbe_read_pixels_rle(struct img_io *io, float *data, int scanline_width, int num_scanlines);
   14.43 +
   14.44 +
   14.45 +int img_register_rgbe(void)
   14.46 +{
   14.47 +	static struct ftype_module mod = {".rgbe", check, read, write};
   14.48 +	return img_register_module(&mod);
   14.49 +}
   14.50 +
   14.51 +
   14.52 +static int check(struct img_io *io)
   14.53 +{
   14.54 +	int xsz, ysz, res;
   14.55 +	long pos = io->seek(0, SEEK_CUR, io->uptr);
   14.56 +
   14.57 +	rgbe_header_info hdr;
   14.58 +	res = rgbe_read_header(io, &xsz, &ysz, &hdr);
   14.59 +
   14.60 +	io->seek(pos, SEEK_SET, io->uptr);
   14.61 +	return res;
   14.62 +}
   14.63 +
   14.64 +static int read(struct img_pixmap *img, struct img_io *io)
   14.65 +{
   14.66 +	int xsz, ysz;
   14.67 +	rgbe_header_info hdr;
   14.68 +
   14.69 +	if(rgbe_read_header(io, &xsz, &ysz, &hdr) == -1) {
   14.70 +		return -1;
   14.71 +	}
   14.72 +
   14.73 +	if(img_set_pixels(img, xsz, ysz, IMG_FMT_RGBF, 0) == -1) {
   14.74 +		return -1;
   14.75 +	}
   14.76 +	if(rgbe_read_pixels_rle(io, img->pixels, xsz, ysz) == -1) {
   14.77 +		return -1;
   14.78 +	}
   14.79 +	return 0;
   14.80 +}
   14.81 +
   14.82 +static int write(struct img_pixmap *img, struct img_io *io)
   14.83 +{
   14.84 +	return -1;	/* TODO */
   14.85 +}
   14.86 +
   14.87 +
   14.88 +static int iofgetc(struct img_io *io)
   14.89 +{
   14.90 +	char c;
   14.91 +	return io->read(&c, 1, io->uptr) < 1 ? -1 : c;
   14.92 +}
   14.93 +
   14.94 +static char *iofgets(char *buf, int size, struct img_io *io)
   14.95 +{
   14.96 +	int c;
   14.97 +	char *ptr = buf;
   14.98 +
   14.99 +	while(--size > 0 && (c = iofgetc(io)) != -1) {
  14.100 +		*ptr++ = c;
  14.101 +		if(c == '\n') break;
  14.102 +	}
  14.103 +	*ptr = 0;
  14.104 +
  14.105 +	return ptr == buf ? 0 : buf;
  14.106 +}
  14.107 +
  14.108 +
  14.109 +/* flags indicating which fields in an rgbe_header_info are valid */
  14.110 +#define RGBE_VALID_PROGRAMTYPE 0x01
  14.111 +#define RGBE_VALID_GAMMA       0x02
  14.112 +#define RGBE_VALID_EXPOSURE    0x04
  14.113 +
  14.114 +/* return codes for rgbe routines */
  14.115 +#define RGBE_RETURN_SUCCESS 0
  14.116 +#define RGBE_RETURN_FAILURE -1
  14.117 +
  14.118 +
  14.119 +#if defined(__cplusplus) || defined(GNUC) || __STDC_VERSION >= 199901L
  14.120 +#define INLINE inline
  14.121 +#else
  14.122 +#define INLINE
  14.123 +#endif
  14.124 +
  14.125 +/* offsets to red, green, and blue components in a data (float) pixel */
  14.126 +#define RGBE_DATA_RED	0
  14.127 +#define RGBE_DATA_GREEN  1
  14.128 +#define RGBE_DATA_BLUE   2
  14.129 +
  14.130 +/* number of floats per pixel */
  14.131 +#define RGBE_DATA_SIZE   3
  14.132 +
  14.133 +enum rgbe_error_codes {
  14.134 +	rgbe_read_error,
  14.135 +	rgbe_write_error,
  14.136 +	rgbe_format_error,
  14.137 +	rgbe_memory_error
  14.138 +};
  14.139 +
  14.140 +
  14.141 +/* default error routine.  change this to change error handling */
  14.142 +static int rgbe_error(int rgbe_error_code, char *msg)
  14.143 +{
  14.144 +	switch (rgbe_error_code) {
  14.145 +	case rgbe_read_error:
  14.146 +		fprintf(stderr, "RGBE read error: %s\n", strerror(errno));
  14.147 +		break;
  14.148 +
  14.149 +	case rgbe_write_error:
  14.150 +		fprintf(stderr, "RGBE write error: %s\n", strerror(errno));
  14.151 +		break;
  14.152 +
  14.153 +	case rgbe_format_error:
  14.154 +		fprintf(stderr, "RGBE bad file format: %s\n", msg);
  14.155 +		break;
  14.156 +
  14.157 +	default:
  14.158 +	case rgbe_memory_error:
  14.159 +		fprintf(stderr, "RGBE error: %s\n", msg);
  14.160 +	}
  14.161 +	return RGBE_RETURN_FAILURE;
  14.162 +}
  14.163 +
  14.164 +/* standard conversion from float pixels to rgbe pixels */
  14.165 +/*static INLINE void float2rgbe(unsigned char rgbe[4], float red, float green, float blue)
  14.166 +{
  14.167 +	float v;
  14.168 +	int e;
  14.169 +
  14.170 +	v = red;
  14.171 +	if(green > v)
  14.172 +		v = green;
  14.173 +	if(blue > v)
  14.174 +		v = blue;
  14.175 +	if(v < 1e-32) {
  14.176 +		rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
  14.177 +	} else {
  14.178 +		v = frexp(v, &e) * 256.0 / v;
  14.179 +		rgbe[0] = (unsigned char)(red * v);
  14.180 +		rgbe[1] = (unsigned char)(green * v);
  14.181 +		rgbe[2] = (unsigned char)(blue * v);
  14.182 +		rgbe[3] = (unsigned char)(e + 128);
  14.183 +	}
  14.184 +}*/
  14.185 +
  14.186 +/* standard conversion from rgbe to float pixels */
  14.187 +/* note: Ward uses ldexp(col+0.5,exp-(128+8)). However we wanted pixels */
  14.188 +/*       in the range [0,1] to map back into the range [0,1]. */
  14.189 +static INLINE void rgbe2float(float *red, float *green, float *blue, unsigned char rgbe[4])
  14.190 +{
  14.191 +	float f;
  14.192 +
  14.193 +	if(rgbe[3]) {				/*nonzero pixel */
  14.194 +		f = ldexp(1.0, rgbe[3] - (int)(128 + 8));
  14.195 +		*red = rgbe[0] * f;
  14.196 +		*green = rgbe[1] * f;
  14.197 +		*blue = rgbe[2] * f;
  14.198 +	} else
  14.199 +		*red = *green = *blue = 0.0;
  14.200 +}
  14.201 +
  14.202 +#if 0
  14.203 +/* default minimal header. modify if you want more information in header */
  14.204 +static int rgbe_write_header(FILE * fp, int width, int height, rgbe_header_info * info)
  14.205 +{
  14.206 +	char *programtype = "RGBE";
  14.207 +
  14.208 +	if(info && (info->valid & RGBE_VALID_PROGRAMTYPE))
  14.209 +		programtype = info->programtype;
  14.210 +	if(fprintf(fp, "#?%s\n", programtype) < 0)
  14.211 +		return rgbe_error(rgbe_write_error, NULL);
  14.212 +	/* The #? is to identify file type, the programtype is optional. */
  14.213 +	if(info && (info->valid & RGBE_VALID_GAMMA)) {
  14.214 +		if(fprintf(fp, "GAMMA=%g\n", info->gamma) < 0)
  14.215 +			return rgbe_error(rgbe_write_error, NULL);
  14.216 +	}
  14.217 +	if(info && (info->valid & RGBE_VALID_EXPOSURE)) {
  14.218 +		if(fprintf(fp, "EXPOSURE=%g\n", info->exposure) < 0)
  14.219 +			return rgbe_error(rgbe_write_error, NULL);
  14.220 +	}
  14.221 +	if(fprintf(fp, "FORMAT=32-bit_rle_rgbe\n\n") < 0)
  14.222 +		return rgbe_error(rgbe_write_error, NULL);
  14.223 +	if(fprintf(fp, "-Y %d +X %d\n", height, width) < 0)
  14.224 +		return rgbe_error(rgbe_write_error, NULL);
  14.225 +	return RGBE_RETURN_SUCCESS;
  14.226 +}
  14.227 +#endif
  14.228 +
  14.229 +/* minimal header reading.  modify if you want to parse more information */
  14.230 +static int rgbe_read_header(struct img_io *io, int *width, int *height, rgbe_header_info * info)
  14.231 +{
  14.232 +	char buf[128];
  14.233 +	float tempf;
  14.234 +	int i;
  14.235 +
  14.236 +	if(info) {
  14.237 +		info->valid = 0;
  14.238 +		info->programtype[0] = 0;
  14.239 +		info->gamma = info->exposure = 1.0;
  14.240 +	}
  14.241 +	if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == NULL)
  14.242 +		return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_read_error, NULL);*/
  14.243 +	if((buf[0] != '#') || (buf[1] != '?')) {
  14.244 +		/* if you want to require the magic token then uncomment the next line */
  14.245 +		/*return rgbe_error(rgbe_format_error,"bad initial token"); */
  14.246 +	} else if(info) {
  14.247 +		info->valid |= RGBE_VALID_PROGRAMTYPE;
  14.248 +		for(i = 0; i < sizeof(info->programtype) - 1; i++) {
  14.249 +			if((buf[i + 2] == 0) || isspace(buf[i + 2]))
  14.250 +				break;
  14.251 +			info->programtype[i] = buf[i + 2];
  14.252 +		}
  14.253 +		info->programtype[i] = 0;
  14.254 +		if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0)
  14.255 +			return rgbe_error(rgbe_read_error, NULL);
  14.256 +	}
  14.257 +	for(;;) {
  14.258 +		if((buf[0] == 0) || (buf[0] == '\n'))
  14.259 +			return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_format_error, "no FORMAT specifier found");*/
  14.260 +		else if(strcmp(buf, "FORMAT=32-bit_rle_rgbe\n") == 0)
  14.261 +			break;				/* format found so break out of loop */
  14.262 +		else if(info && (sscanf(buf, "GAMMA=%g", &tempf) == 1)) {
  14.263 +			info->gamma = tempf;
  14.264 +			info->valid |= RGBE_VALID_GAMMA;
  14.265 +		} else if(info && (sscanf(buf, "EXPOSURE=%g", &tempf) == 1)) {
  14.266 +			info->exposure = tempf;
  14.267 +			info->valid |= RGBE_VALID_EXPOSURE;
  14.268 +		}
  14.269 +		if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0)
  14.270 +			return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_read_error, NULL);*/
  14.271 +	}
  14.272 +	if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0)
  14.273 +		return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_read_error, NULL);*/
  14.274 +	if(strcmp(buf, "\n") != 0)
  14.275 +		return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_format_error, "missing blank line after FORMAT specifier");*/
  14.276 +	if(iofgets(buf, sizeof(buf) / sizeof(buf[0]), io) == 0)
  14.277 +		return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_read_error, NULL);*/
  14.278 +	if(sscanf(buf, "-Y %d +X %d", height, width) < 2)
  14.279 +		return RGBE_RETURN_FAILURE;/*rgbe_error(rgbe_format_error, "missing image size specifier");*/
  14.280 +	return RGBE_RETURN_SUCCESS;
  14.281 +}
  14.282 +
  14.283 +#if 0
  14.284 +/* simple write routine that does not use run length encoding */
  14.285 +
  14.286 +/* These routines can be made faster by allocating a larger buffer and
  14.287 +   fread-ing and fwrite-ing the data in larger chunks */
  14.288 +static int rgbe_write_pixels(FILE * fp, float *data, int numpixels)
  14.289 +{
  14.290 +	unsigned char rgbe[4];
  14.291 +
  14.292 +	while(numpixels-- > 0) {
  14.293 +		float2rgbe(rgbe, data[RGBE_DATA_RED], data[RGBE_DATA_GREEN], data[RGBE_DATA_BLUE]);
  14.294 +		data += RGBE_DATA_SIZE;
  14.295 +		if(fwrite(rgbe, sizeof(rgbe), 1, fp) < 1)
  14.296 +			return rgbe_error(rgbe_write_error, NULL);
  14.297 +	}
  14.298 +	return RGBE_RETURN_SUCCESS;
  14.299 +}
  14.300 +#endif
  14.301 +
  14.302 +/* simple read routine.  will not correctly handle run length encoding */
  14.303 +static int rgbe_read_pixels(struct img_io *io, float *data, int numpixels)
  14.304 +{
  14.305 +	unsigned char rgbe[4];
  14.306 +
  14.307 +	while(numpixels-- > 0) {
  14.308 +		if(io->read(rgbe, sizeof(rgbe), io->uptr) < 1)
  14.309 +			return rgbe_error(rgbe_read_error, NULL);
  14.310 +		rgbe2float(&data[RGBE_DATA_RED], &data[RGBE_DATA_GREEN], &data[RGBE_DATA_BLUE], rgbe);
  14.311 +		data += RGBE_DATA_SIZE;
  14.312 +	}
  14.313 +	return RGBE_RETURN_SUCCESS;
  14.314 +}
  14.315 +
  14.316 +#if 0
  14.317 +/* The code below is only needed for the run-length encoded files. */
  14.318 +
  14.319 +/* Run length encoding adds considerable complexity but does */
  14.320 +
  14.321 +/* save some space.  For each scanline, each channel (r,g,b,e) is */
  14.322 +
  14.323 +/* encoded separately for better compression. */
  14.324 +
  14.325 +static int rgbe_write_bytes_rle(struct img_io *io, unsigned char *data, int numbytes)
  14.326 +{
  14.327 +#define MINRUNLENGTH 4
  14.328 +	int cur, beg_run, run_count, old_run_count, nonrun_count;
  14.329 +	unsigned char buf[2];
  14.330 +
  14.331 +	cur = 0;
  14.332 +	while(cur < numbytes) {
  14.333 +		beg_run = cur;
  14.334 +		/* find next run of length at least 4 if one exists */
  14.335 +		run_count = old_run_count = 0;
  14.336 +		while((run_count < MINRUNLENGTH) && (beg_run < numbytes)) {
  14.337 +			beg_run += run_count;
  14.338 +			old_run_count = run_count;
  14.339 +			run_count = 1;
  14.340 +			while((beg_run + run_count < numbytes) && (run_count < 127)
  14.341 +				  && (data[beg_run] == data[beg_run + run_count]))
  14.342 +				run_count++;
  14.343 +		}
  14.344 +		/* if data before next big run is a short run then write it as such */
  14.345 +		if((old_run_count > 1) && (old_run_count == beg_run - cur)) {
  14.346 +			buf[0] = 128 + old_run_count;	/*write short run */
  14.347 +			buf[1] = data[cur];
  14.348 +			if(fwrite(buf, sizeof(buf[0]) * 2, 1, fp) < 1)
  14.349 +				return rgbe_error(rgbe_write_error, NULL);
  14.350 +			cur = beg_run;
  14.351 +		}
  14.352 +		/* write out bytes until we reach the start of the next run */
  14.353 +		while(cur < beg_run) {
  14.354 +			nonrun_count = beg_run - cur;
  14.355 +			if(nonrun_count > 128)
  14.356 +				nonrun_count = 128;
  14.357 +			buf[0] = nonrun_count;
  14.358 +			if(fwrite(buf, sizeof(buf[0]), 1, fp) < 1)
  14.359 +				return rgbe_error(rgbe_write_error, NULL);
  14.360 +			if(fwrite(&data[cur], sizeof(data[0]) * nonrun_count, 1, fp) < 1)
  14.361 +				return rgbe_error(rgbe_write_error, NULL);
  14.362 +			cur += nonrun_count;
  14.363 +		}
  14.364 +		/* write out next run if one was found */
  14.365 +		if(run_count >= MINRUNLENGTH) {
  14.366 +			buf[0] = 128 + run_count;
  14.367 +			buf[1] = data[beg_run];
  14.368 +			if(fwrite(buf, sizeof(buf[0]) * 2, 1, fp) < 1)
  14.369 +				return rgbe_error(rgbe_write_error, NULL);
  14.370 +			cur += run_count;
  14.371 +		}
  14.372 +	}
  14.373 +	return RGBE_RETURN_SUCCESS;
  14.374 +#undef MINRUNLENGTH
  14.375 +}
  14.376 +
  14.377 +static int rgbe_write_pixels_rle(struct img_io *io, float *data, int scanline_width, int num_scanlines)
  14.378 +{
  14.379 +	unsigned char rgbe[4];
  14.380 +	unsigned char *buffer;
  14.381 +	int i, err;
  14.382 +
  14.383 +	if((scanline_width < 8) || (scanline_width > 0x7fff))
  14.384 +		/* run length encoding is not allowed so write flat */
  14.385 +		return rgbe_write_pixels(io, data, scanline_width * num_scanlines);
  14.386 +	buffer = (unsigned char *)malloc(sizeof(unsigned char) * 4 * scanline_width);
  14.387 +	if(buffer == NULL)
  14.388 +		/* no buffer space so write flat */
  14.389 +		return rgbe_write_pixels(fp, data, scanline_width * num_scanlines);
  14.390 +	while(num_scanlines-- > 0) {
  14.391 +		rgbe[0] = 2;
  14.392 +		rgbe[1] = 2;
  14.393 +		rgbe[2] = scanline_width >> 8;
  14.394 +		rgbe[3] = scanline_width & 0xFF;
  14.395 +		if(fwrite(rgbe, sizeof(rgbe), 1, fp) < 1) {
  14.396 +			free(buffer);
  14.397 +			return rgbe_error(rgbe_write_error, NULL);
  14.398 +		}
  14.399 +		for(i = 0; i < scanline_width; i++) {
  14.400 +			float2rgbe(rgbe, data[RGBE_DATA_RED], data[RGBE_DATA_GREEN], data[RGBE_DATA_BLUE]);
  14.401 +			buffer[i] = rgbe[0];
  14.402 +			buffer[i + scanline_width] = rgbe[1];
  14.403 +			buffer[i + 2 * scanline_width] = rgbe[2];
  14.404 +			buffer[i + 3 * scanline_width] = rgbe[3];
  14.405 +			data += RGBE_DATA_SIZE;
  14.406 +		}
  14.407 +		/* write out each of the four channels separately run length encoded */
  14.408 +		/* first red, then green, then blue, then exponent */
  14.409 +		for(i = 0; i < 4; i++) {
  14.410 +			if((err = rgbe_write_bytes_rle(fp, &buffer[i * scanline_width],
  14.411 +										  scanline_width)) != RGBE_RETURN_SUCCESS) {
  14.412 +				free(buffer);
  14.413 +				return err;
  14.414 +			}
  14.415 +		}
  14.416 +	}
  14.417 +	free(buffer);
  14.418 +	return RGBE_RETURN_SUCCESS;
  14.419 +}
  14.420 +#endif
  14.421 +
  14.422 +static int rgbe_read_pixels_rle(struct img_io *io, float *data, int scanline_width, int num_scanlines)
  14.423 +{
  14.424 +	unsigned char rgbe[4], *scanline_buffer, *ptr, *ptr_end;
  14.425 +	int i, count;
  14.426 +	unsigned char buf[2];
  14.427 +
  14.428 +	if((scanline_width < 8) || (scanline_width > 0x7fff))
  14.429 +		/* run length encoding is not allowed so read flat */
  14.430 +		return rgbe_read_pixels(io, data, scanline_width * num_scanlines);
  14.431 +	scanline_buffer = NULL;
  14.432 +	/* read in each successive scanline */
  14.433 +	while(num_scanlines > 0) {
  14.434 +		if(io->read(rgbe, sizeof(rgbe), io->uptr) < 1) {
  14.435 +			free(scanline_buffer);
  14.436 +			return rgbe_error(rgbe_read_error, NULL);
  14.437 +		}
  14.438 +		if((rgbe[0] != 2) || (rgbe[1] != 2) || (rgbe[2] & 0x80)) {
  14.439 +			/* this file is not run length encoded */
  14.440 +			rgbe2float(&data[0], &data[1], &data[2], rgbe);
  14.441 +			data += RGBE_DATA_SIZE;
  14.442 +			free(scanline_buffer);
  14.443 +			return rgbe_read_pixels(io, data, scanline_width * num_scanlines - 1);
  14.444 +		}
  14.445 +		if((((int)rgbe[2]) << 8 | rgbe[3]) != scanline_width) {
  14.446 +			free(scanline_buffer);
  14.447 +			return rgbe_error(rgbe_format_error, "wrong scanline width");
  14.448 +		}
  14.449 +		if(scanline_buffer == NULL)
  14.450 +			scanline_buffer = (unsigned char *)
  14.451 +				malloc(sizeof(unsigned char) * 4 * scanline_width);
  14.452 +		if(scanline_buffer == NULL)
  14.453 +			return rgbe_error(rgbe_memory_error, "unable to allocate buffer space");
  14.454 +
  14.455 +		ptr = &scanline_buffer[0];
  14.456 +		/* read each of the four channels for the scanline into the buffer */
  14.457 +		for(i = 0; i < 4; i++) {
  14.458 +			ptr_end = &scanline_buffer[(i + 1) * scanline_width];
  14.459 +			while(ptr < ptr_end) {
  14.460 +				if(io->read(buf, sizeof(buf[0]) * 2, io->uptr) < 1) {
  14.461 +					free(scanline_buffer);
  14.462 +					return rgbe_error(rgbe_read_error, NULL);
  14.463 +				}
  14.464 +				if(buf[0] > 128) {
  14.465 +					/* a run of the same value */
  14.466 +					count = buf[0] - 128;
  14.467 +					if((count == 0) || (count > ptr_end - ptr)) {
  14.468 +						free(scanline_buffer);
  14.469 +						return rgbe_error(rgbe_format_error, "bad scanline data");
  14.470 +					}
  14.471 +					while(count-- > 0)
  14.472 +						*ptr++ = buf[1];
  14.473 +				} else {
  14.474 +					/* a non-run */
  14.475 +					count = buf[0];
  14.476 +					if((count == 0) || (count > ptr_end - ptr)) {
  14.477 +						free(scanline_buffer);
  14.478 +						return rgbe_error(rgbe_format_error, "bad scanline data");
  14.479 +					}
  14.480 +					*ptr++ = buf[1];
  14.481 +					if(--count > 0) {
  14.482 +						if(io->read(ptr, sizeof(*ptr) * count, io->uptr) < 1) {
  14.483 +							free(scanline_buffer);
  14.484 +							return rgbe_error(rgbe_read_error, NULL);
  14.485 +						}
  14.486 +						ptr += count;
  14.487 +					}
  14.488 +				}
  14.489 +			}
  14.490 +		}
  14.491 +		/* now convert data from buffer into floats */
  14.492 +		for(i = 0; i < scanline_width; i++) {
  14.493 +			rgbe[0] = scanline_buffer[i];
  14.494 +			rgbe[1] = scanline_buffer[i + scanline_width];
  14.495 +			rgbe[2] = scanline_buffer[i + 2 * scanline_width];
  14.496 +			rgbe[3] = scanline_buffer[i + 3 * scanline_width];
  14.497 +			rgbe2float(&data[RGBE_DATA_RED], &data[RGBE_DATA_GREEN], &data[RGBE_DATA_BLUE], rgbe);
  14.498 +			data += RGBE_DATA_SIZE;
  14.499 +		}
  14.500 +		num_scanlines--;
  14.501 +	}
  14.502 +	free(scanline_buffer);
  14.503 +	return RGBE_RETURN_SUCCESS;
  14.504 +}
    15.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.2 +++ b/prototype/imago2/ftype_module.c	Sun Oct 07 02:04:00 2012 +0300
    15.3 @@ -0,0 +1,118 @@
    15.4 +/*
    15.5 +libimago - a multi-format image file input/output library.
    15.6 +Copyright (C) 2010 John Tsiombikas <nuclear@member.fsf.org>
    15.7 +
    15.8 +This program is free software: you can redistribute it and/or modify
    15.9 +it under the terms of the GNU Lesser General Public License as published
   15.10 +by the Free Software Foundation, either version 3 of the License, or
   15.11 +(at your option) any later version.
   15.12 +
   15.13 +This program is distributed in the hope that it will be useful,
   15.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of
   15.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   15.16 +GNU Lesser General Public License for more details.
   15.17 +
   15.18 +You should have received a copy of the GNU Lesser General Public License
   15.19 +along with this program.  If not, see <http://www.gnu.org/licenses/>.
   15.20 +*/
   15.21 +
   15.22 +#include <stdlib.h>
   15.23 +#include <string.h>
   15.24 +#include "ftype_module.h"
   15.25 +
   15.26 +static struct list_node {
   15.27 +	struct ftype_module *module;
   15.28 +	struct list_node *next;
   15.29 +} *modules;
   15.30 +
   15.31 +/* defined in modules.c which is generated by configure */
   15.32 +void img_modules_init();
   15.33 +
   15.34 +static int done_init;
   15.35 +
   15.36 +int img_register_module(struct ftype_module *mod)
   15.37 +{
   15.38 +	struct list_node *node;
   15.39 +
   15.40 +	if(!(node = malloc(sizeof *node))) {
   15.41 +		return -1;
   15.42 +	}
   15.43 +
   15.44 +	node->module = mod;
   15.45 +	node->next = modules;
   15.46 +	modules = node;
   15.47 +	return 0;
   15.48 +}
   15.49 +
   15.50 +struct ftype_module *img_find_format_module(struct img_io *io)
   15.51 +{
   15.52 +	struct list_node *node;
   15.53 +
   15.54 +	if(!done_init) {
   15.55 +		img_modules_init();
   15.56 +		done_init = 1;
   15.57 +	}
   15.58 +
   15.59 +	node = modules;
   15.60 +	while(node) {
   15.61 +		if(node->module->check(io) != -1) {
   15.62 +			return node->module;
   15.63 +		}
   15.64 +		node = node->next;
   15.65 +	}
   15.66 +	return 0;
   15.67 +}
   15.68 +
   15.69 +struct ftype_module *img_guess_format(const char *fname)
   15.70 +{
   15.71 +	struct list_node *node;
   15.72 +	char *suffix;
   15.73 +	int suffix_len;
   15.74 +
   15.75 +	if(!done_init) {
   15.76 +		img_modules_init();
   15.77 +		done_init = 1;
   15.78 +	}
   15.79 +
   15.80 +	if(!(suffix = strrchr(fname, '.'))) {
   15.81 +		return 0;	/* no suffix, can't guess ... */
   15.82 +	}
   15.83 +	suffix_len = strlen(suffix);
   15.84 +
   15.85 +	node = modules;
   15.86 +	while(node) {
   15.87 +		char *suflist = node->module->suffix;
   15.88 +		char *start, *end;
   15.89 +
   15.90 +		while(*suflist) {
   15.91 +			if(!(start = strstr(suflist, suffix))) {
   15.92 +				break;
   15.93 +			}
   15.94 +			end = start + suffix_len;
   15.95 +
   15.96 +			if(*end == ':' || *end == 0) {
   15.97 +				return node->module;	/* found it */
   15.98 +			}
   15.99 +			suflist = end;
  15.100 +		}
  15.101 +
  15.102 +		node = node->next;
  15.103 +	}
  15.104 +	return 0;
  15.105 +}
  15.106 +
  15.107 +struct ftype_module *img_get_module(int idx)
  15.108 +{
  15.109 +	struct list_node *node;
  15.110 +
  15.111 +	if(!done_init) {
  15.112 +		img_modules_init();
  15.113 +		done_init = 1;
  15.114 +	}
  15.115 +
  15.116 +	node = modules;
  15.117 +	while(node && idx--) {
  15.118 +		node = node->next;
  15.119 +	}
  15.120 +	return node->module;
  15.121 +}
    16.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    16.2 +++ b/prototype/imago2/ftype_module.h	Sun Oct 07 02:04:00 2012 +0300
    16.3 @@ -0,0 +1,39 @@
    16.4 +/*
    16.5 +libimago - a multi-format image file input/output library.
    16.6 +Copyright (C) 2010 John Tsiombikas <nuclear@member.fsf.org>
    16.7 +
    16.8 +This program is free software: you can redistribute it and/or modify
    16.9 +it under the terms of the GNU Lesser General Public License as published
   16.10 +by the Free Software Foundation, either version 3 of the License, or
   16.11 +(at your option) any later version.
   16.12 +
   16.13 +This program is distributed in the hope that it will be useful,
   16.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of
   16.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   16.16 +GNU Lesser General Public License for more details.
   16.17 +
   16.18 +You should have received a copy of the GNU Lesser General Public License
   16.19 +along with this program.  If not, see <http://www.gnu.org/licenses/>.
   16.20 +*/
   16.21 +
   16.22 +#ifndef FTYPE_MODULE_H_
   16.23 +#define FTYPE_MODULE_H_
   16.24 +
   16.25 +#include "imago2.h"
   16.26 +
   16.27 +struct ftype_module {
   16.28 +	char *suffix;	/* used for format autodetection during saving only */
   16.29 +
   16.30 +	int (*check)(struct img_io *io);
   16.31 +	int (*read)(struct img_pixmap *img, struct img_io *io);
   16.32 +	int (*write)(struct img_pixmap *img, struct img_io *io);
   16.33 +};
   16.34 +
   16.35 +int img_register_module(struct ftype_module *mod);
   16.36 +
   16.37 +struct ftype_module *img_find_format_module(struct img_io *io);
   16.38 +struct ftype_module *img_guess_format(const char *fname);
   16.39 +struct ftype_module *img_get_module(int idx);
   16.40 +
   16.41 +
   16.42 +#endif	/* FTYPE_MODULE_H_ */
    17.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    17.2 +++ b/prototype/imago2/imago2.c	Sun Oct 07 02:04:00 2012 +0300
    17.3 @@ -0,0 +1,449 @@
    17.4 +/*
    17.5 +libimago - a multi-format image file input/output library.
    17.6 +Copyright (C) 2010 John Tsiombikas <nuclear@member.fsf.org>
    17.7 +
    17.8 +This program is free software: you can redistribute it and/or modify
    17.9 +it under the terms of the GNU Lesser General Public License as published
   17.10 +by the Free Software Foundation, either version 3 of the License, or
   17.11 +(at your option) any later version.
   17.12 +
   17.13 +This program is distributed in the hope that it will be useful,
   17.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of
   17.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   17.16 +GNU Lesser General Public License for more details.
   17.17 +
   17.18 +You should have received a copy of the GNU Lesser General Public License
   17.19 +along with this program.  If not, see <http://www.gnu.org/licenses/>.
   17.20 +*/
   17.21 +
   17.22 +#include <stdio.h>
   17.23 +#include <stdlib.h>
   17.24 +#include <string.h>
   17.25 +#include "imago2.h"
   17.26 +#include "ftype_module.h"
   17.27 +
   17.28 +static int pixel_size(enum img_fmt fmt);
   17.29 +static size_t def_read(void *buf, size_t bytes, void *uptr);
   17.30 +static size_t def_write(void *buf, size_t bytes, void *uptr);
   17.31 +static long def_seek(long offset, int whence, void *uptr);
   17.32 +
   17.33 +
   17.34 +void img_init(struct img_pixmap *img)
   17.35 +{
   17.36 +	img->pixels = 0;
   17.37 +	img->width = img->height = 0;
   17.38 +	img->fmt = IMG_FMT_RGBA32;
   17.39 +	img->pixelsz = pixel_size(img->fmt);
   17.40 +	img->name = 0;
   17.41 +}
   17.42 +
   17.43 +
   17.44 +void img_destroy(struct img_pixmap *img)
   17.45 +{
   17.46 +	free(img->pixels);
   17.47 +	img->pixels = 0;	/* just in case... */
   17.48 +	img->width = img->height = 0xbadbeef;
   17.49 +	free(img->name);
   17.50 +}
   17.51 +
   17.52 +struct img_pixmap *img_create(void)
   17.53 +{
   17.54 +	struct img_pixmap *p;
   17.55 +
   17.56 +	if(!(p = malloc(sizeof *p))) {
   17.57 +		return 0;
   17.58 +	}
   17.59 +	img_init(p);
   17.60 +	return p;
   17.61 +}
   17.62 +
   17.63 +void img_free(struct img_pixmap *img)
   17.64 +{
   17.65 +	img_destroy(img);
   17.66 +	free(img);
   17.67 +}
   17.68 +
   17.69 +int img_set_name(struct img_pixmap *img, const char *name)
   17.70 +{
   17.71 +	char *tmp;
   17.72 +
   17.73 +	if(!(tmp = malloc(strlen(name) + 1))) {
   17.74 +		return -1;
   17.75 +	}
   17.76 +	strcpy(tmp, name);
   17.77 +	img->name = tmp;
   17.78 +	return 0;
   17.79 +}
   17.80 +
   17.81 +int img_set_format(struct img_pixmap *img, enum img_fmt fmt)
   17.82 +{
   17.83 +	if(img->pixels) {
   17.84 +		return img_convert(img, fmt);
   17.85 +	}
   17.86 +	img->fmt = fmt;
   17.87 +	return 0;
   17.88 +}
   17.89 +
   17.90 +int img_copy(struct img_pixmap *dest, struct img_pixmap *src)
   17.91 +{
   17.92 +	return img_set_pixels(dest, src->width, src->height, src->fmt, src->pixels);
   17.93 +}
   17.94 +
   17.95 +int img_set_pixels(struct img_pixmap *img, int w, int h, enum img_fmt fmt, void *pix)
   17.96 +{
   17.97 +	void *newpix;
   17.98 +	int pixsz = pixel_size(fmt);
   17.99 +
  17.100 +	if(!(newpix = malloc(w * h * pixsz))) {
  17.101 +		return -1;
  17.102 +	}
  17.103 +
  17.104 +	if(pix) {
  17.105 +		memcpy(newpix, pix, w * h * pixsz);
  17.106 +	} else {
  17.107 +		memset(newpix, 0, w * h * pixsz);
  17.108 +	}
  17.109 +
  17.110 +	free(img->pixels);
  17.111 +	img->pixels = newpix;
  17.112 +	img->width = w;
  17.113 +	img->height = h;
  17.114 +	img->pixelsz = pixsz;
  17.115 +	img->fmt = fmt;
  17.116 +	return 0;
  17.117 +}
  17.118 +
  17.119 +void *img_load_pixels(const char *fname, int *xsz, int *ysz, enum img_fmt fmt)
  17.120 +{
  17.121 +	struct img_pixmap img;
  17.122 +
  17.123 +	img_init(&img);
  17.124 +
  17.125 +	if(img_load(&img, fname) == -1) {
  17.126 +		return 0;
  17.127 +	}
  17.128 +	if(img.fmt != fmt) {
  17.129 +		if(img_convert(&img, fmt) == -1) {
  17.130 +			img_destroy(&img);
  17.131 +			return 0;
  17.132 +		}
  17.133 +	}
  17.134 +
  17.135 +	*xsz = img.width;
  17.136 +	*ysz = img.height;
  17.137 +	return img.pixels;
  17.138 +}
  17.139 +
  17.140 +int img_save_pixels(const char *fname, void *pix, int xsz, int ysz, enum img_fmt fmt)
  17.141 +{
  17.142 +	struct img_pixmap img;
  17.143 +
  17.144 +	img_init(&img);
  17.145 +	img.fmt = fmt;
  17.146 +	img.name = (char*)fname;
  17.147 +	img.width = xsz;
  17.148 +	img.height = ysz;
  17.149 +	img.pixels = pix;
  17.150 +
  17.151 +	return img_save(&img, fname);
  17.152 +}
  17.153 +
  17.154 +void img_free_pixels(void *pix)
  17.155 +{
  17.156 +	free(pix);
  17.157 +}
  17.158 +
  17.159 +int img_load(struct img_pixmap *img, const char *fname)
  17.160 +{
  17.161 +	int res;
  17.162 +	FILE *fp;
  17.163 +
  17.164 +	if(!(fp = fopen(fname, "rb"))) {
  17.165 +		return -1;
  17.166 +	}
  17.167 +	res = img_read_file(img, fp);
  17.168 +	fclose(fp);
  17.169 +	return res;
  17.170 +}
  17.171 +
  17.172 +/* TODO implement filetype selection */
  17.173 +int img_save(struct img_pixmap *img, const char *fname)
  17.174 +{
  17.175 +	int res;
  17.176 +	FILE *fp;
  17.177 +
  17.178 +	img_set_name(img, fname);
  17.179 +
  17.180 +	if(!(fp = fopen(fname, "wb"))) {
  17.181 +		return -1;
  17.182 +	}
  17.183 +	res = img_write_file(img, fp);
  17.184 +	fclose(fp);
  17.185 +	return res;
  17.186 +}
  17.187 +
  17.188 +int img_read_file(struct img_pixmap *img, FILE *fp)
  17.189 +{
  17.190 +	struct img_io io = {0, def_read, def_write, def_seek};
  17.191 +
  17.192 +	io.uptr = fp;
  17.193 +	return img_read(img, &io);
  17.194 +}
  17.195 +
  17.196 +int img_write_file(struct img_pixmap *img, FILE *fp)
  17.197 +{
  17.198 +	struct img_io io = {0, def_read, def_write, def_seek};
  17.199 +
  17.200 +	io.uptr = fp;
  17.201 +	return img_write(img, &io);
  17.202 +}
  17.203 +
  17.204 +int img_read(struct img_pixmap *img, struct img_io *io)
  17.205 +{
  17.206 +	struct ftype_module *mod;
  17.207 +
  17.208 +	if((mod = img_find_format_module(io))) {
  17.209 +		return mod->read(img, io);
  17.210 +	}
  17.211 +	return -1;
  17.212 +}
  17.213 +
  17.214 +int img_write(struct img_pixmap *img, struct img_io *io)
  17.215 +{
  17.216 +	struct ftype_module *mod;
  17.217 +
  17.218 +	if(!img->name || !(mod = img_guess_format(img->name))) {
  17.219 +		/* TODO throw some sort of warning? */
  17.220 +		/* TODO implement some sort of module priority or let the user specify? */
  17.221 +		if(!(mod = img_get_module(0))) {
  17.222 +			return -1;
  17.223 +		}
  17.224 +	}
  17.225 +
  17.226 +	return mod->write(img, io);
  17.227 +}
  17.228 +
  17.229 +int img_to_float(struct img_pixmap *img)
  17.230 +{
  17.231 +	enum img_fmt targ_fmt;
  17.232 +
  17.233 +	switch(img->fmt) {
  17.234 +	case IMG_FMT_GREY8:
  17.235 +		targ_fmt = IMG_FMT_GREYF;
  17.236 +		break;
  17.237 +
  17.238 +	case IMG_FMT_RGB24:
  17.239 +		targ_fmt = IMG_FMT_RGBF;
  17.240 +		break;
  17.241 +
  17.242 +	case IMG_FMT_RGBA32:
  17.243 +		targ_fmt = IMG_FMT_RGBAF;
  17.244 +		break;
  17.245 +
  17.246 +	default:
  17.247 +		return 0;	/* already float */
  17.248 +	}
  17.249 +
  17.250 +	return img_convert(img, targ_fmt);
  17.251 +}
  17.252 +
  17.253 +int img_to_integer(struct img_pixmap *img)
  17.254 +{
  17.255 +	enum img_fmt targ_fmt;
  17.256 +
  17.257 +	switch(img->fmt) {
  17.258 +	case IMG_FMT_GREYF:
  17.259 +		targ_fmt = IMG_FMT_GREY8;
  17.260 +		break;
  17.261 +
  17.262 +	case IMG_FMT_RGBF:
  17.263 +		targ_fmt = IMG_FMT_RGB24;
  17.264 +		break;
  17.265 +
  17.266 +	case IMG_FMT_RGBAF:
  17.267 +		targ_fmt = IMG_FMT_RGBA32;
  17.268 +		break;
  17.269 +
  17.270 +	default:
  17.271 +		return 0;	/* already integer */
  17.272 +	}
  17.273 +
  17.274 +	return img_convert(img, targ_fmt);
  17.275 +}
  17.276 +
  17.277 +int img_is_float(struct img_pixmap *img)
  17.278 +{
  17.279 +	return img->fmt >= IMG_FMT_GREYF && img->fmt <= IMG_FMT_RGBAF;
  17.280 +}
  17.281 +
  17.282 +int img_has_alpha(struct img_pixmap *img)
  17.283 +{
  17.284 +	if(img->fmt == IMG_FMT_RGBA32 || img->fmt == IMG_FMT_RGBAF) {
  17.285 +		return 1;
  17.286 +	}
  17.287 +	return 0;
  17.288 +}
  17.289 +
  17.290 +
  17.291 +void img_setpixel(struct img_pixmap *img, int x, int y, void *pixel)
  17.292 +{
  17.293 +	char *dest = (char*)img->pixels + (y * img->width + x) * img->pixelsz;
  17.294 +	memcpy(dest, pixel, img->pixelsz);
  17.295 +}
  17.296 +
  17.297 +void img_getpixel(struct img_pixmap *img, int x, int y, void *pixel)
  17.298 +{
  17.299 +	char *dest = (char*)img->pixels + (y * img->width + x) * img->pixelsz;
  17.300 +	memcpy(pixel, dest, img->pixelsz);
  17.301 +}
  17.302 +
  17.303 +void img_setpixel1i(struct img_pixmap *img, int x, int y, int pix)
  17.304 +{
  17.305 +	img_setpixel4i(img, x, y, pix, pix, pix, pix);
  17.306 +}
  17.307 +
  17.308 +void img_setpixel1f(struct img_pixmap *img, int x, int y, float pix)
  17.309 +{
  17.310 +	img_setpixel4f(img, x, y, pix, pix, pix, pix);
  17.311 +}
  17.312 +
  17.313 +void img_setpixel4i(struct img_pixmap *img, int x, int y, int r, int g, int b, int a)
  17.314 +{
  17.315 +	if(img_is_float(img)) {
  17.316 +		img_setpixel4f(img, x, y, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
  17.317 +	} else {
  17.318 +		unsigned char pixel[4];
  17.319 +		pixel[0] = r;
  17.320 +		pixel[1] = g;
  17.321 +		pixel[2] = b;
  17.322 +		pixel[3] = a;
  17.323 +
  17.324 +		img_setpixel(img, x, y, pixel);
  17.325 +	}
  17.326 +}
  17.327 +
  17.328 +void img_setpixel4f(struct img_pixmap *img, int x, int y, float r, float g, float b, float a)
  17.329 +{
  17.330 +	if(img_is_float(img)) {
  17.331 +		float pixel[4];
  17.332 +		pixel[0] = r;
  17.333 +		pixel[1] = g;
  17.334 +		pixel[2] = b;
  17.335 +		pixel[3] = a;
  17.336 +
  17.337 +		img_setpixel(img, x, y, pixel);
  17.338 +	} else {
  17.339 +		img_setpixel4i(img, x, y, (int)(r * 255.0), (int)(g * 255.0), (int)(b * 255.0), (int)(a * 255.0));
  17.340 +	}
  17.341 +}
  17.342 +
  17.343 +void img_getpixel1i(struct img_pixmap *img, int x, int y, int *pix)
  17.344 +{
  17.345 +	int junk[3];
  17.346 +	img_getpixel4i(img, x, y, pix, junk, junk + 1, junk + 2);
  17.347 +}
  17.348 +
  17.349 +void img_getpixel1f(struct img_pixmap *img, int x, int y, float *pix)
  17.350 +{
  17.351 +	float junk[3];
  17.352 +	img_getpixel4f(img, x, y, pix, junk, junk + 1, junk + 2);
  17.353 +}
  17.354 +
  17.355 +void img_getpixel4i(struct img_pixmap *img, int x, int y, int *r, int *g, int *b, int *a)
  17.356 +{
  17.357 +	if(img_is_float(img)) {
  17.358 +		float pixel[4] = {0, 0, 0, 0};
  17.359 +		img_getpixel(img, x, y, pixel);
  17.360 +		*r = pixel[0] * 255.0;
  17.361 +		*g = pixel[1] * 255.0;
  17.362 +		*b = pixel[2] * 255.0;
  17.363 +		*a = pixel[3] * 255.0;
  17.364 +	} else {
  17.365 +		unsigned char pixel[4];
  17.366 +		img_getpixel(img, x, y, pixel);
  17.367 +		*r = pixel[0];
  17.368 +		*g = pixel[1];
  17.369 +		*b = pixel[2];
  17.370 +		*a = pixel[3];
  17.371 +	}
  17.372 +}
  17.373 +
  17.374 +void img_getpixel4f(struct img_pixmap *img, int x, int y, float *r, float *g, float *b, float *a)
  17.375 +{
  17.376 +	if(img_is_float(img)) {
  17.377 +		float pixel[4] = {0, 0, 0, 0};
  17.378 +		img_getpixel(img, x, y, pixel);
  17.379 +		*r = pixel[0];
  17.380 +		*g = pixel[1];
  17.381 +		*b = pixel[2];
  17.382 +		*a = pixel[3];
  17.383 +	} else {
  17.384 +		unsigned char pixel[4];
  17.385 +		img_getpixel(img, x, y, pixel);
  17.386 +		*r = pixel[0] / 255.0;
  17.387 +		*g = pixel[1] / 255.0;
  17.388 +		*b = pixel[2] / 255.0;
  17.389 +		*a = pixel[3] / 255.0;
  17.390 +	}
  17.391 +}
  17.392 +
  17.393 +void img_io_set_user_data(struct img_io *io, void *uptr)
  17.394 +{
  17.395 +	io->uptr = uptr;
  17.396 +}
  17.397 +
  17.398 +void img_io_set_read_func(struct img_io *io, size_t (*read)(void*, size_t, void*))
  17.399 +{
  17.400 +	io->read = read;
  17.401 +}
  17.402 +
  17.403 +void img_io_set_write_func(struct img_io *io, size_t (*write)(void*, size_t, void*))
  17.404 +{
  17.405 +	io->write = write;
  17.406 +}
  17.407 +
  17.408 +void img_io_set_seek_func(struct img_io *io, long (*seek)(long, int, void*))
  17.409 +{
  17.410 +	io->seek = seek;
  17.411 +}
  17.412 +
  17.413 +
  17.414 +static int pixel_size(enum img_fmt fmt)
  17.415 +{
  17.416 +	switch(fmt) {
  17.417 +	case IMG_FMT_GREY8:
  17.418 +		return 1;
  17.419 +	case IMG_FMT_RGB24:
  17.420 +		return 3;
  17.421 +	case IMG_FMT_RGBA32:
  17.422 +		return 4;
  17.423 +	case IMG_FMT_GREYF:
  17.424 +		return sizeof(float);
  17.425 +	case IMG_FMT_RGBF:
  17.426 +		return 3 * sizeof(float);
  17.427 +	case IMG_FMT_RGBAF:
  17.428 +		return 4 * sizeof(float);
  17.429 +	default:
  17.430 +		break;
  17.431 +	}
  17.432 +	return 0;
  17.433 +}
  17.434 +
  17.435 +static size_t def_read(void *buf, size_t bytes, void *uptr)
  17.436 +{
  17.437 +	return uptr ? fread(buf, 1, bytes, uptr) : 0;
  17.438 +}
  17.439 +
  17.440 +static size_t def_write(void *buf, size_t bytes, void *uptr)
  17.441 +{
  17.442 +	return uptr ? fwrite(buf, 1, bytes, uptr) : 0;
  17.443 +}
  17.444 +
  17.445 +static long def_seek(long offset, int whence, void *uptr)
  17.446 +{
  17.447 +	if(!uptr || fseek(uptr, offset, whence) == -1) {
  17.448 +		return -1;
  17.449 +	}
  17.450 +	return ftell(uptr);
  17.451 +}
  17.452 +
    18.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    18.2 +++ b/prototype/imago2/imago2.h	Sun Oct 07 02:04:00 2012 +0300
    18.3 @@ -0,0 +1,222 @@
    18.4 +/*
    18.5 +libimago - a multi-format image file input/output library.
    18.6 +Copyright (C) 2010-2012 John Tsiombikas <nuclear@member.fsf.org>
    18.7 +
    18.8 +This program is free software: you can redistribute it and/or modify
    18.9 +it under the terms of the GNU Lesser General Public License as published
   18.10 +by the Free Software Foundation, either version 3 of the License, or
   18.11 +(at your option) any later version.
   18.12 +
   18.13 +This program is distributed in the hope that it will be useful,
   18.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of
   18.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   18.16 +GNU Lesser General Public License for more details.
   18.17 +
   18.18 +You should have received a copy of the GNU Lesser General Public License
   18.19 +along with this program.  If not, see <http://www.gnu.org/licenses/>.
   18.20 +*/
   18.21 +
   18.22 +#ifndef IMAGO2_H_
   18.23 +#define IMAGO2_H_
   18.24 +
   18.25 +#include <stdio.h>
   18.26 +
   18.27 +#ifdef __cplusplus
   18.28 +#define IMG_OPTARG(arg, val)	arg = val
   18.29 +#else
   18.30 +#define IMG_OPTARG(arg, val)	arg
   18.31 +#endif
   18.32 +
   18.33 +/* XXX if you change this make sure to also change pack/unpack arrays in conv.c */
   18.34 +enum img_fmt {
   18.35 +	IMG_FMT_GREY8,
   18.36 +	IMG_FMT_RGB24,
   18.37 +	IMG_FMT_RGBA32,
   18.38 +	IMG_FMT_GREYF,
   18.39 +	IMG_FMT_RGBF,
   18.40 +	IMG_FMT_RGBAF,
   18.41 +
   18.42 +	NUM_IMG_FMT
   18.43 +};
   18.44 +
   18.45 +struct img_pixmap {
   18.46 +	void *pixels;
   18.47 +	int width, height;
   18.48 +	enum img_fmt fmt;
   18.49 +	int pixelsz;
   18.50 +	char *name;
   18.51 +};
   18.52 +
   18.53 +struct img_io {
   18.54 +	void *uptr;	/* user-data */
   18.55 +
   18.56 +	size_t (*read)(void *buf, size_t bytes, void *uptr);
   18.57 +	size_t (*write)(void *buf, size_t bytes, void *uptr);
   18.58 +	long (*seek)(long offs, int whence, void *uptr);
   18.59 +};
   18.60 +
   18.61 +#ifdef __cplusplus
   18.62 +extern "C" {
   18.63 +#endif
   18.64 +
   18.65 +/* initialize the img_pixmap structure */
   18.66 +void img_init(struct img_pixmap *img);
   18.67 +/* destroys the img_pixmap structure, freeing the pixel buffer (if available)
   18.68 + * and any other memory held by the pixmap.
   18.69 + */
   18.70 +void img_destroy(struct img_pixmap *img);
   18.71 +
   18.72 +/* convenience function that allocates an img_pixmap struct and then initializes it.
   18.73 + * returns null if the malloc fails.
   18.74 + */
   18.75 +struct img_pixmap *img_create(void);
   18.76 +/* frees a pixmap previously allocated with img_create (free followed by img_destroy) */
   18.77 +void img_free(struct img_pixmap *img);
   18.78 +
   18.79 +int img_set_name(struct img_pixmap *img, const char *name);
   18.80 +
   18.81 +/* set the image pixel format */
   18.82 +int img_set_format(struct img_pixmap *img, enum img_fmt fmt);
   18.83 +
   18.84 +/* copies one pixmap to another.
   18.85 + * equivalent to: img_set_pixels(dest, src->width, src->height, src->fmt, src->pixels)
   18.86 + */
   18.87 +int img_copy(struct img_pixmap *dest, struct img_pixmap *src);
   18.88 +
   18.89 +/* allocates a pixel buffer of the specified dimensions and format, and copies the
   18.90 + * pixels given through the pix pointer into it.
   18.91 + * the pix pointer can be null, in which case there's no copy, just allocation.
   18.92 + *
   18.93 + * C++: fmt and pix have default parameters IMG_FMT_RGBA32 and null respectively.
   18.94 + */
   18.95 +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));
   18.96 +
   18.97 +/* Simplified image loading
   18.98 + * Loads the specified file, and returns a pointer to an array of pixels of the
   18.99 + * requested pixel format. The width and height of the image are returned through
  18.100 + * the xsz and ysz pointers.
  18.101 + * If the image cannot be loaded, the function returns null.
  18.102 + *
  18.103 + * C++: the format argument is optional and defaults to IMG_FMT_RGBA32
  18.104 + */
  18.105 +void *img_load_pixels(const char *fname, int *xsz, int *ysz, IMG_OPTARG(enum img_fmt fmt, IMG_FMT_RGBA32));
  18.106 +
  18.107 +/* Simplified image saving
  18.108 + * Reads an array of pixels supplied through the pix pointer, of dimensions xsz
  18.109 + * and ysz, and pixel-format fmt, and saves it to a file.
  18.110 + * The output filetype is guessed by the filename suffix.
  18.111 + *
  18.112 + * C++: the format argument is optional and defaults to IMG_FMT_RGBA32
  18.113 + */
  18.114 +int img_save_pixels(const char *fname, void *pix, int xsz, int ysz, IMG_OPTARG(enum img_fmt fmt, IMG_FMT_RGBA32));
  18.115 +
  18.116 +/* Frees the memory allocated by img_load_pixels */
  18.117 +void img_free_pixels(void *pix);
  18.118 +
  18.119 +/* Loads an image file into the supplied pixmap */
  18.120 +int img_load(struct img_pixmap *img, const char *fname);
  18.121 +/* Saves the supplied pixmap to a file. The output filetype is guessed by the filename suffix */
  18.122 +int img_save(struct img_pixmap *img, const char *fname);
  18.123 +
  18.124 +/* Reads an image from an open FILE* into the supplied pixmap */
  18.125 +int img_read_file(struct img_pixmap *img, FILE *fp);
  18.126 +/* Writes the supplied pixmap to an open FILE* */
  18.127 +int img_write_file(struct img_pixmap *img, FILE *fp);
  18.128 +
  18.129 +/* Reads an image using user-defined file-i/o functions (see img_io_set_*) */
  18.130 +int img_read(struct img_pixmap *img, struct img_io *io);
  18.131 +/* Writes an image using user-defined file-i/o functions (see img_io_set_*) */
  18.132 +int img_write(struct img_pixmap *img, struct img_io *io);
  18.133 +
  18.134 +/* Converts an image to the specified pixel format */
  18.135 +int img_convert(struct img_pixmap *img, enum img_fmt tofmt);
  18.136 +
  18.137 +/* Converts an image from an integer pixel format to the corresponding floating point one */
  18.138 +int img_to_float(struct img_pixmap *img);
  18.139 +/* Converts an image from a floating point pixel format to the corresponding integer one */
  18.140 +int img_to_integer(struct img_pixmap *img);
  18.141 +
  18.142 +/* Returns non-zero (true) if the supplied image is in a floating point pixel format */
  18.143 +int img_is_float(struct img_pixmap *img);
  18.144 +/* Returns non-zero (true) if the supplied image has an alpha channel */
  18.145 +int img_has_alpha(struct img_pixmap *img);
  18.146 +
  18.147 +
  18.148 +/* don't use these for anything performance-critical */
  18.149 +void img_setpixel(struct img_pixmap *img, int x, int y, void *pixel);
  18.150 +void img_getpixel(struct img_pixmap *img, int x, int y, void *pixel);
  18.151 +
  18.152 +void img_setpixel1i(struct img_pixmap *img, int x, int y, int pix);
  18.153 +void img_setpixel1f(struct img_pixmap *img, int x, int y, float pix);
  18.154 +void img_setpixel4i(struct img_pixmap *img, int x, int y, int r, int g, int b, int a);
  18.155 +void img_setpixel4f(struct img_pixmap *img, int x, int y, float r, float g, float b, float a);
  18.156 +
  18.157 +void img_getpixel1i(struct img_pixmap *img, int x, int y, int *pix);
  18.158 +void img_getpixel1f(struct img_pixmap *img, int x, int y, float *pix);
  18.159 +void img_getpixel4i(struct img_pixmap *img, int x, int y, int *r, int *g, int *b, int *a);
  18.160 +void img_getpixel4f(struct img_pixmap *img, int x, int y, float *r, float *g, float *b, float *a);
  18.161 +
  18.162 +
  18.163 +/* OpenGL helper functions */
  18.164 +
  18.165 +/* Returns the equivalent OpenGL "format" as expected by the 7th argument of glTexImage2D */
  18.166 +unsigned int img_fmt_glfmt(enum img_fmt fmt);
  18.167 +/* Returns the equivalent OpenGL "type" as expected by the 8th argument of glTexImage2D */
  18.168 +unsigned int img_fmt_gltype(enum img_fmt fmt);
  18.169 +/* Returns the equivalent OpenGL "internal format" as expected by the 3rd argument of glTexImage2D */
  18.170 +unsigned int img_fmt_glintfmt(enum img_fmt fmt);
  18.171 +
  18.172 +/* Same as above, based on the pixel format of the supplied image */
  18.173 +unsigned int img_glfmt(struct img_pixmap *img);
  18.174 +unsigned int img_gltype(struct img_pixmap *img);
  18.175 +unsigned int img_glintfmt(struct img_pixmap *img);
  18.176 +
  18.177 +/* Creates an OpenGL texture from the image, and returns the texture id, or 0 for failure */
  18.178 +unsigned int img_gltexture(struct img_pixmap *img);
  18.179 +
  18.180 +/* Load an image and create an OpenGL texture out of it */
  18.181 +unsigned int img_gltexture_load(const char *fname);
  18.182 +unsigned int img_gltexture_read_file(FILE *fp);
  18.183 +unsigned int img_gltexture_read(struct img_io *io);
  18.184 +
  18.185 +/* These functions can be used to fill an img_io struct before it's passed to
  18.186 + * one of the user-defined i/o image reading/writing functions (img_read/img_write).
  18.187 + *
  18.188 + * User-defined i/o functions:
  18.189 + *
  18.190 + * - size_t read_func(void *buffer, size_t bytes, void *user_ptr)
  18.191 + * Must try to fill the buffer with the specified number of bytes, and return
  18.192 + * the number of bytes actually read.
  18.193 + *
  18.194 + * - size_t write_func(void *buffer, size_t bytes, void *user_ptr)
  18.195 + * Must write the specified number of bytes from the supplied buffer and return
  18.196 + * the number of bytes actually written.
  18.197 + *
  18.198 + * - long seek_func(long offset, int whence, void *user_ptr)
  18.199 + * Must seek offset bytes from: the beginning of the file if whence is SEEK_SET,
  18.200 + * the current position if whence is SEEK_CUR, or the end of the file if whence is
  18.201 + * SEEK_END, and return the resulting file offset from the beginning of the file.
  18.202 + * (i.e. seek_func(0, SEEK_CUR, user_ptr); must be equivalent to an ftell).
  18.203 + *
  18.204 + * All three functions get the user-data pointer set through img_io_set_user_data
  18.205 + * as their last argument.
  18.206 + *
  18.207 + * Note: obviously you don't need to set a write function if you're only going
  18.208 + * to call img_read, or the read and seek function if you're only going to call
  18.209 + * img_write.
  18.210 + *
  18.211 + * Note: if the user-supplied write function is buffered, make sure to flush
  18.212 + * (or close the file) after img_write returns.
  18.213 + */
  18.214 +void img_io_set_user_data(struct img_io *io, void *uptr);
  18.215 +void img_io_set_read_func(struct img_io *io, size_t (*read)(void*, size_t, void*));
  18.216 +void img_io_set_write_func(struct img_io *io, size_t (*write)(void*, size_t, void*));
  18.217 +void img_io_set_seek_func(struct img_io *io, long (*seek)(long, int, void*));
  18.218 +
  18.219 +
  18.220 +#ifdef __cplusplus
  18.221 +}
  18.222 +#endif
  18.223 +
  18.224 +
  18.225 +#endif	/* IMAGO_H_ */
    19.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    19.2 +++ b/prototype/imago2/imago_gl.c	Sun Oct 07 02:04:00 2012 +0300
    19.3 @@ -0,0 +1,223 @@
    19.4 +#include "imago2.h"
    19.5 +
    19.6 +
    19.7 +/* to avoid dependency to OpenGL, I'll define all the relevant GL macros manually */
    19.8 +#define GL_UNSIGNED_BYTE		0x1401
    19.9 +#define GL_FLOAT				0x1406
   19.10 +
   19.11 +#define GL_LUMINANCE			0x1909
   19.12 +#define GL_RGB					0x1907
   19.13 +#define GL_RGBA					0x1908
   19.14 +
   19.15 +#define GL_RGBA32F				0x8814
   19.16 +#define GL_RGB32F				0x8815
   19.17 +#define GL_LUMINANCE32F			0x8818
   19.18 +
   19.19 +#define GL_TEXTURE_2D			0x0de1
   19.20 +#define GL_TEXTURE_WRAP_S		0x2802
   19.21 +#define GL_TEXTURE_WRAP_T		0x2803
   19.22 +#define GL_TEXTURE_MAG_FILTER	0x2800
   19.23 +#define GL_TEXTURE_MIN_FILTER	0x2801
   19.24 +#define GL_LINEAR				0x2601
   19.25 +#define GL_REPEAT				0x2901
   19.26 +
   19.27 +
   19.28 +typedef unsigned int GLenum;
   19.29 +typedef unsigned int GLuint;
   19.30 +typedef int GLint;
   19.31 +typedef int GLsizei;
   19.32 +typedef void GLvoid;
   19.33 +
   19.34 +/* for the same reason I'll load GL functions dynamically */
   19.35 +typedef void (*gl_gen_textures_func)(GLsizei, GLuint*);
   19.36 +typedef void (*gl_bind_texture_func)(GLenum, GLuint);
   19.37 +typedef void (*gl_tex_parameteri_func)(GLenum, GLenum, GLint);
   19.38 +typedef void (*gl_tex_image2d_func)(GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid*);
   19.39 +
   19.40 +static gl_gen_textures_func gl_gen_textures;
   19.41 +static gl_bind_texture_func gl_bind_texture;
   19.42 +static gl_tex_parameteri_func gl_tex_parameteri;
   19.43 +static gl_tex_image2d_func gl_tex_image2d;
   19.44 +
   19.45 +static int load_glfunc(void);
   19.46 +
   19.47 +unsigned int img_fmt_glfmt(enum img_fmt fmt)
   19.48 +{
   19.49 +	switch(fmt) {
   19.50 +	case IMG_FMT_GREY8:
   19.51 +	case IMG_FMT_GREYF:
   19.52 +		return GL_LUMINANCE;
   19.53 +
   19.54 +	case IMG_FMT_RGB24:
   19.55 +	case IMG_FMT_RGBF:
   19.56 +		return GL_RGB;
   19.57 +
   19.58 +	case IMG_FMT_RGBA32:
   19.59 +	case IMG_FMT_RGBAF:
   19.60 +		return GL_RGBA;
   19.61 +
   19.62 +	default:
   19.63 +		break;
   19.64 +	}
   19.65 +	return 0;
   19.66 +}
   19.67 +
   19.68 +unsigned int img_fmt_gltype(enum img_fmt fmt)
   19.69 +{
   19.70 +	switch(fmt) {
   19.71 +	case IMG_FMT_GREY8:
   19.72 +	case IMG_FMT_RGB24:
   19.73 +	case IMG_FMT_RGBA32:
   19.74 +		return GL_UNSIGNED_BYTE;
   19.75 +
   19.76 +	case IMG_FMT_GREYF:
   19.77 +	case IMG_FMT_RGBF:
   19.78 +	case IMG_FMT_RGBAF:
   19.79 +		return GL_FLOAT;
   19.80 +
   19.81 +	default:
   19.82 +		break;
   19.83 +	}
   19.84 +	return 0;
   19.85 +}
   19.86 +
   19.87 +unsigned int img_fmt_glintfmt(enum img_fmt fmt)
   19.88 +{
   19.89 +	switch(fmt) {
   19.90 +	case IMG_FMT_GREY8:
   19.91 +		return GL_LUMINANCE;
   19.92 +	case IMG_FMT_RGB24:
   19.93 +		return GL_RGB;
   19.94 +	case IMG_FMT_RGBA32:
   19.95 +		return GL_RGBA;
   19.96 +	case IMG_FMT_GREYF:
   19.97 +		return GL_LUMINANCE32F;
   19.98 +	case IMG_FMT_RGBF:
   19.99 +		return GL_RGB32F;
  19.100 +	case IMG_FMT_RGBAF:
  19.101 +		return GL_RGBA32F;
  19.102 +	default:
  19.103 +		break;
  19.104 +	}
  19.105 +	return 0;
  19.106 +}
  19.107 +
  19.108 +unsigned int img_glfmt(struct img_pixmap *img)
  19.109 +{
  19.110 +	return img_fmt_glfmt(img->fmt);
  19.111 +}
  19.112 +
  19.113 +unsigned int img_gltype(struct img_pixmap *img)
  19.114 +{
  19.115 +	return img_fmt_gltype(img->fmt);
  19.116 +}
  19.117 +
  19.118 +unsigned int img_glintfmt(struct img_pixmap *img)
  19.119 +{
  19.120 +	return img_fmt_glintfmt(img->fmt);
  19.121 +}
  19.122 +
  19.123 +unsigned int img_gltexture(struct img_pixmap *img)
  19.124 +{
  19.125 +	unsigned int tex;
  19.126 +	unsigned int intfmt, fmt, type;
  19.127 +
  19.128 +	if(!gl_gen_textures) {
  19.129 +		if(load_glfunc() == -1) {
  19.130 +			fprintf(stderr, "imago: failed to initialize the OpenGL helpers\n");
  19.131 +			return 0;
  19.132 +		}
  19.133 +	}
  19.134 +
  19.135 +	intfmt = img_glintfmt(img);
  19.136 +	fmt = img_glfmt(img);
  19.137 +	type = img_gltype(img);
  19.138 +
  19.139 +	gl_gen_textures(1, &tex);
  19.140 +	gl_bind_texture(GL_TEXTURE_2D, tex);
  19.141 +	gl_tex_parameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  19.142 +	gl_tex_parameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  19.143 +	gl_tex_parameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  19.144 +	gl_tex_parameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  19.145 +	gl_tex_image2d(GL_TEXTURE_2D, 0, intfmt, img->width, img->height, 0, fmt, type, img->pixels);
  19.146 +	return tex;
  19.147 +}
  19.148 +
  19.149 +unsigned int img_gltexture_load(const char *fname)
  19.150 +{
  19.151 +	struct img_pixmap img;
  19.152 +	unsigned int tex;
  19.153 +
  19.154 +	img_init(&img);
  19.155 +	if(img_load(&img, fname) == -1) {
  19.156 +		img_destroy(&img);
  19.157 +		return 0;
  19.158 +	}
  19.159 +
  19.160 +	tex = img_gltexture(&img);
  19.161 +	img_destroy(&img);
  19.162 +	return tex;
  19.163 +}
  19.164 +
  19.165 +unsigned int img_gltexture_read_file(FILE *fp)
  19.166 +{
  19.167 +	struct img_pixmap img;
  19.168 +	unsigned int tex;
  19.169 +
  19.170 +	img_init(&img);
  19.171 +	if(img_read_file(&img, fp) == -1) {
  19.172 +		img_destroy(&img);
  19.173 +		return 0;
  19.174 +	}
  19.175 +
  19.176 +	tex = img_gltexture(&img);
  19.177 +	img_destroy(&img);
  19.178 +	return tex;
  19.179 +}
  19.180 +
  19.181 +unsigned int img_gltexture_read(struct img_io *io)
  19.182 +{
  19.183 +	struct img_pixmap img;
  19.184 +	unsigned int tex;
  19.185 +
  19.186 +	img_init(&img);
  19.187 +	if(img_read(&img, io) == -1) {
  19.188 +		img_destroy(&img);
  19.189 +		return 0;
  19.190 +	}
  19.191 +
  19.192 +	tex = img_gltexture(&img);
  19.193 +	img_destroy(&img);
  19.194 +	return tex;
  19.195 +}
  19.196 +
  19.197 +#if defined(__unix__) || defined(__APPLE__)
  19.198 +#ifndef __USE_GNU
  19.199 +#define __USE_GNU
  19.200 +#endif
  19.201 +
  19.202 +#include <dlfcn.h>
  19.203 +#endif
  19.204 +#ifdef WIN32
  19.205 +#include <windows.h>
  19.206 +#endif
  19.207 +
  19.208 +static int load_glfunc(void)
  19.209 +{
  19.210 +#if defined(__unix__) || defined(__APPLE__)
  19.211 +	gl_gen_textures = (gl_gen_textures_func)dlsym(RTLD_DEFAULT, "glGenTextures");
  19.212 +	gl_bind_texture = (gl_bind_texture_func)dlsym(RTLD_DEFAULT, "glBindTexture");
  19.213 +	gl_tex_parameteri = (gl_tex_parameteri_func)dlsym(RTLD_DEFAULT, "glTexParameteri");
  19.214 +	gl_tex_image2d = (gl_tex_image2d_func)dlsym(RTLD_DEFAULT, "glTexImage2D");
  19.215 +#endif
  19.216 +
  19.217 +#ifdef WIN32
  19.218 +	HMODULE handle = GetModuleHandle(0);
  19.219 +	gl_gen_textures = (gl_gen_textures_func)GetProcAddress(handle, "glGenTextures");
  19.220 +	gl_bind_texture = (gl_bind_texture_func)GetProcAddress(handle, "glBindTexture");
  19.221 +	gl_tex_parameteri = (gl_tex_parameteri_func)GetProcAddress(handle, "glTexParameteri");
  19.222 +	gl_tex_image2d = (gl_tex_image2d_func)GetProcAddress(handle, "glTexImage2D");
  19.223 +#endif
  19.224 +
  19.225 +	return (gl_gen_textures && gl_bind_texture && gl_tex_parameteri && gl_tex_image2d) ? 0 : -1;
  19.226 +}
    20.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    20.2 +++ b/prototype/imago2/modules.c	Sun Oct 07 02:04:00 2012 +0300
    20.3 @@ -0,0 +1,13 @@
    20.4 +/* this file is generated by ./configure, do not edit */
    20.5 +int img_register_jpeg();
    20.6 +int img_register_png();
    20.7 +int img_register_ppm();
    20.8 +int img_register_rgbe();
    20.9 +
   20.10 +void img_modules_init(void)
   20.11 +{
   20.12 +	img_register_jpeg();
   20.13 +	img_register_png();
   20.14 +	img_register_ppm();
   20.15 +	img_register_rgbe();
   20.16 +}
    21.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    21.2 +++ b/prototype/psys/pattr.c	Sun Oct 07 02:04:00 2012 +0300
    21.3 @@ -0,0 +1,371 @@
    21.4 +#define _GNU_SOURCE
    21.5 +#include <stdio.h>
    21.6 +#include <stdlib.h>
    21.7 +#include <string.h>
    21.8 +#include <errno.h>
    21.9 +#include <ctype.h>
   21.10 +
   21.11 +#ifdef _MSC_VER
   21.12 +#include <malloc.h>
   21.13 +#else
   21.14 +#include <alloca.h>
   21.15 +#endif
   21.16 +
   21.17 +#include "pattr.h"
   21.18 +#include "psys_gl.h"
   21.19 +
   21.20 +enum {
   21.21 +	OPT_STR,
   21.22 +	OPT_NUM,
   21.23 +	OPT_NUM_RANGE,
   21.24 +	OPT_VEC,
   21.25 +	OPT_VEC_RANGE
   21.26 +};
   21.27 +
   21.28 +struct cfgopt {
   21.29 +	char *name;
   21.30 +	int type;
   21.31 +	long tm;
   21.32 +	char *valstr;
   21.33 +	vec3_t val, valrng;
   21.34 +};
   21.35 +
   21.36 +static int init_particle_attr(struct psys_particle_attributes *pattr);
   21.37 +static void destroy_particle_attr(struct psys_particle_attributes *pattr);
   21.38 +static struct cfgopt *get_cfg_opt(const char *line);
   21.39 +static void release_cfg_opt(struct cfgopt *opt);
   21.40 +static char *stripspace(char *str);
   21.41 +
   21.42 +static void *tex_cls;
   21.43 +static unsigned int (*load_texture)(const char*, void*) = psys_gl_load_texture;
   21.44 +static void (*unload_texture)(unsigned int, void*) = psys_gl_unload_texture;
   21.45 +
   21.46 +
   21.47 +void psys_texture_loader(unsigned int (*load)(const char*, void*), void (*unload)(unsigned int, void*), void *cls)
   21.48 +{
   21.49 +	load_texture = load;
   21.50 +	unload_texture = unload;
   21.51 +	tex_cls = cls;
   21.52 +}
   21.53 +
   21.54 +struct psys_attributes *psys_create_attr(void)
   21.55 +{
   21.56 +	struct psys_attributes *attr = malloc(sizeof *attr);
   21.57 +	if(attr) {
   21.58 +		if(psys_init_attr(attr) == -1) {
   21.59 +			free(attr);
   21.60 +			attr = 0;
   21.61 +		}
   21.62 +	}
   21.63 +	return attr;
   21.64 +}
   21.65 +
   21.66 +void psys_free_attr(struct psys_attributes *attr)
   21.67 +{
   21.68 +	psys_destroy_attr(attr);
   21.69 +	free(attr);
   21.70 +}
   21.71 +
   21.72 +int psys_init_attr(struct psys_attributes *attr)
   21.73 +{
   21.74 +	memset(attr, 0, sizeof *attr);
   21.75 +
   21.76 +	if(psys_init_track3(&attr->spawn_range) == -1)
   21.77 +		goto err;
   21.78 +	if(psys_init_track(&attr->rate) == -1)
   21.79 +		goto err;
   21.80 +	if(psys_init_anm_rnd(&attr->life) == -1)
   21.81 +		goto err;
   21.82 +	if(psys_init_anm_rnd(&attr->size) == -1)
   21.83 +		goto err;
   21.84 +	if(psys_init_anm_rnd3(&attr->dir) == -1)
   21.85 +		goto err;
   21.86 +	if(psys_init_track3(&attr->grav) == -1)
   21.87 +		goto err;
   21.88 +
   21.89 +	if(init_particle_attr(&attr->part_attr) == -1)
   21.90 +		goto err;
   21.91 +
   21.92 +	attr->max_particles = -1;
   21.93 +
   21.94 +	anm_set_track_default(&attr->size.value.trk, 1.0);
   21.95 +	anm_set_track_default(&attr->life.value.trk, 1.0);
   21.96 +
   21.97 +	return 0;
   21.98 +
   21.99 +err:
  21.100 +	psys_destroy_attr(attr);
  21.101 +	return -1;
  21.102 +}
  21.103 +
  21.104 +
  21.105 +static int init_particle_attr(struct psys_particle_attributes *pattr)
  21.106 +{
  21.107 +	if(psys_init_track3(&pattr->color) == -1) {
  21.108 +		return -1;
  21.109 +	}
  21.110 +	if(psys_init_track(&pattr->alpha) == -1) {
  21.111 +		psys_destroy_track3(&pattr->color);
  21.112 +		return -1;
  21.113 +	}
  21.114 +	if(psys_init_track(&pattr->size) == -1) {
  21.115 +		psys_destroy_track3(&pattr->color);
  21.116 +		psys_destroy_track(&pattr->alpha);
  21.117 +		return -1;
  21.118 +	}
  21.119 +
  21.120 +	anm_set_track_default(&pattr->color.x, 1.0);
  21.121 +	anm_set_track_default(&pattr->color.y, 1.0);
  21.122 +	anm_set_track_default(&pattr->color.z, 1.0);
  21.123 +	anm_set_track_default(&pattr->alpha.trk, 1.0);
  21.124 +	anm_set_track_default(&pattr->size.trk, 1.0);
  21.125 +	return 0;
  21.126 +}
  21.127 +
  21.128 +
  21.129 +void psys_destroy_attr(struct psys_attributes *attr)
  21.130 +{
  21.131 +	psys_destroy_track3(&attr->spawn_range);
  21.132 +	psys_destroy_track(&attr->rate);
  21.133 +	psys_destroy_anm_rnd(&attr->life);
  21.134 +	psys_destroy_anm_rnd(&attr->size);
  21.135 +	psys_destroy_anm_rnd3(&attr->dir);
  21.136 +	psys_destroy_track3(&attr->grav);
  21.137 +
  21.138 +	destroy_particle_attr(&attr->part_attr);
  21.139 +
  21.140 +	if(attr->tex && unload_texture) {
  21.141 +		unload_texture(attr->tex, tex_cls);
  21.142 +	}
  21.143 +}
  21.144 +
  21.145 +static void destroy_particle_attr(struct psys_particle_attributes *pattr)
  21.146 +{
  21.147 +	psys_destroy_track3(&pattr->color);
  21.148 +	psys_destroy_track(&pattr->alpha);
  21.149 +	psys_destroy_track(&pattr->size);
  21.150 +}
  21.151 +
  21.152 +void psys_eval_attr(struct psys_attributes *attr, anm_time_t tm)
  21.153 +{
  21.154 +	psys_eval_track3(&attr->spawn_range, tm);
  21.155 +	psys_eval_track(&attr->rate, tm);
  21.156 +	psys_eval_anm_rnd(&attr->life, tm);
  21.157 +	psys_eval_anm_rnd(&attr->size, tm);
  21.158 +	psys_eval_anm_rnd3(&attr->dir, tm);
  21.159 +	psys_eval_track3(&attr->grav, tm);
  21.160 +}
  21.161 +
  21.162 +int psys_load_attr(struct psys_attributes *attr, const char *fname)
  21.163 +{
  21.164 +	FILE *fp;
  21.165 +	int res;
  21.166 +
  21.167 +	if(!fname) {
  21.168 +		return -1;
  21.169 +	}
  21.170 +
  21.171 +	if(!(fp = fopen(fname, "r"))) {
  21.172 +		fprintf(stderr, "%s: failed to read file: %s: %s\n", __func__, fname, strerror(errno));
  21.173 +		return -1;
  21.174 +	}
  21.175 +	res = psys_load_attr_stream(attr, fp);
  21.176 +	fclose(fp);
  21.177 +	return res;
  21.178 +}
  21.179 +
  21.180 +int psys_load_attr_stream(struct psys_attributes *attr, FILE *fp)
  21.181 +{
  21.182 +	int lineno = 0;
  21.183 +	char buf[512];
  21.184 +	struct cfgopt *opt = 0;
  21.185 +
  21.186 +	psys_init_attr(attr);
  21.187 +
  21.188 +	while(fgets(buf, sizeof buf, fp)) {
  21.189 +
  21.190 +		lineno++;
  21.191 +
  21.192 +		if(!(opt = get_cfg_opt(buf))) {
  21.193 +			continue;
  21.194 +		}
  21.195 +
  21.196 +		if(strcmp(opt->name, "texture") == 0) {
  21.197 +			if(opt->type != OPT_STR) {
  21.198 +				goto err;
  21.199 +			}
  21.200 +			if(!(attr->tex = load_texture(opt->valstr, tex_cls))) {
  21.201 +				fprintf(stderr, "failed to load texture: %s\n", opt->valstr);
  21.202 +				goto err;
  21.203 +			}
  21.204 +
  21.205 +			release_cfg_opt(opt);
  21.206 +			continue;
  21.207 +		} else if(opt->type == OPT_STR) {
  21.208 +			fprintf(stderr, "invalid particle config: '%s'\n", opt->name);
  21.209 +			goto err;
  21.210 +		}
  21.211 +
  21.212 +		if(strcmp(opt->name, "spawn_range") == 0) {
  21.213 +			psys_set_value3(&attr->spawn_range, opt->tm, opt->val);
  21.214 +		} else if(strcmp(opt->name, "rate") == 0) {
  21.215 +			psys_set_value(&attr->rate, opt->tm, opt->val.x);
  21.216 +		} else if(strcmp(opt->name, "life") == 0) {
  21.217 +			psys_set_anm_rnd(&attr->life, opt->tm, opt->val.x, opt->valrng.x);
  21.218 +		} else if(strcmp(opt->name, "size") == 0) {
  21.219 +			psys_set_anm_rnd(&attr->size, opt->tm, opt->val.x, opt->valrng.x);
  21.220 +		} else if(strcmp(opt->name, "dir") == 0) {
  21.221 +			psys_set_anm_rnd3(&attr->dir, opt->tm, opt->val, opt->valrng);
  21.222 +		} else if(strcmp(opt->name, "grav") == 0) {
  21.223 +			psys_set_value3(&attr->grav, opt->tm, opt->val);
  21.224 +		} else if(strcmp(opt->name, "drag") == 0) {
  21.225 +			attr->drag = opt->val.x;
  21.226 +		} else if(strcmp(opt->name, "pcolor") == 0) {
  21.227 +			psys_set_value3(&attr->part_attr.color, opt->tm, opt->val);
  21.228 +		} else if(strcmp(opt->name, "palpha") == 0) {
  21.229 +			psys_set_value(&attr->part_attr.alpha, opt->tm, opt->val.x);
  21.230 +		} else if(strcmp(opt->name, "psize") == 0) {
  21.231 +			psys_set_value(&attr->part_attr.size, opt->tm, opt->val.x);
  21.232 +		} else {
  21.233 +			fprintf(stderr, "unrecognized particle config option: %s\n", opt->name);
  21.234 +			goto err;
  21.235 +		}
  21.236 +
  21.237 +		release_cfg_opt(opt);
  21.238 +	}
  21.239 +
  21.240 +	return 0;
  21.241 +
  21.242 +err:
  21.243 +	fprintf(stderr, "Line %d: error parsing particle definition\n", lineno);
  21.244 +	release_cfg_opt(opt);
  21.245 +	return -1;
  21.246 +}
  21.247 +
  21.248 +static struct cfgopt *get_cfg_opt(const char *line)
  21.249 +{
  21.250 +	char *buf, *tmp;
  21.251 +	struct cfgopt *opt;
  21.252 +
  21.253 +	line = stripspace((char*)line);
  21.254 +	if(line[0] == '#' || !line[0]) {
  21.255 +		return 0;	/* skip empty lines and comments */
  21.256 +	}
  21.257 +
  21.258 +	if(!(opt = malloc(sizeof *opt))) {
  21.259 +		return 0;
  21.260 +	}
  21.261 +
  21.262 +	if(!(opt->valstr = strchr(line, '='))) {
  21.263 +		release_cfg_opt(opt);
  21.264 +		return 0;
  21.265 +	}
  21.266 +	*opt->valstr++ = 0;
  21.267 +	opt->valstr = stripspace(opt->valstr);
  21.268 +
  21.269 +	/* allocate a working buffer on the stack that could fit the current line */
  21.270 +	buf = alloca(strlen(line) + 1);
  21.271 +	strcpy(buf, line);
  21.272 +	buf = stripspace(buf);
  21.273 +
  21.274 +	/* parse the keyframe time specifier if it exists */
  21.275 +	if((tmp = strchr(buf, '('))) {
  21.276 +		char *endp;
  21.277 +		float tval;
  21.278 +
  21.279 +		*tmp++ = 0;
  21.280 +		opt->name = strdup(buf);
  21.281 +
  21.282 +		tval = strtod(tmp, &endp);
  21.283 +		if(endp == tmp) { /* nada ... */
  21.284 +			opt->tm = 0;
  21.285 +		} else if(*endp == 's') {	/* seconds suffix */
  21.286 +			opt->tm = (long)(tval * 1000.0f);
  21.287 +		} else {
  21.288 +			opt->tm = (long)tval;
  21.289 +		}
  21.290 +	} else {
  21.291 +		opt->name = strdup(buf);
  21.292 +		opt->tm = 0;
  21.293 +	}
  21.294 +
  21.295 +	if(sscanf(opt->valstr, "[%f %f %f] ~ [%f %f %f]", &opt->val.x, &opt->val.y, &opt->val.z,
  21.296 +				&opt->valrng.x, &opt->valrng.y, &opt->valrng.z) == 6) {
  21.297 +		/* value is a vector range */
  21.298 +		opt->type = OPT_VEC_RANGE;
  21.299 +
  21.300 +	} else if(sscanf(opt->valstr, "%f ~ %f", &opt->val.x, &opt->valrng.x) == 2) {
  21.301 +		/* value is a number range */
  21.302 +		opt->type = OPT_NUM_RANGE;
  21.303 +		opt->val.y = opt->val.z = opt->val.x;
  21.304 +		opt->valrng.y = opt->valrng.z = opt->valrng.x;
  21.305 +
  21.306 +	} else if(sscanf(opt->valstr, "[%f %f %f]", &opt->val.x, &opt->val.y, &opt->val.z) == 3) {
  21.307 +		/* value is a vector */
  21.308 +		opt->type = OPT_VEC;
  21.309 +		opt->valrng.x = opt->valrng.y = opt->valrng.z = 0.0f;
  21.310 +
  21.311 +	} else if(sscanf(opt->valstr, "%f", &opt->val.x) == 1) {
  21.312 +		/* value is a number */
  21.313 +		opt->type = OPT_NUM;
  21.314 +		opt->val.y = opt->val.z = opt->val.x;
  21.315 +		opt->valrng.x = opt->valrng.y = opt->valrng.z = 0.0f;
  21.316 +
  21.317 +	} else if(sscanf(opt->valstr, "\"%s\"", buf) == 1) {
  21.318 +		/* just a string... strip the quotes */
  21.319 +		if(buf[strlen(buf) - 1] == '\"') {
  21.320 +			buf[strlen(buf) - 1] = 0;
  21.321 +		}
  21.322 +		opt->type = OPT_STR;
  21.323 +		opt->valstr = strdup(buf);
  21.324 +	} else {
  21.325 +		/* fuck it ... */
  21.326 +		release_cfg_opt(opt);
  21.327 +		return 0;
  21.328 +	}
  21.329 +
  21.330 +	return opt;
  21.331 +}
  21.332 +
  21.333 +static void release_cfg_opt(struct cfgopt *opt)
  21.334 +{
  21.335 +	if(opt) {
  21.336 +		free(opt->name);
  21.337 +	}
  21.338 +}
  21.339 +
  21.340 +
  21.341 +int psys_save_attr(struct psys_attributes *attr, const char *fname)
  21.342 +{
  21.343 +	FILE *fp;
  21.344 +	int res;
  21.345 +
  21.346 +	if(!(fp = fopen(fname, "w"))) {
  21.347 +		fprintf(stderr, "%s: failed to write file: %s: %s\n", __func__, fname, strerror(errno));
  21.348 +		return -1;
  21.349 +	}
  21.350 +	res = psys_save_attr_stream(attr, fp);
  21.351 +	fclose(fp);
  21.352 +	return res;
  21.353 +}
  21.354 +
  21.355 +int psys_save_attr_stream(struct psys_attributes *attr, FILE *fp)
  21.356 +{
  21.357 +	return -1;	/* TODO */
  21.358 +}
  21.359 +
  21.360 +
  21.361 +static char *stripspace(char *str)
  21.362 +{
  21.363 +	char *end;
  21.364 +
  21.365 +	while(*str && isspace(*str)) {
  21.366 +		str++;
  21.367 +	}
  21.368 +
  21.369 +	end = str + strlen(str) - 1;
  21.370 +	while(end >= str && isspace(*end)) {
  21.371 +		*end-- = 0;
  21.372 +	}
  21.373 +	return str;
  21.374 +}
    22.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    22.2 +++ b/prototype/psys/pattr.h	Sun Oct 07 02:04:00 2012 +0300
    22.3 @@ -0,0 +1,57 @@
    22.4 +#ifndef PATTR_H_
    22.5 +#define PATTR_H_
    22.6 +
    22.7 +#include "pstrack.h"
    22.8 +#include "rndval.h"
    22.9 +
   22.10 +/* the particle attributes vary from 0 to 1 during its lifetime */
   22.11 +struct psys_particle_attributes {
   22.12 +	struct psys_track3 color;
   22.13 +	struct psys_track alpha;
   22.14 +	struct psys_track size;
   22.15 +};
   22.16 +
   22.17 +struct psys_attributes {
   22.18 +	unsigned int tex;	/* OpenGL texture to use for the billboard */
   22.19 +
   22.20 +	struct psys_track3 spawn_range;	/* radius of emmiter */
   22.21 +	struct psys_track rate;			/* spawn rate particles per sec */
   22.22 +	struct psys_anm_rnd life;		/* particle life in seconds */
   22.23 +	struct psys_anm_rnd size;		/* base particle size */
   22.24 +	struct psys_anm_rnd3 dir;		/* particle shoot direction */
   22.25 +
   22.26 +	struct psys_track3 grav;		/* external force (usually gravity) */
   22.27 +	float drag;	/* I don't think this needs to animate */
   22.28 +
   22.29 +	/* particle attributes */
   22.30 +	struct psys_particle_attributes part_attr;
   22.31 +
   22.32 +	/* limits */
   22.33 +	int max_particles;
   22.34 +};
   22.35 +
   22.36 +#ifdef __cplusplus
   22.37 +extern "C" {
   22.38 +#endif
   22.39 +
   22.40 +void psys_texture_loader(unsigned int (*load)(const char*, void*), void (*unload)(unsigned int, void*), void *cls);
   22.41 +
   22.42 +struct psys_attributes *psys_create_attr(void);
   22.43 +void psys_free_attr(struct psys_attributes *attr);
   22.44 +
   22.45 +int psys_init_attr(struct psys_attributes *attr);
   22.46 +void psys_destroy_attr(struct psys_attributes *attr);
   22.47 +
   22.48 +void psys_eval_attr(struct psys_attributes *attr, anm_time_t tm);
   22.49 +
   22.50 +int psys_load_attr(struct psys_attributes *attr, const char *fname);
   22.51 +int psys_load_attr_stream(struct psys_attributes *attr, FILE *fp);
   22.52 +
   22.53 +int psys_save_attr(struct psys_attributes *attr, const char *fname);
   22.54 +int psys_save_attr_stream(struct psys_attributes *attr, FILE *fp);
   22.55 +
   22.56 +#ifdef __cplusplus
   22.57 +}
   22.58 +#endif
   22.59 +
   22.60 +#endif	/* PATTR_H_ */
    23.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    23.2 +++ b/prototype/psys/pstrack.c	Sun Oct 07 02:04:00 2012 +0300
    23.3 @@ -0,0 +1,97 @@
    23.4 +#include "pstrack.h"
    23.5 +
    23.6 +int psys_init_track(struct psys_track *track)
    23.7 +{
    23.8 +	track->cache_tm = ANM_TIME_INVAL;
    23.9 +
   23.10 +	if(anm_init_track(&track->trk) == -1) {
   23.11 +		return -1;
   23.12 +	}
   23.13 +	return 0;
   23.14 +}
   23.15 +
   23.16 +void psys_destroy_track(struct psys_track *track)
   23.17 +{
   23.18 +	anm_destroy_track(&track->trk);
   23.19 +}
   23.20 +
   23.21 +int psys_init_track3(struct psys_track3 *track)
   23.22 +{
   23.23 +	track->cache_tm = ANM_TIME_INVAL;
   23.24 +
   23.25 +	if(anm_init_track(&track->x) == -1) {
   23.26 +		return -1;
   23.27 +	}
   23.28 +	if(anm_init_track(&track->y) == -1) {
   23.29 +		anm_destroy_track(&track->x);
   23.30 +		return -1;
   23.31 +	}
   23.32 +	if(anm_init_track(&track->z) == -1) {
   23.33 +		anm_destroy_track(&track->x);
   23.34 +		anm_destroy_track(&track->z);
   23.35 +		return -1;
   23.36 +	}
   23.37 +	return 0;
   23.38 +}
   23.39 +
   23.40 +void psys_destroy_track3(struct psys_track3 *track)
   23.41 +{
   23.42 +	anm_destroy_track(&track->x);
   23.43 +	anm_destroy_track(&track->y);
   23.44 +	anm_destroy_track(&track->z);
   23.45 +}
   23.46 +
   23.47 +void psys_eval_track(struct psys_track *track, anm_time_t tm)
   23.48 +{
   23.49 +	if(track->cache_tm != tm) {
   23.50 +		track->cache_tm = tm;
   23.51 +		track->cache_val = anm_get_value(&track->trk, tm);
   23.52 +	}
   23.53 +}
   23.54 +
   23.55 +void psys_set_value(struct psys_track *track, anm_time_t tm, float v)
   23.56 +{
   23.57 +	anm_set_value(&track->trk, tm, v);
   23.58 +	track->cache_tm = ANM_TIME_INVAL;
   23.59 +}
   23.60 +
   23.61 +float psys_get_value(struct psys_track *track, anm_time_t tm)
   23.62 +{
   23.63 +	psys_eval_track(track, tm);
   23.64 +	return track->cache_val;
   23.65 +}
   23.66 +
   23.67 +float psys_get_cur_value(struct psys_track *track)
   23.68 +{
   23.69 +	return track->cache_val;
   23.70 +}
   23.71 +
   23.72 +
   23.73 +void psys_eval_track3(struct psys_track3 *track, anm_time_t tm)
   23.74 +{
   23.75 +	if(track->cache_tm != tm) {
   23.76 +		track->cache_tm = tm;
   23.77 +		track->cache_vec.x = anm_get_value(&track->x, tm);
   23.78 +		track->cache_vec.y = anm_get_value(&track->y, tm);
   23.79 +		track->cache_vec.z = anm_get_value(&track->z, tm);
   23.80 +	}
   23.81 +}
   23.82 +
   23.83 +void psys_set_value3(struct psys_track3 *track, anm_time_t tm, vec3_t v)
   23.84 +{
   23.85 +	anm_set_value(&track->x, tm, v.x);
   23.86 +	anm_set_value(&track->y, tm, v.y);
   23.87 +	anm_set_value(&track->z, tm, v.z);
   23.88 +	track->cache_tm = ANM_TIME_INVAL;
   23.89 +}
   23.90 +
   23.91 +vec3_t psys_get_value3(struct psys_track3 *track, anm_time_t tm)
   23.92 +{
   23.93 +	psys_eval_track3(track, tm);
   23.94 +	return track->cache_vec;
   23.95 +}
   23.96 +
   23.97 +vec3_t psys_get_cur_value3(struct psys_track3 *track)
   23.98 +{
   23.99 +	return track->cache_vec;
  23.100 +}
    24.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    24.2 +++ b/prototype/psys/pstrack.h	Sun Oct 07 02:04:00 2012 +0300
    24.3 @@ -0,0 +1,37 @@
    24.4 +#ifndef PSTRACK_H_
    24.5 +#define PSTRACK_H_
    24.6 +
    24.7 +#include <vmath/vmath.h>
    24.8 +#include <anim/anim.h>
    24.9 +
   24.10 +struct psys_track {
   24.11 +	struct anm_track trk;
   24.12 +
   24.13 +	anm_time_t cache_tm;
   24.14 +	float cache_val;
   24.15 +};
   24.16 +
   24.17 +struct psys_track3 {
   24.18 +	struct anm_track x, y, z;
   24.19 +
   24.20 +	anm_time_t cache_tm;
   24.21 +	vec3_t cache_vec;
   24.22 +};
   24.23 +
   24.24 +int psys_init_track(struct psys_track *track);
   24.25 +void psys_destroy_track(struct psys_track *track);
   24.26 +
   24.27 +int psys_init_track3(struct psys_track3 *track);
   24.28 +void psys_destroy_track3(struct psys_track3 *track);
   24.29 +
   24.30 +void psys_eval_track(struct psys_track *track, anm_time_t tm);
   24.31 +void psys_set_value(struct psys_track *track, anm_time_t tm, float v);
   24.32 +float psys_get_value(struct psys_track *track, anm_time_t tm);
   24.33 +float psys_get_cur_value(struct psys_track *track);
   24.34 +
   24.35 +void psys_eval_track3(struct psys_track3 *track, anm_time_t tm);
   24.36 +void psys_set_value3(struct psys_track3 *track, anm_time_t tm, vec3_t v);
   24.37 +vec3_t psys_get_value3(struct psys_track3 *track, anm_time_t tm);
   24.38 +vec3_t psys_get_cur_value3(struct psys_track3 *track);
   24.39 +
   24.40 +#endif	/* PSTRACK_H_ */
    25.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    25.2 +++ b/prototype/psys/psys.c	Sun Oct 07 02:04:00 2012 +0300
    25.3 @@ -0,0 +1,325 @@
    25.4 +#include <stdlib.h>
    25.5 +#include <math.h>
    25.6 +#include <assert.h>
    25.7 +#include <pthread.h>
    25.8 +#include "psys.h"
    25.9 +#include "psys_gl.h"
   25.10 +
   25.11 +static int spawn(struct psys_emitter *em, struct psys_particle *p, void *cls);
   25.12 +static void update_particle(struct psys_emitter *em, struct psys_particle *p, float tm, float dt, void *cls);
   25.13 +
   25.14 +/* particle pool */
   25.15 +static struct psys_particle *ppool;
   25.16 +static int ppool_size;
   25.17 +static pthread_mutex_t pool_lock = PTHREAD_MUTEX_INITIALIZER;
   25.18 +
   25.19 +static struct psys_particle *palloc(void);
   25.20 +static void pfree(struct psys_particle *p);
   25.21 +
   25.22 +/* --- constructors and shit --- */
   25.23 +
   25.24 +struct psys_emitter *psys_create(void)
   25.25 +{
   25.26 +	struct psys_emitter *em;
   25.27 +
   25.28 +	if(!(em = malloc(sizeof *em))) {
   25.29 +		return 0;
   25.30 +	}
   25.31 +	if(psys_init(em) == -1) {
   25.32 +		free(em);
   25.33 +		return 0;
   25.34 +	}
   25.35 +	return em;
   25.36 +}
   25.37 +
   25.38 +void psys_free(struct psys_emitter *em)
   25.39 +{
   25.40 +	psys_destroy(em);
   25.41 +	free(em);
   25.42 +}
   25.43 +
   25.44 +int psys_init(struct psys_emitter *em)
   25.45 +{
   25.46 +	memset(em, 0, sizeof *em);
   25.47 +
   25.48 +	if(anm_init_node(&em->prs) == -1) {
   25.49 +		return -1;
   25.50 +	}
   25.51 +	if(psys_init_attr(&em->attr) == -1) {
   25.52 +		anm_destroy_node(&em->prs);
   25.53 +		return -1;
   25.54 +	}
   25.55 +
   25.56 +	em->spawn = spawn;
   25.57 +	em->update = update_particle;
   25.58 +
   25.59 +	em->draw = psys_gl_draw;
   25.60 +	em->draw_start = psys_gl_draw_start;
   25.61 +	em->draw_end = psys_gl_draw_end;
   25.62 +	return 0;
   25.63 +}
   25.64 +
   25.65 +void psys_destroy(struct psys_emitter *em)
   25.66 +{
   25.67 +	struct psys_particle *part;
   25.68 +
   25.69 +	part = em->plist;
   25.70 +	while(part) {
   25.71 +		struct psys_particle *tmp = part;
   25.72 +		part = part->next;
   25.73 +		pfree(tmp);
   25.74 +	}
   25.75 +
   25.76 +	psys_destroy_attr(&em->attr);
   25.77 +}
   25.78 +
   25.79 +void psys_set_pos(struct psys_emitter *em, vec3_t pos, float tm)
   25.80 +{
   25.81 +	anm_set_position(&em->prs, pos, ANM_SEC2TM(tm));
   25.82 +}
   25.83 +
   25.84 +void psys_set_rot(struct psys_emitter *em, quat_t rot, float tm)
   25.85 +{
   25.86 +	anm_set_rotation(&em->prs, rot, ANM_SEC2TM(tm));
   25.87 +}
   25.88 +
   25.89 +void psys_set_pivot(struct psys_emitter *em, vec3_t pivot)
   25.90 +{
   25.91 +	anm_set_pivot(&em->prs, pivot);
   25.92 +}
   25.93 +
   25.94 +vec3_t psys_get_pos(struct psys_emitter *em, float tm)
   25.95 +{
   25.96 +	return anm_get_node_position(&em->prs, ANM_SEC2TM(tm));
   25.97 +}
   25.98 +
   25.99 +quat_t psys_get_rot(struct psys_emitter *em, float tm)
  25.100 +{
  25.101 +	return anm_get_node_rotation(&em->prs, ANM_SEC2TM(tm));
  25.102 +}
  25.103 +
  25.104 +vec3_t psys_get_pivot(struct psys_emitter *em)
  25.105 +{
  25.106 +	return anm_get_pivot(&em->prs);
  25.107 +}
  25.108 +
  25.109 +void psys_clear_collision_planes(struct psys_emitter *em)
  25.110 +{
  25.111 +	struct psys_plane *plane;
  25.112 +
  25.113 +	plane = em->planes;
  25.114 +	while(plane) {
  25.115 +		struct psys_plane *tmp = plane;
  25.116 +		plane = plane->next;
  25.117 +		free(tmp);
  25.118 +	}
  25.119 +}
  25.120 +
  25.121 +int psys_add_collision_plane(struct psys_emitter *em, plane_t plane, float elast)
  25.122 +{
  25.123 +	struct psys_plane *node;
  25.124 +
  25.125 +	if(!(node = malloc(sizeof *node))) {
  25.126 +		return -1;
  25.127 +	}
  25.128 +	node->p = plane;
  25.129 +	node->elasticity = elast;
  25.130 +	node->next = em->planes;
  25.131 +	em->planes = node;
  25.132 +	return 0;
  25.133 +}
  25.134 +
  25.135 +void psys_add_particle(struct psys_emitter *em, struct psys_particle *p)
  25.136 +{
  25.137 +	p->next = em->plist;
  25.138 +	em->plist = p;
  25.139 +
  25.140 +	em->pcount++;
  25.141 +}
  25.142 +
  25.143 +void psys_spawn_func(struct psys_emitter *em, psys_spawn_func_t func, void *cls)
  25.144 +{
  25.145 +	em->spawn = func;
  25.146 +	em->spawn_cls = cls;
  25.147 +}
  25.148 +
  25.149 +void psys_update_func(struct psys_emitter *em, psys_update_func_t func, void *cls)
  25.150 +{
  25.151 +	em->update = func;
  25.152 +	em->upd_cls = cls;
  25.153 +}
  25.154 +
  25.155 +void psys_draw_func(struct psys_emitter *em, psys_draw_func_t draw,
  25.156 +		psys_draw_start_func_t start, psys_draw_end_func_t end, void *cls)
  25.157 +{
  25.158 +	em->draw = draw;
  25.159 +	em->draw_start = start;
  25.160 +	em->draw_end = end;
  25.161 +	em->draw_cls = cls;
  25.162 +}
  25.163 +
  25.164 +/* --- update and render --- */
  25.165 +
  25.166 +void psys_update(struct psys_emitter *em, float tm)
  25.167 +{
  25.168 +	float dt, spawn_dt, spawn_tm;
  25.169 +	int i, spawn_count;
  25.170 +	struct psys_particle *p, pdummy;
  25.171 +	anm_time_t atm = ANM_SEC2TM(tm);
  25.172 +
  25.173 +	assert(em->spawn && em->update);
  25.174 +
  25.175 +	dt = tm - em->last_update;
  25.176 +	if(dt <= 0.0) {
  25.177 +		return;
  25.178 +	}
  25.179 +
  25.180 +	psys_eval_attr(&em->attr, atm);
  25.181 +
  25.182 +	/* how many particles to spawn for this interval ? */
  25.183 +	em->spawn_acc += psys_get_cur_value(&em->attr.rate) * dt;
  25.184 +	if(em->spawn_acc >= 1.0) {
  25.185 +		spawn_count = em->spawn_acc;
  25.186 +		em->spawn_acc = fmod(em->spawn_acc, 1.0);
  25.187 +	} else {
  25.188 +		spawn_count = 0;
  25.189 +	}
  25.190 +
  25.191 +	spawn_dt = dt / (float)spawn_count;
  25.192 +	spawn_tm = em->last_update;
  25.193 +	for(i=0; i<spawn_count; i++) {
  25.194 +		if(em->attr.max_particles >= 0 && em->pcount >= em->attr.max_particles) {
  25.195 +			break;
  25.196 +		}
  25.197 +
  25.198 +		/* update emitter position for this spawning */
  25.199 +		em->cur_pos = anm_get_position(&em->prs, ANM_SEC2TM(spawn_tm));
  25.200 +
  25.201 +		if(!(p = palloc())) {
  25.202 +			return;
  25.203 +		}
  25.204 +		if(em->spawn(em, p, em->spawn_cls) == -1) {
  25.205 +			pfree(p);
  25.206 +		}
  25.207 +		spawn_tm += spawn_dt;
  25.208 +	}
  25.209 +
  25.210 +	/* update all particles */
  25.211 +	p = em->plist;
  25.212 +	while(p) {
  25.213 +		em->update(em, p, tm, dt, em->upd_cls);
  25.214 +		p = p->next;
  25.215 +	}
  25.216 +
  25.217 +	/* cleanup dead particles */
  25.218 +	pdummy.next = em->plist;
  25.219 +	p = &pdummy;
  25.220 +	while(p->next) {
  25.221 +		if(p->next->life <= 0) {
  25.222 +			struct psys_particle *tmp = p->next;
  25.223 +			p->next = p->next->next;
  25.224 +			pfree(tmp);
  25.225 +			em->pcount--;
  25.226 +		} else {
  25.227 +			p = p->next;
  25.228 +		}
  25.229 +	}
  25.230 +	em->plist = pdummy.next;
  25.231 +
  25.232 +	em->last_update = tm;
  25.233 +
  25.234 +	/*printf("particles: %5d\r", em->pcount);*/
  25.235 +}
  25.236 +
  25.237 +void psys_draw(struct psys_emitter *em)
  25.238 +{
  25.239 +	struct psys_particle *p;
  25.240 +
  25.241 +	if(em->draw_start) {
  25.242 +		em->draw_start(em, em->draw_cls);
  25.243 +	}
  25.244 +
  25.245 +	p = em->plist;
  25.246 +	while(p) {
  25.247 +		em->draw(em, p, em->draw_cls);
  25.248 +		p = p->next;
  25.249 +	}
  25.250 +
  25.251 +	if(em->draw_end) {
  25.252 +		em->draw_end(em, em->draw_cls);
  25.253 +	}
  25.254 +}
  25.255 +
  25.256 +static int spawn(struct psys_emitter *em, struct psys_particle *p, void *cls)
  25.257 +{
  25.258 +	struct psys_rnd3 rpos;
  25.259 +	rpos.value = em->cur_pos;
  25.260 +	rpos.range = psys_get_cur_value3(&em->attr.spawn_range);
  25.261 +
  25.262 +	p->pos = psys_eval_rnd3(&rpos);
  25.263 +	p->vel = psys_eval_anm_rnd3(&em->attr.dir, PSYS_EVAL_CUR);
  25.264 +	p->base_size = psys_eval_anm_rnd(&em->attr.size, PSYS_EVAL_CUR);
  25.265 +	p->max_life = p->life = psys_eval_anm_rnd(&em->attr.life, PSYS_EVAL_CUR);
  25.266 +
  25.267 +	p->pattr = &em->attr.part_attr;
  25.268 +
  25.269 +	psys_add_particle(em, p);
  25.270 +	return 0;
  25.271 +}
  25.272 +
  25.273 +static void update_particle(struct psys_emitter *em, struct psys_particle *p, float tm, float dt, void *cls)
  25.274 +{
  25.275 +	vec3_t accel, grav;
  25.276 +	anm_time_t t;
  25.277 +
  25.278 +	grav = psys_get_cur_value3(&em->attr.grav);
  25.279 +
  25.280 +	accel.x = grav.x - p->vel.x * em->attr.drag;
  25.281 +	accel.y = grav.y - p->vel.y * em->attr.drag;
  25.282 +	accel.z = grav.z - p->vel.z * em->attr.drag;
  25.283 +
  25.284 +	p->vel.x += accel.x * dt;
  25.285 +	p->vel.y += accel.y * dt;
  25.286 +	p->vel.z += accel.z * dt;
  25.287 +
  25.288 +	p->pos.x += p->vel.x * dt;
  25.289 +	p->pos.y += p->vel.y * dt;
  25.290 +	p->pos.z += p->vel.z * dt;
  25.291 +
  25.292 +	/* update particle attributes */
  25.293 +	t = (anm_time_t)(1000.0 * (p->max_life - p->life) / p->max_life);
  25.294 +
  25.295 +	p->color = psys_get_value3(&p->pattr->color, t);
  25.296 +	p->alpha = psys_get_value(&p->pattr->alpha, t);
  25.297 +	p->size = p->base_size * psys_get_value(&p->pattr->size, t);
  25.298 +
  25.299 +	p->life -= dt;
  25.300 +}
  25.301 +
  25.302 +/* --- particle allocation pool --- */
  25.303 +
  25.304 +static struct psys_particle *palloc(void)
  25.305 +{
  25.306 +	struct psys_particle *p;
  25.307 +
  25.308 +	pthread_mutex_lock(&pool_lock);
  25.309 +	if(ppool) {
  25.310 +		p = ppool;
  25.311 +		ppool = ppool->next;
  25.312 +		ppool_size--;
  25.313 +	} else {
  25.314 +		p = malloc(sizeof *p);
  25.315 +	}
  25.316 +	pthread_mutex_unlock(&pool_lock);
  25.317 +
  25.318 +	return p;
  25.319 +}
  25.320 +
  25.321 +static void pfree(struct psys_particle *p)
  25.322 +{
  25.323 +	pthread_mutex_lock(&pool_lock);
  25.324 +	p->next = ppool;
  25.325 +	ppool = p;
  25.326 +	ppool_size++;
  25.327 +	pthread_mutex_unlock(&pool_lock);
  25.328 +}
    26.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    26.2 +++ b/prototype/psys/psys.h	Sun Oct 07 02:04:00 2012 +0300
    26.3 @@ -0,0 +1,111 @@
    26.4 +#ifndef LIBPSYS_H_
    26.5 +#define LIBPSYS_H_
    26.6 +
    26.7 +#include <vmath/vmath.h>
    26.8 +#include <anim/anim.h>
    26.9 +#include "rndval.h"
   26.10 +#include "pattr.h"
   26.11 +
   26.12 +struct psys_particle;
   26.13 +struct psys_emitter;
   26.14 +
   26.15 +typedef int (*psys_spawn_func_t)(struct psys_emitter*, struct psys_particle*, void*);
   26.16 +typedef void (*psys_update_func_t)(struct psys_emitter*, struct psys_particle*, float, float, void*);
   26.17 +
   26.18 +typedef void (*psys_draw_func_t)(struct psys_emitter*, struct psys_particle*, void*);
   26.19 +typedef void (*psys_draw_start_func_t)(struct psys_emitter*, void*);
   26.20 +typedef void (*psys_draw_end_func_t)(struct psys_emitter*, void*);
   26.21 +
   26.22 +
   26.23 +struct psys_plane {
   26.24 +	plane_t p;
   26.25 +	float elasticity;
   26.26 +	struct psys_plane *next;
   26.27 +};
   26.28 +
   26.29 +
   26.30 +struct psys_emitter {
   26.31 +	struct anm_node prs;
   26.32 +	vec3_t cur_pos;
   26.33 +
   26.34 +	struct psys_attributes attr;
   26.35 +
   26.36 +	/* list of active particles */
   26.37 +	struct psys_particle *plist;
   26.38 +	int pcount;	/* number of active particles */
   26.39 +
   26.40 +	/* list of collision planes */
   26.41 +	struct psys_plane *planes;
   26.42 +
   26.43 +	/* custom spawn closure */
   26.44 +	void *spawn_cls;
   26.45 +	psys_spawn_func_t spawn;
   26.46 +
   26.47 +	/* custom particle update closure */
   26.48 +	void *upd_cls;
   26.49 +	psys_update_func_t update;
   26.50 +
   26.51 +	/* custom draw closure */
   26.52 +	void *draw_cls;
   26.53 +	psys_draw_func_t draw;
   26.54 +	psys_draw_start_func_t draw_start;
   26.55 +	psys_draw_end_func_t draw_end;
   26.56 +
   26.57 +	float spawn_acc;	/* partial spawn accumulator */
   26.58 +	float last_update;	/* last update time (to calc dt) */
   26.59 +};
   26.60 +
   26.61 +
   26.62 +struct psys_particle {
   26.63 +	vec3_t pos, vel;
   26.64 +	float life, max_life;
   26.65 +	float base_size;
   26.66 +
   26.67 +	struct psys_particle_attributes *pattr;
   26.68 +
   26.69 +	/* current particle attr values calculated during update */
   26.70 +	vec3_t color;
   26.71 +	float alpha, size;
   26.72 +
   26.73 +	struct psys_particle *next;
   26.74 +};
   26.75 +
   26.76 +#ifdef __cplusplus
   26.77 +extern "C" {
   26.78 +#endif
   26.79 +
   26.80 +struct psys_emitter *psys_create(void);
   26.81 +void psys_free(struct psys_emitter *em);
   26.82 +
   26.83 +int psys_init(struct psys_emitter *em);
   26.84 +void psys_destroy(struct psys_emitter *em);
   26.85 +
   26.86 +/* set properties */
   26.87 +void psys_set_pos(struct psys_emitter *em, vec3_t pos, float tm);
   26.88 +void psys_set_rot(struct psys_emitter *em, quat_t rot, float tm);
   26.89 +void psys_set_pivot(struct psys_emitter *em, vec3_t pivot);
   26.90 +
   26.91 +vec3_t psys_get_pos(struct psys_emitter *em, float tm);
   26.92 +quat_t psys_get_rot(struct psys_emitter *em, float tm);
   26.93 +vec3_t psys_get_pivot(struct psys_emitter *em);
   26.94 +
   26.95 +void psys_clear_collision_planes(struct psys_emitter *em);
   26.96 +int psys_add_collision_plane(struct psys_emitter *em, plane_t plane, float elast);
   26.97 +
   26.98 +void psys_add_particle(struct psys_emitter *em, struct psys_particle *p);
   26.99 +
  26.100 +void psys_spawn_func(struct psys_emitter *em, psys_spawn_func_t func, void *cls);
  26.101 +void psys_update_func(struct psys_emitter *em, psys_update_func_t func, void *cls);
  26.102 +void psys_draw_func(struct psys_emitter *em, psys_draw_func_t draw,
  26.103 +		psys_draw_start_func_t start, psys_draw_end_func_t end, void *cls);
  26.104 +
  26.105 +/* update and render */
  26.106 +
  26.107 +void psys_update(struct psys_emitter *em, float tm);
  26.108 +void psys_draw(struct psys_emitter *em);
  26.109 +
  26.110 +#ifdef __cplusplus
  26.111 +}
  26.112 +#endif
  26.113 +
  26.114 +#endif	/* LIBPSYS_H_ */
    27.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    27.2 +++ b/prototype/psys/psys_gl.c	Sun Oct 07 02:04:00 2012 +0300
    27.3 @@ -0,0 +1,106 @@
    27.4 +#include <string.h>
    27.5 +#include <errno.h>
    27.6 +#include <assert.h>
    27.7 +
    27.8 +#ifndef __APPLE__
    27.9 +#ifdef WIN32
   27.10 +#include <windows.h>
   27.11 +#endif
   27.12 +
   27.13 +#include <GL/gl.h>
   27.14 +#else
   27.15 +#include <OpenGL/gl.h>
   27.16 +#endif
   27.17 +
   27.18 +#include <imago2.h>
   27.19 +#include "psys.h"
   27.20 +#include "psys_gl.h"
   27.21 +
   27.22 +void psys_gl_draw_start(struct psys_emitter *em, void *cls)
   27.23 +{
   27.24 +	glPushAttrib(GL_ENABLE_BIT);
   27.25 +	glDisable(GL_LIGHTING);
   27.26 +
   27.27 +	glEnable(GL_BLEND);
   27.28 +	glBlendFunc(GL_SRC_ALPHA, GL_ONE);
   27.29 +
   27.30 +	if(em->attr.tex) {
   27.31 +		glEnable(GL_TEXTURE_2D);
   27.32 +		glBindTexture(GL_TEXTURE_2D, em->attr.tex);
   27.33 +	}
   27.34 +
   27.35 +	glDepthMask(0);
   27.36 +}
   27.37 +
   27.38 +void psys_gl_draw(struct psys_emitter *em, struct psys_particle *p, void *cls)
   27.39 +{
   27.40 +	float hsz = p->size / 2.0;
   27.41 +
   27.42 +	float xform[16];
   27.43 +
   27.44 +	glMatrixMode(GL_MODELVIEW);
   27.45 +	glPushMatrix();
   27.46 +
   27.47 +	glTranslatef(p->pos.x, p->pos.y, p->pos.z);
   27.48 +
   27.49 +	glGetFloatv(GL_MODELVIEW_MATRIX, xform);
   27.50 +	xform[0] = xform[5] = xform[10] = 1.0;
   27.51 +	xform[1] = xform[2] = xform[4] = xform[6] = xform[8] = xform[9] = 0.0;
   27.52 +	glLoadMatrixf(xform);
   27.53 +
   27.54 +	glBegin(GL_QUADS);
   27.55 +	glColor4f(p->color.x, p->color.y, p->color.z, p->alpha);
   27.56 +
   27.57 +	glTexCoord2f(0, 0);
   27.58 +	glVertex2f(-hsz, -hsz);
   27.59 +
   27.60 +	glTexCoord2f(1, 0);
   27.61 +	glVertex2f(hsz, -hsz);
   27.62 +
   27.63 +	glTexCoord2f(1, 1);
   27.64 +	glVertex2f(hsz, hsz);
   27.65 +
   27.66 +	glTexCoord2f(0, 1);
   27.67 +	glVertex2f(-hsz, hsz);
   27.68 +	glEnd();
   27.69 +
   27.70 +	glMatrixMode(GL_MODELVIEW);
   27.71 +	glPopMatrix();
   27.72 +}
   27.73 +
   27.74 +void psys_gl_draw_end(struct psys_emitter *em, void *cls)
   27.75 +{
   27.76 +	glDepthMask(1);
   27.77 +	glPopAttrib();
   27.78 +}
   27.79 +
   27.80 +
   27.81 +unsigned int psys_gl_load_texture(const char *fname, void *cls)
   27.82 +{
   27.83 +	unsigned int tex;
   27.84 +	void *pixels;
   27.85 +	int xsz, ysz;
   27.86 +
   27.87 +	if(!(pixels = img_load_pixels(fname, &xsz, &ysz, IMG_FMT_RGBA32))) {
   27.88 +		return 0;
   27.89 +	}
   27.90 +	printf("%s: creating texture %s (%dx%d)\n", __func__, fname, xsz, ysz);
   27.91 +
   27.92 +	glGenTextures(1, &tex);
   27.93 +	glBindTexture(GL_TEXTURE_2D, tex);
   27.94 +	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
   27.95 +	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
   27.96 +	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
   27.97 +	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
   27.98 +	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, xsz, ysz, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
   27.99 +
  27.100 +	assert(glGetError() == GL_NO_ERROR);
  27.101 +
  27.102 +	img_free_pixels(pixels);
  27.103 +	return tex;
  27.104 +}
  27.105 +
  27.106 +void psys_gl_unload_texture(unsigned int tex, void *cls)
  27.107 +{
  27.108 +	glDeleteTextures(1, &tex);
  27.109 +}
    28.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    28.2 +++ b/prototype/psys/psys_gl.h	Sun Oct 07 02:04:00 2012 +0300
    28.3 @@ -0,0 +1,14 @@
    28.4 +#ifndef PSYS_GL_H_
    28.5 +#define PSYS_GL_H_
    28.6 +
    28.7 +struct psys_particle;
    28.8 +struct psys_emitter;
    28.9 +
   28.10 +void psys_gl_draw_start(struct psys_emitter *em, void *cls);
   28.11 +void psys_gl_draw(struct psys_emitter *em, struct psys_particle *p, void *cls);
   28.12 +void psys_gl_draw_end(struct psys_emitter *em, void *cls);
   28.13 +
   28.14 +unsigned int psys_gl_load_texture(const char *fname, void *cls);
   28.15 +void psys_gl_unload_texture(unsigned int tex, void *cls);
   28.16 +
   28.17 +#endif	/* PSYS_GL_H_ */
    29.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    29.2 +++ b/prototype/psys/rndval.c	Sun Oct 07 02:04:00 2012 +0300
    29.3 @@ -0,0 +1,105 @@
    29.4 +#include <stdlib.h>
    29.5 +#include "rndval.h"
    29.6 +
    29.7 +int psys_init_anm_rnd(struct psys_anm_rnd *r)
    29.8 +{
    29.9 +	if(psys_init_track(&r->value) == -1) {
   29.10 +		return -1;
   29.11 +	}
   29.12 +	if(psys_init_track(&r->range) == -1) {
   29.13 +		psys_destroy_track(&r->value);
   29.14 +		return -1;
   29.15 +	}
   29.16 +	return 0;
   29.17 +}
   29.18 +
   29.19 +void psys_destroy_anm_rnd(struct psys_anm_rnd *r)
   29.20 +{
   29.21 +	psys_destroy_track(&r->value);
   29.22 +	psys_destroy_track(&r->range);
   29.23 +}
   29.24 +
   29.25 +int psys_init_anm_rnd3(struct psys_anm_rnd3 *r)
   29.26 +{
   29.27 +	if(psys_init_track3(&r->value) == -1) {
   29.28 +		return -1;
   29.29 +	}
   29.30 +	if(psys_init_track3(&r->range) == -1) {
   29.31 +		psys_destroy_track3(&r->value);
   29.32 +		return -1;
   29.33 +	}
   29.34 +	return 0;
   29.35 +}
   29.36 +
   29.37 +void psys_destroy_anm_rnd3(struct psys_anm_rnd3 *r)
   29.38 +{
   29.39 +	psys_destroy_track3(&r->value);
   29.40 +	psys_destroy_track3(&r->range);
   29.41 +}
   29.42 +
   29.43 +
   29.44 +void psys_set_rnd(struct psys_rnd *r, float val, float range)
   29.45 +{
   29.46 +	r->value = val;
   29.47 +	r->range = range;
   29.48 +}
   29.49 +
   29.50 +void psys_set_rnd3(struct psys_rnd3 *r, vec3_t val, vec3_t range)
   29.51 +{
   29.52 +	r->value = val;
   29.53 +	r->range = range;
   29.54 +}
   29.55 +
   29.56 +void psys_set_anm_rnd(struct psys_anm_rnd *r, anm_time_t tm, float val, float range)
   29.57 +{
   29.58 +	psys_set_value(&r->value, tm, val);
   29.59 +	psys_set_value(&r->range, tm, range);
   29.60 +}
   29.61 +
   29.62 +void psys_set_anm_rnd3(struct psys_anm_rnd3 *r, anm_time_t tm, vec3_t val, vec3_t range)
   29.63 +{
   29.64 +	psys_set_value3(&r->value, tm, val);
   29.65 +	psys_set_value3(&r->range, tm, range);
   29.66 +}
   29.67 +
   29.68 +
   29.69 +float psys_eval_rnd(struct psys_rnd *r)
   29.70 +{
   29.71 +	return r->value + r->range * (float)rand() / (float)RAND_MAX - 0.5 * r->range;
   29.72 +}
   29.73 +
   29.74 +vec3_t psys_eval_rnd3(struct psys_rnd3 *r)
   29.75 +{
   29.76 +	vec3_t res;
   29.77 +	res.x = r->value.x + r->range.x * (float)rand() / (float)RAND_MAX - 0.5 * r->range.x;
   29.78 +	res.y = r->value.y + r->range.y * (float)rand() / (float)RAND_MAX - 0.5 * r->range.y;
   29.79 +	res.z = r->value.z + r->range.z * (float)rand() / (float)RAND_MAX - 0.5 * r->range.z;
   29.80 +	return res;
   29.81 +}
   29.82 +
   29.83 +
   29.84 +float psys_eval_anm_rnd(struct psys_anm_rnd *r, anm_time_t tm)
   29.85 +{
   29.86 +	struct psys_rnd tmp;
   29.87 +	if(tm == ANM_TIME_INVAL) {
   29.88 +		tmp.value = psys_get_cur_value(&r->value);
   29.89 +		tmp.range = psys_get_cur_value(&r->range);
   29.90 +	} else {
   29.91 +		tmp.value = psys_get_value(&r->value, tm);
   29.92 +		tmp.range = psys_get_value(&r->range, tm);
   29.93 +	}
   29.94 +	return psys_eval_rnd(&tmp);
   29.95 +}
   29.96 +
   29.97 +vec3_t psys_eval_anm_rnd3(struct psys_anm_rnd3 *r, anm_time_t tm)
   29.98 +{
   29.99 +	struct psys_rnd3 tmp;
  29.100 +	if(tm == ANM_TIME_INVAL) {
  29.101 +		tmp.value = psys_get_cur_value3(&r->value);
  29.102 +		tmp.range = psys_get_cur_value3(&r->range);
  29.103 +	} else {
  29.104 +		tmp.value = psys_get_value3(&r->value, tm);
  29.105 +		tmp.range = psys_get_value3(&r->range, tm);
  29.106 +	}
  29.107 +	return psys_eval_rnd3(&tmp);
  29.108 +}
    30.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    30.2 +++ b/prototype/psys/rndval.h	Sun Oct 07 02:04:00 2012 +0300
    30.3 @@ -0,0 +1,43 @@
    30.4 +#ifndef RNDVAL_H_
    30.5 +#define RNDVAL_H_
    30.6 +
    30.7 +#include <vmath/vmath.h>
    30.8 +#include "pstrack.h"
    30.9 +
   30.10 +struct psys_rnd {
   30.11 +	float value, range;
   30.12 +};
   30.13 +
   30.14 +struct psys_rnd3 {
   30.15 +	vec3_t value, range;
   30.16 +};
   30.17 +
   30.18 +struct psys_anm_rnd {
   30.19 +	struct psys_track value, range;
   30.20 +};
   30.21 +
   30.22 +struct psys_anm_rnd3 {
   30.23 +	struct psys_track3 value, range;
   30.24 +};
   30.25 +
   30.26 +#define PSYS_EVAL_CUR	ANM_TIME_INVAL
   30.27 +
   30.28 +int psys_init_anm_rnd(struct psys_anm_rnd *v);
   30.29 +void psys_destroy_anm_rnd(struct psys_anm_rnd *v);
   30.30 +int psys_init_anm_rnd3(struct psys_anm_rnd3 *v);
   30.31 +void psys_destroy_anm_rnd3(struct psys_anm_rnd3 *v);
   30.32 +
   30.33 +void psys_set_rnd(struct psys_rnd *r, float val, float range);
   30.34 +void psys_set_rnd3(struct psys_rnd3 *r, vec3_t val, vec3_t range);
   30.35 +
   30.36 +void psys_set_anm_rnd(struct psys_anm_rnd *r, anm_time_t tm, float val, float range);
   30.37 +void psys_set_anm_rnd3(struct psys_anm_rnd3 *r, anm_time_t tm, vec3_t val, vec3_t range);
   30.38 +
   30.39 +float psys_eval_rnd(struct psys_rnd *r);
   30.40 +vec3_t psys_eval_rnd3(struct psys_rnd3 *r);
   30.41 +
   30.42 +float psys_eval_anm_rnd(struct psys_anm_rnd *r, anm_time_t tm);
   30.43 +vec3_t psys_eval_anm_rnd3(struct psys_anm_rnd3 *r, anm_time_t tm);
   30.44 +
   30.45 +
   30.46 +#endif	/* RNDVAL_H_ */