mirror of
https://bitbucket.org/mfeemster/fractorium.git
synced 2025-06-30 21:36:33 -04:00
--User changes
-Add a palette editor. -Add support for reading .ugr/.gradient/.gradients palette files. -Allow toggling on spinners whose minimum value is not zero. -Allow toggling display of image, affines and grid. -Add new variations: cylinder2, circlesplit, tile_log, truchet_fill, waves2_radial. --Bug fixes -cpow2 was wrong. -Palettes with rapid changes in color would produce slightly different outputs from Apo/Chaotica. This was due to a long standing bug from flam3. -Use exec() on Apple and show() on all other OSes for dialog boxes. -Trying to render a sequence with no frames would crash. -Selecting multiple xforms and rotating them would produce the wrong rotation. -Better handling when parsing flames using different encoding, such as unicode and UTF-8. -Switching between SP/DP didn't reselect the selected flame in the Library tab. --Code changes -Make all types concerning palettes be floats, including PaletteTableWidgetItem. -PaletteTableWidgetItem is no longer templated because all palettes are float. -Include the source colors for user created gradients. -Change parallel_for() calls to work with very old versions of TBB which are lingering on some systems. -Split conditional out of accumulation loop on the CPU for better performance. -Vectorize summing when doing density filter for better performance. -Make all usage of palettes be of type float, double is pointless. -Allow palettes to reside in multiple folders, while ensuring only one of each name is added. -Refactor some palette path searching code. -Make ReadFile() throw and catch an exception if the file operation fails. -A little extra safety in foci and foci3D with a call to Zeps(). -Cast to (real_t) in the OpenCL string for the w variation, which was having trouble compiling on Mac. -Fixing missing comma between paths in InitPaletteList(). -Move Xml and PaletteList classes into cpp to shorten build times when working on them. -Remove default param values for IterOpenCLKernelCreator<T>::SharedDataIndexDefines in cpp file. -Change more NULL to nullptr.
This commit is contained in:
79
Source/Fractorium/PaletteEditor/ColorPanel.cpp
Normal file
79
Source/Fractorium/PaletteEditor/ColorPanel.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
Copyright (C) 2009, Etienne Moutot <e.moutot@gmail.com>
|
||||
|
||||
This file is part of colorPickerWidget.
|
||||
|
||||
colorPickerWidget 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
colorPickerWidget 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "FractoriumPch.h"
|
||||
#include "ColorPanel.h"
|
||||
|
||||
/// <summary>
|
||||
/// Constructor which passes parent widget to the base and initializes the minimum size.
|
||||
/// </summary>
|
||||
/// <param name="p">The parent widget</param>
|
||||
ColorPanel::ColorPanel(QWidget* p)
|
||||
: QPushButton(p)
|
||||
{
|
||||
setMinimumSize(10, 10);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Derived paint event which paints the entire button surface with m_Color.
|
||||
/// </summary>
|
||||
void ColorPanel::paintEvent(QPaintEvent*)
|
||||
{
|
||||
QPainter p(this);
|
||||
p.setPen(m_Pen);
|
||||
p.setBrush(QBrush(m_Color));
|
||||
p.drawRect(QRect(2, 2, width() - 6, height() - 6));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the pen object used to paint the button.
|
||||
/// </summary>
|
||||
/// <param name="pen">The pen object used to paint the button</param>
|
||||
void ColorPanel::Pen(const QPen& pen)
|
||||
{
|
||||
m_Pen = pen;
|
||||
update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the pen object used to paint the button.
|
||||
/// </summary>
|
||||
/// <returns>QPen</returns>
|
||||
QPen ColorPanel::Pen() const
|
||||
{
|
||||
return m_Pen;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the color used to paint the button.
|
||||
/// </summary>
|
||||
/// <param name="color">The color used to paint the button</param>
|
||||
void ColorPanel::Color(const QColor& color)
|
||||
{
|
||||
m_Color = color;
|
||||
update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the color used to paint the button.
|
||||
/// </summary>
|
||||
/// <returns>QColor</returns>
|
||||
QColor ColorPanel::Color() const
|
||||
{
|
||||
return m_Color;
|
||||
}
|
47
Source/Fractorium/PaletteEditor/ColorPanel.h
Normal file
47
Source/Fractorium/PaletteEditor/ColorPanel.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
|
||||
Copyright (C) 2009, Etienne Moutot <e.moutot@gmail.com>
|
||||
|
||||
This file is part of colorPickerWidget.
|
||||
|
||||
colorPickerWidget 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
colorPickerWidget 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
|
||||
/// <summary>
|
||||
/// A derivation of a QPushButton which makes a large, clickable panel
|
||||
/// which custom paints the button based on a user specified color.
|
||||
/// </summary>
|
||||
class ColorPanel : public QPushButton
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ColorPanel(QWidget* p = nullptr);
|
||||
|
||||
void Pen(const QPen& pen);
|
||||
QPen Pen() const;
|
||||
void Color(const QColor& color);
|
||||
QColor Color() const;
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent* event) override;
|
||||
|
||||
private:
|
||||
QPen m_Pen;
|
||||
QColor m_Color;
|
||||
};
|
96
Source/Fractorium/PaletteEditor/ColorPickerWidget.cpp
Normal file
96
Source/Fractorium/PaletteEditor/ColorPickerWidget.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
|
||||
Copyright (C) 2009, Etienne Moutot <e.moutot@gmail.com>
|
||||
|
||||
This file is part of colorPickerWidget.
|
||||
|
||||
colorPickerWidget 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
colorPickerWidget 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "FractoriumPch.h"
|
||||
#include "ColorPickerWidget.h"
|
||||
|
||||
/// <summary>
|
||||
/// Constructor which passes parent widget to the base and initializes the member controls
|
||||
/// on a grid layout.
|
||||
/// </summary>
|
||||
/// <param name="p">The parent widget</param>
|
||||
ColorPickerWidget::ColorPickerWidget(QWidget* p)
|
||||
: QWidget(p)
|
||||
{
|
||||
m_ColorTriangle = new ColorTriangle(this);
|
||||
m_ColorPanel = new ColorPanel(this);
|
||||
m_ColorDialog = new QColorDialog(this);
|
||||
m_ColorPanel->Color(m_ColorTriangle->Color());
|
||||
connect(m_ColorTriangle, SIGNAL(ColorChanged(const QColor&)), this, SLOT(OnTriangleColorChanged(const QColor&)));
|
||||
connect(m_ColorPanel, SIGNAL(clicked()), this, SLOT(OnColorViewerClicked()));
|
||||
auto layout = new QGridLayout(this);
|
||||
layout->setMargin(4);
|
||||
layout->addWidget(m_ColorTriangle, 0, 0, 3, 1);
|
||||
layout->addWidget(m_ColorPanel, 0, 1, 3, 1);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the color used to paint the color panel.
|
||||
/// </summary>
|
||||
/// <returns>QColor</returns>
|
||||
QColor ColorPickerWidget::Color() const
|
||||
{
|
||||
return m_ColorPanel->Color();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the current color for the triangle.
|
||||
/// </summary>
|
||||
/// <param name="col">The parent widget</param>
|
||||
void ColorPickerWidget::SetColorPanelColor(const QColor& col)
|
||||
{
|
||||
if (col.isValid())
|
||||
m_ColorTriangle->Color(col);//Internally emits ColorChanged() which will call OnTriangleColorChanged(), which will call m_ColorPanel->Color().
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridden resize event to set the color panel height slightly
|
||||
/// smaller than the container it's in.
|
||||
/// </summary>
|
||||
/// <param name="col">The resize event</param>
|
||||
void ColorPickerWidget::resizeEvent(QResizeEvent* event)
|
||||
{
|
||||
m_ColorPanel->setMinimumHeight(event->size().height() - 22);
|
||||
m_ColorPanel->setMaximumHeight(event->size().height() - 22);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Slot called when the color panel is clicked, which will show the color
|
||||
/// picker dialog.
|
||||
/// </summary>
|
||||
void ColorPickerWidget::OnColorViewerClicked()
|
||||
{
|
||||
m_ColorDialog->setCurrentColor(m_ColorPanel->Color());
|
||||
auto newColor = m_ColorDialog->getColor(m_ColorPanel->Color(), this);
|
||||
SetColorPanelColor(newColor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Slot called when the color on the triangle changes for any reason,
|
||||
/// either user initiated or programatically called.
|
||||
/// </summary>
|
||||
/// <param name="col">The new color on the triangle</param>
|
||||
void ColorPickerWidget::OnTriangleColorChanged(const QColor& col)
|
||||
{
|
||||
if (col.isValid())
|
||||
m_ColorPanel->Color(col);
|
||||
|
||||
emit ColorChanged(col);
|
||||
}
|
54
Source/Fractorium/PaletteEditor/ColorPickerWidget.h
Normal file
54
Source/Fractorium/PaletteEditor/ColorPickerWidget.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
|
||||
Copyright (C) 2009, Etienne Moutot <e.moutot@gmail.com>
|
||||
|
||||
This file is part of colorPickerWidget.
|
||||
|
||||
colorPickerWidget 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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
colorPickerWidget 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
#include "ColorTriangle.h"
|
||||
#include "ColorPanel.h"
|
||||
|
||||
/// <summary>
|
||||
/// Aggregator class to package a color triangle, color panel, and color dialog
|
||||
/// all together on a layout.
|
||||
/// </summary>
|
||||
class ColorPickerWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ColorPickerWidget(QWidget* p = nullptr);
|
||||
|
||||
QColor Color() const;
|
||||
void SetColorPanelColor(const QColor& col);
|
||||
|
||||
Q_SIGNALS:
|
||||
void ColorChanged(const QColor& col);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
|
||||
private slots:
|
||||
void OnColorViewerClicked();
|
||||
void OnTriangleColorChanged(const QColor& col);
|
||||
|
||||
private:
|
||||
ColorTriangle* m_ColorTriangle;
|
||||
ColorPanel* m_ColorPanel;
|
||||
QColorDialog* m_ColorDialog;
|
||||
};
|
1600
Source/Fractorium/PaletteEditor/ColorTriangle.cpp
Normal file
1600
Source/Fractorium/PaletteEditor/ColorTriangle.cpp
Normal file
File diff suppressed because it is too large
Load Diff
138
Source/Fractorium/PaletteEditor/ColorTriangle.h
Normal file
138
Source/Fractorium/PaletteEditor/ColorTriangle.h
Normal file
@ -0,0 +1,138 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** This file is part of a Qt Solutions component.
|
||||
**
|
||||
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Qt Software Information (qt-info@nokia.com)
|
||||
**
|
||||
** Commercial Usage
|
||||
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||
** accordance with the Qt Solutions Commercial License Agreement provided
|
||||
** with the Software or, alternatively, in accordance with the terms
|
||||
** contained in a written agreement between you and Nokia.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain
|
||||
** additional rights. These rights are described in the Nokia Qt LGPL
|
||||
** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
|
||||
** package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Please note Third Party Software included with Qt Solutions may impose
|
||||
** additional restrictions and it is the user's responsibility to ensure
|
||||
** that they have met the licensing requirements of the GPL, LGPL, or Qt
|
||||
** Solutions Commercial license and the relevant license of the Third
|
||||
** Party Software they are using.
|
||||
**
|
||||
** If you are unsure which license is appropriate for your use, please
|
||||
** contact the sales department at qt-sales@nokia.com.
|
||||
**
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
|
||||
/// <summary>
|
||||
/// DoubleColor, Vertex and ColorTriangle classes.
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// Used to store color values in the range 0..255 as doubles.
|
||||
/// </summary>
|
||||
struct DoubleColor
|
||||
{
|
||||
double r, g, b;
|
||||
|
||||
DoubleColor() : r(0.0), g(0.0), b(0.0) {}
|
||||
DoubleColor(double red, double green, double blue) : r(red), g(green), b(blue) {}
|
||||
DoubleColor(const DoubleColor& c) : r(c.r), g(c.g), b(c.b) {}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Used to store pairs of DoubleColor and DoublePoint in one structure.
|
||||
/// </summary>
|
||||
struct Vertex
|
||||
{
|
||||
DoubleColor color;
|
||||
QPointF point;
|
||||
|
||||
Vertex(const DoubleColor& c, const QPointF& p) : color(c), point(p) {}
|
||||
Vertex(const QColor& c, const QPointF& p)
|
||||
: color(DoubleColor((double)c.red(), (double)c.green(),
|
||||
(double)c.blue())), point(p) {}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Widget for drawing a color triangle which allows users to select colors
|
||||
/// in a manner more intuitive than the usual color picker dialog.
|
||||
/// This class was taken from an open source project named Chaos Helper, which took
|
||||
/// it from the Qt examples, so it mostly remains as-is.
|
||||
/// </summary>
|
||||
class ColorTriangle : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ColorTriangle(QWidget* parent = nullptr);
|
||||
|
||||
void Polish();
|
||||
QColor Color() const;
|
||||
void Color(const QColor& col);
|
||||
|
||||
virtual int heightForWidth(int w) const override;
|
||||
virtual QSize sizeHint() const override;
|
||||
|
||||
signals:
|
||||
void ColorChanged(const QColor& col);
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent*) override;
|
||||
virtual void mouseMoveEvent(QMouseEvent*) override;
|
||||
virtual void mousePressEvent(QMouseEvent*) override;
|
||||
virtual void mouseReleaseEvent(QMouseEvent*) override;
|
||||
virtual void keyPressEvent(QKeyEvent* e) override;
|
||||
virtual void resizeEvent(QResizeEvent*) override;
|
||||
|
||||
private:
|
||||
void GenBackground();
|
||||
void DrawTrigon(QImage* p, const QPointF& a, const QPointF& b, const QPointF& c, const QColor& color);
|
||||
double CalcOuterRadius() const;
|
||||
double RadiusAt(const QPointF& pos, const QRect& rect) const;
|
||||
double AngleAt(const QPointF& pos, const QRect& rect) const;
|
||||
QColor ColorFromPoint(const QPointF& p) const;
|
||||
QPointF PointFromColor(const QColor& col) const;
|
||||
QPointF MovePointToTriangle(double x, double y, const Vertex& a, const Vertex& b, const Vertex& c) const;
|
||||
|
||||
bool mustGenerateBackground;
|
||||
int curHue;
|
||||
int penWidth;
|
||||
int ellipseSize;
|
||||
int outerRadius;
|
||||
double a, b, c;
|
||||
QImage bg;
|
||||
QColor curColor;
|
||||
QPointF pa, pb, pc, pd;
|
||||
QPointF selectorPos;
|
||||
|
||||
enum SelectionMode
|
||||
{
|
||||
Idle,
|
||||
SelectingHue,
|
||||
SelectingSatValue
|
||||
} selMode;
|
||||
};
|
78
Source/Fractorium/PaletteEditor/GradientArrow.h
Normal file
78
Source/Fractorium/PaletteEditor/GradientArrow.h
Normal file
@ -0,0 +1,78 @@
|
||||
/****************************************************************************/
|
||||
// This file is part of the gradLib library originally made by Stian Broen
|
||||
//
|
||||
// For more free libraries, please visit <http://broentech.no>
|
||||
//
|
||||
// gradLib 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program 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 this library. If not, see <http://www.gnu.org/licenses/>
|
||||
/****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
|
||||
/// <summary>
|
||||
/// Class for drawing the small arrows below the gradient in the palette editor.
|
||||
/// The drawing is accomplished via a QPolygon object.
|
||||
/// </summary>
|
||||
class GradientArrow
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Default constructor which sets up the size of the arrow.
|
||||
/// </summary>
|
||||
explicit GradientArrow()
|
||||
{
|
||||
QPolygon area;
|
||||
area << QPoint(5, 5) << QPoint(10, 0) << QPoint(15, 5) << QPoint(15, 15) << QPoint(5, 15) << QPoint(5, 5);
|
||||
Area(area);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor which takes the color and focus state of the arrow.
|
||||
/// </summary>
|
||||
/// <param name="col">The color of the arrow</param>
|
||||
/// <param name="focus">Whether the arrow is focused</param>
|
||||
explicit GradientArrow(QColor col, bool focus)
|
||||
: GradientArrow()
|
||||
{
|
||||
m_Color = col;
|
||||
m_Focus = focus;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy constructor to copy another GradientArrow.
|
||||
/// </summary>
|
||||
/// <param name="other">The GradientArrow object to copy</param>
|
||||
GradientArrow(const GradientArrow& other)
|
||||
: m_Focus(other.Focus()),
|
||||
m_Area(other.Area()),
|
||||
m_Color(other.Color())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Getters and setters.
|
||||
/// </summary>
|
||||
inline bool Focus() const { return m_Focus; }
|
||||
inline void Focus(bool val) { m_Focus = val; }
|
||||
inline const QPolygon Area() const { return m_Area; }
|
||||
inline void Area(const QPolygon& val) {m_Area = val; }
|
||||
inline const QColor Color() const { return m_Color; }
|
||||
inline void Color(const QColor& val) { m_Color = val; }
|
||||
|
||||
private:
|
||||
bool m_Focus;
|
||||
QPolygon m_Area;
|
||||
QColor m_Color;
|
||||
};
|
630
Source/Fractorium/PaletteEditor/GradientColorsView.cpp
Normal file
630
Source/Fractorium/PaletteEditor/GradientColorsView.cpp
Normal file
@ -0,0 +1,630 @@
|
||||
/****************************************************************************/
|
||||
// This file is part of the gradLib library originally made by Stian Broen
|
||||
//
|
||||
// For more free libraries, please visit <http://broentech.no>
|
||||
//
|
||||
// gradLib 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program 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 this library. If not, see <http://www.gnu.org/licenses/>
|
||||
/****************************************************************************/
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
#include "GradientColorsView.h"
|
||||
|
||||
/// <summary>
|
||||
/// Constructor which passes parent widget to the base and sets various size constraints.
|
||||
/// </summary>
|
||||
/// <param name="p">The parent widget</param>
|
||||
GradientColorsView::GradientColorsView(QWidget* p)
|
||||
: QWidget(p)
|
||||
{
|
||||
m_ViewRect = QRect(QPoint(0, 0), QPoint(0, 0));
|
||||
qRegisterMetaType<GradientArrow>("GradientArrow");
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
setMinimumSize(p->width() - 10, p->height() - 10);
|
||||
setMouseTracking(true);
|
||||
ResetToDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the focus to the arrow at the given normalized position.
|
||||
/// </summary>
|
||||
/// <param name="position">The normalized position of the arrow to focus</param>
|
||||
void GradientColorsView::SetFocus(float position)
|
||||
{
|
||||
bool focused = false;
|
||||
|
||||
for (auto& it : m_Arrows)
|
||||
{
|
||||
focused |= position == it.first;
|
||||
it.second.Focus(position == it.first);
|
||||
}
|
||||
|
||||
if (!focused)
|
||||
m_Arrows.begin()->second.Focus(true);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the focus to the arrow at the given index.
|
||||
/// </summary>
|
||||
/// <param name="position">The index of the arrow to focus</param>
|
||||
void GradientColorsView::SetFocus(size_t position)
|
||||
{
|
||||
bool focused = false;
|
||||
size_t index = 0;
|
||||
position = std::min(m_Arrows.size() - 1, position);
|
||||
|
||||
for (auto& it : m_Arrows)
|
||||
{
|
||||
bool b = position == index++;
|
||||
focused |= b;
|
||||
it.second.Focus(b);
|
||||
}
|
||||
|
||||
if (!focused)
|
||||
m_Arrows.begin()->second.Focus(true);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the color of the currently focused arrow to the passed in color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to set the focused arrow to</param>
|
||||
void GradientColorsView::SetFocusColor(const QColor& color)
|
||||
{
|
||||
for (auto& it : m_Arrows)
|
||||
{
|
||||
auto& anArrow = it.second;
|
||||
|
||||
if (anArrow.Focus())
|
||||
{
|
||||
anArrow.Color(color);
|
||||
update();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an arrow whose color will be assigned the passed in color.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to assign to the new arrow</param>
|
||||
void GradientColorsView::AddArrow(const QColor& color)
|
||||
{
|
||||
float position = 0.5f;
|
||||
|
||||
if (m_Arrows.size() >= 256)
|
||||
return;
|
||||
|
||||
if (m_Arrows.empty())
|
||||
{
|
||||
position = 0;
|
||||
}
|
||||
else if (m_Arrows.size() == 1)
|
||||
{
|
||||
position = (m_Arrows.begin()->first < 1) ? 1 : 0;
|
||||
}
|
||||
else if (m_Arrows.size() == 2)
|
||||
{
|
||||
auto b = m_Arrows.begin();
|
||||
auto rb = m_Arrows.rbegin();
|
||||
position = std::abs((rb->first + b->first) / 2.0);
|
||||
|
||||
if (position == b->first)
|
||||
position = b->first / 2.0;
|
||||
else if (position == rb->first)
|
||||
position = (1.0 + rb->first) / 2.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool set = false;
|
||||
auto it = m_Arrows.begin();
|
||||
auto oneBeforeLast = Advance(m_Arrows.begin(), m_Arrows.size() - 1);
|
||||
|
||||
for (; it != oneBeforeLast; ++it)
|
||||
{
|
||||
if (it->second.Focus())
|
||||
{
|
||||
auto next = Advance(it, 1);
|
||||
position = std::abs((next->first + it->first) / 2.0);
|
||||
set = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!set)
|
||||
{
|
||||
it = m_Arrows.begin();
|
||||
position = std::abs((Advance(it, 1)->first + it->first) / 2.0);
|
||||
}
|
||||
}
|
||||
|
||||
AddArrow(position, color);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an arrow whose position and color will be assigned the values passed in.
|
||||
/// If an arrow exists at the specified position, it is overwritten.
|
||||
/// </summary>
|
||||
/// <param name="position">The position to place the new arrow in</param>
|
||||
/// <param name="color">The color to assign to the new arrow</param>
|
||||
void GradientColorsView::AddArrow(float position, const QColor& color)
|
||||
{
|
||||
GradientArrow arrow;
|
||||
arrow.Focus(true);
|
||||
arrow.Color(color);
|
||||
m_Arrows[position] = arrow;
|
||||
SetFocus(position);
|
||||
update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete the currently focused arrow if there are more than 2 arrows.
|
||||
/// Set the focus to the arrow whose index is one greater than the one deleted.
|
||||
/// </summary>
|
||||
void GradientColorsView::DeleteFocusedArrow()
|
||||
{
|
||||
if (m_Arrows.size() <= 2)
|
||||
return;
|
||||
|
||||
size_t index = 0;
|
||||
|
||||
for (auto it = m_Arrows.begin(); it != m_Arrows.end(); ++it)
|
||||
{
|
||||
if (it->second.Focus())
|
||||
{
|
||||
m_Arrows.erase(it);
|
||||
break;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
SetFocus(index);
|
||||
update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invert the values of all colors by subtracting each component from 255.
|
||||
/// </summary>
|
||||
void GradientColorsView::InvertColors()
|
||||
{
|
||||
for (auto& it : m_Arrows)
|
||||
{
|
||||
auto& arrow = it.second;
|
||||
auto col = arrow.Color();
|
||||
arrow.Color(QColor(255 - col.red(), 255 - col.green(), 255 - col.blue()));
|
||||
|
||||
if (arrow.Focus())
|
||||
emit ArrowDoubleClicked(arrow);
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set each component of each color to a random value between 0 and 255 inclusive.
|
||||
/// </summary>
|
||||
void GradientColorsView::RandomColors()
|
||||
{
|
||||
for (auto& it : m_Arrows)
|
||||
it.second.Color(
|
||||
{
|
||||
int(QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedRand(256)),
|
||||
int(QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedRand(256)),
|
||||
int(QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedRand(256))
|
||||
});
|
||||
update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the distance between each arrow to be equal.
|
||||
/// </summary>
|
||||
void GradientColorsView::DistributeColors()
|
||||
{
|
||||
map<float, GradientArrow> arrows;
|
||||
float index = 0, inc = 1.0f / std::max<size_t>(size_t(1), m_Arrows.size() - 1);
|
||||
|
||||
for (auto it : m_Arrows)
|
||||
{
|
||||
arrows[index] = it.second;
|
||||
index = std::min(1.0f, index + inc);
|
||||
}
|
||||
|
||||
m_Arrows = std::move(arrows);
|
||||
update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete all arrows and add a white arrow at index 0, and a black
|
||||
/// arrow at index 1.
|
||||
/// </summary>
|
||||
void GradientColorsView::ResetToDefault()
|
||||
{
|
||||
ClearArrows();
|
||||
AddArrow(0.0, Qt::white);
|
||||
AddArrow(1.0, Qt::black);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all arrows.
|
||||
/// </summary>
|
||||
void GradientColorsView::ClearArrows()
|
||||
{
|
||||
m_Arrows.clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the arrow at the specified index to the specified color, and also
|
||||
/// focus it.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to assign to the arrow at the specified index</param>
|
||||
/// <param name="index">The index of the arrow to assign the color to and focus</param>
|
||||
void GradientColorsView::NewFocusColor(const QColor& color, int index)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (auto& kv : m_Arrows)
|
||||
{
|
||||
auto& arrow = kv.second;
|
||||
|
||||
if (i == index)
|
||||
{
|
||||
arrow.Color(color);
|
||||
arrow.Focus(true);
|
||||
update();
|
||||
}
|
||||
else
|
||||
arrow.Focus(false);
|
||||
|
||||
kv.second = arrow;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the arrow map to the passed in one.
|
||||
/// </summary>
|
||||
/// <param name="newArrows">The new arrows to assign to the internal m_Arrows member</param>
|
||||
void GradientColorsView::SetArrows(map<float, GradientArrow>& newArrows)
|
||||
{
|
||||
m_Arrows = newArrows;
|
||||
update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the number of arrows in the map.
|
||||
/// </summary>
|
||||
/// <returns>int</returns>
|
||||
int GradientColorsView::ArrowCount()
|
||||
{
|
||||
return int(m_Arrows.size());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the index of the focused arrow.
|
||||
/// Return 0 if none are focused.
|
||||
/// </summary>
|
||||
/// <returns>The focused index if at least one arrow is focused, else 0.</returns>
|
||||
int GradientColorsView::GetFocusedIndex()
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
for (auto& kv : m_Arrows)
|
||||
{
|
||||
if (kv.second.Focus())
|
||||
break;
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a pixmap to be used to draw the palette.
|
||||
/// The pixmap is lazily instantiated on the first call, and all subsequent
|
||||
/// calls return a pointer to the same pixmap.
|
||||
/// </summary>
|
||||
/// <returns>The pixmap</returns>
|
||||
QPixmap* GradientColorsView::GetBackGround()
|
||||
{
|
||||
if (!m_Background.get())
|
||||
CreateBackground(m_BackgroundVerSpace, m_BackgroundHorSpace);
|
||||
|
||||
return m_Background.get();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a reference to the arrows map.
|
||||
/// Be very careful what you do with this.
|
||||
/// </summary>
|
||||
/// <returns>A reference to the internal map containing the arrows</returns>
|
||||
map<float, GradientArrow>& GradientColorsView::GetArrows()
|
||||
{
|
||||
return m_Arrows;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populate the palette member with the specified number of elements based on
|
||||
/// interpolating the values in the arrows and return a reference to it.
|
||||
/// </summary>
|
||||
/// <param name="size">The number of elements the palette will have</param>
|
||||
/// <returns>A reference to the internal map containing the arrows</returns>
|
||||
Palette<float>& GradientColorsView::GetPalette(int size)
|
||||
{
|
||||
QSize imageSize(size, 1);
|
||||
QImage image(imageSize, QImage::Format_ARGB32_Premultiplied);
|
||||
QPainter p;
|
||||
QLinearGradient grad(QPoint(0, 0), QPoint(imageSize.width(), imageSize.height()));
|
||||
m_Palette.m_SourceColors.clear();
|
||||
|
||||
for (auto& it : m_Arrows)
|
||||
{
|
||||
auto pos = it.first;
|
||||
auto col = it.second.Color();
|
||||
m_Palette.m_SourceColors[pos] = v4F(col.red() / 255.0f, col.green() / 255.0f, col.blue() / 255.0f, 1.0f);
|
||||
grad.setColorAt(pos, col);
|
||||
}
|
||||
|
||||
p.begin(&image);
|
||||
p.fillRect(image.rect(), grad);
|
||||
p.end();
|
||||
m_Palette.m_Entries.reserve(image.width());
|
||||
|
||||
for (int i = 0; i < image.width(); i++)
|
||||
{
|
||||
QColor col(image.pixel(i, 0));
|
||||
m_Palette[i].r = col.red() / 255.0f;
|
||||
m_Palette[i].g = col.green() / 255.0f;
|
||||
m_Palette[i].b = col.blue() / 255.0f;
|
||||
}
|
||||
|
||||
return m_Palette;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assign the values of the m_SourceColors member of the palette to the
|
||||
/// internal map of arrows. Note this assignment will only take place if
|
||||
/// the number of source colors is 2 or more.
|
||||
/// This will only be the case if it was a user created palette made here.
|
||||
/// All palettes gotten from elsewhere are not assignable.
|
||||
/// </summary>
|
||||
/// <param name="palette">The palette whose source colors will be assigned to the arrow map</param>
|
||||
void GradientColorsView::SetPalette(const Palette<float>& palette)
|
||||
{
|
||||
if (palette.m_SourceColors.size() > 1)
|
||||
{
|
||||
m_Palette = palette;
|
||||
m_Arrows.clear();
|
||||
|
||||
for (auto& col : m_Palette.m_SourceColors)
|
||||
{
|
||||
auto& rgb = col.second;
|
||||
m_Arrows[col.first] = GradientArrow(QColor(rgb.r * 255, rgb.g * 255, rgb.b * 255), false);
|
||||
}
|
||||
|
||||
SetFocus(size_t(0));
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Custom paint event to draw the palette and arrows.
|
||||
/// </summary>
|
||||
void GradientColorsView::paintEvent(QPaintEvent*)
|
||||
{
|
||||
if (m_ViewRect.size().isNull() ||
|
||||
m_ViewRect.size().isEmpty() ||
|
||||
m_ViewRect.topLeft() == m_ViewRect.bottomRight())
|
||||
{
|
||||
m_ViewRect = QRect(QPoint(5, 0), QPoint(width() - 15, height() / 3 * 2 - 10));
|
||||
m_ViewRect.translate(5, 5);
|
||||
CreateBackground();
|
||||
}
|
||||
|
||||
QPainter painter(this);
|
||||
|
||||
if (m_Background.get())
|
||||
painter.drawPixmap(m_ViewRect, *m_Background.get(), m_ViewRect);
|
||||
|
||||
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
QPoint gradStart = QPoint(m_ViewRect.topLeft().x(), m_ViewRect.bottomLeft().y() / 2);
|
||||
QPoint gradStop = QPoint(m_ViewRect.topRight().x(), m_ViewRect.bottomRight().y() / 2);
|
||||
QLinearGradient grad(gradStart, gradStop);
|
||||
|
||||
for (auto& it : m_Arrows)
|
||||
{
|
||||
GradientArrow& arrow = it.second;
|
||||
grad.setColorAt(it.first, arrow.Color());
|
||||
QPolygon arrowPolygon = arrow.Area();
|
||||
int iPosX = it.first * (width() - 20),
|
||||
iPosY = height() / 3 * 2;
|
||||
arrowPolygon.translate(iPosX, iPosY);
|
||||
QPainterPath paintPath;
|
||||
paintPath.addPolygon(arrowPolygon);
|
||||
painter.setBrush(QBrush(arrow.Color()));
|
||||
|
||||
if (arrow.Focus())
|
||||
paintPath.addRect(iPosX + 5, iPosY + 20, 10, 5);
|
||||
|
||||
painter.drawPath(paintPath);
|
||||
painter.setBrush(QBrush(Qt::NoBrush));
|
||||
}
|
||||
|
||||
QBrush brush(grad);
|
||||
painter.fillRect(m_ViewRect, brush);
|
||||
painter.drawRect(m_ViewRect);
|
||||
painter.end();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for detecting when the mouse is pressed on an arrow to begin dragging.
|
||||
/// </summary>
|
||||
/// <param name="e">The mouse event</param>
|
||||
void GradientColorsView::mousePressEvent(QMouseEvent* e)
|
||||
{
|
||||
m_DragStart = e->pos();
|
||||
|
||||
for (auto& it : m_Arrows)
|
||||
{
|
||||
auto& arrow = it.second;
|
||||
QPolygon poly = arrow.Area();
|
||||
poly.translate(it.first * (width() - 20), height() / 3 * 2);
|
||||
|
||||
if (poly.containsPoint(m_DragStart, Qt::OddEvenFill))
|
||||
{
|
||||
m_ArrowMoving = true;
|
||||
arrow.Focus(true);
|
||||
}
|
||||
else
|
||||
arrow.Focus(false);
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for detecting when the mouse is pressed on an arrow to begin dragging.
|
||||
/// </summary>
|
||||
/// <param name="event">The mouse event</param>
|
||||
void GradientColorsView::mouseDoubleClickEvent(QMouseEvent* e)
|
||||
{
|
||||
for (auto& it : m_Arrows)
|
||||
{
|
||||
auto& arrow = it.second;
|
||||
QPolygon poly = arrow.Area();
|
||||
poly.translate(it.first * (width() - 20), height() / 3 * 2);
|
||||
|
||||
if (poly.containsPoint(e->pos(), Qt::OddEvenFill))
|
||||
{
|
||||
arrow.Focus(true);
|
||||
emit ArrowDoubleClicked(arrow);
|
||||
}
|
||||
else
|
||||
arrow.Focus(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for detecting when the mouse is moving during dragging.
|
||||
/// </summary>
|
||||
/// <param name="event">The mouse event</param>
|
||||
void GradientColorsView::mouseMoveEvent(QMouseEvent* e)
|
||||
{
|
||||
if (!m_ArrowMoving) return;
|
||||
|
||||
size_t index = 0;
|
||||
qreal maxMove = 11.5 / (width() - 20);
|
||||
|
||||
for (auto it = m_Arrows.begin(); it != m_Arrows.end(); ++it)
|
||||
{
|
||||
auto& arrow = it->second;
|
||||
|
||||
if (arrow.Focus())
|
||||
{
|
||||
qreal lastPos = it->first;
|
||||
qreal start = m_DragStart.x();
|
||||
qreal end = width() - 20;
|
||||
qreal dPos = ((qreal) e->pos().x() - start) / end;
|
||||
qreal newPos = lastPos + dPos;
|
||||
|
||||
if ( (it->first + dPos > 1) || (it->first + dPos < 0) )
|
||||
return;
|
||||
|
||||
if (dPos < 0 && index > 0)
|
||||
{
|
||||
qreal posBefore = std::prev(it)->first;
|
||||
|
||||
if ( (lastPos - maxMove + dPos) <= posBefore )
|
||||
return;
|
||||
}
|
||||
|
||||
if ((dPos > 0) && (index < (m_Arrows.size() - 1)))
|
||||
{
|
||||
qreal posAfter = std::next(it)->first;
|
||||
|
||||
if ((lastPos + maxMove + dPos) >= posAfter)
|
||||
return;
|
||||
}
|
||||
|
||||
GradientArrow arrowCopy(it->second);
|
||||
m_Arrows.erase(lastPos);
|
||||
m_Arrows[newPos] = arrowCopy;
|
||||
emit ArrowMove(lastPos, arrow);
|
||||
break;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
m_DragStart = e->pos();
|
||||
update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for detecting when the mouse is released during dragging.
|
||||
/// </summary>
|
||||
void GradientColorsView::mouseReleaseEvent(QMouseEvent*)
|
||||
{
|
||||
m_ArrowMoving = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for custom drawing the viewable area when its resized.
|
||||
/// </summary>
|
||||
void GradientColorsView::resizeEvent(QResizeEvent*)
|
||||
{
|
||||
m_ViewRect = QRect(QPoint(5, 0), QPoint(width() - 15, height() / 3 * 2 - 10));
|
||||
m_ViewRect.translate(5, 5);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the background to represent the palette.
|
||||
/// </summary>
|
||||
/// <param name="vertLineSpace">The space between vertical lines to use</param>
|
||||
/// <param name="horLineSpace">The space between horizontal lines to use</param>
|
||||
void GradientColorsView::CreateBackground(int vertLineSpace, int horLineSpace)
|
||||
{
|
||||
m_BackgroundVerSpace = vertLineSpace;
|
||||
m_BackgroundHorSpace = horLineSpace;
|
||||
m_Background = make_unique<QPixmap>(QSize(800, 800));
|
||||
m_Background->fill(Qt::white);
|
||||
QPainter painter(m_Background.get());
|
||||
int x = 0;
|
||||
|
||||
while (x < m_Background->width())//Veritcal lines.
|
||||
{
|
||||
const QPoint lineStart(x, 0);
|
||||
const QPoint lineStop(x, m_Background->height());
|
||||
painter.drawLine(lineStart, lineStop);
|
||||
x += vertLineSpace;
|
||||
}
|
||||
|
||||
int y = 0;
|
||||
|
||||
while (y < m_Background->height())//Horizontal lines.
|
||||
{
|
||||
const QPoint lineStart(0, y);
|
||||
const QPoint lineStop(m_Background->width(), y);
|
||||
painter.drawLine(lineStart, lineStop);
|
||||
y += horLineSpace;
|
||||
}
|
||||
|
||||
painter.end();
|
||||
update();
|
||||
}
|
80
Source/Fractorium/PaletteEditor/GradientColorsView.h
Normal file
80
Source/Fractorium/PaletteEditor/GradientColorsView.h
Normal file
@ -0,0 +1,80 @@
|
||||
/****************************************************************************/
|
||||
// This file is part of the gradLib library originally made by Stian Broen
|
||||
//
|
||||
// For more free libraries, please visit <http://broentech.no>
|
||||
//
|
||||
// gradLib 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program 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 this library. If not, see <http://www.gnu.org/licenses/>
|
||||
/****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
#include "GradientArrow.h"
|
||||
|
||||
/// <summary>
|
||||
/// Class for drawing the resulting palette created by interpolating the key colors
|
||||
/// as well as the arrows underneath the palette.
|
||||
/// The arrows are held in a sorted map whose key is the normalized index of the arrow,
|
||||
/// between 0 and 1 inclusive. They value is the arrow itself.
|
||||
/// The resulting palette is always stored in the m_Palette member.
|
||||
/// </summary>
|
||||
class GradientColorsView : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GradientColorsView(QWidget* p = nullptr);
|
||||
void SetFocus(float position);
|
||||
void SetFocus(size_t position);
|
||||
void SetFocusColor(const QColor& col);
|
||||
void AddArrow(const QColor& color);
|
||||
void AddArrow(float position, const QColor& color);
|
||||
void DeleteFocusedArrow();
|
||||
void InvertColors();
|
||||
void RandomColors();
|
||||
void DistributeColors();
|
||||
void ResetToDefault();
|
||||
void ClearArrows();
|
||||
void NewFocusColor(const QColor& col, int index);
|
||||
void SetArrows(map<float, GradientArrow>& newArrows);
|
||||
int ArrowCount();
|
||||
int GetFocusedIndex();
|
||||
QPixmap* GetBackGround();
|
||||
map<float, GradientArrow>& GetArrows();
|
||||
Palette<float>& GetPalette(int size);
|
||||
void SetPalette(const Palette<float>& palette);
|
||||
|
||||
signals:
|
||||
void ArrowMove(qreal lastPos, const GradientArrow& arrow);
|
||||
void ArrowDoubleClicked(const GradientArrow& arrow);
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent* e) override;
|
||||
virtual void mousePressEvent(QMouseEvent* e) override;
|
||||
virtual void mouseDoubleClickEvent(QMouseEvent* e) override;
|
||||
virtual void mouseMoveEvent(QMouseEvent* e) override;
|
||||
virtual void mouseReleaseEvent(QMouseEvent* e) override;
|
||||
virtual void resizeEvent(QResizeEvent*) override;
|
||||
|
||||
private:
|
||||
void CreateBackground(int vertLineSpace = 5, int horLineSpace = 5);
|
||||
bool m_ArrowMoving = false;
|
||||
int m_BackgroundVerSpace = 5;
|
||||
int m_BackgroundHorSpace = 5;
|
||||
QRect m_ViewRect;
|
||||
QPoint m_DragStart;
|
||||
unique_ptr<QPixmap> m_Background;
|
||||
map<float, GradientArrow> m_Arrows;
|
||||
Palette<float> m_Palette;
|
||||
};
|
498
Source/Fractorium/PaletteEditor/PaletteEditor.cpp
Normal file
498
Source/Fractorium/PaletteEditor/PaletteEditor.cpp
Normal file
@ -0,0 +1,498 @@
|
||||
#include "FractoriumPch.h"
|
||||
#include "PaletteEditor.h"
|
||||
#include "ui_paletteeditor.h"
|
||||
|
||||
/// <summary>
|
||||
/// Constructor which passes parent widget to the base and sets up slots and other ui
|
||||
/// elements.
|
||||
/// This takes a reference to a palette list, which it then searches for user created palettes
|
||||
/// and only populates the combo box with those.
|
||||
/// </summary>
|
||||
/// <param name="paletteList">A reference to an existing palette list, gotten from the main window.</param>
|
||||
/// <param name="p">The parent widget</param>
|
||||
PaletteEditor::PaletteEditor(PaletteList<float>& paletteList, QWidget* p) :
|
||||
QDialog(p),
|
||||
ui(make_unique<Ui::PaletteEditor>()),
|
||||
m_PaletteList(paletteList)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
m_ColorPicker = new ColorPickerWidget(this);
|
||||
m_ColorPicker->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
m_ColorPicker->SetColorPanelColor(Qt::black);
|
||||
QVBoxLayout* colorLayout = new QVBoxLayout();
|
||||
colorLayout->setMargin(3);
|
||||
colorLayout->addWidget(m_ColorPicker);
|
||||
ui->ColorPickerGroupBox->setLayout(colorLayout);
|
||||
ui->ColorPickerGroupBox->setContentsMargins(3, 8, 3, 3);
|
||||
m_GradientColorView = new GradientColorsView(ui->ColorViewGroupBox);
|
||||
connect(m_ColorPicker, SIGNAL(ColorChanged(const QColor&)), this, SLOT(OnColorPickerColorChanged(const QColor&)));
|
||||
connect(m_GradientColorView, SIGNAL(ArrowMove(qreal, const GradientArrow&)), this, SLOT(OnArrowMoved(qreal, const GradientArrow&)));
|
||||
connect(m_GradientColorView, SIGNAL(ArrowDoubleClicked(const GradientArrow&)), this, SLOT(OnArrowDoubleClicked(const GradientArrow&)));
|
||||
connect(ui->CreatePaletteFromImageButton, SIGNAL(clicked()), this, SLOT(OnCreatePaletteFromImageButtonClicked()));
|
||||
connect(ui->CreatePaletteAgainFromImageButton, SIGNAL(clicked()), this, SLOT(OnCreatePaletteAgainFromImageButton()));
|
||||
connect(ui->AddColorButton, SIGNAL(clicked()), this, SLOT(OnAddColorButtonClicked()));
|
||||
connect(ui->RemoveColorButton, SIGNAL(clicked()), this, SLOT(OnRemoveColorButtonClicked()));
|
||||
connect(ui->InvertColorsButton, SIGNAL(clicked()), this, SLOT(OnInvertColorsButtonClicked()));
|
||||
connect(ui->ResetColorsButton, SIGNAL(clicked()), this, SLOT(OnResetToDefaultButtonClicked()));
|
||||
connect(ui->DistributeColorsButton, SIGNAL(clicked()), this, SLOT(OnDistributeColorsButtonClicked()));
|
||||
connect(ui->RandomColorsButton, SIGNAL(clicked()), this, SLOT(OnRandomColorsButtonClicked()));
|
||||
connect(ui->SyncCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSyncCheckBoxStateChanged(int)), Qt::QueuedConnection);
|
||||
connect(ui->PaletteFilenameCombo, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(OnPaletteFilenameComboChanged(const QString&)), Qt::QueuedConnection);
|
||||
connect(ui->PaletteListTable, SIGNAL(cellClicked(int, int)), this, SLOT(OnPaletteCellClicked(int, int)), Qt::QueuedConnection);
|
||||
connect(ui->PaletteListTable, SIGNAL(cellChanged(int, int)), this, SLOT(OnPaletteCellChanged(int, int)), Qt::QueuedConnection);
|
||||
connect(ui->NewPaletteFileButton, SIGNAL(clicked()), this, SLOT(OnNewPaletteFileButtonClicked()));
|
||||
connect(ui->CopyPaletteFileButton, SIGNAL(clicked()), this, SLOT(OnCopyPaletteFileButtonClicked()));
|
||||
connect(ui->AppendPaletteButton, SIGNAL(clicked()), this, SLOT(OnAppendPaletteButtonClicked()));
|
||||
connect(ui->OverwritePaletteButton, SIGNAL(clicked()), this, SLOT(OnOverwritePaletteButtonClicked()));
|
||||
connect(ui->DeletePaletteButton, SIGNAL(clicked()), this, SLOT(OnDeletePaletteButtonClicked()));
|
||||
ui->PaletteListTable->horizontalHeader()->setSectionsClickable(true);
|
||||
auto layout = new QVBoxLayout();
|
||||
layout->setMargin(0);
|
||||
layout->setSpacing(0);
|
||||
layout->addWidget(m_GradientColorView);
|
||||
ui->ColorViewGroupBox->setLayout(layout);
|
||||
auto& pals = m_PaletteList.Palettes();
|
||||
|
||||
for (auto& pal : pals)
|
||||
{
|
||||
if (m_PaletteList.IsModifiable(pal.first))//Only add user created palettes.
|
||||
{
|
||||
QFileInfo info(QString::fromStdString(pal.first));
|
||||
ui->PaletteFilenameCombo->addItem(info.fileName());
|
||||
}
|
||||
}
|
||||
|
||||
if (ui->PaletteFilenameCombo->count() > 0)
|
||||
m_CurrentPaletteFilePath = ui->PaletteFilenameCombo->itemText(0).toStdString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get whether change events here are propagated back to the main window.
|
||||
/// </summary>
|
||||
/// <returns>bool</returns>
|
||||
bool PaletteEditor::Sync()
|
||||
{
|
||||
return ui->SyncCheckBox->isChecked();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a pointer to the palette pixmap from the underlying gradient color view.
|
||||
/// </summary>
|
||||
/// <returns>QPixmap*</returns>
|
||||
QPixmap* PaletteEditor::GetBackGround()
|
||||
{
|
||||
return m_GradientColorView->GetBackGround();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populate and retrieve a reference to the palette from the underlying gradient color view
|
||||
/// using the specified number of elements.
|
||||
/// </summary>
|
||||
/// <param name="size">The number of elements the palette will have</param>
|
||||
/// <returns>A freference to the palette</returns>
|
||||
Palette<float>& PaletteEditor::GetPalette(int size)
|
||||
{
|
||||
return m_GradientColorView->GetPalette(size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the palette of the underlying gradient color view.
|
||||
/// Note this assignment will only take place if
|
||||
/// the number of source colors is 2 or more.
|
||||
/// This will only be the case if it was a user created palette made here.
|
||||
/// All palettes gotten from elsewhere are not assignable.
|
||||
/// </summary>
|
||||
/// <param name="palette">The palette to assign</param>
|
||||
void PaletteEditor::SetPalette(Palette<float>& palette)
|
||||
{
|
||||
if (palette.m_SourceColors.size() > 1)
|
||||
{
|
||||
m_PaletteIndex = std::numeric_limits<int>::max();
|
||||
m_GradientColorView->SetPalette(palette);
|
||||
auto& arrows = m_GradientColorView->GetArrows();
|
||||
|
||||
if (!arrows.empty())
|
||||
m_ColorPicker->SetColorPanelColor(arrows.begin()->second.Color());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a new arrow using the current color.
|
||||
/// Called when the Add Color button is clicked.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnAddColorButtonClicked()
|
||||
{
|
||||
AddArrow(m_ColorPicker->Color());
|
||||
EmitPaletteChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete the focused arrow and optionally redistribute the arrows.
|
||||
/// Called when the Remove Color button is clicked.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnRemoveColorButtonClicked()
|
||||
{
|
||||
m_GradientColorView->DeleteFocusedArrow();
|
||||
|
||||
if (ui->AutoDistributeCheckBox->isChecked())
|
||||
m_GradientColorView->DistributeColors();
|
||||
|
||||
EmitPaletteChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invert the colors of the arrows.
|
||||
/// Called when the Invert Colors button is clicked.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnInvertColorsButtonClicked()
|
||||
{
|
||||
m_GradientColorView->InvertColors();
|
||||
EmitPaletteChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Randomize the colors of the arrows.
|
||||
/// Called when the Random Colors button is clicked.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnRandomColorsButtonClicked()
|
||||
{
|
||||
m_GradientColorView->RandomColors();
|
||||
EmitPaletteChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the distance between each arrow to be equal.
|
||||
/// Called when the Distribute Colors button is clicked.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnDistributeColorsButtonClicked()
|
||||
{
|
||||
m_GradientColorView->DistributeColors();
|
||||
EmitPaletteChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete all arrows and add a white arrow at index 0, and a black
|
||||
/// arrow at index 1.
|
||||
/// Called when the Reset button is clicked.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnResetToDefaultButtonClicked()
|
||||
{
|
||||
m_GradientColorView->ResetToDefault();
|
||||
EmitPaletteChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a palette by opening an image and selecting the colors from
|
||||
/// pixels at random locations.
|
||||
/// Ensure arrows spin box has a value of at least two.
|
||||
/// Called when the From Image button is clicked.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnCreatePaletteFromImageButtonClicked()
|
||||
{
|
||||
auto filenames = SetupOpenImagesDialog();
|
||||
|
||||
if (!filenames.empty())
|
||||
{
|
||||
m_Filename = filenames[0];
|
||||
|
||||
if (ui->ArrowsSpinBox->value() < 2)
|
||||
ui->ArrowsSpinBox->setValue(2);
|
||||
|
||||
auto colors = GetRandomColorsFromImage(m_Filename, ui->ArrowsSpinBox->value());
|
||||
m_GradientColorView->SetArrows(colors);
|
||||
EmitPaletteChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a palette by re-opening the previously selected image and selecting the colors from
|
||||
/// pixels at random locations.
|
||||
/// Ensure arrows spin box has a value of at least two.
|
||||
/// Called when the From Image Again button is clicked.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnCreatePaletteAgainFromImageButton()
|
||||
{
|
||||
if (QFile::exists(m_Filename))
|
||||
{
|
||||
if (ui->ArrowsSpinBox->value() < 2)
|
||||
ui->ArrowsSpinBox->setValue(2);
|
||||
|
||||
auto colors = GetRandomColorsFromImage(m_Filename, ui->ArrowsSpinBox->value());
|
||||
m_GradientColorView->SetArrows(colors);
|
||||
EmitPaletteChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the focus color as a result of selecting a stock in the color picker.
|
||||
/// Called when the color picker signals the ColorChanged event.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnColorPickerColorChanged(const QColor& col)
|
||||
{
|
||||
m_GradientColorView->SetFocusColor(col);
|
||||
EmitPaletteChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the color panel color as a result of double clicking an arrow.
|
||||
/// Called when the color view signals the ArrowDoubleClicked event.
|
||||
/// </summary>
|
||||
/// <param name="arrow">The arrow which was double clicked on</param>
|
||||
void PaletteEditor::OnArrowDoubleClicked(const GradientArrow& arrow)
|
||||
{
|
||||
m_ColorPicker->SetColorPanelColor(arrow.Color());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change whether palette changes are synced with the main window.
|
||||
/// Called when the Sync checkbox is checked/unchecked.
|
||||
/// </summary>
|
||||
/// <param name="state">Ignored</param>
|
||||
void PaletteEditor::OnSyncCheckBoxStateChanged(int state)
|
||||
{
|
||||
EmitPaletteChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the palette file based on the currently selected index in the combo box.
|
||||
/// Called when the index of the palette filename combo box changes.
|
||||
/// </summary>
|
||||
/// <param name="text">The text of the combo box, which is just the palette filename without the path.</param>
|
||||
void PaletteEditor::OnPaletteFilenameComboChanged(const QString& text)
|
||||
{
|
||||
if (!text.isEmpty())//This occasionally seems to get called with an empty string for reasons unknown.
|
||||
{
|
||||
auto paletteTable = ui->PaletteListTable;
|
||||
m_CurrentPaletteFilePath = text.toStdString();
|
||||
::FillPaletteTable(text.toStdString(), paletteTable, m_PaletteList);
|
||||
auto fullname = m_PaletteList.GetFullPathFromFilename(m_CurrentPaletteFilePath);
|
||||
ui->PaletteFilenameCombo->setToolTip(QString::fromStdString(fullname));
|
||||
OnPaletteCellClicked(0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the palette into the editor controls.
|
||||
/// Called when the second column in a row in the palette list is clicked.
|
||||
/// </summary>
|
||||
/// <param name="row">The table row clicked</param>
|
||||
/// <param name="col">The table column clicked</param>
|
||||
void PaletteEditor::OnPaletteCellClicked(int row, int col)
|
||||
{
|
||||
if (col == 1)
|
||||
{
|
||||
if (auto palette = m_PaletteList.GetPaletteByFilename(m_CurrentPaletteFilePath, row))
|
||||
{
|
||||
SetPalette(*palette);
|
||||
m_PaletteIndex = row;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the name of the palette.
|
||||
/// Called when the first column in a row in the palette list is clicked and edited.
|
||||
/// </summary>
|
||||
/// <param name="row">The table row clicked</param>
|
||||
/// <param name="col">The table column clicked</param>
|
||||
void PaletteEditor::OnPaletteCellChanged(int row, int col)
|
||||
{
|
||||
if (col == 0)
|
||||
{
|
||||
if (auto palette = m_PaletteList.GetPaletteByFilename(m_CurrentPaletteFilePath, row))
|
||||
{
|
||||
palette->m_Name = ui->PaletteListTable->item(row, col)->text().toStdString();
|
||||
emit PaletteFileChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new palette file.
|
||||
/// The newly created file will have a unique name.
|
||||
/// Called when the new palette file button is clicked.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnNewPaletteFileButtonClicked()
|
||||
{
|
||||
auto filename = EmberFile<float>::UniqueFilename(GetDefaultUserPath() + "/user-palettes.xml");
|
||||
|
||||
if (m_PaletteList.AddEmptyPaletteFile(filename.toStdString()))
|
||||
{
|
||||
QFileInfo info(filename);
|
||||
ui->PaletteFilenameCombo->addItem(info.fileName());
|
||||
ui->PaletteFilenameCombo->setCurrentIndex(ui->PaletteFilenameCombo->count() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy the current palette file, add it to the combo box and load the new palette file.
|
||||
/// The newly created file will have a unique name.
|
||||
/// Called when the copy palette file button is clicked.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnCopyPaletteFileButtonClicked()
|
||||
{
|
||||
auto& paletteFiles = m_PaletteList.Palettes();
|
||||
auto qscurr = QString::fromStdString(m_CurrentPaletteFilePath);
|
||||
auto qfilename = EmberFile<float>::UniqueFilename(GetDefaultUserPath() + "/" + qscurr);
|
||||
auto filename = qfilename.toStdString();
|
||||
|
||||
if (m_PaletteList.GetPaletteListByFullPath(filename) == nullptr)//Ensure the new filename does not exist in the map.
|
||||
{
|
||||
//Get the list of palettes for the current filename, this will be added as a copy below.
|
||||
if (auto currentPaletteFile = m_PaletteList.GetPaletteListByFilename(m_CurrentPaletteFilePath))//Ensure the list of palettes was properly retrieved.
|
||||
{
|
||||
if (m_PaletteList.AddPaletteFile(filename, *currentPaletteFile))//Add the current vector of palettes to an entry with the new filename.
|
||||
{
|
||||
QFileInfo info(qfilename);
|
||||
ui->PaletteFilenameCombo->addItem(info.fileName());
|
||||
ui->PaletteFilenameCombo->setCurrentIndex(ui->PaletteFilenameCombo->count() - 1);
|
||||
}
|
||||
else
|
||||
QMessageBox::critical(this, "Copy palette file error", "Failed copy palette to " + qfilename + ", because already exists in memory, but not on disk. Did you delete it while the program was running?");
|
||||
}
|
||||
else
|
||||
QMessageBox::critical(this, "Copy palette file error", "The current file " + qscurr + " did not exist. Did you delete it while the program was running?");
|
||||
}
|
||||
else
|
||||
QMessageBox::critical(this, "Copy palette file error", "Failed copy palette to " + qfilename + ", because it likely already exists");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy the current palette to the end of the palette file.
|
||||
/// Called when the append palette button is clicked.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnAppendPaletteButtonClicked()
|
||||
{
|
||||
auto& pal = m_GradientColorView->GetPalette(256);
|
||||
m_PaletteList.AddPaletteToFile(m_CurrentPaletteFilePath, pal);
|
||||
::FillPaletteTable(m_CurrentPaletteFilePath, ui->PaletteListTable, m_PaletteList);
|
||||
m_PaletteIndex = ui->PaletteListTable->rowCount() - 1;
|
||||
emit PaletteFileChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overwrite the current palette in the palette file.
|
||||
/// Called when the overwrite palette button is clicked.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnOverwritePaletteButtonClicked()
|
||||
{
|
||||
auto& pal = m_GradientColorView->GetPalette(256);
|
||||
m_PaletteList.Replace(m_CurrentPaletteFilePath, pal, m_PaletteIndex);
|
||||
::FillPaletteTable(m_CurrentPaletteFilePath, ui->PaletteListTable, m_PaletteList);
|
||||
emit PaletteFileChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete the current palette from the palette file.
|
||||
/// Called when the delete palette button is clicked.
|
||||
/// Note that the palette will not be deleted if it's the only palette in the file.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnDeletePaletteButtonClicked()
|
||||
{
|
||||
auto table = ui->PaletteListTable;
|
||||
|
||||
if (table->rowCount() > 1)
|
||||
{
|
||||
m_PaletteList.Delete(m_CurrentPaletteFilePath, m_PaletteIndex);
|
||||
::FillPaletteTable(m_CurrentPaletteFilePath, table, m_PaletteList);
|
||||
emit PaletteFileChanged();
|
||||
OnPaletteCellClicked(0, table->rowCount() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit a palette changed event.
|
||||
/// Called when an arrow is moved.
|
||||
/// </summary>
|
||||
void PaletteEditor::OnArrowMoved(qreal, const GradientArrow&)
|
||||
{
|
||||
EmitPaletteChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit a palette changed event if the sync checkbox is checked.
|
||||
/// </summary>
|
||||
void PaletteEditor::EmitPaletteChanged()
|
||||
{
|
||||
if (ui->SyncCheckBox->isChecked())
|
||||
emit PaletteChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to lazily instantiate an open file dialog.
|
||||
/// Once created, it will remain alive for the duration of the program run.
|
||||
/// </summary>
|
||||
QStringList PaletteEditor::SetupOpenImagesDialog()
|
||||
{
|
||||
if (!m_FileDialog)
|
||||
{
|
||||
m_FileDialog = new QFileDialog(this);
|
||||
m_FileDialog->setViewMode(QFileDialog::List);
|
||||
m_FileDialog->setFileMode(QFileDialog::ExistingFile);
|
||||
m_FileDialog->setAcceptMode(QFileDialog::AcceptOpen);
|
||||
m_FileDialog->setNameFilter("Image Files (*.png *.jpg *.bmp)");
|
||||
m_FileDialog->setWindowTitle("Open Image");
|
||||
m_FileDialog->setDirectory(QCoreApplication::applicationDirPath());
|
||||
m_FileDialog->selectNameFilter("*.jpg");
|
||||
}
|
||||
|
||||
QStringList filenames;
|
||||
|
||||
if (m_FileDialog->exec() == QDialog::Accepted)
|
||||
{
|
||||
filenames = m_FileDialog->selectedFiles();
|
||||
|
||||
if (!filenames.empty())
|
||||
m_FileDialog->setDirectory(QFileInfo(filenames[0]).canonicalPath());
|
||||
}
|
||||
|
||||
return filenames;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an arrow whose color will be assigned the passed in color.
|
||||
/// Optionally distribute colors and emit a palette changed event.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to assign to the new arrow</param>
|
||||
void PaletteEditor::AddArrow(const QColor& color)
|
||||
{
|
||||
auto count = std::min<int>(std::abs(256 - m_GradientColorView->ArrowCount()), ui->ArrowsSpinBox->value());
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
m_GradientColorView->AddArrow(color);
|
||||
|
||||
if (ui->AutoDistributeCheckBox->isChecked())
|
||||
m_GradientColorView->DistributeColors();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to get the colors of random pixels within an image.
|
||||
/// </summary>
|
||||
/// <param name="filename">The full path to the image file to get random colors from</param>
|
||||
/// <param name="numPoints">The number of colors to get</param>
|
||||
map<float, GradientArrow> PaletteEditor::GetRandomColorsFromImage(QString filename, int numPoints)
|
||||
{
|
||||
map<float, GradientArrow> colors;
|
||||
QTime time = QTime::currentTime();
|
||||
qsrand((uint)time.msec());
|
||||
QImage image(filename);
|
||||
const qreal gSize = 512;
|
||||
float off = 0.0f, inc = 1.0f / std::max(1, numPoints - 1);
|
||||
QLinearGradient grad(QPoint(0, 0), QPoint(gSize, 1));
|
||||
|
||||
for (int i = 0; i < numPoints; i++)
|
||||
{
|
||||
int x = QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedRand() % image.width();
|
||||
int y = QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedRand() % image.height();
|
||||
QRgb rgb = image.pixel(x, y);
|
||||
GradientArrow arrow;
|
||||
arrow.Color(QColor::fromRgb(rgb));
|
||||
arrow.Focus(i == 0);
|
||||
colors[off] = arrow;
|
||||
off += inc;
|
||||
}
|
||||
|
||||
return colors;
|
||||
}
|
78
Source/Fractorium/PaletteEditor/PaletteEditor.h
Normal file
78
Source/Fractorium/PaletteEditor/PaletteEditor.h
Normal file
@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include "FractoriumPch.h"
|
||||
#include "ColorPickerWidget.h"
|
||||
#include "GradientColorsView.h"
|
||||
#include "EmberFile.h"
|
||||
#include "ui_PaletteEditor.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class PaletteEditor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dialog for editing user created palettes.
|
||||
/// This will load with all available user created palettes populated in the combo
|
||||
/// box. As the user changes the selected index, the palettes for that file
|
||||
/// are shown in the list box. The user can click on those and then edit them and either
|
||||
/// save it back to the same position in the file, or append it to the end.
|
||||
/// They can also click in the name column to set/rename the palette name.
|
||||
/// Any changes on this dialog can be "synced". That is, when the Sync checkbox is checked
|
||||
/// any changes result in a signal being sent back to the main window.
|
||||
/// </summary>
|
||||
class PaletteEditor : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PaletteEditor(PaletteList<float>& paletteList, QWidget* p = nullptr);
|
||||
|
||||
public:
|
||||
bool Sync();
|
||||
QPixmap* GetBackGround();
|
||||
Palette<float>& GetPalette(int size);
|
||||
void SetPalette(Palette<float>& palette);
|
||||
|
||||
signals:
|
||||
void PaletteChanged();
|
||||
void PaletteFileChanged();
|
||||
|
||||
private slots:
|
||||
void OnAddColorButtonClicked();
|
||||
void OnRemoveColorButtonClicked();
|
||||
void OnInvertColorsButtonClicked();
|
||||
void OnRandomColorsButtonClicked();
|
||||
void OnDistributeColorsButtonClicked();
|
||||
void OnResetToDefaultButtonClicked();
|
||||
void OnCreatePaletteFromImageButtonClicked();
|
||||
void OnCreatePaletteAgainFromImageButton();
|
||||
void OnColorPickerColorChanged(const QColor& col);
|
||||
void OnArrowDoubleClicked(const GradientArrow& arrow);
|
||||
void OnSyncCheckBoxStateChanged(int state);
|
||||
void OnArrowMoved(qreal lastPos, const GradientArrow& arrow);
|
||||
void OnPaletteFilenameComboChanged(const QString& text);
|
||||
void OnPaletteCellClicked(int row, int col);
|
||||
void OnPaletteCellChanged(int row, int col);
|
||||
void OnNewPaletteFileButtonClicked();
|
||||
void OnCopyPaletteFileButtonClicked();
|
||||
void OnAppendPaletteButtonClicked();
|
||||
void OnOverwritePaletteButtonClicked();
|
||||
void OnDeletePaletteButtonClicked();
|
||||
|
||||
private:
|
||||
void EmitPaletteChanged();
|
||||
QStringList SetupOpenImagesDialog();
|
||||
void AddArrow(const QColor& color);
|
||||
map<float, GradientArrow> GetRandomColorsFromImage(QString filename, int numPoints);
|
||||
|
||||
bool m_PaletteFileChanged = false;
|
||||
int m_PaletteIndex = 0;
|
||||
QString m_Filename;
|
||||
string m_CurrentPaletteFilePath;
|
||||
ColorPickerWidget* m_ColorPicker = nullptr;
|
||||
GradientColorsView* m_GradientColorView = nullptr;
|
||||
QFileDialog* m_FileDialog = nullptr;
|
||||
PaletteList<float>& m_PaletteList;
|
||||
std::unique_ptr<Ui::PaletteEditor> ui;
|
||||
};
|
Reference in New Issue
Block a user