kwo pushed a commit to branch master. http://git.enlightenment.org/legacy/imlib2.git/commit/?id=9ebd8399d2247a79e827d4820a449195377c152a
commit 9ebd8399d2247a79e827d4820a449195377c152a Author: Kim Woelders <[email protected]> Date: Fri Apr 8 13:45:43 2022 +0200 Add J2K (JPEG 2000) loader using openjpeg2 library --- configure.ac | 2 + src/lib/loaders.c | 6 + src/modules/loaders/Makefile.am | 9 ++ src/modules/loaders/loader_j2k.c | 287 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 304 insertions(+) diff --git a/configure.ac b/configure.ac index 9d063e2..c2b59db 100644 --- a/configure.ac +++ b/configure.ac @@ -196,6 +196,7 @@ loader_check_gif() { EC_LOADER_CHECK(GIF, auto, , loader_check_gif) EC_LOADER_CHECK(HEIF, auto, libheif) EC_LOADER_CHECK(JPEG, auto, libjpeg) +EC_LOADER_CHECK(J2K, auto, libopenjp2) EC_LOADER_CHECK(JXL, auto, libjxl libjxl_threads) EC_LOADER_CHECK(PNG, auto, libpng) EC_LOADER_CHECK(SVG, auto, librsvg-2.0 >= 2.46) @@ -284,6 +285,7 @@ echo " Regular image loaders" echo " GIF.....................: $gif_ok" echo " HEIF....................: $heif_ok" echo " JPEG....................: $jpeg_ok" +echo " J2K.....................: $j2k_ok" echo " JXL.....................: $jxl_ok" echo " PNG.....................: $png_ok" echo " SVG.....................: $svg_ok" diff --git a/src/lib/loaders.c b/src/lib/loaders.c index d1142e7..c6098ed 100644 --- a/src/lib/loaders.c +++ b/src/lib/loaders.c @@ -36,6 +36,9 @@ static const char *const ext_ico[] = { "ico", NULL }; #ifdef BUILD_JPEG_LOADER static const char *const ext_jpeg[] = { "jpg", "jpeg", "jfif", "jfi", NULL }; #endif +#ifdef BUILD_J2K_LOADER +static const char *const ext_j2k[] = { "jp2", "j2k", NULL }; +#endif #ifdef BUILD_JXL_LOADER static const char *const ext_jxl[] = { "jxl", NULL }; #endif @@ -86,6 +89,9 @@ static const KnownLoader loaders_known[] = { #ifdef BUILD_JPEG_LOADER {"jpeg", ext_jpeg}, #endif +#ifdef BUILD_J2K_LOADER + {"j2k", ext_j2k}, +#endif #ifdef BUILD_JXL_LOADER {"jxl", ext_jxl}, #endif diff --git a/src/modules/loaders/Makefile.am b/src/modules/loaders/Makefile.am index bd31d54..cdd637c 100644 --- a/src/modules/loaders/Makefile.am +++ b/src/modules/loaders/Makefile.am @@ -23,6 +23,9 @@ endif if BUILD_JPEG_LOADER pkg_LTLIBRARIES += jpeg.la endif +if BUILD_J2K_LOADER +pkg_LTLIBRARIES += j2k.la +endif if BUILD_JXL_LOADER pkg_LTLIBRARIES += jxl.la endif @@ -92,6 +95,12 @@ jpeg_la_LDFLAGS = -module -avoid-version jpeg_la_LIBADD = $(JPEG_LIBS) $(top_builddir)/src/lib/libImlib2.la jpeg_la_LIBTOOLFLAGS = --tag=disable-static +j2k_la_SOURCES = loader_j2k.c +j2k_la_CPPFLAGS = $(J2K_CFLAGS) $(AM_CPPFLAGS) +j2k_la_LDFLAGS = -module -avoid-version +j2k_la_LIBADD = $(J2K_LIBS) $(top_builddir)/src/lib/libImlib2.la +j2k_la_LIBTOOLFLAGS = --tag=disable-static + jxl_la_SOURCES = loader_jxl.c jxl_la_CPPFLAGS = $(JXL_CFLAGS) $(AM_CPPFLAGS) jxl_la_LDFLAGS = -module -avoid-version diff --git a/src/modules/loaders/loader_j2k.c b/src/modules/loaders/loader_j2k.c new file mode 100644 index 0000000..00ed490 --- /dev/null +++ b/src/modules/loaders/loader_j2k.c @@ -0,0 +1,287 @@ +#include "loader_common.h" + +#include <openjpeg.h> + +#define DBG_PFX "LDR-j2k" + +#define JP2_RFC3745_MAGIC "\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a" +#define JP2_MAGIC "\x0d\x0a\x87\x0a" +/* position 45: "\xff\x52" */ +#define J2K_CODESTREAM_MAGIC "\xff\x4f\xff\x51" + +#if IMLIB2_DEBUG +static void +_j2k_cb(const char *type, const char *msg, void *data) +{ + DL("%s: %p: %s: %s", __func__, data, type, msg); +} + +static void +_j2k_cb_info(const char *msg, void *data) +{ + _j2k_cb("info", msg, data); +} + +static void +_j2k_cb_warn(const char *msg, void *data) +{ + _j2k_cb("warn", msg, data); +} + +static void +_j2k_cb_err(const char *msg, void *data) +{ + _j2k_cb("err", msg, data); +} +#endif /*IMLIB2_DEBUG */ + +static struct { + const unsigned char *data, *dptr; + unsigned int size; +} mdata; + +static void +mm_init(const void *src, unsigned int size) +{ + mdata.data = mdata.dptr = src; + mdata.size = size; +} + +static OPJ_SIZE_T +mm_read(void *dst, OPJ_SIZE_T len, void *data) +{ + DL("%s: len=%ld\n", __func__, len); + + if (mdata.dptr >= mdata.data + mdata.size) + return -1; /* Out of data */ + if (mdata.dptr + len > mdata.data + mdata.size) + len = mdata.data + mdata.size - mdata.dptr; + + memcpy(dst, mdata.dptr, len); + mdata.dptr += len; + + return len; +} + +static OPJ_OFF_T +mm_seek_cur(OPJ_OFF_T offs, void *data) +{ + DL("%s: offs=%ld\n", __func__, offs); + + if (mdata.dptr + offs > mdata.data + mdata.size) + return 0; /* Out of data */ + + mdata.dptr += offs; + + return mdata.dptr - mdata.data; +} + +static OPJ_BOOL +mm_seek_set(OPJ_OFF_T offs, void *data) +{ + DL("%s: offs=%ld\n", __func__, offs); + + if (offs > mdata.size) + return OPJ_FALSE; /* Out of data */ + + mdata.dptr = mdata.data + offs; + + return OPJ_TRUE; +} + +int +load2(ImlibImage * im, int load_data) +{ + int rc; + void *fdata; + int ok; + opj_dparameters_t jparam; + opj_codec_t *jcodec; + opj_stream_t *jstream; + opj_image_t *jimage; + OPJ_CODEC_FORMAT jfmt; + int i, j; + uint32_t *dst; + OPJ_INT32 *pa, *pr, *pg, *pb; + unsigned char a, r, g, b; + + fdata = mmap(NULL, im->fsize, PROT_READ, MAP_SHARED, fileno(im->fp), 0); + if (fdata == MAP_FAILED) + return LOAD_BADFILE; + + rc = LOAD_FAIL; + jcodec = NULL; + jstream = NULL; + jimage = NULL; + + /* Signature check */ + if (im->fsize < 12) + goto quit; + + if (memcmp(fdata, JP2_MAGIC, 4) == 0 || + memcmp(fdata, JP2_RFC3745_MAGIC, 12) == 0) + jfmt = OPJ_CODEC_JP2; + else if (memcmp(fdata, J2K_CODESTREAM_MAGIC, 4) == 0) + jfmt = OPJ_CODEC_J2K; + else + goto quit; + + DL("format=%d\n", jfmt); + + memset(&jparam, 0, sizeof(opj_dparameters_t)); + opj_set_default_decoder_parameters(&jparam); + + jcodec = opj_create_decompress(jfmt); + if (!jcodec) + goto quit; + + rc = LOAD_BADIMAGE; /* Format accepted */ + +#if IMLIB2_DEBUG + opj_set_info_handler(jcodec, _j2k_cb_info, NULL); + opj_set_warning_handler(jcodec, _j2k_cb_warn, NULL); + opj_set_error_handler(jcodec, _j2k_cb_err, NULL); +#endif + + ok = opj_setup_decoder(jcodec, &jparam); + if (!ok) + goto quit; + + // May be set with OPJ_NUM_THREADS=number or ALL_CPUS +// opj_codec_set_threads(jcodec, 4); + + if (getenv("JP2_USE_FILE")) + { + jstream = + opj_stream_create_default_file_stream(im->real_file, OPJ_TRUE); + } + else + { + jstream = opj_stream_create(OPJ_J2K_STREAM_CHUNK_SIZE, OPJ_TRUE); + if (!jstream) + goto quit; + + mm_init(fdata, im->fsize); + opj_stream_set_user_data(jstream, &mdata, NULL); + opj_stream_set_user_data_length(jstream, im->fsize); + opj_stream_set_read_function(jstream, mm_read); + opj_stream_set_skip_function(jstream, mm_seek_cur); + opj_stream_set_seek_function(jstream, mm_seek_set); + } + + opj_read_header(jstream, jcodec, &jimage); + if (!jimage) + goto quit; + im->w = jimage->x1 - jimage->x0; + im->h = jimage->y1 - jimage->y0; + IM_FLAG_UPDATE(im, F_HAS_ALPHA, + jimage->numcomps == 4 || jimage->numcomps == 2); + D("WxH=%dx%d alpha=%d numcomp=%d colorspace=%d\n", + im->w, im->h, IM_FLAG_ISSET(im, F_HAS_ALPHA), + jimage->numcomps, jimage->color_space); + + for (i = 0; i < (int)jimage->numcomps; i++) + { + DL("%d: dx/y=%d/%d wxh=%d,%d prec=%d bpp=%d sgnd=%d fact=%d\n", i, + jimage->comps[i].dx, jimage->comps[i].dy, + jimage->comps[i].w, jimage->comps[i].h, + jimage->comps[i].prec, jimage->comps[i].bpp, + jimage->comps[i].sgnd, jimage->comps[i].factor); + if (jimage->comps[0].dx != jimage->comps[i].dx || + jimage->comps[0].dy != jimage->comps[i].dy || + (int)jimage->comps[i].w != im->w || + (int)jimage->comps[i].h != im->h) + goto quit; + } + + if (!load_data) + QUIT_WITH_RC(LOAD_SUCCESS); + + /* Load data */ + + ok = opj_decode(jcodec, jstream, jimage); + if (!ok) + goto quit; + + ok = opj_end_decompress(jcodec, jstream); + if (!ok) + goto quit; + + if (!__imlib_AllocateData(im)) + QUIT_WITH_RC(LOAD_OOM); + + /* Ignoring color_space and data format details... */ + + dst = im->data; + pa = jimage->comps[0].data; /* Avoid compiler warning */ + + switch (jimage->numcomps) + { + default: + goto quit; + + case 4: /* RGBA */ + pa = jimage->comps[3].data; + /* FALLTHROUGH */ + case 3: /* RGB */ + pr = jimage->comps[0].data; + pg = jimage->comps[1].data; + pb = jimage->comps[2].data; + for (i = 0; i < im->h; i++) + { + for (j = 0; j < im->w; j++) + { + r = *pr++; + g = *pg++; + b = *pb++; + a = (jimage->numcomps == 4) ? *pa++ : 0xff; + + *dst++ = PIXEL_ARGB(a, r, g, b); + } + + if (im->lc && __imlib_LoadProgressRows(im, i, 1)) + QUIT_WITH_RC(LOAD_BREAK); + } + break; + + case 2: /* Gray with A */ + pa = jimage->comps[1].data; + /* FALLTHROUGH */ + case 1: /* Gray */ + pg = jimage->comps[0].data; + for (i = 0; i < im->h; i++) + { + for (j = 0; j < im->w; j++) + { + g = *pg++; + a = (jimage->numcomps == 2) ? *pa++ : 0xff; + + *dst++ = PIXEL_ARGB(a, g, g, g); + } + + if (im->lc && __imlib_LoadProgressRows(im, i, 1)) + QUIT_WITH_RC(LOAD_BREAK); + } + break; + } + + rc = LOAD_SUCCESS; + + quit: + if (jimage) + opj_image_destroy(jimage); + if (jstream) + opj_stream_destroy(jstream); + if (jcodec) + opj_destroy_codec(jcodec); + munmap(fdata, im->fsize); + + return rc; +} + +void +formats(ImlibLoader * l) +{ + static const char *const list_formats[] = { "jp2", "j2k" }; + __imlib_LoaderSetFormats(l, list_formats, ARRAY_SIZE(list_formats)); +} --
