Photoshop's remaining blend modes in its groups (12.7a); v1.2.2 kernels for 12.7b
This commit is contained in:
1 parent
29eab10edc
commit
7d47cc367a
26 files changed
+348
-56
No files matched your search
@@ -186,6 +186,10 @@ void AdjustmentLayerTests::adjustmentBlendAndSoftMaskPreserveCoverage()
|
||||
s.updateAdjustment(id, value);
|
||||
s.setLayerBlendMode(LayerBlendMode::multiply);
|
||||
QVERIFY((rendered(s) == std::vector<int>{0, 0, 0, 255, 0, 0, 0, 128, 0, 0, 0, 32, 0, 0, 0, 0}));
|
||||
// Green over red adds to yellow; Swift adjusts as Normal.
|
||||
s.setLayerBlendMode(LayerBlendMode::linearDodge);
|
||||
QVERIFY((part(rendered(s), 0, 4) == std::vector<int>{255, 255, 0, 255}));
|
||||
QCOMPARE(rendered(s)[7], 128);
|
||||
s.setLayerBlendMode(LayerBlendMode::normal);
|
||||
QImage gray(1, 1, QImage::Format_Grayscale8);
|
||||
gray.fill(128);
|
||||
|
||||
@@ -26,13 +26,16 @@ void BlendModePickerTests::theMenuListsEveryModeAndFollowsTheActiveLayer()
|
||||
{
|
||||
EditorSession session;
|
||||
BlendModePicker picker(session);
|
||||
QCOMPARE(picker.count(), int(allLayerBlendModes.size()));
|
||||
// Photoshop's six groups, a separator between each.
|
||||
QCOMPARE(picker.count(), int(allLayerBlendModes.size()) + 5);
|
||||
QStringList titles;
|
||||
for (int index = 0; index < picker.count(); ++index)
|
||||
titles << picker.itemText(index);
|
||||
QCOMPARE(titles.first(), QString("Normal"));
|
||||
QCOMPARE(titles.last(), QString("Luminosity"));
|
||||
QCOMPARE(titles[1], QString("Multiply"));
|
||||
QCOMPARE(titles, (QStringList{"Normal", "", "Darken", "Multiply", "Color Burn", "Linear Burn", "", "Lighten", "Screen", "Color Dodge",
|
||||
"Linear Dodge (Add)", "", "Overlay", "Soft Light", "Hard Light", "Vivid Light", "Linear Light", "Pin Light",
|
||||
"Hard Mix", "", "Difference", "Exclusion", "Subtract", "Divide", "", "Hue", "Saturation", "Color", "Luminosity"}));
|
||||
for (const int separator : {1, 6, 11, 19, 24})
|
||||
QCOMPARE(picker.itemData(separator, Qt::AccessibleDescriptionRole).toString(), QString("separator"));
|
||||
QVERIFY(!picker.isEnabled());
|
||||
session.createDocument(4, 4);
|
||||
session.insert(white());
|
||||
@@ -63,20 +66,20 @@ void BlendModePickerTests::hoveringTriesAModeOnAndChoosingKeepsIt()
|
||||
picker.showPopup();
|
||||
QTRY_VERIFY(picker.view()->isVisible());
|
||||
// Hovering previews on the layer the menu opened for.
|
||||
emit picker.highlighted(1);
|
||||
emit picker.highlighted(3);
|
||||
QCOMPARE(session.blendPreview().value().mode, LayerBlendMode::multiply);
|
||||
QCOMPARE(session.blendPreview().value().layerID, layer);
|
||||
QCOMPARE(session.activeLayer().value().blendMode, LayerBlendMode::normal);
|
||||
emit picker.highlighted(2);
|
||||
emit picker.highlighted(8);
|
||||
QCOMPARE(session.blendPreview().value().mode, LayerBlendMode::screen);
|
||||
// The session's news leaves an open menu alone.
|
||||
picker.view()->setCurrentIndex(picker.model()->index(2, 0));
|
||||
picker.view()->setCurrentIndex(picker.model()->index(8, 0));
|
||||
session.setLayerOpacity(0.5);
|
||||
QVERIFY(session.blendPreview().has_value());
|
||||
QCOMPARE(picker.view()->currentIndex().row(), 2);
|
||||
QCOMPARE(picker.view()->currentIndex().row(), 8);
|
||||
// A click closes the menu, then chooses; preview clears after.
|
||||
picker.hidePopup();
|
||||
emit picker.activated(2);
|
||||
emit picker.activated(8);
|
||||
QCOMPARE(session.activeLayer().value().blendMode, LayerBlendMode::screen);
|
||||
QCOMPARE(picker.currentText(), QString("Screen"));
|
||||
QCOMPARE(session.history.undoName(), QString("Layer Blend Mode"));
|
||||
@@ -85,8 +88,14 @@ void BlendModePickerTests::hoveringTriesAModeOnAndChoosingKeepsIt()
|
||||
// Keys on the closed menu choose for the active layer.
|
||||
picker.setFocus();
|
||||
QTest::keyClick(&picker, Qt::Key_Down);
|
||||
QCOMPARE(session.activeLayer().value().blendMode, LayerBlendMode::overlay);
|
||||
QCOMPARE(session.activeLayer().value().blendMode, LayerBlendMode::colorDodge);
|
||||
QCOMPARE(picker.currentText(), QString("Color Dodge"));
|
||||
// Keys step over the lines between groups.
|
||||
QTest::keyClick(&picker, Qt::Key_Down);
|
||||
QTest::keyClick(&picker, Qt::Key_Down);
|
||||
QCOMPARE(picker.currentText(), QString("Overlay"));
|
||||
session.undo();
|
||||
session.undo();
|
||||
// Closed, the menu follows the session again.
|
||||
session.undo();
|
||||
QCOMPARE(picker.currentText(), QString("Screen"));
|
||||
@@ -104,19 +113,19 @@ void BlendModePickerTests::aMenuClosedWithoutAChoiceLeavesTheLayerAlone()
|
||||
picker.show();
|
||||
QVERIFY(QTest::qWaitForWindowExposed(&picker));
|
||||
picker.showPopup();
|
||||
emit picker.highlighted(4);
|
||||
emit picker.highlighted(3);
|
||||
picker.hidePopup();
|
||||
QCOMPARE(session.activeLayer().value().blendMode, LayerBlendMode::normal);
|
||||
QTRY_VERIFY(!session.blendPreview().has_value());
|
||||
QCOMPARE(picker.currentText(), QString("Normal"));
|
||||
// Another layer active at the choice: nothing changes.
|
||||
picker.showPopup();
|
||||
emit picker.highlighted(4);
|
||||
emit picker.highlighted(3);
|
||||
session.selectLayer(session.document().value().layers.front().id);
|
||||
// Open, the menu keeps its text; closed, the active layer.
|
||||
QCOMPARE(picker.currentText(), QString("Normal"));
|
||||
picker.hidePopup();
|
||||
emit picker.activated(4);
|
||||
emit picker.activated(3);
|
||||
QCOMPARE(session.document().value().layers.front().blendMode, LayerBlendMode::multiply);
|
||||
QCOMPARE(session.document().value().layers.back().blendMode, LayerBlendMode::normal);
|
||||
QCOMPARE(session.history.undoName(), QString("Import Image"));
|
||||
@@ -131,7 +140,7 @@ void BlendModePickerTests::aMenuClosedWithoutAChoiceLeavesTheLayerAlone()
|
||||
session.selectLayer(session.document().value().layers.front().id);
|
||||
picker.setFocus();
|
||||
QTest::keyClick(&picker, Qt::Key_Down);
|
||||
QCOMPARE(session.activeLayer().value().blendMode, LayerBlendMode::screen);
|
||||
QCOMPARE(session.activeLayer().value().blendMode, LayerBlendMode::colorBurn);
|
||||
}
|
||||
|
||||
void BlendModePickerTests::theMaskButtonAddsAWhiteMaskOnce()
|
||||
|
||||
@@ -80,6 +80,20 @@ Rgb reference(LayerBlendMode mode, const Rgb &backdrop, const Rgb &source)
|
||||
case LayerBlendMode::difference: result[channel] = std::abs(b - s); break;
|
||||
case LayerBlendMode::colorDodge: result[channel] = b == 0 ? 0 : s >= 1 ? 1 : std::min(1.0, b / (1 - s)); break;
|
||||
case LayerBlendMode::colorBurn: result[channel] = b >= 1 ? 1 : s <= 0 ? 0 : 1 - std::min(1.0, (1 - b) / s); break;
|
||||
// Photoshop's separable modes that QPainter lacks.
|
||||
case LayerBlendMode::hardLight: result[channel] = hardLight(b, s); break;
|
||||
case LayerBlendMode::exclusion: result[channel] = b + s - 2 * b * s; break;
|
||||
case LayerBlendMode::linearBurn: result[channel] = std::max(0.0, b + s - 1); break;
|
||||
case LayerBlendMode::linearDodge: result[channel] = std::min(1.0, b + s); break;
|
||||
case LayerBlendMode::vividLight:
|
||||
result[channel] = s <= 0.5 ? (b >= 1 ? 1 : s <= 0 ? 0 : 1 - std::min(1.0, (1 - b) / (2 * s)))
|
||||
: (b <= 0 ? 0 : s >= 1 ? 1 : std::min(1.0, b / (2 - 2 * s)));
|
||||
break;
|
||||
case LayerBlendMode::linearLight: result[channel] = std::clamp(b + 2 * s - 1, 0.0, 1.0); break;
|
||||
case LayerBlendMode::pinLight: result[channel] = s <= 0.5 ? std::min(b, 2 * s) : std::max(b, 2 * s - 1); break;
|
||||
case LayerBlendMode::hardMix: result[channel] = b + s >= 1 ? 1 : 0; break;
|
||||
case LayerBlendMode::subtract: result[channel] = std::max(0.0, b - s); break;
|
||||
case LayerBlendMode::divide: result[channel] = s <= 0 ? 1 : std::min(1.0, b / s); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
@@ -140,7 +154,10 @@ void BlendModeTests::everyBlendModeMatchesThePdfFormulas_data()
|
||||
{"overlay", LayerBlendMode::overlay}, {"softLight", LayerBlendMode::softLight}, {"darken", LayerBlendMode::darken}, {"lighten", LayerBlendMode::lighten},
|
||||
{"difference", LayerBlendMode::difference}, {"colorDodge", LayerBlendMode::colorDodge},
|
||||
{"colorBurn", LayerBlendMode::colorBurn}, {"hue", LayerBlendMode::hue}, {"saturation", LayerBlendMode::saturation},
|
||||
{"color", LayerBlendMode::color}, {"luminosity", LayerBlendMode::luminosity}};
|
||||
{"color", LayerBlendMode::color}, {"luminosity", LayerBlendMode::luminosity}, {"hardLight", LayerBlendMode::hardLight},
|
||||
{"exclusion", LayerBlendMode::exclusion}, {"linearBurn", LayerBlendMode::linearBurn}, {"linearDodge", LayerBlendMode::linearDodge},
|
||||
{"vividLight", LayerBlendMode::vividLight}, {"linearLight", LayerBlendMode::linearLight}, {"pinLight", LayerBlendMode::pinLight},
|
||||
{"hardMix", LayerBlendMode::hardMix}, {"subtract", LayerBlendMode::subtract}, {"divide", LayerBlendMode::divide}};
|
||||
for (const auto &[name, mode] : modes)
|
||||
QTest::newRow(name) << mode;
|
||||
}
|
||||
@@ -224,6 +241,10 @@ void BlendModeTests::hslBlendChecksItsInputs()
|
||||
QVERIFY(HslBlend::handles(LayerBlendMode::hue) && HslBlend::handles(LayerBlendMode::saturation));
|
||||
QVERIFY(HslBlend::handles(LayerBlendMode::color) && HslBlend::handles(LayerBlendMode::luminosity));
|
||||
QVERIFY(!HslBlend::handles(LayerBlendMode::normal) && !HslBlend::handles(LayerBlendMode::colorBurn));
|
||||
QVERIFY(!HslBlend::handles(LayerBlendMode::hardLight) && !HslBlend::handles(LayerBlendMode::exclusion));
|
||||
for (const LayerBlendMode mode : {LayerBlendMode::linearBurn, LayerBlendMode::linearDodge, LayerBlendMode::vividLight, LayerBlendMode::linearLight,
|
||||
LayerBlendMode::pinLight, LayerBlendMode::hardMix, LayerBlendMode::subtract, LayerBlendMode::divide})
|
||||
QVERIFY(HslBlend::handles(mode));
|
||||
|
||||
const QImage large(8192, 8192, QImage::Format_RGBA8888_Premultiplied), other(8192, 8192, QImage::Format_RGBA8888_Premultiplied);
|
||||
std::optional<AddressSpaceLimit> limit(std::in_place, 16 * 1024 * 1024);
|
||||
|
||||
@@ -29,8 +29,9 @@ void BlendShortcutTests::shiftPlusAndMinusStepTheActiveLayersBlendModeWhereverFo
|
||||
session.selectTool(NavigationTool::brush);
|
||||
canvas->setFocus();
|
||||
QTRY_VERIFY(canvas->hasFocus());
|
||||
// Photoshop's order: the first darkening mode follows Normal.
|
||||
QTest::keyClick(canvas, Qt::Key_Plus, Qt::ShiftModifier);
|
||||
QCOMPARE(mode(), LayerBlendMode::multiply);
|
||||
QCOMPARE(mode(), LayerBlendMode::darken);
|
||||
// From the list, back past Normal to the last mode.
|
||||
session.selectTool(NavigationTool::lasso);
|
||||
list->setFocus();
|
||||
|
||||
@@ -75,6 +75,7 @@ class CanvasDisplayTests : public QObject {
|
||||
private slots:
|
||||
void theBackgroundCheckerboardShadowAndBorderSurroundTheDocument();
|
||||
void layersCompositeAsTheExportDoes();
|
||||
void photoshopsOwnModesShowAsTheExportDoes();
|
||||
void blendModesMeetNoCheckerboardButTranslucencyDoes();
|
||||
void thePixelGridAppearsFromEightHundredPercent();
|
||||
void fromTwoHundredPercentPixelsAreHardEdged();
|
||||
@@ -190,6 +191,24 @@ void CanvasDisplayTests::layersCompositeAsTheExportDoes()
|
||||
session.cancelTransform();
|
||||
}
|
||||
|
||||
void CanvasDisplayTests::photoshopsOwnModesShowAsTheExportDoes()
|
||||
{
|
||||
// Blended by hand on the canvas's surface, as exported.
|
||||
for (const LayerBlendMode mode : {LayerBlendMode::linearDodge, LayerBlendMode::subtract, LayerBlendMode::hardMix, LayerBlendMode::hardLight}) {
|
||||
Scene scene(20, 10, 60, 40);
|
||||
scene.session.insert(filled(20, 10, qRgba(90, 140, 200, 255), "Ground"), QPointF(10, 5));
|
||||
scene.session.insert(filled(10, 6, qRgba(120, 60, 160, 255), "Top"), QPointF(8, 5));
|
||||
scene.session.setLayerBlendMode(mode);
|
||||
scene.session.zoom(1);
|
||||
const QImage exported = ImageExporter::render(scene.session.projectSnapshot().value()).image.convertToFormat(QImage::Format_ARGB32);
|
||||
const QImage shot = scene.shot();
|
||||
const QPointF origin = scene.origin();
|
||||
for (const QPoint at : {QPoint(5, 4), QPoint(16, 4)})
|
||||
QCOMPARE(shot.pixel(int(origin.x()) + at.x(), int(origin.y()) + at.y()), exported.pixel(at));
|
||||
QVERIFY(exported.pixel(5, 4) != qRgb(120, 60, 160) && exported.pixel(5, 4) != qRgb(90, 140, 200));
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasDisplayTests::blendModesMeetNoCheckerboardButTranslucencyDoes()
|
||||
{
|
||||
Scene multiplied(20, 10, 60, 40);
|
||||
|
||||
@@ -99,6 +99,13 @@ void ClippingStackTests::aStackBlendsOnceWithItsBasesMode()
|
||||
QCOMPARE(drawn.pixel(0, 0), qRgba(100, 50, 25, 255));
|
||||
QCOMPARE(drawn.pixel(1, 1), qRgba(200, 100, 50, 255));
|
||||
QCOMPARE(drawn.pixel(1, 0), qRgba(150, 75, 37, 255));
|
||||
// A hand-blended mode too, where Swift's group falls to Normal.
|
||||
scene.layers[base].blendMode = LayerBlendMode::linearBurn;
|
||||
const QImage burnt = scene.render({background, base, top});
|
||||
QCOMPARE(burnt.pixel(0, 0), qRgba(73, 0, 0, 255));
|
||||
QCOMPARE(burnt.pixel(1, 1), qRgba(200, 100, 50, 255));
|
||||
QCOMPARE(qRed(burnt.pixel(1, 0)), 136);
|
||||
QCOMPARE(qGreen(burnt.pixel(1, 0)), 50);
|
||||
}
|
||||
|
||||
void ClippingStackTests::laterChildrenCoverEarlierOnes()
|
||||
|
||||
@@ -319,7 +319,7 @@ void EditorCanvasTests::shiftPlusAndMinusCycleTheBlendModeAcrossTheWindow()
|
||||
const auto mode = [&] { return session.activeLayer().value().blendMode; };
|
||||
QCOMPARE(mode(), LayerBlendMode::normal);
|
||||
QTest::keyClick(canvas, Qt::Key_Plus, Qt::ShiftModifier);
|
||||
QCOMPARE(mode(), LayerBlendMode::multiply);
|
||||
QCOMPARE(mode(), LayerBlendMode::darken);
|
||||
QTest::keyClick(canvas, Qt::Key_Underscore, Qt::ShiftModifier);
|
||||
QCOMPARE(mode(), LayerBlendMode::normal);
|
||||
QTest::keyClick(canvas, Qt::Key_Underscore, Qt::ShiftModifier);
|
||||
|
||||
@@ -144,7 +144,8 @@ void GradientCanvasTests::escapeCancelsAndReturnApplies()
|
||||
shown.drag(QPointF(50, 150), QPointF(350, 150));
|
||||
QTest::keyClick(shown.canvas, Qt::Key_Return);
|
||||
QTRY_COMPARE(shown.session.history.undoCount(), count + 1);
|
||||
QVERIFY(!shown.session.gradientEdit());
|
||||
// The edit goes a turn after its step lands.
|
||||
QTRY_VERIFY(!shown.session.gradientEdit());
|
||||
shown.drag(QPointF(50, 150), QPointF(350, 150));
|
||||
QTest::keyClick(shown.canvas, Qt::Key_Enter);
|
||||
QTRY_COMPARE(shown.session.history.undoCount(), count + 2);
|
||||
|
||||
@@ -287,8 +287,9 @@ void LayerAppearanceTests::theBlendModeCyclesBothWaysAndWraps()
|
||||
const QUuid below = session.activeLayerID().value();
|
||||
session.insert(asset(0.8));
|
||||
const int count = session.history.undoCount();
|
||||
// Photoshop's order: the first darkening mode follows Normal.
|
||||
session.cycleBlendMode(true);
|
||||
QCOMPARE(session.activeLayer().value().blendMode, LayerBlendMode::multiply);
|
||||
QCOMPARE(session.activeLayer().value().blendMode, LayerBlendMode::darken);
|
||||
// The mode is the active layer's alone.
|
||||
QCOMPARE(layerWith(session, below).blendMode, LayerBlendMode::normal);
|
||||
session.cycleBlendMode(false);
|
||||
@@ -302,7 +303,7 @@ void LayerAppearanceTests::theBlendModeCyclesBothWaysAndWraps()
|
||||
session.setLayerBlendMode(LayerBlendMode::screen);
|
||||
session.previewBlendMode(LayerBlendMode::hue, top);
|
||||
session.cycleBlendMode(true);
|
||||
QCOMPARE(session.activeLayer().value().blendMode, LayerBlendMode::overlay);
|
||||
QCOMPARE(session.activeLayer().value().blendMode, LayerBlendMode::colorDodge);
|
||||
QVERIFY(!session.blendPreview().has_value());
|
||||
// A refused cycle leaves a preview where it was.
|
||||
session.previewBlendMode(LayerBlendMode::hue, top);
|
||||
@@ -311,8 +312,8 @@ void LayerAppearanceTests::theBlendModeCyclesBothWaysAndWraps()
|
||||
session.cycleBlendMode(true);
|
||||
QCOMPARE(session.blendPreview(), std::optional(EditorSession::BlendPreview{top, LayerBlendMode::hue}));
|
||||
QCOMPARE(int(changes.count()), 0);
|
||||
QCOMPARE(session.activeLayer().value().blendMode, LayerBlendMode::overlay);
|
||||
QCOMPARE(int(allLayerBlendModes.size()), 14);
|
||||
QCOMPARE(session.activeLayer().value().blendMode, LayerBlendMode::colorDodge);
|
||||
QCOMPARE(int(allLayerBlendModes.size()), 24);
|
||||
for (size_t index = 0; index < allLayerBlendModes.size(); ++index)
|
||||
QCOMPARE(int(allLayerBlendModes[index]), int(index));
|
||||
}
|
||||
|
||||
@@ -106,17 +106,17 @@ void PSDImportTests::importCreatesDocumentAndExistingCanvasGetsAGroup()
|
||||
void PSDImportTests::cancelledConversionLeavesTheDocumentUnchanged()
|
||||
{
|
||||
const QTemporaryDir folder;
|
||||
const QUrl url = photoshop(folder, "Vivid.psd", {layer("Vivid", QRectF(0, 0, 2, 2), "vLit")}, QSize(2, 2));
|
||||
const QUrl url = photoshop(folder, "Dissolved.psd", {layer("Dissolved", QRectF(0, 0, 2, 2), "diss")}, QSize(2, 2));
|
||||
EditorSession session;
|
||||
bool done = false;
|
||||
session.importImages({url}, std::nullopt, [&done] { done = true; });
|
||||
QVERIFY(session.showsConversionSheet() && session.conversionRequest().value().isReading);
|
||||
QTRY_VERIFY(!session.conversionRequest().value().isReading);
|
||||
const PSDConversionRequest &request = session.conversionRequest().value();
|
||||
QCOMPARE(request.title, QString("Open “Vivid.psd”?"));
|
||||
QCOMPARE(request.title, QString("Open “Dissolved.psd”?"));
|
||||
QCOMPARE(request.confirmTitle, QString("Import"));
|
||||
QCOMPARE(request.conversions.size(), size_t(1));
|
||||
QCOMPARE(request.conversions[0].layerName, QString("Vivid"));
|
||||
QCOMPARE(request.conversions[0].layerName, QString("Dissolved"));
|
||||
QVERIFY(session.isImporting() && !done);
|
||||
session.finishConversion(false);
|
||||
QVERIFY(!session.showsConversionSheet() && !session.conversionRequest());
|
||||
@@ -127,14 +127,14 @@ void PSDImportTests::cancelledConversionLeavesTheDocumentUnchanged()
|
||||
void PSDImportTests::aConfirmedConversionImportsAndCancelWhileReadingStops()
|
||||
{
|
||||
const QTemporaryDir folder;
|
||||
const QUrl url = photoshop(folder, "Vivid.psd", {layer("Vivid", QRectF(0, 0, 2, 2), "vLit")}, QSize(2, 2));
|
||||
const QUrl url = photoshop(folder, "Dissolved.psd", {layer("Dissolved", QRectF(0, 0, 2, 2), "diss")}, QSize(2, 2));
|
||||
EditorSession session;
|
||||
bool done = false;
|
||||
session.importImages({url}, std::nullopt, [&done] { done = true; });
|
||||
QTRY_VERIFY(session.conversionRequest() && !session.conversionRequest().value().isReading);
|
||||
session.finishConversion(true);
|
||||
QTRY_VERIFY(done);
|
||||
QCOMPARE(names(session), QStringList{"Vivid"});
|
||||
QCOMPARE(names(session), QStringList{"Dissolved"});
|
||||
QVERIFY(session.document().value().layers[0].blendMode == LayerBlendMode::normal);
|
||||
// Cancel while reading: nothing lands, nothing is said.
|
||||
EditorSession stopped;
|
||||
|
||||
@@ -103,7 +103,7 @@ private slots:
|
||||
void unusedSpotChannelsAreSkippedBeforeDecode();
|
||||
void unsupportedCompressionOnColorChannelsIsStillRejected();
|
||||
void matchesRequiresPhotoshopMagic();
|
||||
void unknownBlendProducesConversionReport();
|
||||
void unsupportedBlendProducesConversionReport();
|
||||
void softLightImportsWithoutConversion();
|
||||
void folderOpacityImportsOntoTheFolder();
|
||||
void unsupportedHeadersAreRejected();
|
||||
@@ -239,16 +239,26 @@ void PSDRoundTripTests::matchesRequiresPhotoshopMagic()
|
||||
QVERIFY(PSDReader::matches(QByteArray("8BPS")) && !PSDReader::matches(QByteArray("8BP")));
|
||||
}
|
||||
|
||||
void PSDRoundTripTests::unknownBlendProducesConversionReport()
|
||||
void PSDRoundTripTests::unsupportedBlendProducesConversionReport()
|
||||
{
|
||||
// Dissolve scatters pixels by opacity: no twin, so Normal.
|
||||
const QImage fill = PSDFixture::colorImage(2, 2, 1, 0, 0);
|
||||
PSDRecord layer = PSDFixture::record("Vivid", fill, QRectF(0, 0, 2, 2));
|
||||
layer.blendKey = "vLit";
|
||||
PSDRecord layer = PSDFixture::record("Dissolved", fill, QRectF(0, 0, 2, 2));
|
||||
layer.blendKey = "diss";
|
||||
const PSDImport imported = PSDDocumentBuilder::makeImport(PSDReader::read(PSDFixture::data(PSDDocument{2, 2, 72, {layer}}, fill)));
|
||||
QCOMPARE(imported.conversions.size(), size_t(1));
|
||||
QCOMPARE(imported.conversions[0].layerName, QString("Vivid"));
|
||||
QCOMPARE(imported.conversions[0].message, QString("Blend mode “vLit” isn’t supported and will be applied as Normal."));
|
||||
QCOMPARE(imported.conversions[0].layerName, QString("Dissolved"));
|
||||
QCOMPARE(imported.conversions[0].message, QString("Blend mode “diss” isn’t supported and will be applied as Normal."));
|
||||
QVERIFY(imported.layers[0].blendMode == LayerBlendMode::normal);
|
||||
for (const char *key : {"dkCl", "lgCl"})
|
||||
QVERIFY2(!fromPSD(key), key);
|
||||
const std::vector<std::pair<const char *, LayerBlendMode>> keys{
|
||||
{"lbrn", LayerBlendMode::linearBurn}, {"lddg", LayerBlendMode::linearDodge}, {"hLit", LayerBlendMode::hardLight},
|
||||
{"vLit", LayerBlendMode::vividLight}, {"lLit", LayerBlendMode::linearLight}, {"pLit", LayerBlendMode::pinLight},
|
||||
{"hMix", LayerBlendMode::hardMix}, {"smud", LayerBlendMode::exclusion}, {"fsub", LayerBlendMode::subtract},
|
||||
{"fdiv", LayerBlendMode::divide}};
|
||||
for (const auto &[key, mode] : keys)
|
||||
QCOMPARE(fromPSD(key), std::optional(mode));
|
||||
}
|
||||
|
||||
void PSDRoundTripTests::softLightImportsWithoutConversion()
|
||||
|
||||
@@ -202,8 +202,9 @@ void ProjectManifestTests::encodingWritesSwiftsKeysAndLeavesAbsentOnesOut()
|
||||
|
||||
void ProjectManifestTests::everyRawValueRoundTrips()
|
||||
{
|
||||
const QStringList blends{"Normal", "Multiply", "Screen", "Overlay", "Soft Light", "Darken", "Lighten", "Difference", "Color Dodge", "Color Burn",
|
||||
"Hue", "Saturation", "Color", "Luminosity"};
|
||||
const QStringList blends{"Normal", "Darken", "Multiply", "Color Burn", "Linear Burn", "Lighten", "Screen", "Color Dodge",
|
||||
"Linear Dodge (Add)", "Overlay", "Soft Light", "Hard Light", "Vivid Light", "Linear Light", "Pin Light", "Hard Mix",
|
||||
"Difference", "Exclusion", "Subtract", "Divide", "Hue", "Saturation", "Color", "Luminosity"};
|
||||
for (int index = 0; index < blends.size(); ++index) {
|
||||
QCOMPARE(rawValue(LayerBlendMode(index)), blends[index]);
|
||||
QCOMPARE(layerBlendMode(blends[index]), std::optional(LayerBlendMode(index)));
|
||||
|
||||
Reference in new issue
Block a user