Skip to content

Commit a4bc9e3

Browse files
committed
Add files required to cross-compile OpenJPEG
1 parent 2b7c49f commit a4bc9e3

5 files changed

+469
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
openjpeg
2+
out

README.org

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#+FILE_ID: orgfile:42338955-2fed-48e0-b844-da7b50a491ae
2+
* OpenJPEG cross-compiled for JavaScript
3+
:PROPERTIES:
4+
:CUSTOM_ID: id:79a05c19-358f-422c-bcc2-168fa8dc9546
5+
:END:
6+
7+
** Prerequisites
8+
:PROPERTIES:
9+
:CUSTOM_ID: id:6cf2befa-bb64-4bce-93e7-59d189959589
10+
:END:
11+
12+
Software required:
13+
- [[https://emscripten.org/docs/getting_started/downloads.html][emscripten]] (installed via emsdk)
14+
15+
** Build Instructions
16+
:PROPERTIES:
17+
:CUSTOM_ID: id:d5fb1e8f-6543-458c-969a-99263aa9f1dd
18+
:END:
19+
20+
Ensure that you have activated an emscripten SDK. Afterwards, source the emsdk
21+
environment.
22+
#+BEGIN_EXAMPLE
23+
/emsdk/emsdk activate latest
24+
source /emsdk/emsdk_env.sh
25+
#+END_EXAMPLE
26+
27+
Once the environment is set up, clone the [[https://github.com/uclouvain/openjpeg][OpenJPEG official repo]] and checkout
28+
the desired version.
29+
#+BEGIN_EXAMPLE
30+
git clone https://github.com/uclouvain/openjpeg.git
31+
#+END_EXAMPLE
32+
33+
Apply the patch file =openjpeg-js.patch=.
34+
#+BEGIN_EXAMPLE
35+
cd openjpeg/
36+
git apply ../openjpeg-js.patch
37+
#+END_EXAMPLE
38+
39+
After applying the patch, we will now build openjpeg into LLVM bitcode. The
40+
second cmake invocation is to skip the error related to TestEndian from cmake.
41+
Make sure you have sourced the =emsdk_env.sh= as shown above.
42+
#+BEGIN_EXAMPLE
43+
mkdir openjpeg/build
44+
cd openjpeg/build
45+
46+
cmake \
47+
-DCMAKE_TOOLCHAIN_FILE=/path/to/emsdk/fastcomp/emscripten/cmake/Modules/Platform/Emscripten.cmake \
48+
-DBUILD_CODEC=OFF \
49+
-DBUILD_SHARED_LIBS=OFF \
50+
-G'Unix Makefiles' \
51+
-DCMAKE_BUILD_TYPE=Release \
52+
..
53+
54+
cmake .
55+
56+
make
57+
#+END_EXAMPLE
58+
59+
The output will be in =openjpeg/build/bin/libopenjp2.a=.
60+
61+
Once openjpeg has been compiled, it's time to compile the wrapping. Edit
62+
=emcc-compile.sh= and update the =OPENJPEG_ROOT= to the path of the openjpeg
63+
repo from above. You may also adjust the emcc flags as you see fit.
64+
65+
Once edited, just compile! Be sure to have the =emsdk_env.sh= sourced from
66+
above.
67+
#+BEGIN_EXAMPLE
68+
./emcc-compile.sh
69+
#+END_EXAMPLE

emcc-compile.sh

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Editable config
2+
OPENJPEG_ROOT=$HOME/openjpeg-js/openjpeg
3+
OPENJPEG_BUILD_ROOT=$HOME/openjpeg-js/openjpeg/build
4+
OUTPUT_NAME=openJPEG-FixedMemory.js
5+
6+
mkdir -p out
7+
8+
# edit the switches to your desire
9+
emcc \
10+
-I"$OPENJPEG_ROOT"/src/lib/openjp2 \
11+
-I"$OPENJPEG_BUILD_ROOT"/src/lib/openjp2 \
12+
--memory-init-file 0 \
13+
-s WASM=0 \
14+
-s ERROR_ON_UNDEFINED_SYMBOLS=0 \
15+
-s TOTAL_MEMORY=16777216 \
16+
-O3 \
17+
-s EXTRA_EXPORTED_RUNTIME_METHODS=["ccall","cwrap","writeArrayToMemory","getValue"] \
18+
-s NO_FILESYSTEM=1 \
19+
-s EXPORTED_FUNCTIONS="['_opj_version','_opj_decode']" \
20+
-s MODULARIZE=1 \
21+
-s EXPORT_NAME="'OpenJPEG'" \
22+
-o out/"$OUTPUT_NAME" \
23+
"$OPENJPEG_BUILD_ROOT"/bin/libopenjp2.a src/JSOpenJPEGDecoder.c

openjpeg-js.patch

+231
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
diff --git a/CMakeLists.txt b/CMakeLists.txt
2+
index 3ea2424..56f2f18 100644
3+
--- a/CMakeLists.txt
4+
+++ b/CMakeLists.txt
5+
@@ -66,6 +66,7 @@ endif(NOT OPENJPEG_SOVERSION)
6+
set(OPENJPEG_LIBRARY_PROPERTIES
7+
VERSION "${OPENJPEG_VERSION_MAJOR}.${OPENJPEG_VERSION_MINOR}.${OPENJPEG_VERSION_BUILD}"
8+
SOVERSION "${OPENJPEG_SOVERSION}"
9+
+ POSITION_INDEPENDENT_CODE ON
10+
)
11+
12+
# --------------------------------------------------------------------------
13+
diff --git a/src/lib/openjp2/j2k.c b/src/lib/openjp2/j2k.c
14+
index 3d4aa0f..f31d13a 100644
15+
--- a/src/lib/openjp2/j2k.c
16+
+++ b/src/lib/openjp2/j2k.c
17+
@@ -4728,9 +4728,8 @@ static OPJ_BOOL opj_j2k_read_sod(opj_j2k_t *p_j2k,
18+
/* Check enough bytes left in stream before allocation */
19+
if ((OPJ_OFF_T)p_j2k->m_specific_param.m_decoder.m_sot_length >
20+
opj_stream_get_number_byte_left(p_stream)) {
21+
- opj_event_msg(p_manager, EVT_ERROR,
22+
+ opj_event_msg(p_manager, EVT_WARNING,
23+
"Tile part length size inconsistent with stream length\n");
24+
- return OPJ_FALSE;
25+
}
26+
if (p_j2k->m_specific_param.m_decoder.m_sot_length >
27+
UINT_MAX - OPJ_COMMON_CBLK_DATA_EXTRA) {
28+
@@ -5451,8 +5450,8 @@ static OPJ_BOOL opj_j2k_read_unk(opj_j2k_t *p_j2k,
29+
/* Try to read 2 bytes (the next marker ID) from stream and copy them into the buffer*/
30+
if (opj_stream_read_data(p_stream,
31+
p_j2k->m_specific_param.m_decoder.m_header_data, 2, p_manager) != 2) {
32+
- opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
33+
- return OPJ_FALSE;
34+
+ opj_event_msg(p_manager, EVT_WARNING, "No EOC marker. Possibly a truncated stream.\n");
35+
+ return OPJ_TRUE;
36+
}
37+
38+
/* read 2 bytes as the new marker ID*/
39+
diff --git a/src/lib/openjp2/openjpeg.c b/src/lib/openjp2/openjpeg.c
40+
index 7b12303..1cd8d1c 100644
41+
--- a/src/lib/openjp2/openjpeg.c
42+
+++ b/src/lib/openjp2/openjpeg.c
43+
@@ -87,7 +87,130 @@ OPJ_BOOL OPJ_CALLCONV opj_set_error_handler(opj_codec_t * p_codec,
44+
}
45+
46+
/* ---------------------------------------------------------------------- */
47+
+/* Buffer-based */
48+
49+
+static OPJ_SIZE_T
50+
+opj_read_from_buffer (void* pdst, OPJ_SIZE_T len, opj_buffer_info_t* psrc)
51+
+{
52+
+ OPJ_SIZE_T n = psrc->buf + psrc->len - psrc->cur;
53+
+
54+
+ if (n) {
55+
+ if (n > len)
56+
+ n = len;
57+
+
58+
+ memcpy (pdst, psrc->cur, n);
59+
+ psrc->cur += n;
60+
+ }
61+
+ else
62+
+ n = (OPJ_SIZE_T)-1;
63+
+
64+
+ return n;
65+
+}
66+
+
67+
+static OPJ_SIZE_T
68+
+opj_write_to_buffer (void* p_buffer, OPJ_SIZE_T p_nb_bytes,
69+
+ opj_buffer_info_t* p_source_buffer)
70+
+{
71+
+ void* pbuf = p_source_buffer->buf;
72+
+ void* pcur = p_source_buffer->cur;
73+
+
74+
+ OPJ_SIZE_T len = p_source_buffer->len;
75+
+
76+
+ if (0 == len)
77+
+ len = 1;
78+
+
79+
+ OPJ_SIZE_T dist = pcur - pbuf, n = len - dist;
80+
+ assert (dist <= len);
81+
+
82+
+ while (n < p_nb_bytes) {
83+
+ len *= 2;
84+
+ n = len - dist;
85+
+ }
86+
+
87+
+ if (len != p_source_buffer->len) {
88+
+ pbuf = opj_malloc (len);
89+
+
90+
+ if (0 == pbuf)
91+
+ return (OPJ_SIZE_T)-1;
92+
+
93+
+ if (p_source_buffer->buf) {
94+
+ memcpy (pbuf, p_source_buffer->buf, dist);
95+
+ opj_free (p_source_buffer->buf);
96+
+ }
97+
+
98+
+ p_source_buffer->buf = pbuf;
99+
+ p_source_buffer->cur = pbuf + dist;
100+
+ p_source_buffer->len = len;
101+
+ }
102+
+
103+
+ memcpy (p_source_buffer->cur, p_buffer, p_nb_bytes);
104+
+ p_source_buffer->cur += p_nb_bytes;
105+
+
106+
+ return p_nb_bytes;
107+
+}
108+
+
109+
+static OPJ_SIZE_T
110+
+opj_skip_from_buffer (OPJ_SIZE_T len, opj_buffer_info_t* psrc)
111+
+{
112+
+ OPJ_SIZE_T n = psrc->buf + psrc->len - psrc->cur;
113+
+
114+
+ if (n) {
115+
+ if (n > len)
116+
+ n = len;
117+
+
118+
+ psrc->cur += len;
119+
+ }
120+
+ else
121+
+ n = (OPJ_SIZE_T)-1;
122+
+
123+
+ return n;
124+
+}
125+
+
126+
+static OPJ_BOOL
127+
+opj_seek_from_buffer (OPJ_OFF_T len, opj_buffer_info_t* psrc)
128+
+{
129+
+ OPJ_SIZE_T n = psrc->len;
130+
+
131+
+ if (n > len)
132+
+ n = len;
133+
+
134+
+ psrc->cur = psrc->buf + n;
135+
+
136+
+ return OPJ_TRUE;
137+
+}
138+
+
139+
+opj_stream_t* OPJ_CALLCONV
140+
+opj_stream_create_buffer_stream (opj_buffer_info_t* psrc, OPJ_BOOL input)
141+
+{
142+
+ if (!psrc)
143+
+ return 0;
144+
+
145+
+ opj_stream_t* ps = opj_stream_default_create (input);
146+
+
147+
+ if (0 == ps)
148+
+ return 0;
149+
+
150+
+ opj_stream_set_user_data (ps, psrc, 0);
151+
+ opj_stream_set_user_data_length (ps, psrc->len);
152+
+
153+
+ if (input)
154+
+ opj_stream_set_read_function (
155+
+ ps, (opj_stream_read_fn)opj_read_from_buffer);
156+
+ else
157+
+ opj_stream_set_write_function(
158+
+ ps,(opj_stream_write_fn) opj_write_to_buffer);
159+
+
160+
+ opj_stream_set_skip_function (
161+
+ ps, (opj_stream_skip_fn)opj_skip_from_buffer);
162+
+
163+
+ opj_stream_set_seek_function (
164+
+ ps, (opj_stream_seek_fn)opj_seek_from_buffer);
165+
+
166+
+ return ps;
167+
+}
168+
+
169+
+/* ---------------------------------------------------------------------- */
170+
+
171+
static OPJ_SIZE_T opj_read_from_file(void * p_buffer, OPJ_SIZE_T p_nb_bytes,
172+
FILE * p_file)
173+
{
174+
diff --git a/src/lib/openjp2/openjpeg.h b/src/lib/openjp2/openjpeg.h
175+
index dc1e206..688ef80 100644
176+
--- a/src/lib/openjp2/openjpeg.h
177+
+++ b/src/lib/openjp2/openjpeg.h
178+
@@ -82,7 +82,11 @@ Most compilers implement their own version of this keyword ...
179+
# if defined(OPJ_STATIC) /* static library uses "hidden" */
180+
# define OPJ_API __attribute__ ((visibility ("hidden")))
181+
# else
182+
-# define OPJ_API __attribute__ ((visibility ("default")))
183+
+# if defined(EMSCRIPTEN)
184+
+# define OPJ_API __attribute__((used))
185+
+# else
186+
+# define OPJ_API __attribute__ ((visibility ("default")))
187+
+# endif
188+
# endif
189+
# define OPJ_LOCAL __attribute__ ((visibility ("hidden")))
190+
# else
191+
@@ -571,6 +575,12 @@ typedef struct opj_dparameters {
192+
193+
} opj_dparameters_t;
194+
195+
+typedef struct opj_buffer_info {
196+
+ OPJ_BYTE* buf;
197+
+ OPJ_BYTE* cur;
198+
+ OPJ_SIZE_T len;
199+
+} opj_buffer_info_t;
200+
+
201+
202+
/**
203+
* JPEG2000 codec V2.
204+
@@ -1222,6 +1232,9 @@ OPJ_API opj_stream_t* OPJ_CALLCONV opj_stream_create_file_stream(
205+
OPJ_SIZE_T p_buffer_size,
206+
OPJ_BOOL p_is_read_stream);
207+
208+
+OPJ_API opj_stream_t* OPJ_CALLCONV
209+
+opj_stream_create_buffer_stream (opj_buffer_info_t*, OPJ_BOOL);
210+
+
211+
/*
212+
==========================================================
213+
event manager functions definitions
214+
diff --git a/src/lib/openjp2/t2.c b/src/lib/openjp2/t2.c
215+
index 0887b9f..17c9273 100644
216+
--- a/src/lib/openjp2/t2.c
217+
+++ b/src/lib/openjp2/t2.c
218+
@@ -1352,11 +1352,11 @@ static OPJ_BOOL opj_t2_read_packet_data(opj_t2_t* p_t2,
219+
if ((((OPJ_SIZE_T)l_current_data + (OPJ_SIZE_T)l_seg->newlen) <
220+
(OPJ_SIZE_T)l_current_data) ||
221+
(l_current_data + l_seg->newlen > p_src_data + p_max_length)) {
222+
- opj_event_msg(p_manager, EVT_ERROR,
223+
+ opj_event_msg(p_manager, EVT_WARNING,
224+
"read: segment too long (%d) with max (%d) for codeblock %d (p=%d, b=%d, r=%d, c=%d)\n",
225+
l_seg->newlen, p_max_length, cblkno, p_pi->precno, bandno, p_pi->resno,
226+
p_pi->compno);
227+
- return OPJ_FALSE;
228+
+ return OPJ_TRUE;
229+
}
230+
231+
#ifdef USE_JPWL

0 commit comments

Comments
 (0)