14.22: effects, text and shapes outlast size changes and adjustments
This commit is contained in:
17 files changed
+226
-38
No files matched your search
@@ -24,7 +24,8 @@ ImageLayer::ImageLayer(const ProjectLayerRecord &record, std::optional<ImportedI
|
||||
: id(record.id), asset(std::move(asset)), transform(record.transform), name(record.name), isVisible(record.isVisible),
|
||||
parentID(record.parentID), isGroup(record.isGroup == true), opacity(record.opacity.value_or(1)),
|
||||
blendMode(record.blendMode.value_or(LayerBlendMode::normal)), maskSourceID(record.maskSourceID), mask(std::move(mask)),
|
||||
adjustment(record.adjustment), effects(record.effects)
|
||||
adjustment(record.adjustment), shape(LayerShape::loaded(record.shape, this->asset)), effects(record.effects),
|
||||
text(LayerText::loaded(record.text, this->asset))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -32,12 +32,6 @@ void EditorSession::installProject(const ProjectSnapshot &snapshot, const QStrin
|
||||
m_guideDrag = std::nullopt;
|
||||
m_transformEdit = std::nullopt;
|
||||
m_document = CanvasDocument(snapshot);
|
||||
// Install loads shapes and text; Swift's resize rebuild does not.
|
||||
for (size_t index = 0; index < m_document->layers.size(); ++index) {
|
||||
ImageLayer &layer = m_document->layers[index];
|
||||
layer.shape = LayerShape::loaded(snapshot.manifest.layers[index].shape, layer.asset);
|
||||
layer.text = LayerText::loaded(snapshot.manifest.layers[index].text, layer.asset);
|
||||
}
|
||||
setActiveLayerID(snapshot.manifest.activeLayerID);
|
||||
m_projectPath = path;
|
||||
m_renamingLayerID = std::nullopt;
|
||||
|
||||
@@ -133,9 +133,8 @@ void EditorSession::mergeFloatingTransform(const TransformEdit &edit, const Floa
|
||||
layer.transform = merged.transform;
|
||||
layer.isGroup = false;
|
||||
layer.mask = merged.mask;
|
||||
// Swift rebuilds the layer without shape, effects and text.
|
||||
// Swift rebuilds the layer without shape and text.
|
||||
layer.shape = std::nullopt;
|
||||
layer.effects = std::nullopt;
|
||||
layer.text = std::nullopt;
|
||||
m_document->selection = moved;
|
||||
setActiveLayerID(source.id);
|
||||
|
||||
@@ -165,9 +165,8 @@ void EditorSession::finishHueSaturationCommit()
|
||||
beginEdit(QStringLiteral("Hue/Saturation"));
|
||||
ImageLayer &layer = m_document->layers[index];
|
||||
layer.asset = ImportedImage(made.pixels->image, made.pixels->thumbnail.value_or(made.pixels->image), layer.name);
|
||||
// Swift rebuilds the layer without shape, effects and text.
|
||||
// Swift rebuilds the layer without shape and text.
|
||||
layer.shape = std::nullopt;
|
||||
layer.effects = std::nullopt;
|
||||
layer.text = std::nullopt;
|
||||
endEdit();
|
||||
}
|
||||
|
||||
@@ -85,6 +85,24 @@ bool LayerEffects::isValid() const
|
||||
&& (!innerShadow || innerShadow->isValid()) && (!outerGlow || outerGlow->isValid()) && (!innerGlow || innerGlow->isValid());
|
||||
}
|
||||
|
||||
// Swift 1.4.9's Scale Styles for Image Size.
|
||||
LayerEffects LayerEffects::scaled(double factor) const
|
||||
{
|
||||
const auto scale = [factor](double value, double limit) { return std::min(limit, value * factor); };
|
||||
LayerEffects result = *this;
|
||||
if (result.stroke)
|
||||
result.stroke->size = scale(stroke->size, 500);
|
||||
if (result.shadow)
|
||||
result.shadow->distance = scale(shadow->distance, 5000), result.shadow->blur = scale(shadow->blur, 500);
|
||||
if (result.innerShadow)
|
||||
result.innerShadow->distance = scale(innerShadow->distance, 5000), result.innerShadow->blur = scale(innerShadow->blur, 500);
|
||||
if (result.outerGlow)
|
||||
result.outerGlow->size = scale(outerGlow->size, 500);
|
||||
if (result.innerGlow)
|
||||
result.innerGlow->size = scale(innerGlow->size, 500);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<LayerEffectKind> LayerEffects::kinds() const
|
||||
{
|
||||
std::vector<LayerEffectKind> result;
|
||||
|
||||
@@ -127,6 +127,8 @@ struct LayerEffects {
|
||||
void setEnabled(bool enabled, LayerEffectKind kind);
|
||||
// The effects that are switched on.
|
||||
LayerEffects visible() const;
|
||||
// Resampled by `factor`: sizes, distances and blurs, within range.
|
||||
LayerEffects scaled(double factor) const;
|
||||
friend bool operator==(const LayerEffects &, const LayerEffects &) = default;
|
||||
};
|
||||
|
||||
|
||||
@@ -361,9 +361,8 @@ void EditorSession::finishLevelsCommit()
|
||||
beginEdit(QStringLiteral("Levels"));
|
||||
ImageLayer layer = current;
|
||||
layer.asset = made.asset;
|
||||
// Swift rebuilds the layer without shape, effects and text.
|
||||
// Swift rebuilds the layer without shape and text.
|
||||
layer.shape = std::nullopt;
|
||||
layer.effects = std::nullopt;
|
||||
layer.text = std::nullopt;
|
||||
m_document->layers[index] = layer;
|
||||
endEdit();
|
||||
|
||||
@@ -211,9 +211,8 @@ void EditorSession::finishInvert()
|
||||
const ImportedImage asset(*result.image, PixelAdjust::thumbnail(*result.image), current.name);
|
||||
beginEdit(QStringLiteral("Invert"));
|
||||
m_document->layers[size_t(index)].asset = asset;
|
||||
// Swift rebuilds the layer without shape, effects and text.
|
||||
// Swift rebuilds the layer without shape and text.
|
||||
m_document->layers[size_t(index)].shape = std::nullopt;
|
||||
m_document->layers[size_t(index)].effects = std::nullopt;
|
||||
m_document->layers[size_t(index)].text = std::nullopt;
|
||||
}
|
||||
endEdit();
|
||||
|
||||
@@ -41,7 +41,7 @@ ProjectSnapshot resized(const ProjectSnapshot &snapshot, const CanvasSizeOptions
|
||||
.opacity = layer.opacity, .blendMode = layer.blendMode, .maskFile = layer.maskFile,
|
||||
.maskEnabled = layer.maskEnabled, .maskSourceID = layer.maskSourceID, .adjustment = layer.adjustment,
|
||||
.maskPlacement = placement,
|
||||
.maskLinked = layer.maskLinked, .shape = layer.shape,
|
||||
.maskLinked = layer.maskLinked, .shape = layer.shape, .effects = layer.effects,
|
||||
.text = layer.text});
|
||||
}
|
||||
std::map<QUuid, ImportedImage> images = snapshot.images;
|
||||
|
||||
@@ -42,6 +42,8 @@ ProjectSnapshot resized(const ProjectSnapshot &snapshot, const ImageSizeOptions
|
||||
if (options.width * options.height > DocumentLimits::maxSurfacePixels)
|
||||
throw ProjectError(ProjectError::Kind::tooLarge);
|
||||
const double sx = double(options.width) / double(old.width), sy = double(options.height) / double(old.height);
|
||||
// Effects scale by the factors' mean; text and shapes flatten.
|
||||
const double effectScale = std::sqrt(sx * sy);
|
||||
// Guides scale with the pixels.
|
||||
if (manifest.guides) {
|
||||
for (CanvasGuide &guide : *manifest.guides)
|
||||
@@ -108,7 +110,8 @@ ProjectSnapshot resized(const ProjectSnapshot &snapshot, const ImageSizeOptions
|
||||
.opacity = layer.opacity, .blendMode = layer.blendMode, .maskFile = layer.maskFile,
|
||||
.maskEnabled = layer.maskEnabled, .maskSourceID = layer.maskSourceID, .adjustment = layer.adjustment,
|
||||
.maskPlacement = placement,
|
||||
.maskLinked = layer.maskLinked});
|
||||
.maskLinked = layer.maskLinked,
|
||||
.effects = layer.effects ? std::optional(layer.effects->scaled(effectScale)) : std::nullopt});
|
||||
}
|
||||
qCInfo(lcIO) << "resized" << old.width << "x" << old.height << "to" << options.width << "x" << options.height;
|
||||
return {.manifest = manifest, .images = images, .masks = masks};
|
||||
|
||||
Reference in new issue
Block a user