All Classes Namespaces Functions Variables Enumerations Properties Pages
buckettool.cpp
1 /*
2 
3 Pencil2D - Traditional Animation Software
4 Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5 Copyright (C) 2012-2020 Matthew Chiawen Chang
6 
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; version 2 of the License.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15 
16 */
17 #include "buckettool.h"
18 
19 #include <QPixmap>
20 #include <QPainter>
21 #include <QPointer>
22 #include <QtMath>
23 #include <QSettings>
24 #include "pointerevent.h"
25 
26 #include "layer.h"
27 #include "layervector.h"
28 #include "layerbitmap.h"
29 #include "layermanager.h"
30 #include "colormanager.h"
31 #include "strokemanager.h"
32 #include "viewmanager.h"
33 #include "vectorimage.h"
34 #include "editor.h"
35 #include "scribblearea.h"
36 
37 
38 BucketTool::BucketTool(QObject* parent) : StrokeTool(parent)
39 {
40 }
41 
42 ToolType BucketTool::type()
43 {
44  return BUCKET;
45 }
46 
47 void BucketTool::loadSettings()
48 {
49  mPropertyEnabled[TOLERANCE] = true;
50  mPropertyEnabled[WIDTH] = true;
51  mPropertyEnabled[FILL_MODE] = true;
52 
53  QSettings settings(PENCIL2D, PENCIL2D);
54 
55  properties.width = settings.value("fillThickness", 4.0).toDouble();
56  properties.feather = 10;
57  properties.stabilizerLevel = StabilizationLevel::NONE;
58  properties.useAA = DISABLED;
59  properties.tolerance = settings.value(SETTING_BUCKET_TOLERANCE, 32.0).toDouble();
60  properties.toleranceEnabled = settings.value(SETTING_BUCKET_TOLERANCE_ON, false).toBool();
61 
62  properties.bucketFillExpand = settings.value(SETTING_BUCKET_FILL_EXPAND, 2.0).toInt();
63  properties.bucketFillExpandEnabled = settings.value(SETTING_BUCKET_FILL_EXPAND_ON, true).toBool();
64  properties.bucketFillToLayerMode = settings.value(SETTING_BUCKET_FILL_TO_LAYER_MODE, 0).toInt();
65  properties.bucketFillReferenceMode = settings.value(SETTING_BUCKET_FILL_REFERENCE_MODE, 0).toInt();
66  properties.fillMode = settings.value(SETTING_FILL_MODE, 0).toInt();
67 }
68 
69 void BucketTool::resetToDefault()
70 {
71  setWidth(4.0);
72  setTolerance(32.0);
73  setFillMode(0);
74  setFillExpand(2);
75  setFillExpandEnabled(true);
76  setFillToLayer(0);
77  setToleranceEnabled(false);
78  setFillReferenceMode(0);
79 }
80 
81 QCursor BucketTool::cursor()
82 {
83  if (mEditor->preference()->isOn(SETTING::TOOL_CURSOR))
84  {
85  QPixmap pixmap(":icons/bucketTool.png");
86  QPainter painter(&pixmap);
87  painter.end();
88 
89  return QCursor(pixmap, 4, 20);
90  }
91  else
92  {
93  return QCursor(QPixmap(":icons/cross.png"), 10, 10);
94  }
95 }
96 
97 void BucketTool::setTolerance(const int tolerance)
98 {
99  // Set current property
100  properties.tolerance = tolerance;
101 
102  // Update settings
103  QSettings settings(PENCIL2D, PENCIL2D);
104  settings.setValue(SETTING_BUCKET_TOLERANCE, tolerance);
105  settings.sync();
106 }
107 
113 void BucketTool::setWidth(const qreal width)
114 {
115  // Set current property
116  properties.width = width;
117 
118  // Update settings
119  QSettings settings(PENCIL2D, PENCIL2D);
120  settings.setValue("fillThickness", width);
121  settings.sync();
122 }
123 
124 void BucketTool::setFillMode(int mode)
125 {
126  // Set current property
127  properties.fillMode = mode;
128 
129  // Update settings
130  QSettings settings(PENCIL2D, PENCIL2D);
131  settings.setValue(SETTING_FILL_MODE, mode);
132  settings.sync();
133 }
134 
135 void BucketTool::setToleranceEnabled(const bool enabled)
136 {
137  properties.toleranceEnabled = enabled;
138 
139  // Update settings
140  QSettings settings(PENCIL2D, PENCIL2D);
141  settings.setValue(SETTING_BUCKET_TOLERANCE_ON, enabled);
142  settings.sync();
143 }
144 
145 void BucketTool::setFillExpandEnabled(const bool enabled)
146 {
147  properties.bucketFillExpandEnabled = enabled;
148 
149  // Update settings
150  QSettings settings(PENCIL2D, PENCIL2D);
151  settings.setValue(SETTING_BUCKET_FILL_EXPAND_ON, enabled);
152  settings.sync();
153 }
154 
155 void BucketTool::setFillExpand(const int fillExpandValue)
156 {
157  properties.bucketFillExpand = fillExpandValue;
158 
159  // Update settings
160  QSettings settings(PENCIL2D, PENCIL2D);
161  settings.setValue(SETTING_BUCKET_FILL_EXPAND, fillExpandValue);
162  settings.sync();
163 }
164 
165 void BucketTool::setFillToLayer(int layerMode)
166 {
167  properties.bucketFillToLayerMode = layerMode;
168 
169  // Update settings
170  QSettings settings(PENCIL2D, PENCIL2D);
171  settings.setValue(SETTING_BUCKET_FILL_TO_LAYER_MODE, layerMode);
172  settings.sync();
173 }
174 
175 void BucketTool::setFillReferenceMode(int referenceMode)
176 {
177  properties.bucketFillReferenceMode = referenceMode;
178 
179  // Update settings
180  QSettings settings(PENCIL2D, PENCIL2D);
181  settings.setValue(SETTING_BUCKET_FILL_REFERENCE_MODE, referenceMode);
182  settings.sync();
183 }
184 
185 void BucketTool::pointerPressEvent(PointerEvent* event)
186 {
187  startStroke(event->inputType());
188 
189  Layer* targetLayer = mEditor->layers()->currentLayer();
190 
191  if (targetLayer->type() != Layer::BITMAP) { return; }
192 
193  mBitmapBucket = BitmapBucket(mEditor,
194  mEditor->color()->frontColor(),
195  mScribbleArea->getCameraRect(),
196  getCurrentPoint(),
197  properties);
198 
199  // Because we can change layer to on the fly but we do not act reactively on it
200  // it's neccesary to invalidate layer cache on press event, otherwise the cache
201  // will be drawn until a move event has been initiated.
202  mScribbleArea->invalidateLayerPixmapCache();
203 }
204 
205 void BucketTool::pointerMoveEvent(PointerEvent* event)
206 {
207  if (event->buttons() & Qt::LeftButton && event->inputType() == mCurrentInputType)
208  {
209  Layer* layer = mEditor->layers()->currentLayer();
210  if (layer->type() == Layer::VECTOR)
211  {
212  drawStroke();
213  }
214  else if (layer->type() == Layer::BITMAP)
215  {
216  paintBitmap();
217  mFilledOnMove = true;
218  }
219  }
220 }
221 
222 void BucketTool::pointerReleaseEvent(PointerEvent* event)
223 {
224  if (event->inputType() != mCurrentInputType) return;
225 
226  Layer* layer = editor()->layers()->currentLayer();
227  if (layer == nullptr) { return; }
228 
229  if (event->button() == Qt::LeftButton)
230  {
231  // Backup of bitmap image is more complicated now and has therefore been moved to bitmap code
232  if (layer->type() == Layer::VECTOR) {
233  mEditor->backup(typeName());
234  paintVector(layer);
235  }
236  else if (layer->type() == Layer::BITMAP && !mFilledOnMove)
237  {
238  paintBitmap();
239  }
240  }
241  mFilledOnMove = false;
242 
243  endStroke();
244 }
245 
246 bool BucketTool::startAdjusting(Qt::KeyboardModifiers modifiers, qreal argStep)
247 {
248  mQuickSizingProperties.clear();
249  if (mEditor->layers()->currentLayer()->type() == Layer::VECTOR)
250  {
251  mQuickSizingProperties.insert(Qt::ShiftModifier, WIDTH);
252  }
253  else
254  {
255  mQuickSizingProperties.insert(Qt::ControlModifier, TOLERANCE);
256  }
257  return BaseTool::startAdjusting(modifiers, argStep);
258 }
259 
260 void BucketTool::paintBitmap()
261 {
262  mBitmapBucket.paint(getCurrentPoint(), [this](BucketState progress, int layerIndex, int frameIndex)
263  {
264  if (progress == BucketState::WillFillTarget)
265  {
266  mEditor->backup(layerIndex, frameIndex, typeName());
267  }
268  else if (progress == BucketState::DidFillTarget)
269  {
270  mScribbleArea->setModified(layerIndex, frameIndex);
271 
272  // Need to invalidate layer pixmap cache when filling anything else but current layer
273  // otherwise dragging won't show until release event
274  if (properties.bucketFillToLayerMode == 1)
275  {
276  mScribbleArea->invalidateLayerPixmapCache();
277  }
278  }
279  });
280 }
281 
282 void BucketTool::paintVector(Layer* layer)
283 {
284  mScribbleArea->clearBitmapBuffer();
285 
286  VectorImage* vectorImage = static_cast<LayerVector*>(layer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
287  if (vectorImage == nullptr) { return; } // Can happen if the first frame is deleted while drawing
288 
289  if (!vectorImage->isPathFilled())
290  {
291  vectorImage->fillSelectedPath(mEditor->color()->frontColorNumber());
292  }
293 
294  vectorImage->applyWidthToSelection(properties.width);
295  vectorImage->applyColorToSelectedCurve(mEditor->color()->frontColorNumber());
296  vectorImage->applyColorToSelectedArea(mEditor->color()->frontColorNumber());
297 
298  applyChanges();
299 
300  mScribbleArea->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame());
301 }
302 
303 void BucketTool::applyChanges()
304 {
305  mScribbleArea->applyTransformedSelection();
306 }
307 
308 void BucketTool::drawStroke()
309 {
310  StrokeTool::drawStroke();
311 
312  if (properties.stabilizerLevel != strokeManager()->getStabilizerLevel())
313  {
314  strokeManager()->setStabilizerLevel(properties.stabilizerLevel);
315  }
316 
317  QList<QPointF> p = strokeManager()->interpolateStroke();
318 
319  Layer* layer = mEditor->layers()->currentLayer();
320 
321  if (layer->type() == Layer::VECTOR)
322  {
323  mCurrentWidth = 30;
324  int rad = qRound((mCurrentWidth / 2 + 2) * mEditor->view()->scaling());
325 
326  QColor pathColor = qPremultiply(mEditor->color()->frontColor().rgba());
327 
328  QPen pen(pathColor,
329  mCurrentWidth * mEditor->view()->scaling(),
330  Qt::NoPen,
331  Qt::RoundCap,
332  Qt::RoundJoin);
333 
334  if (p.size() == 4)
335  {
336  QPainterPath path(p[0]);
337  path.cubicTo(p[1], p[2], p[3]);
338  mScribbleArea->drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_Source);
339  mScribbleArea->refreshVector(path.boundingRect().toRect(), rad);
340  }
341  }
342 }
void setWidth(const qreal width) override
BrushTool::setWidth.
Definition: buckettool.cpp:113
typedef KeyboardModifiers
QHash::iterator insert(const Key &key, const T &value)
void sync()
void applyColorToSelectedArea(int colorNumber)
VectorImage::applyColorToSelectedArea.
bool isPathFilled()
VectorImage::isPathFilled.
void fillSelectedPath(int color)
VectorImage::fillSelectedPath.
LeftButton
void setModified(int layerNumber, int frameNumber)
Set frame on layer to modified and invalidate current frame cache.
RoundCap
int size() const const
void setValue(const QString &key, const QVariant &value)
Definition: layer.h:38
void applyWidthToSelection(qreal width)
VectorImage::applyWidthToSelection.
Qt::MouseButtons buttons() const
Returns Qt::MouseButtons()
void clear()
CompositionMode_Source
RoundJoin
QColor frontColor(bool useIndexedColor=true)
frontColor
void applyColorToSelectedCurve(int colorNumber)
VectorImage::applyColorToSelectedCurve.
void paint(const QPointF updatedPoint, std::function< void(BucketState, int, int)> progress)
Will paint at the given point, given that it makes sense.
void invalidateLayerPixmapCache()
Invalidate the layer pixmap cache.
Qt::MouseButton button() const
Returns Qt::MouseButton()
QRgb rgba() const const