"));
uint firstParaEndHtml = (uint) html.indexOf(QLatin1String(""), html.indexOf(QLatin1String("
"))+1);
uint firstParaEndBr = (uint) html.indexOf(QLatin1String("
request().url();
requestUrl = source.toString();
streamReader.setDevice(reply);
RssItemList list;
while (!streamReader.atEnd()) {
switch (streamReader.readNext()) {
case QXmlStreamReader::StartElement:
if (streamReader.name() == QLatin1String("item"))
list.append(parseItem());
else if (streamReader.name() == QLatin1String("title"))
blogName = streamReader.readElementText();
else if (streamReader.name() == QLatin1String("link")) {
if (!streamReader.namespaceUri().isEmpty())
break;
QString favIconString(streamReader.readElementText());
QUrl favIconUrl(favIconString);
favIconUrl.setPath(QLatin1String("favicon.ico"));
blogIcon = favIconUrl.toString();
blogIcon = QString(); // XXX: fix the favicon on krita.org!
}
break;
default:
break;
}
}
return list;
}
private:
QXmlStreamReader streamReader;
QString requestUrl;
QString blogIcon;
QString blogName;
};
MultiFeedRssModel::MultiFeedRssModel(QObject *parent) :
QAbstractListModel(parent),
m_networkAccessManager(new KisNetworkAccessManager),
m_articleCount(0)
{
connect(m_networkAccessManager, SIGNAL(finished(QNetworkReply*)),
SLOT(appendFeedData(QNetworkReply*)), Qt::QueuedConnection);
}
MultiFeedRssModel::~MultiFeedRssModel()
{
}
QHash MultiFeedRssModel::roleNames() const
{
QHash roleNames;
roleNames[TitleRole] = "title";
roleNames[DescriptionRole] = "description";
roleNames[PubDateRole] = "pubDate";
roleNames[LinkRole] = "link";
roleNames[BlogNameRole] = "blogName";
roleNames[BlogIconRole] = "blogIcon";
return roleNames;
}
void MultiFeedRssModel::addFeed(const QString& feed)
{
const QUrl feedUrl(feed);
QMetaObject::invokeMethod(m_networkAccessManager, "getUrl",
Qt::QueuedConnection, Q_ARG(QUrl, feedUrl));
}
bool sortForPubDate(const RssItem& item1, const RssItem& item2)
{
return item1.pubDate > item2.pubDate;
}
void MultiFeedRssModel::appendFeedData(QNetworkReply *reply)
{
RssReader reader;
m_aggregatedFeed.append(reader.parse(reply));
std::sort(m_aggregatedFeed.begin(), m_aggregatedFeed.end(), sortForPubDate);
setArticleCount(m_aggregatedFeed.size());
beginResetModel();
endResetModel();
}
void MultiFeedRssModel::removeFeed(const QString &feed)
{
QMutableListIterator it(m_aggregatedFeed);
while (it.hasNext()) {
RssItem item = it.next();
if (item.source == feed)
it.remove();
}
setArticleCount(m_aggregatedFeed.size());
}
int MultiFeedRssModel::rowCount(const QModelIndex &) const
{
return m_aggregatedFeed.size();
}
QVariant MultiFeedRssModel::data(const QModelIndex &index, int role) const
{
RssItem item = m_aggregatedFeed.at(index.row());
switch (role) {
case Qt::DisplayRole:
{
return QString("" + item.title + ""
- "
(" + item.pubDate.toString("MMMM d, yyyy") + ") "
+ "
(" + item.pubDate.toLocalTime().toString(Qt::DefaultLocaleShortDate) + ") "
+ item.description.left(90).append("...") + "
");
}
case TitleRole:
return item.title;
case DescriptionRole:
return item.description;
case PubDateRole:
return item.pubDate.toString("dd-MM-yyyy hh:mm");
case LinkRole:
return item.link;
case BlogNameRole:
return item.blogName;
case BlogIconRole:
return item.blogIcon;
}
return QVariant();
}
diff --git a/libs/ui/canvas/kis_canvas_controller.cpp b/libs/ui/canvas/kis_canvas_controller.cpp
index 2ab25dec3b..7af9689b70 100644
--- a/libs/ui/canvas/kis_canvas_controller.cpp
+++ b/libs/ui/canvas/kis_canvas_controller.cpp
@@ -1,398 +1,386 @@
/*
* Copyright (c) 2010 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_canvas_controller.h"
#include
#include
#include
#include
#include
#include "kis_canvas_decoration.h"
-#include "kis_paintop_transformation_connector.h"
#include "kis_coordinates_converter.h"
#include "kis_canvas2.h"
#include "opengl/kis_opengl_canvas2.h"
#include "KisDocument.h"
#include "kis_image.h"
#include "KisViewManager.h"
#include "KisView.h"
#include "krita_utils.h"
#include "kis_config.h"
#include "kis_signal_compressor_with_param.h"
#include "kis_config_notifier.h"
static const int gRulersUpdateDelay = 80 /* ms */;
struct KisCanvasController::Private {
Private(KisCanvasController *qq)
- : q(qq),
- paintOpTransformationConnector(0)
+ : q(qq)
{
using namespace std::placeholders;
std::function callback(
std::bind(&KisCanvasController::Private::emitPointerPositionChangedSignals, this, _1));
mousePositionCompressor.reset(
new KisSignalCompressorWithParam(
gRulersUpdateDelay,
callback,
KisSignalCompressor::FIRST_ACTIVE));
}
QPointer view;
KisCoordinatesConverter *coordinatesConverter;
KisCanvasController *q;
- KisPaintopTransformationConnector *paintOpTransformationConnector;
QScopedPointer > mousePositionCompressor;
void emitPointerPositionChangedSignals(QPoint pointerPos);
void updateDocumentSizeAfterTransform();
void showRotationValueOnCanvas();
void showMirrorStateOnCanvas();
};
void KisCanvasController::Private::emitPointerPositionChangedSignals(QPoint pointerPos)
{
if (!coordinatesConverter) return;
QPointF documentPos = coordinatesConverter->widgetToDocument(pointerPos);
q->proxyObject->emitDocumentMousePositionChanged(documentPos);
q->proxyObject->emitCanvasMousePositionChanged(pointerPos);
}
void KisCanvasController::Private::updateDocumentSizeAfterTransform()
{
// round the size of the area to the nearest integer instead of getting aligned rect
QSize widgetSize = coordinatesConverter->imageRectInWidgetPixels().toRect().size();
q->updateDocumentSize(widgetSize, true);
KisCanvas2 *kritaCanvas = dynamic_cast(q->canvas());
Q_ASSERT(kritaCanvas);
kritaCanvas->notifyZoomChanged();
}
KisCanvasController::KisCanvasController(QPointerparent, KActionCollection * actionCollection)
: KoCanvasControllerWidget(actionCollection, parent),
m_d(new Private(this))
{
m_d->view = parent;
}
KisCanvasController::~KisCanvasController()
{
delete m_d;
}
void KisCanvasController::setCanvas(KoCanvasBase *canvas)
{
if (canvas) {
KisCanvas2 *kritaCanvas = dynamic_cast(canvas);
KIS_SAFE_ASSERT_RECOVER_RETURN(kritaCanvas);
m_d->coordinatesConverter =
const_cast(kritaCanvas->coordinatesConverter());
-
- m_d->paintOpTransformationConnector =
- new KisPaintopTransformationConnector(kritaCanvas, this);
} else {
m_d->coordinatesConverter = 0;
-
- delete m_d->paintOpTransformationConnector;
- m_d->paintOpTransformationConnector = 0;
}
KoCanvasControllerWidget::setCanvas(canvas);
}
void KisCanvasController::activate()
{
KoCanvasControllerWidget::activate();
}
QPointF KisCanvasController::currentCursorPosition() const
{
KoCanvasBase *canvas = m_d->view->canvasBase();
QWidget *canvasWidget = canvas->canvasWidget();
const QPointF cursorPosWidget = canvasWidget->mapFromGlobal(QCursor::pos());
return m_d->coordinatesConverter->widgetToDocument(cursorPosWidget);
}
void KisCanvasController::keyPressEvent(QKeyEvent *event)
{
/**
* Dirty Hack Alert:
* Do not call the KoCanvasControllerWidget::keyPressEvent()
* to avoid activation of Pan and Default tool activation shortcuts
*/
Q_UNUSED(event);
}
void KisCanvasController::wheelEvent(QWheelEvent *event)
{
/**
* Dirty Hack Alert:
* Do not call the KoCanvasControllerWidget::wheelEvent()
* to disable the default behavior of KoCanvasControllerWidget and QAbstractScrollArea
*/
Q_UNUSED(event);
}
bool KisCanvasController::eventFilter(QObject *watched, QEvent *event)
{
KoCanvasBase *canvas = this->canvas();
if (!canvas || !canvas->canvasWidget() || canvas->canvasWidget() != watched) return false;
if (event->type() == QEvent::MouseMove) {
QMouseEvent *mevent = static_cast(event);
m_d->mousePositionCompressor->start(mevent->pos());
} else if (event->type() == QEvent::TabletMove) {
QTabletEvent *tevent = static_cast(event);
m_d->mousePositionCompressor->start(tevent->pos());
} else if (event->type() == QEvent::FocusIn) {
m_d->view->syncLastActiveNodeToDocument();
}
return false;
}
void KisCanvasController::updateDocumentSize(const QSizeF &sz, bool recalculateCenter)
{
KoCanvasControllerWidget::updateDocumentSize(sz, recalculateCenter);
emit documentSizeChanged();
}
void KisCanvasController::Private::showMirrorStateOnCanvas()
{
bool isXMirrored = coordinatesConverter->xAxisMirrored();
view->viewManager()->
showFloatingMessage(
i18nc("floating message about mirroring",
"Horizontal mirroring: %1 ", isXMirrored ? i18n("ON") : i18n("OFF")),
QIcon(), 500, KisFloatingMessage::Low);
}
void KisCanvasController::mirrorCanvas(bool enable)
{
QPoint newOffset = m_d->coordinatesConverter->mirror(m_d->coordinatesConverter->widgetCenterPoint(), enable, false);
m_d->updateDocumentSizeAfterTransform();
setScrollBarValue(newOffset);
- m_d->paintOpTransformationConnector->notifyTransformationChanged();
m_d->showMirrorStateOnCanvas();
}
void KisCanvasController::Private::showRotationValueOnCanvas()
{
qreal rotationAngle = coordinatesConverter->rotationAngle();
view->viewManager()->
showFloatingMessage(
i18nc("floating message about rotation", "Rotation: %1° ",
KritaUtils::prettyFormatReal(rotationAngle)),
QIcon(), 500, KisFloatingMessage::Low, Qt::AlignCenter);
}
void KisCanvasController::rotateCanvas(qreal angle, const QPointF ¢er)
{
QPoint newOffset = m_d->coordinatesConverter->rotate(center, angle);
m_d->updateDocumentSizeAfterTransform();
setScrollBarValue(newOffset);
- m_d->paintOpTransformationConnector->notifyTransformationChanged();
m_d->showRotationValueOnCanvas();
}
void KisCanvasController::rotateCanvas(qreal angle)
{
rotateCanvas(angle, m_d->coordinatesConverter->widgetCenterPoint());
}
void KisCanvasController::rotateCanvasRight15()
{
rotateCanvas(15.0);
}
void KisCanvasController::rotateCanvasLeft15()
{
rotateCanvas(-15.0);
}
qreal KisCanvasController::rotation() const
{
return m_d->coordinatesConverter->rotationAngle();
}
void KisCanvasController::resetCanvasRotation()
{
QPoint newOffset = m_d->coordinatesConverter->resetRotation(m_d->coordinatesConverter->widgetCenterPoint());
m_d->updateDocumentSizeAfterTransform();
setScrollBarValue(newOffset);
- m_d->paintOpTransformationConnector->notifyTransformationChanged();
m_d->showRotationValueOnCanvas();
}
void KisCanvasController::slotToggleWrapAroundMode(bool value)
{
KisCanvas2 *kritaCanvas = dynamic_cast(canvas());
Q_ASSERT(kritaCanvas);
if (!canvas()->canvasIsOpenGL() && value) {
m_d->view->viewManager()->showFloatingMessage(i18n("You are activating wrap-around mode, but have not enabled OpenGL.\n"
"To visualize wrap-around mode, enable OpenGL."), QIcon());
}
kritaCanvas->setWrapAroundViewingMode(value);
kritaCanvas->image()->setWrapAroundModePermitted(value);
}
bool KisCanvasController::wrapAroundMode() const
{
KisCanvas2 *kritaCanvas = dynamic_cast(canvas());
Q_ASSERT(kritaCanvas);
return kritaCanvas->wrapAroundViewingMode();
}
void KisCanvasController::slotTogglePixelGrid(bool value)
{
KisConfig cfg(false);
cfg.enablePixelGrid(value);
KisConfigNotifier::instance()->notifyPixelGridModeChanged();
}
void KisCanvasController::slotToggleLevelOfDetailMode(bool value)
{
KisCanvas2 *kritaCanvas = dynamic_cast(canvas());
Q_ASSERT(kritaCanvas);
kritaCanvas->setLodAllowedInCanvas(value);
bool result = levelOfDetailMode();
if (!value || result) {
m_d->view->viewManager()->showFloatingMessage(
i18n("Instant Preview Mode: %1", result ?
i18n("ON") : i18n("OFF")),
QIcon(), 500, KisFloatingMessage::Low);
} else {
QString reason;
if (!kritaCanvas->canvasIsOpenGL()) {
reason = i18n("Instant Preview is only supported with OpenGL activated");
}
else if (kritaCanvas->openGLFilterMode() == KisOpenGL::BilinearFilterMode ||
kritaCanvas->openGLFilterMode() == KisOpenGL::NearestFilterMode) {
QString filteringMode =
kritaCanvas->openGLFilterMode() == KisOpenGL::BilinearFilterMode ?
i18n("Bilinear") : i18n("Nearest Neighbour");
reason = i18n("Instant Preview is supported\n in Trilinear or High Quality filtering modes.\nCurrent mode is %1", filteringMode);
}
m_d->view->viewManager()->showFloatingMessage(
i18n("Failed activating Instant Preview mode!\n\n%1", reason),
QIcon(), 5000, KisFloatingMessage::Low);
}
}
bool KisCanvasController::levelOfDetailMode() const
{
KisCanvas2 *kritaCanvas = dynamic_cast(canvas());
Q_ASSERT(kritaCanvas);
return kritaCanvas->lodAllowedInCanvas();
}
void KisCanvasController::saveCanvasState(KisPropertiesConfiguration &config) const
{
const QPointF ¢er = preferredCenter();
config.setProperty("panX", center.x());
config.setProperty("panY", center.y());
config.setProperty("rotation", rotation());
config.setProperty("mirror", m_d->coordinatesConverter->xAxisMirrored());
config.setProperty("wrapAround", wrapAroundMode());
config.setProperty("enableInstantPreview", levelOfDetailMode());
}
void KisCanvasController::restoreCanvasState(const KisPropertiesConfiguration &config)
{
KisCanvas2 *kritaCanvas = dynamic_cast(canvas());
Q_ASSERT(kritaCanvas);
mirrorCanvas(config.getBool("mirror", false));
rotateCanvas(config.getFloat("rotation", 0.0f));
const QPointF ¢er = preferredCenter();
float panX = config.getFloat("panX", center.x());
float panY = config.getFloat("panY", center.y());
setPreferredCenter(QPointF(panX, panY));
slotToggleWrapAroundMode(config.getBool("wrapAround", false));
kritaCanvas->setLodAllowedInCanvas(config.getBool("enableInstantPreview", false));
}
void KisCanvasController::resetScrollBars()
{
// The scrollbar value always points at the top-left corner of the
// bit of image we paint.
KisDocument *doc = m_d->view->document();
if (!doc) return;
QRectF documentBounds = doc->documentBounds();
QRectF viewRect = m_d->coordinatesConverter->imageToWidget(documentBounds);
// Cancel out any existing pan
const QRectF imageBounds = m_d->view->image()->bounds();
const QRectF imageBB = m_d->coordinatesConverter->imageToWidget(imageBounds);
QPointF pan = imageBB.topLeft();
viewRect.translate(-pan);
int drawH = viewport()->height();
int drawW = viewport()->width();
qreal horizontalReserve = vastScrollingFactor() * drawW;
qreal verticalReserve = vastScrollingFactor() * drawH;
qreal xMin = viewRect.left() - horizontalReserve;
qreal yMin = viewRect.top() - verticalReserve;
qreal xMax = viewRect.right() - drawW + horizontalReserve;
qreal yMax = viewRect.bottom() - drawH + verticalReserve;
QScrollBar *hScroll = horizontalScrollBar();
QScrollBar *vScroll = verticalScrollBar();
hScroll->setRange(static_cast(xMin), static_cast(xMax));
vScroll->setRange(static_cast(yMin), static_cast(yMax));
int fontHeight = QFontMetrics(font()).height();
vScroll->setPageStep(drawH);
vScroll->setSingleStep(fontHeight);
hScroll->setPageStep(drawW);
hScroll->setSingleStep(fontHeight);
}
diff --git a/libs/ui/canvas/kis_paintop_transformation_connector.cpp b/libs/ui/canvas/kis_paintop_transformation_connector.cpp
deleted file mode 100644
index bf93986495..0000000000
--- a/libs/ui/canvas/kis_paintop_transformation_connector.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (c) 2013 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_paintop_transformation_connector.h"
-
-#include "kis_canvas_resource_provider.h"
-#include "kis_canvas2.h"
-#include "kis_coordinates_converter.h"
-#include "brushengine/kis_paintop_preset.h"
-#include "brushengine/kis_paintop_settings.h"
-
-
-KisPaintopTransformationConnector::KisPaintopTransformationConnector(KisCanvas2 *canvas, QObject *parent)
- : QObject(parent),
- m_canvas(canvas)
-{
- connect(m_canvas->resourceManager(),
- SIGNAL(canvasResourceChanged(int,QVariant)),
- SLOT(slotCanvasResourceChanged(int,QVariant)));
-}
-
-void KisPaintopTransformationConnector::notifyTransformationChanged()
-{
- KisPaintOpPresetSP preset =
- m_canvas->resourceManager()->
- resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value();
-
- if (preset) {
- const KisCoordinatesConverter *converter = m_canvas->coordinatesConverter();
- preset->settings()->setCanvasRotation(converter->rotationAngle());
- preset->settings()->setCanvasMirroring(converter->xAxisMirrored(),
- converter->yAxisMirrored());
- }
-}
-
-void KisPaintopTransformationConnector::slotCanvasResourceChanged(int key, const QVariant &resource)
-{
- Q_UNUSED(resource);
-
- if (key == KisCanvasResourceProvider::CurrentPaintOpPreset) {
- notifyTransformationChanged();
- }
-}
diff --git a/libs/ui/canvas/kis_paintop_transformation_connector.h b/libs/ui/canvas/kis_paintop_transformation_connector.h
deleted file mode 100644
index 68c751ee0a..0000000000
--- a/libs/ui/canvas/kis_paintop_transformation_connector.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (c) 2013 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 __KIS_PAINTOP_TRANSFORMATION_CONNECTOR_H
-#define __KIS_PAINTOP_TRANSFORMATION_CONNECTOR_H
-
-#include
-#include
-
-#include
-
-class KisPaintopTransformationConnector : public QObject
-{
- Q_OBJECT
-public:
- KisPaintopTransformationConnector(KisCanvas2 *canvas, QObject *parent);
-
-public:
- void notifyTransformationChanged();
-
-public Q_SLOTS:
- void slotCanvasResourceChanged(int key, const QVariant &resource);
-
-private:
- QPointer m_canvas;
-};
-
-#endif /* __KIS_PAINTOP_TRANSFORMATION_CONNECTOR_H */
diff --git a/libs/ui/tool/kis_painting_information_builder.cpp b/libs/ui/tool/kis_painting_information_builder.cpp
index e52259105f..b005a4a67b 100644
--- a/libs/ui/tool/kis_painting_information_builder.cpp
+++ b/libs/ui/tool/kis_painting_information_builder.cpp
@@ -1,198 +1,259 @@
/*
* Copyright (c) 2011 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_painting_information_builder.h"
#include
#include "kis_config.h"
#include "kis_config_notifier.h"
#include "kis_cubic_curve.h"
#include "kis_speed_smoother.h"
#include
#include "kis_canvas_resource_provider.h"
/***********************************************************************/
/* KisPaintingInformationBuilder */
/***********************************************************************/
const int KisPaintingInformationBuilder::LEVEL_OF_PRESSURE_RESOLUTION = 1024;
KisPaintingInformationBuilder::KisPaintingInformationBuilder()
: m_speedSmoother(new KisSpeedSmoother()),
m_pressureDisabled(false)
{
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()),
SLOT(updateSettings()));
updateSettings();
}
KisPaintingInformationBuilder::~KisPaintingInformationBuilder()
{
}
void KisPaintingInformationBuilder::updateSettings()
{
KisConfig cfg(true);
KisCubicCurve curve;
curve.fromString(cfg.pressureTabletCurve());
m_pressureSamples = curve.floatTransfer(LEVEL_OF_PRESSURE_RESOLUTION + 1);
}
KisPaintInformation KisPaintingInformationBuilder::startStroke(KoPointerEvent *event,
int timeElapsed,
const KoCanvasResourceProvider *manager)
{
if (manager) {
m_pressureDisabled = manager->resource(KisCanvasResourceProvider::DisablePressure).toBool();
}
m_startPoint = event->point;
return createPaintingInformation(event, timeElapsed);
}
KisPaintInformation KisPaintingInformationBuilder::continueStroke(KoPointerEvent *event,
int timeElapsed)
{
return createPaintingInformation(event, timeElapsed);
}
QPointF KisPaintingInformationBuilder::adjustDocumentPoint(const QPointF &point, const QPointF &/*startPoint*/)
{
return point;
}
QPointF KisPaintingInformationBuilder::documentToImage(const QPointF &point)
{
return point;
}
QPointF KisPaintingInformationBuilder::imageToView(const QPointF &point)
{
return point;
}
qreal KisPaintingInformationBuilder::calculatePerspective(const QPointF &documentPoint)
{
Q_UNUSED(documentPoint);
return 1.0;
}
+qreal KisPaintingInformationBuilder::canvasRotation() const
+{
+ return 0;
+}
+
+bool KisPaintingInformationBuilder::canvasMirroredX() const
+{
+ return false;
+}
+
+bool KisPaintingInformationBuilder::canvasMirroredY() const
+{
+ return false;
+}
KisPaintInformation KisPaintingInformationBuilder::createPaintingInformation(KoPointerEvent *event,
int timeElapsed)
{
QPointF adjusted = adjustDocumentPoint(event->point, m_startPoint);
QPointF imagePoint = documentToImage(adjusted);
qreal perspective = calculatePerspective(adjusted);
qreal speed = m_speedSmoother->getNextSpeed(imageToView(imagePoint));
- return KisPaintInformation(imagePoint,
+ KisPaintInformation pi(imagePoint,
!m_pressureDisabled ? 1.0 : pressureToCurve(event->pressure()),
event->xTilt(), event->yTilt(),
event->rotation(),
event->tangentialPressure(),
perspective,
timeElapsed,
speed);
+
+ pi.setCanvasRotation(canvasRotation());
+ pi.setCanvasMirroredH(canvasMirroredX());
+ pi.setCanvasMirroredV(canvasMirroredY());
+
+ return pi;
}
KisPaintInformation KisPaintingInformationBuilder::hover(const QPointF &imagePoint,
const KoPointerEvent *event)
{
qreal perspective = calculatePerspective(imagePoint);
qreal speed = m_speedSmoother->getNextSpeed(imageToView(imagePoint));
if (event) {
return KisPaintInformation::createHoveringModeInfo(imagePoint,
PRESSURE_DEFAULT,
event->xTilt(), event->yTilt(),
event->rotation(),
event->tangentialPressure(),
perspective,
- speed);
+ speed,
+ canvasRotation(),
+ canvasMirroredX(),
+ canvasMirroredY());
} else {
- return KisPaintInformation::createHoveringModeInfo(imagePoint);
+ KisPaintInformation pi = KisPaintInformation::createHoveringModeInfo(imagePoint);
+ pi.setCanvasRotation(canvasRotation());
+ pi.setCanvasMirroredH(canvasMirroredX());
+ pi.setCanvasMirroredV(canvasMirroredY());
+ return pi;
}
}
qreal KisPaintingInformationBuilder::pressureToCurve(qreal pressure)
{
return KisCubicCurve::interpolateLinear(pressure, m_pressureSamples);
}
/***********************************************************************/
/* KisConverterPaintingInformationBuilder */
/***********************************************************************/
#include "kis_coordinates_converter.h"
KisConverterPaintingInformationBuilder::KisConverterPaintingInformationBuilder(const KisCoordinatesConverter *converter)
: m_converter(converter)
{
}
QPointF KisConverterPaintingInformationBuilder::documentToImage(const QPointF &point)
{
return m_converter->documentToImage(point);
}
QPointF KisConverterPaintingInformationBuilder::imageToView(const QPointF &point)
{
return m_converter->documentToWidget(point);
}
+qreal KisConverterPaintingInformationBuilder::canvasRotation() const
+{
+ return m_converter->rotationAngle();
+}
+
+bool KisConverterPaintingInformationBuilder::canvasMirroredX() const
+{
+ return m_converter->xAxisMirrored();
+}
+
+bool KisConverterPaintingInformationBuilder::canvasMirroredY() const
+{
+ return m_converter->yAxisMirrored();
+}
+
/***********************************************************************/
-/* KisToolFreehandPaintingInformationBuilder */
+/* KisToolFreehandPaintingInformationBuilder */
/***********************************************************************/
#include "kis_tool_freehand.h"
+#include "kis_canvas2.h"
KisToolFreehandPaintingInformationBuilder::KisToolFreehandPaintingInformationBuilder(KisToolFreehand *tool)
: m_tool(tool)
{
}
QPointF KisToolFreehandPaintingInformationBuilder::documentToImage(const QPointF &point)
{
return m_tool->convertToPixelCoord(point);
}
QPointF KisToolFreehandPaintingInformationBuilder::imageToView(const QPointF &point)
{
return m_tool->pixelToView(point);
}
QPointF KisToolFreehandPaintingInformationBuilder::adjustDocumentPoint(const QPointF &point, const QPointF &startPoint)
{
return m_tool->adjustPosition(point, startPoint);
}
qreal KisToolFreehandPaintingInformationBuilder::calculatePerspective(const QPointF &documentPoint)
{
return m_tool->calculatePerspective(documentPoint);
}
+
+qreal KisToolFreehandPaintingInformationBuilder::canvasRotation() const
+{
+ KisCanvas2 *canvas = dynamic_cast(m_tool->canvas());
+ return canvas->coordinatesConverter()->rotationAngle();
+}
+
+bool KisToolFreehandPaintingInformationBuilder::canvasMirroredX() const
+{
+ KisCanvas2 *canvas = dynamic_cast(m_tool->canvas());
+ return canvas->coordinatesConverter()->xAxisMirrored();
+}
+
+bool KisToolFreehandPaintingInformationBuilder::canvasMirroredY() const
+{
+ KisCanvas2 *canvas = dynamic_cast(m_tool->canvas());
+ return canvas->coordinatesConverter()->yAxisMirrored();
+}
diff --git a/libs/ui/tool/kis_painting_information_builder.h b/libs/ui/tool/kis_painting_information_builder.h
index 42e9820a9d..c5c2e77651 100644
--- a/libs/ui/tool/kis_painting_information_builder.h
+++ b/libs/ui/tool/kis_painting_information_builder.h
@@ -1,112 +1,124 @@
/*
* Copyright (c) 2011 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 __KIS_PAINTING_INFORMATION_BUILDER_H
#define __KIS_PAINTING_INFORMATION_BUILDER_H
#include
#include
#include "kis_types.h"
#include "kritaui_export.h"
#include
class KoPointerEvent;
class KisToolFreehand;
class KisCoordinatesConverter;
class KisSpeedSmoother;
class KoCanvasResourceProvider;
class KRITAUI_EXPORT KisPaintingInformationBuilder : public QObject
{
Q_OBJECT
public:
KisPaintingInformationBuilder();
~KisPaintingInformationBuilder() override;
KisPaintInformation startStroke(KoPointerEvent *event, int timeElapsed, const KoCanvasResourceProvider *manager);
KisPaintInformation continueStroke(KoPointerEvent *event,
int timeElapsed);
KisPaintInformation hover(const QPointF &imagePoint,
const KoPointerEvent *event);
qreal pressureToCurve(qreal pressure);
protected Q_SLOTS:
void updateSettings();
protected:
virtual QPointF adjustDocumentPoint(const QPointF &point, const QPointF &startPoint);
virtual QPointF documentToImage(const QPointF &point);
virtual QPointF imageToView(const QPointF &point);
virtual qreal calculatePerspective(const QPointF &documentPoint);
+ virtual qreal canvasRotation() const;
+ virtual bool canvasMirroredX() const;
+ virtual bool canvasMirroredY() const;
+
private:
KisPaintInformation createPaintingInformation(KoPointerEvent *event,
int timeElapsed);
/**
* Defines how many discrete samples are stored in a precomputed array
* of different pressures.
*/
static const int LEVEL_OF_PRESSURE_RESOLUTION;
private:
QVector m_pressureSamples;
QPointF m_startPoint;
QScopedPointer m_speedSmoother;
bool m_pressureDisabled;
};
class KRITAUI_EXPORT KisConverterPaintingInformationBuilder : public KisPaintingInformationBuilder
{
Q_OBJECT
public:
KisConverterPaintingInformationBuilder(const KisCoordinatesConverter *converter);
protected:
QPointF documentToImage(const QPointF &point) override;
QPointF imageToView(const QPointF &point) override;
+ qreal canvasRotation() const override;
+ bool canvasMirroredX() const override;
+ bool canvasMirroredY() const override;
+
private:
const KisCoordinatesConverter *m_converter;
};
class KRITAUI_EXPORT KisToolFreehandPaintingInformationBuilder : public KisPaintingInformationBuilder
{
Q_OBJECT
public:
KisToolFreehandPaintingInformationBuilder(KisToolFreehand *tool);
protected:
QPointF documentToImage(const QPointF &point) override;
QPointF imageToView(const QPointF &point) override;
QPointF adjustDocumentPoint(const QPointF &point, const QPointF &startPoint) override;
qreal calculatePerspective(const QPointF &documentPoint) override;
+ qreal canvasRotation() const override;
+ bool canvasMirroredX() const override;
+ bool canvasMirroredY() const override;
+
private:
KisToolFreehand *m_tool;
};
#endif /* __KIS_PAINTING_INFORMATION_BUILDER_H */
diff --git a/libs/ui/tool/kis_tool.cc b/libs/ui/tool/kis_tool.cc
index 40622c62f0..eafcf146d9 100644
--- a/libs/ui/tool/kis_tool.cc
+++ b/libs/ui/tool/kis_tool.cc
@@ -1,667 +1,668 @@
/*
* Copyright (c) 2006, 2010 Boudewijn Rempt
*
* 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.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "kis_node_manager.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "opengl/kis_opengl_canvas2.h"
#include "kis_canvas_resource_provider.h"
#include "canvas/kis_canvas2.h"
#include "kis_coordinates_converter.h"
#include "filter/kis_filter_configuration.h"
#include "kis_config.h"
#include "kis_config_notifier.h"
#include "kis_cursor.h"
#include
#include "kis_resources_snapshot.h"
#include
#include "kis_action_registry.h"
#include "kis_tool_utils.h"
struct Q_DECL_HIDDEN KisTool::Private {
QCursor cursor; // the cursor that should be shown on tool activation.
// From the canvas resources
KoPatternSP currentPattern;
KoAbstractGradientSP currentGradient;
KoColor currentFgColor;
KoColor currentBgColor;
float currentExposure{1.0};
KisFilterConfigurationSP currentGenerator;
QWidget* optionWidget{0};
ToolMode m_mode{HOVER_MODE};
bool m_isActive{false};
};
KisTool::KisTool(KoCanvasBase * canvas, const QCursor & cursor)
: KoToolBase(canvas)
, d(new Private)
{
d->cursor = cursor;
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(resetCursorStyle()));
connect(this, SIGNAL(isActiveChanged(bool)), SLOT(resetCursorStyle()));
}
KisTool::~KisTool()
{
delete d;
}
void KisTool::activate(ToolActivation activation, const QSet &shapes)
{
KoToolBase::activate(activation, shapes);
resetCursorStyle();
if (!canvas()) return;
if (!canvas()->resourceManager()) return;
d->currentFgColor = canvas()->resourceManager()->resource(KoCanvasResourceProvider::ForegroundColor).value();
d->currentBgColor = canvas()->resourceManager()->resource(KoCanvasResourceProvider::BackgroundColor).value();
if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::CurrentPattern)) {
d->currentPattern = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPattern).value();
}
if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::CurrentGradient)) {
d->currentGradient = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentGradient).value();
}
KisPaintOpPresetSP preset = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value();
if (preset && preset->settings()) {
preset->settings()->activate();
}
if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::HdrExposure)) {
d->currentExposure = static_cast(canvas()->resourceManager()->resource(KisCanvasResourceProvider::HdrExposure).toDouble());
}
if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::CurrentGeneratorConfiguration)) {
d->currentGenerator = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentGeneratorConfiguration).value();
}
d->m_isActive = true;
emit isActiveChanged(true);
}
void KisTool::deactivate()
{
d->m_isActive = false;
emit isActiveChanged(false);
KoToolBase::deactivate();
}
void KisTool::canvasResourceChanged(int key, const QVariant & v)
{
QString formattedBrushName;
if (key == KisCanvasResourceProvider::CurrentPaintOpPreset) {
formattedBrushName = v.value()->name().replace("_", " ");
}
switch (key) {
case(KoCanvasResourceProvider::ForegroundColor):
d->currentFgColor = v.value();
break;
case(KoCanvasResourceProvider::BackgroundColor):
d->currentBgColor = v.value();
break;
case(KisCanvasResourceProvider::CurrentPattern):
d->currentPattern = v.value();
break;
case(KisCanvasResourceProvider::CurrentGradient):
d->currentGradient = v.value();
break;
case(KisCanvasResourceProvider::HdrExposure):
d->currentExposure = static_cast(v.toDouble());
break;
case(KisCanvasResourceProvider::CurrentGeneratorConfiguration):
d->currentGenerator = static_cast(v.value());
break;
case(KisCanvasResourceProvider::CurrentPaintOpPreset):
emit statusTextChanged(formattedBrushName);
break;
case(KisCanvasResourceProvider::CurrentKritaNode):
resetCursorStyle();
break;
default:
break; // Do nothing
};
}
void KisTool::updateSettingsViews()
{
}
QPointF KisTool::widgetCenterInWidgetPixels()
{
KisCanvas2 *kritaCanvas = dynamic_cast(canvas());
Q_ASSERT(kritaCanvas);
const KisCoordinatesConverter *converter = kritaCanvas->coordinatesConverter();
return converter->flakeToWidget(converter->flakeCenterPoint());
}
QPointF KisTool::convertDocumentToWidget(const QPointF& pt)
{
KisCanvas2 *kritaCanvas = dynamic_cast(canvas());
Q_ASSERT(kritaCanvas);
return kritaCanvas->coordinatesConverter()->documentToWidget(pt);
}
QPointF KisTool::convertToPixelCoord(KoPointerEvent *e)
{
if (!image())
return e->point;
return image()->documentToPixel(e->point);
}
QPointF KisTool::convertToPixelCoord(const QPointF& pt)
{
if (!image())
return pt;
return image()->documentToPixel(pt);
}
QPointF KisTool::convertToPixelCoordAndSnap(KoPointerEvent *e, const QPointF &offset, bool useModifiers)
{
if (!image())
return e->point;
KoSnapGuide *snapGuide = canvas()->snapGuide();
QPointF pos = snapGuide->snap(e->point, offset, useModifiers ? e->modifiers() : Qt::NoModifier);
return image()->documentToPixel(pos);
}
QPointF KisTool::convertToPixelCoordAndSnap(const QPointF& pt, const QPointF &offset)
{
if (!image())
return pt;
KoSnapGuide *snapGuide = canvas()->snapGuide();
QPointF pos = snapGuide->snap(pt, offset, Qt::NoModifier);
return image()->documentToPixel(pos);
}
QPoint KisTool::convertToImagePixelCoordFloored(KoPointerEvent *e)
{
if (!image())
return e->point.toPoint();
return image()->documentToImagePixelFloored(e->point);
}
QPointF KisTool::viewToPixel(const QPointF &viewCoord) const
{
if (!image())
return viewCoord;
return image()->documentToPixel(canvas()->viewConverter()->viewToDocument(viewCoord));
}
QRectF KisTool::convertToPt(const QRectF &rect)
{
if (!image())
return rect;
QRectF r;
//We add 1 in the following to the extreme coords because a pixel always has size
r.setCoords(int(rect.left()) / image()->xRes(), int(rect.top()) / image()->yRes(),
int(rect.right()) / image()->xRes(), int( rect.bottom()) / image()->yRes());
return r;
}
qreal KisTool::convertToPt(qreal value)
{
const qreal avgResolution = 0.5 * (image()->xRes() + image()->yRes());
return value / avgResolution;
}
QPointF KisTool::pixelToView(const QPoint &pixelCoord) const
{
if (!image())
return pixelCoord;
QPointF documentCoord = image()->pixelToDocument(pixelCoord);
return canvas()->viewConverter()->documentToView(documentCoord);
}
QPointF KisTool::pixelToView(const QPointF &pixelCoord) const
{
if (!image())
return pixelCoord;
QPointF documentCoord = image()->pixelToDocument(pixelCoord);
return canvas()->viewConverter()->documentToView(documentCoord);
}
QRectF KisTool::pixelToView(const QRectF &pixelRect) const
{
- if (!image())
+ if (!image()) {
return pixelRect;
+ }
QPointF topLeft = pixelToView(pixelRect.topLeft());
QPointF bottomRight = pixelToView(pixelRect.bottomRight());
- return QRectF(topLeft, bottomRight);
+ return {topLeft, bottomRight};
}
QPainterPath KisTool::pixelToView(const QPainterPath &pixelPolygon) const
{
QTransform matrix;
qreal zoomX, zoomY;
canvas()->viewConverter()->zoom(&zoomX, &zoomY);
matrix.scale(zoomX/image()->xRes(), zoomY/ image()->yRes());
return matrix.map(pixelPolygon);
}
QPolygonF KisTool::pixelToView(const QPolygonF &pixelPath) const
{
QTransform matrix;
qreal zoomX, zoomY;
canvas()->viewConverter()->zoom(&zoomX, &zoomY);
matrix.scale(zoomX/image()->xRes(), zoomY/ image()->yRes());
return matrix.map(pixelPath);
}
void KisTool::updateCanvasPixelRect(const QRectF &pixelRect)
{
canvas()->updateCanvas(convertToPt(pixelRect));
}
void KisTool::updateCanvasViewRect(const QRectF &viewRect)
{
canvas()->updateCanvas(canvas()->viewConverter()->viewToDocument(viewRect));
}
KisImageWSP KisTool::image() const
{
// For now, krita tools only work in krita, not for a krita shape. Krita shapes are for 2.1
KisCanvas2 * kisCanvas = dynamic_cast(canvas());
if (kisCanvas) {
return kisCanvas->currentImage();
}
return 0;
}
QCursor KisTool::cursor() const
{
return d->cursor;
}
KoPatternSP KisTool::currentPattern()
{
return d->currentPattern;
}
KoAbstractGradientSP KisTool::currentGradient()
{
return d->currentGradient;
}
KisPaintOpPresetSP KisTool::currentPaintOpPreset()
{
return canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value();
}
KisNodeSP KisTool::currentNode() const
{
KisNodeSP node = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentKritaNode).value();
return node;
}
KisNodeList KisTool::selectedNodes() const
{
KisCanvas2 * kiscanvas = static_cast(canvas());
KisViewManager* viewManager = kiscanvas->viewManager();
return viewManager->nodeManager()->selectedNodes();
}
KoColor KisTool::currentFgColor()
{
return d->currentFgColor;
}
KoColor KisTool::currentBgColor()
{
return d->currentBgColor;
}
KisImageWSP KisTool::currentImage()
{
return image();
}
KisFilterConfigurationSP KisTool::currentGenerator()
{
return d->currentGenerator;
}
void KisTool::setMode(ToolMode mode) {
d->m_mode = mode;
}
KisTool::ToolMode KisTool::mode() const {
return d->m_mode;
}
void KisTool::setCursor(const QCursor &cursor)
{
d->cursor = cursor;
}
KisTool::AlternateAction KisTool::actionToAlternateAction(ToolAction action) {
KIS_ASSERT_RECOVER_RETURN_VALUE(action != Primary, Secondary);
return (AlternateAction)action;
}
void KisTool::activatePrimaryAction()
{
resetCursorStyle();
}
void KisTool::deactivatePrimaryAction()
{
resetCursorStyle();
}
void KisTool::beginPrimaryAction(KoPointerEvent *event)
{
Q_UNUSED(event);
}
void KisTool::beginPrimaryDoubleClickAction(KoPointerEvent *event)
{
beginPrimaryAction(event);
}
void KisTool::continuePrimaryAction(KoPointerEvent *event)
{
Q_UNUSED(event);
}
void KisTool::endPrimaryAction(KoPointerEvent *event)
{
Q_UNUSED(event);
}
bool KisTool::primaryActionSupportsHiResEvents() const
{
return false;
}
void KisTool::activateAlternateAction(AlternateAction action)
{
Q_UNUSED(action);
}
void KisTool::deactivateAlternateAction(AlternateAction action)
{
Q_UNUSED(action);
}
void KisTool::beginAlternateAction(KoPointerEvent *event, AlternateAction action)
{
Q_UNUSED(event);
Q_UNUSED(action);
}
void KisTool::beginAlternateDoubleClickAction(KoPointerEvent *event, AlternateAction action)
{
beginAlternateAction(event, action);
}
void KisTool::continueAlternateAction(KoPointerEvent *event, AlternateAction action)
{
Q_UNUSED(event);
Q_UNUSED(action);
}
void KisTool::endAlternateAction(KoPointerEvent *event, AlternateAction action)
{
Q_UNUSED(event);
Q_UNUSED(action);
}
void KisTool::mouseDoubleClickEvent(KoPointerEvent *event)
{
Q_UNUSED(event);
}
void KisTool::mouseTripleClickEvent(KoPointerEvent *event)
{
mouseDoubleClickEvent(event);
}
void KisTool::mousePressEvent(KoPointerEvent *event)
{
Q_UNUSED(event);
}
void KisTool::mouseReleaseEvent(KoPointerEvent *event)
{
Q_UNUSED(event);
}
void KisTool::mouseMoveEvent(KoPointerEvent *event)
{
Q_UNUSED(event);
}
void KisTool::deleteSelection()
{
KisResourcesSnapshotSP resources =
new KisResourcesSnapshot(image(), currentNode(), this->canvas()->resourceManager());
if (!blockUntilOperationsFinished()) {
return;
}
if (!KisToolUtils::clearImage(image(), resources->currentNode(), resources->activeSelection())) {
KoToolBase::deleteSelection();
}
}
KisTool::NodePaintAbility KisTool::nodePaintAbility()
{
KisNodeSP node = currentNode();
if (!node) {
return NodePaintAbility::UNPAINTABLE;
}
if (node->inherits("KisShapeLayer")) {
return NodePaintAbility::VECTOR;
}
if (node->inherits("KisCloneLayer")) {
return NodePaintAbility::CLONE;
}
if (node->paintDevice()) {
return NodePaintAbility::PAINT;
}
return NodePaintAbility::UNPAINTABLE;
}
QWidget* KisTool::createOptionWidget()
{
d->optionWidget = new QLabel(i18n("No options"));
d->optionWidget->setObjectName("SpecialSpacer");
return d->optionWidget;
}
#define NEAR_VAL -1000.0
#define FAR_VAL 1000.0
#define PROGRAM_VERTEX_ATTRIBUTE 0
void KisTool::paintToolOutline(QPainter* painter, const QPainterPath &path)
{
KisOpenGLCanvas2 *canvasWidget = dynamic_cast(canvas()->canvasWidget());
if (canvasWidget) {
painter->beginNativePainting();
canvasWidget->paintToolOutline(path);
painter->endNativePainting();
}
else {
painter->save();
painter->setCompositionMode(QPainter::RasterOp_SourceXorDestination);
painter->setPen(QColor(128, 255, 128));
painter->drawPath(path);
painter->restore();
}
}
void KisTool::resetCursorStyle()
{
useCursor(d->cursor);
}
bool KisTool::overrideCursorIfNotEditable()
{
// override cursor for canvas iff this tool is active
// and we can't paint on the active layer
if (isActive()) {
KisNodeSP node = currentNode();
if (node && !node->isEditable()) {
canvas()->setCursor(Qt::ForbiddenCursor);
return true;
}
}
return false;
}
bool KisTool::blockUntilOperationsFinished()
{
KisCanvas2 * kiscanvas = static_cast(canvas());
KisViewManager* viewManager = kiscanvas->viewManager();
return viewManager->blockUntilOperationsFinished(image());
}
void KisTool::blockUntilOperationsFinishedForced()
{
KisCanvas2 * kiscanvas = static_cast(canvas());
KisViewManager* viewManager = kiscanvas->viewManager();
viewManager->blockUntilOperationsFinishedForced(image());
}
bool KisTool::isActive() const
{
return d->m_isActive;
}
bool KisTool::nodeEditable()
{
KisNodeSP node = currentNode();
if (!node) {
return false;
}
bool blockedNoIndirectPainting = false;
const bool presetUsesIndirectPainting =
!currentPaintOpPreset()->settings()->paintIncremental();
if (!presetUsesIndirectPainting) {
const KisIndirectPaintingSupport *indirectPaintingLayer =
dynamic_cast(node.data());
if (indirectPaintingLayer) {
blockedNoIndirectPainting = !indirectPaintingLayer->supportsNonIndirectPainting();
}
}
bool nodeEditable = node->isEditable() && !blockedNoIndirectPainting;
if (!nodeEditable) {
KisCanvas2 * kiscanvas = static_cast(canvas());
QString message;
if (!node->visible() && node->userLocked()) {
message = i18n("Layer is locked and invisible.");
} else if (node->userLocked()) {
message = i18n("Layer is locked.");
} else if(!node->visible()) {
message = i18n("Layer is invisible.");
} else if (blockedNoIndirectPainting) {
message = i18n("Layer can be painted in Wash Mode only.");
} else {
message = i18n("Group not editable.");
}
kiscanvas->viewManager()->showFloatingMessage(message, KisIconUtils::loadIcon("object-locked"));
}
return nodeEditable;
}
bool KisTool::selectionEditable()
{
KisCanvas2 * kisCanvas = static_cast(canvas());
KisViewManager * view = kisCanvas->viewManager();
bool editable = view->selectionEditable();
if (!editable) {
KisCanvas2 * kiscanvas = static_cast(canvas());
kiscanvas->viewManager()->showFloatingMessage(i18n("Local selection is locked."), KisIconUtils::loadIcon("object-locked"));
}
return editable;
}
void KisTool::listenToModifiers(bool listen)
{
Q_UNUSED(listen);
}
bool KisTool::listeningToModifiers()
{
return false;
}
diff --git a/libs/ui/tool/kis_tool_freehand.cc b/libs/ui/tool/kis_tool_freehand.cc
index 44bfc63df7..284869d934 100644
--- a/libs/ui/tool/kis_tool_freehand.cc
+++ b/libs/ui/tool/kis_tool_freehand.cc
@@ -1,461 +1,455 @@
/*
* 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 "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(true).disableTouchOnCanvas()); // Disallow mouse events from finger presses unless enabled
m_infoBuilder = new KisToolFreehandPaintingInformationBuilder(this);
m_helper = new KisToolFreehandHelper(m_infoBuilder, transactionText);
connect(m_helper, SIGNAL(requestExplicitUpdateOutline()), SLOT(explicitUpdateOutline()));
}
KisToolFreehand::~KisToolFreehand()
{
delete m_helper;
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(true);
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;
}
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) {
endStroke();
setMode(KisTool::HOVER_MODE);
}
KisToolPaint::deactivate();
}
void KisToolFreehand::initStroke(KoPointerEvent *event)
{
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()
{
m_helper->endPaint();
bool paintOpIgnoredEvent = currentPaintOpPreset()->settings()->mouseReleaseEvent();
Q_UNUSED(paintOpIgnoredEvent);
}
bool KisToolFreehand::primaryActionSupportsHiResEvents() const
{
return true;
}
void KisToolFreehand::beginPrimaryAction(KoPointerEvent *event)
{
// FIXME: workaround for the Duplicate Op
tryPickByPaintOp(event, PickFgImage);
requestUpdateOutline(event->point, event);
NodePaintAbility paintability = nodePaintAbility();
// XXX: move this to KisTool and make it work properly for clone layers: for clone layers, the shape paint tools don't work either
if (!nodeEditable() || paintability != PAINT) {
if (paintability == KisToolPaint::VECTOR || paintability == KisToolPaint::CLONE){
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);
endStroke();
if (m_assistant && static_cast(canvas())->paintingAssistantsDecoration()) {
static_cast(canvas())->paintingAssistantsDecoration()->endStroke();
}
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()->canvasResourceProvider()->perspectiveGrids()) {
if (grid && grid->contains(pos)) {
perspective = grid->distance(pos);
break;
}
}
if (!currentPaintOpPreset()) {
return false;
}
bool paintOpIgnoredEvent = currentPaintOpPreset()->settings()->
mousePressEvent(KisPaintInformation(convertToPixelCoord(event->point),
m_infoBuilder->pressureToCurve(event->pressure()),
event->xTilt(), event->yTilt(),
event->rotation(),
event->tangentialPressure(),
perspective, 0, 0),
event->modifiers(),
currentNode());
// DuplicateOP during the picking of new source point (origin)
// is the only paintop that returns "false" here
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 (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(true).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 (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);
}
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()->canvasResourceProvider()->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/libs/ui/tool/kis_tool_freehand_helper.cpp b/libs/ui/tool/kis_tool_freehand_helper.cpp
index 7bcae1f1b8..42e12ffda4 100644
--- a/libs/ui/tool/kis_tool_freehand_helper.cpp
+++ b/libs/ui/tool/kis_tool_freehand_helper.cpp
@@ -1,1003 +1,975 @@
/*
* Copyright (c) 2011 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_tool_freehand_helper.h"
#include
#include
#include
#include
#include
#include "kis_algebra_2d.h"
#include "kis_distance_information.h"
#include "kis_painting_information_builder.h"
#include "kis_image.h"
#include "kis_painter.h"
#include
#include
#include "kis_update_time_monitor.h"
#include "kis_stabilized_events_sampler.h"
#include "KisStabilizerDelayedPaintHelper.h"
#include "kis_config.h"
#include "kis_random_source.h"
#include "KisPerStrokeRandomSource.h"
#include "strokes/freehand_stroke.h"
#include "strokes/KisFreehandStrokeInfo.h"
#include
//#define DEBUG_BEZIER_CURVES
// Factor by which to scale the airbrush timer's interval, relative to the actual airbrushing rate.
// Setting this less than 1 makes the timer-generated pseudo-events happen faster than the desired
// airbrush rate, which can improve responsiveness.
const qreal AIRBRUSH_INTERVAL_FACTOR = 0.5;
// The amount of time, in milliseconds, to allow between updates of the spacing information. Only
// used when spacing updates between dabs are enabled.
const qreal SPACING_UPDATE_INTERVAL = 50.0;
// The amount of time, in milliseconds, to allow between updates of the timing information. Only
// used when airbrushing.
const qreal TIMING_UPDATE_INTERVAL = 50.0;
struct KisToolFreehandHelper::Private
{
KisPaintingInformationBuilder *infoBuilder;
KisStrokesFacade *strokesFacade;
KUndo2MagicString transactionText;
bool haveTangent;
QPointF previousTangent;
bool hasPaintAtLeastOnce;
QTime strokeTime;
QTimer strokeTimeoutTimer;
QVector strokeInfos;
KisResourcesSnapshotSP resources;
KisStrokeId strokeId;
KisPaintInformation previousPaintInformation;
KisPaintInformation olderPaintInformation;
KisSmoothingOptionsSP smoothingOptions;
// fake random sources for hovering outline *only*
KisRandomSourceSP fakeDabRandomSource;
KisPerStrokeRandomSourceSP fakeStrokeRandomSource;
// Timer used to generate paint updates periodically even without input events. This is only
// used for paintops that depend on timely updates even when the cursor is not moving, e.g. for
// airbrushing effects.
QTimer airbrushingTimer;
QList history;
QList distanceHistory;
// Keeps track of past cursor positions. This is used to determine the drawing angle when
// drawing the brush outline or starting a stroke.
KisPaintOpUtils::PositionHistory lastCursorPos;
// Stabilizer data
bool usingStabilizer;
QQueue stabilizerDeque;
QTimer stabilizerPollTimer;
KisStabilizedEventsSampler stabilizedSampler;
KisStabilizerDelayedPaintHelper stabilizerDelayedPaintHelper;
QTimer asynchronousUpdatesThresholdTimer;
- int canvasRotation;
- bool canvasMirroredH;
-
qreal effectiveSmoothnessDistance() const;
};
KisToolFreehandHelper::KisToolFreehandHelper(KisPaintingInformationBuilder *infoBuilder,
const KUndo2MagicString &transactionText,
KisSmoothingOptions *smoothingOptions)
: m_d(new Private())
{
m_d->infoBuilder = infoBuilder;
m_d->transactionText = transactionText;
m_d->smoothingOptions = KisSmoothingOptionsSP(
smoothingOptions ? smoothingOptions : new KisSmoothingOptions());
- m_d->canvasRotation = 0;
m_d->fakeDabRandomSource = new KisRandomSource();
m_d->fakeStrokeRandomSource = new KisPerStrokeRandomSource();
m_d->strokeTimeoutTimer.setSingleShot(true);
connect(&m_d->strokeTimeoutTimer, SIGNAL(timeout()), SLOT(finishStroke()));
connect(&m_d->airbrushingTimer, SIGNAL(timeout()), SLOT(doAirbrushing()));
connect(&m_d->asynchronousUpdatesThresholdTimer, SIGNAL(timeout()), SLOT(doAsynchronousUpdate()));
connect(&m_d->stabilizerPollTimer, SIGNAL(timeout()), SLOT(stabilizerPollAndPaint()));
connect(m_d->smoothingOptions.data(), SIGNAL(sigSmoothingTypeChanged()), SLOT(slotSmoothingTypeChanged()));
m_d->stabilizerDelayedPaintHelper.setPaintLineCallback(
[this](const KisPaintInformation &pi1, const KisPaintInformation &pi2) {
paintLine(pi1, pi2);
});
m_d->stabilizerDelayedPaintHelper.setUpdateOutlineCallback(
[this]() {
emit requestExplicitUpdateOutline();
});
}
KisToolFreehandHelper::~KisToolFreehandHelper()
{
delete m_d;
}
void KisToolFreehandHelper::setSmoothness(KisSmoothingOptionsSP smoothingOptions)
{
m_d->smoothingOptions = smoothingOptions;
}
KisSmoothingOptionsSP KisToolFreehandHelper::smoothingOptions() const
{
return m_d->smoothingOptions;
}
QPainterPath KisToolFreehandHelper::paintOpOutline(const QPointF &savedCursorPos,
const KoPointerEvent *event,
const KisPaintOpSettingsSP globalSettings,
KisPaintOpSettings::OutlineMode mode) const
{
KisPaintOpSettingsSP settings = globalSettings;
KisPaintInformation info = m_d->infoBuilder->hover(savedCursorPos, event);
QPointF prevPoint = m_d->lastCursorPos.pushThroughHistory(savedCursorPos);
qreal startAngle = KisAlgebra2D::directionBetweenPoints(prevPoint, savedCursorPos, 0);
- info.setCanvasRotation(m_d->canvasRotation);
- info.setCanvasHorizontalMirrorState( m_d->canvasMirroredH );
KisDistanceInformation distanceInfo(prevPoint, startAngle);
if (!m_d->strokeInfos.isEmpty()) {
settings = m_d->resources->currentPaintOpPreset()->settings();
if (m_d->stabilizerDelayedPaintHelper.running() &&
m_d->stabilizerDelayedPaintHelper.hasLastPaintInformation()) {
info = m_d->stabilizerDelayedPaintHelper.lastPaintInformation();
} else {
info = m_d->previousPaintInformation;
}
/**
* When LoD mode is active it may happen that the helper has
* already started a stroke, but it painted noting, because
* all the work is being calculated by the scaled-down LodN
* stroke. So at first we try to fetch the data from the lodN
* stroke ("buddy") and then check if there is at least
* something has been painted with this distance information
* object.
*/
KisDistanceInformation *buddyDistance =
m_d->strokeInfos.first()->buddyDragDistance();
if (buddyDistance) {
/**
* Tiny hack alert: here we fetch the distance information
* directly from the LodN stroke. Ideally, we should
* upscale its data, but here we just override it with our
* local copy of the coordinates.
*/
distanceInfo = *buddyDistance;
distanceInfo.overrideLastValues(prevPoint, startAngle);
} else if (m_d->strokeInfos.first()->dragDistance->isStarted()) {
distanceInfo = *m_d->strokeInfos.first()->dragDistance;
}
}
KisPaintInformation::DistanceInformationRegistrar registrar =
info.registerDistanceInformation(&distanceInfo);
info.setRandomSource(m_d->fakeDabRandomSource);
info.setPerStrokeRandomSource(m_d->fakeStrokeRandomSource);
QPainterPath outline = settings->brushOutline(info, mode);
if (m_d->resources &&
m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER &&
m_d->smoothingOptions->useDelayDistance()) {
const qreal R = m_d->smoothingOptions->delayDistance() /
m_d->resources->effectiveZoom();
outline.addEllipse(info.pos(), R, R);
}
return outline;
}
void KisToolFreehandHelper::cursorMoved(const QPointF &cursorPos)
{
m_d->lastCursorPos.pushThroughHistory(cursorPos);
}
void KisToolFreehandHelper::initPaint(KoPointerEvent *event,
const QPointF &pixelCoords,
KoCanvasResourceProvider *resourceManager,
KisImageWSP image, KisNodeSP currentNode,
KisStrokesFacade *strokesFacade,
KisNodeSP overrideNode,
KisDefaultBoundsBaseSP bounds)
{
QPointF prevPoint = m_d->lastCursorPos.pushThroughHistory(pixelCoords);
m_d->strokeTime.start();
KisPaintInformation pi =
m_d->infoBuilder->startStroke(event, elapsedStrokeTime(), resourceManager);
qreal startAngle = KisAlgebra2D::directionBetweenPoints(prevPoint, pixelCoords, 0.0);
initPaintImpl(startAngle,
pi,
resourceManager,
image,
currentNode,
strokesFacade,
overrideNode,
bounds);
}
bool KisToolFreehandHelper::isRunning() const
{
return m_d->strokeId;
}
void KisToolFreehandHelper::initPaintImpl(qreal startAngle,
const KisPaintInformation &pi,
KoCanvasResourceProvider *resourceManager,
KisImageWSP image,
KisNodeSP currentNode,
KisStrokesFacade *strokesFacade,
KisNodeSP overrideNode,
KisDefaultBoundsBaseSP bounds)
{
m_d->strokesFacade = strokesFacade;
m_d->haveTangent = false;
m_d->previousTangent = QPointF();
m_d->hasPaintAtLeastOnce = false;
m_d->previousPaintInformation = pi;
m_d->resources = new KisResourcesSnapshot(image,
currentNode,
resourceManager,
bounds);
if(overrideNode) {
m_d->resources->setCurrentNode(overrideNode);
}
const bool airbrushing = m_d->resources->needsAirbrushing();
const bool useSpacingUpdates = m_d->resources->needsSpacingUpdates();
KisDistanceInitInfo startDistInfo(m_d->previousPaintInformation.pos(),
startAngle,
useSpacingUpdates ? SPACING_UPDATE_INTERVAL : LONG_TIME,
airbrushing ? TIMING_UPDATE_INTERVAL : LONG_TIME,
0);
KisDistanceInformation startDist = startDistInfo.makeDistInfo();
createPainters(m_d->strokeInfos,
startDist);
KisStrokeStrategy *stroke =
new FreehandStrokeStrategy(m_d->resources, m_d->strokeInfos, m_d->transactionText);
m_d->strokeId = m_d->strokesFacade->startStroke(stroke);
m_d->history.clear();
m_d->distanceHistory.clear();
if (airbrushing) {
m_d->airbrushingTimer.setInterval(computeAirbrushTimerInterval());
m_d->airbrushingTimer.start();
} else if (m_d->resources->presetNeedsAsynchronousUpdates()) {
m_d->asynchronousUpdatesThresholdTimer.setInterval(80 /* msec */);
m_d->asynchronousUpdatesThresholdTimer.start();
}
if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
stabilizerStart(m_d->previousPaintInformation);
}
// If airbrushing, paint an initial dab immediately. This is a workaround for an issue where
// some paintops (Dyna, Particle, Sketch) might never initialize their spacing/timing
// information until paintAt is called.
if (airbrushing) {
paintAt(pi);
}
}
void KisToolFreehandHelper::paintBezierSegment(KisPaintInformation pi1, KisPaintInformation pi2,
QPointF tangent1, QPointF tangent2)
{
if (tangent1.isNull() || tangent2.isNull()) return;
const qreal maxSanePoint = 1e6;
QPointF controlTarget1;
QPointF controlTarget2;
// Shows the direction in which control points go
QPointF controlDirection1 = pi1.pos() + tangent1;
QPointF controlDirection2 = pi2.pos() - tangent2;
// Lines in the direction of the control points
QLineF line1(pi1.pos(), controlDirection1);
QLineF line2(pi2.pos(), controlDirection2);
// Lines to check whether the control points lay on the opposite
// side of the line
QLineF line3(controlDirection1, controlDirection2);
QLineF line4(pi1.pos(), pi2.pos());
QPointF intersection;
if (line3.intersect(line4, &intersection) == QLineF::BoundedIntersection) {
qreal controlLength = line4.length() / 2;
line1.setLength(controlLength);
line2.setLength(controlLength);
controlTarget1 = line1.p2();
controlTarget2 = line2.p2();
} else {
QLineF::IntersectType type = line1.intersect(line2, &intersection);
if (type == QLineF::NoIntersection ||
intersection.manhattanLength() > maxSanePoint) {
intersection = 0.5 * (pi1.pos() + pi2.pos());
// dbgKrita << "WARNING: there is no intersection point "
// << "in the basic smoothing algorithms";
}
controlTarget1 = intersection;
controlTarget2 = intersection;
}
// shows how near to the controlTarget the value raises
qreal coeff = 0.8;
qreal velocity1 = QLineF(QPointF(), tangent1).length();
qreal velocity2 = QLineF(QPointF(), tangent2).length();
if (velocity1 == 0.0 || velocity2 == 0.0) {
velocity1 = 1e-6;
velocity2 = 1e-6;
warnKrita << "WARNING: Basic Smoothing: Velocity is Zero! Please report a bug:" << ppVar(velocity1) << ppVar(velocity2);
}
qreal similarity = qMin(velocity1/velocity2, velocity2/velocity1);
// the controls should not differ more than 50%
similarity = qMax(similarity, qreal(0.5));
// when the controls are symmetric, their size should be smaller
// to avoid corner-like curves
coeff *= 1 - qMax(qreal(0.0), similarity - qreal(0.8));
Q_ASSERT(coeff > 0);
QPointF control1;
QPointF control2;
if (velocity1 > velocity2) {
control1 = pi1.pos() * (1.0 - coeff) + coeff * controlTarget1;
coeff *= similarity;
control2 = pi2.pos() * (1.0 - coeff) + coeff * controlTarget2;
} else {
control2 = pi2.pos() * (1.0 - coeff) + coeff * controlTarget2;
coeff *= similarity;
control1 = pi1.pos() * (1.0 - coeff) + coeff * controlTarget1;
}
paintBezierCurve(pi1,
control1,
control2,
pi2);
}
qreal KisToolFreehandHelper::Private::effectiveSmoothnessDistance() const
{
const qreal effectiveSmoothnessDistance =
!smoothingOptions->useScalableDistance() ?
smoothingOptions->smoothnessDistance() :
smoothingOptions->smoothnessDistance() / resources->effectiveZoom();
return effectiveSmoothnessDistance;
}
void KisToolFreehandHelper::paintEvent(KoPointerEvent *event)
{
KisPaintInformation info =
m_d->infoBuilder->continueStroke(event,
elapsedStrokeTime());
- info.setCanvasRotation( m_d->canvasRotation );
- info.setCanvasHorizontalMirrorState( m_d->canvasMirroredH );
-
KisUpdateTimeMonitor::instance()->reportMouseMove(info.pos());
paint(info);
}
void KisToolFreehandHelper::paint(KisPaintInformation &info)
{
/**
* Smooth the coordinates out using the history and the
* distance. This is a heavily modified version of an algo used in
* Gimp and described in https://bugs.kde.org/show_bug.cgi?id=281267 and
* http://www24.atwiki.jp/sigetch_2007/pages/17.html. The main
* differences are:
*
* 1) It uses 'distance' instead of 'velocity', since time
* measurements are too unstable in realworld environment
*
* 2) There is no 'Quality' parameter, since the number of samples
* is calculated automatically
*
* 3) 'Tail Aggressiveness' is used for controlling the end of the
* stroke
*
* 4) The formila is a little bit different: 'Distance' parameter
* stands for $3 \Sigma$
*/
if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::WEIGHTED_SMOOTHING
&& m_d->smoothingOptions->smoothnessDistance() > 0.0) {
{ // initialize current distance
QPointF prevPos;
if (!m_d->history.isEmpty()) {
const KisPaintInformation &prevPi = m_d->history.last();
prevPos = prevPi.pos();
} else {
prevPos = m_d->previousPaintInformation.pos();
}
qreal currentDistance = QVector2D(info.pos() - prevPos).length();
m_d->distanceHistory.append(currentDistance);
}
m_d->history.append(info);
qreal x = 0.0;
qreal y = 0.0;
if (m_d->history.size() > 3) {
const qreal sigma = m_d->effectiveSmoothnessDistance() / 3.0; // '3.0' for (3 * sigma) range
qreal gaussianWeight = 1 / (sqrt(2 * M_PI) * sigma);
qreal gaussianWeight2 = sigma * sigma;
qreal distanceSum = 0.0;
qreal scaleSum = 0.0;
qreal pressure = 0.0;
qreal baseRate = 0.0;
Q_ASSERT(m_d->history.size() == m_d->distanceHistory.size());
for (int i = m_d->history.size() - 1; i >= 0; i--) {
qreal rate = 0.0;
const KisPaintInformation nextInfo = m_d->history.at(i);
double distance = m_d->distanceHistory.at(i);
Q_ASSERT(distance >= 0.0);
qreal pressureGrad = 0.0;
if (i < m_d->history.size() - 1) {
pressureGrad = nextInfo.pressure() - m_d->history.at(i + 1).pressure();
const qreal tailAgressiveness = 40.0 * m_d->smoothingOptions->tailAggressiveness();
if (pressureGrad > 0.0 ) {
pressureGrad *= tailAgressiveness * (1.0 - nextInfo.pressure());
distance += pressureGrad * 3.0 * sigma; // (3 * sigma) --- holds > 90% of the region
}
}
if (gaussianWeight2 != 0.0) {
distanceSum += distance;
rate = gaussianWeight * exp(-distanceSum * distanceSum / (2 * gaussianWeight2));
}
if (m_d->history.size() - i == 1) {
baseRate = rate;
} else if (baseRate / rate > 100) {
break;
}
scaleSum += rate;
x += rate * nextInfo.pos().x();
y += rate * nextInfo.pos().y();
if (m_d->smoothingOptions->smoothPressure()) {
pressure += rate * nextInfo.pressure();
}
}
if (scaleSum != 0.0) {
x /= scaleSum;
y /= scaleSum;
if (m_d->smoothingOptions->smoothPressure()) {
pressure /= scaleSum;
}
}
if ((x != 0.0 && y != 0.0) || (x == info.pos().x() && y == info.pos().y())) {
info.setPos(QPointF(x, y));
if (m_d->smoothingOptions->smoothPressure()) {
info.setPressure(pressure);
}
m_d->history.last() = info;
}
}
}
if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::SIMPLE_SMOOTHING
|| m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::WEIGHTED_SMOOTHING)
{
// Now paint between the coordinates, using the bezier curve interpolation
if (!m_d->haveTangent) {
m_d->haveTangent = true;
m_d->previousTangent =
(info.pos() - m_d->previousPaintInformation.pos()) /
qMax(qreal(1.0), info.currentTime() - m_d->previousPaintInformation.currentTime());
} else {
QPointF newTangent = (info.pos() - m_d->olderPaintInformation.pos()) /
qMax(qreal(1.0), info.currentTime() - m_d->olderPaintInformation.currentTime());
if (newTangent.isNull() || m_d->previousTangent.isNull())
{
paintLine(m_d->previousPaintInformation, info);
} else {
paintBezierSegment(m_d->olderPaintInformation, m_d->previousPaintInformation,
m_d->previousTangent, newTangent);
}
m_d->previousTangent = newTangent;
}
m_d->olderPaintInformation = m_d->previousPaintInformation;
// Enable stroke timeout only when not airbrushing.
if (!m_d->airbrushingTimer.isActive()) {
m_d->strokeTimeoutTimer.start(100);
}
}
else if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::NO_SMOOTHING){
paintLine(m_d->previousPaintInformation, info);
}
if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
m_d->stabilizedSampler.addEvent(info);
if (m_d->stabilizerDelayedPaintHelper.running()) {
// Paint here so we don't have to rely on the timer
// This is just a tricky source for a relatively stable 7ms "timer"
m_d->stabilizerDelayedPaintHelper.paintSome();
}
} else {
m_d->previousPaintInformation = info;
}
if(m_d->airbrushingTimer.isActive()) {
m_d->airbrushingTimer.start();
}
}
void KisToolFreehandHelper::endPaint()
{
if (!m_d->hasPaintAtLeastOnce) {
paintAt(m_d->previousPaintInformation);
} else if (m_d->smoothingOptions->smoothingType() != KisSmoothingOptions::NO_SMOOTHING) {
finishStroke();
}
m_d->strokeTimeoutTimer.stop();
if(m_d->airbrushingTimer.isActive()) {
m_d->airbrushingTimer.stop();
}
if (m_d->asynchronousUpdatesThresholdTimer.isActive()) {
m_d->asynchronousUpdatesThresholdTimer.stop();
}
if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
stabilizerEnd();
}
/**
* There might be some timer events still pending, so
* we should cancel them. Use this flag for the purpose.
* Please note that we are not in MT here, so no mutex
* is needed
*/
m_d->strokeInfos.clear();
// last update to complete rendering if there is still something pending
doAsynchronousUpdate(true);
m_d->strokesFacade->endStroke(m_d->strokeId);
m_d->strokeId.clear();
}
void KisToolFreehandHelper::cancelPaint()
{
if (!m_d->strokeId) return;
m_d->strokeTimeoutTimer.stop();
if (m_d->airbrushingTimer.isActive()) {
m_d->airbrushingTimer.stop();
}
if (m_d->asynchronousUpdatesThresholdTimer.isActive()) {
m_d->asynchronousUpdatesThresholdTimer.stop();
}
if (m_d->stabilizerPollTimer.isActive()) {
m_d->stabilizerPollTimer.stop();
}
if (m_d->stabilizerDelayedPaintHelper.running()) {
m_d->stabilizerDelayedPaintHelper.cancel();
}
// see a comment in endPaint()
m_d->strokeInfos.clear();
m_d->strokesFacade->cancelStroke(m_d->strokeId);
m_d->strokeId.clear();
}
int KisToolFreehandHelper::elapsedStrokeTime() const
{
return m_d->strokeTime.elapsed();
}
void KisToolFreehandHelper::stabilizerStart(KisPaintInformation firstPaintInfo)
{
m_d->usingStabilizer = true;
// FIXME: Ugly hack, this is no a "distance" in any way
int sampleSize = qRound(m_d->effectiveSmoothnessDistance());
sampleSize = qMax(3, sampleSize);
// Fill the deque with the current value repeated until filling the sample
m_d->stabilizerDeque.clear();
for (int i = sampleSize; i > 0; i--) {
m_d->stabilizerDeque.enqueue(firstPaintInfo);
}
// Poll and draw regularly
KisConfig cfg(true);
int stabilizerSampleSize = cfg.stabilizerSampleSize();
m_d->stabilizerPollTimer.setInterval(stabilizerSampleSize);
m_d->stabilizerPollTimer.start();
bool delayedPaintEnabled = cfg.stabilizerDelayedPaint();
if (delayedPaintEnabled) {
m_d->stabilizerDelayedPaintHelper.start(firstPaintInfo);
}
m_d->stabilizedSampler.clear();
m_d->stabilizedSampler.addEvent(firstPaintInfo);
}
KisPaintInformation
KisToolFreehandHelper::getStabilizedPaintInfo(const QQueue &queue,
const KisPaintInformation &lastPaintInfo)
{
KisPaintInformation result(lastPaintInfo.pos(),
lastPaintInfo.pressure(),
lastPaintInfo.xTilt(),
lastPaintInfo.yTilt(),
lastPaintInfo.rotation(),
lastPaintInfo.tangentialPressure(),
lastPaintInfo.perspective(),
elapsedStrokeTime(),
lastPaintInfo.drawingSpeed());
if (queue.size() > 1) {
QQueue::const_iterator it = queue.constBegin();
QQueue::const_iterator end = queue.constEnd();
/**
* The first point is going to be overridden by lastPaintInfo, skip it.
*/
it++;
int i = 2;
if (m_d->smoothingOptions->stabilizeSensors()) {
while (it != end) {
qreal k = qreal(i - 1) / i; // coeff for uniform averaging
result.KisPaintInformation::mixOtherWithoutTime(k, *it);
it++;
i++;
}
} else{
while (it != end) {
qreal k = qreal(i - 1) / i; // coeff for uniform averaging
result.KisPaintInformation::mixOtherOnlyPosition(k, *it);
it++;
i++;
}
}
}
return result;
}
void KisToolFreehandHelper::stabilizerPollAndPaint()
{
KisStabilizedEventsSampler::iterator it;
KisStabilizedEventsSampler::iterator end;
std::tie(it, end) = m_d->stabilizedSampler.range();
QVector delayedPaintTodoItems;
for (; it != end; ++it) {
KisPaintInformation sampledInfo = *it;
bool canPaint = true;
if (m_d->smoothingOptions->useDelayDistance()) {
const qreal R = m_d->smoothingOptions->delayDistance() /
m_d->resources->effectiveZoom();
QPointF diff = sampledInfo.pos() - m_d->previousPaintInformation.pos();
qreal dx = sqrt(pow2(diff.x()) + pow2(diff.y()));
if (!(dx > R)) {
if (m_d->resources->needsAirbrushing()) {
sampledInfo.setPos(m_d->previousPaintInformation.pos());
}
else {
canPaint = false;
}
}
}
if (canPaint) {
KisPaintInformation newInfo = getStabilizedPaintInfo(m_d->stabilizerDeque, sampledInfo);
if (m_d->stabilizerDelayedPaintHelper.running()) {
delayedPaintTodoItems.append(newInfo);
} else {
paintLine(m_d->previousPaintInformation, newInfo);
}
m_d->previousPaintInformation = newInfo;
// Push the new entry through the queue
m_d->stabilizerDeque.dequeue();
m_d->stabilizerDeque.enqueue(sampledInfo);
} else if (m_d->stabilizerDeque.head().pos() != m_d->previousPaintInformation.pos()) {
QQueue::iterator it = m_d->stabilizerDeque.begin();
QQueue::iterator end = m_d->stabilizerDeque.end();
while (it != end) {
*it = m_d->previousPaintInformation;
++it;
}
}
}
m_d->stabilizedSampler.clear();
if (m_d->stabilizerDelayedPaintHelper.running()) {
m_d->stabilizerDelayedPaintHelper.update(delayedPaintTodoItems);
} else {
emit requestExplicitUpdateOutline();
}
}
void KisToolFreehandHelper::stabilizerEnd()
{
// Stop the timer
m_d->stabilizerPollTimer.stop();
// Finish the line
if (m_d->smoothingOptions->finishStabilizedCurve()) {
// Process all the existing events first
stabilizerPollAndPaint();
// Draw the finish line with pending events and a time override
m_d->stabilizedSampler.addFinishingEvent(m_d->stabilizerDeque.size());
stabilizerPollAndPaint();
}
if (m_d->stabilizerDelayedPaintHelper.running()) {
m_d->stabilizerDelayedPaintHelper.end();
}
m_d->usingStabilizer = false;
}
void KisToolFreehandHelper::slotSmoothingTypeChanged()
{
if (!isRunning()) {
return;
}
KisSmoothingOptions::SmoothingType currentSmoothingType =
m_d->smoothingOptions->smoothingType();
if (m_d->usingStabilizer
&& (currentSmoothingType != KisSmoothingOptions::STABILIZER)) {
stabilizerEnd();
} else if (!m_d->usingStabilizer
&& (currentSmoothingType == KisSmoothingOptions::STABILIZER)) {
stabilizerStart(m_d->previousPaintInformation);
}
}
void KisToolFreehandHelper::finishStroke()
{
if (m_d->haveTangent) {
m_d->haveTangent = false;
QPointF newTangent = (m_d->previousPaintInformation.pos() - m_d->olderPaintInformation.pos()) /
(m_d->previousPaintInformation.currentTime() - m_d->olderPaintInformation.currentTime());
paintBezierSegment(m_d->olderPaintInformation,
m_d->previousPaintInformation,
m_d->previousTangent,
newTangent);
}
}
void KisToolFreehandHelper::doAirbrushing()
{
// Check that the stroke hasn't ended.
if (!m_d->strokeInfos.isEmpty()) {
// Add a new painting update at a point identical to the previous one, except for the time
// and speed information.
const KisPaintInformation &prevPaint = m_d->previousPaintInformation;
KisPaintInformation nextPaint(prevPaint.pos(),
prevPaint.pressure(),
prevPaint.xTilt(),
prevPaint.yTilt(),
prevPaint.rotation(),
prevPaint.tangentialPressure(),
prevPaint.perspective(),
elapsedStrokeTime(),
0.0);
paint(nextPaint);
}
}
void KisToolFreehandHelper::doAsynchronousUpdate(bool forceUpdate)
{
m_d->strokesFacade->addJob(m_d->strokeId,
new FreehandStrokeStrategy::UpdateData(forceUpdate));
}
int KisToolFreehandHelper::computeAirbrushTimerInterval() const
{
qreal realInterval = m_d->resources->airbrushingInterval() * AIRBRUSH_INTERVAL_FACTOR;
return qMax(1, qFloor(realInterval));
}
void KisToolFreehandHelper::paintAt(int strokeInfoId,
const KisPaintInformation &pi)
{
m_d->hasPaintAtLeastOnce = true;
m_d->strokesFacade->addJob(m_d->strokeId,
new FreehandStrokeStrategy::Data(strokeInfoId, pi));
}
void KisToolFreehandHelper::paintLine(int strokeInfoId,
const KisPaintInformation &pi1,
const KisPaintInformation &pi2)
{
m_d->hasPaintAtLeastOnce = true;
m_d->strokesFacade->addJob(m_d->strokeId,
new FreehandStrokeStrategy::Data(strokeInfoId, pi1, pi2));
}
void KisToolFreehandHelper::paintBezierCurve(int strokeInfoId,
const KisPaintInformation &pi1,
const QPointF &control1,
const QPointF &control2,
const KisPaintInformation &pi2)
{
#ifdef DEBUG_BEZIER_CURVES
KisPaintInformation tpi1;
KisPaintInformation tpi2;
tpi1 = pi1;
tpi2 = pi2;
tpi1.setPressure(0.3);
tpi2.setPressure(0.3);
paintLine(tpi1, tpi2);
tpi1.setPressure(0.6);
tpi2.setPressure(0.3);
tpi1.setPos(pi1.pos());
tpi2.setPos(control1);
paintLine(tpi1, tpi2);
tpi1.setPos(pi2.pos());
tpi2.setPos(control2);
paintLine(tpi1, tpi2);
#endif
m_d->hasPaintAtLeastOnce = true;
m_d->strokesFacade->addJob(m_d->strokeId,
new FreehandStrokeStrategy::Data(strokeInfoId,
pi1, control1, control2, pi2));
}
void KisToolFreehandHelper::createPainters(QVector &strokeInfos,
const KisDistanceInformation &startDist)
{
strokeInfos << new KisFreehandStrokeInfo(startDist);
}
void KisToolFreehandHelper::paintAt(const KisPaintInformation &pi)
{
paintAt(0, pi);
}
void KisToolFreehandHelper::paintLine(const KisPaintInformation &pi1,
const KisPaintInformation &pi2)
{
paintLine(0, pi1, pi2);
}
void KisToolFreehandHelper::paintBezierCurve(const KisPaintInformation &pi1,
const QPointF &control1,
const QPointF &control2,
const KisPaintInformation &pi2)
{
paintBezierCurve(0, pi1, control1, control2, pi2);
}
-
-int KisToolFreehandHelper::canvasRotation()
-{
- return m_d->canvasRotation;
-}
-
-void KisToolFreehandHelper::setCanvasRotation(int rotation)
-{
- m_d->canvasRotation = rotation;
-}
-bool KisToolFreehandHelper::canvasMirroredH()
-{
- return m_d->canvasMirroredH;
-}
-
-void KisToolFreehandHelper::setCanvasHorizontalMirrorState(bool mirrored)
-{
- m_d->canvasMirroredH = mirrored;
-}
diff --git a/libs/ui/tool/kis_tool_freehand_helper.h b/libs/ui/tool/kis_tool_freehand_helper.h
index 39c3c56b9d..6323952380 100644
--- a/libs/ui/tool/kis_tool_freehand_helper.h
+++ b/libs/ui/tool/kis_tool_freehand_helper.h
@@ -1,166 +1,163 @@
/*
* Copyright (c) 2011 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 __KIS_TOOL_FREEHAND_HELPER_H
#define __KIS_TOOL_FREEHAND_HELPER_H
#include
#include
#include "kis_types.h"
#include "kritaui_export.h"
#include
#include "kis_default_bounds.h"
#include
#include "kis_smoothing_options.h"
#include "kundo2magicstring.h"
class KoPointerEvent;
class KoCanvasResourceProvider;
class KisPaintingInformationBuilder;
class KisStrokesFacade;
class KisPostExecutionUndoAdapter;
class KisPaintOp;
class KisFreehandStrokeInfo;
class KRITAUI_EXPORT KisToolFreehandHelper : public QObject
{
Q_OBJECT
public:
KisToolFreehandHelper(KisPaintingInformationBuilder *infoBuilder,
const KUndo2MagicString &transactionText = KUndo2MagicString(),
KisSmoothingOptions *smoothingOptions = 0);
~KisToolFreehandHelper() override;
void setSmoothness(KisSmoothingOptionsSP smoothingOptions);
KisSmoothingOptionsSP smoothingOptions() const;
bool isRunning() const;
void cursorMoved(const QPointF &cursorPos);
/**
* @param event The event
* @param pixelCoords The position of the KoPointerEvent, in pixel coordinates.
* @param resourceManager The canvas resource manager
* @param image The image
* @param currentNode The current node
* @param strokesFacade The strokes facade
* @param overrideNode The override node
* @param bounds The bounds
*/
void initPaint(KoPointerEvent *event,
const QPointF &pixelCoords,
KoCanvasResourceProvider *resourceManager,
KisImageWSP image,
KisNodeSP currentNode,
KisStrokesFacade *strokesFacade,
KisNodeSP overrideNode = 0,
KisDefaultBoundsBaseSP bounds = 0);
void paintEvent(KoPointerEvent *event);
void endPaint();
QPainterPath paintOpOutline(const QPointF &savedCursorPos,
const KoPointerEvent *event,
const KisPaintOpSettingsSP globalSettings,
KisPaintOpSettings::OutlineMode mode) const;
- int canvasRotation();
- void setCanvasRotation(int rotation = 0);
- bool canvasMirroredH();
- void setCanvasHorizontalMirrorState (bool mirrored = false);
+
Q_SIGNALS:
/**
* The signal is emitted when the outline should be updated
* explicitly by the tool. Used by Stabilizer option, because it
* paints on internal timer events instead of the on every paint()
* event
*/
void requestExplicitUpdateOutline();
protected:
void cancelPaint();
int elapsedStrokeTime() const;
void initPaintImpl(qreal startAngle,
const KisPaintInformation &pi,
KoCanvasResourceProvider *resourceManager,
KisImageWSP image,
KisNodeSP node,
KisStrokesFacade *strokesFacade,
KisNodeSP overrideNode = 0,
KisDefaultBoundsBaseSP bounds = 0);
protected:
virtual void createPainters(QVector &strokeInfos,
const KisDistanceInformation &startDist);
// lo-level methods for painting primitives
void paintAt(int strokeInfoId, const KisPaintInformation &pi);
void paintLine(int strokeInfoId,
const KisPaintInformation &pi1,
const KisPaintInformation &pi2);
void paintBezierCurve(int strokeInfoId,
const KisPaintInformation &pi1,
const QPointF &control1,
const QPointF &control2,
const KisPaintInformation &pi2);
// hi-level methods for painting primitives
virtual void paintAt(const KisPaintInformation &pi);
virtual void paintLine(const KisPaintInformation &pi1,
const KisPaintInformation &pi2);
virtual void paintBezierCurve(const KisPaintInformation &pi1,
const QPointF &control1,
const QPointF &control2,
const KisPaintInformation &pi2);
private:
void paint(KisPaintInformation &info);
void paintBezierSegment(KisPaintInformation pi1, KisPaintInformation pi2,
QPointF tangent1, QPointF tangent2);
void stabilizerStart(KisPaintInformation firstPaintInfo);
void stabilizerEnd();
KisPaintInformation getStabilizedPaintInfo(const QQueue &queue,
const KisPaintInformation &lastPaintInfo);
int computeAirbrushTimerInterval() const;
private Q_SLOTS:
void finishStroke();
void doAirbrushing();
void doAsynchronousUpdate(bool forceUpdate = false);
void stabilizerPollAndPaint();
void slotSmoothingTypeChanged();
private:
struct Private;
Private * const m_d;
};
#endif /* __KIS_TOOL_FREEHAND_HELPER_H */
diff --git a/libs/ui/tool/kis_tool_multihand_helper.cpp b/libs/ui/tool/kis_tool_multihand_helper.cpp
index f34368f99f..b99c713be0 100644
--- a/libs/ui/tool/kis_tool_multihand_helper.cpp
+++ b/libs/ui/tool/kis_tool_multihand_helper.cpp
@@ -1,154 +1,134 @@
/*
* Copyright (c) 2011 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_tool_multihand_helper.h"
#include
#include "kis_painter.h"
#include
+#include "kis_algebra_2d.h"
struct KisToolMultihandHelper::Private
{
QVector transformations;
};
KisToolMultihandHelper::KisToolMultihandHelper(KisPaintingInformationBuilder *infoBuilder,
const KUndo2MagicString &transactionText)
: KisToolFreehandHelper(infoBuilder, transactionText)
, d(new Private)
{
}
KisToolMultihandHelper::~KisToolMultihandHelper()
{
delete d;
}
void KisToolMultihandHelper::setupTransformations(const QVector &transformations)
{
d->transformations = transformations;
}
void KisToolMultihandHelper::createPainters(QVector &strokeInfos,
const KisDistanceInformation &startDist)
{
for (int i = 0; i < d->transformations.size(); i++) {
const QTransform &transform = d->transformations[i];
KisDistanceInitInfo __startDistInfo(transform.map(startDist.lastPosition()),
startDist.lastDrawingAngle(),
startDist.getSpacingInterval(),
startDist.getTimingUpdateInterval(),
0);
KisDistanceInformation __startDist = __startDistInfo.makeDistInfo();
strokeInfos << new KisFreehandStrokeInfo(__startDist);
}
}
+void adjustPointInformationRotation(KisPaintInformation &pi, const QTransform &t)
+{
+ KisAlgebra2D::DecomposedMatix d(t);
+
+ qreal rotation = d.angle;
+ const bool mirrorX = KisAlgebra2D::signPZ(d.scaleX) < 0;
+ const bool mirrorY = KisAlgebra2D::signPZ(d.scaleY) < 0;
+
+ pi.setCanvasMirroredH(pi.canvasMirroredH() ^ mirrorX);
+ pi.setCanvasMirroredV(pi.canvasMirroredV() ^ mirrorY);
+
+ if (pi.canvasMirroredH()!= pi.canvasMirroredV()) {
+ rotation = normalizeAngleDegrees(360.0 - rotation);
+ }
+
+ pi.setCanvasRotation(normalizeAngleDegrees(pi.canvasRotation() - rotation));
+}
+
+
void KisToolMultihandHelper::paintAt(const KisPaintInformation &pi)
{
for (int i = 0; i < d->transformations.size(); i++) {
const QTransform &transform = d->transformations[i];
-
KisPaintInformation __pi = pi;
- QLineF rotateme(QPointF (0.0,0.0), QPointF (10.0,10.0));
- rotateme.setAngle(__pi.canvasRotation());
- QLineF rotated = transform.map(rotateme);
-
__pi.setPos(transform.map(__pi.pos()));
- __pi.setCanvasRotation(rotated.angle());
-
- if (__pi.canvasMirroredH()) {
- __pi.setCanvasRotation(180-__pi.canvasRotation());
- __pi.setCanvasRotation(__pi.canvasRotation()+180);
- }
-
+ adjustPointInformationRotation(__pi, transform);
paintAt(i, __pi);
}
}
void KisToolMultihandHelper::paintLine(const KisPaintInformation &pi1,
const KisPaintInformation &pi2)
{
for (int i = 0; i < d->transformations.size(); i++) {
const QTransform &transform = d->transformations[i];
KisPaintInformation __pi1 = pi1;
KisPaintInformation __pi2 = pi2;
__pi1.setPos(transform.map(__pi1.pos()));
__pi2.setPos(transform.map(__pi2.pos()));
- QLineF rotateme(QPointF (0.0,0.0), QPointF (10.0,10.0));
- rotateme.setAngle(__pi1.canvasRotation());
- QLineF rotated = transform.map(rotateme);
- __pi1.setCanvasRotation(rotated.angle());
-
- rotateme.setAngle(__pi2.canvasRotation());
- rotated = transform.map(rotateme);
- __pi2.setCanvasRotation(rotated.angle());
-
- //check mirroring
- if (__pi2.canvasMirroredH()) {
- __pi1.setCanvasRotation(180-__pi1.canvasRotation());
- __pi1.setCanvasRotation(__pi1.canvasRotation()+180);
- __pi2.setCanvasRotation(180-__pi2.canvasRotation());
- __pi2.setCanvasRotation(__pi2.canvasRotation()+180);
- }
-
+ adjustPointInformationRotation(__pi1, transform);
+ adjustPointInformationRotation(__pi2, transform);
paintLine(i, __pi1, __pi2);
}
}
void KisToolMultihandHelper::paintBezierCurve(const KisPaintInformation &pi1,
const QPointF &control1,
const QPointF &control2,
const KisPaintInformation &pi2)
{
for (int i = 0; i < d->transformations.size(); i++) {
const QTransform &transform = d->transformations[i];
KisPaintInformation __pi1 = pi1;
KisPaintInformation __pi2 = pi2;
__pi1.setPos(transform.map(__pi1.pos()));
__pi2.setPos(transform.map(__pi2.pos()));
- QLineF rotateme(QPointF (0.0,0.0), QPointF (10.0,10.0));
- rotateme.setAngle(__pi1.canvasRotation());
- QLineF rotated = transform.map(rotateme);
- __pi1.setCanvasRotation(rotated.angle());
-
- rotateme.setAngle(__pi2.canvasRotation());
- rotated = transform.map(rotateme);
- __pi2.setCanvasRotation(rotated.angle());
-
- if (__pi2.canvasMirroredH()) {
- __pi1.setCanvasRotation(180-__pi1.canvasRotation());
- __pi1.setCanvasRotation(__pi1.canvasRotation()+180);
- __pi2.setCanvasRotation(180-__pi2.canvasRotation());
- __pi2.setCanvasRotation(__pi2.canvasRotation()+180);
- }
-
+ adjustPointInformationRotation(__pi1, transform);
+ adjustPointInformationRotation(__pi2, transform);
QPointF __control1 = transform.map(control1);
QPointF __control2 = transform.map(control2);
paintBezierCurve(i, __pi1, __control1, __control2, __pi2);
}
}
diff --git a/plugins/impex/gif/qgiflibhandler.cpp b/plugins/impex/gif/qgiflibhandler.cpp
index ed3f8227ca..fec916c424 100644
--- a/plugins/impex/gif/qgiflibhandler.cpp
+++ b/plugins/impex/gif/qgiflibhandler.cpp
@@ -1,368 +1,367 @@
/*
* Copyright (C) 2009 Shawn T. Rutledge (shawn.t.rutledge@gmail.com)
* Copyright (c) 2018 Boudewijn Rempt
*
* 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
*
*/
#include "qgiflibhandler.h"
#include
#include