openjp2/j2k: Validate all SGcod/SPcod/SPcoc parameter values.
[openjpeg.git] / src / lib / openjp2 / j2k.c
index 8e4b39666507e9c27b09f0edb75c8baccabcbffe..43be7677ea413d546c708adb2b66d62dba985ef9 100644 (file)
@@ -400,14 +400,14 @@ static OPJ_BOOL opj_j2k_setup_header_writing(opj_j2k_t *p_j2k,
 static OPJ_BOOL opj_j2k_write_first_tile_part(opj_j2k_t *p_j2k,
         OPJ_BYTE * p_data,
         OPJ_UINT32 * p_data_written,
-        OPJ_UINT32 p_total_data_size,
+        OPJ_UINT32 total_data_size,
         opj_stream_private_t *p_stream,
         struct opj_event_mgr * p_manager);
 
 static OPJ_BOOL opj_j2k_write_all_tile_parts(opj_j2k_t *p_j2k,
         OPJ_BYTE * p_data,
         OPJ_UINT32 * p_data_written,
-        OPJ_UINT32 p_total_data_size,
+        OPJ_UINT32 total_data_size,
         opj_stream_private_t *p_stream,
         struct opj_event_mgr * p_manager);
 
@@ -508,7 +508,7 @@ static OPJ_BOOL opj_j2k_write_cod(opj_j2k_t *p_j2k,
                                   opj_event_mgr_t * p_manager);
 
 /**
- * Reads a COD marker (Coding Styke defaults)
+ * Reads a COD marker (Coding style defaults)
  * @param       p_header_data   the data contained in the COD box.
  * @param       p_j2k                   the jpeg2000 codec.
  * @param       p_header_size   the size of the data contained in the COD marker.
@@ -832,14 +832,14 @@ static OPJ_BOOL opj_j2k_write_tlm(opj_j2k_t *p_j2k,
  *
  * @param       p_j2k            J2K codec.
  * @param       p_data           Output buffer
- * @param       p_total_data_size Output buffer size
+ * @param       total_data_size  Output buffer size
  * @param       p_data_written   Number of bytes written into stream
  * @param       p_stream         the stream to write data to.
  * @param       p_manager        the user event manager.
 */
 static OPJ_BOOL opj_j2k_write_sot(opj_j2k_t *p_j2k,
                                   OPJ_BYTE * p_data,
-                                  OPJ_UINT32 p_total_data_size,
+                                  OPJ_UINT32 total_data_size,
                                   OPJ_UINT32 * p_data_written,
                                   const opj_stream_private_t *p_stream,
                                   opj_event_mgr_t * p_manager);
@@ -883,7 +883,7 @@ static OPJ_BOOL opj_j2k_read_sot(opj_j2k_t *p_j2k,
  * @param       p_tile_coder        FIXME DOC
  * @param       p_data              FIXME DOC
  * @param       p_data_written      FIXME DOC
- * @param       p_total_data_size   FIXME DOC
+ * @param       total_data_size   FIXME DOC
  * @param       p_stream            the stream to write data to.
  * @param       p_manager           the user event manager.
 */
@@ -891,7 +891,7 @@ static OPJ_BOOL opj_j2k_write_sod(opj_j2k_t *p_j2k,
                                   opj_tcd_t * p_tile_coder,
                                   OPJ_BYTE * p_data,
                                   OPJ_UINT32 * p_data_written,
-                                  OPJ_UINT32 p_total_data_size,
+                                  OPJ_UINT32 total_data_size,
                                   const opj_stream_private_t *p_stream,
                                   opj_event_mgr_t * p_manager);
 
@@ -1219,6 +1219,7 @@ static OPJ_BOOL opj_j2k_write_epc(opj_j2k_t *p_j2k,
  * A nice message is outputted at errors.
  *
  * @param       p_pocs                  the progression order changes.
+ * @param       tileno                  the tile number of interest
  * @param       p_nb_pocs               the number of progression order changes.
  * @param       p_nb_resolutions        the number of resolutions.
  * @param       numcomps                the number of components
@@ -1228,6 +1229,7 @@ static OPJ_BOOL opj_j2k_write_epc(opj_j2k_t *p_j2k,
  * @return      true if the pocs are valid.
  */
 static OPJ_BOOL opj_j2k_check_poc_val(const opj_poc_t *p_pocs,
+                                      OPJ_UINT32 tileno,
                                       OPJ_UINT32 p_nb_pocs,
                                       OPJ_UINT32 p_nb_resolutions,
                                       OPJ_UINT32 numcomps,
@@ -1615,6 +1617,7 @@ const char *opj_j2k_convert_progression_order(OPJ_PROG_ORDER prg_order)
 }
 
 static OPJ_BOOL opj_j2k_check_poc_val(const opj_poc_t *p_pocs,
+                                      OPJ_UINT32 tileno,
                                       OPJ_UINT32 p_nb_pocs,
                                       OPJ_UINT32 p_nb_resolutions,
                                       OPJ_UINT32 p_num_comps,
@@ -1628,7 +1631,8 @@ static OPJ_BOOL opj_j2k_check_poc_val(const opj_poc_t *p_pocs,
     OPJ_UINT32 step_r = p_num_comps * step_c;
     OPJ_UINT32 step_l = p_nb_resolutions * step_r;
     OPJ_BOOL loss = OPJ_FALSE;
-    OPJ_UINT32 layno0 = 0;
+
+    assert(p_nb_pocs > 0);
 
     packet_array = (OPJ_UINT32*) opj_calloc(step_l * p_num_layers,
                                             sizeof(OPJ_UINT32));
@@ -1638,63 +1642,37 @@ static OPJ_BOOL opj_j2k_check_poc_val(const opj_poc_t *p_pocs,
         return OPJ_FALSE;
     }
 
-    if (p_nb_pocs == 0) {
-        opj_free(packet_array);
-        return OPJ_TRUE;
-    }
-
-    index = step_r * p_pocs->resno0;
-    /* take each resolution for each poc */
-    for (resno = p_pocs->resno0 ; resno < p_pocs->resno1 ; ++resno) {
-        OPJ_UINT32 res_index = index + p_pocs->compno0 * step_c;
-
-        /* take each comp of each resolution for each poc */
-        for (compno = p_pocs->compno0 ; compno < p_pocs->compno1 ; ++compno) {
-            OPJ_UINT32 comp_index = res_index + layno0 * step_l;
-
-            /* and finally take each layer of each res of ... */
-            for (layno = layno0; layno < p_pocs->layno1 ; ++layno) {
-                /*index = step_r * resno + step_c * compno + step_l * layno;*/
-                packet_array[comp_index] = 1;
-                comp_index += step_l;
-            }
-
-            res_index += step_c;
-        }
-
-        index += step_r;
-    }
-    ++p_pocs;
+    /* iterate through all the pocs that match our tile of interest. */
+    for (i = 0; i < p_nb_pocs; ++i) {
+        const opj_poc_t *poc = &p_pocs[i];
+        if (tileno + 1 == poc->tile) {
+            index = step_r * poc->resno0;
 
-    /* iterate through all the pocs */
-    for (i = 1; i < p_nb_pocs ; ++i) {
-        OPJ_UINT32 l_last_layno1 = (p_pocs - 1)->layno1 ;
+            /* take each resolution for each poc */
+            for (resno = poc->resno0 ;
+                    resno < opj_uint_min(poc->resno1, p_nb_resolutions); ++resno) {
+                OPJ_UINT32 res_index = index + poc->compno0 * step_c;
 
-        layno0 = (p_pocs->layno1 > l_last_layno1) ? l_last_layno1 : 0;
-        index = step_r * p_pocs->resno0;
+                /* take each comp of each resolution for each poc */
+                for (compno = poc->compno0 ;
+                        compno < opj_uint_min(poc->compno1, p_num_comps); ++compno) {
+                    /* The layer index always starts at zero for every progression. */
+                    const OPJ_UINT32 layno0 = 0;
+                    OPJ_UINT32 comp_index = res_index + layno0 * step_l;
 
-        /* take each resolution for each poc */
-        for (resno = p_pocs->resno0 ; resno < p_pocs->resno1 ; ++resno) {
-            OPJ_UINT32 res_index = index + p_pocs->compno0 * step_c;
-
-            /* take each comp of each resolution for each poc */
-            for (compno = p_pocs->compno0 ; compno < p_pocs->compno1 ; ++compno) {
-                OPJ_UINT32 comp_index = res_index + layno0 * step_l;
+                    /* and finally take each layer of each res of ... */
+                    for (layno = layno0; layno < opj_uint_min(poc->layno1, p_num_layers);
+                            ++layno) {
+                        packet_array[comp_index] = 1;
+                        comp_index += step_l;
+                    }
 
-                /* and finally take each layer of each res of ... */
-                for (layno = layno0; layno < p_pocs->layno1 ; ++layno) {
-                    /*index = step_r * resno + step_c * compno + step_l * layno;*/
-                    packet_array[comp_index] = 1;
-                    comp_index += step_l;
+                    res_index += step_c;
                 }
 
-                res_index += step_c;
+                index += step_r;
             }
-
-            index += step_r;
         }
-
-        ++p_pocs;
     }
 
     index = 0;
@@ -1702,7 +1680,13 @@ static OPJ_BOOL opj_j2k_check_poc_val(const opj_poc_t *p_pocs,
         for (resno = 0; resno < p_nb_resolutions; ++resno) {
             for (compno = 0; compno < p_num_comps; ++compno) {
                 loss |= (packet_array[index] != 1);
-                /*index = step_r * resno + step_c * compno + step_l * layno;*/
+#ifdef DEBUG_VERBOSE
+                if (packet_array[index] != 1) {
+                    fprintf(stderr,
+                            "Missing packet in POC: layno=%d resno=%d compno=%d\n",
+                            layno, resno, compno);
+                }
+#endif
                 index += step_c;
             }
         }
@@ -1925,7 +1909,8 @@ static OPJ_BOOL opj_j2k_read_soc(opj_j2k_t *p_j2k,
     /* FIXME move it in a index structure included in p_j2k*/
     p_j2k->cstr_index->main_head_start = opj_stream_tell(p_stream) - 2;
 
-    opj_event_msg(p_manager, EVT_INFO, "Start to read j2k main header (%d).\n",
+    opj_event_msg(p_manager, EVT_INFO,
+                  "Start to read j2k main header (%" PRId64 ").\n",
                   p_j2k->cstr_index->main_head_start);
 
     /* Add the marker to the codestream index*/
@@ -2625,7 +2610,7 @@ static OPJ_BOOL opj_j2k_write_cod(opj_j2k_t *p_j2k,
 }
 
 /**
- * Reads a COD marker (Coding Styke defaults)
+ * Reads a COD marker (Coding style defaults)
  * @param       p_header_data   the data contained in the COD box.
  * @param       p_j2k                   the jpeg2000 codec.
  * @param       p_header_size   the size of the data contained in the COD marker.
@@ -2657,12 +2642,17 @@ static OPJ_BOOL opj_j2k_read_cod(opj_j2k_t *p_j2k,
             &l_cp->tcps[p_j2k->m_current_tile_number] :
             p_j2k->m_specific_param.m_decoder.m_default_tcp;
 
+#if 0
+    /* This check was added per https://github.com/uclouvain/openjpeg/commit/daed8cc9195555e101ab708a501af2dfe6d5e001 */
+    /* but this is no longer necessary to handle issue476.jp2 */
+    /* and this actually cause issues on legit files. See https://github.com/uclouvain/openjpeg/issues/1043 */
     /* Only one COD per tile */
     if (l_tcp->cod) {
         opj_event_msg(p_manager, EVT_ERROR,
                       "COD marker already read. No more than one COD marker per tile.\n");
         return OPJ_FALSE;
     }
+#endif
     l_tcp->cod = 1;
 
     /* Make sure room is sufficient */
@@ -2708,6 +2698,12 @@ static OPJ_BOOL opj_j2k_read_cod(opj_j2k_t *p_j2k,
     opj_read_bytes(p_header_data, &l_tcp->mct, 1);          /* SGcod (C) */
     ++p_header_data;
 
+    if (l_tcp->mct > 1) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Invalid multiple component transformation\n");
+        return OPJ_FALSE;
+    }
+
     p_header_size -= 5;
     for (i = 0; i < l_image->numcomps; ++i) {
         l_tcp->tccps[i].csty = l_tcp->csty & J2K_CCP_CSTY_PRT;
@@ -3502,11 +3498,10 @@ static OPJ_BOOL opj_j2k_read_poc(opj_j2k_t *p_j2k,
     l_old_poc_nb = l_tcp->POC ? l_tcp->numpocs + 1 : 0;
     l_current_poc_nb += l_old_poc_nb;
 
-    if (l_current_poc_nb >= 32) {
+    if (l_current_poc_nb >= J2K_MAX_POCS) {
         opj_event_msg(p_manager, EVT_ERROR, "Too many POCs %d\n", l_current_poc_nb);
         return OPJ_FALSE;
     }
-    assert(l_current_poc_nb < 32);
 
     /* now poc is in use.*/
     l_tcp->POC = 1;
@@ -4090,7 +4085,12 @@ static OPJ_BOOL opj_j2k_merge_ppt(opj_tcp_t *p_tcp, opj_event_mgr_t * p_manager)
     /* preconditions */
     assert(p_tcp != 00);
     assert(p_manager != 00);
-    assert(p_tcp->ppt_buffer == NULL);
+
+    if (p_tcp->ppt_buffer != NULL) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "opj_j2k_merge_ppt() has already been called\n");
+        return OPJ_FALSE;
+    }
 
     if (p_tcp->ppt == 0U) {
         return OPJ_TRUE;
@@ -4195,7 +4195,7 @@ static OPJ_BOOL opj_j2k_write_tlm(opj_j2k_t *p_j2k,
 
 static OPJ_BOOL opj_j2k_write_sot(opj_j2k_t *p_j2k,
                                   OPJ_BYTE * p_data,
-                                  OPJ_UINT32 p_total_data_size,
+                                  OPJ_UINT32 total_data_size,
                                   OPJ_UINT32 * p_data_written,
                                   const opj_stream_private_t *p_stream,
                                   opj_event_mgr_t * p_manager
@@ -4208,7 +4208,7 @@ static OPJ_BOOL opj_j2k_write_sot(opj_j2k_t *p_j2k,
 
     OPJ_UNUSED(p_stream);
 
-    if (p_total_data_size < 12) {
+    if (total_data_size < 12) {
         opj_event_msg(p_manager, EVT_ERROR,
                       "Not enough bytes in output buffer to write SOT marker\n");
         return OPJ_FALSE;
@@ -4304,6 +4304,10 @@ static OPJ_BOOL opj_j2k_read_sot(opj_j2k_t *p_j2k,
         opj_event_msg(p_manager, EVT_ERROR, "Error reading SOT marker\n");
         return OPJ_FALSE;
     }
+#ifdef DEBUG_VERBOSE
+    fprintf(stderr, "SOT %d %d %d %d\n",
+            p_j2k->m_current_tile_number, l_tot_len, l_current_part, l_num_parts);
+#endif
 
     l_cp = &(p_j2k->m_cp);
 
@@ -4318,23 +4322,31 @@ static OPJ_BOOL opj_j2k_read_sot(opj_j2k_t *p_j2k,
     l_tile_x = p_j2k->m_current_tile_number % l_cp->tw;
     l_tile_y = p_j2k->m_current_tile_number / l_cp->tw;
 
-    /* Fixes issue with id_000020,sig_06,src_001958,op_flip4,pos_149 */
-    /* of https://github.com/uclouvain/openjpeg/issues/939 */
-    /* We must avoid reading twice the same tile part number for a given tile */
-    /* so as to avoid various issues, like opj_j2k_merge_ppt being called */
-    /* several times. */
-    /* ISO 15444-1 A.4.2 Start of tile-part (SOT) mandates that tile parts */
-    /* should appear in increasing order. */
-    if (l_tcp->m_current_tile_part_number + 1 != (OPJ_INT32)l_current_part) {
-        opj_event_msg(p_manager, EVT_ERROR,
-                      "Invalid tile part index for tile number %d. "
-                      "Got %d, expected %d\n",
-                      p_j2k->m_current_tile_number,
-                      l_current_part,
-                      l_tcp->m_current_tile_part_number + 1);
-        return OPJ_FALSE;
+    if (p_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec < 0 ||
+            p_j2k->m_current_tile_number == (OPJ_UINT32)
+            p_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec) {
+        /* Do only this check if we decode all tile part headers, or if */
+        /* we decode one precise tile. Otherwise the m_current_tile_part_number */
+        /* might not be valid */
+        /* Fixes issue with id_000020,sig_06,src_001958,op_flip4,pos_149 */
+        /* of https://github.com/uclouvain/openjpeg/issues/939 */
+        /* We must avoid reading twice the same tile part number for a given tile */
+        /* so as to avoid various issues, like opj_j2k_merge_ppt being called */
+        /* several times. */
+        /* ISO 15444-1 A.4.2 Start of tile-part (SOT) mandates that tile parts */
+        /* should appear in increasing order. */
+        if (l_tcp->m_current_tile_part_number + 1 != (OPJ_INT32)l_current_part) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Invalid tile part index for tile number %d. "
+                          "Got %d, expected %d\n",
+                          p_j2k->m_current_tile_number,
+                          l_current_part,
+                          l_tcp->m_current_tile_part_number + 1);
+            return OPJ_FALSE;
+        }
     }
-    ++ l_tcp->m_current_tile_part_number;
+
+    l_tcp->m_current_tile_part_number = (OPJ_INT32) l_current_part;
 
 #ifdef USE_JPWL
     if (l_cp->correct) {
@@ -4593,7 +4605,7 @@ static OPJ_BOOL opj_j2k_write_sod(opj_j2k_t *p_j2k,
                                   opj_tcd_t * p_tile_coder,
                                   OPJ_BYTE * p_data,
                                   OPJ_UINT32 * p_data_written,
-                                  OPJ_UINT32 p_total_data_size,
+                                  OPJ_UINT32 total_data_size,
                                   const opj_stream_private_t *p_stream,
                                   opj_event_mgr_t * p_manager
                                  )
@@ -4608,7 +4620,7 @@ static OPJ_BOOL opj_j2k_write_sod(opj_j2k_t *p_j2k,
 
     OPJ_UNUSED(p_stream);
 
-    if (p_total_data_size < 4) {
+    if (total_data_size < 4) {
         opj_event_msg(p_manager, EVT_ERROR,
                       "Not enough bytes in output buffer to write SOD marker\n");
         return OPJ_FALSE;
@@ -4619,7 +4631,7 @@ static OPJ_BOOL opj_j2k_write_sod(opj_j2k_t *p_j2k,
     p_data += 2;
 
     /* make room for the EOF marker */
-    l_remaining_data =  p_total_data_size - 4;
+    l_remaining_data =  total_data_size - 4;
 
     /* update tile coder */
     p_tile_coder->tp_num =
@@ -4657,9 +4669,11 @@ static OPJ_BOOL opj_j2k_write_sod(opj_j2k_t *p_j2k,
 
     if (p_j2k->m_specific_param.m_encoder.m_current_tile_part_number == 0) {
         p_tile_coder->tcd_image->tiles->packno = 0;
+#ifdef deadcode
         if (l_cstr_info) {
             l_cstr_info->packno = 0;
         }
+#endif
     }
 
     *p_data_written = 0;
@@ -6406,7 +6420,9 @@ void opj_j2k_setup_decoder(opj_j2k_t *j2k, opj_dparameters_t *parameters)
 
 OPJ_BOOL opj_j2k_set_threads(opj_j2k_t *j2k, OPJ_UINT32 num_threads)
 {
-    if (opj_has_thread_support()) {
+    /* Currently we pass the thread-pool to the tcd, so we cannot re-set it */
+    /* afterwards */
+    if (opj_has_thread_support() && j2k->m_tcd == NULL) {
         opj_thread_pool_destroy(j2k->m_tp);
         j2k->m_tp = NULL;
         if (num_threads <= (OPJ_UINT32)INT_MAX) {
@@ -6423,14 +6439,27 @@ OPJ_BOOL opj_j2k_set_threads(opj_j2k_t *j2k, OPJ_UINT32 num_threads)
 
 static int opj_j2k_get_default_thread_count()
 {
-    const char* num_threads = getenv("OPJ_NUM_THREADS");
-    if (num_threads == NULL || !opj_has_thread_support()) {
+    const char* num_threads_str = getenv("OPJ_NUM_THREADS");
+    int num_cpus;
+    int num_threads;
+
+    if (num_threads_str == NULL || !opj_has_thread_support()) {
         return 0;
     }
-    if (strcmp(num_threads, "ALL_CPUS") == 0) {
-        return opj_get_num_cpus();
+    num_cpus = opj_get_num_cpus();
+    if (strcmp(num_threads_str, "ALL_CPUS") == 0) {
+        return num_cpus;
+    }
+    if (num_cpus == 0) {
+        num_cpus = 32;
     }
-    return atoi(num_threads);
+    num_threads = atoi(num_threads_str);
+    if (num_threads < 0) {
+        num_threads = 0;
+    } else if (num_threads > 2 * num_cpus) {
+        num_threads = 2 * num_cpus;
+    }
+    return num_threads;
 }
 
 /* ----------------------------------------------------------------------- */
@@ -6832,25 +6861,84 @@ OPJ_BOOL opj_j2k_setup_encoder(opj_j2k_t *p_j2k,
         parameters->tcp_rates[0] = 0;
     }
 
+    if (parameters->cp_disto_alloc) {
+        /* Emit warnings if tcp_rates are not decreasing */
+        for (i = 1; i < (OPJ_UINT32) parameters->tcp_numlayers; i++) {
+            OPJ_FLOAT32 rate_i_corr = parameters->tcp_rates[i];
+            OPJ_FLOAT32 rate_i_m_1_corr = parameters->tcp_rates[i - 1];
+            if (rate_i_corr <= 1.0) {
+                rate_i_corr = 1.0;
+            }
+            if (rate_i_m_1_corr <= 1.0) {
+                rate_i_m_1_corr = 1.0;
+            }
+            if (rate_i_corr >= rate_i_m_1_corr) {
+                if (rate_i_corr != parameters->tcp_rates[i] &&
+                        rate_i_m_1_corr != parameters->tcp_rates[i - 1]) {
+                    opj_event_msg(p_manager, EVT_WARNING,
+                                  "tcp_rates[%d]=%f (corrected as %f) should be strictly lesser "
+                                  "than tcp_rates[%d]=%f (corrected as %f)\n",
+                                  i, parameters->tcp_rates[i], rate_i_corr,
+                                  i - 1, parameters->tcp_rates[i - 1], rate_i_m_1_corr);
+                } else if (rate_i_corr != parameters->tcp_rates[i]) {
+                    opj_event_msg(p_manager, EVT_WARNING,
+                                  "tcp_rates[%d]=%f (corrected as %f) should be strictly lesser "
+                                  "than tcp_rates[%d]=%f\n",
+                                  i, parameters->tcp_rates[i], rate_i_corr,
+                                  i - 1, parameters->tcp_rates[i - 1]);
+                } else if (rate_i_m_1_corr != parameters->tcp_rates[i - 1]) {
+                    opj_event_msg(p_manager, EVT_WARNING,
+                                  "tcp_rates[%d]=%f should be strictly lesser "
+                                  "than tcp_rates[%d]=%f (corrected as %f)\n",
+                                  i, parameters->tcp_rates[i],
+                                  i - 1, parameters->tcp_rates[i - 1], rate_i_m_1_corr);
+                } else {
+                    opj_event_msg(p_manager, EVT_WARNING,
+                                  "tcp_rates[%d]=%f should be strictly lesser "
+                                  "than tcp_rates[%d]=%f\n",
+                                  i, parameters->tcp_rates[i],
+                                  i - 1, parameters->tcp_rates[i - 1]);
+                }
+            }
+        }
+    } else if (parameters->cp_fixed_quality) {
+        /* Emit warnings if tcp_distoratio are not increasing */
+        for (i = 1; i < (OPJ_UINT32) parameters->tcp_numlayers; i++) {
+            if (parameters->tcp_distoratio[i] < parameters->tcp_distoratio[i - 1] &&
+                    !(i == (OPJ_UINT32)parameters->tcp_numlayers - 1 &&
+                      parameters->tcp_distoratio[i] == 0)) {
+                opj_event_msg(p_manager, EVT_WARNING,
+                              "tcp_distoratio[%d]=%f should be strictly greater "
+                              "than tcp_distoratio[%d]=%f\n",
+                              i, parameters->tcp_distoratio[i], i - 1,
+                              parameters->tcp_distoratio[i - 1]);
+            }
+        }
+    }
+
     /* see if max_codestream_size does limit input rate */
     if (parameters->max_cs_size <= 0) {
         if (parameters->tcp_rates[parameters->tcp_numlayers - 1] > 0) {
             OPJ_FLOAT32 temp_size;
-            temp_size = (OPJ_FLOAT32)(image->numcomps * image->comps[0].w *
-                                      image->comps[0].h * image->comps[0].prec) /
-                        (parameters->tcp_rates[parameters->tcp_numlayers - 1] * 8 *
-                         (OPJ_FLOAT32)image->comps[0].dx * (OPJ_FLOAT32)image->comps[0].dy);
-            parameters->max_cs_size = (int) floor(temp_size);
+            temp_size = (OPJ_FLOAT32)(((double)image->numcomps * image->comps[0].w *
+                                       image->comps[0].h * image->comps[0].prec) /
+                                      ((double)parameters->tcp_rates[parameters->tcp_numlayers - 1] * 8 *
+                                       image->comps[0].dx * image->comps[0].dy));
+            if (temp_size > INT_MAX) {
+                parameters->max_cs_size = INT_MAX;
+            } else {
+                parameters->max_cs_size = (int) floor(temp_size);
+            }
         } else {
             parameters->max_cs_size = 0;
         }
     } else {
         OPJ_FLOAT32 temp_rate;
         OPJ_BOOL cap = OPJ_FALSE;
-        temp_rate = (OPJ_FLOAT32)(image->numcomps * image->comps[0].w *
-                                  image->comps[0].h * image->comps[0].prec) /
-                    (OPJ_FLOAT32)(((OPJ_UINT32)parameters->max_cs_size) * 8 * image->comps[0].dx *
-                                  image->comps[0].dy);
+        temp_rate = (OPJ_FLOAT32)(((double)image->numcomps * image->comps[0].w *
+                                   image->comps[0].h * image->comps[0].prec) /
+                                  (((double)parameters->max_cs_size) * 8 * image->comps[0].dx *
+                                   image->comps[0].dy));
         for (i = 0; i < (OPJ_UINT32) parameters->tcp_numlayers; i++) {
             if (parameters->tcp_rates[i] < temp_rate) {
                 parameters->tcp_rates[i] = temp_rate;
@@ -7059,13 +7147,6 @@ OPJ_BOOL opj_j2k_setup_encoder(opj_j2k_t *p_j2k,
                       "Not enough memory to allocate tile coding parameters\n");
         return OPJ_FALSE;
     }
-    if (parameters->numpocs) {
-        /* initialisation of POC */
-        opj_j2k_check_poc_val(parameters->POC, parameters->numpocs,
-                              (OPJ_UINT32)parameters->numresolution, image->numcomps,
-                              (OPJ_UINT32)parameters->tcp_numlayers, p_manager);
-        /* TODO MSD use the return value*/
-    }
 
     for (tileno = 0; tileno < cp->tw * cp->th; tileno++) {
         opj_tcp_t *tcp = &cp->tcps[tileno];
@@ -7084,6 +7165,10 @@ OPJ_BOOL opj_j2k_setup_encoder(opj_j2k_t *p_j2k,
                     tcp->rates[j] = parameters->tcp_rates[j];
                 }
             }
+            if (!cp->m_specific_param.m_enc.m_fixed_quality &&
+                    tcp->rates[j] <= 1.0) {
+                tcp->rates[j] = 0.0;    /* force lossless */
+            }
         }
 
         tcp->csty = (OPJ_UINT32)parameters->csty;
@@ -7095,7 +7180,6 @@ OPJ_BOOL opj_j2k_setup_encoder(opj_j2k_t *p_j2k,
 
         if (parameters->numpocs) {
             /* initialisation of POC */
-            tcp->POC = 1;
             for (i = 0; i < parameters->numpocs; i++) {
                 if (tileno + 1 == parameters->POC[i].tile)  {
                     opj_poc_t *tcp_poc = &tcp->pocs[numpocs_tile];
@@ -7112,7 +7196,16 @@ OPJ_BOOL opj_j2k_setup_encoder(opj_j2k_t *p_j2k,
                 }
             }
 
-            tcp->numpocs = numpocs_tile - 1 ;
+            if (numpocs_tile) {
+
+                /* TODO MSD use the return value*/
+                opj_j2k_check_poc_val(parameters->POC, tileno, parameters->numpocs,
+                                      (OPJ_UINT32)parameters->numresolution, image->numcomps,
+                                      (OPJ_UINT32)parameters->tcp_numlayers, p_manager);
+
+                tcp->POC = 1;
+                tcp->numpocs = numpocs_tile - 1 ;
+            }
         } else {
             tcp->numpocs = 0;
         }
@@ -8191,6 +8284,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) {
@@ -8476,7 +8574,7 @@ static OPJ_BOOL opj_j2k_need_nb_tile_parts_correction(opj_stream_private_t
             break;
         }
 
-        if ((l_tot_len == 0U) || (l_tot_len < 14U)) {
+        if (l_tot_len < 14U) {
             /* last SOT until EOC or invalid Psot value */
             /* assume all is OK */
             if (! opj_stream_seek(p_stream, l_stream_pos_backup, p_manager)) {
@@ -8744,7 +8842,10 @@ OPJ_BOOL opj_j2k_read_tile_header(opj_j2k_t * p_j2k,
 
     /* Current marker is the EOC marker ?*/
     if (l_current_marker == J2K_MS_EOC) {
-        p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_EOC;
+        if (p_j2k->m_specific_param.m_decoder.m_state != J2K_STATE_EOC) {
+            p_j2k->m_current_tile_number = 0;
+            p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_EOC;
+        }
     }
 
     /* FIXME DOC ???*/
@@ -8839,6 +8940,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,
@@ -8953,6 +9056,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);
 
@@ -9054,6 +9162,7 @@ static OPJ_BOOL opj_j2k_update_image_data(opj_tcd_t * p_tcd,
         /* Allocate output component buffer if necessary */
         if (l_img_comp_dest->data == NULL &&
                 l_start_offset_src == 0 && l_start_offset_dest == 0 &&
+                src_data_stride == l_img_comp_dest->w &&
                 l_width_dest == l_img_comp_dest->w &&
                 l_height_dest == l_img_comp_dest->h) {
             /* If the final image matches the tile buffer, then borrow it */
@@ -9152,6 +9261,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,
@@ -9165,7 +9333,7 @@ OPJ_BOOL opj_j2k_set_decode_area(opj_j2k_t *p_j2k,
     OPJ_UINT32 it_comp;
 
     if (p_j2k->m_cp.tw == 1 && p_j2k->m_cp.th == 1 &&
-            &p_j2k->m_cp.tcps[0].m_data != NULL) {
+            p_j2k->m_cp.tcps[0].m_data != NULL) {
         /* In the case of a single-tiled image whose codestream we have already */
         /* ingested, go on */
     }
@@ -9226,7 +9394,7 @@ OPJ_BOOL opj_j2k_set_decode_area(opj_j2k_t *p_j2k,
     }
 
     /* Up */
-    if (p_start_x < 0) {
+    if (p_start_y < 0) {
         opj_event_msg(p_manager, EVT_ERROR,
                       "Up position of the decoded area (region_y0=%d) should be >= 0.\n",
                       p_start_y);
@@ -9577,9 +9745,9 @@ static OPJ_BOOL opj_j2k_read_SPCod_SPCoc(opj_j2k_t *p_j2k,
         return OPJ_FALSE;
     }
 
-    opj_read_bytes(l_current_ptr, &l_tccp->numresolutions,
-                   1);              /* SPcox (D) */
-    ++l_tccp->numresolutions;                                                                               /* tccp->numresolutions = read() + 1 */
+    /* SPcod (D) / SPcoc (A) */
+    opj_read_bytes(l_current_ptr, &l_tccp->numresolutions, 1);
+    ++l_tccp->numresolutions;  /* tccp->numresolutions = read() + 1 */
     if (l_tccp->numresolutions > OPJ_J2K_MAXRLVLS) {
         opj_event_msg(p_manager, EVT_ERROR,
                       "Invalid value for numresolutions : %d, max value is set in openjpeg.h at %d\n",
@@ -9591,19 +9759,22 @@ static OPJ_BOOL opj_j2k_read_SPCod_SPCoc(opj_j2k_t *p_j2k,
     /* If user wants to remove more resolutions than the codestream contains, return error */
     if (l_cp->m_specific_param.m_dec.m_reduce >= l_tccp->numresolutions) {
         opj_event_msg(p_manager, EVT_ERROR,
-                      "Error decoding component %d.\nThe number of resolutions to remove is higher than the number "
-                      "of resolutions of this component\nModify the cp_reduce parameter.\n\n",
-                      compno);
+                      "Error decoding component %d.\nThe number of resolutions "
+                      "to remove (%d) is greater or equal than the number "
+                      "of resolutions of this component (%d)\nModify the cp_reduce parameter.\n\n",
+                      compno, l_cp->m_specific_param.m_dec.m_reduce, l_tccp->numresolutions);
         p_j2k->m_specific_param.m_decoder.m_state |=
             0x8000;/* FIXME J2K_DEC_STATE_ERR;*/
         return OPJ_FALSE;
     }
 
-    opj_read_bytes(l_current_ptr, &l_tccp->cblkw, 1);               /* SPcoc (E) */
+    /* SPcod (E) / SPcoc (B) */
+    opj_read_bytes(l_current_ptr, &l_tccp->cblkw, 1);
     ++l_current_ptr;
     l_tccp->cblkw += 2;
 
-    opj_read_bytes(l_current_ptr, &l_tccp->cblkh, 1);               /* SPcoc (F) */
+    /* SPcod (F) / SPcoc (C) */
+    opj_read_bytes(l_current_ptr, &l_tccp->cblkh, 1);
     ++l_current_ptr;
     l_tccp->cblkh += 2;
 
@@ -9614,8 +9785,8 @@ static OPJ_BOOL opj_j2k_read_SPCod_SPCoc(opj_j2k_t *p_j2k,
         return OPJ_FALSE;
     }
 
-
-    opj_read_bytes(l_current_ptr, &l_tccp->cblksty, 1);             /* SPcoc (G) */
+    /* SPcod (G) / SPcoc (D) */
+    opj_read_bytes(l_current_ptr, &l_tccp->cblksty, 1);
     ++l_current_ptr;
     if (l_tccp->cblksty & 0xC0U) { /* 2 msb are reserved, assume we can't read */
         opj_event_msg(p_manager, EVT_ERROR,
@@ -9623,9 +9794,16 @@ static OPJ_BOOL opj_j2k_read_SPCod_SPCoc(opj_j2k_t *p_j2k,
         return OPJ_FALSE;
     }
 
-    opj_read_bytes(l_current_ptr, &l_tccp->qmfbid, 1);              /* SPcoc (H) */
+    /* SPcod (H) / SPcoc (E) */
+    opj_read_bytes(l_current_ptr, &l_tccp->qmfbid, 1);
     ++l_current_ptr;
 
+    if (l_tccp->qmfbid > 1) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Error reading SPCod SPCoc element, Invalid transformation found\n");
+        return OPJ_FALSE;
+    }
+
     *p_header_size = *p_header_size - 5;
 
     /* use custom precinct size ? */
@@ -9635,8 +9813,9 @@ static OPJ_BOOL opj_j2k_read_SPCod_SPCoc(opj_j2k_t *p_j2k,
             return OPJ_FALSE;
         }
 
+        /* SPcod (I_i) / SPcoc (F_i) */
         for (i = 0; i < l_tccp->numresolutions; ++i) {
-            opj_read_bytes(l_current_ptr, &l_tmp, 1);               /* SPcoc (I_i) */
+            opj_read_bytes(l_current_ptr, &l_tmp, 1);
             ++l_current_ptr;
             /* Precinct exponent 0 is only allowed for lowest resolution level (Table A.21) */
             if ((i != 0) && (((l_tmp & 0xf) == 0) || ((l_tmp >> 4) == 0))) {
@@ -10479,6 +10658,42 @@ static OPJ_BOOL opj_j2k_allocate_tile_element_cstr_index(opj_j2k_t *p_j2k)
     return OPJ_TRUE;
 }
 
+static OPJ_BOOL opj_j2k_are_all_used_components_decoded(opj_j2k_t *p_j2k,
+        opj_event_mgr_t * p_manager)
+{
+    OPJ_UINT32 compno;
+    OPJ_BOOL decoded_all_used_components = OPJ_TRUE;
+
+    if (p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode) {
+        for (compno = 0;
+                compno < p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode; compno++) {
+            OPJ_UINT32 dec_compno =
+                p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode[compno];
+            if (p_j2k->m_output_image->comps[dec_compno].data == NULL) {
+                opj_event_msg(p_manager, EVT_WARNING, "Failed to decode component %d\n",
+                              dec_compno);
+                decoded_all_used_components = OPJ_FALSE;
+            }
+        }
+    } else {
+        for (compno = 0; compno < p_j2k->m_output_image->numcomps; compno++) {
+            if (p_j2k->m_output_image->comps[compno].data == NULL) {
+                opj_event_msg(p_manager, EVT_WARNING, "Failed to decode component %d\n",
+                              compno);
+                decoded_all_used_components = OPJ_FALSE;
+            }
+        }
+    }
+
+    if (decoded_all_used_components == OPJ_FALSE) {
+        opj_event_msg(p_manager, EVT_ERROR, "Failed to decode all used components\n");
+        return OPJ_FALSE;
+    }
+
+    return OPJ_TRUE;
+}
+
+
 static OPJ_BOOL opj_j2k_decode_tiles(opj_j2k_t *p_j2k,
                                      opj_stream_private_t *p_stream,
                                      opj_event_mgr_t * p_manager)
@@ -10590,6 +10805,10 @@ static OPJ_BOOL opj_j2k_decode_tiles(opj_j2k_t *p_j2k,
         }
     }
 
+    if (! opj_j2k_are_all_used_components_decoded(p_j2k, p_manager)) {
+        return OPJ_FALSE;
+    }
+
     return OPJ_TRUE;
 }
 
@@ -10718,6 +10937,10 @@ static OPJ_BOOL opj_j2k_decode_one_tile(opj_j2k_t *p_j2k,
 
     }
 
+    if (! opj_j2k_are_all_used_components_decoded(p_j2k, p_manager)) {
+        return OPJ_FALSE;
+    }
+
     return OPJ_TRUE;
 }
 
@@ -10740,13 +10963,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;
     }
@@ -10797,23 +11078,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,
@@ -10831,6 +11096,12 @@ OPJ_BOOL opj_j2k_get_tile(opj_j2k_t *p_j2k,
         return OPJ_FALSE;
     }
 
+    if (p_image->numcomps < p_j2k->m_private_image->numcomps) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Image has less components than codestream.\n");
+        return OPJ_FALSE;
+    }
+
     if (/*(tile_index < 0) &&*/ (tile_index >= p_j2k->m_cp.tw * p_j2k->m_cp.th)) {
         opj_event_msg(p_manager, EVT_ERROR,
                       "Tile index provided by the user is incorrect %d (max = %d) \n", tile_index,
@@ -10861,7 +11132,7 @@ OPJ_BOOL opj_j2k_get_tile(opj_j2k_t *p_j2k,
     }
 
     l_img_comp = p_image->comps;
-    for (compno = 0; compno < p_image->numcomps; ++compno) {
+    for (compno = 0; compno < p_j2k->m_private_image->numcomps; ++compno) {
         OPJ_INT32 l_comp_x1, l_comp_y1;
 
         l_img_comp->factor = p_j2k->m_private_image->comps[compno].factor;
@@ -10883,6 +11154,18 @@ OPJ_BOOL opj_j2k_get_tile(opj_j2k_t *p_j2k,
         l_img_comp++;
     }
 
+    if (p_image->numcomps > p_j2k->m_private_image->numcomps) {
+        /* Can happen when calling repeatdly opj_get_decoded_tile() on an
+         * image with a color palette, where color palette expansion is done
+         * later in jp2.c */
+        for (compno = p_j2k->m_private_image->numcomps; compno < p_image->numcomps;
+                ++compno) {
+            opj_image_data_free(p_image->comps[compno].data);
+            p_image->comps[compno].data = NULL;
+        }
+        p_image->numcomps = p_j2k->m_private_image->numcomps;
+    }
+
     /* Destroy the previous output image*/
     if (p_j2k->m_output_image) {
         opj_image_destroy(p_j2k->m_output_image);
@@ -10910,20 +11193,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,
@@ -11029,6 +11299,12 @@ OPJ_BOOL opj_j2k_encode(opj_j2k_t * p_j2k,
                 l_current_data = l_new_current_data;
                 l_max_tile_size = l_current_tile_size;
             }
+            if (l_current_data == NULL) {
+                /* Should not happen in practice, but will avoid Coverity to */
+                /* complain about a null pointer dereference */
+                assert(0);
+                return OPJ_FALSE;
+            }
 
             /* copy image data (32 bit) to l_current_data as contiguous, all-component, zero offset buffer */
             /* 32 bit components @ 8 bit precision get converted to 8 bit */
@@ -11476,7 +11752,7 @@ static OPJ_BOOL opj_j2k_setup_header_writing(opj_j2k_t *p_j2k,
 static OPJ_BOOL opj_j2k_write_first_tile_part(opj_j2k_t *p_j2k,
         OPJ_BYTE * p_data,
         OPJ_UINT32 * p_data_written,
-        OPJ_UINT32 p_total_data_size,
+        OPJ_UINT32 total_data_size,
         opj_stream_private_t *p_stream,
         struct opj_event_mgr * p_manager)
 {
@@ -11500,7 +11776,7 @@ static OPJ_BOOL opj_j2k_write_first_tile_part(opj_j2k_t *p_j2k,
 
     l_current_nb_bytes_written = 0;
     l_begin_data = p_data;
-    if (! opj_j2k_write_sot(p_j2k, p_data, p_total_data_size,
+    if (! opj_j2k_write_sot(p_j2k, p_data, total_data_size,
                             &l_current_nb_bytes_written, p_stream,
                             p_manager)) {
         return OPJ_FALSE;
@@ -11508,7 +11784,7 @@ static OPJ_BOOL opj_j2k_write_first_tile_part(opj_j2k_t *p_j2k,
 
     l_nb_bytes_written += l_current_nb_bytes_written;
     p_data += l_current_nb_bytes_written;
-    p_total_data_size -= l_current_nb_bytes_written;
+    total_data_size -= l_current_nb_bytes_written;
 
     if (!OPJ_IS_CINEMA(l_cp->rsiz)) {
 #if 0
@@ -11518,29 +11794,29 @@ static OPJ_BOOL opj_j2k_write_first_tile_part(opj_j2k_t *p_j2k,
                                         p_manager);
             l_nb_bytes_written += l_current_nb_bytes_written;
             p_data += l_current_nb_bytes_written;
-            p_total_data_size -= l_current_nb_bytes_written;
+            total_data_size -= l_current_nb_bytes_written;
 
             l_current_nb_bytes_written = 0;
             opj_j2k_write_qcc_in_memory(p_j2k, compno, p_data, &l_current_nb_bytes_written,
                                         p_manager);
             l_nb_bytes_written += l_current_nb_bytes_written;
             p_data += l_current_nb_bytes_written;
-            p_total_data_size -= l_current_nb_bytes_written;
+            total_data_size -= l_current_nb_bytes_written;
         }
 #endif
-        if (l_cp->tcps[p_j2k->m_current_tile_number].numpocs) {
+        if (l_cp->tcps[p_j2k->m_current_tile_number].POC) {
             l_current_nb_bytes_written = 0;
             opj_j2k_write_poc_in_memory(p_j2k, p_data, &l_current_nb_bytes_written,
                                         p_manager);
             l_nb_bytes_written += l_current_nb_bytes_written;
             p_data += l_current_nb_bytes_written;
-            p_total_data_size -= l_current_nb_bytes_written;
+            total_data_size -= l_current_nb_bytes_written;
         }
     }
 
     l_current_nb_bytes_written = 0;
     if (! opj_j2k_write_sod(p_j2k, l_tcd, p_data, &l_current_nb_bytes_written,
-                            p_total_data_size, p_stream, p_manager)) {
+                            total_data_size, p_stream, p_manager)) {
         return OPJ_FALSE;
     }
 
@@ -11561,7 +11837,7 @@ static OPJ_BOOL opj_j2k_write_first_tile_part(opj_j2k_t *p_j2k,
 static OPJ_BOOL opj_j2k_write_all_tile_parts(opj_j2k_t *p_j2k,
         OPJ_BYTE * p_data,
         OPJ_UINT32 * p_data_written,
-        OPJ_UINT32 p_total_data_size,
+        OPJ_UINT32 total_data_size,
         opj_stream_private_t *p_stream,
         struct opj_event_mgr * p_manager
                                             )
@@ -11594,7 +11870,7 @@ static OPJ_BOOL opj_j2k_write_all_tile_parts(opj_j2k_t *p_j2k,
         l_begin_data = p_data;
 
         if (! opj_j2k_write_sot(p_j2k, p_data,
-                                p_total_data_size,
+                                total_data_size,
                                 &l_current_nb_bytes_written,
                                 p_stream,
                                 p_manager)) {
@@ -11603,18 +11879,18 @@ static OPJ_BOOL opj_j2k_write_all_tile_parts(opj_j2k_t *p_j2k,
 
         l_nb_bytes_written += l_current_nb_bytes_written;
         p_data += l_current_nb_bytes_written;
-        p_total_data_size -= l_current_nb_bytes_written;
+        total_data_size -= l_current_nb_bytes_written;
         l_part_tile_size += l_current_nb_bytes_written;
 
         l_current_nb_bytes_written = 0;
         if (! opj_j2k_write_sod(p_j2k, l_tcd, p_data, &l_current_nb_bytes_written,
-                                p_total_data_size, p_stream, p_manager)) {
+                                total_data_size, p_stream, p_manager)) {
             return OPJ_FALSE;
         }
 
         p_data += l_current_nb_bytes_written;
         l_nb_bytes_written += l_current_nb_bytes_written;
-        p_total_data_size -= l_current_nb_bytes_written;
+        total_data_size -= l_current_nb_bytes_written;
         l_part_tile_size += l_current_nb_bytes_written;
 
         /* Writing Psot in SOT marker */
@@ -11640,7 +11916,7 @@ static OPJ_BOOL opj_j2k_write_all_tile_parts(opj_j2k_t *p_j2k,
             l_begin_data = p_data;
 
             if (! opj_j2k_write_sot(p_j2k, p_data,
-                                    p_total_data_size,
+                                    total_data_size,
                                     &l_current_nb_bytes_written, p_stream,
                                     p_manager)) {
                 return OPJ_FALSE;
@@ -11648,19 +11924,19 @@ static OPJ_BOOL opj_j2k_write_all_tile_parts(opj_j2k_t *p_j2k,
 
             l_nb_bytes_written += l_current_nb_bytes_written;
             p_data += l_current_nb_bytes_written;
-            p_total_data_size -= l_current_nb_bytes_written;
+            total_data_size -= l_current_nb_bytes_written;
             l_part_tile_size += l_current_nb_bytes_written;
 
             l_current_nb_bytes_written = 0;
 
             if (! opj_j2k_write_sod(p_j2k, l_tcd, p_data, &l_current_nb_bytes_written,
-                                    p_total_data_size, p_stream, p_manager)) {
+                                    total_data_size, p_stream, p_manager)) {
                 return OPJ_FALSE;
             }
 
             l_nb_bytes_written += l_current_nb_bytes_written;
             p_data += l_current_nb_bytes_written;
-            p_total_data_size -= l_current_nb_bytes_written;
+            total_data_size -= l_current_nb_bytes_written;
             l_part_tile_size += l_current_nb_bytes_written;
 
             /* Writing Psot in SOT marker */