397 lines
13 KiB
C
397 lines
13 KiB
C
/*
|
|
* The copyright in this software is being made available under the 2-clauses
|
|
* BSD License, included below. This software may be subject to other third
|
|
* party and contributor rights, including patent rights, and no such rights
|
|
* are granted under this license.
|
|
*
|
|
* Copyright (c) 2017, IntoPix SA <contact@intopix.com>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "openjpeg.h"
|
|
#include "format_defs.h"
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
#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"
|
|
|
|
static int infile_format(const char *fname)
|
|
{
|
|
FILE *reader;
|
|
unsigned char buf[12];
|
|
unsigned int l_nb_read;
|
|
|
|
reader = fopen(fname, "rb");
|
|
|
|
if (reader == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
memset(buf, 0, 12);
|
|
l_nb_read = (unsigned int)fread(buf, 1, 12, reader);
|
|
fclose(reader);
|
|
if (l_nb_read != 12) {
|
|
return -1;
|
|
}
|
|
|
|
if (memcmp(buf, JP2_RFC3745_MAGIC, 12) == 0 || memcmp(buf, JP2_MAGIC, 4) == 0) {
|
|
return JP2_CFMT;
|
|
} else if (memcmp(buf, J2K_CODESTREAM_MAGIC, 4) == 0) {
|
|
return J2K_CFMT;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/**
|
|
sample error debug callback expecting no client object
|
|
*/
|
|
static void error_callback(const char *msg, void *client_data)
|
|
{
|
|
(void)client_data;
|
|
fprintf(stdout, "[ERROR] %s", msg);
|
|
}
|
|
/**
|
|
sample warning debug callback expecting no client object
|
|
*/
|
|
static void warning_callback(const char *msg, void *client_data)
|
|
{
|
|
(void)client_data;
|
|
fprintf(stdout, "[WARNING] %s", msg);
|
|
}
|
|
/**
|
|
sample debug callback expecting no client object
|
|
*/
|
|
static void info_callback(const char *msg, void *client_data)
|
|
{
|
|
(void)client_data;
|
|
(void)msg;
|
|
/*fprintf(stdout, "[INFO] %s", msg);*/
|
|
}
|
|
|
|
opj_image_t* decode(
|
|
OPJ_BOOL quiet,
|
|
const char* input_file,
|
|
OPJ_INT32 x0,
|
|
OPJ_INT32 y0,
|
|
OPJ_INT32 x1,
|
|
OPJ_INT32 y1,
|
|
OPJ_UINT32* ptilew,
|
|
OPJ_UINT32* ptileh,
|
|
OPJ_UINT32* pcblkw,
|
|
OPJ_UINT32* pcblkh)
|
|
{
|
|
opj_dparameters_t l_param;
|
|
opj_codec_t * l_codec = NULL;
|
|
opj_image_t * l_image = NULL;
|
|
opj_stream_t * l_stream = NULL;
|
|
|
|
if (!quiet) {
|
|
if (x0 != 0 || x1 != 0 || y0 != 0 || y1 != 0) {
|
|
printf("Decoding %d,%d,%d,%d\n", x0, y0, x1, y1);
|
|
} else {
|
|
printf("Decoding full image\n");
|
|
}
|
|
}
|
|
|
|
l_stream = opj_stream_create_default_file_stream(input_file, OPJ_TRUE);
|
|
if (!l_stream) {
|
|
fprintf(stderr, "ERROR -> failed to create the stream from the file\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* Set the default decoding parameters */
|
|
opj_set_default_decoder_parameters(&l_param);
|
|
|
|
/* */
|
|
l_param.decod_format = infile_format(input_file);
|
|
|
|
|
|
switch (l_param.decod_format) {
|
|
case J2K_CFMT: { /* JPEG-2000 codestream */
|
|
/* Get a decoder handle */
|
|
l_codec = opj_create_decompress(OPJ_CODEC_J2K);
|
|
break;
|
|
}
|
|
case JP2_CFMT: { /* JPEG 2000 compressed image data */
|
|
/* Get a decoder handle */
|
|
l_codec = opj_create_decompress(OPJ_CODEC_JP2);
|
|
break;
|
|
}
|
|
default: {
|
|
fprintf(stderr, "ERROR -> Not a valid JPEG2000 file!\n");
|
|
opj_stream_destroy(l_stream);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* catch events using our callbacks and give a local context */
|
|
opj_set_info_handler(l_codec, info_callback, 00);
|
|
opj_set_warning_handler(l_codec, warning_callback, 00);
|
|
opj_set_error_handler(l_codec, error_callback, 00);
|
|
|
|
/* Setup the decoder decoding parameters using user parameters */
|
|
if (! opj_setup_decoder(l_codec, &l_param)) {
|
|
fprintf(stderr, "ERROR ->failed to setup the decoder\n");
|
|
opj_stream_destroy(l_stream);
|
|
opj_destroy_codec(l_codec);
|
|
return NULL;
|
|
}
|
|
|
|
/* Read the main header of the codestream and if necessary the JP2 boxes*/
|
|
if (! opj_read_header(l_stream, l_codec, &l_image)) {
|
|
fprintf(stderr, "ERROR -> failed to read the header\n");
|
|
opj_stream_destroy(l_stream);
|
|
opj_destroy_codec(l_codec);
|
|
return NULL;
|
|
}
|
|
|
|
{
|
|
opj_codestream_info_v2_t* pCodeStreamInfo = opj_get_cstr_info(l_codec);
|
|
if (ptilew) {
|
|
*ptilew = pCodeStreamInfo->tdx;
|
|
}
|
|
if (ptileh) {
|
|
*ptilew = pCodeStreamInfo->tdy;
|
|
}
|
|
//int numResolutions = pCodeStreamInfo->m_default_tile_info.tccp_info[0].numresolutions;
|
|
if (pcblkw) {
|
|
*pcblkw = 1U << pCodeStreamInfo->m_default_tile_info.tccp_info[0].cblkw;
|
|
}
|
|
if (pcblkh) {
|
|
*pcblkh = 1U << pCodeStreamInfo->m_default_tile_info.tccp_info[0].cblkh;
|
|
}
|
|
opj_destroy_cstr_info(&pCodeStreamInfo);
|
|
}
|
|
|
|
if (x0 != 0 || x1 != 0 || y0 != 0 || y1 != 0) {
|
|
if (!opj_set_decode_area(l_codec, l_image, x0, y0, x1, y1)) {
|
|
fprintf(stderr, "ERROR -> failed to set the decoded area\n");
|
|
opj_stream_destroy(l_stream);
|
|
opj_destroy_codec(l_codec);
|
|
opj_image_destroy(l_image);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Get the decoded image */
|
|
if (!(opj_decode(l_codec, l_stream, l_image))) {
|
|
fprintf(stderr, "ERROR -> failed to decode image!\n");
|
|
opj_stream_destroy(l_stream);
|
|
opj_destroy_codec(l_codec);
|
|
opj_image_destroy(l_image);
|
|
return NULL;
|
|
}
|
|
|
|
if (! opj_end_decompress(l_codec, l_stream)) {
|
|
opj_stream_destroy(l_stream);
|
|
opj_destroy_codec(l_codec);
|
|
opj_image_destroy(l_image);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
opj_stream_destroy(l_stream);
|
|
opj_destroy_codec(l_codec);
|
|
return l_image;
|
|
}
|
|
|
|
OPJ_BOOL check_consistency(opj_image_t* p_image, opj_image_t* p_sub_image)
|
|
{
|
|
OPJ_UINT32 compno;
|
|
for (compno = 0; compno < p_image->numcomps; compno ++) {
|
|
OPJ_UINT32 y;
|
|
OPJ_UINT32 shift_y = p_sub_image->comps[compno].y0 - p_image->comps[compno].y0;
|
|
OPJ_UINT32 shift_x = p_sub_image->comps[compno].x0 - p_image->comps[compno].x0;
|
|
OPJ_UINT32 image_w = p_image->comps[compno].w;
|
|
OPJ_UINT32 sub_image_w = p_sub_image->comps[compno].w;
|
|
for (y = 0; y < p_sub_image->comps[compno].h; y++) {
|
|
OPJ_UINT32 x;
|
|
|
|
for (x = 0; x < sub_image_w; x++) {
|
|
OPJ_INT32 sub_image_val =
|
|
p_sub_image->comps[compno].data[y * sub_image_w + x];
|
|
OPJ_INT32 image_val =
|
|
p_image->comps[compno].data[(y + shift_y) * image_w + x + shift_x];
|
|
if (sub_image_val != image_val) {
|
|
fprintf(stderr,
|
|
"Difference found at subimage pixel (%u,%u) "
|
|
"of compno=%u: got %d, expected %d\n",
|
|
x, y, compno, sub_image_val, image_val);
|
|
return OPJ_FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return OPJ_TRUE;
|
|
}
|
|
|
|
static INLINE OPJ_UINT32 opj_uint_min(OPJ_UINT32 a, OPJ_UINT32 b)
|
|
{
|
|
return (a < b) ? a : b;
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
opj_image_t * l_image = NULL;
|
|
opj_image_t * l_sub_image = NULL;
|
|
OPJ_INT32 da_x0 = 0, da_y0 = 0, da_x1 = 0, da_y1 = 0;
|
|
const char* input_file = NULL;
|
|
OPJ_UINT32 tilew, tileh, cblkw, cblkh;
|
|
OPJ_UINT32 w, h;
|
|
OPJ_UINT32 x, y;
|
|
OPJ_UINT32 step_x, step_y;
|
|
OPJ_BOOL quiet = OPJ_FALSE;
|
|
OPJ_UINT32 nsteps = 100;
|
|
|
|
if (argc < 2) {
|
|
fprintf(stderr,
|
|
"Usage: test_decode_area [-q] [-steps n] input_file_jp2_or_jk2 [x0 y0 x1 y1]\n");
|
|
return 1;
|
|
}
|
|
|
|
{
|
|
int iarg;
|
|
for (iarg = 1; iarg < argc; iarg++) {
|
|
if (strcmp(argv[iarg], "-q") == 0) {
|
|
quiet = OPJ_TRUE;
|
|
} else if (strcmp(argv[iarg], "-steps") == 0 && iarg + 1 < argc) {
|
|
nsteps = (OPJ_UINT32)atoi(argv[iarg + 1]);
|
|
iarg ++;
|
|
} else if (input_file == NULL) {
|
|
input_file = argv[iarg];
|
|
} else if (iarg + 3 < argc) {
|
|
da_x0 = atoi(argv[iarg]);
|
|
da_y0 = atoi(argv[iarg + 1]);
|
|
da_x1 = atoi(argv[iarg + 2]);
|
|
da_y1 = atoi(argv[iarg + 3]);
|
|
iarg += 3;
|
|
}
|
|
}
|
|
}
|
|
|
|
l_image = decode(quiet, input_file, 0, 0, 0, 0,
|
|
&tilew, &tileh, &cblkw, &cblkh);
|
|
if (!l_image) {
|
|
return 1;
|
|
}
|
|
|
|
if (da_x0 != 0 || da_x1 != 0 || da_y0 != 0 || da_y1 != 0) {
|
|
l_sub_image = decode(quiet, input_file, da_x0, da_y0, da_x1, da_y1,
|
|
NULL, NULL, NULL, NULL);
|
|
if (!l_sub_image) {
|
|
fprintf(stderr, "decode failed for %d,%d,%d,%d\n",
|
|
da_x0, da_y0, da_x1, da_y1);
|
|
opj_image_destroy(l_sub_image);
|
|
opj_image_destroy(l_image);
|
|
return 1;
|
|
}
|
|
|
|
if (!check_consistency(l_image, l_sub_image)) {
|
|
fprintf(stderr, "Consistency checked failed for %d,%d,%d,%d\n",
|
|
da_x0, da_y0, da_x1, da_y1);
|
|
opj_image_destroy(l_sub_image);
|
|
opj_image_destroy(l_image);
|
|
return 1;
|
|
}
|
|
opj_image_destroy(l_sub_image);
|
|
opj_image_destroy(l_image);
|
|
return 0;
|
|
}
|
|
|
|
w = l_image->x1 - l_image->x0;
|
|
h = l_image->y1 - l_image->y0;
|
|
step_x = w > nsteps ? w / nsteps : 1;
|
|
step_y = h > nsteps ? h / nsteps : 1;
|
|
for (y = 0; y < h; y += step_y) {
|
|
for (x = 0; x < w; x += step_x) {
|
|
da_x0 = (OPJ_INT32)(l_image->x0 + x);
|
|
da_y0 = (OPJ_INT32)(l_image->y0 + y);
|
|
da_x1 = (OPJ_INT32)opj_uint_min(l_image->x1, l_image->x0 + x + 1);
|
|
da_y1 = (OPJ_INT32)opj_uint_min(l_image->y1, l_image->y0 + y + 1);
|
|
l_sub_image = decode(quiet, input_file, da_x0, da_y0, da_x1, da_y1,
|
|
NULL, NULL, NULL, NULL);
|
|
if (!l_sub_image) {
|
|
fprintf(stderr, "decode failed for %d,%d,%d,%d\n",
|
|
da_x0, da_y0, da_x1, da_y1);
|
|
opj_image_destroy(l_sub_image);
|
|
opj_image_destroy(l_image);
|
|
return 1;
|
|
}
|
|
|
|
if (!check_consistency(l_image, l_sub_image)) {
|
|
fprintf(stderr, "Consistency checked failed for %d,%d,%d,%d\n",
|
|
da_x0, da_y0, da_x1, da_y1);
|
|
opj_image_destroy(l_sub_image);
|
|
opj_image_destroy(l_image);
|
|
return 1;
|
|
}
|
|
opj_image_destroy(l_sub_image);
|
|
|
|
if (step_x > 1 || step_y > 1) {
|
|
if (step_x > 1) {
|
|
da_x0 = (OPJ_INT32)opj_uint_min(l_image->x1, (OPJ_UINT32)da_x0 + 1);
|
|
da_x1 = (OPJ_INT32)opj_uint_min(l_image->x1, (OPJ_UINT32)da_x1 + 1);
|
|
}
|
|
if (step_y > 1) {
|
|
da_y0 = (OPJ_INT32)opj_uint_min(l_image->y1, (OPJ_UINT32)da_y0 + 1);
|
|
da_y1 = (OPJ_INT32)opj_uint_min(l_image->y1, (OPJ_UINT32)da_y1 + 1);
|
|
}
|
|
if (da_x0 < (OPJ_INT32)l_image->x1 && da_y0 < (OPJ_INT32)l_image->y1) {
|
|
l_sub_image = decode(quiet, input_file, da_x0, da_y0, da_x1, da_y1,
|
|
NULL, NULL, NULL, NULL);
|
|
if (!l_sub_image) {
|
|
fprintf(stderr, "decode failed for %d,%d,%d,%d\n",
|
|
da_x0, da_y0, da_x1, da_y1);
|
|
opj_image_destroy(l_sub_image);
|
|
opj_image_destroy(l_image);
|
|
return 1;
|
|
}
|
|
|
|
if (!check_consistency(l_image, l_sub_image)) {
|
|
fprintf(stderr, "Consistency checked failed for %d,%d,%d,%d\n",
|
|
da_x0, da_y0, da_x1, da_y1);
|
|
opj_image_destroy(l_sub_image);
|
|
opj_image_destroy(l_image);
|
|
return 1;
|
|
}
|
|
opj_image_destroy(l_sub_image);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
opj_image_destroy(l_image);
|
|
return 0;
|
|
}
|