Files
OmaPhoto/tests/RenderFixtures.h
雷电芽衣andClaude c864304695
Release / deb (push) Canceled after 0s
Release / rpm (push) Canceled after 0s
Release / arch (push) Canceled after 0s
Release / publish (push) Canceled after 0s
OmaPhoto 1.0.0: the Linux port of Compositor
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>
2026-09-26 00:44:52 -04:00

132 lines
4.3 KiB
C++

#pragma once
#include "Document/BrushStroke.h"
#include "Rendering/RasterSnapshot.h"
#include <QImage>
#include <functional>
#include <memory>
#include <vector>
// An RGBA image filled with one premultiplied pixel value.
inline QImage solid(int width, int height, QRgb premultiplied)
{
QImage image = BrushRaster::context(width, height, false);
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x)
image.setPixel(x, y, premultiplied);
}
return image;
}
// Red holds x, green holds y, opaque.
inline QImage coordinates(int width, int height, int step = 10)
{
QImage image = BrushRaster::context(width, height, false);
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x)
image.setPixel(x, y, qRgba(x * step, y * step, 0, 255));
}
return image;
}
inline QImage gray(int width, int height, int value)
{
QImage image = BrushRaster::context(width, height, true);
image.fill(value);
return image;
}
inline LayerTransform placedAt(QPointF origin, QSizeF size)
{
return {.origin = origin, .size = size, .sampling = LayerSampling::nearest};
}
// Detailed, deterministic pixels.
inline QImage noise(int width, int height, quint32 seed, int alpha = 255)
{
QImage image = BrushRaster::context(width, height, false);
quint32 state = seed;
for (int y = 0; y < height; ++y) {
uchar *row = image.scanLine(y);
for (int x = 0; x < width; ++x, row += 4) {
state = state * 1'664'525u + 1'013'904'223u;
row[0] = uchar(int(uchar(state >> 24)) * alpha / 255);
row[1] = uchar(int(uchar(state >> 16)) * alpha / 255);
row[2] = uchar(int(uchar(state >> 8)) * alpha / 255);
row[3] = uchar(alpha);
}
}
return image;
}
// A painted layer: unchanged pixels and replacement tiles.
inline std::shared_ptr<const RasterSnapshot> painted(const QImage &base, const std::vector<BrushPatch> &patches)
{
return std::make_shared<const RasterSnapshot>(base.width(), base.height(), base, QRectF(base.rect()), patches);
}
// Smooth pixels: a slipped sample changes little.
inline QImage ramp(int width, int height, int seed)
{
QImage image = BrushRaster::context(width, height, false);
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x)
image.setPixel(x, y, qRgba((x + seed) / 4 % 256, y / 4 % 256, seed % 256, 255));
}
return image;
}
inline QImage composite(const QImage &base, const std::vector<BrushPatch> &patches)
{
QImage result = BrushRaster::context(base.width(), base.height(), false);
QPainter painter(&result);
BrushRaster::draw(base, QRectF(base.rect()), painter);
for (const BrushPatch &patch : patches)
BrushRaster::draw(patch.image, patch.rect, painter);
return result;
}
inline QImage render(int side, const std::function<void(QPainter &)> &draw)
{
QImage surface = BrushRaster::context(side, side, false);
QPainter painter(&surface);
draw(painter);
return surface;
}
// Largest channel difference over pixels fully inside `outline`'s layer.
inline int largestDifference(const QImage &expected, const QImage &actual, const QImage &outline)
{
int largest = 0;
const int side = expected.width();
for (int y = 1; y < side - 1; ++y) {
for (int x = 1; x < side - 1; ++x) {
bool inside = true;
for (int dy = -1; dy <= 1 && inside; ++dy) {
for (int dx = -1; dx <= 1 && inside; ++dx)
inside = outline.constScanLine(y + dy)[(x + dx) * 4 + 3] == 255;
}
if (!inside)
continue;
for (int channel = 0; channel < 4; ++channel)
largest = std::max(largest, std::abs(expected.constScanLine(y)[x * 4 + channel] - actual.constScanLine(y)[x * 4 + channel]));
}
}
return largest;
}
inline int largestDifference(const QImage &expected, const QImage &actual)
{
return largestDifference(expected, actual, expected);
}
// Largest channel difference over every pixel.
inline int largestAnywhere(const QImage &expected, const QImage &actual)
{
int largest = 0;
for (int y = 0; y < expected.height(); ++y) {
for (int x = 0; x < expected.width() * 4; ++x)
largest = std::max(largest, std::abs(expected.constScanLine(y)[x] - actual.constScanLine(y)[x]));
}
return largest;
}