From 2e9ec5151c74c930c5da4ddd819fb219517147e5 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Sat, 22 Apr 2023 12:00:41 +0200 Subject: [PATCH 01/49] Canvaspainter improvement WIP --- core_lib/src/canvaspainter.cpp | 282 +++++++++++++----------- core_lib/src/canvaspainter.h | 29 +-- core_lib/src/interface/scribblearea.cpp | 4 +- 3 files changed, 168 insertions(+), 147 deletions(-) diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index 47cbef0c0b..76b7ea2c2f 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -39,7 +39,19 @@ CanvasPainter::~CanvasPainter() void CanvasPainter::setCanvas(QPixmap* canvas) { Q_ASSERT(canvas); - mCanvas = canvas; + qDebug() << canvas->size(); + if (mCanvas == nullptr || mCanvasSize != canvas->size()) { + + mCanvas = canvas; + mPostLayersPixmap = QPixmap(mCanvas->size()); + mPreLayersPixmap = QPixmap(mCanvas->size()); + mPreLayersPixmap.fill(Qt::transparent); + mCanvas->fill(Qt::transparent); + mCurrentLayerPixmap = QPixmap(mCanvas->size()); + mCurrentLayerPixmap.fill(Qt::transparent); + mPostLayersPixmap.fill(Qt::transparent); + mCanvasSize = canvas->size(); + } } void CanvasPainter::setViewTransform(const QTransform view, const QTransform viewInverse) @@ -71,101 +83,82 @@ void CanvasPainter::ignoreTransformedSelection() mRenderTransform = false; } -void CanvasPainter::paintCached() + +void CanvasPainter::paintCached(const QRect& blitRect) { - QPixmap tempPixmap(mCanvas->size()); - tempPixmap.fill(Qt::transparent); - mCanvas->fill(Qt::transparent); - QPainter tempPainter; - QPainter painter; - initializePainter(tempPainter, tempPixmap); - initializePainter(painter, *mCanvas); - - if (!mPreLayersCache) - { - renderPreLayers(painter); - mPreLayersCache.reset(new QPixmap(*mCanvas)); - } - else - { - painter.setWorldMatrixEnabled(false); - painter.drawPixmap(0, 0, *(mPreLayersCache.get())); - painter.setWorldMatrixEnabled(true); - } + QPainter preLayerPainter; + QPainter mainPainter; + QPainter postLayerPainter; - renderCurLayer(painter); + initializePainter(mainPainter, *mCanvas, blitRect); - if (!mPostLayersCache) + if (!mPreLayersPixmapCacheValid) { - renderPostLayers(tempPainter); - mPostLayersCache.reset(new QPixmap(tempPixmap)); - painter.setWorldMatrixEnabled(false); - painter.drawPixmap(0, 0, tempPixmap); - painter.setWorldMatrixEnabled(true); + initializePainter(preLayerPainter, mPreLayersPixmap, blitRect); + renderPreLayers(preLayerPainter, blitRect); + preLayerPainter.end(); + mPreLayersPixmapCacheValid = true; } - else + + mainPainter.setWorldMatrixEnabled(false); + mainPainter.drawPixmap(blitRect, mPreLayersPixmap, blitRect); + mainPainter.setWorldMatrixEnabled(true); + + renderCurLayer(mainPainter, blitRect); + + if (!mPostLayersPixmapCacheValid) { - painter.setWorldMatrixEnabled(false); - painter.drawPixmap(0, 0, *(mPostLayersCache.get())); - painter.setWorldMatrixEnabled(true); + renderPostLayers(postLayerPainter, blitRect); + mPostLayersPixmapCacheValid = true; } -} + mainPainter.setWorldMatrixEnabled(false); + mainPainter.drawPixmap(blitRect, mPostLayersPixmap, blitRect); + mainPainter.setWorldMatrixEnabled(true); +} void CanvasPainter::resetLayerCache() { - mPreLayersCache.reset(); - mPostLayersCache.reset(); + mPreLayersPixmapCacheValid = false; + mPreLayersPixmapCacheValid = false; } -void CanvasPainter::initializePainter(QPainter& painter, QPixmap& pixmap) +void CanvasPainter::initializePainter(QPainter& painter, QPaintDevice& device, const QRect& blitRect) { - painter.begin(&pixmap); + painter.begin(&device); + + // 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); + painter.fillRect(blitRect, Qt::transparent); + + // Surface has been cleared and is ready to be painted on + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); painter.setWorldMatrixEnabled(true); painter.setWorldTransform(mViewTransform); } -void CanvasPainter::renderPreLayers(QPixmap* pixmap) -{ - QPainter painter; - initializePainter(painter, *pixmap); - renderPreLayers(painter); -} - -void CanvasPainter::renderPreLayers(QPainter& painter) +void CanvasPainter::renderPreLayers(QPainter& painter, const QRect& blitRect) { if (mOptions.eLayerVisibility != LayerVisibility::CURRENTONLY || mObject->getLayer(mCurrentLayerIndex)->type() == Layer::CAMERA) { - paintCurrentFrame(painter, 0, mCurrentLayerIndex - 1); + paintCurrentFrame(painter, blitRect, 0, mCurrentLayerIndex - 1); } paintOnionSkin(painter); painter.setOpacity(1.0); } -void CanvasPainter::renderCurLayer(QPixmap* pixmap) -{ - QPainter painter; - initializePainter(painter, *pixmap); - renderCurLayer(painter); -} -void CanvasPainter::renderCurLayer(QPainter& painter) +void CanvasPainter::renderCurLayer(QPainter& painter, const QRect& blitRect) { - paintCurrentFrame(painter, mCurrentLayerIndex, mCurrentLayerIndex); + paintCurrentFrame(painter, blitRect, mCurrentLayerIndex, mCurrentLayerIndex); } -void CanvasPainter::renderPostLayers(QPixmap* pixmap) -{ - QPainter painter; - initializePainter(painter, *pixmap); - renderPostLayers(painter); -} - -void CanvasPainter::renderPostLayers(QPainter& painter) +void CanvasPainter::renderPostLayers(QPainter& painter, const QRect& blitRect) { if (mOptions.eLayerVisibility != LayerVisibility::CURRENTONLY || mObject->getLayer(mCurrentLayerIndex)->type() == Layer::CAMERA) { - paintCurrentFrame(painter, mCurrentLayerIndex + 1, mObject->getLayerCount() - 1); + paintCurrentFrame(painter, blitRect, mCurrentLayerIndex + 1, mObject->getLayerCount() - 1); } } @@ -181,46 +174,62 @@ void CanvasPainter::setPaintSettings(const Object* object, int currentLayer, int mBuffer = buffer; } -void CanvasPainter::paint() +void CanvasPainter::paint(const QRect& blitRect) { - QPainter painter; - initializePainter(painter, *mCanvas); + QPainter preLayerPainter; + QPainter mainPainter; + QPainter postLayerPainter; - renderPreLayers(painter); - renderCurLayer(painter); - renderPostLayers(painter); -} + initializePainter(mainPainter, *mCanvas, blitRect); -void CanvasPainter::paintBackground() -{ - mCanvas->fill(Qt::transparent); + initializePainter(preLayerPainter, mPreLayersPixmap, blitRect); + renderPreLayers(preLayerPainter, blitRect); + preLayerPainter.end(); + + mainPainter.setWorldMatrixEnabled(false); + mainPainter.drawPixmap(blitRect, mPreLayersPixmap, blitRect); + mainPainter.setWorldMatrixEnabled(true); + + renderCurLayer(mainPainter, blitRect); + + initializePainter(postLayerPainter, mPostLayersPixmap, blitRect); + renderPostLayers(postLayerPainter, blitRect); + postLayerPainter.end(); + + mainPainter.setWorldMatrixEnabled(false); + mainPainter.drawPixmap(blitRect, mPostLayersPixmap, blitRect); + mainPainter.setWorldMatrixEnabled(true); + + mPreLayersPixmapCacheValid = true; + mPostLayersPixmapCacheValid = true; } void CanvasPainter::paintOnionSkin(QPainter& painter) { Layer* layer = mObject->getLayer(mCurrentLayerIndex); - mOnionSkinSubPainter.paint(painter, layer, mOnionSkinPainterOptions, mFrameNumber, [&] (OnionSkinPaintState state, int onionFrameNumber) { - if (state == OnionSkinPaintState::PREV) { - switch (layer->type()) - { - case Layer::BITMAP: { paintBitmapFrame(painter, layer, onionFrameNumber, mOnionSkinPainterOptions.colorizePrevFrames, false, false); break; } - case Layer::VECTOR: { paintVectorFrame(painter, layer, onionFrameNumber, mOnionSkinPainterOptions.colorizePrevFrames, false, false); break; } - default: break; - } - } - if (state == OnionSkinPaintState::NEXT) { - switch (layer->type()) - { - case Layer::BITMAP: { paintBitmapFrame(painter, layer, onionFrameNumber, mOnionSkinPainterOptions.colorizeNextFrames, false, false); break; } - case Layer::VECTOR: { paintVectorFrame(painter, layer, onionFrameNumber, mOnionSkinPainterOptions.colorizeNextFrames, false, false); break; } - default: break; - } - } - }); +// mOnionSkinSubPainter.paint(painter, layer, mOnionSkinPainterOptions, mFrameNumber, [&] (OnionSkinPaintState state, int onionFrameNumber) { +// if (state == OnionSkinPaintState::PREV) { +// switch (layer->type()) +// { +// case Layer::BITMAP: { paintBitmapFrame(painter, layer, onionFrameNumber, mOnionSkinPainterOptions.colorizePrevFrames, false, false); break; } +// case Layer::VECTOR: { paintVectorFrame(painter, layer, onionFrameNumber, mOnionSkinPainterOptions.colorizePrevFrames, false, false); break; } +// default: break; +// } +// } +// if (state == OnionSkinPaintState::NEXT) { +// switch (layer->type()) +// { +// case Layer::BITMAP: { paintBitmapFrame(painter, layer, onionFrameNumber, mOnionSkinPainterOptions.colorizeNextFrames, false, false); break; } +// case Layer::VECTOR: { paintVectorFrame(painter, layer, onionFrameNumber, mOnionSkinPainterOptions.colorizeNextFrames, false, false); break; } +// default: break; +// } +// } +// }); } void CanvasPainter::paintBitmapFrame(QPainter& painter, + const QRect& blitRect, Layer* layer, int nFrame, bool colorize, @@ -252,56 +261,66 @@ void CanvasPainter::paintBitmapFrame(QPainter& painter, const bool frameIsEmpty = (paintedImage == nullptr || paintedImage->bounds().isEmpty()); const bool isDrawing = isCurrentFrame && mBuffer && !mBuffer->bounds().isEmpty(); - if (frameIsEmpty && !isDrawing) - { - CANVASPAINTER_LOG(" Early return frame %d, %d", frameIsEmpty, isDrawing); - return; - } +// if (frameIsEmpty && !isDrawing) +// { +// CANVASPAINTER_LOG(" Early return frame %d, %d", frameIsEmpty, isDrawing); +// return; +// } + +// BitmapImage paintToImage = BitmapImage(paintedImage->bounds(), Qt::transparent); +// paintToImage.paste(paintedImage); - BitmapImage paintToImage = BitmapImage(paintedImage->bounds(), Qt::transparent); - paintToImage.paste(paintedImage); + QPainter currentBitmapPainter; + initializePainter(currentBitmapPainter, mCurrentLayerPixmap, blitRect); painter.setOpacity(paintedImage->getOpacity() - (1.0-painter.opacity())); + painter.setWorldMatrixEnabled(false); + if (isCurrentFrame) { - paintToImage.paste(mBuffer, mOptions.cmBufferBlendMode); - } - if (colorize) - { - QBrush colorBrush = QBrush(Qt::transparent); //no color for the current frame + currentBitmapPainter.setWorldMatrixEnabled(true); + currentBitmapPainter.drawImage(paintedImage->topLeft(), *paintedImage->image()); - if (nFrame < mFrameNumber) - { - colorBrush = QBrush(Qt::red); - } - else if (nFrame > mFrameNumber) - { - colorBrush = QBrush(Qt::blue); - } + // paint the buffer which hasn't been applied to a bitmapImage yet + currentBitmapPainter.setCompositionMode(mOptions.cmBufferBlendMode); + currentBitmapPainter.drawImage(mBuffer->topLeft(), *mBuffer->image()); - paintToImage.drawRect(paintedImage->bounds(), - Qt::NoPen, - colorBrush, - QPainter::CompositionMode_SourceIn, - false); + painter.drawPixmap(blitRect, mCurrentLayerPixmap, blitRect); } - // If the current frame on the current layer has a transformation, we apply it. - bool shouldPaintTransform = mRenderTransform && nFrame == mFrameNumber && layer == mObject->getLayer(mCurrentLayerIndex); - if (shouldPaintTransform) - { - paintToImage.clear(mSelection); - } +// if (colorize) +// { +// QBrush colorBrush = QBrush(Qt::transparent); //no color for the current frame - painter.setWorldMatrixEnabled(true); - prescale(&paintToImage); - paintToImage.paintImage(painter, mScaledBitmap, mScaledBitmap.rect(), paintToImage.bounds()); +// if (nFrame < mFrameNumber) +// { +// colorBrush = QBrush(Qt::red); +// } +// else if (nFrame > mFrameNumber) +// { +// colorBrush = QBrush(Qt::blue); +// } - if (shouldPaintTransform) - { - paintTransformedSelection(painter); - } +// painter.setCompositionMode(QPainter::CompositionMode_SourceIn); +// painter.drawRect(paintedImage->bounds()); +// } + + // If the current frame on the current layer has a transformation, we apply it. +// bool shouldPaintTransform = mRenderTransform && nFrame == mFrameNumber && layer == mObject->getLayer(mCurrentLayerIndex); +// if (shouldPaintTransform) +// { +// paintToImage.clear(mSelection); +// } + +// painter.setWorldMatrixEnabled(true); +// prescale(&paintToImage); +// paintToImage.paintImage(painter, mScaledBitmap, mScaledBitmap.rect(), paintToImage.bounds()); + +// if (shouldPaintTransform) +// { +// paintTransformedSelection(painter); +// } // static int cc = 0; // QString path = QString("C:/Temp/pencil2d/canvas-%1-%2-%3.png") // .arg(cc++, 3, 10, QChar('0')) @@ -433,11 +452,10 @@ void CanvasPainter::paintTransformedSelection(QPainter& painter) const * @param startLayer The first layer to paint (inclusive) * @param endLayer The last layer to paint (inclusive) */ -void CanvasPainter::paintCurrentFrame(QPainter& painter, int startLayer, int endLayer) +void CanvasPainter::paintCurrentFrame(QPainter& painter, const QRect& blitRect, int startLayer, int endLayer) { painter.setOpacity(1.0); - bool isCameraLayer = mObject->getLayer(mCurrentLayerIndex)->type() == Layer::CAMERA; for (int i = startLayer; i <= endLayer; ++i) @@ -455,7 +473,7 @@ void CanvasPainter::paintCurrentFrame(QPainter& painter, int startLayer, int end CANVASPAINTER_LOG(" Render Layer[%d] %s", i, layer->name()); switch (layer->type()) { - case Layer::BITMAP: { paintBitmapFrame(painter, layer, mFrameNumber, false, true, i == mCurrentLayerIndex); break; } + case Layer::BITMAP: { paintBitmapFrame(painter, blitRect, layer, mFrameNumber, false, true, i == mCurrentLayerIndex); break; } case Layer::VECTOR: { paintVectorFrame(painter, layer, mFrameNumber, false, true, i == mCurrentLayerIndex); break; } default: break; } diff --git a/core_lib/src/canvaspainter.h b/core_lib/src/canvaspainter.h index d31ef1210b..3afc794441 100644 --- a/core_lib/src/canvaspainter.h +++ b/core_lib/src/canvaspainter.h @@ -62,8 +62,8 @@ class CanvasPainter void ignoreTransformedSelection(); void setPaintSettings(const Object* object, int currentLayer, int frame, QRect rect, BitmapImage* buffer); - void paint(); - void paintCached(); + void paint(const QRect& blitRect); + void paintCached(const QRect& blitRect); void resetLayerCache(); private: @@ -73,23 +73,20 @@ class CanvasPainter * Enriches the painter with a context and sets it's initial matrix. * @param painter The in/out painter * @param pixmap The paint device ie. a pixmap + * @param blitRect The rect where the blitting will occur */ - void initializePainter(QPainter& painter, QPixmap& pixmap); - - void renderPreLayers(QPainter& painter); - void renderCurLayer(QPainter& painter); - void renderPostLayers(QPainter& painter); + void initializePainter(QPainter& painter, QPaintDevice& device, const QRect& blitRect); void paintBackground(); void paintOnionSkin(QPainter& painter); - void renderPostLayers(QPixmap* pixmap); - void renderCurLayer(QPixmap* pixmap); - void renderPreLayers(QPixmap* pixmap); + void renderPostLayers(QPainter& painter, const QRect& blitRect); + void renderCurLayer(QPainter& painter, const QRect& blitRect); + void renderPreLayers(QPainter& painter, const QRect& blitRect); - void paintCurrentFrame(QPainter& painter, int startLayer, int endLayer); + void paintCurrentFrame(QPainter& painter, const QRect& blitRect, int startLayer, int endLayer); - void paintBitmapFrame(QPainter&, Layer* layer, int nFrame, bool colorize, bool useLastKeyFrame, bool isCurrentFrame); + void paintBitmapFrame(QPainter&, const QRect& blitRect, Layer* layer, int nFrame, bool colorize, bool useLastKeyFrame, bool isCurrentFrame); void paintVectorFrame(QPainter&, Layer* layer, int nFrame, bool colorize, bool useLastKeyFrame, bool isCurrentFrame); void paintTransformedSelection(QPainter& painter) const; @@ -101,6 +98,7 @@ class CanvasPainter const Object* mObject = nullptr; QPixmap* mCanvas = nullptr; + QSize mCanvasSize; QTransform mViewTransform; QTransform mViewInverse; @@ -116,7 +114,12 @@ class CanvasPainter QTransform mSelectionTransform; // Caches specifically for when drawing on the canvas - std::unique_ptr mPreLayersCache, mPostLayersCache; + QPixmap mPostLayersPixmap; + QPixmap mPreLayersPixmap; + QPixmap mCurrentLayerPixmap; + bool mPreLayersPixmapCacheValid = false; + bool mPostLayersPixmapCacheValid = false; + OnionSkinSubPainter mOnionSkinSubPainter; OnionSkinPainterOptions mOnionSkinPainterOptions; diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index 8b65f1f45f..f7de23c00f 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -1077,7 +1077,7 @@ void ScribbleArea::paintEvent(QPaintEvent* event) prepCameraPainter(currentFrame); prepOverlays(currentFrame); - mCanvasPainter.paintCached(); + mCanvasPainter.paintCached(event->rect()); mCameraPainter.paintCached(); } @@ -1307,7 +1307,7 @@ void ScribbleArea::drawCanvas(int frame, QRect rect) prepCanvas(frame, rect); prepCameraPainter(frame); prepOverlays(frame); - mCanvasPainter.paint(); + mCanvasPainter.paint(rect); mCameraPainter.paint(); } From 69f7b819a86c41fd1c9c57464720649fcd84770b Mon Sep 17 00:00:00 2001 From: MrStevns Date: Sat, 22 Apr 2023 12:04:21 +0200 Subject: [PATCH 02/49] Implement tiled buffer for faster painting WIP --- core_lib/core_lib.pro | 4 + core_lib/src/canvaspainter.cpp | 18 +- core_lib/src/canvaspainter.h | 5 +- core_lib/src/graphics/bitmap/bitmapimage.cpp | 24 +- core_lib/src/graphics/bitmap/bitmapimage.h | 3 + core_lib/src/graphics/bitmap/tile.cpp | 39 ++ core_lib/src/graphics/bitmap/tile.h | 34 ++ core_lib/src/graphics/bitmap/tiledbuffer.cpp | 583 +++++++++++++++++++ core_lib/src/graphics/bitmap/tiledbuffer.h | 180 ++++++ core_lib/src/interface/scribblearea.cpp | 122 ++-- core_lib/src/interface/scribblearea.h | 8 +- core_lib/src/tool/brushtool.cpp | 1 - core_lib/src/tool/erasertool.cpp | 4 +- core_lib/src/tool/penciltool.cpp | 2 +- core_lib/src/tool/pentool.cpp | 2 +- core_lib/src/tool/polylinetool.cpp | 14 +- core_lib/src/tool/smudgetool.cpp | 4 +- core_lib/src/tool/stroketool.cpp | 1 + core_lib/src/util/blitrect.cpp | 17 + core_lib/src/util/blitrect.h | 1 + 20 files changed, 969 insertions(+), 97 deletions(-) create mode 100644 core_lib/src/graphics/bitmap/tile.cpp create mode 100644 core_lib/src/graphics/bitmap/tile.h create mode 100644 core_lib/src/graphics/bitmap/tiledbuffer.cpp create mode 100644 core_lib/src/graphics/bitmap/tiledbuffer.h diff --git a/core_lib/core_lib.pro b/core_lib/core_lib.pro index e928b977f8..3222f04d96 100644 --- a/core_lib/core_lib.pro +++ b/core_lib/core_lib.pro @@ -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 \ @@ -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 \ diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index 76b7ea2c2f..2beff86baa 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -24,6 +24,7 @@ GNU General Public License for more details. #include "layervector.h" #include "bitmapimage.h" #include "layercamera.h" +#include "tiledbuffer.h" #include "vectorimage.h" #include "painterutils.h" @@ -162,7 +163,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, BitmapImage* buffer, TiledBuffer* tiledBuffer) { Q_UNUSED(rect) Q_ASSERT(object); @@ -172,6 +173,7 @@ void CanvasPainter::setPaintSettings(const Object* object, int currentLayer, int mCurrentLayerIndex = currentLayer; mFrameNumber = frame; mBuffer = buffer; + mTiledBuffer = tiledBuffer; } void CanvasPainter::paint(const QRect& blitRect) @@ -282,9 +284,17 @@ void CanvasPainter::paintBitmapFrame(QPainter& painter, currentBitmapPainter.setWorldMatrixEnabled(true); currentBitmapPainter.drawImage(paintedImage->topLeft(), *paintedImage->image()); - // paint the buffer which hasn't been applied to a bitmapImage yet - currentBitmapPainter.setCompositionMode(mOptions.cmBufferBlendMode); - currentBitmapPainter.drawImage(mBuffer->topLeft(), *mBuffer->image()); +// // paint the buffer which hasn't been applied to a bitmapImage yet +// currentBitmapPainter.setCompositionMode(mOptions.cmBufferBlendMode); +// currentBitmapPainter.drawImage(mBuffer->topLeft(), *mBuffer->image()); + + const auto tiles = mTiledBuffer->tiles(); + for (const Tile* tile : tiles) { + QPixmap pixTest = QPixmap(64,64); +// pixTest.fill(Qt::green); + currentBitmapPainter.drawPixmap(tile->pos(), tile->pixmap()); +// currentBitmapPainter.drawPixmap(tile->pos(), pixTest); + } painter.drawPixmap(blitRect, mCurrentLayerPixmap, blitRect); } diff --git a/core_lib/src/canvaspainter.h b/core_lib/src/canvaspainter.h index 3afc794441..d75f2f9127 100644 --- a/core_lib/src/canvaspainter.h +++ b/core_lib/src/canvaspainter.h @@ -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; @@ -61,7 +63,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, BitmapImage* buffer, TiledBuffer* tilledBuffer); void paint(const QRect& blitRect); void paintCached(const QRect& blitRect); void resetLayerCache(); @@ -105,6 +107,7 @@ class CanvasPainter int mCurrentLayerIndex = 0; int mFrameNumber = 0; BitmapImage* mBuffer = nullptr; + TiledBuffer* mTiledBuffer = nullptr; QImage mScaledBitmap; diff --git a/core_lib/src/graphics/bitmap/bitmapimage.cpp b/core_lib/src/graphics/bitmap/bitmapimage.cpp index c4be6a77c2..9680aebca9 100644 --- a/core_lib/src/graphics/bitmap/bitmapimage.cpp +++ b/core_lib/src/graphics/bitmap/bitmapimage.cpp @@ -24,6 +24,7 @@ GNU General Public License for more details. #include "util.h" #include "blitrect.h" +#include "tiledbuffer.h" BitmapImage::BitmapImage() { @@ -98,7 +99,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. @@ -204,6 +205,27 @@ 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); + for (const Tile* item : tiledBuffer->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); diff --git a/core_lib/src/graphics/bitmap/bitmapimage.h b/core_lib/src/graphics/bitmap/bitmapimage.h index 6122b92559..ead411bfbb 100644 --- a/core_lib/src/graphics/bitmap/bitmapimage.h +++ b/core_lib/src/graphics/bitmap/bitmapimage.h @@ -22,6 +22,8 @@ GNU General Public License for more details. #include #include +class TiledBuffer; + class BitmapImage : public KeyFrame { @@ -50,6 +52,7 @@ class BitmapImage : public KeyFrame BitmapImage copy(); BitmapImage copy(QRect rectangle); void paste(BitmapImage*, QPainter::CompositionMode cm = QPainter::CompositionMode_SourceOver); + void paste(const TiledBuffer* tiledBuffer, QPainter::CompositionMode cm = QPainter::CompositionMode_SourceOver); void moveTopLeft(QPoint point); void moveTopLeft(QPointF point) { moveTopLeft(point.toPoint()); } diff --git a/core_lib/src/graphics/bitmap/tile.cpp b/core_lib/src/graphics/bitmap/tile.cpp new file mode 100644 index 0000000000..294b77ee3f --- /dev/null +++ b/core_lib/src/graphics/bitmap/tile.cpp @@ -0,0 +1,39 @@ +#include "tile.h" + +#include + +Tile::Tile(QSize size): + mTilePixmap(size) +{ + clear(); //Default tiles are transparent +} + +Tile::Tile(QPixmap& pixmap) +{ + mTilePixmap = pixmap; +} + +Tile::~Tile() +{ +} + +QRect Tile::boundingRect() const +{ + return mTilePixmap.rect(); +} + +void Tile::replaceTile(const QPixmap& pixmap) +{ + if (pixmap.isNull()) { return; } + + mTilePixmap = pixmap; + mCacheValid = true; + +} + +void Tile::clear() +{ + mTilePixmap.fill(Qt::transparent); // image cache is transparent too, and aligned to the pixel table: + mCacheValid = true; + mDirty = false; +} diff --git a/core_lib/src/graphics/bitmap/tile.h b/core_lib/src/graphics/bitmap/tile.h new file mode 100644 index 0000000000..1561e82cdd --- /dev/null +++ b/core_lib/src/graphics/bitmap/tile.h @@ -0,0 +1,34 @@ +#ifndef TILE_H +#define TILE_H + +#include + +class Tile +{ +public: + + explicit Tile (QSize size); + explicit Tile (QPixmap& pixmap); + ~Tile(); + + const QPixmap& pixmap() const { return mTilePixmap; } + QPixmap& pixmap() { return mTilePixmap; } + + QRect boundingRect() const; + + void clear(); + void replaceTile(const QPixmap& pixmap); + + bool isDirty() { return mDirty; } + void setDirty(bool dirty) { mDirty = dirty; } + void setPos(const QPoint& pos) { m_pos = pos; } + QPoint pos() const { return m_pos; } + +private: + QPixmap mTilePixmap; + bool mCacheValid; + bool mDirty = false; + QPoint m_pos; +}; + +#endif // TILE_H diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.cpp b/core_lib/src/graphics/bitmap/tiledbuffer.cpp new file mode 100644 index 0000000000..a4e7e8c794 --- /dev/null +++ b/core_lib/src/graphics/bitmap/tiledbuffer.cpp @@ -0,0 +1,583 @@ +/* + +Pencil - Traditional Animation Software +Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon +Copyright (C) 2012-2018 Matthew Chiawen Chang + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +*/ +#include "tiledbuffer.h" +#include "QtGui/qpainterpath.h" + +#include + +#include +#include +#include +#include +#include + +TiledBuffer::TiledBuffer(QObject* parent) : QObject(parent) +{ +} + +TiledBuffer::TiledBuffer(const TiledBuffer& pieces) +{ +} + +TiledBuffer::TiledBuffer(const QRect bounds) +{ +// Tiles tiles; +// mSurface = Surface(tiles, bounds); +} + +TiledBuffer::~TiledBuffer() +{ + clear(); +} + +Tile* TiledBuffer::getTileFromIndex(const QPoint& index) +{ + // it's faster to iterate qstring than a qpoint... + QString idxString = QString::number(index.x())+"_"+QString::number(index.y()); + Tile* selectedTile = mTiles.value(idxString, nullptr); + + if (!selectedTile) { + // Time to allocate it, update table: + selectedTile = new Tile(QSize(UNIFORM_TILESIZE, UNIFORM_TILESIZE)); + mTiles.insert(idxString, selectedTile); + + QPoint tilePos (getTilePos(index)); + selectedTile->setPos(tilePos); + + emit this->onNewTile(this, selectedTile); + } else { + emit this->onUpdateTile(this, selectedTile); + } + + return selectedTile; +} + +void TiledBuffer::drawBrush(const QPointF& point, int brushWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing) { + QRectF brushRect(point.x() - 0.5 * brushWidth, point.y() - 0.5 * brushWidth, brushWidth, brushWidth); + + int radius = (brushWidth + 1); + float tilesize = UNIFORM_TILESIZE; + + // Gather the amount of tiles that fits the size of the brush width + int xLeft = qFloor((qFloor(point.x() - radius)) / tilesize); + int xRight = qFloor((qFloor(point.x() + radius)) / tilesize); + int yTop = qFloor(qFloor(point.y() - radius) / tilesize); + int yBottom = qFloor(qFloor(point.y() + radius) / tilesize); + + for (int tileY = yTop; tileY <= yBottom; tileY++) { + for (int tileX = xLeft; tileX <= xRight; tileX++) { + + Tile* tile = getTileFromIndex(QPoint(tileX, tileY)); + + if (tile) + { + QPainter painter(&tile->pixmap()); + + painter.translate(-tile->pos()); + painter.setRenderHint(QPainter::Antialiasing, antialiasing); + painter.setPen(pen); + painter.setBrush(brush); + painter.setCompositionMode(cm); + painter.drawEllipse(brushRect); + painter.end(); + + mTileBounds.extend(tile->pos(), tile->boundingRect().size()); + } + } + } +} + +void TiledBuffer::drawPath(QPainterPath path, QPen pen, QBrush brush, + QPainter::CompositionMode cm, bool antialiasing) +{ + int width = pen.width(); + + + +// QPainter painter(image()); +// painter.setCompositionMode(cm); +// painter.setRenderHint(QPainter::Antialiasing, antialiasing); +// painter.setPen(pen); +// painter.setBrush(brush); +// painter.setWorldMatrixEnabled(true); +// if (path.length() > 0) +// { +// painter.drawPath( path ); +// } +// else +// { +// // forces drawing when points are coincident (mousedown) +// painter.drawPoint(static_cast(path.elementAt(0).x), static_cast(path.elementAt(0).y)); +// } +// painter.end(); + + int radius = (width + 1); + float tilesize = UNIFORM_TILESIZE; + QRectF pathRect = path.boundingRect(); + + // Gather the amount of tiles that fits the size of the brush width + int xLeft = qFloor((qFloor(pathRect.left() - radius)) / tilesize); + int xRight = qFloor((qFloor(pathRect.right() + radius)) / tilesize); + int yTop = qFloor(qFloor(pathRect.top() - radius) / tilesize); + int yBottom = qFloor(qFloor(pathRect.bottom() + radius) / tilesize); + + for (int tileY = yTop; tileY <= yBottom; tileY++) { + for (int tileX = xLeft; tileX <= xRight; tileX++) { + + Tile* tile = getTileFromIndex(QPoint(tileX, tileY)); + + if (tile) + { + QPainter painter(&tile->pixmap()); + + painter.translate(-tile->pos()); + painter.setRenderHint(QPainter::Antialiasing, antialiasing); + painter.setPen(pen); + painter.setBrush(brush); + painter.setCompositionMode(cm); + painter.drawPath(path); + painter.end(); + + mTileBounds.extend(tile->pos(), tile->boundingRect().size()); + } + } + } +} + + +//void TiledBuffer::createNewSurfaceFromImage(const QString& path, const QPoint& topLeft) +//{ +// QImage image(path); +// createNewSurfaceFromImage(image, topLeft); +//} + +//void TiledBuffer::createNewSurfaceFromImage(const QImage& image, const QPoint& topLeft) +//{ +// QPixmap paintTo(TILESIZE); +// mSurface = Surface(); +// mSurface.bounds = QRect(topLeft, image.size()); + +// QList touchedPoints = touchedTiles(bounds()); +// for (int point = 0; point < touchedPoints.count(); point++) { + +// const QPoint& touchedPoint = touchedPoints.at(point); + +// paintTo.fill(Qt::transparent); +// QPainter painter(&paintTo); + +// painter.save(); +// painter.translate(-touchedPoint); +// painter.drawImage(topLeft, image); +// painter.restore(); +// if (!isTransparent(paintTo.toImage())) { +// mSurface.appendTile(paintTo, touchedPoint); +// } +// } +//} + +void TiledBuffer::addTileToSurface(const QPixmap& pixmap, const QPoint& pos) +{ +// mSurface.appendTile(pixmap, pos); + + +// extendBoundaries(QRect(pos, pixmap.size())); + mImageCacheValid = false; +} + +//void TiledBuffer::appendBitmapSurface(const Surface &surface) +//{ +// mSurface += surface; +// mImageCacheValid = false; +//} + +//void TiledBuffer::renderSurfaceImage() +//{ +// if (!mImageCacheValid) { +// mCachedSurface = surfaceAsImage(); +// mImageCacheValid = true; +// } +//} + +//void TiledBuffer::extendBoundaries(const QRect& rect) +//{ +// mSurface.extendBoundaries(rect); +//} + +//void TiledBuffer::paintSurfaceUsing(const QPixmap& inPixmap, const QPoint& newPos) +//{ +// Surface outSurface; + +// QPixmap outPix = QPixmap(TILESIZE); +// outPix.fill(Qt::transparent); + +// const QRect& adjustedPixRect = QRect(newPos, inPixmap.size()); +// const QList& touchedPoints = touchedTiles(adjustedPixRect); + +// // paint input pixmap on tiles +// for (int point = 0; point < touchedPoints.count(); point++) { + +// const QPoint& touchedPoint = touchedPoints.at(point); +// QPainter painter(&outPix); +// outPix.fill(Qt::transparent); + +// painter.save(); +// painter.translate(-touchedPoint); +// painter.drawPixmap(newPos, inPixmap); +// painter.restore(); +// painter.end(); + +// QImage testImage = outPix.toImage(); +// if (isTransparent(testImage)) { +// continue; +// } + +// outSurface.appendTile(outPix, touchedPoint); +// outSurface.bounds = adjustedPixRect; +// } + +// Surface extraSurface; + +// // paint new tiles on previous tiles if possible, otherwise +// // prepare to be added to bitmapsurface +// QHashIterator> newTiles(outSurface.tiles); +// while (newTiles.hasNext()) { +// newTiles.next(); + +// QPixmap newPix = outSurface.pixmapAtPos(newTiles.key()); +// const QPoint& newPos = outSurface.posFromPixmap(newTiles.value()); +// QPixmap paintToPix = QPixmap(newPix.size()); +// paintToPix.fill(Qt::transparent); + +// bool noMatch = false; +// QHashIterator> cuTiles(mSurface.tiles); +// while (cuTiles.hasNext()) +// { +// cuTiles.next(); + +// QPixmap& existingPix = mSurface.pixmapAtPos(cuTiles.key()); +// const QPoint existingPos = mSurface.posFromPixmap(cuTiles.value()); + +// if (existingPos == newPos) { +// QPainter painter(&existingPix); +// painter.setCompositionMode(QPainter::CompositionMode_SourceOver); +// painter.drawPixmap(QPoint(), newPix); +// painter.end(); +// } else { + +// if (mSurface.contains(newPos)) { +// continue; +// } +// noMatch = true; +// } +// } + +// if (noMatch) +// { +// extraSurface.appendTile(newPix, newPos); +// extraSurface.bounds = outSurface.bounds; +// noMatch = false; +// } +// } + +// appendBitmapSurface(extraSurface); +//} + +//QList TiledBuffer::scanForSurroundingTiles(const QRect& rect) +//{ +// QPoint kernel[] = {QPoint(-64,-64),QPoint(0,-64), QPoint(64,-64), +// QPoint(-64,0), QPoint(0,0), QPoint(64,0), +// QPoint(-64,64), QPoint(0,64), QPoint(64,64)}; + +// QList points; +// QList corners; + +// corners.append({rect.topLeft(), rect.topRight(), rect.bottomLeft(), rect.bottomRight()}); +// for (unsigned int i = 0; i < sizeof(kernel)/sizeof(kernel[0]); i++) { + +// for (int p = 0; p < corners.count(); p++) { +// const QPoint& corner = corners[p]; +// const QPoint& idx = getTileIndex(corner+kernel[i]); +// const QPoint& pos = getTilePos(idx); +// const QRect& rectToIntersect = getRectForPoint(pos); + +// if (rectToIntersect.intersects(rect)) { +// if (points.contains(pos)) { +// continue; +// } + +// points.append(pos); +// } +// } +// } + +// return points; +//} + +//QList TiledBuffer::scanForTilesAtRect(const QRect& rect) +//{ +// const float& imageWidth = static_cast(rect.width()); +// const float& imageHeight = static_cast(rect.height()); +// const float& tileWidth = static_cast(TILESIZE.width()); +// const float& tileHeight = static_cast(TILESIZE.height()); +// const int& nbTilesOnWidth = static_cast(ceil(imageWidth / tileWidth)); +// const int& nbTilesOnHeight = static_cast(ceil(imageHeight / tileHeight)); + +// QList points; + +// QList corners; +// const QPoint& cornerOffset = QPoint(TILESIZE.width(), TILESIZE.height()); + +// corners.append({rect.topLeft(), rect.topRight(), rect.bottomLeft(), rect.bottomRight()}); +// for (int h=0; h < nbTilesOnHeight; h++) { +// for (int w=0; w < nbTilesOnWidth; w++) { + +// const QPoint& tilePos = getTilePos(QPoint(w,h)); +// for (int i = 0; i < corners.count(); i++) { +// QPoint movedPos = getTileIndex(corners[i]-cornerOffset); +// movedPos = getTilePos(movedPos)+tilePos; + +// if (points.contains(movedPos)) { +// continue; +// } + +// if (getRectForPoint(movedPos).intersects(rect)) { +// points.append(movedPos); +// } +// } +// } +// } +// return points; +//} + +//QList TiledBuffer::touchedTiles(const QRect& rect) +//{ +// return scanForTilesAtRect(rect); +//} + +//void TiledBuffer::drawRect(QRect rect, QColor color) +//{ +// float rectWidth = static_cast(rect.width()); +// float rectHeight = static_cast(rect.height()); +// float tileWidth = static_cast(TILESIZE.width()); +// float tileHeight = static_cast(TILESIZE.height()); +// int nbTilesOnWidth = static_cast(ceil(rectWidth / tileWidth)); +// int nbTilesOnHeight = static_cast(ceil(rectHeight / tileHeight)); + +// QPixmap paintTo(UNIFORM_TILESIZE, UNIFORM_TILESIZE); +// mSurface = Surface(); +// mSurface.bounds = QRect(rect.topLeft(), rect.size()); + +// for (int h=0; h < nbTilesOnHeight; h++) { +// for (int w=0; w < nbTilesOnWidth; w++) { +// paintTo.fill(Qt::transparent); +// const QPoint& idx = QPoint(w, h); +// const QPoint& tilePos = getTilePos(idx); + +// const QRect& tileRect = QRect(tilePos, TILESIZE); +// QImage colorImage = QImage(rect.size(), QImage::Format_ARGB32_Premultiplied); +// colorImage.fill(color); +// const QImage& tileImage = colorImage.copy(tileRect); + +// QPainter painter(&paintTo); +// painter.drawImage(QPoint(), tileImage); +// painter.end(); + +// mSurface.appendTile(paintTo, tilePos); +// } +// } +//} + +//QImage TiledBuffer::surfaceAsImage() +//{ +// QImage paintedImage(bounds.size(), QImage::Format_ARGB32_Premultiplied); +// paintedImage.fill(Qt::transparent); + +// QPainter painter(&paintedImage); +// painter.translate(-mSurface.topLeft()); + +// QHashIterator> cuTiles(mSurface.tiles); +// while (cuTiles.hasNext()) { +// cuTiles.next(); +// const QPixmap& pix = mSurface.pixmapAtPos(cuTiles.key()); +// const QPoint& pos = mSurface.posFromPixmap(cuTiles.value()); +// painter.drawPixmap(pos, pix); +// } +// painter.end(); + +// return paintedImage; +//} + +//void TiledBuffer::eraseSelection(const QPoint pos, QPixmap& pixmap, const QRect selection) +//{ +// fillSelection(pos, pixmap, Qt::transparent, selection); +//} + +//void TiledBuffer::eraseSelection(const QRect selection) +//{ +// QHashIterator> cuTiles(mSurface.tiles); +// while (cuTiles.hasNext()) { +// cuTiles.next(); + +// QPixmap& pixmap = mSurface.pixmapAtPos(cuTiles.key()); +// const QPoint& pos = mSurface.posFromPixmap(cuTiles.value()); +// eraseSelection(pos, pixmap, selection); +// } +//} + +//void TiledBuffer::fillSelection(const QPoint& pos, QPixmap& pixmap, QColor color, const QRect selection) +//{ +// QPainter painter(&pixmap); +// painter.translate(-pos); +// painter.setCompositionMode(QPainter::CompositionMode_Source); + +// const QRect& intersection = selection.intersected(getRectForPoint(pos, pixmap.size())); +// painter.fillRect(intersection, color); +// painter.end(); + +//} + +//Surface TiledBuffer::intersectedSurface(const QRect rect) +//{ +// Surface outSurface; +// QHashIterator> cuTiles(mSurface.tiles); +// while (cuTiles.hasNext()) +// { +// cuTiles.next(); +// const QPixmap& pix = mSurface.pixmapAtPos(cuTiles.key()); +// const QPoint& pos = mSurface.posFromPixmap(cuTiles.value()); + +// if (getRectForPoint(pos, pix.size()).intersects(rect)) { +// outSurface.appendTile(pix, pos); +// outSurface.bounds = rect; +// } +// } + +// return outSurface; +//} + +//QPixmap TiledBuffer::cutSurfaceAsPixmap(const QRect selection) +//{ +// Q_ASSERT(!selection.isEmpty()); + +// Surface intersectSurface = intersectedSurface(selection); + +// eraseSelection(selection); + +// QPixmap paintedImage(selection.size()); +// paintedImage.fill(Qt::transparent); + +// QPainter painter(&paintedImage); +// painter.translate(-selection.topLeft()); + +// QHashIterator> interTiles(intersectSurface.tiles); +// while(interTiles.hasNext()) +// { +// interTiles.next(); +// const QPixmap& pix = intersectSurface.pixmapAtPos(interTiles.key()); +// const QPoint& pos = intersectSurface.posFromPixmap(interTiles.value()); +// painter.drawPixmap(pos, pix); +// } +// return paintedImage; +//} + +//QPixmap TiledBuffer::copySurfaceAsPixmap(const QRect selection) +//{ +// Q_ASSERT(!selection.isEmpty()); + +// const Surface& intersectSurface = intersectedSurface(selection); + +// QPixmap paintedImage(selection.size()); +// paintedImage.fill(Qt::transparent); + +// QPainter painter(&paintedImage); +// painter.translate(-selection.topLeft()); +// QHashIterator> interTiles(intersectSurface.tiles); +// while(interTiles.hasNext()) +// { +// interTiles.next(); +// const QPixmap& pix = intersectSurface.pixmapAtPos(interTiles.key()); +// const QPoint& pos = intersectSurface.posFromPixmap(interTiles.value()); +// painter.drawPixmap(pos, pix); +// } +// return paintedImage; +//} + +//QList TiledBuffer::tilePositions() +//{ +// return mTiles.keys(); +//} + +//QList TiledBuffer::pixmaps() +//{ +// return mTiles.values(); +//} + +void TiledBuffer::clear() +{ + QHashIterator i(mTiles); + + while (i.hasNext()) { + i.next(); + Tile* tile = i.value(); + if (tile) + { + // Clear the content of the tile + // + tile->clear(); + mTiles.remove(i.key()); + delete tile; + } + } + + emit this->onClearedSurface(this); +} + +//Status TiledBuffer::writeFile(const QString& filename) +//{ +// if (mTiles.values().first()) { +// const QImage& image = surfaceAsImage(); +// bool b = image.save(filename); +// return (b) ? Status::OK : Status::FAIL; +// } +// return Status::FAIL; +//} + + +QPoint TiledBuffer::getTilePos(const QPoint& idx) const +{ + return QPoint(qRound(UNIFORM_TILESIZE*static_cast(idx.x())), qRound(UNIFORM_TILESIZE*static_cast(idx.y()))); +} + +QPoint TiledBuffer::getTileIndex(const QPoint& pos) const +{ + return QPoint(qRound(static_cast(pos.x())/UNIFORM_TILESIZE), qRound(static_cast(pos.y())/UNIFORM_TILESIZE)); +} + +QPointF TiledBuffer::getTileFIndex(const QPoint& pos) const +{ + return QPointF(static_cast(pos.x())/UNIFORM_TILESIZE, static_cast(pos.y())/UNIFORM_TILESIZE); +} + +QRect TiledBuffer::getRectForPoint(const QPoint& point, const QSize size) const +{ + return QRect(point.x(), point.y(), size.width(), size.height()); +} + +QRect TiledBuffer::getRectForPoint(const QPoint& point) const +{ + return QRect(point.x(), point.y(), UNIFORM_TILESIZE, UNIFORM_TILESIZE); +} diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.h b/core_lib/src/graphics/bitmap/tiledbuffer.h new file mode 100644 index 0000000000..04a1bf06cd --- /dev/null +++ b/core_lib/src/graphics/bitmap/tiledbuffer.h @@ -0,0 +1,180 @@ +#ifndef TILEDBUFFER_H +#define TILEDBUFFER_H + +#include +#include +#include + +#include + +#include "blitrect.h" +#include "pencilerror.h" +#include "tile.h" + +class TiledBuffer: public QObject +{ + Q_OBJECT +public: + TiledBuffer(QObject* parent = nullptr); + TiledBuffer(const TiledBuffer& pieces); + TiledBuffer(const QRect bounds); + ~TiledBuffer(); + +// void refreshSurface(); + +// QImage surfaceAsImage(); + + Status writeFile(const QString& filename); + + void addTileToSurface(const QPixmap& pixmap, const QPoint& pos); +// void appendBitmapSurface(const Surface& surface); + + /** + * @brief paintSurfaceUsing + * Paint the surface using a given pixmap and position as input + * Currently useful for selections but might have other uses. + * @param inPixmap + * @param newPos + */ + void paintSurfaceUsing(const QPixmap& inPixmap, const QPoint& newPos); + + /** + * @brief drawRectOnSurface + * For test and debug currently + * @param rect + * @param color + */ + void drawRect(QRect rect, QColor color); + + /** + * @brief createNewSurfaceFromImage + * Creates a new surface from an input image or path + * Useful if you need to import a new image or load a project + * @param image + * @param topLeft + */ + void createNewSurfaceFromImage(const QImage& image, const QPoint& topLeft); + void createNewSurfaceFromImage(const QString& path, const QPoint& topLeft); + + bool isTransparent(QImage image); + + /** + * @brief extendBoundaries + * Extend boundaries of the surface if the input rectangle lies outside. + * @param rect + */ + void extendBoundaries(const QRect &rect); + + /** + * @brief cutSurfaceAsPixmap + * Retrieves a pixmap from the surface you've selected and erases what's underneath + * @param selection + * @return a pixmap with the given paint within the selection + */ + QPixmap cutSurfaceAsPixmap(const QRect selection); + + /** + * @brief copySurfaceAsPixmap + * Retrives a pixmap from the surface you've selected and keeps the surface intact + * @param selection + * @return a pixmap with the given paint within the selection + */ + QPixmap copySurfaceAsPixmap(const QRect selection); + + /** + * @brief eraseSelection + * Removes paint within a given selection + * Useful for selections. + * @param selection + */ + void eraseSelection(const QRect selection); + void eraseSelection(const QPoint pos, QPixmap& pixmap, const QRect selection); + void fillSelection(const QPoint &pos, QPixmap &pixmap, QColor color, const QRect selection); + void clear(); + + QList pixmaps(); + QList tilePositions(); + +// inline const QRect bounds() { return mSurface.bounds; } + + QImage cachedSurfaceImage() { return mCachedSurface; } + + void renderSurfaceImage(); + +// Surface surface() { return mSurface; } +// const Surface readOnlySurface() const {return mSurface; } + + bool isValid() { return !mTiles.isEmpty(); } + + void drawBrush(const QPointF& point, int brushWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing); + void drawPath(QPainterPath path, QPen pen, QBrush brush, + QPainter::CompositionMode cm, bool antialiasing); + + QHash tiles() const { return mTiles; } + + const QRect& bounds() const { return mTileBounds; } + + +signals: + void onUpdateTile(TiledBuffer *tiledBuffer, Tile* tile); + void onNewTile(TiledBuffer *tiledBuffer, Tile* tile); + void onClearTile(TiledBuffer *tiledBuffer, Tile* tile); + void onClearedSurface(TiledBuffer *tiledBuffer); + +private: + + Tile* getTileFromIndex(const QPoint& index); + + inline QPoint getTilePos(const QPoint& idx) const; + inline QPoint getTileIndex(const QPoint& pos) const; + inline QPointF getTileFIndex(const QPoint& pos) const; + + /** + * @brief getRectForPoint + * Returns a rectangle with a specified size for the given point + * @param point + * @param size + * @return QRect + */ + QRect getRectForPoint(const QPoint& point, const QSize size) const; + + /** + * @brief getRectForPoint + * Returns a rectnagle with the size of TILESIZE (64,64) + * @param point + * @return QRect + */ + QRect getRectForPoint(const QPoint& point) const; + + /** + * @brief touchedTiles + * Will find and return points within and surrounding the selection + * @param QRect rect + * @return list of touched points + */ + QList touchedTiles(const QRect& rect); + +// /** +// * @brief BitmapSurface::intersectedSurface +// * Returns a Surface containing the tiles that intersected the region +// * @param rect +// * @return Surface +// */ +// Surface intersectedSurface(const QRect rect); + + QList scanForSurroundingTiles(const QRect& rect); + QList scanForTilesAtRect(const QRect& rect); + + const int UNIFORM_TILESIZE = 64; + + BlitRect mTileBounds; + + QHash mTiles; + +// Surface mSurface; + QImage mCachedSurface; + + bool mImageCacheValid = false; +}; + +#endif // TILEDBUFFER_H diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index f7de23c00f..4eedab03ea 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -75,6 +75,9 @@ bool ScribbleArea::init() connect(mEditor->select(), &SelectionManager::selectionChanged, this, &ScribbleArea::onSelectionChanged); connect(mEditor->select(), &SelectionManager::needDeleteSelection, this, &ScribbleArea::deleteSelection); + connect(&mTiledBuffer, &TiledBuffer::onUpdateTile, this, &ScribbleArea::updateTile); + connect(&mTiledBuffer, &TiledBuffer::onNewTile, this, &ScribbleArea::updateTile); + mDoubleClickTimer->setInterval(50); mMouseFilterTimer->setInterval(50); @@ -186,6 +189,12 @@ void ScribbleArea::setEffect(SETTING e, bool isOn) /************************************************************************************/ // update methods +void ScribbleArea::updateTile(TiledBuffer* tiledBuffer, Tile* tile) +{ + const QRectF& mappedRect = mEditor->view()->getView().mapRect(QRectF(tile->pos(), tile->boundingRect().size())); + update(mappedRect.toRect()); +} + void ScribbleArea::updateCurrentFrame() { updateFrame(mEditor->currentFrame()); @@ -791,10 +800,8 @@ void ScribbleArea::resizeEvent(QResizeEvent* event) { QWidget::resizeEvent(event); mDevicePixelRatio = devicePixelRatioF(); - mCanvas = QPixmap(QSizeF(size() * mDevicePixelRatio).toSize()); - mCanvas.fill(Qt::transparent); - - mEditor->view()->setCanvasSize(size()); + mCanvas = QPixmap(QSizeF(event->size() * mDevicePixelRatio).toSize()); + mEditor->view()->setCanvasSize(event->size()); invalidateCacheForFrame(mEditor->currentFrame()); invalidateCaches(); @@ -845,63 +852,18 @@ void ScribbleArea::paintBitmapBuffer() default: //nothing break; } - targetImage->paste(mBufferImg, cm); + targetImage->paste(&mTiledBuffer, cm); } - QRect rect = mEditor->view()->mapCanvasToScreen(mBufferImg->bounds()).toRect(); + QRect rect = mEditor->view()->mapCanvasToScreen(mTiledBuffer.bounds()).toRect(); drawCanvas(frameNumber, rect.adjusted(-1, -1, 1, 1)); update(rect); - // Update the cache for the last key-frame. - updateFrame(frameNumber); +// // Update the cache for the last key-frame. +// updateFrame(frameNumber); layer->setModified(frameNumber, true); - - mBufferImg->clear(); -} - -void ScribbleArea::paintBitmapBufferRect(const QRect& rect) -{ - if (mEditor->playback()->isPlaying()) - { - Layer* layer = mEditor->layers()->currentLayer(); - Q_ASSERT(layer); - - BitmapImage* targetImage = currentBitmapImage(layer); - - if (targetImage != nullptr) - { - QPainter::CompositionMode cm = QPainter::CompositionMode_SourceOver; - switch (currentTool()->type()) - { - case ERASER: - cm = QPainter::CompositionMode_DestinationOut; - break; - case BRUSH: - case PEN: - case PENCIL: - if (getTool(currentTool()->type())->properties.preserveAlpha) - { - cm = QPainter::CompositionMode_SourceAtop; - } - break; - default: //nothing - break; - } - targetImage->paste(mBufferImg, cm); - } - - // Clear the buffer - mBufferImg->clear(); - - int frameNumber = mEditor->currentFrame(); - layer->setModified(frameNumber, true); - - updateFrame(frameNumber); - - drawCanvas(frameNumber, rect.adjusted(-1, -1, 1, 1)); - update(rect); - } + mTiledBuffer.clear(); } void ScribbleArea::clearBitmapBuffer() @@ -909,6 +871,10 @@ void ScribbleArea::clearBitmapBuffer() mBufferImg->clear(); } +void ScribbleArea::clearDrawingBuffer() { + mTiledBuffer.clear(); +} + void ScribbleArea::drawLine(QPointF P1, QPointF P2, QPen pen, QPainter::CompositionMode cm) { mBufferImg->drawLine(P1, P2, pen, cm, mPrefs->isOn(SETTING::ANTIALIAS)); @@ -1297,12 +1263,11 @@ void ScribbleArea::prepCanvas(int frame, QRect rect) mCanvasPainter.setViewTransform(vm->getView(), vm->getViewInverse()); mCanvasPainter.setTransformedSelection(sm->mySelectionRect().toRect(), sm->selectionTransform()); - mCanvasPainter.setPaintSettings(object, mEditor->layers()->currentLayerIndex(), frame, rect, mBufferImg); + mCanvasPainter.setPaintSettings(object, mEditor->layers()->currentLayerIndex(), frame, rect, mBufferImg, &mTiledBuffer); } void ScribbleArea::drawCanvas(int frame, QRect rect) { - mCanvas.fill(Qt::transparent); mCanvas.setDevicePixelRatio(mDevicePixelRatio); prepCanvas(frame, rect); prepCameraPainter(frame); @@ -1335,8 +1300,7 @@ void ScribbleArea::drawPen(QPointF thePoint, qreal brushWidth, QColor fillColor, { QRectF rectangle(thePoint.x() - 0.5 * brushWidth, thePoint.y() - 0.5 * brushWidth, brushWidth, brushWidth); - mBufferImg->drawEllipse(rectangle, Qt::NoPen, QBrush(fillColor, Qt::SolidPattern), - QPainter::CompositionMode_Source, useAA); + mTiledBuffer.drawBrush(thePoint, brushWidth, Qt::NoPen, QBrush(fillColor, Qt::SolidPattern), QPainter::CompositionMode_SourceOver, true); } void ScribbleArea::drawPencil(QPointF thePoint, qreal brushWidth, qreal fixedBrushFeather, QColor fillColor, qreal opacity) @@ -1348,19 +1312,38 @@ void ScribbleArea::drawBrush(QPointF thePoint, qreal brushWidth, qreal mOffset, { QRectF rectangle(thePoint.x() - 0.5 * brushWidth, thePoint.y() - 0.5 * brushWidth, brushWidth, brushWidth); + QBrush brush; if (usingFeather) { QRadialGradient radialGrad(thePoint, 0.5 * brushWidth); setGaussianGradient(radialGrad, fillColor, opacity, mOffset); - - mBufferImg->drawEllipse(rectangle, Qt::NoPen, radialGrad, - QPainter::CompositionMode_SourceOver, false); + brush = radialGrad; } else { - mBufferImg->drawEllipse(rectangle, Qt::NoPen, QBrush(fillColor, Qt::SolidPattern), - QPainter::CompositionMode_SourceOver, useAA); + brush = QBrush(fillColor, Qt::SolidPattern); + } + mTiledBuffer.drawBrush(thePoint, brushWidth, Qt::NoPen, brush, QPainter::CompositionMode_SourceOver, true); +} + +void ScribbleArea::drawPolyline(QPainterPath path, QPen pen, bool useAA) +{ + mTiledBuffer.clear(); + mTiledBuffer.drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_SourceOver, useAA); + update(); +} + +void ScribbleArea::endStroke() +{ + mTiledBuffer.clear(); + + int frameNumber = mEditor->currentFrame(); + if (mPrefs->isOn(SETTING::PREV_ONION) || mPrefs->isOn(SETTING::NEXT_ONION)) { + invalidateOnionSkinsCacheAround(frameNumber); + invalidateCaches(); } + invalidateCacheForFrame(frameNumber); + updateFrame(frameNumber); } void ScribbleArea::flipSelection(bool flipVertical) @@ -1419,6 +1402,7 @@ void ScribbleArea::blurBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPointF bmiTmpClip.drawRect(srcRect, Qt::NoPen, radialGrad, QPainter::CompositionMode_Source, mPrefs->isOn(SETTING::ANTIALIAS)); bmiSrcClip.bounds().moveTo(trgRect.topLeft().toPoint()); bmiTmpClip.paste(&bmiSrcClip, QPainter::CompositionMode_SourceIn); + mBufferImg->paste(&bmiTmpClip); } @@ -1474,18 +1458,6 @@ void ScribbleArea::liquifyBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPoi mBufferImg->paste(&bmiTmpClip); } -void ScribbleArea::drawPolyline(QPainterPath path, QPen pen, bool useAA) -{ - QRectF updateRect = mEditor->view()->mapCanvasToScreen(path.boundingRect().toRect()).adjusted(-1, -1, 1, 1); - - // Update region outside updateRect - QRectF boundingRect = updateRect.adjusted(-width(), -height(), width(), height()); - mBufferImg->clear(); - mBufferImg->drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_SourceOver, useAA); - update(boundingRect.toRect()); - -} - /************************************************************************************/ // view handling diff --git a/core_lib/src/interface/scribblearea.h b/core_lib/src/interface/scribblearea.h index afa3e4e070..c6d5d55bcd 100644 --- a/core_lib/src/interface/scribblearea.h +++ b/core_lib/src/interface/scribblearea.h @@ -39,6 +39,7 @@ GNU General Public License for more details. #include "strokemanager.h" #include "selectionpainter.h" #include "camerapainter.h" +#include "tiledbuffer.h" class Layer; class Editor; @@ -131,6 +132,8 @@ class ScribbleArea : public QWidget void setModified(int layerNumber, int frameNumber); void setModified(const Layer* layer, int frameNumber); + void endStroke(); + void flipSelection(bool flipVertical); BaseTool* currentTool() const; @@ -164,6 +167,7 @@ public slots: void paletteColorChanged(QColor); void showLayerNotVisibleWarning(); + void updateTile(TiledBuffer* tiledBuffer, Tile* tile); protected: bool event(QEvent *event) override; @@ -189,9 +193,10 @@ public slots: void liquifyBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPointF thePoint_, qreal brushWidth_, qreal offset_, qreal opacity_); void paintBitmapBuffer(); - void paintBitmapBufferRect(const QRect& rect); +// void paintBitmapBufferRect(const QRect& rect); void paintCanvasCursor(QPainter& painter); void clearBitmapBuffer(); + void clearDrawingBuffer(); void refreshBitmap(const QRectF& rect, int rad); void refreshVector(const QRectF& rect, int rad); void setGaussianGradient(QGradient &gradient, QColor color, qreal opacity, qreal offset); @@ -207,6 +212,7 @@ public slots: void handleDrawingOnEmptyFrame(); BitmapImage* mBufferImg = nullptr; // used to pre-draw vector modifications + TiledBuffer mTiledBuffer; QPixmap mCursorImg; QPixmap mTransCursImg; diff --git a/core_lib/src/tool/brushtool.cpp b/core_lib/src/tool/brushtool.cpp index 6b51ffd15c..5accaa38d0 100644 --- a/core_lib/src/tool/brushtool.cpp +++ b/core_lib/src/tool/brushtool.cpp @@ -257,7 +257,6 @@ void BrushTool::drawStroke() int rad = qRound(brushWidth / 2 + 2); - mScribbleArea->paintBitmapBufferRect(rect); mScribbleArea->refreshBitmap(rect, rad); // Line visualizer diff --git a/core_lib/src/tool/erasertool.cpp b/core_lib/src/tool/erasertool.cpp index b2d1744a34..99902330a5 100644 --- a/core_lib/src/tool/erasertool.cpp +++ b/core_lib/src/tool/erasertool.cpp @@ -207,7 +207,7 @@ void EraserTool::paintAt(QPointF point) int rad = qRound(brushWidth / 2 + 2); //continuously update buffer to update stroke behind grid. - mScribbleArea->paintBitmapBufferRect(rect); +// mScribbleArea->paintBitmapBufferRect(rect); mScribbleArea->refreshBitmap(rect, rad); } @@ -263,7 +263,7 @@ void EraserTool::drawStroke() int rad = qRound(brushWidth / 2 + 2); - mScribbleArea->paintBitmapBufferRect(rect); +// mScribbleArea->paintBitmapBufferRect(rect); mScribbleArea->refreshBitmap(rect, rad); } else if (layer->type() == Layer::VECTOR) diff --git a/core_lib/src/tool/penciltool.cpp b/core_lib/src/tool/penciltool.cpp index 55a1672740..bee0c5cfdb 100644 --- a/core_lib/src/tool/penciltool.cpp +++ b/core_lib/src/tool/penciltool.cpp @@ -264,7 +264,7 @@ void PencilTool::drawStroke() int rad = qRound(brushWidth) / 2 + 2; - mScribbleArea->paintBitmapBufferRect(rect); +// mScribbleArea->paintBitmapBufferRect(rect); mScribbleArea->refreshBitmap(rect, rad); } else if (layer->type() == Layer::VECTOR) diff --git a/core_lib/src/tool/pentool.cpp b/core_lib/src/tool/pentool.cpp index 5d18117bd1..35c334d3e5 100644 --- a/core_lib/src/tool/pentool.cpp +++ b/core_lib/src/tool/pentool.cpp @@ -232,7 +232,7 @@ void PenTool::drawStroke() int rad = qRound(brushWidth) / 2 + 2; - mScribbleArea->paintBitmapBufferRect(rect); +// mScribbleArea->paintBitmapBufferRect(rect); mScribbleArea->refreshBitmap(rect, rad); } else if (layer->type() == Layer::VECTOR) diff --git a/core_lib/src/tool/polylinetool.cpp b/core_lib/src/tool/polylinetool.cpp index dbe9297a92..26e49490a4 100644 --- a/core_lib/src/tool/polylinetool.cpp +++ b/core_lib/src/tool/polylinetool.cpp @@ -125,6 +125,7 @@ void PolylineTool::pointerPressEvent(PointerEvent* event) { if (layer->type() == Layer::BITMAP || layer->type() == Layer::VECTOR) { + mEditor->backup(typeName()); mScribbleArea->handleDrawingOnEmptyFrame(); if (layer->type() == Layer::VECTOR) @@ -136,9 +137,12 @@ void PolylineTool::pointerPressEvent(PointerEvent* event) { mScribbleArea->toggleThinLines(); } + } else if (layer->type() == Layer::BITMAP) { + mScribbleArea->paintBitmapBuffer(); } mPoints << getCurrentPoint(); emit isActiveChanged(POLYLINE, true); + } } } @@ -160,8 +164,6 @@ void PolylineTool::pointerDoubleClickEvent(PointerEvent*) // include the current point before ending the line. mPoints << getCurrentPoint(); - mEditor->backup(typeName()); - endPolyline(mPoints); clearToolData(); } @@ -245,7 +247,7 @@ void PolylineTool::drawPolyline(QList points, QPointF endPoint) void PolylineTool::cancelPolyline() { // Clear the in-progress polyline from the bitmap buffer. - mScribbleArea->clearBitmapBuffer(); + mScribbleArea->clearDrawingBuffer(); mScribbleArea->updateCurrentFrame(); } @@ -276,11 +278,7 @@ void PolylineTool::endPolyline(QList points) if (layer->type() == Layer::BITMAP) { drawPolyline(points, points.last()); - BitmapImage *bitmapImage = static_cast(layer)->getLastBitmapImageAtFrame(mEditor->currentFrame(), 0); - if (bitmapImage == nullptr) { return; } // Can happen if the first frame is deleted while drawing - bitmapImage->paste(mScribbleArea->mBufferImg); } - - mScribbleArea->clearBitmapBuffer(); + mScribbleArea->endStroke(); mEditor->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame()); } diff --git a/core_lib/src/tool/smudgetool.cpp b/core_lib/src/tool/smudgetool.cpp index b3281225ba..18fbc6ff16 100644 --- a/core_lib/src/tool/smudgetool.cpp +++ b/core_lib/src/tool/smudgetool.cpp @@ -324,7 +324,7 @@ void SmudgeTool::drawStroke() mLastBrushPoint = targetPoint; } sourcePoint = targetPoint; - mScribbleArea->paintBitmapBufferRect(rect); +// mScribbleArea->paintBitmapBufferRect(rect); mScribbleArea->refreshBitmap(rect, rad); } } @@ -353,7 +353,7 @@ void SmudgeTool::drawStroke() mLastBrushPoint = targetPoint; } sourcePoint = targetPoint; - mScribbleArea->paintBitmapBufferRect(rect); +// mScribbleArea->paintBitmapBufferRect(rect); mScribbleArea->refreshBitmap(rect, rad); } } diff --git a/core_lib/src/tool/stroketool.cpp b/core_lib/src/tool/stroketool.cpp index cf1b4b6e7c..6b8e8c1122 100644 --- a/core_lib/src/tool/stroketool.cpp +++ b/core_lib/src/tool/stroketool.cpp @@ -101,6 +101,7 @@ void StrokeTool::endStroke() enableCoalescing(); mEditor->setModified(mEditor->currentLayerIndex(), mEditor->currentFrame()); + mScribbleArea->endStroke(); } void StrokeTool::drawStroke() diff --git a/core_lib/src/util/blitrect.cpp b/core_lib/src/util/blitrect.cpp index e578bda31f..9846927ee1 100644 --- a/core_lib/src/util/blitrect.cpp +++ b/core_lib/src/util/blitrect.cpp @@ -49,3 +49,20 @@ void BlitRect::extend(const QPoint p) if (bottom() < p.y()) { setBottom(p.y()); } } } + +void BlitRect::extend(const QPoint& p, const QSize& size) +{ + if (mInitialized == false) + { + setBottomLeft(p); + setTopRight(p); + mInitialized = true; + } + else + { + if (left() > p.x()) { setLeft(p.x()); } + if (top() > p.y()) { setTop(p.y()); } + if (right() - size.width() < p.x()) { setRight(p.x() + size.width()); } + if (bottom() - size.height() < p.y()) { setBottom(p.y() + size.height()); } + } +} diff --git a/core_lib/src/util/blitrect.h b/core_lib/src/util/blitrect.h index b0c10cfb35..eeac4c7872 100644 --- a/core_lib/src/util/blitrect.h +++ b/core_lib/src/util/blitrect.h @@ -28,6 +28,7 @@ class BlitRect : public QRect explicit BlitRect(const QPoint p); explicit BlitRect(const QRect rect); void extend(const QPoint p); + void extend(const QPoint& p, const QSize& size); private: bool mInitialized = false; From b8f8823addeba06fb7785d250ccc58519c8bcf24 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Sun, 23 Apr 2023 12:18:50 +0200 Subject: [PATCH 03/49] 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. --- core_lib/src/canvaspainter.cpp | 453 +++++++++---------- core_lib/src/canvaspainter.h | 42 +- core_lib/src/graphics/vector/vectorimage.cpp | 20 - core_lib/src/graphics/vector/vectorimage.h | 3 +- core_lib/src/interface/scribblearea.cpp | 24 +- core_lib/src/interface/scribblearea.h | 2 +- core_lib/src/tool/buckettool.cpp | 4 +- 7 files changed, 266 insertions(+), 282 deletions(-) diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index 47cbef0c0b..c683f545be 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -23,7 +23,6 @@ GNU General Public License for more details. #include "layerbitmap.h" #include "layervector.h" #include "bitmapimage.h" -#include "layercamera.h" #include "vectorimage.h" #include "painterutils.h" @@ -39,7 +38,21 @@ CanvasPainter::~CanvasPainter() void CanvasPainter::setCanvas(QPixmap* canvas) { Q_ASSERT(canvas); - mCanvas = canvas; + qDebug() << canvas->size(); + if (mCanvas == nullptr || mCanvasSize != canvas->size()) { + + mCanvas = canvas; + mPostLayersPixmap = QPixmap(mCanvas->size()); + mPreLayersPixmap = 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); + mCanvasSize = canvas->size(); + } } void CanvasPainter::setViewTransform(const QTransform view, const QTransform viewInverse) @@ -71,101 +84,98 @@ void CanvasPainter::ignoreTransformedSelection() mRenderTransform = false; } -void CanvasPainter::paintCached() +void CanvasPainter::paintCached(const QRect& blitRect) { - QPixmap tempPixmap(mCanvas->size()); - tempPixmap.fill(Qt::transparent); - mCanvas->fill(Qt::transparent); - QPainter tempPainter; - QPainter painter; - initializePainter(tempPainter, tempPixmap); - initializePainter(painter, *mCanvas); - - if (!mPreLayersCache) - { - renderPreLayers(painter); - mPreLayersCache.reset(new QPixmap(*mCanvas)); - } - else - { - painter.setWorldMatrixEnabled(false); - painter.drawPixmap(0, 0, *(mPreLayersCache.get())); - painter.setWorldMatrixEnabled(true); - } + QPainter preLayerPainter; + QPainter mainPainter; + QPainter postLayerPainter; - renderCurLayer(painter); + initializePainter(mainPainter, *mCanvas, blitRect); - if (!mPostLayersCache) + if (!mPreLayersPixmapCacheValid) { - renderPostLayers(tempPainter); - mPostLayersCache.reset(new QPixmap(tempPixmap)); - painter.setWorldMatrixEnabled(false); - painter.drawPixmap(0, 0, tempPixmap); - painter.setWorldMatrixEnabled(true); + initializePainter(preLayerPainter, mPreLayersPixmap, blitRect); + renderPreLayers(preLayerPainter, blitRect); + preLayerPainter.end(); + mPreLayersPixmapCacheValid = true; } - else + + mainPainter.setWorldMatrixEnabled(false); + mainPainter.drawPixmap(blitRect, mPreLayersPixmap, blitRect); + mainPainter.setWorldMatrixEnabled(true); + + renderCurrentFrame(mainPainter, blitRect); + + if (!mPostLayersPixmapCacheValid) { - painter.setWorldMatrixEnabled(false); - painter.drawPixmap(0, 0, *(mPostLayersCache.get())); - painter.setWorldMatrixEnabled(true); + renderPostLayers(postLayerPainter, blitRect); + mPostLayersPixmapCacheValid = true; } -} + mainPainter.setWorldMatrixEnabled(false); + mainPainter.drawPixmap(blitRect, mPostLayersPixmap, blitRect); + mainPainter.setWorldMatrixEnabled(true); +} void CanvasPainter::resetLayerCache() { - mPreLayersCache.reset(); - mPostLayersCache.reset(); + mPreLayersPixmapCacheValid = false; + mPreLayersPixmapCacheValid = false; } -void CanvasPainter::initializePainter(QPainter& painter, QPixmap& pixmap) +void CanvasPainter::initializePainter(QPainter& painter, QPaintDevice& device, const QRect& blitRect) { - painter.begin(&pixmap); + painter.begin(&device); + + // 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); + painter.fillRect(blitRect, Qt::transparent); + + // Surface has been cleared and is ready to be painted on + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); painter.setWorldMatrixEnabled(true); painter.setWorldTransform(mViewTransform); } -void CanvasPainter::renderPreLayers(QPixmap* pixmap) -{ - QPainter painter; - initializePainter(painter, *pixmap); - renderPreLayers(painter); -} - -void CanvasPainter::renderPreLayers(QPainter& painter) +void CanvasPainter::renderPreLayers(QPainter& painter, const QRect& blitRect) { if (mOptions.eLayerVisibility != LayerVisibility::CURRENTONLY || mObject->getLayer(mCurrentLayerIndex)->type() == Layer::CAMERA) { - paintCurrentFrame(painter, 0, mCurrentLayerIndex - 1); + paintCurrentFrame(painter, blitRect, 0, mCurrentLayerIndex - 1); } - paintOnionSkin(painter); + paintOnionSkin(painter, blitRect); painter.setOpacity(1.0); } -void CanvasPainter::renderCurLayer(QPixmap* pixmap) +void CanvasPainter::renderCurrentFrame(QPainter& painter, const QRect& blitRect) { - QPainter painter; - initializePainter(painter, *pixmap); - renderCurLayer(painter); -} + int currentLayerIndex = mCurrentLayerIndex; + Layer* layer = mObject->getLayer(currentLayerIndex); -void CanvasPainter::renderCurLayer(QPainter& painter) -{ - paintCurrentFrame(painter, mCurrentLayerIndex, mCurrentLayerIndex); -} + painter.setOpacity(1.0); -void CanvasPainter::renderPostLayers(QPixmap* pixmap) -{ - QPainter painter; - initializePainter(painter, *pixmap); - renderPostLayers(painter); + bool isCameraLayer = layer->type() == Layer::CAMERA; + if (layer->visible() == false) + return; + + if (mOptions.eLayerVisibility == LayerVisibility::RELATED && !isCameraLayer) { + painter.setOpacity(calculateRelativeOpacityForLayer(currentLayerIndex, currentLayerIndex, mOptions.fLayerVisibilityThreshold)); + } + + switch (layer->type()) + { + case Layer::BITMAP: { paintCurrentBitmapFrame(painter, blitRect, layer, true); break; } + case Layer::VECTOR: { paintCurrentVectorFrame(painter, blitRect, layer, true); break; } + default: break; + } } -void CanvasPainter::renderPostLayers(QPainter& painter) +void CanvasPainter::renderPostLayers(QPainter& painter, const QRect& blitRect) { if (mOptions.eLayerVisibility != LayerVisibility::CURRENTONLY || mObject->getLayer(mCurrentLayerIndex)->type() == Layer::CAMERA) { - paintCurrentFrame(painter, mCurrentLayerIndex + 1, mObject->getLayerCount() - 1); + paintCurrentFrame(painter, blitRect, mCurrentLayerIndex + 1, mObject->getLayerCount() - 1); } } @@ -181,22 +191,37 @@ void CanvasPainter::setPaintSettings(const Object* object, int currentLayer, int mBuffer = buffer; } -void CanvasPainter::paint() +void CanvasPainter::paint(const QRect& blitRect) { - QPainter painter; - initializePainter(painter, *mCanvas); + QPainter preLayerPainter; + QPainter mainPainter; + QPainter postLayerPainter; - renderPreLayers(painter); - renderCurLayer(painter); - renderPostLayers(painter); -} + initializePainter(mainPainter, *mCanvas, blitRect); -void CanvasPainter::paintBackground() -{ - mCanvas->fill(Qt::transparent); + initializePainter(preLayerPainter, mPreLayersPixmap, blitRect); + renderPreLayers(preLayerPainter, blitRect); + preLayerPainter.end(); + + mainPainter.setWorldMatrixEnabled(false); + mainPainter.drawPixmap(blitRect, mPreLayersPixmap, blitRect); + mainPainter.setWorldMatrixEnabled(true); + + renderCurrentFrame(mainPainter, blitRect); + + initializePainter(postLayerPainter, mPostLayersPixmap, blitRect); + renderPostLayers(postLayerPainter, blitRect); + postLayerPainter.end(); + + mainPainter.setWorldMatrixEnabled(false); + mainPainter.drawPixmap(blitRect, mPostLayersPixmap, blitRect); + mainPainter.setWorldMatrixEnabled(true); + + mPreLayersPixmapCacheValid = true; + mPostLayersPixmapCacheValid = true; } -void CanvasPainter::paintOnionSkin(QPainter& painter) +void CanvasPainter::paintOnionSkin(QPainter& painter, const QRect& blitRect) { Layer* layer = mObject->getLayer(mCurrentLayerIndex); @@ -204,113 +229,148 @@ void CanvasPainter::paintOnionSkin(QPainter& painter) if (state == OnionSkinPaintState::PREV) { switch (layer->type()) { - case Layer::BITMAP: { paintBitmapFrame(painter, layer, onionFrameNumber, mOnionSkinPainterOptions.colorizePrevFrames, false, false); break; } - case Layer::VECTOR: { paintVectorFrame(painter, layer, onionFrameNumber, mOnionSkinPainterOptions.colorizePrevFrames, false, false); break; } + case Layer::BITMAP: { paintBitmapOnionSkinFrame(painter, blitRect, layer, onionFrameNumber, mOnionSkinPainterOptions.colorizePrevFrames); break; } + case Layer::VECTOR: { paintVectorOnionSkinFrame(painter, blitRect, layer, onionFrameNumber, mOnionSkinPainterOptions.colorizePrevFrames); break; } default: break; } } if (state == OnionSkinPaintState::NEXT) { switch (layer->type()) { - case Layer::BITMAP: { paintBitmapFrame(painter, layer, onionFrameNumber, mOnionSkinPainterOptions.colorizeNextFrames, false, false); break; } - case Layer::VECTOR: { paintVectorFrame(painter, layer, onionFrameNumber, mOnionSkinPainterOptions.colorizeNextFrames, false, false); break; } + case Layer::BITMAP: { paintBitmapOnionSkinFrame(painter, blitRect, layer, onionFrameNumber, mOnionSkinPainterOptions.colorizeNextFrames); break; } + case Layer::VECTOR: { paintVectorOnionSkinFrame(painter, blitRect, layer, onionFrameNumber, mOnionSkinPainterOptions.colorizeNextFrames); break; } default: break; } } }); } -void CanvasPainter::paintBitmapFrame(QPainter& painter, - Layer* layer, - int nFrame, - bool colorize, - bool useLastKeyFrame, - bool isCurrentFrame) +void CanvasPainter::paintBitmapOnionSkinFrame(QPainter& painter, const QRect& blitRect, Layer* layer, int nFrame, bool colorize) { -#ifdef _DEBUG - LayerBitmap* bitmapLayer = dynamic_cast(layer); - Q_ASSERT(bitmapLayer); -#else LayerBitmap* bitmapLayer = static_cast(layer); -#endif - CANVASPAINTER_LOG(" Paint Bitmap Frame = %d, UseLastKeyFrame = %d", nFrame, useLastKeyFrame); - BitmapImage* paintedImage = nullptr; - if (useLastKeyFrame) - { - paintedImage = bitmapLayer->getLastBitmapImageAtFrame(nFrame, 0); - CANVASPAINTER_LOG(" Actual frame = %d", paintedImage->pos()); - } - else - { - paintedImage = bitmapLayer->getBitmapImageAtFrame(nFrame); - } + BitmapImage* bitmapImage = bitmapLayer->getLastBitmapImageAtFrame(nFrame, 0); - if (paintedImage == nullptr) { return; } - paintedImage->loadFile(); // Critical! force the BitmapImage to load the image - CANVASPAINTER_LOG(" Paint Image Size: %dx%d", paintedImage->image()->width(), paintedImage->image()->height()); + if (bitmapImage == nullptr) { return; } + QPainter onionSkinPainter; + initializePainter(onionSkinPainter, mOnionSkinPixmap, blitRect); - const bool frameIsEmpty = (paintedImage == nullptr || paintedImage->bounds().isEmpty()); - const bool isDrawing = isCurrentFrame && mBuffer && !mBuffer->bounds().isEmpty(); - if (frameIsEmpty && !isDrawing) - { - CANVASPAINTER_LOG(" Early return frame %d, %d", frameIsEmpty, isDrawing); - return; - } + onionSkinPainter.drawImage(bitmapImage->topLeft(), *bitmapImage->image()); + paintOnionSkinFrame(painter, onionSkinPainter, blitRect, nFrame, colorize, bitmapImage->getOpacity()); +} - BitmapImage paintToImage = BitmapImage(paintedImage->bounds(), Qt::transparent); - paintToImage.paste(paintedImage); +void CanvasPainter::paintVectorOnionSkinFrame(QPainter& painter, const QRect& blitRect, Layer* layer, int nFrame, bool colorize) +{ + LayerVector* vectorLayer = static_cast(layer); - painter.setOpacity(paintedImage->getOpacity() - (1.0-painter.opacity())); - if (isCurrentFrame) + CANVASPAINTER_LOG("Paint Onion skin vector, Frame = %d", nFrame); + VectorImage* vectorImage = vectorLayer->getLastVectorImageAtFrame(nFrame, 0); + if (vectorImage == nullptr) { - paintToImage.paste(mBuffer, mOptions.cmBufferBlendMode); + return; } + QPainter onionSkinPainter; + initializePainter(onionSkinPainter, mOnionSkinPixmap, blitRect); + + vectorImage->paintImage(onionSkinPainter, mOptions.bOutlines, mOptions.bThinLines, mOptions.bAntiAlias); + paintOnionSkinFrame(painter, onionSkinPainter, blitRect, nFrame, colorize, vectorImage->getOpacity()); +} +void CanvasPainter::paintOnionSkinFrame(QPainter& painter, QPainter& onionSkinPainter, const QRect& blitRect, int nFrame, bool colorize, qreal frameOpacity) +{ + // Don't transform the image here as we used the viewTransform in the image output + painter.setWorldMatrixEnabled(false); + // Remember to adjust overall opacity based on opacity value from image + painter.setOpacity(frameOpacity - (1.0-painter.opacity())); if (colorize) { - QBrush colorBrush = QBrush(Qt::transparent); //no color for the current frame + QColor colorBrush = Qt::transparent; //no color for the current frame if (nFrame < mFrameNumber) { - colorBrush = QBrush(Qt::red); + colorBrush = Qt::red; } else if (nFrame > mFrameNumber) { - colorBrush = QBrush(Qt::blue); + colorBrush = Qt::blue; } + onionSkinPainter.setWorldMatrixEnabled(false); - paintToImage.drawRect(paintedImage->bounds(), - Qt::NoPen, - colorBrush, - QPainter::CompositionMode_SourceIn, - false); + onionSkinPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); + onionSkinPainter.setBrush(colorBrush); + onionSkinPainter.drawRect(painter.viewport()); } + painter.drawPixmap(blitRect, mOnionSkinPixmap, blitRect); +} - // If the current frame on the current layer has a transformation, we apply it. - bool shouldPaintTransform = mRenderTransform && nFrame == mFrameNumber && layer == mObject->getLayer(mCurrentLayerIndex); - if (shouldPaintTransform) - { - paintToImage.clear(mSelection); +void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blitRect, Layer* layer, bool isCurrentLayer) +{ + LayerBitmap* bitmapLayer = static_cast(layer); + BitmapImage* paintedImage = bitmapLayer->getLastBitmapImageAtFrame(mFrameNumber);; + + if (paintedImage == nullptr) { return; } + paintedImage->loadFile(); // Critical! force the BitmapImage to load the image + + const bool isDrawing = mBuffer && !mBuffer->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) { + 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); + } } - painter.setWorldMatrixEnabled(true); - prescale(&paintToImage); - paintToImage.paintImage(painter, mScaledBitmap, mScaledBitmap.rect(), paintToImage.bounds()); + painter.drawPixmap(blitRect, mCurrentLayerPixmap, blitRect); +} - if (shouldPaintTransform) +void CanvasPainter::paintCurrentVectorFrame(QPainter& painter, const QRect& blitRect, Layer* layer, bool isCurrentLayer) +{ + LayerVector* vectorLayer = static_cast(layer); + VectorImage* vectorImage = vectorLayer->getLastVectorImageAtFrame(mFrameNumber, 0); + if (vectorImage == nullptr) { - paintTransformedSelection(painter); + return; + } + + QPainter currentVectorPainter; + initializePainter(currentVectorPainter, mCurrentLayerPixmap, blitRect); + + const bool isDrawing = mBuffer && !mBuffer->bounds().isEmpty(); + + // 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.setTransform(QTransform()); + currentVectorPainter.drawImage(mBuffer->topLeft(), *mBuffer->image()); + } else if (mRenderTransform) { + vectorImage->setSelectionTransformation(mSelectionTransform); + } } -// static int cc = 0; -// QString path = QString("C:/Temp/pencil2d/canvas-%1-%2-%3.png") -// .arg(cc++, 3, 10, QChar('0')) -// .arg(layer->name()) -// .arg(mFrameNumber); -// Q_ASSERT(mCanvas->save(path)); + // Don't transform the image here as we used the viewTransform in the image output + painter.setWorldMatrixEnabled(false); + painter.setTransform(QTransform()); + + // Remember to adjust opacity based on addition opacity value from image + painter.setOpacity(vectorImage->getOpacity() - (1.0-painter.opacity())); + painter.drawPixmap(blitRect, mCurrentLayerPixmap, blitRect); } + void CanvasPainter::prescale(BitmapImage* bitmapImage) { QImage origImage = bitmapImage->image()->copy(); @@ -333,98 +393,37 @@ void CanvasPainter::prescale(BitmapImage* bitmapImage) } } -void CanvasPainter::paintVectorFrame(QPainter& painter, - Layer* layer, - int nFrame, - bool colorize, - bool useLastKeyFrame, - bool isCurrentFrame) +void CanvasPainter::paintTransformedSelection(QPainter& painter, BitmapImage* bitmapImage, const QRect& selection) const { -#ifdef _DEBUG - LayerVector* vectorLayer = dynamic_cast(layer); - Q_ASSERT(vectorLayer); -#else - LayerVector* vectorLayer = static_cast(layer); -#endif - - CANVASPAINTER_LOG("Paint Onion skin vector, Frame = %d", nFrame); - VectorImage* vectorImage = nullptr; - if (useLastKeyFrame) - { - vectorImage = vectorLayer->getLastVectorImageAtFrame(nFrame, 0); - } - else - { - vectorImage = vectorLayer->getVectorImageAtFrame(nFrame); - } - if (vectorImage == nullptr) - { + // Make sure there is something selected + if (selection.width() == 0 && selection.height() == 0) return; - } - - QImage* strokeImage = new QImage(mCanvas->size(), QImage::Format_ARGB32_Premultiplied); - - if (mRenderTransform) { - vectorImage->setSelectionTransformation(mSelectionTransform); - } - - vectorImage->outputImage(strokeImage, mViewTransform, mOptions.bOutlines, mOptions.bThinLines, mOptions.bAntiAlias); - // Go through a Bitmap image to paint the onion skin colour - BitmapImage rasterizedVectorImage; - rasterizedVectorImage.setImage(strokeImage); - - if (colorize) - { - QBrush colorBrush = QBrush(Qt::transparent); //no color for the current frame - - if (nFrame < mFrameNumber) - { - colorBrush = QBrush(Qt::red); - } - else if (nFrame > mFrameNumber) - { - colorBrush = QBrush(Qt::blue); - } - rasterizedVectorImage.drawRect(strokeImage->rect(), - Qt::NoPen, colorBrush, - QPainter::CompositionMode_SourceIn, false); - } + QPixmap transformedPixmap = QPixmap(mSelection.size()); + transformedPixmap.fill(Qt::transparent); - // Don't transform the image here as we used the viewTransform in the image output - painter.setWorldMatrixEnabled(false); + QPainter imagePainter(&transformedPixmap); + imagePainter.translate(-selection.topLeft()); + imagePainter.drawImage(bitmapImage->topLeft(), *bitmapImage->image()); + imagePainter.end(); - painter.setOpacity(vectorImage->getOpacity() - (1.0-painter.opacity())); - if (isCurrentFrame) - { - // Paste buffer onto image to see stroke in realtime - rasterizedVectorImage.paste(mBuffer, mOptions.cmBufferBlendMode); - } + painter.save(); - // Paint buffer pasted on top of vector image: - // fixes polyline not being rendered properly - rasterizedVectorImage.paintImage(painter); -} + painter.setTransform(mViewTransform); -void CanvasPainter::paintTransformedSelection(QPainter& painter) const -{ - // Make sure there is something selected - if (mSelection.width() == 0 || mSelection.height() == 0) - return; + // Clear the painted area to make it look like the content has been erased + painter.save(); + painter.setCompositionMode(QPainter::CompositionMode_Clear); + painter.fillRect(selection, QColor(255,255,255,255)); + painter.restore(); - Layer* layer = mObject->getLayer(mCurrentLayerIndex); + // Multiply the selection and view matrix to get proper rotation and scale values + // Now the image origin will be topleft + painter.setTransform(mSelectionTransform*mViewTransform); - if (layer->type() == Layer::BITMAP) - { - // Get the transformed image - BitmapImage* bitmapImage = static_cast(layer)->getLastBitmapImageAtFrame(mFrameNumber, 0); - if (bitmapImage == nullptr) { return; }; - BitmapImage transformedImage = bitmapImage->transformed(mSelection, mSelectionTransform, mOptions.bAntiAlias); - - // Paint the transformation output - painter.setWorldMatrixEnabled(true); - transformedImage.paintImage(painter); - } + // Draw the selection image separately and on top + painter.drawPixmap(selection, transformedPixmap); + painter.restore(); } /** Paints layers within the specified range for the current frame. @@ -433,11 +432,10 @@ void CanvasPainter::paintTransformedSelection(QPainter& painter) const * @param startLayer The first layer to paint (inclusive) * @param endLayer The last layer to paint (inclusive) */ -void CanvasPainter::paintCurrentFrame(QPainter& painter, int startLayer, int endLayer) +void CanvasPainter::paintCurrentFrame(QPainter& painter, const QRect& blitRect, int startLayer, int endLayer) { painter.setOpacity(1.0); - bool isCameraLayer = mObject->getLayer(mCurrentLayerIndex)->type() == Layer::CAMERA; for (int i = startLayer; i <= endLayer; ++i) @@ -451,12 +449,13 @@ void CanvasPainter::paintCurrentFrame(QPainter& painter, int startLayer, int end { painter.setOpacity(calculateRelativeOpacityForLayer(mCurrentLayerIndex, i, mOptions.fLayerVisibilityThreshold)); } + bool isCurrentLayer = mCurrentLayerIndex == i; CANVASPAINTER_LOG(" Render Layer[%d] %s", i, layer->name()); switch (layer->type()) { - case Layer::BITMAP: { paintBitmapFrame(painter, layer, mFrameNumber, false, true, i == mCurrentLayerIndex); break; } - case Layer::VECTOR: { paintVectorFrame(painter, layer, mFrameNumber, false, true, i == mCurrentLayerIndex); break; } + case Layer::BITMAP: { paintCurrentBitmapFrame(painter, blitRect, layer, isCurrentLayer); break; } + case Layer::VECTOR: { paintCurrentVectorFrame(painter, blitRect, layer, isCurrentLayer); break; } default: break; } } diff --git a/core_lib/src/canvaspainter.h b/core_lib/src/canvaspainter.h index d31ef1210b..de88308ae2 100644 --- a/core_lib/src/canvaspainter.h +++ b/core_lib/src/canvaspainter.h @@ -62,8 +62,8 @@ class CanvasPainter void ignoreTransformedSelection(); void setPaintSettings(const Object* object, int currentLayer, int frame, QRect rect, BitmapImage* buffer); - void paint(); - void paintCached(); + void paint(const QRect& blitRect); + void paintCached(const QRect& blitRect); void resetLayerCache(); private: @@ -73,34 +73,36 @@ class CanvasPainter * Enriches the painter with a context and sets it's initial matrix. * @param painter The in/out painter * @param pixmap The paint device ie. a pixmap + * @param blitRect The rect where the blitting will occur */ - void initializePainter(QPainter& painter, QPixmap& pixmap); - - void renderPreLayers(QPainter& painter); - void renderCurLayer(QPainter& painter); - void renderPostLayers(QPainter& painter); + void initializePainter(QPainter& painter, QPaintDevice& device, const QRect& blitRect); void paintBackground(); - void paintOnionSkin(QPainter& painter); - - void renderPostLayers(QPixmap* pixmap); - void renderCurLayer(QPixmap* pixmap); - void renderPreLayers(QPixmap* pixmap); + void paintOnionSkin(QPainter& painter, const QRect& blitRect); - void paintCurrentFrame(QPainter& painter, int startLayer, int endLayer); + void renderPostLayers(QPainter& painter, const QRect& blitRect); + void renderCurrentFrame(QPainter& painter, const QRect& blitRect); + void renderOnionSkin(QPainter& painter, const QRect& blitRect); + void renderPreLayers(QPainter& painter, const QRect& blitRect); - void paintBitmapFrame(QPainter&, Layer* layer, int nFrame, bool colorize, bool useLastKeyFrame, bool isCurrentFrame); - void paintVectorFrame(QPainter&, Layer* layer, int nFrame, bool colorize, bool useLastKeyFrame, bool isCurrentFrame); + void paintCurrentFrame(QPainter& painter, const QRect& blitRect, int startLayer, int endLayer); - void paintTransformedSelection(QPainter& painter) const; + void paintTransformedSelection(QPainter& painter, BitmapImage* bitmapImage, const QRect& selection) const; void prescale(BitmapImage* bitmapImage); private: + 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 paintCurrentBitmapFrame(QPainter& painter, const QRect& blitRect, Layer* layer, bool isCurrentLayer); + void paintCurrentVectorFrame(QPainter& painter, const QRect& blitRect, Layer* layer, bool isCurrentLayer); CanvasPainterOptions mOptions; const Object* mObject = nullptr; QPixmap* mCanvas = nullptr; + QSize mCanvasSize; QTransform mViewTransform; QTransform mViewInverse; @@ -116,7 +118,13 @@ class CanvasPainter QTransform mSelectionTransform; // Caches specifically for when drawing on the canvas - std::unique_ptr mPreLayersCache, mPostLayersCache; + QPixmap mPostLayersPixmap; + QPixmap mPreLayersPixmap; + QPixmap mCurrentLayerPixmap; + QPixmap mOnionSkinPixmap; + bool mPreLayersPixmapCacheValid = false; + bool mPostLayersPixmapCacheValid = false; + OnionSkinSubPainter mOnionSkinSubPainter; OnionSkinPainterOptions mOnionSkinPainterOptions; diff --git a/core_lib/src/graphics/vector/vectorimage.cpp b/core_lib/src/graphics/vector/vectorimage.cpp index 3735d39db5..136d211fbe 100644 --- a/core_lib/src/graphics/vector/vectorimage.cpp +++ b/core_lib/src/graphics/vector/vectorimage.cpp @@ -1235,26 +1235,6 @@ void VectorImage::paintImage(QPainter& painter, } } -/** - * @brief VectorImage::outputImage - * @param image: QImage* - * @param myView: QTransform - * @param simplified: bool - * @param showThinCurves: bool - * @param antialiasing: bool - */ -void VectorImage::outputImage(QImage* image, - QTransform myView, - bool simplified, - bool showThinCurves, - bool antialiasing) -{ - image->fill(qRgba(0, 0, 0, 0)); - QPainter painter(image); - painter.setTransform(myView); - paintImage(painter, simplified, showThinCurves, antialiasing); -} - /** * @brief VectorImage::clear */ diff --git a/core_lib/src/graphics/vector/vectorimage.h b/core_lib/src/graphics/vector/vectorimage.h index 0a31cccd98..69d8ca0bcf 100644 --- a/core_lib/src/graphics/vector/vectorimage.h +++ b/core_lib/src/graphics/vector/vectorimage.h @@ -27,7 +27,7 @@ GNU General Public License for more details. class Object; class QPainter; class QImage; - +class QPixmap; class VectorImage : public KeyFrame { @@ -93,7 +93,6 @@ class VectorImage : public KeyFrame void moveColor(int start, int end); void paintImage(QPainter& painter, bool simplified, bool showThinCurves, bool antialiasing); - void outputImage(QImage* image, QTransform myView, bool simplified, bool showThinCurves, bool antialiasing); // uses paintImage void clear(); void clean(); diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index 8b65f1f45f..2299d111f8 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -174,13 +174,13 @@ void ScribbleArea::updateToolCursor() void ScribbleArea::setCurveSmoothing(int newSmoothingLevel) { mCurveSmoothingLevel = newSmoothingLevel / 20.0; - invalidateCaches(); + invalidatePainterCaches(); } void ScribbleArea::setEffect(SETTING e, bool isOn) { mPrefs->set(e, isOn); - invalidateCaches(); + invalidatePainterCaches(); } /************************************************************************************/ @@ -253,7 +253,7 @@ void ScribbleArea::invalidateAllCache() { QPixmapCache::clear(); mPixmapCacheKeys.clear(); - invalidateCaches(); + invalidatePainterCaches(); mEditor->layers()->currentLayer()->clearDirtyFrames(); update(); @@ -270,7 +270,7 @@ void ScribbleArea::invalidateCacheForFrame(int frameNumber) } } -void ScribbleArea::invalidateCaches() +void ScribbleArea::invalidatePainterCaches() { mCameraPainter.resetCache(); mCanvasPainter.resetLayerCache(); @@ -304,7 +304,7 @@ void ScribbleArea::onPlayStateChanged() int currentFrame = mEditor->currentFrame(); if (mPrefs->isOn(SETTING::PREV_ONION) || mPrefs->isOn(SETTING::NEXT_ONION)) { - invalidateCaches(); + invalidatePainterCaches(); } prepOverlays(currentFrame); @@ -315,7 +315,7 @@ void ScribbleArea::onPlayStateChanged() void ScribbleArea::onScrubbed(int frameNumber) { - invalidateCaches(); + invalidatePainterCaches(); updateFrame(frameNumber); } @@ -323,7 +323,7 @@ void ScribbleArea::onFramesModified() { invalidateCacheForDirtyFrames(); if (mPrefs->isOn(SETTING::PREV_ONION) || mPrefs->isOn(SETTING::NEXT_ONION)) { - invalidateCaches(); + invalidatePainterCaches(); } update(); } @@ -332,7 +332,7 @@ void ScribbleArea::onFrameModified(int frameNumber) { if (mPrefs->isOn(SETTING::PREV_ONION) || mPrefs->isOn(SETTING::NEXT_ONION)) { invalidateOnionSkinsCacheAround(frameNumber); - invalidateCaches(); + invalidatePainterCaches(); } invalidateCacheForFrame(frameNumber); updateFrame(frameNumber); @@ -792,12 +792,11 @@ void ScribbleArea::resizeEvent(QResizeEvent* event) QWidget::resizeEvent(event); mDevicePixelRatio = devicePixelRatioF(); mCanvas = QPixmap(QSizeF(size() * mDevicePixelRatio).toSize()); - mCanvas.fill(Qt::transparent); mEditor->view()->setCanvasSize(size()); invalidateCacheForFrame(mEditor->currentFrame()); - invalidateCaches(); + invalidatePainterCaches(); } void ScribbleArea::showLayerNotVisibleWarning() @@ -1077,7 +1076,7 @@ void ScribbleArea::paintEvent(QPaintEvent* event) prepCameraPainter(currentFrame); prepOverlays(currentFrame); - mCanvasPainter.paintCached(); + mCanvasPainter.paintCached(event->rect()); mCameraPainter.paintCached(); } @@ -1302,12 +1301,11 @@ void ScribbleArea::prepCanvas(int frame, QRect rect) void ScribbleArea::drawCanvas(int frame, QRect rect) { - mCanvas.fill(Qt::transparent); mCanvas.setDevicePixelRatio(mDevicePixelRatio); prepCanvas(frame, rect); prepCameraPainter(frame); prepOverlays(frame); - mCanvasPainter.paint(); + mCanvasPainter.paint(rect); mCameraPainter.paint(); } diff --git a/core_lib/src/interface/scribblearea.h b/core_lib/src/interface/scribblearea.h index afa3e4e070..6c8d450d34 100644 --- a/core_lib/src/interface/scribblearea.h +++ b/core_lib/src/interface/scribblearea.h @@ -217,7 +217,7 @@ public slots: * Call this in most situations where the layer rendering order is affected. * Peviously known as setAllDirty. */ - void invalidateCaches(); + void invalidatePainterCaches(); /** Invalidate cache for the given frame */ void invalidateCacheForFrame(int frameNumber); diff --git a/core_lib/src/tool/buckettool.cpp b/core_lib/src/tool/buckettool.cpp index 74d3ed8ea2..839e322780 100644 --- a/core_lib/src/tool/buckettool.cpp +++ b/core_lib/src/tool/buckettool.cpp @@ -202,7 +202,7 @@ void BucketTool::pointerPressEvent(PointerEvent* event) // Because we can change layer to on the fly but we do not act reactively on it // it's neccesary to invalidate layer cache on press event, otherwise the cache // will be drawn until a move event has been initiated. - mScribbleArea->invalidateCaches(); + mScribbleArea->invalidatePainterCaches(); } void BucketTool::pointerMoveEvent(PointerEvent* event) @@ -276,7 +276,7 @@ void BucketTool::paintBitmap() // otherwise dragging won't show until release event if (properties.bucketFillToLayerMode == 1) { - mScribbleArea->invalidateCaches(); + mScribbleArea->invalidatePainterCaches(); } } }); From 0999f48ba70617b4c280e008748d68a2634d82f0 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Sat, 22 Apr 2023 20:28:43 +0200 Subject: [PATCH 04/49] 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. --- core_lib/src/canvaspainter.cpp | 1 - core_lib/src/graphics/vector/beziercurve.cpp | 4 +- core_lib/src/graphics/vector/beziercurve.h | 2 +- core_lib/src/graphics/vector/vectorimage.cpp | 15 +++ core_lib/src/graphics/vector/vectorimage.h | 2 + core_lib/src/interface/scribblearea.cpp | 116 ++++++------------- core_lib/src/interface/scribblearea.h | 7 +- core_lib/src/tool/brushtool.cpp | 21 ---- core_lib/src/tool/buckettool.cpp | 1 - core_lib/src/tool/erasertool.cpp | 22 ---- core_lib/src/tool/penciltool.cpp | 16 --- core_lib/src/tool/pentool.cpp | 23 ---- core_lib/src/tool/polylinetool.cpp | 3 +- core_lib/src/tool/smudgetool.cpp | 22 ++-- core_lib/src/util/blitrect.cpp | 22 ++++ core_lib/src/util/blitrect.h | 2 + 16 files changed, 92 insertions(+), 187 deletions(-) diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index c683f545be..75c55f9864 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -38,7 +38,6 @@ CanvasPainter::~CanvasPainter() void CanvasPainter::setCanvas(QPixmap* canvas) { Q_ASSERT(canvas); - qDebug() << canvas->size(); if (mCanvas == nullptr || mCanvasSize != canvas->size()) { mCanvas = canvas; diff --git a/core_lib/src/graphics/vector/beziercurve.cpp b/core_lib/src/graphics/vector/beziercurve.cpp index dc49d5331e..07b68c61a4 100644 --- a/core_lib/src/graphics/vector/beziercurve.cpp +++ b/core_lib/src/graphics/vector/beziercurve.cpp @@ -279,7 +279,7 @@ void BezierCurve::setFilled(bool YesOrNo) mFilled = YesOrNo; } -BezierCurve BezierCurve::transformed(QTransform transformation) +BezierCurve BezierCurve::transformed(QTransform transformation) const { BezierCurve transformedCurve = *this; // copy the curve if (isSelected(-1)) { transformedCurve.setOrigin(transformation.map(origin)); } @@ -621,7 +621,7 @@ QPainterPath BezierCurve::getStrokedPath(qreal width, bool usePressure) QRectF BezierCurve::getBoundingRect() { - return getSimplePath().boundingRect(); + return getSimplePath().boundingRect().adjusted(-getWidth(), -getWidth(), getWidth(), getWidth()); } void BezierCurve::createCurve(const QList& pointList, const QList& pressureList, bool smooth) diff --git a/core_lib/src/graphics/vector/beziercurve.h b/core_lib/src/graphics/vector/beziercurve.h index b68044b572..0f1b67f301 100644 --- a/core_lib/src/graphics/vector/beziercurve.h +++ b/core_lib/src/graphics/vector/beziercurve.h @@ -74,7 +74,7 @@ class BezierCurve void setSelected(int i, bool YesOrNo); void setFilled(bool yesOrNo); - BezierCurve transformed(QTransform transformation); + BezierCurve transformed(QTransform transformation) const; void transform(QTransform transformation); void appendCubic(const QPointF& c1Point, const QPointF& c2Point, const QPointF& vertexPoint, qreal pressureValue); diff --git a/core_lib/src/graphics/vector/vectorimage.cpp b/core_lib/src/graphics/vector/vectorimage.cpp index 136d211fbe..18108742be 100644 --- a/core_lib/src/graphics/vector/vectorimage.cpp +++ b/core_lib/src/graphics/vector/vectorimage.cpp @@ -863,6 +863,21 @@ void VectorImage::setSelectionRect(QRectF rectangle) select(rectangle); } +QRectF VectorImage::getBoundsOfTransformedCurves() const +{ + QRectF bounds; + for (int i = 0; i < mCurves.size(); i++) + { + BezierCurve curve; + if (mCurves.at(i).isPartlySelected()) + { + curve = mCurves[i].transformed(mSelectionTransformation); + bounds |= curve.getBoundingRect(); + } + } + return bounds; +} + /** * @brief VectorImage::calculateSelectionRect */ diff --git a/core_lib/src/graphics/vector/vectorimage.h b/core_lib/src/graphics/vector/vectorimage.h index 69d8ca0bcf..fd79e81d5f 100644 --- a/core_lib/src/graphics/vector/vectorimage.h +++ b/core_lib/src/graphics/vector/vectorimage.h @@ -80,6 +80,8 @@ class VectorImage : public KeyFrame void deleteSelectedPoints(); void removeVertex(int curve, int vertex); + QRectF getBoundsOfTransformedCurves() const; + bool isEmpty() const { return mCurves.isEmpty(); } void paste(VectorImage&); diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index 2299d111f8..1399c2d327 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -31,6 +31,7 @@ GNU General Public License for more details. #include "layercamera.h" #include "bitmapimage.h" #include "vectorimage.h" +#include "blitrect.h" #include "onionskinpainteroptions.h" @@ -56,7 +57,6 @@ ScribbleArea::ScribbleArea(QWidget* parent) : QWidget(parent) ScribbleArea::~ScribbleArea() { - delete mBufferImg; } bool ScribbleArea::init() @@ -89,8 +89,6 @@ bool ScribbleArea::init() mLayerVisibility = static_cast(mPrefs->getInt(SETTING::LAYER_VISIBILITY)); - mBufferImg = new BitmapImage; - updateCanvasCursor(); setMouseTracking(true); // reacts to mouse move events, even if the button is not pressed @@ -844,10 +842,10 @@ void ScribbleArea::paintBitmapBuffer() default: //nothing break; } - targetImage->paste(mBufferImg, cm); + targetImage->paste(&mBufferImg, cm); } - QRect rect = mEditor->view()->mapCanvasToScreen(mBufferImg->bounds()).toRect(); + QRect rect = mEditor->view()->mapCanvasToScreen(mBufferImg.bounds()).toRect(); drawCanvas(frameNumber, rect.adjusted(-1, -1, 1, 1)); update(rect); @@ -856,83 +854,23 @@ void ScribbleArea::paintBitmapBuffer() updateFrame(frameNumber); layer->setModified(frameNumber, true); - mBufferImg->clear(); -} - -void ScribbleArea::paintBitmapBufferRect(const QRect& rect) -{ - if (mEditor->playback()->isPlaying()) - { - Layer* layer = mEditor->layers()->currentLayer(); - Q_ASSERT(layer); - - BitmapImage* targetImage = currentBitmapImage(layer); - - if (targetImage != nullptr) - { - QPainter::CompositionMode cm = QPainter::CompositionMode_SourceOver; - switch (currentTool()->type()) - { - case ERASER: - cm = QPainter::CompositionMode_DestinationOut; - break; - case BRUSH: - case PEN: - case PENCIL: - if (getTool(currentTool()->type())->properties.preserveAlpha) - { - cm = QPainter::CompositionMode_SourceAtop; - } - break; - default: //nothing - break; - } - targetImage->paste(mBufferImg, cm); - } - - // Clear the buffer - mBufferImg->clear(); - - int frameNumber = mEditor->currentFrame(); - layer->setModified(frameNumber, true); - - updateFrame(frameNumber); - - drawCanvas(frameNumber, rect.adjusted(-1, -1, 1, 1)); - update(rect); - } + mBufferImg.clear(); } void ScribbleArea::clearBitmapBuffer() { - mBufferImg->clear(); + mBufferImg.clear(); } void ScribbleArea::drawLine(QPointF P1, QPointF P2, QPen pen, QPainter::CompositionMode cm) { - mBufferImg->drawLine(P1, P2, pen, cm, mPrefs->isOn(SETTING::ANTIALIAS)); + mBufferImg.drawLine(P1, P2, pen, cm, mPrefs->isOn(SETTING::ANTIALIAS)); } void ScribbleArea::drawPath(QPainterPath path, QPen pen, QBrush brush, QPainter::CompositionMode cm) { - mBufferImg->drawPath(path, pen, brush, cm, mPrefs->isOn(SETTING::ANTIALIAS)); -} - -void ScribbleArea::refreshBitmap(const QRectF& rect, int rad) -{ - QRectF updatedRect = mEditor->view()->mapCanvasToScreen(rect.normalized().adjusted(-rad, -rad, +rad, +rad)); - update(updatedRect.toRect()); -} - -void ScribbleArea::refreshVector(const QRectF& rect, int rad) -{ - rad += 1; - //QRectF updatedRect = mEditor->view()->mapCanvasToScreen( rect.normalized().adjusted( -rad, -rad, +rad, +rad ) ); - update(rect.normalized().adjusted(-rad, -rad, +rad, +rad).toRect()); - - //qDebug() << "Logical: " << rect; - //qDebug() << "Physical: " << mEditor->view()->mapCanvasToScreen( rect.normalized() ); - //update(); + mBufferImg.drawPath(path, pen, brush, cm, mPrefs->isOn(SETTING::ANTIALIAS)); + update(mBufferImg.bounds().adjusted(-1, -1, 1, 1)); } void ScribbleArea::paintCanvasCursor(QPainter& painter) @@ -1296,7 +1234,7 @@ void ScribbleArea::prepCanvas(int frame, QRect rect) mCanvasPainter.setViewTransform(vm->getView(), vm->getViewInverse()); mCanvasPainter.setTransformedSelection(sm->mySelectionRect().toRect(), sm->selectionTransform()); - mCanvasPainter.setPaintSettings(object, mEditor->layers()->currentLayerIndex(), frame, rect, mBufferImg); + mCanvasPainter.setPaintSettings(object, mEditor->layers()->currentLayerIndex(), frame, rect, &mBufferImg); } void ScribbleArea::drawCanvas(int frame, QRect rect) @@ -1333,8 +1271,9 @@ void ScribbleArea::drawPen(QPointF thePoint, qreal brushWidth, QColor fillColor, { QRectF rectangle(thePoint.x() - 0.5 * brushWidth, thePoint.y() - 0.5 * brushWidth, brushWidth, brushWidth); - mBufferImg->drawEllipse(rectangle, Qt::NoPen, QBrush(fillColor, Qt::SolidPattern), + mBufferImg.drawEllipse(rectangle, Qt::NoPen, QBrush(fillColor, Qt::SolidPattern), QPainter::CompositionMode_Source, useAA); + update(mEditor->view()->mapCanvasToScreen(mBufferImg.bounds()).toRect().adjusted(-1, -1, 1, 1)); } void ScribbleArea::drawPencil(QPointF thePoint, qreal brushWidth, qreal fixedBrushFeather, QColor fillColor, qreal opacity) @@ -1351,14 +1290,16 @@ void ScribbleArea::drawBrush(QPointF thePoint, qreal brushWidth, qreal mOffset, QRadialGradient radialGrad(thePoint, 0.5 * brushWidth); setGaussianGradient(radialGrad, fillColor, opacity, mOffset); - mBufferImg->drawEllipse(rectangle, Qt::NoPen, radialGrad, + mBufferImg.drawEllipse(rectangle, Qt::NoPen, radialGrad, QPainter::CompositionMode_SourceOver, false); } else { - mBufferImg->drawEllipse(rectangle, Qt::NoPen, QBrush(fillColor, Qt::SolidPattern), + mBufferImg.drawEllipse(rectangle, Qt::NoPen, QBrush(fillColor, Qt::SolidPattern), QPainter::CompositionMode_SourceOver, useAA); } + + update(mEditor->view()->mapCanvasToScreen(mBufferImg.bounds()).toRect().adjusted(-1, -1, 1, 1)); } void ScribbleArea::flipSelection(bool flipVertical) @@ -1417,7 +1358,9 @@ void ScribbleArea::blurBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPointF bmiTmpClip.drawRect(srcRect, Qt::NoPen, radialGrad, QPainter::CompositionMode_Source, mPrefs->isOn(SETTING::ANTIALIAS)); bmiSrcClip.bounds().moveTo(trgRect.topLeft().toPoint()); bmiTmpClip.paste(&bmiSrcClip, QPainter::CompositionMode_SourceIn); - mBufferImg->paste(&bmiTmpClip); + mBufferImg.paste(&bmiTmpClip); + + update(mEditor->view()->mapCanvasToScreen(mBufferImg.bounds()).toRect().adjusted(-1, -1, 1, 1)); } void ScribbleArea::liquifyBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPointF thePoint_, qreal brushWidth_, qreal mOffset_, qreal opacity_) @@ -1469,19 +1412,28 @@ void ScribbleArea::liquifyBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPoi } } } - mBufferImg->paste(&bmiTmpClip); + mBufferImg.paste(&bmiTmpClip); + + update(mEditor->view()->mapCanvasToScreen(mBufferImg.bounds()).toRect().adjusted(-1, -1, 1, 1)); } void ScribbleArea::drawPolyline(QPainterPath path, QPen pen, bool useAA) { - QRectF updateRect = mEditor->view()->mapCanvasToScreen(path.boundingRect().toRect()).adjusted(-1, -1, 1, 1); + BlitRect blitRect; + + // In order to clear what was previous dirty, we need to include the previous buffer bound + // this ensures that we won't see stroke artifacts + blitRect.extend(mEditor->view()->mapCanvasToScreen(mBufferImg.bounds()).toRect()); + + QRect updateRect = mEditor->view()->mapCanvasToScreen(path.boundingRect()).toRect(); + // Now extend with the new path bounds mapped to the local coordinate + blitRect.extend(updateRect); - // Update region outside updateRect - QRectF boundingRect = updateRect.adjusted(-width(), -height(), width(), height()); - mBufferImg->clear(); - mBufferImg->drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_SourceOver, useAA); - update(boundingRect.toRect()); + mBufferImg.clear(); + mBufferImg.drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_SourceOver, useAA); + // And update only the affected area + update(blitRect.adjusted(-1, -1, 1, 1)); } /************************************************************************************/ diff --git a/core_lib/src/interface/scribblearea.h b/core_lib/src/interface/scribblearea.h index 6c8d450d34..9f89da382e 100644 --- a/core_lib/src/interface/scribblearea.h +++ b/core_lib/src/interface/scribblearea.h @@ -189,11 +189,8 @@ public slots: void liquifyBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPointF thePoint_, qreal brushWidth_, qreal offset_, qreal opacity_); void paintBitmapBuffer(); - void paintBitmapBufferRect(const QRect& rect); void paintCanvasCursor(QPainter& painter); void clearBitmapBuffer(); - void refreshBitmap(const QRectF& rect, int rad); - void refreshVector(const QRectF& rect, int rad); void setGaussianGradient(QGradient &gradient, QColor color, qreal opacity, qreal offset); void pointerPressEvent(PointerEvent*); @@ -206,7 +203,7 @@ public slots: /// on an empty frame, and if so, takes action according to use preference. void handleDrawingOnEmptyFrame(); - BitmapImage* mBufferImg = nullptr; // used to pre-draw vector modifications + BitmapImage mBufferImg; // used to draw strokes for both bitmap and vector QPixmap mCursorImg; QPixmap mTransCursImg; @@ -244,8 +241,6 @@ public slots: MoveMode mMoveMode = MoveMode::NONE; - BitmapImage mBitmapSelection; // used to temporary store a transformed portion of a bitmap image - std::unique_ptr mStrokeManager; Editor* mEditor = nullptr; diff --git a/core_lib/src/tool/brushtool.cpp b/core_lib/src/tool/brushtool.cpp index 6b51ffd15c..9d1a4d6135 100644 --- a/core_lib/src/tool/brushtool.cpp +++ b/core_lib/src/tool/brushtool.cpp @@ -194,17 +194,12 @@ void BrushTool::paintAt(QPointF point) qreal opacity = (properties.pressure) ? (mCurrentPressure * 0.5) : 1.0; qreal brushWidth = properties.width * pressure; mCurrentWidth = brushWidth; - - BlitRect rect(point.toPoint()); mScribbleArea->drawBrush(point, brushWidth, properties.feather, mEditor->color()->frontColor(), opacity, true); - - int rad = qRound(brushWidth) / 2 + 2; - mScribbleArea->refreshBitmap(rect, rad); } } @@ -217,11 +212,6 @@ void BrushTool::drawStroke() if (layer->type() == Layer::BITMAP) { - for (int i = 0; i < p.size(); i++) - { - p[i] = mEditor->view()->mapScreenToCanvas(p[i]); - } - qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0; qreal opacity = (properties.pressure) ? (mCurrentPressure * 0.5) : 1.0; qreal brushWidth = properties.width * pressure; @@ -230,8 +220,6 @@ void BrushTool::drawStroke() qreal brushStep = (0.5 * brushWidth); brushStep = qMax(1.0, brushStep); - BlitRect rect; - QPointF a = mLastBrushPoint; QPointF b = getCurrentPoint(); @@ -242,7 +230,6 @@ void BrushTool::drawStroke() { QPointF point = mLastBrushPoint + (i + 1) * brushStep * (getCurrentPoint() - mLastBrushPoint) / distance; - rect.extend(point.toPoint()); mScribbleArea->drawBrush(point, brushWidth, properties.feather, @@ -255,11 +242,6 @@ void BrushTool::drawStroke() } } - int rad = qRound(brushWidth / 2 + 2); - - mScribbleArea->paintBitmapBufferRect(rect); - mScribbleArea->refreshBitmap(rect, rad); - // Line visualizer // for debugging // QPainterPath tempPath; @@ -281,8 +263,6 @@ void BrushTool::drawStroke() qreal pressure = (properties.pressure) ? mCurrentPressure : 1; qreal brushWidth = properties.width * pressure; - int rad = qRound((brushWidth / 2 + 2) * mEditor->view()->scaling()); - QPen pen(mEditor->color()->frontColor(), brushWidth * mEditor->view()->scaling(), Qt::SolidLine, @@ -295,7 +275,6 @@ void BrushTool::drawStroke() path.cubicTo(p[1], p[2], p[3]); mScribbleArea->drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_Source); - mScribbleArea->refreshVector(path.boundingRect().toRect(), rad); } } } diff --git a/core_lib/src/tool/buckettool.cpp b/core_lib/src/tool/buckettool.cpp index 839e322780..2ef4f01aa5 100644 --- a/core_lib/src/tool/buckettool.cpp +++ b/core_lib/src/tool/buckettool.cpp @@ -339,7 +339,6 @@ void BucketTool::drawStroke() QPainterPath path(p[0]); path.cubicTo(p[1], p[2], p[3]); mScribbleArea->drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_Source); - mScribbleArea->refreshVector(path.boundingRect().toRect(), rad); } } } diff --git a/core_lib/src/tool/erasertool.cpp b/core_lib/src/tool/erasertool.cpp index b2d1744a34..ba7ecf6379 100644 --- a/core_lib/src/tool/erasertool.cpp +++ b/core_lib/src/tool/erasertool.cpp @@ -195,7 +195,6 @@ void EraserTool::paintAt(QPointF point) qreal brushWidth = properties.width * pressure; mCurrentWidth = brushWidth; - BlitRect rect(point.toPoint()); mScribbleArea->drawBrush(point, brushWidth, properties.feather, @@ -203,13 +202,6 @@ void EraserTool::paintAt(QPointF point) opacity, properties.useFeather, properties.useAA == ON); - - int rad = qRound(brushWidth / 2 + 2); - - //continuously update buffer to update stroke behind grid. - mScribbleArea->paintBitmapBufferRect(rect); - - mScribbleArea->refreshBitmap(rect, rad); } } @@ -222,11 +214,6 @@ void EraserTool::drawStroke() if (layer->type() == Layer::BITMAP) { - for (int i = 0; i < p.size(); i++) - { - p[i] = mEditor->view()->mapScreenToCanvas(p[i]); - } - qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0; qreal opacity = (properties.pressure) ? (mCurrentPressure * 0.5) : 1.0; qreal brushWidth = properties.width * pressure; @@ -247,7 +234,6 @@ void EraserTool::drawStroke() { QPointF point = mLastBrushPoint + (i + 1) * brushStep * (getCurrentPoint() - mLastBrushPoint) / distance; - rect.extend(point.toPoint()); mScribbleArea->drawBrush(point, brushWidth, properties.feather, @@ -260,11 +246,6 @@ void EraserTool::drawStroke() mLastBrushPoint = getCurrentPoint(); } } - - int rad = qRound(brushWidth / 2 + 2); - - mScribbleArea->paintBitmapBufferRect(rect); - mScribbleArea->refreshBitmap(rect, rad); } else if (layer->type() == Layer::VECTOR) { @@ -276,7 +257,6 @@ void EraserTool::drawStroke() qreal brushWidth = mCurrentWidth; QPen pen(Qt::white, brushWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); - int rad = qRound(brushWidth) / 2 + 2; if (p.size() == 4) { @@ -284,9 +264,7 @@ void EraserTool::drawStroke() path.cubicTo(p[1], p[2], p[3]); - qDebug() << path; mScribbleArea->drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_Source); - mScribbleArea->refreshVector(path.boundingRect().toRect(), rad); } } } diff --git a/core_lib/src/tool/penciltool.cpp b/core_lib/src/tool/penciltool.cpp index 55a1672740..e1249b67f4 100644 --- a/core_lib/src/tool/penciltool.cpp +++ b/core_lib/src/tool/penciltool.cpp @@ -207,16 +207,11 @@ void PencilTool::paintAt(QPointF point) qreal fixedBrushFeather = properties.feather; mCurrentWidth = brushWidth; - - BlitRect rect(point.toPoint()); mScribbleArea->drawPencil(point, brushWidth, fixedBrushFeather, mEditor->color()->frontColor(), opacity); - - int rad = qRound(brushWidth) / 2 + 2; - mScribbleArea->refreshBitmap(rect, rad); } } @@ -238,8 +233,6 @@ void PencilTool::drawStroke() qreal fixedBrushFeather = properties.feather; qreal brushStep = qMax(1.0, (0.5 * brushWidth)); - BlitRect rect; - QPointF a = mLastBrushPoint; QPointF b = getCurrentPoint(); @@ -249,7 +242,6 @@ void PencilTool::drawStroke() for (int i = 0; i < steps; i++) { QPointF point = mLastBrushPoint + (i + 1) * brushStep * (getCurrentPoint() - mLastBrushPoint) / distance; - rect.extend(point.toPoint()); mScribbleArea->drawPencil(point, brushWidth, fixedBrushFeather, @@ -261,11 +253,6 @@ void PencilTool::drawStroke() mLastBrushPoint = getCurrentPoint(); } } - - int rad = qRound(brushWidth) / 2 + 2; - - mScribbleArea->paintBitmapBufferRect(rect); - mScribbleArea->refreshBitmap(rect, rad); } else if (layer->type() == Layer::VECTOR) { @@ -277,8 +264,6 @@ void PencilTool::drawStroke() Qt::RoundCap, Qt::RoundJoin); - int rad = qRound((mCurrentWidth / 2 + 2) * mEditor->view()->scaling()); - if (p.size() == 4) { QPainterPath path(p[0]); @@ -286,7 +271,6 @@ void PencilTool::drawStroke() p[2], p[3]); mScribbleArea->drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_Source); - mScribbleArea->refreshVector(path.boundingRect().toRect(), rad); } } } diff --git a/core_lib/src/tool/pentool.cpp b/core_lib/src/tool/pentool.cpp index 5d18117bd1..4822c26fd0 100644 --- a/core_lib/src/tool/pentool.cpp +++ b/core_lib/src/tool/pentool.cpp @@ -163,8 +163,6 @@ void PenTool::pointerReleaseEvent(PointerEvent *event) // draw a single paint dab at the given location void PenTool::paintAt(QPointF point) { - //qDebug() << "Made a single dab at " << point; - Layer* layer = mEditor->layers()->currentLayer(); if (layer->type() == Layer::BITMAP) { @@ -176,11 +174,6 @@ void PenTool::paintAt(QPointF point) brushWidth, mEditor->color()->frontColor(), properties.useAA); - - int rad = qRound(brushWidth) / 2 + 2; - - BlitRect rect(point.toPoint()); - mScribbleArea->refreshBitmap(rect, rad); } } @@ -193,11 +186,6 @@ void PenTool::drawStroke() if (layer->type() == Layer::BITMAP) { - for (int i = 0; i < p.size(); i++) - { - p[i] = mEditor->view()->mapScreenToCanvas(p[i]); - } - qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0; qreal brushWidth = properties.width * pressure; mCurrentWidth = brushWidth; @@ -207,8 +195,6 @@ void PenTool::drawStroke() qreal brushStep = (0.5 * brushWidth); brushStep = qMax(1.0, brushStep); - BlitRect rect; - QPointF a = mLastBrushPoint; QPointF b = getCurrentPoint(); @@ -218,7 +204,6 @@ void PenTool::drawStroke() for (int i = 0; i < steps; i++) { QPointF point = mLastBrushPoint + (i + 1) * brushStep * (getCurrentPoint() - mLastBrushPoint) / distance; - rect.extend(point.toPoint()); mScribbleArea->drawPen(point, brushWidth, mEditor->color()->frontColor(), @@ -229,19 +214,12 @@ void PenTool::drawStroke() mLastBrushPoint = getCurrentPoint(); } } - - int rad = qRound(brushWidth) / 2 + 2; - - mScribbleArea->paintBitmapBufferRect(rect); - mScribbleArea->refreshBitmap(rect, rad); } else if (layer->type() == Layer::VECTOR) { qreal pressure = (properties.pressure) ? mCurrentPressure : 1.0; qreal brushWidth = properties.width * pressure; - int rad = qRound((brushWidth / 2 + 2) * mEditor->view()->scaling()); - QPen pen(mEditor->color()->frontColor(), brushWidth * mEditor->view()->scaling(), Qt::SolidLine, @@ -253,7 +231,6 @@ void PenTool::drawStroke() QPainterPath path(p[0]); path.cubicTo(p[1], p[2], p[3]); mScribbleArea->drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_Source); - mScribbleArea->refreshVector(path.boundingRect().toRect(), rad); } } } diff --git a/core_lib/src/tool/polylinetool.cpp b/core_lib/src/tool/polylinetool.cpp index dbe9297a92..00efa2bf2e 100644 --- a/core_lib/src/tool/polylinetool.cpp +++ b/core_lib/src/tool/polylinetool.cpp @@ -21,7 +21,6 @@ GNU General Public License for more details. #include "editor.h" #include "scribblearea.h" -#include "strokemanager.h" #include "layermanager.h" #include "colormanager.h" #include "viewmanager.h" @@ -278,7 +277,7 @@ void PolylineTool::endPolyline(QList points) drawPolyline(points, points.last()); BitmapImage *bitmapImage = static_cast(layer)->getLastBitmapImageAtFrame(mEditor->currentFrame(), 0); if (bitmapImage == nullptr) { return; } // Can happen if the first frame is deleted while drawing - bitmapImage->paste(mScribbleArea->mBufferImg); + bitmapImage->paste(&mScribbleArea->mBufferImg); } mScribbleArea->clearBitmapBuffer(); diff --git a/core_lib/src/tool/smudgetool.cpp b/core_lib/src/tool/smudgetool.cpp index b3281225ba..d11c56fbf9 100644 --- a/core_lib/src/tool/smudgetool.cpp +++ b/core_lib/src/tool/smudgetool.cpp @@ -215,8 +215,17 @@ void SmudgeTool::pointerMoveEvent(PointerEvent* event) if (vectorImage == nullptr) { return; } // transforms the selection + BlitRect blit; + + // Use the previous dirty bound and extend it with the current dirty bound + // this ensures that we won't get painting artifacts + blit.extend(vectorImage->getBoundsOfTransformedCurves().toRect()); selectMan->setSelectionTransform(QTransform().translate(offsetFromPressPos().x(), offsetFromPressPos().y())); vectorImage->setSelectionTransformation(selectMan->selectionTransform()); + blit.extend(vectorImage->getBoundsOfTransformedCurves().toRect()); + + // And now tell the widget to update the portion in local coordinates + mScribbleArea->update(mEditor->view()->mapCanvasToScreen(blit).toRect().adjusted(-1, -1, 1, 1)); } } } @@ -228,9 +237,9 @@ void SmudgeTool::pointerMoveEvent(PointerEvent* event) if (vectorImage == nullptr) { return; } selectMan->setVertices(vectorImage->getVerticesCloseTo(getCurrentPoint(), selectMan->selectionTolerance())); + mScribbleArea->update(); } } - mEditor->updateCurrentFrame(); } void SmudgeTool::pointerReleaseEvent(PointerEvent* event) @@ -304,14 +313,12 @@ void SmudgeTool::drawStroke() qreal brushStep = 2; qreal distance = QLineF(b, a).length() / 2.0; int steps = qRound(distance / brushStep); - int rad = qRound(brushWidth / 2.0) + 2; QPointF sourcePoint = mLastBrushPoint; for (int i = 0; i < steps; i++) { - targetImage.paste(mScribbleArea->mBufferImg); + targetImage.paste(&mScribbleArea->mBufferImg); QPointF targetPoint = mLastBrushPoint + (i + 1) * (brushStep) * (b - mLastBrushPoint) / distance; - rect.extend(targetPoint.toPoint()); mScribbleArea->liquifyBrush(&targetImage, sourcePoint, targetPoint, @@ -324,8 +331,6 @@ void SmudgeTool::drawStroke() mLastBrushPoint = targetPoint; } sourcePoint = targetPoint; - mScribbleArea->paintBitmapBufferRect(rect); - mScribbleArea->refreshBitmap(rect, rad); } } else // liquify smooth @@ -333,12 +338,11 @@ void SmudgeTool::drawStroke() qreal brushStep = 2.0; qreal distance = QLineF(b, a).length(); int steps = qRound(distance / brushStep); - int rad = qRound(brushWidth / 2.0) + 2; QPointF sourcePoint = mLastBrushPoint; for (int i = 0; i < steps; i++) { - targetImage.paste(mScribbleArea->mBufferImg); + targetImage.paste(&mScribbleArea->mBufferImg); QPointF targetPoint = mLastBrushPoint + (i + 1) * (brushStep) * (b - mLastBrushPoint) / distance; rect.extend(targetPoint.toPoint()); mScribbleArea->blurBrush(&targetImage, @@ -353,8 +357,6 @@ void SmudgeTool::drawStroke() mLastBrushPoint = targetPoint; } sourcePoint = targetPoint; - mScribbleArea->paintBitmapBufferRect(rect); - mScribbleArea->refreshBitmap(rect, rad); } } } diff --git a/core_lib/src/util/blitrect.cpp b/core_lib/src/util/blitrect.cpp index e578bda31f..3b0645cec4 100644 --- a/core_lib/src/util/blitrect.cpp +++ b/core_lib/src/util/blitrect.cpp @@ -49,3 +49,25 @@ void BlitRect::extend(const QPoint p) if (bottom() < p.y()) { setBottom(p.y()); } } } + +void BlitRect::extend(const QRect& rect) +{ + extend(rect.topLeft(), rect.size()); +} + +void BlitRect::extend(const QPoint& p, const QSize& size) +{ + if (mInitialized == false) + { + setTopLeft(p); + setBottomRight(p + QPoint(size.width(), size.height())); + mInitialized = true; + } + else + { + if (left() > p.x()) { setLeft(p.x()); } + if (right() < p.x() + size.width()) { setRight(p.x() + size.width()); } + if (top() > p.y()) { setTop(p.y()); } + if (bottom() < p.y() + size.height()) { setBottom(p.y() + size.height()); } + } +} diff --git a/core_lib/src/util/blitrect.h b/core_lib/src/util/blitrect.h index b0c10cfb35..9c184a4b20 100644 --- a/core_lib/src/util/blitrect.h +++ b/core_lib/src/util/blitrect.h @@ -28,6 +28,8 @@ class BlitRect : public QRect explicit BlitRect(const QPoint p); explicit BlitRect(const QRect rect); void extend(const QPoint p); + void extend(const QPoint& p, const QSize& size); + void extend(const QRect& rect); private: bool mInitialized = false; From 36d9b2eb840eb2051556a1665ab7408ea0bd1e43 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Sun, 23 Apr 2023 09:42:19 +0200 Subject: [PATCH 05/49] 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. --- core_lib/src/graphics/vector/beziercurve.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core_lib/src/graphics/vector/beziercurve.cpp b/core_lib/src/graphics/vector/beziercurve.cpp index 07b68c61a4..5410d5eef4 100644 --- a/core_lib/src/graphics/vector/beziercurve.cpp +++ b/core_lib/src/graphics/vector/beziercurve.cpp @@ -621,7 +621,8 @@ QPainterPath BezierCurve::getStrokedPath(qreal width, bool usePressure) QRectF BezierCurve::getBoundingRect() { - return getSimplePath().boundingRect().adjusted(-getWidth(), -getWidth(), getWidth(), getWidth()); + qreal radius = getWidth() / 2; + return getSimplePath().boundingRect().adjusted(-radius, -radius, radius, radius); } void BezierCurve::createCurve(const QList& pointList, const QList& pressureList, bool smooth) From e41218d2a97fcfe5996f5f9cf8c41d73fdcc49d4 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Sun, 23 Apr 2023 17:29:49 +0200 Subject: [PATCH 06/49] Fix stroke placement for vector polyline This also fixes some inconsistency in the CanvasPainter that I couldn't make sense of.. --- core_lib/src/canvaspainter.cpp | 1 - core_lib/src/interface/scribblearea.cpp | 4 ++-- core_lib/src/tool/brushtool.cpp | 4 +--- core_lib/src/tool/pentool.cpp | 2 +- core_lib/src/tool/polylinetool.cpp | 3 +-- 5 files changed, 5 insertions(+), 9 deletions(-) diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index 75c55f9864..ef40222cc3 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -353,7 +353,6 @@ void CanvasPainter::paintCurrentVectorFrame(QPainter& painter, const QRect& blit if (isCurrentLayer) { if (isDrawing) { currentVectorPainter.setCompositionMode(mOptions.cmBufferBlendMode); - currentVectorPainter.setTransform(QTransform()); currentVectorPainter.drawImage(mBuffer->topLeft(), *mBuffer->image()); } else if (mRenderTransform) { vectorImage->setSelectionTransformation(mSelectionTransform); diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index 1399c2d327..760c3d9f19 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -869,8 +869,8 @@ void ScribbleArea::drawLine(QPointF P1, QPointF P2, QPen pen, QPainter::Composit void ScribbleArea::drawPath(QPainterPath path, QPen pen, QBrush brush, QPainter::CompositionMode cm) { - mBufferImg.drawPath(path, pen, brush, cm, mPrefs->isOn(SETTING::ANTIALIAS)); - update(mBufferImg.bounds().adjusted(-1, -1, 1, 1)); + mBufferImg.drawPath(mEditor->view()->mapScreenToCanvas(path), pen, brush, cm, mPrefs->isOn(SETTING::ANTIALIAS)); + update(mEditor->view()->mapCanvasToScreen(mBufferImg.bounds()).toRect().adjusted(-1, -1, 1, 1)); } void ScribbleArea::paintCanvasCursor(QPainter& painter) diff --git a/core_lib/src/tool/brushtool.cpp b/core_lib/src/tool/brushtool.cpp index 9d1a4d6135..8461e285d7 100644 --- a/core_lib/src/tool/brushtool.cpp +++ b/core_lib/src/tool/brushtool.cpp @@ -25,7 +25,6 @@ GNU General Public License for more details. #include "beziercurve.h" #include "vectorimage.h" -#include "layervector.h" #include "editor.h" #include "colormanager.h" #include "strokemanager.h" @@ -33,7 +32,6 @@ GNU General Public License for more details. #include "viewmanager.h" #include "selectionmanager.h" #include "scribblearea.h" -#include "blitrect.h" #include "pointerevent.h" @@ -264,7 +262,7 @@ void BrushTool::drawStroke() qreal brushWidth = properties.width * pressure; QPen pen(mEditor->color()->frontColor(), - brushWidth * mEditor->view()->scaling(), + brushWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); diff --git a/core_lib/src/tool/pentool.cpp b/core_lib/src/tool/pentool.cpp index 4822c26fd0..62f9996591 100644 --- a/core_lib/src/tool/pentool.cpp +++ b/core_lib/src/tool/pentool.cpp @@ -221,7 +221,7 @@ void PenTool::drawStroke() qreal brushWidth = properties.width * pressure; QPen pen(mEditor->color()->frontColor(), - brushWidth * mEditor->view()->scaling(), + brushWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); diff --git a/core_lib/src/tool/polylinetool.cpp b/core_lib/src/tool/polylinetool.cpp index 00efa2bf2e..866753e002 100644 --- a/core_lib/src/tool/polylinetool.cpp +++ b/core_lib/src/tool/polylinetool.cpp @@ -223,7 +223,6 @@ void PolylineTool::drawPolyline(QList points, QPointF endPoint) { if (mEditor->layers()->currentLayer()->type() == Layer::VECTOR) { - tempPath = mEditor->view()->mapCanvasToScreen(tempPath); if (mScribbleArea->makeInvisible() == true) { pen.setWidth(0); @@ -231,7 +230,7 @@ void PolylineTool::drawPolyline(QList points, QPointF endPoint) } else { - pen.setWidth(properties.width * mEditor->view()->scaling()); + pen.setWidth(properties.width); } } } From 0bd437b323ac9aa2db02ffb9907c8b3cacdf76d6 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Mon, 29 May 2023 13:47:08 +0200 Subject: [PATCH 07/49] Implement faster hashing --- core_lib/src/canvaspainter.cpp | 2 +- core_lib/src/graphics/bitmap/tiledbuffer.cpp | 35 ++++++++++---------- core_lib/src/graphics/bitmap/tiledbuffer.h | 27 +++++++++++---- 3 files changed, 39 insertions(+), 25 deletions(-) diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index 2beff86baa..11cf214c3c 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -290,7 +290,7 @@ void CanvasPainter::paintBitmapFrame(QPainter& painter, const auto tiles = mTiledBuffer->tiles(); for (const Tile* tile : tiles) { - QPixmap pixTest = QPixmap(64,64); +// QPixmap pixTest = QPixmap(64,64); // pixTest.fill(Qt::green); currentBitmapPainter.drawPixmap(tile->pos(), tile->pixmap()); // currentBitmapPainter.drawPixmap(tile->pos(), pixTest); diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.cpp b/core_lib/src/graphics/bitmap/tiledbuffer.cpp index a4e7e8c794..f512519ad1 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.cpp +++ b/core_lib/src/graphics/bitmap/tiledbuffer.cpp @@ -44,18 +44,20 @@ TiledBuffer::~TiledBuffer() clear(); } -Tile* TiledBuffer::getTileFromIndex(const QPoint& index) +Tile* TiledBuffer::getTileFromIndex(int tileX, int tileY) { - // it's faster to iterate qstring than a qpoint... - QString idxString = QString::number(index.x())+"_"+QString::number(index.y()); - Tile* selectedTile = mTiles.value(idxString, nullptr); + TileIndex tileIndex; + tileIndex.x = tileX; + tileIndex.y = tileY; + + Tile* selectedTile = mTiles.value(tileIndex, nullptr); if (!selectedTile) { // Time to allocate it, update table: selectedTile = new Tile(QSize(UNIFORM_TILESIZE, UNIFORM_TILESIZE)); - mTiles.insert(idxString, selectedTile); + mTiles.insert(tileIndex, selectedTile); - QPoint tilePos (getTilePos(index)); + const QPoint& tilePos (getTilePos(tileIndex)); selectedTile->setPos(tilePos); emit this->onNewTile(this, selectedTile); @@ -81,7 +83,7 @@ void TiledBuffer::drawBrush(const QPointF& point, int brushWidth, QPen pen, QBru for (int tileY = yTop; tileY <= yBottom; tileY++) { for (int tileX = xLeft; tileX <= xRight; tileX++) { - Tile* tile = getTileFromIndex(QPoint(tileX, tileY)); + Tile* tile = getTileFromIndex(tileX, tileY); if (tile) { @@ -138,7 +140,7 @@ void TiledBuffer::drawPath(QPainterPath path, QPen pen, QBrush brush, for (int tileY = yTop; tileY <= yBottom; tileY++) { for (int tileX = xLeft; tileX <= xRight; tileX++) { - Tile* tile = getTileFromIndex(QPoint(tileX, tileY)); + Tile* tile = getTileFromIndex(tileX, tileY); if (tile) { @@ -528,7 +530,7 @@ void TiledBuffer::addTileToSurface(const QPixmap& pixmap, const QPoint& pos) void TiledBuffer::clear() { - QHashIterator i(mTiles); + QHashIterator i(mTiles); while (i.hasNext()) { i.next(); @@ -557,19 +559,16 @@ void TiledBuffer::clear() //} -QPoint TiledBuffer::getTilePos(const QPoint& idx) const -{ - return QPoint(qRound(UNIFORM_TILESIZE*static_cast(idx.x())), qRound(UNIFORM_TILESIZE*static_cast(idx.y()))); -} - -QPoint TiledBuffer::getTileIndex(const QPoint& pos) const +QPoint TiledBuffer::getTilePos(const TileIndex& index) const { - return QPoint(qRound(static_cast(pos.x())/UNIFORM_TILESIZE), qRound(static_cast(pos.y())/UNIFORM_TILESIZE)); + return QPoint { qRound(UNIFORM_TILESIZE*static_cast(index.x)), + qRound(UNIFORM_TILESIZE*static_cast(index.y)) }; } -QPointF TiledBuffer::getTileFIndex(const QPoint& pos) const +TileIndex TiledBuffer::getTileIndex(const TileIndex& pos) const { - return QPointF(static_cast(pos.x())/UNIFORM_TILESIZE, static_cast(pos.y())/UNIFORM_TILESIZE); + return { qRound(static_cast(pos.x)/UNIFORM_TILESIZE), + qRound(static_cast(pos.y)/UNIFORM_TILESIZE) }; } QRect TiledBuffer::getRectForPoint(const QPoint& point, const QSize size) const diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.h b/core_lib/src/graphics/bitmap/tiledbuffer.h index 04a1bf06cd..60b83e33af 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.h +++ b/core_lib/src/graphics/bitmap/tiledbuffer.h @@ -11,6 +11,22 @@ #include "pencilerror.h" #include "tile.h" +struct TileIndex { + int x; + int y; +}; + +inline uint qHash(const TileIndex &key, uint seed) +{ + return qHash(key.x, seed) ^ key.y; +} + +inline bool operator==(const TileIndex &e1, const TileIndex &e2) +{ + return e1.x == e2.x + && e1.y == e2.y; +} + class TiledBuffer: public QObject { Q_OBJECT @@ -110,7 +126,7 @@ class TiledBuffer: public QObject void drawPath(QPainterPath path, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing); - QHash tiles() const { return mTiles; } + QHash tiles() const { return mTiles; } const QRect& bounds() const { return mTileBounds; } @@ -123,11 +139,10 @@ class TiledBuffer: public QObject private: - Tile* getTileFromIndex(const QPoint& index); + Tile* getTileFromIndex(int tileX, int tileY); - inline QPoint getTilePos(const QPoint& idx) const; - inline QPoint getTileIndex(const QPoint& pos) const; - inline QPointF getTileFIndex(const QPoint& pos) const; + inline QPoint getTilePos(const TileIndex& index) const; + inline TileIndex getTileIndex(const TileIndex& pos) const; /** * @brief getRectForPoint @@ -169,7 +184,7 @@ class TiledBuffer: public QObject BlitRect mTileBounds; - QHash mTiles; + QHash mTiles; // Surface mSurface; QImage mCachedSurface; From a6d03c7b6e91392c28de27ac721f5a8835fcfd1e Mon Sep 17 00:00:00 2001 From: Jakob Gahde Date: Tue, 30 May 2023 20:18:20 +0200 Subject: [PATCH 08/49] Do some cleanup --- core_lib/src/canvaspainter.cpp | 69 ++++------------------ core_lib/src/canvaspainter.h | 5 -- core_lib/src/graphics/vector/vectorimage.h | 1 - 3 files changed, 11 insertions(+), 64 deletions(-) diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index ef40222cc3..344cfd7750 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -38,7 +38,7 @@ CanvasPainter::~CanvasPainter() void CanvasPainter::setCanvas(QPixmap* canvas) { Q_ASSERT(canvas); - if (mCanvas == nullptr || mCanvasSize != canvas->size()) { + if (mCanvas == nullptr || mCanvas->size() != canvas->size()) { mCanvas = canvas; mPostLayersPixmap = QPixmap(mCanvas->size()); @@ -50,7 +50,6 @@ void CanvasPainter::setCanvas(QPixmap* canvas) mPostLayersPixmap.fill(Qt::transparent); mOnionSkinPixmap = QPixmap(mCanvas->size()); mOnionSkinPixmap.fill(Qt::transparent); - mCanvasSize = canvas->size(); } } @@ -85,29 +84,29 @@ void CanvasPainter::ignoreTransformedSelection() void CanvasPainter::paintCached(const QRect& blitRect) { - QPainter preLayerPainter; - QPainter mainPainter; - QPainter postLayerPainter; - - initializePainter(mainPainter, *mCanvas, blitRect); - if (!mPreLayersPixmapCacheValid) { + QPainter preLayerPainter; initializePainter(preLayerPainter, mPreLayersPixmap, blitRect); renderPreLayers(preLayerPainter, blitRect); preLayerPainter.end(); mPreLayersPixmapCacheValid = true; } + QPainter mainPainter; + initializePainter(mainPainter, *mCanvas, blitRect); mainPainter.setWorldMatrixEnabled(false); mainPainter.drawPixmap(blitRect, mPreLayersPixmap, blitRect); mainPainter.setWorldMatrixEnabled(true); - renderCurrentFrame(mainPainter, blitRect); + paintCurrentFrame(mainPainter, blitRect, mCurrentLayerIndex, mCurrentLayerIndex); if (!mPostLayersPixmapCacheValid) { + QPainter postLayerPainter; + initializePainter(postLayerPainter, mPostLayersPixmap, blitRect); renderPostLayers(postLayerPainter, blitRect); + postLayerPainter.end(); mPostLayersPixmapCacheValid = true; } @@ -147,29 +146,6 @@ void CanvasPainter::renderPreLayers(QPainter& painter, const QRect& blitRect) painter.setOpacity(1.0); } -void CanvasPainter::renderCurrentFrame(QPainter& painter, const QRect& blitRect) -{ - int currentLayerIndex = mCurrentLayerIndex; - Layer* layer = mObject->getLayer(currentLayerIndex); - - painter.setOpacity(1.0); - - bool isCameraLayer = layer->type() == Layer::CAMERA; - if (layer->visible() == false) - return; - - if (mOptions.eLayerVisibility == LayerVisibility::RELATED && !isCameraLayer) { - painter.setOpacity(calculateRelativeOpacityForLayer(currentLayerIndex, currentLayerIndex, mOptions.fLayerVisibilityThreshold)); - } - - switch (layer->type()) - { - case Layer::BITMAP: { paintCurrentBitmapFrame(painter, blitRect, layer, true); break; } - case Layer::VECTOR: { paintCurrentVectorFrame(painter, blitRect, layer, true); break; } - default: break; - } -} - void CanvasPainter::renderPostLayers(QPainter& painter, const QRect& blitRect) { if (mOptions.eLayerVisibility != LayerVisibility::CURRENTONLY || mObject->getLayer(mCurrentLayerIndex)->type() == Layer::CAMERA) @@ -206,7 +182,7 @@ void CanvasPainter::paint(const QRect& blitRect) mainPainter.drawPixmap(blitRect, mPreLayersPixmap, blitRect); mainPainter.setWorldMatrixEnabled(true); - renderCurrentFrame(mainPainter, blitRect); + paintCurrentFrame(mainPainter, blitRect, mCurrentLayerIndex, mCurrentLayerIndex); initializePainter(postLayerPainter, mPostLayersPixmap, blitRect); renderPostLayers(postLayerPainter, blitRect); @@ -305,7 +281,7 @@ void CanvasPainter::paintOnionSkinFrame(QPainter& painter, QPainter& onionSkinPa void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blitRect, Layer* layer, bool isCurrentLayer) { LayerBitmap* bitmapLayer = static_cast(layer); - BitmapImage* paintedImage = bitmapLayer->getLastBitmapImageAtFrame(mFrameNumber);; + BitmapImage* paintedImage = bitmapLayer->getLastBitmapImageAtFrame(mFrameNumber); if (paintedImage == nullptr) { return; } paintedImage->loadFile(); // Critical! force the BitmapImage to load the image @@ -368,29 +344,6 @@ void CanvasPainter::paintCurrentVectorFrame(QPainter& painter, const QRect& blit painter.drawPixmap(blitRect, mCurrentLayerPixmap, blitRect); } - -void CanvasPainter::prescale(BitmapImage* bitmapImage) -{ - QImage origImage = bitmapImage->image()->copy(); - - // copy content of our unmodified qimage - // to our (not yet) scaled bitmap - mScaledBitmap = origImage.copy(); - - if (mOptions.scaling >= 1.0f) - { - // TODO: Qt doesn't handle huge upscaled qimages well... - // possible solution, myPaintLib canvas renderer splits its canvas up in chunks. - } - else - { - // map to correct matrix - QRect mappedOrigImage = mViewTransform.mapRect(bitmapImage->bounds()); - mScaledBitmap = mScaledBitmap.scaled(mappedOrigImage.size(), - Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - } -} - void CanvasPainter::paintTransformedSelection(QPainter& painter, BitmapImage* bitmapImage, const QRect& selection) const { // Make sure there is something selected @@ -440,7 +393,7 @@ void CanvasPainter::paintCurrentFrame(QPainter& painter, const QRect& blitRect, { Layer* layer = mObject->getLayer(i); - if (layer->visible() == false) + if (!layer->visible()) continue; if (mOptions.eLayerVisibility == LayerVisibility::RELATED && !isCameraLayer) diff --git a/core_lib/src/canvaspainter.h b/core_lib/src/canvaspainter.h index de88308ae2..19f70fb7b6 100644 --- a/core_lib/src/canvaspainter.h +++ b/core_lib/src/canvaspainter.h @@ -77,18 +77,14 @@ class CanvasPainter */ void initializePainter(QPainter& painter, QPaintDevice& device, const QRect& blitRect); - void paintBackground(); void paintOnionSkin(QPainter& painter, const QRect& blitRect); void renderPostLayers(QPainter& painter, const QRect& blitRect); - void renderCurrentFrame(QPainter& painter, const QRect& blitRect); - void renderOnionSkin(QPainter& painter, const QRect& blitRect); void renderPreLayers(QPainter& painter, const QRect& blitRect); void paintCurrentFrame(QPainter& painter, const QRect& blitRect, int startLayer, int endLayer); void paintTransformedSelection(QPainter& painter, BitmapImage* bitmapImage, const QRect& selection) const; - void prescale(BitmapImage* bitmapImage); private: void paintBitmapOnionSkinFrame(QPainter& painter, const QRect& blitRect, Layer* layer, int nFrame, bool colorize); @@ -102,7 +98,6 @@ class CanvasPainter const Object* mObject = nullptr; QPixmap* mCanvas = nullptr; - QSize mCanvasSize; QTransform mViewTransform; QTransform mViewInverse; diff --git a/core_lib/src/graphics/vector/vectorimage.h b/core_lib/src/graphics/vector/vectorimage.h index fd79e81d5f..ec97458a53 100644 --- a/core_lib/src/graphics/vector/vectorimage.h +++ b/core_lib/src/graphics/vector/vectorimage.h @@ -27,7 +27,6 @@ GNU General Public License for more details. class Object; class QPainter; class QImage; -class QPixmap; class VectorImage : public KeyFrame { From 85a9c1c3ab3d232abd9213c7d830508ea9626435 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Wed, 31 May 2023 07:45:56 +0200 Subject: [PATCH 09/49] Fix next onion skin being drawn on current frame when it's the last --- core_lib/src/canvaspainter.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index 344cfd7750..1d20e1a1cc 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -224,9 +224,10 @@ void CanvasPainter::paintBitmapOnionSkinFrame(QPainter& painter, const QRect& bl { LayerBitmap* bitmapLayer = static_cast(layer); - BitmapImage* bitmapImage = bitmapLayer->getLastBitmapImageAtFrame(nFrame, 0); + BitmapImage* bitmapImage = bitmapLayer->getBitmapImageAtFrame(nFrame); if (bitmapImage == nullptr) { return; } + QPainter onionSkinPainter; initializePainter(onionSkinPainter, mOnionSkinPixmap, blitRect); @@ -239,11 +240,9 @@ void CanvasPainter::paintVectorOnionSkinFrame(QPainter& painter, const QRect& bl LayerVector* vectorLayer = static_cast(layer); CANVASPAINTER_LOG("Paint Onion skin vector, Frame = %d", nFrame); - VectorImage* vectorImage = vectorLayer->getLastVectorImageAtFrame(nFrame, 0); - if (vectorImage == nullptr) - { - return; - } + VectorImage* vectorImage = vectorLayer->getVectorImageAtFrame(nFrame); + if (vectorImage == nullptr) { return; } + QPainter onionSkinPainter; initializePainter(onionSkinPainter, mOnionSkinPixmap, blitRect); From 8d567509d013847eb9228a88fe30470e0a874126 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Wed, 31 May 2023 07:48:49 +0200 Subject: [PATCH 10/49] 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. --- core_lib/src/canvaspainter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index 1d20e1a1cc..b753790238 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -227,6 +227,7 @@ void CanvasPainter::paintBitmapOnionSkinFrame(QPainter& painter, const QRect& bl BitmapImage* bitmapImage = bitmapLayer->getBitmapImageAtFrame(nFrame); if (bitmapImage == nullptr) { return; } + bitmapImage->loadFile(); // Critical! force the BitmapImage to load the image QPainter onionSkinPainter; initializePainter(onionSkinPainter, mOnionSkinPixmap, blitRect); From 9d7a2da9d451bc0a11d726b38a786813cccc0e13 Mon Sep 17 00:00:00 2001 From: Jakob Gahde Date: Sat, 3 Jun 2023 19:40:20 +0200 Subject: [PATCH 11/49] Update CanvasPainter interface to better match its usage --- core_lib/src/canvaspainter.cpp | 35 +++++++++++-------------- core_lib/src/canvaspainter.h | 7 +++-- core_lib/src/interface/scribblearea.cpp | 5 ++-- 3 files changed, 21 insertions(+), 26 deletions(-) diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index b753790238..529149764e 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -27,30 +27,26 @@ GNU General Public License for more details. #include "painterutils.h" -CanvasPainter::CanvasPainter() +CanvasPainter::CanvasPainter(QPixmap& canvas) : mCanvas(canvas) { + reset(); } CanvasPainter::~CanvasPainter() { } -void CanvasPainter::setCanvas(QPixmap* canvas) +void CanvasPainter::reset() { - Q_ASSERT(canvas); - if (mCanvas == nullptr || mCanvas->size() != canvas->size()) { - - mCanvas = canvas; - mPostLayersPixmap = QPixmap(mCanvas->size()); - mPreLayersPixmap = 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); - } + mPostLayersPixmap = QPixmap(mCanvas.size()); + mPreLayersPixmap = 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); } void CanvasPainter::setViewTransform(const QTransform view, const QTransform viewInverse) @@ -94,7 +90,7 @@ void CanvasPainter::paintCached(const QRect& blitRect) } QPainter mainPainter; - initializePainter(mainPainter, *mCanvas, blitRect); + initializePainter(mainPainter, mCanvas, blitRect); mainPainter.setWorldMatrixEnabled(false); mainPainter.drawPixmap(blitRect, mPreLayersPixmap, blitRect); mainPainter.setWorldMatrixEnabled(true); @@ -114,10 +110,11 @@ void CanvasPainter::paintCached(const QRect& blitRect) mainPainter.drawPixmap(blitRect, mPostLayersPixmap, blitRect); mainPainter.setWorldMatrixEnabled(true); } + void CanvasPainter::resetLayerCache() { mPreLayersPixmapCacheValid = false; - mPreLayersPixmapCacheValid = false; + mPostLayersPixmapCacheValid = false; } void CanvasPainter::initializePainter(QPainter& painter, QPaintDevice& device, const QRect& blitRect) @@ -172,7 +169,7 @@ void CanvasPainter::paint(const QRect& blitRect) QPainter mainPainter; QPainter postLayerPainter; - initializePainter(mainPainter, *mCanvas, blitRect); + initializePainter(mainPainter, mCanvas, blitRect); initializePainter(preLayerPainter, mPreLayersPixmap, blitRect); renderPreLayers(preLayerPainter, blitRect); diff --git a/core_lib/src/canvaspainter.h b/core_lib/src/canvaspainter.h index 19f70fb7b6..58ad6e993d 100644 --- a/core_lib/src/canvaspainter.h +++ b/core_lib/src/canvaspainter.h @@ -50,10 +50,10 @@ class CanvasPainter { Q_DECLARE_TR_FUNCTIONS(CanvasPainter) public: - explicit CanvasPainter(); + explicit CanvasPainter(QPixmap& canvas); virtual ~CanvasPainter(); - void setCanvas(QPixmap* canvas); + void reset(); void setViewTransform(const QTransform view, const QTransform viewInverse); void setOnionSkinOptions(const OnionSkinPainterOptions& onionSkinOptions) { mOnionSkinPainterOptions = onionSkinOptions;} @@ -86,7 +86,6 @@ class CanvasPainter void paintTransformedSelection(QPainter& painter, BitmapImage* bitmapImage, const QRect& selection) const; -private: 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); @@ -97,7 +96,7 @@ class CanvasPainter CanvasPainterOptions mOptions; const Object* mObject = nullptr; - QPixmap* mCanvas = nullptr; + QPixmap& mCanvas; QTransform mViewTransform; QTransform mViewInverse; diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index 760c3d9f19..fcd978f421 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -44,7 +44,7 @@ GNU General Public License for more details. #include "selectionmanager.h" #include "overlaymanager.h" -ScribbleArea::ScribbleArea(QWidget* parent) : QWidget(parent) +ScribbleArea::ScribbleArea(QWidget* parent) : QWidget(parent), mCanvasPainter(mCanvas) { setObjectName("ScribbleArea"); @@ -795,6 +795,7 @@ void ScribbleArea::resizeEvent(QResizeEvent* event) invalidateCacheForFrame(mEditor->currentFrame()); invalidatePainterCaches(); + mCanvasPainter.reset(); } void ScribbleArea::showLayerNotVisibleWarning() @@ -1227,8 +1228,6 @@ void ScribbleArea::prepCanvas(int frame, QRect rect) mCanvasPainter.setOnionSkinOptions(onionSkinOptions); mCanvasPainter.setOptions(o); - mCanvasPainter.setCanvas(&mCanvas); - ViewManager* vm = mEditor->view(); SelectionManager* sm = mEditor->select(); mCanvasPainter.setViewTransform(vm->getView(), vm->getViewInverse()); From 85b1b8809f88e8e30e16eef1433fa55fa47af8d5 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Sun, 4 Jun 2023 13:14:04 +0200 Subject: [PATCH 12/49] Cleanup TiledBuffer --- core_lib/src/graphics/bitmap/tiledbuffer.cpp | 380 +------------------ core_lib/src/graphics/bitmap/tiledbuffer.h | 30 +- core_lib/src/interface/scribblearea.cpp | 38 +- core_lib/src/interface/scribblearea.h | 1 + 4 files changed, 20 insertions(+), 429 deletions(-) diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.cpp b/core_lib/src/graphics/bitmap/tiledbuffer.cpp index 3cb0a994ad..c63de166d0 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.cpp +++ b/core_lib/src/graphics/bitmap/tiledbuffer.cpp @@ -29,16 +29,6 @@ TiledBuffer::TiledBuffer(QObject* parent) : QObject(parent) { } -TiledBuffer::TiledBuffer(const TiledBuffer& pieces) -{ -} - -TiledBuffer::TiledBuffer(const QRect bounds) -{ -// Tiles tiles; -// mSurface = Surface(tiles, bounds); -} - TiledBuffer::~TiledBuffer() { clear(); @@ -160,374 +150,15 @@ void TiledBuffer::drawPath(QPainterPath path, QPen pen, QBrush brush, } } - -//void TiledBuffer::createNewSurfaceFromImage(const QString& path, const QPoint& topLeft) -//{ -// QImage image(path); -// createNewSurfaceFromImage(image, topLeft); -//} - -//void TiledBuffer::createNewSurfaceFromImage(const QImage& image, const QPoint& topLeft) -//{ -// QPixmap paintTo(TILESIZE); -// mSurface = Surface(); -// mSurface.bounds = QRect(topLeft, image.size()); - -// QList touchedPoints = touchedTiles(bounds()); -// for (int point = 0; point < touchedPoints.count(); point++) { - -// const QPoint& touchedPoint = touchedPoints.at(point); - -// paintTo.fill(Qt::transparent); -// QPainter painter(&paintTo); - -// painter.save(); -// painter.translate(-touchedPoint); -// painter.drawImage(topLeft, image); -// painter.restore(); -// if (!isTransparent(paintTo.toImage())) { -// mSurface.appendTile(paintTo, touchedPoint); -// } -// } -//} - -void TiledBuffer::addTileToSurface(const QPixmap& pixmap, const QPoint& pos) +void TiledBuffer::loadTile(const QImage& image, const QPoint& topLeft, Tile* tile) { -// mSurface.appendTile(pixmap, pos); + QPainter painter(&tile->pixmap()); - -// extendBoundaries(QRect(pos, pixmap.size())); - mImageCacheValid = false; + painter.translate(-tile->pos()); + painter.drawImage(topLeft, image); + painter.end(); } -//void TiledBuffer::appendBitmapSurface(const Surface &surface) -//{ -// mSurface += surface; -// mImageCacheValid = false; -//} - -//void TiledBuffer::renderSurfaceImage() -//{ -// if (!mImageCacheValid) { -// mCachedSurface = surfaceAsImage(); -// mImageCacheValid = true; -// } -//} - -//void TiledBuffer::extendBoundaries(const QRect& rect) -//{ -// mSurface.extendBoundaries(rect); -//} - -//void TiledBuffer::paintSurfaceUsing(const QPixmap& inPixmap, const QPoint& newPos) -//{ -// Surface outSurface; - -// QPixmap outPix = QPixmap(TILESIZE); -// outPix.fill(Qt::transparent); - -// const QRect& adjustedPixRect = QRect(newPos, inPixmap.size()); -// const QList& touchedPoints = touchedTiles(adjustedPixRect); - -// // paint input pixmap on tiles -// for (int point = 0; point < touchedPoints.count(); point++) { - -// const QPoint& touchedPoint = touchedPoints.at(point); -// QPainter painter(&outPix); -// outPix.fill(Qt::transparent); - -// painter.save(); -// painter.translate(-touchedPoint); -// painter.drawPixmap(newPos, inPixmap); -// painter.restore(); -// painter.end(); - -// QImage testImage = outPix.toImage(); -// if (isTransparent(testImage)) { -// continue; -// } - -// outSurface.appendTile(outPix, touchedPoint); -// outSurface.bounds = adjustedPixRect; -// } - -// Surface extraSurface; - -// // paint new tiles on previous tiles if possible, otherwise -// // prepare to be added to bitmapsurface -// QHashIterator> newTiles(outSurface.tiles); -// while (newTiles.hasNext()) { -// newTiles.next(); - -// QPixmap newPix = outSurface.pixmapAtPos(newTiles.key()); -// const QPoint& newPos = outSurface.posFromPixmap(newTiles.value()); -// QPixmap paintToPix = QPixmap(newPix.size()); -// paintToPix.fill(Qt::transparent); - -// bool noMatch = false; -// QHashIterator> cuTiles(mSurface.tiles); -// while (cuTiles.hasNext()) -// { -// cuTiles.next(); - -// QPixmap& existingPix = mSurface.pixmapAtPos(cuTiles.key()); -// const QPoint existingPos = mSurface.posFromPixmap(cuTiles.value()); - -// if (existingPos == newPos) { -// QPainter painter(&existingPix); -// painter.setCompositionMode(QPainter::CompositionMode_SourceOver); -// painter.drawPixmap(QPoint(), newPix); -// painter.end(); -// } else { - -// if (mSurface.contains(newPos)) { -// continue; -// } -// noMatch = true; -// } -// } - -// if (noMatch) -// { -// extraSurface.appendTile(newPix, newPos); -// extraSurface.bounds = outSurface.bounds; -// noMatch = false; -// } -// } - -// appendBitmapSurface(extraSurface); -//} - -//QList TiledBuffer::scanForSurroundingTiles(const QRect& rect) -//{ -// QPoint kernel[] = {QPoint(-64,-64),QPoint(0,-64), QPoint(64,-64), -// QPoint(-64,0), QPoint(0,0), QPoint(64,0), -// QPoint(-64,64), QPoint(0,64), QPoint(64,64)}; - -// QList points; -// QList corners; - -// corners.append({rect.topLeft(), rect.topRight(), rect.bottomLeft(), rect.bottomRight()}); -// for (unsigned int i = 0; i < sizeof(kernel)/sizeof(kernel[0]); i++) { - -// for (int p = 0; p < corners.count(); p++) { -// const QPoint& corner = corners[p]; -// const QPoint& idx = getTileIndex(corner+kernel[i]); -// const QPoint& pos = getTilePos(idx); -// const QRect& rectToIntersect = getRectForPoint(pos); - -// if (rectToIntersect.intersects(rect)) { -// if (points.contains(pos)) { -// continue; -// } - -// points.append(pos); -// } -// } -// } - -// return points; -//} - -//QList TiledBuffer::scanForTilesAtRect(const QRect& rect) -//{ -// const float& imageWidth = static_cast(rect.width()); -// const float& imageHeight = static_cast(rect.height()); -// const float& tileWidth = static_cast(TILESIZE.width()); -// const float& tileHeight = static_cast(TILESIZE.height()); -// const int& nbTilesOnWidth = static_cast(ceil(imageWidth / tileWidth)); -// const int& nbTilesOnHeight = static_cast(ceil(imageHeight / tileHeight)); - -// QList points; - -// QList corners; -// const QPoint& cornerOffset = QPoint(TILESIZE.width(), TILESIZE.height()); - -// corners.append({rect.topLeft(), rect.topRight(), rect.bottomLeft(), rect.bottomRight()}); -// for (int h=0; h < nbTilesOnHeight; h++) { -// for (int w=0; w < nbTilesOnWidth; w++) { - -// const QPoint& tilePos = getTilePos(QPoint(w,h)); -// for (int i = 0; i < corners.count(); i++) { -// QPoint movedPos = getTileIndex(corners[i]-cornerOffset); -// movedPos = getTilePos(movedPos)+tilePos; - -// if (points.contains(movedPos)) { -// continue; -// } - -// if (getRectForPoint(movedPos).intersects(rect)) { -// points.append(movedPos); -// } -// } -// } -// } -// return points; -//} - -//QList TiledBuffer::touchedTiles(const QRect& rect) -//{ -// return scanForTilesAtRect(rect); -//} - -//void TiledBuffer::drawRect(QRect rect, QColor color) -//{ -// float rectWidth = static_cast(rect.width()); -// float rectHeight = static_cast(rect.height()); -// float tileWidth = static_cast(TILESIZE.width()); -// float tileHeight = static_cast(TILESIZE.height()); -// int nbTilesOnWidth = static_cast(ceil(rectWidth / tileWidth)); -// int nbTilesOnHeight = static_cast(ceil(rectHeight / tileHeight)); - -// QPixmap paintTo(UNIFORM_TILESIZE, UNIFORM_TILESIZE); -// mSurface = Surface(); -// mSurface.bounds = QRect(rect.topLeft(), rect.size()); - -// for (int h=0; h < nbTilesOnHeight; h++) { -// for (int w=0; w < nbTilesOnWidth; w++) { -// paintTo.fill(Qt::transparent); -// const QPoint& idx = QPoint(w, h); -// const QPoint& tilePos = getTilePos(idx); - -// const QRect& tileRect = QRect(tilePos, TILESIZE); -// QImage colorImage = QImage(rect.size(), QImage::Format_ARGB32_Premultiplied); -// colorImage.fill(color); -// const QImage& tileImage = colorImage.copy(tileRect); - -// QPainter painter(&paintTo); -// painter.drawImage(QPoint(), tileImage); -// painter.end(); - -// mSurface.appendTile(paintTo, tilePos); -// } -// } -//} - -//QImage TiledBuffer::surfaceAsImage() -//{ -// QImage paintedImage(bounds.size(), QImage::Format_ARGB32_Premultiplied); -// paintedImage.fill(Qt::transparent); - -// QPainter painter(&paintedImage); -// painter.translate(-mSurface.topLeft()); - -// QHashIterator> cuTiles(mSurface.tiles); -// while (cuTiles.hasNext()) { -// cuTiles.next(); -// const QPixmap& pix = mSurface.pixmapAtPos(cuTiles.key()); -// const QPoint& pos = mSurface.posFromPixmap(cuTiles.value()); -// painter.drawPixmap(pos, pix); -// } -// painter.end(); - -// return paintedImage; -//} - -//void TiledBuffer::eraseSelection(const QPoint pos, QPixmap& pixmap, const QRect selection) -//{ -// fillSelection(pos, pixmap, Qt::transparent, selection); -//} - -//void TiledBuffer::eraseSelection(const QRect selection) -//{ -// QHashIterator> cuTiles(mSurface.tiles); -// while (cuTiles.hasNext()) { -// cuTiles.next(); - -// QPixmap& pixmap = mSurface.pixmapAtPos(cuTiles.key()); -// const QPoint& pos = mSurface.posFromPixmap(cuTiles.value()); -// eraseSelection(pos, pixmap, selection); -// } -//} - -//void TiledBuffer::fillSelection(const QPoint& pos, QPixmap& pixmap, QColor color, const QRect selection) -//{ -// QPainter painter(&pixmap); -// painter.translate(-pos); -// painter.setCompositionMode(QPainter::CompositionMode_Source); - -// const QRect& intersection = selection.intersected(getRectForPoint(pos, pixmap.size())); -// painter.fillRect(intersection, color); -// painter.end(); - -//} - -//Surface TiledBuffer::intersectedSurface(const QRect rect) -//{ -// Surface outSurface; -// QHashIterator> cuTiles(mSurface.tiles); -// while (cuTiles.hasNext()) -// { -// cuTiles.next(); -// const QPixmap& pix = mSurface.pixmapAtPos(cuTiles.key()); -// const QPoint& pos = mSurface.posFromPixmap(cuTiles.value()); - -// if (getRectForPoint(pos, pix.size()).intersects(rect)) { -// outSurface.appendTile(pix, pos); -// outSurface.bounds = rect; -// } -// } - -// return outSurface; -//} - -//QPixmap TiledBuffer::cutSurfaceAsPixmap(const QRect selection) -//{ -// Q_ASSERT(!selection.isEmpty()); - -// Surface intersectSurface = intersectedSurface(selection); - -// eraseSelection(selection); - -// QPixmap paintedImage(selection.size()); -// paintedImage.fill(Qt::transparent); - -// QPainter painter(&paintedImage); -// painter.translate(-selection.topLeft()); - -// QHashIterator> interTiles(intersectSurface.tiles); -// while(interTiles.hasNext()) -// { -// interTiles.next(); -// const QPixmap& pix = intersectSurface.pixmapAtPos(interTiles.key()); -// const QPoint& pos = intersectSurface.posFromPixmap(interTiles.value()); -// painter.drawPixmap(pos, pix); -// } -// return paintedImage; -//} - -//QPixmap TiledBuffer::copySurfaceAsPixmap(const QRect selection) -//{ -// Q_ASSERT(!selection.isEmpty()); - -// const Surface& intersectSurface = intersectedSurface(selection); - -// QPixmap paintedImage(selection.size()); -// paintedImage.fill(Qt::transparent); - -// QPainter painter(&paintedImage); -// painter.translate(-selection.topLeft()); -// QHashIterator> interTiles(intersectSurface.tiles); -// while(interTiles.hasNext()) -// { -// interTiles.next(); -// const QPixmap& pix = intersectSurface.pixmapAtPos(interTiles.key()); -// const QPoint& pos = intersectSurface.posFromPixmap(interTiles.value()); -// painter.drawPixmap(pos, pix); -// } -// return paintedImage; -//} - -//QList TiledBuffer::tilePositions() -//{ -// return mTiles.keys(); -//} - -//QList TiledBuffer::pixmaps() -//{ -// return mTiles.values(); -//} - void TiledBuffer::clear() { QHashIterator i(mTiles); @@ -560,7 +191,6 @@ void TiledBuffer::clear() // return Status::FAIL; //} - QPoint TiledBuffer::getTilePos(const TileIndex& index) const { return QPoint { qRound(UNIFORM_TILESIZE*static_cast(index.x)), diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.h b/core_lib/src/graphics/bitmap/tiledbuffer.h index 60b83e33af..e1aaa21303 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.h +++ b/core_lib/src/graphics/bitmap/tiledbuffer.h @@ -32,18 +32,11 @@ class TiledBuffer: public QObject Q_OBJECT public: TiledBuffer(QObject* parent = nullptr); - TiledBuffer(const TiledBuffer& pieces); - TiledBuffer(const QRect bounds); ~TiledBuffer(); -// void refreshSurface(); - -// QImage surfaceAsImage(); - Status writeFile(const QString& filename); - void addTileToSurface(const QPixmap& pixmap, const QPoint& pos); -// void appendBitmapSurface(const Surface& surface); + void loadTile(const QImage& image, const QPoint& topLeft, Tile* tile); /** * @brief paintSurfaceUsing @@ -97,29 +90,8 @@ class TiledBuffer: public QObject */ QPixmap copySurfaceAsPixmap(const QRect selection); - /** - * @brief eraseSelection - * Removes paint within a given selection - * Useful for selections. - * @param selection - */ - void eraseSelection(const QRect selection); - void eraseSelection(const QPoint pos, QPixmap& pixmap, const QRect selection); - void fillSelection(const QPoint &pos, QPixmap &pixmap, QColor color, const QRect selection); void clear(); - QList pixmaps(); - QList tilePositions(); - -// inline const QRect bounds() { return mSurface.bounds; } - - QImage cachedSurfaceImage() { return mCachedSurface; } - - void renderSurfaceImage(); - -// Surface surface() { return mSurface; } -// const Surface readOnlySurface() const {return mSurface; } - bool isValid() { return !mTiles.isEmpty(); } void drawBrush(const QPointF& point, int brushWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing); diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index bbd6bef0ac..75aa06bfbc 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -76,7 +76,7 @@ bool ScribbleArea::init() connect(mEditor->select(), &SelectionManager::needDeleteSelection, this, &ScribbleArea::deleteSelection); connect(&mTiledBuffer, &TiledBuffer::onUpdateTile, this, &ScribbleArea::updateTile); - connect(&mTiledBuffer, &TiledBuffer::onNewTile, this, &ScribbleArea::updateTile); + connect(&mTiledBuffer, &TiledBuffer::onNewTile, this, &ScribbleArea::loadTile); mDoubleClickTimer->setInterval(50); mMouseFilterTimer->setInterval(50); @@ -193,6 +193,17 @@ void ScribbleArea::updateTile(TiledBuffer* tiledBuffer, Tile* tile) update(mappedRect.toRect()); } +void ScribbleArea::loadTile(TiledBuffer* tiledBuffer, Tile* tile) +{ + if (currentTool()->type() != ToolType::POLYLINE) { + const auto& bitmapImage = currentBitmapImage(mEditor->layers()->currentLayer()); + const QImage& image = *bitmapImage->image(); + mTiledBuffer.loadTile(image, bitmapImage->topLeft(), tile); + } + const QRectF& mappedRect = mEditor->view()->getView().mapRect(QRectF(tile->pos(), tile->boundingRect().size())); + update(mappedRect.toRect()); +} + void ScribbleArea::updateCurrentFrame() { updateFrame(mEditor->currentFrame()); @@ -831,37 +842,16 @@ void ScribbleArea::paintBitmapBuffer() return; } - // Clear the temporary pixel path BitmapImage* targetImage = currentBitmapImage(layer); if (targetImage != nullptr) { - QPainter::CompositionMode cm = QPainter::CompositionMode_SourceOver; - switch (currentTool()->type()) - { - case ERASER: - cm = QPainter::CompositionMode_DestinationOut; - break; - case BRUSH: - case PEN: - case PENCIL: - if (getTool(currentTool()->type())->properties.preserveAlpha) - { - cm = QPainter::CompositionMode_SourceOver; - } - break; - default: //nothing - break; - } - targetImage->paste(&mTiledBuffer, cm); + targetImage->paste(&mTiledBuffer, QPainter::CompositionMode_Source); } QRect rect = mEditor->view()->mapCanvasToScreen(mTiledBuffer.bounds()).toRect(); - drawCanvas(frameNumber, rect.adjusted(-1, -1, 1, 1)); update(rect); -// // Update the cache for the last key-frame. -// updateFrame(frameNumber); layer->setModified(frameNumber, true); mTiledBuffer.clear(); } @@ -1283,7 +1273,6 @@ void ScribbleArea::drawPen(QPointF thePoint, qreal brushWidth, QColor fillColor, QRectF rectangle(thePoint.x() - 0.5 * brushWidth, thePoint.y() - 0.5 * brushWidth, brushWidth, brushWidth); mTiledBuffer.drawBrush(thePoint, brushWidth, Qt::NoPen, QBrush(fillColor, Qt::SolidPattern), QPainter::CompositionMode_SourceOver, true); - update(mEditor->view()->mapCanvasToScreen(mTiledBuffer.bounds()).toRect().adjusted(-1, -1, 1, 1)); } void ScribbleArea::drawPencil(QPointF thePoint, qreal brushWidth, qreal fixedBrushFeather, QColor fillColor, qreal opacity) @@ -1307,7 +1296,6 @@ void ScribbleArea::drawBrush(QPointF thePoint, qreal brushWidth, qreal mOffset, brush = QBrush(fillColor, Qt::SolidPattern); } mTiledBuffer.drawBrush(thePoint, brushWidth, Qt::NoPen, brush, QPainter::CompositionMode_SourceOver, true); - update(mEditor->view()->mapCanvasToScreen(mTiledBuffer.bounds()).toRect().adjusted(-1, -1, 1, 1)); } void ScribbleArea::drawPolyline(QPainterPath path, QPen pen, bool useAA) diff --git a/core_lib/src/interface/scribblearea.h b/core_lib/src/interface/scribblearea.h index 0b19cd6a2c..82b6e48c10 100644 --- a/core_lib/src/interface/scribblearea.h +++ b/core_lib/src/interface/scribblearea.h @@ -168,6 +168,7 @@ public slots: void showLayerNotVisibleWarning(); void updateTile(TiledBuffer* tiledBuffer, Tile* tile); + void loadTile(TiledBuffer* tiledBuffer, Tile* tile); protected: bool event(QEvent *event) override; From a911b10fc2460c59c5db41cbfaa03439eee81879 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Thu, 8 Jun 2023 17:51:06 +0200 Subject: [PATCH 13/49] Make polyline work --- core_lib/src/canvaspainter.cpp | 7 ++++--- core_lib/src/canvaspainter.h | 4 ++++ core_lib/src/interface/scribblearea.cpp | 10 +++++----- core_lib/src/tool/polylinetool.cpp | 8 +++----- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index b195143e4d..41acd0af39 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -296,9 +296,10 @@ void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blit if (isCurrentLayer) { if (isDrawing) { -// // paint the buffer which hasn't been applied to a bitmapImage yet -// currentBitmapPainter.setCompositionMode(mOptions.cmBufferBlendMode); -// currentBitmapPainter.drawImage(mBuffer->topLeft(), *mBuffer->image()); + if (mOptions.bIgnoreCanvasBuffer) { + currentBitmapPainter.setCompositionMode(QPainter::CompositionMode_Source); + currentBitmapPainter.drawImage(paintedImage->topLeft(), *paintedImage->image()); + } const auto tiles = mTiledBuffer->tiles(); for (const Tile* tile : tiles) { diff --git a/core_lib/src/canvaspainter.h b/core_lib/src/canvaspainter.h index 21bddcc8bb..c1ffcf2474 100644 --- a/core_lib/src/canvaspainter.h +++ b/core_lib/src/canvaspainter.h @@ -41,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; diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index 75aa06bfbc..ab9f53780e 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -195,11 +195,10 @@ void ScribbleArea::updateTile(TiledBuffer* tiledBuffer, Tile* tile) void ScribbleArea::loadTile(TiledBuffer* tiledBuffer, Tile* tile) { - if (currentTool()->type() != ToolType::POLYLINE) { - const auto& bitmapImage = currentBitmapImage(mEditor->layers()->currentLayer()); - const QImage& image = *bitmapImage->image(); - mTiledBuffer.loadTile(image, bitmapImage->topLeft(), tile); - } + const auto& bitmapImage = currentBitmapImage(mEditor->layers()->currentLayer()); + const QImage& image = *bitmapImage->image(); + mTiledBuffer.loadTile(image, bitmapImage->topLeft(), tile); + const QRectF& mappedRect = mEditor->view()->getView().mapRect(QRectF(tile->pos(), tile->boundingRect().size())); update(mappedRect.toRect()); } @@ -1213,6 +1212,7 @@ void ScribbleArea::prepCanvas(int frame, QRect rect) o.fLayerVisibilityThreshold = mPrefs->getFloat(SETTING::LAYER_VISIBILITY_THRESHOLD); o.scaling = mEditor->view()->scaling(); o.cmBufferBlendMode = mEditor->tools()->currentTool()->type() == ToolType::ERASER ? QPainter::CompositionMode_DestinationOut : QPainter::CompositionMode_SourceOver; + o.bIgnoreCanvasBuffer = currentTool()->type() == POLYLINE; OnionSkinPainterOptions onionSkinOptions; onionSkinOptions.enabledWhilePlaying = mPrefs->getInt(SETTING::ONION_WHILE_PLAYBACK); diff --git a/core_lib/src/tool/polylinetool.cpp b/core_lib/src/tool/polylinetool.cpp index b9ede91b1a..41f906cdb5 100644 --- a/core_lib/src/tool/polylinetool.cpp +++ b/core_lib/src/tool/polylinetool.cpp @@ -124,7 +124,6 @@ void PolylineTool::pointerPressEvent(PointerEvent* event) { if (layer->type() == Layer::BITMAP || layer->type() == Layer::VECTOR) { - mEditor->backup(typeName()); mScribbleArea->handleDrawingOnEmptyFrame(); if (layer->type() == Layer::VECTOR) @@ -136,12 +135,9 @@ void PolylineTool::pointerPressEvent(PointerEvent* event) { mScribbleArea->toggleThinLines(); } - } else if (layer->type() == Layer::BITMAP) { - mScribbleArea->paintBitmapBuffer(); } mPoints << getCurrentPoint(); emit isActiveChanged(POLYLINE, true); - } } } @@ -163,6 +159,8 @@ void PolylineTool::pointerDoubleClickEvent(PointerEvent*) // include the current point before ending the line. mPoints << getCurrentPoint(); + mEditor->backup(typeName()); + endPolyline(mPoints); clearToolData(); } @@ -252,7 +250,7 @@ void PolylineTool::cancelPolyline() void PolylineTool::endPolyline(QList points) { Layer* layer = mEditor->layers()->currentLayer(); - mScribbleArea->clearBitmapBuffer(); +// mScribbleArea->clearBitmapBuffer(); if (layer->type() == Layer::VECTOR) { From e55d3fc51236bd91f0b0282399697cfb15771cf9 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Tue, 27 Jun 2023 08:33:12 +0200 Subject: [PATCH 14/49] Fix blur logic of smudge tool Additionally this will fix some artifacts when smudging --- core_lib/src/graphics/bitmap/bitmapimage.cpp | 4 ++- core_lib/src/graphics/bitmap/tiledbuffer.cpp | 33 ++++++++++++++++++ core_lib/src/graphics/bitmap/tiledbuffer.h | 36 +------------------- core_lib/src/interface/scribblearea.cpp | 6 ++-- core_lib/src/tool/smudgetool.cpp | 2 +- 5 files changed, 40 insertions(+), 41 deletions(-) diff --git a/core_lib/src/graphics/bitmap/bitmapimage.cpp b/core_lib/src/graphics/bitmap/bitmapimage.cpp index 9680aebca9..c2dc5a47a2 100644 --- a/core_lib/src/graphics/bitmap/bitmapimage.cpp +++ b/core_lib/src/graphics/bitmap/bitmapimage.cpp @@ -646,7 +646,9 @@ 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, otherwize there will be artifacts shown in some cases + painter.drawRect(rectangle.translated(-mBounds.topLeft()).adjusted(-1, -1, 1, 1)); painter.end(); } modification(); diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.cpp b/core_lib/src/graphics/bitmap/tiledbuffer.cpp index c63de166d0..b85711a0d3 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.cpp +++ b/core_lib/src/graphics/bitmap/tiledbuffer.cpp @@ -93,6 +93,39 @@ void TiledBuffer::drawBrush(const QPointF& point, int brushWidth, QPen pen, QBru } } +void TiledBuffer::drawImage(const QImage& image, const QPointF& point, QPainter::CompositionMode cm, bool antialiasing) { + float tilesize = UNIFORM_TILESIZE; + + float imageXRad = image.width(); + float imageYRad = image.height(); + // Gather the amount of tiles that fits the size of the brush width + int xLeft = qFloor((qFloor(point.x() - imageXRad)) / tilesize); + int xRight = qFloor((qFloor(point.x() + imageXRad)) / tilesize); + int yTop = qFloor(qFloor(point.y() - imageYRad) / tilesize); + int yBottom = qFloor(qFloor(point.y() + imageYRad) / tilesize); + + for (int tileY = yTop; tileY <= yBottom; tileY++) { + for (int tileX = xLeft; tileX <= xRight; tileX++) { + + Tile* tile = getTileFromIndex(tileX, tileY); + + if (tile) + { + QPainter painter(&tile->pixmap()); + + painter.translate(-tile->pos()); + painter.setRenderHint(QPainter::Antialiasing, antialiasing); + painter.setCompositionMode(cm); + painter.drawImage(point, image); + painter.end(); + + mTileBounds.extend(tile->pos(), tile->boundingRect().size()); + } + } + } +} + + void TiledBuffer::drawPath(QPainterPath path, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing) { diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.h b/core_lib/src/graphics/bitmap/tiledbuffer.h index e1aaa21303..076e31b492 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.h +++ b/core_lib/src/graphics/bitmap/tiledbuffer.h @@ -38,15 +38,6 @@ class TiledBuffer: public QObject void loadTile(const QImage& image, const QPoint& topLeft, Tile* tile); - /** - * @brief paintSurfaceUsing - * Paint the surface using a given pixmap and position as input - * Currently useful for selections but might have other uses. - * @param inPixmap - * @param newPos - */ - void paintSurfaceUsing(const QPixmap& inPixmap, const QPoint& newPos); - /** * @brief drawRectOnSurface * For test and debug currently @@ -55,16 +46,6 @@ class TiledBuffer: public QObject */ void drawRect(QRect rect, QColor color); - /** - * @brief createNewSurfaceFromImage - * Creates a new surface from an input image or path - * Useful if you need to import a new image or load a project - * @param image - * @param topLeft - */ - void createNewSurfaceFromImage(const QImage& image, const QPoint& topLeft); - void createNewSurfaceFromImage(const QString& path, const QPoint& topLeft); - bool isTransparent(QImage image); /** @@ -74,22 +55,6 @@ class TiledBuffer: public QObject */ void extendBoundaries(const QRect &rect); - /** - * @brief cutSurfaceAsPixmap - * Retrieves a pixmap from the surface you've selected and erases what's underneath - * @param selection - * @return a pixmap with the given paint within the selection - */ - QPixmap cutSurfaceAsPixmap(const QRect selection); - - /** - * @brief copySurfaceAsPixmap - * Retrives a pixmap from the surface you've selected and keeps the surface intact - * @param selection - * @return a pixmap with the given paint within the selection - */ - QPixmap copySurfaceAsPixmap(const QRect selection); - void clear(); bool isValid() { return !mTiles.isEmpty(); } @@ -97,6 +62,7 @@ class TiledBuffer: public QObject void drawBrush(const QPointF& point, int brushWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing); void drawPath(QPainterPath path, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing); + void drawImage(const QImage& image, const QPointF& point, QPainter::CompositionMode cm, bool antialiasing); QHash tiles() const { return mTiles; } diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index ab9f53780e..5b96d00145 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -1383,12 +1383,10 @@ void ScribbleArea::blurBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPointF BitmapImage bmiSrcClip = bmiSource_->copy(srcRect.toAlignedRect()); BitmapImage bmiTmpClip = bmiSrcClip; // TODO: find a shorter way - bmiTmpClip.drawRect(srcRect, Qt::NoPen, radialGrad, QPainter::CompositionMode_Source, mPrefs->isOn(SETTING::ANTIALIAS)); + bmiTmpClip.drawRect(srcRect, Qt::NoPen, radialGrad, QPainter::CompositionMode_Source, true); bmiSrcClip.bounds().moveTo(trgRect.topLeft().toPoint()); bmiTmpClip.paste(&bmiSrcClip, QPainter::CompositionMode_SourceIn); - mBufferImg->paste(&bmiTmpClip); - - update(mEditor->view()->mapCanvasToScreen(mTiledBuffer.bounds()).toRect().adjusted(-1, -1, 1, 1)); + mTiledBuffer.drawImage(*bmiTmpClip.image(), bmiTmpClip.topLeft(), QPainter::CompositionMode_SourceOver, true); } void ScribbleArea::liquifyBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPointF thePoint_, qreal brushWidth_, qreal mOffset_, qreal opacity_) diff --git a/core_lib/src/tool/smudgetool.cpp b/core_lib/src/tool/smudgetool.cpp index 1a6030524b..0393e94913 100644 --- a/core_lib/src/tool/smudgetool.cpp +++ b/core_lib/src/tool/smudgetool.cpp @@ -342,7 +342,7 @@ void SmudgeTool::drawStroke() QPointF sourcePoint = mLastBrushPoint; for (int i = 0; i < steps; i++) { - targetImage.paste(mScribbleArea->mBufferImg); + targetImage.paste(&mScribbleArea->mTiledBuffer); QPointF targetPoint = mLastBrushPoint + (i + 1) * (brushStep) * (b - mLastBrushPoint) / distance; rect.extend(targetPoint.toPoint()); mScribbleArea->blurBrush(&targetImage, From 9ef0eac1648cabb5d0af57fa98e0b7da69948de7 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Sat, 15 Jul 2023 12:02:31 +0200 Subject: [PATCH 15/49] Replace use of BitmapImage buffer with TiledBuffer - Note that VectorImage hasn't been implemented yet. --- core_lib/src/canvaspainter.cpp | 3 +-- core_lib/src/canvaspainter.h | 3 +-- core_lib/src/interface/scribblearea.cpp | 20 ++++---------------- core_lib/src/interface/scribblearea.h | 3 --- core_lib/src/tool/brushtool.cpp | 2 +- core_lib/src/tool/buckettool.cpp | 2 +- core_lib/src/tool/erasertool.cpp | 2 +- core_lib/src/tool/penciltool.cpp | 2 +- core_lib/src/tool/pentool.cpp | 4 ++-- core_lib/src/tool/smudgetool.cpp | 5 ++--- 10 files changed, 14 insertions(+), 32 deletions(-) diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index 41acd0af39..66234a8c11 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -152,7 +152,7 @@ void CanvasPainter::renderPostLayers(QPainter& painter, const QRect& blitRect) } } -void CanvasPainter::setPaintSettings(const Object* object, int currentLayer, int frame, QRect rect, BitmapImage* buffer, TiledBuffer* tiledBuffer) +void CanvasPainter::setPaintSettings(const Object* object, int currentLayer, int frame, QRect rect, TiledBuffer* tiledBuffer) { Q_UNUSED(rect) Q_ASSERT(object); @@ -161,7 +161,6 @@ void CanvasPainter::setPaintSettings(const Object* object, int currentLayer, int CANVASPAINTER_LOG("Set CurrentLayerIndex = %d", currentLayer); mCurrentLayerIndex = currentLayer; mFrameNumber = frame; - mBuffer = buffer; mTiledBuffer = tiledBuffer; } diff --git a/core_lib/src/canvaspainter.h b/core_lib/src/canvaspainter.h index c1ffcf2474..e39b94dc33 100644 --- a/core_lib/src/canvaspainter.h +++ b/core_lib/src/canvaspainter.h @@ -67,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, TiledBuffer* tilledBuffer); + 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(); @@ -108,7 +108,6 @@ class CanvasPainter int mCurrentLayerIndex = 0; int mFrameNumber = 0; - BitmapImage* mBuffer = nullptr; TiledBuffer* mTiledBuffer = nullptr; QImage mScaledBitmap; diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index 5b96d00145..955b3575c8 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -855,24 +855,14 @@ void ScribbleArea::paintBitmapBuffer() mTiledBuffer.clear(); } -void ScribbleArea::clearBitmapBuffer() +void ScribbleArea::clearDrawingBuffer() { - mBufferImg->clear(); -} - -void ScribbleArea::clearDrawingBuffer() { mTiledBuffer.clear(); } -void ScribbleArea::drawLine(QPointF P1, QPointF P2, QPen pen, QPainter::CompositionMode cm) -{ - mBufferImg->drawLine(P1, P2, pen, cm, mPrefs->isOn(SETTING::ANTIALIAS)); -} - void ScribbleArea::drawPath(QPainterPath path, QPen pen, QBrush brush, QPainter::CompositionMode cm) { - mBufferImg->drawPath(mEditor->view()->mapScreenToCanvas(path), pen, brush, cm, mPrefs->isOn(SETTING::ANTIALIAS)); - update(mEditor->view()->mapCanvasToScreen(mBufferImg->bounds()).toRect().adjusted(-1, -1, 1, 1)); + mTiledBuffer.drawPath(mEditor->view()->mapScreenToCanvas(path), pen, brush, cm, mPrefs->isOn(SETTING::ANTIALIAS)); } void ScribbleArea::paintCanvasCursor(QPainter& painter) @@ -1235,7 +1225,7 @@ void ScribbleArea::prepCanvas(int frame, QRect rect) mCanvasPainter.setViewTransform(vm->getView(), vm->getViewInverse()); mCanvasPainter.setTransformedSelection(sm->mySelectionRect().toRect(), sm->selectionTransform()); - mCanvasPainter.setPaintSettings(object, mEditor->layers()->currentLayerIndex(), frame, rect, mBufferImg, &mTiledBuffer); + mCanvasPainter.setPaintSettings(object, mEditor->layers()->currentLayerIndex(), frame, rect, &mTiledBuffer); } void ScribbleArea::drawCanvas(int frame, QRect rect) @@ -1438,9 +1428,7 @@ void ScribbleArea::liquifyBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPoi } } } - mBufferImg->paste(&bmiTmpClip); - - update(mEditor->view()->mapCanvasToScreen(mBufferImg->bounds()).toRect().adjusted(-1, -1, 1, 1)); + mTiledBuffer.drawImage(*bmiTmpClip.image(), bmiTmpClip.topLeft(), QPainter::CompositionMode_SourceOver, true); } /************************************************************************************/ diff --git a/core_lib/src/interface/scribblearea.h b/core_lib/src/interface/scribblearea.h index 82b6e48c10..a5dd4bbf91 100644 --- a/core_lib/src/interface/scribblearea.h +++ b/core_lib/src/interface/scribblearea.h @@ -185,7 +185,6 @@ public slots: public: void drawPolyline(QPainterPath path, QPen pen, bool useAA); - void drawLine(QPointF P1, QPointF P2, QPen pen, QPainter::CompositionMode cm); void drawPath(QPainterPath path, QPen pen, QBrush brush, QPainter::CompositionMode cm); void drawPen(QPointF thePoint, qreal brushWidth, QColor fillColor, bool useAA = true); void drawPencil(QPointF thePoint, qreal brushWidth, qreal fixedBrushFeather, QColor fillColor, qreal opacity); @@ -195,7 +194,6 @@ public slots: void paintBitmapBuffer(); void paintCanvasCursor(QPainter& painter); - void clearBitmapBuffer(); void clearDrawingBuffer(); void refreshBitmap(const QRectF& rect, int rad); void refreshVector(const QRectF& rect, int rad); @@ -211,7 +209,6 @@ public slots: /// on an empty frame, and if so, takes action according to use preference. void handleDrawingOnEmptyFrame(); - BitmapImage* mBufferImg = nullptr; // used to pre-draw vector modifications TiledBuffer mTiledBuffer; QPixmap mCursorImg; diff --git a/core_lib/src/tool/brushtool.cpp b/core_lib/src/tool/brushtool.cpp index 6411edee36..653f1974cb 100644 --- a/core_lib/src/tool/brushtool.cpp +++ b/core_lib/src/tool/brushtool.cpp @@ -290,7 +290,7 @@ void BrushTool::paintVectorStroke() if (layer->type() == Layer::VECTOR && mStrokePoints.size() > -1) { // Clear the temporary pixel path - mScribbleArea->clearBitmapBuffer(); + mScribbleArea->clearDrawingBuffer(); qreal tol = mScribbleArea->getCurveSmoothing() / mEditor->view()->scaling(); BezierCurve curve(mStrokePoints, mStrokePressures, tol); diff --git a/core_lib/src/tool/buckettool.cpp b/core_lib/src/tool/buckettool.cpp index 2ef4f01aa5..cd6161df07 100644 --- a/core_lib/src/tool/buckettool.cpp +++ b/core_lib/src/tool/buckettool.cpp @@ -284,7 +284,7 @@ void BucketTool::paintBitmap() void BucketTool::paintVector(Layer* layer) { - mScribbleArea->clearBitmapBuffer(); + mScribbleArea->clearDrawingBuffer(); VectorImage* vectorImage = static_cast(layer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0); if (vectorImage == nullptr) { return; } // Can happen if the first frame is deleted while drawing diff --git a/core_lib/src/tool/erasertool.cpp b/core_lib/src/tool/erasertool.cpp index ce17e67aa2..1fa4c5b77c 100644 --- a/core_lib/src/tool/erasertool.cpp +++ b/core_lib/src/tool/erasertool.cpp @@ -274,7 +274,7 @@ void EraserTool::removeVectorPaint() Layer* layer = mEditor->layers()->currentLayer(); if (layer->type() == Layer::VECTOR) { - mScribbleArea->clearBitmapBuffer(); + mScribbleArea->clearDrawingBuffer(); VectorImage* vectorImage = static_cast(layer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0); if (vectorImage == nullptr) { return; } // Can happen if the first frame is deleted while drawing // Clear the area containing the last point diff --git a/core_lib/src/tool/penciltool.cpp b/core_lib/src/tool/penciltool.cpp index 53950f4dfc..6a891ee44d 100644 --- a/core_lib/src/tool/penciltool.cpp +++ b/core_lib/src/tool/penciltool.cpp @@ -281,7 +281,7 @@ void PencilTool::paintVectorStroke(Layer* layer) return; // Clear the temporary pixel path - mScribbleArea->clearBitmapBuffer(); + mScribbleArea->clearDrawingBuffer(); qreal tol = mScribbleArea->getCurveSmoothing() / mEditor->view()->scaling(); BezierCurve curve(mStrokePoints, mStrokePressures, tol); diff --git a/core_lib/src/tool/pentool.cpp b/core_lib/src/tool/pentool.cpp index e2b1d21615..c13bce9cd5 100644 --- a/core_lib/src/tool/pentool.cpp +++ b/core_lib/src/tool/pentool.cpp @@ -238,7 +238,7 @@ void PenTool::drawStroke() void PenTool::paintBitmapStroke() { mScribbleArea->paintBitmapBuffer(); - mScribbleArea->clearBitmapBuffer(); + mScribbleArea->clearDrawingBuffer(); } void PenTool::paintVectorStroke(Layer* layer) @@ -247,7 +247,7 @@ void PenTool::paintVectorStroke(Layer* layer) return; // Clear the temporary pixel path - mScribbleArea->clearBitmapBuffer(); + mScribbleArea->clearDrawingBuffer(); qreal tol = mScribbleArea->getCurveSmoothing() / mEditor->view()->scaling(); BezierCurve curve(mStrokePoints, mStrokePressures, tol); diff --git a/core_lib/src/tool/smudgetool.cpp b/core_lib/src/tool/smudgetool.cpp index 0393e94913..f555380009 100644 --- a/core_lib/src/tool/smudgetool.cpp +++ b/core_lib/src/tool/smudgetool.cpp @@ -257,7 +257,7 @@ void SmudgeTool::pointerReleaseEvent(PointerEvent* event) { drawStroke(); mScribbleArea->paintBitmapBuffer(); - mScribbleArea->clearBitmapBuffer(); + mScribbleArea->clearDrawingBuffer(); endStroke(); } else if (layer->type() == Layer::VECTOR) @@ -317,7 +317,7 @@ void SmudgeTool::drawStroke() QPointF sourcePoint = mLastBrushPoint; for (int i = 0; i < steps; i++) { - targetImage.paste(mScribbleArea->mBufferImg); + targetImage.paste(&mScribbleArea->mTiledBuffer); QPointF targetPoint = mLastBrushPoint + (i + 1) * (brushStep) * (b - mLastBrushPoint) / distance; mScribbleArea->liquifyBrush(&targetImage, sourcePoint, @@ -344,7 +344,6 @@ void SmudgeTool::drawStroke() { targetImage.paste(&mScribbleArea->mTiledBuffer); QPointF targetPoint = mLastBrushPoint + (i + 1) * (brushStep) * (b - mLastBrushPoint) / distance; - rect.extend(targetPoint.toPoint()); mScribbleArea->blurBrush(&targetImage, sourcePoint, targetPoint, From 3eceedd4c8c71692a3dcc6aa3eee036b13823cf1 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Sat, 15 Jul 2023 12:47:24 +0200 Subject: [PATCH 16/49] Make TiledBuffer work for VectorImage --- core_lib/src/canvaspainter.cpp | 8 ++++++-- core_lib/src/interface/scribblearea.cpp | 16 ++++++++++++---- core_lib/src/tool/brushtool.cpp | 12 ++++-------- core_lib/src/tool/brushtool.h | 3 ++- core_lib/src/tool/erasertool.cpp | 1 + core_lib/src/tool/penciltool.cpp | 10 ++++------ core_lib/src/tool/pentool.cpp | 7 +++---- 7 files changed, 32 insertions(+), 25 deletions(-) diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index 66234a8c11..6e82c2ef75 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -327,7 +327,7 @@ 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); @@ -335,7 +335,11 @@ void CanvasPainter::paintCurrentVectorFrame(QPainter& painter, const QRect& blit 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->pos(), tile->pixmap()); + } } else if (mRenderTransform) { vectorImage->setSelectionTransformation(mSelectionTransform); } diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index 955b3575c8..d2bd8b4ec7 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -195,9 +195,15 @@ void ScribbleArea::updateTile(TiledBuffer* tiledBuffer, Tile* tile) void ScribbleArea::loadTile(TiledBuffer* tiledBuffer, Tile* tile) { - const auto& bitmapImage = currentBitmapImage(mEditor->layers()->currentLayer()); - const QImage& image = *bitmapImage->image(); - mTiledBuffer.loadTile(image, bitmapImage->topLeft(), tile); + Layer::LAYER_TYPE layerType = mEditor->layers()->currentLayer()->type(); + if (layerType == Layer::BITMAP) { + const auto& bitmapImage = currentBitmapImage(mEditor->layers()->currentLayer()); + const QImage& image = *bitmapImage->image(); + mTiledBuffer.loadTile(image, bitmapImage->topLeft(), tile); + } else if (layerType == Layer::VECTOR) { + + // Not used, we only use the buffer to paint the stroke before painting the real vector stroke + } const QRectF& mappedRect = mEditor->view()->getView().mapRect(QRectF(tile->pos(), tile->boundingRect().size())); update(mappedRect.toRect()); @@ -1309,7 +1315,9 @@ void ScribbleArea::drawPolyline(QPainterPath path, QPen pen, bool useAA) void ScribbleArea::endStroke() { - paintBitmapBuffer(); + if (mEditor->layers()->currentLayer()->type() == Layer::BITMAP) { + paintBitmapBuffer(); + } int frameNumber = mEditor->currentFrame(); if (mPrefs->isOn(SETTING::PREV_ONION) || mPrefs->isOn(SETTING::NEXT_ONION)) { diff --git a/core_lib/src/tool/brushtool.cpp b/core_lib/src/tool/brushtool.cpp index 653f1974cb..d066145fc8 100644 --- a/core_lib/src/tool/brushtool.cpp +++ b/core_lib/src/tool/brushtool.cpp @@ -173,11 +173,9 @@ void BrushTool::pointerReleaseEvent(PointerEvent *event) drawStroke(); } -// mScribbleArea->paintBitmapBuffer(); -// if (layer->type() == Layer::BITMAP) -// paintBitmapStroke(); -// else if (layer->type() == Layer::VECTOR) -// paintVectorStroke(); + if (layer->type() == Layer::VECTOR) { + paintVectorStroke(layer); + } endStroke(); } @@ -280,13 +278,11 @@ void BrushTool::drawStroke() // This function uses the points from DrawStroke // and turns them into vector lines. -void BrushTool::paintVectorStroke() +void BrushTool::paintVectorStroke(Layer* layer) { if (mStrokePoints.empty()) return; - Layer* layer = mEditor->layers()->currentLayer(); - if (layer->type() == Layer::VECTOR && mStrokePoints.size() > -1) { // Clear the temporary pixel path diff --git a/core_lib/src/tool/brushtool.h b/core_lib/src/tool/brushtool.h index f5b59fc9e9..0e82d65101 100644 --- a/core_lib/src/tool/brushtool.h +++ b/core_lib/src/tool/brushtool.h @@ -21,6 +21,7 @@ GNU General Public License for more details. #include "stroketool.h" #include +class Layer; class BrushTool : public StrokeTool { @@ -38,7 +39,7 @@ class BrushTool : public StrokeTool void pointerReleaseEvent(PointerEvent*) override; void drawStroke(); - void paintVectorStroke(); + void paintVectorStroke(Layer* layer); void paintAt(QPointF point); void setWidth(const qreal width) override; diff --git a/core_lib/src/tool/erasertool.cpp b/core_lib/src/tool/erasertool.cpp index 1fa4c5b77c..94c168bb9c 100644 --- a/core_lib/src/tool/erasertool.cpp +++ b/core_lib/src/tool/erasertool.cpp @@ -180,6 +180,7 @@ void EraserTool::pointerReleaseEvent(PointerEvent *event) { drawStroke(); } + removeVectorPaint(); endStroke(); } diff --git a/core_lib/src/tool/penciltool.cpp b/core_lib/src/tool/penciltool.cpp index 6a891ee44d..5e5e04636d 100644 --- a/core_lib/src/tool/penciltool.cpp +++ b/core_lib/src/tool/penciltool.cpp @@ -29,7 +29,6 @@ GNU General Public License for more details. #include "editor.h" #include "scribblearea.h" -#include "blitrect.h" #include "layervector.h" #include "vectorimage.h" @@ -186,11 +185,10 @@ void PencilTool::pointerReleaseEvent(PointerEvent *event) drawStroke(); } -// Layer* layer = mEditor->layers()->currentLayer(); -// if (layer->type() == Layer::BITMAP) -// paintBitmapStroke(); -// else if (layer->type() == Layer::VECTOR) -// paintVectorStroke(layer); + Layer* layer = mEditor->layers()->currentLayer(); + if (layer->type() == Layer::VECTOR) { + paintVectorStroke(layer); + } endStroke(); } diff --git a/core_lib/src/tool/pentool.cpp b/core_lib/src/tool/pentool.cpp index c13bce9cd5..4b07b18078 100644 --- a/core_lib/src/tool/pentool.cpp +++ b/core_lib/src/tool/pentool.cpp @@ -153,10 +153,9 @@ void PenTool::pointerReleaseEvent(PointerEvent *event) drawStroke(); } -// if (layer->type() == Layer::BITMAP) -// paintBitmapStroke(); -// else if (layer->type() == Layer::VECTOR) -// paintVectorStroke(layer); + if (layer->type() == Layer::VECTOR) { + paintVectorStroke(layer); + } endStroke(); } From b958ce5e36b1a0e533685908dbdc0db06656a01e Mon Sep 17 00:00:00 2001 From: MrStevns Date: Sat, 15 Jul 2023 13:21:20 +0200 Subject: [PATCH 17/49] Cleanup dead code in TiledBuffer --- core_lib/src/graphics/bitmap/tiledbuffer.cpp | 29 -------------------- core_lib/src/graphics/bitmap/tiledbuffer.h | 19 ------------- 2 files changed, 48 deletions(-) diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.cpp b/core_lib/src/graphics/bitmap/tiledbuffer.cpp index b85711a0d3..ebe57a0aae 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.cpp +++ b/core_lib/src/graphics/bitmap/tiledbuffer.cpp @@ -131,25 +131,6 @@ void TiledBuffer::drawPath(QPainterPath path, QPen pen, QBrush brush, { int width = pen.width(); - - -// QPainter painter(image()); -// painter.setCompositionMode(cm); -// painter.setRenderHint(QPainter::Antialiasing, antialiasing); -// painter.setPen(pen); -// painter.setBrush(brush); -// painter.setWorldMatrixEnabled(true); -// if (path.length() > 0) -// { -// painter.drawPath( path ); -// } -// else -// { -// // forces drawing when points are coincident (mousedown) -// painter.drawPoint(static_cast(path.elementAt(0).x), static_cast(path.elementAt(0).y)); -// } -// painter.end(); - int radius = (width + 1); float tilesize = UNIFORM_TILESIZE; QRectF pathRect = path.boundingRect(); @@ -214,16 +195,6 @@ void TiledBuffer::clear() emit this->onClearedSurface(this); } -//Status TiledBuffer::writeFile(const QString& filename) -//{ -// if (mTiles.values().first()) { -// const QImage& image = surfaceAsImage(); -// bool b = image.save(filename); -// return (b) ? Status::OK : Status::FAIL; -// } -// return Status::FAIL; -//} - QPoint TiledBuffer::getTilePos(const TileIndex& index) const { return QPoint { qRound(UNIFORM_TILESIZE*static_cast(index.x)), diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.h b/core_lib/src/graphics/bitmap/tiledbuffer.h index 076e31b492..616995eac7 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.h +++ b/core_lib/src/graphics/bitmap/tiledbuffer.h @@ -99,25 +99,6 @@ class TiledBuffer: public QObject */ QRect getRectForPoint(const QPoint& point) const; - /** - * @brief touchedTiles - * Will find and return points within and surrounding the selection - * @param QRect rect - * @return list of touched points - */ - QList touchedTiles(const QRect& rect); - -// /** -// * @brief BitmapSurface::intersectedSurface -// * Returns a Surface containing the tiles that intersected the region -// * @param rect -// * @return Surface -// */ -// Surface intersectedSurface(const QRect rect); - - QList scanForSurroundingTiles(const QRect& rect); - QList scanForTilesAtRect(const QRect& rect); - const int UNIFORM_TILESIZE = 64; BlitRect mTileBounds; From ec0650262cd6b954303d978a5fff286a3115f0fb Mon Sep 17 00:00:00 2001 From: MrStevns Date: Sat, 15 Jul 2023 14:13:55 +0200 Subject: [PATCH 18/49] Fix not painting bitmap layers when not on current frame --- core_lib/src/canvaspainter.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index 27b96f59b1..dbe0b7723c 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -305,14 +305,17 @@ void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blit for (const Tile* tile : tiles) { currentBitmapPainter.drawPixmap(tile->pos(), tile->pixmap()); } - } else { - currentBitmapPainter.drawImage(paintedImage->topLeft(), *paintedImage->image()); } if (mRenderTransform) { paintTransformedSelection(currentBitmapPainter, paintedImage, mSelection); } } + // When we're not showing the current layer or not drawing, paint the last known image reference + if (!isCurrentLayer || !isDrawing) { + currentBitmapPainter.drawImage(paintedImage->topLeft(), *paintedImage->image()); + } + painter.drawPixmap(blitRect, mCurrentLayerPixmap, blitRect); } From e916591aa7a87210f774c13f615fc51c013382bd Mon Sep 17 00:00:00 2001 From: MrStevns Date: Sun, 16 Jul 2023 10:19:03 +0200 Subject: [PATCH 19/49] Add missing license --- core_lib/src/graphics/bitmap/tile.cpp | 16 ++++++++++++++++ core_lib/src/graphics/bitmap/tile.h | 16 ++++++++++++++++ core_lib/src/graphics/bitmap/tiledbuffer.h | 15 +++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/core_lib/src/graphics/bitmap/tile.cpp b/core_lib/src/graphics/bitmap/tile.cpp index 294b77ee3f..422eb7c898 100644 --- a/core_lib/src/graphics/bitmap/tile.cpp +++ b/core_lib/src/graphics/bitmap/tile.cpp @@ -1,3 +1,19 @@ +/* + +Pencil2D - Traditional Animation Software +Copyright (C) 2012-2020 Matthew Chiawen Chang + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +*/ + #include "tile.h" #include diff --git a/core_lib/src/graphics/bitmap/tile.h b/core_lib/src/graphics/bitmap/tile.h index 1561e82cdd..37a8bb31ee 100644 --- a/core_lib/src/graphics/bitmap/tile.h +++ b/core_lib/src/graphics/bitmap/tile.h @@ -1,3 +1,19 @@ +/* + +Pencil2D - Traditional Animation Software +Copyright (C) 2012-2020 Matthew Chiawen Chang + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +*/ + #ifndef TILE_H #define TILE_H diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.h b/core_lib/src/graphics/bitmap/tiledbuffer.h index 616995eac7..5fc760a3ae 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.h +++ b/core_lib/src/graphics/bitmap/tiledbuffer.h @@ -1,3 +1,18 @@ +/* + +Pencil2D - Traditional Animation Software +Copyright (C) 2012-2020 Matthew Chiawen Chang + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +*/ #ifndef TILEDBUFFER_H #define TILEDBUFFER_H From ed94ba6fb56e687ec57834ef18c900f0845096fb Mon Sep 17 00:00:00 2001 From: MrStevns Date: Sun, 16 Jul 2023 10:23:01 +0200 Subject: [PATCH 20/49] Remove dead code --- core_lib/src/interface/scribblearea.cpp | 48 ------------------------- core_lib/src/interface/scribblearea.h | 3 -- 2 files changed, 51 deletions(-) diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index 197deccdab..072e470566 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -1516,36 +1516,6 @@ void ScribbleArea::cancelTransformedSelection() } } -void ScribbleArea::displaySelectionProperties() -{ - Layer* layer = mEditor->layers()->currentLayer(); - if (layer == nullptr) { return; } - if (layer->type() == Layer::VECTOR) - { - VectorImage* vectorImage = currentVectorImage(layer); - if (vectorImage == nullptr) { return; } - //vectorImage->applySelectionTransformation(); - if (currentTool()->type() == MOVE) - { - int selectedCurve = vectorImage->getFirstSelectedCurve(); - if (selectedCurve != -1) - { - mEditor->tools()->setWidth(vectorImage->curve(selectedCurve).getWidth()); - mEditor->tools()->setFeather(vectorImage->curve(selectedCurve).getFeather()); - mEditor->tools()->setInvisibility(vectorImage->curve(selectedCurve).isInvisible()); - mEditor->tools()->setPressure(vectorImage->curve(selectedCurve).getVariableWidth()); - mEditor->color()->setColorNumber(vectorImage->curve(selectedCurve).getColorNumber()); - } - - int selectedArea = vectorImage->getFirstSelectedArea(); - if (selectedArea != -1) - { - mEditor->color()->setColorNumber(vectorImage->mArea[selectedArea].mColorNumber); - } - } - } -} - void ScribbleArea::toggleThinLines() { bool previousValue = mPrefs->isOn(SETTING::INVISIBLE_LINES); @@ -1685,21 +1655,3 @@ void ScribbleArea::paletteColorChanged(QColor color) invalidateAllCache(); } - -void ScribbleArea::floodFillError(int errorType) -{ - QString message, error; - if (errorType == 1) { message = tr("There is a gap in your drawing (or maybe you have zoomed too much)."); } - if (errorType == 2 || errorType == 3) - { - message = tr("Sorry! This doesn't always work." - "Please try again (zoom a bit, click at another location... )
" - "if it doesn't work, zoom a bit and check that your paths are connected by pressing F1.)."); - } - - if (errorType == 1) { error = tr("Out of bound.", "Bucket tool fill error message"); } - if (errorType == 2) { error = tr("Could not find a closed path.", "Bucket tool fill error message"); } - if (errorType == 3) { error = tr("Could not find the root index.", "Bucket tool fill error message"); } - QMessageBox::warning(this, tr("Flood fill error"), tr("%1

Error: %2").arg(message, error), QMessageBox::Ok, QMessageBox::Ok); - mEditor->deselectAll(); -} diff --git a/core_lib/src/interface/scribblearea.h b/core_lib/src/interface/scribblearea.h index a5dd4bbf91..731e7cf14e 100644 --- a/core_lib/src/interface/scribblearea.h +++ b/core_lib/src/interface/scribblearea.h @@ -68,7 +68,6 @@ class ScribbleArea : public QWidget Editor* editor() const { return mEditor; } void deleteSelection(); - void displaySelectionProperties(); void applyTransformedSelection(); void cancelTransformedSelection(); @@ -140,8 +139,6 @@ class ScribbleArea : public QWidget BaseTool* getTool(ToolType eToolMode); void setCurrentTool(ToolType eToolMode); - void floodFillError(int errorType); - bool isMouseInUse() const { return mMouseInUse; } bool isTabletInUse() const { return mTabletInUse; } bool isPointerInUse() const { return mMouseInUse || mTabletInUse; } From a16efa9e3e59ddeefba018e2f238cea8463fd8b4 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Sun, 16 Jul 2023 14:33:25 +0200 Subject: [PATCH 21/49] Fix drawImage would not get proper update bounds --- core_lib/src/graphics/bitmap/tiledbuffer.cpp | 12 ++++++------ core_lib/src/graphics/bitmap/tiledbuffer.h | 2 +- core_lib/src/interface/scribblearea.cpp | 5 +++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.cpp b/core_lib/src/graphics/bitmap/tiledbuffer.cpp index ebe57a0aae..a5eb659a43 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.cpp +++ b/core_lib/src/graphics/bitmap/tiledbuffer.cpp @@ -93,16 +93,16 @@ void TiledBuffer::drawBrush(const QPointF& point, int brushWidth, QPen pen, QBru } } -void TiledBuffer::drawImage(const QImage& image, const QPointF& point, QPainter::CompositionMode cm, bool antialiasing) { +void TiledBuffer::drawImage(const QImage& image, const QRect& imageBounds, QPainter::CompositionMode cm, bool antialiasing) { float tilesize = UNIFORM_TILESIZE; float imageXRad = image.width(); float imageYRad = image.height(); // Gather the amount of tiles that fits the size of the brush width - int xLeft = qFloor((qFloor(point.x() - imageXRad)) / tilesize); - int xRight = qFloor((qFloor(point.x() + imageXRad)) / tilesize); - int yTop = qFloor(qFloor(point.y() - imageYRad) / tilesize); - int yBottom = qFloor(qFloor(point.y() + imageYRad) / tilesize); + int xLeft = qFloor((qFloor(imageBounds.left() - imageXRad)) / tilesize); + int xRight = qFloor((qFloor(imageBounds.right() + imageXRad)) / tilesize); + int yTop = qFloor(qFloor(imageBounds.top() - imageYRad) / tilesize); + int yBottom = qFloor(qFloor(imageBounds.bottom() + imageYRad) / tilesize); for (int tileY = yTop; tileY <= yBottom; tileY++) { for (int tileX = xLeft; tileX <= xRight; tileX++) { @@ -116,7 +116,7 @@ void TiledBuffer::drawImage(const QImage& image, const QPointF& point, QPainter: painter.translate(-tile->pos()); painter.setRenderHint(QPainter::Antialiasing, antialiasing); painter.setCompositionMode(cm); - painter.drawImage(point, image); + painter.drawImage(imageBounds.topLeft(), image); painter.end(); mTileBounds.extend(tile->pos(), tile->boundingRect().size()); diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.h b/core_lib/src/graphics/bitmap/tiledbuffer.h index 5fc760a3ae..c582f9ecf4 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.h +++ b/core_lib/src/graphics/bitmap/tiledbuffer.h @@ -77,7 +77,7 @@ class TiledBuffer: public QObject void drawBrush(const QPointF& point, int brushWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing); void drawPath(QPainterPath path, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing); - void drawImage(const QImage& image, const QPointF& point, QPainter::CompositionMode cm, bool antialiasing); + void drawImage(const QImage& image, const QRect& imageBounds, QPainter::CompositionMode cm, bool antialiasing); QHash tiles() const { return mTiles; } diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index 072e470566..33a4efff44 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -1035,6 +1035,7 @@ void ScribbleArea::paintEvent(QPaintEvent* event) // paints the canvas painter.setWorldMatrixEnabled(false); painter.drawPixmap(QPoint(0, 0), mCanvas); +// painter.drawRect(event->rect()); currentTool()->paint(painter); @@ -1384,7 +1385,7 @@ void ScribbleArea::blurBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPointF bmiTmpClip.drawRect(srcRect, Qt::NoPen, radialGrad, QPainter::CompositionMode_Source, true); bmiSrcClip.bounds().moveTo(trgRect.topLeft().toPoint()); bmiTmpClip.paste(&bmiSrcClip, QPainter::CompositionMode_SourceIn); - mTiledBuffer.drawImage(*bmiTmpClip.image(), bmiTmpClip.topLeft(), QPainter::CompositionMode_SourceOver, true); + mTiledBuffer.drawImage(*bmiTmpClip.image(), bmiTmpClip.bounds(), QPainter::CompositionMode_SourceOver, true); } void ScribbleArea::liquifyBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPointF thePoint_, qreal brushWidth_, qreal mOffset_, qreal opacity_) @@ -1436,7 +1437,7 @@ void ScribbleArea::liquifyBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPoi } } } - mTiledBuffer.drawImage(*bmiTmpClip.image(), bmiTmpClip.topLeft(), QPainter::CompositionMode_SourceOver, true); + mTiledBuffer.drawImage(*bmiTmpClip.image(), bmiTmpClip.bounds(), QPainter::CompositionMode_SourceOver, true); } /************************************************************************************/ From 63ed190a3d11df21390b8213755ec44e6ad0a0a9 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Sun, 16 Jul 2023 15:07:53 +0200 Subject: [PATCH 22/49] Cleanup TiledBuffer and fix warnings --- core_lib/src/graphics/bitmap/bitmapimage.cpp | 5 +-- core_lib/src/graphics/bitmap/tile.cpp | 15 +------- core_lib/src/graphics/bitmap/tile.h | 6 +--- core_lib/src/graphics/bitmap/tiledbuffer.cpp | 2 +- core_lib/src/graphics/bitmap/tiledbuffer.h | 36 ++++++-------------- core_lib/src/interface/scribblearea.cpp | 7 ++-- core_lib/src/tool/buckettool.cpp | 2 -- 7 files changed, 20 insertions(+), 53 deletions(-) diff --git a/core_lib/src/graphics/bitmap/bitmapimage.cpp b/core_lib/src/graphics/bitmap/bitmapimage.cpp index c2dc5a47a2..a29f02b113 100644 --- a/core_lib/src/graphics/bitmap/bitmapimage.cpp +++ b/core_lib/src/graphics/bitmap/bitmapimage.cpp @@ -216,7 +216,8 @@ void BitmapImage::paste(const TiledBuffer* tiledBuffer, QPainter::CompositionMod QPainter painter(image()); painter.setCompositionMode(cm); - for (const Tile* item : tiledBuffer->tiles()) { + 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); @@ -647,7 +648,7 @@ void BitmapImage::drawRect(QRectF rectangle, QPen pen, QBrush brush, QPainter::C painter.setPen(pen); painter.setBrush(brush); - // Adjust the brush rectangle to be bigger than the bounds itself, otherwize there will be artifacts shown in some cases + // Adjust the brush rectangle to be bigger than the bounds itself, otherwise there will be artifacts shown in some cases painter.drawRect(rectangle.translated(-mBounds.topLeft()).adjusted(-1, -1, 1, 1)); painter.end(); } diff --git a/core_lib/src/graphics/bitmap/tile.cpp b/core_lib/src/graphics/bitmap/tile.cpp index 422eb7c898..d56fd97a64 100644 --- a/core_lib/src/graphics/bitmap/tile.cpp +++ b/core_lib/src/graphics/bitmap/tile.cpp @@ -16,8 +16,6 @@ GNU General Public License for more details. #include "tile.h" -#include - Tile::Tile(QSize size): mTilePixmap(size) { @@ -38,18 +36,7 @@ QRect Tile::boundingRect() const return mTilePixmap.rect(); } -void Tile::replaceTile(const QPixmap& pixmap) -{ - if (pixmap.isNull()) { return; } - - mTilePixmap = pixmap; - mCacheValid = true; - -} - void Tile::clear() { - mTilePixmap.fill(Qt::transparent); // image cache is transparent too, and aligned to the pixel table: - mCacheValid = true; - mDirty = false; + mTilePixmap.fill(Qt::transparent); } diff --git a/core_lib/src/graphics/bitmap/tile.h b/core_lib/src/graphics/bitmap/tile.h index 37a8bb31ee..cc994481bb 100644 --- a/core_lib/src/graphics/bitmap/tile.h +++ b/core_lib/src/graphics/bitmap/tile.h @@ -17,6 +17,7 @@ GNU General Public License for more details. #ifndef TILE_H #define TILE_H +#include #include class Tile @@ -33,17 +34,12 @@ class Tile QRect boundingRect() const; void clear(); - void replaceTile(const QPixmap& pixmap); - bool isDirty() { return mDirty; } - void setDirty(bool dirty) { mDirty = dirty; } void setPos(const QPoint& pos) { m_pos = pos; } QPoint pos() const { return m_pos; } private: QPixmap mTilePixmap; - bool mCacheValid; - bool mDirty = false; QPoint m_pos; }; diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.cpp b/core_lib/src/graphics/bitmap/tiledbuffer.cpp index a5eb659a43..7e8816303c 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.cpp +++ b/core_lib/src/graphics/bitmap/tiledbuffer.cpp @@ -15,12 +15,12 @@ GNU General Public License for more details. */ #include "tiledbuffer.h" -#include "QtGui/qpainterpath.h" #include #include #include +#include #include #include #include diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.h b/core_lib/src/graphics/bitmap/tiledbuffer.h index c582f9ecf4..9215829421 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.h +++ b/core_lib/src/graphics/bitmap/tiledbuffer.h @@ -49,34 +49,21 @@ class TiledBuffer: public QObject TiledBuffer(QObject* parent = nullptr); ~TiledBuffer(); - Status writeFile(const QString& filename); - + /** Loads the input tile into the tiledBuffer */ void loadTile(const QImage& image, const QPoint& topLeft, Tile* tile); - /** - * @brief drawRectOnSurface - * For test and debug currently - * @param rect - * @param color - */ - void drawRect(QRect rect, QColor color); - - bool isTransparent(QImage image); - - /** - * @brief extendBoundaries - * Extend boundaries of the surface if the input rectangle lies outside. - * @param rect - */ - void extendBoundaries(const QRect &rect); - + /** Clears the content of the tiled buffer */ void clear(); + /** Returns true if there are any tiles, otherwise false */ bool isValid() { return !mTiles.isEmpty(); } + /** Draws a brush with the specified parameters to the tiled buffer */ void drawBrush(const QPointF& point, int brushWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing); + /** Draws a path with the specified parameters to the tiled buffer */ void drawPath(QPainterPath path, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing); + /** Draws a image with the specified parameters to the tiled buffer */ void drawImage(const QImage& image, const QRect& imageBounds, QPainter::CompositionMode cm, bool antialiasing); QHash tiles() const { return mTiles; } @@ -85,10 +72,10 @@ class TiledBuffer: public QObject signals: - void onUpdateTile(TiledBuffer *tiledBuffer, Tile* tile); - void onNewTile(TiledBuffer *tiledBuffer, Tile* tile); - void onClearTile(TiledBuffer *tiledBuffer, Tile* tile); - void onClearedSurface(TiledBuffer *tiledBuffer); + void onUpdateTile(TiledBuffer* tiledBuffer, Tile* tile); + void onNewTile(TiledBuffer* tiledBuffer, Tile* tile); + void onClearTile(TiledBuffer* tiledBuffer, Tile* tile); + void onClearedSurface(TiledBuffer* tiledBuffer); private: @@ -120,9 +107,6 @@ class TiledBuffer: public QObject QHash mTiles; -// Surface mSurface; - QImage mCachedSurface; - bool mImageCacheValid = false; }; diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index 33a4efff44..962e71d75a 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -189,12 +189,14 @@ void ScribbleArea::setEffect(SETTING e, bool isOn) void ScribbleArea::updateTile(TiledBuffer* tiledBuffer, Tile* tile) { + Q_UNUSED(tiledBuffer); const QRectF& mappedRect = mEditor->view()->getView().mapRect(QRectF(tile->pos(), tile->boundingRect().size())); update(mappedRect.toRect()); } void ScribbleArea::loadTile(TiledBuffer* tiledBuffer, Tile* tile) { + Q_UNUSED(tiledBuffer) Layer::LAYER_TYPE layerType = mEditor->layers()->currentLayer()->type(); if (layerType == Layer::BITMAP) { const auto& bitmapImage = currentBitmapImage(mEditor->layers()->currentLayer()); @@ -1035,7 +1037,6 @@ void ScribbleArea::paintEvent(QPaintEvent* event) // paints the canvas painter.setWorldMatrixEnabled(false); painter.drawPixmap(QPoint(0, 0), mCanvas); -// painter.drawRect(event->rect()); currentTool()->paint(painter); @@ -1269,7 +1270,7 @@ void ScribbleArea::drawPen(QPointF thePoint, qreal brushWidth, QColor fillColor, { QRectF rectangle(thePoint.x() - 0.5 * brushWidth, thePoint.y() - 0.5 * brushWidth, brushWidth, brushWidth); - mTiledBuffer.drawBrush(thePoint, brushWidth, Qt::NoPen, QBrush(fillColor, Qt::SolidPattern), QPainter::CompositionMode_SourceOver, true); + mTiledBuffer.drawBrush(thePoint, brushWidth, Qt::NoPen, QBrush(fillColor, Qt::SolidPattern), QPainter::CompositionMode_SourceOver, useAA); } void ScribbleArea::drawPencil(QPointF thePoint, qreal brushWidth, qreal fixedBrushFeather, QColor fillColor, qreal opacity) @@ -1292,7 +1293,7 @@ void ScribbleArea::drawBrush(QPointF thePoint, qreal brushWidth, qreal mOffset, { brush = QBrush(fillColor, Qt::SolidPattern); } - mTiledBuffer.drawBrush(thePoint, brushWidth, Qt::NoPen, brush, QPainter::CompositionMode_SourceOver, true); + mTiledBuffer.drawBrush(thePoint, brushWidth, Qt::NoPen, brush, QPainter::CompositionMode_SourceOver, useAA); } void ScribbleArea::drawPolyline(QPainterPath path, QPen pen, bool useAA) diff --git a/core_lib/src/tool/buckettool.cpp b/core_lib/src/tool/buckettool.cpp index cd6161df07..69d4d17767 100644 --- a/core_lib/src/tool/buckettool.cpp +++ b/core_lib/src/tool/buckettool.cpp @@ -324,8 +324,6 @@ void BucketTool::drawStroke() if (layer->type() == Layer::VECTOR) { mCurrentWidth = 30; - int rad = qRound((mCurrentWidth / 2 + 2) * mEditor->view()->scaling()); - QColor pathColor = qPremultiply(mEditor->color()->frontColor().rgba()); QPen pen(pathColor, From 3d90e5c4e79fcaf37ed2881b17494d71ef65cab4 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Sun, 16 Jul 2023 15:08:42 +0200 Subject: [PATCH 23/49] Fix Eraser should use destinationOut composition --- core_lib/src/interface/scribblearea.cpp | 6 +++--- core_lib/src/interface/scribblearea.h | 2 +- core_lib/src/tool/brushtool.cpp | 2 ++ core_lib/src/tool/erasertool.cpp | 2 ++ 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index 962e71d75a..c20eae199f 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -1275,10 +1275,10 @@ void ScribbleArea::drawPen(QPointF thePoint, qreal brushWidth, QColor fillColor, void ScribbleArea::drawPencil(QPointF thePoint, qreal brushWidth, qreal fixedBrushFeather, QColor fillColor, qreal opacity) { - drawBrush(thePoint, brushWidth, fixedBrushFeather, fillColor, opacity, true); + drawBrush(thePoint, brushWidth, fixedBrushFeather, fillColor, QPainter::CompositionMode_SourceOver, opacity, true); } -void ScribbleArea::drawBrush(QPointF thePoint, qreal brushWidth, qreal mOffset, QColor fillColor, qreal opacity, bool usingFeather, bool useAA) +void ScribbleArea::drawBrush(QPointF thePoint, qreal brushWidth, qreal mOffset, QColor fillColor, QPainter::CompositionMode compMode, qreal opacity, bool usingFeather, bool useAA) { QRectF rectangle(thePoint.x() - 0.5 * brushWidth, thePoint.y() - 0.5 * brushWidth, brushWidth, brushWidth); @@ -1293,7 +1293,7 @@ void ScribbleArea::drawBrush(QPointF thePoint, qreal brushWidth, qreal mOffset, { brush = QBrush(fillColor, Qt::SolidPattern); } - mTiledBuffer.drawBrush(thePoint, brushWidth, Qt::NoPen, brush, QPainter::CompositionMode_SourceOver, useAA); + mTiledBuffer.drawBrush(thePoint, brushWidth, Qt::NoPen, brush, compMode, useAA); } void ScribbleArea::drawPolyline(QPainterPath path, QPen pen, bool useAA) diff --git a/core_lib/src/interface/scribblearea.h b/core_lib/src/interface/scribblearea.h index 731e7cf14e..250276bf68 100644 --- a/core_lib/src/interface/scribblearea.h +++ b/core_lib/src/interface/scribblearea.h @@ -185,7 +185,7 @@ public slots: void drawPath(QPainterPath path, QPen pen, QBrush brush, QPainter::CompositionMode cm); void drawPen(QPointF thePoint, qreal brushWidth, QColor fillColor, bool useAA = true); void drawPencil(QPointF thePoint, qreal brushWidth, qreal fixedBrushFeather, QColor fillColor, qreal opacity); - void drawBrush(QPointF thePoint, qreal brushWidth, qreal offset, QColor fillColor, qreal opacity, bool usingFeather = true, bool useAA = false); + void drawBrush(QPointF thePoint, qreal brushWidth, qreal offset, QColor fillColor, QPainter::CompositionMode compMode, qreal opacity, bool usingFeather = true, bool useAA = false); void blurBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPointF thePoint_, qreal brushWidth_, qreal offset_, qreal opacity_); void liquifyBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPointF thePoint_, qreal brushWidth_, qreal offset_, qreal opacity_); diff --git a/core_lib/src/tool/brushtool.cpp b/core_lib/src/tool/brushtool.cpp index d066145fc8..88b6b6e22e 100644 --- a/core_lib/src/tool/brushtool.cpp +++ b/core_lib/src/tool/brushtool.cpp @@ -195,6 +195,7 @@ void BrushTool::paintAt(QPointF point) brushWidth, properties.feather, mEditor->color()->frontColor(), + QPainter::CompositionMode_SourceOver, opacity, true); } @@ -231,6 +232,7 @@ void BrushTool::drawStroke() brushWidth, properties.feather, mEditor->color()->frontColor(), + QPainter::CompositionMode_SourceOver, opacity, true); if (i == (steps - 1)) diff --git a/core_lib/src/tool/erasertool.cpp b/core_lib/src/tool/erasertool.cpp index 94c168bb9c..0ae1005201 100644 --- a/core_lib/src/tool/erasertool.cpp +++ b/core_lib/src/tool/erasertool.cpp @@ -200,6 +200,7 @@ void EraserTool::paintAt(QPointF point) brushWidth, properties.feather, QColor(255, 255, 255, 255), + QPainter::CompositionMode_DestinationOut, opacity, properties.useFeather, properties.useAA == ON); @@ -239,6 +240,7 @@ void EraserTool::drawStroke() brushWidth, properties.feather, Qt::white, + QPainter::CompositionMode_DestinationOut, opacity, properties.useFeather, properties.useAA == ON); From 7812bfdfb7f9acf918289ef481258797be5d19e1 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Thu, 20 Jul 2023 18:46:51 +0200 Subject: [PATCH 24/49] 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. --- core_lib/src/graphics/vector/vectorimage.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core_lib/src/graphics/vector/vectorimage.cpp b/core_lib/src/graphics/vector/vectorimage.cpp index 18108742be..9dfe0980c7 100644 --- a/core_lib/src/graphics/vector/vectorimage.cpp +++ b/core_lib/src/graphics/vector/vectorimage.cpp @@ -1203,6 +1203,7 @@ void VectorImage::paintImage(QPainter& painter, bool showThinCurves, bool antialiasing) { + painter.save(); painter.setRenderHint(QPainter::Antialiasing, antialiasing); painter.setClipping(false); @@ -1248,6 +1249,7 @@ void VectorImage::paintImage(QPainter& painter, curve.drawPath(painter, mObject, mSelectionTransformation, simplified, showThinCurves); painter.setClipping(false); } + painter.restore(); } /** From fb4bc79b55a05ea880e28f8e58772492bfe9a1f7 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Thu, 20 Jul 2023 19:05:58 +0200 Subject: [PATCH 25/49] Fix Polyline can't be removed after applying on vector --- core_lib/src/canvaspainter.cpp | 2 +- core_lib/src/tool/polylinetool.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index dbe0b7723c..7b6ff1dd6b 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -353,7 +353,7 @@ 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); } diff --git a/core_lib/src/tool/polylinetool.cpp b/core_lib/src/tool/polylinetool.cpp index 41f906cdb5..411314fd23 100644 --- a/core_lib/src/tool/polylinetool.cpp +++ b/core_lib/src/tool/polylinetool.cpp @@ -250,7 +250,6 @@ void PolylineTool::cancelPolyline() void PolylineTool::endPolyline(QList points) { Layer* layer = mEditor->layers()->currentLayer(); -// mScribbleArea->clearBitmapBuffer(); if (layer->type() == Layer::VECTOR) { @@ -277,4 +276,5 @@ void PolylineTool::endPolyline(QList points) } mScribbleArea->endStroke(); mEditor->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame()); + mScribbleArea->clearDrawingBuffer(); } From 3154b7c50f68e525f477a097c06663a1cc4ceba7 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Thu, 20 Jul 2023 19:37:31 +0200 Subject: [PATCH 26/49] Fix transformed selection was being painted in wrong order --- core_lib/src/canvaspainter.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index 7b6ff1dd6b..0ae6977747 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -293,9 +293,15 @@ void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blit painter.setOpacity(paintedImage->getOpacity() - (1.0-painter.opacity())); painter.setWorldMatrixEnabled(false); + bool shouldPaintTransformedSelection = mRenderTransform; if (isCurrentLayer) { if (isDrawing) { + // Certain tools require being painted continously, for example the Polyline tool. + // The tiled buffer does not update the area outside of which it paints, + // so in the case in order to see the previously laid down polyline stroke + // the area around it must be drawn again before + // applying the new tiled output on top if (mOptions.bIgnoreCanvasBuffer) { currentBitmapPainter.setCompositionMode(QPainter::CompositionMode_Source); currentBitmapPainter.drawImage(paintedImage->topLeft(), *paintedImage->image()); @@ -306,16 +312,22 @@ void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blit currentBitmapPainter.drawPixmap(tile->pos(), tile->pixmap()); } } - if (mRenderTransform) { - paintTransformedSelection(currentBitmapPainter, paintedImage, mSelection); - } + } else { + // We do not wish to draw selection transformations on anything but the current layer + shouldPaintTransformedSelection = false; } - // When we're not showing the current layer or not drawing, paint the last known image reference + // 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 if (!isCurrentLayer || !isDrawing) { currentBitmapPainter.drawImage(paintedImage->topLeft(), *paintedImage->image()); } + if (shouldPaintTransformedSelection) { + paintTransformedSelection(currentBitmapPainter, paintedImage, mSelection); + } + painter.drawPixmap(blitRect, mCurrentLayerPixmap, blitRect); } From 650ef9725775ca2677d00d8cb461c33083287446 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Thu, 20 Jul 2023 20:29:01 +0200 Subject: [PATCH 27/49] Cleanup after testing --- core_lib/src/interface/scribblearea.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index c20eae199f..e4ff819c8c 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -1268,8 +1268,6 @@ void ScribbleArea::setGaussianGradient(QGradient &gradient, QColor color, qreal void ScribbleArea::drawPen(QPointF thePoint, qreal brushWidth, QColor fillColor, bool useAA) { - QRectF rectangle(thePoint.x() - 0.5 * brushWidth, thePoint.y() - 0.5 * brushWidth, brushWidth, brushWidth); - mTiledBuffer.drawBrush(thePoint, brushWidth, Qt::NoPen, QBrush(fillColor, Qt::SolidPattern), QPainter::CompositionMode_SourceOver, useAA); } @@ -1280,8 +1278,6 @@ void ScribbleArea::drawPencil(QPointF thePoint, qreal brushWidth, qreal fixedBru void ScribbleArea::drawBrush(QPointF thePoint, qreal brushWidth, qreal mOffset, QColor fillColor, QPainter::CompositionMode compMode, qreal opacity, bool usingFeather, bool useAA) { - QRectF rectangle(thePoint.x() - 0.5 * brushWidth, thePoint.y() - 0.5 * brushWidth, brushWidth, brushWidth); - QBrush brush; if (usingFeather) { @@ -1386,7 +1382,7 @@ void ScribbleArea::blurBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPointF bmiTmpClip.drawRect(srcRect, Qt::NoPen, radialGrad, QPainter::CompositionMode_Source, true); bmiSrcClip.bounds().moveTo(trgRect.topLeft().toPoint()); bmiTmpClip.paste(&bmiSrcClip, QPainter::CompositionMode_SourceIn); - mTiledBuffer.drawImage(*bmiTmpClip.image(), bmiTmpClip.bounds(), QPainter::CompositionMode_SourceOver, true); + mTiledBuffer.drawImage(*bmiTmpClip.image(), bmiTmpClip.bounds(), QPainter::CompositionMode_SourceOver, mPrefs->isOn(SETTING::ANTIALIAS)); } void ScribbleArea::liquifyBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPointF thePoint_, qreal brushWidth_, qreal mOffset_, qreal opacity_) @@ -1438,7 +1434,7 @@ void ScribbleArea::liquifyBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPoi } } } - mTiledBuffer.drawImage(*bmiTmpClip.image(), bmiTmpClip.bounds(), QPainter::CompositionMode_SourceOver, true); + mTiledBuffer.drawImage(*bmiTmpClip.image(), bmiTmpClip.bounds(), QPainter::CompositionMode_SourceOver, mPrefs->isOn(SETTING::ANTIALIAS)); } /************************************************************************************/ From 6ac8c5f23c1104e05469c20a31ed3ba44f75b88e Mon Sep 17 00:00:00 2001 From: MrStevns Date: Fri, 21 Jul 2023 08:48:34 +0200 Subject: [PATCH 28/49] Fix tabletRestorePrevTool would always ask for a full canvas update --- core_lib/src/managers/toolmanager.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/core_lib/src/managers/toolmanager.cpp b/core_lib/src/managers/toolmanager.cpp index add88969db..0a53b2c774 100644 --- a/core_lib/src/managers/toolmanager.cpp +++ b/core_lib/src/managers/toolmanager.cpp @@ -334,6 +334,9 @@ int ToolManager::propertySwitch(bool condition, int tool) void ToolManager::tabletSwitchToEraser() { mTabletEraserTool = getTool(ERASER); + + // We should only notify a tool change if we're positive that the state has changed and it should only happen once + // if the user for some reason is using another temporary tool at the same time, that takes first priority if (mTemporaryTool == nullptr) { emit toolChanged(ERASER); @@ -341,10 +344,10 @@ void ToolManager::tabletSwitchToEraser() } void ToolManager::tabletRestorePrevTool() -{ - mTabletEraserTool = nullptr; - if (mTemporaryTool == nullptr) +{ + if (mTemporaryTool == nullptr && mTabletEraserTool != nullptr) { + mTabletEraserTool = nullptr; emit toolChanged(currentTool()->type()); } } From b8ef656174a4114bc8fc8063e7bece43ec37d0db Mon Sep 17 00:00:00 2001 From: MrStevns Date: Sat, 22 Jul 2023 11:15:17 +0200 Subject: [PATCH 29/49] Remove unnecessary clear of cache when setting tool --- app/src/mainwindow2.cpp | 2 +- core_lib/src/interface/scribblearea.cpp | 15 --------------- core_lib/src/interface/scribblearea.h | 2 -- core_lib/src/managers/toolmanager.cpp | 3 +++ 4 files changed, 4 insertions(+), 18 deletions(-) diff --git a/app/src/mainwindow2.cpp b/app/src/mainwindow2.cpp index bbe92fea03..62f28a0337 100644 --- a/app/src/mainwindow2.cpp +++ b/app/src/mainwindow2.cpp @@ -1442,7 +1442,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); diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index e4ff819c8c..6749d5b47f 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -1558,21 +1558,6 @@ BaseTool* ScribbleArea::currentTool() const return editor()->tools()->currentTool(); } -BaseTool* ScribbleArea::getTool(ToolType eToolType) -{ - return editor()->tools()->getTool(eToolType); -} - -void ScribbleArea::setCurrentTool(ToolType eToolMode) -{ - Q_UNUSED(eToolMode) - - // change cursor - setCursor(currentTool()->cursor()); - updateCanvasCursor(); - updateCurrentFrame(); -} - void ScribbleArea::deleteSelection() { auto selectMan = mEditor->select(); diff --git a/core_lib/src/interface/scribblearea.h b/core_lib/src/interface/scribblearea.h index 250276bf68..a31fd35368 100644 --- a/core_lib/src/interface/scribblearea.h +++ b/core_lib/src/interface/scribblearea.h @@ -136,8 +136,6 @@ class ScribbleArea : public QWidget void flipSelection(bool flipVertical); BaseTool* currentTool() const; - BaseTool* getTool(ToolType eToolMode); - void setCurrentTool(ToolType eToolMode); bool isMouseInUse() const { return mMouseInUse; } bool isTabletInUse() const { return mTabletInUse; } diff --git a/core_lib/src/managers/toolmanager.cpp b/core_lib/src/managers/toolmanager.cpp index 0a53b2c774..eb9b0791d4 100644 --- a/core_lib/src/managers/toolmanager.cpp +++ b/core_lib/src/managers/toolmanager.cpp @@ -103,6 +103,9 @@ void ToolManager::setDefaultTool() void ToolManager::setCurrentTool(ToolType eToolType) { + // We're already using this tool + if (mCurrentTool == getTool(eToolType)) { return; } + if (mCurrentTool != nullptr) { mCurrentTool->leavingThisTool(); From 2fdc29d32792f6143dfdc67b859c036deb7eafa1 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Sun, 23 Jul 2023 11:34:20 +0200 Subject: [PATCH 30/49] 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. --- core_lib/src/graphics/bitmap/tiledbuffer.cpp | 27 ++++++++++---------- core_lib/src/graphics/bitmap/tiledbuffer.h | 4 +-- core_lib/src/interface/scribblearea.cpp | 25 ++++++++++-------- 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.cpp b/core_lib/src/graphics/bitmap/tiledbuffer.cpp index 7e8816303c..c41a3b386c 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.cpp +++ b/core_lib/src/graphics/bitmap/tiledbuffer.cpp @@ -58,17 +58,18 @@ Tile* TiledBuffer::getTileFromIndex(int tileX, int tileY) return selectedTile; } -void TiledBuffer::drawBrush(const QPointF& point, int brushWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing) { +void TiledBuffer::drawBrush(const QPointF& point, int brushWidth, int brushCursorWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing) { QRectF brushRect(point.x() - 0.5 * brushWidth, point.y() - 0.5 * brushWidth, brushWidth, brushWidth); - int radius = (brushWidth + 1); float tilesize = UNIFORM_TILESIZE; + int width = qMax(brushCursorWidth,brushWidth); + // Gather the amount of tiles that fits the size of the brush width - int xLeft = qFloor((qFloor(point.x() - radius)) / tilesize); - int xRight = qFloor((qFloor(point.x() + radius)) / tilesize); - int yTop = qFloor(qFloor(point.y() - radius) / tilesize); - int yBottom = qFloor(qFloor(point.y() + radius) / tilesize); + int xLeft = qFloor((qFloor(point.x() - width)) / tilesize); + int xRight = qFloor((qFloor(point.x() + width)) / tilesize); + int yTop = qFloor(qFloor(point.y() - width) / tilesize); + int yBottom = qFloor(qFloor(point.y() + width) / tilesize); for (int tileY = yTop; tileY <= yBottom; tileY++) { for (int tileX = xLeft; tileX <= xRight; tileX++) { @@ -126,20 +127,20 @@ void TiledBuffer::drawImage(const QImage& image, const QRect& imageBounds, QPain } -void TiledBuffer::drawPath(QPainterPath path, QPen pen, QBrush brush, +void TiledBuffer::drawPath(QPainterPath path, int cursorWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing) { - int width = pen.width(); + int pathWidth = pen.width(); - int radius = (width + 1); + int width = (qMax(pathWidth,cursorWidth) + 1); float tilesize = UNIFORM_TILESIZE; QRectF pathRect = path.boundingRect(); // Gather the amount of tiles that fits the size of the brush width - int xLeft = qFloor((qFloor(pathRect.left() - radius)) / tilesize); - int xRight = qFloor((qFloor(pathRect.right() + radius)) / tilesize); - int yTop = qFloor(qFloor(pathRect.top() - radius) / tilesize); - int yBottom = qFloor(qFloor(pathRect.bottom() + radius) / tilesize); + int xLeft = qFloor((qFloor(pathRect.left() - width)) / tilesize); + int xRight = qFloor((qFloor(pathRect.right() + width)) / tilesize); + int yTop = qFloor(qFloor(pathRect.top() - width) / tilesize); + int yBottom = qFloor(qFloor(pathRect.bottom() + width) / tilesize); for (int tileY = yTop; tileY <= yBottom; tileY++) { for (int tileX = xLeft; tileX <= xRight; tileX++) { diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.h b/core_lib/src/graphics/bitmap/tiledbuffer.h index 9215829421..110af60d50 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.h +++ b/core_lib/src/graphics/bitmap/tiledbuffer.h @@ -59,9 +59,9 @@ class TiledBuffer: public QObject bool isValid() { return !mTiles.isEmpty(); } /** Draws a brush with the specified parameters to the tiled buffer */ - void drawBrush(const QPointF& point, int brushWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing); + void drawBrush(const QPointF& point, int brushWidth, int brushCursorWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing); /** Draws a path with the specified parameters to the tiled buffer */ - void drawPath(QPainterPath path, QPen pen, QBrush brush, + void drawPath(QPainterPath path, int cursorWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing); /** Draws a image with the specified parameters to the tiled buffer */ void drawImage(const QImage& image, const QRect& imageBounds, QPainter::CompositionMode cm, bool antialiasing); diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index 6749d5b47f..f5b04d4329 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -868,11 +868,6 @@ void ScribbleArea::clearDrawingBuffer() mTiledBuffer.clear(); } -void ScribbleArea::drawPath(QPainterPath path, QPen pen, QBrush brush, QPainter::CompositionMode cm) -{ - mTiledBuffer.drawPath(mEditor->view()->mapScreenToCanvas(path), pen, brush, cm, mPrefs->isOn(SETTING::ANTIALIAS)); -} - void ScribbleArea::paintCanvasCursor(QPainter& painter) { QTransform view = mEditor->view()->getView(); @@ -922,9 +917,14 @@ void ScribbleArea::updateCanvasCursor() QPoint translatedPos = QPoint(static_cast(mTransformedCursorPos.x() - mCursorCenterPos.x()), static_cast(mTransformedCursorPos.y() - mCursorCenterPos.y())); - update(mTransCursImg.rect().adjusted(-1, -1, 1, 1) - .translated(translatedPos)); + QRect updateRect = mCursorImg.rect().adjusted(-1, -1, 1, 1).translated(translatedPos);; + + // When we're using a tool, the TiledBuffer will take care of this, + // we don't want to cause needless updates + if (!currentTool()->isActive()) { + update(updateRect); + } } void ScribbleArea::handleDrawingOnEmptyFrame() @@ -1266,9 +1266,14 @@ void ScribbleArea::setGaussianGradient(QGradient &gradient, QColor color, qreal gradient.setColorAt(1.0 - (offset / 100.0), QColor(r, g, b, mainColorAlpha - alphaAdded)); } +void ScribbleArea::drawPath(QPainterPath path, QPen pen, QBrush brush, QPainter::CompositionMode cm) +{ + mTiledBuffer.drawPath(mEditor->view()->mapScreenToCanvas(path), mEditor->view()->mapScreenToCanvas(mCursorImg.rect()).width(), pen, brush, cm, mPrefs->isOn(SETTING::ANTIALIAS)); +} + void ScribbleArea::drawPen(QPointF thePoint, qreal brushWidth, QColor fillColor, bool useAA) { - mTiledBuffer.drawBrush(thePoint, brushWidth, Qt::NoPen, QBrush(fillColor, Qt::SolidPattern), QPainter::CompositionMode_SourceOver, useAA); + mTiledBuffer.drawBrush(thePoint, brushWidth, mEditor->view()->mapScreenToCanvas(mCursorImg.rect()).width(), Qt::NoPen, QBrush(fillColor, Qt::SolidPattern), QPainter::CompositionMode_SourceOver, useAA); } void ScribbleArea::drawPencil(QPointF thePoint, qreal brushWidth, qreal fixedBrushFeather, QColor fillColor, qreal opacity) @@ -1289,7 +1294,7 @@ void ScribbleArea::drawBrush(QPointF thePoint, qreal brushWidth, qreal mOffset, { brush = QBrush(fillColor, Qt::SolidPattern); } - mTiledBuffer.drawBrush(thePoint, brushWidth, Qt::NoPen, brush, compMode, useAA); + mTiledBuffer.drawBrush(thePoint, brushWidth, mEditor->view()->mapScreenToCanvas(mCursorImg.rect()).width(), Qt::NoPen, brush, compMode, useAA); } void ScribbleArea::drawPolyline(QPainterPath path, QPen pen, bool useAA) @@ -1305,7 +1310,7 @@ void ScribbleArea::drawPolyline(QPainterPath path, QPen pen, bool useAA) blitRect.extend(updateRect); mTiledBuffer.clear(); - mTiledBuffer.drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_SourceOver, useAA); + mTiledBuffer.drawPath(path, mEditor->view()->mapScreenToCanvas(mCursorImg.rect()).width(), pen, Qt::NoBrush, QPainter::CompositionMode_SourceOver, useAA); // And update only the affected area update(blitRect.adjusted(-1, -1, 1, 1)); From 856f50983bf4e9b3957b6f87ba9469fa35137953 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Sun, 23 Jul 2023 12:57:08 +0200 Subject: [PATCH 31/49] Refactor TiledBuffer - Also add missing onClearTile callback, which is unused but implementation wise is correct now. --- core_lib/src/graphics/bitmap/tiledbuffer.cpp | 56 +++++++++----------- core_lib/src/graphics/bitmap/tiledbuffer.h | 6 +-- 2 files changed, 25 insertions(+), 37 deletions(-) diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.cpp b/core_lib/src/graphics/bitmap/tiledbuffer.cpp index c41a3b386c..9c65bfefaf 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.cpp +++ b/core_lib/src/graphics/bitmap/tiledbuffer.cpp @@ -16,14 +16,9 @@ GNU General Public License for more details. */ #include "tiledbuffer.h" -#include - #include -#include #include -#include #include -#include TiledBuffer::TiledBuffer(QObject* parent) : QObject(parent) { @@ -59,17 +54,15 @@ Tile* TiledBuffer::getTileFromIndex(int tileX, int tileY) } void TiledBuffer::drawBrush(const QPointF& point, int brushWidth, int brushCursorWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing) { - QRectF brushRect(point.x() - 0.5 * brushWidth, point.y() - 0.5 * brushWidth, brushWidth, brushWidth); - - float tilesize = UNIFORM_TILESIZE; - - int width = qMax(brushCursorWidth,brushWidth); + const QRectF brushRect(point.x() - 0.5 * brushWidth, point.y() - 0.5 * brushWidth, brushWidth, brushWidth); + const float tilesize = UNIFORM_TILESIZE; + const int width = qMax(brushCursorWidth,brushWidth); // Gather the amount of tiles that fits the size of the brush width - int xLeft = qFloor((qFloor(point.x() - width)) / tilesize); - int xRight = qFloor((qFloor(point.x() + width)) / tilesize); - int yTop = qFloor(qFloor(point.y() - width) / tilesize); - int yBottom = qFloor(qFloor(point.y() + width) / tilesize); + const int xLeft = qFloor((qFloor(point.x() - width)) / tilesize); + const int xRight = qFloor((qFloor(point.x() + width)) / tilesize); + const int yTop = qFloor(qFloor(point.y() - width) / tilesize); + const int yBottom = qFloor(qFloor(point.y() + width) / tilesize); for (int tileY = yTop; tileY <= yBottom; tileY++) { for (int tileX = xLeft; tileX <= xRight; tileX++) { @@ -95,15 +88,14 @@ void TiledBuffer::drawBrush(const QPointF& point, int brushWidth, int brushCurso } void TiledBuffer::drawImage(const QImage& image, const QRect& imageBounds, QPainter::CompositionMode cm, bool antialiasing) { - float tilesize = UNIFORM_TILESIZE; - - float imageXRad = image.width(); - float imageYRad = image.height(); + const float tilesize = UNIFORM_TILESIZE; + const float imageXRad = image.width(); + const float imageYRad = image.height(); // Gather the amount of tiles that fits the size of the brush width - int xLeft = qFloor((qFloor(imageBounds.left() - imageXRad)) / tilesize); - int xRight = qFloor((qFloor(imageBounds.right() + imageXRad)) / tilesize); - int yTop = qFloor(qFloor(imageBounds.top() - imageYRad) / tilesize); - int yBottom = qFloor(qFloor(imageBounds.bottom() + imageYRad) / tilesize); + const int xLeft = qFloor((qFloor(imageBounds.left() - imageXRad)) / tilesize); + const int xRight = qFloor((qFloor(imageBounds.right() + imageXRad)) / tilesize); + const int yTop = qFloor(qFloor(imageBounds.top() - imageYRad) / tilesize); + const int yBottom = qFloor(qFloor(imageBounds.bottom() + imageYRad) / tilesize); for (int tileY = yTop; tileY <= yBottom; tileY++) { for (int tileX = xLeft; tileX <= xRight; tileX++) { @@ -130,17 +122,16 @@ void TiledBuffer::drawImage(const QImage& image, const QRect& imageBounds, QPain void TiledBuffer::drawPath(QPainterPath path, int cursorWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing) { - int pathWidth = pen.width(); - - int width = (qMax(pathWidth,cursorWidth) + 1); - float tilesize = UNIFORM_TILESIZE; - QRectF pathRect = path.boundingRect(); + const int pathWidth = pen.width(); + const int width = (qMax(pathWidth,cursorWidth) + 1); + const float tilesize = UNIFORM_TILESIZE; + const QRectF pathRect = path.boundingRect(); // Gather the amount of tiles that fits the size of the brush width - int xLeft = qFloor((qFloor(pathRect.left() - width)) / tilesize); - int xRight = qFloor((qFloor(pathRect.right() + width)) / tilesize); - int yTop = qFloor(qFloor(pathRect.top() - width) / tilesize); - int yBottom = qFloor(qFloor(pathRect.bottom() + width) / tilesize); + const int xLeft = qFloor((qFloor(pathRect.left() - width)) / tilesize); + const int xRight = qFloor((qFloor(pathRect.right() + width)) / tilesize); + const int yTop = qFloor(qFloor(pathRect.top() - width) / tilesize); + const int yBottom = qFloor(qFloor(pathRect.bottom() + width) / tilesize); for (int tileY = yTop; tileY <= yBottom; tileY++) { for (int tileX = xLeft; tileX <= xRight; tileX++) { @@ -186,6 +177,7 @@ void TiledBuffer::clear() // Clear the content of the tile // tile->clear(); + emit onClearTile(this, tile); mTiles.remove(i.key()); delete tile; } @@ -193,7 +185,7 @@ void TiledBuffer::clear() mTileBounds = BlitRect(); - emit this->onClearedSurface(this); + emit onClearBuffer(this); } QPoint TiledBuffer::getTilePos(const TileIndex& index) const diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.h b/core_lib/src/graphics/bitmap/tiledbuffer.h index 110af60d50..92d3d3ad77 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.h +++ b/core_lib/src/graphics/bitmap/tiledbuffer.h @@ -19,11 +19,9 @@ GNU General Public License for more details. #include #include #include - #include #include "blitrect.h" -#include "pencilerror.h" #include "tile.h" struct TileIndex { @@ -75,7 +73,7 @@ class TiledBuffer: public QObject void onUpdateTile(TiledBuffer* tiledBuffer, Tile* tile); void onNewTile(TiledBuffer* tiledBuffer, Tile* tile); void onClearTile(TiledBuffer* tiledBuffer, Tile* tile); - void onClearedSurface(TiledBuffer* tiledBuffer); + void onClearBuffer(TiledBuffer* tiledBuffer); private: @@ -106,8 +104,6 @@ class TiledBuffer: public QObject BlitRect mTileBounds; QHash mTiles; - - bool mImageCacheValid = false; }; #endif // TILEDBUFFER_H From 2c3bcc1f69bc61a0bc4e490c90061cf8ce6722fb Mon Sep 17 00:00:00 2001 From: Jakob Gahde Date: Wed, 9 Aug 2023 17:45:42 +0200 Subject: [PATCH 32/49] Some cleanup --- core_lib/src/canvaspainter.cpp | 48 +++---- core_lib/src/graphics/bitmap/bitmapimage.cpp | 4 +- core_lib/src/graphics/bitmap/tile.cpp | 19 ++- core_lib/src/graphics/bitmap/tile.h | 11 +- core_lib/src/graphics/bitmap/tiledbuffer.cpp | 135 ++++++++----------- core_lib/src/graphics/bitmap/tiledbuffer.h | 18 ++- core_lib/src/interface/scribblearea.cpp | 38 ++---- core_lib/src/interface/scribblearea.h | 4 - core_lib/src/tool/pentool.cpp | 6 - core_lib/src/tool/pentool.h | 1 - core_lib/src/tool/polylinetool.cpp | 1 - 11 files changed, 115 insertions(+), 170 deletions(-) diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index 0ae6977747..6138d9145e 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -17,13 +17,12 @@ GNU General Public License for more details. #include "canvaspainter.h" #include -#include #include "object.h" #include "layerbitmap.h" #include "layervector.h" #include "bitmapimage.h" -#include "layercamera.h" +#include "tile.h" #include "tiledbuffer.h" #include "vectorimage.h" @@ -293,38 +292,31 @@ void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blit painter.setOpacity(paintedImage->getOpacity() - (1.0-painter.opacity())); painter.setWorldMatrixEnabled(false); - bool shouldPaintTransformedSelection = mRenderTransform; - if (isCurrentLayer) { - if (isDrawing) { - - // Certain tools require being painted continously, for example the Polyline tool. - // The tiled buffer does not update the area outside of which it paints, - // so in the case in order to see the previously laid down polyline stroke - // the area around it must be drawn again before - // applying the new tiled output on top - if (mOptions.bIgnoreCanvasBuffer) { - currentBitmapPainter.setCompositionMode(QPainter::CompositionMode_Source); - 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 (mOptions.bIgnoreCanvasBuffer) { + currentBitmapPainter.setCompositionMode(QPainter::CompositionMode_Source); + currentBitmapPainter.drawImage(paintedImage->topLeft(), *paintedImage->image()); + } - const auto tiles = mTiledBuffer->tiles(); - for (const Tile* tile : tiles) { - currentBitmapPainter.drawPixmap(tile->pos(), tile->pixmap()); - } + const auto tiles = mTiledBuffer->tiles(); + for (const Tile* tile : tiles) { + currentBitmapPainter.drawPixmap(tile->pos(), tile->pixmap()); } } else { - // We do not wish to draw selection transformations on anything but the current layer - shouldPaintTransformedSelection = false; - } - - // 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 - if (!isCurrentLayer || !isDrawing) { + // 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()); } - if (shouldPaintTransformedSelection) { + // We do not wish to draw selection transformations on anything but the current layer + if (isCurrentLayer && mRenderTransform) { paintTransformedSelection(currentBitmapPainter, paintedImage, mSelection); } diff --git a/core_lib/src/graphics/bitmap/bitmapimage.cpp b/core_lib/src/graphics/bitmap/bitmapimage.cpp index a29f02b113..e2be3641e0 100644 --- a/core_lib/src/graphics/bitmap/bitmapimage.cpp +++ b/core_lib/src/graphics/bitmap/bitmapimage.cpp @@ -24,6 +24,7 @@ GNU General Public License for more details. #include "util.h" #include "blitrect.h" +#include "tile.h" #include "tiledbuffer.h" BitmapImage::BitmapImage() @@ -648,7 +649,8 @@ void BitmapImage::drawRect(QRectF rectangle, QPen pen, QBrush brush, QPainter::C painter.setPen(pen); painter.setBrush(brush); - // Adjust the brush rectangle to be bigger than the bounds itself, otherwise there will be artifacts shown in some cases + // 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(); } diff --git a/core_lib/src/graphics/bitmap/tile.cpp b/core_lib/src/graphics/bitmap/tile.cpp index d56fd97a64..3abfba3c04 100644 --- a/core_lib/src/graphics/bitmap/tile.cpp +++ b/core_lib/src/graphics/bitmap/tile.cpp @@ -16,17 +16,15 @@ GNU General Public License for more details. #include "tile.h" -Tile::Tile(QSize size): +#include + +Tile::Tile(const QPoint& pos, QSize size): + mPos(pos), mTilePixmap(size) { clear(); //Default tiles are transparent } -Tile::Tile(QPixmap& pixmap) -{ - mTilePixmap = pixmap; -} - Tile::~Tile() { } @@ -36,6 +34,15 @@ QRect Tile::boundingRect() const return mTilePixmap.rect(); } +void Tile::load(const QImage& image, const QPoint& topLeft) +{ + QPainter painter(&mTilePixmap); + + painter.translate(-mPos); + painter.drawImage(topLeft, image); + painter.end(); +} + void Tile::clear() { mTilePixmap.fill(Qt::transparent); diff --git a/core_lib/src/graphics/bitmap/tile.h b/core_lib/src/graphics/bitmap/tile.h index cc994481bb..648c9faf65 100644 --- a/core_lib/src/graphics/bitmap/tile.h +++ b/core_lib/src/graphics/bitmap/tile.h @@ -24,23 +24,22 @@ class Tile { public: - explicit Tile (QSize size); - explicit Tile (QPixmap& pixmap); + explicit Tile (const QPoint& pos, QSize size); ~Tile(); const QPixmap& pixmap() const { return mTilePixmap; } QPixmap& pixmap() { return mTilePixmap; } + QPoint pos() const { return mPos; } QRect boundingRect() const; + /** Loads the input image into the tile */ + void load(const QImage& image, const QPoint& topLeft); void clear(); - void setPos(const QPoint& pos) { m_pos = pos; } - QPoint pos() const { return m_pos; } - private: QPixmap mTilePixmap; - QPoint m_pos; + QPoint mPos; }; #endif // TILE_H diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.cpp b/core_lib/src/graphics/bitmap/tiledbuffer.cpp index 9c65bfefaf..69f7b49e1a 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.cpp +++ b/core_lib/src/graphics/bitmap/tiledbuffer.cpp @@ -16,10 +16,11 @@ GNU General Public License for more details. */ #include "tiledbuffer.h" -#include #include #include +#include "tile.h" + TiledBuffer::TiledBuffer(QObject* parent) : QObject(parent) { } @@ -29,21 +30,15 @@ TiledBuffer::~TiledBuffer() clear(); } -Tile* TiledBuffer::getTileFromIndex(int tileX, int tileY) +Tile* TiledBuffer::getTileFromIndex(const TileIndex& tileIndex) { - TileIndex tileIndex; - tileIndex.x = tileX; - tileIndex.y = tileY; - Tile* selectedTile = mTiles.value(tileIndex, nullptr); if (!selectedTile) { // Time to allocate it, update table: - selectedTile = new Tile(QSize(UNIFORM_TILESIZE, UNIFORM_TILESIZE)); - mTiles.insert(tileIndex, selectedTile); - const QPoint& tilePos (getTilePos(tileIndex)); - selectedTile->setPos(tilePos); + selectedTile = new Tile(tilePos, QSize(UNIFORM_TILE_SIZE, UNIFORM_TILE_SIZE)); + mTiles.insert(tileIndex, selectedTile); emit this->onNewTile(this, selectedTile); } else { @@ -55,65 +50,59 @@ Tile* TiledBuffer::getTileFromIndex(int tileX, int tileY) void TiledBuffer::drawBrush(const QPointF& point, int brushWidth, int brushCursorWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing) { const QRectF brushRect(point.x() - 0.5 * brushWidth, point.y() - 0.5 * brushWidth, brushWidth, brushWidth); - const float tilesize = UNIFORM_TILESIZE; + const float tileSize = UNIFORM_TILE_SIZE; const int width = qMax(brushCursorWidth,brushWidth); - // Gather the amount of tiles that fits the size of the brush width - const int xLeft = qFloor((qFloor(point.x() - width)) / tilesize); - const int xRight = qFloor((qFloor(point.x() + width)) / tilesize); - const int yTop = qFloor(qFloor(point.y() - width) / tilesize); - const int yBottom = qFloor(qFloor(point.y() + width) / tilesize); + // Gather the number of tiles that fits the size of the brush width + const int xLeft = qFloor((qFloor(point.x() - width)) / tileSize); + const int xRight = qFloor((qFloor(point.x() + width)) / tileSize); + const int yTop = qFloor(qFloor(point.y() - width) / tileSize); + const int yBottom = qFloor(qFloor(point.y() + width) / tileSize); for (int tileY = yTop; tileY <= yBottom; tileY++) { for (int tileX = xLeft; tileX <= xRight; tileX++) { - Tile* tile = getTileFromIndex(tileX, tileY); + Tile* tile = getTileFromIndex({tileX, tileY}); - if (tile) - { - QPainter painter(&tile->pixmap()); + QPainter painter(&tile->pixmap()); - painter.translate(-tile->pos()); - painter.setRenderHint(QPainter::Antialiasing, antialiasing); - painter.setPen(pen); - painter.setBrush(brush); - painter.setCompositionMode(cm); - painter.drawEllipse(brushRect); - painter.end(); + painter.translate(-tile->pos()); + painter.setRenderHint(QPainter::Antialiasing, antialiasing); + painter.setPen(pen); + painter.setBrush(brush); + painter.setCompositionMode(cm); + painter.drawEllipse(brushRect); + painter.end(); - mTileBounds.extend(tile->pos(), tile->boundingRect().size()); - } + mTileBounds.extend(tile->pos(), tile->boundingRect().size()); } } } void TiledBuffer::drawImage(const QImage& image, const QRect& imageBounds, QPainter::CompositionMode cm, bool antialiasing) { - const float tilesize = UNIFORM_TILESIZE; + const float tileSize = UNIFORM_TILE_SIZE; const float imageXRad = image.width(); const float imageYRad = image.height(); - // Gather the amount of tiles that fits the size of the brush width - const int xLeft = qFloor((qFloor(imageBounds.left() - imageXRad)) / tilesize); - const int xRight = qFloor((qFloor(imageBounds.right() + imageXRad)) / tilesize); - const int yTop = qFloor(qFloor(imageBounds.top() - imageYRad) / tilesize); - const int yBottom = qFloor(qFloor(imageBounds.bottom() + imageYRad) / tilesize); + // Gather the number of tiles that fits the size of the brush width + const int xLeft = qFloor((qFloor(imageBounds.left() - imageXRad)) / tileSize); + const int xRight = qFloor((qFloor(imageBounds.right() + imageXRad)) / tileSize); + const int yTop = qFloor(qFloor(imageBounds.top() - imageYRad) / tileSize); + const int yBottom = qFloor(qFloor(imageBounds.bottom() + imageYRad) / tileSize); for (int tileY = yTop; tileY <= yBottom; tileY++) { for (int tileX = xLeft; tileX <= xRight; tileX++) { - Tile* tile = getTileFromIndex(tileX, tileY); + Tile* tile = getTileFromIndex({tileX, tileY}); - if (tile) - { - QPainter painter(&tile->pixmap()); + QPainter painter(&tile->pixmap()); - painter.translate(-tile->pos()); - painter.setRenderHint(QPainter::Antialiasing, antialiasing); - painter.setCompositionMode(cm); - painter.drawImage(imageBounds.topLeft(), image); - painter.end(); + painter.translate(-tile->pos()); + painter.setRenderHint(QPainter::Antialiasing, antialiasing); + painter.setCompositionMode(cm); + painter.drawImage(imageBounds.topLeft(), image); + painter.end(); - mTileBounds.extend(tile->pos(), tile->boundingRect().size()); - } + mTileBounds.extend(tile->pos(), tile->boundingRect().size()); } } } @@ -124,47 +113,35 @@ void TiledBuffer::drawPath(QPainterPath path, int cursorWidth, QPen pen, QBrush { const int pathWidth = pen.width(); const int width = (qMax(pathWidth,cursorWidth) + 1); - const float tilesize = UNIFORM_TILESIZE; + const float tileSize = UNIFORM_TILE_SIZE; const QRectF pathRect = path.boundingRect(); - // Gather the amount of tiles that fits the size of the brush width - const int xLeft = qFloor((qFloor(pathRect.left() - width)) / tilesize); - const int xRight = qFloor((qFloor(pathRect.right() + width)) / tilesize); - const int yTop = qFloor(qFloor(pathRect.top() - width) / tilesize); - const int yBottom = qFloor(qFloor(pathRect.bottom() + width) / tilesize); + // Gather the number of tiles that fits the size of the brush width + const int xLeft = qFloor((qFloor(pathRect.left() - width)) / tileSize); + const int xRight = qFloor((qFloor(pathRect.right() + width)) / tileSize); + const int yTop = qFloor(qFloor(pathRect.top() - width) / tileSize); + const int yBottom = qFloor(qFloor(pathRect.bottom() + width) / tileSize); for (int tileY = yTop; tileY <= yBottom; tileY++) { for (int tileX = xLeft; tileX <= xRight; tileX++) { - Tile* tile = getTileFromIndex(tileX, tileY); + Tile* tile = getTileFromIndex({tileX, tileY}); - if (tile) - { - QPainter painter(&tile->pixmap()); + QPainter painter(&tile->pixmap()); - painter.translate(-tile->pos()); - painter.setRenderHint(QPainter::Antialiasing, antialiasing); - painter.setPen(pen); - painter.setBrush(brush); - painter.setCompositionMode(cm); - painter.drawPath(path); - painter.end(); + painter.translate(-tile->pos()); + painter.setRenderHint(QPainter::Antialiasing, antialiasing); + painter.setPen(pen); + painter.setBrush(brush); + painter.setCompositionMode(cm); + painter.drawPath(path); + painter.end(); - mTileBounds.extend(tile->pos(), tile->boundingRect().size()); - } + mTileBounds.extend(tile->pos(), tile->boundingRect().size()); } } } -void TiledBuffer::loadTile(const QImage& image, const QPoint& topLeft, Tile* tile) -{ - QPainter painter(&tile->pixmap()); - - painter.translate(-tile->pos()); - painter.drawImage(topLeft, image); - painter.end(); -} - void TiledBuffer::clear() { QHashIterator i(mTiles); @@ -190,14 +167,14 @@ void TiledBuffer::clear() QPoint TiledBuffer::getTilePos(const TileIndex& index) const { - return QPoint { qRound(UNIFORM_TILESIZE*static_cast(index.x)), - qRound(UNIFORM_TILESIZE*static_cast(index.y)) }; + return QPoint { qRound(UNIFORM_TILE_SIZE*static_cast(index.x)), + qRound(UNIFORM_TILE_SIZE*static_cast(index.y)) }; } TileIndex TiledBuffer::getTileIndex(const TileIndex& pos) const { - return { qRound(static_cast(pos.x)/UNIFORM_TILESIZE), - qRound(static_cast(pos.y)/UNIFORM_TILESIZE) }; + return { qRound(static_cast(pos.x)/UNIFORM_TILE_SIZE), + qRound(static_cast(pos.y)/UNIFORM_TILE_SIZE) }; } QRect TiledBuffer::getRectForPoint(const QPoint& point, const QSize size) const @@ -207,5 +184,5 @@ QRect TiledBuffer::getRectForPoint(const QPoint& point, const QSize size) const QRect TiledBuffer::getRectForPoint(const QPoint& point) const { - return QRect(point.x(), point.y(), UNIFORM_TILESIZE, UNIFORM_TILESIZE); + return QRect(point.x(), point.y(), UNIFORM_TILE_SIZE, UNIFORM_TILE_SIZE); } diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.h b/core_lib/src/graphics/bitmap/tiledbuffer.h index 92d3d3ad77..e2137086a5 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.h +++ b/core_lib/src/graphics/bitmap/tiledbuffer.h @@ -16,13 +16,14 @@ GNU General Public License for more details. #ifndef TILEDBUFFER_H #define TILEDBUFFER_H -#include -#include #include #include #include "blitrect.h" -#include "tile.h" + +class QImage; +class QRect; +class Tile; struct TileIndex { int x; @@ -47,14 +48,11 @@ class TiledBuffer: public QObject TiledBuffer(QObject* parent = nullptr); ~TiledBuffer(); - /** Loads the input tile into the tiledBuffer */ - void loadTile(const QImage& image, const QPoint& topLeft, Tile* tile); - /** Clears the content of the tiled buffer */ void clear(); /** Returns true if there are any tiles, otherwise false */ - bool isValid() { return !mTiles.isEmpty(); } + bool isValid() const { return !mTiles.isEmpty(); } /** Draws a brush with the specified parameters to the tiled buffer */ void drawBrush(const QPointF& point, int brushWidth, int brushCursorWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing); @@ -77,7 +75,7 @@ class TiledBuffer: public QObject private: - Tile* getTileFromIndex(int tileX, int tileY); + Tile* getTileFromIndex(const TileIndex& tileIndex); inline QPoint getTilePos(const TileIndex& index) const; inline TileIndex getTileIndex(const TileIndex& pos) const; @@ -93,13 +91,13 @@ class TiledBuffer: public QObject /** * @brief getRectForPoint - * Returns a rectnagle with the size of TILESIZE (64,64) + * Returns a rectangle with the size of UNIFORM_TILE_SIZE (64,64) * @param point * @return QRect */ QRect getRectForPoint(const QPoint& point) const; - const int UNIFORM_TILESIZE = 64; + const int UNIFORM_TILE_SIZE = 64; BlitRect mTileBounds; diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index f5b04d4329..f7130e9b2c 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -32,6 +32,7 @@ GNU General Public License for more details. #include "bitmapimage.h" #include "vectorimage.h" #include "blitrect.h" +#include "tile.h" #include "onionskinpainteroptions.h" @@ -201,7 +202,7 @@ void ScribbleArea::loadTile(TiledBuffer* tiledBuffer, Tile* tile) if (layerType == Layer::BITMAP) { const auto& bitmapImage = currentBitmapImage(mEditor->layers()->currentLayer()); const QImage& image = *bitmapImage->image(); - mTiledBuffer.loadTile(image, bitmapImage->topLeft(), tile); + tile->load(image, bitmapImage->topLeft()); } else if (layerType == Layer::VECTOR) { // Not used, we only use the buffer to paint the stroke before painting the real vector stroke @@ -887,9 +888,6 @@ void ScribbleArea::paintCanvasCursor(QPainter& painter) static_cast(mTransformedCursorPos.y() - mCursorCenterPos.y())), mCursorImg); - // update center of transformed img for rect only - mTransCursImg = mCursorImg.transformed(view); - mCursorCenterPos.setX(centerCal); mCursorCenterPos.setY(centerCal); } @@ -910,20 +908,16 @@ void ScribbleArea::updateCanvasCursor() } else { - mCursorImg = QPixmap(); // if above does not comply, deallocate image + mCursorImg = QPixmap(); // if the above does not comply, deallocate image } - // update cursor rect - QPoint translatedPos = QPoint(static_cast(mTransformedCursorPos.x() - mCursorCenterPos.x()), - static_cast(mTransformedCursorPos.y() - mCursorCenterPos.y())); - - QRect updateRect = mCursorImg.rect().adjusted(-1, -1, 1, 1).translated(translatedPos);; - - - // When we're using a tool, the TiledBuffer will take care of this, + // When we're using a tool, the TiledBuffer will take care of this; // we don't want to cause needless updates if (!currentTool()->isActive()) { - update(updateRect); + // update cursor rect + QPoint translatedPos(static_cast(mTransformedCursorPos.x() - mCursorCenterPos.x()), + static_cast(mTransformedCursorPos.y() - mCursorCenterPos.y())); + update(mCursorImg.rect().adjusted(-1, -1, 1, 1).translated(translatedPos)); } } @@ -1301,7 +1295,7 @@ void ScribbleArea::drawPolyline(QPainterPath path, QPen pen, bool useAA) { BlitRect blitRect; - // In order to clear what was previous dirty, we need to include the previous buffer bound + // In order to clear what was previously dirty, we need to include the previous buffer bound // this ensures that we won't see stroke artifacts blitRect.extend(mEditor->view()->mapCanvasToScreen(mTiledBuffer.bounds()).toRect()); @@ -1322,13 +1316,7 @@ void ScribbleArea::endStroke() paintBitmapBuffer(); } - int frameNumber = mEditor->currentFrame(); - if (mPrefs->isOn(SETTING::PREV_ONION) || mPrefs->isOn(SETTING::NEXT_ONION)) { - invalidateOnionSkinsCacheAround(frameNumber); - invalidatePainterCaches(); - } - invalidateCacheForFrame(frameNumber); - updateFrame(frameNumber); + onFrameModified(mEditor->currentFrame()); } void ScribbleArea::flipSelection(bool flipVertical) @@ -1525,12 +1513,6 @@ void ScribbleArea::toggleThinLines() setEffect(SETTING::INVISIBLE_LINES, !previousValue); } -void ScribbleArea::toggleOutlines() -{ - mIsSimplified = !mIsSimplified; - setEffect(SETTING::OUTLINES, mIsSimplified); -} - void ScribbleArea::setLayerVisibility(LayerVisibility visibility) { mLayerVisibility = visibility; diff --git a/core_lib/src/interface/scribblearea.h b/core_lib/src/interface/scribblearea.h index a31fd35368..518bc51fb3 100644 --- a/core_lib/src/interface/scribblearea.h +++ b/core_lib/src/interface/scribblearea.h @@ -153,7 +153,6 @@ public slots: void clearImage(); void setCurveSmoothing(int); void toggleThinLines(); - void toggleOutlines(); void increaseLayerVisibilityIndex(); void decreaseLayerVisibilityIndex(); void setLayerVisibility(LayerVisibility visibility); @@ -190,8 +189,6 @@ public slots: void paintBitmapBuffer(); void paintCanvasCursor(QPainter& painter); void clearDrawingBuffer(); - void refreshBitmap(const QRectF& rect, int rad); - void refreshVector(const QRectF& rect, int rad); void setGaussianGradient(QGradient &gradient, QColor color, qreal opacity, qreal offset); void pointerPressEvent(PointerEvent*); @@ -207,7 +204,6 @@ public slots: TiledBuffer mTiledBuffer; QPixmap mCursorImg; - QPixmap mTransCursImg; private: diff --git a/core_lib/src/tool/pentool.cpp b/core_lib/src/tool/pentool.cpp index 4b07b18078..482d5c277f 100644 --- a/core_lib/src/tool/pentool.cpp +++ b/core_lib/src/tool/pentool.cpp @@ -234,12 +234,6 @@ void PenTool::drawStroke() } } -void PenTool::paintBitmapStroke() -{ - mScribbleArea->paintBitmapBuffer(); - mScribbleArea->clearDrawingBuffer(); -} - void PenTool::paintVectorStroke(Layer* layer) { if (mStrokePoints.empty()) diff --git a/core_lib/src/tool/pentool.h b/core_lib/src/tool/pentool.h index ff58abc11c..a904944adc 100644 --- a/core_lib/src/tool/pentool.h +++ b/core_lib/src/tool/pentool.h @@ -39,7 +39,6 @@ class PenTool : public StrokeTool void drawStroke(); void paintAt(QPointF point); void paintVectorStroke(Layer *layer); - void paintBitmapStroke(); void setWidth(const qreal width) override; void setPressure(const bool pressure) override; diff --git a/core_lib/src/tool/polylinetool.cpp b/core_lib/src/tool/polylinetool.cpp index 411314fd23..36ea5a6aec 100644 --- a/core_lib/src/tool/polylinetool.cpp +++ b/core_lib/src/tool/polylinetool.cpp @@ -276,5 +276,4 @@ void PolylineTool::endPolyline(QList points) } mScribbleArea->endStroke(); mEditor->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame()); - mScribbleArea->clearDrawingBuffer(); } From 9900f7047858d402cc3c0be79b4576d342efd537 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Fri, 11 Aug 2023 18:12:45 +0200 Subject: [PATCH 33/49] Remove redundant clear of tile before deleting it --- core_lib/src/graphics/bitmap/tiledbuffer.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.cpp b/core_lib/src/graphics/bitmap/tiledbuffer.cpp index 69f7b49e1a..0bc3b3dc47 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.cpp +++ b/core_lib/src/graphics/bitmap/tiledbuffer.cpp @@ -151,9 +151,6 @@ void TiledBuffer::clear() Tile* tile = i.value(); if (tile) { - // Clear the content of the tile - // - tile->clear(); emit onClearTile(this, tile); mTiles.remove(i.key()); delete tile; From 1a29932ff59e62dcf735b835b49da2fe99266008 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Fri, 11 Aug 2023 18:37:33 +0200 Subject: [PATCH 34/49] Update signal/slot naming convention --- core_lib/src/graphics/bitmap/tiledbuffer.cpp | 8 ++++---- core_lib/src/graphics/bitmap/tiledbuffer.h | 8 ++++---- core_lib/src/interface/scribblearea.cpp | 8 ++++---- core_lib/src/interface/scribblearea.h | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.cpp b/core_lib/src/graphics/bitmap/tiledbuffer.cpp index 0bc3b3dc47..69a26c9ecf 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.cpp +++ b/core_lib/src/graphics/bitmap/tiledbuffer.cpp @@ -40,9 +40,9 @@ Tile* TiledBuffer::getTileFromIndex(const TileIndex& tileIndex) selectedTile = new Tile(tilePos, QSize(UNIFORM_TILE_SIZE, UNIFORM_TILE_SIZE)); mTiles.insert(tileIndex, selectedTile); - emit this->onNewTile(this, selectedTile); + emit this->tileCreated(this, selectedTile); } else { - emit this->onUpdateTile(this, selectedTile); + emit this->tileUpdated(this, selectedTile); } return selectedTile; @@ -151,7 +151,7 @@ void TiledBuffer::clear() Tile* tile = i.value(); if (tile) { - emit onClearTile(this, tile); + emit tileCleared(this, tile); mTiles.remove(i.key()); delete tile; } @@ -159,7 +159,7 @@ void TiledBuffer::clear() mTileBounds = BlitRect(); - emit onClearBuffer(this); + emit bufferCleared(this); } QPoint TiledBuffer::getTilePos(const TileIndex& index) const diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.h b/core_lib/src/graphics/bitmap/tiledbuffer.h index e2137086a5..99e0c14408 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.h +++ b/core_lib/src/graphics/bitmap/tiledbuffer.h @@ -68,10 +68,10 @@ class TiledBuffer: public QObject signals: - void onUpdateTile(TiledBuffer* tiledBuffer, Tile* tile); - void onNewTile(TiledBuffer* tiledBuffer, Tile* tile); - void onClearTile(TiledBuffer* tiledBuffer, Tile* tile); - void onClearBuffer(TiledBuffer* tiledBuffer); + void tileUpdated(TiledBuffer* tiledBuffer, Tile* tile); + void tileCreated(TiledBuffer* tiledBuffer, Tile* tile); + void tileCleared(TiledBuffer* tiledBuffer, Tile* tile); + void bufferCleared(TiledBuffer* tiledBuffer); private: diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index f7130e9b2c..8fb835d550 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -76,8 +76,8 @@ bool ScribbleArea::init() connect(mEditor->select(), &SelectionManager::selectionChanged, this, &ScribbleArea::onSelectionChanged); connect(mEditor->select(), &SelectionManager::needDeleteSelection, this, &ScribbleArea::deleteSelection); - connect(&mTiledBuffer, &TiledBuffer::onUpdateTile, this, &ScribbleArea::updateTile); - connect(&mTiledBuffer, &TiledBuffer::onNewTile, this, &ScribbleArea::loadTile); + connect(&mTiledBuffer, &TiledBuffer::tileUpdated, this, &ScribbleArea::onTileUpdated); + connect(&mTiledBuffer, &TiledBuffer::tileCreated, this, &ScribbleArea::onTileCreated); mDoubleClickTimer->setInterval(50); mMouseFilterTimer->setInterval(50); @@ -188,14 +188,14 @@ void ScribbleArea::setEffect(SETTING e, bool isOn) /************************************************************************************/ // update methods -void ScribbleArea::updateTile(TiledBuffer* tiledBuffer, Tile* tile) +void ScribbleArea::onTileUpdated(TiledBuffer* tiledBuffer, Tile* tile) { Q_UNUSED(tiledBuffer); const QRectF& mappedRect = mEditor->view()->getView().mapRect(QRectF(tile->pos(), tile->boundingRect().size())); update(mappedRect.toRect()); } -void ScribbleArea::loadTile(TiledBuffer* tiledBuffer, Tile* tile) +void ScribbleArea::onTileCreated(TiledBuffer* tiledBuffer, Tile* tile) { Q_UNUSED(tiledBuffer) Layer::LAYER_TYPE layerType = mEditor->layers()->currentLayer()->type(); diff --git a/core_lib/src/interface/scribblearea.h b/core_lib/src/interface/scribblearea.h index 518bc51fb3..a4cbfc1aec 100644 --- a/core_lib/src/interface/scribblearea.h +++ b/core_lib/src/interface/scribblearea.h @@ -161,8 +161,8 @@ public slots: void paletteColorChanged(QColor); void showLayerNotVisibleWarning(); - void updateTile(TiledBuffer* tiledBuffer, Tile* tile); - void loadTile(TiledBuffer* tiledBuffer, Tile* tile); + void onTileUpdated(TiledBuffer* tiledBuffer, Tile* tile); + void onTileCreated(TiledBuffer* tiledBuffer, Tile* tile); protected: bool event(QEvent *event) override; From 239dc18b7c18a93e503990af18d5ca925d80adc6 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Tue, 15 Aug 2023 08:38:01 +0200 Subject: [PATCH 35/49] Apply some refactoring to tile --- core_lib/src/graphics/bitmap/tile.cpp | 9 +++------ core_lib/src/graphics/bitmap/tile.h | 7 +++++-- core_lib/src/graphics/bitmap/tiledbuffer.cpp | 6 +++--- core_lib/src/interface/scribblearea.cpp | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core_lib/src/graphics/bitmap/tile.cpp b/core_lib/src/graphics/bitmap/tile.cpp index 3abfba3c04..92134f0a83 100644 --- a/core_lib/src/graphics/bitmap/tile.cpp +++ b/core_lib/src/graphics/bitmap/tile.cpp @@ -19,8 +19,10 @@ GNU General Public License for more details. #include Tile::Tile(const QPoint& pos, QSize size): + mTilePixmap(size), mPos(pos), - mTilePixmap(size) + mBounds(pos, size), + mSize(size) { clear(); //Default tiles are transparent } @@ -29,11 +31,6 @@ Tile::~Tile() { } -QRect Tile::boundingRect() const -{ - return mTilePixmap.rect(); -} - void Tile::load(const QImage& image, const QPoint& topLeft) { QPainter painter(&mTilePixmap); diff --git a/core_lib/src/graphics/bitmap/tile.h b/core_lib/src/graphics/bitmap/tile.h index 648c9faf65..46e7020806 100644 --- a/core_lib/src/graphics/bitmap/tile.h +++ b/core_lib/src/graphics/bitmap/tile.h @@ -30,8 +30,9 @@ class Tile const QPixmap& pixmap() const { return mTilePixmap; } QPixmap& pixmap() { return mTilePixmap; } - QPoint pos() const { return mPos; } - QRect boundingRect() const; + const QPoint& pos() const { return mPos; } + const QRect& bounds() const { return mBounds; } + const QSize& size() const { return mSize; } /** Loads the input image into the tile */ void load(const QImage& image, const QPoint& topLeft); @@ -40,6 +41,8 @@ class Tile private: QPixmap mTilePixmap; QPoint mPos; + QRect mBounds; + QSize mSize; }; #endif // TILE_H diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.cpp b/core_lib/src/graphics/bitmap/tiledbuffer.cpp index 69a26c9ecf..cb9048ba3d 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.cpp +++ b/core_lib/src/graphics/bitmap/tiledbuffer.cpp @@ -74,7 +74,7 @@ void TiledBuffer::drawBrush(const QPointF& point, int brushWidth, int brushCurso painter.drawEllipse(brushRect); painter.end(); - mTileBounds.extend(tile->pos(), tile->boundingRect().size()); + mTileBounds.extend(tile->bounds()); } } } @@ -102,7 +102,7 @@ void TiledBuffer::drawImage(const QImage& image, const QRect& imageBounds, QPain painter.drawImage(imageBounds.topLeft(), image); painter.end(); - mTileBounds.extend(tile->pos(), tile->boundingRect().size()); + mTileBounds.extend(tile->bounds()); } } } @@ -137,7 +137,7 @@ void TiledBuffer::drawPath(QPainterPath path, int cursorWidth, QPen pen, QBrush painter.drawPath(path); painter.end(); - mTileBounds.extend(tile->pos(), tile->boundingRect().size()); + mTileBounds.extend(tile->bounds()); } } } diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index 4995f54d99..f6eef290f3 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -196,7 +196,7 @@ void ScribbleArea::setEffect(SETTING e, bool isOn) void ScribbleArea::onTileUpdated(TiledBuffer* tiledBuffer, Tile* tile) { Q_UNUSED(tiledBuffer); - const QRectF& mappedRect = mEditor->view()->getView().mapRect(QRectF(tile->pos(), tile->boundingRect().size())); + const QRectF& mappedRect = mEditor->view()->getView().mapRect(QRectF(tile->bounds())); update(mappedRect.toRect()); } @@ -213,7 +213,7 @@ void ScribbleArea::onTileCreated(TiledBuffer* tiledBuffer, Tile* tile) // Not used, we only use the buffer to paint the stroke before painting the real vector stroke } - const QRectF& mappedRect = mEditor->view()->getView().mapRect(QRectF(tile->pos(), tile->boundingRect().size())); + const QRectF& mappedRect = mEditor->view()->getView().mapRect(QRectF(tile->bounds())); update(mappedRect.toRect()); } From 836e34fa5b739d27bef86ae955718b60c388a534 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Tue, 29 Aug 2023 18:42:18 +0200 Subject: [PATCH 36/49] Fix blitRect::extend would make tiles odd numbered --- core_lib/src/util/blitrect.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core_lib/src/util/blitrect.cpp b/core_lib/src/util/blitrect.cpp index fa36b3fb75..20fd7baabc 100644 --- a/core_lib/src/util/blitrect.cpp +++ b/core_lib/src/util/blitrect.cpp @@ -52,7 +52,9 @@ void BlitRect::extend(const QPoint p) void BlitRect::extend(const QRect& rect) { - extend(rect.topLeft(), rect.size()); + // For reasons I do not understand, rect.size() or width and height, adds 1 pixel each... + // we subtract one to counter that + extend(rect.topLeft(), QSize(rect.width() - 1, rect.height() - 1)); } void BlitRect::extend(const QPoint& p, const QSize& size) From dd06086eb9613ba7d09bfb57df34d8fa0ca43eb3 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Tue, 29 Aug 2023 18:45:09 +0200 Subject: [PATCH 37/49] Remove unused methods --- core_lib/src/graphics/bitmap/tiledbuffer.cpp | 16 ---------------- core_lib/src/graphics/bitmap/tiledbuffer.h | 19 ------------------- 2 files changed, 35 deletions(-) diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.cpp b/core_lib/src/graphics/bitmap/tiledbuffer.cpp index cb9048ba3d..98630eb3e7 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.cpp +++ b/core_lib/src/graphics/bitmap/tiledbuffer.cpp @@ -167,19 +167,3 @@ QPoint TiledBuffer::getTilePos(const TileIndex& index) const return QPoint { qRound(UNIFORM_TILE_SIZE*static_cast(index.x)), qRound(UNIFORM_TILE_SIZE*static_cast(index.y)) }; } - -TileIndex TiledBuffer::getTileIndex(const TileIndex& pos) const -{ - return { qRound(static_cast(pos.x)/UNIFORM_TILE_SIZE), - qRound(static_cast(pos.y)/UNIFORM_TILE_SIZE) }; -} - -QRect TiledBuffer::getRectForPoint(const QPoint& point, const QSize size) const -{ - return QRect(point.x(), point.y(), size.width(), size.height()); -} - -QRect TiledBuffer::getRectForPoint(const QPoint& point) const -{ - return QRect(point.x(), point.y(), UNIFORM_TILE_SIZE, UNIFORM_TILE_SIZE); -} diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.h b/core_lib/src/graphics/bitmap/tiledbuffer.h index 99e0c14408..25273bff72 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.h +++ b/core_lib/src/graphics/bitmap/tiledbuffer.h @@ -66,7 +66,6 @@ class TiledBuffer: public QObject const QRect& bounds() const { return mTileBounds; } - signals: void tileUpdated(TiledBuffer* tiledBuffer, Tile* tile); void tileCreated(TiledBuffer* tiledBuffer, Tile* tile); @@ -78,24 +77,6 @@ class TiledBuffer: public QObject Tile* getTileFromIndex(const TileIndex& tileIndex); inline QPoint getTilePos(const TileIndex& index) const; - inline TileIndex getTileIndex(const TileIndex& pos) const; - - /** - * @brief getRectForPoint - * Returns a rectangle with a specified size for the given point - * @param point - * @param size - * @return QRect - */ - QRect getRectForPoint(const QPoint& point, const QSize size) const; - - /** - * @brief getRectForPoint - * Returns a rectangle with the size of UNIFORM_TILE_SIZE (64,64) - * @param point - * @return QRect - */ - QRect getRectForPoint(const QPoint& point) const; const int UNIFORM_TILE_SIZE = 64; From adcb3c14ce22fc7e21a9794b4db7b87cef3216b1 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Tue, 29 Aug 2023 19:34:57 +0200 Subject: [PATCH 38/49] Avoid invalidating cache till we're done drawing --- app/src/actioncommands.cpp | 4 +-- app/src/colorpalettewidget.cpp | 2 +- app/src/mainwindow2.cpp | 2 +- core_lib/src/interface/editor.cpp | 9 ++----- core_lib/src/interface/editor.h | 8 +----- core_lib/src/interface/scribblearea.cpp | 35 +++++++++++++------------ core_lib/src/interface/scribblearea.h | 5 +--- core_lib/src/tool/cameratool.cpp | 8 +++--- core_lib/src/tool/movetool.cpp | 4 +-- core_lib/src/tool/polylinetool.cpp | 2 +- core_lib/src/tool/selecttool.cpp | 6 ++--- 11 files changed, 36 insertions(+), 49 deletions(-) diff --git a/app/src/actioncommands.cpp b/app/src/actioncommands.cpp index 1b8102e6a7..35727c69e0 100644 --- a/app/src/actioncommands.cpp +++ b/app/src/actioncommands.cpp @@ -885,7 +885,7 @@ void ActionCommands::changeKeyframeLineColor() QRgb color = mEditor->color()->frontColor().rgb(); LayerBitmap* layer = static_cast(mEditor->layers()->currentLayer()); layer->getBitmapImageAtFrame(mEditor->currentFrame())->fillNonAlphaPixels(color); - mEditor->updateFrame(mEditor->currentFrame()); + mEditor->updateFrame(); } } @@ -900,7 +900,7 @@ void ActionCommands::changeallKeyframeLineColor() if (layer->keyExists(i)) layer->getBitmapImageAtFrame(i)->fillNonAlphaPixels(color); } - mEditor->updateFrame(mEditor->currentFrame()); + mEditor->updateFrame(); } } diff --git a/app/src/colorpalettewidget.cpp b/app/src/colorpalettewidget.cpp index 7f86fcd602..b65b656dc0 100644 --- a/app/src/colorpalettewidget.cpp +++ b/app/src/colorpalettewidget.cpp @@ -636,7 +636,7 @@ void ColorPaletteWidget::clickRemoveColorButton() { fitSwatchSize(); } - mEditor->updateCurrentFrame(); + mEditor->updateFrame(); } bool ColorPaletteWidget::showPaletteWarning() diff --git a/app/src/mainwindow2.cpp b/app/src/mainwindow2.cpp index af89127be6..f59aaa32ce 100644 --- a/app/src/mainwindow2.cpp +++ b/app/src/mainwindow2.cpp @@ -879,7 +879,7 @@ void MainWindow2::importImage() return; } - ui->scribbleArea->updateCurrentFrame(); + ui->scribbleArea->updateFrame(); mTimeLine->updateContent(); } diff --git a/core_lib/src/interface/editor.cpp b/core_lib/src/interface/editor.cpp index b6d3d44bfc..7c4d5bc602 100644 --- a/core_lib/src/interface/editor.cpp +++ b/core_lib/src/interface/editor.cpp @@ -1109,14 +1109,9 @@ void Editor::deselectAll() const } } -void Editor::updateFrame(int frameNumber) +void Editor::updateFrame() { - mScribbleArea->updateFrame(frameNumber); -} - -void Editor::updateCurrentFrame() -{ - mScribbleArea->updateCurrentFrame(); + mScribbleArea->updateFrame(); } void Editor::setCurrentLayerIndex(int i) diff --git a/core_lib/src/interface/editor.h b/core_lib/src/interface/editor.h index 0b5723d3db..eef05b2665 100644 --- a/core_lib/src/interface/editor.h +++ b/core_lib/src/interface/editor.h @@ -166,14 +166,8 @@ class Editor : public QObject /** Will call update() and update the canvas * Only call this directly If you need the cache to be intact and require the frame to be repainted - * Convenient method that does the same as updateFrame but for the current frame */ - void updateCurrentFrame(); - - /** Will call update() and update the canvas - * Only call this directly If you need the cache to be intact and require the frame to be repainted - */ - void updateFrame(int frameNumber); + void updateFrame(); void setModified(int layerNumber, int frameNumber); diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index f6eef290f3..34fc0947e1 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -217,14 +217,12 @@ void ScribbleArea::onTileCreated(TiledBuffer* tiledBuffer, Tile* tile) update(mappedRect.toRect()); } -void ScribbleArea::updateCurrentFrame() +void ScribbleArea::updateFrame() { - updateFrame(mEditor->currentFrame()); -} + if (currentTool()->isActive() && currentTool()->isDrawingTool()) { + return; + } -void ScribbleArea::updateFrame(int frame) -{ - Q_ASSERT(frame >= 0); update(); } @@ -282,12 +280,14 @@ void ScribbleArea::invalidateOnionSkinsCacheAround(int frameNumber) void ScribbleArea::invalidateAllCache() { + if (currentTool()->isDrawingTool() && currentTool()->isActive()) { return; } + QPixmapCache::clear(); mPixmapCacheKeys.clear(); invalidatePainterCaches(); mEditor->layers()->currentLayer()->clearDirtyFrames(); - update(); + updateFrame(); } void ScribbleArea::invalidateCacheForFrame(int frameNumber) @@ -305,7 +305,7 @@ void ScribbleArea::invalidatePainterCaches() { mCameraPainter.resetCache(); mCanvasPainter.resetLayerCache(); - update(); + updateFrame(); } void ScribbleArea::onToolPropertyUpdated(ToolType, ToolPropertyType type) @@ -326,7 +326,7 @@ void ScribbleArea::onToolChanged(ToolType) prepOverlays(frame); prepCameraPainter(frame); invalidateCacheForFrame(frame); - updateCurrentFrame(); + updateFrame(); } @@ -341,13 +341,14 @@ void ScribbleArea::onPlayStateChanged() prepOverlays(currentFrame); prepCameraPainter(currentFrame); invalidateCacheForFrame(currentFrame); - updateFrame(currentFrame); + updateFrame(); } void ScribbleArea::onScrubbed(int frameNumber) { + Q_UNUSED(frameNumber) invalidatePainterCaches(); - updateFrame(frameNumber); + updateFrame(); } void ScribbleArea::onFramesModified() @@ -356,7 +357,7 @@ void ScribbleArea::onFramesModified() if (mPrefs->isOn(SETTING::PREV_ONION) || mPrefs->isOn(SETTING::NEXT_ONION)) { invalidatePainterCaches(); } - update(); + updateFrame(); } void ScribbleArea::onFrameModified(int frameNumber) @@ -366,7 +367,7 @@ void ScribbleArea::onFrameModified(int frameNumber) invalidatePainterCaches(); } invalidateCacheForFrame(frameNumber); - updateFrame(frameNumber); + updateFrame(); } void ScribbleArea::onViewChanged() @@ -383,7 +384,7 @@ void ScribbleArea::onSelectionChanged() { int currentFrame = mEditor->currentFrame(); invalidateCacheForFrame(currentFrame); - updateFrame(currentFrame); + updateFrame(); } void ScribbleArea::onOnionSkinTypeChanged() @@ -859,7 +860,7 @@ void ScribbleArea::paintBitmapBuffer() // just return (since we have nothing to paint on). if (layer->getLastKeyFrameAtPosition(frameNumber) == nullptr) { - updateCurrentFrame(); + updateFrame(); return; } @@ -1488,7 +1489,7 @@ void ScribbleArea::applyTransformedSelection() mEditor->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame()); } - update(); + updateFrame(); } void ScribbleArea::cancelTransformedSelection() @@ -1516,7 +1517,7 @@ void ScribbleArea::cancelTransformedSelection() mOriginalPolygonF = QPolygonF(); mEditor->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame()); - updateCurrentFrame(); + updateFrame(); } } diff --git a/core_lib/src/interface/scribblearea.h b/core_lib/src/interface/scribblearea.h index 2577687c26..5312d998bd 100644 --- a/core_lib/src/interface/scribblearea.h +++ b/core_lib/src/interface/scribblearea.h @@ -86,12 +86,9 @@ class ScribbleArea : public QWidget QRect getCameraRect(); QPointF getCentralPoint(); - /** Update current frame. - * calls update() behind the scene and update cache if necessary */ - void updateCurrentFrame(); /** Update frame. * calls update() behind the scene and update cache if necessary */ - void updateFrame(int frame); + void updateFrame(); /** Frame scrubbed, invalidate relevant cache */ void onScrubbed(int frameNumber); diff --git a/core_lib/src/tool/cameratool.cpp b/core_lib/src/tool/cameratool.cpp index f4fd3c0309..feb0e54621 100644 --- a/core_lib/src/tool/cameratool.cpp +++ b/core_lib/src/tool/cameratool.cpp @@ -229,7 +229,7 @@ void CameraTool::resetCameraPath() Q_ASSERT(layer->type() == Layer::CAMERA); layer->setPathMovedAtFrame(mEditor->currentFrame(), false); - mEditor->updateCurrentFrame(); + mEditor->updateFrame(); } void CameraTool::resetTransform(CameraFieldOption option) @@ -261,7 +261,7 @@ void CameraTool::transformCamera(Qt::KeyboardModifiers keyMod) transformView(layer, mCamMoveMode, getCurrentPoint(), mTransformOffset, -angleDeg, mEditor->currentFrame()); - mEditor->updateCurrentFrame(); + mEditor->updateFrame(); mTransformOffset = getCurrentPoint(); } @@ -271,7 +271,7 @@ void CameraTool::transformCameraPath() LayerCamera* layer = static_cast(editor()->layers()->currentLayer()); layer->updatePathControlPointAtFrame(getCurrentPoint(), mDragPathFrame); - mEditor->updateCurrentFrame(); + mEditor->updateFrame(); } int CameraTool::constrainedRotation(const qreal rotatedAngle, const int rotationIncrement) const @@ -304,7 +304,7 @@ void CameraTool::pointerMoveEvent(PointerEvent* event) } mScribbleArea->updateToolCursor(); mEditor->view()->forceUpdateViewTransform(); - mEditor->updateCurrentFrame(); + mEditor->updateFrame(); } void CameraTool::pointerReleaseEvent(PointerEvent* event) diff --git a/core_lib/src/tool/movetool.cpp b/core_lib/src/tool/movetool.cpp index 8ddc3a3da0..ec02bafc07 100644 --- a/core_lib/src/tool/movetool.cpp +++ b/core_lib/src/tool/movetool.cpp @@ -119,7 +119,7 @@ void MoveTool::pointerPressEvent(PointerEvent* event) mEditor->overlays()->updatePerspective(mapped); } - mEditor->updateCurrentFrame(); + mEditor->updateFrame(); } void MoveTool::pointerMoveEvent(PointerEvent* event) @@ -154,7 +154,7 @@ void MoveTool::pointerMoveEvent(PointerEvent* event) storeClosestVectorCurve(mCurrentLayer); } } - mEditor->updateCurrentFrame(); + mEditor->updateFrame(); } void MoveTool::pointerReleaseEvent(PointerEvent*) diff --git a/core_lib/src/tool/polylinetool.cpp b/core_lib/src/tool/polylinetool.cpp index 36ea5a6aec..c404e343c6 100644 --- a/core_lib/src/tool/polylinetool.cpp +++ b/core_lib/src/tool/polylinetool.cpp @@ -244,7 +244,7 @@ void PolylineTool::cancelPolyline() { // Clear the in-progress polyline from the bitmap buffer. mScribbleArea->clearDrawingBuffer(); - mScribbleArea->updateCurrentFrame(); + mScribbleArea->updateFrame(); } void PolylineTool::endPolyline(QList points) diff --git a/core_lib/src/tool/selecttool.cpp b/core_lib/src/tool/selecttool.cpp index df24930ae2..52d57fa684 100644 --- a/core_lib/src/tool/selecttool.cpp +++ b/core_lib/src/tool/selecttool.cpp @@ -116,7 +116,7 @@ void SelectTool::beginSelection() mAnchorOriginPoint = getLastPoint(); } - mScribbleArea->updateCurrentFrame(); + mScribbleArea->updateFrame(); } void SelectTool::pointerPressEvent(PointerEvent* event) @@ -160,7 +160,7 @@ void SelectTool::pointerMoveEvent(PointerEvent*) } } - mScribbleArea->updateCurrentFrame(); + mScribbleArea->updateFrame(); } void SelectTool::pointerReleaseEvent(PointerEvent* event) @@ -189,7 +189,7 @@ void SelectTool::pointerReleaseEvent(PointerEvent* event) mSelectionRect = mEditor->select()->mapToSelection(mEditor->select()->mySelectionRect()).boundingRect(); mScribbleArea->updateToolCursor(); - mScribbleArea->updateCurrentFrame(); + mScribbleArea->updateFrame(); } bool SelectTool::maybeDeselect() From 3566a5aa1d73c4f1d97c0138304533c2a4536bb1 Mon Sep 17 00:00:00 2001 From: Jakob Gahde Date: Sun, 17 Sep 2023 21:41:50 +0200 Subject: [PATCH 39/49] Remove various dead code from ScribbleArea --- core_lib/src/interface/scribblearea.cpp | 29 ++++++------------------- core_lib/src/interface/scribblearea.h | 24 +------------------- core_lib/src/managers/toolmanager.cpp | 2 +- core_lib/src/tool/stroketool.h | 2 +- 4 files changed, 10 insertions(+), 47 deletions(-) diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index 34fc0947e1..10a97b5581 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -88,7 +88,6 @@ bool ScribbleArea::init() mQuickSizing = mPrefs->isOn(SETTING::QUICK_SIZING); mMakeInvisible = false; - mIsSimplified = mPrefs->isOn(SETTING::OUTLINES); mMultiLayerOnionSkin = mPrefs->isOn(SETTING::MULTILAYER_ONION); mLayerVisibility = static_cast(mPrefs->getInt(SETTING::LAYER_VISIBILITY)); @@ -415,8 +414,6 @@ void ScribbleArea::keyPressEvent(QKeyEvent *event) // Don't handle this event on auto repeat if (event->isAutoRepeat()) { return; } - mKeyboardInUse = true; - if (isPointerInUse()) { return; } // prevents shortcuts calls while drawing if (currentTool()->keyPressEvent(event)) @@ -530,8 +527,6 @@ void ScribbleArea::keyReleaseEvent(QKeyEvent *event) return; } - mKeyboardInUse = false; - if (event->key() == 0) { editor()->tools()->tryClearTemporaryTool(Qt::Key_unknown); @@ -554,7 +549,7 @@ void ScribbleArea::keyReleaseEvent(QKeyEvent *event) // mouse and tablet event handlers void ScribbleArea::wheelEvent(QWheelEvent* event) { - // Don't change view if tool is in use + // Don't change view if the tool is in use if (isPointerInUse()) return; static const bool isX11 = QGuiApplication::platformName() == "xcb"; @@ -693,7 +688,6 @@ void ScribbleArea::pointerPressEvent(PointerEvent* event) const bool isPressed = event->buttons() & Qt::LeftButton; if (isPressed && mQuickSizing) { - //qDebug() << "Start Adjusting" << event->buttons(); if (currentTool()->startAdjusting(event->modifiers(), 1)) { return; @@ -733,13 +727,6 @@ void ScribbleArea::pointerReleaseEvent(PointerEvent* event) return; // [SHIFT]+drag OR [CTRL]+drag } - if (event->buttons() & (Qt::RightButton | Qt::MiddleButton)) - { - mMouseRightButtonInUse = false; - return; - } - - //qDebug() << "release event"; currentTool()->pointerReleaseEvent(event); editor()->tools()->tryClearTemporaryTool(event->button()); @@ -759,11 +746,11 @@ void ScribbleArea::handleDoubleClick() void ScribbleArea::tabletReleaseEventFired() { - // Under certain circumstances a mouse press event will fire after a tablet release event. - // This causes unexpected behaviours for some of the tools, eg. the bucket. - // The problem only seems to occur on windows and only when tapping. - // prior to this fix, the event queue would look like this: - // eg: TabletPress -> TabletRelease -> MousePress + // Under certain circumstances, a mouse press event will fire after a tablet release event. + // This causes unexpected behaviors for some tools, e.g., the bucket tool. + // The problem only seems to occur on Windows and only when tapping. + // Prior to this fix, the event queue would look like this: + // e.g.: TabletPress -> TabletRelease -> MousePress // The following will filter mouse events created after a tablet release event. mTabletReleaseMillisAgo += 50; @@ -1048,10 +1035,9 @@ void ScribbleArea::paintEvent(QPaintEvent* event) currentTool()->paint(painter); - Layer* layer = mEditor->layers()->currentLayer(); - if (!editor()->playback()->isPlaying()) // we don't need to display the following when the animation is playing { + Layer* layer = mEditor->layers()->currentLayer(); if (layer->type() == Layer::VECTOR) { VectorImage* vectorImage = currentVectorImage(layer); @@ -1128,7 +1114,6 @@ void ScribbleArea::paintEvent(QPaintEvent* event) mOverlayPainter.paint(painter); - // paints the selection outline if (mEditor->select()->somethingSelected()) { diff --git a/core_lib/src/interface/scribblearea.h b/core_lib/src/interface/scribblearea.h index 5312d998bd..41c62ca6a1 100644 --- a/core_lib/src/interface/scribblearea.h +++ b/core_lib/src/interface/scribblearea.h @@ -54,7 +54,6 @@ class ScribbleArea : public QWidget Q_OBJECT friend class MoveTool; - friend class EditTool; friend class SmudgeTool; friend class BucketTool; @@ -74,16 +73,12 @@ class ScribbleArea : public QWidget bool isLayerPaintable() const; - QVector calcSelectionCenterPoints(); - void setEffect(SETTING e, bool isOn); LayerVisibility getLayerVisibility() const { return mLayerVisibility; } qreal getCurveSmoothing() const { return mCurveSmoothingLevel; } - bool usePressure() const { return mUsePressure; } bool makeInvisible() const { return mMakeInvisible; } - QRect getCameraRect(); QPointF getCentralPoint(); /** Update frame. @@ -124,10 +119,6 @@ class ScribbleArea : public QWidget /** Tool changed, invalidate cache and frame if needed */ void onToolChanged(ToolType); - /** Set frame on layer to modified and invalidate current frame cache */ - void setModified(int layerNumber, int frameNumber); - void setModified(const Layer* layer, int frameNumber); - void endStroke(); void flipSelection(bool flipVertical); @@ -143,7 +134,6 @@ class ScribbleArea : public QWidget signals: void multiLayerOnionSkinChanged(bool); - void refreshPreview(); void selectionUpdated(); public slots: @@ -233,27 +223,17 @@ public slots: BitmapImage* currentBitmapImage(Layer* layer) const; VectorImage* currentVectorImage(Layer* layer) const; - MoveMode mMoveMode = MoveMode::NONE; - std::unique_ptr mStrokeManager; Editor* mEditor = nullptr; - - bool mIsSimplified = false; - bool mShowThinLines = false; bool mQuickSizing = true; LayerVisibility mLayerVisibility = LayerVisibility::ALL; - bool mUsePressure = true; bool mMakeInvisible = false; - bool mToolCursors = true; qreal mCurveSmoothingLevel = 0.0; - bool mMultiLayerOnionSkin = false; // future use. If required, just add a checkbox to updated it. - QColor mOnionColor; + bool mMultiLayerOnionSkin = false; // Future use. If required, just add a checkbox to update it. int mDeltaFactor = 1; -private: - /* Under certain circumstances a mouse press event will fire after a tablet release event. This causes unexpected behaviours for some of the tools, eg. the bucket. The problem only seems to occur on windows and only when tapping. @@ -262,9 +242,7 @@ public slots: The following will filter mouse events created after a tablet release event. */ void tabletReleaseEventFired(); - bool mKeyboardInUse = false; bool mMouseInUse = false; - bool mMouseRightButtonInUse = false; bool mTabletInUse = false; qreal mDevicePixelRatio = 1.; diff --git a/core_lib/src/managers/toolmanager.cpp b/core_lib/src/managers/toolmanager.cpp index eb9b0791d4..f319c59de9 100644 --- a/core_lib/src/managers/toolmanager.cpp +++ b/core_lib/src/managers/toolmanager.cpp @@ -347,7 +347,7 @@ void ToolManager::tabletSwitchToEraser() } void ToolManager::tabletRestorePrevTool() -{ +{ if (mTemporaryTool == nullptr && mTabletEraserTool != nullptr) { mTabletEraserTool = nullptr; diff --git a/core_lib/src/tool/stroketool.h b/core_lib/src/tool/stroketool.h index e4b5c7d924..7f97c793b5 100644 --- a/core_lib/src/tool/stroketool.h +++ b/core_lib/src/tool/stroketool.h @@ -57,7 +57,7 @@ class StrokeTool : public BaseTool virtual bool emptyFrameActionEnabled(); private: - QPointF mLastPixel { 0, 0 }; + QPointF mLastPixel { 0, 0 }; }; #endif // STROKETOOL_H From 1f6d93c4c11b8777418d4b176106cc52fb068c35 Mon Sep 17 00:00:00 2001 From: Jakob Gahde Date: Sun, 17 Sep 2023 21:44:48 +0200 Subject: [PATCH 40/49] Fix ScribbleArea repaint when tiled buffer does not cover update rect --- core_lib/src/canvaspainter.cpp | 2 +- core_lib/src/interface/scribblearea.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index 6138d9145e..5f96ad5c28 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -299,7 +299,7 @@ void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blit // 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 (mOptions.bIgnoreCanvasBuffer) { + if (!blitRect.contains(mTiledBuffer->bounds()) || mOptions.bIgnoreCanvasBuffer) { currentBitmapPainter.setCompositionMode(QPainter::CompositionMode_Source); currentBitmapPainter.drawImage(paintedImage->topLeft(), *paintedImage->image()); } diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index 10a97b5581..40a4b63dfd 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -1031,7 +1031,7 @@ void ScribbleArea::paintEvent(QPaintEvent* event) // paints the canvas painter.setWorldMatrixEnabled(false); - painter.drawPixmap(QPoint(0, 0), mCanvas); + painter.drawPixmap(event->rect(), mCanvas, event->rect()); currentTool()->paint(painter); From a9dd203eb4fc551a7f62e7bb5941b7893c5a9699 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Sun, 24 Sep 2023 13:32:56 +0200 Subject: [PATCH 41/49] Fix canvas pixmap being too big when using devicePixelRatio > 1 --- core_lib/src/interface/scribblearea.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index 40a4b63dfd..86dafd7919 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -1031,7 +1031,12 @@ void ScribbleArea::paintEvent(QPaintEvent* event) // paints the canvas painter.setWorldMatrixEnabled(false); - painter.drawPixmap(event->rect(), mCanvas, event->rect()); + + // In other places we use the blitRect to paint the buffer pixmap, however + // the main pixmap which needs to be scaled accordingly to DPI, which is not accounted for when using the event rect + // instead we can set a clipRect to avoid the area being updated needlessly + painter.setClipRect(event->rect()); + painter.drawPixmap(QPointF(), mCanvas); currentTool()->paint(painter); From 70fb576d34c036d12adf575623b34bd91a256ec7 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Thu, 28 Sep 2023 08:11:15 +0200 Subject: [PATCH 42/49] Try to fix seams appearing in Qt 6 --- core_lib/src/canvaspainter.cpp | 27 ++++++++++++++++--------- core_lib/src/interface/scribblearea.cpp | 7 +++---- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index 5f96ad5c28..43c1d21157 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -41,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) @@ -93,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(QPointF(), mPreLayersPixmap); mainPainter.setWorldMatrixEnabled(true); paintCurrentFrame(mainPainter, blitRect, mCurrentLayerIndex, mCurrentLayerIndex); @@ -108,7 +112,7 @@ void CanvasPainter::paintCached(const QRect& blitRect) } mainPainter.setWorldMatrixEnabled(false); - mainPainter.drawPixmap(blitRect, mPostLayersPixmap, blitRect); + mainPainter.drawPixmap(QPointF(), mPostLayersPixmap); mainPainter.setWorldMatrixEnabled(true); } @@ -122,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); @@ -177,7 +184,7 @@ void CanvasPainter::paint(const QRect& blitRect) preLayerPainter.end(); mainPainter.setWorldMatrixEnabled(false); - mainPainter.drawPixmap(blitRect, mPreLayersPixmap, blitRect); + mainPainter.drawPixmap(QPointF(), mPreLayersPixmap); mainPainter.setWorldMatrixEnabled(true); paintCurrentFrame(mainPainter, blitRect, mCurrentLayerIndex, mCurrentLayerIndex); @@ -187,7 +194,7 @@ void CanvasPainter::paint(const QRect& blitRect) postLayerPainter.end(); mainPainter.setWorldMatrixEnabled(false); - mainPainter.drawPixmap(blitRect, mPostLayersPixmap, blitRect); + mainPainter.drawPixmap(QPointF(), mPostLayersPixmap); mainPainter.setWorldMatrixEnabled(true); mPreLayersPixmapCacheValid = true; @@ -273,7 +280,7 @@ void CanvasPainter::paintOnionSkinFrame(QPainter& painter, QPainter& onionSkinPa onionSkinPainter.setBrush(colorBrush); onionSkinPainter.drawRect(painter.viewport()); } - painter.drawPixmap(blitRect, mOnionSkinPixmap, blitRect); + painter.drawPixmap(QPointF(), mOnionSkinPixmap); } void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blitRect, Layer* layer, bool isCurrentLayer) @@ -320,7 +327,8 @@ void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blit paintTransformedSelection(currentBitmapPainter, paintedImage, mSelection); } - painter.drawPixmap(blitRect, mCurrentLayerPixmap, blitRect); +// painter.setClipRect(blitRect); + painter.drawPixmap(QPointF(), mCurrentLayerPixmap); } void CanvasPainter::paintCurrentVectorFrame(QPainter& painter, const QRect& blitRect, Layer* layer, bool isCurrentLayer) @@ -357,9 +365,10 @@ void CanvasPainter::paintCurrentVectorFrame(QPainter& painter, const QRect& blit painter.setWorldMatrixEnabled(false); painter.setTransform(QTransform()); +// painter.setClipRect(blitRect); // 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(QPointF(), mCurrentLayerPixmap); } void CanvasPainter::paintTransformedSelection(QPainter& painter, BitmapImage* bitmapImage, const QRect& selection) const diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index 86dafd7919..028068eda7 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -196,7 +196,7 @@ void ScribbleArea::onTileUpdated(TiledBuffer* tiledBuffer, Tile* tile) { Q_UNUSED(tiledBuffer); const QRectF& mappedRect = mEditor->view()->getView().mapRect(QRectF(tile->bounds())); - update(mappedRect.toRect()); + update(mappedRect.toAlignedRect()); } void ScribbleArea::onTileCreated(TiledBuffer* tiledBuffer, Tile* tile) @@ -213,7 +213,7 @@ void ScribbleArea::onTileCreated(TiledBuffer* tiledBuffer, Tile* tile) } const QRectF& mappedRect = mEditor->view()->getView().mapRect(QRectF(tile->bounds())); - update(mappedRect.toRect()); + update(mappedRect.toAlignedRect()); } void ScribbleArea::updateFrame() @@ -819,7 +819,7 @@ void ScribbleArea::resizeEvent(QResizeEvent* event) QWidget::resizeEvent(event); mDevicePixelRatio = devicePixelRatioF(); mCanvas = QPixmap(QSizeF(size() * mDevicePixelRatio).toSize()); - + mCanvas.setDevicePixelRatio(mDevicePixelRatio); mEditor->view()->setCanvasSize(size()); invalidateCacheForFrame(mEditor->currentFrame()); @@ -1236,7 +1236,6 @@ void ScribbleArea::prepCanvas(int frame, QRect rect) void ScribbleArea::drawCanvas(int frame, QRect rect) { - mCanvas.setDevicePixelRatio(mDevicePixelRatio); prepCanvas(frame, rect); prepCameraPainter(frame); prepOverlays(frame); From 7f9687dd621ca14f3b615bf6d1f60526925151eb Mon Sep 17 00:00:00 2001 From: MrStevns Date: Fri, 29 Sep 2023 08:05:12 +0200 Subject: [PATCH 43/49] Fix canvas not being updated when using camera tool --- core_lib/src/tool/basetool.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core_lib/src/tool/basetool.cpp b/core_lib/src/tool/basetool.cpp index c6029edce8..759d49c8e0 100644 --- a/core_lib/src/tool/basetool.cpp +++ b/core_lib/src/tool/basetool.cpp @@ -111,7 +111,7 @@ void BaseTool::pointerDoubleClickEvent(PointerEvent* event) */ bool BaseTool::isDrawingTool() { - if (type() == ToolType::HAND || type() == ToolType::MOVE || type() == ToolType::SELECT ) + if (type() == ToolType::HAND || type() == ToolType::MOVE || type() == ToolType::CAMERA || type() == ToolType::SELECT ) { return false; } From 2ceed465f56e34cebac58f3b597102d3aac74376 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Fri, 29 Sep 2023 17:57:42 +0200 Subject: [PATCH 44/49] Cleanup CanvasPainter --- core_lib/src/canvaspainter.cpp | 8 +++----- core_lib/src/canvaspainter.h | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index 43c1d21157..850019cf77 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -238,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) @@ -253,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); @@ -327,7 +327,6 @@ void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blit paintTransformedSelection(currentBitmapPainter, paintedImage, mSelection); } -// painter.setClipRect(blitRect); painter.drawPixmap(QPointF(), mCurrentLayerPixmap); } @@ -365,7 +364,6 @@ void CanvasPainter::paintCurrentVectorFrame(QPainter& painter, const QRect& blit painter.setWorldMatrixEnabled(false); painter.setTransform(QTransform()); -// painter.setClipRect(blitRect); // Remember to adjust opacity based on additional opacity value from the keyframe painter.setOpacity(vectorImage->getOpacity() - (1.0-painter.opacity())); painter.drawPixmap(QPointF(), mCurrentLayerPixmap); diff --git a/core_lib/src/canvaspainter.h b/core_lib/src/canvaspainter.h index e39b94dc33..f52443b264 100644 --- a/core_lib/src/canvaspainter.h +++ b/core_lib/src/canvaspainter.h @@ -94,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); From 03ba9fa77604b6adb4068193225b980b0bb30abf Mon Sep 17 00:00:00 2001 From: MrStevns Date: Fri, 29 Sep 2023 17:57:55 +0200 Subject: [PATCH 45/49] Adjust blitRect explanation --- core_lib/src/util/blitrect.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/core_lib/src/util/blitrect.cpp b/core_lib/src/util/blitrect.cpp index 20fd7baabc..f378fd93fe 100644 --- a/core_lib/src/util/blitrect.cpp +++ b/core_lib/src/util/blitrect.cpp @@ -52,8 +52,11 @@ void BlitRect::extend(const QPoint p) void BlitRect::extend(const QRect& rect) { - // For reasons I do not understand, rect.size() or width and height, adds 1 pixel each... - // we subtract one to counter that + // For historical reasons the values returned by the bottom() and + // right() functions deviate from the true bottom-right corner of the rectangle: + // The right() function returns left() + width() - 1 and the bottom() + // function returns top() + height() - 1 + // In order to counter that, we subtract 1 from width and height extend(rect.topLeft(), QSize(rect.width() - 1, rect.height() - 1)); } From f3501ca35b101f1ecd0926106a2deda452d3462f Mon Sep 17 00:00:00 2001 From: MrStevns Date: Fri, 29 Sep 2023 18:43:28 +0200 Subject: [PATCH 46/49] Remove the need to allocate an empty QPointF to draw pixmaps --- core_lib/src/canvaspainter.cpp | 18 +++++++++--------- core_lib/src/canvaspainter.h | 4 ++++ core_lib/src/graphics/bitmap/tile.cpp | 1 + core_lib/src/graphics/bitmap/tile.h | 4 +++- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index 850019cf77..61b45c5617 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -97,7 +97,7 @@ void CanvasPainter::paintCached(const QRect& blitRect) QPainter mainPainter; initializePainter(mainPainter, mCanvas, blitRect); mainPainter.setWorldMatrixEnabled(false); - mainPainter.drawPixmap(QPointF(), mPreLayersPixmap); + mainPainter.drawPixmap(pointZero, mPreLayersPixmap); mainPainter.setWorldMatrixEnabled(true); paintCurrentFrame(mainPainter, blitRect, mCurrentLayerIndex, mCurrentLayerIndex); @@ -112,7 +112,7 @@ void CanvasPainter::paintCached(const QRect& blitRect) } mainPainter.setWorldMatrixEnabled(false); - mainPainter.drawPixmap(QPointF(), mPostLayersPixmap); + mainPainter.drawPixmap(pointZero, mPostLayersPixmap); mainPainter.setWorldMatrixEnabled(true); } @@ -184,7 +184,7 @@ void CanvasPainter::paint(const QRect& blitRect) preLayerPainter.end(); mainPainter.setWorldMatrixEnabled(false); - mainPainter.drawPixmap(QPointF(), mPreLayersPixmap); + mainPainter.drawPixmap(pointZero, mPreLayersPixmap); mainPainter.setWorldMatrixEnabled(true); paintCurrentFrame(mainPainter, blitRect, mCurrentLayerIndex, mCurrentLayerIndex); @@ -194,7 +194,7 @@ void CanvasPainter::paint(const QRect& blitRect) postLayerPainter.end(); mainPainter.setWorldMatrixEnabled(false); - mainPainter.drawPixmap(QPointF(), mPostLayersPixmap); + mainPainter.drawPixmap(pointZero, mPostLayersPixmap); mainPainter.setWorldMatrixEnabled(true); mPreLayersPixmapCacheValid = true; @@ -280,7 +280,7 @@ void CanvasPainter::paintOnionSkinFrame(QPainter& painter, QPainter& onionSkinPa onionSkinPainter.setBrush(colorBrush); onionSkinPainter.drawRect(painter.viewport()); } - painter.drawPixmap(QPointF(), mOnionSkinPixmap); + painter.drawPixmap(pointZero, mOnionSkinPixmap); } void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blitRect, Layer* layer, bool isCurrentLayer) @@ -313,7 +313,7 @@ void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blit const auto tiles = mTiledBuffer->tiles(); for (const Tile* tile : tiles) { - currentBitmapPainter.drawPixmap(tile->pos(), tile->pixmap()); + currentBitmapPainter.drawPixmap(tile->posF(), tile->pixmap()); } } else { // When we're drawing using a tool, the surface will be painted by the tiled buffer, @@ -327,7 +327,7 @@ void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blit paintTransformedSelection(currentBitmapPainter, paintedImage, mSelection); } - painter.drawPixmap(QPointF(), mCurrentLayerPixmap); + painter.drawPixmap(pointZero, mCurrentLayerPixmap); } void CanvasPainter::paintCurrentVectorFrame(QPainter& painter, const QRect& blitRect, Layer* layer, bool isCurrentLayer) @@ -353,7 +353,7 @@ void CanvasPainter::paintCurrentVectorFrame(QPainter& painter, const QRect& blit const auto tiles = mTiledBuffer->tiles(); for (const Tile* tile : tiles) { - currentVectorPainter.drawPixmap(tile->pos(), tile->pixmap()); + currentVectorPainter.drawPixmap(tile->posF(), tile->pixmap()); } } else if (mRenderTransform) { vectorImage->setSelectionTransformation(mSelectionTransform); @@ -366,7 +366,7 @@ void CanvasPainter::paintCurrentVectorFrame(QPainter& painter, const QRect& blit // Remember to adjust opacity based on additional opacity value from the keyframe painter.setOpacity(vectorImage->getOpacity() - (1.0-painter.opacity())); - painter.drawPixmap(QPointF(), mCurrentLayerPixmap); + painter.drawPixmap(pointZero, mCurrentLayerPixmap); } void CanvasPainter::paintTransformedSelection(QPainter& painter, BitmapImage* bitmapImage, const QRect& selection) const diff --git a/core_lib/src/canvaspainter.h b/core_lib/src/canvaspainter.h index f52443b264..79e7b54eb5 100644 --- a/core_lib/src/canvaspainter.h +++ b/core_lib/src/canvaspainter.h @@ -125,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 pointZero; + OnionSkinSubPainter mOnionSkinSubPainter; OnionSkinPainterOptions mOnionSkinPainterOptions; diff --git a/core_lib/src/graphics/bitmap/tile.cpp b/core_lib/src/graphics/bitmap/tile.cpp index 92134f0a83..5ea3626afe 100644 --- a/core_lib/src/graphics/bitmap/tile.cpp +++ b/core_lib/src/graphics/bitmap/tile.cpp @@ -21,6 +21,7 @@ GNU General Public License for more details. Tile::Tile(const QPoint& pos, QSize size): mTilePixmap(size), mPos(pos), + mPosF(pos), mBounds(pos, size), mSize(size) { diff --git a/core_lib/src/graphics/bitmap/tile.h b/core_lib/src/graphics/bitmap/tile.h index 46e7020806..b94bf3dc49 100644 --- a/core_lib/src/graphics/bitmap/tile.h +++ b/core_lib/src/graphics/bitmap/tile.h @@ -31,6 +31,7 @@ class Tile QPixmap& pixmap() { return mTilePixmap; } const QPoint& pos() const { return mPos; } + const QPointF& posF() const { return mPosF; } const QRect& bounds() const { return mBounds; } const QSize& size() const { return mSize; } @@ -39,7 +40,8 @@ class Tile void clear(); private: - QPixmap mTilePixmap; + QPixmap mTilePixmap; + QPointF mPosF; QPoint mPos; QRect mBounds; QSize mSize; From ad0afdf69dec089b6e3c756f36346fd518fad3d9 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Fri, 29 Sep 2023 19:35:19 +0200 Subject: [PATCH 47/49] TiledBuffer: Remove unused signals --- core_lib/src/graphics/bitmap/tiledbuffer.cpp | 3 --- core_lib/src/graphics/bitmap/tiledbuffer.h | 2 -- 2 files changed, 5 deletions(-) diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.cpp b/core_lib/src/graphics/bitmap/tiledbuffer.cpp index 98630eb3e7..5d4929e64c 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.cpp +++ b/core_lib/src/graphics/bitmap/tiledbuffer.cpp @@ -151,15 +151,12 @@ void TiledBuffer::clear() Tile* tile = i.value(); if (tile) { - emit tileCleared(this, tile); mTiles.remove(i.key()); delete tile; } } mTileBounds = BlitRect(); - - emit bufferCleared(this); } QPoint TiledBuffer::getTilePos(const TileIndex& index) const diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.h b/core_lib/src/graphics/bitmap/tiledbuffer.h index 25273bff72..7439c628cd 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.h +++ b/core_lib/src/graphics/bitmap/tiledbuffer.h @@ -69,8 +69,6 @@ class TiledBuffer: public QObject signals: void tileUpdated(TiledBuffer* tiledBuffer, Tile* tile); void tileCreated(TiledBuffer* tiledBuffer, Tile* tile); - void tileCleared(TiledBuffer* tiledBuffer, Tile* tile); - void bufferCleared(TiledBuffer* tiledBuffer); private: From a7199f8180d519afd1a961ca80a30f9ca362ef39 Mon Sep 17 00:00:00 2001 From: MrStevns Date: Sat, 30 Sep 2023 09:50:42 +0200 Subject: [PATCH 48/49] Fix typo --- core_lib/src/canvaspainter.cpp | 14 +++++++------- core_lib/src/canvaspainter.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index 61b45c5617..ef13240650 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -97,7 +97,7 @@ void CanvasPainter::paintCached(const QRect& blitRect) QPainter mainPainter; initializePainter(mainPainter, mCanvas, blitRect); mainPainter.setWorldMatrixEnabled(false); - mainPainter.drawPixmap(pointZero, mPreLayersPixmap); + mainPainter.drawPixmap(mPointZero, mPreLayersPixmap); mainPainter.setWorldMatrixEnabled(true); paintCurrentFrame(mainPainter, blitRect, mCurrentLayerIndex, mCurrentLayerIndex); @@ -112,7 +112,7 @@ void CanvasPainter::paintCached(const QRect& blitRect) } mainPainter.setWorldMatrixEnabled(false); - mainPainter.drawPixmap(pointZero, mPostLayersPixmap); + mainPainter.drawPixmap(mPointZero, mPostLayersPixmap); mainPainter.setWorldMatrixEnabled(true); } @@ -184,7 +184,7 @@ void CanvasPainter::paint(const QRect& blitRect) preLayerPainter.end(); mainPainter.setWorldMatrixEnabled(false); - mainPainter.drawPixmap(pointZero, mPreLayersPixmap); + mainPainter.drawPixmap(mPointZero, mPreLayersPixmap); mainPainter.setWorldMatrixEnabled(true); paintCurrentFrame(mainPainter, blitRect, mCurrentLayerIndex, mCurrentLayerIndex); @@ -194,7 +194,7 @@ void CanvasPainter::paint(const QRect& blitRect) postLayerPainter.end(); mainPainter.setWorldMatrixEnabled(false); - mainPainter.drawPixmap(pointZero, mPostLayersPixmap); + mainPainter.drawPixmap(mPointZero, mPostLayersPixmap); mainPainter.setWorldMatrixEnabled(true); mPreLayersPixmapCacheValid = true; @@ -280,7 +280,7 @@ void CanvasPainter::paintOnionSkinFrame(QPainter& painter, QPainter& onionSkinPa onionSkinPainter.setBrush(colorBrush); onionSkinPainter.drawRect(painter.viewport()); } - painter.drawPixmap(pointZero, mOnionSkinPixmap); + painter.drawPixmap(mPointZero, mOnionSkinPixmap); } void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blitRect, Layer* layer, bool isCurrentLayer) @@ -327,7 +327,7 @@ void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blit paintTransformedSelection(currentBitmapPainter, paintedImage, mSelection); } - painter.drawPixmap(pointZero, mCurrentLayerPixmap); + painter.drawPixmap(mPointZero, mCurrentLayerPixmap); } void CanvasPainter::paintCurrentVectorFrame(QPainter& painter, const QRect& blitRect, Layer* layer, bool isCurrentLayer) @@ -366,7 +366,7 @@ void CanvasPainter::paintCurrentVectorFrame(QPainter& painter, const QRect& blit // Remember to adjust opacity based on additional opacity value from the keyframe painter.setOpacity(vectorImage->getOpacity() - (1.0-painter.opacity())); - painter.drawPixmap(pointZero, mCurrentLayerPixmap); + painter.drawPixmap(mPointZero, mCurrentLayerPixmap); } void CanvasPainter::paintTransformedSelection(QPainter& painter, BitmapImage* bitmapImage, const QRect& selection) const diff --git a/core_lib/src/canvaspainter.h b/core_lib/src/canvaspainter.h index 79e7b54eb5..66c6e1d983 100644 --- a/core_lib/src/canvaspainter.h +++ b/core_lib/src/canvaspainter.h @@ -127,7 +127,7 @@ class CanvasPainter // 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 pointZero; + const QPointF mPointZero; OnionSkinSubPainter mOnionSkinSubPainter; From ed2be11f4035acccbacb2ac5a0896d55c72b2d18 Mon Sep 17 00:00:00 2001 From: Jakob Gahde Date: Sat, 30 Sep 2023 14:58:14 +0200 Subject: [PATCH 49/49] Fix compiler warning --- core_lib/src/graphics/bitmap/tile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core_lib/src/graphics/bitmap/tile.cpp b/core_lib/src/graphics/bitmap/tile.cpp index 5ea3626afe..7727e0517e 100644 --- a/core_lib/src/graphics/bitmap/tile.cpp +++ b/core_lib/src/graphics/bitmap/tile.cpp @@ -20,8 +20,8 @@ GNU General Public License for more details. Tile::Tile(const QPoint& pos, QSize size): mTilePixmap(size), - mPos(pos), mPosF(pos), + mPos(pos), mBounds(pos, size), mSize(size) {