Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Painting performance improvements - Tiled buffer #1776

Merged
merged 55 commits into from
Oct 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
2e9ec51
Canvaspainter improvement WIP
MrStevns Apr 22, 2023
69f7b81
Implement tiled buffer for faster painting WIP
MrStevns Apr 22, 2023
b8f8823
CanvasPainter painting improvements
MrStevns Apr 23, 2023
0999f48
Only update the dirty potion
MrStevns Apr 22, 2023
36d9b2e
Fix vector selection being slightly wider than the overall bounds
MrStevns Apr 23, 2023
3ee714f
Merge branch 'master' into painting-blitting-improvements
MrStevns Apr 23, 2023
e41218d
Fix stroke placement for vector polyline
MrStevns Apr 23, 2023
0bd437b
Implement faster hashing
MrStevns May 29, 2023
a6d03c7
Do some cleanup
J5lx May 30, 2023
85a9c1c
Fix next onion skin being drawn on current frame when it's the last
MrStevns May 31, 2023
8d56750
Re-add loadFile if the bitmap image hasn't been loaded yet
MrStevns May 31, 2023
9d7a2da
Update CanvasPainter interface to better match its usage
J5lx Jun 3, 2023
ec00ec1
Merge branch 'painting-blitting-improvements' into painting-blitting-…
MrStevns Jun 4, 2023
85b1b88
Cleanup TiledBuffer
MrStevns Jun 4, 2023
a911b10
Make polyline work
MrStevns Jun 8, 2023
e55d3fc
Fix blur logic of smudge tool
MrStevns Jun 27, 2023
9ef0eac
Replace use of BitmapImage buffer with TiledBuffer
MrStevns Jul 15, 2023
3eceedd
Make TiledBuffer work for VectorImage
MrStevns Jul 15, 2023
b958ce5
Cleanup dead code in TiledBuffer
MrStevns Jul 15, 2023
0086aa7
Merge remote-tracking branch 'origin/master' into painting-blitting-i…
MrStevns Jul 15, 2023
ec06502
Fix not painting bitmap layers when not on current frame
MrStevns Jul 15, 2023
e916591
Add missing license
MrStevns Jul 16, 2023
ed94ba6
Remove dead code
MrStevns Jul 16, 2023
a16efa9
Fix drawImage would not get proper update bounds
MrStevns Jul 16, 2023
63ed190
Cleanup TiledBuffer and fix warnings
MrStevns Jul 16, 2023
3d90e5c
Fix Eraser should use destinationOut composition
MrStevns Jul 16, 2023
7812bfd
Fix anti aliasing was causing unwanted floating precision error
MrStevns Jul 20, 2023
fb4bc79
Fix Polyline can't be removed after applying on vector
MrStevns Jul 20, 2023
3154b7c
Fix transformed selection was being painted in wrong order
MrStevns Jul 20, 2023
650ef97
Cleanup after testing
MrStevns Jul 20, 2023
6ac8c5f
Fix tabletRestorePrevTool would always ask for a full canvas update
MrStevns Jul 21, 2023
b8ef656
Remove unnecessary clear of cache when setting tool
MrStevns Jul 22, 2023
2fdc29d
Fix update artifacts when using dotted cursor
MrStevns Jul 23, 2023
856f509
Refactor TiledBuffer
MrStevns Jul 23, 2023
2c3bcc1
Some cleanup
J5lx Aug 9, 2023
9900f70
Remove redundant clear of tile before deleting it
MrStevns Aug 11, 2023
1a29932
Update signal/slot naming convention
MrStevns Aug 11, 2023
c27e00f
Merge branch 'master' into painting-blitting-improvements-v2
MrStevns Aug 13, 2023
239dc18
Apply some refactoring to tile
MrStevns Aug 15, 2023
836e34f
Fix blitRect::extend would make tiles odd numbered
MrStevns Aug 29, 2023
dd06086
Remove unused methods
MrStevns Aug 29, 2023
adcb3c1
Avoid invalidating cache till we're done drawing
MrStevns Aug 29, 2023
3566a5a
Remove various dead code from ScribbleArea
J5lx Sep 17, 2023
1f6d93c
Fix ScribbleArea repaint when tiled buffer does not cover update rect
J5lx Sep 17, 2023
e79a399
Merge pull request #21 from J5lx/MrStevns/painting-blitting-improveme…
MrStevns Sep 18, 2023
a9dd203
Fix canvas pixmap being too big when using devicePixelRatio > 1
MrStevns Sep 24, 2023
25624da
Merge branch 'master' into painting-blitting-improvements-v2
MrStevns Sep 24, 2023
70fb576
Try to fix seams appearing in Qt 6
MrStevns Sep 28, 2023
7f9687d
Fix canvas not being updated when using camera tool
MrStevns Sep 29, 2023
2ceed46
Cleanup CanvasPainter
MrStevns Sep 29, 2023
03ba9fa
Adjust blitRect explanation
MrStevns Sep 29, 2023
f3501ca
Remove the need to allocate an empty QPointF to draw pixmaps
MrStevns Sep 29, 2023
ad0afdf
TiledBuffer: Remove unused signals
MrStevns Sep 29, 2023
a7199f8
Fix typo
MrStevns Sep 30, 2023
ed2be11
Fix compiler warning
J5lx Sep 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/src/actioncommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -885,7 +885,7 @@ void ActionCommands::changeKeyframeLineColor()
QRgb color = mEditor->color()->frontColor().rgb();
LayerBitmap* layer = static_cast<LayerBitmap*>(mEditor->layers()->currentLayer());
layer->getBitmapImageAtFrame(mEditor->currentFrame())->fillNonAlphaPixels(color);
mEditor->updateFrame(mEditor->currentFrame());
mEditor->updateFrame();
}
}

Expand All @@ -900,7 +900,7 @@ void ActionCommands::changeallKeyframeLineColor()
if (layer->keyExists(i))
layer->getBitmapImageAtFrame(i)->fillNonAlphaPixels(color);
}
mEditor->updateFrame(mEditor->currentFrame());
mEditor->updateFrame();
}
}

Expand Down
2 changes: 1 addition & 1 deletion app/src/colorpalettewidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,7 @@ void ColorPaletteWidget::clickRemoveColorButton()
{
fitSwatchSize();
}
mEditor->updateCurrentFrame();
mEditor->updateFrame();
}

bool ColorPaletteWidget::showPaletteWarning()
Expand Down
4 changes: 2 additions & 2 deletions app/src/mainwindow2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -879,7 +879,7 @@ void MainWindow2::importImage()
return;
}

ui->scribbleArea->updateCurrentFrame();
ui->scribbleArea->updateFrame();
mTimeLine->updateContent();
}

Expand Down Expand Up @@ -1449,7 +1449,7 @@ void MainWindow2::makeConnections(Editor* editor, ColorInspector* colorInspector

void MainWindow2::makeConnections(Editor* editor, ScribbleArea* scribbleArea)
{
connect(editor->tools(), &ToolManager::toolChanged, scribbleArea, &ScribbleArea::setCurrentTool);
connect(editor->tools(), &ToolManager::toolChanged, scribbleArea, &ScribbleArea::updateToolCursor);
connect(editor->tools(), &ToolManager::toolChanged, mToolBox, &ToolBoxWidget::onToolSetActive);
connect(editor->tools(), &ToolManager::toolPropertyChanged, scribbleArea, &ScribbleArea::updateToolCursor);

Expand Down
4 changes: 4 additions & 0 deletions core_lib/core_lib.pro
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ HEADERS += \
src/corelib-pch.h \
src/graphics/bitmap/bitmapbucket.h \
src/graphics/bitmap/bitmapimage.h \
src/graphics/bitmap/tile.h \
src/graphics/bitmap/tiledbuffer.h \
src/graphics/vector/bezierarea.h \
src/graphics/vector/beziercurve.h \
src/graphics/vector/colorref.h \
Expand Down Expand Up @@ -119,6 +121,8 @@ HEADERS += \

SOURCES += src/graphics/bitmap/bitmapimage.cpp \
src/graphics/bitmap/bitmapbucket.cpp \
src/graphics/bitmap/tile.cpp \
src/graphics/bitmap/tiledbuffer.cpp \
src/graphics/vector/bezierarea.cpp \
src/graphics/vector/beziercurve.cpp \
src/graphics/vector/colorref.cpp \
Expand Down
82 changes: 55 additions & 27 deletions core_lib/src/canvaspainter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ GNU General Public License for more details.
#include "canvaspainter.h"

#include <QtMath>
#include <QSettings>

#include "object.h"
#include "layerbitmap.h"
#include "layervector.h"
#include "bitmapimage.h"
#include "tile.h"
#include "tiledbuffer.h"
#include "vectorimage.h"

#include "painterutils.h"
Expand All @@ -40,13 +41,17 @@ void CanvasPainter::reset()
{
mPostLayersPixmap = QPixmap(mCanvas.size());
mPreLayersPixmap = QPixmap(mCanvas.size());
mCurrentLayerPixmap = QPixmap(mCanvas.size());
mOnionSkinPixmap = QPixmap(mCanvas.size());
mPreLayersPixmap.fill(Qt::transparent);
mCanvas.fill(Qt::transparent);
mCurrentLayerPixmap = QPixmap(mCanvas.size());
mCurrentLayerPixmap.fill(Qt::transparent);
mPostLayersPixmap.fill(Qt::transparent);
mOnionSkinPixmap = QPixmap(mCanvas.size());
mOnionSkinPixmap.fill(Qt::transparent);
mCurrentLayerPixmap.setDevicePixelRatio(mCanvas.devicePixelRatioF());
mPreLayersPixmap.setDevicePixelRatio(mCanvas.devicePixelRatioF());
mPostLayersPixmap.setDevicePixelRatio(mCanvas.devicePixelRatioF());
mOnionSkinPixmap.setDevicePixelRatio(mCanvas.devicePixelRatioF());
}

void CanvasPainter::setViewTransform(const QTransform view, const QTransform viewInverse)
Expand Down Expand Up @@ -92,7 +97,7 @@ void CanvasPainter::paintCached(const QRect& blitRect)
QPainter mainPainter;
initializePainter(mainPainter, mCanvas, blitRect);
mainPainter.setWorldMatrixEnabled(false);
mainPainter.drawPixmap(blitRect, mPreLayersPixmap, blitRect);
mainPainter.drawPixmap(mPointZero, mPreLayersPixmap);
mainPainter.setWorldMatrixEnabled(true);

paintCurrentFrame(mainPainter, blitRect, mCurrentLayerIndex, mCurrentLayerIndex);
Expand All @@ -107,7 +112,7 @@ void CanvasPainter::paintCached(const QRect& blitRect)
}

mainPainter.setWorldMatrixEnabled(false);
mainPainter.drawPixmap(blitRect, mPostLayersPixmap, blitRect);
mainPainter.drawPixmap(mPointZero, mPostLayersPixmap);
mainPainter.setWorldMatrixEnabled(true);
}

Expand All @@ -121,6 +126,9 @@ void CanvasPainter::initializePainter(QPainter& painter, QPaintDevice& device, c
{
painter.begin(&device);

// Only draw inside the clipped rectangle
painter.setClipRect(blitRect);

// Clear the area that's about to be painted again, to avoid painting on top of existing pixels
// causing artifacts.
painter.setCompositionMode(QPainter::CompositionMode_Clear);
Expand Down Expand Up @@ -151,7 +159,7 @@ void CanvasPainter::renderPostLayers(QPainter& painter, const QRect& blitRect)
}
}

void CanvasPainter::setPaintSettings(const Object* object, int currentLayer, int frame, QRect rect, BitmapImage* buffer)
void CanvasPainter::setPaintSettings(const Object* object, int currentLayer, int frame, QRect rect, TiledBuffer* tiledBuffer)
{
Q_UNUSED(rect)
Q_ASSERT(object);
Expand All @@ -160,7 +168,7 @@ void CanvasPainter::setPaintSettings(const Object* object, int currentLayer, int
CANVASPAINTER_LOG("Set CurrentLayerIndex = %d", currentLayer);
mCurrentLayerIndex = currentLayer;
mFrameNumber = frame;
mBuffer = buffer;
mTiledBuffer = tiledBuffer;
}

void CanvasPainter::paint(const QRect& blitRect)
Expand All @@ -176,7 +184,7 @@ void CanvasPainter::paint(const QRect& blitRect)
preLayerPainter.end();

mainPainter.setWorldMatrixEnabled(false);
mainPainter.drawPixmap(blitRect, mPreLayersPixmap, blitRect);
mainPainter.drawPixmap(mPointZero, mPreLayersPixmap);
mainPainter.setWorldMatrixEnabled(true);

paintCurrentFrame(mainPainter, blitRect, mCurrentLayerIndex, mCurrentLayerIndex);
Expand All @@ -186,7 +194,7 @@ void CanvasPainter::paint(const QRect& blitRect)
postLayerPainter.end();

mainPainter.setWorldMatrixEnabled(false);
mainPainter.drawPixmap(blitRect, mPostLayersPixmap, blitRect);
mainPainter.drawPixmap(mPointZero, mPostLayersPixmap);
mainPainter.setWorldMatrixEnabled(true);

mPreLayersPixmapCacheValid = true;
Expand Down Expand Up @@ -230,7 +238,7 @@ void CanvasPainter::paintBitmapOnionSkinFrame(QPainter& painter, const QRect& bl
initializePainter(onionSkinPainter, mOnionSkinPixmap, blitRect);

onionSkinPainter.drawImage(bitmapImage->topLeft(), *bitmapImage->image());
paintOnionSkinFrame(painter, onionSkinPainter, blitRect, nFrame, colorize, bitmapImage->getOpacity());
paintOnionSkinFrame(painter, onionSkinPainter, nFrame, colorize, bitmapImage->getOpacity());
}

void CanvasPainter::paintVectorOnionSkinFrame(QPainter& painter, const QRect& blitRect, Layer* layer, int nFrame, bool colorize)
Expand All @@ -245,10 +253,10 @@ void CanvasPainter::paintVectorOnionSkinFrame(QPainter& painter, const QRect& bl
initializePainter(onionSkinPainter, mOnionSkinPixmap, blitRect);

vectorImage->paintImage(onionSkinPainter, mOptions.bOutlines, mOptions.bThinLines, mOptions.bAntiAlias);
paintOnionSkinFrame(painter, onionSkinPainter, blitRect, nFrame, colorize, vectorImage->getOpacity());
paintOnionSkinFrame(painter, onionSkinPainter, nFrame, colorize, vectorImage->getOpacity());
}

void CanvasPainter::paintOnionSkinFrame(QPainter& painter, QPainter& onionSkinPainter, const QRect& blitRect, int nFrame, bool colorize, qreal frameOpacity)
void CanvasPainter::paintOnionSkinFrame(QPainter& painter, QPainter& onionSkinPainter, int nFrame, bool colorize, qreal frameOpacity)
{
// Don't transform the image here as we used the viewTransform in the image output
painter.setWorldMatrixEnabled(false);
Expand All @@ -272,7 +280,7 @@ void CanvasPainter::paintOnionSkinFrame(QPainter& painter, QPainter& onionSkinPa
onionSkinPainter.setBrush(colorBrush);
onionSkinPainter.drawRect(painter.viewport());
}
painter.drawPixmap(blitRect, mOnionSkinPixmap, blitRect);
painter.drawPixmap(mPointZero, mOnionSkinPixmap);
}

void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blitRect, Layer* layer, bool isCurrentLayer)
Expand All @@ -283,27 +291,43 @@ void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blit
if (paintedImage == nullptr) { return; }
paintedImage->loadFile(); // Critical! force the BitmapImage to load the image

const bool isDrawing = mBuffer && !mBuffer->bounds().isEmpty();
const bool isDrawing = mTiledBuffer && !mTiledBuffer->bounds().isEmpty();

QPainter currentBitmapPainter;
initializePainter(currentBitmapPainter, mCurrentLayerPixmap, blitRect);

painter.setOpacity(paintedImage->getOpacity() - (1.0-painter.opacity()));
painter.setWorldMatrixEnabled(false);

currentBitmapPainter.drawImage(paintedImage->topLeft(), *paintedImage->image());
if (isCurrentLayer && isDrawing)
{
// Certain tools require being painted continuously, for example, the Polyline tool.
// The tiled buffer does not update the area outside which it paints,
// so in that case, in order to see the previously laid-down polyline stroke,
// the surrounding area must be drawn again before
// applying the new tiled output on top
if (!blitRect.contains(mTiledBuffer->bounds()) || mOptions.bIgnoreCanvasBuffer) {
currentBitmapPainter.setCompositionMode(QPainter::CompositionMode_Source);
currentBitmapPainter.drawImage(paintedImage->topLeft(), *paintedImage->image());
}

if (isCurrentLayer) {
if (isDrawing) {
// paint the current stroke data which hasn't been applied to a bitmapImage yet
currentBitmapPainter.setCompositionMode(mOptions.cmBufferBlendMode);
currentBitmapPainter.drawImage(mBuffer->topLeft(), *mBuffer->image());
} else if (mRenderTransform) {
paintTransformedSelection(currentBitmapPainter, paintedImage, mSelection);
const auto tiles = mTiledBuffer->tiles();
for (const Tile* tile : tiles) {
currentBitmapPainter.drawPixmap(tile->posF(), tile->pixmap());
}
} else {
// When we're drawing using a tool, the surface will be painted by the tiled buffer,
// and thus we don't want to paint the current image again
// When we're on another layer though, the tiled buffer is not used
currentBitmapPainter.drawImage(paintedImage->topLeft(), *paintedImage->image());
}

painter.drawPixmap(blitRect, mCurrentLayerPixmap, blitRect);
// We do not wish to draw selection transformations on anything but the current layer
if (isCurrentLayer && mRenderTransform) {
paintTransformedSelection(currentBitmapPainter, paintedImage, mSelection);
}

painter.drawPixmap(mPointZero, mCurrentLayerPixmap);
}

void CanvasPainter::paintCurrentVectorFrame(QPainter& painter, const QRect& blitRect, Layer* layer, bool isCurrentLayer)
Expand All @@ -318,15 +342,19 @@ void CanvasPainter::paintCurrentVectorFrame(QPainter& painter, const QRect& blit
QPainter currentVectorPainter;
initializePainter(currentVectorPainter, mCurrentLayerPixmap, blitRect);

const bool isDrawing = mBuffer && !mBuffer->bounds().isEmpty();
const bool isDrawing = mTiledBuffer->isValid();

// Paint existing vector image to the painter
vectorImage->paintImage(currentVectorPainter, mOptions.bOutlines, mOptions.bThinLines, mOptions.bAntiAlias);

if (isCurrentLayer) {
if (isDrawing) {
currentVectorPainter.setCompositionMode(mOptions.cmBufferBlendMode);
currentVectorPainter.drawImage(mBuffer->topLeft(), *mBuffer->image());

const auto tiles = mTiledBuffer->tiles();
for (const Tile* tile : tiles) {
currentVectorPainter.drawPixmap(tile->posF(), tile->pixmap());
}
} else if (mRenderTransform) {
vectorImage->setSelectionTransformation(mSelectionTransform);
}
Expand All @@ -336,9 +364,9 @@ void CanvasPainter::paintCurrentVectorFrame(QPainter& painter, const QRect& blit
painter.setWorldMatrixEnabled(false);
painter.setTransform(QTransform());

// Remember to adjust opacity based on addition opacity value from image
// Remember to adjust opacity based on additional opacity value from the keyframe
painter.setOpacity(vectorImage->getOpacity() - (1.0-painter.opacity()));
painter.drawPixmap(blitRect, mCurrentLayerPixmap, blitRect);
painter.drawPixmap(mPointZero, mCurrentLayerPixmap);
}

void CanvasPainter::paintTransformedSelection(QPainter& painter, BitmapImage* bitmapImage, const QRect& selection) const
Expand Down
16 changes: 13 additions & 3 deletions core_lib/src/canvaspainter.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ GNU General Public License for more details.
#include "onionskinpainteroptions.h"
#include "onionskinsubpainter.h"


class TiledBuffer;
class Object;
class BitmapImage;
class ViewManager;
Expand All @@ -39,6 +41,10 @@ struct CanvasPainterOptions
bool bAntiAlias = false;
bool bThinLines = false;
bool bOutlines = false;

/// When using a tool that can't rely on canvas buffer,
/// for example Polyline because we're continously clearing the surface
bool bIgnoreCanvasBuffer = false;
LayerVisibility eLayerVisibility = LayerVisibility::RELATED;
float fLayerVisibilityThreshold = 0.f;
float scaling = 1.0f;
Expand All @@ -61,7 +67,7 @@ class CanvasPainter
void setTransformedSelection(QRect selection, QTransform transform);
void ignoreTransformedSelection();

void setPaintSettings(const Object* object, int currentLayer, int frame, QRect rect, BitmapImage* buffer);
void setPaintSettings(const Object* object, int currentLayer, int frame, QRect rect, TiledBuffer* tilledBuffer);
void paint(const QRect& blitRect);
void paintCached(const QRect& blitRect);
void resetLayerCache();
Expand All @@ -88,7 +94,7 @@ class CanvasPainter

void paintBitmapOnionSkinFrame(QPainter& painter, const QRect& blitRect, Layer* layer, int nFrame, bool colorize);
void paintVectorOnionSkinFrame(QPainter& painter, const QRect& blitRect, Layer* layer, int nFrame, bool colorize);
void paintOnionSkinFrame(QPainter& painter, QPainter& onionSkinPainter, const QRect& blitRect, int nFrame, bool colorize, qreal frameOpacity);
void paintOnionSkinFrame(QPainter& painter, QPainter& onionSkinPainter, int nFrame, bool colorize, qreal frameOpacity);

void paintCurrentBitmapFrame(QPainter& painter, const QRect& blitRect, Layer* layer, bool isCurrentLayer);
void paintCurrentVectorFrame(QPainter& painter, const QRect& blitRect, Layer* layer, bool isCurrentLayer);
Expand All @@ -102,7 +108,7 @@ class CanvasPainter

int mCurrentLayerIndex = 0;
int mFrameNumber = 0;
BitmapImage* mBuffer = nullptr;
TiledBuffer* mTiledBuffer = nullptr;

QImage mScaledBitmap;

Expand All @@ -119,6 +125,10 @@ class CanvasPainter
bool mPreLayersPixmapCacheValid = false;
bool mPostLayersPixmapCacheValid = false;

// There's a considerable amount of overhead in simply allocating a QPointF on the fly.
// Since we just need to draw it at 0,0, we might as well make a const value for that purpose
const QPointF mPointZero;


OnionSkinSubPainter mOnionSkinSubPainter;
OnionSkinPainterOptions mOnionSkinPainterOptions;
Expand Down
31 changes: 29 additions & 2 deletions core_lib/src/graphics/bitmap/bitmapimage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ GNU General Public License for more details.
#include "util.h"

#include "blitrect.h"
#include "tile.h"
#include "tiledbuffer.h"

BitmapImage::BitmapImage()
{
Expand Down Expand Up @@ -98,7 +100,7 @@ BitmapImage* BitmapImage::clone() const
b->setFileName(""); // don't link to the file of the source bitmap image

const bool validKeyFrame = !fileName().isEmpty();
if (validKeyFrame && !isLoaded())
if (validKeyFrame && !isLoaded())
{
// This bitmapImage is temporarily unloaded.
// since it's not in the memory, we need to copy the linked png file to prevent data loss.
Expand Down Expand Up @@ -204,6 +206,28 @@ void BitmapImage::paste(BitmapImage* bitmapImage, QPainter::CompositionMode cm)
modification();
}

void BitmapImage::paste(const TiledBuffer* tiledBuffer, QPainter::CompositionMode cm)
{
if(tiledBuffer->bounds().width() <= 0 || tiledBuffer->bounds().height() <= 0)
{
return;
}
extend(tiledBuffer->bounds());

QPainter painter(image());

painter.setCompositionMode(cm);
auto const tiles = tiledBuffer->tiles();
for (const Tile* item : tiles) {
const QPixmap& tilePixmap = item->pixmap();
const QPoint& tilePos = item->pos();
painter.drawPixmap(tilePos-mBounds.topLeft(), tilePixmap);
}
painter.end();

modification();
}

void BitmapImage::moveTopLeft(QPoint point)
{
mBounds.moveTopLeft(point);
Expand Down Expand Up @@ -624,7 +648,10 @@ void BitmapImage::drawRect(QRectF rectangle, QPen pen, QBrush brush, QPainter::C
painter.setRenderHint(QPainter::Antialiasing, antialiasing);
painter.setPen(pen);
painter.setBrush(brush);
painter.drawRect(rectangle.translated(-mBounds.topLeft()));

// Adjust the brush rectangle to be bigger than the bounds itself,
// otherwise there will be artifacts shown in some cases when smudging
painter.drawRect(rectangle.translated(-mBounds.topLeft()).adjusted(-1, -1, 1, 1));
painter.end();
}
modification();
Expand Down
Loading