Skip to content

Commit 41e3cb2

Browse files
Merge pull request #1 from VectorDCM/build/add-msvc-wasm-scripts
Add C++ WASM build and MSVC-based build support
2 parents 30fd12c + 09d274e commit 41e3cb2

6 files changed

Lines changed: 408 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,102 @@ jobs:
6565
path: |
6666
build/${{env.BUILD_TYPE}}
6767
compression-level: 9
68+
69+
build-windows-msvc2022:
70+
runs-on: windows-latest
71+
steps:
72+
- name: Checkout repository
73+
uses: actions/checkout@v5
74+
- name: Setup MSVC 2022 environment
75+
uses: ilammy/msvc-dev-cmd@v1
76+
- name: Build library with MSVC 2022
77+
shell: cmd
78+
run: build.bat %BUILD_TYPE%
79+
- name: Run tests (MSVC 2022)
80+
shell: pwsh
81+
run: ./build/tests/${{env.BUILD_TYPE}}/dcmtkhtj2k_tests.exe
82+
- name: Archive MSVC 2022 artifacts
83+
uses: actions/upload-artifact@v4
84+
with:
85+
name: dcmtk-htj2k-build-windows-msvc2022
86+
path: |
87+
build/${{env.BUILD_TYPE}}
88+
compression-level: 9
89+
90+
build-windows-wasm:
91+
runs-on: windows-latest
92+
steps:
93+
- name: Checkout repository
94+
uses: actions/checkout@v5
95+
- name: Setup Emscripten SDK
96+
uses: mymindstorm/setup-emsdk@v14
97+
with:
98+
version: latest
99+
- name: Install MinGW
100+
shell: pwsh
101+
run: choco install mingw --no-progress -y
102+
- name: Ensure MinGW make on PATH
103+
shell: pwsh
104+
run: |
105+
# Prefer existing PATH if mingw32-make is already available.
106+
$makeCmd = Get-Command mingw32-make.exe -ErrorAction SilentlyContinue
107+
if (-not $makeCmd) {
108+
$candidate = Get-ChildItem `
109+
-Path "C:\ProgramData\chocoportable\lib\mingw\tools\install" `
110+
-Recurse -Filter "mingw32-make.exe" `
111+
-ErrorAction SilentlyContinue |
112+
Select-Object -First 1
113+
if (-not $candidate) {
114+
throw "mingw32-make.exe not found after MinGW install."
115+
}
116+
$binDir = Split-Path $candidate.FullName -Parent
117+
# In GitHub Actions, update PATH for subsequent steps via GITHUB_PATH.
118+
# When running locally, GITHUB_PATH/GITHUB_ENV won't exist, so we update $env:PATH directly.
119+
if ($env:GITHUB_PATH) {
120+
$binDir | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
121+
} else {
122+
$env:PATH = "$binDir;$env:PATH"
123+
}
124+
}
125+
- name: Verify Emscripten and MinGW tools
126+
shell: cmd
127+
run: |
128+
setlocal EnableExtensions
129+
set "EMSDK_ENV="
130+
if defined EMSDK if exist "%EMSDK%\emsdk_env.bat" set "EMSDK_ENV=%EMSDK%\emsdk_env.bat"
131+
if not defined EMSDK_ENV if exist "C:\emsdk\emsdk_env.bat" set "EMSDK_ENV=C:\emsdk\emsdk_env.bat"
132+
if not defined EMSDK_ENV if exist "%USERPROFILE%\.emsdk\emsdk_env.bat" set "EMSDK_ENV=%USERPROFILE%\.emsdk\emsdk_env.bat"
133+
if not defined EMSDK_ENV if exist "%USERPROFILE%\emsdk\emsdk_env.bat" set "EMSDK_ENV=%USERPROFILE%\emsdk\emsdk_env.bat"
134+
135+
if defined EMSDK_ENV (
136+
call "%EMSDK_ENV%" >nul
137+
)
138+
139+
where emcc
140+
emcc -v
141+
where emcmake
142+
emcmake --version
143+
where mingw32-make
144+
mingw32-make --version
145+
- name: Build WASM library
146+
shell: cmd
147+
run: |
148+
setlocal EnableExtensions
149+
set "EMSDK_ENV="
150+
if defined EMSDK if exist "%EMSDK%\emsdk_env.bat" set "EMSDK_ENV=%EMSDK%\emsdk_env.bat"
151+
if not defined EMSDK_ENV if exist "C:\emsdk\emsdk_env.bat" set "EMSDK_ENV=C:\emsdk\emsdk_env.bat"
152+
if not defined EMSDK_ENV if exist "%USERPROFILE%\.emsdk\emsdk_env.bat" set "EMSDK_ENV=%USERPROFILE%\.emsdk\emsdk_env.bat"
153+
if not defined EMSDK_ENV if exist "%USERPROFILE%\emsdk\emsdk_env.bat" set "EMSDK_ENV=%USERPROFILE%\emsdk\emsdk_env.bat"
154+
155+
if defined EMSDK_ENV (
156+
call "%EMSDK_ENV%" >nul
157+
)
158+
159+
build_wasm.bat
160+
- name: Archive WASM artifacts
161+
uses: actions/upload-artifact@v4
162+
with:
163+
name: dcmtk-htj2k-build-windows-wasm
164+
path: |
165+
build_wasm/${{env.BUILD_TYPE}}
166+
compression-level: 9

README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ DCMTK-HTJ2K provides HTJ2K codec support for [DCMTK](https://github.com/DCMTK/dc
1919
- **DCMTK Integration**: Seamless integration with DCMTK codec framework.
2020
- **Configurable Parameters**: Support for codeblock dimensions, progression order, number of decompositions, fragment sizes, and encoding options.
2121
- **Cross Platform**: Supports Linux, macOS, and Windows builds.
22+
- **WebAssembly**: Optional Emscripten build producing a C++ static library that you can link into other WebAssembly projects to build wasm for browser or Node.js (see [Building for WebAssembly](#building-for-webassembly-wasm)).
2223

2324
## Dependencies
2425

@@ -41,6 +42,50 @@ The provided build script automatically downloads, builds, and installs dependen
4142
- Header files to `build/ReleaseOrDebug/include/DCMTKHTJ2K/`.
4243
- CMake configuration files to `build/ReleaseOrDebug/lib/cmake/DCMTKHTJ2K/`.
4344

45+
### Windows (MSVC)
46+
47+
From **x64 Native Tools Command Prompt for VS 2022** (or with MSVC and CMake on PATH):
48+
49+
```batch
50+
build.bat [Release|Debug]
51+
```
52+
53+
Defaults to `Release` if omitted. Output is under `build\Release` or `build\Debug`.
54+
55+
### Building for WebAssembly (WASM)
56+
57+
The project can be built for WebAssembly using Emscripten. The result is a C++ static library, not a standalone wasm binary. You link this library into your own Emscripten project to produce the final wasm file for browser or Node.js.
58+
59+
**Prerequisites**
60+
61+
- [Emscripten SDK (emsdk)](https://emscripten.org/docs/getting_started/downloads.html) — installed and on PATH (`emsdk.bat` available)
62+
- [CMake](https://cmake.org/) (3.12+)
63+
- [MinGW](https://www.mingw-w64.org/) with `mingw32-make` on PATH
64+
65+
**Build**
66+
67+
From the project root (e.g. in a normal Command Prompt or PowerShell where emsdk and MinGW are on PATH):
68+
69+
```batch
70+
build_wasm.bat
71+
```
72+
73+
This script will:
74+
75+
1. Clone (if needed) **DCMTK** and **OpenJPH** into `ots_wasm/`
76+
2. Check out DCMTK 3.6.9 and OpenJPH 0.26.0
77+
3. Apply the Emscripten compatibility patch to DCMTK (see `patches/`)
78+
4. Build DCMTK and OpenJPH with Emscripten, then build DCMTK-HTJ2K
79+
80+
**Output**
81+
82+
- Libraries and headers: `build_wasm\Release\` (lib, include, CMake config)
83+
- Static library: `build_wasm\Release\lib\libDCMTKHTJ2K.a` — Emscripten-built archive; link this into your application’s Emscripten build to produce the final wasm file.
84+
85+
**Patches**
86+
87+
- `patches/dcmtk-ofwhere-emscripten.patch` — adds an Emscripten implementation of `OFgetExecutablePath` in DCMTK’s `ofwhere.c` (no real executable path in WASM). See `patches/README.md` for details.
88+
4489
## Usage
4590

4691
### Registering Encoder

build.bat

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
@echo off
2+
setlocal EnableDelayedExpansion
3+
4+
REM Run from "x64 Native Tools Command Prompt for VS 2022" so that MSVC and CMake are available.
5+
REM Or ensure MSVC 2022 and CMake are on PATH.
6+
7+
if "%1"=="Debug" (
8+
set "BUILD_TYPE=Debug"
9+
) else (
10+
set "BUILD_TYPE=Release"
11+
)
12+
13+
set "BUILD_DIR=%CD%\build"
14+
set "BUILD_DIR_LIB=%CD%\build\%BUILD_TYPE%"
15+
set "OTS_DEV_SPACE=%CD%\ots"
16+
17+
if not exist "%OTS_DEV_SPACE%" mkdir "%OTS_DEV_SPACE%"
18+
cd /d "%OTS_DEV_SPACE%"
19+
20+
if not exist "dcmtk" git clone https://github.com/DCMTK/dcmtk.git
21+
cd dcmtk
22+
git fetch
23+
git checkout -f DCMTK-3.6.9
24+
if not exist "%BUILD_TYPE%" mkdir "%BUILD_TYPE%"
25+
cd %BUILD_TYPE%
26+
27+
cmake .. -DCMAKE_BUILD_TYPE=%BUILD_TYPE% ^
28+
-DCMAKE_CXX_STANDARD=11 ^
29+
-DDCMTK_MODULES=ofstd;oficonv;oflog;dcmdata;dcmimgle;dcmimage;dcmjpeg;dcmjpls ^
30+
-DDCMTK_ENABLE_STL=OFF ^
31+
-DDCMTK_WIDE_CHAR_FILE_IO_FUNCTIONS=OFF ^
32+
-DDCMTK_DEFAULT_DICT=builtin ^
33+
-DDCMTK_WITH_TIFF=OFF ^
34+
-DDCMTK_WITH_PNG=OFF ^
35+
-DDCMTK_WITH_OPENSSL=OFF ^
36+
-DDCMTK_WITH_XML=OFF ^
37+
-DDCMTK_WITH_ZLIB=OFF ^
38+
-DDCMTK_WITH_SNDFILE=OFF ^
39+
-DDCMTK_WITH_ICONV=ON ^
40+
-DDCMTK_WITH_WRAP=OFF ^
41+
-DBUILD_APPS=OFF ^
42+
-DCMAKE_INSTALL_PREFIX=%OTS_DEV_SPACE%/dcmtk/%BUILD_TYPE% ^
43+
-DCMAKE_POLICY_DEFAULT_CMP0091=NEW ^
44+
-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded$^<$^<CONFIG:Debug^>:Debug^>
45+
46+
cmake --build . --parallel %NUMBER_OF_PROCESSORS% --config %BUILD_TYPE%
47+
cmake --install . --config %BUILD_TYPE%
48+
49+
cd /d "%OTS_DEV_SPACE%"
50+
if not exist "openjph" git clone https://github.com/aous72/openjph.git
51+
cd openjph
52+
git fetch
53+
git checkout -f 0.26.0
54+
if not exist "%BUILD_TYPE%" mkdir "%BUILD_TYPE%"
55+
cd %BUILD_TYPE%
56+
57+
cmake .. -DCMAKE_BUILD_TYPE=%BUILD_TYPE% ^
58+
-DCMAKE_CXX_STANDARD=11 ^
59+
-DBUILD_SHARED_LIBS=OFF ^
60+
-DOJPH_BUILD_EXECUTABLES=OFF ^
61+
-DCMAKE_INSTALL_PREFIX=%OTS_DEV_SPACE%/openjph/%BUILD_TYPE% ^
62+
-DCMAKE_POLICY_DEFAULT_CMP0091=NEW ^
63+
-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded$^<$^<CONFIG:Debug^>:Debug^>
64+
65+
cmake --build . --parallel %NUMBER_OF_PROCESSORS% --config %BUILD_TYPE%
66+
cmake --install . --config %BUILD_TYPE%
67+
68+
if not exist "%BUILD_DIR%" mkdir "%BUILD_DIR%"
69+
if not exist "%BUILD_DIR_LIB%" mkdir "%BUILD_DIR_LIB%"
70+
cd /d "%BUILD_DIR%"
71+
72+
cmake .. -DCMAKE_BUILD_TYPE=%BUILD_TYPE% ^
73+
-DCMAKE_CXX_STANDARD=11 ^
74+
-DBUILD_SHARED_LIBS=OFF ^
75+
-DBUILD_TESTING=ON ^
76+
-DDCMTK_ROOT=%OTS_DEV_SPACE%/dcmtk/%BUILD_TYPE% ^
77+
-DOPENJPH_DIR=%OTS_DEV_SPACE%/openjph/%BUILD_TYPE%/lib/cmake/openjph ^
78+
-DDCMTKHTJ2K_ROOT=%OTS_DEV_SPACE%/dcmtkhtj2k/%BUILD_TYPE% ^
79+
-DCMAKE_INSTALL_PREFIX=%BUILD_DIR_LIB% ^
80+
-DCMAKE_POLICY_DEFAULT_CMP0091=NEW ^
81+
-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded$^<$^<CONFIG:Debug^>:Debug^>
82+
83+
cmake --build . --parallel %NUMBER_OF_PROCESSORS% --config %BUILD_TYPE%
84+
cmake --install . --config %BUILD_TYPE%
85+
86+
endlocal

build_wasm.bat

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
@echo off
2+
setlocal EnableDelayedExpansion
3+
4+
REM Build DCMTK-HTJ2K for WebAssembly using Emscripten (emsdk latest) and mingw32-make.
5+
REM Prerequisites: Git, CMake, MinGW (mingw32-make on PATH), and emsdk installed.
6+
REM Ensure emsdk and MinGW are available on PATH.
7+
8+
where emsdk.bat >nul 2>nul
9+
if errorlevel 1 (
10+
echo Warning: emsdk not found on PATH.
11+
echo Install emsdk or Add the EMSDK to PATH.
12+
echo Install emsdk from: https://emscripten.org/docs/getting_started/downloads.html
13+
exit /b 1
14+
)
15+
16+
REM Ensure mingw32-make is available (MinGW bin on PATH)
17+
where mingw32-make >nul 2>nul
18+
if errorlevel 1 (
19+
echo Warning: mingw32-make not found on PATH. Add MinGW to PATH.
20+
exit /b 1
21+
)
22+
23+
set "PROJECT_ROOT=%CD%"
24+
set "BUILD_TYPE=Release"
25+
set "OTS_WASM=%CD%\ots_wasm"
26+
set "BUILD_DIR=%CD%\build_wasm"
27+
set "BUILD_DIR_LIB=%CD%\build_wasm\%BUILD_TYPE%"
28+
29+
30+
if not exist "%OTS_WASM%" mkdir "%OTS_WASM%"
31+
cd /d "%OTS_WASM%"
32+
33+
REM ----- DCMTK (WASM) -----
34+
if not exist "dcmtk" git clone https://github.com/DCMTK/dcmtk.git
35+
cd dcmtk
36+
git fetch
37+
git checkout -f DCMTK-3.6.9
38+
REM Apply Emscripten fix for ofwhere.c (DCMTK unchanged; patch applied at build time)
39+
call git apply "%PROJECT_ROOT%\patches\dcmtk-ofwhere-emscripten.patch"
40+
if errorlevel 1 (
41+
echo ERROR: Failed to apply patches\dcmtk-ofwhere-emscripten.patch
42+
echo Ensure the patch matches your DCMTK version.
43+
exit /b 1
44+
)
45+
if not exist "%BUILD_TYPE%" mkdir "%BUILD_TYPE%"
46+
cd %BUILD_TYPE%
47+
48+
call emcmake cmake .. -G "MinGW Makefiles" -DCMAKE_MAKE_PROGRAM=mingw32-make ^
49+
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 ^
50+
-DCMAKE_BUILD_TYPE=%BUILD_TYPE% ^
51+
-DCMAKE_CXX_STANDARD=11 ^
52+
-DDCMTK_MODULES=ofstd;oficonv;oflog;dcmdata;dcmimgle;dcmimage;dcmjpeg;dcmjpls ^
53+
-DDCMTK_ENABLE_STL=OFF ^
54+
-DDCMTK_WIDE_CHAR_FILE_IO_FUNCTIONS=OFF ^
55+
-DDCMTK_DEFAULT_DICT=builtin ^
56+
-DDCMTK_WITH_TIFF=OFF ^
57+
-DDCMTK_WITH_PNG=OFF ^
58+
-DDCMTK_WITH_OPENSSL=OFF ^
59+
-DDCMTK_WITH_XML=OFF ^
60+
-DDCMTK_WITH_ZLIB=OFF ^
61+
-DDCMTK_WITH_SNDFILE=OFF ^
62+
-DDCMTK_WITH_ICONV=ON ^
63+
-DDCMTK_WITH_WRAP=OFF ^
64+
-DBUILD_APPS=OFF ^
65+
-DCMAKE_INSTALL_PREFIX=%OTS_WASM%/dcmtk/%BUILD_TYPE% ^
66+
-DCMAKE_POLICY_DEFAULT_CMP0091=NEW
67+
REM Continue to make even if cmake returned non-zero (e.g. missing BISON/FLEX)
68+
69+
call emmake mingw32-make -j%NUMBER_OF_PROCESSORS%
70+
if errorlevel 1 (echo ERROR: DCMTK make failed. & exit /b 1)
71+
call emmake mingw32-make install
72+
if errorlevel 1 (echo ERROR: DCMTK install failed. & exit /b 1)
73+
74+
cd /d "%OTS_WASM%"
75+
76+
REM ----- OpenJPH (WASM) -----
77+
if not exist "openjph" git clone https://github.com/aous72/openjph.git
78+
cd openjph
79+
git fetch
80+
git checkout -f 0.26.0
81+
if not exist "%BUILD_TYPE%" mkdir "%BUILD_TYPE%"
82+
cd %BUILD_TYPE%
83+
84+
call emcmake cmake .. -G "MinGW Makefiles" -DCMAKE_MAKE_PROGRAM=mingw32-make ^
85+
-DCMAKE_BUILD_TYPE=%BUILD_TYPE% ^
86+
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 ^
87+
-DCMAKE_CXX_STANDARD=11 ^
88+
-DBUILD_SHARED_LIBS=OFF ^
89+
-DOJPH_BUILD_EXECUTABLES=OFF ^
90+
-DCMAKE_INSTALL_PREFIX=%OTS_WASM%/openjph/%BUILD_TYPE% ^
91+
-DCMAKE_POLICY_DEFAULT_CMP0091=NEW
92+
93+
call emmake mingw32-make -j%NUMBER_OF_PROCESSORS%
94+
if errorlevel 1 (echo ERROR: OpenJPH make failed. & exit /b 1)
95+
call emmake mingw32-make install
96+
if errorlevel 1 (echo ERROR: OpenJPH install failed. & exit /b 1)
97+
98+
REM ----- Main project (DCMTK-HTJ2K) WASM -----
99+
if not exist "%BUILD_DIR%" mkdir "%BUILD_DIR%"
100+
if not exist "%BUILD_DIR_LIB%" mkdir "%BUILD_DIR_LIB%"
101+
cd /d "%BUILD_DIR%"
102+
103+
call emcmake cmake .. -G "MinGW Makefiles" -DCMAKE_MAKE_PROGRAM=mingw32-make ^
104+
-DCMAKE_BUILD_TYPE=%BUILD_TYPE% ^
105+
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 ^
106+
-DCMAKE_CXX_STANDARD=11 ^
107+
-DBUILD_SHARED_LIBS=OFF ^
108+
-DBUILD_TESTING=OFF ^
109+
-DCMAKE_PREFIX_PATH=%OTS_WASM%/dcmtk/%BUILD_TYPE% ^
110+
-DDCMTK_DIR=%OTS_WASM%/dcmtk/%BUILD_TYPE%/lib/cmake/dcmtk ^
111+
-DDCMTK_ROOT=%OTS_WASM%/dcmtk/%BUILD_TYPE% ^
112+
-DOPENJPH_DIR=%OTS_WASM%/openjph/%BUILD_TYPE%/lib/cmake/openjph ^
113+
-DDCMTKHTJ2K_ROOT=%OTS_WASM%/dcmtkhtj2k/%BUILD_TYPE% ^
114+
-DCMAKE_INSTALL_PREFIX=%BUILD_DIR_LIB% ^
115+
-DCMAKE_POLICY_DEFAULT_CMP0091=NEW
116+
117+
call emmake mingw32-make -j%NUMBER_OF_PROCESSORS%
118+
if errorlevel 1 (echo ERROR: Main project make failed. & exit /b 1)
119+
call emmake mingw32-make install
120+
if errorlevel 1 (echo ERROR: Main project install failed. & exit /b 1)
121+
122+
cd /d "%~dp0"
123+
echo.
124+
echo WASM build complete. Output in build_wasm\%BUILD_TYPE%
125+
endlocal

patches/README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Patches for DCMTK-HTJ2K Builds
2+
3+
Patches applied at build time to third-party sources. They are intended to be applied on top of the exact versions used by the build scripts.
4+
5+
## dcmtk-ofwhere-emscripten.patch
6+
7+
**Target:** DCMTK `ofstd/libsrc/ofwhere.c`
8+
**Used by:** `build_wasm.bat` (WebAssembly / Emscripten build)
9+
10+
**Purpose:** DCMTK’s `ofwhere.c` implements `OFgetExecutablePath()` per platform. Emscripten/WASM has no real executable path. Without this patch, the Emscripten build would hit the `#else` branch and fail with “unsupported platform”.
11+
12+
**Change:** Adds an `#elif defined(__EMSCRIPTEN__)` branch that returns a placeholder path (`"."`) and a zero `dirname_length`, so the rest of DCMTK can build and run under Emscripten.
13+
14+
**Apply manually (if needed):**
15+
16+
```bash
17+
cd ots_wasm/dcmtk
18+
git apply /path/to/dcmtk-htj2k/patches/dcmtk-ofwhere-emscripten.patch
19+
```
20+
21+
If you use a different DCMTK version, the patch may need to be updated to match the current `ofwhere.c` (e.g. line numbers or surrounding `#if` blocks).

0 commit comments

Comments
 (0)