Evas Jpeg2000 loader: port it to openjpeg 2.*

Test Plan: files in https://github.com/uclouvain/openjpeg-data/tree/master/baseline/nonregression

Reviewers: jpeg, raster, cedric

Differential Revision: https://phab.enlightenment.org/D5033
This commit is contained in:
Vincent Torri 2017-08-08 11:32:47 +09:00 committed by Carsten Haitzler (Rasterman)
parent d5ad85abae
commit db29ee1cd6
2 changed files with 209 additions and 115 deletions

View File

@ -273,7 +273,7 @@ AC_CHECK_HEADER([openjpeg.h], [have_dep="yes"])
if test "x${have_dep}" = "xyes" ; then if test "x${have_dep}" = "xyes" ; then
AC_CHECK_LIB([openjp2], AC_CHECK_LIB([openjp2],
[opj_cio_open], [opj_stream_create],
[ [
evas_image_loader_[]$1[]_libs="-lopenjp2" evas_image_loader_[]$1[]_libs="-lopenjp2"
have_dep="yes" have_dep="yes"
@ -282,21 +282,11 @@ if test "x${have_dep}" = "xyes" ; then
fi fi
if test "x${have_dep}" = "xno" ; then if test "x${have_dep}" = "xno" ; then
PKG_CHECK_EXISTS([libopenjpeg1 >= 1.5], PKG_CHECK_EXISTS([libopenjp2 >= 2.0],
[ [
have_dep="yes" have_dep="yes"
have_dep_pc="yes" have_dep_pc="yes"
requirement="libopenjpeg1 >= 1.5" requirement="libopenjp2 >= 2.0"
],
[have_dep="no"])
fi
if test "x${have_dep}" = "xno" ; then
PKG_CHECK_EXISTS([libopenjpeg >= 1.5],
[
have_dep="yes"
have_dep_pc="yes"
requirement="libopenjpeg >= 1.5"
], ],
[have_dep="no"]) [have_dep="no"])
fi fi

View File

@ -29,148 +29,257 @@ static int _evas_loader_jp2k_log_dom = -1;
#endif #endif
#define INF(...) EINA_LOG_DOM_INFO(_evas_loader_jp2k_log_dom, __VA_ARGS__) #define INF(...) EINA_LOG_DOM_INFO(_evas_loader_jp2k_log_dom, __VA_ARGS__)
#define J2K_CODESTREAM_MAGIC "\xff\x4f\xff\x51"
#define JP2_MAGIC "\x0d\x0a\x87\x0a"
#define JP2_RFC3745_MAGIC "\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a"
typedef struct
{
unsigned char *base;
size_t length;
size_t idx;
} Map_St;
typedef struct _Evas_Loader_Internal Evas_Loader_Internal; typedef struct _Evas_Loader_Internal Evas_Loader_Internal;
struct _Evas_Loader_Internal struct _Evas_Loader_Internal
{ {
Eina_File *f; Eina_File *f;
Evas_Image_Load_Opts *opts; Evas_Image_Load_Opts *opts;
}; };
static void static void
_jp2k_error_cb(const char *msg EINA_UNUSED, void *data EINA_UNUSED) _jp2k_quiet_callback(const char *msg, void *client_data)
{ {
// ERR("OpenJPEG internal error: '%s'.", msg); (void)msg;
(void)client_data;
} }
static void static OPJ_SIZE_T
_jp2k_warning_cb(const char *msg EINA_UNUSED, void *data EINA_UNUSED) _jp2k_read_fn(void *buf, OPJ_SIZE_T size, void *data)
{ {
// WRN("OpenJPEG internal warning: '%s'.", msg); Map_St *map = data;
OPJ_SIZE_T offset;
offset = map->length - map->idx;
if (offset == 0)
return (OPJ_SIZE_T)-1;
if (offset > size)
offset = size;
memcpy(buf, map->base + map->idx, offset);
map->idx += offset;
return offset;
} }
static void static OPJ_OFF_T
_jp2k_info_cb(const char *msg EINA_UNUSED, void *data EINA_UNUSED) _jp2k_seek_cur_fn(OPJ_OFF_T size, void *data)
{ {
// INF("OpenJPEG internal information: '%s'.", msg); Map_St *map = data;
if (size > (OPJ_OFF_T)(map->length - map->idx))
size = (OPJ_OFF_T)(map->length - map->idx);
map->idx += size;
return map->idx;
}
static OPJ_BOOL
_jp2k_seek_set_fn(OPJ_OFF_T size, void *data)
{
Map_St *map = data;
if (size > (OPJ_OFF_T)map->length)
return OPJ_FALSE;
map->idx = size;
return OPJ_TRUE;
} }
static Eina_Bool static Eina_Bool
evas_image_load_file_head_jp2k_internal(unsigned int *w, unsigned int *h, evas_image_load_file_head_jp2k_internal(unsigned int *w, unsigned int *h,
unsigned char *alpha, unsigned char *alpha,
Evas_Image_Load_Opts *opts EINA_UNUSED,
void *map, size_t length, void *map, size_t length,
int *error) int *error)
{ {
opj_event_mgr_t event_mgr; Map_St map_st;
opj_dparameters_t params; opj_dparameters_t core;
opj_dinfo_t *info; opj_codec_t *codec;
opj_cio_t *cio; opj_stream_t *st;
opj_image_t *image; opj_image_t* image;
int format; OPJ_CODEC_FORMAT cfmt;
int k;
const unsigned char sig_j2k[2] =
{ 0xff, 0x4f };
const unsigned char sig_jp2[10] =
{ 0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50, 0x20, 0x20, 0x0d, 0x0a };
if (length < 2) map_st.base = map;
map_st.length = length;
map_st.idx = 0;
/* default parameters */
memset(&core, 0, sizeof(opj_dparameters_t));
opj_set_default_decoder_parameters(&core);
/* magic check */
cfmt = OPJ_CODEC_UNKNOWN;
if (map_st.length >= 4)
{ {
if (memcmp(map_st.base, J2K_CODESTREAM_MAGIC, 4) == 0)
cfmt = OPJ_CODEC_J2K;
else if ((memcmp(map_st.base, JP2_MAGIC, 4) == 0) ||
((map_st.length >= 12) && (memcmp(map_st.base, JP2_RFC3745_MAGIC, 12) == 0)))
cfmt = OPJ_CODEC_JP2;
}
if (cfmt == OPJ_CODEC_UNKNOWN)
{
ERR("jpeg200 file format invalid");
*error = EVAS_LOAD_ERROR_GENERIC; *error = EVAS_LOAD_ERROR_GENERIC;
return EINA_FALSE; return EINA_FALSE;
} }
if ((length >= 2) && (!memcmp(map, sig_j2k, 2))) format = CODEC_J2K; /* codec */
else if ((length >= 10) && (!memcmp(map, sig_jp2, 10))) format = CODEC_JP2; codec = opj_create_decompress(cfmt);
else return EINA_FALSE; if (!codec)
memset(&event_mgr, 0, sizeof(event_mgr));
event_mgr.error_handler = _jp2k_error_cb;
event_mgr.warning_handler = _jp2k_warning_cb;
event_mgr.info_handler = _jp2k_info_cb;
opj_set_default_decoder_parameters(&params);
info = opj_create_decompress(format);
if (!info)
{ {
ERR("can't create codec");
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
return EINA_FALSE; return EINA_FALSE;
} }
opj_set_event_mgr((opj_common_ptr)info, &event_mgr, NULL); opj_set_info_handler(codec, _jp2k_quiet_callback, NULL);
opj_setup_decoder(info, &params); opj_set_warning_handler(codec, _jp2k_quiet_callback, NULL);
opj_set_error_handler(codec, _jp2k_quiet_callback, NULL);
cio = opj_cio_open((opj_common_ptr)info, map, length); if (!opj_setup_decoder(codec, &core))
if (!cio)
{ {
ERR("can't setup decoder");
opj_destroy_codec(codec);
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
return EINA_FALSE;
}
//opj_codec_set_threads(codec, 0)
/* stream */
st = opj_stream_create(OPJ_J2K_STREAM_CHUNK_SIZE, OPJ_TRUE);
if (!st)
{
ERR("can't create stream");
opj_destroy_codec(codec);
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
return EINA_FALSE; return EINA_FALSE;
} }
image = opj_decode(info, cio); opj_stream_set_user_data(st, &map_st, NULL);
if (!image) opj_stream_set_user_data_length(st, map_st.length);
{ opj_stream_set_read_function(st, _jp2k_read_fn);
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; opj_stream_set_skip_function(st, _jp2k_seek_cur_fn);
return EINA_FALSE; opj_stream_set_seek_function(st, _jp2k_seek_set_fn);
}
for (k = 1; k < image->numcomps; k++) opj_read_header(st, codec, &image);
{ *w = image->x1 - image->x0;
if (image->comps[k].w != image->comps[0].w) *h = image->y1 - image->y0;
goto free_image;
if (image->comps[k].h != image->comps[0].h)
goto free_image;
if (image->comps[k].prec > 8)
goto free_image;
}
*w = image->comps[0].w;
*h = image->comps[0].h;
*alpha = ((image->numcomps == 4) || (image->numcomps == 2)) ? 1 : 0; *alpha = ((image->numcomps == 4) || (image->numcomps == 2)) ? 1 : 0;
*error = EVAS_LOAD_ERROR_NONE; *error = EVAS_LOAD_ERROR_NONE;
opj_image_destroy(image); opj_image_destroy(image);
opj_cio_close(cio); opj_stream_destroy(st);
opj_destroy_decompress(info); opj_destroy_codec(codec);
return EINA_TRUE; return EINA_TRUE;
free_image:
*error = EVAS_LOAD_ERROR_GENERIC;
opj_image_destroy(image);
opj_cio_close(cio);
opj_destroy_decompress(info);
return EINA_FALSE;
} }
static Eina_Bool static Eina_Bool
evas_image_load_file_data_jp2k_internal(Evas_Image_Load_Opts *opts EINA_UNUSED, evas_image_load_file_data_jp2k_internal(void *pixels,
Evas_Image_Property *prop EINA_UNUSED,
void *pixels,
void *map, size_t length, void *map, size_t length,
int *error) int *error)
{ {
opj_dparameters_t params; Map_St map_st;
opj_dinfo_t *info; opj_dparameters_t core;
opj_cio_t *cio; opj_codec_t *codec;
opj_image_t *image; opj_stream_t *st;
opj_image_t* image;
unsigned int *iter; unsigned int *iter;
int format; OPJ_CODEC_FORMAT cfmt;
int idx; int idx;
const unsigned char sig_j2k[2] =
{ 0xff, 0x4f };
const unsigned char sig_jp2[10] =
{ 0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50, 0x20, 0x20, 0x0d, 0x0a };
if ((length >= 2) && (!memcmp(map, sig_j2k, 2))) format = CODEC_J2K; map_st.base = map;
else if ((length >= 10) && (!memcmp(map, sig_jp2, 10))) format = CODEC_JP2; map_st.length = length;
else return EINA_FALSE; map_st.idx = 0;
opj_set_default_decoder_parameters(&params); /* default parameters */
info = opj_create_decompress(format); memset(&core, 0, sizeof(opj_dparameters_t));
opj_set_event_mgr((opj_common_ptr)info, NULL, NULL); opj_set_default_decoder_parameters(&core);
opj_setup_decoder(info, &params); core.flags |= OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG;
cio = opj_cio_open((opj_common_ptr)info, map, length);
image = opj_decode(info, cio); /* magic check */
cfmt = OPJ_CODEC_UNKNOWN;
if (map_st.length >= 4)
{
if (memcmp(map_st.base, J2K_CODESTREAM_MAGIC, 4) == 0)
cfmt = OPJ_CODEC_J2K;
else if ((memcmp(map_st.base, JP2_MAGIC, 4) == 0) ||
((map_st.length >= 12) && (memcmp(map_st.base, JP2_RFC3745_MAGIC, 12) == 0)))
cfmt = OPJ_CODEC_JP2;
}
if (cfmt == OPJ_CODEC_UNKNOWN)
{
ERR("jpeg200 file format invalid\n");
*error = EVAS_LOAD_ERROR_GENERIC;
return EINA_FALSE;
}
/* codec */
codec = opj_create_decompress(cfmt);
if (!codec)
{
ERR("can't create codec\n");
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
return EINA_FALSE;
}
opj_set_info_handler(codec, _jp2k_quiet_callback, NULL);
opj_set_warning_handler(codec, _jp2k_quiet_callback, NULL);
opj_set_error_handler(codec, _jp2k_quiet_callback, NULL);
if (!opj_setup_decoder(codec, &core))
{
ERR("can't setup decoder\n");
opj_destroy_codec(codec);
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
return EINA_FALSE;
}
//opj_codec_set_threads(codec, 0)
/* stream */
st = opj_stream_create(OPJ_J2K_STREAM_CHUNK_SIZE, OPJ_TRUE);
if (!st)
{
ERR("can't create stream\n");
opj_destroy_codec(codec);
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
return EINA_FALSE;
}
opj_stream_set_user_data(st, &map_st, NULL);
opj_stream_set_user_data_length(st, map_st.length);
opj_stream_set_read_function(st, _jp2k_read_fn);
opj_stream_set_skip_function(st, _jp2k_seek_cur_fn);
opj_stream_set_seek_function(st, _jp2k_seek_set_fn);
if (!opj_read_header(st, codec, &image))
{
ERR("can not read image header\n");
opj_stream_destroy(st);
opj_destroy_codec(codec);
*error = EVAS_LOAD_ERROR_GENERIC;
return EINA_FALSE;
}
if (!(opj_decode(codec, st, image) && opj_end_decompress(codec, st)))
{
ERR("can not decode image\n");
opj_image_destroy(image);
opj_stream_destroy(st);
opj_destroy_codec(codec);
*error = EVAS_LOAD_ERROR_GENERIC;
return EINA_FALSE;
}
iter = pixels; iter = pixels;
idx = 0; idx = 0;
@ -190,8 +299,8 @@ evas_image_load_file_data_jp2k_internal(Evas_Image_Load_Opts *opts EINA_UNUSED,
int r; int r;
int g; int g;
int b; int b;
int i; unsigned int i;
int j; unsigned int j;
for (j = 0; j < image->comps[0].h; j++) for (j = 0; j < image->comps[0].h; j++)
{ {
@ -235,8 +344,8 @@ evas_image_load_file_data_jp2k_internal(Evas_Image_Load_Opts *opts EINA_UNUSED,
{ {
int a; int a;
int g; int g;
int i; unsigned int i;
int j; unsigned int j;
for (j = 0; j < image->comps[0].h; j++) for (j = 0; j < image->comps[0].h; j++)
{ {
@ -263,8 +372,8 @@ evas_image_load_file_data_jp2k_internal(Evas_Image_Load_Opts *opts EINA_UNUSED,
} }
opj_image_destroy(image); opj_image_destroy(image);
opj_cio_close(cio); opj_stream_destroy(st);
opj_destroy_decompress(info); opj_destroy_codec(codec);
*error = EVAS_LOAD_ERROR_NONE; *error = EVAS_LOAD_ERROR_NONE;
return EINA_TRUE; return EINA_TRUE;
@ -303,12 +412,10 @@ evas_image_load_file_head_jp2k(void *loader_data,
int *error) int *error)
{ {
Evas_Loader_Internal *loader = loader_data; Evas_Loader_Internal *loader = loader_data;
Evas_Image_Load_Opts *opts;
Eina_File *f; Eina_File *f;
void *map; void *map;
Eina_Bool val; Eina_Bool val;
opts = loader->opts;
f = loader->f; f = loader->f;
map = eina_file_map_all(f, EINA_FILE_RANDOM); map = eina_file_map_all(f, EINA_FILE_RANDOM);
@ -320,7 +427,6 @@ evas_image_load_file_head_jp2k(void *loader_data,
val = evas_image_load_file_head_jp2k_internal(&prop->w, &prop->h, val = evas_image_load_file_head_jp2k_internal(&prop->w, &prop->h,
&prop->alpha, &prop->alpha,
opts,
map, eina_file_size_get(f), map, eina_file_size_get(f),
error); error);
@ -331,18 +437,16 @@ evas_image_load_file_head_jp2k(void *loader_data,
static Eina_Bool static Eina_Bool
evas_image_load_file_data_jp2k(void *loader_data, evas_image_load_file_data_jp2k(void *loader_data,
Evas_Image_Property *prop, Evas_Image_Property *prop EINA_UNUSED,
void *pixels, void *pixels,
int *error) int *error)
{ {
Evas_Loader_Internal *loader = loader_data; Evas_Loader_Internal *loader = loader_data;
Evas_Image_Load_Opts *opts;
Eina_File *f; Eina_File *f;
void *map; void *map;
Eina_Bool val = EINA_FALSE; Eina_Bool val = EINA_FALSE;
f = loader->f; f = loader->f;
opts = loader->opts;
map = eina_file_map_all(f, EINA_FILE_WILLNEED); map = eina_file_map_all(f, EINA_FILE_WILLNEED);
if (!map) if (!map)
@ -351,7 +455,7 @@ evas_image_load_file_data_jp2k(void *loader_data,
goto on_error; goto on_error;
} }
val = evas_image_load_file_data_jp2k_internal(opts, prop, pixels, val = evas_image_load_file_data_jp2k_internal(pixels,
map, eina_file_size_get(f), map, eina_file_size_get(f),
error); error);