...
 
Commits (4)
......@@ -22,7 +22,7 @@ ExternalProject_Add( ext_fftw3
PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/patch_mingw.patch
INSTALL_DIR ${PREFIX_ext_fftw3}
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${PREFIX_ext_fftw3} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DENABLE_THREADS=ON -ENABLE_SSE=ON -ENABLE_SSE2=ON -DBUILD_TESTS=OFF
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${PREFIX_ext_fftw3} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DWITH_COMBINED_THREADS=ON -ENABLE_SSE=ON -ENABLE_SSE2=ON -DBUILD_TESTS=OFF
UPDATE_COMMAND ""
)
......
......@@ -114,6 +114,7 @@ xsi:schemaLocation="http://www.kde.org/standards/kxmlgui/1.0 http://www.kde.org
<text>&amp;Snap To</text>
<Action name="view_snap_to_guides"/>
<Action name="view_snap_to_grid"/>
<Action name="view_snap_to_pixel"/>
<Action name="view_snap_orthogonal" />
<Action name="view_snap_node" />
<Action name="view_snap_extension" />
......
......@@ -866,6 +866,17 @@
<statusTip></statusTip>
</Action>
<Action name="view_snap_to_pixel">
<icon></icon>
<text>Snap Pixel</text>
<whatsThis></whatsThis>
<toolTip>Snap Pixel</toolTip>
<iconText>Snap Pixel</iconText>
<activationFlags>1000</activationFlags>
<shortcut></shortcut>
<isCheckable>true</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="view_snap_intersection">
<icon></icon>
<text>Snap Intersection</text>
......
......@@ -69,7 +69,8 @@ public:
GuideLineSnapping = 0x40,
DocumentBoundsSnapping = 0x80,
DocumentCenterSnapping = 0x100,
CustomSnapping = 0x200
CustomSnapping = 0x200,
PixelSnapping = 0x400
};
Q_DECLARE_FLAGS(Strategies, Strategy)
......
......@@ -37,6 +37,13 @@ public:
* be asynchronous, so you should call image->waitForDone() after that.
*/
virtual void forceUpdateTimedNode() = 0;
/**
* @return true if forceUpdateTimedNode() is going to
* produce any real updates, that is the node has any
* updates still pending
*/
virtual bool hasPendingTimedUpdates() const = 0;
};
#endif // KISDELAYEDUPDATENODEINTERFACE_H
......@@ -1514,6 +1514,17 @@ namespace KisLayerUtils {
});
}
bool hasDelayedNodeWithUpdates(KisNodeSP root)
{
return recursiveFindNode(root,
[] (KisNodeSP node) {
KisDelayedUpdateNodeInterface *delayedUpdate =
dynamic_cast<KisDelayedUpdateNodeInterface*>(node.data());
return delayedUpdate ? delayedUpdate->hasPendingTimedUpdates() : false;
});
}
KisImageSP findImageByHierarchy(KisNodeSP node)
{
while (node) {
......
......@@ -51,6 +51,7 @@ namespace KisLayerUtils
*/
KRITAIMAGE_EXPORT bool checkIsCloneOf(KisNodeSP node, const KisNodeList &nodes);
KRITAIMAGE_EXPORT void forceAllDelayedNodesUpdate(KisNodeSP root);
KRITAIMAGE_EXPORT bool hasDelayedNodeWithUpdates(KisNodeSP root);
KRITAIMAGE_EXPORT KisNodeList sortAndFilterMergableInternalNodes(KisNodeList nodes, bool allowMasks = false);
......
......@@ -425,7 +425,7 @@ void KisTransformMask::setY(qint32 y)
void KisTransformMask::forceUpdateTimedNode()
{
if (m_d->updateSignalCompressor.isActive()) {
if (hasPendingTimedUpdates()) {
KIS_SAFE_ASSERT_RECOVER_NOOP(!m_d->staticCacheValid);
m_d->updateSignalCompressor.stop();
......@@ -433,6 +433,11 @@ void KisTransformMask::forceUpdateTimedNode()
}
}
bool KisTransformMask::hasPendingTimedUpdates() const
{
return m_d->updateSignalCompressor.isActive();
}
void KisTransformMask::threadSafeForceStaticImageUpdate()
{
emit sigInternalForceStaticImageUpdate();
......
......@@ -75,6 +75,7 @@ public:
void setY(qint32 y) override;
void forceUpdateTimedNode() override;
bool hasPendingTimedUpdates() const override;
void threadSafeForceStaticImageUpdate();
......
......@@ -43,6 +43,7 @@ set(kritaui_LIB_SRCS
canvas/kis_snap_config.cpp
canvas/kis_snap_line_strategy.cpp
canvas/KisSnapPointStrategy.cpp
canvas/KisSnapPixelStrategy.cpp
canvas/KisMirrorAxisConfig.cpp
dialogs/kis_about_application.cpp
dialogs/kis_dlg_adj_layer_props.cc
......
......@@ -803,6 +803,27 @@ bool KisDocument::initiateSavingInBackground(const QString actionName,
return false;
}
auto waitForImage = [] (KisImageSP image) {
KisMainWindow *window = KisPart::instance()->currentMainwindow();
if (window) {
if (window->viewManager()) {
window->viewManager()->blockUntilOperationsFinishedForced(image);
}
}
};
{
KisNodeSP newRoot = clonedDocument->image()->root();
KIS_SAFE_ASSERT_RECOVER(!KisLayerUtils::hasDelayedNodeWithUpdates(newRoot)) {
KisLayerUtils::forceAllDelayedNodesUpdate(newRoot);
waitForImage(clonedDocument->image());
}
}
KIS_SAFE_ASSERT_RECOVER(clonedDocument->image()->isIdle()) {
waitForImage(clonedDocument->image());
}
KIS_ASSERT_RECOVER_RETURN_VALUE(!d->backgroundSaveDocument, false);
KIS_ASSERT_RECOVER_RETURN_VALUE(!d->backgroundSaveJob.isValid(), false);
d->backgroundSaveDocument.reset(clonedDocument.take());
......
/*
* Copyright (c) 2019 Kuntal Majumder <hellozee@disroot.org>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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 "KisSnapPixelStrategy.h"
#include <QPainterPath>
#include "kis_global.h"
#include "kis_canvas2.h"
#include "KoSnapProxy.h"
KisSnapPixelStrategy::KisSnapPixelStrategy(KoSnapGuide::Strategy type):
KoSnapStrategy(type)
{
}
KisSnapPixelStrategy::~KisSnapPixelStrategy()
{
}
bool KisSnapPixelStrategy::snap(const QPointF &mousePosition, KoSnapProxy *proxy, qreal maxSnapDistance)
{
Q_ASSERT(std::isfinite(maxSnapDistance));
KisCanvas2 *canvas2 = dynamic_cast<KisCanvas2*>(proxy->canvas());
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(canvas2, false);
QPointF spacing = canvas2->coordinatesConverter()->imageToDocument(QPointF(1,1));
// These are not my words but of Jan Hambrecht <jaham@gmx.net>
// And I have to trust them cause this works, :3
// The 1e-10 here is a workaround for some weird division problem.
// 360.00062366 / 2.83465058 gives 127 'exactly' when shown as a qreal,
// but when casting into an int, we get 126. In fact it's 127 - 5.64e-15 !
int col = static_cast<int>((mousePosition.x()) / spacing.x() + 1e-10);
int row = static_cast<int>((mousePosition.y()) / spacing.y() + 1e-10);
QPointF snappedPoint = QPointF(col * spacing.y(), row * spacing.x());
setSnappedPosition(snappedPoint);
return true;
}
QPainterPath KisSnapPixelStrategy::decoration(const KoViewConverter &converter) const
{
QSizeF unzoomedSize = converter.viewToDocument(QSizeF(5, 5));
QPainterPath decoration;
decoration.moveTo(snappedPosition() - QPointF(unzoomedSize.width(), 0));
decoration.lineTo(snappedPosition() + QPointF(unzoomedSize.width(), 0));
decoration.moveTo(snappedPosition() - QPointF(0, unzoomedSize.height()));
decoration.lineTo(snappedPosition() + QPointF(0, unzoomedSize.height()));
return decoration;
}
/*
* Copyright (c) 2019 Kuntal Majumder <hellozee@disroot.org>
*
* 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 __KIS_SNAP_PIXEL_STRATEGY_H
#define __KIS_SNAP_PIXEL_STRATEGY_H
#include <QScopedPointer>
#include "KoSnapStrategy.h"
class KisSnapPixelStrategy : public KoSnapStrategy
{
public:
KisSnapPixelStrategy(KoSnapGuide::Strategy type = KoSnapGuide::PixelSnapping);
~KisSnapPixelStrategy() override;
bool snap(const QPointF &mousePosition, KoSnapProxy * proxy, qreal maxSnapDistance) override;
QPainterPath decoration(const KoViewConverter &converter) const override;
};
#endif /* __KIS_SNAP_PIXEL_STRATEGY_H */
......@@ -35,6 +35,7 @@
#include "kis_algebra_2d.h"
#include <KoSnapGuide.h>
#include "kis_snap_line_strategy.h"
#include "KisSnapPixelStrategy.h"
#include "kis_change_guides_command.h"
#include "kis_snap_config.h"
#include "kis_canvas2.h"
......@@ -211,6 +212,7 @@ void KisGuidesManager::syncActionsStatus()
m_d->syncAction("view_snap_bounding_box", m_d->snapConfig.boundingBox());
m_d->syncAction("view_snap_image_bounds", m_d->snapConfig.imageBounds());
m_d->syncAction("view_snap_image_center", m_d->snapConfig.imageCenter());
m_d->syncAction("view_snap_to_pixel",m_d->snapConfig.toPixel());
}
void KisGuidesManager::Private::updateSnappingStatus(const KisGuidesConfig &value)
......@@ -219,6 +221,7 @@ void KisGuidesManager::Private::updateSnappingStatus(const KisGuidesConfig &valu
KoSnapGuide *snapGuide = view->canvasBase()->snapGuide();
KisSnapLineStrategy *guidesSnap = 0;
KisSnapPixelStrategy *pixelSnap = new KisSnapPixelStrategy();
if (value.snapToGuides()) {
guidesSnap = new KisSnapLineStrategy(KoSnapGuide::GuideLineSnapping);
......@@ -229,6 +232,9 @@ void KisGuidesManager::Private::updateSnappingStatus(const KisGuidesConfig &valu
snapGuide->overrideSnapStrategy(KoSnapGuide::GuideLineSnapping, guidesSnap);
snapGuide->enableSnapStrategy(KoSnapGuide::GuideLineSnapping, guidesSnap);
snapGuide->overrideSnapStrategy(KoSnapGuide::PixelSnapping, pixelSnap);
snapGuide->enableSnapStrategy(KoSnapGuide::PixelSnapping, pixelSnap);
snapGuide->enableSnapStrategy(KoSnapGuide::OrthogonalSnapping, snapConfig.orthogonal());
snapGuide->enableSnapStrategy(KoSnapGuide::NodeSnapping, snapConfig.node());
snapGuide->enableSnapStrategy(KoSnapGuide::ExtensionSnapping, snapConfig.extension());
......@@ -236,6 +242,7 @@ void KisGuidesManager::Private::updateSnappingStatus(const KisGuidesConfig &valu
snapGuide->enableSnapStrategy(KoSnapGuide::BoundingBoxSnapping, snapConfig.boundingBox());
snapGuide->enableSnapStrategy(KoSnapGuide::DocumentBoundsSnapping, snapConfig.imageBounds());
snapGuide->enableSnapStrategy(KoSnapGuide::DocumentCenterSnapping, snapConfig.imageCenter());
snapGuide->enableSnapStrategy(KoSnapGuide::PixelSnapping, snapConfig.toPixel());
snapConfig.saveStaticData();
}
......@@ -332,6 +339,9 @@ void KisGuidesManager::setup(KisActionManager *actionManager)
action = actionManager->createAction("view_snap_image_center");
connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapImageCenter(bool)));
action = actionManager->createAction("view_snap_to_pixel");
connect(action, SIGNAL(toggled(bool)), this, SLOT(setSnapToPixel(bool)));
m_d->updateSnappingStatus(m_d->guidesConfig);
syncActionsStatus();
}
......@@ -746,6 +756,7 @@ void KisGuidesManager::slotShowSnapOptions()
menu.addSection(i18n("Snap to:"));
menu.addAction(m_d->createShortenedAction(i18n("Grid"), "view_snap_to_grid", &menu));
menu.addAction(m_d->createShortenedAction(i18n("Guides"), "view_snap_to_guides", &menu));
menu.addAction(m_d->createShortenedAction(i18n("Pixel"), "view_snap_to_pixel", &menu));
menu.addAction(m_d->createShortenedAction(i18n("Orthogonal"), "view_snap_orthogonal", &menu));
menu.addAction(m_d->createShortenedAction(i18n("Node"), "view_snap_node", &menu));
......@@ -800,3 +811,9 @@ void KisGuidesManager::setSnapImageCenter(bool value)
m_d->snapConfig.setImageCenter(value);
m_d->updateSnappingStatus(m_d->guidesConfig);
}
void KisGuidesManager::setSnapToPixel(bool value)
{
m_d->snapConfig.setToPixel(value);
m_d->updateSnappingStatus(m_d->guidesConfig);
}
......@@ -74,6 +74,7 @@ public Q_SLOTS:
void setSnapBoundingBox(bool value);
void setSnapImageBounds(bool value);
void setSnapImageCenter(bool value);
void setSnapToPixel(bool value);
void slotUploadConfigToDocument();
......
......@@ -27,7 +27,8 @@ KisSnapConfig::KisSnapConfig(bool loadValues)
m_intersection(false),
m_boundingBox(false),
m_imageBounds(true),
m_imageCenter(true)
m_imageCenter(true),
m_toPixel(false)
{
if (loadValues) {
loadStaticData();
......
......@@ -75,6 +75,13 @@ public:
m_imageCenter = value;
}
bool toPixel() const {
return m_toPixel;
}
void setToPixel(bool value) {
m_toPixel = value;
}
void saveStaticData() const;
void loadStaticData();
......@@ -86,6 +93,7 @@ private:
bool m_boundingBox;
bool m_imageBounds;
bool m_imageCenter;
bool m_toPixel;
};
#endif /* __KIS_SNAP_CONFIG_H */
......@@ -115,6 +115,11 @@ public:
m_layer->signalUpdate(m_layer->boundingImageRect());
}
bool hasPendingUpdates() const override
{
return false;
}
void rerenderAfterBeingInvisible() override {}
void resetCache() override {}
void setImage(KisImageWSP /*image*/) override {}
......
......@@ -175,12 +175,16 @@ KisShapeLayer::KisShapeLayer(const KisShapeLayer& _rhs, KoShapeControllerBase* c
*/
const QTransform thisInvertedTransform = this->absoluteTransformation(0).inverted();
m_d->canvas->setUpdatesBlocked(true);
Q_FOREACH (KoShape *shape, _rhs.shapes()) {
KoShape *clonedShape = shape->cloneShape();
KIS_SAFE_ASSERT_RECOVER(clonedShape) { continue; }
clonedShape->setTransformation(shape->absoluteTransformation(0) * thisInvertedTransform);
addShape(clonedShape);
}
m_d->canvas->setUpdatesBlocked(true);
}
KisShapeLayer::KisShapeLayer(const KisShapeLayer& _rhs, const KisShapeLayer &_addShapes)
......@@ -475,6 +479,11 @@ void KisShapeLayer::forceUpdateTimedNode()
m_d->canvas->forceRepaint();
}
bool KisShapeLayer::hasPendingTimedUpdates() const
{
return m_d->canvas->hasPendingUpdates();
}
bool KisShapeLayer::saveShapesToStore(KoStore *store, QList<KoShape *> shapes, const QSizeF &sizeInPt)
{
if (!store->open("content.svg")) {
......
......@@ -146,6 +146,11 @@ public:
*/
void forceUpdateTimedNode() override;
/**
* \return true if there are any pending updates in the delayed queue
*/
bool hasPendingTimedUpdates() const override;
protected:
using KoShape::isVisible;
......
......@@ -111,6 +111,16 @@ KoUnit KisShapeLayerCanvasBase::unit() const
return KoUnit(KoUnit::Point);
}
void KisShapeLayerCanvasBase::setUpdatesBlocked(bool value)
{
m_updatesBlocked = value;
}
bool KisShapeLayerCanvasBase::updatesBlocked() const
{
return m_updatesBlocked;
}
void KisShapeLayerCanvasBase::prepareForDestroying()
{
m_isDestroying = true;
......@@ -138,7 +148,7 @@ KisShapeLayerCanvas::KisShapeLayerCanvas(KisShapeLayer *parent, KisImageWSP imag
connect(&m_asyncUpdateSignalCompressor, SIGNAL(timeout()), SLOT(slotStartAsyncRepaint()));
connect(this, SIGNAL(forwardRepaint()), &m_canvasUpdateCompressor, SLOT(start()));
connect(&m_canvasUpdateCompressor, SIGNAL(timeout()), this, SLOT(repaint()));
connect(&m_canvasUpdateCompressor, SIGNAL(timeout()), this, SLOT(slotStartDirectSyncRepaint()));
setImage(image);
}
......@@ -207,7 +217,7 @@ private:
void KisShapeLayerCanvas::updateCanvas(const QVector<QRectF> &region)
{
if (!m_parentLayer->image() || m_isDestroying) {
if (!m_parentLayer->image() || m_isDestroying || m_updatesBlocked) {
return;
}
......@@ -240,6 +250,7 @@ void KisShapeLayerCanvas::updateCanvas(const QVector<QRectF> &region)
if (qApp->thread() == QThread::currentThread()) {
emit forwardRepaint();
m_hasDirectSyncRepaintInitiated = true;
} else {
m_asyncUpdateSignalCompressor.start();
m_hasUpdateInCompressor = true;
......@@ -258,6 +269,12 @@ void KisShapeLayerCanvas::slotStartAsyncRepaint()
m_image->addSpontaneousJob(new KisRepaintShapeLayerLayerJob(m_parentLayer, this));
}
void KisShapeLayerCanvas::slotStartDirectSyncRepaint()
{
m_hasDirectSyncRepaintInitiated = false;
repaint();
}
void KisShapeLayerCanvas::slotImageSizeChanged()
{
QRegion dirtyCacheRegion;
......@@ -330,12 +347,17 @@ void KisShapeLayerCanvas::forceRepaint()
* The only real solution to this is to port vector tools to strokes framework.
*/
if (m_hasUpdateInCompressor) {
if (hasPendingUpdates()) {
m_asyncUpdateSignalCompressor.stop();
slotStartAsyncRepaint();
}
}
bool KisShapeLayerCanvas::hasPendingUpdates() const
{
return m_hasUpdateInCompressor || m_hasDirectSyncRepaintInitiated;
}
void KisShapeLayerCanvas::resetCache()
{
m_projection->clear();
......
......@@ -47,6 +47,7 @@ public:
virtual void setImage(KisImageWSP image) = 0;
void prepareForDestroying();
virtual void forceRepaint() = 0;
virtual bool hasPendingUpdates() const = 0;
bool hasChangedWhileBeingInvisible();
virtual void rerenderAfterBeingInvisible() = 0;
......@@ -66,12 +67,16 @@ public:
void updateInputMethodInfo() override {}
void setCursor(const QCursor &) override {}
void setUpdatesBlocked(bool value);
bool updatesBlocked() const;
protected:
QScopedPointer<KisImageViewConverter> m_viewConverter;
QScopedPointer<KoShapeManager> m_shapeManager;
QScopedPointer<KoSelectedShapesProxy> m_selectedShapesProxy;
bool m_hasChangedWhileBeingInvisible {false};
bool m_isDestroying {false};
bool m_updatesBlocked {false};
};
/**
......@@ -98,6 +103,7 @@ public:
void updateCanvas(const QRectF& rc) override;
void updateCanvas(const QVector<QRectF> &region);
void forceRepaint() override;
bool hasPendingUpdates() const override;
void resetCache() override;
void rerenderAfterBeingInvisible() override;
......@@ -106,6 +112,7 @@ private Q_SLOTS:
friend class KisRepaintShapeLayerLayerJob;
void repaint();
void slotStartAsyncRepaint();
void slotStartDirectSyncRepaint();
void slotImageSizeChanged();
Q_SIGNALS:
......@@ -121,6 +128,7 @@ private:
KisThreadSafeSignalCompressor m_asyncUpdateSignalCompressor;
volatile bool m_hasUpdateInCompressor = false;
volatile bool m_hasDirectSyncRepaintInitiated = false;
QRegion m_dirtyRegion;
QMutex m_dirtyRegionMutex;
......
......@@ -853,6 +853,7 @@ void KisConfig::loadSnapConfig(KisSnapConfig *config, bool defaultValue) const
config->setBoundingBox(m_cfg.readEntry("globalSnapBoundingBox", defaultConfig.boundingBox()));
config->setImageBounds(m_cfg.readEntry("globalSnapImageBounds", defaultConfig.imageBounds()));
config->setImageCenter(m_cfg.readEntry("globalSnapImageCenter", defaultConfig.imageCenter()));
config->setToPixel(m_cfg.readEntry("globalSnapToPixel", defaultConfig.toPixel()));
}
void KisConfig::saveSnapConfig(const KisSnapConfig &config)
......@@ -864,6 +865,7 @@ void KisConfig::saveSnapConfig(const KisSnapConfig &config)
m_cfg.writeEntry("globalSnapBoundingBox", config.boundingBox());
m_cfg.writeEntry("globalSnapImageBounds", config.imageBounds());
m_cfg.writeEntry("globalSnapImageCenter", config.imageCenter());
m_cfg.writeEntry("globalSnapToPixel", config.toPixel());
}
qint32 KisConfig::checkSize(bool defaultValue) const
......