Merge pull request #529 from renevanderark/master
[openjpeg.git] / src / lib / openjp2 / j2k.c
index 8c62a39b3169549ab1726c754e0501b746c1c13f..ee988b6d5716e35dc8622f0c7b09457dddcdf691 100644 (file)
@@ -780,12 +780,32 @@ static OPJ_BOOL opj_j2k_write_sot(      opj_j2k_t *p_j2k,
                                                                         opj_event_mgr_t * p_manager );
 
 /**
- * Reads a PPT marker (Packed packet headers, tile-part header)
+ * Reads values from a SOT marker (Start of tile-part)
  *
- * @param       p_header_data   the data contained in the PPT box.
- * @param       p_j2k                   the jpeg2000 codec.
+ * the j2k decoder state is not affected. No side effects, no checks except for p_header_size.
+ *
+ * @param       p_header_data   the data contained in the SOT marker.
+ * @param       p_header_size   the size of the data contained in the SOT marker.
+ * @param       p_tile_no       Isot.
+ * @param       p_tot_len       Psot.
+ * @param       p_current_part  TPsot.
+ * @param       p_num_parts     TNsot.
+ * @param       p_manager       the user event manager.
+ */
+static OPJ_BOOL opj_j2k_get_sot_values(OPJ_BYTE *  p_header_data,
+                                                                                                                                                        OPJ_UINT32  p_header_size,
+                                                                                                                                                        OPJ_UINT32* p_tile_no,
+                                                                                                                                                        OPJ_UINT32* p_tot_len,
+                                                                                                                                                        OPJ_UINT32* p_current_part,
+                                                                                                                                                        OPJ_UINT32* p_num_parts,
+                                                                                                                                                        opj_event_mgr_t * p_manager );
+/**
+ * Reads a SOT marker (Start of tile-part)
+ *
+ * @param       p_header_data   the data contained in the SOT marker.
+ * @param       p_j2k           the jpeg2000 codec.
  * @param       p_header_size   the size of the data contained in the PPT marker.
- * @param       p_manager               the user event manager.
+ * @param       p_manager       the user event manager.
 */
 static OPJ_BOOL opj_j2k_read_sot (  opj_j2k_t *p_j2k,
                                     OPJ_BYTE * p_header_data,
@@ -1179,6 +1199,18 @@ static void opj_j2k_set_cinema_parameters(opj_cparameters_t *parameters, opj_ima
 
 static OPJ_BOOL opj_j2k_is_cinema_compliant(opj_image_t *image, OPJ_UINT16 rsiz, opj_event_mgr_t *p_manager);
 
+/**
+ * Checks for invalid number of tile-parts in SOT marker (TPsot==TNsot). See issue 254.
+ *
+ * @param       p_stream            the stream to read data from.
+ * @param       tile_no             tile number we're looking for.
+ * @param       p_correction_needed output value. if true, non conformant codestream needs TNsot correction.
+ * @param       p_manager       the user event manager.
+ *
+ * @return true if the function was successful, false else.
+ */
+static OPJ_BOOL opj_j2k_need_nb_tile_parts_correction(opj_stream_private_t *p_stream, OPJ_UINT32 tile_no, OPJ_BOOL* p_correction_needed, opj_event_mgr_t * p_manager );
+
 /*@}*/
 
 /*@}*/
@@ -1999,14 +2031,8 @@ static OPJ_BOOL opj_j2k_read_siz(opj_j2k_t *p_j2k,
         }
 
         /* testcase issue427-illegal-tile-offset.jp2 */
-        l_tx1 = l_cp->tx0 + l_cp->tdx;
-        if (l_tx1 < l_cp->tx0) { /* manage overflow */
-                l_tx1 = 0xFFFFFFFFU;
-        }
-        l_ty1 = l_cp->ty0 + l_cp->tdy;
-        if (l_ty1 < l_cp->ty0) { /* manage overflow */
-                l_ty1 = 0xFFFFFFFFU;
-        }
+        l_tx1 = opj_uint_adds(l_cp->tx0, l_cp->tdx); /* manage overflow */
+        l_ty1 = opj_uint_adds(l_cp->ty0, l_cp->tdy); /* manage overflow */
         if ((l_cp->tx0 > l_image->x0) || (l_cp->ty0 > l_image->y0) || (l_tx1 <= l_image->x0) || (l_ty1 <= l_image->y0) ) {
                 opj_event_msg(p_manager, EVT_ERROR, "Error with SIZ marker: illegal tile offset\n");
                 return OPJ_FALSE;
@@ -4018,6 +4044,35 @@ OPJ_BOOL opj_j2k_write_sot(     opj_j2k_t *p_j2k,
         return OPJ_TRUE;
 }
 
+static OPJ_BOOL opj_j2k_get_sot_values(OPJ_BYTE *  p_header_data,
+                                                                                                                                                        OPJ_UINT32  p_header_size,
+                                                                                                                                                        OPJ_UINT32* p_tile_no,
+                                                                                                                                                        OPJ_UINT32* p_tot_len,
+                                                                                                                                                        OPJ_UINT32* p_current_part,
+                                                                                                                                                        OPJ_UINT32* p_num_parts,
+                                                                                                                                                        opj_event_mgr_t * p_manager )
+{
+       /* preconditions */
+       assert(p_header_data != 00);
+       assert(p_manager != 00);
+       
+       /* Size of this marker is fixed = 12 (we have already read marker and its size)*/
+       if (p_header_size != 8) {
+               opj_event_msg(p_manager, EVT_ERROR, "Error reading SOT marker\n");
+               return OPJ_FALSE;
+       }
+       
+       opj_read_bytes(p_header_data,p_tile_no,2);      /* Isot */
+       p_header_data+=2;
+       opj_read_bytes(p_header_data,p_tot_len,4);      /* Psot */
+       p_header_data+=4;
+       opj_read_bytes(p_header_data,p_current_part,1); /* TPsot */
+       ++p_header_data;
+       opj_read_bytes(p_header_data,p_num_parts ,1);   /* TNsot */
+       ++p_header_data;
+       return OPJ_TRUE;
+}
+
 OPJ_BOOL opj_j2k_read_sot ( opj_j2k_t *p_j2k,
                             OPJ_BYTE * p_header_data,
                             OPJ_UINT32 p_header_size,
@@ -4030,19 +4085,16 @@ OPJ_BOOL opj_j2k_read_sot ( opj_j2k_t *p_j2k,
         OPJ_UINT32 l_tile_x,l_tile_y;
 
         /* preconditions */
-        assert(p_header_data != 00);
+       
         assert(p_j2k != 00);
         assert(p_manager != 00);
-
-        /* Size of this marker is fixed = 12 (we have already read marker and its size)*/
-        if (p_header_size != 8) {
+       
+        if (! opj_j2k_get_sot_values(p_header_data, p_header_size, &(p_j2k->m_current_tile_number), &l_tot_len, &l_current_part, &l_num_parts, p_manager)) {
                 opj_event_msg(p_manager, EVT_ERROR, "Error reading SOT marker\n");
                 return OPJ_FALSE;
         }
 
         l_cp = &(p_j2k->m_cp);
-        opj_read_bytes(p_header_data,&(p_j2k->m_current_tile_number),2);                /* Isot */
-        p_header_data+=2;
 
         /* testcase 2.pdf.SIGFPE.706.1112 */
         if (p_j2k->m_current_tile_number >= l_cp->tw * l_cp->th) {
@@ -4085,9 +4137,6 @@ OPJ_BOOL opj_j2k_read_sot ( opj_j2k_t *p_j2k,
         /* Optimization possible here with a more complex data structure and with the removing of tiles */
         /* since the time taken by this function can only grow at the time */
 
-        opj_read_bytes(p_header_data,&l_tot_len,4);             /* Psot */
-        p_header_data+=4;
-
         /* PSot should be equal to zero or >=14 or <= 2^32-1 */
         if ((l_tot_len !=0 ) && (l_tot_len < 14) )
         {
@@ -4130,13 +4179,8 @@ OPJ_BOOL opj_j2k_read_sot ( opj_j2k_t *p_j2k,
                         p_j2k->m_specific_param.m_decoder.m_last_tile_part = 1;
                 }
 
-                opj_read_bytes(p_header_data,&l_current_part ,1);       /* TPsot */
-                ++p_header_data;
-
-                opj_read_bytes(p_header_data,&l_num_parts ,1);          /* TNsot */
-                ++p_header_data;
-
                 if (l_num_parts != 0) { /* Number of tile-part header is provided by this tile-part header */
+                        l_num_parts += p_j2k->m_specific_param.m_decoder.m_nb_tile_parts_correction;
                         /* Useful to manage the case of textGBR.jp2 file because two values of TNSot are allowed: the correct numbers of
                          * tile-parts for that tile and zero (A.4.2 of 15444-1 : 2002). */
                         if (l_tcp->m_nb_tile_parts) {
@@ -7365,6 +7409,12 @@ static OPJ_BOOL opj_j2k_copy_default_tcp_and_create_tcd (       opj_j2k_t * p_j2
                 l_tcp->cod = 0;
                 l_tcp->ppt = 0;
                 l_tcp->ppt_data = 00;
+                /* Remove memory not owned by this tile in case of early error return. */
+                l_tcp->m_mct_decoding_matrix = 00;
+                l_tcp->m_nb_max_mct_records = 0;
+                l_tcp->m_mct_records = 00;
+                l_tcp->m_nb_max_mcc_records = 0;
+                l_tcp->m_mcc_records = 00;
                 /* Reconnect the tile-compo coding parameters pointer to the current tile coding parameters*/
                 l_tcp->tccps = l_current_tccp;
 
@@ -7402,6 +7452,8 @@ static OPJ_BOOL opj_j2k_copy_default_tcp_and_create_tcd (       opj_j2k_t * p_j2
 
                         ++l_src_mct_rec;
                         ++l_dest_mct_rec;
+                        /* Update with each pass to free exactly what has been allocated on early return. */
+                        l_tcp->m_nb_max_mct_records += 1;
                 }
 
                 /* Get the mcc_record of the dflt_tile_cp and copy them into the current tile cp*/
@@ -7411,6 +7463,7 @@ static OPJ_BOOL opj_j2k_copy_default_tcp_and_create_tcd (       opj_j2k_t * p_j2
                         return OPJ_FALSE;
                 }
                 memcpy(l_tcp->m_mcc_records,l_default_tcp->m_mcc_records,l_mcc_records_size);
+                l_tcp->m_nb_max_mcc_records = l_default_tcp->m_nb_max_mcc_records;
 
                 /* Copy the mcc record data from dflt_tile_cp to the current tile*/
                 l_src_mcc_rec = l_default_tcp->m_mcc_records;
@@ -7671,6 +7724,104 @@ void opj_j2k_cp_destroy (opj_cp_t *p_cp)
         }
 }
 
+static OPJ_BOOL opj_j2k_need_nb_tile_parts_correction(opj_stream_private_t *p_stream, OPJ_UINT32 tile_no, OPJ_BOOL* p_correction_needed, opj_event_mgr_t * p_manager )
+{
+       OPJ_BYTE   l_header_data[10];
+       OPJ_OFF_T  l_stream_pos_backup;
+       OPJ_UINT32 l_current_marker;
+       OPJ_UINT32 l_marker_size;
+       OPJ_UINT32 l_tile_no, l_tot_len, l_current_part, l_num_parts;
+       
+       /* initialize to no correction needed */
+       *p_correction_needed = OPJ_FALSE;
+       
+       l_stream_pos_backup = opj_stream_tell(p_stream);
+       if (l_stream_pos_backup == -1) {
+               /* let's do nothing */
+               return OPJ_TRUE;
+       }
+       
+       for (;;) {
+               /* Try to read 2 bytes (the next marker ID) from stream and copy them into the buffer */
+               if (opj_stream_read_data(p_stream,l_header_data, 2, p_manager) != 2) {
+                       /* assume all is OK */
+                       if (! opj_stream_seek(p_stream, l_stream_pos_backup, p_manager)) {
+                               return OPJ_FALSE;
+                       }
+                       return OPJ_TRUE;
+               }
+               
+               /* Read 2 bytes from buffer as the new marker ID */
+               opj_read_bytes(l_header_data, &l_current_marker, 2);
+               
+               if (l_current_marker != J2K_MS_SOT) {
+                       /* assume all is OK */
+                       if (! opj_stream_seek(p_stream, l_stream_pos_backup, p_manager)) {
+                               return OPJ_FALSE;
+                       }
+                       return OPJ_TRUE;
+               }
+               
+               /* Try to read 2 bytes (the marker size) from stream and copy them into the buffer */
+               if (opj_stream_read_data(p_stream, l_header_data, 2, p_manager) != 2) {
+                       opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
+                       return OPJ_FALSE;
+               }
+               
+               /* Read 2 bytes from the buffer as the marker size */
+               opj_read_bytes(l_header_data, &l_marker_size, 2);
+               
+               /* Check marker size for SOT Marker */
+               if (l_marker_size != 10) {
+                       opj_event_msg(p_manager, EVT_ERROR, "Inconsistent marker size\n");
+                       return OPJ_FALSE;
+               }
+               l_marker_size -= 2;
+               
+               if (opj_stream_read_data(p_stream, l_header_data, l_marker_size, p_manager) != l_marker_size) {
+                       opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
+                       return OPJ_FALSE;
+               }
+               
+               if (! opj_j2k_get_sot_values(l_header_data, l_marker_size, &l_tile_no, &l_tot_len, &l_current_part, &l_num_parts, p_manager)) {
+                       return OPJ_FALSE;
+               }
+               
+               if (l_tile_no == tile_no) {
+                       /* we found what we were looking for */
+                       break;
+               }
+               
+               if ((l_tot_len == 0U) || (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)) {
+                               return OPJ_FALSE;
+                       }
+                       return OPJ_TRUE;
+               }
+               l_tot_len -= 12U;
+               /* look for next SOT marker */
+               if (opj_stream_skip(p_stream, (OPJ_OFF_T)(l_tot_len), p_manager) != (OPJ_OFF_T)(l_tot_len)) {
+                       /* assume all is OK */
+                       if (! opj_stream_seek(p_stream, l_stream_pos_backup, p_manager)) {
+                               return OPJ_FALSE;
+                       }
+                       return OPJ_TRUE;
+               }
+       }
+       
+       /* check for correction */
+       if (l_current_part == l_num_parts) {
+               *p_correction_needed = OPJ_TRUE;
+       }
+       
+       if (! opj_stream_seek(p_stream, l_stream_pos_backup, p_manager)) {
+               return OPJ_FALSE;
+       }
+       return OPJ_TRUE;
+}
+
 OPJ_BOOL opj_j2k_read_tile_header(      opj_j2k_t * p_j2k,
                                                                     OPJ_UINT32 * p_tile_index,
                                                                     OPJ_UINT32 * p_data_size,
@@ -7835,7 +7986,30 @@ OPJ_BOOL opj_j2k_read_tile_header(      opj_j2k_t * p_j2k,
                         if (! opj_j2k_read_sod(p_j2k, p_stream, p_manager)) {
                                 return OPJ_FALSE;
                         }
-
+                        if (p_j2k->m_specific_param.m_decoder.m_can_decode && !p_j2k->m_specific_param.m_decoder.m_nb_tile_parts_correction_checked) {
+                                /* Issue 254 */
+                                OPJ_BOOL l_correction_needed;
+                                                                                                       
+                                p_j2k->m_specific_param.m_decoder.m_nb_tile_parts_correction_checked = 1;
+                                if(!opj_j2k_need_nb_tile_parts_correction(p_stream, p_j2k->m_current_tile_number, &l_correction_needed, p_manager)) {
+                                        opj_event_msg(p_manager, EVT_ERROR, "opj_j2k_apply_nb_tile_parts_correction error\n");
+                                        return OPJ_FALSE;
+                                }
+                                if (l_correction_needed) {
+                                        OPJ_UINT32 l_nb_tiles = p_j2k->m_cp.tw * p_j2k->m_cp.th;
+                                        OPJ_UINT32 l_tile_no;
+
+                                        p_j2k->m_specific_param.m_decoder.m_can_decode = 0;
+                                        p_j2k->m_specific_param.m_decoder.m_nb_tile_parts_correction = 1;
+                                        /* correct tiles */
+                                        for (l_tile_no = 0U; l_tile_no < l_nb_tiles; ++l_tile_no) {
+                                                if (p_j2k->m_cp.tcps[l_tile_no].m_nb_tile_parts != 0U) {
+                                                        p_j2k->m_cp.tcps[l_tile_no].m_nb_tile_parts+=1;
+                                                }
+                                        }
+                                        opj_event_msg(p_manager, EVT_WARNING, "Non conformant codestream TPsot==TNsot.\n");
+                                }
+                        }
                         if (! p_j2k->m_specific_param.m_decoder.m_can_decode){
                                 /* Try to read 2 bytes (the next marker ID) from stream and copy them into the buffer */
                                 if (opj_stream_read_data(p_stream,p_j2k->m_specific_param.m_decoder.m_header_data,2,p_manager) != 2) {
@@ -8000,10 +8174,10 @@ OPJ_BOOL opj_j2k_update_image_data (opj_tcd_t * p_tcd, OPJ_BYTE * p_data, opj_im
         OPJ_UINT32 l_width_src,l_height_src;
         OPJ_UINT32 l_width_dest,l_height_dest;
         OPJ_INT32 l_offset_x0_src, l_offset_y0_src, l_offset_x1_src, l_offset_y1_src;
-        OPJ_INT32 l_start_offset_src, l_line_offset_src, l_end_offset_src ;
+        OPJ_SIZE_T l_start_offset_src, l_line_offset_src, l_end_offset_src ;
         OPJ_UINT32 l_start_x_dest , l_start_y_dest;
         OPJ_UINT32 l_x0_dest, l_y0_dest, l_x1_dest, l_y1_dest;
-        OPJ_INT32 l_start_offset_dest, l_line_offset_dest;
+        OPJ_SIZE_T l_start_offset_dest, l_line_offset_dest;
 
         opj_image_comp_t * l_img_comp_src = 00;
         opj_image_comp_t * l_img_comp_dest = 00;
@@ -8025,7 +8199,7 @@ OPJ_BOOL opj_j2k_update_image_data (opj_tcd_t * p_tcd, OPJ_BYTE * p_data, opj_im
                 /* Allocate output component buffer if necessary */
                 if (!l_img_comp_dest->data) {
 
-                        l_img_comp_dest->data = (OPJ_INT32*) opj_calloc(l_img_comp_dest->w * l_img_comp_dest->h, sizeof(OPJ_INT32));
+                        l_img_comp_dest->data = (OPJ_INT32*) opj_calloc((OPJ_SIZE_T)l_img_comp_dest->w * (OPJ_SIZE_T)l_img_comp_dest->h, sizeof(OPJ_INT32));
                         if (! l_img_comp_dest->data) {
                                 return OPJ_FALSE;
                         }
@@ -8059,9 +8233,9 @@ OPJ_BOOL opj_j2k_update_image_data (opj_tcd_t * p_tcd, OPJ_BYTE * p_data, opj_im
                 l_height_src = (OPJ_UINT32)(l_res->y1 - l_res->y0);
 
                 /* Border of the current output component*/
-                l_x0_dest = (OPJ_UINT32)opj_int_ceildivpow2((OPJ_INT32)l_img_comp_dest->x0, (OPJ_INT32)l_img_comp_dest->factor);
-                l_y0_dest = (OPJ_UINT32)opj_int_ceildivpow2((OPJ_INT32)l_img_comp_dest->y0, (OPJ_INT32)l_img_comp_dest->factor);
-                l_x1_dest = l_x0_dest + l_img_comp_dest->w;
+                l_x0_dest = opj_uint_ceildivpow2(l_img_comp_dest->x0, l_img_comp_dest->factor);
+                l_y0_dest = opj_uint_ceildivpow2(l_img_comp_dest->y0, l_img_comp_dest->factor);
+                l_x1_dest = l_x0_dest + l_img_comp_dest->w; /* can't overflow given that image->x1 is uint32 */
                 l_y1_dest = l_y0_dest + l_img_comp_dest->h;
 
                 /*if (i == 0) {
@@ -8092,7 +8266,7 @@ OPJ_BOOL opj_j2k_update_image_data (opj_tcd_t * p_tcd, OPJ_BYTE * p_data, opj_im
                         }
                 }
                 else {
-                        l_start_x_dest = 0 ;
+                        l_start_x_dest = 0U;
                         l_offset_x0_src = (OPJ_INT32)l_x0_dest - l_res->x0;
 
                         if ( l_x1_dest >= (OPJ_UINT32)l_res->x1 ) {
@@ -8119,7 +8293,7 @@ OPJ_BOOL opj_j2k_update_image_data (opj_tcd_t * p_tcd, OPJ_BYTE * p_data, opj_im
                         }
                 }
                 else {
-                        l_start_y_dest = 0 ;
+                        l_start_y_dest = 0U;
                         l_offset_y0_src = (OPJ_INT32)l_y0_dest - l_res->y0;
 
                         if ( l_y1_dest >= (OPJ_UINT32)l_res->y1 ) {
@@ -8142,13 +8316,13 @@ OPJ_BOOL opj_j2k_update_image_data (opj_tcd_t * p_tcd, OPJ_BYTE * p_data, opj_im
                 /*-----*/
 
                 /* Compute the input buffer offset */
-                l_start_offset_src = l_offset_x0_src + l_offset_y0_src * (OPJ_INT32)l_width_src;
-                l_line_offset_src = l_offset_x1_src + l_offset_x0_src;
-                l_end_offset_src = l_offset_y1_src * (OPJ_INT32)l_width_src - l_offset_x0_src;
+                l_start_offset_src = (OPJ_SIZE_T)l_offset_x0_src + (OPJ_SIZE_T)l_offset_y0_src * (OPJ_SIZE_T)l_width_src;
+                l_line_offset_src  = (OPJ_SIZE_T)l_offset_x1_src + (OPJ_SIZE_T)l_offset_x0_src;
+                l_end_offset_src   = (OPJ_SIZE_T)l_offset_y1_src * (OPJ_SIZE_T)l_width_src - (OPJ_SIZE_T)l_offset_x0_src;
 
                 /* Compute the output buffer offset */
-                l_start_offset_dest = (OPJ_INT32)(l_start_x_dest + l_start_y_dest * l_img_comp_dest->w);
-                l_line_offset_dest = (OPJ_INT32)(l_img_comp_dest->w - l_width_dest);
+                l_start_offset_dest = (OPJ_SIZE_T)l_start_x_dest + (OPJ_SIZE_T)l_start_y_dest * (OPJ_SIZE_T)l_img_comp_dest->w;
+                l_line_offset_dest  = (OPJ_SIZE_T)l_img_comp_dest->w - (OPJ_SIZE_T)l_width_dest;
 
                 /* Move the output buffer to the first place where we will write*/
                 l_dest_ptr = l_img_comp_dest->data + l_start_offset_dest;
@@ -8422,6 +8596,10 @@ opj_j2k_t* opj_j2k_create_decompress(void)
         l_j2k->m_is_decoder = 1;
         l_j2k->m_cp.m_is_decoder = 1;
 
+#ifdef OPJ_DISABLE_TPSOT_FIX
+        l_j2k->m_specific_param.m_decoder.m_nb_tile_parts_correction_checked = 1;
+#endif
+
         l_j2k->m_specific_param.m_decoder.m_default_tcp = (opj_tcp_t*) opj_calloc(1,sizeof(opj_tcp_t));
         if (!l_j2k->m_specific_param.m_decoder.m_default_tcp) {
                 opj_j2k_destroy(l_j2k);
@@ -9638,7 +9816,7 @@ OPJ_BOOL opj_j2k_decode(opj_j2k_t * p_j2k,
 
         if (!p_image)
                 return OPJ_FALSE;
-
+       
         p_j2k->m_output_image = opj_image_create0();
         if (! (p_j2k->m_output_image)) {
                 return OPJ_FALSE;
@@ -9837,7 +10015,6 @@ OPJ_BOOL opj_j2k_encode(opj_j2k_t * p_j2k,
                                                                                                                }
                                                                                                                return OPJ_FALSE;
                                                                                                        }
-                                                                                                       opj_alloc_tile_component_data(l_tilec);
                         }
                 }
                 l_current_tile_size = opj_tcd_get_encoded_tile_size(p_j2k->m_tcd);