mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-09-29 05:44:14 +02:00
162 lines
6.7 KiB
C++
162 lines
6.7 KiB
C++
/*
|
|
Copyright 2006-2026 The QElectroTech Team
|
|
This file is part of QElectroTech.
|
|
|
|
QElectroTech is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
QElectroTech is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#ifndef IMAGE_TRANSPARENT_COLOR_DIALOG_H
|
|
#define IMAGE_TRANSPARENT_COLOR_DIALOG_H
|
|
|
|
#include <QDialog>
|
|
#include <QImage>
|
|
#include <QColor>
|
|
#include <QLabel>
|
|
#include <QList>
|
|
|
|
class QVBoxLayout;
|
|
class QPushButton;
|
|
class QMouseEvent;
|
|
|
|
/**
|
|
@brief The ClickableImageLabel class
|
|
Displays an image scaled to fit a reasonable dialog size, but always
|
|
samples the picked color from the original, full-resolution image at
|
|
the corresponding coordinate -- never from the (possibly smoothly
|
|
interpolated, and therefore color-blended) scaled-down pixmap being
|
|
displayed, which would make the picked color subtly wrong for
|
|
exactly the pixels near an edge where it matters most.
|
|
*/
|
|
class ClickableImageLabel : public QLabel
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
explicit ClickableImageLabel(const QImage &sourceImage, QWidget *parent = nullptr);
|
|
|
|
/// The same downsampled image already computed for display --
|
|
/// reused as the live-preview source so a tolerance drag runs
|
|
/// its per-tick colour-key pass against a small image instead
|
|
/// of the full-resolution one, which on a large source (a
|
|
/// scanned schematic background, several Mpx) made every
|
|
/// intermediate slider tick visibly lag. resultPixmap() still
|
|
/// computes the final, committed result from the true
|
|
/// full-resolution source -- only the live preview is
|
|
/// downsampled.
|
|
QImage displayImage() const { return m_displayImage; }
|
|
|
|
signals:
|
|
void colorPicked(const QColor &color);
|
|
|
|
protected:
|
|
void mousePressEvent(QMouseEvent *event) override;
|
|
|
|
private:
|
|
QImage m_source;
|
|
QImage m_displayImage;
|
|
qreal m_displayScale = 1.0;
|
|
};
|
|
|
|
/**
|
|
@brief The ImageTransparentColorDialog class
|
|
Lets the user click directly on a preview of the image to sample one
|
|
or more colors -- each click adds to the set rather than replacing
|
|
the previous pick -- each shown as its own row: a colour swatch, a
|
|
slider for that colour's own tolerance right next to it, and a
|
|
remove button. Each colour keeps its own tolerance rather than
|
|
sharing one: a white background and a grey border rarely need the
|
|
same looseness of match, and forcing one tolerance onto both meant
|
|
either the background left ragged edges or the border ate into
|
|
content near it. A live checkerboard-backed preview of the combined
|
|
result updates as any slider moves. A self-contained modal dialog
|
|
rather than a diagram-level "click the canvas to pick" interaction
|
|
mode, since this needs neither undo-during-drag nor coexistence
|
|
with other tools; it only ever needs a handful of clicks, evaluated
|
|
against a pixmap the caller already has in hand.
|
|
*/
|
|
class ImageTransparentColorDialog : public QDialog
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
/// One picked colour and the tolerance it's individually keyed
|
|
/// with -- the whole point of this being a struct rather than
|
|
/// two parallel lists is that the two can never drift out of
|
|
/// index alignment with each other.
|
|
struct PickedColor
|
|
{
|
|
QColor color;
|
|
int tolerance;
|
|
};
|
|
|
|
/// @param basePixmap the pristine source to pick colours from --
|
|
/// the caller's responsibility to pass the true original, not
|
|
/// an already colour-keyed result, or previously-transparent
|
|
/// areas would show as plain background rather than a pickable
|
|
/// surface, and re-picking the same colour would be a no-op.
|
|
/// @param existingColors colours (with their individual
|
|
/// tolerances) already keyed out of basePixmap in a previous
|
|
/// session, shown as rows from the start rather than forcing
|
|
/// them to be re-picked from scratch.
|
|
explicit ImageTransparentColorDialog(const QPixmap &basePixmap,
|
|
const QList<PickedColor> &existingColors = {}, QWidget *parent = nullptr);
|
|
|
|
/// The resulting pixmap: basePixmap unchanged if no colour is
|
|
/// picked, colour-keyed against every picked colour (each at
|
|
/// its own tolerance) otherwise.
|
|
QPixmap resultPixmap() const;
|
|
/// The final set of picked colours and their individual
|
|
/// tolerances, for the caller to remember across dialog
|
|
/// sessions -- may differ from existingColors if any were
|
|
/// added, removed, or had their tolerance adjusted.
|
|
QList<PickedColor> pickedColors() const { return m_pickedColors; }
|
|
|
|
/// Public so DiagramImageItem can re-derive its display pixmap
|
|
/// directly (base + crop + these colours) without needing to
|
|
/// re-open this dialog every time the crop region changes --
|
|
/// binary transparency within tolerance, not a smooth falloff:
|
|
/// every pixel within a colour's own `tolerance` (0-100, mapped
|
|
/// onto the maximum possible RGB distance) of that colour
|
|
/// becomes fully transparent, everything else keeps its
|
|
/// existing alpha untouched. Squared distance throughout,
|
|
/// avoiding a sqrt per pixel; breaks out of the inner loop on
|
|
/// the first matching colour, since further matches wouldn't
|
|
/// change the outcome.
|
|
static QImage applyColorKey(const QImage &source, const QList<PickedColor> &keyColors);
|
|
|
|
private slots:
|
|
void onColorPicked(const QColor &color);
|
|
|
|
private:
|
|
void setToleranceForIndex(int index, int value);
|
|
void removeColor(int index);
|
|
void rebuildSwatches();
|
|
void updatePreview();
|
|
void updateOkEnabled();
|
|
static QPixmap onCheckerboard(const QImage &image);
|
|
|
|
QImage m_sourceImage;
|
|
QImage m_previewSourceImage; // downsampled -- see ClickableImageLabel::displayImage()'s comment for why
|
|
QList<PickedColor> m_pickedColors;
|
|
bool m_startedWithColors = false; // whether existingColors was non-empty -- see updateOkEnabled()'s comment for why this matters
|
|
int m_lastToleranceUsed = 10; // seeds a newly-picked colour's own tolerance, so successive picks in one session feel consistent rather than each resetting to some fixed default
|
|
|
|
ClickableImageLabel *m_sourceLabel;
|
|
QLabel *m_previewLabel;
|
|
QVBoxLayout *m_swatchesLayout; // one row per picked colour, stacked vertically -- each row now carries its own slider, too wide to lay out side by side the way plain swatches once were
|
|
QLabel *m_hintLabel;
|
|
QPushButton *m_okButton;
|
|
};
|
|
|
|
#endif // IMAGE_TRANSPARENT_COLOR_DIALOG_H
|