402 lines
20 KiB
C++
402 lines
20 KiB
C++
#include "CameraRawFixtures.h"
|
||
#include "Document/Filters.h"
|
||
#include <QtTest>
|
||
#include <cmath>
|
||
|
||
// Swift's CameraRawTests: the grade on pixels, group by group.
|
||
using namespace CameraRawFixtures;
|
||
|
||
class CameraRawTests : public QObject {
|
||
Q_OBJECT
|
||
private slots:
|
||
void defaultsLeavePixelsAndAlphaAlone();
|
||
void exposureAndContrastMatchPhotoshop();
|
||
void tonalSlidersMoveTheEndTheyName();
|
||
void temperatureWarmsAndTintMovesTowardMagenta();
|
||
void whiteBalanceAndVibranceMatchPhotoshop();
|
||
void mixerCalibrationCurveAndVignettesMatchPhotoshop();
|
||
void vibranceFavorsDullColorsAndProtectsSkinWhileSaturationDoesNot();
|
||
void textureAndClaritySharpenAnEdgeAndLeaveAFlatField();
|
||
void dehazeDeepensOrLiftsAndKeepsAlpha();
|
||
void glowIsIdleAtZeroAndHalationFringeIsRedderThanDiffusion();
|
||
void vignetteDarkensCornersAndHighlightsOnlyWhileDarkening();
|
||
void grainIsStableAndTheEffectsEyeDropsTheWholeGroup();
|
||
void histogramFollowsTheGradeAndClippingPaintStaysOffTheResult();
|
||
void curveMixerAndGradingChangeOnlyTheirOwnTones();
|
||
void detailSharpeningNoiseAndMaskingPreview();
|
||
void opticsDistortionDefringeAndDetailEye();
|
||
};
|
||
|
||
void CameraRawTests::defaultsLeavePixelsAndAlphaAlone()
|
||
{
|
||
const QImage input = gray(4, 4, 0.5);
|
||
QVERIFY(pixels(CameraRawSettings().apply(input)) == pixels(input));
|
||
CameraRawSettings broken;
|
||
broken.exposure = std::nan("");
|
||
broken.temperature = 400;
|
||
QVERIFY(broken.normalized().exposure == 0 && broken.normalized().temperature == 100);
|
||
QVERIFY(!isImageAdjustment(FilterKind::cameraRaw));
|
||
}
|
||
|
||
// Photoshop's Camera Raw Filter's own numbers, measured.
|
||
void CameraRawTests::exposureAndContrastMatchPhotoshop()
|
||
{
|
||
const QImage input = gray();
|
||
CameraRawSettings settings;
|
||
settings.exposure = 1;
|
||
const std::array<int, 4> brighter = pixels(settings.apply(input))[0];
|
||
QVERIFY2(std::abs(brighter[0] - 181) <= 2, qPrintable(QString::number(brighter[0])));
|
||
QVERIFY(brighter[0] == brighter[1] && brighter[1] == brighter[2]);
|
||
const QImage translucent = gray(4, 4, 0.5);
|
||
QVERIFY((pixels(settings.apply(translucent))[0] == std::array{181, 181, 181, pixels(translucent)[0][3]}));
|
||
settings = CameraRawSettings();
|
||
settings.contrast = 100;
|
||
const auto pushed = pixels(settings.apply(pair(64, 192)));
|
||
QVERIFY2(std::abs(pushed[0][0] - 36) <= 3 && std::abs(pushed[1][0] - 213) <= 3, "contrast +100 drives the pair apart");
|
||
settings.contrast = -100;
|
||
const auto flat = pixels(settings.apply(pair(64, 192)));
|
||
QVERIFY2(std::abs(flat[0][0] - 88) <= 3 && std::abs(flat[1][0] - 174) <= 3, "contrast −100 draws them together");
|
||
}
|
||
|
||
void CameraRawTests::tonalSlidersMoveTheEndTheyName()
|
||
{
|
||
const QImage brightAndMid = pair(230, 128);
|
||
CameraRawSettings settings;
|
||
settings.highlights = -100;
|
||
const auto recovered = pixels(settings.apply(brightAndMid));
|
||
// Photoshop: 230 to 209, mid gray kept.
|
||
QVERIFY(recovered[0][0] < 222 && std::abs(recovered[1][0] - 128) <= 4);
|
||
// Highlights alone reads the picture's log brightness.
|
||
const auto bright = pixels(settings.apply(pair(220, 250)));
|
||
QVERIFY((bright[0] == std::array{197, 197, 197, 255}) && (bright[1] == std::array{239, 240, 240, 255}));
|
||
// Highlights before Whites, as Camera Raw orders them.
|
||
settings.whites = 100;
|
||
const auto both = pixels(settings.apply(pair(128, 230)));
|
||
QVERIFY(both[0][0] == 179 && both[1][0] == 255);
|
||
settings = CameraRawSettings();
|
||
settings.whites = 100;
|
||
const auto clipped = pixels(settings.apply(brightAndMid));
|
||
QVERIFY2(clipped[0][0] == 255 && clipped[1][0] > 128, "whites lift the midtone too, as in Photoshop");
|
||
const auto view = pixels(settings.apply(brightAndMid, CameraRawClipping::highlights));
|
||
QVERIFY2((view[0] == std::array{255, 255, 255, 255}) && (view[1] == std::array{0, 0, 0, 255}), "highlight clipping is not the grade");
|
||
const QImage darkAndMid = pair(20, 128);
|
||
settings = CameraRawSettings();
|
||
settings.shadows = 100;
|
||
const auto opened = pixels(settings.apply(darkAndMid));
|
||
// Photoshop: 20 to 33, mid gray kept.
|
||
QVERIFY(opened[0][0] > 22 && std::abs(opened[1][0] - 128) <= 6);
|
||
settings = CameraRawSettings();
|
||
settings.blacks = -100;
|
||
const auto crushed = pixels(settings.apply(darkAndMid));
|
||
QVERIFY2(crushed[0][0] == 0 && std::abs(crushed[1][0] - 91) <= 3, "blacks darken the midtone too");
|
||
const auto shadowView = pixels(settings.apply(darkAndMid, CameraRawClipping::shadows));
|
||
QVERIFY2((shadowView[0] == std::array{0, 0, 0, 255}) && (shadowView[1] == std::array{255, 255, 255, 255}), "shadow clipping is not the grade");
|
||
}
|
||
|
||
void CameraRawTests::temperatureWarmsAndTintMovesTowardMagenta()
|
||
{
|
||
const QImage input = gray();
|
||
CameraRawSettings settings;
|
||
settings.temperature = 100;
|
||
const std::array<int, 4> warm = pixels(settings.apply(input))[0];
|
||
QVERIFY(warm[0] > 128 && warm[2] < 128 && warm[0] > warm[2]);
|
||
settings = CameraRawSettings();
|
||
settings.tint = 100;
|
||
const std::array<int, 4> magenta = pixels(settings.apply(input))[0];
|
||
QVERIFY(magenta[1] < 128 && magenta[1] < magenta[0] && magenta[1] < magenta[2]);
|
||
}
|
||
|
||
// Once a fraction of Photoshop's strength (Swift's issue #252).
|
||
void CameraRawTests::whiteBalanceAndVibranceMatchPhotoshop()
|
||
{
|
||
struct Case {
|
||
const char *name;
|
||
std::array<int, 3> input;
|
||
std::function<void(CameraRawSettings &)> adjust;
|
||
std::array<int, 3> expected;
|
||
};
|
||
const std::vector<Case> cases{
|
||
{"temperature +100", {128, 128, 128}, [](CameraRawSettings &s) { s.temperature = 100; }, {235, 189, 117}},
|
||
{"temperature −50", {128, 128, 128}, [](CameraRawSettings &s) { s.temperature = -50; }, {102, 146, 210}},
|
||
{"tint +100", {128, 128, 128}, [](CameraRawSettings &s) { s.tint = 100; }, {159, 125, 215}},
|
||
{"tint −100", {128, 128, 128}, [](CameraRawSettings &s) { s.tint = -100; }, {98, 198, 120}},
|
||
{"vibrance +100 spares skin", {153, 107, 107}, [](CameraRawSettings &s) { s.vibrance = 100; }, {167, 98, 98}},
|
||
{"vibrance +100 fills a dull cyan", {107, 153, 153}, [](CameraRawSettings &s) { s.vibrance = 100; }, {0, 160, 160}},
|
||
{"vibrance −100", {92, 92, 230}, [](CameraRawSettings &s) { s.vibrance = -100; }, {164, 164, 210}},
|
||
};
|
||
for (const Case &test : cases) {
|
||
CameraRawSettings settings;
|
||
test.adjust(settings);
|
||
const std::array<int, 4> got = pixels(settings.apply(image(4, 4, test.input[0] / 255.0, test.input[1] / 255.0, test.input[2] / 255.0)))[0];
|
||
for (int c = 0; c < 3; ++c)
|
||
QVERIFY2(std::abs(got[c] - test.expected[c]) <= 4, test.name);
|
||
}
|
||
}
|
||
|
||
// Photoshop's own measured numbers, as Swift's test holds them.
|
||
void CameraRawTests::mixerCalibrationCurveAndVignettesMatchPhotoshop()
|
||
{
|
||
struct Case {
|
||
const char *name;
|
||
std::array<int, 3> input;
|
||
std::function<void(CameraRawSettings &)> adjust;
|
||
std::array<int, 3> expected;
|
||
};
|
||
const std::vector<Case> cases{
|
||
{"orange saturation −60", {230, 115, 0}, [](CameraRawSettings &s) { s.mixer.saturation[1] = -60; }, {189, 130, 107}},
|
||
{"red hue +60", {230, 92, 92}, [](CameraRawSettings &s) { s.mixer.hue[0] = 60; }, {227, 125, 83}},
|
||
{"blue luminance +60", {92, 92, 230}, [](CameraRawSettings &s) { s.mixer.luminance[5] = 60; }, {142, 142, 248}},
|
||
{"blue primary saturation −60", {92, 92, 230}, [](CameraRawSettings &s) { s.calibration.blueSaturation = -60; }, {149, 149, 226}},
|
||
{"red primary hue +60", {230, 92, 92}, [](CameraRawSettings &s) { s.calibration.redHue = 60; }, {228, 118, 37}},
|
||
{"darks −60", {130, 130, 130}, [](CameraRawSettings &s) { s.curve.darks = -60; }, {101, 101, 101}},
|
||
};
|
||
for (const Case &test : cases) {
|
||
CameraRawSettings settings;
|
||
test.adjust(settings);
|
||
const std::array<int, 4> got = pixels(settings.apply(image(4, 4, test.input[0] / 255.0, test.input[1] / 255.0, test.input[2] / 255.0)))[0];
|
||
for (int c = 0; c < 3; ++c)
|
||
QVERIFY2(std::abs(got[c] - test.expected[c]) <= 4, test.name);
|
||
}
|
||
// Mid gray, 3:2, a corner: the lens lifts, post-crop darkens.
|
||
const QImage wide = image(300, 200, 128 / 255.0, 128 / 255.0, 128 / 255.0);
|
||
CameraRawSettings lens;
|
||
lens.optics.vignetteAmount = 100;
|
||
QVERIFY(std::abs(pixels(lens.apply(wide))[0][0] - 223) <= 6);
|
||
CameraRawSettings postCrop;
|
||
postCrop.vignetteAmount = -60;
|
||
QVERIFY(std::abs(pixels(postCrop.apply(wide))[0][0] - 30) <= 6);
|
||
}
|
||
|
||
void CameraRawTests::vibranceFavorsDullColorsAndProtectsSkinWhileSaturationDoesNot()
|
||
{
|
||
const QImage dullGreen = image(4, 4, 77 / 255.0, 153 / 255.0, 77 / 255.0), saturatedGreen = image(4, 4, 20 / 255.0, 200 / 255.0, 20 / 255.0);
|
||
const QImage skin = image(4, 4, 153 / 255.0, 115 / 255.0, 77 / 255.0);
|
||
CameraRawSettings settings;
|
||
settings.vibrance = 100;
|
||
const int dullBefore = chroma(pixels(dullGreen)[0]), saturatedBefore = chroma(pixels(saturatedGreen)[0]), skinBefore = chroma(pixels(skin)[0]);
|
||
const int dullDelta = chroma(pixels(settings.apply(dullGreen))[0]) - dullBefore;
|
||
const int saturatedDelta = chroma(pixels(settings.apply(saturatedGreen))[0]) - saturatedBefore;
|
||
const int skinDelta = chroma(pixels(settings.apply(skin))[0]) - skinBefore;
|
||
QVERIFY(dullDelta > saturatedDelta + 10);
|
||
QCOMPARE(dullBefore, skinBefore);
|
||
QVERIFY(dullDelta > skinDelta + 10);
|
||
const QImage red = image(4, 4, 160 / 255.0, 120 / 255.0, 120 / 255.0), blue = image(4, 4, 100 / 255.0, 100 / 255.0, 140 / 255.0);
|
||
settings = CameraRawSettings();
|
||
settings.saturation = 100;
|
||
const double redRatio = double(chroma(pixels(settings.apply(red))[0])) / chroma(pixels(red)[0]);
|
||
const double blueRatio = double(chroma(pixels(settings.apply(blue))[0])) / chroma(pixels(blue)[0]);
|
||
// Photoshop doubles chroma in linear light: less in sRGB.
|
||
QVERIFY2(std::abs(redRatio - 1.85) < 0.15 && std::abs(blueRatio - 1.73) < 0.15, "saturation raises both");
|
||
}
|
||
|
||
void CameraRawTests::textureAndClaritySharpenAnEdgeAndLeaveAFlatField()
|
||
{
|
||
const QImage flat = gray();
|
||
CameraRawSettings settings;
|
||
settings.texture = 100;
|
||
settings.clarity = 100;
|
||
QVERIFY2(pixels(settings.apply(flat)) == pixels(flat), "a flat field has no local contrast");
|
||
const QImage edge = step();
|
||
const auto original = pixels(edge);
|
||
settings = CameraRawSettings();
|
||
settings.texture = 100;
|
||
const auto textured = pixels(settings.apply(edge));
|
||
QCOMPARE(textured[0][0], original[0][0]);
|
||
QVERIFY2(textured[8][0] == original[8][0], "texture's fine radius does not reach this far");
|
||
QVERIFY(textured[11][0] != original[11][0]);
|
||
settings = CameraRawSettings();
|
||
settings.clarity = 100;
|
||
const auto clarified = pixels(settings.apply(edge));
|
||
QCOMPARE(clarified[0][0], original[0][0]);
|
||
QVERIFY2(clarified[8][0] != original[8][0], "clarity's wider radius reaches further in");
|
||
settings.clarity = -100;
|
||
const auto softened = pixels(settings.apply(edge));
|
||
QVERIFY(std::abs(softened[12][0] - softened[11][0]) < std::abs(original[12][0] - original[11][0]));
|
||
}
|
||
|
||
void CameraRawTests::dehazeDeepensOrLiftsAndKeepsAlpha()
|
||
{
|
||
// A shadow under a pale sky; one colour, no haze.
|
||
QImage skyAndShadow = BrushRaster::context(10, 10, false);
|
||
{
|
||
QPainter painter(&skyAndShadow);
|
||
painter.fillRect(QRect(0, 0, 10, 5), QColor(200, 195, 190));
|
||
painter.fillRect(QRect(0, 5, 10, 5), QColor(30, 30, 30));
|
||
}
|
||
const int shadow = 5 * 10;
|
||
QCOMPARE(pixels(skyAndShadow)[shadow][0], 30);
|
||
CameraRawSettings settings;
|
||
settings.dehaze = 100;
|
||
QVERIFY(pixels(settings.apply(skyAndShadow))[shadow][0] < 30);
|
||
settings.dehaze = -100;
|
||
QVERIFY(pixels(settings.apply(skyAndShadow))[shadow][0] > 30);
|
||
const QImage pale = image(4, 4, 180 / 255.0, 150 / 255.0, 150 / 255.0);
|
||
for (const double amount : {100.0, -100.0}) {
|
||
settings.dehaze = amount;
|
||
QVERIFY((pixels(settings.apply(pale))[0] == std::array{180, 150, 150, 255}));
|
||
}
|
||
const QImage translucent = image(4, 4, 30 / 255.0, 30 / 255.0, 30 / 255.0, 0.5);
|
||
settings.dehaze = 100;
|
||
QCOMPARE(pixels(settings.apply(translucent))[0][3], pixels(translucent)[0][3]);
|
||
}
|
||
|
||
void CameraRawTests::glowIsIdleAtZeroAndHalationFringeIsRedderThanDiffusion()
|
||
{
|
||
QImage spot = image(21, 21, 0, 0, 0);
|
||
for (int y = 8; y < 13; ++y)
|
||
for (int x = 8; x < 13; ++x)
|
||
spot.setPixelColor(x, y, Qt::white);
|
||
CameraRawSettings settings;
|
||
settings.glowWarmth = 100;
|
||
settings.glowRange = 100;
|
||
QVERIFY2(pixels(settings.apply(spot)) == pixels(spot), "range and warmth do nothing until Glow is raised");
|
||
settings.glow = 100;
|
||
settings.glowStyle = CameraRawGlowStyle::diffusion;
|
||
const auto diffusion = pixels(settings.apply(spot));
|
||
settings.glowStyle = CameraRawGlowStyle::halation;
|
||
const auto halation = pixels(settings.apply(spot));
|
||
const size_t fringe = 10 * 21 + 15;
|
||
QVERIFY(diffusion[fringe][0] > diffusion[0][0]);
|
||
QVERIFY((halation[0] == std::array{0, 0, 0, 255}));
|
||
QVERIFY(halation[fringe][0] - halation[fringe][1] > diffusion[fringe][0] - diffusion[fringe][1]);
|
||
}
|
||
|
||
void CameraRawTests::vignetteDarkensCornersAndHighlightsOnlyWhileDarkening()
|
||
{
|
||
const QImage field = gray(9, 9);
|
||
CameraRawSettings settings;
|
||
settings.vignetteAmount = -100;
|
||
const auto darkened = pixels(settings.apply(field));
|
||
QVERIFY(std::abs(darkened[4 * 9 + 4][0] - 128) <= 2 && darkened[0][0] < darkened[4 * 9 + 4][0] - 40);
|
||
const QImage white = image(9, 9, 1, 1, 1);
|
||
settings.vignetteHighlights = 100;
|
||
settings.vignetteStyle = CameraRawVignetteStyle::highlightPriority;
|
||
const int kept = pixels(settings.apply(white))[0][0];
|
||
settings.vignetteHighlights = 0;
|
||
const int exposed = pixels(settings.apply(white))[0][0];
|
||
QVERIFY(kept > exposed + 40);
|
||
settings.vignetteHighlights = 100;
|
||
settings.vignetteStyle = CameraRawVignetteStyle::paintOverlay;
|
||
QVERIFY2(pixels(settings.apply(white))[0][0] < kept, "paint overlay does not use Highlights");
|
||
settings = CameraRawSettings();
|
||
settings.vignetteAmount = 100;
|
||
const auto plain = pixels(settings.apply(field));
|
||
settings.vignetteHighlights = 100;
|
||
QVERIFY2(plain == pixels(settings.apply(field)), "highlights is idle while the vignette lightens");
|
||
}
|
||
|
||
void CameraRawTests::grainIsStableAndTheEffectsEyeDropsTheWholeGroup()
|
||
{
|
||
const QImage field = gray(16, 16);
|
||
CameraRawSettings settings;
|
||
settings.grainSize = 40;
|
||
settings.grainRoughness = 80;
|
||
QVERIFY(pixels(settings.apply(field, std::nullopt, 1, 4)) == pixels(field));
|
||
settings.grainAmount = 70;
|
||
const auto first = pixels(settings.apply(field, std::nullopt, 1, 4));
|
||
QVERIFY(first == pixels(settings.apply(field, std::nullopt, 1, 4)));
|
||
QVERIFY(first != pixels(field));
|
||
QVERIFY(first[0][0] == first[0][1] && first[0][1] == first[0][2]);
|
||
QCOMPARE(pixels(settings.apply(image(4, 4, 0.5, 0.5, 0.5, 0), std::nullopt, 1, 4))[0][3], 0);
|
||
const QImage edge = step();
|
||
settings = CameraRawSettings();
|
||
settings.texture = 100;
|
||
settings.grainAmount = 50;
|
||
const CameraRawSettings hidden = settings.applying({.effects = false});
|
||
QVERIFY(hidden.isIdentity());
|
||
QVERIFY(pixels(hidden.apply(edge, std::nullopt, 1, 2)) == pixels(edge));
|
||
QVERIFY(pixels(settings.apply(edge, std::nullopt, 1, 2)) != pixels(edge));
|
||
}
|
||
|
||
void CameraRawTests::histogramFollowsTheGradeAndClippingPaintStaysOffTheResult()
|
||
{
|
||
const QImage black = image(4, 4, 0, 0, 0), white = image(4, 4, 1, 1, 1);
|
||
QCOMPARE(peakIndex(CameraRawScope::make(black).value().red), 0);
|
||
QCOMPARE(peakIndex(CameraRawScope::make(white).value().red), 255);
|
||
CameraRawSettings settings;
|
||
settings.exposure = 1;
|
||
QVERIFY(peakIndex(CameraRawScope::make(settings.apply(gray())).value().red) > 128);
|
||
const std::array<int, 4> shadowed = pixels(CameraRawScope::overlay(black, true, false))[0];
|
||
QVERIFY(shadowed[2] > shadowed[0]);
|
||
const std::array<int, 4> highlighted = pixels(CameraRawScope::overlay(white, false, true))[0];
|
||
QVERIFY(highlighted[0] > highlighted[2]);
|
||
QVERIFY(pixels(CameraRawScope::overlay(black, false, false)) == pixels(black));
|
||
const CameraRawScope scope = CameraRawScope::make(image(4, 4, 1, 0, 0)).value();
|
||
const auto hottest = std::max_element(scope.vectorscope.begin(), scope.vectorscope.end()) - scope.vectorscope.begin();
|
||
QVERIFY2(hottest % CameraRawScope::scopeSide > CameraRawScope::scopeSide / 2, "red sits on the right of the vectorscope");
|
||
}
|
||
|
||
void CameraRawTests::curveMixerAndGradingChangeOnlyTheirOwnTones()
|
||
{
|
||
const QImage dark = image(4, 4, 0.12, 0.12, 0.12), light = image(4, 4, 0.62, 0.62, 0.62);
|
||
CameraRawSettings settings;
|
||
settings.curve.shadows = 100;
|
||
const int darkGain = pixels(settings.apply(dark))[0][0] - int(std::lround(0.12 * 255));
|
||
const int lightGain = pixels(settings.apply(light))[0][0] - int(std::lround(0.62 * 255));
|
||
QVERIFY(darkGain > lightGain + 8);
|
||
settings = CameraRawSettings();
|
||
settings.curve.rgb = CameraRawCurveSettings::strongContrast();
|
||
QVERIFY(pixels(settings.apply(image(4, 4, 0.25, 0.25, 0.25)))[0][0] < 55);
|
||
settings = CameraRawSettings();
|
||
settings.mixer.hue[0] = 100;
|
||
const std::array<int, 4> shifted = pixels(settings.apply(image(4, 4, 1, 0, 0)))[0];
|
||
QVERIFY(shifted[1] > shifted[2]);
|
||
settings = CameraRawSettings();
|
||
settings.grading.shadows.saturation = 100;
|
||
const std::array<int, 4> gradedDark = pixels(settings.apply(dark))[0];
|
||
const std::array<int, 4> gradedLight = pixels(settings.apply(image(4, 4, 1, 1, 1)))[0];
|
||
QVERIFY(gradedDark[0] > gradedDark[1] + 5 && std::abs(gradedLight[0] - gradedLight[1]) <= 2);
|
||
settings.grading.balance = 100;
|
||
const std::array<int, 4> balanced = pixels(settings.apply(dark))[0];
|
||
QVERIFY(balanced[0] - balanced[1] < gradedDark[0] - gradedDark[1]);
|
||
settings = CameraRawSettings();
|
||
settings.curve.shadows = 100;
|
||
QVERIFY(pixels(settings.applying({.curve = false}).apply(dark)) == pixels(dark));
|
||
}
|
||
|
||
void CameraRawTests::detailSharpeningNoiseAndMaskingPreview()
|
||
{
|
||
const QImage edge = step();
|
||
CameraRawSettings settings;
|
||
settings.detail.sharpenAmount = 150;
|
||
settings.detail.sharpenRadius = 50;
|
||
QVERIFY(pixels(settings.apply(edge)) != pixels(edge));
|
||
// A faint step sharpens too: Masking 0 masks nothing.
|
||
QImage faint = BrushRaster::context(24, 4, false);
|
||
for (int y = 0; y < 4; ++y)
|
||
for (int x = 0; x < 24; ++x)
|
||
faint.setPixelColor(x, y, x < 12 ? QColor(128, 128, 128) : QColor(136, 136, 136));
|
||
const auto sharpened = pixels(settings.apply(faint));
|
||
QVERIFY2(sharpened[11][0] == 121 && sharpened[12][0] == 143, qPrintable(QStringLiteral("%1, %2").arg(sharpened[11][0]).arg(sharpened[12][0])));
|
||
settings = CameraRawSettings();
|
||
settings.detail.noiseLuminance = 80;
|
||
QVERIFY2(pixels(settings.apply(gray(8, 8))) == pixels(gray(8, 8)), "luminance NR leaves a flat field alone");
|
||
settings.detail.sharpenMasking = 50;
|
||
const auto mask = pixels(settings.apply(edge, std::nullopt, 1, 0, -1, true));
|
||
QVERIFY(std::all_of(mask.begin(), mask.end(), [](const std::array<int, 4> &p) { return p[0] == p[1] && p[1] == p[2]; }));
|
||
QVERIFY(settings.detail.adjusts());
|
||
}
|
||
|
||
void CameraRawTests::opticsDistortionDefringeAndDetailEye()
|
||
{
|
||
const QImage stepped = checker();
|
||
CameraRawSettings settings;
|
||
settings.optics.distortion = 100;
|
||
QVERIFY2(pixels(settings.apply(stepped)) != pixels(stepped), "distortion resamples pixels");
|
||
const QImage purple = image(4, 4, 0.8, 0.2, 0.9);
|
||
settings = CameraRawSettings();
|
||
settings.optics.purpleAmount = 100;
|
||
settings.optics.purpleHueLow = 250;
|
||
settings.optics.purpleHueHigh = 320;
|
||
QVERIFY(chroma(pixels(settings.apply(purple))[0]) < chroma(pixels(purple)[0]));
|
||
settings = CameraRawSettings();
|
||
settings.optics.removeChromaticAberration = true;
|
||
QVERIFY(settings.optics.adjusts());
|
||
settings = CameraRawSettings();
|
||
settings.detail.sharpenAmount = 40;
|
||
QVERIFY(pixels(settings.applying({.detail = false}).apply(step())) == pixels(step()));
|
||
}
|
||
|
||
QTEST_GUILESS_MAIN(CameraRawTests)
|
||
#include "CameraRawTests.moc"
|