Skip to content

Commit 755adf1

Browse files
author
Grok Compression
committed
Accept reduced coordinates via dw_reduced flag
Add dw_reduced field to grk_decompress_parameters. When set, decompress window coordinates are interpreted as reduced-space and scaled to full-resolution internally. This allows callers (e.g. GDAL) to pass overview-level coordinates directly. Conditional on dw_reduced: - setDecompressRegion: scale region to full-res - wait(): scale swath coords for TileCompletion - getImage(): reduce composite bounds for caller - post_decompressT2T1: reduce per-tile image bounds Tests: add testReducedCoordsPerTile and update testReduceWithRegion.
1 parent 277e190 commit 755adf1

8 files changed

Lines changed: 164 additions & 8 deletions

File tree

grok.code-workspace

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
},
2424
{
2525
"path": "../tiletopia"
26+
},
27+
{
28+
"path": "../ix"
2629
}
2730
],
2831
"settings": {}

src/lib/core/codestream/CodingParams.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ void CodingParams::init(grk_decompress_parameters* parameters,
181181
dw_y0 = parameters->dw_y0;
182182
dw_x1 = parameters->dw_x1;
183183
dw_y1 = parameters->dw_y1;
184+
dw_reduced = parameters->dw_reduced;
184185
asynchronous_ = parameters->asynchronous;
185186
simulate_synchronous_ = parameters->simulate_synchronous;
186187
decompressCallback_ = parameters->decompress_callback;

src/lib/core/codestream/CodingParams.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ struct CodingParams
444444
double dw_x1 = 0; /* decompress window right boundary*/
445445
double dw_y0 = 0; /* decompress window top boundary*/
446446
double dw_y1 = 0; /* decompress window bottom boundary*/
447+
bool dw_reduced = false; /* if true, dw_x0..dw_y1 are in reduced coordinate space */
447448
std::unique_ptr<PPMMarker> ppmMarkers_;
448449
TileCodingParamsPool tcps_; /** default tile coding parameters */
449450
union

src/lib/core/codestream/decompress/CodeStreamDecompress.cpp

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1248,6 +1248,17 @@ bool CodeStreamDecompress::setDecompressRegion(RectD region)
12481248
region.x1 = (double)ceil(val[2] * w);
12491249
region.y1 = (double)ceil(val[3] * h);
12501250
}
1251+
else if(cp_.dw_reduced && cp_.codingParams_.dec_.reduce_ > 0)
1252+
{
1253+
// Caller passes coordinates in the reduced output space;
1254+
// scale up to full-resolution for internal processing.
1255+
uint32_t scale = 1u << cp_.codingParams_.dec_.reduce_;
1256+
region.x0 *= scale;
1257+
region.y0 *= scale;
1258+
region.x1 *= scale;
1259+
region.y1 *= scale;
1260+
}
1261+
compositeBoundsReduced_ = false;
12511262
Rect16 tilesToDecompress;
12521263
auto canvasRegion = Rect32((uint32_t)region.x0 + image->x0, (uint32_t)region.y0 + image->y0,
12531264
(uint32_t)region.x1 + image->x0, (uint32_t)region.y1 + image->y0);
@@ -1439,7 +1450,25 @@ void CodeStreamDecompress::wait(grk_wait_swath* swath)
14391450
// 1. Swath-based early return: tile data ready but NOT post-processed
14401451
if(swath && tileCompletion_)
14411452
{
1442-
bool rc = tileCompletion_->wait(swath);
1453+
// When caller passed reduced coordinates (dw_reduced), scale up to
1454+
// full-resolution for TileCompletion which uses unreduced tile grid.
1455+
grk_wait_swath fullResSwath = *swath;
1456+
uint8_t reduce = cp_.codingParams_.dec_.reduce_;
1457+
if(cp_.dw_reduced && reduce > 0)
1458+
{
1459+
uint32_t scale = 1u << reduce;
1460+
fullResSwath.x0 *= scale;
1461+
fullResSwath.y0 *= scale;
1462+
fullResSwath.x1 *= scale;
1463+
fullResSwath.y1 *= scale;
1464+
}
1465+
bool rc = tileCompletion_->wait(&fullResSwath);
1466+
// Copy output tile indices back to caller's swath
1467+
swath->tile_x0 = fullResSwath.tile_x0;
1468+
swath->tile_y0 = fullResSwath.tile_y0;
1469+
swath->tile_x1 = fullResSwath.tile_x1;
1470+
swath->tile_y1 = fullResSwath.tile_y1;
1471+
swath->num_tile_cols = fullResSwath.num_tile_cols;
14431472
if(!rc)
14441473
return;
14451474
return;
@@ -2434,6 +2463,19 @@ GrkImage* CodeStreamDecompress::getImage()
24342463
{
24352464
wait(nullptr);
24362465

2466+
// Reduce image-level bounds to match component (output) coordinate space.
2467+
// Only when caller passed reduced coordinates (dw_reduced), the
2468+
// multiTileComposite_ still has unreduced canvas bounds that need reducing.
2469+
auto reduce = cp_.codingParams_.dec_.reduce_;
2470+
if(cp_.dw_reduced && reduce > 0 && !compositeBoundsReduced_)
2471+
{
2472+
multiTileComposite_->x0 = ceildivpow2<uint32_t>(multiTileComposite_->x0, reduce);
2473+
multiTileComposite_->y0 = ceildivpow2<uint32_t>(multiTileComposite_->y0, reduce);
2474+
multiTileComposite_->x1 = ceildivpow2<uint32_t>(multiTileComposite_->x1, reduce);
2475+
multiTileComposite_->y1 = ceildivpow2<uint32_t>(multiTileComposite_->y1, reduce);
2476+
compositeBoundsReduced_ = true;
2477+
}
2478+
24372479
return multiTileComposite_.get();
24382480
}
24392481
GrkImage* CodeStreamDecompress::getCompositeNoWait()

src/lib/core/codestream/decompress/CodeStreamDecompress.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,7 @@ class CodeStreamDecompress final : public CodeStream, public IDecompressor
393393
*
394394
*/
395395
std::unique_ptr<GrkImage, RefCountedDeleter<GrkImage>> multiTileComposite_;
396+
bool compositeBoundsReduced_ = false;
396397

397398
/**
398399
* @brief Holds unreduced unsubsampled decompress region , if set

src/lib/core/grok.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,7 @@ typedef struct _grk_decompress_params
754754
double dw_x1; /* decompress window right boundary*/
755755
double dw_y0; /* decompress window top boundary*/
756756
double dw_y1; /* decompress window bottom boundary*/
757+
bool dw_reduced; /* if true, dw_x0..dw_y1 are in reduced (output) coordinate space */
757758
uint16_t tile_index; /* index of decompressed tile*/
758759

759760
/***************************************************************************

src/lib/core/tile_processor/TileProcessor.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1162,6 +1162,17 @@ void TileProcessor::post_decompressT2T1(GrkImage* scratch)
11621162
{
11631163
grk_unref(image_);
11641164
image_ = scratch->extractFrom(tile_);
1165+
// extractFrom() sets image x0/y0/x1/y1 from unreduced tile canvas
1166+
// coordinates. When caller passed reduced coordinates (dw_reduced),
1167+
// reduce them to match the output coordinate space.
1168+
uint8_t reduce = cp_->codingParams_.dec_.reduce_;
1169+
if(cp_->dw_reduced && reduce > 0)
1170+
{
1171+
image_->x0 = ceildivpow2<uint32_t>(image_->x0, reduce);
1172+
image_->y0 = ceildivpow2<uint32_t>(image_->y0, reduce);
1173+
image_->x1 = ceildivpow2<uint32_t>(image_->x1, reduce);
1174+
image_->y1 = ceildivpow2<uint32_t>(image_->y1, reduce);
1175+
}
11651176
}
11661177
else
11671178
{

tests/GrkLRUCacheTest.cpp

Lines changed: 103 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -883,14 +883,17 @@ static bool testReduceWithRegion()
883883
return false;
884884
}
885885

886-
// Decompress a sub-region at reduce=1
886+
// Decompress a sub-region at reduce=1.
887+
// Coordinates are in reduced output space: 64x64 reduced pixels
888+
// = 128x128 full-res pixels, yielding 64x64 output.
887889
grk_decompress_parameters params{};
888890
params.core.tile_cache_strategy = GRK_TILE_CACHE_IMAGE;
889891
params.core.reduce = 1;
890892
params.dw_x0 = 0;
891893
params.dw_y0 = 0;
892-
params.dw_x1 = 128;
893-
params.dw_y1 = 128;
894+
params.dw_x1 = 64;
895+
params.dw_y1 = 64;
896+
params.dw_reduced = true;
894897
params.asynchronous = true;
895898
params.simulate_synchronous = true;
896899

@@ -919,10 +922,11 @@ static bool testReduceWithRegion()
919922
return false;
920923
}
921924

922-
// At reduce=1, the 128x128 region should produce ~64x64 output
925+
// At reduce=1, the 64x64 reduced-space region produces ~64x64 output
923926
uint32_t w = img->x1 - img->x0;
924927
uint32_t h = img->y1 - img->y0;
925-
spdlog::info("Reduce+region output: {}x{}", w, h);
928+
spdlog::info("Reduce+region output: {}x{} (x0={}, y0={}, x1={}, y1={})", w, h, img->x0, img->y0, img->x1, img->y1);
929+
spdlog::info("Reduce+region comps[0]: w={}, h={}", img->comps[0].w, img->comps[0].h);
926930

927931
std::error_code ec;
928932
std::filesystem::remove(testFile, ec);
@@ -933,8 +937,8 @@ static bool testReduceWithRegion()
933937
return false;
934938
}
935939

936-
// Region is 128x128, reduce=1 → expect ~64x64
937-
if(w > 128 || h > 128)
940+
// Region is 64x64 in reduced space → expect ~64x64 output
941+
if(w > 64 || h > 64)
938942
{
939943
spdlog::error("FAIL: Output larger than region");
940944
return false;
@@ -944,6 +948,94 @@ static bool testReduceWithRegion()
944948
return true;
945949
}
946950

951+
///////////////////////////////////////////////////////////////////
952+
// Test 9b: Reduced-coords API for multi-tile per-tile images
953+
//
954+
// Verifies that grk_decompress_get_tile_image returns tile images
955+
// with x0/y0/x1/y1 in reduced coordinate space (not full-res).
956+
///////////////////////////////////////////////////////////////////
957+
static bool testReducedCoordsPerTile()
958+
{
959+
spdlog::info("=== Test: Reduced-coords per-tile API ===");
960+
961+
// Create 256x256 image with 128x128 tiles (2x2 grid)
962+
std::string testFile =
963+
(std::filesystem::temp_directory_path() / "grk_reduced_coords_test.j2k").string();
964+
if(!createTestImage(testFile, 256, 256, 128, 128, false))
965+
{
966+
spdlog::error("Failed to create test image");
967+
return false;
968+
}
969+
970+
// Decompress at reduce=1 (output 128x128 with 64x64 tiles)
971+
grk_decompress_parameters params{};
972+
params.core.tile_cache_strategy = GRK_TILE_CACHE_IMAGE;
973+
params.core.reduce = 1;
974+
params.core.skip_allocate_composite = true;
975+
params.dw_reduced = true;
976+
params.asynchronous = true;
977+
params.simulate_synchronous = true;
978+
979+
grk_stream_params sp{};
980+
safe_strcpy(sp.file, testFile.data());
981+
CodecPtr codec(grk_decompress_init(&sp, &params));
982+
grk_header_info hi{};
983+
grk_decompress_read_header(codec.get(), &hi);
984+
grk_decompress_update(&params, codec.get());
985+
986+
bool ok = grk_decompress(codec.get(), nullptr);
987+
if(!ok)
988+
{
989+
spdlog::error("Decompress failed");
990+
std::filesystem::remove(testFile);
991+
return false;
992+
}
993+
994+
grk_decompress_wait(codec.get(), nullptr);
995+
996+
// Check each tile's image coordinates are in reduced space
997+
// Full-res tiles: (0,0,128,128), (128,0,256,128), (0,128,128,256), (128,128,256,256)
998+
// Expected reduced: (0,0,64,64), (64,0,128,64), (0,64,64,128), (64,64,128,128)
999+
struct Expected
1000+
{
1001+
uint16_t tileIndex;
1002+
uint32_t x0, y0, x1, y1;
1003+
};
1004+
Expected expected[] = {{0, 0, 0, 64, 64}, {1, 64, 0, 128, 64}, {2, 0, 64, 64, 128}, {3, 64, 64, 128, 128}};
1005+
1006+
for(auto& e : expected)
1007+
{
1008+
auto* tileImg = grk_decompress_get_tile_image(codec.get(), e.tileIndex, true);
1009+
if(!tileImg)
1010+
{
1011+
spdlog::error("FAIL: get_tile_image returned null for tile {}", e.tileIndex);
1012+
std::filesystem::remove(testFile);
1013+
return false;
1014+
}
1015+
if(tileImg->x0 != e.x0 || tileImg->y0 != e.y0 || tileImg->x1 != e.x1 || tileImg->y1 != e.y1)
1016+
{
1017+
spdlog::error("FAIL: tile {} coords ({},{},{},{}) != expected ({},{},{},{})", e.tileIndex,
1018+
tileImg->x0, tileImg->y0, tileImg->x1, tileImg->y1, e.x0, e.y0, e.x1, e.y1);
1019+
std::filesystem::remove(testFile);
1020+
return false;
1021+
}
1022+
// Also verify component dimensions match reduced tile size
1023+
uint32_t cw = tileImg->comps[0].w;
1024+
uint32_t ch = tileImg->comps[0].h;
1025+
if(cw != (e.x1 - e.x0) || ch != (e.y1 - e.y0))
1026+
{
1027+
spdlog::error("FAIL: tile {} comp size {}x{} != expected {}x{}", e.tileIndex, cw, ch,
1028+
e.x1 - e.x0, e.y1 - e.y0);
1029+
std::filesystem::remove(testFile);
1030+
return false;
1031+
}
1032+
}
1033+
1034+
std::filesystem::remove(testFile);
1035+
spdlog::info("PASS: Reduced-coords per-tile API");
1036+
return true;
1037+
}
1038+
9471039
///////////////////////////////////////////////////////////////////
9481040
// Test 10: All tiles via decompressTile then re-read all
9491041
//
@@ -2541,6 +2633,10 @@ int GrkLRUCacheTest::main(int argc, char** argv)
25412633
if(!testReduceWithRegion())
25422634
failures++;
25432635

2636+
// Test 9b: Reduced-coords per-tile API
2637+
if(!testReducedCoordsPerTile())
2638+
failures++;
2639+
25442640
// Test 10: All tiles via decompressTile then re-read
25452641
{
25462642
std::string interFile =

0 commit comments

Comments
 (0)