diff --git a/libs/ui/tool/kis_tool_freehand.cc b/libs/ui/tool/kis_tool_freehand.cc index 2b6000b422..5eb23365e7 100644 --- a/libs/ui/tool/kis_tool_freehand.cc +++ b/libs/ui/tool/kis_tool_freehand.cc @@ -1,520 +1,520 @@ /* * kis_tool_freehand.cc - part of Krita * * Copyright (c) 2003-2007 Boudewijn Rempt * Copyright (c) 2004 Bart Coppens * Copyright (c) 2007,2008,2010 Cyrille Berger * Copyright (c) 2009 Lukáš Tvrdý * * 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; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_tool_freehand.h" #include #include #include #include #include #include #include #include #include #include //pop up palette #include // Krita/image #include #include #include #include #include #include // Krita/ui #include "kis_abstract_perspective_grid.h" #include "kis_config.h" #include "canvas/kis_canvas2.h" #include "kis_cursor.h" #include #include #include "kis_painting_information_builder.h" #include "kis_tool_freehand_helper.h" #include "kis_recording_adapter.h" #include "strokes/freehand_stroke.h" using namespace std::placeholders; // For _1 placeholder KisToolFreehand::KisToolFreehand(KoCanvasBase * canvas, const QCursor & cursor, const KUndo2MagicString &transactionText) : KisToolPaint(canvas, cursor), m_paintopBasedPickingInAction(false), m_brushResizeCompressor(200, std::bind(&KisToolFreehand::slotDoResizeBrush, this, _1)) { m_assistant = false; m_magnetism = 1.0; m_only_one_assistant = true; setSupportOutline(true); setMaskSyntheticEvents(KisConfig().disableTouchOnCanvas()); // Disallow mouse events from finger presses unless enabled m_infoBuilder = new KisToolFreehandPaintingInformationBuilder(this); m_recordingAdapter = new KisRecordingAdapter(); m_helper = new KisToolFreehandHelper(m_infoBuilder, transactionText, m_recordingAdapter); connect(m_helper, SIGNAL(requestExplicitUpdateOutline()), SLOT(explicitUpdateOutline())); m_needEndContinuedStroke = false; m_lastID = KoID("newId-hehe"); connect(&m_updateTimer, SIGNAL(timeout()), SLOT(slotUpdateSystem())); } KisToolFreehand::~KisToolFreehand() { delete m_helper; delete m_recordingAdapter; delete m_infoBuilder; } void KisToolFreehand::mouseMoveEvent(KoPointerEvent *event) { KisToolPaint::mouseMoveEvent(event); m_helper->cursorMoved(convertToPixelCoord(event)); } KisSmoothingOptionsSP KisToolFreehand::smoothingOptions() const { return m_helper->smoothingOptions(); } void KisToolFreehand::resetCursorStyle() { KisConfig cfg; switch (cfg.newCursorStyle()) { case CURSOR_STYLE_NO_CURSOR: useCursor(KisCursor::blankCursor()); break; case CURSOR_STYLE_POINTER: useCursor(KisCursor::arrowCursor()); break; case CURSOR_STYLE_SMALL_ROUND: useCursor(KisCursor::roundCursor()); break; case CURSOR_STYLE_CROSSHAIR: useCursor(KisCursor::crossCursor()); break; case CURSOR_STYLE_TRIANGLE_RIGHTHANDED: useCursor(KisCursor::triangleRightHandedCursor()); break; case CURSOR_STYLE_TRIANGLE_LEFTHANDED: useCursor(KisCursor::triangleLeftHandedCursor()); break; case CURSOR_STYLE_BLACK_PIXEL: useCursor(KisCursor::pixelBlackCursor()); break; case CURSOR_STYLE_WHITE_PIXEL: useCursor(KisCursor::pixelWhiteCursor()); break; case CURSOR_STYLE_TOOLICON: default: KisToolPaint::resetCursorStyle(); break; } } KisPaintingInformationBuilder* KisToolFreehand::paintingInformationBuilder() const { return m_infoBuilder; } KisRecordingAdapter* KisToolFreehand::recordingAdapter() const { return m_recordingAdapter; } void KisToolFreehand::resetHelper(KisToolFreehandHelper *helper) { delete m_helper; m_helper = helper; } int KisToolFreehand::flags() const { return KisTool::FLAG_USES_CUSTOM_COMPOSITEOP|KisTool::FLAG_USES_CUSTOM_PRESET |KisTool::FLAG_USES_CUSTOM_SIZE; } void KisToolFreehand::activate(ToolActivation activation, const QSet &shapes) { KisToolPaint::activate(activation, shapes); } void KisToolFreehand::deactivate() { if (mode() == PAINT_MODE) { m_needEndContinuedStroke = true; endStroke(); setMode(KisTool::HOVER_MODE); } else if (m_helper->isRunning()) { m_needEndContinuedStroke = true; endStroke(); } KisToolPaint::deactivate(); } void KisToolFreehand::initStroke(KoPointerEvent *event) { if (m_helper->isRunning()) { if (m_needEndContinuedStroke) m_needEndContinuedStroke = false; qDebug() << "Inited part of continued stroke\n"; m_helper->initPaintInContinuedStroke(event, canvas()->resourceManager(), image(), currentNode()); m_updateTimer.stop(); } else { m_helper->initPaint(event, convertToPixelCoord(event), canvas()->resourceManager(), image(), currentNode(), image().data()); } } void KisToolFreehand::doStroke(KoPointerEvent *event) { //set canvas information here?// KisCanvas2 *canvas2 = dynamic_cast(canvas()); if (canvas2) { m_helper->setCanvasHorizontalMirrorState(canvas2->xAxisMirrored()); m_helper->setCanvasRotation(canvas2->rotationAngle()); } m_helper->paintEvent(event); } void KisToolFreehand::endStroke() { if (currentPaintOpPreset()->settings()->needsContinuedStroke() && !m_needEndContinuedStroke) { qDebug() << "Finished part of continued stroke\n" << ppVar(m_needEndContinuedStroke); m_helper->endPaintInContinuedStroke(); - m_updateTimer.start(50); + m_updateTimer.start(100); } else { qDebug() << "Stroke finished finaly\n" << ppVar(m_needEndContinuedStroke); m_helper->endPaint(); } } bool KisToolFreehand::primaryActionSupportsHiResEvents() const { return true; } void KisToolFreehand::beginPrimaryAction(KoPointerEvent *event) { if (m_lastID != KoID("newId-hehe") && m_lastID != currentPaintOpPreset()->paintOp() && m_helper->isRunning()) { m_needEndContinuedStroke = true; endStroke(); } m_lastID = currentPaintOpPreset()->paintOp(); m_needEndContinuedStroke = false; // FIXME: workaround for the Duplicate Op tryPickByPaintOp(event, PickFgImage); requestUpdateOutline(event->point, event); NodePaintAbility paintability = nodePaintAbility(); if (!nodeEditable() || paintability != PAINT) { if(paintability == KisToolPaint::VECTOR){ KisCanvas2 * kiscanvas = static_cast(canvas()); QString message = i18n("The brush tool cannot paint on this layer. Please select a paint layer or mask."); kiscanvas->viewManager()->showFloatingMessage(message, koIcon("object-locked")); } event->ignore(); return; } setMode(KisTool::PAINT_MODE); KisCanvas2 *canvas2 = dynamic_cast(canvas()); if (canvas2) { canvas2->viewManager()->disableControls(); } initStroke(event); } void KisToolFreehand::continuePrimaryAction(KoPointerEvent *event) { CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); requestUpdateOutline(event->point, event); /** * Actual painting */ doStroke(event); } void KisToolFreehand::endPrimaryAction(KoPointerEvent *event) { Q_UNUSED(event); CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); m_lastID = currentPaintOpPreset()->paintOp(); endStroke(); if (m_assistant && static_cast(canvas())->paintingAssistantsDecoration()) { static_cast(canvas())->paintingAssistantsDecoration()->endStroke(); } notifyModified(); KisCanvas2 *canvas2 = dynamic_cast(canvas()); if (canvas2) { canvas2->viewManager()->enableControls(); } setMode(KisTool::HOVER_MODE); } bool KisToolFreehand::tryPickByPaintOp(KoPointerEvent *event, AlternateAction action) { if (action != PickFgNode && action != PickFgImage) return false; /** * FIXME: we need some better way to implement modifiers * for a paintop level. This method is used in DuplicateOp only! */ QPointF pos = adjustPosition(event->point, event->point); qreal perspective = 1.0; Q_FOREACH (const QPointer grid, static_cast(canvas())->viewManager()->resourceProvider()->perspectiveGrids()) { if (grid && grid->contains(pos)) { perspective = grid->distance(pos); break; } } if (!currentPaintOpPreset()) { return false; } bool paintOpIgnoredEvent = currentPaintOpPreset()->settings()-> mousePressEvent(KisPaintInformation(convertToPixelCoord(event->point), pressureToCurve(event->pressure()), event->xTilt(), event->yTilt(), event->rotation(), event->tangentialPressure(), perspective, 0, 0), event->modifiers(), currentNode()); return !paintOpIgnoredEvent; } void KisToolFreehand::activateAlternateAction(AlternateAction action) { if (action != ChangeSize) { KisToolPaint::activateAlternateAction(action); return; } useCursor(KisCursor::blankCursor()); setOutlineEnabled(true); } void KisToolFreehand::deactivateAlternateAction(AlternateAction action) { if (action != ChangeSize) { KisToolPaint::deactivateAlternateAction(action); return; } resetCursorStyle(); setOutlineEnabled(false); } void KisToolFreehand::beginAlternateAction(KoPointerEvent *event, AlternateAction action) { if (m_updateTimer.isActive()) m_updateTimer.stop(); if (currentPaintOpPreset()->settings()->needsContinuedStroke()) { m_needEndContinuedStroke = true; endStroke(); } if (tryPickByPaintOp(event, action)) { m_paintopBasedPickingInAction = true; return; } if (action != ChangeSize) { KisToolPaint::beginAlternateAction(event, action); return; } setMode(GESTURE_MODE); m_initialGestureDocPoint = event->point; m_initialGestureGlobalPoint = QCursor::pos(); m_lastDocumentPoint = event->point; m_lastPaintOpSize = currentPaintOpPreset()->settings()->paintOpSize(); } void KisToolFreehand::continueAlternateAction(KoPointerEvent *event, AlternateAction action) { if (tryPickByPaintOp(event, action) || m_paintopBasedPickingInAction) return; if (action != ChangeSize) { KisToolPaint::continueAlternateAction(event, action); return; } QPointF lastWidgetPosition = convertDocumentToWidget(m_lastDocumentPoint); QPointF actualWidgetPosition = convertDocumentToWidget(event->point); QPointF offset = actualWidgetPosition - lastWidgetPosition; KisCanvas2 *canvas2 = dynamic_cast(canvas()); QRect screenRect = QApplication::desktop()->screenGeometry(); qreal scaleX = 0; qreal scaleY = 0; canvas2->coordinatesConverter()->imageScale(&scaleX, &scaleY); const qreal maxBrushSize = KisConfig().readEntry("maximumBrushSize", 1000); const qreal effectiveMaxDragSize = 0.5 * screenRect.width(); const qreal effectiveMaxBrushSize = qMin(maxBrushSize, effectiveMaxDragSize / scaleX); const qreal scaleCoeff = effectiveMaxBrushSize / effectiveMaxDragSize; const qreal sizeDiff = scaleCoeff * offset.x() ; if (qAbs(sizeDiff) > 0.01) { KisPaintOpSettingsSP settings = currentPaintOpPreset()->settings(); const qreal newSize = qBound(0.01, m_lastPaintOpSize + sizeDiff, maxBrushSize); settings->setPaintOpSize(newSize); requestUpdateOutline(m_initialGestureDocPoint, 0); //m_brushResizeCompressor.start(newSize); m_lastDocumentPoint = event->point; m_lastPaintOpSize = newSize; } } void KisToolFreehand::endAlternateAction(KoPointerEvent *event, AlternateAction action) { if (currentPaintOpPreset()->settings()->needsContinuedStroke()) { m_needEndContinuedStroke = false; } if (tryPickByPaintOp(event, action) || m_paintopBasedPickingInAction) { m_paintopBasedPickingInAction = false; return; } if (action != ChangeSize) { KisToolPaint::endAlternateAction(event, action); return; } QCursor::setPos(m_initialGestureGlobalPoint); requestUpdateOutline(m_initialGestureDocPoint, 0); setMode(HOVER_MODE); } bool KisToolFreehand::wantsAutoScroll() const { return false; } void KisToolFreehand::setAssistant(bool assistant) { m_assistant = assistant; } void KisToolFreehand::setOnlyOneAssistantSnap(bool assistant) { m_only_one_assistant = assistant; } void KisToolFreehand::slotDoResizeBrush(qreal newSize) { KisPaintOpSettingsSP settings = currentPaintOpPreset()->settings(); settings->setPaintOpSize(newSize); requestUpdateOutline(m_initialGestureDocPoint, 0); } void KisToolFreehand::slotUpdateSystem() { m_helper->updateContinuedSystem(); } QPointF KisToolFreehand::adjustPosition(const QPointF& point, const QPointF& strokeBegin) { if (m_assistant && static_cast(canvas())->paintingAssistantsDecoration()) { static_cast(canvas())->paintingAssistantsDecoration()->setOnlyOneAssistantSnap(m_only_one_assistant); QPointF ap = static_cast(canvas())->paintingAssistantsDecoration()->adjustPosition(point, strokeBegin); return (1.0 - m_magnetism) * point + m_magnetism * ap; } return point; } qreal KisToolFreehand::calculatePerspective(const QPointF &documentPoint) { qreal perspective = 1.0; Q_FOREACH (const QPointer grid, static_cast(canvas())->viewManager()->resourceProvider()->perspectiveGrids()) { if (grid && grid->contains(documentPoint)) { perspective = grid->distance(documentPoint); break; } } return perspective; } void KisToolFreehand::explicitUpdateOutline() { requestUpdateOutline(m_outlineDocPoint, 0); } QPainterPath KisToolFreehand::getOutlinePath(const QPointF &documentPos, const KoPointerEvent *event, KisPaintOpSettings::OutlineMode outlineMode) { QPointF imagePos = convertToPixelCoord(documentPos); if (currentPaintOpPreset()) return m_helper->paintOpOutline(imagePos, event, currentPaintOpPreset()->settings(), outlineMode); else return QPainterPath(); } diff --git a/plugins/paintops/watercolor/kis_base_splats_plane.cpp b/plugins/paintops/watercolor/kis_base_splats_plane.cpp index 06fec254de..daef792c5b 100644 --- a/plugins/paintops/watercolor/kis_base_splats_plane.cpp +++ b/plugins/paintops/watercolor/kis_base_splats_plane.cpp @@ -1,104 +1,116 @@ /* This file is part of the KDE project * * Copyright (C) 2017 Grigory Tantsevov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_base_splats_plane.h" -KisBaseSplatsPlane::KisBaseSplatsPlane(bool useCaching, KisBaseSplatsPlane *lowLvlPlane) +KisBaseSplatsPlane::KisBaseSplatsPlane(bool useCaching, KisBaseSplatsPlane *lowLvlPlane, const KoColorSpace* colorSpace) : m_lowLvlPlane(lowLvlPlane), m_isDirty(true), m_useCaching(useCaching) { + if (useCaching) + m_cachedPD = new KisPaintDevice(colorSpace); } KisBaseSplatsPlane::~KisBaseSplatsPlane() { } void KisBaseSplatsPlane::add(KisSplat *splat) { m_splats << splat; - setDirty(splat->boundingRect().toAlignedRect()); + if (m_useCaching) { + KisPainter *painter = new KisPainter(m_cachedPD); + splat->doPaint(painter); + } +// setDirty(splat); } void KisBaseSplatsPlane::remove(KisSplat *splat) { m_splats.removeOne(splat); - setDirty(splat->boundingRect().toAlignedRect()); + if (m_useCaching) { + m_cachedPD->clear(splat->boundingRect().toAlignedRect()); + } +// setDirty(splat); } void KisBaseSplatsPlane::paint(KisPainter *gc, QRect rect) { - if (m_useCaching) { + if (rect.isNull()) + return; - if (!m_dirtyRect.isNull()) { - if (!m_cachedPD) { - m_cachedPD = new KisPaintDevice(gc->device()->colorSpace()); - } else { - m_cachedPD->clear(m_dirtyRect); - } + if (m_useCaching) { +// if (!m_dirtySplats.isEmpty()) { +// if (!m_cachedPD) { +// m_cachedPD = new KisPaintDevice(gc->device()->colorSpace()); +// } else { +// m_cachedPD->clear(rect); +// } - KisPainter *painter = new KisPainter(m_cachedPD); - Q_FOREACH (KisSplat *splat, m_splats) { - if (m_dirtyRect.contains(splat->boundingRect().toAlignedRect())) - splat->doPaint(painter); - } +// KisPainter *painter = new KisPainter(m_cachedPD); +// Q_FOREACH (KisSplat *splat, m_dirtySplats) { +// splat->doPaint(painter); +// } // m_isDirty = false; - } +// m_dirtySplats.clear(); +// } gc->bitBlt(rect.topLeft(), m_cachedPD, rect); } else { Q_FOREACH (KisSplat *splat, m_splats) { splat->doPaint(gc); } } } QRect KisBaseSplatsPlane::update(KisWetMap *wetMap) { -// QRect dirtyRect; + QRect dirtyRect; for (auto it = m_splats.begin(); it != m_splats.end();) { KisSplat *splat = *it; - setDirty( splat->boundingRect().toAlignedRect()); + + dirtyRect |= splat->boundingRect().toAlignedRect(); if (splat->update(wetMap) == KisSplat::Fixed) { m_lowLvlPlane->add(splat); { // move to protected call to parent class it = m_splats.erase(it); - setDirty(splat->boundingRect().toAlignedRect()); - + setDirty(splat); + dirtyRect |= splat->boundingRect().toAlignedRect(); } -// setDirty(splat->boundingRect().toAlignedRect()); } else { - setDirty(splat->boundingRect().toAlignedRect()); + setDirty(splat); + dirtyRect |= splat->boundingRect().toAlignedRect(); ++it; } } - return m_dirtyRect; + return dirtyRect; } -void KisBaseSplatsPlane::setDirty(const QRect &rc) +void KisBaseSplatsPlane::setDirty(KisSplat *splat) { - m_dirtyRect |= rc; -// m_isDirty = true; + m_dirtySplats << splat; + m_isDirty = true; } diff --git a/plugins/paintops/watercolor/kis_base_splats_plane.h b/plugins/paintops/watercolor/kis_base_splats_plane.h index af7d87ddeb..dba2e225e0 100644 --- a/plugins/paintops/watercolor/kis_base_splats_plane.h +++ b/plugins/paintops/watercolor/kis_base_splats_plane.h @@ -1,90 +1,89 @@ /* This file is part of the KDE project * * Copyright (C) 2017 Grigory Tantsevov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KIS_ABSTRACT_SPLATS_PLANE_H #define KIS_ABSTRACT_SPLATS_PLANE_H #include "kis_splat.h" #include "kis_wetmap.h" #include "kis_paint_device.h" #include /** * Base class for the splats' containers in watercolor * brush. It needs for separating splats in different states. * This plane storages list of splats and paint it on * paint device. So for this it can: * * 1) add() splats to list and paint device; * * 2) remove() splats from list and paint device; * * 3) paint() paint device with given painter */ class KisBaseSplatsPlane { public: KisBaseSplatsPlane(bool useCaching, - KisBaseSplatsPlane *lowLvlPlane = 0); + KisBaseSplatsPlane *lowLvlPlane = 0, + const KoColorSpace *colorSpace = 0); virtual ~KisBaseSplatsPlane(); /** * @brief add splat to list of splats and paint device * @param splat - new splat */ void add(KisSplat *splat); /** * @brief revome splat from plane * @param splat - removing splat */ void remove(KisSplat *splat); /** * @brief paint plane on given painter in given rect * @param gc - painter * @param rect - zone of paint */ void paint(KisPainter *gc, QRect rect); /** * @brief update plane * @param wetMap - base for updating */ virtual QRect update(KisWetMap *wetMap); protected: - - QList::iterator remove(QList::iterator it); QList m_splats; KisBaseSplatsPlane *m_lowLvlPlane; protected: - void setDirty(const QRect &rc); - QRect m_dirtyRect; + void setDirty(KisSplat *splat); + QList m_dirtySplats; private: KisPaintDeviceSP m_cachedPD; bool m_isDirty; bool m_useCaching; }; #endif // KIS_ABSTRACT_SPLATS_PLANE_H diff --git a/plugins/paintops/watercolor/kis_fixed_splats_plane.cpp b/plugins/paintops/watercolor/kis_fixed_splats_plane.cpp index ca2e73f088..8bb5853022 100644 --- a/plugins/paintops/watercolor/kis_fixed_splats_plane.cpp +++ b/plugins/paintops/watercolor/kis_fixed_splats_plane.cpp @@ -1,48 +1,59 @@ /* * Copyright (c) 2017 Dmitry Kazakov * * 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; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_fixed_splats_plane.h" -KisFixedSplatsPlane::KisFixedSplatsPlane(KisBaseSplatsPlane *driedPlane) - : KisBaseSplatsPlane(true, driedPlane) +KisFixedSplatsPlane::KisFixedSplatsPlane(KisBaseSplatsPlane *driedPlane, const KoColorSpace *colorSpace) + : KisBaseSplatsPlane(true, driedPlane, colorSpace), + m_splatsTree(4, 2) { } QRect KisFixedSplatsPlane::update(KisWetMap *wetMap) { -// QRect dirtyRect; - for (auto it = m_splats.begin(); it != m_splats.end();) { KisSplat *splat = *it; if (splat->update(wetMap) == KisSplat::Dried) { m_lowLvlPlane->add(splat); { // move to protected call to parent class it = m_splats.erase(it); - setDirty(splat->boundingRect().toAlignedRect()); + setDirty(splat); } -// dirtyRect |= splat->boundingRect().toAlignedRect(); } else { ++it; } } - return m_dirtyRect; + return QRect(); } +void KisFixedSplatsPlane::rewet(KisWetMap *wetMap, QPointF pos, qreal rad, KisBaseSplatsPlane *flowingPlane) +{ + QRectF rect(pos.x() - rad, pos.y() - rad, + rad * 2, rad * 2); + + QList reweted = m_splatsTree.intersects(rect);\ + Q_FOREACH(KisSplat *splat, reweted) { + splat->rewet(wetMap, pos, rad); + remove(splat); + m_splatsTree.remove(splat); + flowingPlane->add(splat); + } +} diff --git a/plugins/paintops/watercolor/kis_fixed_splats_plane.h b/plugins/paintops/watercolor/kis_fixed_splats_plane.h index 7a4f8dee64..8f6fbb8653 100644 --- a/plugins/paintops/watercolor/kis_fixed_splats_plane.h +++ b/plugins/paintops/watercolor/kis_fixed_splats_plane.h @@ -1,35 +1,39 @@ /* * Copyright (c) 2017 Dmitry Kazakov * * 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; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KISFIXEDSPLATSPLANE_H #define KISFIXEDSPLATSPLANE_H #include "kis_base_splats_plane.h" +#include "KoRTree.h" class KisFixedSplatsPlane : public KisBaseSplatsPlane { public: - KisFixedSplatsPlane(KisBaseSplatsPlane *driedPlane); + KisFixedSplatsPlane(KisBaseSplatsPlane *driedPlane, const KoColorSpace *colorSpace); QRect update(KisWetMap *wetMap) override; + void rewet(KisWetMap *wetMap, QPointF pos, qreal rad, KisBaseSplatsPlane *flowingPlane); + private: KisBaseSplatsPlane *m_flowingPlane; + KoRTree m_splatsTree; }; #endif // KISFIXEDSPLATSPLANE_H diff --git a/plugins/paintops/watercolor/kis_splat.cpp b/plugins/paintops/watercolor/kis_splat.cpp index cbf7642aa4..4f83502073 100644 --- a/plugins/paintops/watercolor/kis_splat.cpp +++ b/plugins/paintops/watercolor/kis_splat.cpp @@ -1,245 +1,240 @@ /* This file is part of the KDE project * * Copyright (C) 2017 Grigory Tantsevov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_splat.h" #include "kis_random_generator.h" #include #include #include "kis_sequential_iterator.h" #define START_OPACITY 100 #define STANDART_LIFETIME 60 double get_random(qreal min, qreal max) { return (qreal)rand() / RAND_MAX*(max - min) + min; } KisSplat::KisSplat(QPointF offset, int width, const KoColor &color) : m_life(STANDART_LIFETIME), m_roughness(1.f), m_flow(1.f), m_motionBias(QPointF(0.f, 0.f)), m_state(Flowing) { m_initColor.fromKoColor(color); m_fix = 8*STANDART_LIFETIME; int r = width / 2; int n = 128; qreal dt = 2.f * M_PI / n; QPointF p; for (int i = 0; i < n; i++) { p.setX(cos(i * dt)); p.setY(sin(i * dt)); m_vertices.push_back(QPointF(static_cast (r * p.x()) + offset.x(), static_cast (r * p.y()) + offset.y())); m_velocities.push_back(QPointF(2.f * p.x(), 2.f * p.y())); } m_initSize = CalcSize(); m_startTime = QTime::currentTime(); } KisSplat::KisSplat(QPointF offset, QPointF velocityBias, int width, int life, qreal roughness, qreal flow, qreal radialSpeed, const KoColor &color) : m_life(life), m_roughness(roughness), m_flow(flow), m_motionBias(velocityBias), m_state(Flowing) { m_initColor.fromKoColor(color); m_fix = 8*STANDART_LIFETIME; int r = width / 2; int n = 128; qreal dt = 2.f * M_PI / n; QPointF p; for (int i = 0; i < n; i++) { p.setX(cos(i * dt)); p.setY(sin(i * dt)); m_vertices.push_back(QPointF(static_cast (r * p.x()) + offset.x(), static_cast (r * p.y()) + offset.y())); m_velocities.push_back(QPointF(radialSpeed * p.x(), radialSpeed * p.y())); } m_initSize = CalcSize(); m_startTime = QTime::currentTime(); } qreal KisSplat::CalcSize() { if (m_vertices.length() < 3) return 0.f; QPointF v0 = m_vertices[0]; qreal v0x = v0.x(); qreal v0y = v0.y(); QPointF e0 = m_vertices[1] - v0; qreal e0x = e0.x(); qreal e0y = e0.y(); qreal s = 0.f; int length = m_vertices.length(); for (int i = 2; i < length; i++) { QPointF v2 = m_vertices[i]; qreal e1x = v2.x() - v0x; qreal e1y = v2.y() - v0y; s += e0x * e1y - e0y * e1x; e0x = e1x; e0y = e1y; } return s >= 0 ? s : -s; } void KisSplat::clearOldPath(KisPaintDeviceSP dev) { -// QRect rc = m_oldPath.boundingRect().toRect(); -// dev->clear(rc); QRect rect = m_oldPath.boundingRect().toRect(); KisSequentialIterator it(dev, rect); do { QPoint place(it.x(), it.y()); if (m_oldPath.contains(place)) { qint16 *mydata = reinterpret_cast(it.rawData()); mydata[0] = 0; } } while (it.nextPixel()); } void KisSplat::doPaint(KisPainter *painter) { qreal multiply = m_initSize / CalcSize(); if (multiply < 0.f || multiply > 1.f) multiply = 1; qint8 oldOpacity = painter->opacity(); KisPainter::FillStyle oldFillStyle = painter->fillStyle(); KoColor oldColor = painter->paintColor(); -// if (m_state == Flowing) -// clearOldPath(painter->device()); - painter->setOpacity(START_OPACITY * multiply); painter->setFillStyle(KisPainter::FillStyleForegroundColor); painter->setPaintColor(m_initColor); painter->fillPainterPath(this->shape()); painter->setOpacity(oldOpacity); painter->setFillStyle(oldFillStyle); painter->setPaintColor(oldColor); } QPainterPath KisSplat::shape() const { QPainterPath path; int len = m_vertices.length(); path = *(new QPainterPath(m_vertices[0])); for (int i = 0; i < len-2; i+=2) { path.quadTo(m_vertices[i+1], m_vertices[i+2]); } path.quadTo(m_vertices[len-1], m_vertices[0]); return path; } QRectF KisSplat::boundingRect() const { return m_vertices.boundingRect(); } int KisSplat::update(KisWetMap *wetMap) { if (m_life <= 0) { if (m_fix <= 0) { m_state = Dried; return KisSplat::Dried; } else { m_state = Fixed; m_fix--; return KisSplat::Fixed; } } m_life--; m_oldPath = shape(); QVector newVertices; for (int i = 0; i < m_vertices.length(); i++) { QPointF x = m_vertices[i]; QPointF v = m_velocities[i]; QPointF d = (1.f - alpha) * m_motionBias + alpha / get_random(1.f, 1.f + m_roughness) * v; QPointF x1 = x + m_flow * d; + QPointF(get_random(-m_roughness, m_roughness), get_random(-m_roughness, m_roughness)); newVertices.push_back(x1); } QVector wetPoints = wetMap->getWater(newVertices); for (int i = 0; i < wetPoints.size(); i++) { if (wetPoints.at(i) > 0) m_vertices[i] = newVertices.at(i); } if (!m_life) { for (int i = 0; i < m_vertices.length(); i++) { m_velocities[i] = QPointF(0, 0); } } m_state = Flowing; return KisSplat::Flowing; } int KisSplat::rewet(KisWetMap *wetMap, QPointF pos, qreal radius) { QVector vertNum; QVector vertUpdate; for (int i = 0; i < m_vertices.size(); i++) { QVector2D vec(m_vertices.at(i) - pos); if (vec.length() <= radius) { vertNum.push_back(i); vertUpdate.push_back(m_vertices[i]); } } if (vertNum.size() > 0) { QVector newSpeed = wetMap->getSpeed(vertUpdate); for (int i = 0; i < vertNum.size(); i++) { int num = vertNum.at(i); m_velocities[num] = newSpeed.at(i); } m_life = STANDART_LIFETIME; m_fix = 8*STANDART_LIFETIME; m_state = Flowing; return KisSplat::Flowing; } else { m_state = Fixed; return KisSplat::Fixed; } } diff --git a/plugins/paintops/watercolor/kis_watercolor_paintop.cpp b/plugins/paintops/watercolor/kis_watercolor_paintop.cpp index c8b9a1bb77..5fd1674334 100644 --- a/plugins/paintops/watercolor/kis_watercolor_paintop.cpp +++ b/plugins/paintops/watercolor/kis_watercolor_paintop.cpp @@ -1,121 +1,123 @@ /* This file is part of the KDE project * * Copyright (C) 2017 Grigory Tantsevov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_watercolor_paintop.h" #include "kis_watercolor_base_items.h" #include #include #include "KoCompositeOps.h" #include "kis_splat_generator_strategy.h" KisWatercolorPaintOp::KisWatercolorPaintOp(const KisPaintOpSettingsSP settings, KisPainter *painter, KisNodeSP node, KisImageSP image) : KisPaintOp(painter), - m_driedPlane(true), - m_fixedPlane(&m_driedPlane), + m_driedPlane(true, 0, painter->device()->colorSpace()), + m_fixedPlane(&m_driedPlane, painter->device()->colorSpace()), m_flowingPlane(false, &m_fixedPlane) { Q_UNUSED(image); Q_UNUSED(node); m_wetMap = new KisWetMap(); m_watercolorOption.readOptionSetting(settings); - m_lastTime = 0; - m_timer.start(); m_oldPD = new KisPaintDevice(*painter->device().constData()); } KisWatercolorPaintOp::~KisWatercolorPaintOp() { -// KisWatercolorBaseItems::instance()->drySystem(); } KisSpacingInformation KisWatercolorPaintOp::paintAt(const KisPaintInformation &info) { - QRect dirtyRect; - KisSplatGeneratorStrategy *strategy; switch (m_watercolorOption.type) { case 0: strategy = new KisSimpleBrushGenerator(); break; case 1: strategy = new KisWetOnDryGenerator(); break; case 2: strategy = new KisWetOnWetGenerator(); break; case 3: strategy = new KisBlobbyGenerator(); break; case 4: strategy = new KisCrunchyGenerator(); break; default: strategy = new KisSimpleBrushGenerator(); break; } QList newSplats = strategy->generate(m_wetMap, info.pos(), m_watercolorOption.radius, painter()->paintColor()); foreach (KisSplat *splat, newSplats) { m_flowingPlane.add(splat); splat->doPaint(painter()); } + m_fixedPlane.rewet(m_wetMap, info.pos(), m_watercolorOption.radius, &m_flowingPlane); + return updateSpacingImpl(info); } void KisWatercolorPaintOp::updateSystem() { - // Painting new stroke - qint16 time = m_timer.elapsed(); - qint16 timeGone = time - m_lastTime; QRect dirtyRect; - // Updating system -// for (int i = 0; i < timeGone / 33; i++) { - dirtyRect |= m_fixedPlane.update(m_wetMap); - dirtyRect |= m_flowingPlane.update(m_wetMap); - - m_wetMap->update(); -// } - - m_lastTime = time - time % 33; - source()->clear(); - painter()->bitBlt(m_oldPD->exactBounds().topLeft(), - m_oldPD, m_oldPD->exactBounds()); + + QElapsedTimer timer; + timer.start(); + + dirtyRect = m_fixedPlane.update(m_wetMap); + dirtyRect |= m_flowingPlane.update(m_wetMap); + + m_wetMap->update(); + + qDebug() << "\n" << ppVar(timer.elapsed()); + qDebug() << ppVar(dirtyRect); + + source()->clear(dirtyRect); + painter()->bitBlt(dirtyRect.topLeft(), + m_oldPD, dirtyRect); + + qDebug() << ppVar(timer.elapsed()); m_driedPlane.paint(painter(), dirtyRect); + qDebug() << ppVar(timer.elapsed()); m_fixedPlane.paint(painter(), dirtyRect); + qDebug() << ppVar(timer.elapsed()); m_flowingPlane.paint(painter(), dirtyRect); + qDebug() << ppVar(timer.elapsed()) << "\n"; } KisSpacingInformation KisWatercolorPaintOp::updateSpacingImpl(const KisPaintInformation &info) const { Q_UNUSED(info); return KisSpacingInformation(m_watercolorOption.radius / 5); } diff --git a/plugins/paintops/watercolor/kis_watercolor_paintop.h b/plugins/paintops/watercolor/kis_watercolor_paintop.h index a6a2e4f715..43ebe34df1 100644 --- a/plugins/paintops/watercolor/kis_watercolor_paintop.h +++ b/plugins/paintops/watercolor/kis_watercolor_paintop.h @@ -1,77 +1,70 @@ /* This file is part of the KDE project * * Copyright (C) 2017 Grigory Tantsevov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KIS_EXPERIMENT_PAINTOP_H_ #define KIS_EXPERIMENT_PAINTOP_H_ #include #include #include #include "kis_paint_information.h" #include "kis_watercolorop_option.h" #include #include "kis_wetmap.h" #include "kis_splat.h" #include "KoRTree.h" #include "kis_base_splats_plane.h" #include "kis_fixed_splats_plane.h" class KisPainter; class KisWatercolorPaintOp : public KisPaintOp { public: KisWatercolorPaintOp(const KisPaintOpSettingsSP settings, KisPainter *painter, KisNodeSP node, KisImageSP image); ~KisWatercolorPaintOp(); KisSpacingInformation paintAt(const KisPaintInformation& info) override; void updateSystem() override; protected: KisSpacingInformation updateSpacingImpl(const KisPaintInformation &info) const override; private: WatercolorOption m_watercolorOption; - QElapsedTimer m_timer; - qint16 m_lastTime; - KisWetMap *m_wetMap; -// QList m_flowing, -// m_fixed, -// m_dried; -// KoRTree m_fixedTree; KisBaseSplatsPlane m_driedPlane; KisFixedSplatsPlane m_fixedPlane; KisBaseSplatsPlane m_flowingPlane; KisPaintDeviceSP m_oldPD; }; #endif // KIS_EXPERIMENT_PAINTOP_H_ diff --git a/plugins/paintops/watercolor/kis_wetmap.cpp b/plugins/paintops/watercolor/kis_wetmap.cpp index 6d0c580c40..0ee7e13af4 100644 --- a/plugins/paintops/watercolor/kis_wetmap.cpp +++ b/plugins/paintops/watercolor/kis_wetmap.cpp @@ -1,143 +1,143 @@ /* This file is part of the KDE project * * Copyright (C) 2017 Grigory Tantsevov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_wetmap.h" #include "KoColorSpaceRegistry.h" #include "kis_sequential_iterator.h" #include #include #include #include "kis_pixel_selection.h" using namespace Arithmetic; KisWetMap::KisWetMap() { m_wetMap = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb16()); } // Adding water in circle with the given center and radius void KisWetMap::addWater(QPoint pos, qreal radius) { QRect rect(pos - QPointF(radius, radius).toPoint(), pos + QPointF(radius, radius).toPoint()); KisSequentialIterator it(m_wetMap, rect); do { QPoint place(it.x(), it.y()); QVector2D vec(place - pos); if ((vec.x() * vec.x() + vec.y() * vec.y()) <= (radius * radius)) { qint16 *mydata = reinterpret_cast(it.rawData()); vec.normalize(); mydata[0] = unitValue(); // Water value mydata[3] = unitValue(); // Alpha ch mydata[1] = unitValue() * vec.x(); // X of speed mydata[2] = unitValue() * vec.y(); // Y of speed } } while (it.nextPixel()); } // Updating wetmap for simulating drying process void KisWetMap::update() { if (m_wetMap->exactBounds().size() != QSize(0, 0)) { KisSequentialIterator it(m_wetMap, m_wetMap->exactBounds()); do { qint16 *mydata = reinterpret_cast(it.rawData()); if (mydata[0] > 127) { // If there some water mydata[0] -= 128; // The "evaporated" part of the water } else { // If there is no water mydata[1] = zeroValue(); // Remove speed vector mydata[2] = zeroValue(); mydata[3] = zeroValue(); mydata[0] = zeroValue(); } if (m_wetMap->exactBounds().size() == QSize(0, 0)) break; } while (it.nextPixel()); } } // Returns water count in points QVector KisWetMap::getWater(QVector points) { QVector ret; KisRandomConstAccessorSP accesser = m_wetMap->createRandomConstAccessorNG(points.first().toPoint().x(), points.first().toPoint().y()); points.pop_front(); const qint16 *data = reinterpret_cast(accesser->rawDataConst()); ret.push_back(data[0]); foreach (QPointF point, points) { accesser->moveTo(point.toPoint().x(), point.toPoint().y()); data = reinterpret_cast(accesser->rawDataConst()); ret.push_back(data[0]); } return ret; } -// TODO: KisRandomAccessorSP QVector KisWetMap::getSpeed(QVector points) { QVector ret; - KisRandomConstAccessorSP accesser = m_wetMap->createRandomConstAccessorNG(points.first().toPoint().x(), - points.first().toPoint().y()); + KisRandomConstAccessorSP accesser = + m_wetMap->createRandomConstAccessorNG(points.first().toPoint().x(), + points.first().toPoint().y()); points.pop_front(); const qint16 *data = reinterpret_cast(accesser->rawDataConst()); ret.push_back(QPoint(data[1], data[2])); foreach (QPointF point, points) { accesser->moveTo(point.toPoint().x(), point.toPoint().y()); data = reinterpret_cast(accesser->rawDataConst()); ret.push_back(QPoint(data[1], data[2])); } return ret; } // Returns paint device for visualizing wet map KisPaintDeviceSP KisWetMap::getPaintDevice() { KisSequentialConstIterator it(m_wetMap, m_wetMap->exactBounds()); KisPaintDeviceSP ret = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); KisSequentialIterator itRet(ret, m_wetMap->exactBounds()); do { const qint16 *mydata = reinterpret_cast(it.rawDataConst()); quint8 *seldata = itRet.rawData(); seldata[3] = scale(mydata[0]); seldata[1] = zeroValue(); seldata[2] = zeroValue(); seldata[0] = unitValue(); itRet.nextPixel(); } while (it.nextPixel()); return ret; } void KisWetMap::dry() { m_wetMap->clear(); }