libpng 1.6.58.3 → 1.6.58.5
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 +27 -6
- data/.github/workflows/release.yml +20 -2
- data/.gitignore +2 -0
- data/.rspec +1 -1
- data/.rubocop.yml +9 -1
- data/CHANGELOG.md +64 -0
- data/CLAUDE.md +19 -11
- data/README.adoc +36 -7
- data/Rakefile +1 -0
- data/lib/libpng/binding.rb +77 -1
- data/lib/libpng/chunk_walker.rb +204 -1
- data/lib/libpng/decoded_image.rb +8 -0
- data/lib/libpng/metadata_writer.rb +139 -0
- data/lib/libpng/recipe.rb +12 -3
- data/lib/libpng/simplified_decoder.rb +44 -14
- data/lib/libpng/standard_decoder.rb +234 -0
- data/lib/libpng/standard_encoder.rb +23 -1
- data/lib/libpng/text_writer.rb +105 -0
- data/lib/libpng/version.rb +1 -1
- data/lib/libpng.rb +90 -3
- metadata +4 -22
- data/TODO.roadmap/01-claude-md-update.md +0 -27
- data/TODO.roadmap/02-encode-standard-ractor-spec.md +0 -24
- data/TODO.roadmap/03-mini-portile2-bump.md +0 -26
- data/TODO.roadmap/04-changelog.md +0 -34
- data/TODO.roadmap/05-benchmarks.md +0 -41
- data/TODO.roadmap/06-malformed-input-tests.md +0 -47
- data/TODO.roadmap/07-interlace-option.md +0 -40
- data/TODO.roadmap/08-palette-support.md +0 -54
- data/TODO.roadmap/09-16-bit-encode.md +0 -53
- data/TODO.roadmap/10-libpng-1.6.59-bump.md +0 -59
- data/TODO.roadmap/11-read-side-metadata.md +0 -59
- data/TODO.roadmap/12-full-api-surface.md +0 -61
- data/TODO.roadmap/13-c-extension-setjmp.md +0 -77
- data/TODO.roadmap/14-progressive-decode.md +0 -47
- data/TODO.roadmap/15-file-io-variants.md +0 -57
- data/TODO.roadmap/16-architecture-refactor.md +0 -97
- data/TODO.roadmap/17-ruby-version-policy.md +0 -46
- data/TODO.roadmap/18-dependabot.md +0 -52
- data/TODO.roadmap/19-text-chunk-read.md +0 -104
- data/TODO.roadmap/20-color-metadata-read.md +0 -100
- data/TODO.roadmap/README.md +0 -42
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e9edc7cf7190ff782780b914a63bc3a26cd3c1dd71199c26ec144e3e3f72619f
|
|
4
|
+
data.tar.gz: bed30533f0f5b33e4064213aa6bacecb8e815ca56234a8991ae8af70627cb40f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e94cbd6af3fbc2accb177d8f3adc9565582d1853e9f998ff314934b6b5f2816f058164b40a32ee7661b55ea5c58e920c03d600d30e49e7f1d51d5f71e280f3f7
|
|
7
|
+
data.tar.gz: 88fca2579a17b68e184b381483687e43eef88d5a104ac74deb12056bd376e494db7e3b795312fad2042219979a0c9119b2daef8cd4dd2e47c6cf49925a2f5274
|
data/.github/workflows/build.yml
CHANGED
|
@@ -143,6 +143,12 @@ jobs:
|
|
|
143
143
|
platform: x86_64-linux-musl
|
|
144
144
|
- os: ubuntu-24.04-arm
|
|
145
145
|
platform: aarch64-linux-musl
|
|
146
|
+
# OHOS (OpenHarmony / Huawei HarmonyOS PC) is musl-based arm64.
|
|
147
|
+
# Built inside the same Alpine container as aarch64-linux-musl;
|
|
148
|
+
# the resulting binary is byte-compatible. Only the gem's
|
|
149
|
+
# platform label differs so RubyGems on OHOS selects it.
|
|
150
|
+
- os: ubuntu-24.04-arm
|
|
151
|
+
platform: aarch64-linux-ohos
|
|
146
152
|
|
|
147
153
|
steps:
|
|
148
154
|
- uses: actions/checkout@v4
|
|
@@ -168,12 +174,27 @@ jobs:
|
|
|
168
174
|
git config --global --add safe.directory /work
|
|
169
175
|
bundle install --jobs 4
|
|
170
176
|
bundle exec rake "gem:native:${PLATFORM}"
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
+
case "${PLATFORM}" in
|
|
178
|
+
*-ohos)
|
|
179
|
+
# OHOS gem is labeled aarch64-linux-ohos so RubyGems on OHOS
|
|
180
|
+
# selects it, but Alpine Ruby reports *-linux-musl -- a
|
|
181
|
+
# platform mismatch. The .so inside is musl-compatible, so
|
|
182
|
+
# unpack the gem and load it via -Ilib directly to verify
|
|
183
|
+
# the binary works without going through RubyGems install.
|
|
184
|
+
mkdir -p /tmp/ohos-smoke
|
|
185
|
+
gem unpack pkg/libpng-*.gem --target /tmp/ohos-smoke
|
|
186
|
+
(cd /tmp/ohos-smoke/libpng-* && \
|
|
187
|
+
ruby -Ilib -e 'require "libpng"; png = Libpng.encode(2, 2, "\xff" * 16, pixel_format: "RGBA"); puts Libpng.decode(png, pixel_format: "RGBA").inspect')
|
|
188
|
+
;;
|
|
189
|
+
*)
|
|
190
|
+
gem install -b pkg/libpng-*.gem
|
|
191
|
+
# Smoke test from /tmp so bundler's source-tree LOAD_PATH doesn't
|
|
192
|
+
# shadow the installed gem. Activate via `gem` so RubyGems resolves
|
|
193
|
+
# the platform-specific binary gem (Alpine Ruby's platform string
|
|
194
|
+
# is *-linux-musl, which must match the gem's platform suffix).
|
|
195
|
+
(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')
|
|
196
|
+
;;
|
|
197
|
+
esac
|
|
177
198
|
BUILD_EOF
|
|
178
199
|
chmod +x /tmp/alpine-build.sh
|
|
179
200
|
docker run --rm \
|
|
@@ -201,6 +201,10 @@ jobs:
|
|
|
201
201
|
platform: x86_64-linux-musl
|
|
202
202
|
- os: ubuntu-24.04-arm
|
|
203
203
|
platform: aarch64-linux-musl
|
|
204
|
+
# OHOS: same Alpine-built musl arm64 binary as aarch64-linux-musl,
|
|
205
|
+
# packaged under a distinct platform label.
|
|
206
|
+
- os: ubuntu-24.04-arm
|
|
207
|
+
platform: aarch64-linux-ohos
|
|
204
208
|
steps:
|
|
205
209
|
- uses: actions/checkout@v4
|
|
206
210
|
with:
|
|
@@ -218,8 +222,22 @@ jobs:
|
|
|
218
222
|
git config --global --add safe.directory /work
|
|
219
223
|
bundle install --jobs 4
|
|
220
224
|
bundle exec rake "gem:native:${PLATFORM}"
|
|
221
|
-
|
|
222
|
-
|
|
225
|
+
case "${PLATFORM}" in
|
|
226
|
+
*-ohos)
|
|
227
|
+
# OHOS gem is labeled aarch64-linux-ohos so RubyGems on OHOS
|
|
228
|
+
# selects it, but Alpine Ruby reports *-linux-musl. The .so
|
|
229
|
+
# inside is musl-compatible -- unpack and load via -Ilib to
|
|
230
|
+
# verify the binary without going through RubyGems install.
|
|
231
|
+
mkdir -p /tmp/ohos-smoke
|
|
232
|
+
gem unpack pkg/libpng-*.gem --target /tmp/ohos-smoke
|
|
233
|
+
(cd /tmp/ohos-smoke/libpng-* && \
|
|
234
|
+
ruby -Ilib -e 'require "libpng"; png = Libpng.encode(2, 2, "\xff" * 16, pixel_format: "RGBA"); puts Libpng.decode(png, pixel_format: "RGBA").inspect')
|
|
235
|
+
;;
|
|
236
|
+
*)
|
|
237
|
+
gem install -b pkg/libpng-*.gem
|
|
238
|
+
(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')
|
|
239
|
+
;;
|
|
240
|
+
esac
|
|
223
241
|
BUILD_EOF
|
|
224
242
|
chmod +x /tmp/alpine-build.sh
|
|
225
243
|
docker run --rm \
|
data/.gitignore
CHANGED
data/.rspec
CHANGED
data/.rubocop.yml
CHANGED
|
@@ -22,7 +22,7 @@ Metrics/ModuleLength:
|
|
|
22
22
|
Metrics/PerceivedComplexity:
|
|
23
23
|
Max: 20
|
|
24
24
|
Metrics/ParameterLists:
|
|
25
|
-
Max:
|
|
25
|
+
Max: 10
|
|
26
26
|
|
|
27
27
|
Metrics/BlockLength:
|
|
28
28
|
Exclude:
|
|
@@ -37,3 +37,11 @@ Style/FrozenStringLiteralComment:
|
|
|
37
37
|
Exclude:
|
|
38
38
|
- ext/extconf.rb
|
|
39
39
|
- lib/libpng/recipe.rb
|
|
40
|
+
|
|
41
|
+
# libpng's C function names use mixed case + digits (e.g.
|
|
42
|
+
# png_set_strip_16, png_set_expand_gray_1_2_4_to_8). We attach them
|
|
43
|
+
# via FFI with their canonical names so docs and headers cross-
|
|
44
|
+
# reference cleanly.
|
|
45
|
+
Naming/VariableNumber:
|
|
46
|
+
Exclude:
|
|
47
|
+
- lib/libpng/binding.rb
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,70 @@ This gem follows a `{LIBPNG_VERSION}.{LIBPNG_RUBY_ITERATION}` version
|
|
|
7
7
|
scheme. `LIBPNG_VERSION` is the upstream libpng release; `ITERATION`
|
|
8
8
|
bumps for Ruby-side changes and resets to 0 when LIBPNG_VERSION bumps.
|
|
9
9
|
|
|
10
|
+
## [1.6.58.5] - 2026-07-26
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Write-side metadata on `encode_standard`**. Six new keyword options:
|
|
14
|
+
- `text:` -- `Hash<String,String>` of tEXt/iTXt entries. Non-ASCII
|
|
15
|
+
values automatically use iTXt (UTF-8) instead of tEXt (Latin-1).
|
|
16
|
+
- `gamma:` -- `Float` file gamma (e.g. `0.45455` for sRGB).
|
|
17
|
+
- `srgb_intent:` -- `Integer 0..3` rendering intent.
|
|
18
|
+
- `chromaticities:` -- `Hash` with the 8 cHRM primaries.
|
|
19
|
+
- `icc_profile:` -- `Hash` with `:name` and `:data` for an ICC
|
|
20
|
+
color profile (libpng compresses internally).
|
|
21
|
+
- `phys:` -- `Hash` with `:pixels_per_unit_x/y` and `:unit`
|
|
22
|
+
(`0` unknown, `1` meters).
|
|
23
|
+
- **`pHYs` chunk read support**. `Libpng::ChunkWalker#phys_chunk`
|
|
24
|
+
returns a Hash with `:pixels_per_unit_x/y`, `:unit`, and (when
|
|
25
|
+
`unit==1`) computed `:dpi_x/y`. `DecodedImage#phys` is populated
|
|
26
|
+
by both decode paths.
|
|
27
|
+
- **`Libpng.decode_standard`** -- standard libpng read API
|
|
28
|
+
(`png_create_read_struct` -> `png_read_info` -> transforms ->
|
|
29
|
+
`png_read_image`). Counterpart to `encode_standard`; gives
|
|
30
|
+
callers explicit control over which transforms apply
|
|
31
|
+
(palette-to-RGB, gray-to-RGB, alpha add/strip, 16-to-8 demotion,
|
|
32
|
+
interlace handling). Returns the same `DecodedImage` shape as
|
|
33
|
+
`decode`, so metadata access is uniform across both decode paths.
|
|
34
|
+
- New classes/modules: `Libpng::StandardDecoder`,
|
|
35
|
+
`Libpng::MetadataWriter`, `Libpng::TextWriter`, `Libpng::TextEntry`.
|
|
36
|
+
- 27 new specs across `spec/write_metadata_spec.rb` (round-trips for
|
|
37
|
+
every metadata type + validation errors) and
|
|
38
|
+
`spec/standard_decoder_spec.rb` (transform coverage + Ractor safety).
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
- Internal: refactored `StandardEncoder` to delegate metadata writing
|
|
42
|
+
to `MetadataWriter`. Class length back under rubocop limits.
|
|
43
|
+
- Corrected `FILLER_BEFORE`/`FILLER_AFTER` constant values (the code
|
|
44
|
+
had them swapped -- libpng uses `BEFORE=0, AFTER=1`).
|
|
45
|
+
|
|
46
|
+
## [1.6.58.4] - 2026-07-26
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
- `Libpng::ChunkWalker#text_chunks` -- parses `tEXt`, `zTXt`, and `iTXt`
|
|
50
|
+
chunks into a flat Hash of keyword -> UTF-8 String. zTXt values are
|
|
51
|
+
zlib-inflated; tEXt/zTXt values are transcoded from Latin-1 to UTF-8;
|
|
52
|
+
iTXt values stay UTF-8 (with optional compression). Malformed chunks
|
|
53
|
+
are silently skipped so a single broken entry doesn't poison the rest
|
|
54
|
+
of the decode.
|
|
55
|
+
- `Libpng::ChunkWalker#color_chunks` -- parses `gAMA`, `cHRM`, `sRGB`,
|
|
56
|
+
and `iCCP` chunks into a Hash with Symbol keys (`:gamma`,
|
|
57
|
+
`:white_point_x/y`, `:red_x/y`, `:green_x/y`, `:blue_x/y`,
|
|
58
|
+
`:srgb_intent`, `:icc_profile_name`, `:icc_profile`). iCCP profiles
|
|
59
|
+
are zlib-decompressed into raw binary bytes for downstream ICC
|
|
60
|
+
libraries.
|
|
61
|
+
- `Libpng::DecodedImage#text` and `#color` -- new keyword Struct fields,
|
|
62
|
+
populated by `Libpng.decode` via the new ChunkWalker accessors. Both
|
|
63
|
+
default to an empty Hash when the source PNG has no relevant chunks.
|
|
64
|
+
- New platform: `aarch64-linux-ohos` (OpenHarmony / Huawei HarmonyOS PC).
|
|
65
|
+
OHOS is musl-based arm64 -- the resulting shared library is
|
|
66
|
+
byte-compatible with `aarch64-linux-musl`; only the gem's platform
|
|
67
|
+
label differs so RubyGems on OHOS selects the right variant. Built in
|
|
68
|
+
the same Alpine container as the musl gem.
|
|
69
|
+
- Specs: 24 new specs across `spec/text_chunk_spec.rb` and
|
|
70
|
+
`spec/color_metadata_spec.rb` covering all 7 chunk types, malformed
|
|
71
|
+
input handling, Ractor moving, and end-to-end metadata exposure on
|
|
72
|
+
`DecodedImage`.
|
|
73
|
+
|
|
10
74
|
## [1.6.58.3] - 2026-07-26
|
|
11
75
|
|
|
12
76
|
### Added
|
data/CLAUDE.md
CHANGED
|
@@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|
|
6
6
|
|
|
7
7
|
`libpng` is a Ruby gem that wraps the official libpng shared library via FFI. The native `libpng16.{so,dylib,dll}` is **pre-compiled per target platform and shipped inside the gem** — `gem install libpng` must not require a C compiler on the host.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
11 platform gems are published per release (10 native + the source `ruby` gem). Native targets: `x86_64-linux`, `x86_64-linux-musl`, `aarch64-linux`, `aarch64-linux-musl`, `aarch64-linux-ohos`, `x64-mingw32`, `x64-mingw-ucrt`, `aarch64-mingw-ucrt`, `x86_64-darwin`, `arm64-darwin`.
|
|
10
10
|
|
|
11
11
|
## Architecture
|
|
12
12
|
|
|
@@ -20,12 +20,15 @@ loaded via `autoload` from `lib/libpng.rb`. **Never use `require_relative`
|
|
|
20
20
|
| `lib/libpng.rb` | Module + FFI setup + constants + public dispatch (`encode`/`decode`/`encode_standard`) + autoloads |
|
|
21
21
|
| `lib/libpng/version.rb` | `LIBPNG_VERSION`, `LIBPNG_RUBY_ITERATION`, `VERSION` |
|
|
22
22
|
| `lib/libpng/error.rb` | `Libpng::Error` |
|
|
23
|
-
| `lib/libpng/decoded_image.rb` | `Libpng::DecodedImage` (Struct returned by `decode`) |
|
|
24
|
-
| `lib/libpng/chunk_walker.rb` | `Libpng::ChunkWalker` (walk/strip/extract metadata
|
|
23
|
+
| `lib/libpng/decoded_image.rb` | `Libpng::DecodedImage` (Struct returned by `decode` / `decode_standard`) |
|
|
24
|
+
| `lib/libpng/chunk_walker.rb` | `Libpng::ChunkWalker` (walk/strip/extract metadata: `#each_chunk`, `#ihdr_fields`, `#text_chunks` for tEXt/zTXt/iTXt, `#color_chunks` for gAMA/cHRM/sRGB/iCCP, `#phys_chunk` for pHYs, `#strip_ancillary`) |
|
|
25
25
|
| `lib/libpng/bytes_per_pixel.rb` | `Libpng::BytesPerPixel` (pure-data lookup) |
|
|
26
26
|
| `lib/libpng/simplified_encoder.rb` | `Libpng::SimplifiedEncoder` (libpng simplified write API) |
|
|
27
|
-
| `lib/libpng/simplified_decoder.rb` | `Libpng::SimplifiedDecoder` (libpng simplified read API +
|
|
28
|
-
| `lib/libpng/standard_encoder.rb` | `Libpng::StandardEncoder` (libpng standard write API
|
|
27
|
+
| `lib/libpng/simplified_decoder.rb` | `Libpng::SimplifiedDecoder` (libpng simplified read API + metadata via ChunkWalker) |
|
|
28
|
+
| `lib/libpng/standard_encoder.rb` | `Libpng::StandardEncoder` (libpng standard write API; filter/compression/interlace/bit_depth/palette + metadata via MetadataWriter) |
|
|
29
|
+
| `lib/libpng/standard_decoder.rb` | `Libpng::StandardDecoder` (libpng standard read API; explicit transform control) |
|
|
30
|
+
| `lib/libpng/metadata_writer.rb` | `Libpng::MetadataWriter` (validates + writes text/gAMA/sRGB/cHRM/iCCP/pHYs onto a png_ptr/info_ptr pair) |
|
|
31
|
+
| `lib/libpng/text_writer.rb` | `Libpng::TextWriter` + `Libpng::TextEntry` (builds png_text struct array, calls `png_set_text`) |
|
|
29
32
|
| `lib/libpng/recipe.rb` | `Libpng::Recipe < MiniPortileCMake` (builds libpng from source for the source gem) |
|
|
30
33
|
| `ext/extconf.rb` | Gem extension entry. Triggers `Libpng::Recipe` autoload via `require 'libpng'`, then emits a dummy Makefile |
|
|
31
34
|
|
|
@@ -33,7 +36,9 @@ loaded via `autoload` from `lib/libpng.rb`. **Never use `require_relative`
|
|
|
33
36
|
|
|
34
37
|
```ruby
|
|
35
38
|
Libpng.encode(width, height, pixels, pixel_format:, convert_to_8bit:, strip_colorspace:)
|
|
36
|
-
Libpng.encode_standard(width, height, pixels, pixel_format:, filter:, compression_level:, interlace:, bit_depth:, palette
|
|
39
|
+
Libpng.encode_standard(width, height, pixels, pixel_format:, filter:, compression_level:, interlace:, bit_depth:, palette:, **metadata)
|
|
40
|
+
Libpng.decode(png, pixel_format:)
|
|
41
|
+
Libpng.decode_standard(png, pixel_format:, bit_depth:)
|
|
37
42
|
Libpng.decode(png, pixel_format:)
|
|
38
43
|
```
|
|
39
44
|
|
|
@@ -58,7 +63,7 @@ Ruby 4.0 removed `Ractor#take`; use the helper `ractor_result(r)` which prefers
|
|
|
58
63
|
```sh
|
|
59
64
|
bundle install
|
|
60
65
|
bundle exec rake compile # build libpng via MiniPortile (needs cmake + zlib)
|
|
61
|
-
bundle exec rake spec # all specs (~
|
|
66
|
+
bundle exec rake spec # all specs (~132 examples)
|
|
62
67
|
bundle exec rspec spec/libpng_spec.rb:17 # single spec by line
|
|
63
68
|
bundle exec rake rubocop
|
|
64
69
|
bundle exec rake # default: spec + rubocop
|
|
@@ -67,7 +72,7 @@ bundle exec rake gem:native:arm64-darwin # build a pre-compiled gem
|
|
|
67
72
|
bundle exec rake gem:native:any # source gem (compiles on install)
|
|
68
73
|
```
|
|
69
74
|
|
|
70
|
-
Platform gem tasks: `x64-mingw32`, `x64-mingw-ucrt`, `aarch64-mingw-ucrt`, `x86_64-linux`, `x86_64-linux-musl`, `aarch64-linux`, `aarch64-linux-musl`, `x86_64-darwin`, `arm64-darwin`.
|
|
75
|
+
Platform gem tasks: `x64-mingw32`, `x64-mingw-ucrt`, `aarch64-mingw-ucrt`, `x86_64-linux`, `x86_64-linux-musl`, `aarch64-linux`, `aarch64-linux-musl`, `aarch64-linux-ohos`, `x86_64-darwin`, `arm64-darwin`.
|
|
71
76
|
|
|
72
77
|
## Release process
|
|
73
78
|
|
|
@@ -79,7 +84,7 @@ gh workflow run release.yml --repo claricle/libpng-ruby --ref main -f bump-type=
|
|
|
79
84
|
# or: -f bump-type=current # release current VERSION as-is
|
|
80
85
|
```
|
|
81
86
|
|
|
82
|
-
The workflow bumps `lib/libpng/version.rb`, pushes a `v*` tag, builds all
|
|
87
|
+
The workflow bumps `lib/libpng/version.rb`, pushes a `v*` tag, builds all 11 platform gems (10 native + the source `ruby` gem), and publishes to RubyGems via OIDC Trusted Publishing.
|
|
83
88
|
|
|
84
89
|
**Never** push tags or merge to main directly — always go through PRs.
|
|
85
90
|
|
|
@@ -96,14 +101,17 @@ The workflow bumps `lib/libpng/version.rb`, pushes a `v*` tag, builds all 10 pla
|
|
|
96
101
|
- `spec/libpng_spec.rb` — `encode`/`decode` simplified API
|
|
97
102
|
- `spec/libpng_standard_spec.rb` — `encode_standard` core
|
|
98
103
|
- `spec/standard_encoder_options_spec.rb` — `interlace:`/`bit_depth:`/`palette:` options
|
|
104
|
+
- `spec/write_metadata_spec.rb` — text/gAMA/sRGB/cHRM/pHYs write round-trips + validation
|
|
99
105
|
- `spec/decoded_image_metadata_spec.rb` — IHDR metadata + `ChunkWalker`
|
|
106
|
+
- `spec/text_chunk_spec.rb` — tEXt/zTXt/iTXt parsing (read side)
|
|
107
|
+
- `spec/color_metadata_spec.rb` — gAMA/cHRM/sRGB/iCCP parsing (read side)
|
|
108
|
+
- `spec/standard_decoder_spec.rb` — `decode_standard` transforms + Ractor safety
|
|
100
109
|
- `spec/malformed_input_spec.rb` — corrupt PNG input handling
|
|
101
110
|
- `spec/ractor_spec.rb` — Ractor safety for simplified API
|
|
102
|
-
- `spec/ractor_standard_spec.rb` — Ractor safety for standard API
|
|
111
|
+
- `spec/ractor_standard_spec.rb` — Ractor safety for standard write API
|
|
103
112
|
- `spec/benchmark_spec.rb` — encode/decode timing comparison
|
|
104
113
|
|
|
105
114
|
## See also
|
|
106
115
|
|
|
107
|
-
- `TODO.roadmap/` — prioritized backlog with status (done/planned/wontdo) per item
|
|
108
116
|
- `CHANGELOG.md` — release history
|
|
109
117
|
- `REPORT-complex-api-needs.md` — historical report that drove `encode_standard`
|
data/README.adoc
CHANGED
|
@@ -31,8 +31,9 @@ source tarball at install time and compiles it via CMake (requires
|
|
|
31
31
|
|
|
32
32
|
== Supported platforms
|
|
33
33
|
|
|
34
|
-
The following pre-compiled platform gems are published for each
|
|
35
|
-
|
|
34
|
+
The following 11 pre-compiled platform gems are published for each
|
|
35
|
+
release -- 10 native targets plus the platform-agnostic source (`ruby`)
|
|
36
|
+
gem (see the link:https://rubygems.org/gems/libpng/versions[RubyGems versions page]
|
|
36
37
|
for the current set):
|
|
37
38
|
|
|
38
39
|
[cols="1,1,3", options="header"]
|
|
@@ -43,6 +44,7 @@ for the current set):
|
|
|
43
44
|
| `x86_64-linux-musl` | x86_64 Linux (musl, e.g. Alpine) | `ruby:<ver>-alpine` on x86_64
|
|
44
45
|
| `aarch64-linux` | ARM64 Linux (glibc) | `ubuntu-24.04-arm` (native)
|
|
45
46
|
| `aarch64-linux-musl` | ARM64 Linux (musl) | `ruby:<ver>-alpine` on arm64
|
|
47
|
+
| `aarch64-linux-ohos` | ARM64 OpenHarmony / Huawei HarmonyOS PC | `ruby:<ver>-alpine` on arm64 (byte-compatible with `aarch64-linux-musl`)
|
|
46
48
|
| `x64-mingw32` | x64 Windows, RubyInstaller < 3.0 (MSVCRT) | `windows-latest`
|
|
47
49
|
| `x64-mingw-ucrt` | x64 Windows, RubyInstaller >= 3.0 (UCRT) | `windows-latest`
|
|
48
50
|
| `aarch64-mingw-ucrt` | ARM64 Windows (Ruby >= 3.4) | `windows-11-arm` (native)
|
|
@@ -67,6 +69,9 @@ for the current set):
|
|
|
67
69
|
decoded.bit_depth # => 8 (from IHDR)
|
|
68
70
|
decoded.color_type # => 6 (PNG_COLOR_TYPE_RGB_ALPHA, from IHDR)
|
|
69
71
|
decoded.interlace # => 0 (PNG_INTERLACE_NONE, from IHDR)
|
|
72
|
+
decoded.text # => {"Title" => "Sunset", "Author" => "Jane"} (tEXt/zTXt/iTXt)
|
|
73
|
+
decoded.color # => {:gamma => 0.45455, :srgb_intent => 0, ...} (gAMA/cHRM/sRGB/iCCP)
|
|
74
|
+
decoded.phys # => {:pixels_per_unit_x => 3779, :unit => 1, :dpi_x => 95.99, ...} (pHYs)
|
|
70
75
|
|
|
71
76
|
=== Supported pixel formats
|
|
72
77
|
|
|
@@ -105,7 +110,14 @@ by `libemf2svg`'s `rgb2png` -- use `encode_standard` instead of
|
|
|
105
110
|
compression_level: 6,
|
|
106
111
|
interlace: :none,
|
|
107
112
|
bit_depth: 8,
|
|
108
|
-
palette: nil
|
|
113
|
+
palette: nil,
|
|
114
|
+
# Metadata writers (each optional):
|
|
115
|
+
text: { "Title" => "Sunset", "Author" => "Jane" },
|
|
116
|
+
gamma: 0.45455,
|
|
117
|
+
srgb_intent: 0,
|
|
118
|
+
chromaticities: { white_point_x: 0.3127, ... },
|
|
119
|
+
icc_profile: { name: "sRGB IEC61966-2.1", data: bytes },
|
|
120
|
+
phys: { pixels_per_unit_x: 3779, pixels_per_unit_y: 3779, unit: 1 })
|
|
109
121
|
|
|
110
122
|
`pixel_format`:: `:gray`, `:ga`, `:rgb`, `:rgba`, or `:palette`. The
|
|
111
123
|
byte-order variants (`BGR`, `ARGB`, `BGRA`, `ABGR`) are simplified-API
|
|
@@ -144,12 +156,29 @@ Differences from `encode`:
|
|
|
144
156
|
`ensure` block calls `png_destroy_write_struct` to release the C
|
|
145
157
|
state.
|
|
146
158
|
|
|
159
|
+
=== Standard API (decode_standard)
|
|
160
|
+
|
|
161
|
+
`Libpng.decode_standard` is the read-side counterpart to
|
|
162
|
+
`encode_standard`. It calls `png_create_read_struct` +
|
|
163
|
+
`png_set_read_fn` (memory reader) + `png_read_info` + transforms +
|
|
164
|
+
`png_read_image`, giving the caller explicit control over which
|
|
165
|
+
transforms apply (palette-to-RGB, gray-to-RGB, alpha add/strip,
|
|
166
|
+
16-to-8 demotion, interlace handling).
|
|
167
|
+
|
|
168
|
+
decoded = Libpng.decode_standard(png_bytes, pixel_format: "RGBA", bit_depth: 8)
|
|
169
|
+
|
|
170
|
+
Returns the same `Libpng::DecodedImage` shape as `decode`, so metadata
|
|
171
|
+
fields (`text`, `color`, `phys`) work uniformly across both decode
|
|
172
|
+
paths. Use `decode` for the common case (auto-transforms); use
|
|
173
|
+
`decode_standard` when you need to know exactly which transforms
|
|
174
|
+
applied (e.g. for parity with `libemf2svg`'s `png2rgb`).
|
|
175
|
+
|
|
147
176
|
== Ractor safety
|
|
148
177
|
|
|
149
|
-
`Libpng.encode`
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
without locking.
|
|
178
|
+
`Libpng.encode`, `Libpng.encode_standard`, `Libpng.decode`, and
|
|
179
|
+
`Libpng.decode_standard` are all Ractor-safe. Every call allocates and
|
|
180
|
+
frees its own libpng state; no shared mutable state exists on the Ruby
|
|
181
|
+
side. Calls from multiple Ractors run concurrently without locking.
|
|
153
182
|
|
|
154
183
|
Example (Ruby 3.x uses `Ractor#take`; Ruby 4.0+ uses `Ractor#value`):
|
|
155
184
|
|
data/Rakefile
CHANGED
data/lib/libpng/binding.rb
CHANGED
|
@@ -28,7 +28,9 @@ module Libpng
|
|
|
28
28
|
# __dir__ is lib/libpng/; the shared library ships at lib/libpng/.
|
|
29
29
|
ffi_lib File.expand_path(lib_filename, __dir__)
|
|
30
30
|
|
|
31
|
+
# ----------------------------------------------------------------
|
|
31
32
|
# libpng simplified API (png_image / png_image_*).
|
|
33
|
+
# ----------------------------------------------------------------
|
|
32
34
|
attach_function :png_image_begin_read_from_memory,
|
|
33
35
|
%i[pointer pointer size_t], :int
|
|
34
36
|
attach_function :png_image_finish_read,
|
|
@@ -37,7 +39,9 @@ module Libpng
|
|
|
37
39
|
%i[pointer pointer pointer int pointer int pointer], :int
|
|
38
40
|
attach_function :png_image_free, [:pointer], :void
|
|
39
41
|
|
|
40
|
-
#
|
|
42
|
+
# ----------------------------------------------------------------
|
|
43
|
+
# libpng standard write API.
|
|
44
|
+
# ----------------------------------------------------------------
|
|
41
45
|
attach_function :png_create_write_struct,
|
|
42
46
|
%i[string pointer pointer pointer], :pointer
|
|
43
47
|
attach_function :png_create_info_struct, [:pointer], :pointer
|
|
@@ -53,5 +57,77 @@ module Libpng
|
|
|
53
57
|
attach_function :png_set_write_fn,
|
|
54
58
|
%i[pointer pointer pointer pointer], :void
|
|
55
59
|
attach_function :png_write_png, %i[pointer pointer int pointer], :void
|
|
60
|
+
|
|
61
|
+
# ----------------------------------------------------------------
|
|
62
|
+
# Metadata writers (called between png_set_IHDR and png_write_png).
|
|
63
|
+
# ----------------------------------------------------------------
|
|
64
|
+
# png_text is a struct; we pass an array pointer + count.
|
|
65
|
+
attach_function :png_set_text,
|
|
66
|
+
%i[pointer pointer pointer int], :void
|
|
67
|
+
# file_gamma is a double.
|
|
68
|
+
attach_function :png_set_gAMA,
|
|
69
|
+
%i[pointer pointer double], :void
|
|
70
|
+
attach_function :png_set_sRGB,
|
|
71
|
+
%i[pointer pointer int], :void
|
|
72
|
+
# 8 chromaticity doubles (white_x, white_y, r_x, r_y, g_x, g_y, b_x, b_y).
|
|
73
|
+
attach_function :png_set_cHRM,
|
|
74
|
+
%i[pointer pointer double double double double
|
|
75
|
+
double double double double], :void
|
|
76
|
+
# png_set_iCCP(png_ptr, info_ptr, name, compression_type, profile, length)
|
|
77
|
+
attach_function :png_set_iCCP,
|
|
78
|
+
%i[pointer pointer string int pointer uint], :void
|
|
79
|
+
# png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type)
|
|
80
|
+
attach_function :png_set_pHYs,
|
|
81
|
+
%i[pointer pointer uint32 uint32 int], :void
|
|
82
|
+
|
|
83
|
+
# ----------------------------------------------------------------
|
|
84
|
+
# libpng standard read API.
|
|
85
|
+
# ----------------------------------------------------------------
|
|
86
|
+
attach_function :png_create_read_struct,
|
|
87
|
+
%i[string pointer pointer pointer], :pointer
|
|
88
|
+
attach_function :png_destroy_read_struct, %i[pointer pointer pointer], :void
|
|
89
|
+
attach_function :png_set_read_fn,
|
|
90
|
+
%i[pointer pointer pointer pointer], :void
|
|
91
|
+
attach_function :png_read_info, %i[pointer pointer], :void
|
|
92
|
+
attach_function :png_read_update_info, %i[pointer pointer], :void
|
|
93
|
+
attach_function :png_read_image, %i[pointer pointer], :void
|
|
94
|
+
attach_function :png_read_end, %i[pointer pointer], :void
|
|
95
|
+
# Returns the byte count per row AFTER any transforms are applied.
|
|
96
|
+
attach_function :png_get_rowbytes, %i[pointer pointer], :size_t
|
|
97
|
+
# Returns the post-transform color type / bit depth / channels.
|
|
98
|
+
attach_function :png_get_color_type, %i[pointer pointer], :uint8
|
|
99
|
+
attach_function :png_get_bit_depth, %i[pointer pointer], :uint8
|
|
100
|
+
attach_function :png_get_channels, %i[pointer pointer], :uint8
|
|
101
|
+
# Returns post-transform width / height (transforms rarely change these).
|
|
102
|
+
attach_function :png_get_image_width, %i[pointer pointer], :uint32
|
|
103
|
+
attach_function :png_get_image_height, %i[pointer pointer], :uint32
|
|
104
|
+
attach_function :png_get_interlace_type, %i[pointer pointer], :uint8
|
|
105
|
+
# png_get_IHDR signature differs from png_set_IHDR: widths are passed
|
|
106
|
+
# by reference (pointer to uint32) so libpng can write them back.
|
|
107
|
+
attach_function :png_get_IHDR,
|
|
108
|
+
%i[pointer pointer pointer pointer pointer pointer
|
|
109
|
+
pointer pointer pointer], :uint32
|
|
110
|
+
|
|
111
|
+
# ----------------------------------------------------------------
|
|
112
|
+
# Read-side transforms (call between png_read_info and png_read_image).
|
|
113
|
+
# Each takes only (png_ptr); they toggle internal state.
|
|
114
|
+
# ----------------------------------------------------------------
|
|
115
|
+
attach_function :png_set_expand, [:pointer], :void
|
|
116
|
+
attach_function :png_set_palette_to_rgb, [:pointer], :void
|
|
117
|
+
attach_function :png_set_tRNS_to_alpha, [:pointer], :void
|
|
118
|
+
attach_function :png_set_strip_alpha, [:pointer], :void
|
|
119
|
+
attach_function :png_set_strip_16, [:pointer], :void
|
|
120
|
+
attach_function :png_set_expand_gray_1_2_4_to_8, [:pointer], :void
|
|
121
|
+
attach_function :png_set_gray_to_rgb, [:pointer], :void
|
|
122
|
+
attach_function :png_set_rgb_to_gray, %i[pointer int double double], :void
|
|
123
|
+
attach_function :png_set_filler, %i[pointer uint int], :void
|
|
124
|
+
attach_function :png_set_add_alpha, %i[pointer uint int], :void
|
|
125
|
+
attach_function :png_set_packing, [:pointer], :void
|
|
126
|
+
attach_function :png_set_swap, [:pointer], :void
|
|
127
|
+
attach_function :png_set_interlace_handling, [:pointer], :int
|
|
128
|
+
attach_function :png_set_gamma, %i[pointer double double], :void
|
|
129
|
+
|
|
130
|
+
# PNG_INFO_* bit flags returned by png_get_valid.
|
|
131
|
+
attach_function :png_get_valid, %i[pointer pointer uint], :uint
|
|
56
132
|
end
|
|
57
133
|
end
|