OmaPhoto : l'éditeur d'images en calques de Compositor (macOS) porté pour Linux et Omarchy


OmaPhoto : l’éditeur d’images en calques de Compositor (macOS) porté pour Linux et Omarchy

Source: ZacharyZhang-NY/OmaPhoto — release v1.0.0 du 26 septembre 2026

L'icône d'OmaPhoto

Le 26 septembre 2026, OmaPhoto 1.0.0 sortait en un seul commit et douze étoiles : un éditeur d’images en calques pour Linux, écrit d’abord pour Omarchy. Le projet est né d’un constat simple — les éditeurs et compositeurs d’images natifs en calques se font rares sur Linux, et l’équivalent le plus proche du modèle n’existe que sur macOS — et d’une source précise : Compositor, l’éditeur de Wonder Assembly, écrit en Swift et natif macOS. Le code de Compositor n’est pas seulement une référence d’interface : c’est la spécification du portage. OmaPhoto en garde les outils, les menus et les raccourcis (Ctrl pour ⌘, Alt pour ⌥), et lit et écrit les projets .comp : un projet voyage entre les deux applications.

Le thème Omarchy, lu depuis colors.toml

Le point d’entrée du projet est le thème. OmaPhoto lit ~/.local/state/omarchy/current/theme/colors.toml (Omarchy 4.x), construit sa palette Qt à partir des seize clés, et suit le thème en direct : le fichier est surveillé, chaque relecture est journalisée, un changement de thème repeint l’application sans redémarrage. Deux détails valent d’être notés :

  • Pas de feuille de style. Qt ne suit nativement que la palette, et une feuille CSS gèlerait les widgets dans les couleurs du thème au moment de l’écriture. L’application construit donc la palette et laisse Fusion suivre. Le fond du canevas prend la couleur Base du thème, pour que l’écran d’accueil reste lisible en thème clair.
  • Fichiers absents, invalides ou coupés nets. Sans fichier, c’est le thème sombre intégré. Un fichier mal formé, une valeur tronquée, une lecture interrompue (EIO en plein milieu) ou un dossier sans droits ne font pas tomber le thème en silence : l’application le garde, journalise un avertissement, et distingue « absent » de « illisible ». Les tests couvrent ce cas, jusqu’au changement de thème qui atteint la vraie fenêtre.

Hors d’Omarchy, il n’y a rien à lire : l’application démarre sur son thème sombre intégré, sans aller chercher la couleur du bureau.

Ce que le portage garde, et un bug corrigé au passage

La liste des outils est celle de Compositor, reprise telle quelle :

  • Calques : dossiers, masques, masques de découpe, modes de fusion et opacité.
  • Sélections : rectangle, lasso, baguette magique, chargement depuis un calque, dilatation et érosion.
  • Peinture et retouche : pinceau, gomme, correcteur, tampon de duplication, bouillonnement, flou et liquidification, dégradés, formes, texte.
  • Transformations : déplacement, redimensionnement, distorsion des calques et des sélections.
  • Réglages : niveaux, courbes, teinte/saturation, exposition, carte de dégradé et grain, en édition directe ou en calque de réglage.
  • Effets de calque : contour, ombre portée, aplat de couleur, ombre interne.
  • Filtres : flou gaussien et flou directionnel, bruit, correction d’objectif, remplissage sensible au contenu et suppression d’arrière-plan.
  • Fichiers : recadrage, taille du canevas, taille de l’image, export PNG et JPEG, import JPEG, PNG, TIFF et HEIC.

Le plus curieux est ce que le portage a trouvé en chemin. La documentation interne note que l’export de Compositor perd ses effets de calque : l’enregistrement écrit bien la clé effects, mais ni projectSnapshot ni installProject ne la transportent — le même commit avait câblé text et oublié effects. Un utilisateur de la version Mac voit donc ses ombres et ses contours disparaître au moment d’enregistrer ou d’exporter. Le portage corrige, documente l’écart, et en compte une dizaine d’autres « correctifs au-delà de Swift » : des marges d’effets trop courtes qui laissent des bandes sombres pendant le tracé, unLevels et une Teinte/Saturation qui déprémultiplient deux fois les bords doux (le portage garde le résultat qu’attend le test de Swift), et les modes de fusion non séparables réécrits à la main parce que Qt se trompe sur Color Dodge et Color Burn.

Quant aux icônes : macOS a les SF Symbols, Linux n’a rien d’équivalent. Chaque icône de la barre d’outils est donc redessinée en tracés Qt (ToolIcons, LayerIcons), et la feuille ci-dessous est celle que les tests comparent, en deux tailles.

Les icônes d'outils peintes par l'application, en deux tailles

Pas de GPU : QPainter, huit noyaux C, et des chiffres publiés

Le cahier des charges a été taillé au rasoir : backend GPU écarté, pas d’AppImage, pas de Flatpak, pas de mise à jour automatique. Le rendu est CPU, avec QPainter et huit noyaux C récupérés du moteur Metal de Compositor (pinceau, retouche, niveaux, baguette, contenu, flou, bruit, objectif), et les filtres de teinte à fusion HSL sont réécrits en tables de correspondance CPU.

Ce choix a un prix, mais il est mesuré et publié dans AGENTS.md, ce qui est rare :

  • Trait au pinceau : 4K, échelle 2, pointe douce de 800 px → 6,8 à 7,4 ms par image (contre 47 ms avant optimisation). Swift mesure 2,6–2,8 ms sur Mac, mais seulement le trait, pas le panoramique ni le zoom.
  • Navigation sur un document 3840 × 2160 de dix calques : panoramique à l’ajustement 30 ms (p95 37), aux pixels réels 68 ms (p95 74), zoom 77 ms (p95 86) — le coût dominant étant le mode HSL, passé de 85 à 30 ms en mélangeant les lignes en parallèle.
  • Un flou gaussien de grande amplitude coûte 20 à 110 ms sur un document 4K, payé au lancement du trait.
  • Les seuils de non-régression sont fixés à 1,5× ces médianes : une régression fait échouer les tests.

Le rendu du canevas en bandes parallèles reste à faire : il faudrait rendre thread-safe les caches (morceaux, traits, prévisualisations d’effets), et ce n’est pas fait.

Supprimer l’arrière-plan, hors ligne

Le filtre le plus spectaculaire est aussi le plus lourd : Remove Background fait tourner U²-Net (176 Mo de poids, Apache-2.0) via ONNX Runtime, entièrement hors ligne — le modèle est embarqué dans le paquet, hashé dans le PKGBUILD comme dans le Dockerfile.dev. Le README ne le présente pas comme une promesse mais comme une mesure : 90 ms pour un masque sur 32 cœurs. Le masque est produit à 320 px puis raffiné en pleine résolution (« Shift Edge », contraste) dans la lignée du modèle de segmentation.

Cela explique les 166 à 174 Mo des paquets (Arch, Fedora, Ubuntu) : Qt, ONNX Runtime embarqué et le modèle. Contrepartie assumée : rien ne part sur le réseau, ce qui sur un portable vaut peut-être 176 Mo.

Installer : un script par famille

Chaque script télécharge le paquet de la dernière release, le vérifie contre le SHA256SUMS de la release, puis l’installe avec le gestionnaire du système. Les paquets sont construits par GitHub Actions sur un tag, chacun sur son système (.deb sur Ubuntu 24.04 avec ONNX Runtime embarqué, .rpm sur Fedora, PKGBUILD sur Arch). En x86_64 uniquement.

Arch et Omarchy :

curl -fsSLO https://raw.githubusercontent.com/ZacharyZhang-NY/OmaPhoto/main/scripts/install/arch.sh && bash arch.sh

Ubuntu 24.04 et ses dérivées (Linux Mint 22, Pop!_OS 24.04) :

curl -fsSLO https://raw.githubusercontent.com/ZacharyZhang-NY/OmaPhoto/main/scripts/install/ubuntu.sh && bash ubuntu.sh

Fedora :

curl -fsSLO https://raw.githubusercontent.com/ZacharyZhang-NY/OmaPhoto/main/scripts/install/fedora.sh && bash fedora.sh

NixOS, depuis le flake dans le profil :

curl -fsSLO https://raw.githubusercontent.com/ZacharyZhang-NY/OmaPhoto/main/scripts/install/nixos.sh && bash nixos.sh

Deux nuances utiles : le paquet DEB utilise les noms de paquets Ubuntu, donc Debian se compile depuis les sources ; et le libheif de Fedora ne décode pas HEVC — le script invite à ajouter RPM Fusion et libheif-freeworld pour l’import HEIC. Un projet .comp est un dossier sous Linux : le gestionnaire de fichiers peut donc l’ouvrir comme un dossier, un détail que le projet assume.

Les tests sont plus gros que le code

C’est le chiffre qui résume la méthode. Le dépôt compte 564 fichiers : 280 sources (1,5 Mo) et 233 fichiers de tests (2,8 Mo) — près de deux fois le poids du code, et 205 binaires de tests enregistrés dans CTest, lancés en headless (QT_QPA_PLATFORM=offscreen).

La méthode est celle du « jumelage » : l’arbre Swift est la spécification, chaque fichier Swift a un jumeau Linux avec les mêmes noms de type et de fonction, et les tests Swift sont traduits un par un. Le tout est découpé en « items » numérotés (TASKS.md, 402 lignes), où « terminé » veut dire code, tests verts dans le conteneur de dev, et codex review passé. Le journal des revues est impitoyable et lisible : des dizaines de tours, la plupart en échec, des bogues réels trouvés au passage (un use-after-free entre le contrôleur et la file d’import, un nom de sauvegarde qui écrasait un autre projet, des pixels perdus dans un calque de fusion), et l’habitude de vérifier que les tests meurent bien quand on casse le code (mutation testing, scripts/mutate.py).

La suite tourne aussi hors de l’image de dev, en conteneur, avec le compte uid 1000 et HOME/TMPDIR en tmpfs : Arch 205/205, Fedora 42 203/205 puis 205/205 après l’ajout des polices qui manquent à son image (Fedora découpe DejaVu en trois paquets et son sans-serif par défaut est Noto), et le flake NixOS construit puis exécute la suite. Le README documente la dépendance à Qt 6.4 minimum (Ubuntu 24.04), avec C++20, libheif, fontconfig et ONNX Runtime.

Ce qui manque — et pourquoi c’est intéressant à surveiller

OmaPhoto 1.0.0 est un dépôt d’un seul commit, sans capture d’écran dans le README, et la dernière ligne de TASKS.md — « recette de bout en bout avec captures, tableau d’acceptation » — n’est pas cochée. Autrement dit : la recette de validation visuelle, celle qui aurait produit des screens, reste à faire. Le reste de l’état des lieux est assumé :

  • Pas de GPU (choix documenté, pas un oubli), pas d’AppImage ni de Flatpak, pas de mise à jour automatique.
  • x86_64 seulement ; le chemin ARM reste à écrire.
  • Thème Omarchy 4.x seulement : Omarchy 3.x est explicitement hors périmètre, et l’intégration se limite à la lecture de colors.toml — pas de plugin de thème, pas de réglages de thème.
  • Les menus ne sont pas tous portés : la barre de menus est reconstruite entrée par entrée d’après celle de Swift, mais les entrées encore dues restent listées dans TASKS.md (« Menu items owed ») ; « Hide » et « Check for Updates » n’ont pas de jumeau (le bureau gère les fenêtres, les paquets de la distro mettent à jour).

Pour qui vient de l’écosystème Omarchy, c’est quand même une vraie pièce nouvelle : un éditeur en calques natif, dans la langue du bureau, avec interchangeabilité des projets avec le Mac. Et pour qui cherche la méthode plus que l’outil, AGENTS.md (152 lignes denses) est une lecture inhabituelle : chaque écart à l’original est nommé, justifié, et accompagné du test qui le prouve.

Sources