Sub-tile decoding: only decode precincts and codeblocks that intersect the window...
authorEven Rouault <even.rouault@spatialys.com>
Mon, 14 Aug 2017 11:23:57 +0000 (13:23 +0200)
committerEven Rouault <even.rouault@spatialys.com>
Thu, 17 Aug 2017 17:05:54 +0000 (19:05 +0200)
src/lib/openjp2/j2k.c
src/lib/openjp2/t1.c
src/lib/openjp2/t1.h
src/lib/openjp2/t2.c
src/lib/openjp2/t2.h
src/lib/openjp2/tcd.c
src/lib/openjp2/tcd.h
tests/CMakeLists.txt
tests/test_decode_area.c [new file with mode: 0644]
tests/test_tile_encoder.c

index 6c1f55f47faa84c558c073c5df0290c5a58e8d72..fe40e29f18aea4d8ef0a5afa5f64882e65a7df9e 100644 (file)
@@ -16,6 +16,7 @@
  * Copyright (c) 2010-2011, Kaori Hagihara
  * Copyright (c) 2011-2012, Centre National d'Etudes Spatiales (CNES), France
  * Copyright (c) 2012, CS Systemes d'Information, France
+ * Copyright (c) 2017, IntoPIX SA <support@intopix.com>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -5162,7 +5163,13 @@ static OPJ_BOOL opj_j2k_update_rates(opj_j2k_t *p_j2k,
         ++l_img_comp;
     }
 
-    l_tile_size = (OPJ_UINT32)(l_tile_size * 0.1625);  /* 1.3/8 = 0.1625 */
+    /* TODO: where does this magic value come from ? */
+    /* This used to be 1.3 / 8, but with random data and very small code */
+    /* block sizes, this is not enough. For example with */
+    /* bin/test_tile_encoder 1 256 256 32 32 8 0 reversible_with_precinct.j2k 4 4 3 0 0 1 16 16 */
+    /* TODO revise this to take into account the overhead linked to the */
+    /* number of packets and number of code blocks in packets */
+    l_tile_size = (OPJ_UINT32)(l_tile_size * 1.4 / 8);
 
     l_tile_size += opj_j2k_get_specific_header_sizes(p_j2k);
 
@@ -8770,6 +8777,7 @@ OPJ_BOOL opj_j2k_decode_tile(opj_j2k_t * p_j2k,
     OPJ_UINT32 l_current_marker;
     OPJ_BYTE l_data [2];
     opj_tcp_t * l_tcp;
+    opj_image_t* l_image_for_bounds;
 
     /* preconditions */
     assert(p_stream != 00);
@@ -8787,7 +8795,18 @@ OPJ_BOOL opj_j2k_decode_tile(opj_j2k_t * p_j2k,
         return OPJ_FALSE;
     }
 
+    /* When using the opj_read_tile_header / opj_decode_tile_data API */
+    /* such as in test_tile_decoder, m_output_image is NULL, so fall back */
+    /* to the full image dimension. This is a bit surprising that */
+    /* opj_set_decode_area() is only used to determinte intersecting tiles, */
+    /* but full tile decoding is done */
+    l_image_for_bounds = p_j2k->m_output_image ? p_j2k->m_output_image :
+                         p_j2k->m_private_image;
     if (! opj_tcd_decode_tile(p_j2k->m_tcd,
+                              l_image_for_bounds->x0,
+                              l_image_for_bounds->y0,
+                              l_image_for_bounds->x1,
+                              l_image_for_bounds->y1,
                               l_tcp->m_data,
                               l_tcp->m_data_size,
                               p_tile_index,
index eb5d6e0e36e19d71b05fab97af4ad618d64367fa..af6732b38969f6afa6a174771ca6ff65f4b62934 100644 (file)
@@ -1751,7 +1751,7 @@ static void opj_t1_clbl_decode_processor(void* user_data, opj_tls_t* tls)
 }
 
 
-void opj_t1_decode_cblks(opj_thread_pool_t* tp,
+void opj_t1_decode_cblks(opj_tcd_t* tcd,
                          volatile OPJ_BOOL* pret,
                          opj_tcd_tilecomp_t* tilec,
                          opj_tccp_t* tccp,
@@ -1760,6 +1760,7 @@ void opj_t1_decode_cblks(opj_thread_pool_t* tp,
                          OPJ_BOOL check_pterm
                         )
 {
+    opj_thread_pool_t* tp = tcd->thread_pool;
     OPJ_UINT32 resno, bandno, precno, cblkno;
 
     for (resno = 0; resno < tilec->minimum_num_resolutions; ++resno) {
@@ -1775,6 +1776,17 @@ void opj_t1_decode_cblks(opj_thread_pool_t* tp,
                     opj_tcd_cblk_dec_t* cblk = &precinct->cblks.dec[cblkno];
                     opj_t1_cblk_decode_processing_job_t* job;
 
+                    if (!opj_tcd_is_subband_area_of_interest(tcd,
+                            tilec->compno,
+                            resno,
+                            band->bandno,
+                            (OPJ_UINT32)cblk->x0,
+                            (OPJ_UINT32)cblk->y0,
+                            (OPJ_UINT32)cblk->x1,
+                            (OPJ_UINT32)cblk->y1)) {
+                        continue;
+                    }
+
                     job = (opj_t1_cblk_decode_processing_job_t*) opj_calloc(1,
                             sizeof(opj_t1_cblk_decode_processing_job_t));
                     if (!job) {
index 5aa6a07bf213265e3c6f55effebb00fa4c5fef7a..171dfb0a7ae57e5f874f74c6967d80b628b6316e 100644 (file)
@@ -230,7 +230,7 @@ OPJ_BOOL opj_t1_encode_cblks(opj_t1_t *t1,
 
 /**
 Decode the code-blocks of a tile
-@param tp Thread pool
+@param tcd TCD handle
 @param pret Pointer to return value
 @param tilec The tile to decode
 @param tccp Tile coding parameters
@@ -238,7 +238,7 @@ Decode the code-blocks of a tile
 @param p_manager_mutex mutex for the event manager
 @param check_pterm whether PTERM correct termination should be checked
 */
-void opj_t1_decode_cblks(opj_thread_pool_t* tp,
+void opj_t1_decode_cblks(opj_tcd_t* tcd,
                          volatile OPJ_BOOL* pret,
                          opj_tcd_tilecomp_t* tilec,
                          opj_tccp_t* tccp,
index 0fd5300c69b3649b9f31c21d0bf6f799bf45f39f..6f956d1c7f6f988d243a86f64c34f2e8020c7f5e 100644 (file)
@@ -13,6 +13,7 @@
  * Copyright (c) 2005, Herve Drolon, FreeImage Team
  * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
  * Copyright (c) 2012, CS Systemes d'Information, France
+ * Copyright (c) 2017, IntoPIX SA <support@intopix.com>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -369,7 +370,8 @@ static void opj_null_jas_fprintf(FILE* file, const char * format, ...)
 #define JAS_FPRINTF opj_null_jas_fprintf
 #endif
 
-OPJ_BOOL opj_t2_decode_packets(opj_t2_t *p_t2,
+OPJ_BOOL opj_t2_decode_packets(opj_tcd_t* tcd,
+                               opj_t2_t *p_t2,
                                OPJ_UINT32 p_tile_no,
                                opj_tcd_tile_t *p_tile,
                                OPJ_BYTE *p_src,
@@ -434,14 +436,54 @@ OPJ_BOOL opj_t2_decode_packets(opj_t2_t *p_t2,
         memset(first_pass_failed, OPJ_TRUE, l_image->numcomps * sizeof(OPJ_BOOL));
 
         while (opj_pi_next(l_current_pi)) {
+            OPJ_BOOL skip_packet = OPJ_FALSE;
             JAS_FPRINTF(stderr,
                         "packet offset=00000166 prg=%d cmptno=%02d rlvlno=%02d prcno=%03d lyrno=%02d\n\n",
                         l_current_pi->poc.prg1, l_current_pi->compno, l_current_pi->resno,
                         l_current_pi->precno, l_current_pi->layno);
 
-            if (l_tcp->num_layers_to_decode > l_current_pi->layno
-                    && l_current_pi->resno <
-                    p_tile->comps[l_current_pi->compno].minimum_num_resolutions) {
+            /* If the packet layer is greater or equal than the maximum */
+            /* number of layers, skip the packet */
+            if (l_current_pi->layno >= l_tcp->num_layers_to_decode) {
+                skip_packet = OPJ_TRUE;
+            }
+            /* If the packet resolution number is greater than the minimum */
+            /* number of resolution allowed, skip the packet */
+            else if (l_current_pi->resno >=
+                     p_tile->comps[l_current_pi->compno].minimum_num_resolutions) {
+                skip_packet = OPJ_TRUE;
+            } else {
+                /* If no precincts of any band intersects the area of interest, */
+                /* skip the packet */
+                OPJ_UINT32 bandno;
+                opj_tcd_tilecomp_t *tilec = &p_tile->comps[l_current_pi->compno];
+                opj_tcd_resolution_t *res = &tilec->resolutions[l_current_pi->resno];
+
+                skip_packet = OPJ_TRUE;
+                for (bandno = 0; bandno < res->numbands; ++bandno) {
+                    opj_tcd_band_t* band = &res->bands[bandno];
+                    opj_tcd_precinct_t* prec = &band->precincts[l_current_pi->precno];
+
+                    if (opj_tcd_is_subband_area_of_interest(tcd,
+                                                            l_current_pi->compno,
+                                                            l_current_pi->resno,
+                                                            band->bandno,
+                                                            (OPJ_UINT32)prec->x0,
+                                                            (OPJ_UINT32)prec->y0,
+                                                            (OPJ_UINT32)prec->x1,
+                                                            (OPJ_UINT32)prec->y1)) {
+                        skip_packet = OPJ_FALSE;
+                        break;
+                    }
+                }
+                /*
+                                printf("packet cmptno=%02d rlvlno=%02d prcno=%03d lyrno=%02d -> %s\n",
+                                    l_current_pi->compno, l_current_pi->resno,
+                                    l_current_pi->precno, l_current_pi->layno, skip_packet ? "skipped" : "kept");
+                */
+            }
+
+            if (!skip_packet) {
                 l_nb_bytes_read = 0;
 
                 first_pass_failed[l_current_pi->compno] = OPJ_FALSE;
index 111aa2de30ab58b7486445394f180c85dda53e9c..66500b1699334d7752f9ad86eec672379028105b 100644 (file)
@@ -13,6 +13,7 @@
  * Copyright (c) 2005, Herve Drolon, FreeImage Team
  * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
  * Copyright (c) 2012, CS Systemes d'Information, France
+ * Copyright (c) 2017, IntoPIX SA <support@intopix.com>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -94,6 +95,7 @@ OPJ_BOOL opj_t2_encode_packets(opj_t2_t* t2,
 
 /**
 Decode the packets of a tile from a source buffer
+@param tcd TCD handle
 @param t2 T2 handle
 @param tileno number that identifies the tile for which to decode the packets
 @param tile tile for which to decode the packets
@@ -105,7 +107,8 @@ Decode the packets of a tile from a source buffer
 
 @return FIXME DOC
  */
-OPJ_BOOL opj_t2_decode_packets(opj_t2_t *t2,
+OPJ_BOOL opj_t2_decode_packets(opj_tcd_t* tcd,
+                               opj_t2_t *t2,
                                OPJ_UINT32 tileno,
                                opj_tcd_tile_t *tile,
                                OPJ_BYTE *src,
index 53cdcf64d7f87ef2ba2ca14137be9875b81fe8dc..f90816fcf48af40289ca1560cd4aeacb49ca3003 100644 (file)
@@ -14,6 +14,7 @@
  * Copyright (c) 2006-2007, Parvatha Elangovan
  * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
  * Copyright (c) 2012, CS Systemes d'Information, France
+ * Copyright (c) 2017, IntoPIX SA <support@intopix.com>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -790,6 +791,7 @@ static INLINE OPJ_BOOL opj_tcd_init_tile(opj_tcd_t *p_tcd, OPJ_UINT32 p_tile_no,
         l_tilec->y0 = opj_int_ceildiv(l_tile->y0, (OPJ_INT32)l_image_comp->dy);
         l_tilec->x1 = opj_int_ceildiv(l_tile->x1, (OPJ_INT32)l_image_comp->dx);
         l_tilec->y1 = opj_int_ceildiv(l_tile->y1, (OPJ_INT32)l_image_comp->dy);
+        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 */
@@ -1399,6 +1401,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_BYTE *p_src,
                              OPJ_UINT32 p_max_length,
                              OPJ_UINT32 p_tile_no,
@@ -1409,6 +1415,10 @@ OPJ_BOOL opj_tcd_decode_tile(opj_tcd_t *p_tcd,
     OPJ_UINT32 l_data_read;
     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;
 
 #ifdef TODO_MSD /* FIXME */
     /* INDEX >>  */
@@ -1690,6 +1700,7 @@ static OPJ_BOOL opj_tcd_t2_decode(opj_tcd_t *p_tcd,
     }
 
     if (! opj_t2_decode_packets(
+                p_tcd,
                 l_t2,
                 p_tcd->tcd_tileno,
                 p_tcd->tcd_image->tiles,
@@ -1727,7 +1738,7 @@ static OPJ_BOOL opj_tcd_t1_decode(opj_tcd_t *p_tcd, opj_event_mgr_t *p_manager)
     }
 
     for (compno = 0; compno < l_tile->numcomps; ++compno) {
-        opj_t1_decode_cblks(p_tcd->thread_pool, &ret, l_tile_comp, l_tccp,
+        opj_t1_decode_cblks(p_tcd, &ret, l_tile_comp, l_tccp,
                             p_manager, p_manager_mutex, check_pterm);
         if (!ret) {
             break;
@@ -2359,3 +2370,72 @@ OPJ_BOOL opj_tcd_is_band_empty(opj_tcd_band_t* band)
 {
     return (band->x1 - band->x0 == 0) || (band->y1 - band->y0 == 0);
 }
+
+OPJ_BOOL opj_tcd_is_subband_area_of_interest(opj_tcd_t *tcd,
+        OPJ_UINT32 compno,
+        OPJ_UINT32 resno,
+        OPJ_UINT32 bandno,
+        OPJ_UINT32 band_x0,
+        OPJ_UINT32 band_y0,
+        OPJ_UINT32 band_x1,
+        OPJ_UINT32 band_y1)
+{
+    OPJ_UINT32 filter_margin = (tcd->tcp->tccps[compno].qmfbid == 1) ? 2 : 3;
+    opj_tcd_tilecomp_t *tilec = &(tcd->tcd_image->tiles->comps[compno]);
+    opj_image_comp_t* image_comp = &(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(tcd->decoded_x0, image_comp->dx));
+    OPJ_UINT32 tcy0 = opj_uint_max(
+                          (OPJ_UINT32)tilec->y0,
+                          opj_uint_ceildiv(tcd->decoded_y0, image_comp->dy));
+    OPJ_UINT32 tcx1 = opj_uint_min(
+                          (OPJ_UINT32)tilec->x1,
+                          opj_uint_ceildiv(tcd->decoded_x1, image_comp->dx));
+    OPJ_UINT32 tcy1 = opj_uint_min(
+                          (OPJ_UINT32)tilec->y1,
+                          opj_uint_ceildiv(tcd->decoded_y1, image_comp->dy));
+    /* Compute number of decomposition for this band. See table F-1 */
+    OPJ_UINT32 nb = (resno == 0) ?
+                    tilec->numresolutions - 1 :
+                    tilec->numresolutions - resno;
+    /* Map above tile-based coordinates to sub-band-based coordinates per */
+    /* equation B-15 of the standard */
+    OPJ_UINT32 x0b = bandno & 1;
+    OPJ_UINT32 y0b = bandno >> 1;
+    OPJ_UINT32 tbx0 = (nb == 0) ? tcx0 : opj_uint_ceildiv(tcx0 - (1U <<
+                      (nb - 1)) * x0b, 1U << nb);
+    OPJ_UINT32 tby0 = (nb == 0) ? tcy0 : opj_uint_ceildiv(tcy0 - (1U <<
+                      (nb - 1)) * y0b, 1U << nb);
+    OPJ_UINT32 tbx1 = (nb == 0) ? tcx1 : opj_uint_ceildiv(tcx1 - (1U <<
+                      (nb - 1)) * x0b, 1U << nb);
+    OPJ_UINT32 tby1 = (nb == 0) ? tcy1 : opj_uint_ceildiv(tcy1 - (1U <<
+                      (nb - 1)) * y0b, 1U << nb);
+    OPJ_BOOL intersects;
+
+    if (tbx0 < filter_margin) {
+        tbx0 = 0;
+    } else {
+        tbx0 -= filter_margin;
+    }
+    if (tby0 < filter_margin) {
+        tby0 = 0;
+    } else {
+        tby0 -= filter_margin;
+    }
+    tbx1 = opj_uint_adds(tbx1, filter_margin);
+    tby1 = opj_uint_adds(tby1, filter_margin);
+
+    intersects = band_x0 < tbx1 && band_y0 < tby1 && band_x1 > tbx0 &&
+                 band_y1 > tby0;
+
+#ifdef DEBUG_VERBOSE
+    printf("compno=%u resno=%u nb=%u bandno=%u x0b=%u y0b=%u band=%u,%u,%u,%u tb=%u,%u,%u,%u -> %u\n",
+           compno, resno, nb, bandno, x0b, y0b,
+           band_x0, band_y0, band_x1, band_y1,
+           tbx0, tby0, tbx1, tby1, intersects);
+#endif
+    return intersects;
+}
index ff3f042cd6a975d8de323e41bddecc653ed6eed2..bf645639f4bc9bfe1c278d6e2c3e3acea65ee322 100644 (file)
@@ -13,6 +13,7 @@
  * Copyright (c) 2005, Herve Drolon, FreeImage Team
  * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR
  * Copyright (c) 2012, CS Systemes d'Information, France
+ * Copyright (c) 2017, IntoPIX SA <support@intopix.com>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -180,6 +181,8 @@ typedef struct opj_tcd_resolution {
 typedef struct opj_tcd_tilecomp {
     /* dimension of component : left upper corner (x0, y0) right low corner (x1,y1) */
     OPJ_INT32 x0, y0, x1, y1;
+    /* component number */
+    OPJ_UINT32 compno;
     /* number of resolutions level */
     OPJ_UINT32 numresolutions;
     /* number of resolutions level to decode (at max)*/
@@ -252,6 +255,10 @@ typedef struct opj_tcd {
     OPJ_BITFIELD m_is_decoder : 1;
     /** Thread pool */
     opj_thread_pool_t* thread_pool;
+    OPJ_UINT32 decoded_x0;
+    OPJ_UINT32 decoded_y0;
+    OPJ_UINT32 decoded_x1;
+    OPJ_UINT32 decoded_y1;
 } opj_tcd_t;
 
 /** @name Exported functions */
@@ -348,6 +355,10 @@ OPJ_BOOL opj_tcd_encode_tile(opj_tcd_t *p_tcd,
 /**
 Decode a tile from a buffer into a raw image
 @param tcd TCD handle
+@param decoded_x0 Upper left x of region to decode (in grid coordinates)
+@param decoded_y0 Upper left y of region to decode (in grid coordinates)
+@param decoded_x1 Lower right x of region to decode (in grid coordinates)
+@param decoded_y1 Lower right y of region to decode (in grid coordinates)
 @param src Source buffer
 @param len Length of source buffer
 @param tileno Number that identifies one of the tiles to be decoded
@@ -355,6 +366,10 @@ Decode a tile from a buffer into a raw image
 @param manager the event manager.
 */
 OPJ_BOOL opj_tcd_decode_tile(opj_tcd_t *tcd,
+                             OPJ_UINT32 decoded_x0,
+                             OPJ_UINT32 decoded_y0,
+                             OPJ_UINT32 decoded_x1,
+                             OPJ_UINT32 decoded_y1,
                              OPJ_BYTE *src,
                              OPJ_UINT32 len,
                              OPJ_UINT32 tileno,
@@ -409,6 +424,31 @@ OPJ_BOOL opj_tcd_is_band_empty(opj_tcd_band_t* band);
 /** Reinitialize a segment */
 void opj_tcd_reinit_segment(opj_tcd_seg_t* seg);
 
+
+/** Returns whether a sub-band region contributes to the area of interest
+ * tcd->decoded_x0,tcd->decoded_y0,tcd->decoded_x1,tcd->decoded_y1.
+ *
+ * @param tcd    TCD handle.
+ * @param compno Component number
+ * @param resno  Resolution number
+ * @param bandno Band number (*not* band index, ie 0, 1, 2 or 3)
+ * @param x0     Upper left x in subband coordinates
+ * @param y0     Upper left y in subband coordinates
+ * @param x1     Lower right x in subband coordinates
+ * @param y1     Lower right y in subband coordinates
+ * @return OPJ_TRUE whether the sub-band region contributs to the area of
+ *                  interest.
+ */
+OPJ_BOOL opj_tcd_is_subband_area_of_interest(opj_tcd_t *tcd,
+        OPJ_UINT32 compno,
+        OPJ_UINT32 resno,
+        OPJ_UINT32 bandno,
+        OPJ_UINT32 x0,
+        OPJ_UINT32 y0,
+        OPJ_UINT32 x1,
+        OPJ_UINT32 y1);
+
+
 /* ----------------------------------------------------------------------- */
 /*@}*/
 
index 7b107d729a9d99fafa363e13ab00f0f20a9369b0..aa8f9446d25bdcb3e008199e8084e4f816d6367e 100644 (file)
@@ -45,6 +45,9 @@ add_executable(compare_raw_files ${compare_raw_files_SRCS})
 add_executable(test_tile_encoder test_tile_encoder.c)
 target_link_libraries(test_tile_encoder ${OPENJPEG_LIBRARY_NAME})
 
+add_executable(test_decode_area test_decode_area.c)
+target_link_libraries(test_decode_area ${OPENJPEG_LIBRARY_NAME})
+
 # Let's try a couple of possibilities:
 add_test(NAME tte0 COMMAND test_tile_encoder)
 add_test(NAME tte1 COMMAND test_tile_encoder 3 2048 2048 1024 1024 8 1 tte1.j2k)
@@ -80,6 +83,18 @@ set_property(TEST rta4 APPEND PROPERTY DEPENDS tte4)
 add_test(NAME rta5 COMMAND j2k_random_tile_access tte5.j2k)
 set_property(TEST rta5 APPEND PROPERTY DEPENDS tte5)
 
+add_test(NAME tda_prep_reversible_no_precinct COMMAND test_tile_encoder 1 256 256 32 32 8 0 reversible_no_precinct.j2k 4 4 3 0 0 1)
+add_test(NAME tda_reversible_no_precinct COMMAND test_decode_area -q reversible_no_precinct.j2k)
+set_property(TEST tda_reversible_no_precinct APPEND PROPERTY DEPENDS tda_prep_reversible_no_precinct)
+
+add_test(NAME tda_prep_reversible_with_precinct COMMAND test_tile_encoder 1 256 256 32 32 8 0 reversible_with_precinct.j2k 4 4 3 0 0 1 16 16)
+add_test(NAME tda_reversible_with_precinct COMMAND test_decode_area -q reversible_with_precinct.j2k)
+set_property(TEST tda_reversible_with_precinct APPEND PROPERTY DEPENDS tda_prep_reversible_with_precinct)
+
+add_test(NAME tda_prep_irreversible_no_precinct COMMAND test_tile_encoder 1 256 256 32 32 8 1 irreversible_no_precinct.j2k 4 4 3 0 0 1)
+add_test(NAME tda_irreversible_no_precinct COMMAND test_decode_area -q irreversible_no_precinct.j2k)
+set_property(TEST tda_irreversible_no_precinct APPEND PROPERTY DEPENDS tda_prep_irreversible_no_precinct)
+
 add_executable(include_openjpeg include_openjpeg.c)
 
 # No image send to the dashboard if lib PNG is not available.
diff --git a/tests/test_decode_area.c b/tests/test_decode_area.c
new file mode 100644 (file)
index 0000000..5a36747
--- /dev/null
@@ -0,0 +1,394 @@
+/*
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2017, IntoPix SA <contact@intopix.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "openjpeg.h"
+#include "format_defs.h"
+
+
+/* -------------------------------------------------------------------------- */
+#define JP2_RFC3745_MAGIC "\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a"
+#define JP2_MAGIC "\x0d\x0a\x87\x0a"
+/* position 45: "\xff\x52" */
+#define J2K_CODESTREAM_MAGIC "\xff\x4f\xff\x51"
+
+static int infile_format(const char *fname)
+{
+    FILE *reader;
+    unsigned char buf[12];
+    unsigned int l_nb_read;
+
+    reader = fopen(fname, "rb");
+
+    if (reader == NULL) {
+        return -1;
+    }
+
+    memset(buf, 0, 12);
+    l_nb_read = (unsigned int)fread(buf, 1, 12, reader);
+    fclose(reader);
+    if (l_nb_read != 12) {
+        return -1;
+    }
+
+    if (memcmp(buf, JP2_RFC3745_MAGIC, 12) == 0 || memcmp(buf, JP2_MAGIC, 4) == 0) {
+        return JP2_CFMT;
+    } else if (memcmp(buf, J2K_CODESTREAM_MAGIC, 4) == 0) {
+        return J2K_CFMT;
+    } else {
+        return -1;
+    }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+/**
+  sample error debug callback expecting no client object
+ */
+static void error_callback(const char *msg, void *client_data)
+{
+    (void)client_data;
+    fprintf(stdout, "[ERROR] %s", msg);
+}
+/**
+  sample warning debug callback expecting no client object
+ */
+static void warning_callback(const char *msg, void *client_data)
+{
+    (void)client_data;
+    fprintf(stdout, "[WARNING] %s", msg);
+}
+/**
+  sample debug callback expecting no client object
+ */
+static void info_callback(const char *msg, void *client_data)
+{
+    (void)client_data;
+    (void)msg;
+    /*fprintf(stdout, "[INFO] %s", msg);*/
+}
+
+opj_image_t* decode(
+    OPJ_BOOL quiet,
+    const char* input_file,
+    OPJ_INT32 x0,
+    OPJ_INT32 y0,
+    OPJ_INT32 x1,
+    OPJ_INT32 y1,
+    OPJ_UINT32* ptilew,
+    OPJ_UINT32* ptileh,
+    OPJ_UINT32* pcblkw,
+    OPJ_UINT32* pcblkh)
+{
+    opj_dparameters_t l_param;
+    opj_codec_t * l_codec = NULL;
+    opj_image_t * l_image = NULL;
+    opj_stream_t * l_stream = NULL;
+
+    if (!quiet) {
+        if (x0 != 0 || x1 != 0 || y0 != 0 || y1 != 0) {
+            printf("Decoding %d,%d,%d,%d\n", x0, y0, x1, y1);
+        } else {
+            printf("Decoding full image\n");
+        }
+    }
+
+    l_stream = opj_stream_create_default_file_stream(input_file, OPJ_TRUE);
+    if (!l_stream) {
+        fprintf(stderr, "ERROR -> failed to create the stream from the file\n");
+        return NULL;
+    }
+
+    /* Set the default decoding parameters */
+    opj_set_default_decoder_parameters(&l_param);
+
+    /* */
+    l_param.decod_format = infile_format(input_file);
+
+
+    switch (l_param.decod_format) {
+    case J2K_CFMT: { /* JPEG-2000 codestream */
+        /* Get a decoder handle */
+        l_codec = opj_create_decompress(OPJ_CODEC_J2K);
+        break;
+    }
+    case JP2_CFMT: { /* JPEG 2000 compressed image data */
+        /* Get a decoder handle */
+        l_codec = opj_create_decompress(OPJ_CODEC_JP2);
+        break;
+    }
+    default: {
+        fprintf(stderr, "ERROR -> Not a valid JPEG2000 file!\n");
+        opj_stream_destroy(l_stream);
+        return NULL;
+    }
+    }
+
+    /* catch events using our callbacks and give a local context */
+    opj_set_info_handler(l_codec, info_callback, 00);
+    opj_set_warning_handler(l_codec, warning_callback, 00);
+    opj_set_error_handler(l_codec, error_callback, 00);
+
+    /* Setup the decoder decoding parameters using user parameters */
+    if (! opj_setup_decoder(l_codec, &l_param)) {
+        fprintf(stderr, "ERROR ->failed to setup the decoder\n");
+        opj_stream_destroy(l_stream);
+        opj_destroy_codec(l_codec);
+        return NULL;
+    }
+
+    /* Read the main header of the codestream and if necessary the JP2 boxes*/
+    if (! opj_read_header(l_stream, l_codec, &l_image)) {
+        fprintf(stderr, "ERROR -> failed to read the header\n");
+        opj_stream_destroy(l_stream);
+        opj_destroy_codec(l_codec);
+        return NULL;
+    }
+
+    {
+        opj_codestream_info_v2_t* pCodeStreamInfo = opj_get_cstr_info(l_codec);
+        if (ptilew) {
+            *ptilew = pCodeStreamInfo->tdx;
+        }
+        if (ptileh) {
+            *ptilew = pCodeStreamInfo->tdy;
+        }
+        //int numResolutions = pCodeStreamInfo->m_default_tile_info.tccp_info[0].numresolutions;
+        if (pcblkw) {
+            *pcblkw = 1U << pCodeStreamInfo->m_default_tile_info.tccp_info[0].cblkw;
+        }
+        if (pcblkh) {
+            *pcblkh = 1U << pCodeStreamInfo->m_default_tile_info.tccp_info[0].cblkh;
+        }
+        opj_destroy_cstr_info(&pCodeStreamInfo);
+    }
+
+    if (x0 != 0 || x1 != 0 || y0 != 0 || y1 != 0) {
+        if (!opj_set_decode_area(l_codec, l_image, x0, y0, x1, y1)) {
+            fprintf(stderr, "ERROR -> failed to set the decoded area\n");
+            opj_stream_destroy(l_stream);
+            opj_destroy_codec(l_codec);
+            opj_image_destroy(l_image);
+            return NULL;
+        }
+    }
+
+    /* Get the decoded image */
+    if (!(opj_decode(l_codec, l_stream, l_image))) {
+        fprintf(stderr, "ERROR -> failed to decode image!\n");
+        opj_stream_destroy(l_stream);
+        opj_destroy_codec(l_codec);
+        opj_image_destroy(l_image);
+        return NULL;
+    }
+
+    if (! opj_end_decompress(l_codec, l_stream)) {
+        opj_stream_destroy(l_stream);
+        opj_destroy_codec(l_codec);
+        opj_image_destroy(l_image);
+        return NULL;
+    }
+
+
+    opj_stream_destroy(l_stream);
+    opj_destroy_codec(l_codec);
+    return l_image;
+}
+
+OPJ_BOOL check_consistency(opj_image_t* p_image, opj_image_t* p_sub_image)
+{
+    OPJ_UINT32 compno;
+    for (compno = 0; compno < p_image->numcomps; compno ++) {
+        OPJ_UINT32 y;
+        OPJ_UINT32 shift_y = p_sub_image->comps[compno].y0 - p_image->comps[compno].y0;
+        OPJ_UINT32 shift_x = p_sub_image->comps[compno].x0 - p_image->comps[compno].x0;
+        OPJ_UINT32 image_w = p_image->comps[compno].w;
+        OPJ_UINT32 sub_image_w = p_sub_image->comps[compno].w;
+        for (y = 0; y < p_sub_image->comps[compno].h; y++) {
+            OPJ_UINT32 x;
+
+            for (x = 0; x < sub_image_w; x++) {
+                OPJ_INT32 sub_image_val =
+                    p_sub_image->comps[compno].data[y * sub_image_w + x];
+                OPJ_INT32 image_val =
+                    p_image->comps[compno].data[(y + shift_y) * image_w + x + shift_x];
+                if (sub_image_val != image_val) {
+                    fprintf(stderr,
+                            "Difference found at subimage pixel (%u,%u) "
+                            "of compno=%u: got %d, expected %d\n",
+                            x, y, compno, sub_image_val, image_val);
+                    return OPJ_FALSE;
+                }
+            }
+        }
+    }
+    return OPJ_TRUE;
+}
+
+static INLINE OPJ_UINT32 opj_uint_min(OPJ_UINT32  a, OPJ_UINT32  b)
+{
+    return (a < b) ? a : b;
+}
+
+int main(int argc, char** argv)
+{
+    opj_image_t * l_image = NULL;
+    opj_image_t * l_sub_image = NULL;
+    OPJ_INT32 da_x0 = 0, da_y0 = 0, da_x1 = 0, da_y1 = 0;
+    const char* input_file = NULL;
+    OPJ_UINT32 tilew, tileh, cblkw, cblkh;
+    OPJ_UINT32 w, h;
+    OPJ_UINT32 x, y;
+    OPJ_UINT32 step_x, step_y;
+    OPJ_BOOL quiet = OPJ_FALSE;
+    OPJ_UINT32 nsteps = 100;
+
+    if (argc < 2) {
+        fprintf(stderr,
+                "Usage: test_decode_area [-q] [-steps n] input_file_jp2_or_jk2 [x0 y0 x1 y1]\n");
+        return 1;
+    }
+
+    {
+        int iarg;
+        for (iarg = 1; iarg < argc; iarg++) {
+            if (strcmp(argv[iarg], "-q") == 0) {
+                quiet = OPJ_TRUE;
+            } else if (strcmp(argv[iarg], "-steps") == 0 && iarg + 1 < argc) {
+                nsteps = (OPJ_UINT32)atoi(argv[iarg + 1]);
+                iarg ++;
+            } else if (input_file == NULL) {
+                input_file = argv[iarg];
+            } else if (iarg + 3 < argc) {
+                da_x0 = atoi(argv[iarg]);
+                da_y0 = atoi(argv[iarg + 1]);
+                da_x1 = atoi(argv[iarg + 2]);
+                da_y1 = atoi(argv[iarg + 3]);
+                iarg += 3;
+            }
+        }
+    }
+
+    l_image = decode(quiet, input_file, 0, 0, 0, 0,
+                     &tilew, &tileh, &cblkw, &cblkh);
+    if (!l_image) {
+        return 1;
+    }
+
+    if (da_x0 != 0 || da_x1 != 0 || da_y0 != 0 || da_y1 != 0) {
+        l_sub_image = decode(quiet, input_file, da_x0, da_y0, da_x1, da_y1,
+                             NULL, NULL, NULL, NULL);
+        if (!l_sub_image) {
+            fprintf(stderr, "decode failed for %d,%d,%d,%d\n",
+                    da_x0, da_y0, da_x1, da_y1);
+            opj_image_destroy(l_sub_image);
+            opj_image_destroy(l_image);
+            return 1;
+        }
+
+        if (!check_consistency(l_image, l_sub_image)) {
+            fprintf(stderr, "Consistency checked failed for %d,%d,%d,%d\n",
+                    da_x0, da_y0, da_x1, da_y1);
+            opj_image_destroy(l_sub_image);
+            opj_image_destroy(l_image);
+            return 1;
+        }
+        opj_image_destroy(l_sub_image);
+        opj_image_destroy(l_image);
+        return 0;
+    }
+
+    w = l_image->x1 - l_image->x0;
+    h = l_image->y1 - l_image->y0;
+    step_x = w > nsteps ? w / nsteps : 1;
+    step_y = h > nsteps ? h / nsteps : 1;
+    for (y = 0; y < h; y += step_y) {
+        for (x = 0; x < w; x += step_x) {
+            da_x0 = (OPJ_INT32)(l_image->x0 + x);
+            da_y0 = (OPJ_INT32)(l_image->y0 + y);
+            da_x1 = (OPJ_INT32)opj_uint_min(l_image->x1, l_image->x0 + x + 1);
+            da_y1 = (OPJ_INT32)opj_uint_min(l_image->y1, l_image->y0 + y + 1);
+            l_sub_image = decode(quiet, input_file, da_x0, da_y0, da_x1, da_y1,
+                                 NULL, NULL, NULL, NULL);
+            if (!l_sub_image) {
+                fprintf(stderr, "decode failed for %d,%d,%d,%d\n",
+                        da_x0, da_y0, da_x1, da_y1);
+                opj_image_destroy(l_sub_image);
+                opj_image_destroy(l_image);
+                return 1;
+            }
+
+            if (!check_consistency(l_image, l_sub_image)) {
+                fprintf(stderr, "Consistency checked failed for %d,%d,%d,%d\n",
+                        da_x0, da_y0, da_x1, da_y1);
+                opj_image_destroy(l_sub_image);
+                opj_image_destroy(l_image);
+                return 1;
+            }
+            opj_image_destroy(l_sub_image);
+
+            if (step_x > 1 || step_y > 1) {
+                if (step_x > 1) {
+                    da_x0 = (OPJ_INT32)opj_uint_min(l_image->x1, (OPJ_UINT32)da_x0 + 1);
+                    da_x1 = (OPJ_INT32)opj_uint_min(l_image->x1, (OPJ_UINT32)da_x1 + 1);
+                }
+                if (step_y > 1) {
+                    da_y0 = (OPJ_INT32)opj_uint_min(l_image->y1, (OPJ_UINT32)da_y0 + 1);
+                    da_y1 = (OPJ_INT32)opj_uint_min(l_image->y1, (OPJ_UINT32)da_y1 + 1);
+                }
+                l_sub_image = decode(quiet, input_file, da_x0, da_y0, da_x1, da_y1,
+                                     NULL, NULL, NULL, NULL);
+                if (!l_sub_image) {
+                    fprintf(stderr, "decode failed for %d,%d,%d,%d\n",
+                            da_x0, da_y0, da_x1, da_y1);
+                    opj_image_destroy(l_sub_image);
+                    opj_image_destroy(l_image);
+                    return 1;
+                }
+
+                if (!check_consistency(l_image, l_sub_image)) {
+                    fprintf(stderr, "Consistency checked failed for %d,%d,%d,%d\n",
+                            da_x0, da_y0, da_x1, da_y1);
+                    opj_image_destroy(l_sub_image);
+                    opj_image_destroy(l_image);
+                    return 1;
+                }
+                opj_image_destroy(l_sub_image);
+            }
+        }
+    }
+
+    opj_image_destroy(l_image);
+    return 0;
+}
index 861b8c70e31cc02ed02a455ff918f1af82a895b7..028d38ebf675dbc469e8ba632e66221b5c80d2a4 100644 (file)
@@ -60,6 +60,16 @@ static void info_callback(const char *msg, void *client_data)
     fprintf(stdout, "[INFO] %s", msg);
 }
 
+static INLINE OPJ_UINT32 opj_uint_max(OPJ_UINT32  a, OPJ_UINT32  b)
+{
+    return (a > b) ? a : b;
+}
+
+static INLINE OPJ_UINT32 opj_uint_min(OPJ_UINT32  a, OPJ_UINT32  b)
+{
+    return (a < b) ? a : b;
+}
+
 /* -------------------------------------------------------------------------- */
 
 #define NUM_COMPS_MAX 4
@@ -70,7 +80,7 @@ int main(int argc, char *argv[])
     opj_image_t * l_image;
     opj_image_cmptparm_t l_params [NUM_COMPS_MAX];
     opj_stream_t * l_stream;
-    OPJ_UINT32 l_nb_tiles;
+    OPJ_UINT32 l_nb_tiles_width, l_nb_tiles_height, l_nb_tiles;
     OPJ_UINT32 l_data_size;
     size_t len;
 
@@ -98,9 +108,18 @@ int main(int argc, char *argv[])
     int comp_prec;
     int irreversible;
     const char *output_file;
+    int cblockw_init = 64;
+    int cblockh_init = 64;
+    int numresolution = 6;
+    OPJ_UINT32 offsetx = 0;
+    OPJ_UINT32 offsety = 0;
+    int quality_loss = 1;
+    int is_rand = 0;
+
+    opj_set_default_encoder_parameters(&l_param);
 
-    /* should be test_tile_encoder 3 2000 2000 1000 1000 8 tte1.j2k */
-    if (argc == 9) {
+    /* should be test_tile_encoder 3 2000 2000 1000 1000 8 tte1.j2k [64 64] [6] [0 0] [0] [256 256] */
+    if (argc >= 9) {
         num_comps = (OPJ_UINT32)atoi(argv[1]);
         image_width = atoi(argv[2]);
         image_height = atoi(argv[3]);
@@ -109,6 +128,28 @@ int main(int argc, char *argv[])
         comp_prec = atoi(argv[6]);
         irreversible = atoi(argv[7]);
         output_file = argv[8];
+        if (argc >= 12) {
+            quality_loss = 0;
+            cblockw_init = atoi(argv[9]);
+            cblockh_init = atoi(argv[10]);
+        }
+        if (argc >= 13) {
+            numresolution = atoi(argv[11]);
+        }
+        if (argc >= 14) {
+            offsetx = (OPJ_UINT32)atoi(argv[12]);
+            offsety = (OPJ_UINT32)atoi(argv[13]);
+        }
+        if (argc >= 15) {
+            is_rand = atoi(argv[14]);
+        }
+        for (i = 15; i + 1 < (OPJ_UINT32)argc &&
+                l_param.res_spec < OPJ_J2K_MAXRLVLS; i += 2) {
+            l_param.csty |= 0x01;
+            l_param.prcw_init[l_param.res_spec] = atoi(argv[i]);
+            l_param.prch_init[l_param.res_spec] = atoi(argv[i + 1]);
+            l_param.res_spec ++;
+        }
     } else {
         num_comps = 3;
         image_width = 2000;
@@ -122,8 +163,11 @@ int main(int argc, char *argv[])
     if (num_comps > NUM_COMPS_MAX) {
         return 1;
     }
-    l_nb_tiles = (OPJ_UINT32)(image_width / tile_width) * (OPJ_UINT32)(
-                     image_height / tile_height);
+    l_nb_tiles_width = (offsetx + (OPJ_UINT32)image_width +
+                        (OPJ_UINT32)tile_width - 1) / (OPJ_UINT32)tile_width;
+    l_nb_tiles_height = (offsety + (OPJ_UINT32)image_height +
+                         (OPJ_UINT32)tile_height - 1) / (OPJ_UINT32)tile_height;
+    l_nb_tiles = l_nb_tiles_width * l_nb_tiles_height;
     l_data_size = (OPJ_UINT32)tile_width * (OPJ_UINT32)tile_height *
                   (OPJ_UINT32)num_comps * (OPJ_UINT32)(comp_prec / 8);
 
@@ -134,16 +178,21 @@ int main(int argc, char *argv[])
     fprintf(stdout,
             "Encoding random values -> keep in mind that this is very hard to compress\n");
     for (i = 0; i < l_data_size; ++i) {
-        l_data[i] = (OPJ_BYTE)i; /*rand();*/
+        if (is_rand) {
+            l_data[i] = (OPJ_BYTE)rand();
+        } else {
+            l_data[i] = (OPJ_BYTE)i;
+        }
     }
 
-    opj_set_default_encoder_parameters(&l_param);
     /** you may here add custom encoding parameters */
     /* rate specifications */
     /** number of quality layers in the stream */
-    l_param.tcp_numlayers = 1;
-    l_param.cp_fixed_quality = 1;
-    l_param.tcp_distoratio[0] = 20;
+    if (quality_loss) {
+        l_param.tcp_numlayers = 1;
+        l_param.cp_fixed_quality = 1;
+        l_param.tcp_distoratio[0] = 20;
+    }
     /* is using others way of calculation */
     /* l_param.cp_disto_alloc = 1 or l_param.cp_fixed_alloc = 1 */
     /* l_param.tcp_rates[0] = ... */
@@ -158,6 +207,10 @@ int main(int argc, char *argv[])
     l_param.cp_tdx = tile_width;
     l_param.cp_tdy = tile_height;
 
+    /* code block size */
+    l_param.cblockw_init = cblockw_init;
+    l_param.cblockh_init = cblockh_init;
+
     /* use irreversible encoding ?*/
     l_param.irreversible = irreversible;
 
@@ -187,7 +240,7 @@ int main(int argc, char *argv[])
     /* l_param.mode = 0;*/
 
     /** number of resolutions */
-    l_param.numresolution = 6;
+    l_param.numresolution = numresolution;
 
     /** progression order to use*/
     /** OPJ_LRCP, OPJ_RLCP, OPJ_RPCL, PCRL, CPRL */
@@ -221,8 +274,8 @@ int main(int argc, char *argv[])
         l_current_param_ptr->sgnd = 0;
         l_current_param_ptr->prec = (OPJ_UINT32)comp_prec;
 
-        l_current_param_ptr->x0 = 0;
-        l_current_param_ptr->y0 = 0;
+        l_current_param_ptr->x0 = offsetx;
+        l_current_param_ptr->y0 = offsety;
 
         ++l_current_param_ptr;
     }
@@ -251,10 +304,10 @@ int main(int argc, char *argv[])
         return 1;
     }
 
-    l_image->x0 = 0;
-    l_image->y0 = 0;
-    l_image->x1 = (OPJ_UINT32)image_width;
-    l_image->y1 = (OPJ_UINT32)image_height;
+    l_image->x0 = offsetx;
+    l_image->y0 = offsety;
+    l_image->x1 = offsetx + (OPJ_UINT32)image_width;
+    l_image->y1 = offsety + (OPJ_UINT32)image_height;
     l_image->color_space = OPJ_CLRSPC_SRGB;
 
     if (! opj_setup_encoder(l_codec, &l_param, l_image)) {
@@ -286,7 +339,18 @@ int main(int argc, char *argv[])
     }
 
     for (i = 0; i < l_nb_tiles; ++i) {
-        if (! opj_write_tile(l_codec, i, l_data, l_data_size, l_stream)) {
+        OPJ_UINT32 tile_y = i / l_nb_tiles_width;
+        OPJ_UINT32 tile_x = i % l_nb_tiles_width;
+        OPJ_UINT32 tile_x0 = opj_uint_max(l_image->x0, tile_x * (OPJ_UINT32)tile_width);
+        OPJ_UINT32 tile_y0 = opj_uint_max(l_image->y0,
+                                          tile_y * (OPJ_UINT32)tile_height);
+        OPJ_UINT32 tile_x1 = opj_uint_min(l_image->x1,
+                                          (tile_x + 1) * (OPJ_UINT32)tile_width);
+        OPJ_UINT32 tile_y1 = opj_uint_min(l_image->y1,
+                                          (tile_y + 1) * (OPJ_UINT32)tile_height);
+        OPJ_UINT32 tilesize = (tile_x1 - tile_x0) * (tile_y1 - tile_y0) *
+                              (OPJ_UINT32)num_comps * (OPJ_UINT32)(comp_prec / 8);
+        if (! opj_write_tile(l_codec, i, l_data, tilesize, l_stream)) {
             fprintf(stderr, "ERROR -> test_tile_encoder: failed to write the tile %d!\n",
                     i);
             opj_stream_destroy(l_stream);