14.27: Camera Raw's Color Mixer, Calibration, curve, vignettes and Color Grading match Photoshop
This commit is contained in:
26 files changed
+375
-113
No files matched your search
@@ -126,13 +126,13 @@ void CameraRawColorTests::gradingWheelsAndBalanceReachTheKernel()
|
||||
// Balance runs in hundredths: half is between none and all.
|
||||
const int none = shadowTint(0), half = shadowTint(50), all = shadowTint(100);
|
||||
QVERIFY2(none > half && half > all, qPrintable(QStringLiteral("%1 %2 %3").arg(none).arg(half).arg(all)));
|
||||
// Luminance runs in hundredths too: measured once, then held.
|
||||
// Luminance comes from Photoshop's tables: Python's own lookup.
|
||||
const auto lifted = [](double luminance) {
|
||||
CameraRawSettings settings;
|
||||
settings.grading.shadows.luminance = luminance;
|
||||
return pixels(settings.apply(image(4, 4, 0.12, 0.12, 0.12)))[0][0];
|
||||
};
|
||||
QVERIFY(lifted(0) == 31 && lifted(50) == 53 && lifted(100) == 76);
|
||||
QVERIFY(lifted(0) == 31 && lifted(50) == 46 && lifted(100) == 56);
|
||||
}
|
||||
|
||||
void CameraRawColorTests::aPointColorReachesOnlyItsRange()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "CameraRawFixtures.h"
|
||||
#include <QtTest>
|
||||
#include <functional>
|
||||
#include <cmath>
|
||||
|
||||
// Swift 1.4.5: Camera Raw's curves as Photoshop's, the model.
|
||||
@@ -161,8 +162,10 @@ void CameraRawCurveTests::eachRegionBendsOnlyItsRange()
|
||||
|
||||
void CameraRawCurveTests::theToneTableRunsThroughThePointCurve()
|
||||
{
|
||||
// Dividers moved: the formula draws the parametric curve.
|
||||
CameraRawCurveSettings curve;
|
||||
curve.lights = 59;
|
||||
curve.shadowSplit = 30;
|
||||
curve.rgb = {{0, 0}, {0.5, 0.6}, {1, 1}};
|
||||
const std::vector<float> table = curve.toneTable();
|
||||
QCOMPARE(table.size(), size_t(256));
|
||||
@@ -175,6 +178,17 @@ void CameraRawCurveTests::theToneTableRunsThroughThePointCurve()
|
||||
QVERIFY2(std::abs(table[size_t(index)] - expected) < 0.01, qPrintable(QString::number(index)));
|
||||
}
|
||||
QCOMPARE(table[255], 1.0f);
|
||||
// As measured, the tables draw it: the point curve alone.
|
||||
CameraRawCurveSettings measured = curve;
|
||||
measured.shadowSplit = 25;
|
||||
QVERIFY(measured.hasMeasuredDividers() && measured.toneTable() == point.toneTable());
|
||||
for (const auto &change : std::vector<std::function<void(CameraRawCurveSettings &)>>{[](CameraRawCurveSettings &c) { c.darkSplit = 49; },
|
||||
[](CameraRawCurveSettings &c) { c.lightSplit = 76; },
|
||||
[](CameraRawCurveSettings &c) { c.refineSaturation = 1; }}) {
|
||||
CameraRawCurveSettings moved = measured;
|
||||
change(moved);
|
||||
QVERIFY(!moved.hasMeasuredDividers());
|
||||
}
|
||||
}
|
||||
|
||||
void CameraRawCurveTests::refineSaturationEasesOrStrengthens()
|
||||
@@ -183,6 +197,7 @@ void CameraRawCurveTests::refineSaturationEasesOrStrengthens()
|
||||
CameraRawSettings settings;
|
||||
settings.curve.darks = -51;
|
||||
settings.curve.lights = 59;
|
||||
settings.curve.darkSplit = 45;
|
||||
// At zero each channel runs through the tone curve alone.
|
||||
const std::vector<float> table = settings.curve.toneTable();
|
||||
const std::array<int, 4> source = pixels(orange)[0], curved = pixels(settings.apply(orange))[0];
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "CameraRawFixtures.h"
|
||||
#include "Document/CameraRawTables.h"
|
||||
#include <QtTest>
|
||||
#include <cmath>
|
||||
extern "C" {
|
||||
@@ -178,8 +179,11 @@ void CameraRawLensTests::calibrationRunsAtItsProcessVersion()
|
||||
for (const CameraRawProcessVersion version : {CameraRawProcessVersion::version1, CameraRawProcessVersion::version4, CameraRawProcessVersion::version6}) {
|
||||
CameraRawSettings settings;
|
||||
settings.calibration = {version, 20, 80, 30, -40, 60, -70, 50};
|
||||
// Shadows Tint by the kernel; the primaries from Photoshop's tables.
|
||||
QImage direct = red.copy();
|
||||
adjust_camera_raw_calibration(direct.bits(), 3, 1, size_t(direct.bytesPerLine()), 20, 80, 30, -40, 60, -70, 50, int(version) + 1);
|
||||
adjust_camera_raw_calibration(direct.bits(), 3, 1, size_t(direct.bytesPerLine()), 20, 0, 0, 0, 0, 0, 0, int(version) + 1);
|
||||
const std::vector<float> table = CameraRawTables::compose(CameraRawTables::stages(settings.normalized(), {}));
|
||||
adjust_camera_raw(direct.bits(), 3, 1, size_t(direct.bytesPerLine()), table.data(), CameraRawTables::grid, 0);
|
||||
QVERIFY(pixels(settings.apply(red)) == pixels(direct));
|
||||
}
|
||||
QCOMPARE(rawValue(CameraRawProcessVersion::version3), QString("Version 3"));
|
||||
|
||||
@@ -102,8 +102,10 @@ void CameraRawRulesTests::anUntouchedGradeHandsTheImageBack()
|
||||
void CameraRawRulesTests::theClippingAndMaskViewsShowNothingElse()
|
||||
{
|
||||
const QImage picture = colourful();
|
||||
// Calibration's primaries ride in the Light table, as Swift's.
|
||||
CameraRawSettings light;
|
||||
light.whites = 100;
|
||||
light.calibration.redHue = 80;
|
||||
const CameraRawSettings all = everything();
|
||||
QVERIFY(pixels(all.apply(picture, CameraRawClipping::highlights)) == pixels(light.apply(picture, CameraRawClipping::highlights)));
|
||||
QVERIFY(pixels(all.apply(picture, CameraRawClipping::shadows)) == pixels(light.apply(picture, CameraRawClipping::shadows)));
|
||||
|
||||
@@ -12,6 +12,7 @@ private slots:
|
||||
void aStageBlendsTheTablesAroundItsValue();
|
||||
void eachSliderPicksItsOwnTables();
|
||||
void theAdaptiveSlidersBlendAcrossBrightness();
|
||||
void theColourGroupsPickTheirTables();
|
||||
void neutralizeFindsTheGridsGrayest();
|
||||
void identitySlidersComposeNothing();
|
||||
};
|
||||
@@ -33,16 +34,18 @@ int indexOf(const uint8_t *table)
|
||||
void CameraRawTablesTests::theTablesDecodeAsWritten()
|
||||
{
|
||||
const std::vector<uint8_t> &tables = CameraRawTables::tables();
|
||||
QCOMPARE(tables.size(), size_t(285) * 17 * 17 * 17 * 3);
|
||||
QCOMPARE(tables.size(), size_t(763) * 17 * 17 * 17 * 3);
|
||||
quint64 hash = 0xcbf29ce484222325ULL;
|
||||
for (const uint8_t byte : tables)
|
||||
hash = (hash ^ byte) * 0x100000001b3ULL;
|
||||
QCOMPARE(hash, quint64(0x770d674136320424ULL));
|
||||
QCOMPARE(hash, quint64(0x4967372b0e6d3d38ULL));
|
||||
QCOMPARE(std::vector<int>(entry(40, 2456), entry(40, 2456) + 3), (std::vector<int>{128, 128, 128}));
|
||||
QCOMPARE(std::vector<int>(entry(81, 100), entry(81, 100) + 3), (std::vector<int>{0, 4, 32}));
|
||||
QCOMPARE(std::vector<int>(entry(81, 100), entry(81, 100) + 3), (std::vector<int>{0, 5, 32}));
|
||||
QCOMPARE(std::vector<int>(entry(146, 4912), entry(146, 4912) + 3), (std::vector<int>{255, 255, 255}));
|
||||
QCOMPARE(std::vector<int>(entry(200, 2456), entry(200, 2456) + 3), (std::vector<int>{172, 172, 172}));
|
||||
QCOMPARE(std::vector<int>(entry(284, 100), entry(284, 100) + 3), (std::vector<int>{70, 108, 255}));
|
||||
QCOMPARE(std::vector<int>(entry(200, 2456), entry(200, 2456) + 3), (std::vector<int>{173, 173, 173}));
|
||||
QCOMPARE(std::vector<int>(entry(284, 100), entry(284, 100) + 3), (std::vector<int>{69, 107, 255}));
|
||||
QCOMPARE(std::vector<int>(entry(500, 1000), entry(500, 1000) + 3), (std::vector<int>{63, 112, 211}));
|
||||
QCOMPARE(std::vector<int>(entry(762, 4000), entry(762, 4000) + 3), (std::vector<int>{228, 235, 110}));
|
||||
}
|
||||
|
||||
void CameraRawTablesTests::aStageBlendsTheTablesAroundItsValue()
|
||||
@@ -132,26 +135,92 @@ void CameraRawTablesTests::theAdaptiveSlidersBlendAcrossBrightness()
|
||||
QVERIFY(std::abs(read.brightest - 128) < 0.01 && std::abs(read.luminance - 128) < 0.01 && std::abs(read.logBrightest - 128) < 0.01);
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Each slot: index and weight, a null table reading −1.
|
||||
std::vector<double> listed(const CameraRawStage &stage, int filled)
|
||||
{
|
||||
std::vector<double> out;
|
||||
for (int slot = 0; slot < filled; ++slot) {
|
||||
out.push_back(indexOf(stage.table[slot]));
|
||||
out.push_back(stage.weight[slot]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
// Mixer, Calibration, the parametric curve and Grading, against Python.
|
||||
void CameraRawTablesTests::theColourGroupsPickTheirTables()
|
||||
{
|
||||
CameraRawGradingSettings grading{{210, 40, -20}, {100, 25, 10}, {30, 55, 15}, {300, 10, -5}, 70, -30};
|
||||
const std::vector<CameraRawStage> graded = CameraRawTables::gradingStages(grading);
|
||||
QCOMPARE(graded.size(), size_t(8));
|
||||
QCOMPARE(listed(graded[0], 2), (std::vector<double>{744, 0.4f, 745, 0.6f}));
|
||||
QCOMPARE(listed(graded[3], 2), (std::vector<double>{759, 0.1f, 760, 0.9f}));
|
||||
QCOMPARE(listed(graded[4], 4), (std::vector<double>{-1, 0.6f, 665, 0.4f, -1, 0, 668, 0}));
|
||||
QCOMPARE(listed(graded[5], 4), (std::vector<double>{-1, 0, 608, 0.6666666865348816, -1, 0, 611, 0.3333333432674408}));
|
||||
QCOMPARE(listed(graded[6], 8), (std::vector<double>{566, 0.4343999922275543, 567, 0.1656000018119812, 569, 0, 570, 0, 710, 0.2896000146865845, 711,
|
||||
0.1103999987244606, 713, 0, 714, 0}));
|
||||
QCOMPARE(listed(graded[7], 8), (std::vector<double>{549, 0.5472000241279602, 550, 0.052799999713897705, 552, 0, 553, 0, 693, 0.36480000615119934, 694,
|
||||
0.03519999980926514, 696, 0, 697, 0}));
|
||||
// Past 330 a wheel wraps round to red's tables.
|
||||
CameraRawGradingSettings wrapped;
|
||||
wrapped.global = {345, 50, 0};
|
||||
QCOMPARE(listed(CameraRawTables::gradingStages(wrapped).at(0), 4), (std::vector<double>{668, 0, 669, 0.5, 635, 0, 636, 0.5}));
|
||||
// Balance +100 leaves the shadow wheel no share at all.
|
||||
CameraRawGradingSettings balanced;
|
||||
balanced.shadows = {200, 40, 0};
|
||||
balanced.highlights = {30, 20, 0};
|
||||
balanced.balance = 100;
|
||||
QCOMPARE(CameraRawTables::gradingStages(balanced).size(), size_t(1));
|
||||
CameraRawMixerSettings mixer;
|
||||
for (size_t index = 0; index < 8; ++index) {
|
||||
mixer.hue[index] = 5 + double(index);
|
||||
mixer.saturation[index] = -30 + 7 * double(index);
|
||||
mixer.luminance[index] = 20 - 4 * double(index);
|
||||
}
|
||||
const std::vector<CameraRawStage> mixed = CameraRawTables::mixerStages(mixer);
|
||||
QCOMPARE(mixed.size(), size_t(23));
|
||||
QCOMPARE(listed(mixed[0], 2), (std::vector<double>{287, 0.6f, 288, 0.4f}));
|
||||
QCOMPARE(listed(mixed[5], 2), (std::vector<double>{316, 0.08f, 317, 0.92f}));
|
||||
QCOMPARE(listed(mixed[22], 2), (std::vector<double>{402, 0.76f, 403, 0.24f}));
|
||||
CameraRawSettings settings;
|
||||
settings.calibration = {CameraRawProcessVersion::version6, 15, 80, -40, 30, 0, -70, 50};
|
||||
settings.curve.shadows = -60;
|
||||
settings.curve.darks = 10;
|
||||
settings.curve.highlights = 75;
|
||||
const std::vector<CameraRawStage> stages = CameraRawTables::stages(settings, {});
|
||||
QCOMPARE(stages.size(), size_t(8));
|
||||
QCOMPARE(listed(stages[0], 2), (std::vector<double>{408, 0.4f, 409, 0.6f}));
|
||||
QCOMPARE(listed(stages[2], 2), (std::vector<double>{417, 0.4f, 418, 0.6f}));
|
||||
QCOMPARE(listed(stages[4], 2), (std::vector<double>{433, 1, 434, 0}));
|
||||
QCOMPARE(listed(stages[5], 2), (std::vector<double>{435, 0.2f, 436, 0.8f}));
|
||||
QCOMPARE(listed(stages[7], 2), (std::vector<double>{453, 0.5f, 454, 0.5f}));
|
||||
// Moved dividers leave the curve to the formula.
|
||||
settings.curve.lightSplit = 70;
|
||||
QCOMPARE(CameraRawTables::stages(settings, {}).size(), size_t(5));
|
||||
QCOMPARE(indexOf(CameraRawTables::exposureTables()), 81);
|
||||
}
|
||||
|
||||
void CameraRawTablesTests::neutralizeFindsTheGridsGrayest()
|
||||
{
|
||||
// Python's own search over the same tables found these.
|
||||
const CameraRawSettings::Balance warm = CameraRawTables::neutralize(160 / 255.0, 140 / 255.0, 120 / 255.0).value();
|
||||
QVERIFY(warm.temperature == -19 && warm.tint == -7);
|
||||
QVERIFY(warm.temperature == -19 && warm.tint == -6);
|
||||
const CameraRawSettings::Balance blue = CameraRawTables::neutralize(0.2, 0.45, 0.8).value();
|
||||
QVERIFY(blue.temperature == 100 && blue.tint == 25.5);
|
||||
QVERIFY(blue.temperature == 100 && blue.tint == 26);
|
||||
const CameraRawSettings::Balance green = CameraRawTables::neutralize(0.3, 0.5, 0.25).value();
|
||||
QVERIFY(green.temperature == 5 && green.tint == 90.5);
|
||||
QVERIFY(green.temperature == 5 && green.tint == 91);
|
||||
// White stays white anywhere: the first of the ties wins.
|
||||
const CameraRawSettings::Balance white = CameraRawTables::neutralize(1, 1, 1).value();
|
||||
QVERIFY(white.temperature == 0 && white.tint == 0);
|
||||
// A refinement past −100 keeps to the range.
|
||||
const CameraRawSettings::Balance edge = CameraRawTables::neutralize(84 / 255.0, 1 / 255.0, 97 / 255.0).value();
|
||||
QVERIFY(edge.temperature == -14.5 && edge.tint == -100);
|
||||
QVERIFY(edge.temperature == -16 && edge.tint == -100);
|
||||
const CameraRawSettings::Balance hot = CameraRawTables::neutralize(240 / 255.0, 80 / 255.0, 40 / 255.0).value();
|
||||
QVERIFY(hot.temperature == -100 && hot.tint == -13.5);
|
||||
// Ties among searched points: tint rising within each temperature.
|
||||
const CameraRawSettings::Balance tie = CameraRawTables::neutralize(144 / 255.0, 216 / 255.0, 47 / 255.0).value();
|
||||
QVERIFY(tie.temperature == -100 && tie.tint == 60);
|
||||
QVERIFY(tie.temperature == -100 && tie.tint == 82);
|
||||
// Any channel missing leaves nothing to balance from.
|
||||
QVERIFY(!CameraRawTables::neutralize(0.5, 0.0001, 0.5));
|
||||
QVERIFY(!CameraRawTables::neutralize(0, 128 / 255.0, 128 / 255.0));
|
||||
@@ -182,8 +251,8 @@ void CameraRawTablesTests::identitySlidersComposeNothing()
|
||||
const auto near = [](std::array<float, 3> got, std::array<float, 3> expected) {
|
||||
return std::abs(got[0] - expected[0]) < 1e-6f && std::abs(got[1] - expected[1]) < 1e-6f && std::abs(got[2] - expected[2]) < 1e-6f;
|
||||
};
|
||||
QVERIFY(near(at(16, 16, 16), {0.94272572f, 0.86208665f, 0.74603087f}));
|
||||
QVERIFY(near(at(16, 4, 28), {0.86772656f, 0.55830115f, 0.98769760f}));
|
||||
QVERIFY(near(at(16, 16, 16), {0.94676805f, 0.86324614f, 0.75338721f}));
|
||||
QVERIFY(near(at(16, 4, 28), {0.87189686f, 0.55294615f, 0.98683822f}));
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(CameraRawTablesTests)
|
||||
|
||||
@@ -14,6 +14,7 @@ private slots:
|
||||
void tonalSlidersMoveTheEndTheyName();
|
||||
void temperatureWarmsAndTintMovesTowardMagenta();
|
||||
void whiteBalanceAndVibranceMatchPhotoshop();
|
||||
void mixerCalibrationCurveAndVignettesMatchPhotoshop();
|
||||
void vibranceFavorsDullColorsAndProtectsSkinWhileSaturationDoesNot();
|
||||
void textureAndClaritySharpenAnEdgeAndLeaveAFlatField();
|
||||
void dehazeDeepensOrLiftsAndKeepsAlpha();
|
||||
@@ -67,11 +68,11 @@ void CameraRawTests::tonalSlidersMoveTheEndTheyName()
|
||||
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][0] == 197 && bright[1][0] == 240);
|
||||
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] == 177 && both[1][0] == 255);
|
||||
QVERIFY(both[0][0] == 179 && both[1][0] == 255);
|
||||
settings = CameraRawSettings();
|
||||
settings.whites = 100;
|
||||
const auto clipped = pixels(settings.apply(brightAndMid));
|
||||
@@ -132,6 +133,40 @@ void CameraRawTests::whiteBalanceAndVibranceMatchPhotoshop()
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
@@ -130,7 +130,7 @@ void CameraRawWiringTests::autoAveragesLinearLight()
|
||||
QImage dark = BrushRaster::context(1, 1, false);
|
||||
dark.setPixelColor(0, 0, QColor(3, 5, 8));
|
||||
const CameraRawSettings::Balance shade = CameraRawSettings::autoBalance(dark).value();
|
||||
QVERIFY(shade.temperature == 50.5 && shade.tint == 20);
|
||||
QVERIFY(shade.temperature == 53 && shade.tint == 15.5);
|
||||
}
|
||||
|
||||
void CameraRawWiringTests::eachEyeClearsEveryFieldOfItsGroup()
|
||||
@@ -186,7 +186,8 @@ void CameraRawWiringTests::opticsAndDetailReachTheKernelsInOrder()
|
||||
.noiseColorSmoothness = 80};
|
||||
const double k = settings.optics.distortionK(PixelFilter::lensStrength);
|
||||
const QImage direct = kernel(picture, [k](uchar *bytes, size_t w, size_t h, size_t s) {
|
||||
adjust_camera_raw_optics(bytes, w, h, s, 1, 1, 60, 40, k, 30, 260, 320, 40, 50, 130, -45, 30, 1);
|
||||
adjust_camera_raw_optics(bytes, w, h, s, 1, 1, 60, 40, k, 30, 260, 320, 40, 50, 130, -45, 30, 1, CameraRawTables::exposureTables(),
|
||||
CameraRawTables::size);
|
||||
adjust_camera_raw_detail(bytes, w, h, s, 80, 30, 60, 20, 40, 70, 30, 50, 20, 80, 1);
|
||||
});
|
||||
QVERIFY(pixels(settings.apply(picture)) == pixels(direct));
|
||||
@@ -269,21 +270,17 @@ void CameraRawWiringTests::curveMixerAndGradingReachTheKernelInOrder()
|
||||
const CameraRawCurveSettings shape = curve.normalized();
|
||||
const std::vector<float> tone = shape.toneTable(), red = shape.channelTable(shape.red), green = shape.channelTable(shape.green),
|
||||
blue = shape.channelTable(shape.blue);
|
||||
std::vector<float> mixer;
|
||||
for (size_t index = 0; index < 8; ++index)
|
||||
mixer.push_back(float((5 + double(index)) / 100));
|
||||
for (size_t index = 0; index < 8; ++index)
|
||||
mixer.push_back(float((-30 + 7 * double(index)) / 100));
|
||||
for (size_t index = 0; index < 8; ++index)
|
||||
mixer.push_back(float((20 - 4 * double(index)) / 100));
|
||||
// The colours and wheels arrive as tables; formulas idle.
|
||||
const std::vector<float> mixer(24, 0), grade(12, 0);
|
||||
const std::vector<float> mixerTable = CameraRawTables::compose(CameraRawTables::mixerStages(settings.mixer.normalized()));
|
||||
const std::vector<float> gradeTable = CameraRawTables::compose(CameraRawTables::gradingStages(settings.grading.normalized()));
|
||||
QVERIFY(!mixerTable.empty() && !gradeTable.empty());
|
||||
const std::vector<float> points{float(200 / 360.0), 0.6f, 0.4f, 0.25f, -0.35f, 0.15f, float(40 / 360.0), 0.5f, 0.3f,
|
||||
float(40 / 360.0), 0.8f, 0.6f, -0.1f, 0.2f, -0.45f, float(60 / 360.0), 0.3f, 0.6f};
|
||||
const std::vector<float> grade{float(210 / 360.0), 0.4f, -0.2f, float(100 / 360.0), 0.25f, 0.1f,
|
||||
float(30 / 360.0), 0.55f, 0.15f, float(300 / 360.0), 0.1f, -0.05f};
|
||||
for (const int visualize : {-1, 1}) {
|
||||
const QImage direct = kernel(picture, [&](uchar *bytes, size_t w, size_t h, size_t stride) {
|
||||
adjust_camera_raw_curve_color(bytes, w, h, stride, tone.data(), red.data(), green.data(), blue.data(), 0.4, mixer.data(), 2, points.data(),
|
||||
grade.data(), 0.7, -0.3, visualize);
|
||||
grade.data(), 0.7, -0.3, visualize, mixerTable.data(), gradeTable.data(), CameraRawTables::grid);
|
||||
});
|
||||
QVERIFY(pixels(settings.apply(picture, std::nullopt, 1, 0, visualize)) == pixels(direct));
|
||||
}
|
||||
@@ -305,7 +302,8 @@ void CameraRawWiringTests::theSharpenMaskAndEachLensFlagReachTheirKernels()
|
||||
lens.optics.enableLensProfile = !chromatic;
|
||||
const double k = lens.optics.distortionK(PixelFilter::lensStrength);
|
||||
const QImage direct = kernel(picture, [&](uchar *bytes, size_t w, size_t h, size_t s) {
|
||||
adjust_camera_raw_optics(bytes, w, h, s, chromatic ? 1 : 0, chromatic ? 0 : 1, 100, 100, k, 0, 270, 310, 0, 60, 120, 0, 50, 1);
|
||||
adjust_camera_raw_optics(bytes, w, h, s, chromatic ? 1 : 0, chromatic ? 0 : 1, 100, 100, k, 0, 270, 310, 0, 60, 120, 0, 50, 1,
|
||||
CameraRawTables::exposureTables(), CameraRawTables::size);
|
||||
});
|
||||
QVERIFY(pixels(lens.apply(picture)) == pixels(direct));
|
||||
}
|
||||
|
||||
@@ -391,4 +391,5 @@ compositor_test(SliderSnapTests)
|
||||
compositor_test(ProjectResizeFailureTests)
|
||||
|
||||
# The `.svgz` fixture is gzip, written through zlib.
|
||||
find_package(ZLIB REQUIRED)
|
||||
target_link_libraries(SVGImportTests PRIVATE ZLIB::ZLIB)
|
||||
Reference in new issue
Block a user