Skip to content

Commit

Permalink
Improve CameraPainter and BackgroundWidget performance (#1789)
Browse files Browse the repository at this point in the history
* Canvaspainter improvement WIP

* Implement tiled buffer for faster painting WIP

* CanvasPainter painting improvements

- Split painting for current frame and onion skinning
- Introduce blitRect for updating only the dirty portion of the frame
- Optimization: Only do an expensive fill when the size changes, otherwise rely on the blitRect to clear the dirty portion.

* Only update the dirty potion

Aside from cleaning up various extra update methods that are no longer necessary, I've made a minor change in vectorImage, in order to get it's correct dirty region.

* Fix vector selection being slightly wider than the overall bounds

This was a necessary change in order to calculate the correct bounds that wouldn't draw artifacts.

* Fix stroke placement for vector polyline

This also fixes some inconsistency in the CanvasPainter that I couldn't make sense of..

* Implement faster hashing

* Do some cleanup

* Fix next onion skin being drawn on current frame when it's the last

* Re-add loadFile if the bitmap image hasn't been loaded yet

Although i don't like that this exists in our painter, I believe we added it because for some reason the image hasn't been loaded yet, so let's leave it for now... ideally though i've really like to get rid of such weird mutable things that has little to do with painting.

* Update CanvasPainter interface to better match its usage

* Cleanup TiledBuffer

* Make polyline work

* Fix blur logic of smudge tool

Additionally this will fix some artifacts when smudging

* Replace use of BitmapImage buffer with TiledBuffer

- Note that VectorImage hasn't been implemented yet.

* Make TiledBuffer work for VectorImage

* Cleanup dead code in TiledBuffer

* Fix not painting bitmap layers when not on current frame

* Add missing license

* Remove dead code

* Implement blitting in CameraPainter

* Fix drawImage would not get proper update bounds

* Cleanup TiledBuffer and fix warnings

* Fix Eraser should use destinationOut composition

* Fix anti aliasing was causing unwanted floating precision error

This was causing the gaps to be drawn between the tiled buffer tiles in some cases... particularly when erasing and you had zoomed in or out.

* Fix Polyline can't be removed after applying on vector

* Fix transformed selection was being painted in wrong order

* Cleanup after testing

* Fix tabletRestorePrevTool would always ask for a full canvas update

* Remove unnecessary clear of cache when setting tool

* Fix update artifacts when using dotted cursor

- In addition this fix makes it possible to ignore an additional update event which would previously be done by updateCanvasCursor, but because the TiledBuffer will handle this now, we only update the cursor when a tool is not active.

* Refactor TiledBuffer

- Also add missing onClearTile callback, which is unused but implementation wise is correct now.

* Some cleanup

* Remove redundant clear of tile before deleting it

* Update signal/slot naming convention

* Apply some refactoring to tile

* Fix blitRect::extend would make tiles odd numbered

* Remove unused methods

* Avoid invalidating cache till we're done drawing

* Remove various dead code from ScribbleArea

* Fix ScribbleArea repaint when tiled buffer does not cover update rect

* Fix canvas pixmap being too big when using devicePixelRatio > 1

* Apply clip rect logic to BackgroundWidget

* Try to fix seams appearing in Qt 6

* Fix canvas not being updated when using camera tool

* Cleanup CanvasPainter

* Adjust blitRect explanation

* Remove the need to allocate an empty QPointF to draw pixmaps

* TiledBuffer: Remove unused signals

* Use clipRect instead of paint(target, pix, source)

in order to support HDPI properly
  • Loading branch information
MrStevns authored Oct 1, 2023
1 parent 86e5539 commit d3be339
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 56 deletions.
98 changes: 59 additions & 39 deletions core_lib/src/camerapainter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,21 @@ GNU General Public License for more details.

#include "painterutils.h"

CameraPainter::CameraPainter()
CameraPainter::CameraPainter(QPixmap& canvas) : mCanvas(canvas)
{
reset();
}

void CameraPainter::reset()
{
mCameraPixmap = QPixmap(mCanvas.size());
mCameraPixmap.setDevicePixelRatio(mCanvas.devicePixelRatioF());
mCameraPixmap.fill(Qt::transparent);
}

void CameraPainter::resetCache()
{
mCameraCacheValid = false;
}

void CameraPainter::preparePainter(const Object* object,
Expand All @@ -52,50 +64,51 @@ void CameraPainter::preparePainter(const Object* object,
mViewScale = viewScale;
}

void CameraPainter::paint() const
void CameraPainter::paint(const QRect& blitRect)
{
QPainter painter;
initializePainter(painter, *mCanvas);
paintVisuals(painter);
initializePainter(painter, mCanvas, blitRect, false);
paintVisuals(painter, blitRect);

mCameraCacheValid = true;
}

void CameraPainter::paintCached()
void CameraPainter::paintCached(const QRect& blitRect)
{
if (!mCachedPaint) {
QPainter tempPainter;
QPixmap cachedPixmap(mCanvas->size());
cachedPixmap.fill(Qt::transparent);
initializePainter(tempPainter, cachedPixmap);

paintVisuals(tempPainter);
mCachedPaint.reset(new QPixmap(cachedPixmap));
tempPainter.end();
}

QPainter painter;
initializePainter(painter, *mCanvas);
painter.drawPixmap(0, 0, *mCachedPaint.get());
painter.end();
// As always, initialize the painter with the canvas image, as this is what we'll paint on
// In this case though because the canvas has already been painted, we're not interested in
// having the blitter clear the image again, as that would remove our previous painted data, ie. strokes...
initializePainter(painter, mCanvas, blitRect, false);
if (!mCameraCacheValid) {
paintVisuals(painter, blitRect);
painter.end();
mCameraCacheValid = true;
} else {
painter.setWorldMatrixEnabled(false);
painter.drawPixmap(mZeroPoint, mCameraPixmap);
painter.setWorldMatrixEnabled(true);
painter.end();
}
}

void CameraPainter::setCanvas(QPixmap* canvas)
void CameraPainter::initializePainter(QPainter& painter, QPixmap& pixmap, const QRect& blitRect, bool blitEnabled)
{
mCanvas = canvas;
}
painter.begin(&pixmap);

void CameraPainter::resetCache()
{
mCachedPaint.reset();
}
if (blitEnabled) {
painter.setCompositionMode(QPainter::CompositionMode_Clear);
painter.fillRect(blitRect, Qt::transparent);
// Surface has been cleared and is ready to be painted on
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
}

void CameraPainter::initializePainter(QPainter& painter, QPixmap& pixmap) const
{
painter.begin(&pixmap);
painter.setClipRect(blitRect);
painter.setWorldMatrixEnabled(true);
painter.setWorldTransform(mViewTransform);
painter.setWorldMatrixEnabled(false);
}

void CameraPainter::paintVisuals(QPainter& painter) const
void CameraPainter::paintVisuals(QPainter& painter, const QRect& blitRect)
{
LayerCamera* cameraLayerBelow = static_cast<LayerCamera*>(mObject->getLayerBelow(mCurrentLayerIndex, Layer::CAMERA));

Expand All @@ -105,6 +118,9 @@ void CameraPainter::paintVisuals(QPainter& painter) const

if (mLayerVisibility == LayerVisibility::CURRENTONLY && currentLayer->type() != Layer::CAMERA) { return; }

QPainter visualsPainter;
initializePainter(visualsPainter, mCameraPixmap, blitRect, true);

if (!mIsPlaying || mOnionSkinOptions.enabledWhilePlaying) {

int startLayerI = 0;
Expand All @@ -117,26 +133,29 @@ void CameraPainter::paintVisuals(QPainter& painter) const

bool isCurrentLayer = cameraLayer == cameraLayerBelow;

painter.save();
painter.setOpacity(1);
visualsPainter.save();
visualsPainter.setOpacity(1);
if (mLayerVisibility == LayerVisibility::RELATED && !isCurrentLayer) {
painter.setOpacity(calculateRelativeOpacityForLayer(mCurrentLayerIndex, i, mRelativeLayerOpacityThreshold));
visualsPainter.setOpacity(calculateRelativeOpacityForLayer(mCurrentLayerIndex, i, mRelativeLayerOpacityThreshold));
}

paintOnionSkinning(painter, cameraLayer);
paintOnionSkinning(visualsPainter, cameraLayer);

painter.restore();
visualsPainter.restore();
}
}

if (!cameraLayerBelow->visible()) { return; }

QTransform camTransform = cameraLayerBelow->getViewAtFrame(mFrameIndex);
QRect cameraRect = cameraLayerBelow->getViewRect();
paintBorder(painter, camTransform, cameraRect);
paintBorder(visualsPainter, camTransform, cameraRect);

painter.setWorldMatrixEnabled(false);
painter.drawPixmap(mZeroPoint, mCameraPixmap);
}

void CameraPainter::paintBorder(QPainter& painter, const QTransform& camTransform, const QRect& camRect) const
void CameraPainter::paintBorder(QPainter& painter, const QTransform& camTransform, const QRect& camRect)
{
painter.save();
QRect viewRect = painter.viewport();
Expand All @@ -159,12 +178,13 @@ void CameraPainter::paintBorder(QPainter& painter, const QTransform& camTransfor
painter.restore();
}

void CameraPainter::paintOnionSkinning(QPainter& painter, const LayerCamera* cameraLayer) const
void CameraPainter::paintOnionSkinning(QPainter& painter, const LayerCamera* cameraLayer)
{
QPen onionSkinPen;

painter.save();
painter.setBrush(Qt::NoBrush);
painter.setWorldMatrixEnabled(false);

onionSkinPen.setStyle(Qt::PenStyle::DashLine);
mOnionSkinPainter.paint(painter, cameraLayer, mOnionSkinOptions, mFrameIndex, [&] (OnionSkinPaintState state, int onionSkinNumber) {
Expand Down
24 changes: 14 additions & 10 deletions core_lib/src/camerapainter.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,28 +37,31 @@ class KeyFrame;
class CameraPainter
{
public:
explicit CameraPainter();
explicit CameraPainter(QPixmap& canvas);

void paint() const;
void paintCached();
void paint(const QRect& blitRect);
void paintCached(const QRect& blitRect);

void setOnionSkinPainterOptions(const OnionSkinPainterOptions& options) { mOnionSkinOptions = options; }
void setCanvas(QPixmap* canvas);
void preparePainter(const Object* object, int layerIndex, int frameIndex, const QTransform& transform, bool isPlaying, LayerVisibility layerVisibility, float relativeLayerOpacityThreshold, qreal viewScale);
void reset();

void resetCache();

private:
void initializePainter(QPainter& painter, QPixmap& pixmap) const;
void paintVisuals(QPainter& painter) const;
void paintBorder(QPainter& painter, const QTransform& camTransform, const QRect& camRect) const;
void paintOnionSkinning(QPainter& painter, const LayerCamera* cameraLayer) const;
void initializePainter(QPainter& painter, QPixmap& pixmap, const QRect& blitRect, bool blitEnabled);
void paintVisuals(QPainter& painter, const QRect& blitRect);
void paintBorder(QPainter& painter, const QTransform& camTransform, const QRect& camRect);
void paintOnionSkinning(QPainter& painter, const LayerCamera* cameraLayer);

const Object* mObject = nullptr;
QPixmap* mCanvas = nullptr;
QPixmap& mCanvas;

std::unique_ptr<QPixmap> mCachedPaint = nullptr;
QPixmap mCameraPixmap;
QTransform mViewTransform;

const QPointF mZeroPoint;

OnionSkinSubPainter mOnionSkinPainter;
OnionSkinPainterOptions mOnionSkinOptions;

Expand All @@ -69,6 +72,7 @@ class CameraPainter
qreal mViewScale = 0;

bool mIsPlaying = false;
bool mCameraCacheValid = false;
};

#endif // CAMERAPAINTER_H
7 changes: 5 additions & 2 deletions core_lib/src/interface/backgroundwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ GNU General Public License for more details.

#include <QStyleOption>
#include <QPainter>
#include <QPaintEvent>


BackgroundWidget::BackgroundWidget(QWidget* parent) : QWidget(parent)
Expand Down Expand Up @@ -66,11 +67,13 @@ void BackgroundWidget::settingUpdated(SETTING setting)
}
}

void BackgroundWidget::paintEvent(QPaintEvent *)
void BackgroundWidget::paintEvent(QPaintEvent* event)
{
QStyleOption opt;
opt.initFrom(this);
QPainter painter(this);
painter.setClipRect(event->rect());

style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);

if (mHasShadow)
Expand Down Expand Up @@ -112,7 +115,7 @@ void BackgroundWidget::loadBackgroundStyle()
setStyleSheet(mStyle);
}

void BackgroundWidget::drawShadow( QPainter& painter )
void BackgroundWidget::drawShadow(QPainter& painter)
{
int radius1 = 12;
int radius2 = 8;
Expand Down
2 changes: 1 addition & 1 deletion core_lib/src/interface/backgroundwidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public slots:

protected:

void paintEvent( QPaintEvent* ) override;
void paintEvent(QPaintEvent* event) override;


private slots:
Expand Down
8 changes: 4 additions & 4 deletions core_lib/src/interface/scribblearea.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ GNU General Public License for more details.
#include "selectionmanager.h"
#include "overlaymanager.h"

ScribbleArea::ScribbleArea(QWidget* parent) : QWidget(parent), mCanvasPainter(mCanvas)
ScribbleArea::ScribbleArea(QWidget* parent) : QWidget(parent), mCanvasPainter(mCanvas), mCameraPainter(mCanvas)
{
setObjectName("ScribbleArea");

Expand Down Expand Up @@ -825,6 +825,7 @@ void ScribbleArea::resizeEvent(QResizeEvent* event)
invalidateCacheForFrame(mEditor->currentFrame());
invalidatePainterCaches();
mCanvasPainter.reset();
mCameraPainter.reset();
}

void ScribbleArea::showLayerNotVisibleWarning()
Expand Down Expand Up @@ -1010,7 +1011,7 @@ void ScribbleArea::paintEvent(QPaintEvent* event)
prepOverlays(currentFrame);

mCanvasPainter.paintCached(event->rect());
mCameraPainter.paintCached();
mCameraPainter.paintCached(event->rect());
}

if (currentTool()->type() == MOVE)
Expand Down Expand Up @@ -1193,7 +1194,6 @@ void ScribbleArea::prepCameraPainter(int frame)
onionSkinOptions.minOpacity = mPrefs->getInt(SETTING::ONION_MIN_OPACITY);

mCameraPainter.setOnionSkinPainterOptions(onionSkinOptions);
mCameraPainter.setCanvas(&mCanvas);
}

void ScribbleArea::prepCanvas(int frame, QRect rect)
Expand Down Expand Up @@ -1240,7 +1240,7 @@ void ScribbleArea::drawCanvas(int frame, QRect rect)
prepCameraPainter(frame);
prepOverlays(frame);
mCanvasPainter.paint(rect);
mCameraPainter.paint();
mCameraPainter.paint(rect);
}

void ScribbleArea::setGaussianGradient(QGradient &gradient, QColor color, qreal opacity, qreal offset)
Expand Down

0 comments on commit d3be339

Please sign in to comment.