efl/src/modules/evas/loaders/pmaps/evas_image_load_pmaps.c

596 lines
12 KiB
C

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef HAVE_EVIL
# include <Evil.h>
#endif
#include "evas_common.h"
#include "evas_private.h"
#define FILE_BUFFER_SIZE 1024 * 32
#define FILE_BUFFER_UNREAD_SIZE 16
/* The buffer to load pmaps images */
typedef struct Pmaps_Buffer Pmaps_Buffer;
struct Pmaps_Buffer
{
Eina_File *file;
unsigned char *map;
size_t position;
/* the buffer */
DATA8 buffer[FILE_BUFFER_SIZE];
DATA8 unread[FILE_BUFFER_UNREAD_SIZE];
DATA8 *current;
DATA8 *end;
char type[3];
unsigned char unread_len:7;
unsigned char last_buffer:1;
/* image properties */
int w;
int h;
int max;
/* interface */
int (*int_get) (Pmaps_Buffer *b, int *val);
int (*color_get) (Pmaps_Buffer *b, DATA32 *color);
};
/* internal used functions */
static Eina_Bool pmaps_buffer_open(Pmaps_Buffer *b, Eina_File *f, int *error);
static void pmaps_buffer_close(Pmaps_Buffer *b);
static Eina_Bool pmaps_buffer_header_parse(Pmaps_Buffer *b, int *error);
static int pmaps_buffer_plain_int_get(Pmaps_Buffer *b, int *val);
static int pmaps_buffer_1byte_int_get(Pmaps_Buffer *b, int *val);
static int pmaps_buffer_2byte_int_get(Pmaps_Buffer *b, int *val);
static int pmaps_buffer_gray_get(Pmaps_Buffer *b, DATA32 *color);
static int pmaps_buffer_rgb_get(Pmaps_Buffer *b, DATA32 *color);
static int pmaps_buffer_plain_bw_get(Pmaps_Buffer *b, DATA32 *color);
static size_t pmaps_buffer_plain_update(Pmaps_Buffer *b);
static size_t pmaps_buffer_raw_update(Pmaps_Buffer *b);
static int pmaps_buffer_comment_skip(Pmaps_Buffer *b);
static void *
evas_image_load_file_open_pmaps(Eina_File *f, const char *key EINA_UNUSED,
Evas_Image_Load_Opts *opts EINA_UNUSED,
Evas_Image_Animated *animated EINA_UNUSED,
int *error EINA_UNUSED)
{
return f;
}
static void
evas_image_load_file_close_pmaps(void *loader_data EINA_UNUSED)
{
}
static Eina_Bool
evas_image_load_file_head_pmaps(void *loader_data,
Evas_Image_Property *prop,
int *error)
{
Eina_File *f = loader_data;
Pmaps_Buffer b;
if (!pmaps_buffer_open(&b, f, error))
{
pmaps_buffer_close(&b);
return EINA_FALSE;
}
if (!pmaps_buffer_header_parse(&b, error))
{
pmaps_buffer_close(&b);
return EINA_FALSE;
}
prop->w = b.w;
prop->h = b.h;
pmaps_buffer_close(&b);
*error = EVAS_LOAD_ERROR_NONE;
return EINA_TRUE;
}
static Eina_Bool
evas_image_load_file_data_pmaps(void *loader_data,
Evas_Image_Property *prop,
void *pixels,
int *error)
{
Eina_File *f = loader_data;
Pmaps_Buffer b;
int size;
DATA32 *ptr;
Eina_Bool r = EINA_FALSE;
if (!pmaps_buffer_open(&b, f, error))
goto on_error;
if (!pmaps_buffer_header_parse(&b, error))
goto on_error;
size = b.w * b.h;
if ((int) prop->w != b.w ||
(int) prop->h != b.h)
goto on_error;
ptr = pixels;
if (b.type[1] != '4')
{
while (size > 0 && b.color_get(&b, ptr))
{
size--;
ptr++;
}
}
else
{
while (size > 0
&& (b.current != b.end || pmaps_buffer_raw_update(&b)))
{
int i;
for (i = 7; i >= 0 && size > 0; i--)
{
if (*b.current & (1 << i))
*ptr = 0xff000000;
else
*ptr = 0xffffffff;
ptr++;
size--;
}
b.current++;
}
}
/* if there are some pix missing, give them a proper default */
memset(ptr, 0xff, 4 * size);
*error = EVAS_LOAD_ERROR_NONE;
r = EINA_TRUE;
on_error:
pmaps_buffer_close(&b);
return r;
}
/* internal used functions */
static Eina_Bool
pmaps_buffer_open(Pmaps_Buffer *b, Eina_File *f, int *error)
{
size_t len;
b->file = f;
b->map = eina_file_map_all(b->file, EINA_FILE_SEQUENTIAL);
if (!b->map)
{
*error = EVAS_LOAD_ERROR_DOES_NOT_EXIST;
eina_file_close(b->file);
b->file = NULL;
return EINA_FALSE;
}
b->position = 0;
*b->buffer = 0;
*b->unread = 0;
b->last_buffer = 0;
b->unread_len = 0;
len = pmaps_buffer_plain_update(b);
if (len < 3)
{
*error = EVAS_LOAD_ERROR_CORRUPT_FILE;
eina_file_map_free(b->file, b->map);
eina_file_close(b->file);
b->map = NULL;
b->file = NULL;
return EINA_FALSE;
}
/* copy the type */
b->type[0] = b->buffer[0];
b->type[1] = b->buffer[1];
b->type[2] = 0;
/* skip the PX */
b->current = b->buffer + 2;
*error = EVAS_LOAD_ERROR_NONE;
return EINA_TRUE;
}
static void
pmaps_buffer_close(Pmaps_Buffer *b)
{
if (b->file)
{
if (b->map) eina_file_map_free(b->file, b->map);
b->map = NULL;
b->file = NULL;
}
}
static Eina_Bool
pmaps_buffer_header_parse(Pmaps_Buffer *b, int *error)
{
/* if there is no P at the beginning it is not a file we can parse */
if (b->type[0] != 'P')
{
*error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
return EINA_FALSE;
}
/* get the width */
if (!pmaps_buffer_plain_int_get(b, &(b->w)) || b->w < 1)
{
*error = EVAS_LOAD_ERROR_CORRUPT_FILE;
return EINA_FALSE;
}
/* get the height */
if (!pmaps_buffer_plain_int_get(b, &(b->h)) || b->h < 1)
{
*error = EVAS_LOAD_ERROR_CORRUPT_FILE;
return EINA_FALSE;
}
/* get the maximum value. P1 and P4 don't have a maximum value. */
if (!(b->type[1] == '1' || b->type[1] == '4')
&& (!pmaps_buffer_plain_int_get(b, &(b->max)) || b->max < 1))
{
*error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
return EINA_FALSE;
}
/* set up the color get callback */
switch (b->type[1])
{
/* Black and White */
case '1':
b->color_get = pmaps_buffer_plain_bw_get;
break;
case '4':
/* Binary black and white use another format */
b->color_get = NULL;
break;
case '2':
case '5':
b->color_get = pmaps_buffer_gray_get;
break;
case '3':
case '6':
b->color_get = pmaps_buffer_rgb_get;
break;
case '7':
/* XXX write me */
return 0;
break;
default:
return 0;
}
/* set up the int get callback */
switch (b->type[1])
{
/* RAW */
case '5':
case '6':
if (b->max < 256)
b->int_get = pmaps_buffer_1byte_int_get;
else
b->int_get = pmaps_buffer_2byte_int_get;
if (b->current == b->end && !pmaps_buffer_raw_update(b))
return 0;
b->current++;
break;
/* Plain */
case '2':
case '3':
b->int_get = pmaps_buffer_plain_int_get;
break;
/* Black and White Bitmaps don't use that callback */
case '1':
case '4':
b->int_get = NULL;
/* we need to skip the next character fpr P4 it
* doesn't hurt if we do it for the P1 as well */
b->current++;
break;
}
return 1;
}
static size_t
pmaps_buffer_plain_update(Pmaps_Buffer *b)
{
size_t r;
size_t max;
/* if we already are in the last buffer we can not update it */
if (b->last_buffer)
return 0;
/* if we have unread bytes we need to put them before the new read
* stuff */
if (b->unread_len)
memcpy(b->buffer, b->unread, b->unread_len);
max = FILE_BUFFER_SIZE - b->unread_len - 1;
if (b->position + max > eina_file_size_get(b->file))
max = eina_file_size_get(b->file) - b->position;
memcpy(&b->buffer[b->unread_len], b->map + b->position, max);
b->position += max;
r = max + b->unread_len;
/* we haven't read anything nor have we bytes in the unread buffer */
if (r == 0)
{
b->buffer[0] = '\0';
b->end = b->buffer;
b->last_buffer = 1;
return 0;
}
if (r < FILE_BUFFER_SIZE - 1)
{
/*we reached eof */ ;
b->last_buffer = 1;
}
b->buffer[r] = 0;
b->unread[0] = '\0';
b->unread_len = 0;
b->buffer[r] = '\0';
b->current = b->buffer;
b->end = b->buffer + r;
return r;
}
static size_t
pmaps_buffer_raw_update(Pmaps_Buffer *b)
{
size_t r;
size_t max;
if (b->last_buffer)
return 0;
if (b->unread_len)
memcpy(b->buffer, b->unread, b->unread_len);
max = FILE_BUFFER_SIZE - b->unread_len;
if (b->position + max > eina_file_size_get(b->file))
max = eina_file_size_get(b->file) - b->position;
memcpy(&b->buffer[b->unread_len], b->map + b->position, max);
b->position += max;
r = max + b->unread_len;
if (r < FILE_BUFFER_SIZE)
{
/*we reached eof */
b->last_buffer = 1;
}
b->end = b->buffer + r;
b->current = b->buffer;
if (b->unread_len)
{
/* the buffer is now read */
*b->unread = 0;
b->unread_len = 0;
}
return r;
}
static int
pmaps_buffer_plain_int_get(Pmaps_Buffer *b, int *val)
{
char *start;
DATA8 lastc;
/* first skip all white space
* Note: we are skipping here actually every character than is not
* a digit */
while (!isdigit(*b->current))
{
if (*b->current == '\0')
{
if (!pmaps_buffer_plain_update(b))
return 0;
continue;
}
if (*b->current == '#' && !pmaps_buffer_comment_skip(b))
return 0;
b->current++;
}
start = (char *)b->current;
/* now find the end of the number */
while (isdigit(*b->current))
b->current++;
lastc = *b->current;
*b->current = '\0';
*val = atoi(start);
*b->current = lastc;
return 1;
}
static int
pmaps_buffer_1byte_int_get(Pmaps_Buffer *b, int *val)
{
/* are we at the end of the buffer? */
if (b->current == b->end && !pmaps_buffer_raw_update(b))
return 0;
*val = *b->current;
b->current++;
return 1;
}
static int
pmaps_buffer_2byte_int_get(Pmaps_Buffer *b, int *val)
{
/* are we at the end of the buffer? */
if (b->current == b->end && !pmaps_buffer_raw_update(b))
return 0;
*val = (int)(*b->current << 8);
b->current++;
/* are we at the end of the buffer? */
if (b->current == b->end && !pmaps_buffer_raw_update(b))
return 0;
*val |= *b->current;
b->current++;
return 1;
}
static int
pmaps_buffer_comment_skip(Pmaps_Buffer *b)
{
while (*b->current != '\n')
{
if (*b->current == '\0')
{
if (!pmaps_buffer_plain_update(b))
return 0;
continue;
}
b->current++;
}
return 1;
}
static int
pmaps_buffer_rgb_get(Pmaps_Buffer *b, DATA32 *color)
{
int vr, vg, vb;
if (!b->int_get(b, &vr) || !b->int_get(b, &vg) || !b->int_get(b, &vb))
return 0;
if (b->max != 255)
{
vr = (vr * 255) / b->max;
vg = (vg * 255) / b->max;
vb = (vb * 255) / b->max;
}
if (vr > 255)
vr = 255;
if (vg > 255)
vg = 255;
if (vb > 255)
vb = 255;
*color = ARGB_JOIN(0xff, vr, vg, vb);
return 1;
}
static int
pmaps_buffer_gray_get(Pmaps_Buffer *b, DATA32 *color)
{
int val;
if (!b->int_get(b, &val))
return 0;
if (b->max != 255)
val = (val * 255) / b->max;
if (val > 255)
val = 255;
*color = ARGB_JOIN(0xff, val, val, val);
return 1;
}
static int
pmaps_buffer_plain_bw_get(Pmaps_Buffer *b, DATA32 *val)
{
/* first skip all white space
* Note: we are skipping here actually every character than is not
* a digit */
while (!isdigit(*b->current))
{
if (*b->current == '\0')
{
if (!pmaps_buffer_raw_update(b))
return 0;
continue;
}
if (*b->current == '#' && !pmaps_buffer_comment_skip(b))
return 0;
b->current++;
}
if (*b->current == '0')
*val = 0xffffffff;
else
*val = 0xff000000;
b->current++;
return 1;
}
/* external functions */
Evas_Image_Load_Func evas_image_load_pmaps_func = {
EINA_TRUE,
evas_image_load_file_open_pmaps,
evas_image_load_file_close_pmaps,
evas_image_load_file_head_pmaps,
evas_image_load_file_data_pmaps,
NULL,
EINA_FALSE
};
static int
module_open(Evas_Module *em)
{
if (!em)
return 0;
em->functions = (void *)(&evas_image_load_pmaps_func);
return 1;
}
static void
module_close(Evas_Module *em EINA_UNUSED)
{
}
static Evas_Module_Api evas_modapi = {
EVAS_MODULE_API_VERSION,
"pmaps",
"none",
{
module_open,
module_close
}
};
EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, pmaps);
#ifndef EVAS_STATIC_BUILD_PMAPS
EVAS_EINA_MODULE_DEFINE(image_loader, pmaps);
#endif