test_decode_area: fix to make it work with odd image dimensions
[openjpeg.git] / tests / test_decode_area.c
1 /*
2  * The copyright in this software is being made available under the 2-clauses
3  * BSD License, included below. This software may be subject to other third
4  * party and contributor rights, including patent rights, and no such rights
5  * are granted under this license.
6  *
7  * Copyright (c) 2017, IntoPix SA <contact@intopix.com>
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include <string.h>
33 #include <stdlib.h>
34
35 #include "openjpeg.h"
36 #include "format_defs.h"
37
38
39 /* -------------------------------------------------------------------------- */
40 #define JP2_RFC3745_MAGIC "\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a"
41 #define JP2_MAGIC "\x0d\x0a\x87\x0a"
42 /* position 45: "\xff\x52" */
43 #define J2K_CODESTREAM_MAGIC "\xff\x4f\xff\x51"
44
45 static int infile_format(const char *fname)
46 {
47     FILE *reader;
48     unsigned char buf[12];
49     unsigned int l_nb_read;
50
51     reader = fopen(fname, "rb");
52
53     if (reader == NULL) {
54         return -1;
55     }
56
57     memset(buf, 0, 12);
58     l_nb_read = (unsigned int)fread(buf, 1, 12, reader);
59     fclose(reader);
60     if (l_nb_read != 12) {
61         return -1;
62     }
63
64     if (memcmp(buf, JP2_RFC3745_MAGIC, 12) == 0 || memcmp(buf, JP2_MAGIC, 4) == 0) {
65         return JP2_CFMT;
66     } else if (memcmp(buf, J2K_CODESTREAM_MAGIC, 4) == 0) {
67         return J2K_CFMT;
68     } else {
69         return -1;
70     }
71 }
72
73
74 /* -------------------------------------------------------------------------- */
75
76 /**
77   sample error debug callback expecting no client object
78  */
79 static void error_callback(const char *msg, void *client_data)
80 {
81     (void)client_data;
82     fprintf(stdout, "[ERROR] %s", msg);
83 }
84 /**
85   sample warning debug callback expecting no client object
86  */
87 static void warning_callback(const char *msg, void *client_data)
88 {
89     (void)client_data;
90     fprintf(stdout, "[WARNING] %s", msg);
91 }
92 /**
93   sample debug callback expecting no client object
94  */
95 static void info_callback(const char *msg, void *client_data)
96 {
97     (void)client_data;
98     (void)msg;
99     /*fprintf(stdout, "[INFO] %s", msg);*/
100 }
101
102 opj_image_t* decode(
103     OPJ_BOOL quiet,
104     const char* input_file,
105     OPJ_INT32 x0,
106     OPJ_INT32 y0,
107     OPJ_INT32 x1,
108     OPJ_INT32 y1,
109     OPJ_UINT32* ptilew,
110     OPJ_UINT32* ptileh,
111     OPJ_UINT32* pcblkw,
112     OPJ_UINT32* pcblkh)
113 {
114     opj_dparameters_t l_param;
115     opj_codec_t * l_codec = NULL;
116     opj_image_t * l_image = NULL;
117     opj_stream_t * l_stream = NULL;
118
119     if (!quiet) {
120         if (x0 != 0 || x1 != 0 || y0 != 0 || y1 != 0) {
121             printf("Decoding %d,%d,%d,%d\n", x0, y0, x1, y1);
122         } else {
123             printf("Decoding full image\n");
124         }
125     }
126
127     l_stream = opj_stream_create_default_file_stream(input_file, OPJ_TRUE);
128     if (!l_stream) {
129         fprintf(stderr, "ERROR -> failed to create the stream from the file\n");
130         return NULL;
131     }
132
133     /* Set the default decoding parameters */
134     opj_set_default_decoder_parameters(&l_param);
135
136     /* */
137     l_param.decod_format = infile_format(input_file);
138
139
140     switch (l_param.decod_format) {
141     case J2K_CFMT: { /* JPEG-2000 codestream */
142         /* Get a decoder handle */
143         l_codec = opj_create_decompress(OPJ_CODEC_J2K);
144         break;
145     }
146     case JP2_CFMT: { /* JPEG 2000 compressed image data */
147         /* Get a decoder handle */
148         l_codec = opj_create_decompress(OPJ_CODEC_JP2);
149         break;
150     }
151     default: {
152         fprintf(stderr, "ERROR -> Not a valid JPEG2000 file!\n");
153         opj_stream_destroy(l_stream);
154         return NULL;
155     }
156     }
157
158     /* catch events using our callbacks and give a local context */
159     opj_set_info_handler(l_codec, info_callback, 00);
160     opj_set_warning_handler(l_codec, warning_callback, 00);
161     opj_set_error_handler(l_codec, error_callback, 00);
162
163     /* Setup the decoder decoding parameters using user parameters */
164     if (! opj_setup_decoder(l_codec, &l_param)) {
165         fprintf(stderr, "ERROR ->failed to setup the decoder\n");
166         opj_stream_destroy(l_stream);
167         opj_destroy_codec(l_codec);
168         return NULL;
169     }
170
171     /* Read the main header of the codestream and if necessary the JP2 boxes*/
172     if (! opj_read_header(l_stream, l_codec, &l_image)) {
173         fprintf(stderr, "ERROR -> failed to read the header\n");
174         opj_stream_destroy(l_stream);
175         opj_destroy_codec(l_codec);
176         return NULL;
177     }
178
179     {
180         opj_codestream_info_v2_t* pCodeStreamInfo = opj_get_cstr_info(l_codec);
181         if (ptilew) {
182             *ptilew = pCodeStreamInfo->tdx;
183         }
184         if (ptileh) {
185             *ptilew = pCodeStreamInfo->tdy;
186         }
187         //int numResolutions = pCodeStreamInfo->m_default_tile_info.tccp_info[0].numresolutions;
188         if (pcblkw) {
189             *pcblkw = 1U << pCodeStreamInfo->m_default_tile_info.tccp_info[0].cblkw;
190         }
191         if (pcblkh) {
192             *pcblkh = 1U << pCodeStreamInfo->m_default_tile_info.tccp_info[0].cblkh;
193         }
194         opj_destroy_cstr_info(&pCodeStreamInfo);
195     }
196
197     if (x0 != 0 || x1 != 0 || y0 != 0 || y1 != 0) {
198         if (!opj_set_decode_area(l_codec, l_image, x0, y0, x1, y1)) {
199             fprintf(stderr, "ERROR -> failed to set the decoded area\n");
200             opj_stream_destroy(l_stream);
201             opj_destroy_codec(l_codec);
202             opj_image_destroy(l_image);
203             return NULL;
204         }
205     }
206
207     /* Get the decoded image */
208     if (!(opj_decode(l_codec, l_stream, l_image))) {
209         fprintf(stderr, "ERROR -> failed to decode image!\n");
210         opj_stream_destroy(l_stream);
211         opj_destroy_codec(l_codec);
212         opj_image_destroy(l_image);
213         return NULL;
214     }
215
216     if (! opj_end_decompress(l_codec, l_stream)) {
217         opj_stream_destroy(l_stream);
218         opj_destroy_codec(l_codec);
219         opj_image_destroy(l_image);
220         return NULL;
221     }
222
223
224     opj_stream_destroy(l_stream);
225     opj_destroy_codec(l_codec);
226     return l_image;
227 }
228
229 OPJ_BOOL check_consistency(opj_image_t* p_image, opj_image_t* p_sub_image)
230 {
231     OPJ_UINT32 compno;
232     for (compno = 0; compno < p_image->numcomps; compno ++) {
233         OPJ_UINT32 y;
234         OPJ_UINT32 shift_y = p_sub_image->comps[compno].y0 - p_image->comps[compno].y0;
235         OPJ_UINT32 shift_x = p_sub_image->comps[compno].x0 - p_image->comps[compno].x0;
236         OPJ_UINT32 image_w = p_image->comps[compno].w;
237         OPJ_UINT32 sub_image_w = p_sub_image->comps[compno].w;
238         for (y = 0; y < p_sub_image->comps[compno].h; y++) {
239             OPJ_UINT32 x;
240
241             for (x = 0; x < sub_image_w; x++) {
242                 OPJ_INT32 sub_image_val =
243                     p_sub_image->comps[compno].data[y * sub_image_w + x];
244                 OPJ_INT32 image_val =
245                     p_image->comps[compno].data[(y + shift_y) * image_w + x + shift_x];
246                 if (sub_image_val != image_val) {
247                     fprintf(stderr,
248                             "Difference found at subimage pixel (%u,%u) "
249                             "of compno=%u: got %d, expected %d\n",
250                             x, y, compno, sub_image_val, image_val);
251                     return OPJ_FALSE;
252                 }
253             }
254         }
255     }
256     return OPJ_TRUE;
257 }
258
259 static INLINE OPJ_UINT32 opj_uint_min(OPJ_UINT32  a, OPJ_UINT32  b)
260 {
261     return (a < b) ? a : b;
262 }
263
264 int main(int argc, char** argv)
265 {
266     opj_image_t * l_image = NULL;
267     opj_image_t * l_sub_image = NULL;
268     OPJ_INT32 da_x0 = 0, da_y0 = 0, da_x1 = 0, da_y1 = 0;
269     const char* input_file = NULL;
270     OPJ_UINT32 tilew, tileh, cblkw, cblkh;
271     OPJ_UINT32 w, h;
272     OPJ_UINT32 x, y;
273     OPJ_UINT32 step_x, step_y;
274     OPJ_BOOL quiet = OPJ_FALSE;
275     OPJ_UINT32 nsteps = 100;
276
277     if (argc < 2) {
278         fprintf(stderr,
279                 "Usage: test_decode_area [-q] [-steps n] input_file_jp2_or_jk2 [x0 y0 x1 y1]\n");
280         return 1;
281     }
282
283     {
284         int iarg;
285         for (iarg = 1; iarg < argc; iarg++) {
286             if (strcmp(argv[iarg], "-q") == 0) {
287                 quiet = OPJ_TRUE;
288             } else if (strcmp(argv[iarg], "-steps") == 0 && iarg + 1 < argc) {
289                 nsteps = (OPJ_UINT32)atoi(argv[iarg + 1]);
290                 iarg ++;
291             } else if (input_file == NULL) {
292                 input_file = argv[iarg];
293             } else if (iarg + 3 < argc) {
294                 da_x0 = atoi(argv[iarg]);
295                 da_y0 = atoi(argv[iarg + 1]);
296                 da_x1 = atoi(argv[iarg + 2]);
297                 da_y1 = atoi(argv[iarg + 3]);
298                 iarg += 3;
299             }
300         }
301     }
302
303     l_image = decode(quiet, input_file, 0, 0, 0, 0,
304                      &tilew, &tileh, &cblkw, &cblkh);
305     if (!l_image) {
306         return 1;
307     }
308
309     if (da_x0 != 0 || da_x1 != 0 || da_y0 != 0 || da_y1 != 0) {
310         l_sub_image = decode(quiet, input_file, da_x0, da_y0, da_x1, da_y1,
311                              NULL, NULL, NULL, NULL);
312         if (!l_sub_image) {
313             fprintf(stderr, "decode failed for %d,%d,%d,%d\n",
314                     da_x0, da_y0, da_x1, da_y1);
315             opj_image_destroy(l_sub_image);
316             opj_image_destroy(l_image);
317             return 1;
318         }
319
320         if (!check_consistency(l_image, l_sub_image)) {
321             fprintf(stderr, "Consistency checked failed for %d,%d,%d,%d\n",
322                     da_x0, da_y0, da_x1, da_y1);
323             opj_image_destroy(l_sub_image);
324             opj_image_destroy(l_image);
325             return 1;
326         }
327         opj_image_destroy(l_sub_image);
328         opj_image_destroy(l_image);
329         return 0;
330     }
331
332     w = l_image->x1 - l_image->x0;
333     h = l_image->y1 - l_image->y0;
334     step_x = w > nsteps ? w / nsteps : 1;
335     step_y = h > nsteps ? h / nsteps : 1;
336     for (y = 0; y < h; y += step_y) {
337         for (x = 0; x < w; x += step_x) {
338             da_x0 = (OPJ_INT32)(l_image->x0 + x);
339             da_y0 = (OPJ_INT32)(l_image->y0 + y);
340             da_x1 = (OPJ_INT32)opj_uint_min(l_image->x1, l_image->x0 + x + 1);
341             da_y1 = (OPJ_INT32)opj_uint_min(l_image->y1, l_image->y0 + y + 1);
342             l_sub_image = decode(quiet, input_file, da_x0, da_y0, da_x1, da_y1,
343                                  NULL, NULL, NULL, NULL);
344             if (!l_sub_image) {
345                 fprintf(stderr, "decode failed for %d,%d,%d,%d\n",
346                         da_x0, da_y0, da_x1, da_y1);
347                 opj_image_destroy(l_sub_image);
348                 opj_image_destroy(l_image);
349                 return 1;
350             }
351
352             if (!check_consistency(l_image, l_sub_image)) {
353                 fprintf(stderr, "Consistency checked failed for %d,%d,%d,%d\n",
354                         da_x0, da_y0, da_x1, da_y1);
355                 opj_image_destroy(l_sub_image);
356                 opj_image_destroy(l_image);
357                 return 1;
358             }
359             opj_image_destroy(l_sub_image);
360
361             if (step_x > 1 || step_y > 1) {
362                 if (step_x > 1) {
363                     da_x0 = (OPJ_INT32)opj_uint_min(l_image->x1, (OPJ_UINT32)da_x0 + 1);
364                     da_x1 = (OPJ_INT32)opj_uint_min(l_image->x1, (OPJ_UINT32)da_x1 + 1);
365                 }
366                 if (step_y > 1) {
367                     da_y0 = (OPJ_INT32)opj_uint_min(l_image->y1, (OPJ_UINT32)da_y0 + 1);
368                     da_y1 = (OPJ_INT32)opj_uint_min(l_image->y1, (OPJ_UINT32)da_y1 + 1);
369                 }
370                 if (da_x0 < (OPJ_INT32)l_image->x1 && da_y0 < (OPJ_INT32)l_image->y1) {
371                     l_sub_image = decode(quiet, input_file, da_x0, da_y0, da_x1, da_y1,
372                                          NULL, NULL, NULL, NULL);
373                     if (!l_sub_image) {
374                         fprintf(stderr, "decode failed for %d,%d,%d,%d\n",
375                                 da_x0, da_y0, da_x1, da_y1);
376                         opj_image_destroy(l_sub_image);
377                         opj_image_destroy(l_image);
378                         return 1;
379                     }
380
381                     if (!check_consistency(l_image, l_sub_image)) {
382                         fprintf(stderr, "Consistency checked failed for %d,%d,%d,%d\n",
383                                 da_x0, da_y0, da_x1, da_y1);
384                         opj_image_destroy(l_sub_image);
385                         opj_image_destroy(l_image);
386                         return 1;
387                     }
388                     opj_image_destroy(l_sub_image);
389                 }
390             }
391         }
392     }
393
394     opj_image_destroy(l_image);
395     return 0;
396 }