Finishing filters: Vignette, Bloom / Glow, Tonal Contrast (12.9a)
This commit is contained in:
1 parent
96ce546508
commit
fdb62978c5
20 files changed
+756
-35
No files matched your search
@@ -18,6 +18,9 @@ public:
|
||||
void openGradientMapColorPicker(bool highlights);
|
||||
// The Gradient Map follows the picker's working colour.
|
||||
void previewGradientMapColor();
|
||||
// The picker on the open Vignette's colour, which follows it.
|
||||
void openVignetteColorPicker();
|
||||
void previewVignetteColor();
|
||||
// The picker on an effect of the open panel.
|
||||
void openEffectColorPicker(LayerEffectKind kind);
|
||||
// The effect follows the picker's working colour.
|
||||
@@ -30,3 +33,4 @@ public:
|
||||
|
||||
private:
|
||||
void setGradientMapColor(const PaletteColor &color, bool highlights);
|
||||
void setVignetteColor(const PaletteColor &color);
|
||||
@@ -105,6 +105,8 @@ QString ColorPickerTarget::title() const
|
||||
return highlights ? QStringLiteral("Color Picker (Gradient Map Highlights)") : QStringLiteral("Color Picker (Gradient Map Shadows)");
|
||||
if (kind == Kind::effect)
|
||||
return QStringLiteral("Color Picker (%1 Color)").arg(rawValue(effect));
|
||||
if (kind == Kind::vignette)
|
||||
return QStringLiteral("Color Picker (Vignette Color)");
|
||||
return background ? QStringLiteral("Color Picker (Background Color)") : QStringLiteral("Color Picker (Foreground Color)");
|
||||
}
|
||||
|
||||
@@ -241,6 +243,8 @@ void EditorSession::closeColorPicker(bool commit)
|
||||
} else if (picker.target.kind == ColorPickerTarget::Kind::gradientMap) {
|
||||
// The end previewed the working colour; Cancel puts it back.
|
||||
setGradientMapColor(commit ? color : picker.original, picker.target.highlights);
|
||||
} else if (picker.target.kind == ColorPickerTarget::Kind::vignette) {
|
||||
setVignetteColor(commit ? color : picker.original);
|
||||
} else if (picker.target.kind == ColorPickerTarget::Kind::effect) {
|
||||
const PaletteColor chosen = commit ? color : picker.original;
|
||||
changeEffects([&](LayerEffects &effects) { effects.setColor(chosen, picker.target.effect); });
|
||||
@@ -258,6 +262,16 @@ void EditorSession::openGradientMapColorPicker(bool highlights)
|
||||
notify();
|
||||
}
|
||||
|
||||
void EditorSession::openVignetteColorPicker()
|
||||
{
|
||||
// A commit keeps the project busy: no `committing` term.
|
||||
if (!canEditPalette() || m_colorPicker || !m_filterEdit || m_filterEdit->kind != FilterKind::vignette)
|
||||
return;
|
||||
const AdjustmentColor value = m_filterEdit->settings.vignetteColor;
|
||||
m_colorPicker = ColorPickerState({ColorPickerTarget::Kind::vignette}, PaletteColor{value.red, value.green, value.blue});
|
||||
notify();
|
||||
}
|
||||
|
||||
void EditorSession::openEffectColorPicker(LayerEffectKind kind)
|
||||
{
|
||||
if (!canEditPalette() || m_colorPicker || !m_effectsEditing)
|
||||
@@ -281,6 +295,22 @@ void EditorSession::previewGradientMapColor()
|
||||
setGradientMapColor(m_colorPicker->color(), m_colorPicker->target.highlights);
|
||||
}
|
||||
|
||||
void EditorSession::previewVignetteColor()
|
||||
{
|
||||
if (m_colorPicker && m_colorPicker->target.kind == ColorPickerTarget::Kind::vignette)
|
||||
setVignetteColor(m_colorPicker->color());
|
||||
}
|
||||
|
||||
// Its picker lives only while its Vignette edit is open.
|
||||
void EditorSession::setVignetteColor(const PaletteColor &color)
|
||||
{
|
||||
FilterSettings settings = m_filterEdit.value().settings;
|
||||
settings.vignetteColor = AdjustmentColor(color);
|
||||
if (settings == m_filterEdit->settings)
|
||||
return;
|
||||
updateFilter(settings, m_filterEdit->preview);
|
||||
}
|
||||
|
||||
void EditorSession::setGradientMapColor(const PaletteColor &color, bool highlights)
|
||||
{
|
||||
// An adjustment's editor can close under its picker (9.5c).
|
||||
|
||||
@@ -35,9 +35,9 @@ struct PickerHSB {
|
||||
|
||||
enum class LayerEffectKind;
|
||||
|
||||
// The picker's targets: palette, text, effect, Gradient Map end.
|
||||
// The picker's targets: palette, text, effect, a filter's colour.
|
||||
struct ColorPickerTarget {
|
||||
enum class Kind { palette, text, gradientMap, effect };
|
||||
enum class Kind { palette, text, gradientMap, effect, vignette };
|
||||
Kind kind;
|
||||
bool background = false;
|
||||
std::optional<QUuid> draftID = std::nullopt;
|
||||
|
||||
@@ -102,6 +102,8 @@ struct SessionJobs {
|
||||
FilterSettings settings;
|
||||
// Camera Raw's scope of the grade itself.
|
||||
std::optional<CameraRawScope> scope = std::nullopt;
|
||||
// Its grid's version: a newer grid drops it.
|
||||
quint64 sourceVersion = 0;
|
||||
};
|
||||
struct FilterMade {
|
||||
std::optional<ImportedImage> asset;
|
||||
|
||||
@@ -69,6 +69,9 @@ void EditorSession::updateFilter(const FilterSettings &settings, bool preview)
|
||||
try {
|
||||
edit.growForBlur();
|
||||
edit.preparedPreview.reset();
|
||||
// A waiting job moves to the grid its version names.
|
||||
if (edit.pending)
|
||||
edit.pending = edit.previewJob();
|
||||
} catch (const ProjectError &error) {
|
||||
setBrushError(QString::fromUtf8(error.what()));
|
||||
} catch (const ExportError &error) {
|
||||
@@ -112,22 +115,22 @@ void EditorSession::renderFilterPreview()
|
||||
const FilterJob job = *std::exchange(edit.pending, std::nullopt);
|
||||
m_filterPreviewFor = edit.id;
|
||||
m_filterRendering = true;
|
||||
m_filterPreview.setFuture(QtConcurrent::run([job]() -> Filtered {
|
||||
m_filterPreview.setFuture(QtConcurrent::run([job, version = edit.previewSourceVersion]() -> Filtered {
|
||||
try {
|
||||
if (job.kind == FilterKind::cameraRaw) {
|
||||
auto [image, scope] = CameraRawScope::preview(job);
|
||||
return Filtered{std::move(image), std::nullopt, job.settings, std::move(scope)};
|
||||
return Filtered{std::move(image), std::nullopt, job.settings, std::move(scope), version};
|
||||
}
|
||||
return Filtered{PixelFilter::run(job), std::nullopt, job.settings};
|
||||
return Filtered{PixelFilter::run(job), std::nullopt, job.settings, std::nullopt, version};
|
||||
} catch (const ContentFillError &error) {
|
||||
return Filtered{std::nullopt, QString::fromUtf8(error.what()), job.settings};
|
||||
return Filtered{std::nullopt, QString::fromUtf8(error.what()), job.settings, std::nullopt, version};
|
||||
} catch (const SubjectRemovalError &error) {
|
||||
return Filtered{std::nullopt, QString::fromUtf8(error.what()), job.settings};
|
||||
return Filtered{std::nullopt, QString::fromUtf8(error.what()), job.settings, std::nullopt, version};
|
||||
} catch (const ExportError &error) {
|
||||
return Filtered{std::nullopt, QString::fromUtf8(error.what()), job.settings};
|
||||
return Filtered{std::nullopt, QString::fromUtf8(error.what()), job.settings, std::nullopt, version};
|
||||
} catch (const ProjectError &error) {
|
||||
// Settings past their bounds: Black & White, Color Balance.
|
||||
return Filtered{std::nullopt, QString::fromUtf8(error.what()), job.settings};
|
||||
return Filtered{std::nullopt, QString::fromUtf8(error.what()), job.settings, std::nullopt, version};
|
||||
}
|
||||
}));
|
||||
}
|
||||
@@ -138,8 +141,11 @@ void EditorSession::finishFilterPreview()
|
||||
{
|
||||
m_filterRendering = false;
|
||||
const Filtered result = m_filterPreview.result();
|
||||
// An edit cancelled meanwhile takes nothing; a new one waited.
|
||||
if (m_filterEdit && m_filterEdit->id == m_filterPreviewFor) {
|
||||
// A cancelled edit takes nothing; an older grid renders again.
|
||||
if (m_filterEdit && m_filterEdit->id == m_filterPreviewFor && result.sourceVersion != m_filterEdit->previewSourceVersion) {
|
||||
m_filterEdit->preparing = false;
|
||||
notify();
|
||||
} else if (m_filterEdit && m_filterEdit->id == m_filterPreviewFor) {
|
||||
FilterEdit &edit = *m_filterEdit;
|
||||
edit.preparing = false;
|
||||
edit.previewError = result.failure;
|
||||
@@ -164,8 +170,8 @@ void EditorSession::finishFilterPreview()
|
||||
|
||||
void EditorSession::cancelFilter()
|
||||
{
|
||||
// A Gradient Map colour being picked goes with the panel.
|
||||
if (m_colorPicker && m_colorPicker->target.kind == ColorPickerTarget::Kind::gradientMap)
|
||||
// A filter colour being picked goes with the panel.
|
||||
if (m_colorPicker && (m_colorPicker->target.kind == ColorPickerTarget::Kind::gradientMap || m_colorPicker->target.kind == ColorPickerTarget::Kind::vignette))
|
||||
closeColorPicker(false);
|
||||
if (finishAdjustmentEditing(false) || !m_filterEdit || m_filterEdit->committing)
|
||||
return;
|
||||
@@ -181,7 +187,7 @@ void EditorSession::commitFilter(std::function<void()> done)
|
||||
if (done)
|
||||
QMetaObject::invokeMethod(this, done, Qt::QueuedConnection);
|
||||
};
|
||||
if (m_colorPicker && m_colorPicker->target.kind == ColorPickerTarget::Kind::gradientMap)
|
||||
if (m_colorPicker && (m_colorPicker->target.kind == ColorPickerTarget::Kind::gradientMap || m_colorPicker->target.kind == ColorPickerTarget::Kind::vignette))
|
||||
closeColorPicker(true);
|
||||
if (finishAdjustmentEditing(true) || !m_filterEdit || m_filterEdit->committing) {
|
||||
finish();
|
||||
@@ -204,6 +210,11 @@ void EditorSession::commitFilter(std::function<void()> done)
|
||||
// Nothing to change closes as Cancel does, with no step.
|
||||
if ((edit.kind == FilterKind::lensCorrection && edit.settings.distortion == 0)
|
||||
|| (edit.kind == FilterKind::exposure && edit.settings.exposure == ExposureSettings())
|
||||
|| (edit.kind == FilterKind::vignette && edit.settings.vignetteAmount == 0)
|
||||
|| (edit.kind == FilterKind::bloomGlow && edit.settings.bloomAmount == 0)
|
||||
|| (edit.kind == FilterKind::tonalContrast
|
||||
&& (edit.settings.tonalAmount == 0
|
||||
|| (edit.settings.tonalShadows == 0 && edit.settings.tonalMidtones == 0 && edit.settings.tonalHighlights == 0)))
|
||||
|| (edit.kind == FilterKind::grain && edit.settings.grain.amount == 0)
|
||||
|| (edit.kind == FilterKind::cameraRaw && rendered.cameraRaw.isIdentity())) {
|
||||
cancelFilter();
|
||||
@@ -224,7 +235,7 @@ void EditorSession::commitFilter(std::function<void()> done)
|
||||
return;
|
||||
}
|
||||
const std::optional<QImage> cached = isAutomatic(edit.kind) && edit.preparedSettings == edit.settings ? edit.preparedPreview : std::nullopt;
|
||||
const bool spreads = edit.kind == FilterKind::gaussianBlur || edit.kind == FilterKind::motionBlur;
|
||||
const bool spreads = edit.kind == FilterKind::gaussianBlur || edit.kind == FilterKind::motionBlur || edit.kind == FilterKind::bloomGlow;
|
||||
// A painted layer flattens in the worker, which catches failures.
|
||||
m_filterCommit.setFuture(QtConcurrent::run([kind = edit.kind, original = edit.original, grownImage = edit.grownImage, settings = rendered,
|
||||
selection = edit.selection, mapping = edit.mapping, seed = edit.seed, cached,
|
||||
@@ -339,9 +350,8 @@ void EditorSession::finishFilterCommit()
|
||||
layer.asset = made.asset;
|
||||
layer.transform = made.transform.value_or(current.transform);
|
||||
layer.mask = mask;
|
||||
// Swift rebuilds the layer without shape, effects and text.
|
||||
// Swift rebuilds the layer without shape and text; effects stay.
|
||||
layer.shape = std::nullopt;
|
||||
layer.effects = std::nullopt;
|
||||
layer.text = std::nullopt;
|
||||
m_document->layers[index] = layer;
|
||||
endEdit();
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <numbers>
|
||||
|
||||
extern "C" {
|
||||
#include "AdjustPixels.h"
|
||||
#include "BrushPixels.h"
|
||||
#include "LensPixels.h"
|
||||
#include "NoisePixels.h"
|
||||
@@ -23,6 +24,9 @@ QString rawValue(FilterKind kind)
|
||||
case FilterKind::gaussianBlur: return QStringLiteral("Gaussian Blur");
|
||||
case FilterKind::motionBlur: return QStringLiteral("Motion Blur");
|
||||
case FilterKind::addNoise: return QStringLiteral("Add Noise");
|
||||
case FilterKind::vignette: return QStringLiteral("Vignette");
|
||||
case FilterKind::bloomGlow: return QStringLiteral("Bloom / Glow");
|
||||
case FilterKind::tonalContrast: return QStringLiteral("Tonal Contrast");
|
||||
case FilterKind::lensCorrection: return QStringLiteral("Lens Correction");
|
||||
case FilterKind::cameraRaw: return QStringLiteral("Camera Raw Filter");
|
||||
case FilterKind::removeBackground: return QStringLiteral("Remove Background");
|
||||
@@ -61,6 +65,19 @@ FilterSettings FilterSettings::normalized() const
|
||||
result.angle = clamp(angle, -90, 90, 0);
|
||||
result.distance = clamp(distance, 1, 2000, 10);
|
||||
result.amount = clamp(amount, 0.1, 400, 10);
|
||||
result.vignetteAmount = clamp(vignetteAmount, 0, 100, 35);
|
||||
result.vignetteColor = vignetteColor.clamped();
|
||||
result.vignetteMidpoint = clamp(vignetteMidpoint, 0, 100, 50);
|
||||
result.vignetteRoundness = clamp(vignetteRoundness, -100, 100, 0);
|
||||
result.vignetteFeather = clamp(vignetteFeather, 0, 100, 60);
|
||||
result.vignetteHighlights = clamp(vignetteHighlights, 0, 100, 25);
|
||||
result.bloomAmount = clamp(bloomAmount, 0, 100, 40);
|
||||
result.bloomRadius = clamp(bloomRadius, 1, 150, 24);
|
||||
result.tonalAmount = clamp(tonalAmount, 0, 100, 50);
|
||||
result.tonalRadius = clamp(tonalRadius, 1, 100, 16);
|
||||
result.tonalShadows = clamp(tonalShadows, -100, 100, 40);
|
||||
result.tonalMidtones = clamp(tonalMidtones, -100, 100, 60);
|
||||
result.tonalHighlights = clamp(tonalHighlights, -100, 100, 30);
|
||||
result.distortion = clamp(distortion, -100, 100, 0);
|
||||
result.refineEdges = clamp(refineEdges, 0, 40, 12);
|
||||
result.matteContrast = clamp(matteContrast, 0, 100, 25);
|
||||
@@ -107,6 +124,19 @@ QImage drawn(const QImage &image)
|
||||
BrushRaster::draw(image, QRectF(0, 0, image.width(), image.height()), painter);
|
||||
return context;
|
||||
}
|
||||
|
||||
// CIBloom is private: the pixels plus their Gaussian glow, scaled.
|
||||
QImage bloom(QImage pixels, double sigma, double intensity)
|
||||
{
|
||||
const QImage glow = PixelAdjust::gaussianBlur(pixels, sigma, false);
|
||||
for (int y = 0; y < pixels.height(); ++y) {
|
||||
uchar *row = pixels.scanLine(y);
|
||||
const uchar *light = glow.constScanLine(y);
|
||||
for (int index = 0; index < pixels.width() * 4; ++index)
|
||||
row[index] = uchar(std::min(255.0, std::round(row[index] + intensity * light[index])));
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
}
|
||||
|
||||
QImage PixelFilter::run(const FilterJob &job)
|
||||
@@ -137,6 +167,21 @@ QImage PixelFilter::run(const FilterJob &job)
|
||||
noise_add_at(image.bits(), size_t(width), size_t(height), size_t(image.bytesPerLine()), float(settings.amount), settings.gaussian ? 1 : 0,
|
||||
settings.monochromatic ? 1 : 0, job.seed, int64_t(std::floor(job.noiseOrigin.x())), int64_t(std::floor(job.noiseOrigin.y())));
|
||||
break;
|
||||
case FilterKind::vignette:
|
||||
image = drawn(job.image);
|
||||
adjust_colored_vignette(image.bits(), size_t(width), size_t(height), size_t(image.bytesPerLine()), settings.vignetteAmount,
|
||||
settings.vignetteMidpoint, settings.vignetteRoundness, settings.vignetteFeather, settings.vignetteHighlights,
|
||||
settings.vignetteColor.red, settings.vignetteColor.green, settings.vignetteColor.blue);
|
||||
break;
|
||||
case FilterKind::bloomGlow: image = bloom(drawn(job.image), settings.bloomRadius * job.scale, settings.bloomAmount / 50); break;
|
||||
case FilterKind::tonalContrast: {
|
||||
// The detail's base: the layer softened, clear past its edge.
|
||||
const QImage base = PixelAdjust::gaussianBlur(drawn(job.image), settings.tonalRadius * job.scale, false);
|
||||
image = drawn(job.image);
|
||||
adjust_tonal_contrast(image.bits(), base.constBits(), size_t(width), size_t(height), size_t(image.bytesPerLine()), size_t(base.bytesPerLine()),
|
||||
settings.tonalAmount, settings.tonalShadows, settings.tonalMidtones, settings.tonalHighlights);
|
||||
break;
|
||||
}
|
||||
case FilterKind::lensCorrection: {
|
||||
// Relative to the image's size, so previews bend alike.
|
||||
const QImage source = drawn(job.image);
|
||||
@@ -173,6 +218,7 @@ double FilterEdit::blurMargin(FilterKind kind, const FilterSettings &settings)
|
||||
switch (kind) {
|
||||
case FilterKind::gaussianBlur: return settings.radius * 3 + 2;
|
||||
case FilterKind::motionBlur: return settings.distance / 2 + 2;
|
||||
case FilterKind::bloomGlow: return settings.bloomRadius * 3 + 2;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
@@ -218,6 +264,7 @@ void FilterEdit::grow(const QRectF &extent)
|
||||
void FilterEdit::prepare(const ImportedImage &source, const LayerTransform &placed)
|
||||
{
|
||||
const QSize size = source.size();
|
||||
++previewSourceVersion;
|
||||
mapping = BrushRaster::pixelToDocument(placed, size.width(), size.height());
|
||||
// Noise and grain preview whole: enlarged, grain looks coarse.
|
||||
const bool whole = kind == FilterKind::addNoise || kind == FilterKind::grain || kind == FilterKind::contentAwareFill || kind == FilterKind::removeBackground;
|
||||
|
||||
+21
-2
@@ -16,8 +16,8 @@ struct ImageLayer;
|
||||
|
||||
// The Filter and Image menus' filters.
|
||||
enum class FilterKind {
|
||||
gaussianBlur, motionBlur, addNoise, lensCorrection, cameraRaw, removeBackground, contentAwareFill, curves, exposure, gradientMap, grain, blackWhite,
|
||||
colorBalance
|
||||
gaussianBlur, motionBlur, addNoise, vignette, bloomGlow, tonalContrast, lensCorrection, cameraRaw, removeBackground, contentAwareFill, curves,
|
||||
exposure, gradientMap, grain, blackWhite, colorBalance
|
||||
};
|
||||
// The menu's title, Swift's rawValue.
|
||||
QString rawValue(FilterKind kind);
|
||||
@@ -27,6 +27,7 @@ bool isAutomatic(FilterKind kind);
|
||||
bool isImageAdjustment(FilterKind kind);
|
||||
// Swift's allCases, the menus' order.
|
||||
inline constexpr std::array allFilterKinds{FilterKind::gaussianBlur, FilterKind::motionBlur, FilterKind::addNoise,
|
||||
FilterKind::vignette, FilterKind::bloomGlow, FilterKind::tonalContrast,
|
||||
FilterKind::lensCorrection, FilterKind::cameraRaw, FilterKind::removeBackground,
|
||||
FilterKind::contentAwareFill,
|
||||
FilterKind::curves, FilterKind::exposure, FilterKind::gradientMap,
|
||||
@@ -49,6 +50,22 @@ struct FilterSettings {
|
||||
bool gaussian = false;
|
||||
// The same noise on every channel: brightness alone.
|
||||
bool monochromatic = false;
|
||||
// Vignette: the edge colour, its strength and falloff.
|
||||
double vignetteAmount = 35;
|
||||
AdjustmentColor vignetteColor = AdjustmentColor(0, 0, 0);
|
||||
double vignetteMidpoint = 50;
|
||||
double vignetteRoundness = 0;
|
||||
double vignetteFeather = 60;
|
||||
double vignetteHighlights = 25;
|
||||
// Bloom / Glow: strength, and blur radius in layer pixels.
|
||||
double bloomAmount = 40;
|
||||
double bloomRadius = 24;
|
||||
// Tonal Contrast: one detail radius, a strength per tone.
|
||||
double tonalAmount = 50;
|
||||
double tonalRadius = 16;
|
||||
double tonalShadows = 40;
|
||||
double tonalMidtones = 60;
|
||||
double tonalHighlights = 30;
|
||||
// Positive straightens barrel distortion, negative pincushion.
|
||||
double distortion = 0;
|
||||
CurvesSettings curves{};
|
||||
@@ -151,6 +168,8 @@ public:
|
||||
std::optional<QImage> preparedPreview;
|
||||
// The settings the prepared preview was made with.
|
||||
std::optional<FilterSettings> preparedSettings;
|
||||
// Moves with each new grid: an older render lands nothing.
|
||||
quint64 previewSourceVersion = 0;
|
||||
std::optional<FilterJob> pending;
|
||||
CameraRawPanel rawPanel;
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
#include "UI/ColorPaletteControls.h"
|
||||
#include "UI/FilterSheet.h"
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QSlider>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
// Swift's finishing filters: Vignette, Bloom / Glow, Tonal Contrast.
|
||||
void FilterSheet::finishing(FilterKind kind)
|
||||
{
|
||||
const auto key = [](double FilterSettings::*field) { return [field](FilterSettings &settings) -> double & { return settings.*field; }; };
|
||||
const auto help = [this](const QString &words) { m_controls.back().slider->parentWidget()->setToolTip(words); };
|
||||
if (kind == FilterKind::vignette) {
|
||||
auto *box = new QWidget(this);
|
||||
auto *row = new QHBoxLayout(box);
|
||||
row->setContentsMargins(0, 0, 0, 0);
|
||||
row->setSpacing(8);
|
||||
auto *title = new QLabel(QStringLiteral("Color"), box);
|
||||
title->setFixedWidth(95);
|
||||
m_vignetteSwatch = new SwatchButton([this] {
|
||||
const AdjustmentColor colour = settings().vignetteColor;
|
||||
return PaletteColor{colour.red, colour.green, colour.blue};
|
||||
}, 6, 1.5, 1, box);
|
||||
m_vignetteSwatch->setObjectName(QStringLiteral("vignetteSwatch"));
|
||||
m_vignetteSwatch->setFixedSize(24, 24);
|
||||
m_vignetteSwatch->setToolTip(QStringLiteral("Choose the vignette color"));
|
||||
m_vignetteSwatch->setAccessibleName(QStringLiteral("Vignette color"));
|
||||
connect(m_vignetteSwatch, &QAbstractButton::clicked, this, [this] { m_session.openVignetteColorPicker(); });
|
||||
row->addWidget(title);
|
||||
row->addWidget(m_vignetteSwatch);
|
||||
row->addStretch(1);
|
||||
m_column->addWidget(box);
|
||||
control(QStringLiteral("Amount"), key(&FilterSettings::vignetteAmount), 0, 100, QStringLiteral("%"), 0, false);
|
||||
help(QStringLiteral("Blend the chosen color into the edges while keeping the center unchanged"));
|
||||
control(QStringLiteral("Midpoint"), key(&FilterSettings::vignetteMidpoint), 0, 100, QStringLiteral("%"), 0, false);
|
||||
control(QStringLiteral("Roundness"), key(&FilterSettings::vignetteRoundness), -100, 100, QString(), 0, false);
|
||||
control(QStringLiteral("Feather"), key(&FilterSettings::vignetteFeather), 0, 100, QStringLiteral("%"), 0, false);
|
||||
control(QStringLiteral("Highlights"), key(&FilterSettings::vignetteHighlights), 0, 100, QStringLiteral("%"), 0, false);
|
||||
help(QStringLiteral("Protect bright areas near the edge"));
|
||||
} else if (kind == FilterKind::bloomGlow) {
|
||||
control(QStringLiteral("Amount"), key(&FilterSettings::bloomAmount), 0, 100, QStringLiteral("%"), 0, false);
|
||||
control(QStringLiteral("Radius"), key(&FilterSettings::bloomRadius), 1, 150, QStringLiteral("px"), 0, true);
|
||||
} else {
|
||||
control(QStringLiteral("Amount"), key(&FilterSettings::tonalAmount), 0, 100, QStringLiteral("%"), 0, false);
|
||||
control(QStringLiteral("Shadows"), key(&FilterSettings::tonalShadows), -100, 100, QStringLiteral("%"), 0, false);
|
||||
control(QStringLiteral("Midtones"), key(&FilterSettings::tonalMidtones), -100, 100, QStringLiteral("%"), 0, false);
|
||||
control(QStringLiteral("Highlights"), key(&FilterSettings::tonalHighlights), -100, 100, QStringLiteral("%"), 0, false);
|
||||
control(QStringLiteral("Radius"), key(&FilterSettings::tonalRadius), 1, 100, QStringLiteral("px"), 0, true);
|
||||
}
|
||||
}
|
||||
@@ -201,6 +201,9 @@ FilterSheet::FilterSheet(EditorSession &session, QWidget *parent)
|
||||
control(QStringLiteral("Amount"), key(&FilterSettings::amount), 0.1, 400, QStringLiteral("%"), 1, true);
|
||||
noise();
|
||||
break;
|
||||
case FilterKind::vignette:
|
||||
case FilterKind::bloomGlow:
|
||||
case FilterKind::tonalContrast: finishing(m_session.filterEdit()->kind); break;
|
||||
case FilterKind::lensCorrection:
|
||||
control(QStringLiteral("Remove Distortion"), key(&FilterSettings::distortion), -100, 100, QString(), 0, false);
|
||||
m_column->addWidget(words(QStringLiteral("Positive straightens lines that bow outward (barrel); negative, lines that bow inward (pincushion)."),
|
||||
@@ -388,7 +391,10 @@ void FilterSheet::synchronize()
|
||||
if (picked != m_pickerColour) {
|
||||
m_pickerColour = picked;
|
||||
m_session.previewGradientMapColor();
|
||||
m_session.previewVignetteColor();
|
||||
}
|
||||
if (m_vignetteSwatch)
|
||||
m_vignetteSwatch->update();
|
||||
const FilterEdit &edit = m_session.filterEdit().value();
|
||||
for (Control &control : m_controls) {
|
||||
FilterSettings copy = edit.settings;
|
||||
|
||||
@@ -53,6 +53,8 @@ private:
|
||||
void control(const QString &title, std::function<double &(FilterSettings &)> key, double low, double high, const QString &unit,
|
||||
int decimals, bool logarithmic);
|
||||
void noise();
|
||||
// Vignette's colour and rows, Bloom / Glow's, Tonal Contrast's.
|
||||
void finishing(FilterKind kind);
|
||||
// Remove Background's words, Quality, and Advanced's three rows.
|
||||
void background();
|
||||
// Black & White's six families and Tint; Color Balance's nine.
|
||||
@@ -72,6 +74,7 @@ private:
|
||||
std::vector<Control> m_controls;
|
||||
CurvesControls *m_curves = nullptr;
|
||||
GradientMapControls *m_gradientMap = nullptr;
|
||||
SwatchButton *m_vignetteSwatch = nullptr;
|
||||
QButtonGroup *m_distribution = nullptr;
|
||||
QCheckBox *m_monochromatic = nullptr;
|
||||
QButtonGroup *m_quality = nullptr;
|
||||
|
||||
Reference in new issue
Block a user