libpng 1.6.58.0 → 1.6.58.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/build.yml +74 -4
- data/.github/workflows/release.yml +61 -29
- data/.rubocop.yml +3 -3
- data/README.adoc +128 -19
- data/Rakefile +3 -0
- data/ext/extconf.rb +6 -0
- data/lib/libpng/recipe.rb +65 -8
- data/lib/libpng/version.rb +1 -1
- data/lib/libpng.rb +177 -0
- metadata +6 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bd40beeea8a0333bc4f83c461da33eec3bb0ffdcf9982dc289efab87da554d17
|
|
4
|
+
data.tar.gz: d9d837770569d6fdc083d2d074138679c70b43eeac8f666f906fb44f053ec2ef
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 621b7892d277ccdb0ab0535c38ee69c11b9e4b866e05ec7ef7248fd1df9bc4aa6f5a81a5b560eb67a6491ce00c76f853c3e7d3014273890d2dc9a11b76beb1a6
|
|
7
|
+
data.tar.gz: ec661e92e1f7c8b3ddeae8c2b43dc5ff9795ae2ee5aa34b344887134c605e54513b41209e19df74bc04d3bc4e63ed2524202b153841a724a98b48944515276e6
|
data/.github/workflows/build.yml
CHANGED
|
@@ -53,7 +53,7 @@ jobs:
|
|
|
53
53
|
|
|
54
54
|
- name: Install build tools (Windows)
|
|
55
55
|
if: startsWith(matrix.os, 'windows')
|
|
56
|
-
uses:
|
|
56
|
+
uses: step-security/msvc-dev-cmd@v1
|
|
57
57
|
|
|
58
58
|
- run: bundle exec rake
|
|
59
59
|
|
|
@@ -71,15 +71,24 @@ jobs:
|
|
|
71
71
|
- os: ubuntu-latest
|
|
72
72
|
platform: x86_64-linux
|
|
73
73
|
ruby-version: ${{ needs.prepare.outputs.default-ruby-version }}
|
|
74
|
+
- os: ubuntu-24.04-arm
|
|
75
|
+
platform: aarch64-linux
|
|
76
|
+
ruby-version: ${{ needs.prepare.outputs.default-ruby-version }}
|
|
74
77
|
- os: windows-latest
|
|
75
78
|
platform: x64-mingw32
|
|
76
79
|
ruby-version: ${{ needs.prepare.outputs.default-ruby-version }}
|
|
77
80
|
- os: windows-latest
|
|
78
81
|
platform: x64-mingw-ucrt
|
|
79
82
|
ruby-version: ${{ needs.prepare.outputs.default-ruby-version }}
|
|
80
|
-
- os:
|
|
83
|
+
- os: windows-11-arm
|
|
84
|
+
platform: aarch64-mingw-ucrt
|
|
85
|
+
ruby-version: '3.4'
|
|
86
|
+
- os: macos-15-intel
|
|
81
87
|
platform: x86_64-darwin
|
|
82
88
|
ruby-version: ${{ needs.prepare.outputs.default-ruby-version }}
|
|
89
|
+
- os: macos-latest
|
|
90
|
+
platform: arm64-darwin
|
|
91
|
+
ruby-version: ${{ needs.prepare.outputs.default-ruby-version }}
|
|
83
92
|
|
|
84
93
|
steps:
|
|
85
94
|
- uses: actions/checkout@v4
|
|
@@ -103,7 +112,9 @@ jobs:
|
|
|
103
112
|
|
|
104
113
|
- name: Install build tools (Windows)
|
|
105
114
|
if: startsWith(matrix.os, 'windows')
|
|
106
|
-
uses:
|
|
115
|
+
uses: step-security/msvc-dev-cmd@v1
|
|
116
|
+
with:
|
|
117
|
+
arch: ${{ matrix.platform == 'aarch64-mingw-ucrt' && 'arm64' || 'x64' }}
|
|
107
118
|
|
|
108
119
|
- run: bundle exec rake gem:native:${{ matrix.platform }}
|
|
109
120
|
|
|
@@ -116,5 +127,64 @@ jobs:
|
|
|
116
127
|
run: gem install -b pkg/libpng-*.gem
|
|
117
128
|
|
|
118
129
|
- name: Test conversion
|
|
130
|
+
shell: bash
|
|
131
|
+
run: |
|
|
132
|
+
ruby -rlibpng -e 'png = Libpng.encode(2, 2, "\xff" * 16, pixel_format: "RGBA"); File.binwrite("test.png", png); puts Libpng.decode(png, pixel_format: "RGBA").inspect'
|
|
133
|
+
|
|
134
|
+
build_musl:
|
|
135
|
+
name: build ${{ matrix.platform }} (Alpine)
|
|
136
|
+
runs-on: ${{ matrix.os }}
|
|
137
|
+
needs: prepare
|
|
138
|
+
strategy:
|
|
139
|
+
fail-fast: false
|
|
140
|
+
matrix:
|
|
141
|
+
include:
|
|
142
|
+
- os: ubuntu-latest
|
|
143
|
+
platform: x86_64-linux-musl
|
|
144
|
+
- os: ubuntu-24.04-arm
|
|
145
|
+
platform: aarch64-linux-musl
|
|
146
|
+
|
|
147
|
+
steps:
|
|
148
|
+
- uses: actions/checkout@v4
|
|
149
|
+
|
|
150
|
+
# Run the entire build inside an Alpine container via `docker run` rather
|
|
151
|
+
# than the workflow `container:` field. The latter only works on x64
|
|
152
|
+
# runners (Actions' JS runtime has no arm64-musl build), but arm64 Ubuntu
|
|
153
|
+
# runners can run arm64 Alpine images natively via Docker.
|
|
154
|
+
- name: Build in Alpine container
|
|
155
|
+
shell: bash
|
|
156
|
+
env:
|
|
157
|
+
PLATFORM: ${{ matrix.platform }}
|
|
158
|
+
RUBY_VERSION: ${{ needs.prepare.outputs.default-ruby-version }}
|
|
119
159
|
run: |
|
|
120
|
-
|
|
160
|
+
cat > /tmp/alpine-build.sh <<'BUILD_EOF'
|
|
161
|
+
#!/bin/sh
|
|
162
|
+
set -e
|
|
163
|
+
apk add --no-cache build-base cmake ninja git file zlib-dev
|
|
164
|
+
# The mounted /work volume is owned by the host runner user; git in
|
|
165
|
+
# the container (running as root) refuses to operate on it without
|
|
166
|
+
# this. Without it, `git ls-files` in the gemspec returns empty and
|
|
167
|
+
# the built gem is missing lib/libpng.rb.
|
|
168
|
+
git config --global --add safe.directory /work
|
|
169
|
+
bundle install --jobs 4
|
|
170
|
+
bundle exec rake "gem:native:${PLATFORM}"
|
|
171
|
+
gem install -b pkg/libpng-*.gem
|
|
172
|
+
# Smoke test from /tmp so bundler's source-tree LOAD_PATH doesn't
|
|
173
|
+
# shadow the installed gem. Activate via `gem` so RubyGems resolves
|
|
174
|
+
# the platform-specific binary gem (Alpine Ruby's platform string
|
|
175
|
+
# is *-linux-musl, which must match the gem's platform suffix).
|
|
176
|
+
(cd /tmp && ruby -e 'gem "libpng"; require "libpng"; png = Libpng.encode(2, 2, "\xff" * 16, pixel_format: "RGBA"); File.binwrite("test.png", png); puts Libpng.decode(png, pixel_format: "RGBA").inspect')
|
|
177
|
+
BUILD_EOF
|
|
178
|
+
chmod +x /tmp/alpine-build.sh
|
|
179
|
+
docker run --rm \
|
|
180
|
+
-v "$PWD:/work" \
|
|
181
|
+
-v /tmp/alpine-build.sh:/alpine-build.sh \
|
|
182
|
+
-w /work \
|
|
183
|
+
-e PLATFORM \
|
|
184
|
+
ruby:${RUBY_VERSION}-alpine \
|
|
185
|
+
/alpine-build.sh
|
|
186
|
+
|
|
187
|
+
- uses: actions/upload-artifact@v4
|
|
188
|
+
with:
|
|
189
|
+
name: pkg-${{ matrix.platform }}
|
|
190
|
+
path: pkg/*.gem
|
|
@@ -129,15 +129,24 @@ jobs:
|
|
|
129
129
|
- os: ubuntu-latest
|
|
130
130
|
platform: x86_64-linux
|
|
131
131
|
ruby-version: '3.3'
|
|
132
|
+
- os: ubuntu-24.04-arm
|
|
133
|
+
platform: aarch64-linux
|
|
134
|
+
ruby-version: '3.3'
|
|
132
135
|
- os: windows-latest
|
|
133
136
|
platform: x64-mingw32
|
|
134
137
|
ruby-version: '3.3'
|
|
135
138
|
- os: windows-latest
|
|
136
139
|
platform: x64-mingw-ucrt
|
|
137
140
|
ruby-version: '3.3'
|
|
138
|
-
- os:
|
|
141
|
+
- os: windows-11-arm
|
|
142
|
+
platform: aarch64-mingw-ucrt
|
|
143
|
+
ruby-version: '3.4'
|
|
144
|
+
- os: macos-15-intel
|
|
139
145
|
platform: x86_64-darwin
|
|
140
146
|
ruby-version: '3.3'
|
|
147
|
+
- os: macos-latest
|
|
148
|
+
platform: arm64-darwin
|
|
149
|
+
ruby-version: '3.3'
|
|
141
150
|
steps:
|
|
142
151
|
- uses: actions/checkout@v4
|
|
143
152
|
with:
|
|
@@ -161,7 +170,9 @@ jobs:
|
|
|
161
170
|
|
|
162
171
|
- name: Install build tools (Windows)
|
|
163
172
|
if: startsWith(matrix.os, 'windows')
|
|
164
|
-
uses:
|
|
173
|
+
uses: step-security/msvc-dev-cmd@v1
|
|
174
|
+
with:
|
|
175
|
+
arch: ${{ matrix.platform == 'aarch64-mingw-ucrt' && 'arm64' || 'x64' }}
|
|
165
176
|
|
|
166
177
|
- run: bundle exec rake gem:native:${{ matrix.platform }}
|
|
167
178
|
|
|
@@ -174,10 +185,11 @@ jobs:
|
|
|
174
185
|
run: gem install -b pkg/libpng-*.gem
|
|
175
186
|
|
|
176
187
|
- name: Test conversion
|
|
188
|
+
shell: bash
|
|
177
189
|
run: |
|
|
178
|
-
ruby -rlibpng -e
|
|
190
|
+
ruby -rlibpng -e 'png = Libpng.encode(2, 2, "\xff" * 16, pixel_format: "RGBA"); puts Libpng.decode(png, pixel_format: "RGBA").inspect'
|
|
179
191
|
|
|
180
|
-
|
|
192
|
+
build_musl:
|
|
181
193
|
needs: bump
|
|
182
194
|
if: always() && !cancelled() && !failure() && (needs.bump.result == 'success' || needs.bump.result == 'skipped')
|
|
183
195
|
runs-on: ${{ matrix.os }}
|
|
@@ -186,43 +198,45 @@ jobs:
|
|
|
186
198
|
matrix:
|
|
187
199
|
include:
|
|
188
200
|
- os: ubuntu-latest
|
|
189
|
-
platform:
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
platform: arm64-darwin
|
|
193
|
-
ruby-version: '3.3'
|
|
201
|
+
platform: x86_64-linux-musl
|
|
202
|
+
- os: ubuntu-24.04-arm
|
|
203
|
+
platform: aarch64-linux-musl
|
|
194
204
|
steps:
|
|
195
205
|
- uses: actions/checkout@v4
|
|
196
206
|
with:
|
|
197
207
|
ref: ${{ needs.bump.outputs.sha || github.ref }}
|
|
198
208
|
|
|
199
|
-
- name:
|
|
200
|
-
|
|
209
|
+
- name: Build in Alpine container
|
|
210
|
+
shell: bash
|
|
211
|
+
env:
|
|
212
|
+
PLATFORM: ${{ matrix.platform }}
|
|
201
213
|
run: |
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
214
|
+
cat > /tmp/alpine-build.sh <<'BUILD_EOF'
|
|
215
|
+
#!/bin/sh
|
|
216
|
+
set -e
|
|
217
|
+
apk add --no-cache build-base cmake ninja git file zlib-dev
|
|
218
|
+
git config --global --add safe.directory /work
|
|
219
|
+
bundle install --jobs 4
|
|
220
|
+
bundle exec rake "gem:native:${PLATFORM}"
|
|
221
|
+
gem install -b pkg/libpng-*.gem
|
|
222
|
+
(cd /tmp && ruby -e 'gem "libpng"; require "libpng"; png = Libpng.encode(2, 2, "\xff" * 16, pixel_format: "RGBA"); puts Libpng.decode(png, pixel_format: "RGBA").inspect')
|
|
223
|
+
BUILD_EOF
|
|
224
|
+
chmod +x /tmp/alpine-build.sh
|
|
225
|
+
docker run --rm \
|
|
226
|
+
-v "$PWD:/work" \
|
|
227
|
+
-v /tmp/alpine-build.sh:/alpine-build.sh \
|
|
228
|
+
-w /work \
|
|
229
|
+
-e PLATFORM \
|
|
230
|
+
ruby:3.3-alpine \
|
|
231
|
+
/alpine-build.sh
|
|
215
232
|
|
|
216
233
|
- uses: actions/upload-artifact@v4
|
|
217
234
|
with:
|
|
218
235
|
name: pkg-${{ matrix.platform }}
|
|
219
236
|
path: pkg/*.gem
|
|
220
237
|
|
|
221
|
-
- name: Install gem
|
|
222
|
-
run: gem install -b pkg/libpng-*.gem
|
|
223
|
-
|
|
224
238
|
publish:
|
|
225
|
-
needs: [ bump, build,
|
|
239
|
+
needs: [ bump, build, build_musl ]
|
|
226
240
|
if: always() && !cancelled() && !failure()
|
|
227
241
|
runs-on: ubuntu-latest
|
|
228
242
|
steps:
|
|
@@ -245,5 +259,23 @@ jobs:
|
|
|
245
259
|
uses: rubygems/configure-rubygems-credentials@main
|
|
246
260
|
|
|
247
261
|
- name: Publish to rubygems.org
|
|
262
|
+
shell: bash
|
|
248
263
|
run: |
|
|
249
|
-
|
|
264
|
+
set +e
|
|
265
|
+
for gem in pkg/*.gem; do
|
|
266
|
+
output=$(gem push -V "$gem" 2>&1)
|
|
267
|
+
rc=$?
|
|
268
|
+
if [ $rc -eq 0 ]; then
|
|
269
|
+
echo "$output"
|
|
270
|
+
continue
|
|
271
|
+
fi
|
|
272
|
+
# RubyGems rejects re-pushing the same version with 422, even for
|
|
273
|
+
# identical content. Tolerate that so we can publish newly-added
|
|
274
|
+
# platform variants at a version whose source gem already exists.
|
|
275
|
+
if echo "$output" | grep -qE "already (exists|pushed)|Conflict|Repushing"; then
|
|
276
|
+
echo "Skip $(basename "$gem") (already published)"
|
|
277
|
+
continue
|
|
278
|
+
fi
|
|
279
|
+
echo "$output" >&2
|
|
280
|
+
exit 1
|
|
281
|
+
done
|
data/.rubocop.yml
CHANGED
|
@@ -10,15 +10,15 @@ Gemspec/DevelopmentDependencies:
|
|
|
10
10
|
# methods that walk libpng's chunked API; splitting would obscure the
|
|
11
11
|
# 1-to-1 mapping with the C API.
|
|
12
12
|
Metrics/AbcSize:
|
|
13
|
-
Max:
|
|
13
|
+
Max: 70
|
|
14
14
|
Metrics/ClassLength:
|
|
15
15
|
Max: 200
|
|
16
16
|
Metrics/CyclomaticComplexity:
|
|
17
17
|
Max: 20
|
|
18
18
|
Metrics/MethodLength:
|
|
19
|
-
Max:
|
|
19
|
+
Max: 70
|
|
20
20
|
Metrics/ModuleLength:
|
|
21
|
-
Max:
|
|
21
|
+
Max: 350
|
|
22
22
|
Metrics/PerceivedComplexity:
|
|
23
23
|
Max: 20
|
|
24
24
|
Metrics/ParameterLists:
|
data/README.adoc
CHANGED
|
@@ -22,9 +22,33 @@ Or install manually:
|
|
|
22
22
|
|
|
23
23
|
gem install libpng
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
No C compiler, libpng development headers, or system packages
|
|
27
|
-
at install time.
|
|
25
|
+
RubyGems automatically selects the pre-compiled gem that matches your
|
|
26
|
+
platform. No C compiler, libpng development headers, or system packages
|
|
27
|
+
are required at install time when a binary gem is available. The
|
|
28
|
+
platform-agnostic `ruby` gem is the fallback: it downloads the libpng
|
|
29
|
+
source tarball at install time and compiles it via CMake (requires
|
|
30
|
+
`cmake`, `ninja`, and zlib headers).
|
|
31
|
+
|
|
32
|
+
== Supported platforms
|
|
33
|
+
|
|
34
|
+
The following pre-compiled platform gems are published for each release
|
|
35
|
+
(see the link:https://rubygems.org/gems/libpng/versions[RubyGems versions page]
|
|
36
|
+
for the current set):
|
|
37
|
+
|
|
38
|
+
[cols="1,1,3", options="header"]
|
|
39
|
+
|===
|
|
40
|
+
| Gem platform | Target | Build host
|
|
41
|
+
| `ruby` (source) | Any platform (compiles on install) | n/a
|
|
42
|
+
| `x86_64-linux` | x86_64 Linux (glibc) | `ubuntu-latest`
|
|
43
|
+
| `x86_64-linux-musl` | x86_64 Linux (musl, e.g. Alpine) | `ruby:<ver>-alpine` on x86_64
|
|
44
|
+
| `aarch64-linux` | ARM64 Linux (glibc) | `ubuntu-24.04-arm` (native)
|
|
45
|
+
| `aarch64-linux-musl` | ARM64 Linux (musl) | `ruby:<ver>-alpine` on arm64
|
|
46
|
+
| `x64-mingw32` | x64 Windows, RubyInstaller < 3.0 (MSVCRT) | `windows-latest`
|
|
47
|
+
| `x64-mingw-ucrt` | x64 Windows, RubyInstaller >= 3.0 (UCRT) | `windows-latest`
|
|
48
|
+
| `aarch64-mingw-ucrt` | ARM64 Windows (Ruby >= 3.4) | `windows-11-arm` (native)
|
|
49
|
+
| `x86_64-darwin` | Intel macOS | `macos-15-intel`
|
|
50
|
+
| `arm64-darwin` | Apple Silicon macOS | `macos-latest`
|
|
51
|
+
|===
|
|
28
52
|
|
|
29
53
|
== Usage
|
|
30
54
|
|
|
@@ -52,12 +76,57 @@ at install time.
|
|
|
52
76
|
|
|
53
77
|
Libpng.encode(width, height, pixels,
|
|
54
78
|
pixel_format: "RGBA",
|
|
55
|
-
convert_to_8bit: false
|
|
79
|
+
convert_to_8bit: false,
|
|
80
|
+
strip_colorspace: true)
|
|
56
81
|
|
|
57
|
-
`convert_to_8bit: true
|
|
82
|
+
`convert_to_8bit: true`:: causes libpng to convert 16-bit input to 8-bit
|
|
58
83
|
on write (input is still passed as a packed byte buffer; use this if you
|
|
59
84
|
have packed 16-bit-per-channel data).
|
|
60
85
|
|
|
86
|
+
`strip_colorspace: true`:: (default) walks the PNG chunk list after
|
|
87
|
+
encode and keeps only `IHDR`, `IDAT`, and `IEND`, dropping the sRGB/gAMA
|
|
88
|
+
chunks libpng's simplified API emits by default. This matches the
|
|
89
|
+
"classic" libpng write path (e.g. libemf2svg) and produces byte-identical
|
|
90
|
+
output. Set to `false` to keep the ancillary color chunks.
|
|
91
|
+
|
|
92
|
+
=== Standard API (encode_standard)
|
|
93
|
+
|
|
94
|
+
For parity with libpng's classic write path -- the same code path used
|
|
95
|
+
by `libemf2svg`'s `rgb2png` -- use `encode_standard` instead of
|
|
96
|
+
`encode`. It calls `png_create_write_struct` + `png_set_IHDR` +
|
|
97
|
+
`png_set_rows` + `png_write_png(PNG_TRANSFORM_IDENTITY)` directly, with
|
|
98
|
+
filter and zlib-compression control exposed:
|
|
99
|
+
|
|
100
|
+
Libpng.encode_standard(width, height, pixels,
|
|
101
|
+
pixel_format: "RGBA",
|
|
102
|
+
filter: :default,
|
|
103
|
+
compression_level: 6)
|
|
104
|
+
|
|
105
|
+
`pixel_format`:: `GRAY`, `GA`, `RGB`, or `RGBA` only. The byte-order
|
|
106
|
+
variants (`BGR`, `ARGB`, `BGRA`, `ABGR`) are simplified-API only --
|
|
107
|
+
the standard API always emits host-order.
|
|
108
|
+
|
|
109
|
+
`filter`:: One of `:default` (adaptive -- libpng picks the best filter
|
|
110
|
+
per row, the libemf2svg default), `:adaptive` (same), `:none`, `:sub`,
|
|
111
|
+
`:up`, `:avg`, `:paeth`, or `:all`.
|
|
112
|
+
|
|
113
|
+
`compression_level`:: zlib level 0-9. Default 6 (Z_DEFAULT_COMPRESSION,
|
|
114
|
+
matching libemf2svg).
|
|
115
|
+
|
|
116
|
+
Output is written directly into a Ruby String via `png_set_write_fn` --
|
|
117
|
+
no `Tempfile`, no disk I/O. The FFI write callback is held on a local
|
|
118
|
+
Array so it survives the libpng calls.
|
|
119
|
+
|
|
120
|
+
Differences from `encode`:
|
|
121
|
+
- Emits only `IHDR` / `IDAT` / `IEND` (no post-hoc chunk stripping
|
|
122
|
+
needed).
|
|
123
|
+
- Standard API only -- no `convert_to_8bit` option.
|
|
124
|
+
- Errors are raised via a `png_set_error_fn` callback (set on
|
|
125
|
+
`png_create_write_struct`). libpng expects error callbacks to
|
|
126
|
+
longjmp; we rb_raise instead, which unwinds the Ruby stack. The
|
|
127
|
+
`ensure` block calls `png_destroy_write_struct` to release the C
|
|
128
|
+
state.
|
|
129
|
+
|
|
61
130
|
== Ractor safety
|
|
62
131
|
|
|
63
132
|
`Libpng.encode` and `Libpng.decode` are Ractor-safe. Every call
|
|
@@ -65,25 +134,26 @@ allocates and frees its own libpng `png_image`; no shared mutable state
|
|
|
65
134
|
exists on the Ruby side. Calls from multiple Ractors run concurrently
|
|
66
135
|
without locking.
|
|
67
136
|
|
|
68
|
-
Example:
|
|
137
|
+
Example (Ruby 3.x uses `Ractor#take`; Ruby 4.0+ uses `Ractor#value`):
|
|
69
138
|
|
|
70
139
|
r = Ractor.new do
|
|
71
140
|
Libpng.encode(2, 2, "\xFF" * 16, pixel_format: "RGBA")
|
|
72
141
|
end
|
|
73
|
-
r.take
|
|
142
|
+
r.take # Ruby 3.x
|
|
143
|
+
r.value # Ruby 4.0+ (Ractor#take was removed in 4.0)
|
|
74
144
|
|
|
75
145
|
== Versioning
|
|
76
146
|
|
|
77
147
|
This gem follows a `{LIBPNG_VERSION}.{ITERATION}` scheme:
|
|
78
148
|
|
|
79
149
|
- `LIBPNG_VERSION` is the upstream libpng release the gem is built
|
|
80
|
-
against (currently 1.6.
|
|
150
|
+
against (currently `1.6.58`).
|
|
81
151
|
- `ITERATION` is a counter that bumps on Ruby-side changes (recipe bug
|
|
82
152
|
fixes, CI tweaks, doc updates). It resets to 0 each time
|
|
83
153
|
`LIBPNG_VERSION` bumps.
|
|
84
154
|
|
|
85
|
-
For example, `1.6.
|
|
86
|
-
`1.6.
|
|
155
|
+
For example, `1.6.58.0` is the first release against libpng 1.6.58,
|
|
156
|
+
`1.6.58.1` is a Ruby-side fix, and `1.6.59.0` would be a release against
|
|
87
157
|
the next upstream libpng.
|
|
88
158
|
|
|
89
159
|
== How it works
|
|
@@ -91,15 +161,54 @@ the next upstream libpng.
|
|
|
91
161
|
The gem has three layers:
|
|
92
162
|
|
|
93
163
|
1. `lib/libpng.rb` -- Ruby FFI wrapper. Loads `libpng16.{so,dylib,dll}`
|
|
94
|
-
from the gem's `lib/libpng/` directory at runtime
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
164
|
+
from the gem's `lib/libpng/` directory at runtime and exposes
|
|
165
|
+
`Libpng.encode` / `Libpng.decode` against libpng's "simplified"
|
|
166
|
+
high-level C API (`png_image_*`). The wrapper manually lays out the
|
|
167
|
+
`png_image` struct via explicit byte offsets into an
|
|
168
|
+
`FFI::MemoryPointer` rather than `FFI::Struct`, because `FFI::Struct`
|
|
169
|
+
carries class-level state that is not shareable across non-main
|
|
170
|
+
Ractors.
|
|
171
|
+
|
|
172
|
+
2. `lib/libpng/recipe.rb` -- a `MiniPortileCMake` recipe that downloads
|
|
173
|
+
the libpng source tarball pinned by `LIBPNG_URL` / `LIBPNG_SHA256`,
|
|
174
|
+
runs CMake with `PNG_SHARED=ON PNG_STATIC=OFF PNG_TESTS=OFF`, and
|
|
175
|
+
copies the resulting shared lib into `lib/libpng/`. Invoked only when
|
|
176
|
+
installing the **source** (`ruby` platform) gem.
|
|
177
|
+
|
|
178
|
+
3. `ext/extconf.rb` -- gem extension entry point. Calls `Recipe#cook_if_not`,
|
|
179
|
+
which is a no-op once the per-platform checkpoint file
|
|
180
|
+
(`ports/.../<name>-<ver>-<platform>.installed`) exists. After cooking,
|
|
181
|
+
emits a dummy `Makefile` via `mkmf` so RubyGems is satisfied (no Ruby
|
|
182
|
+
C extension is compiled). The pre-compiled platform gems unset
|
|
183
|
+
`spec.extensions`, so this never runs for them.
|
|
184
|
+
|
|
185
|
+
=== Pre-compiled platform gems
|
|
186
|
+
|
|
187
|
+
`Rakefile` defines `gem:native:<platform>` tasks for each target. Each
|
|
188
|
+
task clones the gemspec, sets `spec.platform`, adds
|
|
189
|
+
`lib/libpng/*.{dll,so,dylib}` to `spec.files`, removes the
|
|
190
|
+
`mini_portile2` dependency (no longer needed at install time), and
|
|
191
|
+
unsets `spec.extensions` -- the resulting gem installs with no compiler.
|
|
192
|
+
|
|
193
|
+
The release workflow (`.github/workflows/release.yml`) builds all 10
|
|
194
|
+
platforms in parallel on the appropriate native runner:
|
|
195
|
+
|
|
196
|
+
- x86_64 Linux glibc -- `ubuntu-latest`
|
|
197
|
+
- ARM64 Linux glibc -- `ubuntu-24.04-arm` (native)
|
|
198
|
+
- x86_64 Linux musl -- `ruby:<ver>-alpine` via `docker run` on `ubuntu-latest`
|
|
199
|
+
- ARM64 Linux musl -- `ruby:<ver>-alpine` via `docker run` on `ubuntu-24.04-arm`
|
|
200
|
+
- x64 Windows (both MSVCRT and UCRT) -- `windows-latest` with `step-security/msvc-dev-cmd`
|
|
201
|
+
- ARM64 Windows -- `windows-11-arm` (native) with `arch: arm64`
|
|
202
|
+
- Intel macOS -- `macos-15-intel`
|
|
203
|
+
- Apple Silicon macOS -- `macos-latest`
|
|
204
|
+
|
|
205
|
+
Linux glibc and macOS builds run natively on the matching runner. Linux
|
|
206
|
+
musl builds run inside `ruby:<ver>-alpine` containers via `docker run`
|
|
207
|
+
rather than the workflow `container:` field, because Actions' JS runtime
|
|
208
|
+
has no arm64-musl build and therefore cannot run Alpine containers on
|
|
209
|
+
arm64 runners. Windows builds use the Visual Studio toolchain via
|
|
210
|
+
`step-security/msvc-dev-cmd` (a maintained drop-in for the deleted
|
|
211
|
+
`ilammy/msvc-dev-tools`).
|
|
103
212
|
|
|
104
213
|
== License
|
|
105
214
|
|
data/Rakefile
CHANGED
data/ext/extconf.rb
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
$: << File.expand_path(File.join(File.dirname(__FILE__), '../lib'))
|
|
2
2
|
|
|
3
3
|
require 'libpng/recipe'
|
|
4
|
+
require 'mkmf'
|
|
4
5
|
|
|
5
6
|
recipe = Libpng::Recipe.new
|
|
6
7
|
recipe.cook_if_not
|
|
8
|
+
|
|
9
|
+
# RubyGems requires every extconf.rb to leave a Makefile behind, even if
|
|
10
|
+
# no native extension is compiled (libpng is built by Recipe#cook above
|
|
11
|
+
# and loaded via FFI at runtime). dummy_makefile satisfies that contract.
|
|
12
|
+
create_makefile('libpng/dummy')
|
data/lib/libpng/recipe.rb
CHANGED
|
@@ -32,6 +32,7 @@ module Libpng
|
|
|
32
32
|
|
|
33
33
|
@target = ROOT.join(@target).to_s
|
|
34
34
|
@printed = {}
|
|
35
|
+
setup_cross_compile if cross_compile?
|
|
35
36
|
end
|
|
36
37
|
|
|
37
38
|
# libpng ships a CMake build alongside the autotools one. We use CMake
|
|
@@ -69,10 +70,12 @@ module Libpng
|
|
|
69
70
|
|
|
70
71
|
def install
|
|
71
72
|
super
|
|
72
|
-
# After `make install`, the
|
|
73
|
-
#
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
# After `make install`, the shared lib lives under ports/<name>/<ver>/.
|
|
74
|
+
# On Linux/macOS that's lib/. On Windows, CMake's GNUInstallDirs puts
|
|
75
|
+
# the .dll in bin/ and the import library (.dll.a) in lib/ — we only
|
|
76
|
+
# ship the .dll, so search both.
|
|
77
|
+
libs = Dir.glob(File.join(port_path, shared_lib_install_glob))
|
|
78
|
+
raise "no libpng shared lib produced under #{port_path}" if libs.empty?
|
|
76
79
|
|
|
77
80
|
target_dir = ROOT.join('lib', 'libpng')
|
|
78
81
|
FileUtils.mkdir_p(target_dir)
|
|
@@ -125,6 +128,16 @@ module Libpng
|
|
|
125
128
|
end
|
|
126
129
|
end
|
|
127
130
|
|
|
131
|
+
# Glob (with port_path prefix) for the freshly installed shared lib.
|
|
132
|
+
# On Windows the .dll installs to bin/; on Unix-likes it stays in lib/.
|
|
133
|
+
def shared_lib_install_glob
|
|
134
|
+
if MiniPortile.windows?
|
|
135
|
+
'{bin,lib}/libpng16*.dll'
|
|
136
|
+
else
|
|
137
|
+
"lib/#{shared_lib_glob}"
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
128
141
|
def each_built_lib(&block)
|
|
129
142
|
Dir.glob(ROOT.join('lib', 'libpng', shared_lib_glob)).each(&block)
|
|
130
143
|
end
|
|
@@ -134,10 +147,16 @@ module Libpng
|
|
|
134
147
|
case @host
|
|
135
148
|
when /\Ax86_64.*mingw32/
|
|
136
149
|
'x64-mingw32'
|
|
150
|
+
when /\A(aarch64|arm64).*mingw/
|
|
151
|
+
'aarch64-mingw-ucrt'
|
|
152
|
+
when /\Ax86_64.*linux-musl/
|
|
153
|
+
'x86_64-linux-musl'
|
|
154
|
+
when /\A(aarch64|arm64).*linux-musl/
|
|
155
|
+
'aarch64-linux-musl'
|
|
137
156
|
when /\Ax86_64.*linux/
|
|
138
157
|
'x86_64-linux'
|
|
139
|
-
when /\A(arm64
|
|
140
|
-
'
|
|
158
|
+
when /\A(aarch64|arm64).*linux/
|
|
159
|
+
'aarch64-linux'
|
|
141
160
|
when /\Ax86_64.*(darwin|macos|osx)/
|
|
142
161
|
'x86_64-darwin'
|
|
143
162
|
when /\A(arm64|aarch64).*(darwin|macos|osx)/
|
|
@@ -154,13 +173,49 @@ module Libpng
|
|
|
154
173
|
'arm64-darwin'
|
|
155
174
|
when /\Ax86_64.*(darwin|macos|osx)/
|
|
156
175
|
'x86_64-darwin'
|
|
176
|
+
when /\A(arm64|aarch64).*linux-musl/
|
|
177
|
+
'aarch64-linux-musl'
|
|
157
178
|
when /\A(arm64|aarch64).*linux/
|
|
158
179
|
'aarch64-linux'
|
|
180
|
+
when /\Ax86_64.*linux-musl/
|
|
181
|
+
'x86_64-linux-musl'
|
|
159
182
|
else
|
|
160
183
|
ENV.fetch('target_platform', host_platform)
|
|
161
184
|
end
|
|
162
185
|
end
|
|
163
186
|
|
|
187
|
+
def cross_compile?
|
|
188
|
+
target_platform != host_platform
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Configure MiniPortile + CMake for cross-compilation. Native builds
|
|
192
|
+
# (host_platform == target_platform) skip this entirely.
|
|
193
|
+
def setup_cross_compile
|
|
194
|
+
# All targeted platforms now have native runners (ubuntu-24.04-arm for
|
|
195
|
+
# aarch64-linux, windows-11-arm for aarch64-mingw-ucrt, Alpine containers
|
|
196
|
+
# for the musl variants). This hook is kept as a seam for future
|
|
197
|
+
# cross-compile targets (e.g. aarch64-linux on an x86_64 host).
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def cpu_type
|
|
201
|
+
case target_platform
|
|
202
|
+
when 'aarch64-linux', 'aarch64-linux-musl', 'arm64-darwin', 'aarch64-mingw-ucrt' then 'aarch64'
|
|
203
|
+
when 'x86_64-linux', 'x86_64-linux-musl', 'x86_64-darwin', /\Ax64-mingw/ then 'x86_64'
|
|
204
|
+
else
|
|
205
|
+
super
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def cmake_system_name
|
|
210
|
+
case target_platform
|
|
211
|
+
when 'aarch64-linux', 'x86_64-linux', 'aarch64-linux-musl', 'x86_64-linux-musl' then 'Linux'
|
|
212
|
+
when 'arm64-darwin', 'x86_64-darwin' then 'Darwin'
|
|
213
|
+
when /\A(aarch64-)?mingw/, /\Ax64-mingw/ then 'Windows'
|
|
214
|
+
else
|
|
215
|
+
super
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
164
219
|
def target_format
|
|
165
220
|
@target_format ||=
|
|
166
221
|
case target_platform
|
|
@@ -168,10 +223,12 @@ module Libpng
|
|
|
168
223
|
/Mach-O 64-bit dynamically linked shared library arm64/
|
|
169
224
|
when 'x86_64-darwin'
|
|
170
225
|
/Mach-O 64-bit dynamically linked shared library x86_64/
|
|
171
|
-
when 'aarch64-linux'
|
|
226
|
+
when 'aarch64-linux', 'aarch64-linux-musl'
|
|
172
227
|
/ELF 64-bit LSB shared object, ARM aarch64/
|
|
173
|
-
when 'x86_64-linux'
|
|
228
|
+
when 'x86_64-linux', 'x86_64-linux-musl'
|
|
174
229
|
/ELF 64-bit LSB shared object, x86-64/
|
|
230
|
+
when 'aarch64-mingw-ucrt'
|
|
231
|
+
/PE32\+ executable.*\(DLL\).*ARM64/
|
|
175
232
|
when /\Ax64-mingw(32|-ucrt)/
|
|
176
233
|
/PE32\+ executable.*\(DLL\).*x86-64/
|
|
177
234
|
else
|
data/lib/libpng/version.rb
CHANGED
data/lib/libpng.rb
CHANGED
|
@@ -45,6 +45,20 @@ module Libpng
|
|
|
45
45
|
%i[pointer pointer pointer int pointer int pointer], :int
|
|
46
46
|
attach_function :png_image_free, [:pointer], :void
|
|
47
47
|
|
|
48
|
+
# libpng standard (non-simplified) write API. Used by encode_standard.
|
|
49
|
+
attach_function :png_create_write_struct,
|
|
50
|
+
%i[string pointer pointer pointer], :pointer
|
|
51
|
+
attach_function :png_create_info_struct, [:pointer], :pointer
|
|
52
|
+
attach_function :png_destroy_write_struct, %i[pointer pointer], :void
|
|
53
|
+
attach_function :png_set_IHDR,
|
|
54
|
+
%i[pointer pointer uint32 uint32 int int int int int], :void
|
|
55
|
+
attach_function :png_set_rows, %i[pointer pointer pointer], :void
|
|
56
|
+
attach_function :png_set_filter, %i[pointer int int], :void
|
|
57
|
+
attach_function :png_set_compression_level, %i[pointer int], :void
|
|
58
|
+
attach_function :png_set_write_fn,
|
|
59
|
+
%i[pointer pointer pointer pointer], :void
|
|
60
|
+
attach_function :png_write_png, %i[pointer pointer int pointer], :void
|
|
61
|
+
|
|
48
62
|
# PNG_IMAGE_FORMAT_* bit flags and named formats from png.h.
|
|
49
63
|
FORMAT_FLAG_ALPHA = 0x01
|
|
50
64
|
FORMAT_FLAG_COLOR = 0x02
|
|
@@ -77,6 +91,64 @@ module Libpng
|
|
|
77
91
|
'ABGR' => FORMAT_ABGR
|
|
78
92
|
}.freeze
|
|
79
93
|
|
|
94
|
+
# PNG_COLOR_MASK_* and PNG_COLOR_TYPE_* (png.h).
|
|
95
|
+
COLOR_MASK_PALETTE = 0x01
|
|
96
|
+
COLOR_MASK_COLOR = 0x02
|
|
97
|
+
COLOR_MASK_ALPHA = 0x04
|
|
98
|
+
|
|
99
|
+
COLOR_TYPE_GRAY = 0
|
|
100
|
+
COLOR_TYPE_PALETTE = COLOR_MASK_COLOR | COLOR_MASK_PALETTE
|
|
101
|
+
COLOR_TYPE_RGB = COLOR_MASK_COLOR
|
|
102
|
+
COLOR_TYPE_RGB_ALPHA = COLOR_MASK_COLOR | COLOR_MASK_ALPHA
|
|
103
|
+
COLOR_TYPE_GRAY_ALPHA = COLOR_MASK_ALPHA
|
|
104
|
+
|
|
105
|
+
# Map our FORMAT_* to PNG_COLOR_TYPE_*. encode_standard doesn't support
|
|
106
|
+
# the BGR/ARGB/BGRA/ABGR byte-order variants because the standard API
|
|
107
|
+
# encodes host-order; callers wanting those formats should use the
|
|
108
|
+
# simplified encode path or pre-swap bytes.
|
|
109
|
+
COLOR_TYPE_BY_FORMAT = {
|
|
110
|
+
FORMAT_GRAY => COLOR_TYPE_GRAY,
|
|
111
|
+
FORMAT_GA => COLOR_TYPE_GRAY_ALPHA,
|
|
112
|
+
FORMAT_AG => COLOR_TYPE_GRAY_ALPHA,
|
|
113
|
+
FORMAT_RGB => COLOR_TYPE_RGB,
|
|
114
|
+
FORMAT_RGBA => COLOR_TYPE_RGB_ALPHA
|
|
115
|
+
}.freeze
|
|
116
|
+
|
|
117
|
+
# png_set_IHDR pass-through constants (png.h).
|
|
118
|
+
INTERLACE_NONE = 0
|
|
119
|
+
INTERLACE_ADAM7 = 1
|
|
120
|
+
COMPRESSION_TYPE_DEFAULT = 0
|
|
121
|
+
FILTER_TYPE_DEFAULT = 0
|
|
122
|
+
TRANSFORM_IDENTITY = 0x0000
|
|
123
|
+
|
|
124
|
+
# png_set_filter bitmask (PNG_FILTER_*). :default lets libpng use
|
|
125
|
+
# adaptive filtering across all filter types -- this is what
|
|
126
|
+
# libemf2svg/rgb2png effectively does via PNG_FILTER_TYPE_DEFAULT in
|
|
127
|
+
# the IHDR.
|
|
128
|
+
FILTER_HEURISTIC_DEFAULT = 0
|
|
129
|
+
FILTER_NONE = 0x08
|
|
130
|
+
FILTER_SUB = 0x10
|
|
131
|
+
FILTER_UP = 0x20
|
|
132
|
+
FILTER_AVG = 0x40
|
|
133
|
+
FILTER_PAETH = 0x80
|
|
134
|
+
FILTER_ALL = FILTER_NONE | FILTER_SUB | FILTER_UP | FILTER_AVG | FILTER_PAETH
|
|
135
|
+
|
|
136
|
+
FILTER_MASK_BY_NAME = {
|
|
137
|
+
default: nil, # don't call png_set_filter at all
|
|
138
|
+
adaptive: FILTER_ALL, # call with all filters allowed (same as default)
|
|
139
|
+
none: FILTER_NONE,
|
|
140
|
+
sub: FILTER_SUB,
|
|
141
|
+
up: FILTER_UP,
|
|
142
|
+
avg: FILTER_AVG,
|
|
143
|
+
paeth: FILTER_PAETH,
|
|
144
|
+
all: FILTER_ALL
|
|
145
|
+
}.freeze
|
|
146
|
+
|
|
147
|
+
# PNG_LIBPNG_VER_STRING. Used as the user_png_ver arg to
|
|
148
|
+
# png_create_write_struct. Must match the libpng16.{so,dylib,dll} we
|
|
149
|
+
# ship -- if these ever drift, png_create_write_struct aborts.
|
|
150
|
+
LIBPNG_VER_STRING_C = Libpng::LIBPNG_VERSION.dup.freeze
|
|
151
|
+
|
|
80
152
|
# png_image::version value libpng checks for. Defined in png.h as
|
|
81
153
|
# PNG_IMAGE_VERSION == 1.
|
|
82
154
|
PNG_IMAGE_VERSION = 1
|
|
@@ -192,6 +264,111 @@ module Libpng
|
|
|
192
264
|
end
|
|
193
265
|
end
|
|
194
266
|
|
|
267
|
+
# Encode raw pixels via libpng's standard write API
|
|
268
|
+
# (png_create_write_struct -> png_set_IHDR -> png_set_rows ->
|
|
269
|
+
# png_write_png(PNG_TRANSFORM_IDENTITY)). This matches the byte
|
|
270
|
+
# output of the "classic" libpng write path used by libemf2svg's
|
|
271
|
+
# rgb2png, and avoids the sRGB/gAMA chunks the simplified API
|
|
272
|
+
# injects by default.
|
|
273
|
+
#
|
|
274
|
+
# +width+, +height+ image dimensions in pixels
|
|
275
|
+
# +pixels+ String of raw pixel bytes (row-major, top-down)
|
|
276
|
+
# +pixel_format+ "RGB", "RGBA", "GRAY", "GA" (default: "RGBA")
|
|
277
|
+
# +filter+ :default (adaptive), :none, :sub, :up, :avg,
|
|
278
|
+
# :paeth, :all, :adaptive (default: :default)
|
|
279
|
+
# +compression_level+ zlib level 0-9 (default: 6 = Z_DEFAULT_COMPRESSION)
|
|
280
|
+
#
|
|
281
|
+
# Output is accumulated in a Ruby String via png_set_write_fn --
|
|
282
|
+
# no Tempfile, no disk I/O. The FFI write callback is held on a
|
|
283
|
+
# local Array so it isn't GC'd mid-call.
|
|
284
|
+
#
|
|
285
|
+
# Errors are raised via a png_set_error_fn callback (set on
|
|
286
|
+
# png_create_write_struct). libpng expects error callbacks to
|
|
287
|
+
# longjmp; rb_raise from the FFI callback unwinds the Ruby stack
|
|
288
|
+
# instead, which works for our usage but means the png_struct is
|
|
289
|
+
# not cleanly torn down inside libpng. The ensure block calls
|
|
290
|
+
# png_destroy_write_struct to release the C state.
|
|
291
|
+
#
|
|
292
|
+
# Ractor-safe: all state is per-call. FFI callbacks are not
|
|
293
|
+
# shareable across Ractors, but they're locals and stay scoped
|
|
294
|
+
# to one Ractor.
|
|
295
|
+
def encode_standard(width, height, pixels, pixel_format: 'RGBA',
|
|
296
|
+
filter: :default, compression_level: 6)
|
|
297
|
+
raise Error, 'width must be positive' unless width.positive?
|
|
298
|
+
raise Error, 'height must be positive' unless height.positive?
|
|
299
|
+
|
|
300
|
+
fmt = FORMAT_BY_NAME[pixel_format.to_s.upcase] ||
|
|
301
|
+
raise(Error, "unknown pixel_format #{pixel_format.inspect}")
|
|
302
|
+
color_type = COLOR_TYPE_BY_FORMAT[fmt] ||
|
|
303
|
+
raise(Error, "encode_standard does not support #{pixel_format} " \
|
|
304
|
+
'(use GRAY, GA, RGB, or RGBA; byte-order variants ' \
|
|
305
|
+
'like BGRA/ARGB/ABGR are simplified-API only)')
|
|
306
|
+
|
|
307
|
+
bytes_per_pixel = bytes_per_pixel_for_format(fmt)
|
|
308
|
+
stride = width * bytes_per_pixel
|
|
309
|
+
expected = stride * height
|
|
310
|
+
raise Error, "pixels too short: expected #{expected}, got #{pixels.bytesize}" if pixels.bytesize < expected
|
|
311
|
+
|
|
312
|
+
filter_sym = filter.to_sym
|
|
313
|
+
unless FILTER_MASK_BY_NAME.key?(filter_sym)
|
|
314
|
+
raise Error, "unknown filter #{filter.inspect} " \
|
|
315
|
+
'(expected :default, :adaptive, :none, :sub, :up, :avg, :paeth, or :all)'
|
|
316
|
+
end
|
|
317
|
+
filter_mask = FILTER_MASK_BY_NAME[filter_sym]
|
|
318
|
+
raise Error, 'compression_level must be 0..9' unless (0..9).cover?(compression_level)
|
|
319
|
+
|
|
320
|
+
output = String.new.force_encoding('ASCII-8BIT')
|
|
321
|
+
|
|
322
|
+
# FFI::Function objects need a stable reference for the duration of
|
|
323
|
+
# the libpng calls -- otherwise they can be GC'd before libpng
|
|
324
|
+
# invokes them.
|
|
325
|
+
callbacks = []
|
|
326
|
+
write_cb = FFI::Function.new(:void, %i[pointer pointer size_t]) do |_, data, len|
|
|
327
|
+
output << data.read_bytes(len)
|
|
328
|
+
end
|
|
329
|
+
error_cb = FFI::Function.new(:void, %i[pointer string]) do |_, msg|
|
|
330
|
+
raise Error, "libpng: #{msg}"
|
|
331
|
+
end
|
|
332
|
+
callbacks << write_cb << error_cb
|
|
333
|
+
|
|
334
|
+
png_ptr = FFI::Pointer.new(0)
|
|
335
|
+
info_ptr = FFI::Pointer.new(0)
|
|
336
|
+
begin
|
|
337
|
+
png_ptr = png_create_write_struct(LIBPNG_VER_STRING_C, nil, error_cb, nil)
|
|
338
|
+
raise Error, 'png_create_write_struct returned NULL' if png_ptr.null?
|
|
339
|
+
|
|
340
|
+
info_ptr = png_create_info_struct(png_ptr)
|
|
341
|
+
raise Error, 'png_create_info_struct returned NULL' if info_ptr.null?
|
|
342
|
+
|
|
343
|
+
png_set_compression_level(png_ptr, compression_level)
|
|
344
|
+
png_set_write_fn(png_ptr, nil, write_cb, nil)
|
|
345
|
+
png_set_IHDR(png_ptr, info_ptr, width, height, 8, color_type,
|
|
346
|
+
INTERLACE_NONE, COMPRESSION_TYPE_DEFAULT, FILTER_TYPE_DEFAULT)
|
|
347
|
+
png_set_filter(png_ptr, FILTER_HEURISTIC_DEFAULT, filter_mask) if filter_mask
|
|
348
|
+
|
|
349
|
+
FFI::MemoryPointer.new(:uint8, pixels.bytesize) do |px|
|
|
350
|
+
px.write_bytes(pixels)
|
|
351
|
+
FFI::MemoryPointer.new(:pointer, height) do |rows|
|
|
352
|
+
height.times { |y| rows.put_pointer(y * FFI.type_size(:pointer), px + (y * stride)) }
|
|
353
|
+
png_set_rows(png_ptr, info_ptr, rows)
|
|
354
|
+
png_write_png(png_ptr, info_ptr, TRANSFORM_IDENTITY, nil)
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
output
|
|
359
|
+
ensure
|
|
360
|
+
unless png_ptr.null?
|
|
361
|
+
FFI::MemoryPointer.new(:pointer) do |pp|
|
|
362
|
+
pp.write_pointer(png_ptr)
|
|
363
|
+
FFI::MemoryPointer.new(:pointer) do |ip|
|
|
364
|
+
ip.write_pointer(info_ptr)
|
|
365
|
+
png_destroy_write_struct(pp, ip)
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
|
|
195
372
|
private
|
|
196
373
|
|
|
197
374
|
# Lower-level encode that produces the raw libpng output (with sRGB
|
metadata
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: libpng
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.6.58.
|
|
4
|
+
version: 1.6.58.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: exe
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2026-07-26 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
14
|
name: ffi
|
|
@@ -71,6 +72,7 @@ metadata:
|
|
|
71
72
|
source_code_uri: https://github.com/claricle/libpng-ruby
|
|
72
73
|
changelog_uri: https://github.com/claricle/libpng-ruby/blob/main/README.adoc#versioning
|
|
73
74
|
rubygems_mfa_required: 'true'
|
|
75
|
+
post_install_message:
|
|
74
76
|
rdoc_options: []
|
|
75
77
|
require_paths:
|
|
76
78
|
- lib
|
|
@@ -85,7 +87,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
85
87
|
- !ruby/object:Gem::Version
|
|
86
88
|
version: '0'
|
|
87
89
|
requirements: []
|
|
88
|
-
rubygems_version:
|
|
90
|
+
rubygems_version: 3.5.22
|
|
91
|
+
signing_key:
|
|
89
92
|
specification_version: 4
|
|
90
93
|
summary: libpng for Ruby (pre-compiled, FFI-based).
|
|
91
94
|
test_files: []
|