Skip to content

Commit bf71d79

Browse files
committed
feat: add owned image capture and checked raster conversions
1 parent 871597a commit bf71d79

30 files changed

Lines changed: 1745 additions & 53 deletions

‎.github/workflows/ci.yml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ jobs:
5151
echo "ZC_EXIF_READER=$PWD/zig-out/read-image-exif" >> "$GITHUB_ENV"
5252
cc -std=c11 -Wall -Wextra -Werror tests/decode_apng.c $(pkg-config --cflags --libs libpng zlib) -o zig-out/decode-apng
5353
echo "ZC_APNG_DECODER=$PWD/zig-out/decode-apng" >> "$GITHUB_ENV"
54+
cc -std=c11 -Wall -Wextra -Werror tests/decode_pixels.c $(pkg-config --cflags --libs libpng) -o zig-out/decode-pixels
55+
echo "ZC_PIXEL_DECODER=$PWD/zig-out/decode-pixels" >> "$GITHUB_ENV"
5456
cc -std=c11 -Wall -Wextra -Werror tests/decode_gif.c -I"$GIF_TEST_PREFIX/include" -L"$GIF_TEST_PREFIX/lib" -Wl,-rpath,"$GIF_TEST_PREFIX/lib" -lgif -o zig-out/decode-gif
5557
echo "ZC_GIF_DECODER=$PWD/zig-out/decode-gif" >> "$GITHUB_ENV"
5658
echo "ZC_IMAGE_DECODER=$PWD/zig-out/decode-images" >> "$GITHUB_ENV"

‎.github/workflows/home-apng.yml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ name: Home APNG conformance
22
on:
33
push:
44
branches: [main]
5-
paths: ['src/home/apng_encoder.zig', 'src/home/raster_encoder.zig', 'src/home/zlib_stream.zig', 'tests/apng*', '.github/workflows/home-apng.yml']
5+
paths: ['src/home/pixel_view.zig', 'src/home/apng_encoder.zig', 'src/home/raster_encoder.zig', 'src/home/zlib_stream.zig', 'tests/apng*', '.github/workflows/home-apng.yml']
66
pull_request:
7-
paths: ['src/home/apng_encoder.zig', 'src/home/raster_encoder.zig', 'src/home/zlib_stream.zig', 'tests/apng*', '.github/workflows/home-apng.yml']
7+
paths: ['src/home/pixel_view.zig', 'src/home/apng_encoder.zig', 'src/home/raster_encoder.zig', 'src/home/zlib_stream.zig', 'tests/apng*', '.github/workflows/home-apng.yml']
88
workflow_dispatch:
99
permissions:
1010
contents: read

‎.github/workflows/home-gif.yml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ name: Home GIF conformance
22
on:
33
push:
44
branches: [main]
5-
paths: ['src/home/gif_encoder.zig', 'src/home/gif_lzw.zig', 'src/home/palette_quantizer.zig', 'src/home/raster_encoder.zig', 'tests/gif_raster_fixture.zig', 'tests/decode_gif.c', '.github/workflows/home-gif.yml']
5+
paths: ['src/home/pixel_view.zig', 'src/home/gif_encoder.zig', 'src/home/gif_lzw.zig', 'src/home/palette_quantizer.zig', 'src/home/raster_encoder.zig', 'tests/gif_raster_fixture.zig', 'tests/decode_gif.c', '.github/workflows/home-gif.yml']
66
pull_request:
7-
paths: ['src/home/gif_encoder.zig', 'src/home/gif_lzw.zig', 'src/home/palette_quantizer.zig', 'src/home/raster_encoder.zig', 'tests/gif_raster_fixture.zig', 'tests/decode_gif.c', '.github/workflows/home-gif.yml']
7+
paths: ['src/home/pixel_view.zig', 'src/home/gif_encoder.zig', 'src/home/gif_lzw.zig', 'src/home/palette_quantizer.zig', 'src/home/raster_encoder.zig', 'tests/gif_raster_fixture.zig', 'tests/decode_gif.c', '.github/workflows/home-gif.yml']
88
workflow_dispatch:
99
permissions:
1010
contents: read

‎.github/workflows/home-pixels.yml‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: Home image pixels
2+
on:
3+
push:
4+
branches: [main]
5+
paths: ['src/home/pixel_view.zig', 'src/home/raster_encoder.zig', 'include/zig_camera.h', 'tests/pixel_*', '.github/workflows/home-pixels.yml']
6+
pull_request:
7+
paths: ['src/home/pixel_view.zig', 'src/home/raster_encoder.zig', 'include/zig_camera.h', 'tests/pixel_*', '.github/workflows/home-pixels.yml']
8+
workflow_dispatch:
9+
permissions:
10+
contents: read
11+
jobs:
12+
pixels:
13+
strategy:
14+
fail-fast: false
15+
matrix:
16+
os: [ubuntu-24.04, ubuntu-24.04-arm, macos-15]
17+
zig: ['0.15.2', 'master']
18+
runs-on: ${{ matrix.os }}
19+
steps:
20+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262
21+
- uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29
22+
with:
23+
version: ${{ matrix.zig }}
24+
- name: Pixel ownership and independent sample reference
25+
shell: bash
26+
run: |
27+
set -euo pipefail
28+
for mode in Debug ReleaseSafe; do
29+
zig test -O "$mode" -lc src/home/pixel_view.zig
30+
zig build-obj -O "$mode" -fcompiler-rt -lc --dep pixel -Mroot=tests/pixel_fixture.zig -Mpixel=src/home/pixel_view.zig -femit-bin=/tmp/home-pixels.o
31+
cc -std=c11 -O2 -Wall -Wextra -Werror tests/pixel_reference.c /tmp/home-pixels.o -o /tmp/home-pixels
32+
/tmp/home-pixels
33+
done

‎NOTICE‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2424
SOFTWARE.
2525

2626
Additional Home adaptations and reusable modules:
27+
- src/home/pixel_view.zig and raster_encoder.zig reuse Home's shared checked
28+
integer raster view and consumer aliases at
29+
ec74cce5a9c51b33a4e3b88a476c80a56eaf2ce5. tests/pixel_fixture.zig and
30+
pixel_reference.c reuse that revision's independent sample-reference fixtures.
2731
- src/home/gif_encoder.zig, gif_lzw.zig and palette_quantizer.zig are verbatim
2832
Home Image checked GIF/weighted palette/dictionary modules at
2933
c206b47164384ae99c3abb842ed2816afa34df0b. Tests/gif_raster_fixture.zig and

‎README.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ AVC/HEVC/AAC adapters, containers and bounded media queues. No FFmpeg or Python
66
runtime, bindings, test scripts or build scripts are used by this project.
77
Physical camera performance and image quality have **not been measured**.
88

9+
Version 0.84 adds [owned image objects](docs/image-objects.md) across capture,
10+
focus-locked capture and scoped callbacks, with independent RGB8/RGB16 pixels,
11+
checked Home clone/crop/conversion, encoding and joined staged saves after camera
12+
release. Home's Image API also receives transactional word conversion and deep
13+
animation-frame cloning fixes.
14+
915
Version 0.83 adds [GIF still capture](docs/gif.md) through C/Zig/Bun/CLI, reusing
1016
Home's bounded palette and dictionary LZW with independent giflib readback.
1117
Ordinary and focus-locked file captures now keep output private through request

‎build.zig.zon‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
.{
22
.name = .zig_camera,
3-
.version = "0.83.0",
3+
.version = "0.84.0",
44
.fingerprint = 0x774c3582060c5787,
55
.minimum_zig_version = "0.15.2",
66
.dependencies = .{},

‎docs/home-os-reuse.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,3 +451,12 @@ return status without decoding. Per ADR-MC3, the checked GIF/palette algorithms
451451
improve Home stdlib and are reused in camera; hosted file jobs/libcamera leases
452452
remain camera ownership. No OS source is copied, changed or executed, and no
453453
physical support or throughput result is inferred.
454+
455+
The 0.84 image-object audit fetches unchanged clean Home OS main at
456+
151269666bc09aa15a271f1c1f4603f681132e65 and reads ADR-MC3, camera/media,
457+
framebuffer and VideoCore7 sources. Kernel RGB565, packed framebuffer format
458+
constants, nominal capture and container image-name copying do not implement a
459+
checked hosted gray/RGB/RGBA16 owner/converter. The reusable pixel primitive and
460+
Image ownership fixes improve Home stdlib per ADR-MC3; libcamera leases, borrowed
461+
FFI input and staged native I/O remain hosted camera ownership. No OS code is
462+
copied, changed or executed, and no physical acceptance/performance follows.

‎docs/home-reuse.md‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,3 +1375,38 @@ The callback/publication fix is also checked against the previous 0.82 library,
13751375
including CAF restoration failure and cancellation. The normal local runner
13761376
still fails macOS SDK/libSystem linkage before project compilation; direct
13771377
native/library builds pass. Linux real-backend/platform CI remains a release gate.
1378+
1379+
The 0.84 owned-image audit starts at Home
1380+
c206b47164384ae99c3abb842ed2816afa34df0b and inspects Image.clone/toFormat,
1381+
getPixel/setPixel, existing RGB16/RGBA views and the shared raster encoders.
1382+
clone drops frames/loop count; toFormat leaves unsupported target allocations
1383+
uninitialized and getPixel yields black for word sources. Home contribution
1384+
ec74cce5a9c51b33a4e3b88a476c80a56eaf2ce5 extracts the existing raster view into
1385+
a std-only checked pixel module, adds exact integer conversion/cropped ownership,
1386+
fixes deep clones and publishes main/frame conversions transactionally. Indexed
1387+
source expansion is reused; unsupported indexed targets reject explicitly.
1388+
1389+
The portable module and raster consumer aliases are copied verbatim here, along
1390+
with independent reference fixtures. Each local pinned/development Zig Debug/
1391+
ReleaseSafe build passes three view/owner tests and 576 independent C fixtures.
1392+
APNG's 216-fixture and GIF's 252-fixture matrices also pass in all four builds with
1393+
the extracted view; complete Home Image API passes 159 development-Zig tests.
1394+
Home hosted Actions has no exact-head run and is not described as green.
1395+
1396+
C borrowed raster operations and Zig/Bun owned capture objects reuse those checked
1397+
algorithms and existing Home encoding/metadata paths. Frame copies retain no
1398+
lease; standalone saves reuse the 32-owner staged native file factory, snapshot
1399+
pixels through admission, and join publication/disposal. No additional native
1400+
pixel pool or queue/state algorithm is added. Raw image objects use explicit Exif
1401+
rather than inventing capture metadata. No Python or FFmpeg is added. General
1402+
resampling/decoding, more formats/conversions, image semantics and physical
1403+
acceptance remain open.
1404+
1405+
Final local camera validation passes 399 target-qualified ReleaseSafe native
1406+
tests, all 1,083 Bun tests with image/GIF/APNG/ICC/Exif/AVC/HEVC/AAC readback and
1407+
SDL enabled, C ABI execution, typecheck/fmt, and the independent word/alpha PNG
1408+
reader. Raw source fields and converted/cropped/blob outputs are checked before
1409+
writes; source images survive camera release, closure, GC and joined saves.
1410+
The ordinary local build runner still fails macOS SDK/libSystem linkage before
1411+
project compilation; direct native/library builds pass. Platform CI and real
1412+
Linux backend compile/load/no-camera smoke remain publication gates.

‎docs/image-objects.md‎

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Owned image objects
2+
3+
Version 0.84 adds Bun `Frame.makeImage`, `Camera.captureImageAsync`/`captureImage`
4+
and `captureFocusLockedImageAsync`/`captureFocusLockedImage`. They produce
5+
`CapturedImage` instances with independent compact pixels rather than retained
6+
camera buffers. Main/lores RGB/BGR/RGBX/BGRX, planar/semi-planar YUV420, packed422
7+
and RGB16 sources reuse the existing checked native copy/conversion paths.
8+
Request callbacks also expose `makeImage`; their scoped request expires while
9+
the copied image remains usable. Capture selection keeps existing flush/drop/
10+
SyncReady and CAF restoration semantics.
11+
12+
Default precision follows the source: RGB16 remains sixteen bits; other inputs
13+
produce RGB8. `bits: 8` explicitly reduces word sources, and `bits: 16` rejects
14+
an eight-bit capture rather than inventing sensor precision. Each capture makes
15+
one independent native copy into owned Bun bytes. Geometry comes from the native
16+
stream descriptor. The scalar `sourceMetadata` snapshot, including its crop, is
17+
frozen, as is the source color description. This is source provenance; cropping
18+
an image does not change the recorded sensor ROI, and copied RGB bytes no longer
19+
have the source stream's packed YUV layout. Full/vendor metadata catalogs remain
20+
available from the original request.
21+
22+
```ts
23+
using image = await camera.captureImage({ stream: 'main' })
24+
await camera.close()
25+
using crop = image.crop(10, 10, 320, 240)
26+
using rgba16 = crop.convert({ format: 'rgba', bits: 16, endian: 'big' })
27+
await rgba16.saveAsync('crop.png')
28+
```
29+
30+
Images expose immutable geometry/format/stride information, `getPixel` (RGBA8),
31+
`getPixel16` (RGBA16), `copyPixels`, `clone`, checked `crop`/`convert`, `encode`,
32+
synchronous atomic `save`, joined staged `saveAsync`, `close` and `Symbol.dispose`.
33+
Returned pixel copies are independent and writable. Closing an image releases
34+
its private Bun reference; it does not invalidate other images or returned
35+
copies. Already admitted save jobs retain their own input reference and can
36+
complete after source closure. Standalone image jobs belong to their caller,
37+
independently of the source camera's lifetime.
38+
39+
`CapturedImage.fromRaster` copies checked gray/RGB/RGBA8/16 data with explicit
40+
endianness and optional row padding. Caller property accessors are consumed
41+
before native access, and intrinsic typed-array properties detect detached or
42+
misrepresented buffers. Conversion uses Home's new checked integer view:
43+
8-to-16 byte replication, 16-to-8 high-byte reduction, native-precision integer
44+
luma and explicit alpha dropping, without gamma/gamut conversion/compositing.
45+
Identical formats copy active rows. There is no resampling or hidden quality
46+
reduction. Converted/cropped/source pixels and encoded output are limited to
47+
256 MiB by the raster API; allocator scratch is additional.
48+
49+
JPEG/PNG/BMP/TIFF/PPM/GIF reuse Home encoders. PNG/TIFF/PPM retain appropriate
50+
word precision; JPEG/BMP/GIF reduce to their supported representation. JPEG and
51+
PPM reject RGBA until an explicit RGB conversion is requested. GIF thresholds
52+
alpha as documented in gif.md. Gray pixels reject RGB ICC profiles. Other RGB
53+
ICC/explicit Exif options retain the existing validation; built-in sRGB requires
54+
a matching source declaration. `fromRaster.colorSpace` declares interpretation
55+
without converting pixels. The color values remain caller-owned assertions.
56+
57+
An image object has no live capture request. Its bare saves default to no Exif,
58+
matching an independent image save; typed/raw caller Exif is supported without
59+
automatic capture fields. `exif: true` or `captureMetadata: true` explicitly
60+
reject. Request encoding/file APIs retain their existing request-derived Exif
61+
defaults. Raw sensor output still requires the calibrated DNG/request APIs.
62+
63+
`saveAsync` reserves the existing bounded native file owner before yielding,
64+
then stages privately and joins publication/disposal. The shared factory admits
65+
at most 32 owners. Cancellation before publication preserves the destination;
66+
the native publication claim rejects late cancellation. The 30-second default
67+
deadline is rechecked after encoding. Encoding is synchronous on the Bun owner;
68+
native file I/O is asynchronous. This is not a new background codec worker.
69+
70+
Zig exposes `Frame.makeImage(allocator, stream, bits)` returning
71+
`ImagePixels.Owned`; `null` selects native precision. Deinitialize that independent
72+
owner explicitly. `ImagePixels.View`/`Owned.copy` provide checked word conversion
73+
and cropped copies; request metadata can be copied separately before release.
74+
C uses borrowed `zc_raster` descriptors (the existing 48-byte raster layout),
75+
`zc_raster_convert`/`crop` size queries and copies, `zc_raster_pixel16`, owned-blob
76+
`zc_raster_encode`, atomic `zc_raster_save` and staged
77+
`zc_image_file_prepare_raster`. Native calls consume pixel pointers synchronously
78+
and retain none. The existing public ABI layouts and version remain unchanged.
79+
80+
Home's Image API additionally receives corrected deep frame/palette cloning,
81+
Color16 access and transactional main/frame conversion, replacing uninitialized
82+
sixteen-bit destinations and black source conversions. Indexed source expansion
83+
is shared with PNG/GIF; indexed destinations explicitly reject. Home's portable
84+
view and independent test fixtures are reused verbatim here.
85+
86+
The portable matrix checks 576 independent C-reference fixtures per pinned/
87+
development Zig Debug/ReleaseSafe build. Bun tests cover all processed main/lores
88+
sources, lifetime independence, conversion, source detachment/accessors, callback/
89+
CAF images, explicit Exif/ICC, save cancellation/GC/late claims and bounded burst
90+
admission/reuse. Independent libpng checks every encoded gray/RGB/RGBA8/16 byte,
91+
word, alpha sample and byte order. C/Zig tests check bounds, overlap, no-prefix
92+
copies, blob ownership and request/camera retirement. These tests are software
93+
evidence, not physical sensor quality, format acceptance or camera performance.
94+
95+
Further image transforms/resizing, temporary-mode image conveniences, palette/
96+
float/gamut conversion, general external image decoding and complete animation/
97+
image-object semantics remain work. The full Picamera2 parity goal remains open.

0 commit comments

Comments
 (0)