350 lines
9.8 KiB
C++
350 lines
9.8 KiB
C++
#include "imageControl.h"
|
|
#include "RasterImageLayer.h"
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
|
|
ImageControl::ImageControl(QWidget* parent)
|
|
: QDialog(parent)
|
|
{
|
|
ui.setupUi(this);
|
|
|
|
ui.sliderRed->setPageStep(0);
|
|
ui.sliderGreen->setPageStep(0);
|
|
ui.sliderBlue->setPageStep(0);
|
|
|
|
ui.sliderRed->setSingleStep(0);
|
|
ui.sliderGreen->setSingleStep(0);
|
|
ui.sliderBlue->setSingleStep(0);
|
|
|
|
// Spinbox valueChanged: only sync the paired slider (no render)
|
|
connect(ui.spinRed, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ImageControl::onSpinRedValueChanged);
|
|
connect(ui.spinGreen, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ImageControl::onSpinGreenValueChanged);
|
|
connect(ui.spinBlue, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ImageControl::onSpinBlueValueChanged);
|
|
|
|
// Spinbox editingFinished: commit on Enter key / focus lost (trigger render)
|
|
connect(ui.spinRed, &QDoubleSpinBox::editingFinished, this, &ImageControl::onSpinRedEditingFinished);
|
|
connect(ui.spinGreen, &QDoubleSpinBox::editingFinished, this, &ImageControl::onSpinGreenEditingFinished);
|
|
connect(ui.spinBlue, &QDoubleSpinBox::editingFinished, this, &ImageControl::onSpinBlueEditingFinished);
|
|
|
|
// Slider valueChanged: only sync the paired spinbox (no render)
|
|
// Slider now represents band index (0 .. N-1)
|
|
connect(ui.sliderRed, &QSlider::valueChanged, this, &ImageControl::onSliderRedValueChanged);
|
|
connect(ui.sliderGreen, &QSlider::valueChanged, this, &ImageControl::onSliderGreenValueChanged);
|
|
connect(ui.sliderBlue, &QSlider::valueChanged, this, &ImageControl::onSliderBlueValueChanged);
|
|
|
|
// Slider sliderReleased: commit on mouse release (trigger render)
|
|
connect(ui.sliderRed, &QSlider::sliderReleased, this, &ImageControl::onSliderRedReleased);
|
|
connect(ui.sliderGreen, &QSlider::sliderReleased, this, &ImageControl::onSliderGreenReleased);
|
|
connect(ui.sliderBlue, &QSlider::sliderReleased, this, &ImageControl::onSliderBlueReleased);
|
|
|
|
// Connect preset buttons
|
|
connect(ui.btnTrueColor, &QPushButton::clicked, this, &ImageControl::onTrueColorClicked);
|
|
connect(ui.btnColorInfrared, &QPushButton::clicked, this, &ImageControl::onColorInfraredClicked);
|
|
|
|
// Spinbox only commits on Enter, not on every keystroke
|
|
ui.spinRed->setKeyboardTracking(false);
|
|
ui.spinGreen->setKeyboardTracking(false);
|
|
ui.spinBlue->setKeyboardTracking(false);
|
|
|
|
ui.groupAdjustments->setStyleSheet(R"(
|
|
QDoubleSpinBox {
|
|
border: 1px solid #999;
|
|
border-radius: 4px;
|
|
padding: 2px 20px 2px 6px; /* 右侧留空间给按钮 */
|
|
background: #0e1c4c;
|
|
selection-background-color: #0078d7;
|
|
font-size: 12px;
|
|
color:#ACCDFF ;
|
|
}
|
|
|
|
QDoubleSpinBox::up-button {
|
|
subcontrol-origin: border;
|
|
subcontrol-position: top right;
|
|
width: 16px;
|
|
border-left: 1px solid #ccc;
|
|
}
|
|
|
|
QDoubleSpinBox::down-button {
|
|
subcontrol-origin: border;
|
|
subcontrol-position: bottom right;
|
|
width: 16px;
|
|
border-left: 1px solid #ccc;
|
|
}
|
|
|
|
QDoubleSpinBox::up-arrow {
|
|
image: url(:/svg/resources/icons/svg/arrow_up.svg);
|
|
width: 10px;
|
|
height: 10px;
|
|
}
|
|
|
|
QDoubleSpinBox::down-arrow {
|
|
image: url(:/svg/resources/icons/svg/arrow_down.svg);
|
|
width: 10px;
|
|
height: 10px;
|
|
}
|
|
|
|
QDoubleSpinBox::up-button:hover,
|
|
QDoubleSpinBox::down-button:hover {
|
|
background: #e6f2ff;
|
|
}
|
|
|
|
QDoubleSpinBox::up-button:pressed,
|
|
QDoubleSpinBox::down-button:pressed {
|
|
background: #cce4ff;
|
|
}
|
|
)");
|
|
}
|
|
|
|
ImageControl::~ImageControl()
|
|
{
|
|
}
|
|
|
|
void ImageControl::setActiveLayer(RasterImageLayer* layer)
|
|
{
|
|
m_activeLayer = layer;
|
|
|
|
if (!layer) {
|
|
setEnabled(false);
|
|
m_wavelengths.clear();
|
|
return;
|
|
}
|
|
setEnabled(true);
|
|
|
|
// Get band wavelengths from the underlying RasterLayer's header
|
|
m_wavelengths = layer->layer()->bandWavelengths();
|
|
std::sort(m_wavelengths.begin(), m_wavelengths.end());
|
|
|
|
if (m_wavelengths.empty()) {
|
|
setEnabled(false);
|
|
return;
|
|
}
|
|
|
|
m_minWave = m_wavelengths.front();
|
|
m_maxWave = m_wavelengths.back();
|
|
|
|
// Compute spinbox step as the average wavelength interval between adjacent bands
|
|
double step = 1.0;
|
|
if (m_wavelengths.size() >= 2) {
|
|
step = (m_maxWave - m_minWave) / (m_wavelengths.size() - 1);
|
|
}
|
|
|
|
blockAllSignals(true);
|
|
|
|
// Configure spinbox ranges and step
|
|
ui.spinRed->setMinimum(m_minWave);
|
|
ui.spinRed->setMaximum(m_maxWave);
|
|
ui.spinRed->setSingleStep(step);
|
|
ui.spinGreen->setMinimum(m_minWave);
|
|
ui.spinGreen->setMaximum(m_maxWave);
|
|
ui.spinGreen->setSingleStep(step);
|
|
ui.spinBlue->setMinimum(m_minWave);
|
|
ui.spinBlue->setMaximum(m_maxWave);
|
|
ui.spinBlue->setSingleStep(step);
|
|
|
|
// Slider now represents band index (0 .. N-1), step = 1
|
|
int maxIdx = static_cast<int>(m_wavelengths.size()) - 1;
|
|
ui.sliderRed->setMinimum(0);
|
|
ui.sliderRed->setMaximum(maxIdx);
|
|
ui.sliderGreen->setMinimum(0);
|
|
ui.sliderGreen->setMaximum(maxIdx);
|
|
ui.sliderBlue->setMinimum(0);
|
|
ui.sliderBlue->setMaximum(maxIdx);
|
|
|
|
// Set current values from layer's render params (multiband)
|
|
auto params = layer->multibandParams();
|
|
|
|
int rIdx = nearestBandIndex(params.rWave);
|
|
int gIdx = nearestBandIndex(params.gWave);
|
|
int bIdx = nearestBandIndex(params.bWave);
|
|
|
|
ui.spinRed->setValue(m_wavelengths[rIdx]);
|
|
ui.spinGreen->setValue(m_wavelengths[gIdx]);
|
|
ui.spinBlue->setValue(m_wavelengths[bIdx]);
|
|
|
|
ui.sliderRed->setValue(rIdx);
|
|
ui.sliderGreen->setValue(gIdx);
|
|
ui.sliderBlue->setValue(bIdx);
|
|
|
|
blockAllSignals(false);
|
|
}
|
|
|
|
RasterImageLayer* ImageControl::activeLayer() const
|
|
{
|
|
return m_activeLayer;
|
|
}
|
|
|
|
int ImageControl::nearestBandIndex(double wave) const
|
|
{
|
|
if (m_wavelengths.empty()) return 0;
|
|
int best = 0;
|
|
double bestDiff = std::abs(m_wavelengths[0] - wave);
|
|
for (int i = 1; i < static_cast<int>(m_wavelengths.size()); ++i) {
|
|
double d = std::abs(m_wavelengths[i] - wave);
|
|
if (d < bestDiff) {
|
|
bestDiff = d;
|
|
best = i;
|
|
}
|
|
}
|
|
return best;
|
|
}
|
|
|
|
void ImageControl::setControlsToBandIndex(QDoubleSpinBox* spin, QSlider* slider, int idx)
|
|
{
|
|
if (idx < 0 || idx >= static_cast<int>(m_wavelengths.size())) return;
|
|
double wv = m_wavelengths[idx];
|
|
spin->blockSignals(true);
|
|
spin->setValue(wv);
|
|
spin->blockSignals(false);
|
|
slider->blockSignals(true);
|
|
slider->setValue(idx);
|
|
slider->blockSignals(false);
|
|
}
|
|
|
|
// --- Spinbox valueChanged: snap to nearest band, sync slider, no render ---
|
|
|
|
void ImageControl::onSpinRedValueChanged(double val)
|
|
{
|
|
int idx = nearestBandIndex(val);
|
|
ui.sliderRed->blockSignals(true);
|
|
ui.sliderRed->setValue(idx);
|
|
ui.sliderRed->blockSignals(false);
|
|
}
|
|
|
|
void ImageControl::onSpinGreenValueChanged(double val)
|
|
{
|
|
int idx = nearestBandIndex(val);
|
|
ui.sliderGreen->blockSignals(true);
|
|
ui.sliderGreen->setValue(idx);
|
|
ui.sliderGreen->blockSignals(false);
|
|
}
|
|
|
|
void ImageControl::onSpinBlueValueChanged(double val)
|
|
{
|
|
int idx = nearestBandIndex(val);
|
|
ui.sliderBlue->blockSignals(true);
|
|
ui.sliderBlue->setValue(idx);
|
|
ui.sliderBlue->blockSignals(false);
|
|
}
|
|
|
|
// --- Slider valueChanged: map band index to wavelength, sync spinbox, no render ---
|
|
|
|
void ImageControl::onSliderRedValueChanged(int val)
|
|
{
|
|
if (val < 0 || val >= static_cast<int>(m_wavelengths.size())) return;
|
|
ui.spinRed->blockSignals(true);
|
|
ui.spinRed->setValue(m_wavelengths[val]);
|
|
ui.spinRed->blockSignals(false);
|
|
}
|
|
|
|
void ImageControl::onSliderGreenValueChanged(int val)
|
|
{
|
|
if (val < 0 || val >= static_cast<int>(m_wavelengths.size())) return;
|
|
ui.spinGreen->blockSignals(true);
|
|
ui.spinGreen->setValue(m_wavelengths[val]);
|
|
ui.spinGreen->blockSignals(false);
|
|
}
|
|
|
|
void ImageControl::onSliderBlueValueChanged(int val)
|
|
{
|
|
if (val < 0 || val >= static_cast<int>(m_wavelengths.size())) return;
|
|
ui.spinBlue->blockSignals(true);
|
|
ui.spinBlue->setValue(m_wavelengths[val]);
|
|
ui.spinBlue->blockSignals(false);
|
|
}
|
|
|
|
// --- Spinbox editingFinished: snap to nearest band wavelength, then commit ---
|
|
|
|
void ImageControl::onSpinRedEditingFinished()
|
|
{
|
|
int idx = nearestBandIndex(ui.spinRed->value());
|
|
setControlsToBandIndex(ui.spinRed, ui.sliderRed, idx);
|
|
emitBandChange();
|
|
}
|
|
|
|
void ImageControl::onSpinGreenEditingFinished()
|
|
{
|
|
int idx = nearestBandIndex(ui.spinGreen->value());
|
|
setControlsToBandIndex(ui.spinGreen, ui.sliderGreen, idx);
|
|
emitBandChange();
|
|
}
|
|
|
|
void ImageControl::onSpinBlueEditingFinished()
|
|
{
|
|
int idx = nearestBandIndex(ui.spinBlue->value());
|
|
setControlsToBandIndex(ui.spinBlue, ui.sliderBlue, idx);
|
|
emitBandChange();
|
|
}
|
|
|
|
// --- Slider sliderReleased: commit on mouse release ---
|
|
|
|
void ImageControl::onSliderRedReleased()
|
|
{
|
|
emitBandChange();
|
|
}
|
|
|
|
void ImageControl::onSliderGreenReleased()
|
|
{
|
|
emitBandChange();
|
|
}
|
|
|
|
void ImageControl::onSliderBlueReleased()
|
|
{
|
|
emitBandChange();
|
|
}
|
|
|
|
// --- Preset buttons ---
|
|
|
|
void ImageControl::onTrueColorClicked()
|
|
{
|
|
blockAllSignals(true);
|
|
int rIdx = nearestBandIndex(665.0);
|
|
int gIdx = nearestBandIndex(560.0);
|
|
int bIdx = nearestBandIndex(490.0);
|
|
setControlsToBandIndex(ui.spinRed, ui.sliderRed, rIdx);
|
|
setControlsToBandIndex(ui.spinGreen, ui.sliderGreen, gIdx);
|
|
setControlsToBandIndex(ui.spinBlue, ui.sliderBlue, bIdx);
|
|
blockAllSignals(false);
|
|
emitBandChange();
|
|
}
|
|
|
|
void ImageControl::onColorInfraredClicked()
|
|
{
|
|
blockAllSignals(true);
|
|
int rIdx = nearestBandIndex(800.0);
|
|
int gIdx = nearestBandIndex(665.0);
|
|
int bIdx = nearestBandIndex(560.0);
|
|
setControlsToBandIndex(ui.spinRed, ui.sliderRed, rIdx);
|
|
setControlsToBandIndex(ui.spinGreen, ui.sliderGreen, gIdx);
|
|
setControlsToBandIndex(ui.spinBlue, ui.sliderBlue, bIdx);
|
|
blockAllSignals(false);
|
|
emitBandChange();
|
|
}
|
|
|
|
void ImageControl::emitBandChange()
|
|
{
|
|
double r = ui.spinRed->value();
|
|
double g = ui.spinGreen->value();
|
|
double b = ui.spinBlue->value();
|
|
|
|
// Update active layer's stored render params (multiband)
|
|
if (m_activeLayer) {
|
|
auto params = m_activeLayer->multibandParams();
|
|
params.rWave = r;
|
|
params.gWave = g;
|
|
params.bWave = b;
|
|
m_activeLayer->setMultibandParams(params);
|
|
}
|
|
|
|
emit bandSelectionChanged(r, g, b);
|
|
}
|
|
|
|
void ImageControl::blockAllSignals(bool block)
|
|
{
|
|
ui.spinRed->blockSignals(block);
|
|
ui.spinGreen->blockSignals(block);
|
|
ui.spinBlue->blockSignals(block);
|
|
ui.sliderRed->blockSignals(block);
|
|
ui.sliderGreen->blockSignals(block);
|
|
ui.sliderBlue->blockSignals(block);
|
|
}
|