jxl - add actual files ... as i had to get patch manually from phab
missed these... arc broken... sorry
This commit is contained in:
parent
01fb3233eb
commit
d7521c69dd
|
@ -0,0 +1,526 @@
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <jxl/decode.h>
|
||||||
|
#include <jxl/resizable_parallel_runner.h>
|
||||||
|
|
||||||
|
#include <Ecore.h>
|
||||||
|
#include "Evas_Loader.h"
|
||||||
|
#include "evas_common_private.h"
|
||||||
|
|
||||||
|
typedef struct _Evas_Loader_Internal Evas_Loader_Internal;
|
||||||
|
struct _Evas_Loader_Internal
|
||||||
|
{
|
||||||
|
Eina_File *f;
|
||||||
|
Evas_Image_Load_Opts *opts;
|
||||||
|
Evas_Image_Animated *animated;
|
||||||
|
JxlParallelRunner *runner;
|
||||||
|
JxlDecoder *decoder;
|
||||||
|
double duration;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int _evas_loader_jxl_log_dom = -1;
|
||||||
|
|
||||||
|
#ifdef ERR
|
||||||
|
# undef ERR
|
||||||
|
#endif
|
||||||
|
#define ERR(...) EINA_LOG_DOM_ERR(_evas_loader_jxl_log_dom, __VA_ARGS__)
|
||||||
|
|
||||||
|
#ifdef WRN
|
||||||
|
# undef WRN
|
||||||
|
#endif
|
||||||
|
#define WRN(...) EINA_LOG_DOM_WARN(_evas_loader_jxl_log_dom, __VA_ARGS__)
|
||||||
|
|
||||||
|
#ifdef INF
|
||||||
|
# undef INF
|
||||||
|
#endif
|
||||||
|
#define INF(...) EINA_LOG_DOM_INFO(_evas_loader_jxl_log_dom, __VA_ARGS__)
|
||||||
|
|
||||||
|
void _rgba_to_bgra(void *pixels, int size /* in pixels */)
|
||||||
|
{
|
||||||
|
unsigned long long int *iter = pixels;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < (size >> 1); i++, iter++)
|
||||||
|
{
|
||||||
|
*iter =
|
||||||
|
/* we keep A and G */
|
||||||
|
(*iter & 0xff00ff00ff00ff00) |
|
||||||
|
/* we shift R */
|
||||||
|
((*iter & 0x000000ff000000ff) << 16) |
|
||||||
|
/* we shift B */
|
||||||
|
((*iter & 0x00ff000000ff0000) >> 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Eina_Bool
|
||||||
|
evas_image_load_file_head_jxl_internal(Evas_Loader_Internal *loader,
|
||||||
|
Emile_Image_Property *prop,
|
||||||
|
void *map, size_t length,
|
||||||
|
int *error)
|
||||||
|
{
|
||||||
|
Evas_Image_Animated *animated;
|
||||||
|
JxlBasicInfo basic_info;
|
||||||
|
JxlFrameHeader frame_header;
|
||||||
|
JxlDecoder *decoder;
|
||||||
|
JxlDecoderStatus s;
|
||||||
|
JxlDecoderStatus st;
|
||||||
|
uint32_t frame_count = 0;
|
||||||
|
Eina_Bool ret;
|
||||||
|
|
||||||
|
animated = loader->animated;
|
||||||
|
|
||||||
|
ret = EINA_FALSE;
|
||||||
|
prop->w = 0;
|
||||||
|
prop->h = 0;
|
||||||
|
prop->alpha = EINA_FALSE;
|
||||||
|
|
||||||
|
decoder = JxlDecoderCreate(NULL);
|
||||||
|
if (!decoder)
|
||||||
|
{
|
||||||
|
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
JxlDecoderSetKeepOrientation(decoder, JXL_TRUE);
|
||||||
|
|
||||||
|
st = JxlDecoderSubscribeEvents(decoder,
|
||||||
|
JXL_DEC_BASIC_INFO |
|
||||||
|
JXL_DEC_FRAME);
|
||||||
|
if (st != JXL_DEC_SUCCESS)
|
||||||
|
{
|
||||||
|
ERR("Can not subscribe to JXL events");
|
||||||
|
*error = EVAS_LOAD_ERROR_GENERIC;
|
||||||
|
goto destroy_decoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
st = JxlDecoderSetInput(decoder, map, length);
|
||||||
|
if (st != JXL_DEC_SUCCESS)
|
||||||
|
{
|
||||||
|
ERR("Can not set JXL input");
|
||||||
|
*error = EVAS_LOAD_ERROR_GENERIC;
|
||||||
|
goto destroy_decoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
JxlDecoderCloseInput(decoder);
|
||||||
|
|
||||||
|
/* First, JXL_DEC_BASIC_INFO event */
|
||||||
|
st = JxlDecoderProcessInput(decoder);
|
||||||
|
if (st != JXL_DEC_BASIC_INFO)
|
||||||
|
{
|
||||||
|
ERR("Can not set JXL input (JXL_DEC_BASIC_INFO): %d", st);
|
||||||
|
*error = EVAS_LOAD_ERROR_GENERIC;
|
||||||
|
goto release_input;
|
||||||
|
}
|
||||||
|
|
||||||
|
s = JxlDecoderGetBasicInfo(decoder, &basic_info);
|
||||||
|
if (s != JXL_DEC_SUCCESS)
|
||||||
|
{
|
||||||
|
ERR("Can not retrieve basic info");
|
||||||
|
*error = EVAS_LOAD_ERROR_GENERIC;
|
||||||
|
goto release_input;
|
||||||
|
}
|
||||||
|
|
||||||
|
prop->w = basic_info.xsize;
|
||||||
|
prop->h = basic_info.ysize;
|
||||||
|
/* if size is invalid, we exit */
|
||||||
|
if ((prop->w < 1) ||
|
||||||
|
(prop->h < 1) ||
|
||||||
|
(prop->w > IMG_MAX_SIZE) ||
|
||||||
|
(prop->h > IMG_MAX_SIZE) ||
|
||||||
|
IMG_TOO_BIG(prop->w, prop->h))
|
||||||
|
{
|
||||||
|
if (IMG_TOO_BIG(prop->w, prop->h))
|
||||||
|
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
|
||||||
|
else
|
||||||
|
*error= EVAS_LOAD_ERROR_GENERIC;
|
||||||
|
goto release_input;
|
||||||
|
}
|
||||||
|
|
||||||
|
prop->alpha = (basic_info.alpha_bits != 0);
|
||||||
|
|
||||||
|
/* Then, JXL_DEC_FRAME event */
|
||||||
|
|
||||||
|
if (basic_info.have_animation)
|
||||||
|
{
|
||||||
|
frame_count = 0;
|
||||||
|
}
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
st = JxlDecoderProcessInput(decoder);
|
||||||
|
if (st == JXL_DEC_FRAME)
|
||||||
|
{
|
||||||
|
JxlDecoderGetFrameHeader(decoder, &frame_header);
|
||||||
|
frame_count++;
|
||||||
|
if (frame_header.is_last)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finally, JXL_DEC_SUCCESS event */
|
||||||
|
st = JxlDecoderProcessInput(decoder);
|
||||||
|
if (st != JXL_DEC_SUCCESS)
|
||||||
|
{
|
||||||
|
ERR("Can not set JXL input (JXL_DEC_SUCCESS)");
|
||||||
|
*error = EVAS_LOAD_ERROR_GENERIC;
|
||||||
|
goto release_input;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (basic_info.have_animation)
|
||||||
|
{
|
||||||
|
animated->loop_hint = basic_info.animation.num_loops ? EVAS_IMAGE_ANIMATED_HINT_NONE : EVAS_IMAGE_ANIMATED_HINT_LOOP;
|
||||||
|
animated->frame_count = frame_count;
|
||||||
|
animated->loop_count = basic_info.animation.num_loops;
|
||||||
|
animated->animated = EINA_TRUE;
|
||||||
|
loader->duration = ((double)frame_header.duration * (double)basic_info.animation.tps_denominator) / (double)basic_info.animation.tps_numerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
*error = EVAS_LOAD_ERROR_NONE;
|
||||||
|
ret = EINA_TRUE;
|
||||||
|
|
||||||
|
release_input:
|
||||||
|
JxlDecoderReleaseInput(decoder);
|
||||||
|
destroy_decoder:
|
||||||
|
JxlDecoderDestroy(decoder);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Eina_Bool
|
||||||
|
evas_image_load_file_data_jxl_internal(Evas_Loader_Internal *loader,
|
||||||
|
Emile_Image_Property *prop,
|
||||||
|
void *pixels,
|
||||||
|
void *map, size_t length,
|
||||||
|
int *error)
|
||||||
|
{
|
||||||
|
Evas_Image_Animated *animated;
|
||||||
|
JxlParallelRunner *runner;
|
||||||
|
JxlDecoder *decoder;
|
||||||
|
JxlPixelFormat pixel_format;
|
||||||
|
JxlDecoderStatus st;
|
||||||
|
size_t buffer_size;
|
||||||
|
Eina_Bool ret = EINA_FALSE;
|
||||||
|
|
||||||
|
double t1, t2;
|
||||||
|
t1 = ecore_time_get();
|
||||||
|
|
||||||
|
animated = loader->animated;
|
||||||
|
|
||||||
|
runner = loader->runner;
|
||||||
|
decoder = loader->decoder;
|
||||||
|
if (!runner || !decoder)
|
||||||
|
{
|
||||||
|
runner = JxlResizableParallelRunnerCreate(NULL);
|
||||||
|
if (!runner)
|
||||||
|
{
|
||||||
|
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder = JxlDecoderCreate(NULL);
|
||||||
|
if (!decoder)
|
||||||
|
{
|
||||||
|
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
st = JxlDecoderSetParallelRunner(decoder,
|
||||||
|
JxlResizableParallelRunner,
|
||||||
|
runner);
|
||||||
|
if (st != JXL_DEC_SUCCESS)
|
||||||
|
{
|
||||||
|
ERR("Can not set JXL runner");
|
||||||
|
*error = EVAS_LOAD_ERROR_GENERIC;
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
JxlResizableParallelRunnerSetThreads(runner,
|
||||||
|
JxlResizableParallelRunnerSuggestThreads(prop->w, prop->h));
|
||||||
|
|
||||||
|
JxlDecoderSetKeepOrientation(decoder, JXL_TRUE);
|
||||||
|
|
||||||
|
st = JxlDecoderSetInput(decoder, map, length);
|
||||||
|
if (st != JXL_DEC_SUCCESS)
|
||||||
|
{
|
||||||
|
ERR("Can not set JXL input");
|
||||||
|
*error = EVAS_LOAD_ERROR_GENERIC;
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
JxlDecoderCloseInput(decoder);
|
||||||
|
|
||||||
|
st = JxlDecoderSubscribeEvents(decoder,
|
||||||
|
JXL_DEC_FULL_IMAGE);
|
||||||
|
if (st != JXL_DEC_SUCCESS)
|
||||||
|
{
|
||||||
|
ERR("Can not subscribe to JXL events");
|
||||||
|
*error = EVAS_LOAD_ERROR_GENERIC;
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pixel_format.num_channels = 4;
|
||||||
|
pixel_format.data_type = JXL_TYPE_UINT8;
|
||||||
|
#ifdef WORDS_BIGENDIAN
|
||||||
|
pixel_format.endianness = JXL_BIG_ENDIAN;
|
||||||
|
#else
|
||||||
|
pixel_format.endianness = JXL_LITTLE_ENDIAN;
|
||||||
|
#endif
|
||||||
|
pixel_format.align = 0;
|
||||||
|
|
||||||
|
if (animated->animated)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* According to the libjxl devsn there is a better way than
|
||||||
|
* JxlDecoderSkipFrames(), but i can't...
|
||||||
|
*/
|
||||||
|
JxlDecoderSkipFrames(decoder, animated->cur_frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
st = JxlDecoderProcessInput(decoder);
|
||||||
|
if (animated->animated)
|
||||||
|
{
|
||||||
|
if (st == JXL_DEC_SUCCESS)
|
||||||
|
goto on_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (st != JXL_DEC_NEED_IMAGE_OUT_BUFFER)
|
||||||
|
{
|
||||||
|
ERR("Can not process JXL_DEC_NEED_IMAGE_OUT_BUFFER events (st=%d)", st);
|
||||||
|
*error = EVAS_LOAD_ERROR_GENERIC;
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
st = JxlDecoderImageOutBufferSize(decoder,
|
||||||
|
&pixel_format,
|
||||||
|
&buffer_size);
|
||||||
|
if (st != JXL_DEC_SUCCESS)
|
||||||
|
{
|
||||||
|
ERR("JxlDecoderImageOutBufferSize failed");
|
||||||
|
*error = EVAS_LOAD_ERROR_GENERIC;
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer_size != (size_t)(prop->w * prop->h * 4))
|
||||||
|
{
|
||||||
|
ERR("buffer size does not match image size");
|
||||||
|
*error = EVAS_LOAD_ERROR_GENERIC;
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
st = JxlDecoderSetImageOutBuffer(decoder,
|
||||||
|
&pixel_format,
|
||||||
|
pixels,
|
||||||
|
buffer_size);
|
||||||
|
if (st != JXL_DEC_SUCCESS)
|
||||||
|
{
|
||||||
|
ERR("Can not set image output buffer");
|
||||||
|
*error = EVAS_LOAD_ERROR_GENERIC;
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
st = JxlDecoderProcessInput(decoder);
|
||||||
|
if (st != JXL_DEC_FULL_IMAGE)
|
||||||
|
{
|
||||||
|
ERR("Can not process JXL_DEC_FULL_IMAGE events");
|
||||||
|
*error = EVAS_LOAD_ERROR_GENERIC;
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
_rgba_to_bgra(pixels, prop->w * prop->h);
|
||||||
|
t2 = ecore_time_get();
|
||||||
|
printf("time: %e\n", t2 - t1);
|
||||||
|
|
||||||
|
on_success:
|
||||||
|
*error = EVAS_LOAD_ERROR_NONE;
|
||||||
|
ret = EINA_TRUE;
|
||||||
|
|
||||||
|
on_error:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *
|
||||||
|
evas_image_load_file_open_jxl(Eina_File *f, Eina_Stringshare *key EINA_UNUSED,
|
||||||
|
Evas_Image_Load_Opts *opts,
|
||||||
|
Evas_Image_Animated *animated,
|
||||||
|
int *error)
|
||||||
|
{
|
||||||
|
Evas_Loader_Internal *loader;
|
||||||
|
|
||||||
|
loader = calloc(1, sizeof (Evas_Loader_Internal));
|
||||||
|
if (!loader)
|
||||||
|
{
|
||||||
|
*error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
loader->f = f;
|
||||||
|
loader->opts = opts;
|
||||||
|
loader->animated = animated;
|
||||||
|
|
||||||
|
return loader;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
evas_image_load_file_close_jxl(void *loader_data)
|
||||||
|
{
|
||||||
|
Evas_Loader_Internal *loader;
|
||||||
|
|
||||||
|
loader = loader_data;
|
||||||
|
if (loader->decoder)
|
||||||
|
{
|
||||||
|
JxlDecoderReleaseInput(loader->decoder);
|
||||||
|
JxlDecoderDestroy(loader->decoder);
|
||||||
|
/* if decoder is valid, runner is necessarly valid */
|
||||||
|
JxlResizableParallelRunnerDestroy(loader->runner);
|
||||||
|
}
|
||||||
|
free(loader_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Eina_Bool
|
||||||
|
evas_image_load_file_head_jxl(void *loader_data,
|
||||||
|
Evas_Image_Property *prop,
|
||||||
|
int *error)
|
||||||
|
{
|
||||||
|
Evas_Loader_Internal *loader = loader_data;
|
||||||
|
Eina_File *f;
|
||||||
|
void *map;
|
||||||
|
Eina_Bool val;
|
||||||
|
|
||||||
|
f = loader->f;
|
||||||
|
|
||||||
|
map = eina_file_map_all(f, EINA_FILE_RANDOM);
|
||||||
|
if (!map)
|
||||||
|
{
|
||||||
|
*error = EVAS_LOAD_ERROR_DOES_NOT_EXIST;
|
||||||
|
return EINA_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = evas_image_load_file_head_jxl_internal(loader,
|
||||||
|
(Emile_Image_Property *)prop,
|
||||||
|
map, eina_file_size_get(f),
|
||||||
|
error);
|
||||||
|
|
||||||
|
eina_file_map_free(f, map);
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Eina_Bool
|
||||||
|
evas_image_load_file_data_jxl(void *loader_data,
|
||||||
|
Evas_Image_Property *prop,
|
||||||
|
void *pixels,
|
||||||
|
int *error)
|
||||||
|
{
|
||||||
|
Evas_Loader_Internal *loader;
|
||||||
|
Eina_File *f;
|
||||||
|
void *map;
|
||||||
|
Eina_Bool val = EINA_FALSE;
|
||||||
|
|
||||||
|
loader = (Evas_Loader_Internal *)loader_data;
|
||||||
|
f = loader->f;
|
||||||
|
|
||||||
|
map = eina_file_map_all(f, EINA_FILE_WILLNEED);
|
||||||
|
if (!map)
|
||||||
|
{
|
||||||
|
*error = EVAS_LOAD_ERROR_DOES_NOT_EXIST;
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = evas_image_load_file_data_jxl_internal(loader,
|
||||||
|
(Emile_Image_Property *)prop,
|
||||||
|
pixels,
|
||||||
|
map, eina_file_size_get(f),
|
||||||
|
error);
|
||||||
|
|
||||||
|
eina_file_map_free(f, map);
|
||||||
|
|
||||||
|
on_error:
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double
|
||||||
|
evas_image_load_frame_duration_jxl(void *loader_data,
|
||||||
|
int start_frame,
|
||||||
|
int frame_num)
|
||||||
|
{
|
||||||
|
Evas_Loader_Internal *loader;
|
||||||
|
Evas_Image_Animated *animated;
|
||||||
|
|
||||||
|
loader = (Evas_Loader_Internal *)loader_data;
|
||||||
|
animated = loader->animated;
|
||||||
|
|
||||||
|
if (!animated->animated)
|
||||||
|
return -1.0;
|
||||||
|
|
||||||
|
if (frame_num < 0)
|
||||||
|
return -1.0;
|
||||||
|
|
||||||
|
if ((start_frame + frame_num) > animated->frame_count)
|
||||||
|
return -1.0;
|
||||||
|
|
||||||
|
if (frame_num < 1)
|
||||||
|
frame_num = 1;
|
||||||
|
|
||||||
|
return loader->duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Evas_Image_Load_Func evas_image_load_jxl_func =
|
||||||
|
{
|
||||||
|
EVAS_IMAGE_LOAD_VERSION,
|
||||||
|
evas_image_load_file_open_jxl,
|
||||||
|
evas_image_load_file_close_jxl,
|
||||||
|
evas_image_load_file_head_jxl,
|
||||||
|
NULL,
|
||||||
|
evas_image_load_file_data_jxl,
|
||||||
|
evas_image_load_frame_duration_jxl,
|
||||||
|
EINA_TRUE,
|
||||||
|
EINA_FALSE
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
module_open(Evas_Module *em)
|
||||||
|
{
|
||||||
|
if (!em) return 0;
|
||||||
|
|
||||||
|
_evas_loader_jxl_log_dom = eina_log_domain_register("evas-jxl", EINA_COLOR_BLUE);
|
||||||
|
if (_evas_loader_jxl_log_dom < 0)
|
||||||
|
{
|
||||||
|
EINA_LOG_ERR("Can not create a module log domain.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
em->functions = (void *)(&evas_image_load_jxl_func);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
module_close(Evas_Module *em EINA_UNUSED)
|
||||||
|
{
|
||||||
|
if (_evas_loader_jxl_log_dom >= 0)
|
||||||
|
{
|
||||||
|
eina_log_domain_unregister(_evas_loader_jxl_log_dom);
|
||||||
|
_evas_loader_jxl_log_dom = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Evas_Module_Api evas_modapi =
|
||||||
|
{
|
||||||
|
EVAS_MODULE_API_VERSION,
|
||||||
|
"jxl",
|
||||||
|
"none",
|
||||||
|
{
|
||||||
|
module_open,
|
||||||
|
module_close
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, jxl);
|
||||||
|
|
||||||
|
#ifndef EVAS_STATIC_BUILD_JXL
|
||||||
|
EVAS_EINA_MODULE_DEFINE(image_loader, jxl);
|
||||||
|
#endif
|
|
@ -0,0 +1,213 @@
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <jxl/encode.h>
|
||||||
|
#include <jxl/resizable_parallel_runner.h>
|
||||||
|
|
||||||
|
#include "evas_common_private.h"
|
||||||
|
#include "evas_private.h"
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
save_image_jxl(RGBA_Image *im, const char *file, int quality)
|
||||||
|
{
|
||||||
|
FILE *f;
|
||||||
|
JxlParallelRunner *runner;
|
||||||
|
JxlEncoder *encoder;
|
||||||
|
JxlPixelFormat pixel_format;
|
||||||
|
JxlBasicInfo basic_info;
|
||||||
|
JxlColorEncoding color_encoding;
|
||||||
|
JxlEncoderFrameSettings* frame_settings;
|
||||||
|
JxlEncoderStatus st;
|
||||||
|
JxlEncoderStatus process_result;
|
||||||
|
unsigned char *compressed;
|
||||||
|
unsigned char *next;
|
||||||
|
void *pixels;
|
||||||
|
unsigned long long int *iter_src;
|
||||||
|
unsigned long long int *iter_dst;
|
||||||
|
size_t size;
|
||||||
|
size_t avail;
|
||||||
|
size_t sz;
|
||||||
|
unsigned int i;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!im || !im->image.data || !file || !*file)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
f = fopen(file, "wb");
|
||||||
|
if (!f)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
runner = JxlResizableParallelRunnerCreate(NULL);
|
||||||
|
if (!runner)
|
||||||
|
goto close_f;
|
||||||
|
|
||||||
|
encoder = JxlEncoderCreate(NULL);
|
||||||
|
if (!encoder)
|
||||||
|
goto destroy_runner;
|
||||||
|
|
||||||
|
st = JxlEncoderSetParallelRunner(encoder,
|
||||||
|
JxlResizableParallelRunner,
|
||||||
|
runner);
|
||||||
|
if (st != JXL_ENC_SUCCESS)
|
||||||
|
goto destroy_encoder;
|
||||||
|
|
||||||
|
JxlResizableParallelRunnerSetThreads(runner,
|
||||||
|
JxlResizableParallelRunnerSuggestThreads(im->cache_entry.w, im->cache_entry.h));
|
||||||
|
|
||||||
|
pixel_format.num_channels = 4;
|
||||||
|
pixel_format.data_type = JXL_TYPE_UINT8;
|
||||||
|
#ifdef WORDS_BIGENDIAN
|
||||||
|
pixel_format.endianness = JXL_BIG_ENDIAN;
|
||||||
|
#else
|
||||||
|
pixel_format.endianness = JXL_LITTLE_ENDIAN;
|
||||||
|
#endif
|
||||||
|
pixel_format.align = 0;
|
||||||
|
|
||||||
|
JxlEncoderInitBasicInfo(&basic_info);
|
||||||
|
basic_info.xsize = im->cache_entry.w;
|
||||||
|
basic_info.ysize = im->cache_entry.h;
|
||||||
|
basic_info.bits_per_sample = 8;
|
||||||
|
basic_info.exponent_bits_per_sample = 0;
|
||||||
|
basic_info.uses_original_profile = JXL_FALSE;
|
||||||
|
basic_info.num_color_channels = 3;
|
||||||
|
basic_info.num_extra_channels =1;
|
||||||
|
basic_info.alpha_bits = 8;
|
||||||
|
st = JxlEncoderSetBasicInfo(encoder, &basic_info);
|
||||||
|
if (st != JXL_ENC_SUCCESS)
|
||||||
|
goto destroy_encoder;
|
||||||
|
|
||||||
|
memset(&color_encoding, 0, sizeof(JxlColorEncoding));
|
||||||
|
JxlColorEncodingSetToSRGB(&color_encoding,
|
||||||
|
/*is gray ? */
|
||||||
|
pixel_format.num_channels < 3);
|
||||||
|
st = JxlEncoderSetColorEncoding(encoder, &color_encoding);
|
||||||
|
if (st != JXL_ENC_SUCCESS)
|
||||||
|
goto destroy_encoder;
|
||||||
|
|
||||||
|
frame_settings = JxlEncoderFrameSettingsCreate(encoder, NULL);
|
||||||
|
if (!frame_settings)
|
||||||
|
goto destroy_encoder;
|
||||||
|
|
||||||
|
st = JxlEncoderFrameSettingsSetOption(frame_settings,
|
||||||
|
JXL_ENC_FRAME_SETTING_EFFORT,
|
||||||
|
(quality * 7) /100);
|
||||||
|
if (st != JXL_ENC_SUCCESS)
|
||||||
|
goto destroy_encoder;
|
||||||
|
|
||||||
|
/* conversion RGBA --> BGRA */
|
||||||
|
pixels = malloc(4 * im->cache_entry.w * im->cache_entry.h);
|
||||||
|
if (!pixels)
|
||||||
|
goto destroy_encoder;
|
||||||
|
|
||||||
|
iter_src = (unsigned long long int *)im->image.data;
|
||||||
|
iter_dst = (unsigned long long int *)pixels;
|
||||||
|
|
||||||
|
for (i = 0; i < ((im->cache_entry.w * im->cache_entry.h) >> 1); i++, iter_src++, iter_dst++)
|
||||||
|
{
|
||||||
|
*iter_dst =
|
||||||
|
/* we keep A and G */
|
||||||
|
(*iter_src & 0xff00ff00ff00ff00) |
|
||||||
|
/* we shift R */
|
||||||
|
((*iter_src & 0x000000ff000000ff) << 16) |
|
||||||
|
/* we shift B */
|
||||||
|
((*iter_src & 0x00ff000000ff0000) >> 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
st = JxlEncoderAddImageFrame(frame_settings, &pixel_format,
|
||||||
|
(void*)pixels,
|
||||||
|
sizeof(int) * im->cache_entry.w * im->cache_entry.h);
|
||||||
|
if (st != JXL_ENC_SUCCESS)
|
||||||
|
goto free_pixels;
|
||||||
|
|
||||||
|
JxlEncoderCloseInput(encoder);
|
||||||
|
|
||||||
|
size = 64;
|
||||||
|
compressed = (unsigned char *)malloc(size);
|
||||||
|
if (!compressed)
|
||||||
|
goto free_pixels;
|
||||||
|
|
||||||
|
next = compressed;
|
||||||
|
avail = size - (next - compressed);
|
||||||
|
process_result = JXL_ENC_NEED_MORE_OUTPUT;
|
||||||
|
while (process_result == JXL_ENC_NEED_MORE_OUTPUT)
|
||||||
|
{
|
||||||
|
process_result = JxlEncoderProcessOutput(encoder, &next, &avail);
|
||||||
|
if (process_result == JXL_ENC_NEED_MORE_OUTPUT)
|
||||||
|
{
|
||||||
|
size_t offset = next - compressed;
|
||||||
|
size *= 2;
|
||||||
|
compressed = realloc(compressed, size);
|
||||||
|
next = compressed + offset;
|
||||||
|
avail = size - offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
size = next - compressed;
|
||||||
|
compressed = realloc(compressed, size);
|
||||||
|
if (process_result != JXL_ENC_SUCCESS)
|
||||||
|
goto free_compressed;
|
||||||
|
|
||||||
|
sz = fwrite(compressed, size, 1, f);
|
||||||
|
if (sz != 1)
|
||||||
|
goto free_compressed;
|
||||||
|
|
||||||
|
ret = 1;
|
||||||
|
|
||||||
|
free_compressed:
|
||||||
|
free(compressed);
|
||||||
|
free_pixels:
|
||||||
|
free(pixels);
|
||||||
|
destroy_encoder:
|
||||||
|
JxlEncoderDestroy(encoder);
|
||||||
|
destroy_runner:
|
||||||
|
JxlResizableParallelRunnerDestroy(runner);
|
||||||
|
close_f:
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int evas_image_save_file_jxl(RGBA_Image *im, const char *file, const char *key EINA_UNUSED,
|
||||||
|
int quality, int compress EINA_UNUSED, const char *encoding EINA_UNUSED)
|
||||||
|
{
|
||||||
|
return save_image_jxl(im, file, quality);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Evas_Image_Save_Func evas_image_save_jxl_func =
|
||||||
|
{
|
||||||
|
evas_image_save_file_jxl
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
module_open(Evas_Module *em)
|
||||||
|
{
|
||||||
|
if (!em) return 0;
|
||||||
|
em->functions = (void *)(&evas_image_save_jxl_func);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
module_close(Evas_Module *em EINA_UNUSED)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static Evas_Module_Api evas_modapi =
|
||||||
|
{
|
||||||
|
EVAS_MODULE_API_VERSION,
|
||||||
|
"jxl",
|
||||||
|
"none",
|
||||||
|
{
|
||||||
|
module_open,
|
||||||
|
module_close
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_SAVER, image_saver, jxl);
|
||||||
|
|
||||||
|
#ifndef EVAS_STATIC_BUILD_JXL
|
||||||
|
EVAS_EINA_MODULE_DEFINE(image_saver, jxl);
|
||||||
|
#endif
|
Loading…
Reference in New Issue