Skip to content

Commit 3a85dcb

Browse files
authored
rawpy: Add version 0.27.0 (#2193)
1 parent c7ccbbe commit 3a85dcb

3 files changed

Lines changed: 338 additions & 0 deletions

File tree

‎.github/workflows/build-rawpy.yml‎

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# SPDX-FileCopyrightText: 2026 The RISE Project
2+
# SPDX-License-Identifier: MIT
3+
---
4+
name: Build rawpy wheels (riscv64)
5+
6+
on:
7+
workflow_dispatch:
8+
inputs:
9+
version:
10+
description: 'Version glob to (re)build; empty builds every version of docs/packages/rawpy.yaml not released yet'
11+
required: false
12+
default: ''
13+
pull_request:
14+
branches: [main]
15+
paths:
16+
- '.github/workflows/build-rawpy.yml'
17+
- 'docs/packages/rawpy.yaml'
18+
- 'patches/rawpy/**'
19+
push:
20+
branches: [main]
21+
paths:
22+
- '.github/workflows/build-rawpy.yml'
23+
- 'docs/packages/rawpy.yaml'
24+
- 'patches/rawpy/**'
25+
26+
concurrency:
27+
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
28+
cancel-in-progress: true
29+
30+
permissions:
31+
contents: read # to fetch code (actions/checkout)
32+
33+
env:
34+
MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64
35+
36+
jobs:
37+
setup:
38+
uses: $/.github/workflows/_setup.yml
39+
with:
40+
package: rawpy
41+
version: ${{ inputs.version }}
42+
43+
build_wheels:
44+
needs: [setup]
45+
if: needs.setup.outputs.versions != '[]'
46+
name: Build rawpy ${{ matrix.version }} ${{ matrix.python }}-manylinux_riscv64
47+
runs-on: ubuntu-24.04-riscv
48+
strategy:
49+
fail-fast: false
50+
matrix:
51+
version: ${{ fromJSON(needs.setup.outputs.versions) }}
52+
python: ["cp312", "cp313", "cp314", "cp314t"]
53+
54+
env:
55+
RAWPY_VERSION: ${{ matrix.version }}
56+
57+
steps:
58+
- name: Checkout rawpy v${{ env.RAWPY_VERSION }}
59+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
60+
with:
61+
repository: letmaik/rawpy
62+
ref: v${{ env.RAWPY_VERSION }}
63+
submodules: true
64+
persist-credentials: false
65+
66+
- name: Checkout python-wheels
67+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
68+
with:
69+
path: python-wheels
70+
persist-credentials: false
71+
72+
- name: Patch rawpy source
73+
run: git apply python-wheels/patches/rawpy/${{ env.RAWPY_VERSION }}/*.patch
74+
75+
- name: Build wheels
76+
uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0
77+
with:
78+
output-dir: wheelhouse/
79+
only: ${{ matrix.python }}-manylinux_riscv64
80+
env:
81+
CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }}
82+
# Rocky 10 riscv64 ships lcms2/jasper directly; its libjpeg-turbo lacks the
83+
# JPEG8 ABI (DNG lossy codec) upstream's own build-linux.sh gets by building
84+
# from source with -DWITH_JPEG8=ON, so only that one is built the same way
85+
# upstream does (riscv64 has no libjpeg-turbo SIMD kernels, hence -DWITH_SIMD=0).
86+
CIBW_BEFORE_BUILD: >
87+
dnf install -y zlib-ng-compat-devel lcms2-devel jasper-devel &&
88+
curl -fsSL -o /tmp/libjpeg-turbo.tar.gz
89+
https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/3.1.3/libjpeg-turbo-3.1.3.tar.gz &&
90+
echo "075920b826834ac4ddf97661cc73491047855859affd671d52079c6867c1c6c0 /tmp/libjpeg-turbo.tar.gz" | sha256sum -c - &&
91+
tar xzf /tmp/libjpeg-turbo.tar.gz -C /tmp &&
92+
cmake -S /tmp/libjpeg-turbo-3.1.3 -B /tmp/libjpeg-turbo-3.1.3/build
93+
-DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release
94+
-DENABLE_SHARED=ON -DENABLE_STATIC=OFF -DWITH_JPEG8=ON -DWITH_SIMD=0 &&
95+
cmake --build /tmp/libjpeg-turbo-3.1.3/build -j"$(nproc)" --target install &&
96+
rm -f /usr/include/jconfig-32.h /usr/include/jconfig-64.h &&
97+
curl -fsSL -o {project}/LICENSE.lcms2 https://raw.githubusercontent.com/mm2/Little-CMS/lcms2.16/LICENSE &&
98+
curl -fsSL -o {project}/LICENSE.libjpeg-turbo https://raw.githubusercontent.com/libjpeg-turbo/libjpeg-turbo/3.1.3/LICENSE.md &&
99+
curl -fsSL -o {project}/LICENSE.jasper https://raw.githubusercontent.com/jasper-software/jasper/version-4.1.0/LICENSE.txt
100+
CIBW_ENVIRONMENT: PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/
101+
CIBW_TEST_ENVIRONMENT: CI=true
102+
CIBW_TEST_EXTRAS: test
103+
CIBW_TEST_SOURCES: test examples
104+
CIBW_TEST_COMMAND: >-
105+
python -c "import importlib.metadata as m; names={p.name for p in m.files('rawpy') if '.dist-info/licenses/' in str(p)}; assert names == {'LICENSE','LICENSE.LibRaw','LICENSE.lcms2','LICENSE.libjpeg-turbo','LICENSE.jasper'}, names" &&
106+
python -m pytest -v test
107+
108+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
109+
with:
110+
name: rawpy-${{ env.RAWPY_VERSION }}-${{ matrix.python }}-manylinux_riscv64
111+
path: wheelhouse/*.whl
112+
if-no-files-found: error
113+
114+
gpl_sources:
115+
name: Collect GPL sources (gcc) for rawpy ${{ matrix.version }}
116+
needs: [setup]
117+
if: needs.setup.outputs.versions != '[]'
118+
runs-on: ubuntu-24.04-riscv
119+
strategy:
120+
fail-fast: false
121+
matrix:
122+
version: ${{ fromJSON(needs.setup.outputs.versions) }}
123+
steps:
124+
- uses: riseproject-dev/python-wheels/actions/collect-gpl-sources@main
125+
with:
126+
image: ${{ env.MANYLINUX_RISCV64_IMAGE }}
127+
128+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
129+
with:
130+
name: rawpy-${{ matrix.version }}-gpl-sources
131+
path: gpl-sources.tar
132+
if-no-files-found: error
133+
134+
publish:
135+
name: Publish rawpy ${{ matrix.version }}
136+
needs: [setup, build_wheels, gpl_sources]
137+
if: needs.setup.outputs.versions != '[]'
138+
strategy:
139+
fail-fast: false
140+
matrix:
141+
version: ${{ fromJSON(needs.setup.outputs.versions) }}
142+
permissions:
143+
contents: write
144+
pull-requests: write
145+
uses: $/.github/workflows/_publish-wheel.yml
146+
secrets:
147+
app-private-key: ${{ secrets.RISEPROJECT_APP_PRIVATE_KEY }}
148+
with:
149+
artifact-pattern: rawpy-${{ matrix.version }}-*-manylinux_riscv64
150+
gpl-sources-artifact: rawpy-${{ matrix.version }}-gpl-sources
151+
gpl-sources-description: gcc

‎docs/packages/rawpy.yaml‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package-name: rawpy
2+
source-code: https://github.com/letmaik/rawpy
3+
license: MIT
4+
versions:
5+
- version: 0.27.0
6+
patched: true
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2+
From: Ludovic Henry <git@ludovic.dev>
3+
Date: Mon, 21 Sep 2026 20:00:00 +0000
4+
Subject: [PATCH] use schedule(static) for the dynamic and guided OpenMP loops
5+
6+
Upstream-Status: Inappropriate [works around a libgomp defect on the riscv64 runners, riseproject-dev/python-wheels#617]
7+
8+
libgomp's dynamic and guided work-share schedules fault on the riscv64
9+
machines these wheels are built and tested on (see CLAUDE.md gotcha 166).
10+
copy_fuji_uncropped/copy_bayer in raw2image.cpp run schedule(dynamic) on
11+
every single postprocess() call regardless of camera or demosaic
12+
algorithm, and ahd_demosaic.cpp (the default demosaic algorithm) and
13+
dht_demosaic.cpp (the optional DHT algorithm) run schedule(dynamic)/
14+
schedule(guided) too, so this is reachable by ordinary decoding, not an
15+
edge case.
16+
17+
Moving all 16 affected loops in the vendored LibRaw copy to
18+
schedule(static) costs only load balancing on rows/columns whose
19+
iterations differ in cost, and buys a wheel that does not crash. Revert
20+
once the runners' toolchain is fixed.
21+
---
22+
external/LibRaw/src/demosaic/ahd_demosaic.cpp | 2 +-
23+
external/LibRaw/src/demosaic/dht_demosaic.cpp | 26 +++++++++++++-------------
24+
external/LibRaw/src/preprocessing/raw2image.cpp | 4 ++--
25+
3 files changed, 16 insertions(+), 16 deletions(-)
26+
27+
diff --git a/external/LibRaw/src/demosaic/ahd_demosaic.cpp b/external/LibRaw/src/demosaic/ahd_demosaic.cpp
28+
index ae87b28..ae6ad6d 100644
29+
--- a/external/LibRaw/src/demosaic/ahd_demosaic.cpp
30+
+++ b/external/LibRaw/src/demosaic/ahd_demosaic.cpp
31+
@@ -306,7 +306,7 @@ void LibRaw::ahd_interpolate()
32+
char** buffers = malloc_omp_buffers(buffer_count, buffer_size);
33+
34+
#ifdef LIBRAW_USE_OPENMP
35+
-#pragma omp parallel for schedule(dynamic) default(none) shared(terminate_flag) firstprivate(buffers)
36+
+#pragma omp parallel for schedule(static) default(none) shared(terminate_flag) firstprivate(buffers)
37+
#endif
38+
for (int top = 2; top < height - 5; top += LIBRAW_AHD_TILE - 6)
39+
{
40+
diff --git a/external/LibRaw/src/demosaic/dht_demosaic.cpp b/external/LibRaw/src/demosaic/dht_demosaic.cpp
41+
index 9fa603f..b23dc61 100644
42+
--- a/external/LibRaw/src/demosaic/dht_demosaic.cpp
43+
+++ b/external/LibRaw/src/demosaic/dht_demosaic.cpp
44+
@@ -259,7 +259,7 @@ void DHT::hide_hots()
45+
{
46+
int iwidth = libraw.imgdata.sizes.iwidth;
47+
#if defined(LIBRAW_USE_OPENMP)
48+
-#pragma omp parallel for schedule(guided) firstprivate(iwidth)
49+
+#pragma omp parallel for schedule(static) firstprivate(iwidth)
50+
#endif
51+
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
52+
{
53+
@@ -401,7 +401,7 @@ void DHT::restore_hots()
54+
#ifdef _MSC_VER
55+
#pragma omp parallel for firstprivate(iwidth)
56+
#else
57+
-#pragma omp parallel for schedule(guided) firstprivate(iwidth) collapse(2)
58+
+#pragma omp parallel for schedule(static) firstprivate(iwidth) collapse(2)
59+
#endif
60+
#endif
61+
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
62+
@@ -423,7 +423,7 @@ void DHT::restore_hots()
63+
void DHT::make_diag_dirs()
64+
{
65+
#if defined(LIBRAW_USE_OPENMP)
66+
-#pragma omp parallel for schedule(guided)
67+
+#pragma omp parallel for schedule(static)
68+
#endif
69+
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
70+
{
71+
@@ -442,7 +442,7 @@ void DHT::make_diag_dirs()
72+
// refine_diag_dirs(i, (i & 1) ^ 1);
73+
// }
74+
#if defined(LIBRAW_USE_OPENMP)
75+
-#pragma omp parallel for schedule(guided)
76+
+#pragma omp parallel for schedule(static)
77+
#endif
78+
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
79+
{
80+
@@ -453,28 +453,28 @@ void DHT::make_diag_dirs()
81+
void DHT::make_hv_dirs()
82+
{
83+
#if defined(LIBRAW_USE_OPENMP)
84+
-#pragma omp parallel for schedule(guided)
85+
+#pragma omp parallel for schedule(static)
86+
#endif
87+
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
88+
{
89+
make_hv_dline(i);
90+
}
91+
#if defined(LIBRAW_USE_OPENMP)
92+
-#pragma omp parallel for schedule(guided)
93+
+#pragma omp parallel for schedule(static)
94+
#endif
95+
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
96+
{
97+
refine_hv_dirs(i, i & 1);
98+
}
99+
#if defined(LIBRAW_USE_OPENMP)
100+
-#pragma omp parallel for schedule(guided)
101+
+#pragma omp parallel for schedule(static)
102+
#endif
103+
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
104+
{
105+
refine_hv_dirs(i, (i & 1) ^ 1);
106+
}
107+
#if defined(LIBRAW_USE_OPENMP)
108+
-#pragma omp parallel for schedule(guided)
109+
+#pragma omp parallel for schedule(static)
110+
#endif
111+
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
112+
{
113+
@@ -689,7 +689,7 @@ void DHT::refine_idiag_dirs(int i)
114+
void DHT::make_greens()
115+
{
116+
#if defined(LIBRAW_USE_OPENMP)
117+
-#pragma omp parallel for schedule(guided)
118+
+#pragma omp parallel for schedule(static)
119+
#endif
120+
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
121+
{
122+
@@ -765,7 +765,7 @@ void DHT::make_gline(int i)
123+
void DHT::illustrate_dirs()
124+
{
125+
#if defined(LIBRAW_USE_OPENMP)
126+
-#pragma omp parallel for schedule(guided)
127+
+#pragma omp parallel for schedule(static)
128+
#endif
129+
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
130+
{
131+
@@ -956,7 +956,7 @@ void DHT::make_rb()
132+
{
133+
#if defined(LIBRAW_USE_OPENMP)
134+
#pragma omp barrier
135+
-#pragma omp parallel for schedule(guided)
136+
+#pragma omp parallel for schedule(static)
137+
#endif
138+
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
139+
{
140+
@@ -964,7 +964,7 @@ void DHT::make_rb()
141+
}
142+
#if defined(LIBRAW_USE_OPENMP)
143+
#pragma omp barrier
144+
-#pragma omp parallel for schedule(guided)
145+
+#pragma omp parallel for schedule(static)
146+
#endif
147+
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
148+
{
149+
@@ -982,7 +982,7 @@ void DHT::copy_to_image()
150+
#ifdef _MSC_VER
151+
#pragma omp parallel for
152+
#else
153+
-#pragma omp parallel for schedule(guided) collapse(2)
154+
+#pragma omp parallel for schedule(static) collapse(2)
155+
#endif
156+
#endif
157+
for (int i = 0; i < libraw.imgdata.sizes.iheight; ++i)
158+
diff --git a/external/LibRaw/src/preprocessing/raw2image.cpp b/external/LibRaw/src/preprocessing/raw2image.cpp
159+
index e343762..9fd2f1e 100644
160+
--- a/external/LibRaw/src/preprocessing/raw2image.cpp
161+
+++ b/external/LibRaw/src/preprocessing/raw2image.cpp
162+
@@ -221,7 +221,7 @@ void LibRaw::copy_fuji_uncropped(unsigned short cblack[4],
163+
unsigned short *dmaxp)
164+
{
165+
#if defined(LIBRAW_USE_OPENMP)
166+
-#pragma omp parallel for schedule(dynamic) default(none) firstprivate(cblack) shared(dmaxp)
167+
+#pragma omp parallel for schedule(static) default(none) firstprivate(cblack) shared(dmaxp)
168+
#endif
169+
for (int row = 0; row < int(S.raw_height) - int(S.top_margin) * 2; row++)
170+
{
171+
@@ -276,7 +276,7 @@ void LibRaw::copy_bayer(unsigned short cblack[4], unsigned short *dmaxp)
172+
// Both cropped and uncropped
173+
int maxHeight = MIN(int(S.height),int(S.raw_height)-int(S.top_margin));
174+
#if defined(LIBRAW_USE_OPENMP)
175+
-#pragma omp parallel for schedule(dynamic) default(none) shared(dmaxp) firstprivate(cblack, maxHeight)
176+
+#pragma omp parallel for schedule(static) default(none) shared(dmaxp) firstprivate(cblack, maxHeight)
177+
#endif
178+
for (int row = 0; row < maxHeight ; row++)
179+
{
180+
--
181+
2.43.0

0 commit comments

Comments
 (0)