libpng 1.6.58.3-aarch64-linux → 1.6.58.5-aarch64-linux

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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build.yml +27 -6
  3. data/.github/workflows/release.yml +20 -2
  4. data/.gitignore +2 -0
  5. data/.rspec +1 -1
  6. data/.rubocop.yml +9 -1
  7. data/CHANGELOG.md +64 -0
  8. data/CLAUDE.md +19 -11
  9. data/README.adoc +36 -7
  10. data/Rakefile +1 -0
  11. data/lib/libpng/binding.rb +77 -1
  12. data/lib/libpng/chunk_walker.rb +204 -1
  13. data/lib/libpng/decoded_image.rb +8 -0
  14. data/lib/libpng/metadata_writer.rb +139 -0
  15. data/lib/libpng/recipe.rb +12 -3
  16. data/lib/libpng/simplified_decoder.rb +44 -14
  17. data/lib/libpng/standard_decoder.rb +234 -0
  18. data/lib/libpng/standard_encoder.rb +23 -1
  19. data/lib/libpng/text_writer.rb +105 -0
  20. data/lib/libpng/version.rb +1 -1
  21. data/lib/libpng.rb +90 -3
  22. metadata +4 -22
  23. data/TODO.roadmap/01-claude-md-update.md +0 -27
  24. data/TODO.roadmap/02-encode-standard-ractor-spec.md +0 -24
  25. data/TODO.roadmap/03-mini-portile2-bump.md +0 -26
  26. data/TODO.roadmap/04-changelog.md +0 -34
  27. data/TODO.roadmap/05-benchmarks.md +0 -41
  28. data/TODO.roadmap/06-malformed-input-tests.md +0 -47
  29. data/TODO.roadmap/07-interlace-option.md +0 -40
  30. data/TODO.roadmap/08-palette-support.md +0 -54
  31. data/TODO.roadmap/09-16-bit-encode.md +0 -53
  32. data/TODO.roadmap/10-libpng-1.6.59-bump.md +0 -59
  33. data/TODO.roadmap/11-read-side-metadata.md +0 -59
  34. data/TODO.roadmap/12-full-api-surface.md +0 -61
  35. data/TODO.roadmap/13-c-extension-setjmp.md +0 -77
  36. data/TODO.roadmap/14-progressive-decode.md +0 -47
  37. data/TODO.roadmap/15-file-io-variants.md +0 -57
  38. data/TODO.roadmap/16-architecture-refactor.md +0 -97
  39. data/TODO.roadmap/17-ruby-version-policy.md +0 -46
  40. data/TODO.roadmap/18-dependabot.md +0 -52
  41. data/TODO.roadmap/19-text-chunk-read.md +0 -104
  42. data/TODO.roadmap/20-color-metadata-read.md +0 -100
  43. data/TODO.roadmap/README.md +0 -42
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c99b54213a0437e99e0c62660477b0b3e46489c7a89d24e4c9e4b8133c5deed4
4
- data.tar.gz: '0892df05776e5219036924186abd7ed6fb39e90bb84d5c4132c482ac7321391a'
3
+ metadata.gz: 89fa7393749554967c34ebe20efcbd49f363173c69300f89179dda7ec7423852
4
+ data.tar.gz: 4c4294acaa4c03b0230526ecab16c7712fda4cdc637dfa8ad9968bb558d00a64
5
5
  SHA512:
6
- metadata.gz: 32d1a8ad1401d3d081d5bc95ee50069eb6c74a5f8789736edd262a7db0ff5e48e5307845512591dd3f17010f310a47f2796dc26019603181261e4029dc65d151
7
- data.tar.gz: bae5fa03af3175439cddc1b46e705e1f6f083ef714bbb3be02a72c36b7e90b9f8445ef4a8a9c324d88747aaa8904160d93810b6800841687a69f7a72e260af1f
6
+ metadata.gz: d37bd40ed11951a91e1e6a7ddb5f4f655822f0b57c778dd48d93807547bc636768b1d08a3275cae927f5ad32309c0fd918eac556c15e0a347b2444e7898db911
7
+ data.tar.gz: 9a71584b9007b471af11f019e54cc6ee44dd062a79cac2caf9df6e462fe400ee050a4d93c1226c67a25e16552a1388bcaffdb8b7bcaacfc9e3055c11f4dfa4e3
@@ -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
- 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
+ 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
- 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')
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
@@ -12,6 +12,8 @@
12
12
  /lib/libpng/*.so.*
13
13
  /lib/libpng/*.dylib
14
14
  /lib/libpng/*.dll
15
+ /Makefile
16
+ /lib/libpng/Makefile
15
17
  /.idea/
16
18
  /.vscode/
17
19
  *.gem
data/.rspec CHANGED
@@ -1,3 +1,3 @@
1
- RSpec
2
1
  --format documentation
3
2
  --color
3
+ --require spec_helper
data/.rubocop.yml CHANGED
@@ -22,7 +22,7 @@ Metrics/ModuleLength:
22
22
  Metrics/PerceivedComplexity:
23
23
  Max: 20
24
24
  Metrics/ParameterLists:
25
- Max: 9
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
- 10 platform gems are published per release (see README for the full matrix).
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 from PNG bytes) |
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 + IHDR metadata) |
28
- | `lib/libpng/standard_encoder.rb` | `Libpng::StandardEncoder` (libpng standard write API with filter/compression/interlace/bit_depth/palette options) |
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 (~81 examples)
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 10 platform gems, and publishes to RubyGems via OIDC Trusted Publishing.
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 release
35
- (see the link:https://rubygems.org/gems/libpng/versions[RubyGems versions page]
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` and `Libpng.decode` are Ractor-safe. Every call
150
- allocates and frees its own libpng `png_image`; no shared mutable state
151
- exists on the Ruby side. Calls from multiple Ractors run concurrently
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
@@ -39,6 +39,7 @@ platforms = %w[
39
39
  x86_64-linux-musl
40
40
  aarch64-linux
41
41
  aarch64-linux-musl
42
+ aarch64-linux-ohos
42
43
  x86_64-darwin
43
44
  arm64-darwin
44
45
  ]
@@ -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
- # libpng standard (non-simplified) write API.
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