Skip to content

Commit

Permalink
WIP: swatch textured fill
Browse files Browse the repository at this point in the history
Closes #74.
  • Loading branch information
mitchcurtis committed Sep 28, 2019
1 parent 400b6de commit c435346
Show file tree
Hide file tree
Showing 15 changed files with 320 additions and 133 deletions.
2 changes: 1 addition & 1 deletion app/application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ Application::Application(int &argc, char **argv, const QString &applicationName)
qmlRegisterUncreatableType<Project>("App", 1, 0, "Project", QLatin1String("Cannot create objects of type Project"));
qmlRegisterUncreatableType<LayeredImageProject>("App", 1, 0, "LayeredImageProject",
QLatin1String("Cannot create objects of type LayeredImageProject"));
qmlRegisterUncreatableType<TexturedFillParameter>("App", 1, 0, "TexturedFillParameter",
qmlRegisterUncreatableType<TexturedFillVarianceParameter>("App", 1, 0, "TexturedFillParameter",
QLatin1String("Cannot create objects of type TexturedFillParameter"));
qmlRegisterUncreatableType<TexturedFillParameters>("App", 1, 0, "TexturedFillParameters",
QLatin1String("Cannot create objects of type TexturedFillParameters"));
Expand Down
9 changes: 7 additions & 2 deletions app/qml/main.qml
Original file line number Diff line number Diff line change
Expand Up @@ -136,14 +136,19 @@ ApplicationWindow {
property alias windowVisibility: window.visibility
}

Connections {
target: projectManager
onCreationFailed: errorPopup.showError(errorMessage)
}

Connections {
target: projectManager.project ? projectManager.project : null
onErrorOccurred: errorPopup.showError(errorMessage)
}

Connections {
target: projectManager
onCreationFailed: errorPopup.showError(errorMessage)
target: canvas
onErrorOccurred: errorPopup.showError(errorMessage)
}

Ui.UiStateSerialisation {
Expand Down
7 changes: 7 additions & 0 deletions app/qml/ui/+nativemenubar/MenuBar.qml
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,13 @@ Item {

Platform.MenuSeparator {}

Platform.MenuItem {
objectName: "addSelectedColoursToTexturedFillSwatchMenuItem"
text: qsTr("Add to textured fill swatch")
onTriggered: canvas.addSelectedColoursToTexturedFillSwatch()
enabled: isImageProjectType && canvas && canvas.hasSelection
}

Platform.MenuItem {
objectName: "texturedFillSettingsMenuItem"
//: Opens a dialog that allows the user to change the settings for the Textured Fill tool.
Expand Down
163 changes: 103 additions & 60 deletions app/qml/ui/TexturedFillSettingsDialog.qml
Original file line number Diff line number Diff line change
Expand Up @@ -46,106 +46,149 @@ Dialog {
}

contentItem: GridLayout {
id: mainGridLayout
columns: 2

Layout.topMargin: 4

CheckBox {
id: hueVarianceCheckBox
objectName: "hueVarianceCheckBox"
text: qsTr("Hue variance")
checked: previewItem.parameters.hue.enabled
Label {
text: qsTr("Fill type")
}
ComboBox {
id: fillTypeComboBox
model: [ qsTr("Variance"), qsTr("Swatch") ]
}

Layout.fillWidth: true
GridLayout {
id: varianceGridLayout
columns: 2
visible: fillTypeComboBox.currentIndex === TexturedFillParameters.VarianceFillType

ToolTip.text: qsTr("Enable random variance in hue")
ToolTip.visible: hovered
ToolTip.delay: UiConstants.toolTipDelay
ToolTip.timeout: UiConstants.toolTipTimeout
Layout.columnSpan: 2

onToggled: previewItem.parameters.hue.enabled = checked
}
CheckBox {
id: hueVarianceCheckBox
objectName: "hueVarianceCheckBox"
text: qsTr("Hue variance")
checked: previewItem.parameters.hue.enabled

// https://bugreports.qt.io/browse/QTBUG-67311
TexturedFillVarianceRangedSlider {
id: hueVarianceSlider
objectName: "hueVarianceSlider"
displayName: "hue"
parameter: previewItem.parameters.hue
enabled: hueVarianceCheckBox.checked
Layout.fillWidth: true

Layout.fillWidth: true
}
ToolTip.text: qsTr("Enable random variance in hue")
ToolTip.visible: hovered
ToolTip.delay: UiConstants.toolTipDelay
ToolTip.timeout: UiConstants.toolTipTimeout

CheckBox {
id: saturationVarianceCheckBox
objectName: "saturationVarianceCheckBox"
text: qsTr("Saturation variance")
checked: previewItem.parameters.saturation.enabled
onToggled: previewItem.parameters.hue.enabled = checked
}

Layout.fillWidth: true
// https://bugreports.qt.io/browse/QTBUG-67311
TexturedFillVarianceRangedSlider {
id: hueVarianceSlider
objectName: "hueVarianceSlider"
displayName: "hue"
parameter: previewItem.parameters.hue
enabled: hueVarianceCheckBox.checked

ToolTip.text: qsTr("Enable random variance in saturation")
ToolTip.visible: hovered
ToolTip.delay: UiConstants.toolTipDelay
ToolTip.timeout: UiConstants.toolTipTimeout
Layout.fillWidth: true
}

onToggled: previewItem.parameters.saturation.enabled = checked
}
CheckBox {
id: saturationVarianceCheckBox
objectName: "saturationVarianceCheckBox"
text: qsTr("Saturation variance")
checked: previewItem.parameters.saturation.enabled

TexturedFillVarianceRangedSlider {
id: saturationVarianceSlider
objectName: "saturationVarianceSlider"
displayName: "saturation"
parameter: previewItem.parameters.saturation
enabled: saturationVarianceCheckBox.checked
Layout.fillWidth: true

Layout.fillWidth: true
}
ToolTip.text: qsTr("Enable random variance in saturation")
ToolTip.visible: hovered
ToolTip.delay: UiConstants.toolTipDelay
ToolTip.timeout: UiConstants.toolTipTimeout

CheckBox {
id: lightnessVarianceCheckBox
objectName: "lightnessVarianceCheckBox"
text: qsTr("Lightness variance")
checked: previewItem.parameters.lightness.enabled
onToggled: previewItem.parameters.saturation.enabled = checked
}

Layout.fillWidth: true
TexturedFillVarianceRangedSlider {
id: saturationVarianceSlider
objectName: "saturationVarianceSlider"
displayName: "saturation"
parameter: previewItem.parameters.saturation
enabled: saturationVarianceCheckBox.checked

Layout.fillWidth: true
}

CheckBox {
id: lightnessVarianceCheckBox
objectName: "lightnessVarianceCheckBox"
text: qsTr("Lightness variance")
checked: previewItem.parameters.lightness.enabled

ToolTip.text: qsTr("Enable random variance in lightness")
ToolTip.visible: hovered
ToolTip.delay: UiConstants.toolTipDelay
ToolTip.timeout: UiConstants.toolTipTimeout
Layout.fillWidth: true

onToggled: previewItem.parameters.lightness.enabled = checked
ToolTip.text: qsTr("Enable random variance in lightness")
ToolTip.visible: hovered
ToolTip.delay: UiConstants.toolTipDelay
ToolTip.timeout: UiConstants.toolTipTimeout

onToggled: previewItem.parameters.lightness.enabled = checked
}

TexturedFillVarianceRangedSlider {
id: lightnessVarianceSlider
objectName: "lightnessVarianceSlider"
displayName: "lightness"
parameter: previewItem.parameters.lightness
enabled: lightnessVarianceCheckBox.checked

Layout.fillWidth: true
}
}

TexturedFillVarianceRangedSlider {
id: lightnessVarianceSlider
objectName: "lightnessVarianceSlider"
displayName: "lightness"
parameter: previewItem.parameters.lightness
enabled: lightnessVarianceCheckBox.checked
SwatchGridView {
id: swatchGridView
objectName: "texturedFillSwatchGridView"
readOnly: false
// swatchContextMenu: contextMenu
visible: fillTypeComboBox.currentIndex === TexturedFillParameters.SwatchFillType

Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredHeight: varianceGridLayout.height
Layout.columnSpan: 2

ScrollBar.vertical: ScrollBar {
parent: mainGridLayout

Layout.fillHeight: true
}

model: SwatchModel {
project: dialog.project
}
}

Label {
text: qsTr("Preview scale")
}

Layout.fillWidth: true
}
Slider {
id: previewScaleSlider
objectName: "previewScaleSlider"
from: 1
to: 30

Layout.fillWidth: true
Layout.maximumWidth: lightnessVarianceSlider.width

ToolTip {
parent: previewScaleSlider.handle
text: previewScaleSlider.value.toFixed(1)
visible: previewScaleSlider.pressed
}
}

Rectangle {
id: texturedFillPreviewBackground
objectName: "texturedFillPreviewBackground"
Expand Down
37 changes: 33 additions & 4 deletions lib/fillalgorithms.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <QRandomGenerator>
#include <QQueue>

#include "swatchcolour.h"
#include "texturedfillparameters.h"
#include "tile.h"
#include "tilesetproject.h"
Expand Down Expand Up @@ -172,10 +173,10 @@ qreal toRange(qreal randomNumber, qreal min, qreal max)
return randomNumber * (max - min) + min;
}

class TextureFillColourProvider : public FillColourProvider
class VarianceTextureFillColourProvider : public FillColourProvider
{
public:
TextureFillColourProvider(const TexturedFillParameters &parameters) :
VarianceTextureFillColourProvider(const TexturedFillParameters &parameters) :
mParameters(parameters)
{
}
Expand Down Expand Up @@ -214,16 +215,44 @@ class TextureFillColourProvider : public FillColourProvider
const TexturedFillParameters &mParameters;
};

class SwatchTextureFillColourProvider : public FillColourProvider
{
public:
SwatchTextureFillColourProvider(const TexturedFillParameters &parameters) :
mParameters(parameters)
{
}

QColor colour(const QColor &) const override
{
const auto randomGen = QRandomGenerator::global();
const QVector<SwatchColour> swatchColours = mParameters.swatch()->colours();
const int swatchColourIndex = randomGen->bounded(0, swatchColours.size());
const QColor newColour = swatchColours.at(swatchColourIndex).colour();
return newColour;
}

const TexturedFillParameters &mParameters;
};

QImage texturedFill(const QImage *image, const QPoint &startPos, const QColor &targetColour,
const QColor &replacementColour, const TexturedFillParameters &parameters)
{
return imagePixelFloodFill(image, startPos, targetColour, replacementColour, TextureFillColourProvider(parameters));
if (parameters.type() == TexturedFillParameters::VarianceFillType)
return imagePixelFloodFill(image, startPos, targetColour, replacementColour, VarianceTextureFillColourProvider(parameters));

// Swatch.
return imagePixelFloodFill(image, startPos, targetColour, replacementColour, SwatchTextureFillColourProvider(parameters));
}

QImage greedyTexturedFill(const QImage *image, const QPoint &startPos, const QColor &targetColour,
const QColor &replacementColour, const TexturedFillParameters &parameters)
{
return imageGreedyPixelFill(image, startPos, targetColour, replacementColour, TextureFillColourProvider(parameters));
if (parameters.type() == TexturedFillParameters::VarianceFillType)
return imageGreedyPixelFill(image, startPos, targetColour, replacementColour, VarianceTextureFillColourProvider(parameters));

// Swatch.
return imageGreedyPixelFill(image, startPos, targetColour, replacementColour, SwatchTextureFillColourProvider(parameters));
}

// TODO: convert these to non-recursive algorithms as above
Expand Down
25 changes: 25 additions & 0 deletions lib/imagecanvas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1920,6 +1920,31 @@ void ImageCanvas::endModifyingSelectionHsl(AdjustmentAction adjustmentAction)
emit adjustingImageChanged();
}

void ImageCanvas::addSelectedColoursToTexturedFillSwatch()
{
if (!mHasSelection)
return;

// Since this operation is blocking, we have to make it quite limited in the size of selections it allows.
static const int maxSelectionSize = 256;
if (mSelectionArea.width() * mSelectionArea.height() > maxSelectionSize * maxSelectionSize) {
error(tr("Too many pixels selected: %1x%2 exceeds limit of %3/%4.")
.arg(mSelectionArea.width()).arg(mSelectionArea.height()).arg(maxSelectionSize).arg(maxSelectionSize));
return;
}

QVector<QColor> uniqueColours;
static const int maxUniqueColours = 100;
const Utils::FindUniqueColoursResult result = Utils::findUniqueColours(mSelectionContents, maxUniqueColours, uniqueColours);
if (result == Utils::MaximumUniqueColoursExceeded) {
emit errorOccurred(tr("Too many unique colours selected: the maximum is %1 colours.")
.arg(maxUniqueColours));
return;
}

mTexturedFillParameters.swatch()->addColours(uniqueColours);
}

void ImageCanvas::copySelection()
{
if (!mHasSelection)
Expand Down
1 change: 1 addition & 0 deletions lib/imagecanvas.h
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ public slots:
void modifySelectionHsl(qreal hue, qreal saturation, qreal lightness, qreal alpha = 0.0,
AlphaAdjustmentFlags alphaAdjustmentFlags = DefaultAlphaAdjustment);
void endModifyingSelectionHsl(AdjustmentAction adjustmentAction);
void addSelectedColoursToTexturedFillSwatch();
void copySelection();
void paste();
void deleteSelectionOrContents();
Expand Down
17 changes: 17 additions & 0 deletions lib/swatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ void Swatch::addColour(const QString &name, const QColor &colour)
emit postColourAdded();
}

void Swatch::addColours(const QVector<QColor> &colours)
{
emit preColoursAdded();

for (const auto colour : colours) {
mColours.append(SwatchColour(QString(), colour));
}

emit postColoursAdded();
}

void Swatch::renameColour(int index, const QString &newName)
{
if (!isValidIndex(index))
Expand Down Expand Up @@ -103,6 +114,12 @@ void Swatch::write(QJsonObject &json) const
json["colours"] = colourArray;
}

void Swatch::reset()
{
// Should be fine to have no signals for this for now...
mColours.clear();
}

void Swatch::copy(const Swatch &other)
{
emit preImported();
Expand Down
Loading

0 comments on commit c435346

Please sign in to comment.