Sub-tile decoding: only allocate tile component buffer of the needed dimension
[openjpeg.git] / src / lib / openjp2 / tcd.c
index 1c56c1b373e1ada2fc28d7a30f9941f294711315..c221c6ed4fc35898d9e97c8b5eb537eaaa23d5cc 100644 (file)
@@ -190,6 +190,10 @@ static OPJ_BOOL opj_tcd_rate_allocate_encode(opj_tcd_t *p_tcd,
         opj_codestream_info_t *p_cstr_info,
         opj_event_mgr_t *p_manager);
 
+
+static OPJ_BOOL opj_tcd_is_whole_tilecomp_decoding(opj_tcd_t *tcd,
+        OPJ_UINT32 compno);
+
 /* ----------------------------------------------------------------------- */
 
 /**
@@ -679,7 +683,7 @@ OPJ_BOOL opj_alloc_tile_component_data(opj_tcd_tilecomp_t *l_tilec)
             ((l_tilec->data_size_needed > l_tilec->data_size) &&
              (l_tilec->ownsData == OPJ_FALSE))) {
         l_tilec->data = (OPJ_INT32 *) opj_image_data_alloc(l_tilec->data_size_needed);
-        if (! l_tilec->data) {
+        if (!l_tilec->data && l_tilec->data_size_needed != 0) {
             return OPJ_FALSE;
         }
         /*fprintf(stderr, "tAllocate data of tilec (int): %d x OPJ_UINT32n",l_data_size);*/
@@ -794,22 +798,6 @@ static INLINE OPJ_BOOL opj_tcd_init_tile(opj_tcd_t *p_tcd, OPJ_UINT32 p_tile_no,
         l_tilec->compno = compno;
         /*fprintf(stderr, "\tTile compo border = %d,%d,%d,%d\n", l_tilec->x0, l_tilec->y0,l_tilec->x1,l_tilec->y1);*/
 
-        /* compute l_data_size with overflow check */
-        l_data_size = (OPJ_UINT32)(l_tilec->x1 - l_tilec->x0);
-        /* issue 733, l_data_size == 0U, probably something wrong should be checked before getting here */
-        if ((l_data_size > 0U) &&
-                ((((OPJ_UINT32) - 1) / l_data_size) < (OPJ_UINT32)(l_tilec->y1 -
-                        l_tilec->y0))) {
-            opj_event_msg(manager, EVT_ERROR, "Not enough memory for tile data\n");
-            return OPJ_FALSE;
-        }
-        l_data_size = l_data_size * (OPJ_UINT32)(l_tilec->y1 - l_tilec->y0);
-
-        if ((((OPJ_UINT32) - 1) / (OPJ_UINT32)sizeof(OPJ_UINT32)) < l_data_size) {
-            opj_event_msg(manager, EVT_ERROR, "Not enough memory for tile data\n");
-            return OPJ_FALSE;
-        }
-        l_data_size = l_data_size * (OPJ_UINT32)sizeof(OPJ_UINT32);
         l_tilec->numresolutions = l_tccp->numresolutions;
         if (l_tccp->numresolutions < l_cp->m_specific_param.m_dec.m_reduce) {
             l_tilec->minimum_num_resolutions = 1;
@@ -818,15 +806,37 @@ static INLINE OPJ_BOOL opj_tcd_init_tile(opj_tcd_t *p_tcd, OPJ_UINT32 p_tile_no,
                                                l_cp->m_specific_param.m_dec.m_reduce;
         }
 
-        l_tilec->data_size_needed = l_data_size;
-        if (p_tcd->m_is_decoder && !opj_alloc_tile_component_data(l_tilec)) {
-            opj_event_msg(manager, EVT_ERROR, "Not enough memory for tile data\n");
-            return OPJ_FALSE;
+        if (isEncoder) {
+            /* compute l_data_size with overflow check */
+            l_data_size = (OPJ_UINT32)(l_tilec->x1 - l_tilec->x0);
+            /* issue 733, l_data_size == 0U, probably something wrong should be checked before getting here */
+            if ((l_data_size > 0U) &&
+                    ((((OPJ_UINT32) - 1) / l_data_size) < (OPJ_UINT32)(l_tilec->y1 -
+                            l_tilec->y0))) {
+                opj_event_msg(manager, EVT_ERROR, "Not enough memory for tile data\n");
+                return OPJ_FALSE;
+            }
+            l_data_size = l_data_size * (OPJ_UINT32)(l_tilec->y1 - l_tilec->y0);
+
+            if ((((OPJ_UINT32) - 1) / (OPJ_UINT32)sizeof(OPJ_UINT32)) < l_data_size) {
+                opj_event_msg(manager, EVT_ERROR, "Not enough memory for tile data\n");
+                return OPJ_FALSE;
+            }
+            l_data_size = l_data_size * (OPJ_UINT32)sizeof(OPJ_UINT32);
+
+            l_tilec->data_size_needed = l_data_size;
         }
 
         l_data_size = l_tilec->numresolutions * (OPJ_UINT32)sizeof(
                           opj_tcd_resolution_t);
 
+        opj_aligned_free(l_tilec->data_win);
+        l_tilec->data_win = NULL;
+        l_tilec->win_x0 = 0;
+        l_tilec->win_y0 = 0;
+        l_tilec->win_x1 = 0;
+        l_tilec->win_y1 = 0;
+
         if (l_tilec->resolutions == 00) {
             l_tilec->resolutions = (opj_tcd_resolution_t *) opj_malloc(l_data_size);
             if (! l_tilec->resolutions) {
@@ -875,6 +885,28 @@ static INLINE OPJ_BOOL opj_tcd_init_tile(opj_tcd_t *p_tcd, OPJ_UINT32 p_tile_no,
             l_res->y0 = opj_int_ceildivpow2(l_tilec->y0, (OPJ_INT32)l_level_no);
             l_res->x1 = opj_int_ceildivpow2(l_tilec->x1, (OPJ_INT32)l_level_no);
             l_res->y1 = opj_int_ceildivpow2(l_tilec->y1, (OPJ_INT32)l_level_no);
+
+            if (!isEncoder && resno + 1 == l_tilec->minimum_num_resolutions) {
+                /* compute l_data_size with overflow check */
+                OPJ_UINT32 res_w = (OPJ_UINT32)(l_res->x1 - l_res->x0);
+                OPJ_UINT32 res_h = (OPJ_UINT32)(l_res->y1 - l_res->y0);
+
+                /* issue 733, l_data_size == 0U, probably something wrong should be checked before getting here */
+                if (res_h > 0 && res_h > (((OPJ_UINT32) - 1) / res_h)) {
+                    opj_event_msg(manager, EVT_ERROR, "Not enough memory for tile data\n");
+                    return OPJ_FALSE;
+                }
+                l_data_size = res_w * res_h;
+
+                if ((((OPJ_UINT32) - 1) / (OPJ_UINT32)sizeof(OPJ_UINT32)) < l_data_size) {
+                    opj_event_msg(manager, EVT_ERROR, "Not enough memory for tile data\n");
+                    return OPJ_FALSE;
+                }
+                l_data_size *= (OPJ_UINT32)sizeof(OPJ_UINT32);
+
+                l_tilec->data_size_needed = l_data_size;
+            }
+
             /*fprintf(stderr, "\t\t\tres_x0= %d, res_y0 =%d, res_x1=%d, res_y1=%d\n", l_res->x0, l_res->y0, l_res->x1, l_res->y1);*/
             /* p. 35, table A-23, ISO/IEC FDIS154444-1 : 2000 (18 august 2000) */
             l_pdx = l_tccp->prcw[resno];
@@ -1249,6 +1281,9 @@ static OPJ_BOOL opj_tcd_code_block_dec_allocate(opj_tcd_cblk_dec_t *
         OPJ_UINT32 l_numchunksalloc = p_code_block->numchunksalloc;
         OPJ_UINT32 i;
 
+        opj_free(p_code_block->decoded_data);
+        p_code_block->decoded_data = 00;
+
         memset(p_code_block, 0, sizeof(opj_tcd_cblk_dec_t));
         p_code_block->segs = l_segs;
         p_code_block->m_current_max_segs = l_current_max_segs;
@@ -1262,7 +1297,8 @@ static OPJ_BOOL opj_tcd_code_block_dec_allocate(opj_tcd_cblk_dec_t *
     return OPJ_TRUE;
 }
 
-OPJ_UINT32 opj_tcd_get_decoded_tile_size(opj_tcd_t *p_tcd)
+OPJ_UINT32 opj_tcd_get_decoded_tile_size(opj_tcd_t *p_tcd,
+        OPJ_BOOL take_into_account_partial_decoding)
 {
     OPJ_UINT32 i;
     OPJ_UINT32 l_data_size = 0;
@@ -1288,8 +1324,13 @@ OPJ_UINT32 opj_tcd_get_decoded_tile_size(opj_tcd_t *p_tcd)
         }
 
         l_res = l_tile_comp->resolutions + l_tile_comp->minimum_num_resolutions - 1;
-        l_temp = (OPJ_UINT32)((l_res->x1 - l_res->x0) * (l_res->y1 -
-                              l_res->y0)); /* x1*y1 can't overflow */
+        if (take_into_account_partial_decoding && !p_tcd->whole_tile_decoding) {
+            l_temp = (l_res->win_x1 - l_res->win_x0) *
+                     (l_res->win_y1 - l_res->win_y0);
+        } else {
+            l_temp = (OPJ_UINT32)((l_res->x1 - l_res->x0) * (l_res->y1 -
+                                  l_res->y0)); /* x1*y1 can't overflow */
+        }
         if (l_size_comp && UINT_MAX / l_size_comp < l_temp) {
             return UINT_MAX;
         }
@@ -1401,10 +1442,10 @@ OPJ_BOOL opj_tcd_encode_tile(opj_tcd_t *p_tcd,
 }
 
 OPJ_BOOL opj_tcd_decode_tile(opj_tcd_t *p_tcd,
-                             OPJ_UINT32 decoded_x0,
-                             OPJ_UINT32 decoded_y0,
-                             OPJ_UINT32 decoded_x1,
-                             OPJ_UINT32 decoded_y1,
+                             OPJ_UINT32 win_x0,
+                             OPJ_UINT32 win_y0,
+                             OPJ_UINT32 win_x1,
+                             OPJ_UINT32 win_y1,
                              OPJ_BYTE *p_src,
                              OPJ_UINT32 p_max_length,
                              OPJ_UINT32 p_tile_no,
@@ -1413,12 +1454,66 @@ OPJ_BOOL opj_tcd_decode_tile(opj_tcd_t *p_tcd,
                             )
 {
     OPJ_UINT32 l_data_read;
+    OPJ_UINT32 compno;
+
     p_tcd->tcd_tileno = p_tile_no;
     p_tcd->tcp = &(p_tcd->cp->tcps[p_tile_no]);
-    p_tcd->decoded_x0 = decoded_x0;
-    p_tcd->decoded_y0 = decoded_y0;
-    p_tcd->decoded_x1 = decoded_x1;
-    p_tcd->decoded_y1 = decoded_y1;
+    p_tcd->win_x0 = win_x0;
+    p_tcd->win_y0 = win_y0;
+    p_tcd->win_x1 = win_x1;
+    p_tcd->win_y1 = win_y1;
+    p_tcd->whole_tile_decoding = OPJ_TRUE;
+
+    for (compno = 0; compno < p_tcd->image->numcomps; compno++) {
+        if (!opj_tcd_is_whole_tilecomp_decoding(p_tcd, compno)) {
+            p_tcd->whole_tile_decoding = OPJ_FALSE;
+            break;
+        }
+    }
+
+    if (p_tcd->whole_tile_decoding) {
+        for (compno = 0; compno < p_tcd->image->numcomps; compno++) {
+            if (!opj_alloc_tile_component_data(&(p_tcd->tcd_image->tiles->comps[compno]))) {
+                opj_event_msg(p_manager, EVT_ERROR, "Not enough memory for tile data\n");
+                return OPJ_FALSE;
+            }
+        }
+    } else {
+        /* Compute restricted tile-component and tile-resolution coordinates */
+        /* of the window of interest, but defer the memory allocation until */
+        /* we know the resno_decoded */
+        for (compno = 0; compno < p_tcd->image->numcomps; compno++) {
+            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]);
+            /* Compute the intersection of the area of interest, expressed in tile coordinates */
+            /* with the tile coordinates */
+            tilec->win_x0 = opj_uint_max(
+                                (OPJ_UINT32)tilec->x0,
+                                opj_uint_ceildiv(p_tcd->win_x0, image_comp->dx));
+            tilec->win_y0 = opj_uint_max(
+                                (OPJ_UINT32)tilec->y0,
+                                opj_uint_ceildiv(p_tcd->win_y0, image_comp->dy));
+            tilec->win_x1 = opj_uint_min(
+                                (OPJ_UINT32)tilec->x1,
+                                opj_uint_ceildiv(p_tcd->win_x1, image_comp->dx));
+            tilec->win_y1 = opj_uint_min(
+                                (OPJ_UINT32)tilec->y1,
+                                opj_uint_ceildiv(p_tcd->win_y1, image_comp->dy));
+
+            for (resno = 0; resno < tilec->numresolutions; ++resno) {
+                opj_tcd_resolution_t *res = tilec->resolutions + resno;
+                res->win_x0 = opj_uint_ceildivpow2(tilec->win_x0,
+                                                   tilec->numresolutions - 1 - resno);
+                res->win_y0 = opj_uint_ceildivpow2(tilec->win_y0,
+                                                   tilec->numresolutions - 1 - resno);
+                res->win_x1 = opj_uint_ceildivpow2(tilec->win_x1,
+                                                   tilec->numresolutions - 1 - resno);
+                res->win_y1 = opj_uint_ceildivpow2(tilec->win_y1,
+                                                   tilec->numresolutions - 1 - resno);
+            }
+        }
+    }
 
 #ifdef TODO_MSD /* FIXME */
     /* INDEX >>  */
@@ -1461,6 +1556,42 @@ OPJ_BOOL opj_tcd_decode_tile(opj_tcd_t *p_tcd,
     }
     /* FIXME _ProfStop(PGROUP_T1); */
 
+
+    /* For subtile decoding, now we know the resno_decoded, we can allocate */
+    /* the tile data buffer */
+    if (!p_tcd->whole_tile_decoding) {
+        for (compno = 0; compno < p_tcd->image->numcomps; compno++) {
+            opj_tcd_tilecomp_t* tilec = &(p_tcd->tcd_image->tiles->comps[compno]);
+            opj_image_comp_t* image_comp = &(p_tcd->image->comps[compno]);
+            opj_tcd_resolution_t *res = tilec->resolutions + image_comp->resno_decoded;
+            OPJ_UINT32 w = res->win_x1 - res->win_x0;
+            OPJ_UINT32 h = res->win_y1 - res->win_y0;
+            OPJ_UINT32 l_data_size;
+
+            opj_aligned_free(tilec->data_win);
+            tilec->data_win = NULL;
+
+            if (w > 0 && h > 0) {
+                if (w > ((OPJ_UINT32) - 1) / h) {
+                    opj_event_msg(p_manager, EVT_ERROR, "Not enough memory for tile data\n");
+                    return OPJ_FALSE;
+                }
+                l_data_size = w * h;
+                if (l_data_size > ((OPJ_UINT32) - 1) / sizeof(OPJ_INT32)) {
+                    opj_event_msg(p_manager, EVT_ERROR, "Not enough memory for tile data\n");
+                    return OPJ_FALSE;
+                }
+                l_data_size *= (OPJ_UINT32)sizeof(OPJ_INT32);
+
+                tilec->data_win = opj_aligned_malloc(l_data_size);
+                if (tilec->data_win == NULL) {
+                    opj_event_msg(p_manager, EVT_ERROR, "Not enough memory for tile data\n");
+                    return OPJ_FALSE;
+                }
+            }
+        }
+    }
+
     /*----------------DWT---------------------*/
 
     /* FIXME _ProfStart(PGROUP_DWT); */
@@ -1502,7 +1633,7 @@ OPJ_BOOL opj_tcd_update_tile_data(opj_tcd_t *p_tcd,
     OPJ_UINT32 l_size_comp, l_remaining;
     OPJ_UINT32 l_stride, l_width, l_height;
 
-    l_data_size = opj_tcd_get_decoded_tile_size(p_tcd);
+    l_data_size = opj_tcd_get_decoded_tile_size(p_tcd, OPJ_TRUE);
     if (l_data_size == UINT_MAX || l_data_size > p_dest_length) {
         return OPJ_FALSE;
     }
@@ -1511,12 +1642,23 @@ OPJ_BOOL opj_tcd_update_tile_data(opj_tcd_t *p_tcd,
     l_img_comp = p_tcd->image->comps;
 
     for (i = 0; i < p_tcd->image->numcomps; ++i) {
+        const OPJ_INT32* l_src_data;
         l_size_comp = l_img_comp->prec >> 3; /*(/ 8)*/
         l_remaining = l_img_comp->prec & 7;  /* (%8) */
         l_res = l_tilec->resolutions + l_img_comp->resno_decoded;
-        l_width = (OPJ_UINT32)(l_res->x1 - l_res->x0);
-        l_height = (OPJ_UINT32)(l_res->y1 - l_res->y0);
-        l_stride = (OPJ_UINT32)(l_tilec->x1 - l_tilec->x0) - l_width;
+        if (p_tcd->whole_tile_decoding) {
+            l_width = (OPJ_UINT32)(l_res->x1 - l_res->x0);
+            l_height = (OPJ_UINT32)(l_res->y1 - l_res->y0);
+            l_stride = (OPJ_UINT32)(l_tilec->resolutions[l_tilec->minimum_num_resolutions -
+                                                                     1].x1 -
+                                    l_tilec->resolutions[l_tilec->minimum_num_resolutions - 1].x0) - l_width;
+            l_src_data = l_tilec->data;
+        } else {
+            l_width = l_res->win_x1 - l_res->win_x0;
+            l_height = l_res->win_y1 - l_res->win_y0;
+            l_stride = 0;
+            l_src_data = l_tilec->data_win;
+        }
 
         if (l_remaining) {
             ++l_size_comp;
@@ -1529,7 +1671,7 @@ OPJ_BOOL opj_tcd_update_tile_data(opj_tcd_t *p_tcd,
         switch (l_size_comp) {
         case 1: {
             OPJ_CHAR * l_dest_ptr = (OPJ_CHAR *) p_dest;
-            const OPJ_INT32 * l_src_ptr = l_tilec->data;
+            const OPJ_INT32 * l_src_ptr = l_src_data;
 
             if (l_img_comp->sgnd) {
                 for (j = 0; j < l_height; ++j) {
@@ -1551,7 +1693,7 @@ OPJ_BOOL opj_tcd_update_tile_data(opj_tcd_t *p_tcd,
         }
         break;
         case 2: {
-            const OPJ_INT32 * l_src_ptr = l_tilec->data;
+            const OPJ_INT32 * l_src_ptr = l_src_data;
             OPJ_INT16 * l_dest_ptr = (OPJ_INT16 *) p_dest;
 
             if (l_img_comp->sgnd) {
@@ -1579,7 +1721,7 @@ OPJ_BOOL opj_tcd_update_tile_data(opj_tcd_t *p_tcd,
         break;
         case 4: {
             OPJ_INT32 * l_dest_ptr = (OPJ_INT32 *) p_dest;
-            OPJ_INT32 * l_src_ptr = l_tilec->data;
+            const OPJ_INT32 * l_src_ptr = l_src_data;
 
             for (j = 0; j < l_height; ++j) {
                 memcpy(l_dest_ptr, l_src_ptr, l_width * sizeof(OPJ_INT32));
@@ -1674,6 +1816,9 @@ static void opj_tcd_free_tile(opj_tcd_t *p_tcd)
             l_tile_comp->data_size = 0;
             l_tile_comp->data_size_needed = 0;
         }
+
+        opj_aligned_free(l_tile_comp->data_win);
+
         ++l_tile_comp;
     }
 
@@ -1764,18 +1909,6 @@ static OPJ_BOOL opj_tcd_dwt_decode(opj_tcd_t *p_tcd)
     opj_image_comp_t * l_img_comp = p_tcd->image->comps;
 
     for (compno = 0; compno < l_tile->numcomps; compno++) {
-        /*
-        if (tcd->cp->reduce != 0) {
-                tcd->image->comps[compno].resno_decoded =
-                        tile->comps[compno].numresolutions - tcd->cp->reduce - 1;
-                if (tcd->image->comps[compno].resno_decoded < 0)
-                {
-                        return false;
-                }
-        }
-        numres2decode = tcd->image->comps[compno].resno_decoded + 1;
-        if(numres2decode > 0){
-        */
 
         if (l_tccp->qmfbid == 1) {
             if (! opj_dwt_decode(p_tcd, l_tile_comp,
@@ -1796,6 +1929,7 @@ static OPJ_BOOL opj_tcd_dwt_decode(opj_tcd_t *p_tcd)
 
     return OPJ_TRUE;
 }
+
 static OPJ_BOOL opj_tcd_mct_decode(opj_tcd_t *p_tcd, opj_event_mgr_t *p_manager)
 {
     opj_tcd_tile_t * l_tile = p_tcd->tcd_image->tiles;
@@ -1807,17 +1941,40 @@ static OPJ_BOOL opj_tcd_mct_decode(opj_tcd_t *p_tcd, opj_event_mgr_t *p_manager)
         return OPJ_TRUE;
     }
 
-    l_samples = (OPJ_UINT32)((l_tile_comp->x1 - l_tile_comp->x0) *
-                             (l_tile_comp->y1 - l_tile_comp->y0));
+    if (p_tcd->whole_tile_decoding) {
+        /* A bit inefficient: we process more data than needed if */
+        /* resno_decoded < l_tile_comp->minimum_num_resolutions-1, */
+        /* but we would need to take into account a stride then */
+        l_samples = (OPJ_UINT32)((
+                                     l_tile_comp->resolutions[l_tile_comp->minimum_num_resolutions - 1].x1 -
+                                     l_tile_comp->resolutions[l_tile_comp->minimum_num_resolutions - 1].x0) *
+                                 (l_tile_comp->resolutions[l_tile_comp->minimum_num_resolutions - 1].y1 -
+                                  l_tile_comp->resolutions[l_tile_comp->minimum_num_resolutions - 1].y0));
+    } else {
+        opj_tcd_resolution_t* l_res;
+        l_res = l_tile_comp->resolutions + p_tcd->image->comps[0].resno_decoded;
+        l_samples = (l_res->win_x1 - l_res->win_x0) *
+                    (l_res->win_y1 - l_res->win_y0);
+    }
 
     if (l_tile->numcomps >= 3) {
+        opj_tcd_resolution_t* res_comp0 = l_tile->comps[0].resolutions +
+                                          p_tcd->image->comps[0].resno_decoded;
+        opj_tcd_resolution_t* res_comp1 = l_tile->comps[1].resolutions +
+                                          p_tcd->image->comps[1].resno_decoded;
+        opj_tcd_resolution_t* res_comp2 = l_tile->comps[2].resolutions +
+                                          p_tcd->image->comps[2].resno_decoded;
+        OPJ_INT32 l_res_samples = (OPJ_INT32)(res_comp0->x1 - res_comp0->x0) *
+                                  (res_comp0->y1 - res_comp0->y0);
         /* testcase 1336.pdf.asan.47.376 */
-        if ((l_tile->comps[0].x1 - l_tile->comps[0].x0) * (l_tile->comps[0].y1 -
-                l_tile->comps[0].y0) < (OPJ_INT32)l_samples ||
-                (l_tile->comps[1].x1 - l_tile->comps[1].x0) * (l_tile->comps[1].y1 -
-                        l_tile->comps[1].y0) < (OPJ_INT32)l_samples ||
-                (l_tile->comps[2].x1 - l_tile->comps[2].x0) * (l_tile->comps[2].y1 -
-                        l_tile->comps[2].y0) < (OPJ_INT32)l_samples) {
+        if (p_tcd->image->comps[0].resno_decoded !=
+                p_tcd->image->comps[1].resno_decoded ||
+                p_tcd->image->comps[0].resno_decoded !=
+                p_tcd->image->comps[2].resno_decoded ||
+                (res_comp1->x1 - res_comp1->x0) * (res_comp1->y1 -
+                        res_comp1->y0) != l_res_samples ||
+                (res_comp2->x1 - res_comp2->x0) * (res_comp2->y1 -
+                        res_comp2->y0) != l_res_samples) {
             opj_event_msg(p_manager, EVT_ERROR,
                           "Tiles don't all have the same dimension. Skip the MCT step.\n");
             return OPJ_FALSE;
@@ -1834,7 +1991,11 @@ static OPJ_BOOL opj_tcd_mct_decode(opj_tcd_t *p_tcd, opj_event_mgr_t *p_manager)
             }
 
             for (i = 0; i < l_tile->numcomps; ++i) {
-                l_data[i] = (OPJ_BYTE*) l_tile_comp->data;
+                if (p_tcd->whole_tile_decoding) {
+                    l_data[i] = (OPJ_BYTE*) l_tile_comp->data;
+                } else {
+                    l_data[i] = (OPJ_BYTE*) l_tile_comp->data_win;
+                }
                 ++l_tile_comp;
             }
 
@@ -1855,15 +2016,29 @@ static OPJ_BOOL opj_tcd_mct_decode(opj_tcd_t *p_tcd, opj_event_mgr_t *p_manager)
             opj_free(l_data);
         } else {
             if (l_tcp->tccps->qmfbid == 1) {
-                opj_mct_decode(l_tile->comps[0].data,
-                               l_tile->comps[1].data,
-                               l_tile->comps[2].data,
-                               l_samples);
+                if (p_tcd->whole_tile_decoding) {
+                    opj_mct_decode(l_tile->comps[0].data,
+                                   l_tile->comps[1].data,
+                                   l_tile->comps[2].data,
+                                   l_samples);
+                } else {
+                    opj_mct_decode(l_tile->comps[0].data_win,
+                                   l_tile->comps[1].data_win,
+                                   l_tile->comps[2].data_win,
+                                   l_samples);
+                }
             } else {
-                opj_mct_decode_real((OPJ_FLOAT32*)l_tile->comps[0].data,
-                                    (OPJ_FLOAT32*)l_tile->comps[1].data,
-                                    (OPJ_FLOAT32*)l_tile->comps[2].data,
-                                    l_samples);
+                if (p_tcd->whole_tile_decoding) {
+                    opj_mct_decode_real((OPJ_FLOAT32*)l_tile->comps[0].data,
+                                        (OPJ_FLOAT32*)l_tile->comps[1].data,
+                                        (OPJ_FLOAT32*)l_tile->comps[2].data,
+                                        l_samples);
+                } else {
+                    opj_mct_decode_real((OPJ_FLOAT32*)l_tile->comps[0].data_win,
+                                        (OPJ_FLOAT32*)l_tile->comps[1].data_win,
+                                        (OPJ_FLOAT32*)l_tile->comps[2].data_win,
+                                        l_samples);
+                }
             }
         }
     } else {
@@ -1896,12 +2071,24 @@ static OPJ_BOOL opj_tcd_dc_level_shift_decode(opj_tcd_t *p_tcd)
 
     for (compno = 0; compno < l_tile->numcomps; compno++) {
         l_res = l_tile_comp->resolutions + l_img_comp->resno_decoded;
-        l_width = (OPJ_UINT32)(l_res->x1 - l_res->x0);
-        l_height = (OPJ_UINT32)(l_res->y1 - l_res->y0);
-        l_stride = (OPJ_UINT32)(l_tile_comp->x1 - l_tile_comp->x0) - l_width;
 
-        assert(l_height == 0 ||
-               l_width + l_stride <= l_tile_comp->data_size / l_height); /*MUPDF*/
+        if (!p_tcd->whole_tile_decoding) {
+            l_width = l_res->win_x1 - l_res->win_x0;
+            l_height = l_res->win_y1 - l_res->win_y0;
+            l_stride = 0;
+            l_current_ptr = l_tile_comp->data_win;
+        } else {
+            l_width = (OPJ_UINT32)(l_res->x1 - l_res->x0);
+            l_height = (OPJ_UINT32)(l_res->y1 - l_res->y0);
+            l_stride = (OPJ_UINT32)(
+                           l_tile_comp->resolutions[l_tile_comp->minimum_num_resolutions - 1].x1 -
+                           l_tile_comp->resolutions[l_tile_comp->minimum_num_resolutions - 1].x0)
+                       - l_width;
+            l_current_ptr = l_tile_comp->data;
+
+            assert(l_height == 0 ||
+                   l_width + l_stride <= l_tile_comp->data_size / l_height); /*MUPDF*/
+        }
 
         if (l_img_comp->sgnd) {
             l_min = -(1 << (l_img_comp->prec - 1));
@@ -1911,7 +2098,6 @@ static OPJ_BOOL opj_tcd_dc_level_shift_decode(opj_tcd_t *p_tcd)
             l_max = (OPJ_INT32)((1U << l_img_comp->prec) - 1);
         }
 
-        l_current_ptr = l_tile_comp->data;
 
         if (l_tccp->qmfbid == 1) {
             for (j = 0; j < l_height; ++j) {
@@ -1981,6 +2167,9 @@ static void opj_tcd_code_block_dec_deallocate(opj_tcd_precinct_t * p_precinct)
                 l_code_block->chunks = 00;
             }
 
+            opj_free(l_code_block->decoded_data);
+            l_code_block->decoded_data = NULL;
+
             ++l_code_block;
         }
 
@@ -2396,16 +2585,16 @@ OPJ_BOOL opj_tcd_is_subband_area_of_interest(opj_tcd_t *tcd,
     /* with the tile coordinates */
     OPJ_UINT32 tcx0 = opj_uint_max(
                           (OPJ_UINT32)tilec->x0,
-                          opj_uint_ceildiv(tcd->decoded_x0, image_comp->dx));
+                          opj_uint_ceildiv(tcd->win_x0, image_comp->dx));
     OPJ_UINT32 tcy0 = opj_uint_max(
                           (OPJ_UINT32)tilec->y0,
-                          opj_uint_ceildiv(tcd->decoded_y0, image_comp->dy));
+                          opj_uint_ceildiv(tcd->win_y0, image_comp->dy));
     OPJ_UINT32 tcx1 = opj_uint_min(
                           (OPJ_UINT32)tilec->x1,
-                          opj_uint_ceildiv(tcd->decoded_x1, image_comp->dx));
+                          opj_uint_ceildiv(tcd->win_x1, image_comp->dx));
     OPJ_UINT32 tcy1 = opj_uint_min(
                           (OPJ_UINT32)tilec->y1,
-                          opj_uint_ceildiv(tcd->decoded_y1, image_comp->dy));
+                          opj_uint_ceildiv(tcd->win_y1, image_comp->dy));
     /* Compute number of decomposition for this band. See table F-1 */
     OPJ_UINT32 nb = (resno == 0) ?
                     tilec->numresolutions - 1 :
@@ -2452,3 +2641,44 @@ OPJ_BOOL opj_tcd_is_subband_area_of_interest(opj_tcd_t *tcd,
 #endif
     return intersects;
 }
+
+/** Returns whether a tile componenent is fully decoded, taking into account
+ * p_tcd->win_* members.
+ *
+ * @param p_tcd    TCD handle.
+ * @param compno Component number
+ * @return OPJ_TRUE whether the tile componenent is fully decoded
+ */
+static OPJ_BOOL opj_tcd_is_whole_tilecomp_decoding(opj_tcd_t *p_tcd,
+        OPJ_UINT32 compno)
+{
+    opj_tcd_tilecomp_t* tilec = &(p_tcd->tcd_image->tiles->comps[compno]);
+    opj_image_comp_t* image_comp = &(p_tcd->image->comps[compno]);
+    /* Compute the intersection of the area of interest, expressed in tile coordinates */
+    /* with the tile coordinates */
+    OPJ_UINT32 tcx0 = opj_uint_max(
+                          (OPJ_UINT32)tilec->x0,
+                          opj_uint_ceildiv(p_tcd->win_x0, image_comp->dx));
+    OPJ_UINT32 tcy0 = opj_uint_max(
+                          (OPJ_UINT32)tilec->y0,
+                          opj_uint_ceildiv(p_tcd->win_y0, image_comp->dy));
+    OPJ_UINT32 tcx1 = opj_uint_min(
+                          (OPJ_UINT32)tilec->x1,
+                          opj_uint_ceildiv(p_tcd->win_x1, image_comp->dx));
+    OPJ_UINT32 tcy1 = opj_uint_min(
+                          (OPJ_UINT32)tilec->y1,
+                          opj_uint_ceildiv(p_tcd->win_y1, image_comp->dy));
+
+    OPJ_UINT32 shift = tilec->numresolutions - tilec->minimum_num_resolutions;
+    /* Tolerate small margin within the reduced resolution factor to consider if */
+    /* the whole tile path must be taken */
+    return (tcx0 >= (OPJ_UINT32)tilec->x0 &&
+            tcy0 >= (OPJ_UINT32)tilec->y0 &&
+            tcx1 <= (OPJ_UINT32)tilec->x1 &&
+            tcy1 <= (OPJ_UINT32)tilec->y1 &&
+            (shift >= 32 ||
+             (((tcx0 - (OPJ_UINT32)tilec->x0) >> shift) == 0 &&
+              ((tcy0 - (OPJ_UINT32)tilec->y0) >> shift) == 0 &&
+              (((OPJ_UINT32)tilec->x1 - tcx1) >> shift) == 0 &&
+              (((OPJ_UINT32)tilec->y1 - tcy1) >> shift) == 0)));
+}