Add capability to decode only a subset of all components of an image.
authorEven Rouault <even.rouault@spatialys.com>
Tue, 19 Sep 2017 14:52:07 +0000 (16:52 +0200)
committerEven Rouault <even.rouault@spatialys.com>
Tue, 19 Sep 2017 15:06:19 +0000 (17:06 +0200)
This adds a opj_set_decoded_components(opj_codec_t *p_codec,
OPJ_UINT32 numcomps, const OPJ_UINT32* comps_indices) function,
and equivalent "opj_decompress -c compno[,compno]*" option.

When specified, neither the MCT transform nor JP2 channel transformations
will be applied.

Tests added for various combinations of whole image vs tiled-based decoding,
full or reduced resolution, use of decode area or not.

12 files changed:
src/bin/jp2/opj_decompress.c
src/lib/openjp2/j2k.c
src/lib/openjp2/j2k.h
src/lib/openjp2/jp2.c
src/lib/openjp2/jp2.h
src/lib/openjp2/openjpeg.c
src/lib/openjp2/openjpeg.h
src/lib/openjp2/opj_codec.h
src/lib/openjp2/tcd.c
src/lib/openjp2/tcd.h
tests/nonregression/md5refs.txt
tests/nonregression/test_suite.ctest.in

index 479d83822eee5f715d8396e01f5e999051e8eb78..6c28277ab1824c33b5723764c0ed2c34545f3f44 100644 (file)
@@ -152,6 +152,10 @@ typedef struct opj_decompress_params {
     int num_threads;
     /* Quiet */
     int quiet;
+    /** number of components to decode */
+    OPJ_UINT32 numcomps;
+    /** indices of components to decode */
+    OPJ_UINT32* comps_indices;
 } opj_decompress_parameters;
 
 /* -------------------------------------------------------------------------- */
@@ -227,6 +231,10 @@ static void decode_help_display(void)
             "    If 'C' is specified (default), values are clipped.\n"
             "    If 'S' is specified, values are scaled.\n"
             "    A 0 value can be specified (meaning original bit depth).\n");
+    fprintf(stdout, "  -c first_comp_index[,second_comp_index][,...]\n"
+            "    OPTIONAL\n"
+            "    To limit the number of components to decoded.\n"
+            "    Component indices are numbered starting at 0.\n");
     fprintf(stdout, "  -force-rgb\n"
             "    Force output image colorspace to RGB\n"
             "  -upsample\n"
@@ -560,7 +568,7 @@ int parse_cmdline_decoder(int argc, char **argv,
         {"quiet", NO_ARG,  NULL, 1},
     };
 
-    const char optlist[] = "i:o:r:l:x:d:t:p:"
+    const char optlist[] = "i:o:r:l:x:d:t:p:c:"
 
                            /* UniPG>> */
 #ifdef USE_JPWL
@@ -770,6 +778,25 @@ int parse_cmdline_decoder(int argc, char **argv,
                 return 1;
             }
         }
+        break;
+
+        /* ----------------------------------------------------- */
+        case 'c': { /* Componenets */
+            const char* iter = opj_optarg;
+            while (1) {
+                parameters->numcomps ++;
+                parameters->comps_indices = (OPJ_UINT32*) realloc(
+                                                parameters->comps_indices,
+                                                parameters->numcomps * sizeof(OPJ_UINT32));
+                parameters->comps_indices[parameters->numcomps - 1] =
+                    (OPJ_UINT32) atoi(iter);
+                iter = strchr(iter, ',');
+                if (iter == NULL) {
+                    break;
+                }
+                iter ++;
+            }
+        }
         break;
             /* ----------------------------------------------------- */
 
@@ -1015,6 +1042,9 @@ static void destroy_parameters(opj_decompress_parameters* parameters)
             free(parameters->precision);
             parameters->precision = NULL;
         }
+
+        free(parameters->comps_indices);
+        parameters->comps_indices = NULL;
     }
 }
 
@@ -1455,6 +1485,20 @@ int main(int argc, char **argv)
             goto fin;
         }
 
+        if (parameters.numcomps) {
+            if (! opj_set_decoded_components(l_codec,
+                                             parameters.numcomps,
+                                             parameters.comps_indices)) {
+                fprintf(stderr,
+                        "ERROR -> opj_decompress: failed to set the component indices!\n");
+                opj_destroy_codec(l_codec);
+                opj_stream_destroy(l_stream);
+                opj_image_destroy(image);
+                failed = 1;
+                goto fin;
+            }
+        }
+
         if (getenv("USE_OPJ_SET_DECODED_RESOLUTION_FACTOR") != NULL) {
             /* For debugging/testing purposes, and also an illustration on how to */
             /* use the alternative API opj_set_decoded_resolution_factor() instead */
index 3e2bb749d9640dc02bfd85fe51138a6ee8fc2eae..c3f6ba11cdcd98d69e37a902ae9e6bfeb5109d81 100644 (file)
@@ -8266,6 +8266,11 @@ void opj_j2k_destroy(opj_j2k_t *p_j2k)
             p_j2k->m_specific_param.m_decoder.m_header_data = 00;
             p_j2k->m_specific_param.m_decoder.m_header_data_size = 0;
         }
+
+        opj_free(p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode);
+        p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode = 00;
+        p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode = 0;
+
     } else {
 
         if (p_j2k->m_specific_param.m_encoder.m_encoded_tile_data) {
@@ -8914,6 +8919,8 @@ OPJ_BOOL opj_j2k_decode_tile(opj_j2k_t * p_j2k,
                               l_image_for_bounds->y0,
                               l_image_for_bounds->x1,
                               l_image_for_bounds->y1,
+                              p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode,
+                              p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode,
                               l_tcp->m_data,
                               l_tcp->m_data_size,
                               p_tile_index,
@@ -9028,6 +9035,11 @@ static OPJ_BOOL opj_j2k_update_image_data(opj_tcd_t * p_tcd,
             p_src_data = l_tilec->data_win;
         }
 
+        if (p_src_data == NULL) {
+            /* Happens for partial component decoding */
+            continue;
+        }
+
         l_width_src = (OPJ_UINT32)(res_x1 - res_x0);
         l_height_src = (OPJ_UINT32)(res_y1 - res_y0);
 
@@ -9228,6 +9240,65 @@ static OPJ_BOOL opj_j2k_update_image_dimensions(opj_image_t* p_image,
     return OPJ_TRUE;
 }
 
+OPJ_BOOL opj_j2k_set_decoded_components(opj_j2k_t *p_j2k,
+                                        OPJ_UINT32 numcomps,
+                                        const OPJ_UINT32* comps_indices,
+                                        opj_event_mgr_t * p_manager)
+{
+    OPJ_UINT32 i;
+    OPJ_BOOL* already_mapped;
+
+    if (p_j2k->m_private_image == NULL) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "opj_read_header() should be called before "
+                      "opj_set_decoded_components().\n");
+        return OPJ_FALSE;
+    }
+
+    already_mapped = (OPJ_BOOL*) opj_calloc(sizeof(OPJ_BOOL),
+                                            p_j2k->m_private_image->numcomps);
+    if (already_mapped == NULL) {
+        return OPJ_FALSE;
+    }
+
+    for (i = 0; i < numcomps; i++) {
+        if (comps_indices[i] >= p_j2k->m_private_image->numcomps) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Invalid component index: %u\n",
+                          comps_indices[i]);
+            opj_free(already_mapped);
+            return OPJ_FALSE;
+        }
+        if (already_mapped[comps_indices[i]]) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Component index %u used several times\n",
+                          comps_indices[i]);
+            opj_free(already_mapped);
+            return OPJ_FALSE;
+        }
+        already_mapped[comps_indices[i]] = OPJ_TRUE;
+    }
+    opj_free(already_mapped);
+
+    opj_free(p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode);
+    if (numcomps) {
+        p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode =
+            (OPJ_UINT32*) opj_malloc(numcomps * sizeof(OPJ_UINT32));
+        if (p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode == NULL) {
+            p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode = 0;
+            return OPJ_FALSE;
+        }
+        memcpy(p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode,
+               comps_indices,
+               numcomps * sizeof(OPJ_UINT32));
+    } else {
+        p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode = NULL;
+    }
+    p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode = numcomps;
+
+    return OPJ_TRUE;
+}
+
 
 OPJ_BOOL opj_j2k_set_decode_area(opj_j2k_t *p_j2k,
                                  opj_image_t* p_image,
@@ -10817,13 +10888,71 @@ static OPJ_BOOL opj_j2k_setup_decoding_tile(opj_j2k_t *p_j2k,
     return OPJ_TRUE;
 }
 
+static OPJ_BOOL opj_j2k_move_data_from_codec_to_output_image(opj_j2k_t * p_j2k,
+        opj_image_t * p_image)
+{
+    OPJ_UINT32 compno;
+
+    /* Move data and copy one information from codec to output image*/
+    if (p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode > 0) {
+        opj_image_comp_t* newcomps =
+            (opj_image_comp_t*) opj_malloc(
+                p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode *
+                sizeof(opj_image_comp_t));
+        if (newcomps == NULL) {
+            opj_image_destroy(p_j2k->m_private_image);
+            p_j2k->m_private_image = NULL;
+            return OPJ_FALSE;
+        }
+        for (compno = 0; compno < p_image->numcomps; compno++) {
+            opj_image_data_free(p_image->comps[compno].data);
+            p_image->comps[compno].data = NULL;
+        }
+        for (compno = 0;
+                compno < p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode; compno++) {
+            OPJ_UINT32 src_compno =
+                p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode[compno];
+            memcpy(&(newcomps[compno]),
+                   &(p_j2k->m_output_image->comps[src_compno]),
+                   sizeof(opj_image_comp_t));
+            newcomps[compno].resno_decoded =
+                p_j2k->m_output_image->comps[src_compno].resno_decoded;
+            newcomps[compno].data = p_j2k->m_output_image->comps[src_compno].data;
+            p_j2k->m_output_image->comps[src_compno].data = NULL;
+        }
+        for (compno = 0; compno < p_image->numcomps; compno++) {
+            assert(p_j2k->m_output_image->comps[compno].data == NULL);
+            opj_image_data_free(p_j2k->m_output_image->comps[compno].data);
+            p_j2k->m_output_image->comps[compno].data = NULL;
+        }
+        p_image->numcomps = p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode;
+        opj_free(p_image->comps);
+        p_image->comps = newcomps;
+    } else {
+        for (compno = 0; compno < p_image->numcomps; compno++) {
+            p_image->comps[compno].resno_decoded =
+                p_j2k->m_output_image->comps[compno].resno_decoded;
+            opj_image_data_free(p_image->comps[compno].data);
+            p_image->comps[compno].data = p_j2k->m_output_image->comps[compno].data;
+#if 0
+            char fn[256];
+            sprintf(fn, "/tmp/%d.raw", compno);
+            FILE *debug = fopen(fn, "wb");
+            fwrite(p_image->comps[compno].data, sizeof(OPJ_INT32),
+                   p_image->comps[compno].w * p_image->comps[compno].h, debug);
+            fclose(debug);
+#endif
+            p_j2k->m_output_image->comps[compno].data = NULL;
+        }
+    }
+    return OPJ_TRUE;
+}
+
 OPJ_BOOL opj_j2k_decode(opj_j2k_t * p_j2k,
                         opj_stream_private_t * p_stream,
                         opj_image_t * p_image,
                         opj_event_mgr_t * p_manager)
 {
-    OPJ_UINT32 compno;
-
     if (!p_image) {
         return OPJ_FALSE;
     }
@@ -10874,23 +11003,7 @@ OPJ_BOOL opj_j2k_decode(opj_j2k_t * p_j2k,
     }
 
     /* Move data and copy one information from codec to output image*/
-    for (compno = 0; compno < p_image->numcomps; compno++) {
-        p_image->comps[compno].resno_decoded =
-            p_j2k->m_output_image->comps[compno].resno_decoded;
-        opj_image_data_free(p_image->comps[compno].data);
-        p_image->comps[compno].data = p_j2k->m_output_image->comps[compno].data;
-#if 0
-        char fn[256];
-        sprintf(fn, "/tmp/%d.raw", compno);
-        FILE *debug = fopen(fn, "wb");
-        fwrite(p_image->comps[compno].data, sizeof(OPJ_INT32),
-               p_image->comps[compno].w * p_image->comps[compno].h, debug);
-        fclose(debug);
-#endif
-        p_j2k->m_output_image->comps[compno].data = NULL;
-    }
-
-    return OPJ_TRUE;
+    return opj_j2k_move_data_from_codec_to_output_image(p_j2k, p_image);
 }
 
 OPJ_BOOL opj_j2k_get_tile(opj_j2k_t *p_j2k,
@@ -11005,20 +11118,7 @@ OPJ_BOOL opj_j2k_get_tile(opj_j2k_t *p_j2k,
     }
 
     /* Move data and copy one information from codec to output image*/
-    for (compno = 0; compno < p_image->numcomps; compno++) {
-        p_image->comps[compno].resno_decoded =
-            p_j2k->m_output_image->comps[compno].resno_decoded;
-
-        if (p_image->comps[compno].data) {
-            opj_image_data_free(p_image->comps[compno].data);
-        }
-
-        p_image->comps[compno].data = p_j2k->m_output_image->comps[compno].data;
-
-        p_j2k->m_output_image->comps[compno].data = NULL;
-    }
-
-    return OPJ_TRUE;
+    return opj_j2k_move_data_from_codec_to_output_image(p_j2k, p_image);
 }
 
 OPJ_BOOL opj_j2k_set_decoded_resolution_factor(opj_j2k_t *p_j2k,
index c9308f7dd8233ac9cb2c1d2d6ff6b0f914561033..b5fa5143e77780bcde3b13f6c45d69b44ccd6154 100644 (file)
@@ -480,6 +480,10 @@ typedef struct opj_j2k_dec {
      * SOD reader function. FIXME NOT USED for the moment
      */
     OPJ_BOOL   m_last_tile_part;
+
+    OPJ_UINT32   m_numcomps_to_decode;
+    OPJ_UINT32  *m_comps_indices_to_decode;
+
     /** to tell that a tile can be decoded. */
     OPJ_BITFIELD m_can_decode : 1;
     OPJ_BITFIELD m_discard_tiles : 1;
@@ -705,6 +709,21 @@ OPJ_BOOL opj_j2k_read_tile_header(opj_j2k_t * p_j2k,
                                   opj_event_mgr_t * p_manager);
 
 
+/** Sets the indices of the components to decode.
+ *
+ * @param p_j2k         the jpeg2000 codec.
+ * @param numcomps      Number of components to decode.
+ * @param comps_indices Array of num_compts indices (numbering starting at 0)
+ *                      corresponding to the components to decode.
+ * @param p_manager     Event manager
+ *
+ * @return OPJ_TRUE in case of success.
+ */
+OPJ_BOOL opj_j2k_set_decoded_components(opj_j2k_t *p_j2k,
+                                        OPJ_UINT32 numcomps,
+                                        const OPJ_UINT32* comps_indices,
+                                        opj_event_mgr_t * p_manager);
+
 /**
  * Sets the given area to be decoded. This function should be called right after opj_read_header and before any tile header reading.
  *
index 84d393273f8c4cf9339534ef5ce9512e133e461d..81d03480fcae0cc681c997a05e32c024707479b3 100644 (file)
@@ -1607,6 +1607,11 @@ OPJ_BOOL opj_jp2_decode(opj_jp2_t *jp2,
         return OPJ_FALSE;
     }
 
+    if (jp2->j2k->m_specific_param.m_decoder.m_numcomps_to_decode) {
+        /* Bypass all JP2 component transforms */
+        return OPJ_TRUE;
+    }
+
     if (!jp2->ignore_pclr_cmap_cdef) {
         if (!opj_jp2_check_color(p_image, &(jp2->color), p_manager)) {
             return OPJ_FALSE;
@@ -3069,6 +3074,16 @@ void opj_jp2_destroy(opj_jp2_t *jp2)
     }
 }
 
+OPJ_BOOL opj_jp2_set_decoded_components(opj_jp2_t *p_jp2,
+                                        OPJ_UINT32 numcomps,
+                                        const OPJ_UINT32* comps_indices,
+                                        opj_event_mgr_t * p_manager)
+{
+    return opj_j2k_set_decoded_components(p_jp2->j2k,
+                                          numcomps, comps_indices,
+                                          p_manager);
+}
+
 OPJ_BOOL opj_jp2_set_decode_area(opj_jp2_t *p_jp2,
                                  opj_image_t* p_image,
                                  OPJ_INT32 p_start_x, OPJ_INT32 p_start_y,
@@ -3100,6 +3115,11 @@ OPJ_BOOL opj_jp2_get_tile(opj_jp2_t *p_jp2,
         return OPJ_FALSE;
     }
 
+    if (p_jp2->j2k->m_specific_param.m_decoder.m_numcomps_to_decode) {
+        /* Bypass all JP2 component transforms */
+        return OPJ_TRUE;
+    }
+
     if (!opj_jp2_check_color(p_image, &(p_jp2->color), p_manager)) {
         return OPJ_FALSE;
     }
index 3ff66ebecf6d66d07302589badb1441ee73d2575..34abd5118e3740a02c5692ee92aba3f1a7004431 100644 (file)
@@ -235,6 +235,12 @@ Decoding parameters are returned in jp2->j2k->cp.
 */
 void opj_jp2_setup_decoder(opj_jp2_t *jp2, opj_dparameters_t *parameters);
 
+/** Allocates worker threads for the compressor/decompressor.
+ *
+ * @param jp2 JP2 decompressor handle
+ * @param num_threads Number of threads.
+ * @return OPJ_TRUE in case of success.
+ */
 OPJ_BOOL opj_jp2_set_threads(opj_jp2_t *jp2, OPJ_UINT32 num_threads);
 
 /**
@@ -327,6 +333,21 @@ OPJ_BOOL opj_jp2_read_header(opj_stream_private_t *p_stream,
                              opj_image_t ** p_image,
                              opj_event_mgr_t * p_manager);
 
+/** Sets the indices of the components to decode.
+ *
+ * @param jp2 JP2 decompressor handle
+ * @param numcomps Number of components to decode.
+ * @param comps_indices Array of num_compts indices (numbering starting at 0)
+ *                     corresponding to the components to decode.
+ * @param p_manager Event manager;
+ *
+ * @return OPJ_TRUE in case of success.
+ */
+OPJ_BOOL opj_jp2_set_decoded_components(opj_jp2_t *jp2,
+                                        OPJ_UINT32 numcomps,
+                                        const OPJ_UINT32* comps_indices,
+                                        opj_event_mgr_t * p_manager);
+
 /**
  * Reads a tile header.
  * @param  p_jp2         the jpeg2000 codec.
index 4e649a74f378d1a08a0f015280d5052f0b54cd3d..5ba205e15cddd4a8be8834af6a0b7d74ce54a23b 100644 (file)
@@ -245,6 +245,12 @@ opj_codec_t* OPJ_CALLCONV opj_create_decompress(OPJ_CODEC_FORMAT p_format)
                          OPJ_UINT32 res_factor,
                          struct opj_event_mgr * p_manager)) opj_j2k_set_decoded_resolution_factor;
 
+        l_codec->m_codec_data.m_decompression.opj_set_decoded_components =
+            (OPJ_BOOL(*)(void * p_codec,
+                         OPJ_UINT32 numcomps,
+                         const OPJ_UINT32 * comps_indices,
+                         struct opj_event_mgr * p_manager)) opj_j2k_set_decoded_components;
+
         l_codec->opj_set_threads =
             (OPJ_BOOL(*)(void * p_codec, OPJ_UINT32 num_threads)) opj_j2k_set_threads;
 
@@ -327,6 +333,12 @@ opj_codec_t* OPJ_CALLCONV opj_create_decompress(OPJ_CODEC_FORMAT p_format)
                          OPJ_UINT32 res_factor,
                          opj_event_mgr_t * p_manager)) opj_jp2_set_decoded_resolution_factor;
 
+        l_codec->m_codec_data.m_decompression.opj_set_decoded_components =
+            (OPJ_BOOL(*)(void * p_codec,
+                         OPJ_UINT32 numcomps,
+                         const OPJ_UINT32 * comps_indices,
+                         struct opj_event_mgr * p_manager)) opj_jp2_set_decoded_components;
+
         l_codec->opj_set_threads =
             (OPJ_BOOL(*)(void * p_codec, OPJ_UINT32 num_threads)) opj_jp2_set_threads;
 
@@ -426,6 +438,27 @@ OPJ_BOOL OPJ_CALLCONV opj_read_header(opj_stream_t *p_stream,
     return OPJ_FALSE;
 }
 
+
+OPJ_BOOL OPJ_CALLCONV opj_set_decoded_components(opj_codec_t *p_codec,
+        OPJ_UINT32 numcomps,
+        const OPJ_UINT32* comps_indices)
+{
+    if (p_codec) {
+        opj_codec_private_t * l_codec = (opj_codec_private_t *) p_codec;
+
+        if (! l_codec->is_decompressor) {
+            return OPJ_FALSE;
+        }
+
+        return  l_codec->m_codec_data.m_decompression.opj_set_decoded_components(
+                    l_codec->m_codec,
+                    numcomps,
+                    comps_indices,
+                    &(l_codec->m_event_mgr));
+    }
+    return OPJ_FALSE;
+}
+
 OPJ_BOOL OPJ_CALLCONV opj_decode(opj_codec_t *p_codec,
                                  opj_stream_t *p_stream,
                                  opj_image_t* p_image)
index 79e0bb4fc7ebc492d99a4f8db08e60258be449a4..2888e4d1af9ce914fad3f54522c870bcefe3cbc5 100644 (file)
@@ -1336,6 +1336,29 @@ OPJ_API OPJ_BOOL OPJ_CALLCONV opj_read_header(opj_stream_t *p_stream,
         opj_codec_t *p_codec,
         opj_image_t **p_image);
 
+
+/** Restrict the number of components to decode.
+ *
+ * This function should be called after opj_read_header().
+ *
+ * Normally all the components are decoded. This call enables to restrict
+ * the set of decoded components to the specified indices.
+ * Note that neither the MCT transform nor JP2 channel transformatios will be applied.
+ *
+ * Note: opj_decode_tile_data() should not be used together with opj_set_decoded_components().
+ *
+ * @param   p_codec         the jpeg2000 codec to read.
+ * @param   numcomps        Size of the comps_indices array.
+ * @param   comps_indices   Array of numcomps values representing the indices
+ *                          of the components to decode (relative to the
+ *                          codestream, starting at 0)
+ *
+ * @return OPJ_TRUE         in case of success.
+ */
+OPJ_API OPJ_BOOL OPJ_CALLCONV opj_set_decoded_components(opj_codec_t *p_codec,
+        OPJ_UINT32 numcomps,
+        const OPJ_UINT32* comps_indices);
+
 /**
  * Sets the given area to be decoded. This function should be called right after opj_read_header and before any tile header reading.
  *
@@ -1452,6 +1475,8 @@ OPJ_API OPJ_BOOL OPJ_CALLCONV opj_read_tile_header(opj_codec_t *p_codec,
  * Reads a tile data. This function is compulsory and allows one to decode tile data. opj_read_tile_header should be called before.
  * The user may need to refer to the image got by opj_read_header to understand the size being taken by the tile.
  *
+ * Note: opj_decode_tile_data() should not be used together with opj_set_decoded_components().
+ *
  * @param   p_codec         the jpeg2000 codec.
  * @param   p_tile_index    the index of the tile being decoded, this should be the value set by opj_read_tile_header.
  * @param   p_data          pointer to a memory block that will hold the decoded data.
index 2dbeac96355f7d4776bf225c843210a62ba8c55b..b962b121633e1fb10c65a8b2bee5b5d99c8cdee3 100644 (file)
@@ -111,6 +111,11 @@ typedef struct opj_codec_private {
                     OPJ_UINT32 res_factor,
                     opj_event_mgr_t * p_manager);
 
+            /** Set the decoded components */
+            OPJ_BOOL(*opj_set_decoded_components)(void * p_codec,
+                                                  OPJ_UINT32 num_comps,
+                                                  const OPJ_UINT32* comps_indices,
+                                                  opj_event_mgr_t * p_manager);
         } m_decompression;
 
         /**
index fad9d23c02039d2b5a9b824aa5aeee218741b7c3..1dd15405d17d451e89bd3760834b169eb630882c 100644 (file)
@@ -679,6 +679,9 @@ void opj_tcd_destroy(opj_tcd_t *tcd)
             opj_free(tcd->tcd_image);
             tcd->tcd_image = 00;
         }
+
+        opj_free(tcd->used_component);
+
         opj_free(tcd);
     }
 }
@@ -1439,6 +1442,8 @@ OPJ_BOOL opj_tcd_decode_tile(opj_tcd_t *p_tcd,
                              OPJ_UINT32 win_y0,
                              OPJ_UINT32 win_x1,
                              OPJ_UINT32 win_y1,
+                             OPJ_UINT32 numcomps_to_decode,
+                             const OPJ_UINT32 *comps_indices,
                              OPJ_BYTE *p_src,
                              OPJ_UINT32 p_max_length,
                              OPJ_UINT32 p_tile_no,
@@ -1457,7 +1462,27 @@ OPJ_BOOL opj_tcd_decode_tile(opj_tcd_t *p_tcd,
     p_tcd->win_y1 = win_y1;
     p_tcd->whole_tile_decoding = OPJ_TRUE;
 
+    opj_free(p_tcd->used_component);
+    p_tcd->used_component = NULL;
+
+    if (numcomps_to_decode) {
+        OPJ_BOOL* used_component = (OPJ_BOOL*) opj_calloc(sizeof(OPJ_BOOL),
+                                   p_tcd->image->numcomps);
+        if (used_component == NULL) {
+            return OPJ_FALSE;
+        }
+        for (compno = 0; compno < numcomps_to_decode; compno++) {
+            used_component[ comps_indices[compno] ] = OPJ_TRUE;
+        }
+
+        p_tcd->used_component = used_component;
+    }
+
     for (compno = 0; compno < p_tcd->image->numcomps; compno++) {
+        if (p_tcd->used_component != NULL && !p_tcd->used_component[compno]) {
+            continue;
+        }
+
         if (!opj_tcd_is_whole_tilecomp_decoding(p_tcd, compno)) {
             p_tcd->whole_tile_decoding = OPJ_FALSE;
             break;
@@ -1475,6 +1500,10 @@ OPJ_BOOL opj_tcd_decode_tile(opj_tcd_t *p_tcd,
             OPJ_SIZE_T res_w = (OPJ_SIZE_T)(l_res->x1 - l_res->x0);
             OPJ_SIZE_T res_h = (OPJ_SIZE_T)(l_res->y1 - l_res->y0);
 
+            if (p_tcd->used_component != NULL && !p_tcd->used_component[compno]) {
+                continue;
+            }
+
             /* issue 733, l_data_size == 0U, probably something wrong should be checked before getting here */
             if (res_h > 0 && res_w > SIZE_MAX / res_h) {
                 opj_event_msg(p_manager, EVT_ERROR,
@@ -1506,6 +1535,11 @@ OPJ_BOOL opj_tcd_decode_tile(opj_tcd_t *p_tcd,
             OPJ_UINT32 resno;
             opj_tcd_tilecomp_t* tilec = &(p_tcd->tcd_image->tiles->comps[compno]);
             opj_image_comp_t* image_comp = &(p_tcd->image->comps[compno]);
+
+            if (p_tcd->used_component != NULL && !p_tcd->used_component[compno]) {
+                continue;
+            }
+
             /* Compute the intersection of the area of interest, expressed in tile coordinates */
             /* with the tile coordinates */
             tilec->win_x0 = opj_uint_max(
@@ -1600,6 +1634,10 @@ OPJ_BOOL opj_tcd_decode_tile(opj_tcd_t *p_tcd,
             opj_image_data_free(tilec->data_win);
             tilec->data_win = NULL;
 
+            if (p_tcd->used_component != NULL && !p_tcd->used_component[compno]) {
+                continue;
+            }
+
             if (w > 0 && h > 0) {
                 if (w > SIZE_MAX / h) {
                     opj_event_msg(p_manager, EVT_ERROR,
@@ -1916,14 +1954,17 @@ static OPJ_BOOL opj_tcd_t1_decode(opj_tcd_t *p_tcd, opj_event_mgr_t *p_manager)
         check_pterm = OPJ_TRUE;
     }
 
-    for (compno = 0; compno < l_tile->numcomps; ++compno) {
+    for (compno = 0; compno < l_tile->numcomps;
+            ++compno, ++l_tile_comp, ++l_tccp) {
+        if (p_tcd->used_component != NULL && !p_tcd->used_component[compno]) {
+            continue;
+        }
+
         opj_t1_decode_cblks(p_tcd, &ret, l_tile_comp, l_tccp,
                             p_manager, p_manager_mutex, check_pterm);
         if (!ret) {
             break;
         }
-        ++l_tile_comp;
-        ++l_tccp;
     }
 
     opj_thread_pool_wait_completion(p_tcd->thread_pool, 0);
@@ -1942,7 +1983,11 @@ static OPJ_BOOL opj_tcd_dwt_decode(opj_tcd_t *p_tcd)
     opj_tccp_t * l_tccp = p_tcd->tcp->tccps;
     opj_image_comp_t * l_img_comp = p_tcd->image->comps;
 
-    for (compno = 0; compno < l_tile->numcomps; compno++) {
+    for (compno = 0; compno < l_tile->numcomps;
+            compno++, ++l_tile_comp, ++l_img_comp, ++l_tccp) {
+        if (p_tcd->used_component != NULL && !p_tcd->used_component[compno]) {
+            continue;
+        }
 
         if (l_tccp->qmfbid == 1) {
             if (! opj_dwt_decode(p_tcd, l_tile_comp,
@@ -1956,9 +2001,6 @@ static OPJ_BOOL opj_tcd_dwt_decode(opj_tcd_t *p_tcd)
             }
         }
 
-        ++l_tile_comp;
-        ++l_img_comp;
-        ++l_tccp;
     }
 
     return OPJ_TRUE;
@@ -1971,7 +2013,7 @@ static OPJ_BOOL opj_tcd_mct_decode(opj_tcd_t *p_tcd, opj_event_mgr_t *p_manager)
     opj_tcd_tilecomp_t * l_tile_comp = l_tile->comps;
     OPJ_UINT32 l_samples, i;
 
-    if (! l_tcp->mct) {
+    if (l_tcp->mct == 0 || p_tcd->used_component != NULL) {
         return OPJ_TRUE;
     }
 
@@ -2132,7 +2174,13 @@ static OPJ_BOOL opj_tcd_dc_level_shift_decode(opj_tcd_t *p_tcd)
     l_tccp = p_tcd->tcp->tccps;
     l_img_comp = p_tcd->image->comps;
 
-    for (compno = 0; compno < l_tile->numcomps; compno++) {
+    for (compno = 0; compno < l_tile->numcomps;
+            compno++, ++l_img_comp, ++l_tccp, ++l_tile_comp) {
+
+        if (p_tcd->used_component != NULL && !p_tcd->used_component[compno]) {
+            continue;
+        }
+
         l_res = l_tile_comp->resolutions + l_img_comp->resno_decoded;
 
         if (!p_tcd->whole_tile_decoding) {
@@ -2191,10 +2239,6 @@ static OPJ_BOOL opj_tcd_dc_level_shift_decode(opj_tcd_t *p_tcd)
                 l_current_ptr += l_stride;
             }
         }
-
-        ++l_img_comp;
-        ++l_tccp;
-        ++l_tile_comp;
     }
 
     return OPJ_TRUE;
index 63c22c4577df968e46e5e8565cd95b4c7f887fed..e3214c1d982ad9b4ce57d17d007d6bd562883fe2 100644 (file)
@@ -280,6 +280,8 @@ typedef struct opj_tcd {
     OPJ_UINT32 win_y1;
     /** Only valid for decoding. Whether the whole tile is decoded, or just the region in win_x0/win_y0/win_x1/win_y1 */
     OPJ_BOOL   whole_tile_decoding;
+    /* Array of size image->numcomps indicating if a component must be decoded. NULL if all components must be decoded */
+    OPJ_BOOL* used_component;
 } opj_tcd_t;
 
 /** @name Exported functions */
@@ -381,6 +383,10 @@ Decode a tile from a buffer into a raw image
 @param win_y0 Upper left y of region to decode (in grid coordinates)
 @param win_x1 Lower right x of region to decode (in grid coordinates)
 @param win_y1 Lower right y of region to decode (in grid coordinates)
+@param numcomps_to_decode  Size of the comps_indices array, or 0 if decoding all components.
+@param comps_indices   Array of numcomps values representing the indices
+                       of the components to decode (relative to the
+                       codestream, starting at 0). Or NULL if decoding all components.
 @param src Source buffer
 @param len Length of source buffer
 @param tileno Number that identifies one of the tiles to be decoded
@@ -392,6 +398,8 @@ OPJ_BOOL opj_tcd_decode_tile(opj_tcd_t *tcd,
                              OPJ_UINT32 win_y0,
                              OPJ_UINT32 win_x1,
                              OPJ_UINT32 win_y1,
+                             OPJ_UINT32 numcomps_to_decode,
+                             const OPJ_UINT32 *comps_indices,
                              OPJ_BYTE *src,
                              OPJ_UINT32 len,
                              OPJ_UINT32 tileno,
index e95a10f082b6fd93527195efd64dc1a96e7ac29e..6076538bad1a54728081437007ffcf7a1e84acbb 100644 (file)
@@ -310,3 +310,9 @@ d1bb7f93f4c0eb984b2e9c54e544b7e9  broken.jpc_1.pgx
 b704ad4c0cfefffd78c20a54f5541265  dwt_interleave_h.gsr105.jp2_d_1_1_33_33_0.pgx
 9d7fe43cd7a50b7bbaf712926ee11980  dwt_interleave_h.gsr105.jp2_d_1_1_33_33_1.pgx
 0960b580f991ff10f693b24aa41ad58b  dwt_interleave_h.gsr105.jp2_d_1_1_33_33_2.pgx
+fa7382fd8b2e788b28b807e200dd95b9  file1.jp2-c0.tif
+ed79b7fe443955cdefba2b039ddc846a  file1.jp2-c0_1_2.tif
+ac8f6ab3acc9c692ed7c41bd62a0e1e8  file1.jp2-c0-r1.tif
+fbfcf662b6f7549574b2885490fbcf12  file1.jp2-c0-d10_20_30_40.tif
+fa7382fd8b2e788b28b807e200dd95b9  file1.jp2-c0-t0.tif
+ac8f6ab3acc9c692ed7c41bd62a0e1e8  file1.jp2-c0-t0-r1.tif
index 718d05996f7960a1de2d3b065e1a4b34e17f6e38..31ae777b8f288eef558cdd26b95514c538ec0665 100644 (file)
@@ -605,3 +605,21 @@ opj_decompress -i @INPUT_NR_PATH@/issue979.j2k -o @TEMP_PATH@/issue979.j2k.pgx
 
 # https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=3115
 opj_decompress -i @INPUT_NR_PATH@/dwt_interleave_h.gsr105.jp2 -o @TEMP_PATH@/dwt_interleave_h.gsr105.jp2_d_1_1_33_33.pgx -d 1,1,33,33
+
+# partial component decoding with opj_decode(): one component
+opj_decompress -i @INPUT_CONF_PATH@/file1.jp2 -o @TEMP_PATH@/file1.jp2-c0.tif -c 0
+# partial component decoding with opj_decode(): 3 components without MCT
+opj_decompress -i @INPUT_CONF_PATH@/file1.jp2 -o @TEMP_PATH@/file1.jp2-c0_1_2.tif -c 0,1,2
+# partial component decoding with opj_decode() and opj_set_decode_area()
+opj_decompress -i @INPUT_CONF_PATH@/file1.jp2 -o @TEMP_PATH@/file1.jp2-c0-d10_20_30_40.tif -c 0 -d 10,20,30,40
+# partial component decoding with opj_decode() and reduced resolution
+opj_decompress -i @INPUT_CONF_PATH@/file1.jp2 -o @TEMP_PATH@/file1.jp2-c0-r1.tif -c 0 -r 1
+# partial component decoding with opj_get_decoded_tile()
+opj_decompress -i @INPUT_CONF_PATH@/file1.jp2 -o @TEMP_PATH@/file1.jp2-c0-t0.tif -c 0 -t 0
+# partial component decoding with opj_get_decoded_tile() and reduced resolution
+opj_decompress -i @INPUT_CONF_PATH@/file1.jp2 -o @TEMP_PATH@/file1.jp2-c0-t0-r1.tif -c 0 -t 0 -r 1
+
+# try to map the same component several times
+!opj_decompress -i @INPUT_CONF_PATH@/file1.jp2 -o @TEMP_PATH@/file1.jp2-c0_0.tif -c 0,0
+# try to map an invalid component
+!opj_decompress -i @INPUT_CONF_PATH@/file1.jp2 -o @TEMP_PATH@/file1.jp2-c10.tif -c 10