12.3a: guides, the layout grid, Snap To and the View entries (Compositor 1.1.7)
This commit is contained in:
1 parent
4bfc6c237e
commit
338451fc35
25 files changed
+1029
-52
No files matched your search
@@ -20,7 +20,12 @@ ProjectSnapshot resized(const ProjectSnapshot &snapshot, const CanvasSizeOptions
|
||||
if (options.width == old.width && options.height == old.height && offset == QPointF(0, 0))
|
||||
return snapshot;
|
||||
ProjectManifest manifest{.resolution = old.resolution, .documentID = old.documentID, .width = options.width,
|
||||
.height = options.height, .activeLayerID = old.activeLayerID, .layers = {}};
|
||||
.height = options.height, .activeLayerID = old.activeLayerID, .layers = {}, .guides = old.guides};
|
||||
// Guides move with the pixels.
|
||||
if (manifest.guides) {
|
||||
for (CanvasGuide &guide : *manifest.guides)
|
||||
guide = guide.offset(offset.x(), offset.y());
|
||||
}
|
||||
for (const ProjectLayerRecord &layer : old.layers) {
|
||||
LayerTransform transform = layer.transform;
|
||||
transform.origin += offset;
|
||||
|
||||
@@ -33,7 +33,7 @@ ProjectSnapshot resized(const ProjectSnapshot &snapshot, const ImageSizeOptions
|
||||
throw ProjectError(ProjectError::Kind::tooLarge);
|
||||
const ProjectManifest &old = snapshot.manifest;
|
||||
ProjectManifest manifest{.resolution = options.resolution, .documentID = old.documentID, .width = options.width,
|
||||
.height = options.height, .activeLayerID = old.activeLayerID, .layers = {}};
|
||||
.height = options.height, .activeLayerID = old.activeLayerID, .layers = {}, .guides = old.guides};
|
||||
if (old.width == options.width && old.height == options.height) {
|
||||
manifest.layers = old.layers;
|
||||
return {.manifest = manifest, .images = snapshot.images, .masks = snapshot.masks};
|
||||
@@ -41,6 +41,11 @@ ProjectSnapshot resized(const ProjectSnapshot &snapshot, const ImageSizeOptions
|
||||
if (options.width * options.height > 100'000'000)
|
||||
throw ProjectError(ProjectError::Kind::tooLarge);
|
||||
const double sx = double(options.width) / double(old.width), sy = double(options.height) / double(old.height);
|
||||
// Guides scale with the pixels.
|
||||
if (manifest.guides) {
|
||||
for (CanvasGuide &guide : *manifest.guides)
|
||||
guide = guide.scaled(sx, sy);
|
||||
}
|
||||
std::map<QUuid, ImportedImage> images, masks;
|
||||
qint64 usedPixels = 0, usedMaskPixels = 0;
|
||||
for (const ProjectLayerRecord &layer : old.layers) {
|
||||
|
||||
@@ -115,6 +115,26 @@ LayerTextStyle textStyle(const QJsonValue &value)
|
||||
.boxSize = optional(object, "boxSize", size)};
|
||||
}
|
||||
|
||||
CanvasGuide guide(const QJsonValue &value)
|
||||
{
|
||||
const QJsonObject object = value.toObject();
|
||||
const std::optional<CanvasGuide::Axis> axis = guideAxis(string(object.value("axis")));
|
||||
// A value that is no object has no axis: refused.
|
||||
if (!axis)
|
||||
refuse();
|
||||
return {uuid(object.value("id")), *axis, number(object.value("position"))};
|
||||
}
|
||||
|
||||
std::vector<CanvasGuide> guides(const QJsonValue &value)
|
||||
{
|
||||
if (!value.isArray())
|
||||
refuse();
|
||||
std::vector<CanvasGuide> result;
|
||||
for (const QJsonValue &each : value.toArray())
|
||||
result.push_back(guide(each));
|
||||
return result;
|
||||
}
|
||||
|
||||
ProjectLayerRecord layer(const QJsonValue &value)
|
||||
{
|
||||
const QJsonObject object = value.toObject();
|
||||
@@ -211,6 +231,12 @@ QByteArray ProjectManifest::encoded() const
|
||||
object.insert("resolution", *resolution);
|
||||
if (activeLayerID)
|
||||
object.insert("activeLayerID", uuidString(*activeLayerID));
|
||||
if (guides) {
|
||||
QJsonArray lines;
|
||||
for (const CanvasGuide &guide : *guides)
|
||||
lines.append(QJsonObject{{"id", uuidString(guide.id)}, {"axis", rawValue(guide.axis)}, {"position", guide.position}});
|
||||
object.insert("guides", lines);
|
||||
}
|
||||
// Swift indents by two; four would outgrow 4 MiB sooner.
|
||||
QByteArray json;
|
||||
for (const QByteArray &line : QJsonDocument(object).toJson(QJsonDocument::Indented).split('\n')) {
|
||||
@@ -232,7 +258,8 @@ ProjectManifest ProjectManifest::decoded(const QByteArray &data)
|
||||
return {.format = string(object.value("format")), .version = integer(object.value("version")),
|
||||
.colorSpace = string(object.value("colorSpace")), .resolution = optional(object, "resolution", number),
|
||||
.documentID = uuid(object.value("documentID")), .width = integer(object.value("width")),
|
||||
.height = integer(object.value("height")), .activeLayerID = optional(object, "activeLayerID", uuid), .layers = records};
|
||||
.height = integer(object.value("height")), .activeLayerID = optional(object, "activeLayerID", uuid), .layers = records,
|
||||
.guides = optional(object, "guides", ::guides)};
|
||||
}
|
||||
|
||||
std::pair<QString, qint64> ProjectManifest::header(const QByteArray &data)
|
||||
|
||||
@@ -38,6 +38,26 @@ QString description(ProjectError::Kind kind)
|
||||
|
||||
constexpr qint64 manifestBytes = 4 * 1024 * 1024, assetBytes = 512 * 1024 * 1024;
|
||||
|
||||
// Guides came with version 8: a thousand, each once, bounded.
|
||||
void validateGuides(const ProjectManifest &manifest)
|
||||
{
|
||||
const std::vector<CanvasGuide> guides = manifest.guides.value_or(std::vector<CanvasGuide>());
|
||||
if (manifest.version < 8) {
|
||||
if (!guides.empty())
|
||||
throw ProjectError(ProjectError::Kind::invalid);
|
||||
return;
|
||||
}
|
||||
if (guides.size() > 1'000)
|
||||
throw ProjectError(ProjectError::Kind::tooLarge);
|
||||
QSet<QUuid> ids;
|
||||
for (const CanvasGuide &guide : guides) {
|
||||
// The range refuses NaN and infinity as well.
|
||||
if (ids.contains(guide.id) || !(std::abs(guide.position) <= 1'000'000))
|
||||
throw ProjectError(ProjectError::Kind::invalid);
|
||||
ids.insert(guide.id);
|
||||
}
|
||||
}
|
||||
|
||||
void validate(const ProjectManifest &manifest)
|
||||
{
|
||||
if (manifest.format != QLatin1String("com.compositor.project"))
|
||||
@@ -88,6 +108,7 @@ void validate(const ProjectManifest &manifest)
|
||||
}
|
||||
if (manifest.activeLayerID && !ids.contains(*manifest.activeLayerID))
|
||||
throw ProjectError(ProjectError::Kind::invalid);
|
||||
validateGuides(manifest);
|
||||
}
|
||||
|
||||
// Images share 100 million pixels; masks share another.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include "Document/Guides.h"
|
||||
#include "Document/LayerAdjustment.h"
|
||||
#include "Document/LayerAppearance.h"
|
||||
#include "Document/LayerEffects.h"
|
||||
@@ -60,6 +61,8 @@ struct ProjectManifest {
|
||||
qint64 height;
|
||||
std::optional<QUuid> activeLayerID;
|
||||
std::vector<ProjectLayerRecord> layers;
|
||||
// Alignment guides; versions 1 to 7 carry none.
|
||||
std::optional<std::vector<CanvasGuide>> guides = std::nullopt;
|
||||
|
||||
// Pretty JSON with sorted keys; absent optionals are left out.
|
||||
QByteArray encoded() const;
|
||||
|
||||
Reference in new issue
Block a user