https://doi.org/10.5201/ipol.2020.245
Raw File
Tip revision: 5f23fb0b6e1d50d996ac54daaa7e637e5d8decaf authored by Software Heritage on 05 May 2020, 00:00:00 UTC
ipol: Deposit 1363 in collection ipol
Tip revision: 5f23fb0
iio.c
/*
* iio.c
*
* Copyright (C) 2019, Enric Meinhardt-Llopis, Gabriele Facciolo, Carlo de Franchis, Juan Cardelino.CMLA, ÉNS Paris-Saclay.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

// 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
//


// #includes                                                                {{{1

#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <libgen.h> // needed for dirname() multi-platform

#ifdef __MINGW32__ // needed for tmpfile(), this flag is also set by MINGW64 
#include <windows.h>
#endif


#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 || __OpenBSD__
#  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_ASC 23
#define IIO_FORMAT_PDS 24
#define IIO_FORMAT_RAW 25
#define IIO_FORMAT_RWA 26
#define IIO_FORMAT_CSV 27
#define IIO_FORMAT_VRT 28
#define IIO_FORMAT_FFD 29
#define IIO_FORMAT_DLM 30
#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++)

//#define IIO_SHOW_DEBUG_MESSAGES
#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) /* nothing */
#endif//IIO_SHOW_DEBUG_MESSAGES

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



// typedefs                                                                 {{{1

typedef long long longlong;
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
#  if __STDC_VERSION__ >= 201112L
_Thread_local
#  endif
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>
#include <sys/types.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 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);
//	if (global_hack_to_never_fail)
//	{
//		IIO_DEBUG("now wave a dead chicken and press enter\n");
//		getchar();
//		return;
//	}
//	exit(43);
#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 do_nop(void *p, ...)
{
    va_list argp;
    va_start(argp, p);
    va_end(argp);
}

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

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);
}

#  if __STDC_VERSION__ >= 201112L
_Thread_local
#  endif
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 (*p == 'w')
            return stdout;
        else if (*p == 'r')
            return stdin;
        else
            fail("unknown fopen mode \"%s\"", p);
    }
    if (0 == strcmp("--", s) && *p == 'w') return stderr;

    // NOTE: the 'b' flag is required for I/O on Windows systems
    // on unix, it is ignored
    char pp[3] = { p[0], 'b', '\0' };
    f = fopen(s, pp);
    if (f == NULL)
        fail("can not open file \"%s\" in mode \"%s\"",// (%s)",
             s, pp);//, 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 pick_char_for_sure(FILE *f)
{
    int c = getc(f);
    if (EOF == c)
    {
        xfclose(f);
        fail("input file ended before expected");
    }
    //IIO_DEBUG("pcs = '%c'\n", c);
    return c;
}

static void eat_spaces(FILE *f)
{
    //IIO_DEBUG("inside eat spaces\n");
    int c;
    do
        c = pick_char_for_sure(f);
    while (isspace(c));
    ungetc(c, f);
}

static void eat_line(FILE *f)
{
    //IIO_DEBUG("inside eat line\n");
    while (pick_char_for_sure(f) != '\n')
        ;
}

static void eat_spaces_and_comments(FILE *f)
{
    //IIO_DEBUG("inside eat spaces and comments\n");
    int c, comment_char = '#';
    eat_spaces(f);
uppsala:
    c = pick_char_for_sure(f);
    if (c == comment_char)
    {
        eat_line(f);
        eat_spaces(f);
    }
    else
        ungetc(c, f);
    if (c == comment_char) goto uppsala;
}

static void fill_temporary_filename(char *out)
{
#ifdef I_CAN_HAS_MKSTEMP
    char tfn[] = "/tmp/iio_tmp_file_XXXXXX\0";
    int r = mkstemp(tfn);
    if (r == -1)
    {
        perror("hola");
        fail("could not create tmp filename");
    }
#else
    static char buf[L_tmpnam+1];
    char *tfn = tmpnam(buf);
#endif//I_CAN_HAS_MKSTEMP
    strncpy(out, tfn, FILENAME_MAX);
}


// 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, typically
    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

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);
    }
    //if (x->contiguous_data)
    //	assert(x->data == (void*)(x+1));
}

// 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
// the present solution works well for the typical 32 and 64 bit platforms
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 int iio_inttyp(const char *typename)
{
    int n = strlen(typename);
    char utyp[n+1];
    utyp[n] = '\0';
    for (int i = 0; i < n; i++)
        utyp[i] = toupper(typename[i]);
#define M(t) if(!strcmp(utyp,#t))return IIO_TYPE_ ## t
    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);
#undef M
    fail("unrecognized typename \"%s\"", typename);
}

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(LUM);
        M(JUV);
        M(PCM);
        M(ASC);
        M(RAW);
        M(RWA);
        M(PDS);
        M(CSV);
        M(VRT);
        M(FFD);
        M(DLM);
        M(UNRECOGNIZED);
    default:
        fail("caca de la grossa (%d)", format);
    }
#undef M
}

inline
static void iio_print_image_info(FILE *f, struct iio_image *x)
{
    fprintf(f, "iio_print_image_info %p\n", (void *)x);
    fprintf(f, "dimension = %d\n", x->dimension);
    int *s = x->sizes;
    switch(x->dimension)
    {
    case 1:
        fprintf(f, "size = %d\n", s[0]);
        break;
    case 2:
        fprintf(f, "sizes = %dx%d\n", s[0],s[1]);
        break;
    case 3:
        fprintf(f, "sizes = %dx%dx%d\n", s[0],s[1],s[2]);
        break;
    case 4:
        fprintf(f, "sizes = %dx%dx%dx%d\n", s[0],s[1],s[2],s[3]);
        break;
    default:
        fail("unsupported dimension %d", x->dimension);
    }
    fprintf(f, "pixel_dimension = %d\n", x->pixel_dimension);
    fprintf(f, "type = %s\n", iio_strtyp(x->type));
    fprintf(f, "data = %p\n", (void *)x->data);
}


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);
}

static void inplace_swap_pixels(struct iio_image *x, int i, int j, int a, int b)
{
    if (x->dimension != 2)
        fail("can only flip 2-dimensional images");
    int w = x->sizes[0];
    //int h = x->sizes[1]; // unused
    int pixsize = x->pixel_dimension * iio_image_sample_size(x);
    uint8_t *p = (i + j * w) * pixsize + (uint8_t*)x->data;
    uint8_t *q = (a + b * w) * pixsize + (uint8_t*)x->data;
    for (int k = 0; k < pixsize; k++)
    {
        uint8_t tmp = p[k];
        p[k] = q[k];
        q[k] = tmp;
    }
}

static void inplace_flip_horizontal(struct iio_image *x)
{
    int w = x->sizes[0];
    int h = x->sizes[1];
    for (int j = 0; j < h; j++)
        for (int i = 0; i < w/2; i++)
            inplace_swap_pixels(x, i, j, w - i - 1, j);
}

static void inplace_flip_vertical(struct iio_image *x)
{
    int w = x->sizes[0];
    int h = x->sizes[1];
    for (int j = 0; j < h/2; j++)
        for (int i = 0; i < w; i++)
            inplace_swap_pixels(x, i, j, i, h - j - 1);
}

static void inplace_transpose(struct iio_image *x)
{
    int w = x->sizes[0];
    int h = x->sizes[1];
    if (w != h)
        fail("rectangular inplace transpose not implemented");
    for (int i = 0; i < w; i++)
        for (int j = 0; j < i; j++)
            inplace_swap_pixels(x, i, j, j, i);
}

static void inplace_reorient(struct iio_image *x, int orientation)
{
    int orx = orientation & 0xff;
    int ory = (orientation & 0xff00)/0x100;
    if (isupper(orx))
        inplace_flip_horizontal(x);
    if (isupper(ory))
        inplace_flip_vertical(x);
    if (toupper(orx) > toupper(ory))
        inplace_transpose(x);
}


// data conversion                                                          {{{1

// macros to crop a numeric value
#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;
    case CC(F8,F4):
        *(double*)dest = *(  float*)src;
        break;
    case CC(F4,F8):
        *( float*)dest = *( double*)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);
    // TODO: do something sensible for 2 or 4 channels
    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_UINT16:
    {
        uint16_t (*xd)[3] = x->data;
        uint16_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)
{
    static char filename[FILENAME_MAX];
    fill_temporary_filename(filename);
    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 I_CAN_KEEP_TMP_FILES
    remove(filename);
#else
    fprintf(stderr, "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;
#ifdef __MINGW32__
    // creating a tempfile can be very slow
    // this is extremely inefficient
    char filename[FILENAME_MAX], pathname[FILENAME_MAX];
    GetTempPath(FILENAME_MAX, pathname);
    GetTempFileName(pathname,"temp",0,filename);
    f = fopen(filename,"w+bTD");
    IIO_DEBUG("creating MINGW temp file %s\n", filename);
#else
    f = tmpfile();
#endif // MINGW32
    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)
{
    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)
{
    FORL(pd) FORI(n)
    clear[pd*i + l] = broken[n*l + i];
}

static
void repair_broken_pixels(void *clear, void *broken, int n, int pd, int sz)
{
    char *c = clear;
    char *b = broken;
    FORL(pd) FORI(n)
    memcpy(c + sz*(pd*i+l), b + sz*(n*l + i), sz);
}

static void repair_broken_pixels_inplace(void *x, int n, int pd, int sz)
{
    char *t = malloc(n * pd * sz);
    memcpy(t, x, n * pd * sz);
    repair_broken_pixels(x, t, n, pd, sz);
    free(t);
}

static void
recover_broken_pixels_double(double *clear, double *broken, int n, int 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;
    int depth;
    w = png_get_image_width(pp, pi);
    h = png_get_image_height(pp, pi);
    channels = png_get_channels(pp, pi);
    depth = png_get_bit_depth(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 depth = %d\n", depth);
    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 width = %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);

    // 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 (optimization): 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 TIFF *tiffopen_fancy(const char *filename, char *mode)
{
    char *comma = strrchr(filename, ',');
    if (*mode != 'r' || !comma)
def:
        return TIFFOpen(filename, mode);

    int aftercomma = strlen(comma + 1);
    int ndigits = strspn(comma + 1, "0123456789");

    if (aftercomma != ndigits) goto def;

    char buf[FILENAME_MAX];
    strncpy(buf, filename, FILENAME_MAX);
    comma = strrchr(buf, ',');
    *comma = '\0';
    int index = atoi(comma + 1);

    TIFF *tif = TIFFOpen(buf, mode);
    if (!tif) return tif;
    for (int i = 0; i < index; i++)
        TIFFReadDirectory(tif);

    return tif;
}

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
    TIFFSetWarningHandler(NULL);//suppress warnings

    //fprintf(stderr, "TIFFOpen \"%s\"\n", filename);
    TIFF *tif = tiffopen_fancy(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);

    // 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));

    uint16_t planarity;
    r = TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planarity);
    if (r != 1) planarity = PLANARCONFIG_CONTIG;
    bool broken = planarity == PLANARCONFIG_SEPARATE;


    // 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);
    IIO_DEBUG("uss = %d\n", (int)uscanline_size);
    int sls = TIFFScanlineSize(tif);
    IIO_DEBUG("sls(r) = %d\n", (int)sls);
    if ((int)scanline_size != sls)
        fprintf(stderr, "scanline_size,sls = %d,%d\n", (int)scanline_size,sls);
    //assert((int)scanline_size == sls);
    if (!broken)
        assert((int)scanline_size == sls);
    else
        assert((int)scanline_size == spp*sls);
    assert((int)scanline_size >= sls);
    uint8_t *data = xmalloc(w * h * spp * rbps);
    uint8_t *buf = xmalloc(scanline_size);

    // use a particular reader for tiled tiff
    if (TIFFIsTiled(tif))
    {
        int tisize = TIFFTileSize(tif);
        uint32_t tilewidth, tilelength;
        TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tilewidth);
        TIFFGetField(tif, TIFFTAG_TILELENGTH, &tilelength);
        IIO_DEBUG("tilewidth = %u\n", tilewidth);
        IIO_DEBUG("tilelength = %u\n", tilelength);
        IIO_DEBUG("tisize = %d (%u)\n", tisize, tilewidth*tilelength);

        if (bps < 8)
            fail("only byte-oriented tiles are supported (%d)",bps);
        int Bps = bps/8;

        IIO_DEBUG("bps = %u\n", bps);
        IIO_DEBUG("Bps = %d\n", Bps);

        uint8_t *tbuf = xmalloc(tisize*Bps*spp);
        for (uint32_t tx = 0; tx < w; tx += tilewidth)
            for (uint32_t ty = 0; ty < h; ty += tilelength)
            {
                IIO_DEBUG("tile at %u %u\n", tx, ty);
                if (!broken)
                {
                    if (-1 == TIFFReadTile(tif, tbuf, tx, ty, 0, 0))
                        memset(tbuf, -1, TIFFTileSize(tif));
                }
                for (uint16_t l = 0; l < spp; l++)
                {
                    int L = l, Spp = spp;
                    if (broken)
                    {
                        TIFFReadTile(tif, tbuf, tx, ty, 0, l);
                        L = 0;
                        Spp = 1;
                    }
                    for (uint32_t j = 0; j < tilelength; j++)
                        for (uint32_t i = 0; i < tilewidth; i++)
                            for (int b = 0; b < Bps; b++)
                            {
                                uint32_t ii = i + tx;
                                uint32_t jj = j + ty;
                                if (ii < w && jj < h)
                                {
                                    int idx_i = ((j*tilewidth + i)*Spp + L)*Bps + b;
                                    int idx_o = ((jj*w + ii)*spp + l)*Bps + b;
                                    uint8_t s = tbuf[idx_i];
                                    ((uint8_t*)data)[idx_o] = s;
                                }
                            }
                }
            }
        xfree(tbuf);
    }
    else
    {

        // dump scanline data
        if (broken && bps < 8) fail("cannot unpack broken scanlines");
        if (!broken) 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*sls, buf, sls);
            }
        }
        else
        {
            FORI(h)
            {
                FORJ(spp)
                {
                    r = TIFFReadScanline(tif, buf, i, j);
                    if (r < 0)
                        fail("tiff bad %d/%d;%d", i, (int)h, j);
                    memcpy(data + i*spp*sls + j*sls, buf, sls);
                }
                repair_broken_pixels_inplace(data + i*spp*sls,
                                             w, spp, bps/8);
            }
        }
    }
    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 = pick_char_for_sure(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 = pick_char_for_sure(don);
        c *= 256;
        c += pick_char_for_sure(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)header;
    (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);
    eat_spaces_and_comments(f);
    if (1 != fscanf(f, "%d", &w)) return -1;
    eat_spaces_and_comments(f);
    if (1 != fscanf(f, "%d", &h)) return -2;
    if (c1 == 'Q')
    {
        if (1 != fscanf(f, "%d", &d)) return -3;
        eat_spaces_and_comments(f);
    }
    if (c2 == 7 || c2 == 9)
    {
        if (1 != fscanf(f, "%d", &pd)) return -4;
        eat_spaces_and_comments(f);
    }
    if (1 != fscanf(f, "%d", &m)) return -5;
    // maxval is ignored and the image is always read into floats
    if (!isspace(pick_char_for_sure(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 (xfree(data),-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;
    if (1 != fscanf(f, " %d", &w)) return -1;
    if (1 != fscanf(f, " %d", &h)) return -2;
    if (1 != fscanf(f, " %g", &scale)) return -3;
    if (!isspace(pick_char_for_sure(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 (xfree(data),-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
// (megawave's image format)

// documentation of this format:
//
// 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 = pick_char_for_sure(f);
    int b = pick_char_for_sure(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 = pick_char_for_sure(f);
    int b = pick_char_for_sure(f);
    int c = pick_char_for_sure(f);
    int d = pick_char_for_sure(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 = pick_char_for_sure(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);
    FORI(28) rim_getshort(f, swp); // skip shit (ascii numbers and zeros)
    FORI(lencomm)
    {
        int c = pick_char_for_sure(f); // skip further shit (comments)
        (void)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);
    (void)pm_band;
    uint32_t pm_form = rim_getint(f, swp);
    uint32_t pm_cmtsize = rim_getint(f, swp);
    (void)pm_cmtsize;

    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_2endianness(void *tt, int n)
{
    char *t = tt;
    FORI(n)
    {
        char tmp[2] = {t[0], t[1]};
        t[0] = tmp[1];
        t[1] = tmp[0];
        t += 2;
    }
}
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(pick_char_for_sure(f))) return -1;
    if (3 != fscanf(f, "%d %d\n%g", &w, &h, &scale)) return -2;
    if (!isspace(pick_char_for_sure(f))) return -3;
    float *data = xmalloc(w*h*4*pd);
    if (1 != fread(data, w*h*4*pd, 1, f)) return (xfree(data),-4);

    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 (xfree(data),-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] = pick_char_for_sure(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 (xfree(u),-2);
    float *v = xmalloc(w*h*sf);
    r = fread(v, sf, w*h, f);
    if(r != w*h) return (xfree(u),xfree(v),-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_lum12(struct iio_image *x,
                               FILE *f, char *header, int nheader)
{
    int w = *(uint32_t*)header;
    int h = *(uint32_t*)(header+4);
    while (nheader++ < 11968)
        pick_char_for_sure(f);
    uint16_t *data = xmalloc(w*h*sizeof*data);
    if (1 != fread(data, w*h*2, 1, f)) return (xfree(data),-1);
    x->dimension = 2;
    x->sizes[0] = w;
    x->sizes[1] = h;
    x->pixel_dimension = 1;
    x->type = IIO_TYPE_UINT16;
    x->contiguous_data = false;
    x->data = data;
    return 0;
}

static int read_beheaded_lum(struct iio_image *x,
                             FILE *f, char *header, int nheader)
{
    if (header[8] == '1')
        return read_beheaded_lum12(x, f, header, nheader);
    int w = lum_pickshort(header+2);
    int h = lum_pickshort(header+6);
    while (nheader++ < 0xf94)
        pick_char_for_sure(f);
    float *data = xmalloc(w*h*sizeof*data);
    if (1 != fread(data, w*h*4, 1, f)) return (xfree(data),-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;

    fail("BMP reader not yet finished");

    xfree(bmp);

    return 0;
}


// EXR reader                                                               {{{2

#ifdef I_CAN_HAS_LIBEXR
#include <ImfCRgbaFile.h>
// EXTERNALIZED TO :  read_exr_float.cpp

static int read_whole_exr(struct iio_image *x, const 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);

    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);
    }
    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);

    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



// ASC reader                                                               {{{2
static int read_beheaded_asc(struct iio_image *x,
                             FILE *f, char *header, int nheader)
{
    (void)nheader;
    assert(header[nheader-1] == '\n');
    int n[4], r = sscanf(header, "%d %d %d %d\n", n, n+1, n+2, n+3);
    if (r != 4) return 1;
    x->dimension = 2;
    x->sizes[0] = n[0];
    x->sizes[1] = n[1];
    x->sizes[2] = n[2];
    if (n[2] > 1)
        x->dimension = 3;
    x->pixel_dimension = n[3];
    x->type = IIO_TYPE_FLOAT;
    int nsamples = iio_image_number_of_samples(x);
    float *xdata = xmalloc(nsamples * sizeof*xdata);
    read_qnm_numbers(xdata, f, nsamples, 0, true);
    x->data = xmalloc(nsamples * sizeof*xdata);
    recover_broken_pixels_float(x->data, xdata, n[0]*n[1]*n[2], n[3]);
    xfree(xdata);
    x->contiguous_data = false;
    return 0;
}

// PDS reader                                                               {{{2

// read a line of text until either
// 	- n characters are read
// 	- a newline character is found
// 	- the end of file is reached
// returns the number of read characters, not including the end zero
// Calling this functions should always result in a valid string on l
static int getlinen(char *l, int n, FILE *f)
{
    int c, i = 0;
    while (i < n-1 && (c = fgetc(f)) != EOF && c != '\n')
        if (isprint(c))
            l[i++] = c;
    if (c == EOF) return -1;
    l[i] = '\0';
    return i;
}

// parse a line of the form "KEY = VALUE"
static void pds_parse_line(char *key, char *value, char *line)
{
    int r = sscanf(line, "%s = %s\n", key, value);
    if (r != 2)
    {
        *key = *value = '\0';
        return;
    }
    IIO_DEBUG("PARSED \"%s\" = \"%s\"\n", key, value);
}

#ifndef SAMPLEFORMAT_UINT
// definitions form tiff.h, needed for pds
#define SAMPLEFORMAT_UINT  1
#define SAMPLEFORMAT_INT  2
#define SAMPLEFORMAT_IEEEFP  3
#define SAMPLEFORMAT_VOID  4
#define SAMPLEFORMAT_COMPLEXINT 5
#define SAMPLEFORMAT_COMPLEXIEEEFP 6
#endif//SAMPLEFORMAT_UINT

static int read_beheaded_pds(struct iio_image *x,
                             FILE *f, char *header, int nheader)
{
    (void)header;
    (void)nheader;
    // check that the file is named, and not a pipe
    const char *fn;
    fn = global_variable_containing_the_name_of_the_last_opened_file;
    if (!fn)
        return 1;

    // get an object name, if different to "^IMAGE"
    char *object_id = getenv("IIO_PDS_OBJECT");
    if (!object_id)
        object_id = "^IMAGE";

    // parse the header and obtain the image dimensions and type name
    int n, nmax = 1000, cx = 0;
    char line[nmax], key[nmax], value[nmax];
    int rbytes = -1, w = -1, h = -1, spp = 1, bps = 1, obj = -1;
    int sfmt = SAMPLEFORMAT_UINT;
    bool in_object = false;
    bool flip_h = false, flip_v = false, allturn = false;
    while ((n = getlinen(line, nmax, f)) >= 0  && cx++ < nmax)
    {
        pds_parse_line(key, value, line);
        if (!*key || !*value) continue;
        IIO_DEBUG("PDS \"%s\" = \"%s\"\n", key, value);
        if (!strcmp(key, "RECORD_BYTES")) rbytes = atoi(value);
        if (!strcmp(key, object_id))      obj = atoi(value);
        if (!strcmp(key, "OBJECT") && !strcmp(value, object_id+1))
            in_object = true;
        if (!in_object) continue;
        if (!strcmp(key, "LINES"))        h = atoi(value);
        if (!strcmp(key, "LINE_SAMPLES")) w = atoi(value);
        if (!strcmp(key, "SAMPLE_BITS"))  bps = atoi(value);
        if (!strcmp(key, "BANDS"))        spp = atoi(value);
        if (!strcmp(key, "SAMPLE_TYPE"))
        {
            if (strstr(value, "REAL")) sfmt = SAMPLEFORMAT_IEEEFP;
            if (strstr(value, "UNSIGNED")) sfmt = SAMPLEFORMAT_UINT;
            if (strstr(value, "INTEGER"))sfmt=SAMPLEFORMAT_UINT;
        }
        if (!strcmp(key, "SAMPLE_DISPLAY_DIRECTION"))
            flip_h = allturn !=! strcmp(value, "RIGHT");
        if (!strcmp(key, "LINE_DISPLAY_DIRECTION"))
            flip_v = allturn !=! strcmp(value, "DOWN");
        // TODO: support the 8 possible rotations and orientations
        // (RAW-equivalents: xy xY Xy XY yx yX Yx YX)
        if (!strcmp(key, "END_OBJECT") && !strcmp(value, object_id+1))
            break;
    }

    IIO_DEBUG("rbytes = %d\n", rbytes);
    IIO_DEBUG("object_id = %s\n", object_id);
    IIO_DEBUG("obj = %d\n", obj);
    IIO_DEBUG("w = %d\n", w);
    IIO_DEBUG("h = %d\n", h);
    IIO_DEBUG("bps = %d\n", bps);
    IIO_DEBUG("spp = %d\n", spp);

    // identify the sample type
    int typ = -1;
    if (sfmt==SAMPLEFORMAT_IEEEFP && bps==32) typ = IIO_TYPE_FLOAT;
    if (sfmt==SAMPLEFORMAT_IEEEFP && bps==64) typ = IIO_TYPE_DOUBLE;
    if (sfmt==SAMPLEFORMAT_UINT && bps==8)    typ = IIO_TYPE_UINT8;
    if (sfmt==SAMPLEFORMAT_UINT && bps==16)   typ = IIO_TYPE_UINT16;
    if (sfmt==SAMPLEFORMAT_UINT && bps==32)   typ = IIO_TYPE_UINT32;
    assert(typ > 0);

    // fill-in the image struct
    x->dimension = 2;
    x->sizes[0] = w;
    x->sizes[1] = h;
    x->pixel_dimension = spp;
    x->type = typ;
    x->contiguous_data = false;

    // alloc memory for image data
    int size = w * h * spp * (bps/8);
    x->data = xmalloc(size);

    // read data
    n = fseek(f, rbytes * (obj - 1), SEEK_SET);
    if (n)
    {
        free(x->data);
        return 2;
    }
    n = fread(x->data, size, 1, f);
    if (n != 1)
    {
        free(x->data);
        return 3;
    }

    // if necessary, transpose data
    if (flip_h) inplace_flip_horizontal(x);
    if (flip_v) inplace_flip_vertical(x);

    // return
    return 0;
}

// CSV reader                                                               {{{2

static int read_beheaded_csv(struct iio_image *x,
                             FILE *fin, char *header, int nheader)
{
    // load whole file
    long filesize;
    char *filedata = load_rest_of_file(&filesize, fin, header, nheader);

    // height = number of newlines
    int h = 0;
    for (int i = 0 ; i < filesize; i++) if (filedata[i] == '\n') h += 1;

    // width = ( number of commas  + h ) / h
    int nc = 0;
    for (int i = 0 ; i < filesize; i++) if (filedata[i] == ',') nc += 1;
    int w = nc / h + 1;

    // fill-in the image struct
    x->dimension = 2;
    x->sizes[0] = w;
    x->sizes[1] = h;
    x->pixel_dimension = 1;
    x->type = IIO_TYPE_FLOAT;
    x->contiguous_data = false;

    // alloc memory for image data
    int size = w * h * sizeof(float);
    x->data = xmalloc(size);
    float *numbers = x->data;

    // read data
    char *delim = ",\n", *tok = strtok(filedata, delim);
    while (tok && numbers < (float*)(x->data)+w*h)
    {
        *numbers++ = atof(tok);
        tok = strtok(NULL, delim);
    }

    // cleanup and exit
    free(filedata);
    return 0;
}

// DLM reader                                                               {{{2

static int read_beheaded_dlm(struct iio_image *x,
                             FILE *fin, char *header, int nheader)
{
    (void)x;
    (void)fin;
    (void)header;
    (void)nheader;
    fail("dlm reader not implemented, use csv by now\n");
    return 1;
}


// VRT reader                                                               {{{2
//
// VRT = GDAL Virtual images are a text file describing the relative
// position of several tiles, specified by their filenames.  The idea is neat
// but the format itself is an abomination based on wrong misconceptions.
// Here we provide a minimal implementation for some common cases.

static int insideP(int w, int h, int i, int j)
{
    return i>=0 && j>=0 && i<w && j<h;
}

static int xml_get_numeric_attr(int *out, char *line, char *tag, char *attr)
{
    if (!strstr(line, tag)) return 0;
    line = strstr(line, attr);
    if (!line) return 0;
    *out = atoi(line + 2 + strlen(attr));
    return 1;
}

static int xml_get_tag_content(char *out, char *line, char *tag)
{
    int n = strlen(line);
    char fmt[n], tmp[n];
    snprintf(fmt, n, " <%s %%*[^>]>%%[^<]s</%s>", tag, tag);
    int r = sscanf(line, fmt, tmp);
    if (r != 1) return 0;
    memcpy(out, tmp, 1+strlen(tmp));
    return 1;
}


static int read_beheaded_vrt(struct iio_image *x,
                             FILE *fin, char *header, int nheader)
{
    (void)header;
    (void)nheader;
    int n = FILENAME_MAX + 0x200, cx = 0, w = 0, h = 0;
    char fname[n], dirvrt[n], fullfname[n], line[n], *sl = fgets(line, n, fin);
    if (!sl) return 1;
    cx += xml_get_numeric_attr(&w, line, "Dataset", "rasterXSize");
    cx += xml_get_numeric_attr(&h, line, "Dataset", "rasterYSize");
//	if (!w*h) return 2;
    if (w*h == 0) return 2;
    if (cx != 2) return 3;
    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 = xmalloc(w * h * sizeof(float));
    float (*xx)[w] = x->data;
    int pos[4] = {0,0,0,0}, pos_cx = 0, has_fname = 0;

    // obtain the path where the vrt file is located
    strncpy(dirvrt, global_variable_containing_the_name_of_the_last_opened_file, n);
    char* dirvrt2 = dirname(dirvrt);

    while (1)
    {
        sl = fgets(line, n, fin);
        if (!sl) break;
        pos_cx += xml_get_numeric_attr(pos+0, line, "DstRect", "xOff");
        pos_cx += xml_get_numeric_attr(pos+1, line, "DstRect", "yOff");
        pos_cx += xml_get_numeric_attr(pos+2, line, "DstRect", "xSize");
        pos_cx += xml_get_numeric_attr(pos+3, line, "DstRect", "ySize");
        has_fname += xml_get_tag_content(fname, line, "SourceFilename");
        if (pos_cx == 4 && has_fname == 1)
        {
            pos_cx = has_fname = 0;
            int wt, ht;
            snprintf(fullfname,FILENAME_MAX,"%s/%s",dirvrt2,fname);
            float *xt = iio_read_image_float(fullfname, &wt, &ht);
            for (int j = 0; j < pos[3]; j++)
                for (int i = 0; i < pos[2]; i++)
                {
                    int ii = i + pos[0];
                    int jj = j + pos[1];
                    if (insideP(w,h, ii,jj) && insideP(wt,ht, i,j))
                        xx[jj][ii] = xt[j*wt+i];
                }
            xfree(xt);
        }
    }
    return 0;
}

// FARBFELD reader                                                          {{{2
static int read_beheaded_ffd(struct iio_image *x,
                             FILE *fin, char *header, int nheader)
{
    (void)header;
    (void)nheader;
    for (int i = 0; i < 4; i++)
        pick_char_for_sure(fin);
    int s[8];
    for (int i = 0; i < 8; i++)
        s[i] = pick_char_for_sure(fin);
    uint32_t w = s[3] + 0x100 * s[2] + 0x10000 * s[1] + 0x1000000 * s[0];
    uint32_t h = s[7] + 0x100 * s[6] + 0x10000 * s[5] + 0x1000000 * s[4];

    x->dimension = 2;
    x->sizes[0] = w;
    x->sizes[1] = h;
    x->pixel_dimension = 4;
    x->type = IIO_TYPE_UINT16;
    x->contiguous_data = false;
    x->data = xmalloc(w * h * 4 * sizeof(uint16_t));
    uint32_t r = fread(x->data, 2, w*h*4, fin);
    if (r != w*h*4) return 1;
    switch_2endianness(x->data, r);
    return 0;
}

// RAW reader                                                               {{{2

// Note: there are two raw readers, either
//
// 1) the user supplies the dimensions and data format, or their location
// 2) the program uses several heuristics to find the dimensions
//
// They are named, respectively, explicit and fancy.
//
// Note2: the fancy reader is not yet implemented

//
// Documentation for the "explicit" raw reader
// -------------------------------------------
//
// Idea: to read a raw file named "file.xxx", open the image
// with name "RAW[...]:file.xxx".  The "..." specify the
// details of the raw format.
//
//
// Example:
//
// RAW[w320,h200,tFLOAT]:file.xxx
//
// This reads 320x200 floats from "file.xxx".
//
// The contents of [ ] are a list of "tokens", separated by ","
//
// Each token is a character followed by its value
//
// Valid characters with their meaning:
//
// w = width
// h = height
// p = pixel dimension (e.g. 1 or 3)
//
// t =  one of "INT8", "UINT8", "INT16", "UINT16", "INT32", "UINT32", "INT64",
// "UINT64", "FLOAT", "DOUBLE", "LONGDOUBLE", "HALF", "UINT1",
// "UINT2", "UINT4", "CHAR", "SHORT", "INT", "LONG", "LONGLONG",
//
// o = offset bytes to be ignored from the start of the file
//     (if negative, ignored from the byte after th end of the file)
//     ((default=-1 == EOF))
//
// b = 0,1 wether pixel channels are contiguous or broken into planes
//
// e = 0,1 controls the endianness.  By default, the native one
//
// r = xy, xY, Xy, XY, yx, Yx, yX, YX
//         controls the orientation of the coordinates (uppercase=reverse)
//
// All the numeric fields can be read from the same file.  For example,
// "w@44/2" says that the width is read from position 44 of the file
// as an uint16, etc.
//
//
// Annex: Specifying the RAW type using environment variables.
//
// Typically, when IIO fails to recognize the filetype, it tries some desperate
// measures.  One of this desperate measures is enabled by the environment
// variable  IIO_RAW, that specifies the "rawstring" of the file to be opened.
//

// if f ~ /RAW[.*]:.*/ return the position of the colon
static char *raw_prefix(const char *f)
{
    if (f != strstr(f, "RAW["))
        return NULL;
    char *colon = strchr(f, ':');
    if (!colon || colon[-1] != ']')
        return NULL;
    return colon;
}

//// if f ~ /RWA[.*]:.*/ return the position of the colon
//static char *rwa_prefix(const char *f)
//{
//	if (f != strstr(f, "RWA["))
//		return NULL;
//	char *colon = strchr(f, ':');
//	if (!colon || colon[-1] != ']')
//		return NULL;
//	return colon;
//}

// explicit raw reader (input = a given block of memory)
static int parse_raw_binary_image_explicit(struct iio_image *x,
        void *data, size_t ndata,
        int w, int h, int pd,
        int header_bytes, int sample_type,
        bool broken_pixels, bool endianness)
{
    (void)broken_pixels;
    size_t nsamples = w*h*pd;
    size_t ss = iio_type_size(sample_type);
    if (ndata < header_bytes + nsamples*ss)
    {
        fprintf(stderr, "WARNING:bad raw file size (%zu != %d + %zu)",
                ndata, header_bytes, nsamples*ss);
        //return 1;
    }
    int sizes[2] = {w, h};
    iio_image_build_independent(x, 2, sizes, sample_type, pd);
    size_t n = nsamples * ss;
    memcpy(x->data, header_bytes + (char*)data, n);
    if (endianness)
    {
        if (ss == 2)
            switch_2endianness(x->data, nsamples);
        if (ss >= 4)
            switch_4endianness(x->data, nsamples);
    }
    return 0;
}

// get an integer field from a data file, whose position
// and type is determined by "tok"
static int raw_gfp(void *dat, int siz, char *tok, int endianness)
{
    int fpos, fsiz = -4;
    if (2 == sscanf(tok, "%d/%d", &fpos, &fsiz));
    else if (1 == sscanf(tok, "%d", &fpos));
    else return 0;
    IIO_DEBUG("raw gfp tok=%s fpos=%d fiz=%d\n", tok, fpos, fsiz);
    void *pvalue = fpos + (char*)dat;
    if (fpos < 0 || abs(fsiz) + fpos > siz)
        fail("can not read field beyond data size");
    if (endianness && abs(fsiz)==2) switch_2endianness(pvalue, 1);
    if (endianness && abs(fsiz)==4) switch_4endianness(pvalue, 1);
    switch(fsiz)
    {
    case  1:
        return *( uint8_t*)pvalue;
    case  2:
        return *(uint16_t*)pvalue;
    case  4:
        return *(uint32_t*)pvalue;
    case -1:
        return *(  int8_t*)pvalue;
    case -2:
        return *( int16_t*)pvalue;
    case -4:
        return *( int32_t*)pvalue;
    default:
        fail("unrecognized field size %d", fsiz);
    }
}

static int read_raw_named_image(struct iio_image *x, const char *filespec)
{
    // filespec => description + filename
    char *colon = raw_prefix(filespec);
    size_t desclen = colon - filespec - 5;
    char description[desclen+1];
    char *filename = colon + 1;
    memcpy(description, filespec+4, desclen);
    description[desclen] = '\0';

    // read data from file
    long file_size;
    void *file_contents = NULL;
    {
        FILE *f = xfopen(filename, "r");
        file_contents = load_rest_of_file(&file_size, f, NULL, 0);
        xfclose(f);
    }

    // fill-in data description
    int width = -1;
    int height = -1;
    int pixel_dimension = 1;
    int brokenness = 0;
    int endianness = 0;
    int sample_type = IIO_TYPE_UINT8;
    int offset = -1;
    int orientation = 0;

    // parse description string
    char *delim = ",", *tok = strtok(description, delim);
    int field;
    while (tok)
    {
        IIO_DEBUG("\ttoken = %s\n", tok);
        if (tok[1] == '@')
            field = raw_gfp(file_contents, file_size, 2+tok,
                            endianness);
        else
            field = atoi(1+tok);
        IIO_DEBUG("\tfield=%d\n", field);
        switch(*tok)
        {
        case 'w':
            width           = field;
            break;
        case 'h':
            height          = field;
            break;
        case 'd':
        case 'p':
            pixel_dimension = field;
            break;
        case 'o':
            offset          = field;
            break;
        case 'b':
            brokenness      = field;
            break;
        case 'e':
            endianness      = field;
            break;
        case 't':
            sample_type     = iio_inttyp(1+tok);
            break;
        case 'r':
            orientation     = tok[1]+256*tok[2];
            break;
        }
        tok = strtok(NULL, delim);
    }
    int sample_size = iio_type_size(sample_type);

    IIO_DEBUG("w = %d\n", width);
    IIO_DEBUG("h = %d\n", height);
    IIO_DEBUG("p = %d\n", pixel_dimension);
    IIO_DEBUG("o = %d\n", offset);
    IIO_DEBUG("b = %d\n", brokenness);
    IIO_DEBUG("t = %s\n", iio_strtyp(sample_type));

    // estimate missing dimensions
    IIO_DEBUG("before estimation w=%d h=%d o=%d\n", width, height, offset);
    int pd = pixel_dimension;
    int ss = sample_size;
    if (offset < 0 && width > 0 && height > 0)
        offset = file_size - width * height * pd * ss;
    if (width < 0 && offset > 0 && height > 0)
        width = (file_size - offset)/(height * pd * ss);
    if (height < 0 && offset > 0 && width > 0)
        height = (file_size - offset)/(width * pd * ss);
    if (offset < 0) offset = 0;
    if (height < 0) height = file_size/(width * pd * ss);
    if (width  < 0) width  = file_size/(height * pd * ss);
    if (offset < 0 || width < 0 || height < 0)
        fail("could not determine width, height and offset"
             "(got %d,%d,%d)", width, height, offset);
    IIO_DEBUG("after estimation w=%d h=%d o=%d\n", width, height, offset);

    int used_data_size = offset+width*height*pd*ss;
    if (used_data_size > file_size)
        fail("raw file is not large enough");

    int r = parse_raw_binary_image_explicit(x,
                                            file_contents, file_size,
                                            width, height, pixel_dimension,
                                            offset, sample_type, brokenness, endianness);
    if (orientation)
        inplace_reorient(x, orientation);
    xfree(file_contents);
    return r;
}

// read a RAW image specified by the IIO_RAW environment
// caveat: the image *must* be a named file, not a pipe
// (this is for simplicity of the implementation, this restriction can be
// removed when necessary)
static int read_beheaded_raw(struct iio_image *x,
                             FILE *f, char *header, int nheader)
{
    (void)f;
    (void)header;
    (void)nheader;
    const char *fn;
    fn = global_variable_containing_the_name_of_the_last_opened_file;
    if (!fn)
        return 1;

    char *rp = getenv("IIO_RAW");
    if (!rp)
        return 2;

    char buf[FILENAME_MAX];
    snprintf(buf, FILENAME_MAX, "RAW[%s]:%s", rp, fn);
    IIO_DEBUG("simulating read of filename \"%s\"\n", buf);
    return read_raw_named_image(x, buf);
}

// WHATEVER reader                                                          {{{2

//static int read_image(struct iio_image*, const char *);
static int read_image_f(struct iio_image*, FILE *);
inline
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);
    xfclose(fin);
    char *filename = put_data_into_temporary_file(filedata, filesize);
    xfree(filedata);

    //char command_format[] = "convert - %s < %s\0";
    char command_format[] = "/usr/bin/convert - %s < %s\0";
    char ppmname[strlen(filename)+5];
    snprintf(ppmname, FILENAME_MAX, "%s.ppm", filename);
    char command[strlen(command_format)+1+2*strlen(filename)];
    snprintf(command, FILENAME_MAX, 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;
}


// RAW PHOTO reader                                                               {{{2

#ifdef I_USE_LIBRAW
int try_reading_file_with_libraw(const char *fname, struct iio_image *x);
int try_reading_file_with_libraw_4channels(const char *fname, struct iio_image *x);
#endif


// 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;
    if (bit_depth == 16) transforms |= PNG_TRANSFORM_SWAP_ENDIAN;
    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)
{
    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);
        break;
    case 4:
        TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
        TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, 1, caca);
        break;
    default:
        TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    }

    // disable TIFF compression when saving large images
    if (x->sizes[0] * x->sizes[1] < 2000*2000)
        TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_LZW);
    else
        TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);

    switch(x->type)
    {
    case IIO_TYPE_DOUBLE:
    case IIO_TYPE_FLOAT:
        tsf = SAMPLEFORMAT_IEEEFP;
        break;
    case IIO_TYPE_INT8:
    case IIO_TYPE_INT:
    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);

    // define TIFFTAG_ROWSPERSTRIP to satisfy some readers (e.g. gdal)
    uint32_t rows_per_strip = x->sizes[1];
    rows_per_strip = TIFFDefaultStripSize(tif, rows_per_strip);
    TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, rows_per_strip);

    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, "-"))
    {
        char tfn[FILENAME_MAX];
        fill_temporary_filename(tfn);
        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);
}

// PFM writer                                                               {{{2
static void iio_save_image_as_pfm(const char *filename, struct iio_image *x)
{
    assert(x->type == IIO_TYPE_FLOAT);
    assert(x->dimension == 2);
    assert(x->pixel_dimension == 1 || x->pixel_dimension == 3);
    FILE *f = xfopen(filename, "w");
    int dimchar = 1 < x->pixel_dimension ? 'F' : 'f';
    int w = x->sizes[0];
    int h = x->sizes[1];
    float scale = -1;
    fprintf(f, "P%c\n%d %d\n%g\n", dimchar, w, h, scale);
    fwrite(x->data, w * h * x->pixel_dimension * sizeof(float), 1,f);
    xfclose(f);
}

// ASC writer                                                               {{{2
inline
static void iio_save_image_as_asc(const char *filename, struct iio_image *x)
{
    FILE *f = xfopen(filename, "w");
    int w = x->sizes[0];
    int h = x->sizes[1];
    int d = x->sizes[2];
    int pd = x->pixel_dimension;
    float *t = x->data;
    fprintf(f, "%d %d %d %d\n", w, h, d, pd);
    for (int i = 0; i < w*h*d*pd; i++)
        fprintf(f, "%a\n", t[i]);
    fwrite(x->data, w * h * x->pixel_dimension * sizeof(float), 1,f);
    xfclose(f);
}

// CSV writer                                                               {{{2
static void iio_save_image_as_csv(const char *filename, struct iio_image *x)
{
    FILE *f = xfopen(filename, "w");
    int w = x->sizes[0];
    int h = x->sizes[1];
    int d = x->sizes[2];
    int pd = x->pixel_dimension;
    assert(d == 1);
    assert(pd == 1);
    assert(x->type == IIO_TYPE_FLOAT);
    float *t = x->data;
    for (int i = 0; i < w*h; i++)
        fprintf(f, "%.9g%c", t[i], (i+1)%w?',':'\n');
    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 = pick_char_for_sure(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 void line_to_header_buffer(FILE *f, uint8_t *buf, int *nbuf, int bufmax)
{
    while (*nbuf < bufmax)
    {
        int c = pick_char_for_sure(f);
        buf[*nbuf] = c;
        *nbuf += 1;
        if (c == '\n')
            break;
    }
}

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;

    //
    // hand-crafted state machine follows
    //

    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; // middlebury flow

    if (b[0]=='P' && b[1]=='D' && b[2]=='S' && b[3]=='_')
        return IIO_FORMAT_PDS; // NASA's planetary data science

    if (b[0]=='<' && b[1]=='V' && b[2]=='R' && b[3]=='T')
        return IIO_FORMAT_VRT; // gdal virtual image

    if (b[0]=='f' && b[1]=='a' && b[2]=='r' && b[3]=='b')
        return IIO_FORMAT_FFD; // farbfeld

    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;
        if (b[3]==0xee || b[3]==0xed) // Adobe JPEG
            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;
    if (b[8]=='1'&&b[9]=='2'&&b[10]=='L'&&b[11]=='I')
        return IIO_FORMAT_LUM;

    if (!strchr((char*)b, '\n'))
        line_to_header_buffer(f, b, nbuf, bufmax);
    int t[4];
    if (4 == sscanf((char*)b, "%d %d %d %d\n", t, t+1, t+2, t+3) && t[2]==1)
        return IIO_FORMAT_ASC;

    // fill the rest of the buffer, for computing statistics
    while (*nbuf < bufmax)
        add_to_header_buffer(f, b, nbuf, bufmax);

    bool buffer_statistics_agree_with_csv(uint8_t*, int);
    if (buffer_statistics_agree_with_csv(b, bufmax))
        return IIO_FORMAT_CSV;

    bool buffer_statistics_agree_with_dlm(uint8_t*, int);
    if (buffer_statistics_agree_with_dlm(b, bufmax))
        return IIO_FORMAT_DLM;

    if (getenv("IIO_RAW"))
        return IIO_FORMAT_RAW;

    return IIO_FORMAT_UNRECOGNIZED;
}

bool buffer_statistics_agree_with_csv(uint8_t *b, int n)
{
    char tmp[n+1];
    memcpy(tmp, b, n);
    tmp[n] = '\0';
    return (n = strspn(tmp, "0123456789.e+-,naifNAIF\n"));
    //IIO_DEBUG("strcspn(\"%s\") = %d\n", tmp, r);
}

bool buffer_statistics_agree_with_dlm(uint8_t *b, int n)
{
    char tmp[n+1];
    memcpy(tmp, b, n);
    tmp[n] = '\0';
    return (n = strspn(tmp, "0123456789.eE+- naifNAIF\n"));
    //IIO_DEBUG("strcspn(\"%s\") = %d\n", tmp, r);
}

static bool seekable_filenameP(const char *filename)
{
    if (filename[0] == '-')
        return false;
#ifdef I_CAN_POSIX
    FILE *f = xfopen(filename, "r");
    int r = fseek(f, 0, SEEK_CUR);
    xfclose(f);
    return r != -1;
#else
    return true;
#endif
}

static bool comma_named_tiff(const char *filename)
{
    char *comma = strrchr(filename, ',');
    if (!comma) return false;

    int lnumber = strlen(comma + 1);
    int ldigits = strspn(comma + 1, "0123456789");
    if (lnumber != ldigits) return false;

    char rfilename[FILENAME_MAX];
    strncpy(rfilename, filename, FILENAME_MAX);
    comma = rfilename + (comma - filename);
    *comma = '\0';

    bool retval = false;
    if (seekable_filenameP(rfilename))
    {
        FILE *f = xfopen(rfilename, "r");
        int bufmax = 0x100, nbuf, format;
        char buf[0x100] = {0};
        format = guess_format(f, buf, &nbuf, bufmax);
        retval = format == IIO_FORMAT_TIFF;
        xfclose(f);
    }
    return retval;
}

// 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_ASC:
        return read_beheaded_asc (x, f, h, hn);
    case IIO_FORMAT_BMP:
        return read_beheaded_bmp (x, f, h, hn);
    case IIO_FORMAT_PDS:
        return read_beheaded_pds (x, f, h, hn);
    case IIO_FORMAT_RAW:
        return read_beheaded_raw (x, f, h, hn);
    case IIO_FORMAT_CSV:
        return read_beheaded_csv (x, f, h, hn);
    case IIO_FORMAT_VRT:
        return read_beheaded_vrt (x, f, h, hn);
    case IIO_FORMAT_FFD:
        return read_beheaded_ffd (x, f, h, hn);
    case IIO_FORMAT_DLM:
        return read_beheaded_dlm (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.
// Nearly everything passes through here (except the "raw"  images)
//
static int read_image_f(struct iio_image *x, FILE *f)
{
    int bufmax = 0x100, nbuf, format;
    char buf[0x100] = {0};
    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)
{
    int r; // the return-value of this function

#ifndef IIO_ABORT_ON_ERROR
    if (setjmp(global_jump_buffer))
    {
        IIO_DEBUG("SOME ERROR HAPPENED AND WAS HANDLED\n");
        return 1;
    }
    //if (iio_single_jmpstuff(false, true)) {
    //	IIO_DEBUG("SOME ERROR HAPPENED AND WAS HANDLED\n");
    //	exit(42);
    //}
#endif//IIO_ABORT_ON_ERROR

    // check for semantical name
    if (fname == strstr(fname, "zero:"))
    {
        int s[2], pd = 1;
        if (3 == sscanf(fname+5, "%dx%d,%d", s, s+1, &pd));
        else if (2 == sscanf(fname+5, "%dx%d", s, s+1));
        else fail("bad semantical name \"%s\"", fname);
        iio_image_build_independent(x, 2, s, IIO_TYPE_CHAR, pd);
        for (int i = 0; i < *s*s[1]*pd; i++)
            ((char*)x->data)[i] = 0;
        return 0;
    }
    if (fname == strstr(fname, "one:"))
    {
        int s[2], pd = 1;
        if (3 == sscanf(fname+4, "%dx%d,%d", s, s+1, &pd));
        else if (2 == sscanf(fname+4, "%dx%d", s, s+1));
        else fail("bad semantical name \"%s\"", fname);
        iio_image_build_independent(x, 2, s, IIO_TYPE_CHAR, pd);
        for (int i = 0; i < *s*s[1]*pd; i++)
            ((char*)x->data)[i] = 1;
        return 0;
    }
    if (fname == strstr(fname, "constant:"))
    {
        float value;
        int s[2], pd = 1;
        if (3 == sscanf(fname+9, "%g:%dx%d", &value, s, s+1));
        else fail("bad semantical name \"%s\"", fname);
        iio_image_build_independent(x, 2, s, IIO_TYPE_CHAR, pd);
        for (int i = 0; i < *s*s[1]*pd; i++)
            ((char*)x->data)[i] = value;
        return 0;
    }


#ifdef I_CAN_HAS_WGET
    // check for URL
    if (fname == strstr(fname, "http://")
            || fname==strstr(fname, "https://") )
    {
        // TODO: for security, sanitize the fname
        char tfn[FILENAME_MAX], cmd[FILENAME_MAX];
        fill_temporary_filename(tfn);
        snprintf(cmd, FILENAME_MAX, "wget %s -q -O %s", fname, tfn);
        int rsys = system(cmd);
        if (rsys != 0) fail("system wget returned %d", rsys);
        FILE *f = xfopen(tfn, "r");
        r = read_image_f(x, f);
        xfclose(f);
        delete_temporary_file(tfn);

    }
    else
#endif//I_CAN_HAS_WGET

        if (false)
        {
            ;
#ifdef I_CAN_HAS_LIBTIFF
        }
        else if (comma_named_tiff(fname))
        {
            r = read_whole_tiff(x, fname);
#endif//I_CAN_HAS_LIBTIFF
#ifdef I_USE_LIBRAW
        }
        else if (try_reading_file_with_libraw(fname, x))
        {
            r=0;
#endif//I_USE_LIBRAW
        }
        else if (raw_prefix(fname))
        {
            r = read_raw_named_image(x, fname);
            //} else if (rwa_prefix(fname)) {
            //	r = read_rwa_named_image(x, fname);
        }
        else
        {
            // call CORE
            FILE *f = xfopen(fname, "r");
            r = read_image_f(x, f);
            xfclose(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("invalid 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;
    }
    *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);
    if (!r) return rfail("could not read image");
    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
int *iio_read_image_int(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_INT);
    return x->data;
}

// API 2D
uint8_t *iio_read_image_uint8(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_UINT8);
    return x->data;
}


// API (output)                                                             {{{1

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

static bool this_float_is_actually_a_short(float x)
{
    return (x == floor(x)) && (x >= 0) && (x < 65536);
}

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;
}

inline static bool these_floats_are_actually_shorts(float *t, int n)
{
    IIO_DEBUG("checking %d floats for shortness (%p)\n", n, (void*)t);
    FORI(n)
    if (!this_float_is_actually_a_short(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");
    //if (raw_prefix(fname)) {
    //	r = write_raw_named_image(fname, x);
    //	return;
    //}
    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, ".pfm") && typ == IIO_TYPE_FLOAT
            && (x->pixel_dimension == 1 || x->pixel_dimension == 3))
    {
        iio_save_image_as_pfm(filename, x);
        return;
    }
    if (string_suffix(filename, ".csv") && typ == IIO_TYPE_FLOAT
            && x->pixel_dimension == 1)
    {
        iio_save_image_as_csv(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;
    }
#ifdef I_CAN_HAS_LIBTIFF
    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;
    }
#endif//I_CAN_HAS_LIBTIFF
    if (typ != IIO_TYPE_DOUBLE && typ != IIO_TYPE_FLOAT && typ != IIO_TYPE_UINT8 && typ != IIO_TYPE_INT16 && typ != IIO_TYPE_INT8 && typ != IIO_TYPE_UINT32 && typ != IIO_TYPE_UINT16)
        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;
    }
#ifdef I_CAN_HAS_LIBTIFF
    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;
        }
    }
#endif//I_CAN_HAS_LIBTIFF
#ifdef I_CAN_HAS_LIBPNG
    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;
            }
            if (typ == IIO_TYPE_INT || typ == IIO_TYPE_UINT32)
            {
                void *old_data = x->data;
                x->data = xmalloc(nsamp*sizeof(int));
                memcpy(x->data, old_data, nsamp*sizeof(int));
                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)
    {
        char *pngname = strstr(filename, "PNG16:");
        if (pngname == filename)
        {
            if (typ != IIO_TYPE_UINT16)
            {
                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_UINT16);
                iio_save_image_default(filename, x);//recursive
                xfree(x->data);
                x->data = old_data;
                return;
            }
            iio_save_image_as_png(filename+6, 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;
            }
            if (typ == IIO_TYPE_INT || typ == IIO_TYPE_UINT32)
            {
                void *old_data = x->data;
                x->data = xmalloc(nsamp*sizeof(int));
                memcpy(x->data, old_data, nsamp*sizeof(int));
                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;
        }
    }
#endif//I_CAN_HAS_LIBPNG
    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]);
        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
#ifdef I_CAN_HAS_LIBTIFF
        iio_save_image_as_tiff_smarter(filename, x);
#else
        fail("\n\n\nThis particular data format can not yet be saved."
             "\nPlease, ask enric.\n");
#endif//I_CAN_HAS_LIBTIFF
    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_int_vec(char *filename, int *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_INT;
    x->data = data;
    x->contiguous_data = false;
    iio_save_image_default(filename, x);
}

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_double_split(char *filename, double *data,
                                 int w, int h, int pd)
{
    double *rdata = xmalloc(w*h*pd*sizeof*rdata);
    recover_broken_pixels_double(rdata, data, w*h, pd);
    iio_save_image_double_vec(filename, rdata, w, h, pd);
    xfree(rdata);
}

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_int(char *filename, int *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_INT;
    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