Skip to content

Commit

Permalink
Fix endianness when decoding on big endian systems (#72)
Browse files Browse the repository at this point in the history
  • Loading branch information
scaramallion authored Oct 25, 2024
1 parent 09decd2 commit 2a38369
Show file tree
Hide file tree
Showing 13 changed files with 854 additions and 768 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/pytest-builds.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
python-version: ['3.10', '3.11', '3.12', '3.13']
arch: ['x64', 'x86']

steps:
Expand Down Expand Up @@ -50,7 +50,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
python-version: ['3.10', '3.11', '3.12', '3.13']

steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -86,7 +86,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
python-version: ['3.10', '3.11', '3.12', '3.13']

steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -117,7 +117,7 @@ jobs:
pytest --cov=libjpeg --cov-append libjpeg/tests
- name: Switch to pydicom dev and rerun pytest
if: ${{ contains('3.10 3.11 3.12', matrix.python-version) }}
if: ${{ contains('3.10 3.11 3.12 3.13', matrix.python-version) }}
run: |
pip uninstall -y pydicom
pip install git+https://github.com/pydicom/pydicom
Expand Down
137 changes: 69 additions & 68 deletions .github/workflows/release-wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,6 @@ jobs:
matrix:
include:
# Windows 32 bit
- os: windows-latest
python: 38
platform_id: win32
- os: windows-latest
python: 39
platform_id: win32
Expand All @@ -57,11 +54,11 @@ jobs:
- os: windows-latest
python: 312
platform_id: win32
- os: windows-latest
python: 313
platform_id: win32

# Windows 64 bit
- os: windows-latest
python: 38
platform_id: win_amd64
- os: windows-latest
python: 39
platform_id: win_amd64
Expand All @@ -74,12 +71,11 @@ jobs:
- os: windows-latest
python: 312
platform_id: win_amd64
- os: windows-latest
python: 313
platform_id: win_amd64

# Linux 64 bit manylinux2014
- os: ubuntu-latest
python: 38
platform_id: manylinux_x86_64
manylinux_image: manylinux2014
- os: ubuntu-latest
python: 39
platform_id: manylinux_x86_64
Expand All @@ -96,11 +92,12 @@ jobs:
python: 312
platform_id: manylinux_x86_64
manylinux_image: manylinux2014
- os: ubuntu-latest
python: 313
platform_id: manylinux_x86_64
manylinux_image: manylinux2014

# Linux aarch64
- os: ubuntu-latest
python: 38
platform_id: manylinux_aarch64
- os: ubuntu-latest
python: 39
platform_id: manylinux_aarch64
Expand All @@ -113,11 +110,11 @@ jobs:
- os: ubuntu-latest
python: 312
platform_id: manylinux_aarch64
- os: ubuntu-latest
python: 313
platform_id: manylinux_aarch64

# MacOS x86_64
- os: macos-12
python: 38
platform_id: macosx_x86_64
- os: macos-12
python: 39
platform_id: macosx_x86_64
Expand All @@ -130,6 +127,9 @@ jobs:
- os: macos-12
python: 312
platform_id: macosx_x86_64
- os: macos-12
python: 313
platform_id: macosx_x86_64

steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -178,9 +178,6 @@ jobs:
matrix:
include:
# MacOS arm64
- os: macos-14
python: 38
platform_id: macosx_arm64
- os: macos-14
python: 39
platform_id: macosx_arm64
Expand All @@ -193,6 +190,9 @@ jobs:
- os: macos-14
python: 312
platform_id: macosx_arm64
- os: macos-14
python: 313
platform_id: macosx_arm64

steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -222,61 +222,62 @@ jobs:
path: ./dist/*.whl


test-package:
name: Test built package
needs: [ build-wheels, build-sdist, build-wheels-macos-arm64 ]
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']

steps:
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Download the wheels
uses: actions/download-artifact@v4
with:
path: dist/
merge-multiple: true

- name: Install from package wheels and test
# If testing wheel builds might need --pre to install dev version
run: |
python -m venv testwhl
source testwhl/bin/activate
python -m pip install -U pip
python -m pip install pytest pydicom pylibjpeg
python -m pip uninstall -y pylibjpeg-libjpeg
python -m pip uninstall -y pylibjpeg-openjpeg
python -m pip install git+https://github.com/pydicom/pylibjpeg-data
python -m pip install -U --pre --find-links dist/ pylibjpeg-libjpeg
python -m pytest --pyargs libjpeg.tests
deactivate
- name: Install from package tarball and test
run: |
python -m venv testsrc
source testsrc/bin/activate
python -m pip install -U pip
python -m pip install pytest pydicom pylibjpeg
python -m pip uninstall -y pylibjpeg-libjpeg
python -m pip uninstall -y pylibjpeg-openjpeg
python -m pip install git+https://github.com/pydicom/pylibjpeg-data
python -m pip install -U dist/pylibjpeg*libjpeg-*.tar.gz
python -m pytest --pyargs libjpeg.tests
deactivate
# test-package:
# name: Test built package
# needs: [ build-wheels, build-sdist, build-wheels-macos-arm64 ]
# runs-on: ubuntu-latest
# timeout-minutes: 30
# strategy:
# fail-fast: false
# matrix:
# python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
#
# steps:
# - name: Set up Python ${{ matrix.python-version }}
# uses: actions/setup-python@v5
# with:
# python-version: ${{ matrix.python-version }}
#
# - name: Download the wheels
# uses: actions/download-artifact@v4
# with:
# path: dist/
# merge-multiple: true
#
# - name: Install from package wheels and test
# # If testing wheel builds might need --pre to install dev version
# run: |
# python -m venv testwhl
# source testwhl/bin/activate
# python -m pip install -U pip
# python -m pip install pytest pydicom pylibjpeg
# python -m pip uninstall -y pylibjpeg-libjpeg
# python -m pip uninstall -y pylibjpeg-openjpeg
# python -m pip install git+https://github.com/pydicom/pylibjpeg-data
# python -m pip install -U --pre --find-links dist/ pylibjpeg-libjpeg
# python -m pytest --pyargs libjpeg.tests
# deactivate
#
# - name: Install from package tarball and test
# run: |
# python -m venv testsrc
# source testsrc/bin/activate
# python -m pip install -U pip
# python -m pip install pytest pydicom pylibjpeg
# python -m pip uninstall -y pylibjpeg-libjpeg
# python -m pip uninstall -y pylibjpeg-openjpeg
# python -m pip install git+https://github.com/pydicom/pylibjpeg-data
# python -m pip install -U dist/pylibjpeg*libjpeg-*.tar.gz
# python -m pytest --pyargs libjpeg.tests
# deactivate

# The pypi upload fails with non-linux containers, so grab the uploaded
# artifacts and run using those
# See: https://github.com/pypa/gh-action-pypi-publish/discussions/15
deploy:
name: Upload wheels to PyPI
needs: [ test-package ]
# needs: [ test-package ]
needs: [ build-wheels, build-sdist, build-wheels-macos-arm64 ]
runs-on: ubuntu-latest
environment:
name: pypi
Expand Down
10 changes: 10 additions & 0 deletions build.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from pathlib import Path
import platform
import shutil
from struct import unpack
import subprocess
import sys
from typing import Any, List, Dict
Expand Down Expand Up @@ -33,9 +34,17 @@ def build(setup_kwargs: Any) -> Any:
# Skip configuration if running with `sdist`
if 'sdist' not in sys.argv:
opts = get_gcc_args()
if sys.byteorder == "big":
if "-mfpmath=387" in opts["ADDOPTS"]:
opts["ADDOPTS"].remove("-mfpmath=387")

extra_compile_args += opts['ADDOPTS']
extra_link_args += opts['EXTRA_LIBS']

macros = [("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION")]
if unpack("h", b"\x00\x01")[0] == 1:
macros.append(("JPG_BIG_ENDIAN", "1"))

ext = Extension(
'_libjpeg',
[os.fspath(p) for p in get_source_files()],
Expand All @@ -47,6 +56,7 @@ def build(setup_kwargs: Any) -> Any:
],
extra_compile_args=extra_compile_args,
extra_link_args=extra_link_args,
define_macros=macros,
)

ext_modules = cythonize(
Expand Down
13 changes: 13 additions & 0 deletions docs/changes/v2.1.0.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.. _v2.1.0:

2.1.0
=====

* Use ``libjpeg`` v1.67
* Fixed a decoding failure for JPEG 10918 file with an Adobe v101 APP14 marker

2.1.1
=====

* Updated dependencies to allow NumPy > 2.0
* Tests updated to use ``pydicom.pixels``
6 changes: 6 additions & 0 deletions docs/changes/v2.2.0.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.. _v2.2.0:

2.2.0
=====

* Support NumPy > 2.0 with Python 3.9+
7 changes: 7 additions & 0 deletions docs/changes/v2.3.0.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.. _v2.3.0:

2.3.0
=====

* Supported Python versions are 3.9 to 3.13
* Added support for decoding on big endian systems
8 changes: 4 additions & 4 deletions lib/interface/decode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ std::string Decode(
Parameters
----------
char *inArray
Pointer to the first element of a numpy.ndarray containing the JPEG
data to be decompressed.
Pointer to the first element of a bytearray or numpy.ndarray containing the
JPEG data to be decompressed.
char *outArray
Pointer to the first element of a numpy.ndarray where the decompressed
JPEG data should be written.
Pointer to the first element of bytearray or numpy.ndarray where the
decompressed JPEG data should be written.
int inLength
Length of the input array
int outLength
Expand Down
21 changes: 17 additions & 4 deletions lib/interface/streamhook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ JPG_LONG OStreamHook(struct JPG_Hook *hook, struct JPG_TagItem *tags)
static ULONG OpenComponents = 0;
struct StreamMemory *omm = (struct StreamMemory *)(hook->hk_pData);
struct StreamData *out = (struct StreamData *)(omm->omm_pTarget);
// Pointer to the output numpy array, currently at offset out->position
// Pointer to the output bytearray/numpy array, currently at offset out->position
char *oArray = (char *)(out->pData);

UWORD comp = tags->GetTagData(JPGTAG_BIO_COMPONENT);
Expand Down Expand Up @@ -323,11 +323,24 @@ JPG_LONG OStreamHook(struct JPG_Hook *hook, struct JPG_TagItem *tags)
}
} else {
// DICOM should always be integer input
// Write pixel data to target
ULONG size = omm->omm_ucPixelType & CTYP_SIZE_MASK;
// Need to byte swap if on a big endian system
#ifdef JPG_BIG_ENDIAN
if (omm->omm_ucPixelType == CTYP_UWORD) {
ULONG count = width * height * omm->omm_usDepth;
UWORD *data = (UWORD *)omm->omm_pMemPtr;

do {
*data = (*data >> 8) | ((*data & 0xff) << 8);
data++;
} while(--count);
}
#endif

// Write the decoded data to the output
// For each pixel
ULONG count = width * height * omm->omm_usDepth;
ULONG size = omm->omm_ucPixelType & CTYP_SIZE_MASK;
UBYTE *mem = (UBYTE *)(omm->omm_pMemPtr);
// For each pixel
for (ULONG ii = 1; ii <= count; ii++) {
// For each byte of the pixel
for (ULONG jj = 1; jj <= size; jj++) {
Expand Down
Loading

0 comments on commit 2a38369

Please sign in to comment.