@@ -247,12 +247,16 @@ void PacketIter::genPrecinctInfo(PacketIterInfoComponent* comp, PacketIterInfoRe
247247 * Preconditions for OPT:
248248 * 1. Decompression (not compression)
249249 * 2. Single progression (no POC)
250- * 3. Tile origin at (0,0)
251- * 4. No subsampling (all dx=dy=1)
252- * 5. All components have the same number of resolutions
253- * 6. For PCRL/CPRL: projected precinct sizes are non-decreasing as resolution decreases
250+ * 3. No subsampling (all dx=dy=1)
251+ * 4. All components have the same number of resolutions
252+ * 5. For PCRL/CPRL: projected precinct sizes are non-decreasing as resolution decreases
254253 * (ensures the highest-resolution precinct grid covers all lower-resolution precincts)
255254 *
255+ * The OPT path uses per-resolution precinct step sizes for spatial
256+ * progressions (RPCL), avoiding degenerate iteration when tiles have
257+ * large dimensions or many resolution levels combined with small
258+ * global dx/dy.
259+ *
256260 * @return true if OPT path was used, false if caller should use the non-OPT path
257261 */
258262bool PacketIter::genPrecinctInfoOPT (void )
@@ -261,9 +265,6 @@ bool PacketIter::genPrecinctInfoOPT(void)
261265 return false ;
262266
263267 auto tb = packetManager->getTileBounds ();
264- // tile origin at (0,0) will simplify computations
265- if (tb.x0 || tb.y0 )
266- return false ;
267268 // no subsampling
268269 for (uint16_t compno = 0 ; compno < numcomps; ++compno)
269270 {
@@ -281,6 +282,11 @@ bool PacketIter::genPrecinctInfoOPT(void)
281282 break ;
282283 case GRK_PCRL:
283284 case GRK_CPRL:
285+ // PCRL/CPRL OPT spatial loops use precinct-aligned bounds and bitwise
286+ // alignment checks that assume tile origin falls on a precinct boundary.
287+ // Non-zero tile origins break these assumptions.
288+ if (tb.x0 || tb.y0 )
289+ return false ;
284290 // if P occurs before R, then we must ensure that for all resolutions, the precinct
285291 // projected onto canvas is a "multiple" of the highest resolution precinct,
286292 // so that the P loops covers all precincts from all resolutions
@@ -823,11 +829,13 @@ bool PacketIter::genPrecinctX0GridPCRL_OPT(ResPrecinctInfo* rpInfo)
823829}
824830void PacketIter::genPrecinctY0GridRPCL_OPT (ResPrecinctInfo* rpInfo)
825831{
826- py0grid_ = (uint32_t )(ceildivpow2 (y, rpInfo->decompLevel_ ) >> rpInfo->precHeightExp );
832+ py0grid_ = (uint32_t )(ceildivpow2 (y, rpInfo->decompLevel_ ) >> rpInfo->precHeightExp ) -
833+ rpInfo->resInPrecGridY0 ;
827834}
828835void PacketIter::genPrecinctX0GridRPCL_OPT (ResPrecinctInfo* rpInfo)
829836{
830- px0grid_ = (uint32_t )(ceildivpow2 (x, rpInfo->decompLevel_ ) >> rpInfo->precWidthExp );
837+ px0grid_ = (uint32_t )(ceildivpow2 (x, rpInfo->decompLevel_ ) >> rpInfo->precWidthExp ) -
838+ rpInfo->resInPrecGridX0 ;
831839}
832840/* *
833841 * Compute the minimum spatial step sizes (dx, dy) across all components and
@@ -984,6 +992,28 @@ bool PacketIter::isWholeTile(void)
984992
985993bool PacketIter::next (SparseBuffer* compressedPackets)
986994{
995+ // OPT path: per-resolution precinct info avoids degenerate spatial
996+ // iteration for tiles with large dimensions or many resolution levels.
997+ if (precinctInfoOPT_)
998+ {
999+ switch (prog.progression )
1000+ {
1001+ case GRK_LRCP:
1002+ return next_lrcpOPT ();
1003+ case GRK_RLCP:
1004+ return next_rlcpOPT ();
1005+ case GRK_PCRL:
1006+ return next_pcrlOPT ();
1007+ case GRK_RPCL:
1008+ return next_rpclOPT (compressedPackets);
1009+ case GRK_CPRL:
1010+ return next_cprlOPT (compressedPackets);
1011+ default :
1012+ return false ;
1013+ }
1014+ }
1015+
1016+ // Non-OPT fallback: compression, multiple progressions, subsampling, etc.
9871017 switch (prog.progression )
9881018 {
9891019 case GRK_LRCP:
@@ -996,23 +1026,22 @@ bool PacketIter::next(SparseBuffer* compressedPackets)
9961026 // spatial progressions require non-zero step sizes to avoid infinite loops
9971027 if (dx == 0 || dy == 0 )
9981028 return false ;
999- if (prog.progression == GRK_PCRL)
1000- return next_pcrl ();
1001- else if (prog.progression == GRK_RPCL)
1002- return next_rpcl (compressedPackets);
1003- else
1004- return next_cprl (compressedPackets);
1029+ switch (prog.progression )
1030+ {
1031+ case GRK_PCRL:
1032+ return next_pcrl ();
1033+ case GRK_RPCL:
1034+ return next_rpcl (compressedPackets);
1035+ default : // GRK_CPRL
1036+ return next_cprl (compressedPackets);
1037+ }
10051038 default :
10061039 return false ;
10071040 }
1008-
1009- return false ;
10101041}
10111042
1012- bool PacketIter::next_cprl (SparseBuffer* compressedPackets )
1043+ bool PacketIter::next_cprl (SparseBuffer*)
10131044{
1014- if (precinctInfoOPT_)
1015- return next_cprlOPT (compressedPackets);
10161045 for (; compno < prog.comp_e ; compno++)
10171046 {
10181047 auto comp = comps + compno;
@@ -1053,8 +1082,6 @@ bool PacketIter::next_cprl(SparseBuffer* compressedPackets)
10531082}
10541083bool PacketIter::next_pcrl ()
10551084{
1056- if (precinctInfoOPT_)
1057- return next_pcrlOPT ();
10581085 for (; y < prog.ty1 ; y += dyActive, dyActive = dy)
10591086 {
10601087 for (; x < prog.tx1 ; x += dxActive, dxActive = dx)
@@ -1098,32 +1125,17 @@ bool PacketIter::next_pcrl()
10981125}
10991126bool PacketIter::next_lrcp ()
11001127{
1101- if (precinctInfoOPT_)
1102- return next_lrcpOPT ();
1103-
11041128 for (; layno < prog.lay_e ; layno++)
11051129 {
11061130 for (; resno < prog.res_e ; resno++)
11071131 {
1108- uint64_t prec_e = 0 ;
1109- if (precinctInfoOPT_)
1110- {
1111- if (resno >= comps->numresolutions )
1112- continue ;
1113- auto res = comps->resolutions + resno;
1114- prec_e = (uint64_t )res->precinctGridWidth * res->precinctGridHeight ;
1115- }
11161132 for (; compno < prog.comp_e ; compno++)
11171133 {
11181134 auto comp = comps + compno;
1119- if (!precinctInfoOPT_)
1120- {
1121- // skip resolutions greater than current component resolution
1122- if (resno >= comp->numresolutions )
1123- continue ;
1124- auto res = comp->resolutions + resno;
1125- prec_e = (uint64_t )res->precinctGridWidth * res->precinctGridHeight ;
1126- }
1135+ if (resno >= comp->numresolutions )
1136+ continue ;
1137+ auto res = comp->resolutions + resno;
1138+ uint64_t prec_e = (uint64_t )res->precinctGridWidth * res->precinctGridHeight ;
11271139 if (incrementInner)
11281140 precinctIndex++;
11291141 if (precinctIndex < prec_e)
@@ -1144,30 +1156,17 @@ bool PacketIter::next_lrcp()
11441156}
11451157bool PacketIter::next_rlcp ()
11461158{
1147- if (precinctInfoOPT_)
1148- return next_rlcpOPT ();
11491159 for (; resno < prog.res_e ; resno++)
11501160 {
1151- uint64_t prec_e = 0 ;
1152- if (precinctInfoOPT_)
1153- {
1154- if (resno >= comps->numresolutions )
1155- continue ;
1156- auto res = comps->resolutions + resno;
1157- prec_e = (uint64_t )res->precinctGridWidth * res->precinctGridHeight ;
1158- }
11591161 for (; layno < prog.lay_e ; layno++)
11601162 {
11611163 for (; compno < prog.comp_e ; compno++)
11621164 {
11631165 auto comp = comps + compno;
1164- if (!precinctInfoOPT_)
1165- {
1166- if (resno >= comp->numresolutions )
1167- continue ;
1168- auto res = comp->resolutions + resno;
1169- prec_e = (uint64_t )res->precinctGridWidth * res->precinctGridHeight ;
1170- }
1166+ if (resno >= comp->numresolutions )
1167+ continue ;
1168+ auto res = comp->resolutions + resno;
1169+ uint64_t prec_e = (uint64_t )res->precinctGridWidth * res->precinctGridHeight ;
11711170 if (incrementInner)
11721171 precinctIndex++;
11731172 if (precinctIndex < prec_e)
@@ -1186,11 +1185,8 @@ bool PacketIter::next_rlcp()
11861185
11871186 return false ;
11881187}
1189- bool PacketIter::next_rpcl (SparseBuffer* compressedPackets )
1188+ bool PacketIter::next_rpcl (SparseBuffer*)
11901189{
1191- if (precinctInfoOPT_)
1192- return next_rpclOPT (compressedPackets);
1193-
11941190 for (; resno < prog.res_e ; resno++)
11951191 {
11961192 // if all remaining components have degenerate precinct grid, then
0 commit comments