https://doi.org/10.5201/ipol.2021.340
Raw File
iio.c

// IIO: a library for reading small images {{{1
//
// Goal: load an image (of unknown format) from a given FILE*
//
// Technique: read the first few bytes of the file to identify the format, and
// call the appropriate image library (lipng, lipjpeg, libtiff, etc).  For
// simple, uncompressed formats, write the loading code by hand.  For formats
// having a library with a nice API, call the library functions.  For other or
// unrecognized formats use an external program (convert, anytopnm,
// gm convert...) to convert them into a readable format.  If anything else
// fails, assume that the image is in headings+raw format, and try to extract
// its dimensions and headings using some heuristics (file name containing
// "%dx%d", headings containing ascii numbers, etc.)
//
// Difficulties: most image libraries expect to be fed a whole file, not a
// beheaded file.  Thus, some hackery is necessary.
//
// See file "iio.txt" for slightly more detailed documentation, and "iio.h" for
// the API
//
// Copyright (c) Enric Meinhardt-Llopis 2012

// #includes {{{1

#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#include "iio.h" // only for IIO_MAX_DIMENSION



#ifdef I_CAN_HAS_LIBPNG
// ugly "feature" in png.h forces this header to be included first
#  include <png.h>
#endif



// #defines {{{1

//
// configuration
//

#if _POSIX_C_SOURCE >= 200809L
#  define I_CAN_HAS_FMEMOPEN 1
#endif

#if _POSIX_C_SOURCE >= 200112L
#  define I_CAN_HAS_MKSTEMP 1
#endif

//
// enum-like, only used internally
//

#define IIO_TYPE_INT8 1
#define IIO_TYPE_UINT8 2
#define IIO_TYPE_INT16 3
#define IIO_TYPE_UINT16 4
#define IIO_TYPE_INT32 5
#define IIO_TYPE_UINT32 6
#define IIO_TYPE_FLOAT 7
#define IIO_TYPE_DOUBLE 8
#define IIO_TYPE_LONGDOUBLE 9
#define IIO_TYPE_INT64 10
#define IIO_TYPE_UINT64 11
#define IIO_TYPE_HALF 12
#define IIO_TYPE_UINT1 13
#define IIO_TYPE_UINT2 14
#define IIO_TYPE_UINT4 15
#define IIO_TYPE_CHAR 16
#define IIO_TYPE_SHORT 17
#define IIO_TYPE_INT 18
#define IIO_TYPE_LONG 19
#define IIO_TYPE_LONGLONG 20

#define IIO_FORMAT_WHATEVER 0
#define IIO_FORMAT_QNM 1
#define IIO_FORMAT_PNG 2
#define IIO_FORMAT_JPEG 3
#define IIO_FORMAT_TIFF 4
#define IIO_FORMAT_RIM 5
#define IIO_FORMAT_BMP 6
#define IIO_FORMAT_EXR 7
#define IIO_FORMAT_JP2 8
#define IIO_FORMAT_VTK 9
#define IIO_FORMAT_CIMG 10
#define IIO_FORMAT_PAU 11
#define IIO_FORMAT_DICOM 12
#define IIO_FORMAT_PFM 13
#define IIO_FORMAT_NIFTI 14
#define IIO_FORMAT_PCX 15
#define IIO_FORMAT_GIF 16
#define IIO_FORMAT_XPM 17
#define IIO_FORMAT_RAFA 18
#define IIO_FORMAT_FLO 19
#define IIO_FORMAT_JUV 20
#define IIO_FORMAT_LUM 21
#define IIO_FORMAT_PCM 22
#define IIO_FORMAT_UNRECOGNIZED (-1)

//
// sugar
//
#define FORI(n) for(int i=0;i<(int)(n);i++)
#define FORJ(n) for(int j=0;j<(int)(n);j++)
#define FORK(n) for(int k=0;k<(int)(n);k++)
#define FORL(n) for(int l=0;l<(int)(n);l++)

#ifdef IIO_SHOW_DEBUG_MESSAGES
#  define IIO_DEBUG(...) do {\
	fprintf(stderr,"DEBUG(%s:%d:%s): ",__FILE__,__LINE__,__PRETTY_FUNCTION__);\
	fprintf(stderr,__VA_ARGS__);} while(0)
#else//IIO_SHOW_DEBUG_MESSAGES
#  define IIO_DEBUG(...) do { do_nop("",__VA_ARGS__); } while(0) /* no-res */
#endif//IIO_SHOW_DEBUG_MESSAGES

//
// hacks
//
#ifndef __attribute__
#  ifndef __GNUC__
#    define __attribute__(x) /*NOTHING*/
#  endif
#endif



// typedefs {{{1

typedef long long longong;
typedef long double longdouble;




// utility functions {{{1

#ifndef IIO_ABORT_ON_ERROR

// NOTE: libpng has a nasty "feature" whereby you have to include libpng.h
// before setjmp.h if you want to use both.   This induces the following
// hackery:
#  ifndef I_CAN_HAS_LIBPNG
#    include <setjmp.h>
#  endif//I_CAN_HAS_LIBPNG
static jmp_buf global_jump_buffer;
#endif//IIO_ABORT_ON_ERROR

//#include <errno.h> // only for errno
#include <ctype.h> // for isspace
#include <math.h> // for floorf
#include <stdlib.h>

#ifdef I_CAN_HAS_LINUX
#  include <unistd.h>
static const char *emptystring = "";
static const char *myname(void)
{
#  define n 0x29a
	static char buf[n];
	pid_t p = getpid();
	snprintf(buf, n, "/proc/%d/cmdline", p);
	FILE *f = fopen(buf, "r");
	if (!f) return emptystring;
	int c, i = 0;
	while ((c = fgetc(f)) != EOF && i < n) {
#  undef n
		buf[i] = c ? c : ' ';
		i += 1;
	}
	if (i) buf[i-1] = '\0';
	fclose(f);
	return buf;
}
#else
static const char *myname(void) { return ""; }
#endif//I_CAN_HAS_LINUX

static void do_nop(const char *x, ...)
{
	va_list argp;
	va_start(argp, x);
	va_end(argp);
}

static void fail(const char *fmt, ...) __attribute__((noreturn,format(printf,1,2)));
static void fail(const char *fmt, ...)

{
	va_list argp;
	fprintf(stderr, "\nERROR(\"%s\"): ", myname());
	va_start(argp, fmt);
	vfprintf(stderr, fmt, argp);
	va_end(argp);
	fprintf(stderr, "\n\n");
	fflush(NULL);
#ifndef IIO_ABORT_ON_ERROR
	longjmp(global_jump_buffer, 1);
	//iio_single_jmpstuff(true, false);
#else//IIO_ABORT_ON_ERROR
#  ifdef NDEBUG
	exit(-1);
#  else//NDEBUG
	//print_trace(stderr);
	exit(*(int *)0x43);
#  endif//NDEBUG
#endif//IIO_ABORT_ON_ERROR
}

static void *xmalloc(size_t size)
{
	if (size == 0)
		fail("xmalloc: zero size");
	void *new = malloc(size);
	if (!new)
	{
		double sm = size / (0x100000 * 1.0);
		fail("xmalloc: out of memory when requesting "
			"%zu bytes (%gMB)",//:\"%s\"",
			size, sm);//, strerror(errno));
	}
	return new;
}

static void *xrealloc(void *p, size_t s)
{
	void *r = realloc(p, s);
	if (!r) fail("realloc failed");
	return r;
}

static void xfree(void *p)
{
	if (!p)
		fail("thou shalt not free a null pointer!");
	free(p);
}

static const
char *global_variable_containing_the_name_of_the_last_opened_file = NULL;

static FILE *xfopen(const char *s, const char *p)
{
	global_variable_containing_the_name_of_the_last_opened_file = NULL;
	FILE *f;

	if (!s) fail("trying to open a file with NULL name");

	if (0 == strcmp("-", s))
	{
		if (0 == strcmp("w", p))
			return stdout;
		else if (0 == strcmp("r", p))
			return stdin;
		else
			fail("unknown fopen mode \"%s\"", p);
	}
	if (0 == strcmp("--", s) && 0 == strcmp("w", p)) return stderr;

	f = fopen(s, p);
	if (f == NULL)
		fail("can not open file \"%s\" in mode \"%s\"",// (%s)",
				s, p);//, strerror(errno));
	global_variable_containing_the_name_of_the_last_opened_file = s;
	return f;
}

static void xfclose(FILE *f)
{
	global_variable_containing_the_name_of_the_last_opened_file = NULL;
	if (f != stdout && f != stdin && f != stderr) {
		int r = fclose(f);
		if (r) fail("fclose error");// \"%s\"", strerror(errno));
	}
}

static int pilla_caracter_segur(FILE *f)
{
	int c = getc(f);
	if (EOF == c)
		fail("input file ended before expected");
	return c;
}

static void menja_espais(FILE *f)
{
	int c;
	do
		c = pilla_caracter_segur(f);
	while (isspace(c));
	ungetc(c, f);
}

static void menja_linia(FILE *f)
{
	while (pilla_caracter_segur(f) != '\n')
		;
}

static void menja_espais_i_comentaris(FILE *f)
{
	int c, comment_char = '#';
	menja_espais(f);
	while(1) {
		c = pilla_caracter_segur(f);
		if (c == comment_char)
		{
			menja_linia(f);
			menja_espais(f);
		} else
			ungetc(c, f);
		if (c == comment_char) break;
	}
}



// struct iio_image { ... }; {{{1

// This struct is used for exchanging image information between internal
// functions.  It could be safely eliminated, and this information be passed as
// five or six variables.
struct iio_image {
	int dimension;        // 1, 2, 3 or 4
	int sizes[IIO_MAX_DIMENSION];
	int pixel_dimension;
	int type;             // IIO_TYPE_*

	int meta;             // IIO_META_*
	int format;           // IIO_FORMAT_*

	bool contiguous_data;
	bool caca[3];
	void *data;
};


// struct iio_image management {{{1

// TODO: reduce the silly number of constructors to 1

static void iio_image_assert_struct_consistency(struct iio_image *x)
{
	assert(x->dimension > 0);
	assert(x->dimension <= IIO_MAX_DIMENSION);
	FORI(x->dimension) assert(x->sizes[i] > 0);
	assert(x->pixel_dimension > 0);
	switch(x->type) {
	case IIO_TYPE_INT8: case IIO_TYPE_UINT8: case IIO_TYPE_INT16:
	case IIO_TYPE_UINT16: case IIO_TYPE_INT32: case IIO_TYPE_UINT32:
	case IIO_TYPE_FLOAT: case IIO_TYPE_DOUBLE: case IIO_TYPE_LONGDOUBLE:
	case IIO_TYPE_INT64: case IIO_TYPE_UINT64: case IIO_TYPE_HALF:
	case IIO_TYPE_CHAR: case IIO_TYPE_SHORT: case IIO_TYPE_INT:
	case IIO_TYPE_LONG: case IIO_TYPE_LONGLONG: break;
	default: assert(false);
	}
}

// API
static size_t iio_type_size(int type)
{
	switch(type) {
	case IIO_TYPE_INT8: return 1;
	case IIO_TYPE_UINT8: return 1;
	case IIO_TYPE_INT16: return 2;
	case IIO_TYPE_UINT16: return 2;
	case IIO_TYPE_INT32: return 4;
	case IIO_TYPE_UINT32: return 4;
	case IIO_TYPE_INT64: return 8;
	case IIO_TYPE_UINT64: return 8;
	case IIO_TYPE_CHAR: return sizeof(char); // 1
	case IIO_TYPE_SHORT: return sizeof(short);
	case IIO_TYPE_INT: return sizeof(int);
	case IIO_TYPE_LONG: return sizeof(long);
	case IIO_TYPE_LONGLONG: return sizeof(long long);
	case IIO_TYPE_FLOAT: return sizeof(float);
	case IIO_TYPE_DOUBLE: return sizeof(double);
	case IIO_TYPE_LONGDOUBLE: return sizeof(long double);
	case IIO_TYPE_HALF: return sizeof(float)/2;
	default: fail("unrecognized type %d", type);
	}
}

// XXX TODO FIXME: this is architecture dependent!
// this function actually requires A LOT of magic to be portable
static int normalize_type(int type_in)
{
	int type_out;
	switch(type_in) {
	case IIO_TYPE_CHAR: type_out = IIO_TYPE_UINT8; break;
	case IIO_TYPE_SHORT: type_out = IIO_TYPE_INT16; break;
	case IIO_TYPE_INT: type_out = IIO_TYPE_UINT32; break;
	default: type_out = type_in; break;
	}
	if (type_out != type_in) {
		// the following assertion fails on many architectures
		assert(iio_type_size(type_in) == iio_type_size(type_out));
	}
	return type_out;
}


// internal API
static
int iio_type_id(size_t sample_size, bool ieeefp_sample, bool signed_sample)
{
	if (ieeefp_sample) {
		if (signed_sample) fail("signed floats are a no-no!");
		switch(sample_size) {
		case sizeof(float):       return IIO_TYPE_FLOAT;
		case sizeof(double):      return IIO_TYPE_DOUBLE;
		case sizeof(long double): return IIO_TYPE_LONGDOUBLE;
		case sizeof(float)/2:     return IIO_TYPE_HALF;
		default: fail("bad float size %zu", sample_size);
		}
	} else {
		switch(sample_size) {
		case 1: return signed_sample ? IIO_TYPE_INT8 : IIO_TYPE_UINT8;
		case 2: return signed_sample ? IIO_TYPE_INT16 : IIO_TYPE_UINT16;
		case 4: return signed_sample ? IIO_TYPE_INT32 : IIO_TYPE_UINT32;
		case 8: return signed_sample ? IIO_TYPE_INT64 : IIO_TYPE_UINT64;
		default: fail("bad integral size %zu", sample_size);
		}
	}
}

// internal API
static void iio_type_unid(int *size, bool *ieefp, bool *signedness, int type)
{
	int t = normalize_type(type);
	*size = iio_type_size(t);
	*ieefp = (t==IIO_TYPE_HALF || t==IIO_TYPE_FLOAT
			|| t==IIO_TYPE_DOUBLE || t==IIO_TYPE_LONGDOUBLE);
	*signedness = (t==IIO_TYPE_INT8 || t==IIO_TYPE_INT16
			|| t==IIO_TYPE_INT32 || t==IIO_TYPE_INT64);
}

// internal API
static int iio_image_number_of_elements(struct iio_image *x)
{
	iio_image_assert_struct_consistency(x);
	int r = 1;
	FORI(x->dimension) r *= x->sizes[i];
	return r;
}

// internal API
static int iio_image_number_of_samples(struct iio_image *x)
{
	return iio_image_number_of_elements(x) * x->pixel_dimension;
}

// internal API
static size_t  iio_image_sample_size(struct iio_image *x)
{
	return iio_type_size(x->type);
}

static const char *iio_strtyp(int type)
{
#define M(t) case IIO_TYPE_ ## t: return #t
	switch(type) {
	M(INT8); M(UINT8); M(INT16); M(UINT16); M(INT32); M(UINT32); M(INT64);
	M(UINT64); M(FLOAT); M(DOUBLE); M(LONGDOUBLE); M(HALF); M(UINT1);
	M(UINT2); M(UINT4); M(CHAR); M(SHORT); M(INT); M(LONG); M(LONGLONG);
	default: return "unrecognized";
	}
#undef M
}

static const char *iio_strfmt(int format)
{
#define M(f) case IIO_FORMAT_ ## f: return #f
	switch(format) {
	M(WHATEVER); M(QNM); M(PNG); M(JPEG);
	M(TIFF); M(RIM); M(BMP); M(EXR); M(JP2);
	M(VTK); M(CIMG); M(PAU); M(DICOM); M(PFM); M(NIFTI);
	M(PCX); M(GIF); M(XPM); M(RAFA); M(FLO);
	M(UNRECOGNIZED);
	default: fail("caca de la grossa");
	}
#undef M
}



static void iio_image_fill(struct iio_image *x,
		int dimension, int *sizes,
		int type, int pixel_dimension)
{
	x->dimension = dimension;
	FORI(dimension) x->sizes[i] = sizes[i];
	x->type = type;
	x->pixel_dimension = pixel_dimension;
	x->data = NULL;
	x->format = -1;
	x->meta = -1;
	x->contiguous_data = false;
}


static void iio_wrap_image_struct_around_data(struct iio_image *x,
		int dimension, int *sizes, int pixel_dimension,
		int type, void *data)
{
	assert(dimension < IIO_MAX_DIMENSION);
	x->dimension = dimension;
	FORI(x->dimension) x->sizes[i] = sizes[i];
	x->pixel_dimension = pixel_dimension;
	x->type = type;
	x->data = data;
	x->contiguous_data = false;
	x->meta = -42;
	x->format = -42;
}


static void iio_image_build_independent(struct iio_image *x,
		int dimension, int *sizes, int type, int pixel_dimension)
{
	assert(dimension > 0);
	assert(dimension <= 4);
	assert(pixel_dimension > 0);
	iio_image_fill(x, dimension, sizes, type, pixel_dimension);
	size_t datalength = 1; FORI(dimension) datalength *= sizes[i];
	size_t datasize = datalength * iio_type_size(type) * pixel_dimension;
	x->data = xmalloc(datasize);
}



// data conversion {{{1

#define T0(x) ((x)>0?(x):0)
#define T8(x) ((x)>0?((x)<0xff?(x):0xff):0)
#define T6(x) ((x)>0?((x)<0xffff?(x):0xffff):0)

#define CC(a,b) (77*(a)+(b)) // hash number of a conversion pair
#define I8 IIO_TYPE_INT8
#define U8 IIO_TYPE_UINT8
#define I6 IIO_TYPE_INT16
#define U6 IIO_TYPE_UINT16
#define I2 IIO_TYPE_INT32
#define U2 IIO_TYPE_UINT32
#define I4 IIO_TYPE_INT64
#define U4 IIO_TYPE_UINT64
#define F4 IIO_TYPE_FLOAT
#define F8 IIO_TYPE_DOUBLE
#define F6 IIO_TYPE_LONGDOUBLE
static void convert_datum(void *dest, void *src, int dest_fmt, int src_fmt)
{
	// NOTE: verify that this switch is optimized outside of the loop in
	// which it is contained.  Otherwise, produce some self-modifying code
	// here.
	switch(CC(dest_fmt,src_fmt)) {

	// change of sign (lossless, but changes interpretation)
	case CC(I8,U8): *(  int8_t*)dest = *( uint8_t*)src; break;
	case CC(I6,U6): *( int16_t*)dest = *(uint16_t*)src; break;
	case CC(I2,U2): *( int32_t*)dest = *(uint32_t*)src; break;
	case CC(U8,I8): *( uint8_t*)dest = *(  int8_t*)src; break;
	case CC(U6,I6): *(uint16_t*)dest = *( int16_t*)src; break;
	case CC(U2,I2): *(uint32_t*)dest = *( int32_t*)src; break;

	// different size signed integers (3 lossy, 3 lossless)
	case CC(I8,I6): *(  int8_t*)dest = *( int16_t*)src; break;//iw810
	case CC(I8,I2): *(  int8_t*)dest = *( int32_t*)src; break;//iw810
	case CC(I6,I2): *( int16_t*)dest = *( int32_t*)src; break;//iw810
	case CC(I6,I8): *( int16_t*)dest = *(  int8_t*)src; break;
	case CC(I2,I8): *( int32_t*)dest = *(  int8_t*)src; break;
	case CC(I2,I6): *( int32_t*)dest = *( int16_t*)src; break;

	// different size unsigned integers (3 lossy, 3 lossless)
	case CC(U8,U6): *( uint8_t*)dest = *(uint16_t*)src; break;//iw810
	case CC(U8,U2): *( uint8_t*)dest = *(uint32_t*)src; break;//iw810
	case CC(U6,U2): *(uint16_t*)dest = *(uint32_t*)src; break;//iw810
	case CC(U6,U8): *(uint16_t*)dest = *( uint8_t*)src; break;
	case CC(U2,U8): *(uint32_t*)dest = *( uint8_t*)src; break;
	case CC(U2,U6): *(uint32_t*)dest = *(uint16_t*)src; break;

	// diferent size mixed integers, make signed (3 lossy, 3 lossless)
	case CC(I8,U6): *(  int8_t*)dest = *(uint16_t*)src; break;//iw810
	case CC(I8,U2): *(  int8_t*)dest = *(uint32_t*)src; break;//iw810
	case CC(I6,U2): *( int16_t*)dest = *(uint32_t*)src; break;//iw810
	case CC(I6,U8): *( int16_t*)dest = *( uint8_t*)src; break;
	case CC(I2,U8): *( int32_t*)dest = *( uint8_t*)src; break;
	case CC(I2,U6): *( int32_t*)dest = *(uint16_t*)src; break;

	// diferent size mixed integers, make unsigned (3 lossy, 3 lossless)
	case CC(U8,I6): *( uint8_t*)dest = *( int16_t*)src; break;//iw810
	case CC(U8,I2): *( uint8_t*)dest = *( int32_t*)src; break;//iw810
	case CC(U6,I2): *(uint16_t*)dest = *( int32_t*)src; break;//iw810
	case CC(U6,I8): *(uint16_t*)dest = *(  int8_t*)src; break;
	case CC(U2,I8): *(uint32_t*)dest = *(  int8_t*)src; break;
	case CC(U2,I6): *(uint32_t*)dest = *( int16_t*)src; break;

	// from float (typically lossy, except for small integers)
	case CC(I8,F4): *(  int8_t*)dest = *( float*)src; break;//iw810
	case CC(I6,F4): *( int16_t*)dest = *( float*)src; break;//iw810
	case CC(I2,F4): *( int32_t*)dest = *( float*)src; break;
	case CC(I8,F8): *(  int8_t*)dest = *(double*)src; break;//iw810
	case CC(I6,F8): *( int16_t*)dest = *(double*)src; break;//iw810
	case CC(I2,F8): *( int32_t*)dest = *(double*)src; break;//iw810
	case CC(U8,F4): *( uint8_t*)dest = T8(0.5+*( float*)src); break;//iw810
	case CC(U6,F4): *(uint16_t*)dest = T6(0.5+*( float*)src); break;//iw810
	case CC(U2,F4): *(uint32_t*)dest = *( float*)src; break;
	case CC(U8,F8): *( uint8_t*)dest = T8(0.5+*(double*)src); break;//iw810
	case CC(U6,F8): *(uint16_t*)dest = T6(0.5+*(double*)src); break;//iw810
	case CC(U2,F8): *(uint32_t*)dest = *(double*)src; break;//iw810

	// to float (typically lossless, except for large integers)
	case CC(F4,I8): *( float*)dest = *(  int8_t*)src; break;
	case CC(F4,I6): *( float*)dest = *( int16_t*)src; break;
	case CC(F4,I2): *( float*)dest = *( int32_t*)src; break;//iw810
	case CC(F8,I8): *(double*)dest = *(  int8_t*)src; break;
	case CC(F8,I6): *(double*)dest = *( int16_t*)src; break;
	case CC(F8,I2): *(double*)dest = *( int32_t*)src; break;
	case CC(F4,U8): *( float*)dest = *( uint8_t*)src; break;
	case CC(F4,U6): *( float*)dest = *(uint16_t*)src; break;
	case CC(F4,U2): *( float*)dest = *(uint32_t*)src; break;//iw810
	case CC(F8,U8): *(double*)dest = *( uint8_t*)src; break;
	case CC(F8,U6): *(double*)dest = *(uint16_t*)src; break;
	case CC(F8,U2): *(double*)dest = *(uint32_t*)src; break;

#ifdef I_CAN_HAS_INT64
	// to int64_t and uint64_t
	case CC(I4,I8): *( int64_t*)dest = *(  int8_t*)src; break;
	case CC(I4,I6): *( int64_t*)dest = *( int16_t*)src; break;
	case CC(I4,I2): *( int64_t*)dest = *( int32_t*)src; break;
	case CC(I4,U8): *( int64_t*)dest = *( uint8_t*)src; break;
	case CC(I4,U6): *( int64_t*)dest = *(uint16_t*)src; break;
	case CC(I4,U2): *( int64_t*)dest = *(uint32_t*)src; break;
	case CC(I4,F4): *( int64_t*)dest = *(   float*)src; break;
	case CC(I4,F8): *( int64_t*)dest = *(  double*)src; break;
	case CC(U4,I8): *(uint64_t*)dest = *(  int8_t*)src; break;
	case CC(U4,I6): *(uint64_t*)dest = *( int16_t*)src; break;
	case CC(U4,I2): *(uint64_t*)dest = *( int32_t*)src; break;
	case CC(U4,U8): *(uint64_t*)dest = *( uint8_t*)src; break;
	case CC(U4,U6): *(uint64_t*)dest = *(uint16_t*)src; break;
	case CC(U4,U2): *(uint64_t*)dest = *(uint32_t*)src; break;
	case CC(U4,F4): *(uint64_t*)dest = *(   float*)src; break;
	case CC(U4,F8): *(uint64_t*)dest = *(  double*)src; break;

	// from int64_t and uint64_t
	case CC(I8,I4): *(  int8_t*)dest = *( int64_t*)src; break;
	case CC(I6,I4): *( int16_t*)dest = *( int64_t*)src; break;
	case CC(I2,I4): *( int32_t*)dest = *( int64_t*)src; break;
	case CC(U8,I4): *( uint8_t*)dest = *( int64_t*)src; break;
	case CC(U6,I4): *(uint16_t*)dest = *( int64_t*)src; break;
	case CC(U2,I4): *(uint32_t*)dest = *( int64_t*)src; break;
	case CC(F4,I4): *(   float*)dest = *( int64_t*)src; break;
	case CC(F8,I4): *(  double*)dest = *( int64_t*)src; break;
	case CC(I8,U4): *(  int8_t*)dest = *(uint64_t*)src; break;
	case CC(I6,U4): *( int16_t*)dest = *(uint64_t*)src; break;
	case CC(I2,U4): *( int32_t*)dest = *(uint64_t*)src; break;
	case CC(U8,U4): *( uint8_t*)dest = *(uint64_t*)src; break;
	case CC(U6,U4): *(uint16_t*)dest = *(uint64_t*)src; break;
	case CC(U2,U4): *(uint32_t*)dest = *(uint64_t*)src; break;
	case CC(F4,U4): *(   float*)dest = *(uint64_t*)src; break;
	case CC(F8,U4): *(  double*)dest = *(uint64_t*)src; break;
#endif//I_CAN_HAS_INT64

#ifdef I_CAN_HAS_LONGDOUBLE
	// to longdouble
	case CC(F6,I8): *(longdouble*)dest = *(  int8_t*)src; break;
	case CC(F6,I6): *(longdouble*)dest = *( int16_t*)src; break;
	case CC(F6,I2): *(longdouble*)dest = *( int32_t*)src; break;
	case CC(F6,U8): *(longdouble*)dest = *( uint8_t*)src; break;
	case CC(F6,U6): *(longdouble*)dest = *(uint16_t*)src; break;
	case CC(F6,U2): *(longdouble*)dest = *(uint32_t*)src; break;
	case CC(F6,F4): *(longdouble*)dest = *(   float*)src; break;
	case CC(F6,F8): *(longdouble*)dest = *(  double*)src; break;

	// from longdouble
	case CC(I8,F6): *(  int8_t*)dest = *(longdouble*)src; break;
	case CC(I6,F6): *( int16_t*)dest = *(longdouble*)src; break;
	case CC(I2,F6): *( int32_t*)dest = *(longdouble*)src; break;
	case CC(U8,F6): *( uint8_t*)dest = T8(0.5+*(longdouble*)src); break;
	case CC(U6,F6): *(uint16_t*)dest = T6(0.5+*(longdouble*)src); break;
	case CC(U2,F6): *(uint32_t*)dest = *(longdouble*)src; break;
	case CC(F4,F6): *(   float*)dest = *(longdouble*)src; break;
	case CC(F8,F6): *(  double*)dest = *(longdouble*)src; break;

#ifdef I_CAN_HAS_INT64 //(nested)
	case CC(F6,I4): *(longdouble*)dest = *( int64_t*)src; break;
	case CC(F6,U4): *(longdouble*)dest = *(uint64_t*)src; break;
	case CC(I4,F6): *( int64_t*)dest = *(longdouble*)src; break;
	case CC(U4,F6): *(uint64_t*)dest = *(longdouble*)src; break;
#endif//I_CAN_HAS_INT64 (nested)

#endif//I_CAN_HAS_LONGDOUBLE

	default: fail("bad conversion from %d to %d", src_fmt, dest_fmt);
	}
}
#undef CC
#undef I8
#undef U8
#undef I6
#undef U6
#undef I2
#undef U2
#undef F4
#undef F8
#undef F6

static void *convert_data(void *src, int n, int dest_fmt, int src_fmt)
{
	if (src_fmt == IIO_TYPE_FLOAT)
		IIO_DEBUG("first float sample = %g\n", *(float*)src);
	size_t src_width = iio_type_size(src_fmt);
	size_t dest_width = iio_type_size(dest_fmt);
	IIO_DEBUG("converting %d samples from %s to %s\n", n, iio_strtyp(src_fmt), iio_strtyp(dest_fmt));
	IIO_DEBUG("src width = %zu\n", src_width);
	IIO_DEBUG("dest width = %zu\n", dest_width);
	char *r = xmalloc(n * dest_width);
	// NOTE: the switch inside "convert_datum" should be optimized
	// outside of this loop
	FORI(n) {
		void *to  = i * dest_width + r;
		void *from = i * src_width + (char *)src;
		convert_datum(to, from, dest_fmt, src_fmt);
	}
	xfree(src);
	if (dest_fmt == IIO_TYPE_INT16)
		IIO_DEBUG("first short sample = %d\n", *(int16_t*)r);
	return r;
}

static void unpack_nibbles_to_bytes(uint8_t out[2], uint8_t in)
{
	out[1] = (in & 0x0f);    //0b00001111
	out[0] = (in & 0xf0)>>4; //0b11110000
}

static void unpack_couples_to_bytes(uint8_t out[4], uint8_t in)
{
	out[3] = (in & 0x03);    //0b00000011
	out[2] = (in & 0x0c)>>2; //0b00001100
	out[1] = (in & 0x30)>>4; //0b00110000
	out[0] = (in & 0xc0)>>6; //0b11000000
}

static void unpack_bits_to_bytes(uint8_t out[8], uint8_t in)
{
	out[7] = (in & 1) ? 1 : 0;
	out[6] = (in & 2) ? 1 : 0;
	out[5] = (in & 4) ? 1 : 0;
	out[4] = (in & 8) ? 1 : 0;
	out[3] = (in & 16) ? 1 : 0;
	out[2] = (in & 32) ? 1 : 0;
	out[1] = (in & 64) ? 1 : 0;
	out[0] = (in & 128) ? 1 : 0;
}

static void unpack_to_bytes_here(uint8_t *dest, uint8_t *src, int n, int bits)
{
	fprintf(stderr, "unpacking %d bytes %d-fold\n", n, bits);
	assert(bits==1 || bits==2 || bits==4);
	//size_t unpack_factor = 8 / bits;
	switch(bits) {
	case 1: FORI(n)    unpack_bits_to_bytes(dest + 8*i, src[i]); break;
	case 2: FORI(n) unpack_couples_to_bytes(dest + 4*i, src[i]); break;
	case 4: FORI(n) unpack_nibbles_to_bytes(dest + 2*i, src[i]); break;
	default: fail("very strange error");
	}
}


static void iio_convert_samples(struct iio_image *x, int desired_type)
{
	assert(!x->contiguous_data);
	int source_type = normalize_type(x->type);
	if (source_type == desired_type) return;
	IIO_DEBUG("converting from %s to %s\n", iio_strtyp(x->type), iio_strtyp(desired_type));
	int n = iio_image_number_of_samples(x);
	x->data = convert_data(x->data, n, desired_type, source_type);
	x->type = desired_type;
}

static void iio_hacky_colorize(struct iio_image *x, int pd)
{
	assert(!x->contiguous_data);
	if (x->pixel_dimension != 1)
		fail("please, do not colorize color stuff");
	int n = iio_image_number_of_elements(x);
	int ss = iio_image_sample_size(x);
	void *rdata = xmalloc(n*ss*pd);
	char *data_dest = rdata;
	char *data_src = x->data;
	FORI(n)
		FORJ(pd)
			memcpy(data_dest + (pd*i + j)*ss, data_src + i*ss, ss);
	xfree(x->data);
	x->data=rdata;
	x->pixel_dimension = pd;
}

// uncolorize
static void iio_hacky_uncolorize(struct iio_image *x)
{
	assert(!x->contiguous_data);
	if (x->pixel_dimension != 3)
		fail("please, do not uncolorize non-color stuff");
	assert(x->pixel_dimension == 3);
	int source_type = normalize_type(x->type);
	int n = iio_image_number_of_elements(x);
	switch(source_type) {
	case IIO_TYPE_UINT8: {
		uint8_t (*xd)[3] = x->data;
		uint8_t *r = xmalloc(n*sizeof*r);
		FORI(n)
			r[i] = .299*xd[i][0] + .587*xd[i][1] + .114*xd[i][2];
		xfree(x->data);
		x->data = r;
		}
		break;
	case IIO_TYPE_FLOAT: {
		float (*xd)[3] = x->data;
		float *r = xmalloc(n*sizeof*r);
		FORI(n)
			r[i] = .299*xd[i][0] + .587*xd[i][1] + .114*xd[i][2];
		xfree(x->data);
		x->data = r;
		}
		break;
	default: fail("uncolorize type not supported");
	}
	x->pixel_dimension = 1;
}

// uncolorize
static void iio_hacky_uncolorizea(struct iio_image *x)
{
	assert(!x->contiguous_data);
	if (x->pixel_dimension != 4)
		fail("please, do not uncolorizea non-colora stuff");
	assert(x->pixel_dimension == 4);
	int source_type = normalize_type(x->type);
	int n = iio_image_number_of_elements(x);
	switch(source_type) {
	case IIO_TYPE_UINT8: {
		uint8_t (*xd)[4] = x->data;
		uint8_t *r = xmalloc(n*sizeof*r);
		FORI(n)
			r[i] = .299*xd[i][0] + .587*xd[i][1] + .114*xd[i][2];
		xfree(x->data);
		x->data = r;
		}
		break;
	case IIO_TYPE_FLOAT: {
		float (*xd)[4] = x->data;
		float *r = xmalloc(n*sizeof*r);
		FORI(n)
			r[i] = .299*xd[i][0] + .587*xd[i][1] + .114*xd[i][2];
		xfree(x->data);
		x->data = r;
		}
		break;
	default: fail("uncolorizea type not supported");
	}
	x->pixel_dimension = 1;
}




// general memory and file utilities {{{1


// Input: a partially read stream "f"
// (of which "bufn" bytes are already read into "buf")
//
// Output: a malloc'd block with the whole file content
//
// Implementation: re-invent the wheel
static void *load_rest_of_file(long *on, FILE *f, void *buf, size_t bufn)
{
	size_t n = bufn, ntop = n + 0x3000;
	char *t =  xmalloc(ntop);
	if (!t) fail("out of mem (%zu) while loading file", ntop);
	memcpy(t, buf, bufn);
	while (1) {
		if (n >= ntop) {
			ntop = 1000 + 2*(ntop + 1);
			t = xrealloc(t, ntop);
			if (!t) fail("out of mem (%zu) loading file", ntop);

		}
		int r = fgetc(f);
		if (r == EOF)
			break;
		t[n] = r;//iw810
		n += 1;
	}
	*on = n;
	return t;
}

// Input: a pointer to raw data
//
// Output: the name of a temporary file containing the data
//
// Implementation: re-invent the wheel
static char *put_data_into_temporary_file(void *filedata, size_t filesize)
{
#ifdef I_CAN_HAS_MKSTEMP
	static char filename[] = "/tmp/iio_temporal_file_XXXXXX\0";
	int r = mkstemp(filename);
	if (r == -1) fail("caca [pditf]");
#else
	// WARNING XXX XXX XXX ERROR FIXME TODO WARNING:
	// this function is not reentrant
	static char buf[L_tmpnam+1];
	//
	// from TMPNAM(3):
	//
	//The  tmpnam()  function  returns  a pointer to a string that is a
	//valid filename, and such that a file with this name did  not  exist
	//at  some point  in  time, so that naive programmers may think it a
	//suitable name for a temporary file.
	//
	char *filename = tmpnam(buf);
	// MULTIPLE RACE CONDITIONS HERE
#endif
	FILE *f = xfopen(filename, "w");
	int cx = fwrite(filedata, filesize, 1, f);
	if (cx != 1) fail("fwrite to temporary file failed");
	xfclose(f);
	return filename;
}

static void delete_temporary_file(char *filename)
{
	(void)filename;
#ifdef NDEBUG
	remove(filename);
#else
	IIO_DEBUG("WARNING: kept temporary file %s around\n", filename);
#endif
}


// Allows read access to memory via a FILE*
// Always returns a valid FILE*
static FILE *iio_fmemopen(void *data, size_t size)
{
#ifdef I_CAN_HAS_FMEMOPEN // GNU case
	FILE *r = fmemopen(data, size, "r");
	if (!r) fail("fmemopen failed");
	return r;
#elif  I_CAN_HAS_FUNOPEN // BSD case
	fail("implement fmemopen using funopen here");
#else // portable case
	FILE *f = tmpfile();
	if (!f) fail("tmpfile failed");
	int cx = fwrite(data, size, 1, f);
	if (cx != 1) fail("fwrite failed");
	rewind(f);
	return f;
#endif // I_CAN_HAS_...
}


// beautiful hack follows
static void *matrix_build(int w, int h, size_t n)
{
	size_t p = sizeof(void *);
	char *r = xmalloc(h*p + w*h*n);
	for (int i = 0; i < h; i++)
		*(void **)(r + i*p) = r + h*p + i*w*n;
	return r;
}

static void *wrap_2dmatrix_around_data(void *x, int w, int h, size_t s)
{
	void *r = matrix_build(w, h, s);
	char *y = h*sizeof(void *) + (char *)r;
	memcpy(y, x, w*h*s);
	xfree(x);
	return r;
}



// todo make this function more general, or a front-end to a general
// "data trasposition" routine
static void break_pixels_float(float *broken, float *clear, int n, int pd)
{
	//fprintf(stderr, "breaking %d %d-dimensional vectors\n", n, pd);
	FORI(n) FORL(pd)
		broken[n*l + i] = clear[pd*i + l];
}

static void
recover_broken_pixels_float(float *clear, float *broken, int n, int pd)
{
	//fprintf(stderr, "unbreaking %d %d-dimensional vectors\n", n, pd);
	FORL(pd) FORI(n)
		clear[pd*i + l] = broken[n*l + i];
}

// individual format readers {{{1
// PNG reader {{{2

static void swap_two_bytes(char *here)
{
	char tmp = here[0];
	here[0] = here[1];
	here[1] = tmp;
}

#ifdef I_CAN_HAS_LIBPNG
//#include <png.h>
#include <limits.h> // for CHAR_BIT only
static int read_beheaded_png(struct iio_image *x,
		FILE *f, char *header, int nheader)
{
	(void)header;
	// TODO: reorder this mess
	png_structp pp = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
	if (!pp) fail("png_create_read_struct fail");
	png_infop pi = png_create_info_struct(pp);
	if (!pi) fail("png_create_info_struct fail");
	if (setjmp(png_jmpbuf(pp))) fail("png error");
	png_init_io(pp, f);
	png_set_sig_bytes(pp, nheader);
	int transforms = PNG_TRANSFORM_IDENTITY
			| PNG_TRANSFORM_PACKING
			| PNG_TRANSFORM_EXPAND
			;
	png_read_png(pp, pi, transforms, NULL);
	png_uint_32 w, h;
	int channels, rowbytes;
	int depth, color;//, interl, compr, filt;
	w = png_get_image_width(pp, pi);
	h = png_get_image_height(pp, pi);
	channels = png_get_channels(pp, pi);
	rowbytes = png_get_rowbytes(pp, pi);
	depth = png_get_bit_depth(pp, pi);
	color = png_get_color_type(pp, pi);
	IIO_DEBUG("png get width = %d\n", (int)w);
	IIO_DEBUG("png get height = %d\n", (int)h);
	IIO_DEBUG("png get channels = %d\n", channels);
	IIO_DEBUG("png get rowbytes = %d\n", rowbytes);
	IIO_DEBUG("png get depth = %d\n", depth);
	IIO_DEBUG("png get color = %d\n", color);
	int sizes[2] = {w, h};
	png_bytepp rows = png_get_rows(pp, pi);
	x->format = IIO_FORMAT_PNG;
	x->meta = -42;
	switch (depth) {
	case 1:
	case 8:
		IIO_DEBUG("first byte = %d\n", (int)rows[0][0]);
		iio_image_build_independent(x,2,sizes,IIO_TYPE_CHAR,channels);
		FORJ(h) FORI(w) FORL(channels) {
			char *data = x->data;
			png_byte *b = rows[j] + i*channels + l;
			data[(i+j*w)*channels+l] = *b;
		}
		x->type = IIO_TYPE_CHAR;
		break;
	case 16:
		iio_image_build_independent(x,2,sizes,IIO_TYPE_UINT16,channels);
		FORJ(h) FORI(w) FORL(channels) {
			uint16_t *data = x->data;
			png_byte *b = rows[j] + 2*(i*channels + l);
			swap_two_bytes((char*)b);
			uint16_t *tb = (uint16_t *)b;
			data[(i+j*w)*channels+l] = *tb;
		}
		x->type = IIO_TYPE_UINT16;
		break;
	default: fail("unsuported bit depth %d", depth);
	}
	png_destroy_read_struct(&pp, &pi, NULL);
	return 0;
}

#endif//I_CAN_HAS_LIBPNG

// JPEG reader {{{2

#ifdef I_CAN_HAS_LIBJPEG
#  include <jpeglib.h>

static int read_whole_jpeg(struct iio_image *x, FILE *f)
{
	// allocate and initialize a JPEG decompression object
	struct jpeg_decompress_struct cinfo[1];
	struct jpeg_error_mgr jerr[1];
	cinfo->err = jpeg_std_error(jerr);
	jpeg_create_decompress(cinfo);

	// specify the source of the compressed data
	jpeg_stdio_src(cinfo, f);

	// obtain image info
	jpeg_read_header(cinfo, 1);
	int size[2], depth;
	size[0] = cinfo->image_width;
	size[1] = cinfo->image_height;
	depth = cinfo->num_components;
	IIO_DEBUG("jpeg header widht = %d\n", size[0]);
	IIO_DEBUG("jpeg header height = %d\n", size[1]);
	IIO_DEBUG("jpeg header colordepth = %d\n", depth);
	iio_image_build_independent(x, 2, size, IIO_TYPE_CHAR, depth);

	// set parameters for decompression
	// cinfo->do_fancy_upsampling = 0;
	// colorspace selection, etc

	// start decompress
	jpeg_start_decompress(cinfo);
	assert(size[0] == (int)cinfo->output_width);
	assert(size[1] == (int)cinfo->output_height);
	assert(depth == cinfo->out_color_components);
	assert(cinfo->output_components == cinfo->out_color_components);

	// read scanlines
	FORI(size[1]) {
		void *wheretoputit = i*depth*size[0] + (char *)x->data;
		JSAMPROW scanline[1] = { wheretoputit };
		int r = jpeg_read_scanlines(cinfo, scanline, 1);
		if (1 != r) fail("failed to rean jpeg scanline %d", i);
	}

	// finish decompress
	jpeg_finish_decompress(cinfo);

	// release the JPEG decompression object
	jpeg_destroy_decompress(cinfo);

	return 0;
}

static int read_beheaded_jpeg(struct iio_image *x,
		FILE *fin, char *header, int nheader)
{
	long filesize;
	// TODO: if "f" is rewindable, rewind it!
	void *filedata = load_rest_of_file(&filesize, fin, header, nheader);
	FILE *f = iio_fmemopen(filedata, filesize);

	int r = read_whole_jpeg(x, f);
	if (r) fail("read whole jpeg returned %d", r);
	fclose(f);
	xfree(filedata);

	return 0;
}
#endif//I_CAN_HAS_LIBJPEG

// TIFF reader {{{2

#ifdef I_CAN_HAS_LIBTIFF
#  include <tiffio.h>

static int read_whole_tiff(struct iio_image *x, const char *filename)
{
	// tries to read data in the correct format (via scanlines)
	// if it fails, it tries to read ABGR data

	TIFF *tif = TIFFOpen(filename, "r");
	if (!tif) fail("could not open TIFF file \"%s\"", filename);
	uint32_t w, h;
	uint16_t spp, bps, fmt;
	int r = 0, fmt_iio=-1;
	r += TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);
	IIO_DEBUG("tiff get field width %d (r=%d)\n", (int)w, r);
	r += TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h);
	IIO_DEBUG("tiff get field length %d (r=%d)\n", (int)h, r);
	if (r != 2) fail("can not read tiff of unknown size");

	r = TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &spp);
	if(!r) spp=1;
	if(r)IIO_DEBUG("tiff get field spp %d (r=%d)\n", spp, r);

	r = TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bps);
	if(!r) bps=1;
	if(r)IIO_DEBUG("tiff get field bps %d (r=%d)\n", bps, r);

	r = TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &fmt);
	if(!r) fmt = SAMPLEFORMAT_UINT;
	if(r) IIO_DEBUG("tiff get field fmt %d (r=%d)\n", fmt, r);
	//if (r != 5) error("some tiff getfield failed (r=%d)", r);

	// TODO: consider the missing cases (run through PerlMagick's format database)

	IIO_DEBUG("fmt  = %d\n", fmt);
	// set appropriate size and type flags
	if (fmt == SAMPLEFORMAT_UINT) {
		if (1 == bps) fmt_iio = IIO_TYPE_UINT1;
		else if (2 == bps) fmt_iio = IIO_TYPE_UINT2;
		else if (4 == bps) fmt_iio = IIO_TYPE_UINT4;
		else if (8 == bps) fmt_iio = IIO_TYPE_UINT8;
		else if (16 == bps) fmt_iio = IIO_TYPE_UINT16;
		else if (32 == bps) fmt_iio = IIO_TYPE_UINT32;
		else fail("unrecognized UINT type of size %d bits", bps);
	} else if (fmt == SAMPLEFORMAT_INT) {
		if (8 == bps) fmt_iio = IIO_TYPE_INT8;
		else if (16 == bps) fmt_iio = IIO_TYPE_INT16;
		else if (32 == bps) fmt_iio = IIO_TYPE_INT32;
		else fail("unrecognized INT type of size %d bits", bps);
	} else if (fmt == SAMPLEFORMAT_IEEEFP) {
		IIO_DEBUG("floating tiff!\n");
		if (32 == bps) fmt_iio = IIO_TYPE_FLOAT;
		else if (64 == bps) fmt_iio = IIO_TYPE_DOUBLE;
		else fail("unrecognized FLOAT type of size %d bits", bps);
	} else fail("unrecognized tiff sample format %d (see tiff.h)", fmt);

	if (bps >= 8 && bps != 8*iio_type_size(fmt_iio)) {
		IIO_DEBUG("bps = %d\n", bps);
		IIO_DEBUG("fmt_iio = %d\n", fmt_iio);
		IIO_DEBUG("ts = %zu\n", iio_type_size(fmt_iio));
		IIO_DEBUG("8*ts = %zu\n", 8*iio_type_size(fmt_iio));
	}
	if (bps >= 8) assert(bps == 8*iio_type_size(fmt_iio));


	// acquire memory block
	uint32_t scanline_size = (w * spp * bps)/8;
	int rbps = bps/8 ? bps/8 : 1;
	uint32_t uscanline_size = w * spp * rbps;
	IIO_DEBUG("bps = %d\n", (int)bps);
	IIO_DEBUG("spp = %d\n", (int)spp);
	IIO_DEBUG("sls = %d\n", (int)scanline_size);
	int sls = TIFFScanlineSize(tif);
	IIO_DEBUG("sls(r) = %d\n", (int)sls);
	assert((int)scanline_size == sls);
	scanline_size = sls;
	uint8_t *data = xmalloc(w * h * spp * rbps);
	//FORI(h*scanline_size) data[i] = 42;
	uint8_t *buf = xmalloc(scanline_size);

	// dump scanline data
	FORI(h) {
		r = TIFFReadScanline(tif, buf, i, 0);
		if (r < 0) fail("error reading tiff row %d/%d", i, (int)h);

		if (bps < 8) {
			fprintf(stderr, "unpacking %dth scanline\n", i);
			unpack_to_bytes_here(data + i*uscanline_size, buf,
					scanline_size, bps);
			fmt_iio = IIO_TYPE_UINT8;
		} else {
			memcpy(data + i*scanline_size, buf, scanline_size);
		}
	}
	TIFFClose(tif);


	xfree(buf);

	// fill struct fields
	x->dimension = 2;
	x->sizes[0] = w;
	x->sizes[1] = h;
	x->pixel_dimension = spp;
	x->type = fmt_iio;
	x->format = x->meta = -42;
	x->data = data;
	x->contiguous_data = false;
	return 0;
}

// Note: TIFF library only reads from a named file.  The only way to use the
// library if you have your image in a stream, is to write the data to a
// temporary file, and read it from there.  This can be optimized in the future
// by recovering the original filename through hacks, in case it was not a pipe.
static int read_beheaded_tiff(struct iio_image *x,
		FILE *fin, char *header, int nheader)
{
	if (global_variable_containing_the_name_of_the_last_opened_file) {
		int r = read_whole_tiff(x,
		global_variable_containing_the_name_of_the_last_opened_file);
		if (r) fail("read whole tiff returned %d", r);
		return 0;
	}

	long filesize;
	void *filedata = load_rest_of_file(&filesize, fin, header, nheader);
	char *filename = put_data_into_temporary_file(filedata, filesize);
	xfree(filedata);


	int r = read_whole_tiff(x, filename);
	if (r) fail("read whole tiff returned %d", r);

	delete_temporary_file(filename);

	return 0;
}

#endif//I_CAN_HAS_LIBTIFF

// QNM readers {{{2

#include <ctype.h>

static void llegeix_floats_en_bytes(FILE *don, float *on, int quants)
{
	for (int i = 0; i < quants; i++)
	{
		float c;
		c = pilla_caracter_segur(don);//iw810
		on[i] = c;
	}
}

static void llegeix_floats_en_shorts(FILE *don, float *on, int quants)
{
	for (int i = 0; i < quants; i++)
	{
		float c;
		c = pilla_caracter_segur(don);
		c *= 256;
		c += pilla_caracter_segur(don);
		on[i] = c;
	}
}

static void llegeix_floats_en_ascii(FILE *don, float *on, int quants)
{
	for (int i = 0; i < quants; i++)
	{
		int r;
		float c;
		r = fscanf(don, "%f ", &c);
		if (r != 1)
			fail("no s'han pogut llegir %d numerets del fitxer &%p\n",
					quants, (void *)don);
		on[i] = c;
	}
}

static int read_qnm_numbers(float *data, FILE *f, int n, int m, bool use_ascii)
{
	if (use_ascii)
		llegeix_floats_en_ascii(f, data, n);
	else {
		if (m < 0x100)
			llegeix_floats_en_bytes(f, data, n);
		else if (m < 0x10000)
			llegeix_floats_en_shorts(f, data, n);
		else
			fail("too large maxval %d", m);
	}
	// TODO: error checking
	return n;
}

// qnm_types:
//  2 P2 (ascii  2d grayscale pgm)
//  5 P5 (binary 2d grayscale pgm)
//  3 P3 (ascii  2d color     ppm)
//  6 P6 (binary 2d color     ppm)
// 12 Q2 (ascii  3d grayscale pgm)
// 15 Q5 (binary 3d grayscale pgm)
// 13 Q3 (ascii  3d color     ppm)
// 16 Q6 (binary 3d color     ppm)
// 17 Q7 (ascii  3d nd           )
// 19 Q9 (binary 3d nd           )
static int read_beheaded_qnm(struct iio_image *x,
		FILE *f, char *header, int nheader)
{
	assert(nheader == 2);
	(void)nheader;
	int w, h, d = 1, m, pd = 1;
	int c1 = header[0];
	int c2 = header[1] - '0';
	IIO_DEBUG("QNM reader (%c %d)...\n", c1, c2);
	menja_espais_i_comentaris(f);
	if (1 != fscanf(f, "%d", &w)) return -1;
	menja_espais_i_comentaris(f);
	if (1 != fscanf(f, "%d", &h)) return -2;
	if (c1 == 'Q') {
		if (1 != fscanf(f, "%d", &d)) return -3;
		menja_espais_i_comentaris(f);
	}
	if (c2 == 7 || c2 == 9) {
		if (1 != fscanf(f, "%d", &pd)) return -4;
		menja_espais_i_comentaris(f);
	}
	if (1 != fscanf(f, "%d", &m)) return -5;
	// maxval is ignored and the image is always read into floats
	if (!isspace(pilla_caracter_segur(f))) return -6;

	bool use_ascii = (c2 == 2 || c2 == 3 || c2 == 7);
	bool use_2d = (d == 1); if (!use_2d) assert(c1 == 'Q');
	if (c2 == 3 || c2 == 6)
		pd = 3;
	size_t nn = w * h * d * pd; // number of numbers
	float *data = xmalloc(nn * sizeof*data);
	IIO_DEBUG("QNM reader w = %d\n", w);
	IIO_DEBUG("QNM reader h = %d\n", h);
	IIO_DEBUG("QNM reader d = %d\n", d);
	IIO_DEBUG("QNM reader pd = %d\n", pd);
	IIO_DEBUG("QNM reader m = %d\n", m);
	IIO_DEBUG("QNM reader use_2d = %d\n", use_2d);
	IIO_DEBUG("QNM reader use_ascii = %d\n", use_ascii);
	int r = read_qnm_numbers(data, f, nn, m, use_ascii);
	if (nn - r) return -7;

	x->dimension = use_2d ? 2 : 3;
	x->sizes[0] = w;
	x->sizes[1] = h;
	if (d > 1) x->sizes[2] = d;
	x->pixel_dimension = pd;
	x->type = IIO_TYPE_FLOAT;
	x->contiguous_data = false;
	x->data = data;
	return 0;
}

// PCM reader {{{2
// PCM is a file format to store complex float images
// it is used by some people also for optical flow fields
static int read_beheaded_pcm(struct iio_image *x,
		FILE *f, char *header, int nheader)
{
	(void)header;
	assert(nheader == 2);
	int w, h;
	float scale;
	//menja_espais_i_comentaris(f);
	if (1 != fscanf(f, " %d", &w)) return -1;
	//menja_espais_i_comentaris(f);
	if (1 != fscanf(f, " %d", &h)) return -2;
	//menja_espais_i_comentaris(f);
	if (1 != fscanf(f, " %g", &scale)) return -3;
	if (!isspace(pilla_caracter_segur(f))) return -6;

	fprintf(stderr, "%d PCM w h scale = %d %d %g\n", nheader, w, h, scale);

	assert(sizeof(float) == 4);
	float *data = xmalloc(w * h * 2 * sizeof(float));
	int r = fread(data, sizeof(float), w * h * 2, f);
	if (r != w * h * 2) return -7;
	x->dimension = 2;
	x->sizes[0] = w;
	x->sizes[1] = h;
	x->pixel_dimension = 2;
	x->type = IIO_TYPE_FLOAT;
	x->contiguous_data = false;
	x->data = data;
	return 0;
}


// RIM reader {{{2


// CIMAGE header bytes: ("IMG" format)
// short 0: 'MI'
// short 1: comment length
// short 2: image width
// short 3: image height
//

// FIMAGE header bytes: ("RIM" format)
// short 0: 'IR'
// short 1: comment length
// short 2: image width
// short 3: image height
//

// CCIMAGE header bytes: ("MTI" format)
//
//
//

static uint16_t rim_getshort(FILE *f, bool swp)
{
	int a = pilla_caracter_segur(f);
	int b = pilla_caracter_segur(f);
	IIO_DEBUG("rgs %.2x%.2x\n", a, b);
	assert(a >= 0);
	assert(b >= 0);
	assert(a < 256);
	assert(b < 256);
	uint16_t r = swp ? b * 0x100 + a : a * 0x100 + b;
	return r;
}

// Note: different order than for shorts.
// Fascinating braindeadness.
static uint32_t rim_getint(FILE *f, bool swp)
{
	int a = pilla_caracter_segur(f);
	int b = pilla_caracter_segur(f);
	int c = pilla_caracter_segur(f);
	int d = pilla_caracter_segur(f);
	IIO_DEBUG("rgi %.2x%.2x %.2x%.2x\n", a, b, c, d);
	assert(a >= 0); assert(b >= 0); assert(c >= 0); assert(d >= 0);
	assert(a < 256); assert(b < 256); assert(c < 256); assert(d < 256);
	uint32_t r = swp ?
		a*0x1000000 + b*0x10000 + c*0x100 + d:
		d*0x1000000 + c*0x10000 + b*0x100 + a;
	return r;

}

static int read_beheaded_rim_cimage(struct iio_image *x, FILE *f, bool swp)
{
	uint16_t lencomm = rim_getshort(f, swp);
	uint16_t dx = rim_getshort(f, swp);
	uint16_t dy = rim_getshort(f, swp);
	IIO_DEBUG("RIM READER lencomm = %d\n", (int)lencomm);
	IIO_DEBUG("RIM READER dx = %d\n", (int)dx);
	IIO_DEBUG("RIM READER dy = %d\n", (int)dy);
	FORI(28) rim_getshort(f, swp); // skip shit (ascii numbers and zeros)
	FORI(lencomm) {
		int c = pilla_caracter_segur(f); // skip further shit (comments)
		(void)c;
		IIO_DEBUG("RIM READER comment[%d] = '%c'\n", i, c);
	}
	float *data = xmalloc(dx * dy);
	size_t r = fread(data, 1, dx*dy, f);
	if (r != (size_t)(dx*dy))
		fail("could not read entire RIM file:\n"
				"expected %zu chars, but got only %zu",
				(size_t)dx*dy, r);
	int s[2] = {dx, dy};
	iio_wrap_image_struct_around_data(x, 2, s, 1, IIO_TYPE_UINT8, data);
	return 0;
}

static void byteswap4(void *data, int n)
{
	char *t = data;
	FORI(n) {
		char *t4 = t + 4*i;
		char tt[4] = {t4[3], t4[2], t4[1], t4[0]};
		FORL(4) t4[l] = tt[l];
	}
}

static int read_beheaded_rim_fimage(struct iio_image *x, FILE *f, bool swp)
{
	IIO_DEBUG("rim reader fimage swp = %d", swp);
	uint16_t lencomm = rim_getshort(f, swp);
	uint16_t dx = rim_getshort(f, swp);
	uint16_t dy = rim_getshort(f, swp);
	//IIO_DEBUG("RIM READER lencomm = %d\n", (int)lencomm);
	//IIO_DEBUG("RIM READER dx = %d\n", (int)dx);
	//IIO_DEBUG("RIM READER dy = %d\n", (int)dy);
	FORI(28) rim_getshort(f, swp); // skip shit (ascii numbers and zeros)
	FORI(lencomm) {
		int c = pilla_caracter_segur(f); // skip further shit (comments)
		(void)c;
		//IIO_DEBUG("RIM READER comment[%d] = '%c'\n", i, c);
	}
	// now, read dx*dy floats
	float *data = xmalloc(dx * dy * sizeof*data);
	size_t r = fread(data, sizeof*data, dx*dy, f);
	if (r != (size_t)(dx*dy))
		fail("could not read entire RIM file:\n"
				"expected %zu floats, but got only %zu",
				(size_t)dx*dy, r);
	assert(sizeof(float) == 4);
	if (swp) byteswap4(data, r);
	int s[2] = {dx, dy};
	iio_wrap_image_struct_around_data(x, 2, s, 1, IIO_TYPE_FLOAT, data);
	return 0;
}

static int read_beheaded_rim_ccimage(struct iio_image *x, FILE *f, bool swp)
{
	uint16_t iv = rim_getshort(f, swp);
	if (iv != 0x4956 && iv != 0x5649 && iv != 0x4557 && iv != 0x5745)
		fail("bad ccimage header %x",(int)iv);
	uint32_t pm_np = rim_getint(f, swp);
	uint32_t pm_nrow = rim_getint(f, swp);
	uint32_t pm_ncol = rim_getint(f, swp);
	uint32_t pm_band = rim_getint(f, swp);
	uint32_t pm_form = rim_getint(f, swp);
	uint32_t pm_cmtsize = rim_getint(f, swp);

	IIO_DEBUG("RIM READER pm_np = %d\n", (int)pm_np);
	IIO_DEBUG("RIM READER pm_nrow = %d\n", (int)pm_nrow);
	IIO_DEBUG("RIM READER pm_ncol = %d\n", (int)pm_ncol);
	IIO_DEBUG("RIM READER pm_band = %d\n", (int)pm_band);
	IIO_DEBUG("RIM READER pm_form = %x\n", (int)pm_form);
	IIO_DEBUG("RIM READER pm_cmtsize = %d\n", (int)pm_cmtsize);
	uint32_t nsamples = pm_np * pm_nrow * pm_ncol;
	if (pm_form == 0x8001) { // ccimage
		uint8_t *data = xmalloc(nsamples);
		size_t r = fread(data, 1, nsamples, f);
		if (r != nsamples) fail("rim reader failed at reading %zu "
			"samples (got only %zu)\n", (size_t)nsamples, r);
		uint8_t *good_data = xmalloc(nsamples);
		FORJ(pm_nrow) FORI(pm_ncol) FORL(pm_np)
			good_data[l+(i+j*pm_ncol)*pm_np] =
			data[i + j*pm_ncol + l*pm_ncol*pm_nrow];
		xfree(data); data = good_data;
		int s[2] = {pm_ncol, pm_nrow};
		iio_wrap_image_struct_around_data(x, 2, s, pm_np, IIO_TYPE_UINT8, data);
	} else if (pm_form == 0xc004) { // cfimage
		float *data = xmalloc(4*nsamples);
		size_t r = fread(data, 4, nsamples, f);
		if (r != nsamples) fail("rim reader failed at reading %zu "
			"samples (got only %zu)\n", (size_t)nsamples, r);
		float *good_data = xmalloc(4*nsamples);
		FORJ(pm_nrow) FORI(pm_ncol) FORL(pm_np)
			good_data[l+(i+j*pm_ncol)*pm_np] =
			data[i + j*pm_ncol + l*pm_ncol*pm_nrow];
		xfree(data); data = good_data;
		int s[2] = {pm_ncol, pm_nrow};
		iio_wrap_image_struct_around_data(x, 2, s, pm_np, IIO_TYPE_FLOAT, data);
	} else
		fail("unsupported PM_form %x", pm_form);
	return 0;
}

static int read_beheaded_rim(struct iio_image *x, FILE *f, char *h, int nh)
{
	assert(nh == 2);(void)nh;
	if (h[0] == 'I' && h[1] == 'R')
		return read_beheaded_rim_fimage(x, f, false);
	if (h[0] == 'R' && h[1] == 'I')
		return read_beheaded_rim_fimage(x, f, true);

	if (h[0] == 'M' && h[1] == 'I')
		return read_beheaded_rim_cimage(x, f, false);
	if (h[0] == 'I' && h[1] == 'M')
		return read_beheaded_rim_cimage(x, f, true);

	if (h[0] == 'W' && h[1] == 'E')
		return read_beheaded_rim_ccimage(x, f, false);
	if (h[0] == 'V' && h[1] == 'I')
		return read_beheaded_rim_ccimage(x, f, true);
	return 1;
}
static void switch_4endianness(void *tt, int n)
{
	char *t = tt;
	FORI(n) {
		char tmp[4] = {t[0], t[1], t[2], t[3]};
		t[0] = tmp[3];
		t[1] = tmp[2];
		t[2] = tmp[1];
		t[3] = tmp[0];
		t += 4;
	}
}

// PFM reader {{{2
static int read_beheaded_pfm(struct iio_image *x,
		FILE *f, char *header, int nheader)
{
	assert(4 == sizeof(float));
	assert(nheader == 2);(void)nheader;
	assert('f' == tolower(header[1]));
	int w, h, pd = isupper(header[1]) ? 3 : 1;
	float scale;
	if (!isspace(pilla_caracter_segur(f))) return -1;
	if (3 != fscanf(f, "%d %d\n%g", &w, &h, &scale)) return -2;
	if (!isspace(pilla_caracter_segur(f))) return -3;
	float *data = xmalloc(w*h*4*pd);
	if (1 != fread(data, w*h*4*pd, 1, f)) return -4;
	//if (scale < 0) switch_4endianness(data, w*h*pd);

	x->dimension = 2;
	x->sizes[0] = w;
	x->sizes[1] = h;
	x->pixel_dimension = pd;
	x->type = IIO_TYPE_FLOAT;
	x->contiguous_data = false;
	x->data = data;
	return 0;
}

// FLO reader {{{2
static int read_beheaded_flo(struct iio_image *x,
		FILE *f, char *header, int nheader)
{
	(void)header; (void)nheader;
	int w = rim_getint(f, false);
	int h = rim_getint(f, false);
	float *data = xmalloc(w*h*2*sizeof*data);
	if (1 != fread(data, w*h*4*2, 1, f)) return -1;

	x->dimension = 2;
	x->sizes[0] = w;
	x->sizes[1] = h;
	x->pixel_dimension = 2;
	x->type = IIO_TYPE_FLOAT;
	x->contiguous_data = false;
	x->data = data;
	return 0;
}

// JUV reader {{{2
static int read_beheaded_juv(struct iio_image *x,
		FILE *f, char *header, int nheader)
{
	char buf[255];
	FORI(nheader) buf[i] = header[i];
	FORI(255-nheader) buf[i+nheader] = pilla_caracter_segur(f);
	int w, h, r = sscanf(buf, "#UV {\n dimx %d dimy %d\n}\n", &w, &h);
	if (r != 2) return -1;
	size_t sf = sizeof(float);
	float *u = xmalloc(w*h*sf); r = fread(u, sf, w*h, f);if(r!=w*h)return-2;
	float *v = xmalloc(w*h*sf); r = fread(v, sf, w*h, f);if(r!=w*h)return-2;
	float *uv = xmalloc(2*w*h*sf);
	FORI(w*h) uv[2*i] = u[i];
	FORI(w*h) uv[2*i+1] = v[i];
	xfree(u); xfree(v);
	x->dimension = 2;
	x->sizes[0] = w;
	x->sizes[1] = h;
	x->pixel_dimension = 2;
	x->type = IIO_TYPE_FLOAT;
	x->contiguous_data = false;
	x->data = uv;
	return 0;
}

// LUM reader {{{2

static int lum_pickshort(char *ss)
{
	uint8_t *s = (uint8_t*)ss;
	int a = s[0];
	int b = s[1];
	return 0x100*a + b;
}

static int read_beheaded_lum(struct iio_image *x,
		FILE *f, char *header, int nheader)
{
	int w = lum_pickshort(header+2);
	int h = lum_pickshort(header+6);
	while (nheader++ < 0xf94)
		pilla_caracter_segur(f);
	float *data = xmalloc(w*h*sizeof*data);
	if (1 != fread(data, w*h*4, 1, f)) return -1;
	switch_4endianness(data, w*h);
	x->dimension = 2;
	x->sizes[0] = w;
	x->sizes[1] = h;
	x->pixel_dimension = 1;
	x->type = IIO_TYPE_FLOAT;
	x->contiguous_data = false;
	x->data = data;
	return 0;
}

// BMP reader {{{2
static int read_beheaded_bmp(struct iio_image *x,
		FILE *f, char *header, int nheader)
{
	long len;
	char *bmp = load_rest_of_file(&len, f, header, nheader);
	(void)bmp; (void)x;
	//uint32_t pix_offset = *(uint32_t*)(bmp+0xa);

	fail("BMP reader not yet finished");

	xfree(bmp);

	//x->dimension = 2;
	//x->sizes[0] = dib_width;
	//x->sizes[1] = dib_height;
	//x->pixel_dimension = 1;
	//x->type = IIO_TYPE_FLOAT;
	//x->contiguous_data = false;
	//x->data = data;
	return 0;
	//fprintf(stderr, "bmp reader\n");
	//uint32_t aoffset = *(uint32_t*)(header+0xa);
	//fprintf(stderr, "aoffset = %d\n", aoffset);
	//assert(nheader == 14); // ignore the file header
	//uint32_t dib_size = rim_getint(f, false);
	//fprintf(stderr, "dib_size = %d\n", dib_size);
	//if (dib_size != 40) error("only windows-like bitmaps ara supported");
	//int32_t dib_width = rim_getint(f, false);
	//int32_t dib_height = rim_getint(f, false);
	//uint16_t dib_planes = rim_getshort(f, true);
	//uint16_t dib_bpp = rim_getshort(f, true);
	//uint32_t dib_compression = rim_getint(f, false);
	//fprintf(stderr, "w,h = %dx%d, bpp=%d (p=%d), compression=%d\n",
	//		dib_width, dib_height,
	//		dib_bpp, dib_planes, dib_compression);
	////if (dib_planes != 1) error("BMP READER: only one plane is supported (not %d)", dib_planes);
	//if (dib_compression) error("compressed BMP are not supported");
	//uint32_t dib_imsize = rim_getint(f, false);
	//int32_t dib_hres = rim_getint(f, false);
	//int32_t dib_vres = rim_getint(f, false);
	//uint32_t dib_ncolors = rim_getint(f, false);
	//uint32_t dib_nicolors = rim_getint(f, false);
	//fprintf(stderr, "ncolors = %d\n", dib_ncolors);
	//error("fins aquí hem arribat!");
}


// EXR reader {{{2

#ifdef I_CAN_HAS_LIBEXR
#include <ImfCRgbaFile.h>
// EXTERNALIZED TO :  read_exr_float.cpp
//extern int read_whole_exr(struct iio_image *x, char *filename);

static int read_whole_exr(struct iio_image *x, char *filename)
{
	struct ImfInputFile *f = ImfOpenInputFile(filename);
	if (!f) fail("could not read exr from %s", filename);
	int r;

	const char *nom = ImfInputFileName(f);
	IIO_DEBUG("ImfInputFileName returned %s\n", nom);

	r = ImfInputChannels(f);
	IIO_DEBUG("ImfInputChannels returned %d\n", r);
	// this data is only for information.  The file is always
	// converted to RGB
	//if (r != IMF_WRITE_RGBA)
	//	error("only RGBA EXR supported so far (got %d)", r);

	const struct ImfHeader *header = ImfInputHeader(f);
	int xmin, ymin, xmax, ymax;
	ImfHeaderDataWindow(header, &xmin, &ymin, &xmax, &ymax);
	IIO_DEBUG("xmin ymin xmax ymax = %d %d %d %d\n", xmin,ymin,xmax,ymax);

	int width = xmax - xmin + 1;
	int height = ymax - ymin + 1;
	struct ImfRgba *data = xmalloc(width*height*sizeof*data);

	r = ImfInputSetFrameBuffer(f, data, 1, width);
	IIO_DEBUG("ImfInputSetFrameBuffer returned %d\n", r);

	r = ImfInputReadPixels(f, ymin, ymax);
	IIO_DEBUG("ImfInputReadPixels returned %d\n", r);

	r = ImfCloseInputFile(f);
	IIO_DEBUG("ImfCloseInputFile returned %d\n", r);

	float *finaldata = xmalloc(4*width*height*sizeof*data);
	FORI(width*height) {
		finaldata[4*i+0] = ImfHalfToFloat(data[i].r);
		finaldata[4*i+1] = ImfHalfToFloat(data[i].g);
		finaldata[4*i+2] = ImfHalfToFloat(data[i].b);
		finaldata[4*i+3] = ImfHalfToFloat(data[i].a);
		//IIO_DEBUG("read %d rgb %g %g %g\n", i, finaldata[3*i+0], finaldata[3*i+1], finaldata[3*i+2]);
	}
	xfree(data);

	// fill struct fields
	x->dimension = 2;
	x->sizes[0] = width;
	x->sizes[1] = height;
	x->pixel_dimension = 4;
	x->type = IIO_TYPE_FLOAT;
	x->format = x->meta = -42;
	x->data = finaldata;
	x->contiguous_data = false;
	return 0;
}

// Note: OpenEXR library only reads from a named file.  The only way to use the
// library if you have your image in a stream, is to write the data to a
// temporary file, and read it from there.  This can be optimized in the future
// by recovering the original filename through hacks, in case it was not a pipe.
static int read_beheaded_exr(struct iio_image *x,
		FILE *fin, char *header, int nheader)
{
	if (global_variable_containing_the_name_of_the_last_opened_file) {
		int r = read_whole_exr(x,
		global_variable_containing_the_name_of_the_last_opened_file);
		if (r) fail("read whole tiff returned %d", r);
		return 0;
	}

	long filesize;
	void *filedata = load_rest_of_file(&filesize, fin, header, nheader);
	char *filename = put_data_into_temporary_file(filedata, filesize);
	xfree(filedata);
	//IIO_DEBUG("tmpfile = \"%s\"\n", filename);


	int r = read_whole_exr(x, filename);
	if (r) fail("read whole exr returned %d", r);

	delete_temporary_file(filename);

	return 0;
}

#endif//I_CAN_HAS_LIBEXR

// WHATEVER reader {{{2

//static int read_image(struct iio_image*, const char *);
static int read_image_f(struct iio_image*, FILE *);
static int read_beheaded_whatever(struct iio_image *x,
	       	FILE *fin, char *header, int nheader)
{
	// dump data to file
	long filesize;
	void *filedata = load_rest_of_file(&filesize, fin, header, nheader);
	char *filename = put_data_into_temporary_file(filedata, filesize);
	xfree(filedata);
	//IIO_DEBUG("tmpfile = \"%s\"\n", filename);

	//char command_format[] = "convert - %s < %s\0";
	char command_format[] = "/usr/bin/convert - %s < %s\0";
	char ppmname[strlen(filename)+5];
	sprintf(ppmname, "%s.ppm", filename);
	char command[strlen(command_format)+1+2*strlen(filename)];
	sprintf(command, command_format, ppmname, filename);
	IIO_DEBUG("COMMAND: %s\n", command);
	int r = system(command);
	IIO_DEBUG("command returned %d\n", r);
	if (r) fail("could not run command \"%s\" successfully", command);

	FILE *f = xfopen(ppmname, "r");
	r = read_image_f(x, f);
	xfclose(f);

	delete_temporary_file(filename);
	delete_temporary_file(ppmname);

	return r;
}

// individual format writers {{{1
// PNG writer {{{2

#ifdef I_CAN_HAS_LIBPNG

static void iio_save_image_as_png(const char *filename, struct iio_image *x)
{
	png_structp pp = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0,0,0);
	if (!pp) fail("png_create_write_struct fail");
	png_infop pi = png_create_info_struct(pp);
	if (!pi) fail("png_create_info_struct fail");
	if (setjmp(png_jmpbuf(pp))) fail("png write error");

	if (x->dimension != 2) fail("can only save 2d images");
	int width = x->sizes[0];
	int height = x->sizes[1];
	int bit_depth = 0;
	switch(normalize_type(x->type)) {
	case IIO_TYPE_UINT16:
	case IIO_TYPE_INT16: bit_depth = 16; break;
	case IIO_TYPE_INT8:
	case IIO_TYPE_UINT8: bit_depth = 8; break;
	default: fail("can not yet save samples of type %s as PNG",
				 iio_strtyp(x->type));
	}
	assert(bit_depth > 0);
	int color_type = PNG_COLOR_TYPE_PALETTE;
	switch(x->pixel_dimension) {
	case 1: color_type = PNG_COLOR_TYPE_GRAY; break;
	case 2: color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break;
	case 3: color_type = PNG_COLOR_TYPE_RGB; break;
	case 4: color_type = PNG_COLOR_TYPE_RGB_ALPHA; break;
	default: fail("can not save %d-dimensional samples as PNG",
				 x->pixel_dimension);
	}
	assert(color_type != PNG_COLOR_TYPE_PALETTE);

	FILE *f = xfopen(filename, "w");
	png_init_io(pp, f);

	int ss = bit_depth/8;
	int pd = x->pixel_dimension;
	png_bytepp row = xmalloc(height * sizeof(png_bytep));
	FORI(height) row[i] = i*pd*width*ss + (uint8_t *)x->data;
	png_set_IHDR(pp, pi, width, height, bit_depth, color_type,
			PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
			PNG_FILTER_TYPE_DEFAULT);
	png_set_rows(pp, pi, row);
	int transforms = PNG_TRANSFORM_IDENTITY;
	png_write_png(pp, pi, transforms, NULL);
	xfclose(f);
	png_destroy_write_struct(&pp, &pi);
	xfree(row);
}

#endif//I_CAN_HAS_LIBPNG

// TIFF writer {{{2

#ifdef I_CAN_HAS_LIBTIFF

static void iio_save_image_as_tiff(const char *filename, struct iio_image *x)
{
	//fprintf(stderr, "saving image as tiff file \"%s\"\n", filename);
	if (x->dimension != 2)
		fail("only 2d images can be saved as TIFFs");
	TIFF *tif = TIFFOpen(filename, "w");
	if (!tif) fail("could not open TIFF file \"%s\"", filename);

	int ss = iio_image_sample_size(x);
	int sls = x->sizes[0]*x->pixel_dimension*ss;
	int tsf;

	TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, x->sizes[0]);
	TIFFSetField(tif, TIFFTAG_IMAGELENGTH, x->sizes[1]);
	TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
	TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, x->pixel_dimension);
	TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, ss * 8);
	uint16 caca[1] = {EXTRASAMPLE_UNASSALPHA};
	switch (x->pixel_dimension) {
	case 1:
		TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
		break;
	case 3:
		TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
		break;
	case 2:
		TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
		//TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, 1, caca);
		break;
	case 4:
		TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
		TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, 1, caca);
		break;
	default:
		TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
		//error("bad pixel dimension %d for TIFF", x->pixel_dimension);
	}
	/////TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
	TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_LZW);
	//TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
	switch(x->type) {
	case IIO_TYPE_FLOAT: tsf = SAMPLEFORMAT_IEEEFP; break;
	case IIO_TYPE_INT8:
	case IIO_TYPE_INT16:
	case IIO_TYPE_INT32: tsf = SAMPLEFORMAT_INT; break;
	case IIO_TYPE_UINT8:
	case IIO_TYPE_UINT16:
	case IIO_TYPE_UINT32: tsf = SAMPLEFORMAT_UINT; break;
	default: fail("can not save samples of type %s on tiff file",
				 iio_strtyp(x->type));
	}
	TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, tsf);

	FORI(x->sizes[1]) {
		void *line = i*sls + (char *)x->data;
		int r = TIFFWriteScanline(tif, line, i, 0);
		if (r < 0) fail("error writing %dth TIFF scanline", i);
	}

	TIFFClose(tif);
}

static void iio_save_image_as_tiff_smarter(const char *filename,
		struct iio_image *x)
{
	char *tiffname = strstr(filename, "TIFF:");
	if (tiffname == filename) {
		iio_save_image_as_tiff_smarter(filename+5, x);
		return;
	}
	if (0 == strcmp(filename, "-")) {
#ifdef I_CAN_HAS_MKSTEMP
		static char tfn[] = "/tmp/iio_temporal_tiff_XXXXXX\0";
		int r = mkstemp(tfn);
		if (r == -1) fail("caca [tiff smarter]");
#else
		static char buf[L_tmpnam+1];
		char *tfn = tmpnam(buf);
#endif//I_CAN_HAS_MKSTEMP
		iio_save_image_as_tiff(tfn, x);
		FILE *f = xfopen(tfn, "r");
		int c;
		while ((c = fgetc(f)) != EOF)
			fputc(c, stdout);
		fclose(f);
		delete_temporary_file(tfn);
	} else
		iio_save_image_as_tiff(filename, x);
}

#endif//I_CAN_HAS_LIBTIFF


// JUV writer {{{2
static void iio_save_image_as_juv(const char *filename, struct iio_image *x)
{
	assert(x->type == IIO_TYPE_FLOAT);
	assert(x->dimension == 2);
	char buf[255]; FORI(255) buf[i] = ' ';
	snprintf(buf, 255, "#UV {\n dimx %d dimy %d\n}\n",
			x->sizes[0], x->sizes[1]);
	size_t sf = sizeof(float);
	int w = x->sizes[0];
	int h = x->sizes[1];
	float *uv = x->data;
	float *u = xmalloc(w*h*sf); FORI(w*h) u[i] = uv[2*i];
	float *v = xmalloc(w*h*sf); FORI(w*h) v[i] = uv[2*i+1];
	FILE *f = xfopen(filename, "w");
	fwrite(buf, 1, 255, f);
	fwrite(u, sf, w*h, f);
	fwrite(v, sf, w*h, f);
	xfclose(f);
	xfree(u); xfree(v);
}


// FLO writer {{{2
static void iio_save_image_as_flo(const char *filename, struct iio_image *x)
{
	assert(x->type == IIO_TYPE_FLOAT);
	assert(x->dimension == 2);
	union { char s[4]; float f; } pieh = {"PIEH"};
	assert(sizeof(float) == 4);
	assert(pieh.f == 202021.25);
	uint32_t w = x->sizes[0];
	uint32_t h = x->sizes[1];
	FILE *f = xfopen(filename, "w");
	fwrite(&pieh.f, 4, 1, f);
	fwrite(&w, 4, 1, f);
	fwrite(&h, 4, 1, f);
	fwrite(x->data, 4, w*h*2, f);
	xfclose(f);
}

// RIM writer {{{2

static void rim_putshort(FILE *f, uint16_t n)
{
	int a = n % 0x100;
	int b = n / 0x100;
	assert(a >= 0);
	assert(b >= 0);
	assert(a < 256);
	assert(b < 256);
	fputc(b, f);
	fputc(a, f);
}

static void iio_save_image_as_rim_fimage(const char *fname, struct iio_image *x)
{
	if (x->type != IIO_TYPE_FLOAT) fail("fimage expects float data");
	if (x->dimension != 2) fail("fimage expects 2d image");
	if (x->pixel_dimension != 1) fail("fimage expects gray image");
	FILE *f = xfopen(fname, "w");
	fputc('I', f);
	fputc('R', f);
	rim_putshort(f, 2);
	rim_putshort(f, x->sizes[0]);
	rim_putshort(f, x->sizes[1]);
	FORI(29) rim_putshort(f, 0);
	int r = fwrite(x->data, sizeof(float), x->sizes[0]*x->sizes[1], f);
	if (r != x->sizes[0]*x->sizes[1])
		fail("could not write an entire fimage for some reason");
	xfclose(f);
}

static void iio_save_image_as_rim_cimage(const char *fname, struct iio_image *x)
{
	if (x->type != IIO_TYPE_UINT8) fail("cimage expects byte data");
	if (x->dimension != 2) fail("cimage expects 2d image");
	if (x->pixel_dimension != 1) fail("cimage expects gray image");
	FILE *f = xfopen(fname, "w");
	fputc('M', f);
	fputc('I', f);
	rim_putshort(f, 2);
	rim_putshort(f, x->sizes[0]);
	rim_putshort(f, x->sizes[1]);
	FORI(29) rim_putshort(f, 0);
	int r = fwrite(x->data, 1, x->sizes[0]*x->sizes[1], f);
	if (r != x->sizes[0]*x->sizes[1])
		fail("could not write an entire cimage for some reason");
	xfclose(f);
}

// guess format using magic {{{1


static char add_to_header_buffer(FILE *f, uint8_t *buf, int *nbuf, int bufmax)
{
	int c = pilla_caracter_segur(f);
	if (*nbuf >= bufmax)
		fail("buffer header too small (%d)", bufmax);
	buf[*nbuf] = c;//iw810
	IIO_DEBUG("ATHB[%d] = %x \"%c\"\n", *nbuf, c, c);
	*nbuf += 1;
	return c;
}

static int guess_format(FILE *f, char *buf, int *nbuf, int bufmax)
{
	assert(sizeof(uint8_t)==sizeof(char));
	uint8_t *b = (uint8_t*)buf;
	*nbuf = 0;

	//
	// program a state machine here
	//

	b[0] = add_to_header_buffer(f, b, nbuf, bufmax);
	b[1] = add_to_header_buffer(f, b, nbuf, bufmax);
	if (b[0]=='P' || b[0]=='Q')
		if (b[1] >= '1' && b[1] <= '9')
			return IIO_FORMAT_QNM;

	if (b[0]=='P' && (b[1]=='F' || b[1]=='f'))
		return IIO_FORMAT_PFM;

#ifdef I_CAN_HAS_LIBTIFF
	if ((b[0]=='M' && buf[1]=='M') || (b[0]=='I' && buf[1]=='I'))
		return IIO_FORMAT_TIFF;
#endif//I_CAN_HAS_LIBTIFF

	if (b[0]=='I' && b[1]=='R') return IIO_FORMAT_RIM;
	if (b[0]=='R' && b[1]=='I') return IIO_FORMAT_RIM;
	if (b[0]=='M' && b[1]=='I') return IIO_FORMAT_RIM;
	if (b[0]=='I' && b[1]=='M') return IIO_FORMAT_RIM;
	if (b[0]=='W' && b[1]=='E') return IIO_FORMAT_RIM;
	if (b[0]=='V' && b[1]=='I') return IIO_FORMAT_RIM;

	if (b[0]=='P' && b[1]=='C') return IIO_FORMAT_PCM;

	if (b[0]=='B' && b[1]=='M') {
		FORI(12) add_to_header_buffer(f, b, nbuf, bufmax);
		return IIO_FORMAT_BMP;
	}



	b[2] = add_to_header_buffer(f, b, nbuf, bufmax);
	b[3] = add_to_header_buffer(f, b, nbuf, bufmax);

#ifdef I_CAN_HAS_LIBPNG
	if (b[1]=='P' && b[2]=='N' && b[3]=='G')
		return IIO_FORMAT_PNG;
#endif//I_CAN_HAS_LIBPNG

#ifdef I_CAN_HAS_LIBEXR
	if (b[0]==0x76 && b[1]==0x2f && b[2]==0x31 && b[3]==0x01)
		return IIO_FORMAT_EXR;
#endif//I_CAN_HAS_LIBEXR

	if (b[0]=='#' && b[1]=='U' && b[2]=='V')
		return IIO_FORMAT_JUV;

	if (b[0]=='P' && b[1]=='I' && b[2]=='E' && b[3]=='H')
		return IIO_FORMAT_FLO;

	b[4] = add_to_header_buffer(f, b, nbuf, bufmax);
	b[5] = add_to_header_buffer(f, b, nbuf, bufmax);
	b[6] = add_to_header_buffer(f, b, nbuf, bufmax);
	b[7] = add_to_header_buffer(f, b, nbuf, bufmax);

#ifdef I_CAN_HAS_LIBJPEG
	if (b[0]==0xff && b[1]==0xd8 && b[2]==0xff) {
		if (b[3]==0xe0 && b[6]=='J' && b[7]=='F')
			return IIO_FORMAT_JPEG;
		if (b[3]==0xe1 && b[6]=='E' && b[7]=='x')
			return IIO_FORMAT_JPEG;
	}
#endif//I_CAN_HAS_LIBPNG

	b[8] = add_to_header_buffer(f, b, nbuf, bufmax);
	b[9] = add_to_header_buffer(f, b, nbuf, bufmax);
	b[10] = add_to_header_buffer(f, b, nbuf, bufmax);
	b[11] = add_to_header_buffer(f, b, nbuf, bufmax);

	if (b[8]=='F'&&b[9]=='L'&&b[10]=='O'&&b[11]=='A')
		return IIO_FORMAT_LUM;

	return IIO_FORMAT_UNRECOGNIZED;
}

// dispatcher {{{1

// "centralized dispatcher"
static
int read_beheaded_image(struct iio_image *x, FILE *f, char *h, int hn, int fmt)
{
	IIO_DEBUG("rbi fmt = %d\n", fmt);
	// these functions can be defined in separate, independent files
	switch(fmt) {
	case IIO_FORMAT_QNM:   return read_beheaded_qnm (x, f, h, hn);
	case IIO_FORMAT_RIM:   return read_beheaded_rim (x, f, h, hn);
	case IIO_FORMAT_PFM:   return read_beheaded_pfm (x, f, h, hn);
	case IIO_FORMAT_FLO:   return read_beheaded_flo (x, f, h, hn);
	case IIO_FORMAT_JUV:   return read_beheaded_juv (x, f, h, hn);
	case IIO_FORMAT_LUM:   return read_beheaded_lum (x, f, h, hn);
	case IIO_FORMAT_PCM:   return read_beheaded_pcm (x, f, h, hn);
	case IIO_FORMAT_BMP:   return read_beheaded_bmp (x, f, h, hn);

#ifdef I_CAN_HAS_LIBPNG
	case IIO_FORMAT_PNG:   return read_beheaded_png (x, f, h, hn);
#endif

#ifdef I_CAN_HAS_LIBJPEG
	case IIO_FORMAT_JPEG:  return read_beheaded_jpeg(x, f, h, hn);
#endif

#ifdef I_CAN_HAS_LIBTIFF
	case IIO_FORMAT_TIFF:  return read_beheaded_tiff(x, f, h, hn);
#endif

#ifdef I_CAN_HAS_LIBEXR
	case IIO_FORMAT_EXR:   return read_beheaded_exr (x, f, h, hn);
#endif

		/*
	case IIO_FORMAT_JP2:   return read_beheaded_jp2 (x, f, h, hn);
	case IIO_FORMAT_VTK:   return read_beheaded_vtk (x, f, h, hn);
	case IIO_FORMAT_CIMG:  return read_beheaded_cimg(x, f, h, hn);
	case IIO_FORMAT_PAU:   return read_beheaded_pau (x, f, h, hn);
	case IIO_FORMAT_DICOM: return read_beheaded_dicom(x, f, h, hn);
	case IIO_FORMAT_NIFTI: return read_beheaded_nifti(x, f, h, hn);
	case IIO_FORMAT_PCX:   return read_beheaded_pcx (x, f, h, hn);
	case IIO_FORMAT_GIF:   return read_beheaded_gif (x, f, h, hn);
	case IIO_FORMAT_XPM:   return read_beheaded_xpm (x, f, h, hn);
	case IIO_FORMAT_RAFA:   return read_beheaded_rafa (x, f, h, hn);
	*/

//#ifdef I_CAN_HAS_WHATEVER
	case IIO_FORMAT_UNRECOGNIZED: return read_beheaded_whatever(x,f,h,hn);
//#else
//	case IIO_FORMAT_UNRECOGNIZED: return -2;
//#endif

	default:              return -17;
	}
}





// general image reader {{{1



//
// This function is the core of the library.
// Everything passes through here.
//
static int read_image_f(struct iio_image *x, FILE *f)
{
	int bufmax = 0x100, nbuf, format;
	char buf[bufmax];
	format = guess_format(f, buf, &nbuf, bufmax);
	IIO_DEBUG("iio file format guess: %s {%d}\n", iio_strfmt(format), nbuf);
	assert(nbuf > 0);
	return read_beheaded_image(x, f, buf, nbuf, format);
}

static int read_image(struct iio_image *x, const char *fname)
{
#ifndef IIO_ABORT_ON_ERROR
	if (setjmp(global_jump_buffer)) {
		IIO_DEBUG("SOME ERROR HAPPENED AND WAS HANDLED\n");
		return 1;
	}
#endif//IIO_ABORT_ON_ERROR
	FILE *f = xfopen(fname, "r");
	int r = read_image_f(x, f);
	fclose(f);

	IIO_DEBUG("READ IMAGE return value = %d\n", r);
	IIO_DEBUG("READ IMAGE dimension = %d\n", x->dimension);
	switch(x->dimension) {
	case 1: IIO_DEBUG("READ IMAGE sizes = %d\n",x->sizes[0]);break;
	case 2: IIO_DEBUG("READ IMAGE sizes = %d x %d\n",x->sizes[0],x->sizes[1]);break;
	case 3: IIO_DEBUG("READ IMAGE sizes = %d x %d x %d\n",x->sizes[0],x->sizes[1],x->sizes[2]);break;
	case 4: IIO_DEBUG("READ IMAGE sizes = %d x %d x %d x %d\n",x->sizes[0],x->sizes[1],x->sizes[2],x->sizes[3]);break;
	default: fail("caca [dimension = %d]", x->dimension);
	}
	IIO_DEBUG("READ IMAGE pixel_dimension = %d\n",x->pixel_dimension);
	IIO_DEBUG("READ IMAGE type = %s\n", iio_strtyp(x->type));
	IIO_DEBUG("READ IMAGE contiguous_data = %d\n",x->contiguous_data);

	return r;
}


static void iio_save_image_default(const char *filename, struct iio_image *x);



// API (input) {{{1

static void *rfail(const char *fmt, ...)
{
#ifdef IIO_ABORT_ON_ERROR
	va_list argp;
	va_start(argp, fmt);
	fail(fmt, argp);
	va_end(argp);
#else
	(void)fmt;
#endif
	return NULL;
}

// 2D only
static
void *iio_read_image(const char *fname, int *w, int *h, int desired_sample_type)
{
	struct iio_image x[1];
	int r = read_image(x, fname);
	if (r) return rfail("could not read image");
	if (x->dimension != 2) {
		x->dimension = 2;
	}
	*w = x->sizes[0];
	*h = x->sizes[1];
	iio_convert_samples(x, desired_sample_type);
	return x->data;
}

// API 2D
float *iio_read_image_float_vec(const char *fname, int *w, int *h, int *pd)
{
	struct iio_image x[1];
	int r = read_image(x, fname);
	if (r) return rfail("could not read image");
	if (x->dimension != 2) {
		x->dimension = 2;
		//error("non 2d image");
	}
	*w = x->sizes[0];
	*h = x->sizes[1];
	*pd = x->pixel_dimension;
	iio_convert_samples(x, IIO_TYPE_FLOAT);
	return x->data;
}

// API 2D
float *iio_read_image_float_split(const char *fname, int *w, int *h, int *pd)
{
	float *r = iio_read_image_float_vec(fname, w, h, pd);
	float *rbroken = xmalloc(*w**h**pd*sizeof*rbroken);
	break_pixels_float(rbroken, r, *w**h, *pd);
	xfree(r);
	return rbroken;
}

// API 2D
float *iio_read_image_float_rgb(const char *fname, int *w, int *h)
{
	struct iio_image x[1];
	int r = read_image(x, fname);
	if (r) return rfail("could not read image");
	if (x->dimension != 2) {
		x->dimension = 2;
		return rfail("non 2d image");
	}
	if (x->pixel_dimension != 3) {
		iio_hacky_colorize(x, 3);
	}
	*w = x->sizes[0];
	*h = x->sizes[1];
	iio_convert_samples(x, IIO_TYPE_FLOAT);
	return x->data;
}

// API 2D
double *iio_read_image_double_vec(const char *fname, int *w, int *h, int *pd)
{
	struct iio_image x[1];
	int r = read_image(x, fname);
	if (r) return rfail("could not read image");
	if (x->dimension != 2) {
		x->dimension = 2;
	}
	*w = x->sizes[0];
	*h = x->sizes[1];
	*pd = x->pixel_dimension;
	iio_convert_samples(x, IIO_TYPE_DOUBLE);
	return x->data;
}

// API 2D
uint8_t *iio_read_image_uint8_vec(const char *fname, int *w, int *h, int *pd)
{
	struct iio_image x[1];
	int r = read_image(x, fname);
	if (r) return rfail("could not read image");
	if (x->dimension != 2) {
		x->dimension = 2;
	}
	*w = x->sizes[0];
	*h = x->sizes[1];
	*pd = x->pixel_dimension;
	iio_convert_samples(x, IIO_TYPE_UINT8);
	return x->data;
}

// API 2D
uint16_t *iio_read_image_uint16_vec(const char *fname, int *w, int *h, int *pd)
{
	struct iio_image x[1];
	int r = read_image(x, fname);
	if (r) return rfail("could not read image");
	if (x->dimension != 2) {
		x->dimension = 2;
	}
	*w = x->sizes[0];
	*h = x->sizes[1];
	*pd = x->pixel_dimension;
	iio_convert_samples(x, IIO_TYPE_UINT16);
	return x->data;
}

// API 2D
uint8_t (*iio_read_image_uint8_rgb(const char *fname, int *w, int *h))[3]
{
	struct iio_image x[1];
	int r = read_image(x, fname);
	if (r) return rfail("could not read image");
	if (x->dimension != 2) {
		x->dimension = 2;
		fail("non 2d image");
	}
	if (x->pixel_dimension != 3)
		fail("non-color image");
	*w = x->sizes[0];
	*h = x->sizes[1];
	iio_convert_samples(x, IIO_TYPE_UINT8);
	return x->data;
}

// API 2D
uint8_t (**iio_read_image_uint8_matrix_rgb(const char *fnam, int *w, int *h))[3]
{
	struct iio_image x[1];
	int r = read_image(x, fnam);
	if (r) return rfail("could not read image");
	if (x->dimension != 2) {
		x->dimension = 2;
		return rfail("non 2d image");
	}
	if (x->pixel_dimension != 3) {
		iio_hacky_colorize(x, 3);
	}
	*w = x->sizes[0];
	*h = x->sizes[1];
	iio_convert_samples(x, IIO_TYPE_UINT8);
	return wrap_2dmatrix_around_data(x->data, *w, *h, 3);
}

// API 2D
float (**iio_read_image_float_matrix_rgb(const char *fnam, int *w, int *h))[3]
{
	struct iio_image x[1];
	int r = read_image(x, fnam);
	if (r) return rfail("could not read image");
	if (x->dimension != 2) {
		x->dimension = 2;
		return rfail("non 2d image");
	}
	if (x->pixel_dimension != 3) {
		iio_hacky_colorize(x, 3);
	}
	*w = x->sizes[0];
	*h = x->sizes[1];
	iio_convert_samples(x, IIO_TYPE_FLOAT);
	return wrap_2dmatrix_around_data(x->data, *w, *h, 3*sizeof(float));
}

// API 2D
uint8_t ***iio_read_image_uint8_matrix_vec(const char *fname,
	       	int *w, int *h, int *pd)
{
	struct iio_image x[1];
	int r = read_image(x, fname);
	if (r) return rfail("could not read image");
	if (x->dimension != 2) {
		x->dimension = 2;
		return rfail("non 2d image");
	}
	*w = x->sizes[0];
	*h = x->sizes[1];
	*pd = x->pixel_dimension;
	fprintf(stderr, "matrix_vec pd = %d\n", *pd);
	iio_convert_samples(x, IIO_TYPE_UINT8);
	return wrap_2dmatrix_around_data(x->data, *w, *h, *pd);
}

// API 2D
void *iio_read_image_float_matrix_vec(const char *fname,
	       	int *w, int *h, int *pd)
{
	struct iio_image x[1];
	int r = read_image(x, fname);
	if (r) return rfail("could not read image");
	if (x->dimension != 2) {
		x->dimension = 2;
		return rfail("non 2d image");
	}
	*w = x->sizes[0];
	*h = x->sizes[1];
	*pd = x->pixel_dimension;
	iio_convert_samples(x, IIO_TYPE_FLOAT);
	return wrap_2dmatrix_around_data(x->data, *w, *h, *pd*sizeof(float));
}

// API 2D
uint8_t **iio_read_image_uint8_matrix(const char *fname, int *w, int *h)
{
	struct iio_image x[1];
	int r = read_image(x, fname);
	if (r) return rfail("could not read image");
	if (x->dimension != 2) {
		x->dimension = 2;
		return rfail("non 2d image");
	}
	if (x->pixel_dimension == 3)
		iio_hacky_uncolorize(x);
	if (x->pixel_dimension != 1)
		fail("non-scalar image");
	*w = x->sizes[0];
	*h = x->sizes[1];
	iio_convert_samples(x, IIO_TYPE_UINT8);
	return wrap_2dmatrix_around_data(x->data, *w, *h, 1);
}

float **iio_read_image_float_matrix(const char *fname, int *w, int *h)
{
	struct iio_image x[1];
	int r = read_image(x, fname);
	if (r) return rfail("could not read image");
	if (x->dimension != 2) {
		x->dimension = 2;
		return rfail("non 2d image");
	}
	if (x->pixel_dimension == 3)
		iio_hacky_uncolorize(x);
	if (x->pixel_dimension != 1)
		return rfail("non-scalar image");
	*w = x->sizes[0];
	*h = x->sizes[1];
	iio_convert_samples(x, IIO_TYPE_FLOAT);
	return wrap_2dmatrix_around_data(x->data, *w, *h, sizeof(float));
}

// API nd general
void *iio_read_nd_image_as_stored(char *fname,
		int *dimension, int *sizes,
		int *samples_per_pixel, int *sample_size,
		bool *ieefp_samples, bool *signed_samples)
{
	struct iio_image x[1];
	int r = read_image(x, fname);
	if (r) return rfail("so much fail");
	*dimension = x->dimension;
	FORI(x->dimension) sizes[i] = x->sizes[i];
	*samples_per_pixel = x->pixel_dimension;
	iio_type_unid(sample_size, ieefp_samples, signed_samples, x->type);
	return x->data;
}

// API nd general
void *iio_read_nd_image_as_desired(char *fname,
		int *dimension, int *sizes,
		int *samples_per_pixel, int desired_sample_size,
		bool desired_ieeefp_samples, bool desired_signed_samples)
{
	struct iio_image x[1];
	int r = read_image(x, fname);
	if (r) return rfail("so much fail");
	int desired_type = iio_type_id(desired_sample_size,
				desired_ieeefp_samples, desired_signed_samples);
	iio_convert_samples(x, desired_type);
	*dimension = x->dimension;
	FORI(x->dimension) sizes[i] = x->sizes[i];
	*samples_per_pixel = x->pixel_dimension;
	return x->data;
}

// API 2D
float *iio_read_image_float(const char *fname, int *w, int *h)
{
	struct iio_image x[1];
	int r = read_image(x, fname);
	if (r) return rfail("could not read image");
	if (x->dimension != 2) {
		x->dimension = 2;
		return rfail("non 2d image");
	}
	if (x->pixel_dimension == 3)
		iio_hacky_uncolorize(x);
	if (x->pixel_dimension == 4)
		iio_hacky_uncolorizea(x);
	if (x->pixel_dimension != 1)
		return rfail("non-scalarizable image");
	*w = x->sizes[0];
	*h = x->sizes[1];
	iio_convert_samples(x, IIO_TYPE_FLOAT);
	return x->data;
}

// API 2D
double *iio_read_image_double(const char *fname, int *w, int *h)
{
	struct iio_image x[1];
	int r = read_image(x, fname);
	if (r) return rfail("could not read image");
	if (x->dimension != 2) {
		x->dimension = 2;
		return rfail("non 2d image");
	}
	if (x->pixel_dimension == 3)
		iio_hacky_uncolorize(x);
	if (x->pixel_dimension != 1)
		return rfail("non-scalar image");
	*w = x->sizes[0];
	*h = x->sizes[1];
	iio_convert_samples(x, IIO_TYPE_DOUBLE);
	return x->data;
}


// API 2D
uint8_t *iio_read_image_uint8(const char *fname, int *w, int *h)
{
	return iio_read_image(fname, w, h, IIO_TYPE_UINT8);
}




// API (output) {{{1

static bool this_float_is_actually_a_byte(float x)
{
	return (x == floorf(x)) && (x >= 0) && (x < 256);
}

static bool these_floats_are_actually_bytes(float *t, int n)
{
	IIO_DEBUG("checking %d floats for byteness (%p)\n", n, (void*)t);
	FORI(n)
		if (!this_float_is_actually_a_byte(t[i]))
			return false;
	return true;
}

static bool string_suffix(const char *s, const char *suf)
{
	int len_s = strlen(s);
	int len_suf = strlen(suf);
	if (len_s < len_suf)
		return false;
	return 0 == strcmp(suf, s + (len_s - len_suf));
}

// Note:
// This function was written without being designed.  See file "saving.txt" for
// an attempt at designing it.
static void iio_save_image_default(const char *filename, struct iio_image *x)
{
	int typ = normalize_type(x->type);
	if (x->dimension != 2) fail("de moment només escrivim 2D");
	//static bool silly = true;
	if (string_suffix(filename, ".uv") && typ == IIO_TYPE_FLOAT
				&& x->pixel_dimension == 2) {
		iio_save_image_as_juv(filename, x);
		return;
	}
	if (string_suffix(filename, ".flo") && typ == IIO_TYPE_FLOAT
				&& x->pixel_dimension == 2) {
		iio_save_image_as_flo(filename, x);
		return;
	}
	if (string_suffix(filename, ".mw") && typ == IIO_TYPE_FLOAT
				&& x->pixel_dimension == 1) {
		iio_save_image_as_rim_fimage(filename, x);
		return;
	}
	if (string_suffix(filename, ".mw") && typ == IIO_TYPE_UINT8
				&& x->pixel_dimension == 1) {
		iio_save_image_as_rim_cimage(filename, x);
		return;
	}
	if (x->pixel_dimension != 1 && x->pixel_dimension != 3 && x->pixel_dimension != 4 && x->pixel_dimension != 2 )
	{
		iio_save_image_as_tiff_smarter(filename, x);
		return;
		//error("de moment només escrivim gris ó RGB");
	}
	if (typ != IIO_TYPE_FLOAT && typ != IIO_TYPE_UINT8 && typ != IIO_TYPE_INT16 && typ != IIO_TYPE_INT8)
		fail("de moment només fem floats o bytes (got %d)",typ);
	int nsamp = iio_image_number_of_samples(x);
	if (typ == IIO_TYPE_FLOAT &&
			these_floats_are_actually_bytes(x->data, nsamp))
	{
		void *old_data = x->data;
		x->data = xmalloc(nsamp*sizeof(float));
		memcpy(x->data, old_data, nsamp*sizeof(float));
		iio_convert_samples(x, IIO_TYPE_UINT8);
		iio_save_image_default(filename, x); // recursive call
		xfree(x->data);
		x->data = old_data;
		return;
	}
	if (true) {
		if (false
				|| string_suffix(filename, ".tiff")
				|| string_suffix(filename, ".tif")
				|| string_suffix(filename, ".TIFF")
				|| string_suffix(filename, ".TIF")
		   )
		{
			iio_save_image_as_tiff_smarter(filename, x);
			return;
		}
	}
	if (true) {
		char *tiffname = strstr(filename, "TIFF:");
		if (tiffname == filename) {
			iio_save_image_as_tiff_smarter(filename+5, x);
			return;
		}
	}
	if (true) {
		char *pngname = strstr(filename, "PNG:");
		if (pngname == filename) {
			if (typ == IIO_TYPE_FLOAT) {
				void *old_data = x->data;
				x->data = xmalloc(nsamp*sizeof(float));
				memcpy(x->data, old_data, nsamp*sizeof(float));
				iio_convert_samples(x, IIO_TYPE_UINT8);
				iio_save_image_default(filename, x);//recursive
				xfree(x->data);
				x->data = old_data;
				return;
			}
			iio_save_image_as_png(filename+4, x);
			return;
		}
	}
	if (true) {
		if (false
				|| string_suffix(filename, ".png")
				|| string_suffix(filename, ".PNG")
				|| (typ==IIO_TYPE_UINT8&&x->pixel_dimension==4)
				|| (typ==IIO_TYPE_UINT8&&x->pixel_dimension==2)
		   )
		{
			if (typ == IIO_TYPE_FLOAT) {
				void *old_data = x->data;
				x->data = xmalloc(nsamp*sizeof(float));
				memcpy(x->data, old_data, nsamp*sizeof(float));
				iio_convert_samples(x, IIO_TYPE_UINT8);
				iio_save_image_default(filename, x);//recursive
				xfree(x->data);
				x->data = old_data;
				return;
			}
			iio_save_image_as_png(filename, x);
			return;
		}
	}
	IIO_DEBUG("SIDEF:\n");
#ifdef IIO_SHOW_DEBUG_MESSAGES
	iio_print_image_info(stderr, x);
#endif
	FILE *f = xfopen(filename, "w");
	if (x->pixel_dimension == 1 && typ == IIO_TYPE_FLOAT) {
		int m = these_floats_are_actually_bytes(x->data,x->sizes[0]*x->sizes[1]) ? 255 : 65535;
		fprintf(f, "P2\n%d %d\n%d\n", x->sizes[0], x->sizes[1], m);
		float *data = x->data;
		FORI(x->sizes[0]*x->sizes[1])
			fprintf(f, "%a\n", data[i]);
	} else if (x->pixel_dimension == 3 && typ == IIO_TYPE_FLOAT) {
		int m = these_floats_are_actually_bytes(x->data,3*x->sizes[0]*x->sizes[1]) ? 255 : 65535;
		float *data = x->data;
		fprintf(f, "P3\n%d %d\n%d\n", x->sizes[0], x->sizes[1], m);
		FORI(3*x->sizes[0]*x->sizes[1]) {
			fprintf(f, "%g\n", data[i]);
		}
	} else if (x->pixel_dimension == 3 && typ == IIO_TYPE_UINT8) {
		uint8_t *data = x->data;
		int w = x->sizes[0];
		int h = x->sizes[1];
		if (w*h <= 10000) {
			fprintf(f, "P3\n%d %d\n255\n", w, h);
			FORI(3*w*h) {
				int datum = data[i];
				fprintf(f, "%d\n", datum);
			}
		} else {
			fprintf(f, "P6\n%d %d\n255\n", w, h);
			fwrite(data, 3*w*h, 1, f);
		}
	} else if (x->pixel_dimension == 4 && typ == IIO_TYPE_UINT8) {
		fprintf(stderr, "IIO WARNING: assuming 4 chanels mean RGBA\n");
		uint8_t *data = x->data;
		fprintf(f, "P3\n%d %d\n255\n", x->sizes[0], x->sizes[1]);
		//error("write correctly here");
		FORI(4*x->sizes[0]*x->sizes[1]) {
			if (i%4 == 3) continue;
			int datum = data[i];
			fprintf(f, "%d\n", datum);
		}
	} else if (x->pixel_dimension == 1 && typ == IIO_TYPE_UINT8) {
		uint8_t *data = x->data;
		int w = x->sizes[0];
		int h = x->sizes[1];
		if (w*h <= 10000) {
			fprintf(f, "P2\n%d %d\n255\n", w, h);
			FORI(w*h) {
				int datum = data[i];
				fprintf(f, "%d\n", datum);
			}
		} else {
			fprintf(f, "P5\n%d %d\n255\n", w, h);
			fwrite(data, w*h, 1, f);
		}
	} else
			iio_save_image_as_tiff_smarter(filename, x);
	xfclose(f);
}

void iio_save_image_uint8_matrix_rgb(char *filename, uint8_t (**data)[3],
		int w, int h)
{
	struct iio_image x[1];
	x->dimension = 2;
	x->sizes[0] = w;
	x->sizes[1] = h;
	x->pixel_dimension = 3;
	x->type = IIO_TYPE_UINT8;
	x->data = data[0][0];
	iio_save_image_default(filename, x);
}

void iio_save_image_uint8_matrix(char *filename, uint8_t **data, int w, int h)
{
	struct iio_image x[1];
	x->dimension = 2;
	x->sizes[0] = w;
	x->sizes[1] = h;
	x->pixel_dimension = 1;
	x->type = IIO_TYPE_UINT8;
	x->data = data[0];
	iio_save_image_default(filename, x);
}

void iio_save_image_float_vec(char *filename, float *data,
		int w, int h, int pd)
{
	struct iio_image x[1];
	x->dimension = 2;
	x->sizes[0] = w;
	x->sizes[1] = h;
	x->pixel_dimension = pd;
	x->type = IIO_TYPE_FLOAT;
	x->data = data;
	x->contiguous_data = false;
	iio_save_image_default(filename, x);
}

void iio_save_image_float_split(char *filename, float *data,
		int w, int h, int pd)
{
	float *rdata = xmalloc(w*h*pd*sizeof*rdata);
	recover_broken_pixels_float(rdata, data, w*h, pd);
	iio_save_image_float_vec(filename, rdata, w, h, pd);
	xfree(rdata);
}

void iio_save_image_double_vec(char *filename, double *data,
		int w, int h, int pd)
{
	struct iio_image x[1];
	x->dimension = 2;
	x->sizes[0] = w;
	x->sizes[1] = h;
	x->pixel_dimension = pd;
	x->type = IIO_TYPE_DOUBLE;
	x->data = data;
	x->contiguous_data = false;
	iio_save_image_default(filename, x);
}

void iio_save_image_float(char *filename, float *data, int w, int h)
{
	struct iio_image x[1];
	x->dimension = 2;
	x->sizes[0] = w;
	x->sizes[1] = h;
	x->pixel_dimension = 1;
	x->type = IIO_TYPE_FLOAT;
	x->data = data;
	x->contiguous_data = false;
	iio_save_image_default(filename, x);
}

void iio_save_image_double(char *filename, double *data, int w, int h)
{
	struct iio_image x[1];
	x->dimension = 2;
	x->sizes[0] = w;
	x->sizes[1] = h;
	x->pixel_dimension = 1;
	x->type = IIO_TYPE_DOUBLE;
	x->data = data;
	x->contiguous_data = false;
	iio_save_image_default(filename, x);
}

void iio_save_image_uint8_vec(char *filename, uint8_t *data,
		int w, int h, int pd)
{
	struct iio_image x[1];
	x->dimension = 2;
	x->sizes[0] = w;
	x->sizes[1] = h;
	x->pixel_dimension = pd;
	x->type = IIO_TYPE_UINT8;
	x->data = data;
	x->contiguous_data = false;
	iio_save_image_default(filename, x);
}

void iio_save_image_uint16_vec(char *filename, uint16_t *data,
		int w, int h, int pd)
{
	struct iio_image x[1];
	x->dimension = 2;
	x->sizes[0] = w;
	x->sizes[1] = h;
	x->pixel_dimension = pd;
	x->type = IIO_TYPE_UINT16;
	x->data = data;
	x->contiguous_data = false;
	iio_save_image_default(filename, x);
}



// vim:set foldmethod=marker:
back to top