Skip to content

Commit 453bfb9

Browse files
Merge pull request #618 from mdaus/j2k-multi-band-partial-block-read
multi-channel partial block reading
2 parents a9dbd0a + ceaa278 commit 453bfb9

File tree

8 files changed

+719
-47
lines changed

8 files changed

+719
-47
lines changed

.github/workflows/frequent_check.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
cd out
3535
ctest -C ${{ matrix.configuration }} --output-on-failure
3636
- name: Add msbuild to PATH
37-
uses: microsoft/setup-msbuild@v1.1 # https://github.com/marketplace/actions/setup-msbuild
37+
uses: microsoft/setup-msbuild@v2 # https://github.com/marketplace/actions/setup-msbuild
3838
with:
3939
msbuild-architecture: x64
4040
- name: msbuild
@@ -63,7 +63,7 @@ jobs:
6363
cmake --build . --config ${{ matrix.configuration }} -j
6464
cmake --build . --config ${{ matrix.configuration }} --target install
6565
- name: Add msbuild to PATH
66-
uses: microsoft/setup-msbuild@v1.1 # https://github.com/marketplace/actions/setup-msbuild
66+
uses: microsoft/setup-msbuild@v2 # https://github.com/marketplace/actions/setup-msbuild
6767
with:
6868
msbuild-architecture: x64
6969
- name: msbuild

.github/workflows/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
cd out
3838
ctest -C ${{ matrix.configuration }} --output-on-failure
3939
- name: Add msbuild to PATH
40-
uses: microsoft/setup-msbuild@v1.1 # https://github.com/marketplace/actions/setup-msbuild
40+
uses: microsoft/setup-msbuild@v2 # https://github.com/marketplace/actions/setup-msbuild
4141
with:
4242
msbuild-architecture: x64
4343
- name: msbuild
@@ -66,7 +66,7 @@ jobs:
6666
cmake --build . --config ${{ matrix.configuration }} -j
6767
cmake --build . --config ${{ matrix.configuration }} --target install
6868
- name: Add msbuild to PATH
69-
uses: microsoft/setup-msbuild@v1.1 # https://github.com/marketplace/actions/setup-msbuild
69+
uses: microsoft/setup-msbuild@v2 # https://github.com/marketplace/actions/setup-msbuild
7070
with:
7171
msbuild-architecture: x64
7272
- name: msbuild

modules/c/j2k/CMakeLists.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ coda_add_tests(
3838
test_j2k_read_region.c
3939
test_j2k_read_tile.c)
4040

41+
coda_add_tests(
42+
MODULE_NAME ${MODULE_NAME}
43+
DIRECTORY "unittests"
44+
UNITTEST
45+
SOURCES
46+
test_j2k_nitf_partial_block.c)
47+
4148
# Build all J2KCompress and J2KDecompress
4249
set(j2k_shared_srcs J2KCompress J2KDecompress)
4350
foreach(j2k_shared ${j2k_shared_srcs})
@@ -52,4 +59,4 @@ foreach(j2k_shared ${j2k_shared_srcs})
5259
set_target_properties(${j2k_shared} PROPERTIES PREFIX "")
5360
endforeach()
5461

55-
install(TARGETS ${j2k_shared_srcs} DESTINATION "share/nitf/plugins")
62+
install(TARGETS ${j2k_shared_srcs} DESTINATION "share/nitf/plugins")

modules/c/j2k/source/OpenJPEGImpl.c

Lines changed: 27 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,6 @@ OpenJPEG_readHeader(OpenJPEGReaderImpl *impl, nrt_Error *error)
517517
opj_codestream_info_v2_t* codeStreamInfo = NULL;
518518
NRT_BOOL rc = NRT_SUCCESS;
519519
OPJ_UINT32 tileWidth, tileHeight;
520-
OPJ_UINT32 imageWidth, imageHeight;
521520

522521
if (!OpenJPEG_setup(impl, &stream, &codec, error))
523522
{
@@ -559,18 +558,6 @@ OpenJPEG_readHeader(OpenJPEGReaderImpl *impl, nrt_Error *error)
559558
goto CATCH_ERROR;
560559
}
561560

562-
/* TODO: We need special handling that's not implemented in readTile() to
563-
* accommodate partial tiles with more than one band. */
564-
imageWidth = image->x1 - image->x0;
565-
imageHeight = image->y1 - image->y0;
566-
if (image->numcomps > 1 &&
567-
(imageWidth % tileWidth != 0 || imageHeight % tileHeight != 0))
568-
{
569-
nrt_Error_init(error, "No image components found", NRT_CTXT,
570-
NRT_ERR_UNK);
571-
goto CATCH_ERROR;
572-
}
573-
574561
if (!impl->container)
575562
{
576563
/* initialize the container */
@@ -951,27 +938,14 @@ OpenJPEGReader_readTile(J2K_USER_DATA *data, uint32_t tileX, uint32_t tileY,
951938
*/
952939
const OPJ_UINT32 thisTileWidth = tileX1 - tileX0;
953940
const OPJ_UINT32 thisTileHeight = tileY1 - tileY0;
954-
if (thisTileWidth < tileWidth)
941+
if (thisTileWidth < tileWidth || thisTileHeight < tileHeight)
955942
{
956-
/* TODO: The current approach below only works for single band
957-
* imagery. For RGB data, I believe it is stored as all
958-
* red, then all green, then all blue, so we would need
959-
* a temp buffer rather than reusing the current buffer.
960-
*/
961-
if (nComponents != 1)
962-
{
963-
nrt_Error_init(
964-
error,
965-
"Partial tile width not implemented for multi-band",
966-
NRT_CTXT, NRT_ERR_UNK);
967-
goto CATCH_ERROR;
968-
}
969-
970943
numBitsPerPixel =
971944
j2k_Container_getPrecision(impl->container, error);
972945
numBytesPerPixel =
973946
(numBitsPerPixel / 8) + (numBitsPerPixel % 8 != 0);
974-
fullBufSize = ((uint64_t)tileWidth) * thisTileHeight * numBytesPerPixel;
947+
fullBufSize = ((uint64_t)tileWidth) * tileHeight *
948+
numBytesPerPixel * nComponents;
975949
}
976950
else
977951
{
@@ -996,7 +970,8 @@ OpenJPEGReader_readTile(J2K_USER_DATA *data, uint32_t tileX, uint32_t tileY,
996970
goto CATCH_ERROR;
997971
}
998972

999-
if (thisTileWidth < tileWidth)
973+
if (buf != NULL &&
974+
(thisTileHeight < tileHeight || thisTileWidth < tileWidth))
1000975
{
1001976
/* We have a tile that isn't as wide as it "should" be
1002977
* Need to add in the extra columns ourselves. By marching
@@ -1005,19 +980,29 @@ OpenJPEGReader_readTile(J2K_USER_DATA *data, uint32_t tileX, uint32_t tileY,
1005980
const size_t srcStride = thisTileWidth * numBytesPerPixel;
1006981
const size_t destStride = tileWidth * numBytesPerPixel;
1007982
const size_t numLeftoverBytes = destStride - srcStride;
1008-
OPJ_UINT32 lastRow = thisTileHeight - 1;
1009-
size_t srcOffset = lastRow * srcStride;
1010-
size_t destOffset = lastRow * destStride;
1011-
OPJ_UINT32 ii;
1012-
uint8_t* bufPtr = buf != NULL ? *buf : NULL;
1013-
1014-
for (ii = 0;
1015-
ii < thisTileHeight;
1016-
++ii, srcOffset -= srcStride, destOffset -= destStride)
983+
int comp;
984+
for (comp = nComponents - 1; comp >= 0; comp--)
1017985
{
1018-
uint8_t* const dest = bufPtr + destOffset;
1019-
memmove(dest, bufPtr + srcOffset, srcStride);
1020-
memset(dest + srcStride, 0, numLeftoverBytes);
986+
size_t srcOffset =
987+
(comp * thisTileHeight + thisTileHeight - 1) *
988+
srcStride;
989+
size_t destOffset =
990+
(comp * tileHeight + thisTileHeight - 1) *
991+
destStride;
992+
OPJ_UINT32 ii;
993+
uint8_t* bufPtr = *buf;
994+
995+
// Zero remaining rows in block (if any)
996+
memset(bufPtr + destOffset + destStride,
997+
0,
998+
(tileHeight - thisTileHeight) * destStride);
999+
for (ii = 0; ii < thisTileHeight;
1000+
++ii, srcOffset -= srcStride, destOffset -= destStride)
1001+
{
1002+
uint8_t* const dest = bufPtr + destOffset;
1003+
memmove(dest, bufPtr + srcOffset, srcStride);
1004+
memset(dest + srcStride, 0, numLeftoverBytes);
1005+
}
10211006
}
10221007
}
10231008
}

modules/c/j2k/unittests/Test.h

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
/* =========================================================================
2+
* This file is part of NITRO
3+
* =========================================================================
4+
*
5+
* (C) Copyright 2004 - 2014, MDA Information Systems LLC
6+
*
7+
* NITRO is free software; you can redistribute it and/or modify
8+
* it under the terms of the GNU Lesser General Public License as published by
9+
* the Free Software Foundation; either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public
18+
* License along with this program; if not, If not,
19+
* see <http://www.gnu.org/licenses/>.
20+
*
21+
*/
22+
23+
#ifndef __TEST_H__
24+
#define __TEST_H__
25+
26+
#ifdef __cplusplus
27+
28+
#include <import/sys.h>
29+
30+
#include <string>
31+
32+
#define CHECK(X) \
33+
X(std::string(#X)); \
34+
std::cout << #X << ": PASSED" << std::endl
35+
#define TEST_ASSERT(X) \
36+
if (!(X)) \
37+
{ \
38+
die_printf("%s (%s,%s,%d): FAILED: Value should not be NULL\n", \
39+
testName.c_str(), \
40+
__FILE__, \
41+
__FUNC__, \
42+
__LINE__) \
43+
}
44+
#define TEST_ASSERT_NULL(X) \
45+
if ((X) != NULL) \
46+
{ \
47+
die_printf("%s (%s,%s,%d): FAILED: Value should be NULL\n", \
48+
testName.c_str(), \
49+
__FILE__, \
50+
__FUNC__, \
51+
__LINE__) \
52+
}
53+
#define TEST_ASSERT_EQ(X1, X2) \
54+
if ((X1) != (X2)) \
55+
{ \
56+
die_printf("%s (%s,%s,%d): FAILED: Recv'd %s, Expected %s\n", \
57+
testName.c_str(), \
58+
__FILE__, \
59+
__FUNC__, \
60+
__LINE__, \
61+
std::to_string(X1).c_str(), \
62+
std::to_string(X2).c_str()) \
63+
}
64+
#define TEST_ASSERT_ALMOST_EQ(X1, X2) \
65+
if (fabs((X1) - (X2)) > std::numeric_limits<float>::epsilon()) \
66+
{ \
67+
die_printf("%s (%s,%s,%d): FAILED: Recv'd %s, Expected %s\n", \
68+
testName.c_str(), \
69+
__FILE__, \
70+
__FUNC__, \
71+
__LINE__, \
72+
std::to_string(X1).c_str(), \
73+
std::to_string(X2).c_str()) \
74+
}
75+
#define TEST_CASE(X) void X(std::string testName)
76+
77+
#else
78+
79+
#include <stdio.h>
80+
#include <stdlib.h>
81+
#include <string.h>
82+
83+
/* Negotiate the 'context' */
84+
#define TEST_FILE __FILE__
85+
#define TEST_LINE __LINE__
86+
#if defined(__GNUC__)
87+
#define TEST_FUNC __PRETTY_FUNCTION__
88+
#elif __STDC_VERSION__ < 199901
89+
#define TEST_FUNC "unknown function"
90+
#else /* Should be c99 */
91+
#define TEST_FUNC __func__
92+
#endif
93+
94+
#define CHECK(X) \
95+
X(#X); \
96+
fprintf(stderr, "%s : PASSED\n", #X);
97+
#define CHECK_ARGS(X) \
98+
X(#X, argc, argv); \
99+
fprintf(stderr, "%s : PASSED\n", #X);
100+
#define TEST_ASSERT(X) \
101+
if (!(X)) \
102+
{ \
103+
fprintf(stderr, \
104+
"%s (%s,%s,%d) : FAILED: Value should not be NULL\n", \
105+
testName, \
106+
TEST_FILE, \
107+
TEST_FUNC, \
108+
TEST_LINE); \
109+
exit(EXIT_FAILURE); \
110+
}
111+
#define TEST_ASSERT_NULL(X) \
112+
if ((X) != NULL) \
113+
{ \
114+
fprintf(stderr, \
115+
"%s (%s,%s,%d) : FAILED: Value should be NULL\n", \
116+
testName, \
117+
TEST_FILE, \
118+
TEST_FUNC, \
119+
TEST_LINE); \
120+
exit(EXIT_FAILURE); \
121+
}
122+
#define TEST_ASSERT_EQ_STR(X1, X2) \
123+
if (strcmp((X1), (X2)) != 0) \
124+
{ \
125+
fprintf(stderr, \
126+
"%s (%s,%s,%d) : FAILED: Recv'd %s, Expected %s\n", \
127+
testName, \
128+
TEST_FILE, \
129+
TEST_FUNC, \
130+
TEST_LINE, \
131+
X1, \
132+
X2); \
133+
exit(EXIT_FAILURE); \
134+
}
135+
#define TEST_ASSERT_EQ_INT(X1, X2) \
136+
if ((X1) != (X2)) \
137+
{ \
138+
fprintf(stderr, \
139+
"%s (%s,%s,%d) : FAILED: Recv'd %d, Expected %d\n", \
140+
testName, \
141+
TEST_FILE, \
142+
TEST_FUNC, \
143+
TEST_LINE, \
144+
(int)X1, \
145+
(int)X2); \
146+
exit(EXIT_FAILURE); \
147+
}
148+
/* TODO use epsilon for comparing floating points */
149+
#define TEST_ASSERT_EQ_FLOAT(X1, X2) \
150+
if (fabs((X1) - (X2)) > .0000001f) \
151+
{ \
152+
fprintf(stderr, \
153+
"%s (%s,%s,%d) : FAILED: Recv'd %f, Expected %f\n", \
154+
testName, \
155+
TEST_FILE, \
156+
TEST_FUNC, \
157+
TEST_LINE, \
158+
X1, \
159+
X2); \
160+
exit(EXIT_FAILURE); \
161+
}
162+
163+
#define TEST_CASE(X) void X(const char* testName)
164+
#define TEST_CASE_ARGS(X) void X(const char* testName, int argc, char** argv)
165+
166+
#define TEST_MAIN(X) \
167+
int main(int argc, char** argv) \
168+
{ \
169+
{ \
170+
(void)argc; \
171+
(void)argv; \
172+
} \
173+
X; \
174+
return 0; \
175+
}
176+
177+
#endif
178+
179+
#endif

0 commit comments

Comments
 (0)