13.19d: 2190a1b verified; sizeLabel's ties tested; tests read optionals through value(); two mask tests brought to 13.12
This commit is contained in:
1 parent
ab34fd6ace
commit
3757eb8910
40 files changed
+233
-220
No files matched your search
@@ -191,14 +191,14 @@ void BlurNoiseAdjustmentTests::theStoreTakesTheKindsFromVersion9()
|
||||
QCOMPARE(stored(snapshot), std::optional(ProjectError::Kind::invalid));
|
||||
// Invalid settings refuse it at any version.
|
||||
snapshot.manifest.version = 9;
|
||||
snapshot.manifest.layers[1].adjustment->setGaussianRadius(300);
|
||||
snapshot.manifest.layers[1].adjustment.value().setGaussianRadius(300);
|
||||
QCOMPARE(stored(snapshot), std::optional(ProjectError::Kind::invalid));
|
||||
}
|
||||
// The rule reads the kind: older kinds may carry keys.
|
||||
ProjectSnapshot older = twoLayers();
|
||||
older.manifest.version = 7;
|
||||
older.manifest.layers[1].adjustment = LayerAdjustment{AdjustmentKind::levels};
|
||||
older.manifest.layers[1].adjustment->setGaussianRadius(5);
|
||||
older.manifest.layers[1].adjustment.value().setGaussianRadius(5);
|
||||
QCOMPARE(stored(older), std::nullopt);
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ void BlurNoiseAdjustmentTests::eachNoiseLayerRollsItsOwnSeed()
|
||||
session.setAdjustmentEditingID(std::nullopt);
|
||||
session.addAdjustment(AdjustmentKind::addNoise);
|
||||
const LayerAdjustment second = session.activeLayer().value().adjustment.value();
|
||||
QVERIFY(first.noiseSeed && second.noiseSeed && *first.noiseSeed != *second.noiseSeed);
|
||||
QVERIFY(first.noiseSeed && second.noiseSeed && first.noiseSeed.value() != second.noiseSeed.value());
|
||||
// Only noise rolls one; the rest keep their defaults.
|
||||
session.setAdjustmentEditingID(std::nullopt);
|
||||
session.addAdjustment(AdjustmentKind::gaussianBlur);
|
||||
|
||||
@@ -28,11 +28,11 @@ inline QImage preview(const BrushStroke &stroke, QSizeF canvas)
|
||||
QPainter painter(&context);
|
||||
const ImageLayer &layer = stroke.layer;
|
||||
const std::optional<ImportedImage> &asset = layer.asset;
|
||||
const QImage image = asset && !asset->raster ? asset->image() : QImage();
|
||||
const QImage image = asset && !asset.value().raster ? asset.value().image() : QImage();
|
||||
LayerRenderer::drawBrushPreview(image, stroke.paintTransform, stroke.paintTransform.center(), painter,
|
||||
{.opacity = layer.opacity, .blendMode = layer.blendMode, .mask = layer.mask ? layer.mask->enabledImage().value_or(QImage()) : QImage()},
|
||||
{.opacity = layer.opacity, .blendMode = layer.blendMode, .mask = layer.mask ? layer.mask.value().enabledImage().value_or(QImage()) : QImage()},
|
||||
{.patches = stroke.patches(), .pixelWidth = stroke.width, .pixelHeight = stroke.height, .paintingMask = stroke.isMask,
|
||||
.sourceRect = stroke.sourceRect, .raster = asset ? asset->raster : nullptr});
|
||||
.sourceRect = stroke.sourceRect, .raster = asset ? asset.value().raster : nullptr});
|
||||
painter.end();
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ void CanvasEditContractTests::aGradientThatCannotFillCancels()
|
||||
scene.session.selectTool(NavigationTool::gradient);
|
||||
scene.session.beginGradient(QPointF(10, 30));
|
||||
QVERIFY(scene.session.gradientEdit());
|
||||
scene.session.gradientEdit()->raster->pixelLimit = 100;
|
||||
scene.session.gradientEdit().value().raster->pixelLimit = 100;
|
||||
scene.session.moveGradient(std::nullopt, QPointF(70, 30));
|
||||
QVERIFY(!scene.session.gradientEdit() && scene.session.brushError().has_value());
|
||||
QCOMPARE(scene.session.history.undoCount(), count);
|
||||
|
||||
@@ -19,7 +19,7 @@ struct Shown {
|
||||
explicit Shown(std::optional<QSize> document = QSize(100, 100), QSize size = QSize(400, 300)) : canvas(new CanvasView(session, &window))
|
||||
{
|
||||
if (document)
|
||||
session.createDocument(document->width(), document->height());
|
||||
session.createDocument(document.value().width(), document.value().height());
|
||||
window.resize(size);
|
||||
canvas->setGeometry(QRect(QPoint(0, 0), size));
|
||||
window.show();
|
||||
|
||||
@@ -253,8 +253,8 @@ void EffectsCanvasTests::aSurfaceIsMadeAnewForOtherSettings()
|
||||
QVERIFY(session.finishBrushImmediately());
|
||||
// A canvas alone follows nothing: the old surface stays.
|
||||
LayerEffects cyan = outline(2);
|
||||
cyan.stroke->red = 0;
|
||||
cyan.stroke->blue = 1;
|
||||
cyan.stroke.value().red = 0;
|
||||
cyan.stroke.value().blue = 1;
|
||||
session.setEffects(cyan);
|
||||
session.beginBrush(QPointF(45, 32));
|
||||
session.continueBrush(QPointF(55, 32));
|
||||
|
||||
+23
-23
@@ -146,56 +146,56 @@ void EffectsPanelTests::theSheetEditsItsEffect()
|
||||
auto &slider = find<QSlider>(sheet, "sizeSlider");
|
||||
QCOMPARE(size.text(), QString("4"));
|
||||
type(size, "12");
|
||||
QCOMPARE(editor.effects().stroke->size, 12.0);
|
||||
QCOMPARE(editor.effects().stroke.value().size, 12.0);
|
||||
// The slider spans 0 to 20; typing reaches 500.
|
||||
QCOMPARE(slider.value(), 600);
|
||||
// No finite number changes nothing; the field goes back.
|
||||
type(size, "abc");
|
||||
QVERIFY(editor.effects().stroke->size == 12 && size.text() == "12");
|
||||
QVERIFY(editor.effects().stroke.value().size == 12 && size.text() == "12");
|
||||
type(size, "inf");
|
||||
QVERIFY(editor.effects().stroke->size == 12 && size.text() == "12");
|
||||
QVERIFY(editor.effects().stroke.value().size == 12 && size.text() == "12");
|
||||
// Up steps one, Shift ten; past the range, clamped.
|
||||
QTest::keyClick(&size, Qt::Key_Up);
|
||||
QCOMPARE(editor.effects().stroke->size, 13.0);
|
||||
QCOMPARE(editor.effects().stroke.value().size, 13.0);
|
||||
QTest::keyClick(&size, Qt::Key_Up, Qt::ShiftModifier);
|
||||
QCOMPARE(editor.effects().stroke->size, 23.0);
|
||||
QCOMPARE(editor.effects().stroke.value().size, 23.0);
|
||||
// Past its end the thumb rests there, writing nothing back.
|
||||
QCOMPARE(slider.value(), 1000);
|
||||
// A step replaces pending typing, even where it clamps.
|
||||
typing(size, "12");
|
||||
QTest::keyClick(&size, Qt::Key_Up);
|
||||
QVERIFY(editor.effects().stroke->size == 24 && size.text() == "24" && !size.isModified());
|
||||
QVERIFY(editor.effects().stroke.value().size == 24 && size.text() == "24" && !size.isModified());
|
||||
size.clearFocus();
|
||||
QCOMPARE(editor.effects().stroke->size, 24.0);
|
||||
QCOMPARE(editor.effects().stroke.value().size, 24.0);
|
||||
type(size, "900");
|
||||
QVERIFY(editor.effects().stroke->size == 500 && size.text() == "500");
|
||||
QVERIFY(editor.effects().stroke.value().size == 500 && size.text() == "500");
|
||||
typing(size, "12");
|
||||
QTest::keyClick(&size, Qt::Key_Up);
|
||||
QVERIFY(editor.effects().stroke->size == 500 && size.text() == "500" && !size.isModified());
|
||||
QVERIFY(editor.effects().stroke.value().size == 500 && size.text() == "500" && !size.isModified());
|
||||
size.clearFocus();
|
||||
QCOMPARE(editor.effects().stroke->size, 500.0);
|
||||
QCOMPARE(editor.effects().stroke.value().size, 500.0);
|
||||
// The slider sets any value; the field rounds, ties even.
|
||||
slider.setValue(525);
|
||||
QVERIFY(editor.effects().stroke->size == 10.5 && size.text() == "10");
|
||||
QVERIFY(editor.effects().stroke.value().size == 10.5 && size.text() == "10");
|
||||
// Leaving an untouched field keeps the exact value.
|
||||
size.setFocus();
|
||||
size.clearFocus();
|
||||
QCOMPARE(editor.effects().stroke->size, 10.5);
|
||||
QCOMPARE(editor.effects().stroke.value().size, 10.5);
|
||||
// Typing waits while other changes arrive.
|
||||
auto &opacity = find<QLineEdit>(sheet, "opacityField");
|
||||
QCOMPARE(opacity.text(), QString("100"));
|
||||
typing(size, "3");
|
||||
find<QSlider>(sheet, "opacitySlider").setValue(500);
|
||||
QVERIFY(editor.effects().stroke->opacity == 0.5 && opacity.text() == "50" && size.text() == "3");
|
||||
QVERIFY(editor.effects().stroke.value().opacity == 0.5 && opacity.text() == "50" && size.text() == "3");
|
||||
size.clearFocus();
|
||||
QCOMPARE(editor.effects().stroke->size, 3.0);
|
||||
QCOMPARE(editor.effects().stroke.value().size, 3.0);
|
||||
// Inside or Outside, as Swift's segments, following an undo.
|
||||
const QList<QToolButton *> position = find<QWidget>(sheet, "strokePosition").findChildren<QToolButton *>();
|
||||
QVERIFY(position.size() == 2 && position[0]->text() == "Outside" && position[1]->text() == "Inside" && position[0]->isChecked());
|
||||
position[1]->click();
|
||||
QVERIFY(editor.effects().stroke->inside && position[1]->isChecked());
|
||||
QVERIFY(editor.effects().stroke.value().inside && position[1]->isChecked());
|
||||
editor.session.undo();
|
||||
QVERIFY(!editor.effects().stroke->inside && position[0]->isChecked());
|
||||
QVERIFY(!editor.effects().stroke.value().inside && position[0]->isChecked());
|
||||
position[1]->click();
|
||||
// The swatch opens the picker, whose colour shows at once.
|
||||
auto &swatch = find<QAbstractButton>(sheet, "effectColor");
|
||||
@@ -207,7 +207,7 @@ void EffectsPanelTests::theSheetEditsItsEffect()
|
||||
Painted counter(paints);
|
||||
swatch.installEventFilter(&counter);
|
||||
editor.session.setColorPickerHSB(PickerHSB(PaletteColor{1, 0, 0}));
|
||||
QCOMPARE(editor.effects().stroke->color(), (PaletteColor{1, 0, 0}));
|
||||
QCOMPARE(editor.effects().stroke.value().color(), (PaletteColor{1, 0, 0}));
|
||||
QTRY_VERIFY(paints > 0);
|
||||
// A black rim, a white ring inside, then the colour.
|
||||
const QImage shot = swatch.grab().toImage();
|
||||
@@ -218,7 +218,7 @@ void EffectsPanelTests::theSheetEditsItsEffect()
|
||||
// Return commits, then goes on to OK, keeping everything.
|
||||
typing(size, "7");
|
||||
QTest::keyClick(&size, Qt::Key_Return);
|
||||
QVERIFY(!editor.session.effectsEditing() && editor.effects().stroke->size == 7 && editor.effects().stroke->inside);
|
||||
QVERIFY(!editor.session.effectsEditing() && editor.effects().stroke.value().size == 7 && editor.effects().stroke.value().inside);
|
||||
QTRY_VERIFY(!editor.sheet());
|
||||
}
|
||||
|
||||
@@ -236,11 +236,11 @@ void EffectsPanelTests::aWheelOnTheSliderReplacesTheTyping()
|
||||
Qt::NoScrollPhase, false);
|
||||
QApplication::sendEvent(&slider, &wheel);
|
||||
const double wheeled = 20.0 * (200 - QApplication::wheelScrollLines()) / 1000;
|
||||
QCOMPARE(editor.effects().stroke->size, wheeled);
|
||||
QCOMPARE(editor.effects().stroke.value().size, wheeled);
|
||||
QVERIFY(size.hasFocus() && size.text() == QString::number(std::lround(wheeled)) && !size.isModified());
|
||||
// Return then keeps the slider's value, going on to OK.
|
||||
QTest::keyClick(&size, Qt::Key_Return);
|
||||
QVERIFY(!editor.session.effectsEditing() && editor.effects().stroke->size == wheeled);
|
||||
QVERIFY(!editor.session.effectsEditing() && editor.effects().stroke.value().size == wheeled);
|
||||
}
|
||||
|
||||
void EffectsPanelTests::theSheetKeepsSwiftsMeasures()
|
||||
@@ -355,14 +355,14 @@ void EffectsPanelTests::cancelAndEscapePutTheEffectBack()
|
||||
auto &cancel = find<QPushButton>(*editor.sheet(), "effectsCancel");
|
||||
cancel.setFocus();
|
||||
QTest::keyClick(&cancel, Qt::Key_Return);
|
||||
QVERIFY(editor.effects().shadow->angle == -45 && !editor.session.effectsEditing());
|
||||
QVERIFY(editor.effects().shadow.value().angle == -45 && !editor.session.effectsEditing());
|
||||
// An existing shadow: Cancel puts it back as it was.
|
||||
editor.session.selectEffect(LayerEffectKind::shadow, editor.layer, true);
|
||||
QCOMPARE(find<QLineEdit>(*editor.sheet(), "distanceField").text(), QString("20"));
|
||||
find<QSlider>(*editor.sheet(), "distanceSlider").setValue(400);
|
||||
QCOMPARE(editor.effects().shadow->distance, 40.0);
|
||||
QCOMPARE(editor.effects().shadow.value().distance, 40.0);
|
||||
find<QPushButton>(*editor.sheet(), "effectsCancel").click();
|
||||
QVERIFY(editor.effects().shadow->distance == 20 && !editor.session.effectsEditing());
|
||||
QVERIFY(editor.effects().shadow.value().distance == 20 && !editor.session.effectsEditing());
|
||||
// A new overlay's panel: Escape takes the overlay away again.
|
||||
editor.session.addEffect(LayerEffectKind::colorOverlay);
|
||||
QVERIFY(editor.effects().colorOverlay);
|
||||
|
||||
@@ -92,7 +92,7 @@ void EffectsPreviewCacheTests::aPreviewLandsAfterItsDelayAsTheRendererDrawsIt()
|
||||
QCOMPARE(*landings.count, 1);
|
||||
// No effects, or none shown: nothing, and the entry goes.
|
||||
ImageLayer hidden = layer;
|
||||
hidden.effects->stroke->enabled = false;
|
||||
hidden.effects.value().stroke.value().enabled = false;
|
||||
QVERIFY(!ask(cache, hidden, landings) && !cache.rendered(layer.id));
|
||||
QTest::qWait(150);
|
||||
QVERIFY(!cache.rendered(layer.id));
|
||||
@@ -110,7 +110,7 @@ void EffectsPreviewCacheTests::movingTheLayerKeepsThePreviewUnlessAMaskIsPlaced(
|
||||
EffectsPreviewCache cache;
|
||||
ImageLayer layer = layerOf(filled(20, 10, QColor(0, 0, 255)), outline(3));
|
||||
layer.mask = LayerMask(LayerMask::assetFrom(gray(20, 10, 255)));
|
||||
const QImage mask = layer.mask->enabledImage().value();
|
||||
const QImage mask = layer.mask.value().enabledImage().value();
|
||||
const Landings landings;
|
||||
ask(cache, layer, landings, mask);
|
||||
const qint64 first = landed(cache, layer, landings, 1).image.cacheKey();
|
||||
@@ -149,7 +149,7 @@ void EffectsPreviewCacheTests::settingsKeepTheLastPreviewButNewPixelsDropIt()
|
||||
const EffectsPreviewCache::Result second = landed(cache, layer, landings, 2);
|
||||
QCOMPARE(second.inset, 7.0);
|
||||
// Another kind shown or hidden: the last preview stands.
|
||||
layer.effects->colorOverlay = ColorOverlayEffect();
|
||||
layer.effects.value().colorOverlay = ColorOverlayEffect();
|
||||
QCOMPARE(ask(cache, layer, landings).value().image.cacheKey(), second.image.cacheKey());
|
||||
landed(cache, layer, landings, 3);
|
||||
// New pixels drop it.
|
||||
@@ -158,10 +158,10 @@ void EffectsPreviewCacheTests::settingsKeepTheLastPreviewButNewPixelsDropIt()
|
||||
const qint64 red = landed(cache, layer, landings, 4).image.cacheKey();
|
||||
// A mask added or removed on those pixels keeps it.
|
||||
layer.mask = LayerMask(LayerMask::assetFrom(gray(20, 10, 255)));
|
||||
QCOMPARE(ask(cache, layer, landings, layer.mask->enabledImage()).value().image.cacheKey(), red);
|
||||
QCOMPARE(ask(cache, layer, landings, layer.mask.value().enabledImage()).value().image.cacheKey(), red);
|
||||
QVERIFY(landed(cache, layer, landings, 5).image.cacheKey() != red);
|
||||
// Switched off again, the unmasked effects are known: no render.
|
||||
layer.mask->isEnabled = false;
|
||||
layer.mask.value().isEnabled = false;
|
||||
QCOMPARE(ask(cache, layer, landings).value().image.cacheKey(), red);
|
||||
QTest::qWait(200);
|
||||
QCOMPARE(*landings.count, 5);
|
||||
@@ -325,7 +325,7 @@ void EffectsPreviewCacheTests::preparingDropsLayersWithoutEffectsAndSharesTheBud
|
||||
ask(cache, small, landings);
|
||||
landed(cache, small, landings, 5);
|
||||
ImageLayer off = small;
|
||||
off.effects->stroke->enabled = false;
|
||||
off.effects.value().stroke.value().enabled = false;
|
||||
cache.seed(wide.id, filled(4, 4, Qt::green), placedAt(QPointF(0, 0), QSizeF(4, 4)));
|
||||
cache.prepare({off});
|
||||
QVERIFY(!cache.rendered(small.id) && !cache.rendered(wide.id));
|
||||
@@ -374,7 +374,7 @@ void EffectsPreviewCacheTests::aMaskHidesPixelsInTheReducedPreview()
|
||||
}
|
||||
QVERIFY(low == 125 && high == 129);
|
||||
layer.mask = LayerMask(LayerMask::assetFrom(stripes.scaled(6000, 100)));
|
||||
ask(cache, layer, landings, layer.mask->enabledImage());
|
||||
ask(cache, layer, landings, layer.mask.value().enabledImage());
|
||||
QVERIFY(landed(cache, layer, landings, 4).image == row);
|
||||
}
|
||||
|
||||
@@ -397,13 +397,13 @@ void EffectsPreviewCacheTests::aReducedPreviewScalesEveryLength()
|
||||
// The renderer on the reduced pixels, every length reduced alike.
|
||||
const double factor = 1528.0 / (6000 + 2 * LayerEffectsRenderer::margin(effects));
|
||||
const QImage reduced = image.scaled(1518, 25, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
effects.stroke->size *= factor;
|
||||
effects.shadow->distance *= factor;
|
||||
effects.shadow->blur *= factor;
|
||||
effects.innerShadow->distance *= factor;
|
||||
effects.innerShadow->blur *= factor;
|
||||
effects.outerGlow->size *= factor;
|
||||
effects.innerGlow->size *= factor;
|
||||
effects.stroke.value().size *= factor;
|
||||
effects.shadow.value().distance *= factor;
|
||||
effects.shadow.value().blur *= factor;
|
||||
effects.innerShadow.value().distance *= factor;
|
||||
effects.innerShadow.value().blur *= factor;
|
||||
effects.outerGlow.value().size *= factor;
|
||||
effects.innerGlow.value().size *= factor;
|
||||
QVERIFY(result.image == LayerEffectsRenderer::render(reduced, std::nullopt, effects).image);
|
||||
}
|
||||
|
||||
|
||||
@@ -260,7 +260,7 @@ void FilterSessionTests::aCoveringMaskStaysPutUnderABlur()
|
||||
session->insert(ImportedImage(image, image, QStringLiteral("Quarter")));
|
||||
const QUuid id = session->activeLayerID().value();
|
||||
session->applySelection(rectPath(QRectF(0, 0, 20, 20)), SelectionMode::replace, "Select");
|
||||
session->addMask(false);
|
||||
session->addMask(true);
|
||||
session->selectLayerTarget(id, false);
|
||||
session->beginFilter(FilterKind::gaussianBlur);
|
||||
QVERIFY(committed(*session));
|
||||
|
||||
@@ -276,11 +276,11 @@ void FinishingFilterTests::aPreviewForAnOlderGridShowsOnThatGrid()
|
||||
int older = 0;
|
||||
QObject::connect(&session, &EditorSession::changed, &session, [&] {
|
||||
const std::optional<FilterEdit> &edit = session.filterEdit();
|
||||
if (!edit || !edit->preparedPreview)
|
||||
if (!edit || !edit.value().preparedPreview)
|
||||
return;
|
||||
if (QSizeF(edit->preparedPreview->size()) != edit->preparedTransform.value().size)
|
||||
if (QSizeF(edit.value().preparedPreview.value().size()) != edit.value().preparedTransform.value().size)
|
||||
mismatched = true;
|
||||
older += edit->preparedPreview->size() != edit->previewSource.size();
|
||||
older += edit.value().preparedPreview.value().size() != edit.value().previewSource.size();
|
||||
});
|
||||
session.updateFilter(FilterSettings{.bloomRadius = 5}, true);
|
||||
const LayerTransform grid = session.filterEdit().value().grownTransform.value();
|
||||
@@ -289,7 +289,7 @@ void FinishingFilterTests::aPreviewForAnOlderGridShowsOnThatGrid()
|
||||
QVERIFY(session.filterEdit().value().grownTransform.value().size.width() > grid.size.width());
|
||||
QTRY_VERIFY(session.filterEdit().value().preparedPreview && !session.filterEdit().value().preparing);
|
||||
QVERIFY(!mismatched && older > 0);
|
||||
QCOMPARE(session.filterEdit().value().preparedPreview->size(), session.filterEdit().value().previewSource.size());
|
||||
QCOMPARE(session.filterEdit().value().preparedPreview.value().size(), session.filterEdit().value().previewSource.size());
|
||||
QCOMPARE(session.filterEdit().value().preparedTransform, session.filterEdit().value().grownTransform);
|
||||
// Preview off: nothing waits; the stale render ends it.
|
||||
session.updateFilter(FilterSettings{.bloomRadius = 5}, true);
|
||||
@@ -313,7 +313,7 @@ void FinishingFilterTests::aJobWaitingBehindAnotherEditShowsOnItsGrid()
|
||||
bool mismatched = false, armed = true;
|
||||
QObject::connect(&session, &EditorSession::changed, &session, [&] {
|
||||
const std::optional<FilterEdit> &edit = session.filterEdit();
|
||||
if (edit && edit->preparedPreview && QSizeF(edit->preparedPreview->size()) != edit->preparedTransform.value().size)
|
||||
if (edit && edit.value().preparedPreview && QSizeF(edit.value().preparedPreview.value().size()) != edit.value().preparedTransform.value().size)
|
||||
mismatched = true;
|
||||
// The waiting job starts: Preview comes back next turn.
|
||||
if (std::exchange(armed, false))
|
||||
@@ -322,7 +322,7 @@ void FinishingFilterTests::aJobWaitingBehindAnotherEditShowsOnItsGrid()
|
||||
QTRY_VERIFY(!armed);
|
||||
QTRY_VERIFY(session.filterEdit().value().preparedPreview && !session.filterEdit().value().preparing);
|
||||
QVERIFY(!mismatched);
|
||||
QCOMPARE(session.filterEdit().value().preparedPreview->size(), session.filterEdit().value().previewSource.size());
|
||||
QCOMPARE(session.filterEdit().value().preparedPreview.value().size(), session.filterEdit().value().previewSource.size());
|
||||
}
|
||||
|
||||
void FinishingFilterTests::theVignettePickerPreviewsAndPutsBack()
|
||||
|
||||
+23
-23
@@ -48,8 +48,8 @@ void HistoryTests::undoAndRedoReturnTheRecordedSnapshots()
|
||||
DocumentHistory history;
|
||||
const std::optional<CanvasDocument> before = documentNamed("Before");
|
||||
std::optional<CanvasDocument> after = before;
|
||||
after->layers[0].name = "After";
|
||||
const QUuid selection = before->layers[0].id;
|
||||
after.value().layers[0].name = "After";
|
||||
const QUuid selection = before.value().layers[0].id;
|
||||
history.begin("Rename Layer", before, std::nullopt);
|
||||
history.end(after, selection);
|
||||
QCOMPARE(history.undoCount(), 1);
|
||||
@@ -96,9 +96,9 @@ void HistoryTests::nestedTransactionsRecordOneEntry()
|
||||
history.redo();
|
||||
history.begin("Layer Setup", start, std::nullopt);
|
||||
QVERIFY(!history.canUndo());
|
||||
edited->layers[0].name = "B";
|
||||
edited.value().layers[0].name = "B";
|
||||
history.begin("Inner", edited, std::nullopt);
|
||||
edited->layers[0].name = "C";
|
||||
edited.value().layers[0].name = "C";
|
||||
history.end(edited, std::nullopt);
|
||||
QCOMPARE(history.undoCount(), 1);
|
||||
QVERIFY(!history.canUndo());
|
||||
@@ -114,12 +114,12 @@ void HistoryTests::noOpEditsPreserveRedo()
|
||||
DocumentHistory history;
|
||||
const std::optional<CanvasDocument> start = documentNamed("A");
|
||||
std::optional<CanvasDocument> renamed = start;
|
||||
renamed->layers[0].name = "B";
|
||||
renamed.value().layers[0].name = "B";
|
||||
history.begin("Rename Layer", start, std::nullopt);
|
||||
history.end(renamed, std::nullopt);
|
||||
history.undo();
|
||||
history.begin("Rename Layer", start, std::nullopt);
|
||||
history.end(start, start->layers[0].id);
|
||||
history.end(start, start.value().layers[0].id);
|
||||
QCOMPARE(history.undoCount(), 0);
|
||||
QVERIFY(history.canRedo());
|
||||
QVERIFY(!history.isModified());
|
||||
@@ -203,16 +203,16 @@ void HistoryTests::historyBoundsEntriesAndUniqueRetainedPixels()
|
||||
{
|
||||
DocumentHistory history(2, 0);
|
||||
std::optional<CanvasDocument> doc = CanvasDocument(64, 32);
|
||||
doc->layers.push_back(ImageLayer(asset(64, 32, "fixture"), QPointF(0, 0)));
|
||||
doc.value().layers.push_back(ImageLayer(asset(64, 32, "fixture"), QPointF(0, 0)));
|
||||
for (const QString name : {"A", "B", "C"}) {
|
||||
history.begin("Rename", doc, doc->layers[0].id);
|
||||
doc->layers[0].name = name;
|
||||
history.end(doc, doc->layers[0].id);
|
||||
history.begin("Rename", doc, doc.value().layers[0].id);
|
||||
doc.value().layers[0].name = name;
|
||||
history.end(doc, doc.value().layers[0].id);
|
||||
}
|
||||
QCOMPARE(history.undoCount(), 2);
|
||||
QCOMPARE(history.retainedBytes(doc), 0);
|
||||
history.begin("Delete", doc, doc->layers[0].id);
|
||||
doc->layers.clear();
|
||||
history.begin("Delete", doc, doc.value().layers[0].id);
|
||||
doc.value().layers.clear();
|
||||
history.end(doc, std::nullopt);
|
||||
QCOMPARE(history.undoCount(), 0);
|
||||
QCOMPARE(history.retainedBytes(doc), 0);
|
||||
@@ -222,15 +222,15 @@ void HistoryTests::retainedBytesCountEachHistoryOnlyImageOnce()
|
||||
{
|
||||
DocumentHistory history;
|
||||
std::optional<CanvasDocument> doc = CanvasDocument(64, 32);
|
||||
doc->layers.push_back(ImageLayer(asset(10, 10, "old"), QPointF(0, 0)));
|
||||
doc.value().layers.push_back(ImageLayer(asset(10, 10, "old"), QPointF(0, 0)));
|
||||
for (const QString name : {"A", "B"}) {
|
||||
history.begin("Rename", doc, std::nullopt);
|
||||
doc->layers[0].name = name;
|
||||
doc.value().layers[0].name = name;
|
||||
history.end(doc, std::nullopt);
|
||||
}
|
||||
QCOMPARE(history.retainedBytes(doc), 0);
|
||||
history.begin("Replace", doc, std::nullopt);
|
||||
doc->layers[0].asset = asset(20, 10, "new");
|
||||
doc.value().layers[0].asset = asset(20, 10, "new");
|
||||
history.end(doc, std::nullopt);
|
||||
const qint64 oldBytes = 10 * 10 * 4 + 8 * 8 * 4;
|
||||
QCOMPARE(history.retainedBytes(doc), oldBytes);
|
||||
@@ -242,13 +242,13 @@ void HistoryTests::byteLimitDropsTheOldestPastEntryFirst()
|
||||
const qint64 small = 10 * 10 * 4 + 8 * 8 * 4;
|
||||
DocumentHistory history(100, small);
|
||||
std::optional<CanvasDocument> doc = CanvasDocument(64, 32);
|
||||
doc->layers.push_back(ImageLayer(asset(10, 10, "x"), QPointF(0, 0)));
|
||||
doc.value().layers.push_back(ImageLayer(asset(10, 10, "x"), QPointF(0, 0)));
|
||||
history.begin("First", doc, std::nullopt);
|
||||
doc->layers[0].asset = asset(10, 10, "y");
|
||||
doc.value().layers[0].asset = asset(10, 10, "y");
|
||||
history.end(doc, std::nullopt);
|
||||
QCOMPARE(history.undoCount(), 1);
|
||||
history.begin("Second", doc, std::nullopt);
|
||||
doc->layers[0].asset = asset(10, 10, "z");
|
||||
doc.value().layers[0].asset = asset(10, 10, "z");
|
||||
history.end(doc, std::nullopt);
|
||||
QCOMPARE(history.undoCount(), 1);
|
||||
QCOMPARE(history.undoName(), QString("Second"));
|
||||
@@ -260,10 +260,10 @@ void HistoryTests::undoTrimsRedoWhenItsPixelsExceedTheLimit()
|
||||
const qint64 small = 10 * 10 * 4 + 8 * 8 * 4;
|
||||
DocumentHistory history(100, small);
|
||||
std::optional<CanvasDocument> doc = CanvasDocument(64, 32);
|
||||
doc->layers.push_back(ImageLayer(asset(10, 10, "small"), QPointF(0, 0)));
|
||||
doc.value().layers.push_back(ImageLayer(asset(10, 10, "small"), QPointF(0, 0)));
|
||||
const std::optional<CanvasDocument> before = doc;
|
||||
history.begin("Enlarge", doc, std::nullopt);
|
||||
doc->layers[0].asset = asset(40, 40, "large");
|
||||
doc.value().layers[0].asset = asset(40, 40, "large");
|
||||
history.end(doc, std::nullopt);
|
||||
QCOMPARE(history.undoCount(), 1);
|
||||
const DocumentHistory::Snapshot undone = history.undo().value();
|
||||
@@ -277,12 +277,12 @@ void HistoryTests::undoTrimsThePastBeforeTheRedoStack()
|
||||
const qint64 small = 10 * 10 * 4 + 8 * 8 * 4;
|
||||
DocumentHistory history(100, 2 * small);
|
||||
std::optional<CanvasDocument> doc = CanvasDocument(64, 32);
|
||||
doc->layers.push_back(ImageLayer(asset(10, 10, "a"), QPointF(0, 0)));
|
||||
doc.value().layers.push_back(ImageLayer(asset(10, 10, "a"), QPointF(0, 0)));
|
||||
history.begin("First", doc, std::nullopt);
|
||||
doc->layers[0].asset = asset(10, 10, "b");
|
||||
doc.value().layers[0].asset = asset(10, 10, "b");
|
||||
history.end(doc, std::nullopt);
|
||||
history.begin("Second", doc, std::nullopt);
|
||||
doc->layers[0].asset = asset(15, 15, "c");
|
||||
doc.value().layers[0].asset = asset(15, 15, "c");
|
||||
history.end(doc, std::nullopt);
|
||||
QCOMPARE(history.undoCount(), 2);
|
||||
history.undo();
|
||||
|
||||
@@ -244,7 +244,7 @@ void ImageAdjustmentTests::newAdjustmentLayersStartFromThePaletteRenderAndEditIn
|
||||
session.setAdjustmentEditingID(std::nullopt);
|
||||
session.addAdjustment(AdjustmentKind::grain);
|
||||
const std::vector<ImageLayer> &layers = session.document().value().layers;
|
||||
QCOMPARE(std::count_if(layers.begin(), layers.end(), [](const ImageLayer &layer) { return layer.adjustment && layer.adjustment->kind == AdjustmentKind::grain; }), 2);
|
||||
QCOMPARE(std::count_if(layers.begin(), layers.end(), [](const ImageLayer &layer) { return layer.adjustment && layer.adjustment.value().kind == AdjustmentKind::grain; }), 2);
|
||||
QVERIFY2(session.activeLayer().value().adjustment.value().grain().seed != first, "each Grain layer gets its own pattern");
|
||||
QVERIFY(std::find(allAdjustmentKinds.begin(), allAdjustmentKinds.end(), AdjustmentKind::exposure) != allAdjustmentKinds.end()
|
||||
&& filterKind(AdjustmentKind::exposure) == FilterKind::exposure);
|
||||
|
||||
@@ -135,7 +135,7 @@ void ImageFileDropTests::aPictureIsCopiedToAFileFirst()
|
||||
sky.image = PSDFixture::colorImage(4, 2, 0, 0, 1);
|
||||
sky.bounds = QRectF(0, 0, 4, 2);
|
||||
QMimeData photoshop;
|
||||
photoshop.setData("image/vnd.adobe.photoshop", PSDFixture::data(PSDDocument{4, 2, 72, {sky}}, *sky.image));
|
||||
photoshop.setData("image/vnd.adobe.photoshop", PSDFixture::data(PSDDocument{4, 2, 72, {sky}}, sky.image.value()));
|
||||
photoshop.setData("application/x-other", "ignored");
|
||||
QCOMPARE(ImageFileDrop::providers(photoshop).front()->formats(), QStringList{"image/vnd.adobe.photoshop"});
|
||||
photoshop.setData("image/tiff", "tiff first");
|
||||
@@ -165,7 +165,7 @@ void ImageFileDropTests::aPictureIsCopiedToAFileFirst()
|
||||
done = false;
|
||||
ImageFileDrop::importProviders(raw, developed, std::nullopt, nullptr, std::nullopt, [&done] { done = true; });
|
||||
QTRY_VERIFY(developed.rawDevelop());
|
||||
developed.finishRawDevelop(developed.rawDevelop()->settings);
|
||||
developed.finishRawDevelop(developed.rawDevelop().value().settings);
|
||||
QTRY_VERIFY(done);
|
||||
QCOMPARE(names(developed), QStringList{"Dropped"});
|
||||
QCOMPARE(takeCopy(before), QString("Dropped.dng"));
|
||||
|
||||
@@ -222,7 +222,7 @@ void ImageTrimTests::theSheetSaysSwiftsWordsAndReturnsItsChoice()
|
||||
ok.click();
|
||||
QVERIFY(answer.value() == edges(TrimBasedOn::bottomRightPixelColor, false, true, false, false));
|
||||
sheet.findChild<QPushButton *>("trimCancel")->click();
|
||||
QVERIFY(answer && !answer->has_value());
|
||||
QVERIFY(answer && !answer.value().has_value());
|
||||
}
|
||||
|
||||
void ImageTrimTests::theControllerTrimsThroughTheSheetAsOneStep()
|
||||
|
||||
@@ -77,7 +77,7 @@ void InnerGlowTests::innerGlowCodableRoundTrip()
|
||||
QCOMPARE(ManifestJson::effects(encoded), original);
|
||||
// Absent `enabled` stays absent, read as shown.
|
||||
LayerEffects shown = original;
|
||||
shown.innerGlow->enabled = std::nullopt;
|
||||
shown.innerGlow.value().enabled = std::nullopt;
|
||||
QVERIFY(!ManifestJson::encoded(shown).value("innerGlow").toObject().contains("enabled"));
|
||||
QCOMPARE(ManifestJson::effects(ManifestJson::encoded(shown)), shown);
|
||||
// A key of the wrong type, or missing, refuses it.
|
||||
@@ -165,10 +165,10 @@ void InnerGlowTests::itSitsOverTheOverlayAndUnderTheInnerShadow()
|
||||
const LayerEffectsRenderer::Rendered shadowed = LayerEffectsRenderer::render(square(40, Qt::black), std::nullopt, effects);
|
||||
QCOMPARE(at(shadowed, 0, 20), at(covered, 0, 20));
|
||||
// A sideways shadow covers the glow on one edge.
|
||||
effects.innerShadow->distance = 4;
|
||||
const int x = effects.innerShadow->offset().width() > 0 ? 0 : 39;
|
||||
effects.innerShadow.value().distance = 4;
|
||||
const int x = effects.innerShadow.value().offset().width() > 0 ? 0 : 39;
|
||||
const QColor edge = at(LayerEffectsRenderer::render(square(40, Qt::black), std::nullopt, effects), x, 20);
|
||||
QVERIFY(std::abs(effects.innerShadow->offset().width()) > 3.9);
|
||||
QVERIFY(std::abs(effects.innerShadow.value().offset().width()) > 3.9);
|
||||
QVERIFY(edge.redF() > 0.9 && edge.greenF() < 0.1);
|
||||
}
|
||||
|
||||
@@ -195,11 +195,11 @@ void InnerGlowTests::theSessionAddsCopiesAndCancelsIt()
|
||||
session.addEffect(LayerEffectKind::innerGlow);
|
||||
QCOMPARE(session.history.undoName(), QString("Add Inner Glow"));
|
||||
QVERIFY(session.activeEffects().innerGlow == InnerGlowEffect());
|
||||
session.changeEffects([](LayerEffects &effects) { effects.innerGlow->size = 44; });
|
||||
session.changeEffects([](LayerEffects &effects) { effects.innerGlow.value().size = 44; });
|
||||
session.finishEffectsEditing(false);
|
||||
QVERIFY(!session.activeEffects().innerGlow);
|
||||
session.addEffect(LayerEffectKind::innerGlow);
|
||||
session.changeEffects([](LayerEffects &effects) { effects.innerGlow->size = 44; });
|
||||
session.changeEffects([](LayerEffects &effects) { effects.innerGlow.value().size = 44; });
|
||||
session.finishEffectsEditing(true);
|
||||
// Adding it again keeps the glow it has.
|
||||
session.addEffect(LayerEffectKind::innerGlow);
|
||||
|
||||
@@ -132,14 +132,14 @@ void LayerAdjustmentTests::validityFollowsSwiftsBounds()
|
||||
QCOMPARE((LayerAdjustment{.kind = AdjustmentKind::hsv, .hue = hue, .saturation = saturation, .lightness = lightness}.isValid()), valid);
|
||||
// Each range's values too, and every band's handles finite.
|
||||
LayerAdjustment ranged = full();
|
||||
ranged.hsvSettings->adjustments[ColorRange::blues] = {0, 0, 101};
|
||||
ranged.hsvSettings.value().adjustments[ColorRange::blues] = {0, 0, 101};
|
||||
QVERIFY(!ranged.isValid());
|
||||
ranged = full();
|
||||
ranged.hsvSettings->bands[ColorRange::cyans].falloffEnd = std::numeric_limits<double>::infinity();
|
||||
ranged.hsvSettings.value().bands[ColorRange::cyans].falloffEnd = std::numeric_limits<double>::infinity();
|
||||
QVERIFY(!ranged.isValid());
|
||||
// The legacy fields count only when no range settings exist.
|
||||
ranged = full();
|
||||
ranged.hsvSettings->adjustments[ColorRange::reds] = {-360, -100, -100};
|
||||
ranged.hsvSettings.value().adjustments[ColorRange::reds] = {-360, -100, -100};
|
||||
QVERIFY(ranged.isValid());
|
||||
// Levels as normalized, then every kind's own rules.
|
||||
LayerAdjustment levels{AdjustmentKind::hsv};
|
||||
@@ -308,7 +308,7 @@ void LayerAdjustmentTests::theStoreKeepsAdjustmentsToVersion7WithoutPixels()
|
||||
blank.isGroup = true;
|
||||
QCOMPARE(stored(snapshot), ProjectError::Kind::invalid);
|
||||
blank.isGroup = std::nullopt;
|
||||
blank.adjustment->hue = 400;
|
||||
blank.adjustment.value().hue = 400;
|
||||
QCOMPARE(stored(snapshot), ProjectError::Kind::invalid);
|
||||
}
|
||||
|
||||
@@ -440,7 +440,7 @@ void LayerAdjustmentTests::adjustmentsTravelWithTheirLayers()
|
||||
session->duplicateActiveLayer();
|
||||
QVERIFY(session->activeLayer().value().adjustment == full());
|
||||
ImageLayer changed = session->activeLayer().value();
|
||||
changed.adjustment->hue = 11;
|
||||
changed.adjustment.value().hue = 11;
|
||||
QVERIFY(!(changed == session->activeLayer().value()));
|
||||
}
|
||||
|
||||
|
||||
@@ -237,15 +237,15 @@ void LayerEffectRowTests::theEyeShowsOrHidesItsEffect()
|
||||
LayerEffectRow &stroke = *listed.effects(0)[0];
|
||||
// One step each way; the choice stays as it was.
|
||||
listed.click(listed.eye(stroke), QPoint(10, 11));
|
||||
QVERIFY(!layerWith(listed.session, top).effects->stroke->isEnabled() && listed.session.history.undoName() == "Hide Stroke");
|
||||
QVERIFY(!layerWith(listed.session, top).effects.value().stroke.value().isEnabled() && listed.session.history.undoName() == "Hide Stroke");
|
||||
QVERIFY(!listed.chosen() && listed.eye(stroke).accessibleName() == "Show Stroke");
|
||||
listed.click(listed.eye(stroke), QPoint(10, 11));
|
||||
QVERIFY(layerWith(listed.session, top).effects->stroke->isEnabled() && listed.session.history.undoName() == "Show Stroke");
|
||||
QVERIFY(layerWith(listed.session, top).effects.value().stroke.value().isEnabled() && listed.session.history.undoName() == "Show Stroke");
|
||||
// It rests while layers cannot be edited.
|
||||
listed.session.setIsProjectBusy(true);
|
||||
QVERIFY(!listed.eye(stroke).isEnabled());
|
||||
listed.click(listed.eye(stroke), QPoint(10, 11));
|
||||
QVERIFY(layerWith(listed.session, top).effects->stroke->isEnabled() && !listed.chosen());
|
||||
QVERIFY(layerWith(listed.session, top).effects.value().stroke.value().isEnabled() && !listed.chosen());
|
||||
listed.session.setIsProjectBusy(false);
|
||||
QVERIFY(listed.eye(stroke).isEnabled());
|
||||
}
|
||||
@@ -270,14 +270,14 @@ void LayerEffectRowTests::pressesFollowSwiftsOrder()
|
||||
listed.session.dropEffectSelection();
|
||||
listed.press(listed.eye(shadow), QPoint(10, 5), Qt::AltModifier);
|
||||
listed.release(listed.eye(shadow), QPoint(10, 5), Qt::AltModifier);
|
||||
QVERIFY(layerWith(listed.session, top).effects->shadow->isEnabled() && listed.chosen() == (LayerEffectSelection{top, LayerEffectKind::shadow}));
|
||||
QVERIFY(layerWith(listed.session, top).effects.value().shadow.value().isEnabled() && listed.chosen() == (LayerEffectSelection{top, LayerEffectKind::shadow}));
|
||||
// An Alt double click on the eye hides nothing either.
|
||||
listed.session.dropEffectSelection();
|
||||
QTest::mouseDClick(listed.list.windowHandle(), Qt::LeftButton, Qt::AltModifier, listed.at(listed.eye(shadow), QPoint(12, 5)));
|
||||
QVERIFY(layerWith(listed.session, top).effects->shadow->isEnabled() && listed.chosen() == (LayerEffectSelection{top, LayerEffectKind::shadow}));
|
||||
QVERIFY(layerWith(listed.session, top).effects.value().shadow.value().isEnabled() && listed.chosen() == (LayerEffectSelection{top, LayerEffectKind::shadow}));
|
||||
// On the strip, the eye's Alt press clips too.
|
||||
listed.click(listed.eye(shadow), QPoint(10, 20), Qt::AltModifier);
|
||||
QVERIFY(!layerWith(listed.session, top).maskSourceID && !listed.chosen() && layerWith(listed.session, top).effects->shadow->isEnabled());
|
||||
QVERIFY(!layerWith(listed.session, top).maskSourceID && !listed.chosen() && layerWith(listed.session, top).effects.value().shadow.value().isEnabled());
|
||||
// Its own row's press lets go, keeping the layer.
|
||||
listed.session.selectEffect(LayerEffectKind::stroke, top);
|
||||
listed.click(listed.cell(0), QPoint(listed.cell(0).width() - 20, 20));
|
||||
@@ -295,7 +295,7 @@ void LayerEffectRowTests::pressesFollowSwiftsOrder()
|
||||
QVERIFY(listed.chosen() == (LayerEffectSelection{top, LayerEffectKind::shadow}));
|
||||
listed.release(shadow, QPoint(150, 5), Qt::AltModifier | Qt::ControlModifier);
|
||||
listed.click(listed.eye(shadow), QPoint(10, 5), Qt::AltModifier | Qt::ControlModifier);
|
||||
QVERIFY(!layerWith(listed.session, top).effects->shadow->isEnabled());
|
||||
QVERIFY(!layerWith(listed.session, top).effects.value().shadow.value().isEnabled());
|
||||
// A plain press on another effect chooses it at once.
|
||||
listed.press(stroke, QPoint(150, 12));
|
||||
QVERIFY(listed.chosen() == (LayerEffectSelection{top, LayerEffectKind::stroke}));
|
||||
@@ -412,7 +412,7 @@ void LayerEffectRowTests::effectDropsLandOnTheRowUnderThePointer()
|
||||
const std::unique_ptr<QMimeData> probe(effectDrag(listed.list, payload));
|
||||
QCOMPARE(listed.list.dropTarget(*probe, Qt::CopyAction, onMiddle).value(), (LayerDropTarget{.row = 1, .onRow = true, .atBottom = false, .copying = true}));
|
||||
QVERIFY(listed.drop(effectDrag(listed.list, payload), onMiddle));
|
||||
QVERIFY(listed.lastAction == Qt::CopyAction && layerWith(listed.session, middle).effects->stroke->size == 12);
|
||||
QVERIFY(listed.lastAction == Qt::CopyAction && layerWith(listed.session, middle).effects.value().stroke.value().size == 12);
|
||||
QVERIFY(listed.session.history.undoName() == "Copy Stroke" && listed.chosen() == (LayerEffectSelection{middle, LayerEffectKind::stroke}));
|
||||
// Refused: its own row, a missing kind, strangers, bad words.
|
||||
QVERIFY(!listed.drop(effectDrag(listed.list, payload), onTop));
|
||||
@@ -448,7 +448,7 @@ void LayerEffectRowTests::aRowRebuiltUnderItsOwnPressLivesOn()
|
||||
QTest::mouseDClick(shadow.data(), Qt::LeftButton, Qt::NoModifier, QPoint(150, 12));
|
||||
QVERIFY(shadow && !shadow->isVisible());
|
||||
QVERIFY(listed.session.effectsEditing() == (LayerEffectSelection{top, LayerEffectKind::shadow}) && listed.list.hasFocus());
|
||||
QVERIFY(!layerWith(listed.session, top).effects->stroke && listed.effects(0).size() == 1);
|
||||
QVERIFY(!layerWith(listed.session, top).effects.value().stroke && listed.effects(0).size() == 1);
|
||||
QTRY_VERIFY(!shadow);
|
||||
}
|
||||
|
||||
|
||||
@@ -86,32 +86,32 @@ QImage replayed(const QImage &pixels, const LayerEffects &effects)
|
||||
for (int x = 0; x < pixels.width(); ++x)
|
||||
shader.shape.push_back(float(pixels.constScanLine(y)[x * 4 + 3]) / 255.f);
|
||||
}
|
||||
const auto on = [](const auto &effect) { return effect && effect->isEnabled() && effect->opacity > 0; };
|
||||
const bool stroke = on(effects.stroke) && effects.stroke->size > 0;
|
||||
const auto on = [](const auto &effect) { return effect && effect.value().isEnabled() && effect.value().opacity > 0; };
|
||||
const bool stroke = on(effects.stroke) && effects.stroke.value().size > 0;
|
||||
std::vector<float> ring(shader.shape.size()), cast(shader.shape.size()), inner(shader.shape.size()), glow(shader.shape.size()),
|
||||
innerGlow(shader.shape.size());
|
||||
if (stroke) {
|
||||
const int reach = std::max(1, int(std::lround(effects.stroke->size)));
|
||||
const std::vector<float> moved = shader.spread(shader.spread(shader.shape, reach, effects.stroke->inside, true), reach, effects.stroke->inside, false);
|
||||
const int reach = std::max(1, int(std::lround(effects.stroke.value().size)));
|
||||
const std::vector<float> moved = shader.spread(shader.spread(shader.shape, reach, effects.stroke.value().inside, true), reach, effects.stroke.value().inside, false);
|
||||
for (size_t index = 0; index < ring.size(); ++index)
|
||||
ring[index] = std::clamp(effects.stroke->inside ? shader.shape[index] - moved[index] : moved[index] - shader.shape[index], 0.f, 1.f);
|
||||
ring[index] = std::clamp(effects.stroke.value().inside ? shader.shape[index] - moved[index] : moved[index] - shader.shape[index], 0.f, 1.f);
|
||||
}
|
||||
if (on(effects.shadow))
|
||||
cast = shader.softened(shader.shift(shader.shape, float(effects.shadow->offset().width()), float(effects.shadow->offset().height())),
|
||||
float(effects.shadow->blur / 2));
|
||||
const bool glowing = on(effects.outerGlow) && effects.outerGlow->size > 0;
|
||||
cast = shader.softened(shader.shift(shader.shape, float(effects.shadow.value().offset().width()), float(effects.shadow.value().offset().height())),
|
||||
float(effects.shadow.value().blur / 2));
|
||||
const bool glowing = on(effects.outerGlow) && effects.outerGlow.value().size > 0;
|
||||
if (glowing)
|
||||
glow = shader.softened(shader.shape, float(effects.outerGlow->size / 2));
|
||||
const bool glowingInside = on(effects.innerGlow) && effects.innerGlow->size > 0;
|
||||
glow = shader.softened(shader.shape, float(effects.outerGlow.value().size / 2));
|
||||
const bool glowingInside = on(effects.innerGlow) && effects.innerGlow.value().size > 0;
|
||||
if (glowingInside) {
|
||||
const std::vector<float> softened = shader.softened(shader.shape, float(effects.innerGlow->size / 2));
|
||||
const std::vector<float> softened = shader.softened(shader.shape, float(effects.innerGlow.value().size / 2));
|
||||
for (size_t index = 0; index < innerGlow.size(); ++index)
|
||||
innerGlow[index] = std::clamp(shader.shape[index] * (1.f - softened[index]), 0.f, 1.f);
|
||||
}
|
||||
if (on(effects.innerShadow)) {
|
||||
const std::vector<float> moved = shader.softened(shader.shift(shader.shape, float(effects.innerShadow->offset().width()),
|
||||
float(effects.innerShadow->offset().height())),
|
||||
float(effects.innerShadow->blur / 2));
|
||||
const std::vector<float> moved = shader.softened(shader.shift(shader.shape, float(effects.innerShadow.value().offset().width()),
|
||||
float(effects.innerShadow.value().offset().height())),
|
||||
float(effects.innerShadow.value().blur / 2));
|
||||
for (size_t index = 0; index < inner.size(); ++index)
|
||||
inner[index] = std::clamp(shader.shape[index] * (1.f - moved[index]), 0.f, 1.f);
|
||||
}
|
||||
@@ -127,25 +127,25 @@ QImage replayed(const QImage &pixels, const LayerEffects &effects)
|
||||
alpha = coverage + alpha * (1.f - coverage);
|
||||
};
|
||||
if (on(effects.shadow))
|
||||
blend(effects.shadow->color(), std::clamp(cast[index] * float(effects.shadow->opacity), 0.f, 1.f));
|
||||
blend(effects.shadow.value().color(), std::clamp(cast[index] * float(effects.shadow.value().opacity), 0.f, 1.f));
|
||||
if (glowing)
|
||||
blend(effects.outerGlow->color(), std::clamp(glow[index] * (1.f - shader.shape[index]) * float(effects.outerGlow->opacity), 0.f, 1.f));
|
||||
const float strokeCoverage = stroke ? std::clamp(ring[index] * float(effects.stroke->opacity), 0.f, 1.f) : 0.f;
|
||||
if (stroke && !effects.stroke->inside)
|
||||
blend(effects.stroke->color(), strokeCoverage);
|
||||
blend(effects.outerGlow.value().color(), std::clamp(glow[index] * (1.f - shader.shape[index]) * float(effects.outerGlow.value().opacity), 0.f, 1.f));
|
||||
const float strokeCoverage = stroke ? std::clamp(ring[index] * float(effects.stroke.value().opacity), 0.f, 1.f) : 0.f;
|
||||
if (stroke && !effects.stroke.value().inside)
|
||||
blend(effects.stroke.value().color(), strokeCoverage);
|
||||
const uchar *source = pixels.constScanLine(y) + x * 4;
|
||||
const float sourceAlpha = float(source[3]) / 255.f;
|
||||
for (int channel = 0; channel < 3; ++channel)
|
||||
colour[channel] = float(source[channel]) / 255.f + colour[channel] * (1.f - sourceAlpha);
|
||||
alpha = sourceAlpha + alpha * (1.f - sourceAlpha);
|
||||
if (on(effects.colorOverlay))
|
||||
blend(effects.colorOverlay->color(), std::clamp(shader.shape[index] * float(effects.colorOverlay->opacity), 0.f, 1.f));
|
||||
blend(effects.colorOverlay.value().color(), std::clamp(shader.shape[index] * float(effects.colorOverlay.value().opacity), 0.f, 1.f));
|
||||
if (glowingInside)
|
||||
blend(effects.innerGlow->color(), std::clamp(innerGlow[index] * float(effects.innerGlow->opacity), 0.f, 1.f));
|
||||
blend(effects.innerGlow.value().color(), std::clamp(innerGlow[index] * float(effects.innerGlow.value().opacity), 0.f, 1.f));
|
||||
if (on(effects.innerShadow))
|
||||
blend(effects.innerShadow->color(), std::clamp(inner[index] * float(effects.innerShadow->opacity), 0.f, 1.f));
|
||||
if (stroke && effects.stroke->inside)
|
||||
blend(effects.stroke->color(), strokeCoverage);
|
||||
blend(effects.innerShadow.value().color(), std::clamp(inner[index] * float(effects.innerShadow.value().opacity), 0.f, 1.f));
|
||||
if (stroke && effects.stroke.value().inside)
|
||||
blend(effects.stroke.value().color(), strokeCoverage);
|
||||
uchar *out = result.scanLine(y) + x * 4;
|
||||
for (int channel = 0; channel < 3; ++channel)
|
||||
out[channel] = uchar(std::clamp(colour[channel], 0.f, 1.f) * 255.f + 0.5f);
|
||||
@@ -219,7 +219,7 @@ void LayerEffectsKernelTests::everyEffectMatchesTheShader_data()
|
||||
all.innerShadow = inner.innerShadow;
|
||||
all.outerGlow = glow.outerGlow;
|
||||
all.innerGlow = innerGlow.innerGlow;
|
||||
all.shadow->blur = 0;
|
||||
all.shadow.value().blur = 0;
|
||||
wide.stroke = StrokeEffect{.size = 9, .red = 1, .opacity = 1};
|
||||
wide.shadow = ShadowEffect{.angle = 200, .distance = 0.5, .blur = 0.01, .opacity = 1};
|
||||
QTest::newRow("outside stroke") << outside;
|
||||
|
||||
@@ -60,11 +60,11 @@ void LayerEffectsRendererTests::theMarginHoldsWhatReachesOut()
|
||||
LayerEffects cast = stroke(10);
|
||||
cast.shadow = ShadowEffect{.distance = 5, .blur = 2.5};
|
||||
QCOMPARE(LayerEffectsRenderer::margin(cast), 15.0);
|
||||
cast.shadow->blur = 4;
|
||||
cast.shadow.value().blur = 4;
|
||||
QCOMPARE(LayerEffectsRenderer::margin(cast), 19.0);
|
||||
// Switched off, it takes no room.
|
||||
cast.shadow->enabled = false;
|
||||
cast.stroke->enabled = false;
|
||||
cast.shadow.value().enabled = false;
|
||||
cast.stroke.value().enabled = false;
|
||||
QCOMPARE(LayerEffectsRenderer::margin(cast), 2.0);
|
||||
}
|
||||
|
||||
@@ -113,11 +113,11 @@ void LayerEffectsRendererTests::renderingPadsMasksThenRunsTheKernel()
|
||||
QVERIFY(pixel(soft, 2, 10)[3] == 0 && pixel(soft, 65, 10)[3] == 255);
|
||||
// Switched-off effects are none; invalid or vast ones refuse.
|
||||
LayerEffects hidden = stroke(3);
|
||||
hidden.stroke->enabled = false;
|
||||
hidden.stroke.value().enabled = false;
|
||||
QCOMPARE(LayerEffectsRenderer::render(image, std::nullopt, hidden).image.size(), QSize(24, 14));
|
||||
// A switched-off effect is not judged.
|
||||
LayerEffects wild = stroke(600);
|
||||
wild.stroke->enabled = false;
|
||||
wild.stroke.value().enabled = false;
|
||||
QVERIFY(LayerEffectsRenderer::render(image, std::nullopt, wild).image == LayerEffectsRenderer::render(image, std::nullopt, hidden).image);
|
||||
try {
|
||||
LayerEffectsRenderer::render(image, std::nullopt, stroke(501));
|
||||
@@ -142,7 +142,7 @@ void LayerEffectsRendererTests::theCacheKeepsEightWithinItsBudget()
|
||||
QVERIFY(!LayerEffectsRenderer::cached(image, std::nullopt, std::nullopt));
|
||||
QVERIFY(!LayerEffectsRenderer::cached(image, std::nullopt, LayerEffects()));
|
||||
LayerEffects hidden = stroke(2);
|
||||
hidden.stroke->enabled = false;
|
||||
hidden.stroke.value().enabled = false;
|
||||
QVERIFY(!LayerEffectsRenderer::cached(image, std::nullopt, hidden));
|
||||
QVERIFY(!LayerEffectsRenderer::cached(image, std::nullopt, stroke(600)));
|
||||
// The same pixels, mask and effects: the same result.
|
||||
|
||||
@@ -83,7 +83,7 @@ void LayerEffectsSessionTests::addingAnEffectTakesItsDefaultsAndOpensItsPanel()
|
||||
present.shadow = ShadowEffect{.distance = 33};
|
||||
session.setEffects(present);
|
||||
session.addEffect(LayerEffectKind::shadow);
|
||||
QVERIFY(two.effects(two.top).value().shadow->distance == 33);
|
||||
QVERIFY(two.effects(two.top).value().shadow.value().distance == 33);
|
||||
session.finishEffectsEditing(true);
|
||||
const int kept = session.history.undoCount();
|
||||
session.addEffect(LayerEffectKind::colorOverlay);
|
||||
@@ -128,15 +128,15 @@ void LayerEffectsSessionTests::cancelPutsBackThisPanelsEffectAlone()
|
||||
session.setEffects(start);
|
||||
session.selectEffect(LayerEffectKind::stroke, two.top, true);
|
||||
QVERIFY(session.effectsEditingOriginal() == start);
|
||||
session.changeEffects([](LayerEffects &effects) { effects.stroke->size = 9; });
|
||||
session.changeEffects([](LayerEffects &effects) { effects.stroke.value().size = 9; });
|
||||
QCOMPARE(session.history.undoName(), QString("Edit Stroke"));
|
||||
// Another effect changed meanwhile keeps its change.
|
||||
LayerEffects other = two.effects(two.top).value();
|
||||
other.shadow->distance = 30;
|
||||
other.shadow.value().distance = 30;
|
||||
session.setEffects(other);
|
||||
session.finishEffectsEditing(false);
|
||||
const LayerEffects after = two.effects(two.top).value();
|
||||
QVERIFY(after.stroke->size == 3 && after.shadow->distance == 30);
|
||||
QVERIFY(after.stroke.value().size == 3 && after.shadow.value().distance == 30);
|
||||
QCOMPARE(session.history.undoName(), QString("Cancel Stroke"));
|
||||
QVERIFY(!session.effectsEditing() && !session.effectsEditingOriginal());
|
||||
// The chosen effect stays chosen while its layer shows it.
|
||||
@@ -147,26 +147,26 @@ void LayerEffectsSessionTests::cancelPutsBackThisPanelsEffectAlone()
|
||||
QVERIFY(!two.effects(two.top).value().innerShadow);
|
||||
QVERIFY(!session.effectSelection());
|
||||
session.selectEffect(LayerEffectKind::shadow, two.top, true);
|
||||
session.changeEffects([](LayerEffects &effects) { effects.shadow->blur = 2; });
|
||||
session.changeEffects([](LayerEffects &effects) { effects.shadow.value().blur = 2; });
|
||||
const int steps = session.history.undoCount();
|
||||
session.finishEffectsEditing(true);
|
||||
QVERIFY(session.history.undoCount() == steps && two.effects(two.top).value().shadow->blur == 2);
|
||||
QVERIFY(session.history.undoCount() == steps && two.effects(two.top).value().shadow.value().blur == 2);
|
||||
// Nothing open, nothing to finish.
|
||||
session.finishEffectsEditing(false);
|
||||
QCOMPARE(session.history.undoCount(), steps);
|
||||
// Opening another panel cancels the open one's changes.
|
||||
session.selectEffect(LayerEffectKind::stroke, two.top, true);
|
||||
session.changeEffects([](LayerEffects &effects) { effects.stroke->size = 40; });
|
||||
session.changeEffects([](LayerEffects &effects) { effects.stroke.value().size = 40; });
|
||||
session.selectEffect(LayerEffectKind::shadow, two.top, true);
|
||||
QVERIFY(two.effects(two.top).value().stroke->size == 3 && session.history.undoName() == QString("Cancel Stroke"));
|
||||
QVERIFY(two.effects(two.top).value().stroke.value().size == 3 && session.history.undoName() == QString("Cancel Stroke"));
|
||||
session.finishEffectsEditing(true);
|
||||
// An overlay's Cancel puts back the overlay alone.
|
||||
session.addEffect(LayerEffectKind::colorOverlay);
|
||||
session.finishEffectsEditing(true);
|
||||
session.selectEffect(LayerEffectKind::colorOverlay, two.top, true);
|
||||
session.changeEffects([](LayerEffects &effects) { effects.colorOverlay->opacity = 0.25; });
|
||||
session.changeEffects([](LayerEffects &effects) { effects.colorOverlay.value().opacity = 0.25; });
|
||||
session.finishEffectsEditing(false);
|
||||
QCOMPARE(two.effects(two.top).value().colorOverlay->opacity, 1.0);
|
||||
QCOMPARE(two.effects(two.top).value().colorOverlay.value().opacity, 1.0);
|
||||
// Its layer's effects all gone, Cancel has nothing to restore.
|
||||
session.selectEffect(LayerEffectKind::shadow, two.top, true);
|
||||
session.setEffects(LayerEffects(), two.top);
|
||||
@@ -195,7 +195,7 @@ void LayerEffectsSessionTests::setEffectsIsOneStepOnAValidChange()
|
||||
// The same again, an invalid one, a missing layer: nothing.
|
||||
session.setEffects(effects, two.bottom);
|
||||
LayerEffects wild = effects;
|
||||
wild.stroke->size = 501;
|
||||
wild.stroke.value().size = 501;
|
||||
session.setEffects(wild, two.bottom);
|
||||
session.setEffects(effects, QUuid::createUuid());
|
||||
QCOMPARE(session.history.undoCount(), steps + 1);
|
||||
@@ -220,18 +220,18 @@ void LayerEffectsSessionTests::panelEditsStayWithTheirLayer()
|
||||
EditorSession &session = two.session;
|
||||
session.addEffect(LayerEffectKind::stroke);
|
||||
session.selectLayer(two.bottom);
|
||||
session.changeEffects([](LayerEffects &effects) { effects.stroke->size = 12; });
|
||||
QVERIFY(two.effects(two.top).value().stroke->size == 12 && !two.effects(two.bottom));
|
||||
session.changeEffects([](LayerEffects &effects) { effects.stroke.value().size = 12; });
|
||||
QVERIFY(two.effects(two.top).value().stroke.value().size == 12 && !two.effects(two.bottom));
|
||||
// Once its effect is gone, edits have nowhere to go.
|
||||
LayerEffects without = two.effects(two.top).value();
|
||||
without.stroke.reset();
|
||||
without.shadow = ShadowEffect();
|
||||
session.setEffects(without, two.top);
|
||||
const int steps = session.history.undoCount();
|
||||
session.changeEffects([](LayerEffects &effects) { effects.shadow->blur = 1; });
|
||||
session.changeEffects([](LayerEffects &effects) { effects.shadow.value().blur = 1; });
|
||||
QCOMPARE(session.history.undoCount(), steps);
|
||||
session.finishEffectsEditing(true);
|
||||
session.changeEffects([](LayerEffects &effects) { effects.shadow->blur = 1; });
|
||||
session.changeEffects([](LayerEffects &effects) { effects.shadow.value().blur = 1; });
|
||||
QCOMPARE(session.history.undoCount(), steps);
|
||||
}
|
||||
|
||||
@@ -251,7 +251,7 @@ void LayerEffectsSessionTests::copyingAnEffectClosesTheTargetsPanelFirst()
|
||||
target.stroke = StrokeEffect();
|
||||
session.setEffects(target, two.bottom);
|
||||
session.selectEffect(LayerEffectKind::shadow, two.bottom, true);
|
||||
session.changeEffects([](LayerEffects &effects) { effects.shadow->blur = 7; });
|
||||
session.changeEffects([](LayerEffects &effects) { effects.shadow.value().blur = 7; });
|
||||
session.copyEffect(LayerEffectKind::shadow, two.top, two.bottom);
|
||||
QCOMPARE(session.history.undoName(), QString("Copy Drop Shadow"));
|
||||
QVERIFY(!session.effectsEditing() && two.effects(two.bottom).value().shadow == source.shadow);
|
||||
@@ -259,7 +259,7 @@ void LayerEffectsSessionTests::copyingAnEffectClosesTheTargetsPanelFirst()
|
||||
QVERIFY(session.effectSelection() == (LayerEffectSelection{two.bottom, LayerEffectKind::shadow}) && session.activeLayerID() == two.bottom);
|
||||
// The open edit was kept: one undo brings it back.
|
||||
session.undo();
|
||||
QVERIFY(two.effects(two.bottom).value().shadow->blur == 7);
|
||||
QVERIFY(two.effects(two.bottom).value().shadow.value().blur == 7);
|
||||
session.redo();
|
||||
// No source, a blank or folder target, busy: no copy.
|
||||
QVERIFY(!session.canCopyEffect(LayerEffectKind::shadow, QUuid::createUuid(), two.bottom));
|
||||
@@ -278,9 +278,9 @@ void LayerEffectsSessionTests::togglingAndRemovingAnEffect()
|
||||
session.addEffect(LayerEffectKind::stroke);
|
||||
session.finishEffectsEditing(true);
|
||||
session.toggleEffect(LayerEffectKind::stroke, two.top);
|
||||
QVERIFY(two.effects(two.top).value().stroke->enabled == false && session.history.undoName() == QString("Hide Stroke"));
|
||||
QVERIFY(two.effects(two.top).value().stroke.value().enabled == false && session.history.undoName() == QString("Hide Stroke"));
|
||||
session.toggleEffect(LayerEffectKind::stroke, two.top);
|
||||
QVERIFY(two.effects(two.top).value().stroke->enabled == true && session.history.undoName() == QString("Show Stroke"));
|
||||
QVERIFY(two.effects(two.top).value().stroke.value().enabled == true && session.history.undoName() == QString("Show Stroke"));
|
||||
// Toggling a missing effect, or none at all, does nothing.
|
||||
const int steps = session.history.undoCount();
|
||||
session.toggleEffect(LayerEffectKind::shadow, two.top);
|
||||
@@ -381,7 +381,7 @@ void LayerEffectsSessionTests::thePickerEditsTheOpenEffect()
|
||||
QVERIFY(session.colorPicker());
|
||||
const int steps = session.history.undoCount();
|
||||
session.previewEffectColor();
|
||||
QVERIFY(session.history.undoCount() == steps && two.effects(two.top).value().stroke->red == 1);
|
||||
QVERIFY(session.history.undoCount() == steps && two.effects(two.top).value().stroke.value().red == 1);
|
||||
session.openEffectColorPicker(LayerEffectKind::shadow);
|
||||
QVERIFY(session.colorPicker().value().target.kind == ColorPickerTarget::Kind::palette);
|
||||
session.closeColorPicker(false);
|
||||
@@ -408,7 +408,7 @@ void LayerEffectsSessionTests::thePickerEditsTheOpenEffect()
|
||||
session.setColorPickerHSB(PickerHSB(PaletteColor{1, 1, 0}));
|
||||
session.previewEffectColor();
|
||||
session.addEffect(LayerEffectKind::stroke);
|
||||
QVERIFY(!session.colorPicker() && two.effects(two.top).value().shadow->color() == PaletteColor::black());
|
||||
QVERIFY(!session.colorPicker() && two.effects(two.top).value().shadow.value().color() == PaletteColor::black());
|
||||
}
|
||||
|
||||
void LayerEffectsSessionTests::everyChangeIsAnnouncedLast()
|
||||
@@ -424,7 +424,7 @@ void LayerEffectsSessionTests::everyChangeIsAnnouncedLast()
|
||||
return seen == left ? QString() : seen.join("; ") + " != " + left.join("; ");
|
||||
};
|
||||
QCOMPARE(stale([&] { session.addEffect(LayerEffectKind::stroke); }), QString());
|
||||
QCOMPARE(stale([&] { session.changeEffects([](LayerEffects &effects) { effects.stroke->size = 6; }); }), QString());
|
||||
QCOMPARE(stale([&] { session.changeEffects([](LayerEffects &effects) { effects.stroke.value().size = 6; }); }), QString());
|
||||
QCOMPARE(stale([&] { session.openEffectColorPicker(LayerEffectKind::stroke); }), QString());
|
||||
QCOMPARE(stale([&] { session.setColorPickerHSB(PickerHSB(PaletteColor{1, 0, 0})); }), QString());
|
||||
QCOMPARE(stale([&] { session.previewEffectColor(); }), QString());
|
||||
|
||||
@@ -118,16 +118,16 @@ void LayerEffectsTests::validityFollowsSwiftsBounds()
|
||||
QVERIFY(!InnerShadowEffect{.angle = -360.5}.isValid() && !InnerShadowEffect{.distance = -1}.isValid() && !InnerShadowEffect{.blur = notANumber}.isValid());
|
||||
// One bad effect spoils the set.
|
||||
LayerEffects effects = every();
|
||||
effects.innerShadow->blur = infinity;
|
||||
effects.innerShadow.value().blur = infinity;
|
||||
QVERIFY(!effects.isValid());
|
||||
effects = every();
|
||||
effects.stroke->size = 501;
|
||||
effects.stroke.value().size = 501;
|
||||
QVERIFY(!effects.isValid());
|
||||
effects = every();
|
||||
effects.shadow->distance = -1;
|
||||
effects.shadow.value().distance = -1;
|
||||
QVERIFY(!effects.isValid());
|
||||
effects = every();
|
||||
effects.colorOverlay->opacity = 2;
|
||||
effects.colorOverlay.value().opacity = 2;
|
||||
QVERIFY(!effects.isValid());
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ void LayerEffectsTests::theHelpersReadAndWriteOneKind()
|
||||
effects.setEnabled(true, LayerEffectKind::stroke);
|
||||
effects.setEnabled(false, LayerEffectKind::innerShadow);
|
||||
effects.setEnabled(true, LayerEffectKind::shadow);
|
||||
QVERIFY(effects.stroke->enabled == true && effects.innerShadow->enabled == false && !effects.shadow);
|
||||
QVERIFY(effects.stroke.value().enabled == true && effects.innerShadow.value().enabled == false && !effects.shadow);
|
||||
// Visible keeps what is switched on.
|
||||
const LayerEffects shown = effects.visible();
|
||||
QVERIFY(shown.stroke == effects.stroke && !shown.innerShadow && !shown.shadow && !shown.colorOverlay);
|
||||
|
||||
+12
-12
@@ -351,8 +351,8 @@ void LayerMaskTests::maskTransformPrefersThePlacement()
|
||||
QCOMPARE(layer.maskTransform(), layer.transform);
|
||||
layer.mask = wideMask();
|
||||
QCOMPARE(layer.maskTransform(), layer.transform);
|
||||
layer.mask->placement = LayerTransform{.origin = {5, 6}, .size = {7, 8}};
|
||||
QCOMPARE(layer.maskTransform(), *layer.mask->placement);
|
||||
layer.mask.value().placement = LayerTransform{.origin = {5, 6}, .size = {7, 8}};
|
||||
QCOMPARE(layer.maskTransform(), layer.mask.value().placement.value());
|
||||
}
|
||||
|
||||
void LayerMaskTests::layersCompareAndRecordTheirMasks()
|
||||
@@ -370,7 +370,7 @@ void LayerMaskTests::layersCompareAndRecordTheirMasks()
|
||||
QVERIFY(!(masked == layer));
|
||||
QCOMPARE(masked, ImageLayer(masked));
|
||||
ImageLayer disabled = masked;
|
||||
disabled.mask->isEnabled = false;
|
||||
disabled.mask.value().isEnabled = false;
|
||||
QVERIFY(!(disabled == masked));
|
||||
const ProjectLayerRecord hidden = disabled.hierarchyRecord();
|
||||
QCOMPARE(hidden.maskEnabled, std::optional<bool>(false));
|
||||
@@ -382,12 +382,12 @@ void LayerMaskTests::layersCompareAndRecordTheirMasks()
|
||||
QCOMPARE(record.maskPlacement, std::nullopt);
|
||||
QCOMPARE(record.maskLinked, std::optional<bool>(true));
|
||||
ImageLayer unlinked = masked;
|
||||
unlinked.mask->isLinked = false;
|
||||
unlinked.mask->placement = LayerTransform{.origin = {5, 6}, .size = {7, 8}};
|
||||
unlinked.mask.value().isLinked = false;
|
||||
unlinked.mask.value().placement = LayerTransform{.origin = {5, 6}, .size = {7, 8}};
|
||||
const ProjectLayerRecord moved = unlinked.hierarchyRecord();
|
||||
QCOMPARE(moved.maskEnabled, std::optional<bool>(true));
|
||||
QCOMPARE(moved.maskLinked, std::optional<bool>(false));
|
||||
QCOMPARE(moved.maskPlacement, unlinked.mask->placement);
|
||||
QCOMPARE(moved.maskPlacement, unlinked.mask.value().placement);
|
||||
}
|
||||
|
||||
void LayerMaskTests::historyCountsMaskPixels()
|
||||
@@ -395,18 +395,18 @@ void LayerMaskTests::historyCountsMaskPixels()
|
||||
DocumentHistory history;
|
||||
std::optional<CanvasDocument> document = CanvasDocument(40, 20);
|
||||
const ImportedImage pixels(BrushRaster::context(40, 20, false), QImage(), "Photo");
|
||||
document->layers.push_back(ImageLayer(pixels, QPointF(0, 0)));
|
||||
document->layers[0].mask = LayerMask(LayerMask::assetFrom(gray(200, 100, 255)));
|
||||
const qint64 maskBytes = document->layers[0].mask->asset.byteCount()
|
||||
+ document->layers[0].mask->asset.thumbnail.sizeInBytes();
|
||||
document.value().layers.push_back(ImageLayer(pixels, QPointF(0, 0)));
|
||||
document.value().layers[0].mask = LayerMask(LayerMask::assetFrom(gray(200, 100, 255)));
|
||||
const qint64 maskBytes = document.value().layers[0].mask.value().asset.byteCount()
|
||||
+ document.value().layers[0].mask.value().asset.thumbnail.sizeInBytes();
|
||||
QCOMPARE(maskBytes, qint64(200 * 100 + 96 * 48));
|
||||
history.begin("Disable Layer Mask", document, std::nullopt);
|
||||
document->layers[0].mask->isEnabled = false;
|
||||
document.value().layers[0].mask.value().isEnabled = false;
|
||||
history.end(document, std::nullopt);
|
||||
QCOMPARE(history.undoCount(), 1);
|
||||
QCOMPARE(history.retainedBytes(document), 0);
|
||||
history.begin("Delete Layer Mask", document, std::nullopt);
|
||||
document->layers[0].mask.reset();
|
||||
document.value().layers[0].mask.reset();
|
||||
history.end(document, std::nullopt);
|
||||
QCOMPARE(history.retainedBytes(document), maskBytes);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ void LayerTests::importedLayerTakesItsSizeAndNameFromTheAsset()
|
||||
QCOMPARE(layer.name, QString("Photo"));
|
||||
QCOMPARE(layer.origin(), QPointF(40, -10));
|
||||
QCOMPARE(layer.size(), QSizeF(320, 200));
|
||||
QCOMPARE(layer.asset->image().size(), QSize(320, 200));
|
||||
QCOMPARE(layer.asset.value().image().size(), QSize(320, 200));
|
||||
QVERIFY(layer.isVisible);
|
||||
QCOMPARE(layer.opacity, 1.0);
|
||||
QCOMPARE(layer.blendMode, LayerBlendMode::normal);
|
||||
|
||||
@@ -326,7 +326,7 @@ void MaskAloneTests::aPendingGradientElsewhereStaysOutOfTheView()
|
||||
session.selectLayerTarget(shown.id, false);
|
||||
session.beginGradient(QPointF(0, 80));
|
||||
session.moveGradient(std::nullopt, QPointF(200, 80));
|
||||
QVERIFY(session.gradientEdit().has_value() && !session.gradientEdit()->raster->isMask);
|
||||
QVERIFY(session.gradientEdit().has_value() && !session.gradientEdit().value().raster->isMask);
|
||||
session.toggleMaskAlone(shown.id);
|
||||
// The commit is posted: the pixels' raster lingers, unshown.
|
||||
QVERIFY(session.maskAloneLayer().has_value() && session.gradientEdit().has_value());
|
||||
@@ -338,7 +338,7 @@ void MaskAloneTests::aPendingGradientElsewhereStaysOutOfTheView()
|
||||
session.addLayerMask();
|
||||
session.beginGradient(QPointF(0, 80));
|
||||
session.moveGradient(std::nullopt, QPointF(200, 80));
|
||||
QVERIFY(session.gradientEdit().has_value() && session.gradientEdit()->raster->isMask && session.gradientEdit()->raster->layer.id == other);
|
||||
QVERIFY(session.gradientEdit().has_value() && session.gradientEdit().value().raster->isMask && session.gradientEdit().value().raster->layer.id == other);
|
||||
session.toggleMaskAlone(shown.id);
|
||||
QCOMPARE(session.maskAloneLayer().value().id, shown.id);
|
||||
QVERIFY(session.gradientEdit().has_value());
|
||||
|
||||
@@ -284,6 +284,11 @@ void NativeResolutionPaintTests::layersPanelSaysHowFarALayerIsScaled()
|
||||
QCOMPARE(sizeLabel(layer), QString("500 × 300 px · 50%"));
|
||||
layer.transform.size = QSizeF(2333, 2000);
|
||||
QCOMPARE(sizeLabel(layer), QString("2333 × 2000 px · 233.3%"));
|
||||
// Exact ties go to the even tenth.
|
||||
layer.transform.size = QSizeF(1062.5, 300);
|
||||
QCOMPARE(sizeLabel(layer), QString("1063 × 300 px · 106.2%"));
|
||||
layer.transform.size = QSizeF(1187.5, 300);
|
||||
QCOMPARE(sizeLabel(layer), QString("1188 × 300 px · 118.8%"));
|
||||
// A blank layer has no pixels to measure against.
|
||||
QCOMPARE(sizeLabel(ImageLayer(QStringLiteral("Blank"), QSizeF(30, 20))), QString("30 × 20 px"));
|
||||
// The row shows it.
|
||||
|
||||
@@ -80,7 +80,7 @@ void OuterGlowTests::outerGlowCodableRoundTrip()
|
||||
QCOMPARE(ManifestJson::effects(encoded), original);
|
||||
// Absent `enabled` stays absent, read as shown.
|
||||
LayerEffects shown = original;
|
||||
shown.outerGlow->enabled = std::nullopt;
|
||||
shown.outerGlow.value().enabled = std::nullopt;
|
||||
QVERIFY(!ManifestJson::encoded(shown).value("outerGlow").toObject().contains("enabled"));
|
||||
QCOMPARE(ManifestJson::effects(ManifestJson::encoded(shown)), shown);
|
||||
// A key of the wrong type, or missing, refuses it.
|
||||
@@ -104,7 +104,7 @@ void OuterGlowTests::layerEffectsIntegration()
|
||||
QVERIFY(effects.color(LayerEffectKind::outerGlow) == (PaletteColor{1, 0, 0}));
|
||||
QVERIFY((effects.kinds() == std::vector{LayerEffectKind::outerGlow}));
|
||||
effects.setColor(PaletteColor{0, 1, 0}, LayerEffectKind::outerGlow);
|
||||
QVERIFY(effects.outerGlow->green == 1 && effects.outerGlow->red == 0);
|
||||
QVERIFY(effects.outerGlow.value().green == 1 && effects.outerGlow.value().red == 0);
|
||||
effects.setEnabled(false, LayerEffectKind::outerGlow);
|
||||
QVERIFY(!effects.isEnabled(LayerEffectKind::outerGlow) && !effects.visible().outerGlow);
|
||||
effects.remove(LayerEffectKind::outerGlow);
|
||||
@@ -202,7 +202,7 @@ void OuterGlowTests::theGlowWidensTheMargin()
|
||||
{
|
||||
LayerEffects effects = glowing(7.2, 1, 1, 1, 1);
|
||||
QCOMPARE(LayerEffectsRenderer::margin(effects), 24.0);
|
||||
effects.outerGlow->enabled = false;
|
||||
effects.outerGlow.value().enabled = false;
|
||||
QCOMPARE(LayerEffectsRenderer::margin(effects), 2.0);
|
||||
}
|
||||
|
||||
@@ -215,11 +215,11 @@ void OuterGlowTests::theSessionAddsCopiesAndCancelsIt()
|
||||
session.addEffect(LayerEffectKind::outerGlow);
|
||||
QCOMPARE(session.history.undoName(), QString("Add Outer Glow"));
|
||||
QVERIFY(session.activeEffects().outerGlow == OuterGlowEffect());
|
||||
session.changeEffects([](LayerEffects &effects) { effects.outerGlow->size = 44; });
|
||||
session.changeEffects([](LayerEffects &effects) { effects.outerGlow.value().size = 44; });
|
||||
session.finishEffectsEditing(false);
|
||||
QVERIFY(!session.activeEffects().outerGlow);
|
||||
session.addEffect(LayerEffectKind::outerGlow);
|
||||
session.changeEffects([](LayerEffects &effects) { effects.outerGlow->size = 44; });
|
||||
session.changeEffects([](LayerEffects &effects) { effects.outerGlow.value().size = 44; });
|
||||
session.finishEffectsEditing(true);
|
||||
session.insert(ImportedImage(square(), QImage(), QStringLiteral("Two")));
|
||||
const QUuid second = session.activeLayerID().value();
|
||||
|
||||
@@ -149,7 +149,7 @@ inline QByteArray engine(const TypeBlock &block)
|
||||
return "<<\n/EngineDict\n<<\n/Editor\n<<\n/Text " + parenthesized(block.engineText.value_or(block.text)) + "\n>>\n/ParagraphRun\n<<\n/RunArray\n[\n<<\n"
|
||||
+ "/ParagraphSheet\n<<\n/Properties\n<<\n/Justification " + block.justification + "\n>>\n>>\n>>\n]\n>>\n"
|
||||
+ "/StyleRun\n<<\n/RunArray\n[\n" + runs + "\n]\n>>\n>>\n/ResourceDict\n<<\n/FontSet\n[\n<<\n/Name " + parenthesized(block.font)
|
||||
+ "\n>>\n" + (block.secondFont ? "<<\n/Name " + parenthesized(*block.secondFont) + "\n>>\n" : QByteArray()) + "]\n>>\n>>";
|
||||
+ "\n>>\n" + (block.secondFont ? "<<\n/Name " + parenthesized(block.secondFont.value()) + "\n>>\n" : QByteArray()) + "]\n>>\n>>";
|
||||
}
|
||||
|
||||
inline QByteArray tySh(const TypeBlock &block)
|
||||
@@ -164,9 +164,9 @@ inline QByteArray tySh(const TypeBlock &block)
|
||||
items.emplace_back(block.wordsKey, textItem(block.text));
|
||||
items.emplace_back("Ornt", enumItem("Ornt", block.vertical ? "Vrtc" : "Hrzn"));
|
||||
if (block.bounds)
|
||||
items.emplace_back("bounds", rectItem(*block.bounds));
|
||||
items.emplace_back("bounds", rectItem(block.bounds.value()));
|
||||
if (block.glyphBounds)
|
||||
items.emplace_back("boundingBox", rectItem(*block.glyphBounds));
|
||||
items.emplace_back("boundingBox", rectItem(block.glyphBounds.value()));
|
||||
if (block.withEngine)
|
||||
items.emplace_back("EngineData", rawItem(engine(block)));
|
||||
descriptor(data, "TxLr", items);
|
||||
|
||||
+8
-8
@@ -128,27 +128,27 @@ inline std::vector<Channel> emptyChannels()
|
||||
|
||||
inline Prepared layer(const PSDRecord &record, bool large = false)
|
||||
{
|
||||
const int width = record.image ? record.image->width() : 0, height = record.image ? record.image->height() : 0;
|
||||
const int width = record.image ? record.image.value().width() : 0, height = record.image ? record.image.value().height() : 0;
|
||||
const int left = int(std::round(record.bounds.left())), top = int(std::round(record.bounds.top()));
|
||||
Prepared prepared;
|
||||
prepared.record = record;
|
||||
if (record.image && width > 0 && height > 0) {
|
||||
const Planes split = planes(*record.image);
|
||||
const Planes split = planes(record.image.value());
|
||||
for (const auto &[id, plane] : {std::pair<qint16, const std::vector<uchar> &>(-1, split.alpha), {0, split.red}, {1, split.green}, {2, split.blue}})
|
||||
prepared.channels.push_back({id, channelPayload(plane, width, height, large)});
|
||||
} else {
|
||||
prepared.channels = emptyChannels();
|
||||
}
|
||||
if (record.mask)
|
||||
prepared.channels.push_back({-2, channelPayload(grayPlane(*record.mask), record.mask->width(), record.mask->height(), large)});
|
||||
prepared.channels.push_back({-2, channelPayload(grayPlane(record.mask.value()), record.mask.value().width(), record.mask.value().height(), large)});
|
||||
prepared.top = top;
|
||||
prepared.left = left;
|
||||
prepared.bottom = top + height;
|
||||
prepared.right = left + width;
|
||||
prepared.maskTop = top;
|
||||
prepared.maskLeft = left;
|
||||
prepared.maskBottom = top + (record.mask ? record.mask->height() : 0);
|
||||
prepared.maskRight = left + (record.mask ? record.mask->width() : 0);
|
||||
prepared.maskBottom = top + (record.mask ? record.mask.value().height() : 0);
|
||||
prepared.maskRight = left + (record.mask ? record.mask.value().width() : 0);
|
||||
return prepared;
|
||||
}
|
||||
|
||||
@@ -170,9 +170,9 @@ inline Prepared emptyLayer(const QString &name, const QString &blendKey, int sec
|
||||
prepared.isDivider = section == 3;
|
||||
prepared.channels = emptyChannels();
|
||||
if (record.mask) {
|
||||
prepared.channels.push_back({-2, channelPayload(grayPlane(*record.mask), record.mask->width(), record.mask->height(), large)});
|
||||
prepared.maskBottom = record.mask->height();
|
||||
prepared.maskRight = record.mask->width();
|
||||
prepared.channels.push_back({-2, channelPayload(grayPlane(record.mask.value()), record.mask.value().width(), record.mask.value().height(), large)});
|
||||
prepared.maskBottom = record.mask.value().height();
|
||||
prepared.maskRight = record.mask.value().width();
|
||||
}
|
||||
return prepared;
|
||||
}
|
||||
|
||||
@@ -78,10 +78,10 @@ inline QByteArray file(const std::vector<Record> &records, const QByteArray &res
|
||||
PSDFixture::Buffer extra;
|
||||
if (record.mask) {
|
||||
extra.u32(20);
|
||||
extra.i32(record.mask->top());
|
||||
extra.i32(record.mask->left());
|
||||
extra.i32(record.mask->top() + record.mask->height());
|
||||
extra.i32(record.mask->left() + record.mask->width());
|
||||
extra.i32(record.mask.value().top());
|
||||
extra.i32(record.mask.value().left());
|
||||
extra.i32(record.mask.value().top() + record.mask.value().height());
|
||||
extra.i32(record.mask.value().left() + record.mask.value().width());
|
||||
extra.u8(record.maskDefault);
|
||||
extra.u8(record.maskFlags);
|
||||
extra.u16(0);
|
||||
|
||||
@@ -437,15 +437,15 @@ void RasterSnapshotTests::historyBudgetNeverFlattensPaintedTiles()
|
||||
const auto raster = std::make_shared<const RasterSnapshot>(40, 30, QImage(), QRectF(), std::vector<BrushPatch>());
|
||||
DocumentHistory history;
|
||||
std::optional<CanvasDocument> document = CanvasDocument(40, 30);
|
||||
document->layers.push_back(ImageLayer(painted(raster), QPointF(0, 0)));
|
||||
QVERIFY(document->layers[0] == ImageLayer(document->layers[0]));
|
||||
document.value().layers.push_back(ImageLayer(painted(raster), QPointF(0, 0)));
|
||||
QVERIFY(document.value().layers[0] == ImageLayer(document.value().layers[0]));
|
||||
history.begin("Select Layer", document, std::nullopt);
|
||||
history.end(document, document->layers[0].id);
|
||||
history.end(document, document.value().layers[0].id);
|
||||
QCOMPARE(history.undoCount(), 0);
|
||||
QVERIFY(!raster->hasMaterializedPixels());
|
||||
history.begin("Delete Layer", document, std::nullopt);
|
||||
const qint64 thumbnailBytes = document->layers[0].asset->thumbnail.sizeInBytes();
|
||||
document->layers.clear();
|
||||
const qint64 thumbnailBytes = document.value().layers[0].asset.value().thumbnail.sizeInBytes();
|
||||
document.value().layers.clear();
|
||||
history.end(document, std::nullopt);
|
||||
QCOMPARE(history.retainedBytes(document), qint64(40 * 30 * 4) + thumbnailBytes);
|
||||
QVERIFY(!raster->hasMaterializedPixels());
|
||||
|
||||
@@ -72,10 +72,10 @@ void RawDevelopTests::theSheetsSettingsDevelopTheLayer()
|
||||
bool done = false;
|
||||
session.importImages({raw, png(folder)}, std::nullopt, [&done] { done = true; });
|
||||
QTRY_VERIFY(session.rawDevelop());
|
||||
QCOMPARE(session.rawDevelop()->path, raw.toLocalFile());
|
||||
QVERIFY(session.rawDevelop()->settings == RawImporter::asShot(raw.toLocalFile()).value());
|
||||
QCOMPARE(session.rawDevelop().value().path, raw.toLocalFile());
|
||||
QVERIFY(session.rawDevelop().value().settings == RawImporter::asShot(raw.toLocalFile()).value());
|
||||
QVERIFY(session.isImporting() && !session.document());
|
||||
RawDevelopSettings settings = session.rawDevelop()->settings;
|
||||
RawDevelopSettings settings = session.rawDevelop().value().settings;
|
||||
settings.boost = 0;
|
||||
settings.exposure = 1;
|
||||
// The sheet hears at once that it has gone.
|
||||
@@ -139,7 +139,7 @@ void RawDevelopTests::whatCannotBeDevelopedIsReported()
|
||||
QFile broken(gray.toLocalFile());
|
||||
QVERIFY(broken.open(QIODevice::WriteOnly) && broken.write("gone") == 4);
|
||||
broken.close();
|
||||
session.finishRawDevelop(session.rawDevelop()->settings);
|
||||
session.finishRawDevelop(session.rawDevelop().value().settings);
|
||||
QTRY_VERIFY(done);
|
||||
QCOMPARE(session.importError().value(), "Gray.dng: " + unreadable);
|
||||
QCOMPARE(names(session), QStringList{"Plain"});
|
||||
@@ -149,7 +149,7 @@ void RawDevelopTests::whatCannotBeDevelopedIsReported()
|
||||
const QUrl warm = dng(folder, "Warm.dng");
|
||||
session.importImages({warm}, std::nullopt, [&done] { done = true; });
|
||||
QTRY_VERIFY(session.rawDevelop());
|
||||
RawDevelopSettings candle = session.rawDevelop()->settings;
|
||||
RawDevelopSettings candle = session.rawDevelop().value().settings;
|
||||
candle.temperature = 2000;
|
||||
session.finishRawDevelop(candle);
|
||||
QTRY_VERIFY(done);
|
||||
@@ -240,7 +240,7 @@ void RawDevelopTests::theSheetPreviewsAndAnswers()
|
||||
QTest::qWait(50);
|
||||
QCOMPARE(preview->grab().toImage().pixelColor(200, 170), QColor(139, 139, 139));
|
||||
QVERIFY(!spinner->isHidden());
|
||||
RawDevelopSettings dimmer = session.rawDevelop()->settings;
|
||||
RawDevelopSettings dimmer = session.rawDevelop().value().settings;
|
||||
dimmer.exposure = -1;
|
||||
wait->start();
|
||||
QTRY_COMPARE_WITH_TIMEOUT(preview->grab().toImage().pixelColor(200, 170), centre(RawImporter::develop(path, dimmer)), 1000);
|
||||
@@ -249,7 +249,7 @@ void RawDevelopTests::theSheetPreviewsAndAnswers()
|
||||
exposure->setValue(100);
|
||||
QCOMPARE(label(sheet, "rawExposureValue"), QString("1.00 EV"));
|
||||
QVERIFY(reset->isEnabled());
|
||||
RawDevelopSettings brighter = session.rawDevelop()->settings;
|
||||
RawDevelopSettings brighter = session.rawDevelop().value().settings;
|
||||
brighter.exposure = 1;
|
||||
const QColor expected = centre(RawImporter::develop(path, brighter));
|
||||
QTRY_COMPARE(preview->grab().toImage().pixelColor(200, 170), expected);
|
||||
|
||||
@@ -75,12 +75,12 @@ void RemoveBackgroundTests::thePreviewClearsTheBackgroundAndTheCommitMasksIt()
|
||||
const FilterEdit &edit = session->filterEdit().value();
|
||||
QVERIFY(edit.previewSource.size() == QSize(400, 300) && !edit.previewError);
|
||||
const QImage preview = edit.previewImage(id).value();
|
||||
QVERIFY(preview.pixelColor(0, 0).alpha() == 0 && preview.pixelColor(200, 150) == before.asset->image().pixelColor(200, 150));
|
||||
QVERIFY(preview.pixelColor(0, 0).alpha() == 0 && preview.pixelColor(200, 150) == before.asset.value().image().pixelColor(200, 150));
|
||||
const int steps = session->history.undoCount();
|
||||
QVERIFY(committed(*session));
|
||||
// The pixels stay; a mask hides the background instead.
|
||||
const ImageLayer after = layerWith(*session, id);
|
||||
QVERIFY(after.asset->identity() == before.asset->identity() && after.transform == before.transform);
|
||||
QVERIFY(after.asset.value().identity() == before.asset.value().identity() && after.transform == before.transform);
|
||||
const LayerMask &mask = after.mask.value();
|
||||
QVERIFY(mask.isEnabled && !mask.placement && mask.isLinked);
|
||||
QVERIFY(mask.asset.image().format() == QImage::Format_Grayscale8 && mask.asset.size() == QSize(400, 300));
|
||||
|
||||
@@ -176,7 +176,7 @@ void SelectionFeatherTests::aMaskFromAFeatheredSelectionFades()
|
||||
EditorSession session;
|
||||
session.createDocument(60, 20, true);
|
||||
session.setSelection(DocumentSelection{rectPath(QRectF(20, 0, 20, 20)), true, 6}, "probe");
|
||||
session.addMask(false);
|
||||
session.addMask(true);
|
||||
const QImage mask = session.activeLayer().value().mask.value().asset.image();
|
||||
QCOMPARE(mask, session.document().value().layers.back().mask.value().asset.image());
|
||||
QVERIFY2(fading(mask, 10, 0, 1) >= 4, "the mask has a hard edge");
|
||||
|
||||
@@ -53,7 +53,11 @@ void SelectionSignalTests::selectionCallsAnnounceAndRefuseInSilence()
|
||||
QVERIFY(session->selection().has_value());
|
||||
QVERIFY(announced([&] { session->selectAll(); }));
|
||||
QVERIFY(!announced([&] { session->selectAll(); }));
|
||||
// Inverse of everything leaves nothing to deselect.
|
||||
QVERIFY(announced([&] { session->invertSelection(); }));
|
||||
QVERIFY(!session->selection().has_value());
|
||||
QVERIFY(!announced([&] { session->deselect(); }));
|
||||
QVERIFY(announced([&] { session->selectAll(); }));
|
||||
QVERIFY(announced([&] { session->deselect(); }));
|
||||
QVERIFY(announced([&] { session->toggleMarqueeKind(); }));
|
||||
QVERIFY(announced([&] { session->toggleLassoKind(); }));
|
||||
|
||||
@@ -27,10 +27,10 @@ inline QStringList described(const EditorSession &session)
|
||||
"tool error " + session.brushError().value_or(QStringLiteral("none")),
|
||||
"renaming " + text(session.renamingLayerID()),
|
||||
"adjusting " + text(session.adjustmentEditingID()) + (session.adjustmentOriginal().has_value() ? " open" : ""),
|
||||
"effects " + (session.effectsEditing() ? text(session.effectsEditing()->layerID) + " " + rawValue(session.effectsEditing()->kind) : QStringLiteral("closed"))
|
||||
+ (session.effectsEditingOriginal() ? " " + QJsonDocument(ManifestJson::encoded(*session.effectsEditingOriginal())).toJson(QJsonDocument::Compact) : QString())
|
||||
+ " chosen " + (session.effectSelection() ? text(session.effectSelection()->layerID) + " " + rawValue(session.effectSelection()->kind) : QStringLiteral("none")),
|
||||
"picker " + (session.colorPicker() ? session.colorPicker()->target.title() + " " + session.colorPicker()->color().hex() : QStringLiteral("closed")),
|
||||
"effects " + (session.effectsEditing() ? text(session.effectsEditing().value().layerID) + " " + rawValue(session.effectsEditing().value().kind) : QStringLiteral("closed"))
|
||||
+ (session.effectsEditingOriginal() ? " " + QJsonDocument(ManifestJson::encoded(session.effectsEditingOriginal().value())).toJson(QJsonDocument::Compact) : QString())
|
||||
+ " chosen " + (session.effectSelection() ? text(session.effectSelection().value().layerID) + " " + rawValue(session.effectSelection().value().kind) : QStringLiteral("none")),
|
||||
"picker " + (session.colorPicker() ? session.colorPicker().value().target.title() + " " + session.colorPicker().value().color().hex() : QStringLiteral("closed")),
|
||||
QStringLiteral("mask target %1").arg(int(session.isMaskSelected())),
|
||||
QStringLiteral("history %1%2").arg(int(session.canUndo())).arg(int(session.canRedo())),
|
||||
QStringLiteral("names %1/%2").arg(session.history.undoName(), session.history.redoName()),
|
||||
|
||||
@@ -320,7 +320,7 @@ void ShapeLayerTests::aProjectKeepsLiveShapesOnly()
|
||||
// Swift's keys; a rectangle writes no line fields.
|
||||
QCOMPARE(firstShape(snapshot, 1).keys(), (QStringList{"blue", "cornerRadius", "green", "kind", "red"}));
|
||||
QCOMPARE(firstShape(snapshot, 1).value("kind").toString(), QString("Rectangle"));
|
||||
QCOMPARE(firstShape(snapshot, 2).value("start").toArray(), (QJsonArray{line.start->x(), line.start->y()}));
|
||||
QCOMPARE(firstShape(snapshot, 2).value("start").toArray(), (QJsonArray{line.start.value().x(), line.start.value().y()}));
|
||||
QCOMPARE(firstShape(snapshot, 2).value("lineWidth").toDouble(), 3.0);
|
||||
const ProjectManifest decoded = ProjectManifest::decoded(snapshot.manifest.encoded());
|
||||
QCOMPARE(decoded.layers[1].shape, snapshot.manifest.layers[1].shape);
|
||||
|
||||
@@ -126,7 +126,7 @@ struct Oracle {
|
||||
pickUp(point);
|
||||
return;
|
||||
}
|
||||
const QPointF from = *last;
|
||||
const QPointF from = last.value();
|
||||
const double distance = std::hypot(point.x() - from.x(), point.y() - from.y());
|
||||
const double spacing = std::max(1.0, diameter * (smudging ? 0.005 : 0.025));
|
||||
if (distance < spacing)
|
||||
|
||||
@@ -125,7 +125,7 @@ void TextColorModelTests::everyChannelOfARunIsChecked()
|
||||
session->setPaletteColor(red, false);
|
||||
QVERIFY(session->finishText());
|
||||
ProjectSnapshot snapshot = session->projectSnapshot().value();
|
||||
snapshot.manifest.layers.back().text->colorRuns->front().blue = 2;
|
||||
snapshot.manifest.layers.back().text.value().colorRuns.value().front().blue = 2;
|
||||
QTemporaryDir root;
|
||||
try {
|
||||
ProjectStore::save(snapshot, root.filePath(QStringLiteral("Bad.comp")));
|
||||
|
||||
@@ -220,7 +220,7 @@ void WritingProjectsGuideTests::hsvSettingsWinOverTheLegacyFields()
|
||||
QJsonObject adjustment = layer.value(QStringLiteral("adjustment")).toObject();
|
||||
adjustment.insert(QStringLiteral("hue"), legacyHue);
|
||||
if (masterHue) {
|
||||
const QJsonObject values{{"hue", *masterHue}, {"saturation", 0}, {"lightness", 0}};
|
||||
const QJsonObject values{{"hue", masterHue.value()}, {"saturation", 0}, {"lightness", 0}};
|
||||
adjustment.insert(QStringLiteral("hsvSettings"), QJsonObject{{"range", "Master"}, {"colorize", false}, {"invertRange", false},
|
||||
{"adjustments", QJsonArray{"Master", values}}, {"bands", QJsonArray{}}});
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user