2013-03-25 12:04:10 +00:00
|
|
|
/*
|
2020-01-28 14:32:34 +00:00
|
|
|
* (C) Copyright 2005- ECMWF.
|
2013-03-25 12:04:10 +00:00
|
|
|
*
|
|
|
|
* This software is licensed under the terms of the Apache Licence Version 2.0
|
|
|
|
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
|
|
|
|
*
|
|
|
|
* In applying this licence, ECMWF does not waive the privileges and immunities granted to it by
|
|
|
|
* virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "grib_api_internal.h"
|
|
|
|
|
|
|
|
#if HAVE_LIBOPENJPEG
|
2016-07-25 14:57:48 +00:00
|
|
|
|
2013-03-25 12:04:10 +00:00
|
|
|
#include "openjpeg.h"
|
2024-03-04 11:03:26 +00:00
|
|
|
|
2024-02-28 19:35:25 +00:00
|
|
|
// The older versions did not have the opj_config.h file
|
2024-03-04 11:03:26 +00:00
|
|
|
// So we use a more recent macro to detect whether it is there.
|
|
|
|
// Also see https://github.com/uclouvain/openjpeg/issues/1514
|
2024-02-28 19:35:25 +00:00
|
|
|
#if defined(OPJ_IMG_INFO)
|
|
|
|
#include "opj_config.h"
|
|
|
|
#endif
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2020-01-22 13:10:59 +00:00
|
|
|
static void openjpeg_warning(const char* msg, void* client_data)
|
2013-03-25 12:04:10 +00:00
|
|
|
{
|
2020-01-22 13:10:59 +00:00
|
|
|
grib_context_log((grib_context*)client_data, GRIB_LOG_WARNING, "openjpeg: %s", msg);
|
2013-03-25 12:04:10 +00:00
|
|
|
}
|
|
|
|
|
2020-01-22 13:10:59 +00:00
|
|
|
static void openjpeg_error(const char* msg, void* client_data)
|
2013-03-25 12:04:10 +00:00
|
|
|
{
|
2020-01-22 13:10:59 +00:00
|
|
|
grib_context_log((grib_context*)client_data, GRIB_LOG_ERROR, "openjpeg: %s", msg);
|
2013-03-25 12:04:10 +00:00
|
|
|
}
|
|
|
|
|
2020-01-22 13:10:59 +00:00
|
|
|
static void openjpeg_info(const char* msg, void* client_data)
|
2013-03-25 12:04:10 +00:00
|
|
|
{
|
2016-07-27 09:42:07 +00:00
|
|
|
/* grib_context_log((grib_context*)client_data,GRIB_LOG_INFO,"openjpeg: %s",msg); */
|
2013-03-25 12:04:10 +00:00
|
|
|
}
|
|
|
|
|
2016-07-25 14:57:48 +00:00
|
|
|
/* Note: The old OpenJPEG versions (e.g. v1.5.2) did not have this macro.
|
|
|
|
* From OpenJPEG v2.1.0 onwards there is the macro OPJ_VERSION_MAJOR */
|
2020-01-22 13:10:59 +00:00
|
|
|
#if !defined(OPJ_VERSION_MAJOR) /* The old interface */
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2020-01-22 13:10:59 +00:00
|
|
|
int grib_openjpeg_encode(grib_context* c, j2k_encode_helper* helper)
|
2016-07-25 14:57:48 +00:00
|
|
|
{
|
2020-01-22 13:10:59 +00:00
|
|
|
int err = GRIB_SUCCESS;
|
2016-07-27 09:42:07 +00:00
|
|
|
const int numcomps = 1;
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
int i;
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2020-01-22 13:10:59 +00:00
|
|
|
const double* values = helper->values;
|
|
|
|
long no_values = helper->no_values;
|
2016-07-27 09:42:07 +00:00
|
|
|
double reference_value = helper->reference_value;
|
|
|
|
double divisor = helper->divisor;
|
|
|
|
double decimal = helper->decimal;
|
2020-01-22 13:10:59 +00:00
|
|
|
int* data;
|
|
|
|
|
2020-01-22 14:48:06 +00:00
|
|
|
opj_cparameters_t parameters = {0,}; /* compression parameters */
|
|
|
|
opj_event_mgr_t event_mgr = {0,}; /* event manager */
|
2020-01-22 13:10:59 +00:00
|
|
|
opj_image_t* image = NULL;
|
2020-01-22 14:48:06 +00:00
|
|
|
opj_image_cmptparm_t cmptparm = {0,};
|
2020-01-22 13:10:59 +00:00
|
|
|
opj_cio_t* cio = NULL;
|
2016-07-27 09:42:07 +00:00
|
|
|
opj_cinfo_t* cinfo = NULL;
|
2013-03-25 12:04:10 +00:00
|
|
|
|
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
/* set encoding parameters to default values */
|
|
|
|
opj_set_default_encoder_parameters(¶meters);
|
|
|
|
|
2016-07-25 14:57:48 +00:00
|
|
|
grib_context_log(c, GRIB_LOG_DEBUG, "grib_openjpeg_encode: OpenJPEG version %s", opj_version());
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
parameters.tcp_numlayers = 1;
|
|
|
|
parameters.cp_disto_alloc = 1;
|
|
|
|
parameters.tcp_rates[0] = helper->compression;
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
/* initialize image component */
|
|
|
|
cmptparm.prec = helper->bits_per_value;
|
|
|
|
cmptparm.bpp = helper->bits_per_value; /* Not sure about this one and the previous. What is the difference? */
|
|
|
|
cmptparm.sgnd = 0;
|
|
|
|
cmptparm.dx = 1;
|
|
|
|
cmptparm.dy = 1;
|
|
|
|
cmptparm.w = helper->width;
|
|
|
|
cmptparm.h = helper->height;
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
/* create the image */
|
|
|
|
image = opj_image_create(numcomps, &cmptparm, CLRSPC_GRAY);
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2020-01-22 13:10:59 +00:00
|
|
|
if (!image) {
|
2016-07-27 09:42:07 +00:00
|
|
|
err = GRIB_ENCODING_ERROR;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
image->x0 = 0;
|
|
|
|
image->y0 = 0;
|
|
|
|
image->x1 = helper->width;
|
|
|
|
image->y1 = helper->height;
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2020-01-22 13:10:59 +00:00
|
|
|
Assert(cmptparm.prec <= sizeof(image->comps[0].data[0]) * 8 - 1); /* BR: -1 because I don't know what happens if the sign bit is set */
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2020-01-22 13:10:59 +00:00
|
|
|
Assert(helper->no_values == image->comps[0].h * image->comps[0].w);
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
/* Simple packing */
|
|
|
|
data = image->comps[0].data;
|
2020-01-22 13:10:59 +00:00
|
|
|
for (i = 0; i < no_values; i++) {
|
|
|
|
unsigned long unsigned_val = (unsigned long)((((values[i] * decimal) - (reference_value)) * divisor) + 0.5);
|
|
|
|
data[i] = unsigned_val;
|
2016-07-27 09:42:07 +00:00
|
|
|
}
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
/* get a J2K compressor handle */
|
|
|
|
cinfo = opj_create_compress(CODEC_J2K);
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
/* catch events using our callbacks and give a local context */
|
|
|
|
event_mgr.error_handler = openjpeg_error;
|
|
|
|
event_mgr.info_handler = openjpeg_info;
|
|
|
|
event_mgr.warning_handler = openjpeg_warning;
|
|
|
|
opj_set_event_mgr((opj_common_ptr)cinfo, &event_mgr, c);
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
/* setup the encoder parameters using the current image and user parameters */
|
|
|
|
opj_setup_encoder(cinfo, ¶meters, image);
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
/* open a byte stream for writing */
|
|
|
|
/* allocate memory for all tiles */
|
|
|
|
cio = opj_cio_open((opj_common_ptr)cinfo, NULL, 0);
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
/* encode image */
|
|
|
|
if (!opj_encode(cinfo, cio, image, NULL)) {
|
|
|
|
err = GRIB_ENCODING_ERROR;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
helper->jpeg_length = cio_tell(cio);
|
2020-01-22 13:10:59 +00:00
|
|
|
memcpy(helper->jpeg_buffer, cio->buffer, helper->jpeg_length);
|
2013-03-25 12:04:10 +00:00
|
|
|
|
|
|
|
cleanup:
|
2020-01-22 13:10:59 +00:00
|
|
|
if (cio)
|
|
|
|
opj_cio_close(cio);
|
|
|
|
if (cinfo)
|
|
|
|
opj_destroy_compress(cinfo);
|
|
|
|
if (image)
|
|
|
|
opj_image_destroy(image);
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
return err;
|
2013-03-25 12:04:10 +00:00
|
|
|
}
|
|
|
|
|
2021-12-04 14:11:54 +00:00
|
|
|
int grib_openjpeg_decode(grib_context* c, unsigned char* buf, const size_t* buflen, double* val, const size_t* n_vals)
|
2020-01-22 13:10:59 +00:00
|
|
|
{
|
2016-07-27 09:42:07 +00:00
|
|
|
int err = GRIB_SUCCESS;
|
|
|
|
int i;
|
2020-01-22 13:10:59 +00:00
|
|
|
unsigned long mask;
|
|
|
|
int* data;
|
2016-07-27 09:42:07 +00:00
|
|
|
size_t count;
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2020-01-22 14:48:06 +00:00
|
|
|
opj_dparameters_t parameters = {0,}; /* decompression parameters */
|
2020-01-22 13:10:59 +00:00
|
|
|
opj_dinfo_t* dinfo = NULL; /* handle to a decompressor */
|
2020-01-22 14:48:06 +00:00
|
|
|
opj_event_mgr_t event_mgr = {0,}; /* event manager */
|
2020-01-22 13:10:59 +00:00
|
|
|
opj_cio_t* cio = NULL;
|
|
|
|
opj_image_t* image = NULL;
|
2020-01-22 14:48:06 +00:00
|
|
|
opj_image_comp_t comp = {0,};
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
/* set decoding parameters to default values */
|
|
|
|
opj_set_default_decoder_parameters(¶meters);
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
/* JPEG-2000 codestream */
|
2016-07-25 14:57:48 +00:00
|
|
|
grib_context_log(c, GRIB_LOG_DEBUG, "grib_openjpeg_decode: OpenJPEG version %s", opj_version());
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
/* get a decoder handle */
|
|
|
|
dinfo = opj_create_decompress(CODEC_J2K);
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
/* catch events using our callbacks and give a local context */
|
|
|
|
event_mgr.error_handler = openjpeg_error;
|
|
|
|
event_mgr.info_handler = openjpeg_info;
|
|
|
|
event_mgr.warning_handler = openjpeg_warning;
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
opj_set_event_mgr((opj_common_ptr)dinfo, &event_mgr, c);
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
/* setup the decoder decoding parameters using user parameters */
|
|
|
|
opj_setup_decoder(dinfo, ¶meters);
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
/* open a byte stream */
|
|
|
|
cio = opj_cio_open((opj_common_ptr)dinfo, buf, *buflen);
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
image = opj_decode(dinfo, cio);
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2020-01-22 13:10:59 +00:00
|
|
|
if (!image) {
|
2016-07-27 09:42:07 +00:00
|
|
|
grib_context_log(c, GRIB_LOG_ERROR, "openjpeg: failed to decode image");
|
|
|
|
err = GRIB_DECODING_ERROR;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2020-01-22 13:10:59 +00:00
|
|
|
if (!(*n_vals <= image->comps[0].w * image->comps[0].h)) {
|
2016-07-27 09:42:07 +00:00
|
|
|
err = GRIB_DECODING_ERROR;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2020-01-22 13:10:59 +00:00
|
|
|
if ((image->numcomps != 1) || !(image->x1 * image->y1)) {
|
2016-07-27 09:42:07 +00:00
|
|
|
err = GRIB_DECODING_ERROR;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
Assert(image->comps[0].sgnd == 0);
|
2020-01-22 13:10:59 +00:00
|
|
|
Assert(comp.prec <= sizeof(image->comps[0].data[0]) * 8 - 1); /* BR: -1 because I don't know what happens if the sign bit is set */
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2020-01-22 13:10:59 +00:00
|
|
|
Assert(image->comps[0].prec < sizeof(mask) * 8 - 1);
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
data = image->comps[0].data;
|
|
|
|
mask = (1 << image->comps[0].prec) - 1;
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
count = image->comps[0].w * image->comps[0].h;
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2020-01-22 13:10:59 +00:00
|
|
|
for (i = 0; i < count; i++)
|
2016-07-27 09:42:07 +00:00
|
|
|
val[i] = data[i] & mask;
|
2013-03-25 12:04:10 +00:00
|
|
|
|
|
|
|
cleanup:
|
2016-07-27 09:42:07 +00:00
|
|
|
/* close the byte stream */
|
2020-01-22 13:10:59 +00:00
|
|
|
if (cio)
|
|
|
|
opj_cio_close(cio);
|
|
|
|
if (dinfo)
|
|
|
|
opj_destroy_decompress(dinfo);
|
|
|
|
if (image)
|
|
|
|
opj_image_destroy(image);
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
return err;
|
2013-03-25 12:04:10 +00:00
|
|
|
}
|
|
|
|
|
2022-08-13 13:54:41 +00:00
|
|
|
#else /* OPENJPEG VERSION 2 - macro OPJ_VERSION_MAJOR is defined */
|
2016-07-25 14:57:48 +00:00
|
|
|
|
|
|
|
/* OpenJPEG 2.1 version of grib_openjpeg_encoding.c */
|
|
|
|
|
2019-09-25 13:35:43 +00:00
|
|
|
/* opj_* Helper code from
|
|
|
|
* https://groups.google.com/forum/#!topic/openjpeg/8cebr0u7JgY
|
|
|
|
*/
|
2016-07-25 14:57:48 +00:00
|
|
|
/* These routines are added to use memory instead of a file for input and output */
|
|
|
|
/* struct need to treat memory as a stream */
|
|
|
|
typedef struct
|
|
|
|
{
|
2020-01-22 13:10:59 +00:00
|
|
|
OPJ_UINT8* pData; /* our data */
|
|
|
|
OPJ_SIZE_T dataSize; /* how big is our data */
|
|
|
|
OPJ_SIZE_T offset; /* where we are currently in our data */
|
|
|
|
j2k_encode_helper* helper;
|
2016-07-25 14:57:48 +00:00
|
|
|
} opj_memory_stream;
|
|
|
|
|
|
|
|
/* This will read from our memory to the buffer */
|
2020-01-22 13:10:59 +00:00
|
|
|
static OPJ_SIZE_T opj_memory_stream_read(void* buffer, OPJ_SIZE_T nb_bytes, void* p_user_data)
|
2016-07-25 14:57:48 +00:00
|
|
|
{
|
2020-01-22 13:10:59 +00:00
|
|
|
opj_memory_stream* mstream = (opj_memory_stream*)p_user_data; /* Our data */
|
|
|
|
OPJ_SIZE_T nb_bytes_read = nb_bytes; /* Amount to move to buffer */
|
2016-07-25 14:57:48 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
/* Check if the current offset is outside our data buffer */
|
|
|
|
if (mstream->offset >= mstream->dataSize)
|
2020-01-22 13:10:59 +00:00
|
|
|
return (OPJ_SIZE_T)-1;
|
2016-07-25 14:57:48 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
/* Check if we are reading more than we have */
|
|
|
|
if (nb_bytes > (mstream->dataSize - mstream->offset))
|
|
|
|
nb_bytes_read = mstream->dataSize - mstream->offset;
|
2016-07-25 14:57:48 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
memcpy(buffer, &(mstream->pData[mstream->offset]), nb_bytes_read);
|
2016-07-25 14:57:48 +00:00
|
|
|
mstream->offset += nb_bytes_read; /* Update the pointer to the new location */
|
2016-07-27 09:42:07 +00:00
|
|
|
return nb_bytes_read;
|
2016-07-25 14:57:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Write from the buffer to our memory */
|
2020-01-22 13:10:59 +00:00
|
|
|
static OPJ_SIZE_T opj_memory_stream_write(void* buffer, OPJ_SIZE_T nb_bytes, void* user_data)
|
2016-07-25 14:57:48 +00:00
|
|
|
{
|
2020-01-22 13:10:59 +00:00
|
|
|
opj_memory_stream* mstream = (opj_memory_stream*)user_data; /* our data */
|
|
|
|
OPJ_SIZE_T nb_bytes_write = nb_bytes; /* Amount to move to buffer */
|
2016-07-25 14:57:48 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
/* Check if the current offset is outside our data buffer */
|
|
|
|
if (mstream->offset >= mstream->dataSize)
|
|
|
|
return (OPJ_SIZE_T)-1;
|
2016-07-25 14:57:48 +00:00
|
|
|
|
|
|
|
/* Check if we are writing more than we have space for */
|
|
|
|
if (nb_bytes > (mstream->dataSize - mstream->offset))
|
2016-07-27 09:42:07 +00:00
|
|
|
nb_bytes_write = mstream->dataSize - mstream->offset;
|
2016-07-25 14:57:48 +00:00
|
|
|
|
|
|
|
/* Copy the data from the internal buffer */
|
2016-07-27 09:42:07 +00:00
|
|
|
memcpy(&(mstream->pData[mstream->offset]), buffer, nb_bytes_write);
|
2016-07-25 14:57:48 +00:00
|
|
|
mstream->offset += nb_bytes_write; /* Update the pointer to the new location */
|
2016-07-27 09:42:07 +00:00
|
|
|
return nb_bytes_write;
|
2016-07-25 14:57:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Moves the pointer forward, but never more than we have */
|
2020-01-22 13:10:59 +00:00
|
|
|
static OPJ_OFF_T opj_memory_stream_skip(OPJ_OFF_T nb_bytes, void* user_data)
|
2016-07-25 14:57:48 +00:00
|
|
|
{
|
2020-01-22 13:10:59 +00:00
|
|
|
opj_memory_stream* mstream = (opj_memory_stream*)user_data;
|
2016-07-27 09:42:07 +00:00
|
|
|
OPJ_SIZE_T l_nb_bytes;
|
2016-07-25 14:57:48 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
if (nb_bytes < 0)
|
2020-01-22 13:10:59 +00:00
|
|
|
return -1; /* No skipping backwards */
|
|
|
|
l_nb_bytes = (OPJ_SIZE_T)nb_bytes; /* Allowed because it is positive */
|
2016-07-25 14:57:48 +00:00
|
|
|
/* Do not allow jumping past the end */
|
2016-07-27 09:42:07 +00:00
|
|
|
if (l_nb_bytes > mstream->dataSize - mstream->offset)
|
2020-02-10 17:11:46 +00:00
|
|
|
l_nb_bytes = mstream->dataSize - mstream->offset; /* Jump the max. */
|
|
|
|
mstream->offset += l_nb_bytes; /* Make the jump */
|
|
|
|
return l_nb_bytes; /* Return how far we jumped */
|
2016-07-25 14:57:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Sets the pointer to anywhere in the memory */
|
2020-01-22 13:10:59 +00:00
|
|
|
static OPJ_BOOL opj_memory_stream_seek(OPJ_OFF_T nb_bytes, void* user_data)
|
2016-07-25 14:57:48 +00:00
|
|
|
{
|
2020-01-22 13:10:59 +00:00
|
|
|
opj_memory_stream* mstream = (opj_memory_stream*)user_data;
|
2016-07-25 14:57:48 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
if (nb_bytes < 0)
|
2016-07-25 14:57:48 +00:00
|
|
|
return OPJ_FALSE; /* Not before the buffer */
|
2020-01-22 13:10:59 +00:00
|
|
|
if (nb_bytes > (OPJ_OFF_T)mstream->dataSize)
|
|
|
|
return OPJ_FALSE; /* Not after the buffer */
|
|
|
|
mstream->offset = (OPJ_SIZE_T)nb_bytes; /* Move to new position */
|
2016-07-25 14:57:48 +00:00
|
|
|
return OPJ_TRUE;
|
|
|
|
}
|
|
|
|
|
2020-01-22 13:10:59 +00:00
|
|
|
static void opj_memory_stream_do_nothing(void* p_user_data)
|
2016-07-25 14:57:48 +00:00
|
|
|
{
|
2016-07-27 09:42:07 +00:00
|
|
|
OPJ_ARG_NOT_USED(p_user_data);
|
2016-07-25 14:57:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Create a stream to use memory as the input or output */
|
2016-10-19 12:44:32 +00:00
|
|
|
static opj_stream_t* opj_stream_create_default_memory_stream(opj_memory_stream* memoryStream, OPJ_BOOL is_read_stream)
|
2016-07-25 14:57:48 +00:00
|
|
|
{
|
2020-01-22 13:10:59 +00:00
|
|
|
opj_stream_t* stream;
|
2016-07-25 14:57:48 +00:00
|
|
|
|
2020-01-22 13:10:59 +00:00
|
|
|
if (!(stream = opj_stream_default_create(is_read_stream)))
|
|
|
|
return (NULL);
|
2016-07-25 14:57:48 +00:00
|
|
|
/* Set how to work with the frame buffer */
|
2020-01-22 13:10:59 +00:00
|
|
|
if (is_read_stream)
|
|
|
|
opj_stream_set_read_function(stream, opj_memory_stream_read);
|
|
|
|
else
|
|
|
|
opj_stream_set_write_function(stream, opj_memory_stream_write);
|
|
|
|
|
|
|
|
opj_stream_set_seek_function(stream, opj_memory_stream_seek);
|
|
|
|
opj_stream_set_skip_function(stream, opj_memory_stream_skip);
|
|
|
|
opj_stream_set_user_data(stream, memoryStream, opj_memory_stream_do_nothing);
|
|
|
|
opj_stream_set_user_data_length(stream, memoryStream->dataSize);
|
|
|
|
return stream;
|
2016-07-25 14:57:48 +00:00
|
|
|
}
|
|
|
|
|
2020-01-22 13:10:59 +00:00
|
|
|
int grib_openjpeg_encode(grib_context* c, j2k_encode_helper* helper)
|
2016-07-27 09:42:07 +00:00
|
|
|
{
|
2020-01-22 13:10:59 +00:00
|
|
|
int err = GRIB_SUCCESS;
|
2016-07-27 09:42:07 +00:00
|
|
|
const int numcomps = 1;
|
|
|
|
int i;
|
|
|
|
|
2020-01-22 13:10:59 +00:00
|
|
|
const double* values = helper->values;
|
|
|
|
long no_values = helper->no_values;
|
2016-07-27 09:42:07 +00:00
|
|
|
double reference_value = helper->reference_value;
|
|
|
|
double divisor = helper->divisor;
|
|
|
|
double decimal = helper->decimal;
|
2020-01-22 13:10:59 +00:00
|
|
|
int* data;
|
|
|
|
|
2020-01-22 14:48:06 +00:00
|
|
|
opj_cparameters_t parameters = {0,}; /* compression parameters */
|
2020-01-22 13:10:59 +00:00
|
|
|
opj_codec_t* codec = NULL;
|
|
|
|
opj_image_t* image = NULL;
|
2020-01-22 14:48:06 +00:00
|
|
|
opj_image_cmptparm_t cmptparm = {0,};
|
2020-01-22 13:10:59 +00:00
|
|
|
opj_stream_t* stream = NULL;
|
2016-07-27 09:42:07 +00:00
|
|
|
opj_memory_stream mstream;
|
|
|
|
|
|
|
|
/* set encoding parameters to default values */
|
|
|
|
opj_set_default_encoder_parameters(¶meters);
|
2016-07-25 14:57:48 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
grib_context_log(c, GRIB_LOG_DEBUG, "grib_openjpeg_encode: OpenJPEG version %s", opj_version());
|
2016-07-25 14:57:48 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
parameters.tcp_numlayers = 1;
|
|
|
|
parameters.cp_disto_alloc = 1;
|
2016-07-28 12:00:58 +00:00
|
|
|
/* parameters.numresolution = 1; */
|
2020-01-22 13:10:59 +00:00
|
|
|
parameters.tcp_rates[0] = helper->compression;
|
2016-07-27 09:42:07 +00:00
|
|
|
|
2016-07-28 12:00:58 +00:00
|
|
|
/* By default numresolution = 6 (must be between 1 and 32)
|
|
|
|
* This may be too large for some of our datasets, eg. 1xn, so adjust ...
|
|
|
|
*/
|
|
|
|
parameters.numresolution = 6;
|
2020-01-22 13:10:59 +00:00
|
|
|
while ((helper->width < (OPJ_UINT32)(1 << (parameters.numresolution - 1U))) ||
|
|
|
|
(helper->height < (OPJ_UINT32)(1 << (parameters.numresolution - 1U)))) {
|
2016-07-28 12:00:58 +00:00
|
|
|
parameters.numresolution--;
|
|
|
|
}
|
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
/* initialize image component */
|
|
|
|
cmptparm.prec = helper->bits_per_value;
|
|
|
|
cmptparm.sgnd = 0;
|
|
|
|
cmptparm.dx = 1;
|
|
|
|
cmptparm.dy = 1;
|
|
|
|
cmptparm.w = helper->width;
|
|
|
|
cmptparm.h = helper->height;
|
|
|
|
|
|
|
|
/* create the image */
|
|
|
|
image = opj_image_create(numcomps, &cmptparm, OPJ_CLRSPC_GRAY);
|
2020-01-22 13:10:59 +00:00
|
|
|
if (!image) {
|
2016-07-27 09:42:07 +00:00
|
|
|
err = GRIB_ENCODING_ERROR;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
image->x0 = 0;
|
|
|
|
image->y0 = 0;
|
|
|
|
image->x1 = helper->width;
|
|
|
|
image->y1 = helper->height;
|
|
|
|
|
2020-01-22 13:10:59 +00:00
|
|
|
Assert(cmptparm.prec <= sizeof(image->comps[0].data[0]) * 8 - 1); /* BR: -1 because I don't know what happens if the sign bit is set */
|
|
|
|
Assert(helper->no_values == image->comps[0].h * image->comps[0].w);
|
2016-07-27 09:42:07 +00:00
|
|
|
|
|
|
|
/* Simple packing */
|
|
|
|
data = image->comps[0].data;
|
2020-01-22 13:10:59 +00:00
|
|
|
for (i = 0; i < no_values; i++) {
|
|
|
|
unsigned long unsigned_val = (unsigned long)((((values[i] * decimal) - (reference_value)) * divisor) + 0.5);
|
|
|
|
data[i] = unsigned_val;
|
2016-07-27 09:42:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* get a J2K compressor handle */
|
|
|
|
codec = opj_create_compress(OPJ_CODEC_J2K);
|
|
|
|
|
|
|
|
opj_set_info_handler(codec, openjpeg_info, c);
|
|
|
|
opj_set_warning_handler(codec, openjpeg_warning, c);
|
2020-01-22 13:10:59 +00:00
|
|
|
opj_set_error_handler(codec, openjpeg_error, c);
|
2016-07-27 09:42:07 +00:00
|
|
|
|
|
|
|
/* setup the encoder parameters using the current image and user parameters */
|
|
|
|
if (!opj_setup_encoder(codec, ¶meters, image)) {
|
|
|
|
grib_context_log(c, GRIB_LOG_ERROR, "openjpeg: failed to setup encoder");
|
|
|
|
err = GRIB_ENCODING_ERROR;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* open a byte stream for writing */
|
2020-01-22 13:10:59 +00:00
|
|
|
mstream.helper = helper;
|
|
|
|
mstream.pData = (OPJ_UINT8*)helper->jpeg_buffer;
|
|
|
|
mstream.offset = 0;
|
2016-07-27 09:42:07 +00:00
|
|
|
mstream.dataSize = helper->buffer_size;
|
2020-01-22 13:10:59 +00:00
|
|
|
stream = opj_stream_create_default_memory_stream(&mstream, OPJ_STREAM_WRITE);
|
2016-07-27 09:42:07 +00:00
|
|
|
if (stream == NULL) {
|
|
|
|
grib_context_log(c, GRIB_LOG_ERROR, "openjpeg: failed create default memory stream");
|
|
|
|
err = GRIB_ENCODING_ERROR;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (!opj_start_compress(codec, image, stream)) {
|
|
|
|
grib_context_log(c, GRIB_LOG_ERROR, "openjpeg: failed to setup encoder");
|
|
|
|
err = GRIB_ENCODING_ERROR;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* encode image */
|
|
|
|
if (!opj_encode(codec, stream)) {
|
|
|
|
grib_context_log(c, GRIB_LOG_ERROR, "openjpeg: opj_encode failed");
|
|
|
|
err = GRIB_ENCODING_ERROR;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!opj_end_compress(codec, stream)) {
|
|
|
|
grib_context_log(c, GRIB_LOG_ERROR, "openjpeg: opj_end_compress failed");
|
|
|
|
err = GRIB_ENCODING_ERROR;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
helper->jpeg_length = mstream.offset;
|
2016-07-25 14:57:48 +00:00
|
|
|
|
|
|
|
cleanup:
|
2020-01-22 13:10:59 +00:00
|
|
|
if (codec)
|
|
|
|
opj_destroy_codec(codec);
|
|
|
|
if (stream)
|
|
|
|
opj_stream_destroy(stream);
|
|
|
|
if (image)
|
|
|
|
opj_image_destroy(image);
|
2016-07-25 14:57:48 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
return err;
|
2016-07-25 14:57:48 +00:00
|
|
|
}
|
2013-03-25 12:04:10 +00:00
|
|
|
|
2021-12-04 14:01:44 +00:00
|
|
|
int grib_openjpeg_decode(grib_context* c, unsigned char* buf, const size_t* buflen, double* val, const size_t* n_vals)
|
2016-07-27 09:42:07 +00:00
|
|
|
{
|
|
|
|
int err = GRIB_SUCCESS;
|
|
|
|
int i;
|
2020-01-22 13:10:59 +00:00
|
|
|
unsigned long mask;
|
|
|
|
int* data;
|
2016-07-27 09:42:07 +00:00
|
|
|
size_t count;
|
|
|
|
|
2020-01-22 14:48:06 +00:00
|
|
|
opj_dparameters_t parameters = {0,}; /* decompression parameters */
|
2020-01-22 13:10:59 +00:00
|
|
|
opj_stream_t* stream = NULL;
|
2016-07-27 09:42:07 +00:00
|
|
|
opj_memory_stream mstream;
|
2020-01-22 13:10:59 +00:00
|
|
|
opj_image_t* image = NULL;
|
|
|
|
opj_codec_t* codec = NULL;
|
2020-01-22 14:48:06 +00:00
|
|
|
opj_image_comp_t comp = {0,};
|
2016-07-27 09:42:07 +00:00
|
|
|
|
|
|
|
/* set decoding parameters to default values */
|
|
|
|
opj_set_default_decoder_parameters(¶meters);
|
|
|
|
parameters.decod_format = 1; /* JP2_FMT */
|
2016-07-25 14:57:48 +00:00
|
|
|
|
|
|
|
/* JPEG-2000 codestream */
|
|
|
|
grib_context_log(c, GRIB_LOG_DEBUG, "grib_openjpeg_decode: OpenJPEG version %s", opj_version());
|
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
/* get a decoder handle */
|
|
|
|
codec = opj_create_decompress(OPJ_CODEC_J2K);
|
|
|
|
|
|
|
|
/* catch events using our callbacks and give a local context */
|
|
|
|
opj_set_info_handler(codec, openjpeg_info, c);
|
|
|
|
opj_set_warning_handler(codec, openjpeg_warning, c);
|
2020-01-22 13:10:59 +00:00
|
|
|
opj_set_error_handler(codec, openjpeg_error, c);
|
2016-07-27 09:42:07 +00:00
|
|
|
|
|
|
|
/* initialize our memory stream */
|
2020-01-22 13:10:59 +00:00
|
|
|
mstream.pData = buf;
|
2016-07-27 09:42:07 +00:00
|
|
|
mstream.dataSize = *buflen;
|
2020-01-22 13:10:59 +00:00
|
|
|
mstream.offset = 0;
|
2016-07-27 09:42:07 +00:00
|
|
|
/* open a byte stream from memory stream */
|
2020-01-22 13:10:59 +00:00
|
|
|
stream = opj_stream_create_default_memory_stream(&mstream, OPJ_STREAM_READ);
|
2016-07-27 09:42:07 +00:00
|
|
|
|
|
|
|
/* setup the decoder decoding parameters using user parameters */
|
|
|
|
if (!opj_setup_decoder(codec, ¶meters)) {
|
|
|
|
grib_context_log(c, GRIB_LOG_ERROR, "openjpeg: failed to setup decoder");
|
|
|
|
err = GRIB_DECODING_ERROR;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2020-01-22 13:10:59 +00:00
|
|
|
if (!opj_read_header(stream, codec, &image)) {
|
2016-07-27 09:42:07 +00:00
|
|
|
grib_context_log(c, GRIB_LOG_ERROR, "openjpeg: failed to read the header");
|
|
|
|
err = GRIB_DECODING_ERROR;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (!opj_decode(codec, stream, image)) {
|
|
|
|
grib_context_log(c, GRIB_LOG_ERROR, "openjpeg: failed to decode");
|
|
|
|
err = GRIB_DECODING_ERROR;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2020-01-22 13:10:59 +00:00
|
|
|
if (!(*n_vals <= image->comps[0].w * image->comps[0].h)) {
|
2016-07-27 09:42:07 +00:00
|
|
|
err = GRIB_DECODING_ERROR;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2020-01-22 13:10:59 +00:00
|
|
|
if ((image->numcomps != 1) || (image->x1 * image->y1) == 0) {
|
2016-07-27 09:42:07 +00:00
|
|
|
err = GRIB_DECODING_ERROR;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
Assert(image->comps[0].sgnd == 0);
|
2020-01-22 13:10:59 +00:00
|
|
|
Assert(comp.prec <= sizeof(image->comps[0].data[0]) * 8 - 1); /* BR: -1 because I don't know what happens if the sign bit is set */
|
2016-07-27 09:42:07 +00:00
|
|
|
|
2020-01-22 13:10:59 +00:00
|
|
|
Assert(image->comps[0].prec < sizeof(mask) * 8 - 1);
|
2016-07-27 09:42:07 +00:00
|
|
|
|
|
|
|
data = image->comps[0].data;
|
|
|
|
mask = (1 << image->comps[0].prec) - 1;
|
|
|
|
|
|
|
|
count = image->comps[0].w * image->comps[0].h;
|
|
|
|
|
2020-01-22 13:10:59 +00:00
|
|
|
for (i = 0; i < count; i++)
|
2016-07-27 09:42:07 +00:00
|
|
|
val[i] = data[i] & mask;
|
|
|
|
|
|
|
|
if (!opj_end_decompress(codec, stream)) {
|
|
|
|
grib_context_log(c, GRIB_LOG_ERROR, "openjpeg: failed in opj_end_decompress");
|
|
|
|
err = GRIB_DECODING_ERROR;
|
|
|
|
}
|
|
|
|
|
2016-07-25 14:57:48 +00:00
|
|
|
cleanup:
|
2016-07-27 09:42:07 +00:00
|
|
|
/* close the byte stream */
|
2020-01-22 13:10:59 +00:00
|
|
|
if (codec)
|
|
|
|
opj_destroy_codec(codec);
|
|
|
|
if (stream)
|
|
|
|
opj_stream_destroy(stream);
|
|
|
|
if (image)
|
|
|
|
opj_image_destroy(image);
|
2016-07-25 14:57:48 +00:00
|
|
|
|
2016-07-27 09:42:07 +00:00
|
|
|
return err;
|
2016-07-25 14:57:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* OPENJPEG_VERSION */
|
|
|
|
|
|
|
|
#else /* No OpenJPEG */
|
|
|
|
|
2021-12-04 14:11:54 +00:00
|
|
|
int grib_openjpeg_decode(grib_context* c, unsigned char* buf, const size_t* buflen, double* val, const size_t* n_vals)
|
2016-07-27 09:42:07 +00:00
|
|
|
{
|
2023-12-26 17:54:09 +00:00
|
|
|
grib_context_log(c, GRIB_LOG_ERROR, "grib_openjpeg_decode: OpenJPEG JPEG support not enabled.");
|
2019-08-15 14:58:52 +00:00
|
|
|
return GRIB_FUNCTIONALITY_NOT_ENABLED;
|
2013-03-25 12:04:10 +00:00
|
|
|
}
|
|
|
|
|
2020-01-22 13:10:59 +00:00
|
|
|
int grib_openjpeg_encode(grib_context* c, j2k_encode_helper* helper)
|
2016-07-27 09:42:07 +00:00
|
|
|
{
|
2023-12-26 17:54:09 +00:00
|
|
|
grib_context_log(c, GRIB_LOG_ERROR, "grib_openjpeg_encode: OpenJPEG JPEG support not enabled.");
|
2019-08-15 14:58:52 +00:00
|
|
|
return GRIB_FUNCTIONALITY_NOT_ENABLED;
|
2013-03-25 12:04:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|