diff --git a/3rdlib/paint_lib/src/paintlib-pch.h b/3rdlib/paint_lib/src/paintlib-pch.h index d6a6f4d3a..f814476e7 100644 --- a/3rdlib/paint_lib/src/paintlib-pch.h +++ b/3rdlib/paint_lib/src/paintlib-pch.h @@ -1,6 +1,3 @@ -#ifndef PAINTLIBPCH_H -#define PAINTLIBPCH_H - /* Add C includes here */ #if defined __cplusplus @@ -10,4 +7,4 @@ #include #include -#endif // PAINTLIBPCH_H +#endif diff --git a/3rdlib/paint_lib/src/qtmypaint/mphandler.cpp b/3rdlib/paint_lib/src/qtmypaint/mphandler.cpp index 9b000393e..20ad36adf 100755 --- a/3rdlib/paint_lib/src/qtmypaint/mphandler.cpp +++ b/3rdlib/paint_lib/src/qtmypaint/mphandler.cpp @@ -148,7 +148,6 @@ void MPHandler::endStroke() { mypaint_brush_reset(mBrush->brush); - mSurface->clear(); } float MPHandler::getBrushSettingBaseValue(MyPaintBrushSetting setting) diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index cb2763baf..fe37c9c31 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -25,6 +25,8 @@ GNU General Public License for more details. #include "vectorimage.h" #include "painterutils.h" +#include "mptile.h" +#include "tiledbuffer.h" CanvasPainter::CanvasPainter(QPixmap& canvas) : mCanvas(canvas) { @@ -157,7 +159,7 @@ void CanvasPainter::renderPostLayers(QPainter& painter, const QRect& blitRect) } } -void CanvasPainter::setPaintSettings(const Object* object, int currentLayer, int frame, QHash tiledBuffer, const QRect& tilesBounds) +void CanvasPainter::setPaintSettings(const Object* object, int currentLayer, int frame, QHash tiledHash, const QRect& tilesBounds, const TiledBuffer* tiledBuffer) { Q_ASSERT(object); mObject = object; @@ -165,8 +167,9 @@ void CanvasPainter::setPaintSettings(const Object* object, int currentLayer, int CANVASPAINTER_LOG("Set CurrentLayerIndex = %d", currentLayer); mCurrentLayerIndex = currentLayer; mFrameNumber = frame; - mTiledBuffer = tiledBuffer; + mTiledHash = tiledHash; mTilesRect = tilesBounds; + mTiledBuffer = tiledBuffer; } void CanvasPainter::paint(const QRect& blitRect) @@ -289,7 +292,7 @@ void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blit if (paintedImage == nullptr) { return; } paintedImage->loadFile(); // Critical! force the BitmapImage to load the image - const bool isDrawing = !mTiledBuffer.isEmpty(); + const bool isDrawing = !mTiledHash.isEmpty(); QPainter currentBitmapPainter; initializePainter(currentBitmapPainter, mCurrentLayerPixmap, blitRect); @@ -297,7 +300,7 @@ void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blit painter.setOpacity(paintedImage->getOpacity() - (1.0-painter.opacity())); painter.setWorldMatrixEnabled(false); - if (isCurrentLayer && isDrawing) + if ((isCurrentLayer && isDrawing) || (mOptions.bIgnoreCanvasBuffer)) { // Certain tools require being painted continuously, for example, the Polyline tool. // The tiled buffer does not update the area outside which it paints, @@ -309,7 +312,7 @@ void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blit currentBitmapPainter.drawImage(paintedImage->topLeft(), *paintedImage->image()); } - const auto tilesMap = mTiledBuffer; + const auto tilesMap = mTiledHash; for (const MPTile* tile : tilesMap) { currentBitmapPainter.drawImage(tile->pos(), tile->image()); } @@ -340,7 +343,7 @@ void CanvasPainter::paintCurrentVectorFrame(QPainter& painter, const QRect& blit QPainter currentVectorPainter; initializePainter(currentVectorPainter, mCurrentLayerPixmap, blitRect); - const bool isDrawing = !mTiledBuffer.isEmpty(); + const bool isDrawing = mTiledBuffer->isValid(); // Paint existing vector image to the painter vectorImage->paintImage(currentVectorPainter, mOptions.bOutlines, mOptions.bThinLines, mOptions.bAntiAlias); @@ -349,9 +352,9 @@ void CanvasPainter::paintCurrentVectorFrame(QPainter& painter, const QRect& blit if (isDrawing) { currentVectorPainter.setCompositionMode(mOptions.cmBufferBlendMode); - const auto tiles = mTiledBuffer; - for (const MPTile* tile : tiles) { - currentVectorPainter.drawImage(tile->pos(), tile->image()); + const auto tiles = mTiledBuffer->tiles(); + for (const Tile* tile : tiles) { + currentVectorPainter.drawPixmap(tile->posF(), tile->pixmap()); } } else if (mRenderTransform) { vectorImage->setSelectionTransformation(mSelectionTransform); diff --git a/core_lib/src/canvaspainter.h b/core_lib/src/canvaspainter.h index 60d96cb0e..d465cee70 100644 --- a/core_lib/src/canvaspainter.h +++ b/core_lib/src/canvaspainter.h @@ -30,8 +30,8 @@ GNU General Public License for more details. #include "onionskinpainteroptions.h" #include "onionskinsubpainter.h" -#include "mpsurface.h" - +class MPTile; +class TiledBuffer; class Object; class BitmapImage; class ViewManager; @@ -67,7 +67,7 @@ class CanvasPainter void setTransformedSelection(QRect selection, QTransform transform); void ignoreTransformedSelection(); - void setPaintSettings(const Object* object, int currentLayer, int frame, QHash tilesBuffer, const QRect& tilesBounds); + void setPaintSettings(const Object* object, int currentLayer, int frame, QHash tiledHash, const QRect& tilesBounds, const TiledBuffer* tiledBuffer); void paint(const QRect& blitRect); void paintCached(const QRect& blitRect); void resetLayerCache(); @@ -108,9 +108,11 @@ class CanvasPainter int mCurrentLayerIndex = 0; int mFrameNumber = 0; - QHash mTiledBuffer; + QHash mTiledHash; QRect mTilesRect; + const TiledBuffer* mTiledBuffer; + QImage mScaledBitmap; // Handle selection transformation diff --git a/core_lib/src/graphics/bitmap/bitmapimage.h b/core_lib/src/graphics/bitmap/bitmapimage.h index 2f66cc6af..119b4280c 100644 --- a/core_lib/src/graphics/bitmap/bitmapimage.h +++ b/core_lib/src/graphics/bitmap/bitmapimage.h @@ -22,8 +22,7 @@ GNU General Public License for more details. #include #include -class MPTile; - +#include "mptile.h" class BitmapImage : public KeyFrame { diff --git a/core_lib/src/graphics/bitmap/tile.cpp b/core_lib/src/graphics/bitmap/tile.cpp deleted file mode 100644 index 7727e0517..000000000 --- a/core_lib/src/graphics/bitmap/tile.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - -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 - -Tile::Tile(const QPoint& pos, QSize size): - mTilePixmap(size), - mPosF(pos), - mPos(pos), - mBounds(pos, size), - mSize(size) -{ - clear(); //Default tiles are transparent -} - -Tile::~Tile() -{ -} - -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 deleted file mode 100644 index b94bf3dc4..000000000 --- a/core_lib/src/graphics/bitmap/tile.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - -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 - -#include -#include - -class Tile -{ -public: - - explicit Tile (const QPoint& pos, QSize size); - ~Tile(); - - const QPixmap& pixmap() const { return mTilePixmap; } - 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; } - - /** Loads the input image into the tile */ - void load(const QImage& image, const QPoint& topLeft); - void clear(); - -private: - QPixmap mTilePixmap; - QPointF mPosF; - 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 deleted file mode 100644 index 5d4929e64..000000000 --- a/core_lib/src/graphics/bitmap/tiledbuffer.cpp +++ /dev/null @@ -1,166 +0,0 @@ -/* - -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 -#include - -#include "tile.h" - -TiledBuffer::TiledBuffer(QObject* parent) : QObject(parent) -{ -} - -TiledBuffer::~TiledBuffer() -{ - clear(); -} - -Tile* TiledBuffer::getTileFromIndex(const TileIndex& tileIndex) -{ - Tile* selectedTile = mTiles.value(tileIndex, nullptr); - - if (!selectedTile) { - // Time to allocate it, update table: - const QPoint& tilePos (getTilePos(tileIndex)); - selectedTile = new Tile(tilePos, QSize(UNIFORM_TILE_SIZE, UNIFORM_TILE_SIZE)); - mTiles.insert(tileIndex, selectedTile); - - emit this->tileCreated(this, selectedTile); - } else { - emit this->tileUpdated(this, selectedTile); - } - - return selectedTile; -} - -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_TILE_SIZE; - const int width = qMax(brushCursorWidth,brushWidth); - - // 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}); - - 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->bounds()); - } - } -} - -void TiledBuffer::drawImage(const QImage& image, const QRect& imageBounds, QPainter::CompositionMode cm, bool antialiasing) { - const float tileSize = UNIFORM_TILE_SIZE; - const float imageXRad = image.width(); - const float imageYRad = image.height(); - // 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}); - - QPainter painter(&tile->pixmap()); - - painter.translate(-tile->pos()); - painter.setRenderHint(QPainter::Antialiasing, antialiasing); - painter.setCompositionMode(cm); - painter.drawImage(imageBounds.topLeft(), image); - painter.end(); - - mTileBounds.extend(tile->bounds()); - } - } -} - - -void TiledBuffer::drawPath(QPainterPath path, int cursorWidth, QPen pen, QBrush brush, - QPainter::CompositionMode cm, bool antialiasing) -{ - const int pathWidth = pen.width(); - const int width = (qMax(pathWidth,cursorWidth) + 1); - const float tileSize = UNIFORM_TILE_SIZE; - const QRectF pathRect = path.boundingRect(); - - // 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}); - - 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->bounds()); - } - } -} - -void TiledBuffer::clear() -{ - QHashIterator i(mTiles); - - while (i.hasNext()) { - i.next(); - Tile* tile = i.value(); - if (tile) - { - mTiles.remove(i.key()); - delete tile; - } - } - - mTileBounds = BlitRect(); -} - -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)) }; -} diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.h b/core_lib/src/graphics/bitmap/tiledbuffer.h deleted file mode 100644 index 7439c628c..000000000 --- a/core_lib/src/graphics/bitmap/tiledbuffer.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - -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 - -#include -#include - -#include "blitrect.h" - -class QImage; -class QRect; -class Tile; - -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 -public: - TiledBuffer(QObject* parent = nullptr); - ~TiledBuffer(); - - /** Clears the content of the tiled buffer */ - void clear(); - - /** Returns true if there are any tiles, otherwise false */ - 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); - /** Draws a path with the specified parameters to the tiled buffer */ - 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); - - QHash tiles() const { return mTiles; } - - const QRect& bounds() const { return mTileBounds; } - -signals: - void tileUpdated(TiledBuffer* tiledBuffer, Tile* tile); - void tileCreated(TiledBuffer* tiledBuffer, Tile* tile); - -private: - - Tile* getTileFromIndex(const TileIndex& tileIndex); - - inline QPoint getTilePos(const TileIndex& index) const; - - const int UNIFORM_TILE_SIZE = 64; - - BlitRect mTileBounds; - - QHash mTiles; -}; - -#endif // TILEDBUFFER_H diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index addfb8e11..872a5e133 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -77,6 +77,9 @@ bool ScribbleArea::init() connect(mMyPaint, &MPHandler::tileUpdated, this, &ScribbleArea::updateTile); connect(mMyPaint, &MPHandler::tileCleared, this, &ScribbleArea::clearTile); + connect(&mTiledBuffer, &TiledBuffer::tileUpdated, this, &ScribbleArea::onTileUpdated); + connect(&mTiledBuffer, &TiledBuffer::tileCreated, this, &ScribbleArea::onTileCreated); + connect(mPrefs, &PreferenceManager::optionChanged, this, &ScribbleArea::settingUpdated); connect(mEditor->tools(), &ToolManager::toolPropertyChanged, this, &ScribbleArea::onToolPropertyUpdated); connect(mEditor->tools(), &ToolManager::toolChanged, this, &ScribbleArea::onToolChanged); @@ -202,10 +205,6 @@ void ScribbleArea::setEffect(SETTING e, bool isOn) void ScribbleArea::updateFrame() { - if (currentTool()->isActive() && currentTool()->isDrawingTool()) { - return; - } - update(); } @@ -263,8 +262,6 @@ void ScribbleArea::invalidateOnionSkinsCacheAround(int frameNumber) void ScribbleArea::invalidateAllCache() { - if (currentTool()->isDrawingTool() && currentTool()->isActive()) { return; } - QPixmapCache::clear(); mPixmapCacheKeys.clear(); invalidatePainterCaches(); @@ -332,10 +329,8 @@ void ScribbleArea::onWillScrub(int frameNumber) Q_UNUSED(frameNumber) // If we're in the middle of a painting session, stop it and save what was painted - if (mIsPainting) { - paintBitmapBuffer(); - invalidatePainterCaches(); - clearDrawingBuffer(); + + if (mTiledBuffer.isValid() || !mTilesBlitRect.isEmpty()) { endStroke(); } } @@ -366,16 +361,6 @@ void ScribbleArea::onFrameModified(int frameNumber) updateFrame(); } -void ScribbleArea::onDidDraw(int frameNumber) -{ - if (mPrefs->isOn(SETTING::PREV_ONION) || mPrefs->isOn(SETTING::NEXT_ONION)) { - invalidateOnionSkinsCacheAround(frameNumber); - invalidatePainterCaches(); - } - invalidateCacheForFrame(frameNumber); - updateFrame(); -} - void ScribbleArea::onViewChanged() { invalidateAllCache(); @@ -872,6 +857,7 @@ void ScribbleArea::clearDrawingBuffer() mMyPaint->clearSurface(); mBufferTiles.clear(); mTilesBlitRect = BlitRect(); + mTiledBuffer.clear(); } void ScribbleArea::paintCanvasCursor(QPainter& painter) @@ -1216,7 +1202,7 @@ void ScribbleArea::prepCanvas(int frame) mCanvasPainter.setViewTransform(vm->getView(), vm->getViewInverse()); mCanvasPainter.setTransformedSelection(sm->mySelectionRect().toRect(), sm->selectionTransform()); - mCanvasPainter.setPaintSettings(object, mEditor->layers()->currentLayerIndex(), frame, mBufferTiles, mTilesBlitRect); + mCanvasPainter.setPaintSettings(object, mEditor->layers()->currentLayerIndex(), frame, mBufferTiles, mTilesBlitRect, &mTiledBuffer); } void ScribbleArea::drawCanvas(int frame, QRect rect) @@ -1273,6 +1259,30 @@ void ScribbleArea::loadMPBrush(const QByteArray &content) updateCanvasCursor(); } +void ScribbleArea::onTileUpdated(TiledBuffer* tiledBuffer, Tile* tile) +{ + Q_UNUSED(tiledBuffer); + const QRectF& mappedRect = mEditor->view()->getView().mapRect(QRectF(tile->bounds())); + update(mappedRect.toAlignedRect()); +} + +void ScribbleArea::onTileCreated(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()); + const QImage& image = *bitmapImage->image(); + 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 + } + + const QRectF& mappedRect = mEditor->view()->getView().mapRect(QRectF(tile->bounds())); + update(mappedRect.toAlignedRect()); +} + void ScribbleArea::updateTile(MPSurface *surface, MPTile *tile) { Q_UNUSED(surface) @@ -1298,11 +1308,11 @@ void ScribbleArea::loadTile(MPSurface* surface, MPTile* tile) // Therefore we only load the mypaint surface with bitmap data when not using the polyline tool. // TODO: This code would be better served in StrokeTool rather than here. - if (mIsPainting && currentTool()->type() != ToolType::POLYLINE) { +// if (mIsPainting && currentTool()->type() != ToolType::POLYLINE) { const auto& bitmapImage = currentBitmapImage(layer); const QImage& image = *bitmapImage->image(); mMyPaint->loadTile(image, bitmapImage->topLeft(), tile); - } +// } mTilesBlitRect.extend(tile->pos(), tile->boundingRect().size()); const QRectF& mappedRect = mEditor->view()->getView().mapRect(QRectF(tile->pos(), tile->boundingRect().size())); update(mappedRect.toRect()); @@ -1326,16 +1336,13 @@ void ScribbleArea::clearTile(MPSurface *surface, QRect tileRect) void ScribbleArea::startStroke() { mMyPaint->startStroke(); - mIsPainting = true; } void ScribbleArea::strokeTo(QPointF point, float pressure, float xtilt, float ytilt, double dt) { - if (!mIsPainting) { return; } + Q_ASSERT(mEditor->layers()->currentLayer()->type() == Layer::BITMAP); - if (mEditor->layers()->currentLayer()->type() == Layer::BITMAP) { - mMyPaint->strokeTo(static_cast(point.x()), static_cast(point.y()), pressure, xtilt, ytilt, dt); - } + mMyPaint->strokeTo(static_cast(point.x()), static_cast(point.y()), pressure, xtilt, ytilt, dt); } void ScribbleArea::forceUpdateMyPaintStates() @@ -1350,43 +1357,49 @@ void ScribbleArea::forceUpdateMyPaintStates() } void ScribbleArea::endStroke() -{ +{ if (mEditor->layers()->currentLayer()->type() == Layer::BITMAP) { - paintBitmapBuffer(); + mMyPaint->endStroke(); } - - clearTilesBuffer(); - mIsPainting = false; - mMyPaint->endStroke(); + if (mPrefs->isOn(SETTING::PREV_ONION) || mPrefs->isOn(SETTING::NEXT_ONION)) { + invalidateOnionSkinsCacheAround(mEditor->currentFrame()); + invalidatePainterCaches(); + } + invalidateCacheForFrame(mEditor->currentFrame()); + updateFrame(); - onDidDraw(mEditor->currentFrame()); qDebug() << "end stroke"; } -void ScribbleArea::clearTilesBuffer() +void ScribbleArea::flipSelection(bool flipVertical) { - // clear the temp tiles buffer - if (!mBufferTiles.isEmpty()) { - mBufferTiles.clear(); - } + mEditor->select()->flipSelection(flipVertical); } -void ScribbleArea::flipSelection(bool flipVertical) +void ScribbleArea::drawPath(QPainterPath path, QPen pen, QBrush brush, QPainter::CompositionMode cm) { - mEditor->select()->flipSelection(flipVertical); + mTiledBuffer.drawPath(mEditor->view()->mapScreenToCanvas(path), mEditor->view()->mapScreenToCanvas(mCursorImg.rect()).width(), pen, brush, cm, mPrefs->isOn(SETTING::ANTIALIAS)); } -//void ScribbleArea::drawPolyline(QPainterPath path, QPen pen, bool useAA) -//{ -// QRectF updateRect = mEditor->view()->mapCanvasToScreen(path.boundingRect().toRect()).adjusted(-1, -1, 1, 1); +void ScribbleArea::drawPolyline(QPainterPath path, QPen pen, bool useAA) +{ + BlitRect blitRect; + + // 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()); + + 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()); -//} + mTiledBuffer.clear(); + 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)); +} /************************************************************************************/ // view handling diff --git a/core_lib/src/interface/scribblearea.h b/core_lib/src/interface/scribblearea.h index e3dee1c0c..fb8800bb5 100644 --- a/core_lib/src/interface/scribblearea.h +++ b/core_lib/src/interface/scribblearea.h @@ -46,6 +46,7 @@ GNU General Public License for more details. #include "selectionpainter.h" #include "camerapainter.h" #include "blitrect.h" +#include "tiledbuffer.h" #include "brushsetting.h" @@ -60,7 +61,6 @@ class VectorImage; class MPHandler; class MPSurface; class MPTile; -class QGraphicsPixmapItem; class ScribbleArea : public QWidget { @@ -136,10 +136,6 @@ class ScribbleArea : public QWidget /** Tool changed, invalidate cache and frame if needed */ void onToolChanged(ToolType); - - /** After applying a stroke, - * note: optimization to avoid clearing mypaint when we draw on the canvas */ - void onDidDraw(int frameNumber); void startStroke(); void strokeTo(QPointF point, float pressure, float xtilt, float ytilt, double dt); @@ -198,6 +194,9 @@ public slots: void clearTile(MPSurface *surface, QRect tileRect); void loadTile(MPSurface* surface, MPTile* tile); + void onTileUpdated(TiledBuffer* tiledBuffer, Tile* tile); + void onTileCreated(TiledBuffer* tiledBuffer, Tile* tile); + protected: bool event(QEvent *event) override; void tabletEvent(QTabletEvent*) override; @@ -212,8 +211,8 @@ public slots: void resizeEvent(QResizeEvent*) override; public: -// void drawPolyline(QPainterPath path, QPen pen, bool useAA); -// void drawPath(QPainterPath path, QPen pen, QBrush brush, QPainter::CompositionMode cm); + void drawPolyline(QPainterPath path, QPen pen, bool useAA); + void drawPath(QPainterPath path, QPen pen, QBrush brush, QPainter::CompositionMode cm); void pointerPressEvent(PointerEvent*); void pointerMoveEvent(PointerEvent*); @@ -221,8 +220,6 @@ public slots: void updateCanvasCursor(); - void clearTilesBuffer(); - /// Call this when starting to use a paint tool. Checks whether we are drawing /// on an empty frame, and if so, takes action according to use preference. void handleDrawingOnEmptyFrame(); @@ -231,6 +228,7 @@ public slots: MPHandler* mMyPaint = nullptr; QHash mBufferTiles; + TiledBuffer mTiledBuffer; BlitRect mTilesBlitRect; private: @@ -310,8 +308,6 @@ public slots: QPoint mCursorCenterPos; QPointF mTransformedCursorPos; - bool mIsPainting = false; - PreferenceManager* mPrefs = nullptr; QPixmap mCanvas; diff --git a/core_lib/src/tool/brushtool.cpp b/core_lib/src/tool/brushtool.cpp index 6b958d493..814a0fa73 100644 --- a/core_lib/src/tool/brushtool.cpp +++ b/core_lib/src/tool/brushtool.cpp @@ -189,15 +189,3 @@ void BrushTool::drawStroke(PointerEvent* event) } } } - -void BrushTool::endStroke() -{ - Layer* layer = mEditor->layers()->currentLayer(); - - if (layer->type() == Layer::BITMAP) - StrokeTool::paintBitmapStroke(); - else if (layer->type() == Layer::VECTOR) - StrokeTool::paintVectorStroke(); - - StrokeTool::endStroke(); -} diff --git a/core_lib/src/tool/brushtool.h b/core_lib/src/tool/brushtool.h index 60e8f11e8..4ab2d14ee 100644 --- a/core_lib/src/tool/brushtool.h +++ b/core_lib/src/tool/brushtool.h @@ -34,7 +34,6 @@ class BrushTool : public StrokeTool void resetToDefault() override; QCursor cursor() override; - void endStroke(); void drawStroke(PointerEvent* event); void pointerMoveEvent(PointerEvent*) override; diff --git a/core_lib/src/tool/erasertool.cpp b/core_lib/src/tool/erasertool.cpp index c0b9b0a19..9cd7ed55d 100644 --- a/core_lib/src/tool/erasertool.cpp +++ b/core_lib/src/tool/erasertool.cpp @@ -169,18 +169,6 @@ void EraserTool::drawStroke(PointerEvent* event) } } -void EraserTool::endStroke() -{ - Layer* layer = mEditor->layers()->currentLayer(); - - if (layer->type() == Layer::BITMAP) - paintBitmapStroke(); - else if (layer->type() == Layer::VECTOR) - paintVectorStroke(); - - StrokeTool::endStroke(); -} - void EraserTool::removeVectorPaint() { Layer* layer = mEditor->layers()->currentLayer(); diff --git a/core_lib/src/tool/erasertool.h b/core_lib/src/tool/erasertool.h index 81a245f4b..4b76776ba 100644 --- a/core_lib/src/tool/erasertool.h +++ b/core_lib/src/tool/erasertool.h @@ -36,7 +36,6 @@ class EraserTool : public StrokeTool void pointerReleaseEvent(PointerEvent*) override; void drawStroke(PointerEvent* event); - void endStroke(); void removeVectorPaint(); void updateStrokes(PointerEvent* event); diff --git a/core_lib/src/tool/penciltool.cpp b/core_lib/src/tool/penciltool.cpp index d529e0e91..2dd1c30b6 100644 --- a/core_lib/src/tool/penciltool.cpp +++ b/core_lib/src/tool/penciltool.cpp @@ -173,18 +173,6 @@ void PencilTool::pointerReleaseEvent(PointerEvent *event) endStroke(); } -void PencilTool::endStroke() -{ - Layer* layer = mEditor->layers()->currentLayer(); - - if (layer->type() == Layer::BITMAP) - StrokeTool::paintBitmapStroke(); - else if (layer->type() == Layer::VECTOR) - StrokeTool::paintVectorStroke(); - - StrokeTool::endStroke(); -} - void PencilTool::drawStroke(PointerEvent* event) { StrokeTool::drawStroke(event); diff --git a/core_lib/src/tool/penciltool.h b/core_lib/src/tool/penciltool.h index 9c546bbdf..184bede80 100644 --- a/core_lib/src/tool/penciltool.h +++ b/core_lib/src/tool/penciltool.h @@ -38,7 +38,6 @@ class PencilTool : public StrokeTool void pointerReleaseEvent(PointerEvent*) override; void drawStroke(PointerEvent* event); - void endStroke(); void setWidth(const qreal width) override; void setFeather(const qreal feather) override; diff --git a/core_lib/src/tool/pentool.cpp b/core_lib/src/tool/pentool.cpp index 46afb8e5e..e34b54f40 100644 --- a/core_lib/src/tool/pentool.cpp +++ b/core_lib/src/tool/pentool.cpp @@ -155,15 +155,3 @@ void PenTool::drawStroke(PointerEvent* event) } } } - -void PenTool::endStroke() -{ - Layer* layer = mEditor->layers()->currentLayer(); - - if (layer->type() == Layer::BITMAP) - StrokeTool::paintBitmapStroke(); - else if (layer->type() == Layer::VECTOR) - StrokeTool::paintVectorStroke(); - - StrokeTool::endStroke(); -} diff --git a/core_lib/src/tool/pentool.h b/core_lib/src/tool/pentool.h index a9d353796..67bb33c47 100644 --- a/core_lib/src/tool/pentool.h +++ b/core_lib/src/tool/pentool.h @@ -37,7 +37,6 @@ class PenTool : public StrokeTool void pointerReleaseEvent(PointerEvent*) override; void drawStroke(PointerEvent* event); - void endStroke(); 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 99348ea09..3c76b37eb 100644 --- a/core_lib/src/tool/polylinetool.cpp +++ b/core_lib/src/tool/polylinetool.cpp @@ -164,75 +164,16 @@ void PolylineTool::pointerPressOnBitmap(PointerEvent* event) } mScribbleArea->paintBitmapBuffer(); - mScribbleArea->clearTilesBuffer(); + mScribbleArea->clearDrawingBuffer(); } void PolylineTool::pointerMoveEvent(PointerEvent* event) { - Layer* layer = mEditor->layers()->currentLayer(); - - - if (mPoints.size() > 0) - { - if (layer->type() == Layer::VECTOR) - { - pointerMoveOnVector(event); - } else if (layer->type() == Layer::BITMAP) { - pointerMoveOnBitmap(event); - } - } + drawPolyline(mPoints, getCurrentPoint(), event->timeStamp()); previousPoint = getCurrentPoint(); } -void PolylineTool::pointerMoveOnVector(PointerEvent *) -{ - QPen pen(mEditor->color()->frontColor(), - properties.width, - Qt::SolidLine, - Qt::RoundCap, - Qt::RoundJoin); - - QPainterPath tempPath; - if (properties.bezier_state) - { - tempPath = BezierCurve(mPoints).getSimplePath(); - } - else - { - tempPath = BezierCurve(mPoints).getStraightPath(); - } - tempPath.lineTo(getCurrentPoint()); - - tempPath = mEditor->view()->mapCanvasToScreen(tempPath); - if (mScribbleArea->makeInvisible() == true) - { - pen.setWidth(0); - pen.setStyle(Qt::DotLine); - } - else - { - pen.setWidth(properties.width * mEditor->view()->scaling()); - } - - mScribbleArea->drawPolyline(tempPath, pen, true); -} - -void PolylineTool::pointerMoveOnBitmap(PointerEvent * event) -{ - mScribbleArea->mMyPaint->clearSurface(); - mScribbleArea->mMyPaint->startStroke(); - - // TODO: can we get the old bezier functionality back when using mypaint? - for(int i=0; itimeStamp())); - } - drawStroke(getCurrentPoint(), calculateDeltaTime(event->timeStamp())); - - mScribbleArea->updateFrame(); -} - double PolylineTool::calculateDeltaTime(quint64) { // FIXME: Variable dt won't work, will probably have to rework how the polyline is drawn @@ -279,7 +220,7 @@ bool PolylineTool::keyPressEvent(QKeyEvent* event) break; } - return BaseTool::keyPressEvent(event); + return StrokeTool::keyPressEvent(event); } void PolylineTool::drawPolyline(QList points, QPointF endPoint, quint64 timeStamp) @@ -287,17 +228,19 @@ void PolylineTool::drawPolyline(QList points, QPointF endPoint, quint64 Layer* layer = mEditor->layers()->currentLayer(); if (points.size() > 0) { - if (layer->type() == Layer::BITMAP) { - mScribbleArea->mMyPaint->clearSurface(); - mScribbleArea->mMyPaint->startStroke(); + BlitRect previousTilesRect = mScribbleArea->mTilesBlitRect; + mScribbleArea->clearDrawingBuffer(); + mScribbleArea->startStroke(); for(int i=0; iupdateFrame(); - mScribbleArea->mMyPaint->endStroke(); + + // In order to clear the polyline overall dirty bounds, we need to do an additional update, otherwise there will be residue + // of the previous stroke in some cases. + updateDirtyRect(points, previousTilesRect); } else if (layer->type() == Layer::VECTOR) { @@ -339,6 +282,27 @@ void PolylineTool::drawPolyline(QList points, QPointF endPoint, quint64 } } +void PolylineTool::updateDirtyRect(QList linePoints, BlitRect dirtyRect) +{ + BlitRect blitRect; + + // 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(dirtyRect).toRect()); + + BlitRect lineBounds; + + for (QPointF point : linePoints) { + lineBounds.extend(point.toPoint()); + } + QRect updateRect = mEditor->view()->mapCanvasToScreen(lineBounds).toRect(); + + // Now extend with the new path bounds mapped to the local coordinate + blitRect.extend(updateRect); + + // And update only the affected area + mScribbleArea->update(blitRect.adjusted(-1, -1, 1, 1)); +} void PolylineTool::cancelPolyline() { @@ -353,7 +317,6 @@ void PolylineTool::endPolyline(QList points, quint64 timeStamp) if (layer->type() == Layer::VECTOR) { - mScribbleArea->clearDrawingBuffer(); BezierCurve curve = BezierCurve(points, properties.bezier_state); if (mScribbleArea->makeInvisible() == true) { @@ -367,15 +330,13 @@ void PolylineTool::endPolyline(QList points, quint64 timeStamp) curve.setVariableWidth(false); curve.setInvisibility(mScribbleArea->makeInvisible()); - static_cast(layer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0)->addCurve(curve, mEditor->view()->scaling()); + VectorImage* vectorImage = static_cast(layer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0); + if (vectorImage == nullptr) { return; } // Can happen if the first frame is deleted while drawing + vectorImage->addCurve(curve, mEditor->view()->scaling()); } if (layer->type() == Layer::BITMAP) { - for(int i=0; i mPoints; QPointF previousPoint; + void updateDirtyRect(QList linePoints, BlitRect dirtyRect); void drawPolyline(QList points, QPointF endPoint, quint64 timeStamp); void cancelPolyline(); void endPolyline(QList points, quint64 timeStamp); diff --git a/core_lib/src/tool/smudgetool.cpp b/core_lib/src/tool/smudgetool.cpp index ab7d874fa..b52e395a8 100644 --- a/core_lib/src/tool/smudgetool.cpp +++ b/core_lib/src/tool/smudgetool.cpp @@ -262,14 +262,7 @@ void SmudgeTool::pointerReleaseEvent(PointerEvent* event) mEditor->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame()); } - Layer* layer = mEditor->layers()->currentLayer(); - - if (layer->type() == Layer::BITMAP) - StrokeTool::paintBitmapStroke(); - else if (layer->type() == Layer::VECTOR) - StrokeTool::paintVectorStroke(); - - StrokeTool::endStroke(); + endStroke(); } } diff --git a/core_lib/src/tool/strokemanager.cpp b/core_lib/src/tool/strokemanager.cpp index 8db5d32fd..b4e9a6b4f 100644 --- a/core_lib/src/tool/strokemanager.cpp +++ b/core_lib/src/tool/strokemanager.cpp @@ -255,7 +255,7 @@ QList StrokeManager::noInpolOp(QList points) { setPressure(getPressure()); - points << mLastPixel << mCurrentPixel; + points << mLastPixel << mLastPixel << mCurrentPixel << mCurrentPixel; // Set lastPixel to CurrentPixel // new interpolated pixel diff --git a/core_lib/src/tool/stroketool.cpp b/core_lib/src/tool/stroketool.cpp index 7d03e3de4..3abc92b03 100644 --- a/core_lib/src/tool/stroketool.cpp +++ b/core_lib/src/tool/stroketool.cpp @@ -61,7 +61,9 @@ void StrokeTool::startStroke(PointerEvent::InputType inputType) mEditor->backup(typeName()); - mScribbleArea->startStroke(); + if (mEditor->layers()->currentLayer()->type() == Layer::BITMAP) { + mScribbleArea->startStroke(); + } mFirstDraw = true; mLastPixel = getCurrentPixel(); @@ -106,6 +108,13 @@ bool StrokeTool::emptyFrameActionEnabled() void StrokeTool::endStroke() { + const Layer* layer = mEditor->layers()->currentLayer(); + if (layer->type() == Layer::BITMAP) + commitBitmapStroke(); + if (layer->type() == Layer::VECTOR) { + commitVectorStroke(); + } + strokeManager()->interpolateEnd(); mStrokePressures << strokeManager()->getPressure(); mStrokePoints.clear(); @@ -132,7 +141,12 @@ void StrokeTool::drawStroke(const QPointF pos, double dt) const QPointF pixel = pos; const float pressure = static_cast(mCurrentPressure); - mScribbleArea->strokeTo(pixel, pressure, mCurrentXTilt, mCurrentYTilt, dt); + + if (mEditor->layers()->currentLayer()->type() == Layer::BITMAP) { + mScribbleArea->strokeTo(pixel, pressure, mCurrentXTilt, mCurrentYTilt, dt); + } else { + // Only mypaint utilizes a strokeTo method currently... + } if ( pixel != mLastPixel || !mFirstDraw ) { @@ -153,16 +167,15 @@ double StrokeTool::calculateDeltaTime(quint64 timeStamp) return frameTime; } -void StrokeTool::paintBitmapStroke() +void StrokeTool::commitBitmapStroke() { mScribbleArea->paintBitmapBuffer(); - mScribbleArea->invalidatePainterCaches(); mScribbleArea->clearDrawingBuffer(); } // This function uses the points from DrawStroke // and turns them into vector lines. -void StrokeTool::paintVectorStroke() +void StrokeTool::commitVectorStroke() { if (mStrokePoints.empty()) return; @@ -171,8 +184,6 @@ void StrokeTool::paintVectorStroke() if (layer->type() == Layer::VECTOR && mStrokePoints.size() > -1) { - // Clear the temporary pixel path - mScribbleArea->clearDrawingBuffer(); qreal tol = mScribbleArea->getCurveSmoothing() / mEditor->view()->scaling(); BezierCurve curve(mStrokePoints, mStrokePressures, tol); @@ -193,7 +204,7 @@ void StrokeTool::paintVectorStroke() vectorImage->setSelected(vectorImage->getLastCurveNumber(), true); + mScribbleArea->clearDrawingBuffer(); mEditor->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame()); - mScribbleArea->invalidatePainterCaches(); } } diff --git a/core_lib/src/tool/stroketool.h b/core_lib/src/tool/stroketool.h index 7d903d3a2..b99a05a6e 100644 --- a/core_lib/src/tool/stroketool.h +++ b/core_lib/src/tool/stroketool.h @@ -38,8 +38,8 @@ class StrokeTool : public BaseTool void drawStroke(PointerEvent* event); void endStroke(); - void paintBitmapStroke(); - void paintVectorStroke(); + void commitBitmapStroke(); + void commitVectorStroke(); bool keyPressEvent(QKeyEvent* event) override;