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

+91
View File
@@ -0,0 +1,91 @@
#include "UI/CanvasThumbnail.h"
#include "Document/BrushStroke.h"
#include "IO/ImageExporter.h"
#include "Logging.h"
#include "Rendering/LayerRenderer.h"
#include <QPainter>
#include <cmath>
namespace {
// A canvas-shaped picture; `draw` gets pixels and their scale.
QPixmap render(QSizeF canvas, double box, const std::function<void(QPainter &, QSize, double)> &draw)
{
const QSize points = CanvasThumbnail::fittedSize(canvas, box);
const QSize pixels = points * CanvasThumbnail::backingScale;
QImage image;
try {
image = BrushRaster::context(pixels.width(), pixels.height(), false);
} catch (const ExportError &error) {
qCWarning(lcRendering) << "a thumbnail could not be allocated:" << pixels << error.what();
QPixmap empty(pixels);
empty.fill(Qt::transparent);
empty.setDevicePixelRatio(CanvasThumbnail::backingScale);
return empty;
}
{
QPainter painter(&image);
draw(painter, pixels, canvas.width() > 0 ? pixels.width() / canvas.width() : 1);
}
QPixmap result = QPixmap::fromImage(image);
result.setDevicePixelRatio(CanvasThumbnail::backingScale);
return result;
}
// The image where the transform puts it, as the canvas.
void place(const QImage &image, const LayerTransform &transform, double scale, QPainter &painter)
{
LayerRenderer::draw(image, transform, QPointF(transform.center().x() * scale, transform.center().y() * scale), painter, {.scale = scale});
}
}
QSize CanvasThumbnail::fittedSize(QSizeF canvas, double box)
{
if (!(canvas.width() > 0) || !(canvas.height() > 0) || !std::isfinite(canvas.width()) || !std::isfinite(canvas.height()))
return QSize(int(box), int(box));
const double scale = box / std::max(canvas.width(), canvas.height());
return QSize(std::max(1, int(std::round(canvas.width() * scale))), std::max(1, int(std::round(canvas.height() * scale))));
}
QPixmap CanvasThumbnail::layer(const QImage &image, const LayerTransform &transform, QSizeF canvas, double box)
{
return render(canvas, box, [&](QPainter &painter, QSize size, double scale) {
painter.fillRect(QRect(QPoint(0, 0), size), QColor::fromRgbF(0.22, 0.22, 0.22));
const double tile = 6 * backingScale;
for (int row = 0; row < int(std::ceil(size.height() / tile)); ++row) {
for (int column = 0; column < int(std::ceil(size.width() / tile)); ++column) {
if ((row + column) % 2 == 0)
painter.fillRect(QRectF(column * tile, row * tile, tile, tile), QColor::fromRgbF(0.32, 0.32, 0.32));
}
}
if (!image.isNull())
place(image, transform, scale, painter);
});
}
QPixmap CanvasThumbnail::mask(const QImage &image, const LayerTransform &transform, QSizeF canvas, double box)
{
return render(canvas, box, [&](QPainter &painter, QSize size, double scale) {
const double tone = edgeTone(image);
painter.fillRect(QRect(QPoint(0, 0), size), QColor::fromRgbF(tone, tone, tone));
place(image, transform, scale, painter);
});
}
double CanvasThumbnail::edgeTone(const QImage &image)
{
const QImage gray = image.convertToFormat(QImage::Format_Grayscale8);
if (gray.width() <= 0 || gray.height() <= 0)
return 1;
qint64 total = 0, count = 0;
for (int y = 0; y < gray.height(); ++y) {
const uchar *row = gray.constScanLine(y);
const bool edgeRow = y == 0 || y == gray.height() - 1;
for (int x = 0; x < gray.width(); ++x) {
if (edgeRow || x == 0 || x == gray.width() - 1) {
total += row[x];
++count;
}
}
}
return count == 0 ? 1 : double(total) / double(count) / 255;
}