Skip to content

Commit 2a38369

Browse files
authored
Fix endianness when decoding on big endian systems (#72)
1 parent 09decd2 commit 2a38369

File tree

13 files changed

+854
-768
lines changed

13 files changed

+854
-768
lines changed

.github/workflows/pytest-builds.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
strategy:
1313
fail-fast: false
1414
matrix:
15-
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
15+
python-version: ['3.10', '3.11', '3.12', '3.13']
1616
arch: ['x64', 'x86']
1717

1818
steps:
@@ -50,7 +50,7 @@ jobs:
5050
strategy:
5151
fail-fast: false
5252
matrix:
53-
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
53+
python-version: ['3.10', '3.11', '3.12', '3.13']
5454

5555
steps:
5656
- uses: actions/checkout@v4
@@ -86,7 +86,7 @@ jobs:
8686
strategy:
8787
fail-fast: false
8888
matrix:
89-
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
89+
python-version: ['3.10', '3.11', '3.12', '3.13']
9090

9191
steps:
9292
- uses: actions/checkout@v4
@@ -117,7 +117,7 @@ jobs:
117117
pytest --cov=libjpeg --cov-append libjpeg/tests
118118
119119
- name: Switch to pydicom dev and rerun pytest
120-
if: ${{ contains('3.10 3.11 3.12', matrix.python-version) }}
120+
if: ${{ contains('3.10 3.11 3.12 3.13', matrix.python-version) }}
121121
run: |
122122
pip uninstall -y pydicom
123123
pip install git+https://github.com/pydicom/pydicom

.github/workflows/release-wheels.yml

Lines changed: 69 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,6 @@ jobs:
4242
matrix:
4343
include:
4444
# Windows 32 bit
45-
- os: windows-latest
46-
python: 38
47-
platform_id: win32
4845
- os: windows-latest
4946
python: 39
5047
platform_id: win32
@@ -57,11 +54,11 @@ jobs:
5754
- os: windows-latest
5855
python: 312
5956
platform_id: win32
57+
- os: windows-latest
58+
python: 313
59+
platform_id: win32
6060

6161
# Windows 64 bit
62-
- os: windows-latest
63-
python: 38
64-
platform_id: win_amd64
6562
- os: windows-latest
6663
python: 39
6764
platform_id: win_amd64
@@ -74,12 +71,11 @@ jobs:
7471
- os: windows-latest
7572
python: 312
7673
platform_id: win_amd64
74+
- os: windows-latest
75+
python: 313
76+
platform_id: win_amd64
7777

7878
# Linux 64 bit manylinux2014
79-
- os: ubuntu-latest
80-
python: 38
81-
platform_id: manylinux_x86_64
82-
manylinux_image: manylinux2014
8379
- os: ubuntu-latest
8480
python: 39
8581
platform_id: manylinux_x86_64
@@ -96,11 +92,12 @@ jobs:
9692
python: 312
9793
platform_id: manylinux_x86_64
9894
manylinux_image: manylinux2014
95+
- os: ubuntu-latest
96+
python: 313
97+
platform_id: manylinux_x86_64
98+
manylinux_image: manylinux2014
9999

100100
# Linux aarch64
101-
- os: ubuntu-latest
102-
python: 38
103-
platform_id: manylinux_aarch64
104101
- os: ubuntu-latest
105102
python: 39
106103
platform_id: manylinux_aarch64
@@ -113,11 +110,11 @@ jobs:
113110
- os: ubuntu-latest
114111
python: 312
115112
platform_id: manylinux_aarch64
113+
- os: ubuntu-latest
114+
python: 313
115+
platform_id: manylinux_aarch64
116116

117117
# MacOS x86_64
118-
- os: macos-12
119-
python: 38
120-
platform_id: macosx_x86_64
121118
- os: macos-12
122119
python: 39
123120
platform_id: macosx_x86_64
@@ -130,6 +127,9 @@ jobs:
130127
- os: macos-12
131128
python: 312
132129
platform_id: macosx_x86_64
130+
- os: macos-12
131+
python: 313
132+
platform_id: macosx_x86_64
133133

134134
steps:
135135
- uses: actions/checkout@v4
@@ -178,9 +178,6 @@ jobs:
178178
matrix:
179179
include:
180180
# MacOS arm64
181-
- os: macos-14
182-
python: 38
183-
platform_id: macosx_arm64
184181
- os: macos-14
185182
python: 39
186183
platform_id: macosx_arm64
@@ -193,6 +190,9 @@ jobs:
193190
- os: macos-14
194191
python: 312
195192
platform_id: macosx_arm64
193+
- os: macos-14
194+
python: 313
195+
platform_id: macosx_arm64
196196

197197
steps:
198198
- uses: actions/checkout@v4
@@ -222,61 +222,62 @@ jobs:
222222
path: ./dist/*.whl
223223

224224

225-
test-package:
226-
name: Test built package
227-
needs: [ build-wheels, build-sdist, build-wheels-macos-arm64 ]
228-
runs-on: ubuntu-latest
229-
timeout-minutes: 30
230-
strategy:
231-
fail-fast: false
232-
matrix:
233-
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
234-
235-
steps:
236-
- name: Set up Python ${{ matrix.python-version }}
237-
uses: actions/setup-python@v5
238-
with:
239-
python-version: ${{ matrix.python-version }}
240-
241-
- name: Download the wheels
242-
uses: actions/download-artifact@v4
243-
with:
244-
path: dist/
245-
merge-multiple: true
246-
247-
- name: Install from package wheels and test
248-
# If testing wheel builds might need --pre to install dev version
249-
run: |
250-
python -m venv testwhl
251-
source testwhl/bin/activate
252-
python -m pip install -U pip
253-
python -m pip install pytest pydicom pylibjpeg
254-
python -m pip uninstall -y pylibjpeg-libjpeg
255-
python -m pip uninstall -y pylibjpeg-openjpeg
256-
python -m pip install git+https://github.com/pydicom/pylibjpeg-data
257-
python -m pip install -U --pre --find-links dist/ pylibjpeg-libjpeg
258-
python -m pytest --pyargs libjpeg.tests
259-
deactivate
260-
261-
- name: Install from package tarball and test
262-
run: |
263-
python -m venv testsrc
264-
source testsrc/bin/activate
265-
python -m pip install -U pip
266-
python -m pip install pytest pydicom pylibjpeg
267-
python -m pip uninstall -y pylibjpeg-libjpeg
268-
python -m pip uninstall -y pylibjpeg-openjpeg
269-
python -m pip install git+https://github.com/pydicom/pylibjpeg-data
270-
python -m pip install -U dist/pylibjpeg*libjpeg-*.tar.gz
271-
python -m pytest --pyargs libjpeg.tests
272-
deactivate
225+
# test-package:
226+
# name: Test built package
227+
# needs: [ build-wheels, build-sdist, build-wheels-macos-arm64 ]
228+
# runs-on: ubuntu-latest
229+
# timeout-minutes: 30
230+
# strategy:
231+
# fail-fast: false
232+
# matrix:
233+
# python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
234+
#
235+
# steps:
236+
# - name: Set up Python ${{ matrix.python-version }}
237+
# uses: actions/setup-python@v5
238+
# with:
239+
# python-version: ${{ matrix.python-version }}
240+
#
241+
# - name: Download the wheels
242+
# uses: actions/download-artifact@v4
243+
# with:
244+
# path: dist/
245+
# merge-multiple: true
246+
#
247+
# - name: Install from package wheels and test
248+
# # If testing wheel builds might need --pre to install dev version
249+
# run: |
250+
# python -m venv testwhl
251+
# source testwhl/bin/activate
252+
# python -m pip install -U pip
253+
# python -m pip install pytest pydicom pylibjpeg
254+
# python -m pip uninstall -y pylibjpeg-libjpeg
255+
# python -m pip uninstall -y pylibjpeg-openjpeg
256+
# python -m pip install git+https://github.com/pydicom/pylibjpeg-data
257+
# python -m pip install -U --pre --find-links dist/ pylibjpeg-libjpeg
258+
# python -m pytest --pyargs libjpeg.tests
259+
# deactivate
260+
#
261+
# - name: Install from package tarball and test
262+
# run: |
263+
# python -m venv testsrc
264+
# source testsrc/bin/activate
265+
# python -m pip install -U pip
266+
# python -m pip install pytest pydicom pylibjpeg
267+
# python -m pip uninstall -y pylibjpeg-libjpeg
268+
# python -m pip uninstall -y pylibjpeg-openjpeg
269+
# python -m pip install git+https://github.com/pydicom/pylibjpeg-data
270+
# python -m pip install -U dist/pylibjpeg*libjpeg-*.tar.gz
271+
# python -m pytest --pyargs libjpeg.tests
272+
# deactivate
273273

274274
# The pypi upload fails with non-linux containers, so grab the uploaded
275275
# artifacts and run using those
276276
# See: https://github.com/pypa/gh-action-pypi-publish/discussions/15
277277
deploy:
278278
name: Upload wheels to PyPI
279-
needs: [ test-package ]
279+
# needs: [ test-package ]
280+
needs: [ build-wheels, build-sdist, build-wheels-macos-arm64 ]
280281
runs-on: ubuntu-latest
281282
environment:
282283
name: pypi

build.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from pathlib import Path
44
import platform
55
import shutil
6+
from struct import unpack
67
import subprocess
78
import sys
89
from typing import Any, List, Dict
@@ -33,9 +34,17 @@ def build(setup_kwargs: Any) -> Any:
3334
# Skip configuration if running with `sdist`
3435
if 'sdist' not in sys.argv:
3536
opts = get_gcc_args()
37+
if sys.byteorder == "big":
38+
if "-mfpmath=387" in opts["ADDOPTS"]:
39+
opts["ADDOPTS"].remove("-mfpmath=387")
40+
3641
extra_compile_args += opts['ADDOPTS']
3742
extra_link_args += opts['EXTRA_LIBS']
3843

44+
macros = [("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION")]
45+
if unpack("h", b"\x00\x01")[0] == 1:
46+
macros.append(("JPG_BIG_ENDIAN", "1"))
47+
3948
ext = Extension(
4049
'_libjpeg',
4150
[os.fspath(p) for p in get_source_files()],
@@ -47,6 +56,7 @@ def build(setup_kwargs: Any) -> Any:
4756
],
4857
extra_compile_args=extra_compile_args,
4958
extra_link_args=extra_link_args,
59+
define_macros=macros,
5060
)
5161

5262
ext_modules = cythonize(

docs/changes/v2.1.0.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.. _v2.1.0:
2+
3+
2.1.0
4+
=====
5+
6+
* Use ``libjpeg`` v1.67
7+
* Fixed a decoding failure for JPEG 10918 file with an Adobe v101 APP14 marker
8+
9+
2.1.1
10+
=====
11+
12+
* Updated dependencies to allow NumPy > 2.0
13+
* Tests updated to use ``pydicom.pixels``

docs/changes/v2.2.0.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.. _v2.2.0:
2+
3+
2.2.0
4+
=====
5+
6+
* Support NumPy > 2.0 with Python 3.9+

docs/changes/v2.3.0.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.. _v2.3.0:
2+
3+
2.3.0
4+
=====
5+
6+
* Supported Python versions are 3.9 to 3.13
7+
* Added support for decoding on big endian systems

lib/interface/decode.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ std::string Decode(
3030
Parameters
3131
----------
3232
char *inArray
33-
Pointer to the first element of a numpy.ndarray containing the JPEG
34-
data to be decompressed.
33+
Pointer to the first element of a bytearray or numpy.ndarray containing the
34+
JPEG data to be decompressed.
3535
char *outArray
36-
Pointer to the first element of a numpy.ndarray where the decompressed
37-
JPEG data should be written.
36+
Pointer to the first element of bytearray or numpy.ndarray where the
37+
decompressed JPEG data should be written.
3838
int inLength
3939
Length of the input array
4040
int outLength

lib/interface/streamhook.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ JPG_LONG OStreamHook(struct JPG_Hook *hook, struct JPG_TagItem *tags)
8888
static ULONG OpenComponents = 0;
8989
struct StreamMemory *omm = (struct StreamMemory *)(hook->hk_pData);
9090
struct StreamData *out = (struct StreamData *)(omm->omm_pTarget);
91-
// Pointer to the output numpy array, currently at offset out->position
91+
// Pointer to the output bytearray/numpy array, currently at offset out->position
9292
char *oArray = (char *)(out->pData);
9393

9494
UWORD comp = tags->GetTagData(JPGTAG_BIO_COMPONENT);
@@ -323,11 +323,24 @@ JPG_LONG OStreamHook(struct JPG_Hook *hook, struct JPG_TagItem *tags)
323323
}
324324
} else {
325325
// DICOM should always be integer input
326-
// Write pixel data to target
327-
ULONG size = omm->omm_ucPixelType & CTYP_SIZE_MASK;
326+
// Need to byte swap if on a big endian system
327+
#ifdef JPG_BIG_ENDIAN
328+
if (omm->omm_ucPixelType == CTYP_UWORD) {
329+
ULONG count = width * height * omm->omm_usDepth;
330+
UWORD *data = (UWORD *)omm->omm_pMemPtr;
331+
332+
do {
333+
*data = (*data >> 8) | ((*data & 0xff) << 8);
334+
data++;
335+
} while(--count);
336+
}
337+
#endif
338+
339+
// Write the decoded data to the output
340+
// For each pixel
328341
ULONG count = width * height * omm->omm_usDepth;
342+
ULONG size = omm->omm_ucPixelType & CTYP_SIZE_MASK;
329343
UBYTE *mem = (UBYTE *)(omm->omm_pMemPtr);
330-
// For each pixel
331344
for (ULONG ii = 1; ii <= count; ii++) {
332345
// For each byte of the pixel
333346
for (ULONG jj = 1; jj <= size; jj++) {

0 commit comments

Comments
 (0)