mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-26 02:47:07 -06:00
* Open HEIC/HEIF photos (#355) iPhones save HEIC by default and Photoshop opens it; PhotoCraft had no HEIF code, so .heic was missing from File > Open and failed to decode. Add Format::Heif to photocraft-codecs, decoded by heic-rs (pure Rust, forbid(unsafe), MIT OR Apache-2.0, wasm-ready): single pictures and grid-tiled photos, 8-bit to U8 and 10/12-bit to U16, auxiliary alpha, ICC, EXIF and XMP. The container's irot/imir/clap are applied and EXIF Orientation is rewritten to 1, so photos are never turned twice. Limits are checked against the declared size before decoding. Read-only (no pure-Rust HEVC encoder), listed in ASYMMETRIC_EXCEPTIONS. File > Open (desktop, web) and the batch list accept heic/heif/hif. Tests compare against Apple's own decode and a 16-bit source, cover orientation, metadata, limits and every truncation; fixtures from heic-rs and pillow-heif are attributed in ATTRIBUTION.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * HEIF: pin heic-rs exactly and add a decode_heif fuzz target Review follow-up: heic-rs is young and parses untrusted files, so pin it (=0.1.1) and fuzz the HEIF path. The target covers both orientation paths and is meant to run with -timeout so a hang is reported, not only a panic. It is added to the CI fuzz matrix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * HEIF: a heic-rs panic is a malformed-file error, not a crash Fuzzing decode_heif found two out-of-range slices in heic-rs 0.1.1 on malformed files: - in its box parser (boxes.rs:130), reachable from a 72-byte file, even through the metadata-only probe; - during HEVC reconstruction (hevc/decode/recon.rs:70). The background job path catches panics, but the synchronous ones (jobs off, the CLI) did not. The HEIF codec now runs the decoder under catch_unwind and reports a panic as CodecError::Malformed, as gpu/health.rs already does around wgpu. The fuzz target also calls heic-rs directly, so its panics still reach the fuzzer. The 72-byte input is a regression test, which fails without the guard. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * HEIF: move the decoder into an optional photocraft-heif crate The heic-rs decoding from #372 now lives in `crates/heif` (photocraft-heif, standalone, depends only on heic-rs =0.1.1): probe, decode with a pixel limit, container transforms, alpha, 8/10-bit, ICC/EXIF/XMP and the catch_unwind guard, behind a small API of its own (Info, Options, Decoded, Error). photocraft-codecs uses it behind a new non-default `heif` feature; the apps and photocraft-io forward it. HEIF detection, extensions and mime stay compiled in, so without the feature a HEIC file is still recognised and opening it is a clear "HEIC/HEIF support isn't included in this build of PhotoCraft" error. `cargo xtask layers` allows exactly codecs -> heif between standalone crates. The fixtures are no longer committed: `cargo xtask corpus --heif` fetches them from heic-rs (tests/fixtures, v0.1.1) and pillow-heif (tests/images) at pinned commits, verified against xtask/heif-corpus.sha256 (same bytes as the PR's copies). The fixture tests run with `--features corpus,heif` (`cargo xtask test-corpus` enables both). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Build CI and official binaries with HEIC support (--features heif) CI test and clippy, the FreeBSD job, and the macOS, Linux (and Flatpak), FreeBSD, Windows and web packaging all build with the `heif` feature; CI also tests codecs/io without it on Linux so the "not in this build" path stays covered. `cargo xtask wasm` also checks photocraft-codecs with `--features heif`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Docs: HEIC open is an optional build feature, on in official builds Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com>
104 lines
4.1 KiB
Bash
Executable File
104 lines
4.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Build and package PhotoCraft for FreeBSD:
|
|
#
|
|
# $DIST/photocraft-<version>-freebsd-x86_64.tar.gz a /usr/local-style tree:
|
|
# photocraft-<version>-freebsd-x86_64/{bin, share/applications, share/icons, share/mime,
|
|
# share/metainfo, share/doc/photocraft}
|
|
#
|
|
# Install by copying the tree's contents into /usr/local:
|
|
# tar -xzf photocraft-<version>-freebsd-x86_64.tar.gz --strip-components 1 -C /usr/local
|
|
#
|
|
# Usage: packaging/freebsd/package.sh [--skip-build] [--dry-run]
|
|
# --dry-run stage the tree from stub binaries and list it, without building (works on any OS)
|
|
#
|
|
# Needs: bash, cargo, and the packages installed in .github/workflows/freebsd.yml.
|
|
set -euo pipefail
|
|
# shellcheck source=../env.sh
|
|
. "$(dirname "${BASH_SOURCE[0]}")/../env.sh"
|
|
LINUX="$ROOT/packaging/linux"
|
|
APP_ID=ai.storyteller.photocraft
|
|
|
|
SKIP_BUILD=0
|
|
DRY_RUN=0
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--skip-build) SKIP_BUILD=1; shift ;;
|
|
--dry-run) DRY_RUN=1; SKIP_BUILD=1; shift ;;
|
|
-h | --help) sed -n '2,15p' "$0"; exit 0 ;;
|
|
*) echo "unknown argument: $1" >&2; exit 2 ;;
|
|
esac
|
|
done
|
|
|
|
# FreeBSD's uname says amd64; release file names use x86_64 like the Linux and Windows ones.
|
|
MACHINE="$(uname -m)"
|
|
case "$MACHINE" in
|
|
amd64 | x86_64) ARCH=x86_64 ;;
|
|
arm64 | aarch64) ARCH=aarch64 ;;
|
|
*) echo "unsupported architecture $MACHINE" >&2; exit 2 ;;
|
|
esac
|
|
if [ "$DRY_RUN" = 0 ] && [ "$(uname -s)" != FreeBSD ]; then
|
|
echo "error: build on FreeBSD (or pass --dry-run to check the tree layout)" >&2
|
|
exit 2
|
|
fi
|
|
BASENAME="photocraft-$VERSION-freebsd-$ARCH"
|
|
|
|
echo "==> PhotoCraft $VERSION for FreeBSD $ARCH"
|
|
|
|
# The release VM has 12 GB; full parallelism on the biggest crates runs it out of memory.
|
|
export CARGO_BUILD_JOBS="${CARGO_BUILD_JOBS:-4}"
|
|
if [ "$SKIP_BUILD" = 0 ]; then
|
|
(cd "$ROOT" && cargo build --release --locked -p photocraft -p photocraft-cli --features heif)
|
|
fi
|
|
WORK="$CARGO_TARGET_DIR/freebsd-package"
|
|
STAGE="$WORK/$BASENAME"
|
|
rm -rf "$WORK"
|
|
mkdir -p "$WORK"
|
|
|
|
BIN="$CARGO_TARGET_DIR/release"
|
|
OUT_DIR="$DIST"
|
|
if [ "$DRY_RUN" = 1 ]; then
|
|
# Stub binaries, and the tarball stays in the work dir so dist/ only ever holds real builds.
|
|
BIN="$WORK/stub-bin"
|
|
OUT_DIR="$WORK"
|
|
mkdir -p "$BIN"
|
|
for b in photocraft photocraft-cli; do
|
|
printf '#!/bin/sh\necho "%s %s (dry-run stub)"\n' "$b" "$VERSION" >"$BIN/$b"
|
|
chmod 755 "$BIN/$b"
|
|
done
|
|
fi
|
|
|
|
# ---- stage a /usr/local-style tree --------------------------------------------------------------
|
|
# FreeBSD's install(1) has no -D: create the directories first.
|
|
mkdir -p "$STAGE/bin" "$STAGE/share/applications" "$STAGE/share/mime/packages" \
|
|
"$STAGE/share/metainfo" "$STAGE/share/icons" "$STAGE/share/doc/photocraft"
|
|
install -m 755 "$BIN/photocraft" "$BIN/photocraft-cli" "$STAGE/bin/"
|
|
strip "$STAGE/bin/photocraft" "$STAGE/bin/photocraft-cli" 2>/dev/null || true
|
|
# The desktop entry, MIME type, metainfo and icons are the freedesktop files Linux ships.
|
|
install -m 644 "$LINUX/$APP_ID.desktop" "$STAGE/share/applications/$APP_ID.desktop"
|
|
install -m 644 "$LINUX/$APP_ID.mime.xml" "$STAGE/share/mime/packages/$APP_ID.xml"
|
|
sed -e "s/@VERSION@/$VERSION/g" -e "s/@DATE@/$PHOTOCRAFT_BUILD_DATE/g" \
|
|
"$LINUX/$APP_ID.metainfo.xml.in" >"$STAGE/share/metainfo/$APP_ID.metainfo.xml"
|
|
cp -R "$ROOT/assets/app-icon/hicolor" "$STAGE/share/icons/"
|
|
copy_docs "$STAGE/share/doc/photocraft"
|
|
for f in NOTICE ATTRIBUTION.md; do
|
|
if [ -f "$ROOT/$f" ]; then install -m 644 "$ROOT/$f" "$STAGE/share/doc/photocraft/"; fi
|
|
done
|
|
|
|
for f in bin/photocraft bin/photocraft-cli "share/applications/$APP_ID.desktop" \
|
|
"share/icons/hicolor/256x256/apps/$APP_ID.png" "share/icons/hicolor/scalable/apps/$APP_ID.svg" \
|
|
share/doc/photocraft/LICENSE-MIT share/doc/photocraft/LICENSE-APACHE; do
|
|
if [ ! -e "$STAGE/$f" ]; then echo "error: $f is missing from the package" >&2; exit 1; fi
|
|
done
|
|
|
|
# ---- .tar.gz ------------------------------------------------------------------------------------
|
|
mkdir -p "$OUT_DIR"
|
|
tar -C "$WORK" -czf "$OUT_DIR/$BASENAME.tar.gz" "$BASENAME"
|
|
echo "wrote $OUT_DIR/$BASENAME.tar.gz"
|
|
|
|
"$STAGE/bin/photocraft-cli" --version
|
|
if [ "$DRY_RUN" = 1 ]; then
|
|
echo "==> dry run: tarball contents"
|
|
tar -tzvf "$OUT_DIR/$BASENAME.tar.gz"
|
|
fi
|
|
echo "==> done"
|