OmaPhoto 1.0.0: the Linux port of Compositor
Release / deb (push) Canceled after 0s
Release / rpm (push) Canceled after 0s
Release / arch (push) Canceled after 0s
Release / publish (push) Canceled after 0s

A layered image editor for Linux and Omarchy, ported from Compositor
(github.com/robbietilton/Compositor), whose Swift source is its
specification. It reads and writes Compositor's .comp projects.

Packages for Ubuntu, Fedora and Arch come from the release workflow;
scripts/install/ holds one install script per distribution family,
and flake.nix serves NixOS.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
雷电芽衣andClaude committed 2026-09-26 00:44:52 -04:00
commit c864304695
564 files changed
+100804

No files matched your search

+95
View File
@@ -0,0 +1,95 @@
#pragma once
#include "Document/LayerAdjustment.h"
#include "Document/LayerAppearance.h"
#include "Document/LayerEffects.h"
#include "Document/LayerTransform.h"
#include "Document/ShapeTool.h"
#include "Document/TypeTool.h"
#include "IO/ImageImporter.h"
#include <QByteArray>
#include <QString>
#include <QUuid>
#include <map>
#include <optional>
#include <stdexcept>
#include <utility>
#include <vector>
struct LayerMask;
// Absent optionals stay out of the manifest.
struct ProjectLayerRecord {
QUuid id;
QString name;
bool isVisible;
LayerTransform transform;
std::optional<QString> imageFile;
std::optional<QUuid> parentID = std::nullopt;
std::optional<bool> isGroup = std::nullopt;
std::optional<double> opacity = std::nullopt;
std::optional<LayerBlendMode> blendMode = std::nullopt;
std::optional<QString> maskFile = std::nullopt;
std::optional<bool> maskEnabled = std::nullopt;
std::optional<QUuid> maskSourceID = std::nullopt;
std::optional<LayerAdjustment> adjustment = std::nullopt;
std::optional<LayerTransform> maskPlacement = std::nullopt;
// Absent in older projects, which means linked.
std::optional<bool> maskLinked = std::nullopt;
// A shape layer's shape; older versions keep the pixels alone.
std::optional<LayerShapeStyle> shape = std::nullopt;
// What the layer draws round itself; its pixels stay.
std::optional<LayerEffects> effects = std::nullopt;
std::optional<LayerTextStyle> text = std::nullopt;
};
// A UUID as Swift writes it: upper case, no braces.
QString uuidString(const QUuid &id);
struct ProjectManifest {
QString format = QStringLiteral("com.compositor.project");
// Swift's Int: a wild number must reach validation whole.
qint64 version = 7;
QString colorSpace = QStringLiteral("sRGB");
// Version-1 projects carry none: 72 pixels per inch.
std::optional<double> resolution = std::nullopt;
QUuid documentID;
qint64 width;
qint64 height;
std::optional<QUuid> activeLayerID;
std::vector<ProjectLayerRecord> layers;
// Pretty JSON with sorted keys; absent optionals are left out.
QByteArray encoded() const;
// Throws ProjectError(invalid) on anything Swift's decoder refuses.
static ProjectManifest decoded(const QByteArray &data);
// Format and version alone: newer files may not decode.
static std::pair<QString, qint64> header(const QByteArray &data);
};
struct ProjectSnapshot {
ProjectManifest manifest;
std::map<QUuid, ImportedImage> images;
std::map<QUuid, ImportedImage> masks = {};
// A record's mask, from its mask file and fields.
std::optional<LayerMask> mask(const ProjectLayerRecord &layer) const;
};
class ProjectError : public std::runtime_error {
public:
enum class Kind { invalid, version, missingImage, tooLarge, encode };
explicit ProjectError(Kind kind);
static ProjectError unsupportedVersion(qint64 version);
const Kind kind;
// Set only for Kind::version.
const std::optional<qint64> version;
private:
ProjectError(Kind kind, std::optional<qint64> version, const QString &description);
};
// A .comp project: a directory holding manifest.json and images/.
namespace ProjectStore {
// Replaces the project in one step or leaves it untouched.
void save(const ProjectSnapshot &snapshot, const QString &path);
ProjectSnapshot load(const QString &path);
}