Pencil2D Animation
Download Community News Docs Contribute

core_lib/src/interface/scribblearea.cpp Source File

  • Main Page
  • Related Pages
  • Classes
  • Files
  •  
  • File List
Loading...
Searching...
No Matches
  • core_lib
  • src
  • interface
scribblearea.cpp
1/*
2
3Pencil2D - Traditional Animation Software
4Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5Copyright (C) 2012-2020 Matthew Chiawen Chang
6
7This program is free software; you can redistribute it and/or
8modify it under the terms of the GNU General Public License
9as published by the Free Software Foundation; version 2 of the License.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16*/
17
18#include "scribblearea.h"
19
20#include <cmath>
21#include <QGuiApplication>
22#include <QMessageBox>
23#include <QPixmapCache>
24
25#include "pointerevent.h"
26#include "beziercurve.h"
27#include "object.h"
28#include "editor.h"
29#include "layerbitmap.h"
30#include "layervector.h"
31#include "layercamera.h"
32#include "bitmapimage.h"
33#include "vectorimage.h"
34#include "blitrect.h"
35
36#include "onionskinpainteroptions.h"
37
38#include "colormanager.h"
39#include "toolmanager.h"
40#include "strokemanager.h"
41#include "layermanager.h"
42#include "playbackmanager.h"
43#include "viewmanager.h"
44#include "selectionmanager.h"
45#include "overlaymanager.h"
46
47ScribbleArea::ScribbleArea(QWidget* parent) : QWidget(parent), mCanvasPainter(mCanvas)
48{
49 setObjectName("ScribbleArea");
50
51 // Qt::WA_StaticContents ensure that the widget contents are rooted to the top-left corner
52 // and don't change when the widget is resized.
53 setAttribute(Qt::WA_StaticContents);
54
55 mStrokeManager.reset(new StrokeManager);
56}
57
58ScribbleArea::~ScribbleArea()
59{
60}
61
62bool ScribbleArea::init()
63{
64 mPrefs = mEditor->preference();
65 mDoubleClickTimer = new QTimer(this);
66 mMouseFilterTimer = new QTimer(this);
67
68 connect(mPrefs, &PreferenceManager::optionChanged, this, &ScribbleArea::settingUpdated);
69 connect(mEditor->tools(), &ToolManager::toolPropertyChanged, this, &ScribbleArea::onToolPropertyUpdated);
70 connect(mEditor->tools(), &ToolManager::toolChanged, this, &ScribbleArea::onToolChanged);
71
72 connect(mDoubleClickTimer, &QTimer::timeout, this, &ScribbleArea::handleDoubleClick);
73 connect(mMouseFilterTimer, &QTimer::timeout, this, &ScribbleArea::tabletReleaseEventFired);
74
75 connect(mEditor->select(), &SelectionManager::selectionChanged, this, &ScribbleArea::onSelectionChanged);
76 connect(mEditor->select(), &SelectionManager::needDeleteSelection, this, &ScribbleArea::deleteSelection);
77
78 mDoubleClickTimer->setInterval(50);
79 mMouseFilterTimer->setInterval(50);
80
81 const int curveSmoothingLevel = mPrefs->getInt(SETTING::CURVE_SMOOTHING);
82 mCurveSmoothingLevel = curveSmoothingLevel / 20.0; // default value is 1.0
83
84 mQuickSizing = mPrefs->isOn(SETTING::QUICK_SIZING);
85 mMakeInvisible = false;
86
87 mIsSimplified = mPrefs->isOn(SETTING::OUTLINES);
88 mMultiLayerOnionSkin = mPrefs->isOn(SETTING::MULTILAYER_ONION);
89
90 mLayerVisibility = static_cast<LayerVisibility>(mPrefs->getInt(SETTING::LAYER_VISIBILITY));
91
92 updateCanvasCursor();
93
94 setMouseTracking(true); // reacts to mouse move events, even if the button is not pressed
95#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
96 setTabletTracking(true); // tablet tracking first added in 5.9
97#endif
98
99 setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding));
100
101 QPixmapCache::setCacheLimit(100 * 1024); // unit is kb, so it's 100MB cache
102 mPixmapCacheKeys.clear();
103
104 return true;
105}
106
107void ScribbleArea::settingUpdated(SETTING setting)
108{
109 switch (setting)
110 {
111 case SETTING::CURVE_SMOOTHING:
112 setCurveSmoothing(mPrefs->getInt(SETTING::CURVE_SMOOTHING));
113 break;
114 case SETTING::TOOL_CURSOR:
115 updateToolCursor();
116 break;
117 case SETTING::ONION_PREV_FRAMES_NUM:
118 case SETTING::ONION_NEXT_FRAMES_NUM:
119 case SETTING::ONION_MIN_OPACITY:
120 case SETTING::ONION_MAX_OPACITY:
121 invalidateAllCache();
122 break;
123 case SETTING::ANTIALIAS:
124 case SETTING::GRID:
125 case SETTING::GRID_SIZE_W:
126 case SETTING::GRID_SIZE_H:
127 case SETTING::OVERLAY_CENTER:
128 case SETTING::OVERLAY_THIRDS:
129 case SETTING::OVERLAY_GOLDEN:
130 case SETTING::OVERLAY_SAFE:
131 case SETTING::OVERLAY_PERSPECTIVE1:
132 case SETTING::OVERLAY_PERSPECTIVE2:
133 case SETTING::OVERLAY_PERSPECTIVE3:
134 case SETTING::ACTION_SAFE_ON:
135 case SETTING::ACTION_SAFE:
136 case SETTING::TITLE_SAFE_ON:
137 case SETTING::TITLE_SAFE:
138 case SETTING::OVERLAY_SAFE_HELPER_TEXT_ON:
139 case SETTING::PREV_ONION:
140 case SETTING::NEXT_ONION:
141 case SETTING::ONION_BLUE:
142 case SETTING::ONION_RED:
143 case SETTING::INVISIBLE_LINES:
144 case SETTING::OUTLINES:
145 case SETTING::ONION_TYPE:
146 case SETTING::ONION_WHILE_PLAYBACK:
147 invalidateAllCache();
148 break;
149 case SETTING::QUICK_SIZING:
150 mQuickSizing = mPrefs->isOn(SETTING::QUICK_SIZING);
151 break;
152 case SETTING::MULTILAYER_ONION:
153 mMultiLayerOnionSkin = mPrefs->isOn(SETTING::MULTILAYER_ONION);
154 invalidateAllCache();
155 break;
156 case SETTING::LAYER_VISIBILITY_THRESHOLD:
157 case SETTING::LAYER_VISIBILITY:
158 setLayerVisibility(static_cast<LayerVisibility>(mPrefs->getInt(SETTING::LAYER_VISIBILITY)));
159 break;
160 default:
161 break;
162 }
163
164}
165
166void ScribbleArea::updateToolCursor()
167{
168 setCursor(currentTool()->cursor());
169 updateCanvasCursor();
170}
171
172void ScribbleArea::setCurveSmoothing(int newSmoothingLevel)
173{
174 mCurveSmoothingLevel = newSmoothingLevel / 20.0;
175 invalidatePainterCaches();
176}
177
178void ScribbleArea::setEffect(SETTING e, bool isOn)
179{
180 mPrefs->set(e, isOn);
181 invalidatePainterCaches();
182}
183
184/************************************************************************************/
185// update methods
186
187void ScribbleArea::updateCurrentFrame()
188{
189 updateFrame(mEditor->currentFrame());
190}
191
192void ScribbleArea::updateFrame(int frame)
193{
194 Q_ASSERT(frame >= 0);
195 update();
196}
197
198void ScribbleArea::invalidateCacheForDirtyFrames()
199{
200 Layer* currentLayer = mEditor->layers()->currentLayer();
201 for (int pos : currentLayer->dirtyFrames()) {
202
203 invalidateCacheForFrame(pos);
204 invalidateOnionSkinsCacheAround(pos);
205 }
206 currentLayer->clearDirtyFrames();
207}
208
209void ScribbleArea::invalidateOnionSkinsCacheAround(int frameNumber)
210{
211 if (frameNumber < 0) { return; }
212
213 bool isOnionAbsolute = mPrefs->getString(SETTING::ONION_TYPE) == "absolute";
214 Layer *layer = mEditor->layers()->currentLayer(0);
215
216 // The current layer can be null if updateFrame is triggered when creating a new project
217 if (!layer) return;
218
219 if (mPrefs->isOn(SETTING::PREV_ONION))
220 {
221 int onionFrameNumber = frameNumber;
222 if (isOnionAbsolute)
223 {
224 onionFrameNumber = layer->getPreviousFrameNumber(onionFrameNumber + 1, true);
225 }
226
227 for(int i = 1; i <= mPrefs->getInt(SETTING::ONION_PREV_FRAMES_NUM); i++)
228 {
229 onionFrameNumber = layer->getPreviousFrameNumber(onionFrameNumber, isOnionAbsolute);
230 if (onionFrameNumber < 0) break;
231
232 invalidateCacheForFrame(onionFrameNumber);
233 }
234 }
235
236 if (mPrefs->isOn(SETTING::NEXT_ONION))
237 {
238 int onionFrameNumber = frameNumber;
239
240 for(int i = 1; i <= mPrefs->getInt(SETTING::ONION_NEXT_FRAMES_NUM); i++)
241 {
242 onionFrameNumber = layer->getNextFrameNumber(onionFrameNumber, isOnionAbsolute);
243 if (onionFrameNumber < 0) break;
244
245 invalidateCacheForFrame(onionFrameNumber);
246 }
247 }
248}
249
250void ScribbleArea::invalidateAllCache()
251{
252 QPixmapCache::clear();
253 mPixmapCacheKeys.clear();
254 invalidatePainterCaches();
255 mEditor->layers()->currentLayer()->clearDirtyFrames();
256
257 update();
258}
259
260void ScribbleArea::invalidateCacheForFrame(int frameNumber)
261{
262 auto cacheKeyIter = mPixmapCacheKeys.find(static_cast<unsigned int>(frameNumber));
263 if (cacheKeyIter != mPixmapCacheKeys.end())
264 {
265 QPixmapCache::remove(cacheKeyIter.value());
266 unsigned int key = cacheKeyIter.key();
267 mPixmapCacheKeys.remove(key);
268 }
269}
270
271void ScribbleArea::invalidatePainterCaches()
272{
273 mCameraPainter.resetCache();
274 mCanvasPainter.resetLayerCache();
275 update();
276}
277
278void ScribbleArea::onToolPropertyUpdated(ToolType, ToolPropertyType type)
279{
280 switch (type)
281 {
282 case ToolPropertyType::CAMERAPATH:
283 onFrameModified(mEditor->currentFrame());
284 break;
285 default:
286 break;
287 }
288}
289
290void ScribbleArea::onToolChanged(ToolType)
291{
292 int frame = mEditor->currentFrame();
293 prepOverlays(frame);
294 prepCameraPainter(frame);
295 invalidateCacheForFrame(frame);
296 updateCurrentFrame();
297}
298
299
300void ScribbleArea::onPlayStateChanged()
301{
302 int currentFrame = mEditor->currentFrame();
303 if (mPrefs->isOn(SETTING::PREV_ONION) ||
304 mPrefs->isOn(SETTING::NEXT_ONION)) {
305 invalidatePainterCaches();
306 }
307
308 prepOverlays(currentFrame);
309 prepCameraPainter(currentFrame);
310 invalidateCacheForFrame(currentFrame);
311 updateFrame(currentFrame);
312}
313
314void ScribbleArea::onScrubbed(int frameNumber)
315{
316 invalidatePainterCaches();
317 updateFrame(frameNumber);
318}
319
320void ScribbleArea::onFramesModified()
321{
322 invalidateCacheForDirtyFrames();
323 if (mPrefs->isOn(SETTING::PREV_ONION) || mPrefs->isOn(SETTING::NEXT_ONION)) {
324 invalidatePainterCaches();
325 }
326 update();
327}
328
329void ScribbleArea::onFrameModified(int frameNumber)
330{
331 if (mPrefs->isOn(SETTING::PREV_ONION) || mPrefs->isOn(SETTING::NEXT_ONION)) {
332 invalidateOnionSkinsCacheAround(frameNumber);
333 invalidatePainterCaches();
334 }
335 invalidateCacheForFrame(frameNumber);
336 updateFrame(frameNumber);
337}
338
339void ScribbleArea::onViewChanged()
340{
341 invalidateAllCache();
342}
343
344void ScribbleArea::onLayerChanged()
345{
346 invalidateAllCache();
347}
348
349void ScribbleArea::onSelectionChanged()
350{
351 int currentFrame = mEditor->currentFrame();
352 invalidateCacheForFrame(currentFrame);
353 updateFrame(currentFrame);
354}
355
356void ScribbleArea::onOnionSkinTypeChanged()
357{
358 invalidateAllCache();
359}
360
361void ScribbleArea::onObjectLoaded()
362{
363 invalidateAllCache();
364}
365
366bool ScribbleArea::event(QEvent *event)
367{
368 if (event->type() == QEvent::WindowDeactivate)
369 {
370 editor()->tools()->clearTemporaryTool();
371 }
372 return QWidget::event(event);
373}
374
375/************************************************************************/
376/* key event handlers */
377/************************************************************************/
378
379void ScribbleArea::keyPressEvent(QKeyEvent *event)
380{
381 // Don't handle this event on auto repeat
382 if (event->isAutoRepeat()) { return; }
383
384 mKeyboardInUse = true;
385
386 if (isPointerInUse()) { return; } // prevents shortcuts calls while drawing
387
388 if (currentTool()->keyPressEvent(event))
389 {
390 return; // has been handled by tool
391 }
392
393 // --- fixed control key shortcuts ---
394 if (event->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier) &&
395 editor()->tools()->setTemporaryTool(ERASER, {}, event->modifiers()))
396 {
397 return;
398 }
399
400 // ---- fixed normal keys ----
401
402 auto selectMan = mEditor->select();
403 bool isSomethingSelected = selectMan->somethingSelected();
404 if (isSomethingSelected)
405 {
406 keyEventForSelection(event);
407 }
408 else
409 {
410 keyEvent(event);
411 }
412}
413
414void ScribbleArea::keyEventForSelection(QKeyEvent* event)
415{
416 auto selectMan = mEditor->select();
417 switch (event->key())
418 {
419 case Qt::Key_Right:
420 selectMan->translate(QPointF(1, 0));
421 selectMan->calculateSelectionTransformation();
422 mEditor->frameModified(mEditor->currentFrame());
423 return;
424 case Qt::Key_Left:
425 selectMan->translate(QPointF(-1, 0));
426 selectMan->calculateSelectionTransformation();
427 mEditor->frameModified(mEditor->currentFrame());
428 return;
429 case Qt::Key_Up:
430 selectMan->translate(QPointF(0, -1));
431 selectMan->calculateSelectionTransformation();
432 mEditor->frameModified(mEditor->currentFrame());
433 return;
434 case Qt::Key_Down:
435 selectMan->translate(QPointF(0, 1));
436 selectMan->calculateSelectionTransformation();
437 mEditor->frameModified(mEditor->currentFrame());
438 return;
439 case Qt::Key_Return:
440 applyTransformedSelection();
441 mEditor->deselectAll();
442 return;
443 case Qt::Key_Escape:
444 cancelTransformedSelection();
445 mEditor->deselectAll();
446 return;
447 case Qt::Key_Backspace:
448 deleteSelection();
449 mEditor->deselectAll();
450 return;
451 case Qt::Key_Space:
452 if (editor()->tools()->setTemporaryTool(HAND, Qt::Key_Space, Qt::NoModifier))
453 {
454 return;
455 }
456 break;
457 default:
458 break;
459 }
460 event->ignore();
461}
462
463void ScribbleArea::keyEvent(QKeyEvent* event)
464{
465 switch (event->key())
466 {
467 case Qt::Key_Right:
468 mEditor->scrubForward();
469 break;
470 case Qt::Key_Left:
471 mEditor->scrubBackward();
472 break;
473 case Qt::Key_Up:
474 mEditor->layers()->gotoNextLayer();
475 break;
476 case Qt::Key_Down:
477 mEditor->layers()->gotoPreviouslayer();
478 break;
479 case Qt::Key_Space:
480 if(editor()->tools()->setTemporaryTool(HAND, Qt::Key_Space, Qt::NoModifier))
481 {
482 return;
483 }
484 break;
485 default:
486 break;
487 }
488 event->ignore();
489}
490
491void ScribbleArea::keyReleaseEvent(QKeyEvent *event)
492{
493 // Don't handle this event on auto repeat
494 //
495 if (event->isAutoRepeat()) {
496 return;
497 }
498
499 mKeyboardInUse = false;
500
501 if (event->key() == 0)
502 {
503 editor()->tools()->tryClearTemporaryTool(Qt::Key_unknown);
504 }
505 else
506 {
507 editor()->tools()->tryClearTemporaryTool(static_cast<Qt::Key>(event->key()));
508 }
509
510 if (isPointerInUse()) { return; }
511
512 if (currentTool()->keyReleaseEvent(event))
513 {
514 // has been handled by tool
515 return;
516 }
517}
518
519/************************************************************************************/
520// mouse and tablet event handlers
521void ScribbleArea::wheelEvent(QWheelEvent* event)
522{
523 // Don't change view if tool is in use
524 if (isPointerInUse()) return;
525
526 static const bool isX11 = QGuiApplication::platformName() == "xcb";
527 const QPoint pixels = event->pixelDelta();
528 const QPoint angle = event->angleDelta();
529 const QPointF offset = mEditor->view()->mapScreenToCanvas(event->posF());
530
531 const qreal currentScale = mEditor->view()->scaling();
532 // From the pixelDelta documentation: On X11 this value is driver-specific and unreliable, use angleDelta() instead
533 if (!isX11 && !pixels.isNull())
534 {
535 // XXX: This pixel-based zooming algorithm currently has some shortcomings compared to the angle-based one:
536 // Zooming in is faster than zooming out and scrolling twice with delta x yields different zoom than
537 // scrolling once with delta 2x. Someone with the ability to test this code might want to "upgrade" it.
538 const int delta = pixels.y();
539 const qreal newScale = currentScale * (1 + (delta * 0.01));
540 mEditor->view()->scaleAtOffset(newScale, offset);
541 }
542 else if (!angle.isNull())
543 {
544 const int delta = angle.y();
545 // 12 rotation steps at "standard" wheel resolution (120/step) result in 100x zoom
546 const qreal newScale = currentScale * std::pow(100, delta / (12.0 * 120));
547 mEditor->view()->scaleAtOffset(newScale, offset);
548 }
549 updateCanvasCursor();
550 event->accept();
551}
552
553void ScribbleArea::tabletEvent(QTabletEvent *e)
554{
555 PointerEvent event(e);
556
557 if (event.pointerType() == QTabletEvent::Eraser)
558 {
559 editor()->tools()->tabletSwitchToEraser();
560 }
561 else
562 {
563 editor()->tools()->tabletRestorePrevTool();
564 }
565
566 if (event.eventType() == QTabletEvent::TabletPress)
567 {
568 event.accept();
569 mStrokeManager->pointerPressEvent(&event);
570 mStrokeManager->setTabletInUse(true);
571 if (mIsFirstClick)
572 {
573 mIsFirstClick = false;
574 mDoubleClickTimer->start();
575 pointerPressEvent(&event);
576 }
577 else
578 {
579 qreal distance = QLineF(currentTool()->getCurrentPressPoint(), currentTool()->getLastPressPoint()).length();
580
581 if (mDoubleClickMillis <= DOUBLE_CLICK_THRESHOLD && distance < 5.0) {
582 currentTool()->pointerDoubleClickEvent(&event);
583 }
584 else
585 {
586 // in case we handled the event as double click but really should have handled it as single click.
587 pointerPressEvent(&event);
588 }
589 }
590 mTabletInUse = event.isAccepted();
591 }
592 else if (event.eventType() == QTabletEvent::TabletMove)
593 {
594 if (!(event.buttons() & (Qt::LeftButton | Qt::RightButton)) || mTabletInUse)
595 {
596 mStrokeManager->pointerMoveEvent(&event);
597 pointerMoveEvent(&event);
598 }
599 }
600 else if (event.eventType() == QTabletEvent::TabletRelease)
601 {
602 mTabletReleaseMillisAgo = 0;
603 mMouseFilterTimer->start();
604 if (mTabletInUse)
605 {
606 mStrokeManager->pointerReleaseEvent(&event);
607 pointerReleaseEvent(&event);
608 mStrokeManager->setTabletInUse(false);
609 mTabletInUse = false;
610 }
611 }
612
613#if defined Q_OS_LINUX
614 // Generate mouse events on linux to work around bug where the
615 // widget will not receive mouseEnter/mouseLeave
616 // events and the cursor will not update correctly.
617 // See https://codereview.qt-project.org/c/qt/qtbase/+/255384
618 // Scribblearea should not do anything with the mouse event when mTabletInUse is true.
619 event.ignore();
620#else
621 // Always accept so that mouse events are not generated (theoretically)
622 // Unfortunately Windows sometimes generates the events anyway
623 // As long as mTabletInUse is true, mouse events *should* be ignored even when
624 // the are generated
625 event.accept();
626#endif
627}
628
629void ScribbleArea::pointerPressEvent(PointerEvent* event)
630{
631 bool isCameraLayer = mEditor->layers()->currentLayer()->type() == Layer::CAMERA;
632 if ((currentTool()->type() != HAND || isCameraLayer) && (event->button() != Qt::RightButton) && (event->button() != Qt::MidButton || isCameraLayer))
633 {
634 Layer* layer = mEditor->layers()->currentLayer();
635 if (!layer->visible())
636 {
637 event->ignore();
638 // This needs to be async so that mTabletInUse is set to false before
639 // further events are created (modal dialogs do not currently block tablet events)
640 QTimer::singleShot(0, this, &ScribbleArea::showLayerNotVisibleWarning);
641 return;
642 }
643 }
644
645 if (event->buttons() & (Qt::MidButton | Qt::RightButton) &&
646 editor()->tools()->setTemporaryTool(HAND, event->buttons()))
647 {
648 currentTool()->pointerPressEvent(event);
649 }
650
651 const bool isPressed = event->buttons() & Qt::LeftButton;
652 if (isPressed && mQuickSizing)
653 {
654 //qDebug() << "Start Adjusting" << event->buttons();
655 if (currentTool()->startAdjusting(event->modifiers(), 1))
656 {
657 return;
658 }
659 }
660
661 if (event->button() == Qt::LeftButton)
662 {
663 currentTool()->pointerPressEvent(event);
664 }
665}
666
667void ScribbleArea::pointerMoveEvent(PointerEvent* event)
668{
669 updateCanvasCursor();
670
671 if (event->buttons() & (Qt::LeftButton | Qt::RightButton))
672 {
673
674 // --- use SHIFT + drag to resize WIDTH / use CTRL + drag to resize FEATHER ---
675 if (currentTool()->isAdjusting())
676 {
677 currentTool()->adjustCursor(event->modifiers());
678 return;
679 }
680 }
681
682 currentTool()->pointerMoveEvent(event);
683}
684
685void ScribbleArea::pointerReleaseEvent(PointerEvent* event)
686{
687 if (currentTool()->isAdjusting())
688 {
689 currentTool()->stopAdjusting();
690 mEditor->tools()->setWidth(static_cast<float>(currentTool()->properties.width));
691 return; // [SHIFT]+drag OR [CTRL]+drag
692 }
693
694 if (event->buttons() & (Qt::RightButton | Qt::MiddleButton))
695 {
696 mMouseRightButtonInUse = false;
697 return;
698 }
699
700 //qDebug() << "release event";
701 currentTool()->pointerReleaseEvent(event);
702
703 editor()->tools()->tryClearTemporaryTool(event->button());
704}
705
706void ScribbleArea::handleDoubleClick()
707{
708 mDoubleClickMillis += 100;
709
710 if (mDoubleClickMillis >= DOUBLE_CLICK_THRESHOLD)
711 {
712 mDoubleClickMillis = 0;
713 mIsFirstClick = true;
714 mDoubleClickTimer->stop();
715 }
716}
717
718void ScribbleArea::tabletReleaseEventFired()
719{
720 // Under certain circumstances a mouse press event will fire after a tablet release event.
721 // This causes unexpected behaviours for some of the tools, eg. the bucket.
722 // The problem only seems to occur on windows and only when tapping.
723 // prior to this fix, the event queue would look like this:
724 // eg: TabletPress -> TabletRelease -> MousePress
725 // The following will filter mouse events created after a tablet release event.
726 mTabletReleaseMillisAgo += 50;
727
728 if (mTabletReleaseMillisAgo >= MOUSE_FILTER_THRESHOLD) {
729 mTabletReleaseMillisAgo = 0;
730 mMouseFilterTimer->stop();
731 }
732}
733
734bool ScribbleArea::isLayerPaintable() const
735{
736 Layer* layer = mEditor->layers()->currentLayer();
737 if (layer == nullptr) { return false; }
738
739 return layer->type() == Layer::BITMAP || layer->type() == Layer::VECTOR;
740}
741
742void ScribbleArea::mousePressEvent(QMouseEvent* e)
743{
744 if (mTabletInUse || (mMouseFilterTimer->isActive() && mTabletReleaseMillisAgo < MOUSE_FILTER_THRESHOLD))
745 {
746 e->ignore();
747 return;
748 }
749
750 PointerEvent event(e);
751
752 mStrokeManager->pointerPressEvent(&event);
753
754 pointerPressEvent(&event);
755 mMouseInUse = event.isAccepted();
756}
757
758void ScribbleArea::mouseMoveEvent(QMouseEvent* e)
759{
760 if (mTabletInUse || (mMouseFilterTimer->isActive() && mTabletReleaseMillisAgo < MOUSE_FILTER_THRESHOLD)) { e->ignore(); return; }
761 PointerEvent event(e);
762
763 mStrokeManager->pointerMoveEvent(&event);
764
765 pointerMoveEvent(&event);
766}
767
768void ScribbleArea::mouseReleaseEvent(QMouseEvent* e)
769{
770 if (mTabletInUse || (mMouseFilterTimer->isActive() && mTabletReleaseMillisAgo < MOUSE_FILTER_THRESHOLD)) { e->ignore(); return; }
771 PointerEvent event(e);
772
773 mStrokeManager->pointerReleaseEvent(&event);
774
775 pointerReleaseEvent(&event);
776 mMouseInUse = (e->buttons() & Qt::RightButton) || (e->buttons() & Qt::LeftButton);
777}
778
779void ScribbleArea::mouseDoubleClickEvent(QMouseEvent* e)
780{
781 if (mStrokeManager->isTabletInUse()) { e->ignore(); return; }
782 PointerEvent event(e);
783 mStrokeManager->pointerPressEvent(&event);
784
785 currentTool()->pointerDoubleClickEvent(&event);
786}
787
788void ScribbleArea::resizeEvent(QResizeEvent* event)
789{
790 QWidget::resizeEvent(event);
791 mDevicePixelRatio = devicePixelRatioF();
792 mCanvas = QPixmap(QSizeF(size() * mDevicePixelRatio).toSize());
793
794 mEditor->view()->setCanvasSize(size());
795
796 invalidateCacheForFrame(mEditor->currentFrame());
797 invalidatePainterCaches();
798 mCanvasPainter.reset();
799}
800
801void ScribbleArea::showLayerNotVisibleWarning()
802{
803 QMessageBox::warning(this, tr("Warning"),
804 tr("You are trying to modify a hidden layer! Please select another layer (or make the current layer visible)."),
805 QMessageBox::Ok,
806 QMessageBox::Ok);
807}
808
809void ScribbleArea::paintBitmapBuffer()
810{
811 LayerBitmap* layer = static_cast<LayerBitmap*>(mEditor->layers()->currentLayer());
812 Q_ASSERT(layer);
813 Q_ASSERT(layer->type() == Layer::BITMAP);
814
815 int frameNumber = mEditor->currentFrame();
816
817 // If there is no keyframe at or before the current position,
818 // just return (since we have nothing to paint on).
819 if (layer->getLastKeyFrameAtPosition(frameNumber) == nullptr)
820 {
821 updateCurrentFrame();
822 return;
823 }
824
825 // Clear the temporary pixel path
826 BitmapImage* targetImage = currentBitmapImage(layer);
827 if (targetImage != nullptr)
828 {
829 QPainter::CompositionMode cm = QPainter::CompositionMode_SourceOver;
830 switch (currentTool()->type())
831 {
832 case ERASER:
833 cm = QPainter::CompositionMode_DestinationOut;
834 break;
835 case BRUSH:
836 case PEN:
837 case PENCIL:
838 if (getTool(currentTool()->type())->properties.preserveAlpha)
839 {
840 cm = QPainter::CompositionMode_SourceOver;
841 }
842 break;
843 default: //nothing
844 break;
845 }
846 targetImage->paste(&mBufferImg, cm);
847 }
848
849 QRect rect = mEditor->view()->mapCanvasToScreen(mBufferImg.bounds()).toRect();
850
851 drawCanvas(frameNumber, rect.adjusted(-1, -1, 1, 1));
852 update(rect);
853
854 // Update the cache for the last key-frame.
855 updateFrame(frameNumber);
856 layer->setModified(frameNumber, true);
857
858 mBufferImg.clear();
859}
860
861void ScribbleArea::clearBitmapBuffer()
862{
863 mBufferImg.clear();
864}
865
866void ScribbleArea::drawLine(QPointF P1, QPointF P2, QPen pen, QPainter::CompositionMode cm)
867{
868 mBufferImg.drawLine(P1, P2, pen, cm, mPrefs->isOn(SETTING::ANTIALIAS));
869}
870
871void ScribbleArea::drawPath(QPainterPath path, QPen pen, QBrush brush, QPainter::CompositionMode cm)
872{
873 mBufferImg.drawPath(mEditor->view()->mapScreenToCanvas(path), pen, brush, cm, mPrefs->isOn(SETTING::ANTIALIAS));
874 update(mEditor->view()->mapCanvasToScreen(mBufferImg.bounds()).toRect().adjusted(-1, -1, 1, 1));
875}
876
877void ScribbleArea::paintCanvasCursor(QPainter& painter)
878{
879 QTransform view = mEditor->view()->getView();
880 QPointF mousePos = currentTool()->isAdjusting() ? currentTool()->getCurrentPressPoint() : currentTool()->getCurrentPoint();
881 int centerCal = mCursorImg.width() / 2;
882
883 mTransformedCursorPos = view.map(mousePos);
884
885 // reset matrix
886 view.reset();
887
888 painter.setTransform(view);
889 mCursorCenterPos.setX(centerCal);
890 mCursorCenterPos.setY(centerCal);
891
892 painter.drawPixmap(QPoint(static_cast<int>(mTransformedCursorPos.x() - mCursorCenterPos.x()),
893 static_cast<int>(mTransformedCursorPos.y() - mCursorCenterPos.y())),
894 mCursorImg);
895
896 // update center of transformed img for rect only
897 mTransCursImg = mCursorImg.transformed(view);
898
899 mCursorCenterPos.setX(centerCal);
900 mCursorCenterPos.setY(centerCal);
901}
902
903void ScribbleArea::updateCanvasCursor()
904{
905 float scalingFac = mEditor->view()->scaling();
906 qreal brushWidth = currentTool()->properties.width;
907 qreal brushFeather = currentTool()->properties.feather;
908 if (currentTool()->isAdjusting())
909 {
910 mCursorImg = currentTool()->quickSizeCursor(scalingFac);
911 }
912 else if (mEditor->preference()->isOn(SETTING::DOTTED_CURSOR))
913 {
914 bool useFeather = currentTool()->properties.useFeather;
915 mCursorImg = currentTool()->canvasCursor(static_cast<float>(brushWidth), static_cast<float>(brushFeather), useFeather, scalingFac, width());
916 }
917 else
918 {
919 mCursorImg = QPixmap(); // if above does not comply, deallocate image
920 }
921
922 // update cursor rect
923 QPoint translatedPos = QPoint(static_cast<int>(mTransformedCursorPos.x() - mCursorCenterPos.x()),
924 static_cast<int>(mTransformedCursorPos.y() - mCursorCenterPos.y()));
925
926 update(mTransCursImg.rect().adjusted(-1, -1, 1, 1)
927 .translated(translatedPos));
928
929}
930
931void ScribbleArea::handleDrawingOnEmptyFrame()
932{
933 auto layer = mEditor->layers()->currentLayer();
934
935 if (!layer || !layer->isPaintable())
936 {
937 return;
938 }
939
940 if (currentTool()->type() == ERASER) {
941 return;
942 }
943
944 int frameNumber = mEditor->currentFrame();
945 if (layer->getKeyFrameAt(frameNumber)) { return; }
946
947 // Drawing on an empty frame; take action based on preference.
948 int action = mPrefs->getInt(SETTING::DRAW_ON_EMPTY_FRAME_ACTION);
949 auto previousKeyFrame = layer->getLastKeyFrameAtPosition(frameNumber);
950 switch (action)
951 {
952 case KEEP_DRAWING_ON_PREVIOUS_KEY:
953 {
954 if (previousKeyFrame == nullptr) {
955 mEditor->addNewKey();
956 } else {
957 onFrameModified(previousKeyFrame->pos());
958 }
959 break;
960 }
961 case DUPLICATE_PREVIOUS_KEY:
962 {
963 if (previousKeyFrame != nullptr) {
964 KeyFrame* dupKey = previousKeyFrame->clone();
965 layer->addKeyFrame(frameNumber, dupKey);
966 mEditor->scrubTo(frameNumber);
967 break;
968 }
969 }
970 // if the previous keyframe doesn't exist,
971 // an empty keyframe needs to be created, so
972 // fallthrough
973 case CREATE_NEW_KEY:
974 mEditor->addNewKey();
975
976 // Refresh canvas
977 drawCanvas(frameNumber, mCanvas.rect());
978 break;
979 default:
980 break;
981 }
982}
983
984void ScribbleArea::paintEvent(QPaintEvent* event)
985{
986 int currentFrame = mEditor->currentFrame();
987 if (!currentTool()->isActive())
988 {
989 // --- we retrieve the canvas from the cache; we create it if it doesn't exist
990 const int frameNumber = mEditor->layers()->lastFrameAtFrame(currentFrame);
991
992 if (frameNumber < 0)
993 {
994 drawCanvas(currentFrame, event->rect());
995 }
996 else
997 {
998 auto cacheKeyIter = mPixmapCacheKeys.find(static_cast<unsigned>(frameNumber));
999
1000 if (cacheKeyIter == mPixmapCacheKeys.end() || !QPixmapCache::find(cacheKeyIter.value(), &mCanvas))
1001 {
1002 drawCanvas(currentFrame, event->rect());
1003 mPixmapCacheKeys[static_cast<unsigned>(currentFrame)] = QPixmapCache::insert(mCanvas);
1004 //qDebug() << "Repaint canvas!";
1005 }
1006 else
1007 {
1008 // Simply use the cached canvas from PixmapCache
1009 }
1010 }
1011 }
1012 else
1013 {
1014 prepCanvas(currentFrame, event->rect());
1015 prepCameraPainter(currentFrame);
1016 prepOverlays(currentFrame);
1017
1018 mCanvasPainter.paintCached(event->rect());
1019 mCameraPainter.paintCached();
1020 }
1021
1022 if (currentTool()->type() == MOVE)
1023 {
1024 Layer* layer = mEditor->layers()->currentLayer();
1025 Q_CHECK_PTR(layer);
1026 if (layer->type() == Layer::VECTOR)
1027 {
1028 VectorImage* vectorImage = currentVectorImage(layer);
1029 if (vectorImage != nullptr)
1030 {
1031 vectorImage->setModified(true);
1032 }
1033 }
1034 }
1035
1036 QPainter painter(this);
1037
1038 // paints the canvas
1039 painter.setWorldMatrixEnabled(false);
1040 painter.drawPixmap(QPoint(0, 0), mCanvas);
1041
1042 currentTool()->paint(painter);
1043
1044 Layer* layer = mEditor->layers()->currentLayer();
1045
1046 if (!editor()->playback()->isPlaying()) // we don't need to display the following when the animation is playing
1047 {
1048 if (layer->type() == Layer::VECTOR)
1049 {
1050 VectorImage* vectorImage = currentVectorImage(layer);
1051 if (vectorImage != nullptr)
1052 {
1053 switch (currentTool()->type())
1054 {
1055 case SMUDGE:
1056 case HAND:
1057 {
1058 auto selectMan = mEditor->select();
1059 painter.save();
1060 painter.setWorldMatrixEnabled(false);
1061 painter.setRenderHint(QPainter::Antialiasing, false);
1062 // ----- paints the edited elements
1063 QPen pen2(Qt::black, 0.5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
1064 painter.setPen(pen2);
1065 QColor color;
1066 // ------------ vertices of the edited curves
1067 color = QColor(200, 200, 200);
1068 painter.setBrush(color);
1069 VectorSelection vectorSelection = selectMan->vectorSelection;
1070 for (int k = 0; k < vectorSelection.curve.size(); k++)
1071 {
1072 int curveNumber = vectorSelection.curve.at(k);
1073
1074 for (int vertexNumber = -1; vertexNumber < vectorImage->getCurveSize(curveNumber); vertexNumber++)
1075 {
1076 QPointF vertexPoint = vectorImage->getVertex(curveNumber, vertexNumber);
1077 QRectF rectangle(mEditor->view()->mapCanvasToScreen(vertexPoint) - QPointF(3.0, 3.0), QSizeF(7, 7));
1078 if (rect().contains(mEditor->view()->mapCanvasToScreen(vertexPoint).toPoint()))
1079 {
1080 painter.drawRect(rectangle);
1081 }
1082 }
1083 }
1084 // ------------ selected vertices of the edited curves
1085 color = QColor(100, 100, 255);
1086 painter.setBrush(color);
1087 for (int k = 0; k < vectorSelection.vertex.size(); k++)
1088 {
1089 VertexRef vertexRef = vectorSelection.vertex.at(k);
1090 QPointF vertexPoint = vectorImage->getVertex(vertexRef);
1091 QRectF rectangle0 = QRectF(mEditor->view()->mapCanvasToScreen(vertexPoint) - QPointF(3.0, 3.0), QSizeF(7, 7));
1092 painter.drawRect(rectangle0);
1093 }
1094 // ----- paints the closest vertices
1095 color = QColor(255, 0, 0);
1096 painter.setBrush(color);
1097 QList<VertexRef> closestVertices = selectMan->closestVertices();
1098 if (vectorSelection.curve.size() > 0)
1099 {
1100 for (int k = 0; k < closestVertices.size(); k++)
1101 {
1102 VertexRef vertexRef = closestVertices.at(k);
1103 QPointF vertexPoint = vectorImage->getVertex(vertexRef);
1104
1105 QRectF rectangle = QRectF(mEditor->view()->mapCanvasToScreen(vertexPoint) - QPointF(3.0, 3.0), QSizeF(7, 7));
1106 painter.drawRect(rectangle);
1107 }
1108 }
1109 painter.restore();
1110 break;
1111 }
1112 default:
1113 {
1114 break;
1115 }
1116 } // end switch
1117 }
1118 }
1119
1120 paintCanvasCursor(painter);
1121
1122 mOverlayPainter.paint(painter);
1123
1124
1125 // paints the selection outline
1126 if (mEditor->select()->somethingSelected())
1127 {
1128 paintSelectionVisuals(painter);
1129 }
1130 }
1131
1132 // outlines the frame of the viewport
1133#ifdef _DEBUG
1134 painter.setWorldMatrixEnabled(false);
1135 painter.setPen(QPen(Qt::gray, 2));
1136 painter.setBrush(Qt::NoBrush);
1137 painter.drawRect(QRect(0, 0, width(), height()));
1138#endif
1139
1140 event->accept();
1141}
1142
1143void ScribbleArea::paintSelectionVisuals(QPainter &painter)
1144{
1145 Object* object = mEditor->object();
1146
1147 auto selectMan = mEditor->select();
1148
1149 QRectF currentSelectionRect = selectMan->mySelectionRect();
1150
1151 if (currentSelectionRect.isEmpty()) { return; }
1152
1153 TransformParameters params = { currentSelectionRect, editor()->view()->getView(), selectMan->selectionTransform() };
1154 mSelectionPainter.paint(painter, object, mEditor->currentLayerIndex(), currentTool(), params);
1155 emit selectionUpdated();
1156}
1157
1158BitmapImage* ScribbleArea::currentBitmapImage(Layer* layer) const
1159{
1160 Q_ASSERT(layer->type() == Layer::BITMAP);
1161 auto bitmapLayer = static_cast<LayerBitmap*>(layer);
1162 return bitmapLayer->getLastBitmapImageAtFrame(mEditor->currentFrame());
1163}
1164
1165VectorImage* ScribbleArea::currentVectorImage(Layer* layer) const
1166{
1167 Q_ASSERT(layer->type() == Layer::VECTOR);
1168 auto vectorLayer = static_cast<LayerVector*>(layer);
1169 return vectorLayer->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
1170}
1171
1172void ScribbleArea::prepCameraPainter(int frame)
1173{
1174 Object* object = mEditor->object();
1175
1176 mCameraPainter.preparePainter(object,
1177 mEditor->currentLayerIndex(),
1178 frame,
1179 mEditor->view()->getView(),
1180 mEditor->playback()->isPlaying(),
1181 mLayerVisibility,
1182 mPrefs->getFloat(SETTING::LAYER_VISIBILITY_THRESHOLD),
1183 mEditor->view()->getViewScaleInverse());
1184
1185 OnionSkinPainterOptions onionSkinOptions;
1186 onionSkinOptions.enabledWhilePlaying = mPrefs->getInt(SETTING::ONION_WHILE_PLAYBACK);
1187 onionSkinOptions.isPlaying = mEditor->playback()->isPlaying();
1188 onionSkinOptions.isAbsolute = (mPrefs->getString(SETTING::ONION_TYPE) == "absolute");
1189 onionSkinOptions.skinPrevFrames = mPrefs->isOn(SETTING::PREV_ONION);
1190 onionSkinOptions.skinNextFrames = mPrefs->isOn(SETTING::NEXT_ONION);
1191 onionSkinOptions.colorizePrevFrames = mPrefs->isOn(SETTING::ONION_RED);
1192 onionSkinOptions.colorizeNextFrames = mPrefs->isOn(SETTING::ONION_BLUE);
1193 onionSkinOptions.framesToSkinPrev = mPrefs->getInt(SETTING::ONION_PREV_FRAMES_NUM);
1194 onionSkinOptions.framesToSkinNext = mPrefs->getInt(SETTING::ONION_NEXT_FRAMES_NUM);
1195 onionSkinOptions.maxOpacity = mPrefs->getInt(SETTING::ONION_MAX_OPACITY);
1196 onionSkinOptions.minOpacity = mPrefs->getInt(SETTING::ONION_MIN_OPACITY);
1197
1198 mCameraPainter.setOnionSkinPainterOptions(onionSkinOptions);
1199 mCameraPainter.setCanvas(&mCanvas);
1200}
1201
1202void ScribbleArea::prepCanvas(int frame, QRect rect)
1203{
1204 Object* object = mEditor->object();
1205
1206 CanvasPainterOptions o;
1207 o.bAntiAlias = mPrefs->isOn(SETTING::ANTIALIAS);
1208 o.bThinLines = mPrefs->isOn(SETTING::INVISIBLE_LINES);
1209 o.bOutlines = mPrefs->isOn(SETTING::OUTLINES);
1210 o.eLayerVisibility = mLayerVisibility;
1211 o.fLayerVisibilityThreshold = mPrefs->getFloat(SETTING::LAYER_VISIBILITY_THRESHOLD);
1212 o.scaling = mEditor->view()->scaling();
1213 o.cmBufferBlendMode = mEditor->tools()->currentTool()->type() == ToolType::ERASER ? QPainter::CompositionMode_DestinationOut : QPainter::CompositionMode_SourceOver;
1214
1215 OnionSkinPainterOptions onionSkinOptions;
1216 onionSkinOptions.enabledWhilePlaying = mPrefs->getInt(SETTING::ONION_WHILE_PLAYBACK);
1217 onionSkinOptions.isPlaying = mEditor->playback()->isPlaying();
1218 onionSkinOptions.isAbsolute = (mPrefs->getString(SETTING::ONION_TYPE) == "absolute");
1219 onionSkinOptions.skinPrevFrames = mPrefs->isOn(SETTING::PREV_ONION);
1220 onionSkinOptions.skinNextFrames = mPrefs->isOn(SETTING::NEXT_ONION);
1221 onionSkinOptions.colorizePrevFrames = mPrefs->isOn(SETTING::ONION_RED);
1222 onionSkinOptions.colorizeNextFrames = mPrefs->isOn(SETTING::ONION_BLUE);
1223 onionSkinOptions.framesToSkinPrev = mPrefs->getInt(SETTING::ONION_PREV_FRAMES_NUM);
1224 onionSkinOptions.framesToSkinNext = mPrefs->getInt(SETTING::ONION_NEXT_FRAMES_NUM);
1225 onionSkinOptions.maxOpacity = mPrefs->getInt(SETTING::ONION_MAX_OPACITY);
1226 onionSkinOptions.minOpacity = mPrefs->getInt(SETTING::ONION_MIN_OPACITY);
1227
1228 mCanvasPainter.setOnionSkinOptions(onionSkinOptions);
1229 mCanvasPainter.setOptions(o);
1230
1231 ViewManager* vm = mEditor->view();
1232 SelectionManager* sm = mEditor->select();
1233 mCanvasPainter.setViewTransform(vm->getView(), vm->getViewInverse());
1234 mCanvasPainter.setTransformedSelection(sm->mySelectionRect().toRect(), sm->selectionTransform());
1235
1236 mCanvasPainter.setPaintSettings(object, mEditor->layers()->currentLayerIndex(), frame, rect, &mBufferImg);
1237}
1238
1239void ScribbleArea::drawCanvas(int frame, QRect rect)
1240{
1241 mCanvas.setDevicePixelRatio(mDevicePixelRatio);
1242 prepCanvas(frame, rect);
1243 prepCameraPainter(frame);
1244 prepOverlays(frame);
1245 mCanvasPainter.paint(rect);
1246 mCameraPainter.paint();
1247}
1248
1249void ScribbleArea::setGaussianGradient(QGradient &gradient, QColor color, qreal opacity, qreal offset)
1250{
1251 if (offset < 0) { offset = 0; }
1252 if (offset > 100) { offset = 100; }
1253
1254 int r = color.red();
1255 int g = color.green();
1256 int b = color.blue();
1257 qreal a = color.alphaF();
1258
1259 int mainColorAlpha = qRound(a * 255 * opacity);
1260
1261 // the more feather (offset), the more softness (opacity)
1262 int alphaAdded = qRound((mainColorAlpha * offset) / 100);
1263
1264 gradient.setColorAt(0.0, QColor(r, g, b, mainColorAlpha - alphaAdded));
1265 gradient.setColorAt(1.0, QColor(r, g, b, 0));
1266 gradient.setColorAt(1.0 - (offset / 100.0), QColor(r, g, b, mainColorAlpha - alphaAdded));
1267}
1268
1269void ScribbleArea::drawPen(QPointF thePoint, qreal brushWidth, QColor fillColor, bool useAA)
1270{
1271 QRectF rectangle(thePoint.x() - 0.5 * brushWidth, thePoint.y() - 0.5 * brushWidth, brushWidth, brushWidth);
1272
1273 mBufferImg.drawEllipse(rectangle, Qt::NoPen, QBrush(fillColor, Qt::SolidPattern),
1274 QPainter::CompositionMode_Source, useAA);
1275 update(mEditor->view()->mapCanvasToScreen(mBufferImg.bounds()).toRect().adjusted(-1, -1, 1, 1));
1276}
1277
1278void ScribbleArea::drawPencil(QPointF thePoint, qreal brushWidth, qreal fixedBrushFeather, QColor fillColor, qreal opacity)
1279{
1280 drawBrush(thePoint, brushWidth, fixedBrushFeather, fillColor, opacity, true);
1281}
1282
1283void ScribbleArea::drawBrush(QPointF thePoint, qreal brushWidth, qreal mOffset, QColor fillColor, qreal opacity, bool usingFeather, bool useAA)
1284{
1285 QRectF rectangle(thePoint.x() - 0.5 * brushWidth, thePoint.y() - 0.5 * brushWidth, brushWidth, brushWidth);
1286
1287 if (usingFeather)
1288 {
1289 QRadialGradient radialGrad(thePoint, 0.5 * brushWidth);
1290 setGaussianGradient(radialGrad, fillColor, opacity, mOffset);
1291
1292 mBufferImg.drawEllipse(rectangle, Qt::NoPen, radialGrad,
1293 QPainter::CompositionMode_SourceOver, false);
1294 }
1295 else
1296 {
1297 mBufferImg.drawEllipse(rectangle, Qt::NoPen, QBrush(fillColor, Qt::SolidPattern),
1298 QPainter::CompositionMode_SourceOver, useAA);
1299 }
1300
1301 update(mEditor->view()->mapCanvasToScreen(mBufferImg.bounds()).toRect().adjusted(-1, -1, 1, 1));
1302}
1303
1304void ScribbleArea::flipSelection(bool flipVertical)
1305{
1306 mEditor->select()->flipSelection(flipVertical);
1307}
1308
1309void ScribbleArea::prepOverlays(int frame)
1310{
1311 OverlayPainterOptions o;
1312
1313 o.bGrid = mPrefs->isOn(SETTING::GRID);
1314 o.nGridSizeW = mPrefs->getInt(SETTING::GRID_SIZE_W);
1315 o.nGridSizeH = mPrefs->getInt(SETTING::GRID_SIZE_H);
1316 o.bCenter = mPrefs->isOn(SETTING::OVERLAY_CENTER);
1317 o.bThirds = mPrefs->isOn(SETTING::OVERLAY_THIRDS);
1318 o.bGoldenRatio = mPrefs->isOn(SETTING::OVERLAY_GOLDEN);
1319 o.bSafeArea = mPrefs->isOn(SETTING::OVERLAY_SAFE);
1320 o.bPerspective1 = mPrefs->isOn(SETTING::OVERLAY_PERSPECTIVE1);
1321 o.bPerspective2 = mPrefs->isOn(SETTING::OVERLAY_PERSPECTIVE2);
1322 o.bPerspective3 = mPrefs->isOn(SETTING::OVERLAY_PERSPECTIVE3);
1323 o.nOverlayAngle = mPrefs->getInt(SETTING::OVERLAY_ANGLE);
1324 o.bActionSafe = mPrefs->isOn(SETTING::ACTION_SAFE_ON);
1325 o.nActionSafe = mPrefs->getInt(SETTING::ACTION_SAFE);
1326 o.bShowSafeAreaHelperText = mPrefs->isOn(SETTING::OVERLAY_SAFE_HELPER_TEXT_ON);
1327 o.bTitleSafe = mPrefs->isOn(SETTING::TITLE_SAFE_ON);
1328 o.nTitleSafe = mPrefs->getInt(SETTING::TITLE_SAFE);
1329 o.nOverlayAngle = mPrefs->getInt(SETTING::OVERLAY_ANGLE);
1330 o.bShowHandle = mEditor->tools()->currentTool()->type() == MOVE && mEditor->layers()->currentLayer()->type() != Layer::CAMERA;
1331
1332 o.mSinglePerspPoint = mEditor->overlays()->getSinglePerspectivePoint();
1333 o.mLeftPerspPoint = mEditor->overlays()->getLeftPerspectivePoint();
1334 o.mRightPerspPoint = mEditor->overlays()->getRightPerspectivePoint();
1335 o.mMiddlePerspPoint = mEditor->overlays()->getMiddlePerspectivePoint();
1336
1337 o.nFrameIndex = frame;
1338
1339 mOverlayPainter.setOptions(o);
1340 mOverlayPainter.preparePainter(mEditor->layers()->getCameraLayerBelow(mEditor->currentLayerIndex()), palette());
1341
1342 ViewManager* vm = mEditor->view();
1343 mOverlayPainter.setViewTransform(vm->getView());
1344}
1345
1346void ScribbleArea::blurBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPointF thePoint_, qreal brushWidth_, qreal mOffset_, qreal opacity_)
1347{
1348 QRadialGradient radialGrad(thePoint_, 0.5 * brushWidth_);
1349 setGaussianGradient(radialGrad, QColor(255, 255, 255, 127), opacity_, mOffset_);
1350
1351 QRectF srcRect(srcPoint_.x() - 0.5 * brushWidth_, srcPoint_.y() - 0.5 * brushWidth_, brushWidth_, brushWidth_);
1352 QRectF trgRect(thePoint_.x() - 0.5 * brushWidth_, thePoint_.y() - 0.5 * brushWidth_, brushWidth_, brushWidth_);
1353
1354 BitmapImage bmiSrcClip = bmiSource_->copy(srcRect.toAlignedRect());
1355 BitmapImage bmiTmpClip = bmiSrcClip; // TODO: find a shorter way
1356
1357 bmiTmpClip.drawRect(srcRect, Qt::NoPen, radialGrad, QPainter::CompositionMode_Source, mPrefs->isOn(SETTING::ANTIALIAS));
1358 bmiSrcClip.bounds().moveTo(trgRect.topLeft().toPoint());
1359 bmiTmpClip.paste(&bmiSrcClip, QPainter::CompositionMode_SourceIn);
1360 mBufferImg.paste(&bmiTmpClip);
1361
1362 update(mEditor->view()->mapCanvasToScreen(mBufferImg.bounds()).toRect().adjusted(-1, -1, 1, 1));
1363}
1364
1365void ScribbleArea::liquifyBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPointF thePoint_, qreal brushWidth_, qreal mOffset_, qreal opacity_)
1366{
1367 QPointF delta = (thePoint_ - srcPoint_); // increment vector
1368 QRectF trgRect(thePoint_.x() - 0.5 * brushWidth_, thePoint_.y() - 0.5 * brushWidth_, brushWidth_, brushWidth_);
1369
1370 QRadialGradient radialGrad(thePoint_, 0.5 * brushWidth_);
1371 setGaussianGradient(radialGrad, QColor(255, 255, 255, 255), opacity_, mOffset_);
1372
1373 // Create gradient brush
1374 BitmapImage bmiTmpClip;
1375 bmiTmpClip.drawRect(trgRect, Qt::NoPen, radialGrad, QPainter::CompositionMode_Source, mPrefs->isOn(SETTING::ANTIALIAS));
1376
1377 // Slide texture/pixels of the source image
1378 qreal factor, factorGrad;
1379
1380 for (int yb = bmiTmpClip.top(); yb < bmiTmpClip.bottom(); yb++)
1381 {
1382 for (int xb = bmiTmpClip.left(); xb < bmiTmpClip.right(); xb++)
1383 {
1384 QColor color;
1385 color.setRgba(bmiTmpClip.pixel(xb, yb));
1386 factorGrad = color.alphaF(); // any from r g b a is ok
1387
1388 int xa = xb - factorGrad * delta.x();
1389 int ya = yb - factorGrad * delta.y();
1390
1391 color.setRgba(bmiSource_->pixel(xa, ya));
1392 factor = color.alphaF();
1393
1394 if (factor > 0.0)
1395 {
1396 color.setRed(color.red() / factor);
1397 color.setGreen(color.green() / factor);
1398 color.setBlue(color.blue() / factor);
1399 color.setAlpha(255); // Premultiplied color
1400
1401 color.setRed(color.red()*factorGrad);
1402 color.setGreen(color.green()*factorGrad);
1403 color.setBlue(color.blue()*factorGrad);
1404 color.setAlpha(255 * factorGrad); // Premultiplied color
1405
1406 bmiTmpClip.setPixel(xb, yb, color.rgba());
1407 }
1408 else
1409 {
1410 bmiTmpClip.setPixel(xb, yb, qRgba(255, 255, 255, 0));
1411 }
1412 }
1413 }
1414 mBufferImg.paste(&bmiTmpClip);
1415
1416 update(mEditor->view()->mapCanvasToScreen(mBufferImg.bounds()).toRect().adjusted(-1, -1, 1, 1));
1417}
1418
1419void ScribbleArea::drawPolyline(QPainterPath path, QPen pen, bool useAA)
1420{
1421 BlitRect blitRect;
1422
1423 // In order to clear what was previous dirty, we need to include the previous buffer bound
1424 // this ensures that we won't see stroke artifacts
1425 blitRect.extend(mEditor->view()->mapCanvasToScreen(mBufferImg.bounds()).toRect());
1426
1427 QRect updateRect = mEditor->view()->mapCanvasToScreen(path.boundingRect()).toRect();
1428 // Now extend with the new path bounds mapped to the local coordinate
1429 blitRect.extend(updateRect);
1430
1431 mBufferImg.clear();
1432 mBufferImg.drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_SourceOver, useAA);
1433
1434 // And update only the affected area
1435 update(blitRect.adjusted(-1, -1, 1, 1));
1436}
1437
1438/************************************************************************************/
1439// view handling
1440
1441QPointF ScribbleArea::getCentralPoint()
1442{
1443 return mEditor->view()->mapScreenToCanvas(QPointF(width() / 2, height() / 2));
1444}
1445
1446void ScribbleArea::applyTransformedSelection()
1447{
1448 mCanvasPainter.ignoreTransformedSelection();
1449
1450 Layer* layer = mEditor->layers()->currentLayer();
1451 if (layer == nullptr) { return; }
1452
1453 auto selectMan = mEditor->select();
1454 if (selectMan->somethingSelected())
1455 {
1456 if (selectMan->mySelectionRect().isEmpty() || selectMan->selectionTransform().isIdentity()) { return; }
1457
1458 if (layer->type() == Layer::BITMAP)
1459 {
1460 handleDrawingOnEmptyFrame();
1461 BitmapImage* bitmapImage = currentBitmapImage(layer);
1462 if (bitmapImage == nullptr) { return; }
1463 BitmapImage transformedImage = bitmapImage->transformed(selectMan->mySelectionRect().toRect(), selectMan->selectionTransform(), true);
1464
1465
1466 bitmapImage->clear(selectMan->mySelectionRect());
1467 bitmapImage->paste(&transformedImage, QPainter::CompositionMode_SourceOver);
1468 }
1469 else if (layer->type() == Layer::VECTOR)
1470 {
1471 // Unfortunately this doesn't work right currently so vector transforms
1472 // will always be applied on the previous keyframe when on an empty frame
1473 //handleDrawingOnEmptyFrame();
1474 VectorImage* vectorImage = currentVectorImage(layer);
1475 if (vectorImage == nullptr) { return; }
1476
1477 vectorImage->applySelectionTransformation();
1478 }
1479
1480 mEditor->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame());
1481 }
1482
1483 update();
1484}
1485
1486void ScribbleArea::cancelTransformedSelection()
1487{
1488 mCanvasPainter.ignoreTransformedSelection();
1489
1490 auto selectMan = mEditor->select();
1491 if (selectMan->somethingSelected())
1492 {
1493 Layer* layer = mEditor->layers()->currentLayer();
1494 if (layer == nullptr) { return; }
1495
1496 if (layer->type() == Layer::VECTOR)
1497 {
1498 VectorImage* vectorImage = currentVectorImage(layer);
1499 if (vectorImage != nullptr)
1500 {
1501 vectorImage->setSelectionTransformation(QTransform());
1502 }
1503 }
1504
1505 mEditor->select()->setSelection(selectMan->mySelectionRect(), false);
1506
1507 selectMan->resetSelectionProperties();
1508 mOriginalPolygonF = QPolygonF();
1509
1510 mEditor->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame());
1511 updateCurrentFrame();
1512 }
1513}
1514
1515void ScribbleArea::displaySelectionProperties()
1516{
1517 Layer* layer = mEditor->layers()->currentLayer();
1518 if (layer == nullptr) { return; }
1519 if (layer->type() == Layer::VECTOR)
1520 {
1521 VectorImage* vectorImage = currentVectorImage(layer);
1522 if (vectorImage == nullptr) { return; }
1523 //vectorImage->applySelectionTransformation();
1524 if (currentTool()->type() == MOVE)
1525 {
1526 int selectedCurve = vectorImage->getFirstSelectedCurve();
1527 if (selectedCurve != -1)
1528 {
1529 mEditor->tools()->setWidth(vectorImage->curve(selectedCurve).getWidth());
1530 mEditor->tools()->setFeather(vectorImage->curve(selectedCurve).getFeather());
1531 mEditor->tools()->setInvisibility(vectorImage->curve(selectedCurve).isInvisible());
1532 mEditor->tools()->setPressure(vectorImage->curve(selectedCurve).getVariableWidth());
1533 mEditor->color()->setColorNumber(vectorImage->curve(selectedCurve).getColorNumber());
1534 }
1535
1536 int selectedArea = vectorImage->getFirstSelectedArea();
1537 if (selectedArea != -1)
1538 {
1539 mEditor->color()->setColorNumber(vectorImage->mArea[selectedArea].mColorNumber);
1540 }
1541 }
1542 }
1543}
1544
1545void ScribbleArea::toggleThinLines()
1546{
1547 bool previousValue = mPrefs->isOn(SETTING::INVISIBLE_LINES);
1548 setEffect(SETTING::INVISIBLE_LINES, !previousValue);
1549}
1550
1551void ScribbleArea::toggleOutlines()
1552{
1553 mIsSimplified = !mIsSimplified;
1554 setEffect(SETTING::OUTLINES, mIsSimplified);
1555}
1556
1557void ScribbleArea::setLayerVisibility(LayerVisibility visibility)
1558{
1559 mLayerVisibility = visibility;
1560 mPrefs->set(SETTING::LAYER_VISIBILITY, static_cast<int>(mLayerVisibility));
1561
1562 invalidateAllCache();
1563}
1564
1565void ScribbleArea::increaseLayerVisibilityIndex()
1566{
1567 ++mLayerVisibility;
1568 mPrefs->set(SETTING::LAYER_VISIBILITY, static_cast<int>(mLayerVisibility));
1569
1570 invalidateAllCache();
1571}
1572
1573void ScribbleArea::decreaseLayerVisibilityIndex()
1574{
1575 --mLayerVisibility;
1576 mPrefs->set(SETTING::LAYER_VISIBILITY, static_cast<int>(mLayerVisibility));
1577
1578 invalidateAllCache();
1579}
1580
1581/************************************************************************************/
1582// tool handling
1583
1584BaseTool* ScribbleArea::currentTool() const
1585{
1586 return editor()->tools()->currentTool();
1587}
1588
1589BaseTool* ScribbleArea::getTool(ToolType eToolType)
1590{
1591 return editor()->tools()->getTool(eToolType);
1592}
1593
1594void ScribbleArea::setCurrentTool(ToolType eToolMode)
1595{
1596 Q_UNUSED(eToolMode)
1597
1598 // change cursor
1599 setCursor(currentTool()->cursor());
1600 updateCanvasCursor();
1601 updateCurrentFrame();
1602}
1603
1604void ScribbleArea::deleteSelection()
1605{
1606 auto selectMan = mEditor->select();
1607 if (selectMan->somethingSelected()) // there is something selected
1608 {
1609 Layer* layer = mEditor->layers()->currentLayer();
1610 if (layer == nullptr) { return; }
1611
1612 handleDrawingOnEmptyFrame();
1613
1614 mEditor->backup(tr("Delete Selection", "Undo Step: clear the selection area."));
1615
1616 selectMan->clearCurves();
1617 if (layer->type() == Layer::VECTOR)
1618 {
1619 VectorImage* vectorImage = currentVectorImage(layer);
1620 Q_CHECK_PTR(vectorImage);
1621 vectorImage->deleteSelection();
1622 }
1623 else if (layer->type() == Layer::BITMAP)
1624 {
1625 BitmapImage* bitmapImage = currentBitmapImage(layer);
1626 Q_CHECK_PTR(bitmapImage);
1627 bitmapImage->clear(selectMan->mySelectionRect());
1628 }
1629 mEditor->setModified(mEditor->currentLayerIndex(), mEditor->currentFrame());
1630 }
1631}
1632
1633void ScribbleArea::clearImage()
1634{
1635 Layer* layer = mEditor->layers()->currentLayer();
1636 if (layer == nullptr) { return; }
1637
1638 if (layer->type() == Layer::VECTOR)
1639 {
1640 mEditor->backup(tr("Clear Image", "Undo step text"));
1641
1642 VectorImage* vectorImage = currentVectorImage(layer);
1643 if (vectorImage != nullptr)
1644 {
1645 vectorImage->clear();
1646 }
1647 mEditor->select()->clearCurves();
1648 mEditor->select()->clearVertices();
1649 }
1650 else if (layer->type() == Layer::BITMAP)
1651 {
1652 mEditor->backup(tr("Clear Image", "Undo step text"));
1653
1654 BitmapImage* bitmapImage = currentBitmapImage(layer);
1655 if (bitmapImage == nullptr) return;
1656 bitmapImage->clear();
1657 }
1658 else
1659 {
1660 return; // skip updates when nothing changes
1661 }
1662 mEditor->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame());
1663}
1664
1665void ScribbleArea::paletteColorChanged(QColor color)
1666{
1667 Q_UNUSED(color)
1668
1669 for (int i = 0; i < mEditor->layers()->count(); i++)
1670 {
1671 Layer* layer = mEditor->layers()->getLayer(i);
1672 if (layer->type() == Layer::VECTOR)
1673 {
1674 VectorImage* vectorImage = static_cast<LayerVector*>(layer)->getVectorImageAtFrame(mEditor->currentFrame());
1675 if (vectorImage != nullptr)
1676 {
1677 vectorImage->modification();
1678 }
1679 }
1680 }
1681
1682 invalidateAllCache();
1683}
1684
1685void ScribbleArea::floodFillError(int errorType)
1686{
1687 QString message, error;
1688 if (errorType == 1) { message = tr("There is a gap in your drawing (or maybe you have zoomed too much)."); }
1689 if (errorType == 2 || errorType == 3)
1690 {
1691 message = tr("Sorry! This doesn't always work."
1692 "Please try again (zoom a bit, click at another location... )<br>"
1693 "if it doesn't work, zoom a bit and check that your paths are connected by pressing F1.).");
1694 }
1695
1696 if (errorType == 1) { error = tr("Out of bound.", "Bucket tool fill error message"); }
1697 if (errorType == 2) { error = tr("Could not find a closed path.", "Bucket tool fill error message"); }
1698 if (errorType == 3) { error = tr("Could not find the root index.", "Bucket tool fill error message"); }
1699 QMessageBox::warning(this, tr("Flood fill error"), tr("%1<br><br>Error: %2").arg(message, error), QMessageBox::Ok, QMessageBox::Ok);
1700 mEditor->deselectAll();
1701}
BaseTool
Definition: basetool.h:70
BaseTool::canvasCursor
static QPixmap canvasCursor(float brushWidth, float brushFeather, bool useFeather, float scalingFac, int windowWidth)
precision circular cursor: used for drawing a cursor within scribble area.
Definition: basetool.cpp:125
BaseTool::quickSizeCursor
QPixmap quickSizeCursor(qreal scalingFac)
precision circular cursor: used for drawing stroke size while adjusting
Definition: basetool.cpp:213
BitmapImage
Definition: bitmapimage.h:27
BlitRect
Definition: blitrect.h:25
Editor::frameModified
void frameModified(int frameNumber)
This should be emitted after modifying the frame content.
KeyFrame
Definition: keyframe.h:30
LayerBitmap
Definition: layerbitmap.h:26
Layer
Definition: layer.h:38
Layer::dirtyFrames
QList< int > dirtyFrames() const
Returns a list of dirty frame positions.
Definition: layer.h:161
Layer::clearDirtyFrames
void clearDirtyFrames()
Clear the list of dirty keyframes.
Definition: layer.h:168
LayerVector
Definition: layervector.h:26
Object
Definition: object.h:42
PointerEvent
Definition: pointerevent.h:8
PointerEvent::button
Qt::MouseButton button() const
Returns Qt::MouseButton()
Definition: pointerevent.cpp:45
PointerEvent::modifiers
Qt::KeyboardModifiers modifiers() const
Returns the modifier created by keyboard while a device was in use.
Definition: pointerevent.cpp:151
PointerEvent::buttons
Qt::MouseButtons buttons() const
Returns Qt::MouseButtons()
Definition: pointerevent.cpp:61
ScribbleArea::onScrubbed
void onScrubbed(int frameNumber)
Frame scrubbed, invalidate relevant cache.
Definition: scribblearea.cpp:314
ScribbleArea::onToolPropertyUpdated
void onToolPropertyUpdated(ToolType, ToolPropertyType)
Tool property updated, invalidate cache and frame if needed.
Definition: scribblearea.cpp:278
ScribbleArea::onViewChanged
void onViewChanged()
View updated, invalidate relevant cache.
Definition: scribblearea.cpp:339
ScribbleArea::onObjectLoaded
void onObjectLoaded()
Object updated, invalidate all cache.
Definition: scribblearea.cpp:361
ScribbleArea::invalidateCacheForDirtyFrames
void invalidateCacheForDirtyFrames()
invalidate cache for dirty keyframes.
Definition: scribblearea.cpp:198
ScribbleArea::handleDrawingOnEmptyFrame
void handleDrawingOnEmptyFrame()
Call this when starting to use a paint tool.
Definition: scribblearea.cpp:931
ScribbleArea::invalidatePainterCaches
void invalidatePainterCaches()
Invalidate the layer pixmap and camera painter caches.
Definition: scribblearea.cpp:271
ScribbleArea::invalidateOnionSkinsCacheAround
void invalidateOnionSkinsCacheAround(int frame)
invalidate onion skin cache around frame
Definition: scribblearea.cpp:209
ScribbleArea::onSelectionChanged
void onSelectionChanged()
Selection was changed, keep cache.
Definition: scribblearea.cpp:349
ScribbleArea::onOnionSkinTypeChanged
void onOnionSkinTypeChanged()
Onion skin type changed, all frames will be affected.
Definition: scribblearea.cpp:356
ScribbleArea::onFramesModified
void onFramesModified()
Multiple frames modified, invalidate cache for affected frames.
Definition: scribblearea.cpp:320
ScribbleArea::onToolChanged
void onToolChanged(ToolType)
Tool changed, invalidate cache and frame if needed.
Definition: scribblearea.cpp:290
ScribbleArea::onPlayStateChanged
void onPlayStateChanged()
Playstate changed, invalidate relevant cache.
Definition: scribblearea.cpp:300
ScribbleArea::updateCurrentFrame
void updateCurrentFrame()
Update current frame.
Definition: scribblearea.cpp:187
ScribbleArea::invalidateAllCache
void invalidateAllCache()
Invalidate all cache.
Definition: scribblearea.cpp:250
ScribbleArea::invalidateCacheForFrame
void invalidateCacheForFrame(int frameNumber)
Invalidate cache for the given frame.
Definition: scribblearea.cpp:260
ScribbleArea::updateFrame
void updateFrame(int frame)
Update frame.
Definition: scribblearea.cpp:192
ScribbleArea::onLayerChanged
void onLayerChanged()
Layer changed, invalidate relevant cache.
Definition: scribblearea.cpp:344
ScribbleArea::onFrameModified
void onFrameModified(int frameNumber)
Frame modified, invalidate cache for frame if any.
Definition: scribblearea.cpp:329
SelectionManager
Definition: selectionmanager.h:33
SelectionManager::flipSelection
void flipSelection(bool flipVertical)
ScribbleArea::flipSelection flip selection along the X or Y axis.
Definition: selectionmanager.cpp:328
StrokeManager
Definition: strokemanager.h:33
VectorImage
Definition: vectorimage.h:32
VectorImage::getFirstSelectedArea
int getFirstSelectedArea()
VectorImage::getFirstSelectedArea.
Definition: vectorimage.cpp:801
VectorImage::getFirstSelectedCurve
int getFirstSelectedCurve()
VectorImage::getFirstSelectedCurve.
Definition: vectorimage.cpp:787
VectorImage::getVertex
QPointF getVertex(int curveNumber, int vertexNumber)
VectorImage::getVertex.
Definition: vectorimage.cpp:1573
VectorImage::clear
void clear()
VectorImage::clear.
Definition: vectorimage.cpp:1256
VectorImage::applySelectionTransformation
void applySelectionTransformation()
VectorImage::applySelectionTransformation.
Definition: vectorimage.cpp:1281
VectorImage::setSelectionTransformation
void setSelectionTransformation(QTransform transform)
VectorImage::setSelectionTransformation.
Definition: vectorimage.cpp:898
VectorImage::getCurveSize
int getCurveSize(int curveNumber)
VectorImage::getCurveSize.
Definition: vectorimage.cpp:1711
VectorImage::deleteSelection
void deleteSelection()
VectorImage::deleteSelection.
Definition: vectorimage.cpp:907
VectorSelection
Definition: vectorselection.h:26
VertexRef
Definition: vertexref.h:22
ViewManager
Definition: viewmanager.h:26
QBrush
QColor
QColor::alphaF
qreal alphaF() const const
QColor::blue
int blue() const const
QColor::green
int green() const const
QColor::red
int red() const const
QColor::rgba
QRgb rgba() const const
QColor::setAlpha
void setAlpha(int alpha)
QColor::setBlue
void setBlue(int blue)
QColor::setGreen
void setGreen(int green)
QColor::setRed
void setRed(int red)
QColor::setRgba
void setRgba(QRgb rgba)
QEvent
QEvent::WindowDeactivate
WindowDeactivate
QEvent::ignore
void ignore()
QEvent::type
QEvent::Type type() const const
QGradient
QGradient::setColorAt
void setColorAt(qreal position, const QColor &color)
QGuiApplication::platformName
platformName
QKeyEvent
QKeyEvent::isAutoRepeat
bool isAutoRepeat() const const
QKeyEvent::key
int key() const const
QKeyEvent::modifiers
Qt::KeyboardModifiers modifiers() const const
QLineF
QLineF::length
qreal length() const const
QList
QList::at
const T & at(int i) const const
QList::size
int size() const const
QMap::clear
void clear()
QMap::end
QMap::iterator end()
QMap::find
QMap::iterator find(const Key &key)
QMap::remove
int remove(const Key &key)
QMessageBox::Ok
Ok
QMessageBox::warning
QMessageBox::StandardButton warning(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton)
QMouseEvent
QMouseEvent::buttons
Qt::MouseButtons buttons() const const
QObject::connect
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject::tr
QString tr(const char *sourceText, const char *disambiguation, int n)
QPaintDevice::devicePixelRatioF
qreal devicePixelRatioF() const const
QPainter
QPainter::CompositionMode
CompositionMode
QPainter::Antialiasing
Antialiasing
QPainter::drawPixmap
void drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
QPainter::drawRect
void drawRect(const QRectF &rectangle)
QPainter::restore
void restore()
QPainter::save
void save()
QPainter::setBrush
void setBrush(const QBrush &brush)
QPainter::setPen
void setPen(const QColor &color)
QPainter::setRenderHint
void setRenderHint(QPainter::RenderHint hint, bool on)
QPainter::setTransform
void setTransform(const QTransform &transform, bool combine)
QPainter::setWorldMatrixEnabled
void setWorldMatrixEnabled(bool enable)
QPainterPath
QPainterPath::boundingRect
QRectF boundingRect() const const
QPaintEvent
QPaintEvent::rect
const QRect & rect() const const
QPen
QPixmap::transformed
QPixmap transformed(const QMatrix &matrix, Qt::TransformationMode mode) const const
QPixmap
QPixmap::rect
QRect rect() const const
QPixmap::setDevicePixelRatio
void setDevicePixelRatio(qreal scaleFactor)
QPixmap::width
int width() const const
QPixmapCache::find
QPixmap * find(const QString &key)
QPixmapCache::clear
void clear()
QPixmapCache::insert
bool insert(const QString &key, const QPixmap &pixmap)
QPixmapCache::remove
void remove(const QString &key)
QPixmapCache::setCacheLimit
void setCacheLimit(int n)
QPoint
QPoint::isNull
bool isNull() const const
QPoint::setX
void setX(int x)
QPoint::setY
void setY(int y)
QPoint::x
int x() const const
QPoint::y
int y() const const
QPointF
QPointF::toPoint
QPoint toPoint() const const
QPointF::x
qreal x() const const
QPointF::y
qreal y() const const
QPolygonF
QRadialGradient
QRect
QRect::adjusted
QRect adjusted(int dx1, int dy1, int dx2, int dy2) const const
QRect::moveTo
void moveTo(int x, int y)
QRect::translated
QRect translated(int dx, int dy) const const
QRectF
QRectF::isEmpty
bool isEmpty() const const
QRectF::toRect
QRect toRect() const const
QResizeEvent
QSizeF
QSizePolicy
QSizePolicy::MinimumExpanding
MinimumExpanding
QString
Qt::NoBrush
NoBrush
Qt::black
black
Qt::Key_Right
Key_Right
Qt::ControlModifier
ControlModifier
Qt::LeftButton
LeftButton
Qt::RoundCap
RoundCap
Qt::RoundJoin
RoundJoin
Qt::SolidLine
SolidLine
Qt::WA_StaticContents
WA_StaticContents
QTabletEvent
QTabletEvent::Eraser
Eraser
QTimer
QTimer::setInterval
void setInterval(int msec)
QTimer::isActive
bool isActive() const const
QTimer::singleShot
singleShot
QTimer::start
void start(int msec)
QTimer::stop
void stop()
QTimer::timeout
void timeout()
QTransform
QTransform::map
QPoint map(const QPoint &point) const const
QTransform::reset
void reset()
QWheelEvent::posF
const QPointF & posF() const const
QWheelEvent
QWheelEvent::buttons
Qt::MouseButtons buttons() const const
QWidget
QWidget::setCursor
void setCursor(const QCursor &)
QWidget::event
virtual bool event(QEvent *event) override
QWidget::height
height
QWidget::setMouseTracking
void setMouseTracking(bool enable)
QWidget::palette
palette
QWidget::pos
pos
QWidget::rect
rect
QWidget::resizeEvent
virtual void resizeEvent(QResizeEvent *event)
QWidget::size
size
QWidget::setSizePolicy
void setSizePolicy(QSizePolicy)
QWidget::setTabletTracking
void setTabletTracking(bool enable)
QWidget::update
void update()
QWidget::width
width
CanvasPainterOptions
Definition: canvaspainter.h:38
OnionSkinPainterOptions
Definition: onionskinpainteroptions.h:20
OverlayPainterOptions
Definition: overlaypainter.h:12
TransformParameters
Definition: selectionpainter.h:29
Generated on Mon Jun 5 2023 19:54:05 for Pencil2D by doxygen 1.9.6 based on revision afd72d68a001f57b4de14011d6ef944ba0717158