Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b07cff29ed | ||
|
|
8881bfead1 | ||
|
|
2d219dd115 | ||
|
|
2807b77e8c | ||
|
|
d0088fac89 |
No files matched your search
@@ -1,4 +1,4 @@
|
||||
# A version tag builds the three packages, each on its own system, and publishes them.
|
||||
# A version tag builds the four packages, each on its own system, and publishes them.
|
||||
name: Release
|
||||
on:
|
||||
push:
|
||||
@@ -6,41 +6,62 @@ on:
|
||||
permissions:
|
||||
contents: write
|
||||
jobs:
|
||||
deb:
|
||||
version:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v7
|
||||
# The tag must name the version each build file carries.
|
||||
- run: |
|
||||
v=${GITHUB_REF_NAME#v}
|
||||
grep -qF "project(omaphoto VERSION $v " CMakeLists.txt || { echo "CMakeLists.txt is not $v"; exit 1; }
|
||||
grep -qxF "pkgver=$v" packaging/arch/PKGBUILD || { echo "PKGBUILD is not $v"; exit 1; }
|
||||
grep -qF "version = \"$v\";" flake.nix || { echo "flake.nix is not $v"; exit 1; }
|
||||
deb:
|
||||
needs: version
|
||||
runs-on: ubuntu-24.04
|
||||
# One DEB per Ubuntu LTS: each names its own libraries.
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- codename: noble
|
||||
base: ubuntu:24.04
|
||||
- codename: resolute
|
||||
base: ubuntu:26.04
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
# The dev image carries ONNX Runtime, its notices and the model.
|
||||
- run: docker build -t omaphoto-dev -f Dockerfile.dev .
|
||||
- run: docker build -t omaphoto-dev --build-arg BASE=${{ matrix.base }} -f Dockerfile.dev .
|
||||
- run: |
|
||||
docker run --rm -u "$(id -u):$(id -g)" -e HOME=/tmp -v "$PWD:/src" -w /src omaphoto-dev sh -c '
|
||||
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DOMAPHOTO_BUNDLE_ORT=ON -DOMAPHOTO_MODEL=/usr/local/share/omaphoto/u2net.onnx &&
|
||||
cmake --build build --target omaphoto && cd build && cpack -G DEB'
|
||||
mv build/omaphoto_*.deb omaphoto_amd64.deb
|
||||
- uses: actions/upload-artifact@v4
|
||||
mv build/omaphoto_*.deb omaphoto-${{ matrix.codename }}_amd64.deb
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: deb
|
||||
path: omaphoto_amd64.deb
|
||||
name: deb-${{ matrix.codename }}
|
||||
path: omaphoto-${{ matrix.codename }}_amd64.deb
|
||||
rpm:
|
||||
needs: version
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v7
|
||||
- run: docker build -t omaphoto-fedora scripts/distro/fedora
|
||||
- run: |
|
||||
docker run --rm -u "$(id -u):$(id -g)" -e HOME=/tmp -v "$PWD:/src" -w /src omaphoto-fedora sh -c '
|
||||
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DOMAPHOTO_MODEL=/usr/local/share/omaphoto/u2net.onnx &&
|
||||
-DOMAPHOTO_BUNDLE_ORT=ON -DOMAPHOTO_MODEL=/usr/local/share/omaphoto/u2net.onnx &&
|
||||
cmake --build build --target omaphoto && cd build && cpack -G RPM'
|
||||
mv build/omaphoto-*.x86_64.rpm omaphoto.x86_64.rpm
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: rpm
|
||||
path: omaphoto.x86_64.rpm
|
||||
arch:
|
||||
needs: version
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v7
|
||||
- run: docker build -t omaphoto-arch scripts/distro/arch
|
||||
# makepkg refuses root; a user of the runner's id builds.
|
||||
- run: |
|
||||
@@ -48,7 +69,7 @@ jobs:
|
||||
useradd -u $(id -u) builder && chown -R builder /src &&
|
||||
su builder -c 'cd /src/packaging/arch && makepkg -f'"
|
||||
mv packaging/arch/omaphoto-[0-9]*-x86_64.pkg.tar.zst omaphoto-x86_64.pkg.tar.zst
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: arch
|
||||
path: omaphoto-x86_64.pkg.tar.zst
|
||||
@@ -56,13 +77,13 @@ jobs:
|
||||
needs: [deb, rpm, arch]
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
merge-multiple: true
|
||||
path: packages
|
||||
- working-directory: packages
|
||||
run: sha256sum omaphoto_amd64.deb omaphoto.x86_64.rpm omaphoto-x86_64.pkg.tar.zst > SHA256SUMS
|
||||
run: sha256sum omaphoto-noble_amd64.deb omaphoto-resolute_amd64.deb omaphoto.x86_64.rpm omaphoto-x86_64.pkg.tar.zst > SHA256SUMS
|
||||
- env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
|
||||
@@ -75,8 +75,8 @@ Platform adaptations, each forced by a missing Qt analog:
|
||||
- Brush speed (8.3's gate: 4K at a scale of 2, an 800 px soft brush, median frame at most 8 ms): 6.8–7.4 ms measured, from 47. The soft tip's density comes from a table of 8192 steps over d²/r² from h² to the rim, in radii because hardness alone shapes it, linear between steps; inside h the tip is solid, the table's first value, so a near-hard tip keeps every step across its fade (from 0.9995 on, a table over the whole radius missed the falloff by up to 46 levels). Once the table's steps are finer than float can tell apart ((1 − h²) / 8192 under float's epsilon, from about 0.9995), even that table misses by a few levels: there `tipDensity` evaluates the shader's own formula, float for float, for dabs and segments alike, its solid and empty ends returning at once what the formula yields, so only samples in the thin fade pay for `exp` and `log`. `BrushCoverageTests` holds dabs and strokes on both sides of the switch against the shader's own quadrature, replayed in float. The kernel keeps the shader's order of float operations to the letter (`midpoint ± halfLength · node − projection`, never regrouped): 8192 pixels along a near-hard stroke a regrouping moved a pixel three levels, and a reference written from the twin's own code repeated it. Its one read per lookup is checked at the upper step, so a lost bound aborts. Only a segment passing through the solid core can look up inside it, so the lower clamp runs for those alone (`quadrature<true>`): for every segment it cost 0.3 ms a frame. Bands of 32 rows are the tasks. The halving's bands take their rows, sums and source row from the caller's scratch, so a worker allocates nothing and a failed allocation throws where the caller catches it (QtConcurrent wraps a worker's exception in `QUnhandledException`). `publish` copies each tile and makes its selection coverage on the calling thread, so a failure throws there and a worker's malloc arena never meets a starved allocator, then fills side by side. Pieces compose and halve side by side (`sideBySide`), each halving unthreaded: Qt 6.4 deadlocks on a `blockingMap` inside a map task. The Lanczos colour rows sum four channels in a GCC vector (`vector_size(16)`), each lane as the scalar sum, and the vertical pass runs tap by tap over whole rows; `BrushCoverage.cpp` and `DownsampleCache.cpp` build at `-O3`, where GCC vectorises them. The shadow is skipped when the dirty rect lies inside the document, whose opaque fill covers it. Equivalent mutants: that skip, `fill`'s skip of a zero weight, the colour row's right clamp (it only ever reads padding), the table's interpolation (under a third of a level), its lower clamp at step 1 (the first steps of every fade sit at the density's cap), the clamped quadrature for every segment, the formula for every hardness and `tipDensity`'s two early returns (each only costs time). `BrushPerformanceTests` runs under `BRUSH_BENCHMARK=1`.
|
||||
- Canvas speed (11.1): `CanvasPerformanceTests` (under `CANVAS_BENCHMARK=1`, at a backing scale of two, as the brush benchmark) pans a 1000-point canvas by trackpad deltas through the canvas's own wheel path, at fit and at Actual Pixels (`zoom(1)`, as the menu sets it), and zooms it in 5% steps around its middle, over a 3840 × 2160 document of ten noise layers (photographs and halves, opacities, multiply, screen, overlay and two Hue layers). Measured on the dev container (32 cores): pan at fit 30 ms median (p95 37), pan at Actual Pixels 68 ms (p95 74), zoom 77 ms (p95 86); with every layer normal 16, 15 and 29 ms. The hand-blended HSL modes are the cost: `HslBlend::blend` ran one thread over the layer's device area, 40 ms a Hue layer at fit; its rows now blend side by side (pointers taken first, since `scanLine` detaches), the same bytes, 3× faster (pan at fit 85 → 30 ms), and the mode is checked before the rows start, since a worker's exception comes back wrapped. At Actual Pixels a Hue layer still costs about 25 ms at 2× (the backdrop's copy and conversion, the enlarged surface and its draw back); the rest is QPainter's single-threaded smooth scaling, about 40 megapixels a frame at this size. Painting the canvas in parallel bands would need its caches (pieces, strokes, effects previews) made safe across threads, and is not done. The guards sit at half again these medians (45, 100 and 115 ms), so a regression fails. Compared with `docs/brush-performance.md`: Swift measures strokes only (2.6–2.8 ms median on macOS); the twin's stroke gate is 8.3's (6.8–7.4 ms at 4K, scale two), and Swift has no pan or zoom figures to compare. `Rendering/PoolMap` (Linux-only) wraps every `QtConcurrent::blockingMap`: Qt 6.4's deadlocks when a pool thread waits on a full pool (a worker's blur or blend on one core, or two workers on two), so a pool thread lends its place while it waits (`releaseThread`, then `reserveThread`, Qt's documented remedy); `PoolMapTests` runs the blend, the blur and a map on a pool of one thread.
|
||||
- Desktop integration (11.2): the app id is `io.github.ZacharyZhang_NY.OmaPhoto` (Compositor's was its bundle identifier, `com.wonderassembly.compositor`), which names the desktop entry, the icon and the MIME package, and which `main` gives Qt as the desktop file name, so a Wayland compositor matches the window to its entry and icon; on X11 Qt 6.4 names the window class after the application (`omaphoto`, `OmaPhoto`), so the entry's `StartupWMClass` is `OmaPhoto`. `packaging/io.github.ZacharyZhang_NY.OmaPhoto.desktop` launches `omaphoto %F` (files named at launch reach `ProjectWorkspace::receive`, as Finder's Open With reaches the delegate) and lists Swift's two document types: its projects (`application/x-compositor-project`) and JPEG, PNG, HEIC and TIFF. `io.github.ZacharyZhang_NY.OmaPhoto.xml` declares the project type for shared-mime-info, a folder (`inode/directory`) named `*.comp`, commented "OmaPhoto Project", where Swift says "Compositor Project"; a file manager may still open such a folder as a folder, since a project is a directory on Linux. `cmake --install` lays down the app, the entry, the MIME package, Compositor's app icon (`packaging/icons`) at six hicolor sizes (16 to 512) and U²-Net's licence under `share/licenses/omaphoto`; `OMAPHOTO_MODEL`, a path, installs the model as `share/omaphoto/u2net.onnx` whatever its own name, the file itself when given a link (`file(REAL_PATH)`), where `SubjectRemoval` looks, and packages pass it (11.3). A direct install (no `DESTDIR`) runs `update-mime-database` on its prefix, required, so the project type registers at once; a staged install leaves that to the package's hooks. `DesktopIntegrationTests` reads both files, stages an install and compares the whole file list (the model included when the build was configured with one, read from its cache), and installs directly with a stand-in `update-mime-database` first on the path and any `DESTDIR` of the caller cleared; the call in `main` is not under test.
|
||||
- Packages (11.3): one CMake install serves every package. It lays down the app, its entry, MIME package, icons and both licences (the app's MIT `LICENSE` and U²-Net's, under `share/licenses/omaphoto`), and `OMAPHOTO_MODEL` adds the model. `OMAPHOTO_BUNDLE_ORT` (off by default) installs ONNX Runtime beside the app in `lib/compositor` with the app's run path set there, for Ubuntu 24.04, which packages none, and Microsoft's `LICENSE` and `ThirdPartyNotices.txt` with it (`OMAPHOTO_ORT_NOTICES`, where `Dockerfile.dev` keeps them from the release; a configure that cannot find them fails); every other system brings its own. `DesktopIntegrationTests` expects the runtime (in the configured library folder), its version link and its notices in a bundling build, as it expects the model in a build given one, reading what CMake itself evaluated through compile definitions (both checked by hand in a second build). CPack makes the DEB (Ubuntu) and the RPM (Fedora), each on its own system: the DEB's library dependencies come from `dpkg-shlibdeps`, which finds the bundled runtime through the run path, and it names what no library does (`libheif-plugin-libde265` for HEVC, `qt6-image-formats-plugins` for TIFF, `shared-mime-info`, `hicolor-icon-theme`); the RPM asks for `qt6-qtimageformats`, `shared-mime-info` and `hicolor-icon-theme`, its libraries found by rpmbuild, and suggests RPM Fusion's `libheif-freeworld`, since Fedora's own libheif decodes no HEVC. Packages stage their install (`DESTDIR`), so their systems' hooks and triggers refresh the MIME and icon caches. `Dockerfile.dev` gains `dpkg-dev`, `file` and `fakeroot` for the DEB. `packaging/arch/PKGBUILD` builds this checkout for Arch and Omarchy (`makepkg` there), fetching the model by its checksum and depending on `onnxruntime`, which `onnxruntime-cpu` and the GPU builds provide. `flake.nix` sits at the repository root; it builds the app alone and installs staged (`DESTDIR=/`) so the store holds no MIME cache. The DEB was built and read back in the dev image; the RPM, the PKGBUILD and the flake are built on their own systems in 11.4.
|
||||
- Distro checks (11.4): `scripts/distro-check.sh arch|fedora|nixos` builds `scripts/distro/<name>/Dockerfile` and runs dev.sh's configure, build and full test run in it, warnings as errors, as uid 1000 (root ignores the read-only folders and unreadable files the tests make), the tree mounted read-only and `HOME` and `TMPDIR` on a tmpfs (a sync on the container's overlay waits on the host's disk); NixOS builds the flake's package (nixpkgs pinned by the committed `flake.lock`) as root, since Nix writes its store, then runs the tests in its `devShells.default` as uid 1000 (`chroot --userspec`, with `HOME` and `TMPDIR` put back, since `nix develop` points `TMPDIR` at a folder root made); the shell sets the plugin path, the model's data path and a fontconfig file of DejaVu with fontconfig's own rules from the store, since NixOS keeps them in `/etc/fonts/conf.d`, which a container lacks. Every image carries DejaVu Sans, Serif and Mono, the faces the tests name (Fedora splits them into three packages). Ubuntu 24.04 is the dev image itself. What newer Qt and GCC asked for: `QImage::flipped` from Qt 6.9 where 6.4 has `mirrored`; `Q_OBJECT` on every class a test finds with `findChild` (Qt 6.11 refuses one without); `DistortWarp` projects points itself (`project` in `Distort.cpp`), since Qt 6.11's `QTransform::map` clips w for points past the horizon where Swift divides whatever its sign; a local pragma for GCC 16's false `-Wmismatched-new-delete` in `DownsampleTests`' replaced operators. Tests follow the systems where only a message or a count differs: libheif 1.23's longer messages, Qt 6.9's TIFF plugin logging libtiff's errors as criticals, fonts named rather than counted, the Type icon's letters in the system's own sans (Fedora's is Noto: the icon sheet compares them only under DejaVu Sans), and `QT_FORCE_STDERR_LOGGING` for the launched app, since a journald-built Qt logs to the journal when stderr is no console. The five starvation tests place their failure by Qt 6.4's own allocations and skip on any other Qt (`placesStarvation` in `AddressSpaceLimit.h`); the limit itself holds there.
|
||||
- Packages (11.3): one CMake install serves every package. It lays down the app, its entry, MIME package, icons and both licences (the app's MIT `LICENSE` and U²-Net's, under `share/licenses/omaphoto`), and `OMAPHOTO_MODEL` adds the model. `OMAPHOTO_BUNDLE_ORT` (off by default) installs ONNX Runtime beside the app in `lib/omaphoto` (under the library folder) with the app's run path set there, and Microsoft's `LICENSE` and `ThirdPartyNotices.txt` with it (`OMAPHOTO_ORT_NOTICES`, where the images keep them from the release; a configure that cannot find them fails). Every package bundles (11.4d, issue #5): ONNX Runtime stamps each release's symbols with its version (`VERS_1.20.1`), so a package linked against a system's runtime refuses to install, or to start, once that system updates it. The DEB, the RPM and the Arch package carry Microsoft's 1.21.0 build, pinned by checksum, which the dev, Fedora and Arch images install into `/usr/local` too, so the tests run against the runtime that ships; the flake builds from nixpkgs' own, since Nix rebuilds its closure with it. `DesktopIntegrationTests` expects the runtime (in the configured library folder), its version link and its notices in a bundling build, as it expects the model in a build given one, reading what CMake itself evaluated through compile definitions (both checked by hand in a second build). CPack makes the DEB (Ubuntu) and the RPM (Fedora), each on its own system: the DEB's library dependencies come from `dpkg-shlibdeps`, which finds the bundled runtime through the run path, and it names what no library does (`libheif-plugin-libde265` for HEVC, `qt6-image-formats-plugins` for TIFF, `shared-mime-info`, `hicolor-icon-theme`); the RPM asks for `qt6-qtimageformats`, `shared-mime-info` and `hicolor-icon-theme`, its libraries found by rpmbuild but the bundled runtime left out of what it provides and requires (`__provides_exclude_from`, `__requires_exclude` in `CPACK_RPM_SPEC_MORE_DEFINE`), and suggests RPM Fusion's `libheif-freeworld`, since Fedora's own libheif decodes no HEVC. Packages stage their install (`DESTDIR`), so their systems' hooks and triggers refresh the MIME and icon caches. `Dockerfile.dev` gains `dpkg-dev`, `file` and `fakeroot` for the DEB. `packaging/arch/PKGBUILD` builds this checkout for Arch and Omarchy (`makepkg` there), fetching the model and Microsoft's runtime by their checksums; its `prepare()` lays the runtime out as that build's CMake config expects (`lib64`, `include/onnxruntime` beside `lib/cmake`) and the package bundles it. `flake.nix` sits at the repository root; it builds the app alone and installs staged (`DESTDIR=/`) so the store holds no MIME cache. The DEB was built and read back in the dev image; the RPM, the PKGBUILD and the flake are built on their own systems in 11.4.
|
||||
- Distro checks (11.4): `scripts/distro-check.sh arch|fedora|nixos|resolute` builds `scripts/distro/<name>/Dockerfile` (for `resolute`, `Dockerfile.dev` on Ubuntu 26.04; Fedora is 43 since 11.4d) and runs dev.sh's configure, build and full test run in it, warnings as errors, as uid 1000 (root ignores the read-only folders and unreadable files the tests make), the tree mounted read-only and `HOME` and `TMPDIR` on a tmpfs (a sync on the container's overlay waits on the host's disk); NixOS builds the flake's package (nixpkgs pinned by the committed `flake.lock`) as root, since Nix writes its store, then runs the tests in its `devShells.default` as uid 1000 (`chroot --userspec`, with `HOME` and `TMPDIR` put back, since `nix develop` points `TMPDIR` at a folder root made); the shell sets the plugin path, the model's data path and a fontconfig file of DejaVu with fontconfig's own rules from the store, since NixOS keeps them in `/etc/fonts/conf.d`, which a container lacks. Every image carries DejaVu Sans, Serif and Mono, the faces the tests name (Fedora splits them into three packages). Ubuntu 24.04 is the dev image itself. What newer Qt and GCC asked for: `QImage::flipped` from Qt 6.9 where 6.4 has `mirrored`; `Q_OBJECT` on every class a test finds with `findChild` (Qt 6.11 refuses one without); `DistortWarp` projects points itself (`project` in `Distort.cpp`), since `QTransform::map` clamps w for points past the horizon from Qt 6.10 (6.10.2 and 6.11.2 probed; 6.4 divides), where Swift divides whatever its sign, and `DistortTests` pins it past the horizon (on 6.4 the two agree, so a mutant there is equivalent); a local pragma for GCC 16's false `-Wmismatched-new-delete` in `DownsampleTests`' replaced operators. Tests follow the systems where only a message or a count differs: libheif 1.23's longer messages, Qt 6.9's TIFF plugin logging libtiff's errors as criticals, fonts named rather than counted, the Type icon's letters in the system's own sans (Fedora's is Noto: the icon sheet compares them only under DejaVu Sans), and `QT_FORCE_STDERR_LOGGING` for the launched app, since a journald-built Qt logs to the journal when stderr is no console. The five starvation tests place their failure by Qt 6.4's own allocations and skip on any other Qt (`placesStarvation` in `AddressSpaceLimit.h`); the limit itself holds there. `ProjectExportFailureTests` opens a panel and an alert once in `initTestCase`, before any limit, as the app's window has before any export: a snapshot that fails opens its alert while the limit holds, and on every system that alert was the process's first font lookup and the panel's first open, costs the app never meets there. In the NixOS shell, which reads fontconfig's whole `conf.d` without a cache, the lookup's failed allocation segfaulted inside fontconfig (`FcHashTableFind`, found under gdb); on the dev image a panel test run alone ran out inside the panel's first open. `theJPEGRenderTakesTheSnapshotWithoutACopy` holds the JPEG render's snapshot to a move under a 2 MB limit, as `thePanelOpensWithoutCopyingTheSnapshot` holds the PNG panel's. The flake leaves out `dev.sh`'s `build/`, which nixpkgs' CMake hook would take for its own build folder.
|
||||
- Spot Healing (8.4): `HealPixels.c` compiles in place beside the other kernels. `SpotHealingMode`, `allSpotHealingModes` and `rawValue` (Swift's names) sit in `BrushStroke.h`, and `BrushSettings` gains `healing` and `healingMode`. While the stroke lasts its tiles show Swift's wash, gray 0.12 at 45% through the coverage, never erasing. `heal` takes the painted bounds from the kernel's `heal_coverage_bounds`, draws the source into a region about three spots wider, and hands the kernel the coverage packed row by row, as Swift's gray context holds it (the twin copies each tile's rows where Swift draws each coverage image); the seed is `QRandomGenerator::global()`, Swift's `UInt32.random`. Each touched tile is then its base with the healed region copied over, blended with the base through `PixelAdjust::blend` under a selection, where Swift clips the context. The kernel's failure is `ProjectError(tooLarge)`; pixels or packed coverage that cannot be allocated are `ExportError(render)`, as Swift's contexts. The session's `beginBrush` takes Spot Healing on pixels alone, never on a mask, as Swift's guard; `finishBrushImmediately` heals before the commit, one `Spot Healing` step; `spotHealingMode` is a getter and a setter that notifies; Tab steps the type. `isBrushTool` is Swift's four tools: the circle, the right-drag tip, the brackets, the digits and `BrushControls` serve each, and a press with Clone Stamp or the Smear begins nothing until 8.5 and 8.6 bring them. `ContentView` keeps one bar across the four, as Swift's single `isBrushTool` branch keeps its view; the bar titles itself by tool (Spot Healing, Clone Stamp, Smear, Eraser, Brush), shows Paint and Erase for the Brush alone and the three types for Spot Healing alone, and names opacity Strength for the Smear; the clone's Aligned and Sample arrive with 8.5, the Smear's modes with 8.6. Equivalent mutant: the healed tile starting from its context rather than its base, since the region holds the whole wash and the copy replaces it.
|
||||
- Clone Stamp (8.5): `Document/CloneStamp.h` holds `CloneSettings`, `CloneStamp.cpp` Swift's extension: `setCloneSource` (a point that is no number is refused; a new source restarts the alignment), `cloneStrokeOffset`, `cloneSamplePoint` and `cloneSample`, whose context that cannot be allocated logs on `omaphoto.app` where Swift's `try?` is silent. The session keeps `cloneSource`, `cloneSettings` (a setter that notifies), `cloneOffset` and `parkedBrushTips` by `tipFamily` (Clone Stamp 1, the Smear 2, the rest 0; the two parked tips start at Swift's 40, 0, 1). `selectTool` parks size, hardness and opacity when the family changes and reads the tip it restores with `at`: the session starts in family 0 and leaving a family parks it, so Swift's `if let` always finds one. `beginTransform` and Transform Selection set the Move tool directly, as Swift's do, and park nothing. `beginBrush` takes Clone Stamp on pixels alone, asks for a source first (`brushError`: "Alt-click where Clone Stamp should copy from first."), samples the document as the stroke starts and fixes the offset; Tab toggles the sample; the step is `Clone Stamp`. `BrushStroke::Clone` is Swift's tuple. `publish` makes each tile's view of the sample on the calling thread (`clonePixels`): under a scale or turn QPainter draws it bilinear, Swift's medium quality; a whole translation copies it; a fractional one is drawn at the floor and shifted by `BrushRaster::shiftedByFraction`, since QPainter snaps an unscaled image. `fillClone` then composites it source-over through coverage × opacity × selection, each byte rounded once, where Swift clips the context to the coverage. Swift's `!isMask || isBlur` holds since 8.6: a mask takes a clone only from Blur. The bar shows Aligned (a `QCheckBox` written on `clicked`, so the session's own changes write nothing back), This Layer and All Layers, and "Alt-click to set the source" in `PlaceholderText` while there is no source. On the canvas an Alt-press with Clone Stamp sets the source and paints nothing; Swift's cursor refresh there is dead (Alt is held, so the crosshair stays) and left out. With a source the pointer hides unless Alt or Space is held. `updateBrushCursor` gives the overlay the source crosshair in view points and, between strokes and without Alt, `clonePreview` (the source around the stamped point at screen resolution, at most 1024 pixels, kept under Swift's key; Swift's `.medium` there is overridden by `LayerRenderer`'s own quality and left out) with `cloneTip` (one click painted by the brush engine, kept by size and hardness). `BrushCursorOverlay` gains `marker`, `preview`, `previewOpacity` and `tip`, images compared by `cacheKey` as Swift's `!==`; under an antialiased ellipse clip at the brush's opacity it draws the preview and cuts it to the tip inside `AdjustmentSurface::draw`, Swift's transparency layer, so the tip applies at the drawing's resolution however far a capped preview is enlarged, and draws the crosshair white 3 under black 1 with round caps. A modifier change refreshes the brush cursor, as Swift's monitor does. Equivalent mutants: `displayedTransform` in `cloneSample` and `clonePreview` (it differs from the stored transform only in a transform edit, which leaves Clone Stamp for Move, or a filter edit, which refuses strokes; the preview would show it only with a filter sheet open over Clone Stamp).
|
||||
- The Smear (8.6): `SmudgeLiquify.h/.cpp` hold `BlurToolMode` and `WarpStroke`, Swift's float arithmetic to the letter (the positive guards of `weight` included) on a document-sized copy drawn through the layer's displayed transform; `image()` is that copy itself, where Swift snapshots it after each append, since the twin reads it only between appends. `beginWarp` and `finishWarp` twin the extension: a warp takes pixels alone (`brushError`: "Smudge and Liquify work on a layer's pixels, not its mask."), and the commit paints the copy along the stroke with a hard tip four pixels wider, `replacesWithClone` so clear pixels clear, one step named by the mode. `BlurTool.cpp` holds `blurSample`: sigma a tenth of the brush within 1.5 and 30; the layer blurred with clear past the document's edge; a mask on its edge tone, its placed box black under its coverage, blurred with the edge repeated. Core Image's Gaussian is `PixelAdjust::gaussianBlur`: separable, weights exp(−i²/2σ²) over three sigmas, normalised, in float on the stored premultiplied values (Swift's Core Image context is unmanaged), each byte rounded once, rows in parallel. Where Core Image approximates a large blur, the twin's is exact; it costs 20–110 ms for a 4K document on 32 cores (sigma 1.5 to 30), paid as a Blur stroke begins, since Swift's sample is taken whole. `BrushStroke` gains `isBlur` (a mask takes a clone only from Blur), `replacesWithClone` (a copy by coverage: `fillClone` keeps 1 − coverage of the old pixel) and `editName` (the step's name when set). `clonePixels` resamples every clone, a gray one too, into premultiplied colour, so a sample's edge keeps the share of the pixel the image covers, and `fillClone` composites that alpha into a mask as CoreGraphics draws a gray image's edge: a white mask half a pixel past the canvas stays white. The session keeps `blurMode` and `warpStroke`; `beginBrush` hands Liquify and Smudge to `beginWarp`; `continueBrush`, `cancelBrush` and `finishBrushImmediately` take the warp first; exactly Swift's six places read it: `canEditLayers`, `canUseHistory`, `canStartProjectOperation`, `selectLayer`, `selectTool`, `cycleToolMode` (`selectLayers` and `selectLayerTarget` read the brush stroke alone, as Swift's). Tab steps the Smear's modes. The bar shows the three modes with Swift's help; the status line says "Drag to soften", "Drag to smudge" or "Drag to push pixels" (`hint` takes the Smear's mode). On the canvas the warp's copy stands in for its layer across the canvas, through the layer's mask at 2048, its opacity and blend mode, and a step repaints the view (no dirty rect, as Swift's); drags, releases, Escape, wheels, pinches and the tip drag treat a warp as a stroke. Equivalent mutants: `displayedTransform` in `beginWarp` and `blurSample` (painting refuses transform and filter edits), the radius rounded to nearest (pixels past its floor lie outside the brush), `u >= h` for `u > h` (weight 1 either way), a warp without dabs going on to commit (it appends nothing), copy mode weighing by coverage times opacity (a warp commits at full opacity), `fillClone`'s format guard (a clone is always sampled in colour), and the Gaussian's branch-free interior moved by a pixel (it reads one tap weighing under 1% of the centre).
|
||||
@@ -131,7 +131,7 @@ Deliberate name breaks: `MetalBrushCoverage` → `BrushCoverage`, `MetalLayerEff
|
||||
- Tool icons: SF Symbols have no Linux twin, and icon themes differ by distro, so `src/UI/ToolIcons` draws each tool's icon as paths in an 18-point square, in one colour that the caller takes from the palette (the Omarchy theme retints them for free). Swift's two hand-drawn icons keep their geometry there (`GradientToolIcon`'s Floyd–Steinberg fade, `CloneStampToolIcon`'s four parts); their Swift homes (`GradientControls`, `BrushControls`) arrive later. The eyedropper's glyph lives in `Rendering/EyedropperIcon`, since the canvas's cursor draws it too. Icons that follow tool state (ellipse marquee, polygonal lasso, eraser) arrive with that state. `tests/fixtures/ToolIcons.png` is the reference sheet: `ToolIconTests` redraws it, allows a font a few pixels and a stroke none, and writes the fresh sheet beside the test binary; after a wanted change, look at it and commit it.
|
||||
- Omarchy theme: `src/UI/OmarchyTheme` reads `~/.local/state/omarchy/current/theme/colors.toml` (Omarchy 4.x: flat `key = "value"` lines, `mode` dark or light, the twenty-two stock themes' keys required, `orange` optional and `yellow` its stand-in) into `OmarchyColors`, applies them as the application palette (Window and Text from `background` and `foreground`, Base from `darker_background`, Button from `lighter_background`, Highlight and Link from `accent` with the background as highlighted text, PlaceholderText from `dark_foreground`, BrightText from `orange` or `yellow`, Mid from `muted`, the disabled roles from `muted`) and sets the Fusion style, which follows a palette on every desktop where a platform style does not; no stylesheet is needed. Keys and values read in double or single quotes. A missing key, or a recognised key whose value is bare or half-quoted or no `#rrggbb`, refuses the file with the key's name; a file that exists but cannot be opened, or sits behind a folder that cannot be searched (`stat` fails other than `ENOENT`), is refused too; either way the app keeps the palette it has and logs a warning, where an absent file means the built-in dark, Swift's grays, installed before the first load. `omarchy-theme-set` removes `current/theme`, moves `next-theme` in and writes `theme.name`, so the watcher holds `current/`, `current/theme` and the file, adds them back after each load (a removed path leaves a `QFileSystemWatcher`) and renews the file's watch every load (it follows the inode, which an atomic replacement swaps), watches the nearest ancestor the watcher accepts while `current/` is missing or barred, letting it go once the directory is watched, and loads once 100 ms after the last event. A read that stops short (an error, or fewer bytes than the file holds) is refused like a malformed file. Each load logs the theme's name and mode on `omaphoto.app`. `main` makes the theme before the window. Widgets take colours from the palette, never from literals; the canvas keeps Swift's document grays. The UI font stays Qt's default: `omarchy font current` is not followed.
|
||||
- No updater. Distro packages own updates.
|
||||
- Distribution (11.4b): releases live on GitHub under fixed asset names (`omaphoto_amd64.deb`, `omaphoto.x86_64.rpm`, `omaphoto-x86_64.pkg.tar.zst`) with `SHA256SUMS`, so `releases/latest/download/<name>` always reaches the newest. `scripts/install/ubuntu.sh`, `fedora.sh` and `arch.sh` read `/etc/os-release` (`ID` or `ID_LIKE`; Omarchy says `ID_LIKE=arch`), refuse another family or another machine than x86_64 (the DEB, built on Ubuntu 24.04, names Ubuntu's `t64` libraries, which Debian 13 lacks, so Debian is no family of it), verify the package against the sums and install it with apt, dnf or pacman under sudo, so the package manager resolves dependencies; apt reads the file as its own user, so the download folder is opened to it. pacman asks which build provides `onnxruntime`. Fedora's script names RPM Fusion's `libheif-freeworld` when it is missing, since Fedora's libheif decodes no HEVC, and adds no repository itself. `nixos.sh` installs the flake into the user's profile (`nix profile install`, the name every Nix still takes). The README runs each script after downloading it, so the package manager's questions read the terminal and fish runs it too. `release.yml` builds each package with the command the distro checks use, the DEB bundling ONNX Runtime from the dev image, the Arch package through `makepkg` as a user; the version is CMake's `project` version, the PKGBUILD's and the flake's, kept equal by hand.
|
||||
- Distribution (11.4b): releases live on GitHub under fixed asset names (`omaphoto-noble_amd64.deb`, `omaphoto-resolute_amd64.deb`, `omaphoto.x86_64.rpm`, `omaphoto-x86_64.pkg.tar.zst`; 1.0.0 had one DEB, `omaphoto_amd64.deb`) with `SHA256SUMS`, so `releases/latest/download/<name>` always reaches the newest. `scripts/install/fedora.sh` and `arch.sh` read `/etc/os-release` (`ID` or `ID_LIKE`; Omarchy says `ID_LIKE=arch`) and `ubuntu.sh` its `UBUNTU_CODENAME`, which Ubuntu's derivatives set to their base: each DEB names its release's Qt libraries (24.04's carry a `t64` suffix that 26.04's drop, and Debian 13 has neither), so the release carries one DEB per LTS, built on it (`Dockerfile.dev` takes the base as `BASE`), and the script takes the one whose codename matches, `noble` or `resolute`, refusing any other. Each refuses another system or another machine than x86_64, verify the package against the sums and install it with apt, dnf or pacman under sudo, so the package manager resolves dependencies; apt reads the file as its own user, so the download folder is opened to it. Fedora's script names RPM Fusion's `libheif-freeworld` when it is missing, since Fedora's libheif decodes no HEVC, and adds no repository itself. `nixos.sh` installs the flake at the newest release's tag, which GitHub's `releases/latest` redirect names, into the user's profile (`nix profile install`, the name every Nix still takes), so no user builds unreleased work from `main`. The README runs each script after downloading it, so the package manager's questions read the terminal and fish runs it too. `release.yml` builds each package with the command the distro checks use, the DEBs on the dev image over each base, the RPM on Fedora 43, the oldest Fedora still supported, whose Qt symbols a later Fedora keeps, the Arch package through `makepkg` as a user; the version is CMake's `project` version, the PKGBUILD's and the flake's, kept equal by hand, and the workflow's first job refuses a tag that names another.
|
||||
- Remove Background runs an open-licensed model on ONNX Runtime.
|
||||
- A `.comp` project is a directory on Linux; its layout matches the macOS package.
|
||||
- Shortcuts map ⌘ to Ctrl and ⌥ to Alt.
|
||||
@@ -142,7 +142,7 @@ No sudo is needed. Docker does the work.
|
||||
|
||||
- `scripts/dev.sh build` — configure and build in the Ubuntu 24.04 container, warnings as errors. With no `CMAKE_BUILD_TYPE` the build is `RelWithDebInfo`: pixel code must stay optimized. Every target is compiled with `_GLIBCXX_ASSERTIONS`: an empty `std::optional` or a bad index aborts, in the tests and in the app, where it would otherwise pass by luck.
|
||||
- `scripts/dev.sh test` — build, then run every test headless (`QT_QPA_PLATFORM=offscreen`). `HOME` and `/tmp` sit on a tmpfs, as in the distro checks: the store's syncs on the container's disk wait on a busy host.
|
||||
- `scripts/dev.sh run` — run the app on the host Wayland display.
|
||||
- `scripts/dev.sh run` — build, then run the app on the host Wayland display (issues #1 and #2, 11.4e): the home folder is mounted at its own path with `HOME` pointing there, so files, settings and the Omarchy theme resolve as in a native run, and each `/dev/dri/renderD*` passes with its group, since Mesa's EGL fails without one (Zink's Vulkan and the dri2 screen). On NVIDIA's own driver the container's Mesa cannot drive the node and EGL still warns; the app paints on the CPU either way. The reporter's black and stale areas cleared with the node; this host never showed them.
|
||||
- `scripts/mutate.py FILE [--targets T1,T2]` — mutation runner, run from the repository root. Reads `old ==> new` blocks separated by `@@` lines on stdin, or with `--delete [--from N --to M]` removes each statement line in turn. A mutant counts as caught only when it builds and a test fails. Run it on every item before review.
|
||||
- `scripts/heic-fixtures.sh` — rewrites the HEIC test fixtures in a throwaway container that has `heif-enc` and x265. Its parts are real files under `scripts/heic-fixtures/` (`Dockerfile`, `sources.cpp`, `generate.sh`), so lint reads each in its own language.
|
||||
- `scripts/lint.py` — the house rules, from the repository root: every file under 500 lines, every comment one line and at most ten words. Each file is read by a scanner for its own language: C++ (`//` and `/* */` outside literals), Python (its own tokenizer), CMake (`#` outside quotes, escapes and bracket arguments, which open only where an argument begins, tracked as state through escapes; bracket comments too), Docker (any line that begins with `#`) and shell. The shell scanner reads the subset the scripts keep to (quotes, escapes, `$( )` and `${ }` to any depth, here-strings, `#` where a word begins) and refuses the rest by name and line, so nothing passes unread: `<<` (a heredoc or a shift), backticks, `$'…'`, arithmetic, `((`, `case` inside `$( )`, and a continued line anywhere but between words (it could split `$(`, `<<` or a keyword). Write scripts inside that subset. A file of any other kind under `scripts` fails lint. It reads `src`, `tests`, everything under `scripts`, `CMakeLists.txt` and `Dockerfile.dev`. `--self-test` checks the checker. Run both before every review.
|
||||
|
||||
+4
-2
@@ -1,5 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
project(omaphoto VERSION 1.0.0 LANGUAGES C CXX)
|
||||
project(omaphoto VERSION 1.0.1 LANGUAGES C CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
@@ -195,7 +195,7 @@ install(CODE "
|
||||
find_program(UPDATE_MIME update-mime-database REQUIRED)
|
||||
execute_process(COMMAND \${UPDATE_MIME} \"\${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/mime\" COMMAND_ERROR_IS_FATAL ANY)
|
||||
endif()")
|
||||
# Ubuntu 24.04 packages no ONNX Runtime: its package carries one.
|
||||
# Packages carry their ONNX Runtime; each release renames its symbols.
|
||||
option(OMAPHOTO_BUNDLE_ORT "Install ONNX Runtime beside the app" OFF)
|
||||
set(OMAPHOTO_ORT_NOTICES /usr/local/share/doc/onnxruntime CACHE PATH "ONNX Runtime's LICENSE and notices")
|
||||
if(OMAPHOTO_BUNDLE_ORT)
|
||||
@@ -208,6 +208,8 @@ if(OMAPHOTO_BUNDLE_ORT)
|
||||
endforeach()
|
||||
install(IMPORTED_RUNTIME_ARTIFACTS onnxruntime::onnxruntime LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/omaphoto)
|
||||
set_target_properties(omaphoto PROPERTIES INSTALL_RPATH "$ORIGIN/../${CMAKE_INSTALL_LIBDIR}/omaphoto")
|
||||
# rpmbuild would offer the bundled library to the system.
|
||||
set(CPACK_RPM_SPEC_MORE_DEFINE "%global __provides_exclude_from ^%{_libdir}/omaphoto/.*$\n%global __requires_exclude ^libonnxruntime[.]so")
|
||||
endif()
|
||||
# Debian and Fedora packages; each built on its own system.
|
||||
set(CPACK_PACKAGE_NAME omaphoto)
|
||||
|
||||
+3
-2
@@ -1,11 +1,12 @@
|
||||
FROM ubuntu:24.04
|
||||
ARG BASE=ubuntu:24.04
|
||||
FROM $BASE
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
g++ cmake ninja-build qt6-base-dev libgl1-mesa-dev qt6-wayland fonts-dejavu-core \
|
||||
qt6-image-formats-plugins libheif-dev libheif-plugin-libde265 libfontconfig-dev \
|
||||
ca-certificates curl dpkg-dev file fakeroot \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
# Ubuntu 24.04 packages no ONNX Runtime: Microsoft's own build.
|
||||
# The ONNX Runtime the packages carry: Microsoft's own build.
|
||||
RUN curl -fsSL -o /tmp/ort.tgz \
|
||||
https://github.com/microsoft/onnxruntime/releases/download/v1.21.0/onnxruntime-linux-x64-1.21.0.tgz \
|
||||
&& echo "7485c7e7aac6501b27e353dcbe068e45c61ab51fbaf598d13970dfae669d20bf /tmp/ort.tgz" | sha256sum -c - \
|
||||
|
||||
@@ -18,7 +18,7 @@ It is a Linux port of [Compositor](https://github.com/robbietilton/Compositor) b
|
||||
|
||||
## Install
|
||||
|
||||
Each script downloads the newest release's package, checks it against the release's `SHA256SUMS` and installs it with your system's package manager. Packages are built for x86_64.
|
||||
Each script installs the newest release. The Arch, Ubuntu and Fedora scripts download its package, check it against the release's `SHA256SUMS` and install it with your system's package manager; the NixOS script builds the release's flake. Packages are built for x86_64.
|
||||
|
||||
Arch and Omarchy:
|
||||
|
||||
@@ -26,13 +26,13 @@ Arch and Omarchy:
|
||||
curl -fsSLO https://raw.githubusercontent.com/ZacharyZhang-NY/OmaPhoto/main/scripts/install/arch.sh && bash arch.sh
|
||||
```
|
||||
|
||||
Ubuntu 24.04 or later, and its derivatives (Linux Mint 22, Pop!_OS 24.04):
|
||||
Ubuntu 24.04 or 26.04 LTS and systems built on them (Linux Mint 22, Pop!_OS 24.04):
|
||||
|
||||
```sh
|
||||
curl -fsSLO https://raw.githubusercontent.com/ZacharyZhang-NY/OmaPhoto/main/scripts/install/ubuntu.sh && bash ubuntu.sh
|
||||
```
|
||||
|
||||
The DEB uses Ubuntu's package names, so Debian itself builds from source (below).
|
||||
Each DEB names its Ubuntu release's libraries, so other Ubuntu releases and Debian build from source (below).
|
||||
|
||||
Fedora:
|
||||
|
||||
@@ -42,13 +42,13 @@ curl -fsSLO https://raw.githubusercontent.com/ZacharyZhang-NY/OmaPhoto/main/scri
|
||||
|
||||
Fedora's own libheif decodes no HEVC. For HEIC import, add [RPM Fusion](https://rpmfusion.org/Configuration) and install `libheif-freeworld`; the script reminds you.
|
||||
|
||||
NixOS installs from the flake into your profile:
|
||||
NixOS installs the release's flake into your profile:
|
||||
|
||||
```sh
|
||||
curl -fsSLO https://raw.githubusercontent.com/ZacharyZhang-NY/OmaPhoto/main/scripts/install/nixos.sh && bash nixos.sh
|
||||
```
|
||||
|
||||
Or add `github:ZacharyZhang-NY/OmaPhoto` as a flake input and put its `packages.x86_64-linux.default` in your configuration.
|
||||
Or add `github:ZacharyZhang-NY/OmaPhoto/<tag>`, a release tag, as a flake input and put its `packages.x86_64-linux.default` in your configuration.
|
||||
|
||||
The packages themselves sit on the [releases page](https://github.com/ZacharyZhang-NY/OmaPhoto/releases).
|
||||
|
||||
@@ -59,11 +59,13 @@ The build runs in Docker, so the host needs only Docker:
|
||||
```sh
|
||||
scripts/dev.sh build # configure and build in Ubuntu 24.04
|
||||
scripts/dev.sh test # every test, headless
|
||||
scripts/dev.sh run # the app on your Wayland display
|
||||
scripts/dev.sh run # build, then the app on your Wayland display
|
||||
```
|
||||
|
||||
By hand: C++20, Qt 6.4 or later (Widgets, Concurrent, image formats), CMake, Ninja, libheif, fontconfig and ONNX Runtime. `-DOMAPHOTO_MODEL=path/to/u2net.onnx` installs Remove Background's model. `scripts/distro-check.sh arch|fedora|nixos` builds and tests on those systems.
|
||||
`run` gives the container your home folder at its own path, so the app opens and saves your files and reads your Omarchy theme, and the GPU's render nodes (`/dev/dri/renderD*`), which Mesa's EGL wants. Files outside your home folder stay out of reach.
|
||||
|
||||
By hand: C++20, Qt 6.4 or later (Widgets, Concurrent, image formats), CMake, Ninja, libheif, fontconfig and ONNX Runtime. `-DOMAPHOTO_MODEL=path/to/u2net.onnx` installs Remove Background's model. `scripts/distro-check.sh arch|fedora|nixos|resolute` builds and tests on those systems (`resolute` is Ubuntu 26.04).
|
||||
|
||||
## Licence
|
||||
|
||||
MIT, as Compositor is; see `LICENSE`. Remove Background uses U²-Net (Apache-2.0, `licenses/U-2-Net.txt`) through ONNX Runtime (MIT). The Ubuntu package carries ONNX Runtime with its notices.
|
||||
MIT, as Compositor is; see `LICENSE`. Remove Background uses U²-Net (Apache-2.0, `licenses/U-2-Net.txt`) through ONNX Runtime (MIT). Every package carries ONNX Runtime with its notices.
|
||||
@@ -268,7 +268,7 @@ The panel of 4.6 shows and edits what the session holds so far. Each item adds i
|
||||
- 11.3 round 1: FAIL, three findings. The bundled runtime shipped without Microsoft's licence and notices; the image keeps them from the release and a bundling install lays them under `share/licenses/compositor/onnxruntime`, a configure without them failing. The install test failed for a bundling build; it reads `COMPOSITOR_BUNDLE_ORT` and expects the library, its version link and the notices (a second build, bundling with the model, passes). A flake comment ran to eleven words.
|
||||
- 11.3 round 2: FAIL, two findings in the install test: it looked for the runtime in `lib`, where a `/usr` prefix on Ubuntu gives `lib/x86_64-linux-gnu`, and took only `ON` for the bundle option, where CMake takes any true value. CMake now hands the test its own evaluated values (`COMPOSITOR_BUNDLED`, `COMPOSITOR_LIBDIR`, `COMPOSITOR_MODEL_PATH`), and a second build bundling with `TRUE` under `/usr` passes.
|
||||
- 11.3 round 3: PASS, no findings.
|
||||
- [ ] 11.4 Distro checks in Docker: build and full test run on Ubuntu 24.04, Fedora, Arch, NixOS. Seen early on the Arch host (Qt 6.11.2, GCC 16), in a build without warnings as errors: `findChild` needs `Q_OBJECT` on the class (fixed in 8.2: `ToolHeaderBar`); 13 of 107 targets fail or time out: BrushTests (the address-space crop test commits), HighDpiCanvasTests, ImageImportTests, TiledPieceTests, ToolIconTests, TransformInspectorTests, FilterSheetTests (Return on a focused Cancel no longer waits for OK), CompositorMenusTests; ContentViewTests, ProjectOpenTests, ProjectWorkspaceFlowTests, ProjectWorkspaceViewTests and ProjectControllerTests time out. Each is judged here: a Qt change the app must follow, or a test that leans on 6.4.
|
||||
- [x] 11.4 Distro checks in Docker: build and full test run on Ubuntu 24.04, Fedora, Arch, NixOS. Seen early on the Arch host (Qt 6.11.2, GCC 16), in a build without warnings as errors: `findChild` needs `Q_OBJECT` on the class (fixed in 8.2: `ToolHeaderBar`); 13 of 107 targets fail or time out: BrushTests (the address-space crop test commits), HighDpiCanvasTests, ImageImportTests, TiledPieceTests, ToolIconTests, TransformInspectorTests, FilterSheetTests (Return on a focused Cancel no longer waits for OK), CompositorMenusTests; ContentViewTests, ProjectOpenTests, ProjectWorkspaceFlowTests, ProjectWorkspaceViewTests and ProjectControllerTests time out. Each is judged here: a Qt change the app must follow, or a test that leans on 6.4.
|
||||
- 11.4 state: `linux/scripts/distro-check.sh arch|fedora|nixos` builds `linux/scripts/distro/<name>/Dockerfile` and runs dev.sh's configure, build and full test run in it (warnings as errors, the tree mounted read-only); NixOS builds the flake's package, then runs the tests in the flake's new `devShells.default` (plugin path, DejaVu through fontconfig, the model on the data path). Real containers start now through `sudo docker`; Ubuntu is the dev image itself. Still to do: run Arch (started), Fedora and NixOS, judge each failure, build the RPM on Fedora (`cpack -G RPM`) and the PKGBUILD on Arch (`makepkg`, as a user, not root), then review.
|
||||
- 11.4 Arch run 1 (Qt 6.11.2, GCC 16, in a container): the build needed three fixes (`QImage::flipped` from Qt 6.9 in `ImageImporter`, `Q_OBJECT` on `PickerField` and `SwatchButton` for Qt 6.11's `findChild`, a local pragma for GCC 16's false `-Wmismatched-new-delete` in `DownsampleTests`' replaced operators); 193 of 205 tests pass. Failing, to judge: BrushTests, SpotHealingTests, TextLayoutTests, ImageImportTests, OmarchyThemeTests, ProjectWorkspaceViewTests, ProjectTests, EffectsCanvasTests (aborted), EffectsFailureTests, SubjectRemovalFailureTests, ProjectExportTests, ImageFileDropTests. Log: the scratchpad's arch-check.log.
|
||||
- 11.4 Arch run 2 onward: running as uid 1000 (as dev.sh) cleared the four tests root broke (read-only folders and unreadable files mean nothing to root). Fixed since: TextLayoutTests counted Ubuntu's DejaVu split (now: the named faces, sorted, once each); ImageFileDropTests dropped before the last import ended (now waits for `canSwitch`); ImageImportTests (libheif 1.23's longer message; Qt 6.9's TIFF plugin logs libtiff errors as criticals, expected from 6.9 on); EffectsCanvasTests: Qt 6.11's `QTransform::map` clips w for points past the horizon where Swift divides by it whatever its sign, so `DistortWarp` projects points itself (`project` in `Distort.cpp`). Left: ProjectWorkspaceViewTests (the app launched with a file) and the memory-starvation tests (BrushTests, SpotHealingTests, EffectsFailureTests, SubjectRemovalFailureTests). A kept Arch build: `~/.cache/compositor-arch` (mount it at /build in compositor-arch).
|
||||
@@ -277,7 +277,22 @@ The panel of 4.6 shows and edits what the session holds so far. Each item adds i
|
||||
- 11.4 after the reboot (real Docker, no sudo): Arch 205 of 205. Fedora 42 (Qt 6.10) 203 of 205, both fonts: Fedora splits DejaVu, so its image installs Serif and Mono beside Sans, as Ubuntu's `fonts-dejavu-core` and Arch's `ttf-dejavu` carry them; and Fedora's own default sans is Noto (`default-fonts-core-sans`), in which the Type tool's "Aa" draws, so `ToolIconTests` compares the Type cells with the DejaVu sheet only under DejaVu Sans (the other tests hold that icon's ink, box and uniqueness under any face; a band 8 points narrower fails on Fedora). Rerun: 205 of 205.
|
||||
- 11.4 packages: the RPM (`cpack -G RPM` on Fedora) installs with dnf on a fresh Fedora 42 with RPM Fusion, which brings `libheif-freeworld`; its trigger registers the project type, and every library resolves. The PKGBUILD builds with `makepkg` as a user on Arch and installs with pacman, whose hook registers the project type; every library resolves. It builds from the checkout, `Compositor/Assets.xcassets` (the icons) included.
|
||||
- 11.4 NixOS: the flake had no `flake.lock`, and the tree is read-only there; the lock is committed now, pinning nixpkgs. The package builds. Its first test run, as root, failed the four tests root breaks and three on fonts: the shell's `makeFontsConf` reads its rules from `/etc/fonts/conf.d`, which NixOS keeps and a bare container lacks, so Helvetica fell to DejaVu Math TeX Gyre; the shell now includes fontconfig's own `conf.d` from the store. Nix writes its store as root with root's home, and the tests then drop to uid 1000 (`chroot --userspec`, coreutils) with `HOME` and `TMPDIR` back on the tmpfs, since `nix develop` points `TMPDIR` at a folder root made.
|
||||
- [ ] 11.4b Standalone OmaPhoto: the port moves to its own public repository, `ZacharyZhang-NY/OmaPhoto` (the user's decision, 2026-09-26), the app at the root, the kernels in `kernels/`, the icons in `packaging/icons/`; renamed where a user sees it (name, binary, app id, desktop entry, icon, packages, logging, build options), Compositor's project format kept; the README credits Compositor; one install script per family in `scripts/install/`; `release.yml` builds and publishes the packages on a tag. Version 1.0.0.
|
||||
- [x] 11.4b Standalone OmaPhoto: the port moves to its own public repository, `ZacharyZhang-NY/OmaPhoto` (the user's decision, 2026-09-26), the app at the root, the kernels in `kernels/`, the icons in `packaging/icons/`; renamed where a user sees it (name, binary, app id, desktop entry, icon, packages, logging, build options), Compositor's project format kept; the README credits Compositor; one install script per family in `scripts/install/`; `release.yml` builds and publishes the packages on a tag. Version 1.0.0.
|
||||
- 11.4 and 11.4b round 1 (Fable 5.1, max): FAIL, six findings. The DEB names Ubuntu 24.04's `t64` Qt libraries, which no later Ubuntu publishes, yet the README promised "24.04 or later" and `ubuntu.sh` let 26.04 through to apt's refusal: the script now takes `UBUNTU_CODENAME=noble` alone and the README says so. AGENTS.md said `lib/compositor` for the bundled runtime (now `lib/omaphoto`). The PKGBUILD's comment said `linux/packaging/arch`. `nixos.sh` installed `main`, not the release the README named: it now resolves the newest release's tag from GitHub's redirect and installs the flake there. Nothing tied the tag to the version the files carry: the workflow's first job refuses a tag that names another (checked by hand against a matching tag, a wrong CMake, PKGBUILD and flake version). No test pinned `project`: `DistortTests::pointsPastTheHorizonDivideAsSwiftDoes` carries a box above a trapezoid's horizon (w from -2 to -1) through `carried` and `mapPath`; on Arch the body and both call sites mutated to `map.map` each fail it, on Qt 6.4 they are equivalent (6.4's `map` divides too).
|
||||
- 11.4 and 11.4b round 2: PASS, no findings.
|
||||
- [x] 11.4c NixOS: the flake leaves out `dev.sh`'s `build/` (the package's own build folder collided with it); ProjectExportFailureTests crashes in the full NixOS run, never alone: find the cause.
|
||||
- 11.4c work: the flake's source leaves out the top-level `build/` (`cleanSourceWith`); the NixOS run built the package with it. The crash reproduced every time under ctest and run directly in a kept container with the check's own build; gdb put it in fontconfig's `FcHashTableFind`, under `QMessageBox::setText` in `showError`, under the PNG export's failed snapshot: the alert opened while the 512 KB limit held, and it was the process's first font lookup, whose failed allocation fontconfig dereferences. The app had already handled the failure (it logged the export error). `main` now resolves the alert's fonts before the tests, as the app's window does before any export: five direct runs and ctest pass in the container; with the PNG or the JPEG snapshot's alert removed (`mutate.py`), the test still fails.
|
||||
- 11.4c round 1: FAIL, two findings. `thePanelOpensWithoutCopyingTheSnapshot` passed only on the heap its siblings left: alone on the dev image it failed 40 of 40 (it crashed 45 of 45 before the fix), since its limit also held the panel's first open. `initTestCase` now opens a panel and an alert once, the refusal flow, before any limit (the fonts line in `main` went); every test alone and the whole binary pass 40 of 40 on Ubuntu, Arch and NixOS. AGENTS.md put the first font lookup on NixOS alone; it is the first on every system. `mutate.py` then found the JPEG render's capture could copy the snapshot unseen (a gap since 10.3): `theJPEGRenderTakesTheSnapshotWithoutACopy` holds it to a move under a 2 MB limit, the pool thread held so the render waits; the four mutants (both snapshot alerts removed, both captures copied) each fail.
|
||||
- 11.4c round 2: PASS, no findings.
|
||||
- [x] 11.4d Issue #5: the RPM demands Fedora 42's exact ONNX Runtime (`libonnxruntime.so.1(VERS_1.20.1)`), and ONNX Runtime stamps a new symbol version on every release, so any other Fedora or update refuses it; the Arch package breaks at run time the same way. The user's decisions (2026-09-26): packages carry Microsoft's runtime they were built with, as the DEB does (the RPM and the Arch package bundle it; the Fedora and Arch images test against it too); the RPM builds on the oldest Fedora still supported (43); a second DEB built on Ubuntu 26.04 LTS (resolute) joins the release beside 24.04's (noble), `Dockerfile.dev` taking the base as an argument, and `ubuntu.sh` picks by `UBUNTU_CODENAME`.
|
||||
- 11.4d work: the Fedora image is 43 and, like the Arch image, carries Microsoft's ONNX Runtime 1.21.0 (the dev image's block, pinned) in place of the system's; `Dockerfile.dev` takes `BASE`, and `distro-check.sh resolute` runs it on Ubuntu 26.04. The RPM and the Arch package bundle the runtime (`OMAPHOTO_BUNDLE_ORT`): the RPM keeps it out of what it provides and requires, the PKGBUILD fetches the tarball by checksum, lays it out for its CMake config in `prepare()` and drops `onnxruntime`. The release builds `omaphoto-noble_amd64.deb` and `omaphoto-resolute_amd64.deb` (a matrix), and `ubuntu.sh` takes the one its `UBUNTU_CODENAME` names. Evidence: 205 of 205 on Ubuntu 24.04, Arch, Fedora 43 and Ubuntu 26.04; the workflow's package commands build all four; each installs and starts in fresh containers (24.04, 26.04, Fedora 43, 44 and 44 beside `onnxruntime`, Arch and Arch beside `onnxruntime-cpu`), `ldd` finding the bundled runtime; 24.04's DEB is refused on 26.04; the RPM names no `onnxruntime` among its requires or provides; `ubuntu.sh` against a stand-in `curl` installs the right DEB on 24.04 and 26.04 and refuses Debian 13.
|
||||
- 11.4d round 1: FAIL, three findings, all words: `arch.sh` still said pacman asks which runtime provides `onnxruntime` (the line went), the README's distro checks left out `resolute`, the workflow's heading said three packages (four).
|
||||
- 11.4d round 2: PASS, no findings. Committed; pushed with 1.0.1's tag, since `ubuntu.sh` asks for its asset names.
|
||||
- [x] 11.4e Issues #1 and #2: `dev.sh run` reaches the host's files (the home folder) and the GPU's render nodes, and builds before it runs.
|
||||
- 11.4e work: `dev.sh run` builds first, mounts the home folder at its own path with `HOME` there (the theme's separate mount went: it now reads from its real path), and passes each render node with its group. Evidence: before, the same EGL and Zink warnings as issue #2 (`run-nodev.log`), the window whole on this Hyprland host (`run-nodev.png`), so the black areas did not reproduce here; after, `dev.sh run ~/Pictures/Wallpapers/kafka-5k2k.jpg` built, imported the wallpaper from the home folder (`run-dev.log`, `run-dev.png`), read the theme from its real path, and `docker inspect` showed both render nodes, group 987 and `HOME=/home/zacharyzhang`; the Zink error went, and this host's NVIDIA driver leaves Mesa's dri2 warning.
|
||||
- 11.4e round 1: PASS, no findings.
|
||||
- [ ] 11.4f Release 1.0.1: the fixes above, the Node 24 actions, the install scripts rerun against the new release; the issues answered and closed.
|
||||
- [ ] 12.0 Port Compositor 1.3: survey the upstream changes from 1.1 to 1.3.x and split them into ordered items here before any code.
|
||||
- [ ] 11.5 End-to-end acceptance run with screenshots, README Linux section, `AGENTS.md` sync, MVP acceptance table.
|
||||
|
||||
## Skills by phase
|
||||
|
||||
@@ -13,8 +13,12 @@
|
||||
in {
|
||||
packages.x86_64-linux.default = pkgs.stdenv.mkDerivation {
|
||||
pname = "omaphoto";
|
||||
version = "1.0.0";
|
||||
src = self;
|
||||
version = "1.0.1";
|
||||
# dev.sh builds in ./build; the package starts clean.
|
||||
src = pkgs.lib.cleanSourceWith {
|
||||
src = self;
|
||||
filter = path: type: path != "${toString self}/build";
|
||||
};
|
||||
nativeBuildInputs = with pkgs; [ cmake ninja pkg-config qt6.wrapQtAppsHook ];
|
||||
buildInputs = with pkgs; [ qt6.qtbase qt6.qtimageformats libheif libde265 fontconfig onnxruntime ];
|
||||
cmakeFlags = [ "-DOMAPHOTO_MODEL=${model}" ];
|
||||
|
||||
+20
-6
@@ -1,19 +1,33 @@
|
||||
# Built from this checkout: run makepkg in linux/packaging/arch.
|
||||
# Built from this checkout: run makepkg in packaging/arch.
|
||||
pkgname=omaphoto
|
||||
pkgver=1.0.0
|
||||
pkgver=1.0.1
|
||||
pkgrel=1
|
||||
pkgdesc='Layered image editing for Linux, a port of Compositor'
|
||||
arch=('x86_64')
|
||||
url='https://github.com/ZacharyZhang-NY/OmaPhoto'
|
||||
license=('MIT')
|
||||
depends=('qt6-base' 'qt6-imageformats' 'libheif' 'libde265' 'fontconfig' 'onnxruntime' 'hicolor-icon-theme' 'shared-mime-info')
|
||||
depends=('qt6-base' 'qt6-imageformats' 'libheif' 'libde265' 'fontconfig' 'hicolor-icon-theme' 'shared-mime-info')
|
||||
makedepends=('cmake' 'ninja')
|
||||
source=('u2net.onnx::https://github.com/danielgatis/rembg/releases/download/v0.0.0/u2net.onnx')
|
||||
sha256sums=('8d10d2f3bb75ae3b6d527c77944fc5e7dcd94b29809d47a739a7a728a912b491')
|
||||
# ONNX Runtime ships inside: Arch's changes its symbols each release.
|
||||
source=('u2net.onnx::https://github.com/danielgatis/rembg/releases/download/v0.0.0/u2net.onnx'
|
||||
'https://github.com/microsoft/onnxruntime/releases/download/v1.21.0/onnxruntime-linux-x64-1.21.0.tgz')
|
||||
sha256sums=('8d10d2f3bb75ae3b6d527c77944fc5e7dcd94b29809d47a739a7a728a912b491'
|
||||
'7485c7e7aac6501b27e353dcbe068e45c61ab51fbaf598d13970dfae669d20bf')
|
||||
noextract=('u2net.onnx')
|
||||
|
||||
# The layout Microsoft's CMake config expects.
|
||||
prepare() {
|
||||
local ort="$srcdir/onnxruntime-linux-x64-1.21.0"
|
||||
rm -rf ort
|
||||
mkdir -p ort/include ort/lib/cmake
|
||||
cp -r "$ort/include" ort/include/onnxruntime
|
||||
cp -a "$ort/lib" ort/lib64
|
||||
cp -r "$ort/lib/cmake/onnxruntime" ort/lib/cmake/
|
||||
}
|
||||
|
||||
build() {
|
||||
cmake -S "$startdir/../.." -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DOMAPHOTO_MODEL="$srcdir/u2net.onnx"
|
||||
cmake -S "$startdir/../.." -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DOMAPHOTO_MODEL="$srcdir/u2net.onnx" \
|
||||
-DOMAPHOTO_BUNDLE_ORT=ON -Donnxruntime_DIR="$srcdir/ort/lib/cmake/onnxruntime" -DOMAPHOTO_ORT_NOTICES="$srcdir/onnxruntime-linux-x64-1.21.0"
|
||||
cmake --build build --target omaphoto
|
||||
}
|
||||
|
||||
|
||||
+7
-5
@@ -26,12 +26,14 @@ run)
|
||||
socket=$WAYLAND_DISPLAY
|
||||
[[ $socket = /* ]] || socket=$XDG_RUNTIME_DIR/$socket
|
||||
socket=$(readlink -f "$socket")
|
||||
# The desktop's theme, where Omarchy keeps it, read-only.
|
||||
theme=$HOME/.local/state/omarchy
|
||||
mounts=()
|
||||
[[ -d $theme ]] && mounts=(-v "$theme:/tmp/.local/state/omarchy:ro")
|
||||
# The home folder at its own path: files, settings, theme.
|
||||
mounts=(-v "$HOME:$HOME" -e HOME="$HOME")
|
||||
# The GPU's render nodes, which Mesa's EGL needs.
|
||||
for node in /dev/dri/renderD*; do
|
||||
[[ -e $node ]] && mounts+=(--device "$node" --group-add "$(stat -c %g "$node")")
|
||||
done
|
||||
in_container "${mounts[@]}" -v "$socket:$runtime/wayland-0" -e WAYLAND_DISPLAY=wayland-0 \
|
||||
-e QT_QPA_PLATFORM=wayland "$image" build/omaphoto "${@:2}"
|
||||
-e QT_QPA_PLATFORM=wayland "$image" sh -c "$configure_and_build && exec build/omaphoto \"\$@\"" sh "${@:2}"
|
||||
;;
|
||||
*)
|
||||
echo "usage: dev.sh build|test|run [args]" >&2
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
# Builds and tests the app in one distribution's own container.
|
||||
set -euo pipefail
|
||||
distro=${1:?"usage: distro-check.sh arch|fedora|nixos"}
|
||||
distro=${1:?"usage: distro-check.sh arch|fedora|nixos|resolute"}
|
||||
here=$(cd "$(dirname "$0")" && pwd)
|
||||
repo=$(cd "$here/.." && pwd)
|
||||
docker build -t "omaphoto-$distro" "$here/distro/$distro"
|
||||
# Ubuntu 26.04 is the dev image on another base.
|
||||
if [ "$distro" = resolute ]; then
|
||||
docker build -t omaphoto-resolute --build-arg BASE=ubuntu:26.04 -f "$repo/Dockerfile.dev" "$here"
|
||||
else
|
||||
docker build -t "omaphoto-$distro" "$here/distro/$distro"
|
||||
fi
|
||||
steps="cmake -S . -B /tmp/build -G Ninja -DOMAPHOTO_WERROR=ON && cmake --build /tmp/build && ctest --test-dir /tmp/build --output-on-failure --timeout 120"
|
||||
# Not root, as dev.sh: tests refuse what root may do.
|
||||
user=1000:1000
|
||||
|
||||
@@ -1,8 +1,21 @@
|
||||
# Arch and Omarchy: Qt, GCC and ONNX Runtime as shipped.
|
||||
# Arch and Omarchy: Qt and GCC as shipped.
|
||||
FROM archlinux:latest
|
||||
RUN pacman -Syu --noconfirm --needed base-devel cmake ninja pkgconf qt6-base qt6-imageformats libheif libde265 \
|
||||
fontconfig onnxruntime-cpu ttf-dejavu curl \
|
||||
fontconfig ttf-dejavu curl \
|
||||
&& pacman -Scc --noconfirm
|
||||
# The ONNX Runtime the packages carry: Microsoft's own build.
|
||||
RUN curl -fsSL -o /tmp/ort.tgz \
|
||||
https://github.com/microsoft/onnxruntime/releases/download/v1.21.0/onnxruntime-linux-x64-1.21.0.tgz \
|
||||
&& echo "7485c7e7aac6501b27e353dcbe068e45c61ab51fbaf598d13970dfae669d20bf /tmp/ort.tgz" | sha256sum -c - \
|
||||
&& mkdir -p /tmp/ort /usr/local/include/onnxruntime /usr/local/lib64 \
|
||||
&& tar -xzf /tmp/ort.tgz -C /tmp/ort --strip-components=1 \
|
||||
&& cp -r /tmp/ort/include/. /usr/local/include/onnxruntime/ \
|
||||
&& mkdir -p /usr/local/lib/cmake && mv /tmp/ort/lib/cmake/onnxruntime /usr/local/lib/cmake/ \
|
||||
&& cp -a /tmp/ort/lib/. /usr/local/lib64/ \
|
||||
&& mkdir -p /usr/local/share/doc/onnxruntime \
|
||||
&& cp /tmp/ort/LICENSE /tmp/ort/ThirdPartyNotices.txt /usr/local/share/doc/onnxruntime/ \
|
||||
&& echo /usr/local/lib64 > /etc/ld.so.conf.d/onnxruntime.conf && ldconfig \
|
||||
&& rm -rf /tmp/ort /tmp/ort.tgz
|
||||
# U²-Net for Remove Background, pinned by its checksum.
|
||||
RUN mkdir -p /usr/local/share/omaphoto \
|
||||
&& curl -fsSL -o /usr/local/share/omaphoto/u2net.onnx \
|
||||
|
||||
@@ -1,9 +1,22 @@
|
||||
# Fedora's own Qt and ONNX Runtime; HEVC from RPM Fusion.
|
||||
FROM fedora:42
|
||||
RUN dnf install -y https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-42.noarch.rpm \
|
||||
# Fedora 43, the oldest still supported; HEVC from RPM Fusion.
|
||||
FROM fedora:43
|
||||
RUN dnf install -y https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-43.noarch.rpm \
|
||||
&& dnf install -y gcc-c++ cmake ninja-build pkgconf qt6-qtbase-devel qt6-qtimageformats libheif-devel libheif-freeworld \
|
||||
fontconfig-devel onnxruntime-devel dejavu-sans-fonts dejavu-serif-fonts dejavu-sans-mono-fonts curl rpm-build file \
|
||||
fontconfig-devel dejavu-sans-fonts dejavu-serif-fonts dejavu-sans-mono-fonts curl rpm-build file \
|
||||
&& dnf clean all
|
||||
# The ONNX Runtime the packages carry: Microsoft's own build.
|
||||
RUN curl -fsSL -o /tmp/ort.tgz \
|
||||
https://github.com/microsoft/onnxruntime/releases/download/v1.21.0/onnxruntime-linux-x64-1.21.0.tgz \
|
||||
&& echo "7485c7e7aac6501b27e353dcbe068e45c61ab51fbaf598d13970dfae669d20bf /tmp/ort.tgz" | sha256sum -c - \
|
||||
&& mkdir -p /tmp/ort /usr/local/include/onnxruntime /usr/local/lib64 \
|
||||
&& tar -xzf /tmp/ort.tgz -C /tmp/ort --strip-components=1 \
|
||||
&& cp -r /tmp/ort/include/. /usr/local/include/onnxruntime/ \
|
||||
&& mkdir -p /usr/local/lib/cmake && mv /tmp/ort/lib/cmake/onnxruntime /usr/local/lib/cmake/ \
|
||||
&& cp -a /tmp/ort/lib/. /usr/local/lib64/ \
|
||||
&& mkdir -p /usr/local/share/doc/onnxruntime \
|
||||
&& cp /tmp/ort/LICENSE /tmp/ort/ThirdPartyNotices.txt /usr/local/share/doc/onnxruntime/ \
|
||||
&& echo /usr/local/lib64 > /etc/ld.so.conf.d/onnxruntime.conf && ldconfig \
|
||||
&& rm -rf /tmp/ort /tmp/ort.tgz
|
||||
# U²-Net for Remove Background, pinned by its checksum.
|
||||
RUN mkdir -p /usr/local/share/omaphoto \
|
||||
&& curl -fsSL -o /usr/local/share/omaphoto/u2net.onnx \
|
||||
|
||||
@@ -21,5 +21,4 @@ curl -fL --proto '=https' -o "$work/$package" "$base/$package"
|
||||
curl -fsSL --proto '=https' -o "$work/SHA256SUMS" "$base/SHA256SUMS"
|
||||
# The package must match the checksum published beside it.
|
||||
(cd "$work" && grep " $package\$" SHA256SUMS | sha256sum -c -)
|
||||
# pacman asks which ONNX Runtime build provides onnxruntime.
|
||||
sudo pacman -U "$work/$package"
|
||||
@@ -9,4 +9,14 @@ if [ "$(uname -m)" != x86_64 ]; then
|
||||
echo "OmaPhoto's flake builds for x86_64 alone." >&2
|
||||
exit 1
|
||||
fi
|
||||
nix --extra-experimental-features "nix-command flakes" profile install github:ZacharyZhang-NY/OmaPhoto
|
||||
# The newest release's tag, as GitHub redirects to it.
|
||||
latest=$(curl -fsSLI --proto '=https' -o /dev/null -w '%{url_effective}' https://github.com/ZacharyZhang-NY/OmaPhoto/releases/latest)
|
||||
tag=${latest##*/}
|
||||
case "$tag" in
|
||||
v[0-9]*) ;;
|
||||
*)
|
||||
echo "No release found at $latest." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
nix --extra-experimental-features "nix-command flakes" profile install "github:ZacharyZhang-NY/OmaPhoto/$tag"
|
||||
@@ -1,16 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
# Installs OmaPhoto's latest DEB on Ubuntu and its derivatives.
|
||||
# Installs OmaPhoto's latest DEB on Ubuntu LTS and derivatives.
|
||||
set -euo pipefail
|
||||
base=https://github.com/ZacharyZhang-NY/OmaPhoto/releases/latest/download
|
||||
package=omaphoto_amd64.deb
|
||||
. /etc/os-release
|
||||
case " ${ID:-} ${ID_LIKE:-} " in
|
||||
*" ubuntu "*) ;;
|
||||
# Each DEB names its LTS's libraries; derivatives name their base.
|
||||
case "${UBUNTU_CODENAME:-}" in
|
||||
noble | resolute) ;;
|
||||
*)
|
||||
echo "This script is for Ubuntu and its derivatives; this system is ${PRETTY_NAME:-unknown}." >&2
|
||||
echo "This script is for Ubuntu 24.04 or 26.04 and systems built on them; this system is ${PRETTY_NAME:-unknown}." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
package=omaphoto-${UBUNTU_CODENAME}_amd64.deb
|
||||
if [ "$(uname -m)" != x86_64 ]; then
|
||||
echo "OmaPhoto's packages are built for x86_64 alone." >&2
|
||||
exit 1
|
||||
|
||||
@@ -16,7 +16,7 @@ extern "C" {
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Swift divides by w whatever its sign; Qt 6.11 clips.
|
||||
// Swift divides by w whatever its sign; Qt 6.10 clamps.
|
||||
QPointF project(const QTransform &map, QPointF point)
|
||||
{
|
||||
const double w = map.m13() * point.x() + map.m23() * point.y() + map.m33();
|
||||
|
||||
@@ -45,6 +45,7 @@ private slots:
|
||||
void carriedCornersAndWarpedMasks();
|
||||
void nudgesGroupsAndRefusals();
|
||||
void mapPathCarriesCurvesFlipsAndTheFillRule();
|
||||
void pointsPastTheHorizonDivideAsSwiftDoes();
|
||||
};
|
||||
|
||||
void DistortTests::cornersRunClockwiseFromTheTopLeft()
|
||||
@@ -349,5 +350,28 @@ void DistortTests::mapPathCarriesCurvesFlipsAndTheFillRule()
|
||||
QVERIFY(!DistortWarp::mapPath(ellipse, placement, QSizeF(0, 10), transform, box).has_value());
|
||||
}
|
||||
|
||||
// Past the horizon Swift divides by w; Qt 6.10 clamps.
|
||||
void DistortTests::pointsPastTheHorizonDivideAsSwiftDoes()
|
||||
{
|
||||
const Corners trapezoid = {QPointF(0, 0), QPointF(40, 0), QPointF(30, 20), QPointF(10, 20)};
|
||||
const QTransform map = DistortWarp::homography(trapezoid);
|
||||
const auto divided = [&map](QPointF p) {
|
||||
const double w = map.m13() * p.x() + map.m23() * p.y() + map.m33();
|
||||
return QPointF((map.m11() * p.x() + map.m21() * p.y() + map.m31()) / w, (map.m12() * p.x() + map.m22() * p.y() + map.m32()) / w);
|
||||
};
|
||||
const LayerTransform unit{.origin = {0, 0}, .size = {1, 1}};
|
||||
const Corners far = DistortWarp::corners({.origin = {0, -3}, .size = {1, 1}});
|
||||
for (const QPointF corner : far)
|
||||
QVERIFY(map.m13() * corner.x() + map.m23() * corner.y() + map.m33() < 0);
|
||||
const Corners carried = DistortWarp::carried({.origin = {0, -3}, .size = {1, 1}}, unit, trapezoid);
|
||||
for (size_t index = 0; index < 4; ++index)
|
||||
QVERIFY(near(carried[index], divided(far[index])));
|
||||
QPainterPath line;
|
||||
line.moveTo(far[0]);
|
||||
line.lineTo(far[2]);
|
||||
const QPainterPath mapped = DistortWarp::mapPath(line, QTransform(), QSizeF(1, 1), unit, trapezoid).value();
|
||||
QVERIFY(near(mapped.elementAt(0), divided(far[0])) && near(mapped.elementAt(1), divided(far[2])));
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(DistortTests)
|
||||
#include "DistortTests.moc"
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "DialogDesk.h"
|
||||
#include "IO/ImageExporter.h"
|
||||
#include "IO/ProjectController.h"
|
||||
#include "UI/JPEGExportSheet.h"
|
||||
#include <QApplication>
|
||||
#include <QSemaphore>
|
||||
#include <QTemporaryDir>
|
||||
@@ -14,12 +15,14 @@
|
||||
class ProjectExportFailureTests : public QObject {
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void aRenderThatFindsNoMemoryIsARenderError();
|
||||
void aSnapshotThatFindsNoMemoryIsExplained();
|
||||
void aJPEGRenderThatFindsNoMemoryIsExplained();
|
||||
void aPanelsHandOffThatFindsNoMemoryIsExplained();
|
||||
void thePanelOpensWithoutCopyingTheSnapshot();
|
||||
void aRefusedNameCopiesNoSnapshot();
|
||||
void theJPEGRenderTakesTheSnapshotWithoutACopy();
|
||||
};
|
||||
|
||||
namespace {
|
||||
@@ -56,6 +59,20 @@ struct Desk {
|
||||
};
|
||||
}
|
||||
|
||||
// The app opened panels and alerts before any export.
|
||||
void ProjectExportFailureTests::initTestCase()
|
||||
{
|
||||
Desk desk;
|
||||
QTemporaryDir folder;
|
||||
QFile taken(folder.filePath("taken.jpg.png"));
|
||||
QVERIFY(taken.open(QIODevice::WriteOnly));
|
||||
taken.close();
|
||||
desk.alerts.replies = {folder.filePath("taken.jpg"), "OK"};
|
||||
desk.controller.exportPNG([&desk] { desk.done = true; });
|
||||
QTRY_VERIFY(desk.done);
|
||||
QCOMPARE(desk.alerts.seen.size(), 2);
|
||||
}
|
||||
|
||||
void ProjectExportFailureTests::aRenderThatFindsNoMemoryIsARenderError()
|
||||
{
|
||||
const ProjectSnapshot snapshot = manyLayers();
|
||||
@@ -164,6 +181,30 @@ void ProjectExportFailureTests::aRefusedNameCopiesNoSnapshot()
|
||||
QVERIFY(!desk.session.isProjectBusy());
|
||||
}
|
||||
|
||||
// The worker takes the snapshot; a copy would not fit.
|
||||
void ProjectExportFailureTests::theJPEGRenderTakesTheSnapshotWithoutACopy()
|
||||
{
|
||||
Desk desk;
|
||||
QThreadPool *pool = QThreadPool::globalInstance();
|
||||
pool->setMaxThreadCount(1);
|
||||
QSemaphore entered, release;
|
||||
const QFuture<void> held = QtConcurrent::run([&] {
|
||||
entered.release();
|
||||
release.acquire();
|
||||
});
|
||||
entered.acquire();
|
||||
{
|
||||
malloc_trim(0);
|
||||
const AddressSpaceLimit limit(2048ll * 1024);
|
||||
desk.controller.exportJPEG([&desk] { desk.done = true; });
|
||||
}
|
||||
release.release();
|
||||
QTRY_VERIFY(desk.window.findChild<JPEGExportSheet *>());
|
||||
QTest::keyClick(desk.window.findChild<JPEGExportSheet *>(), Qt::Key_Escape);
|
||||
QTRY_VERIFY(desk.done);
|
||||
QVERIFY(desk.alerts.seen.isEmpty() && !desk.session.isProjectBusy());
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// Before any thread: workers share the heap the limit counts.
|
||||
|
||||
@@ -384,7 +384,7 @@ void ProjectWorkspaceViewTests::filesAtLaunchGoToTheWorkspace()
|
||||
const qsizetype imported = log.indexOf("imported " + folder.filePath("Picture.png").toUtf8() + " 4 x 2");
|
||||
// Both arrived, the project first, as they were named.
|
||||
QVERIFY2(opened >= 0 && imported > opened, log.constData());
|
||||
QVERIFY2(log.contains("OmaPhoto 1.0.0 on Qt"), log.constData());
|
||||
QVERIFY2(log.contains("OmaPhoto " OMAPHOTO_VERSION " on Qt"), log.constData());
|
||||
}
|
||||
|
||||
QTEST_MAIN(ProjectWorkspaceViewTests)
|
||||
|
||||
Reference in new issue
Block a user