diff --git a/krita/data/cursors/cursors.qrc b/krita/data/cursors/cursors.qrc
index 46529e3237..1c51ade673 100644
--- a/krita/data/cursors/cursors.qrc
+++ b/krita/data/cursors/cursors.qrc
@@ -1,27 +1,28 @@
color-picker_image_background.xpm
color-picker_image_foreground.xpm
color-picker_layer_background.xpm
color-picker_layer_foreground.xpm
cursor-cross.xpm
cursor-pixel-white.xpm
cursor-pixel-black.xpm
cursor-round.xpm
cursor-triangle_lefthanded.xpm
cursor-triangle_righthanded.xpm
exposure-cursor-gesture.xpm
gamma-cursor-gesture.xpm
precise-pick-layer-icon.xpm
rotate_discrete.xpm
rotate_smooth.xpm
move-tool.png
move-selection.png
shear_x.xpm
shear_y.xpm
zoom_discrete.xpm
zoom_smooth.xpm
tool_freehand_cursor.png
+ tool_color_picker_cursor.png
diff --git a/krita/data/cursors/tool_color_picker_cursor.png b/krita/data/cursors/tool_color_picker_cursor.png
new file mode 100644
index 0000000000..6bed15cc3c
Binary files /dev/null and b/krita/data/cursors/tool_color_picker_cursor.png differ
diff --git a/libs/ui/widgets/kis_scratch_pad.cpp b/libs/ui/widgets/kis_scratch_pad.cpp
index ac644b71f7..0b33221a62 100644
--- a/libs/ui/widgets/kis_scratch_pad.cpp
+++ b/libs/ui/widgets/kis_scratch_pad.cpp
@@ -1,651 +1,651 @@
/* This file is part of the KDE project
* Copyright 2010 (C) Boudewijn Rempt
* Copyright 2011 (C) Dmitry Kazakov
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "kis_scratch_pad.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "kis_config.h"
#include "kis_image.h"
#include "kis_undo_stores.h"
#include "kis_update_scheduler.h"
#include "kis_post_execution_undo_adapter.h"
#include "kis_scratch_pad_event_filter.h"
#include "kis_painting_information_builder.h"
#include "kis_tool_freehand_helper.h"
#include "kis_image_patch.h"
#include "kis_canvas_widget_base.h"
#include "kis_layer_projection_plane.h"
#include "kis_node_graph_listener.h"
#include "kis_transaction.h"
class KisScratchPadNodeListener : public KisNodeGraphListener
{
public:
KisScratchPadNodeListener(KisScratchPad *scratchPad)
: m_scratchPad(scratchPad)
{
}
void requestProjectionUpdate(KisNode *node, const QVector &rects, bool resetAnimationCache) override {
KisNodeGraphListener::requestProjectionUpdate(node, rects, resetAnimationCache);
QMutexLocker locker(&m_lock);
Q_FOREACH (const QRect &rc, rects) {
m_scratchPad->imageUpdated(rc);
}
}
private:
KisScratchPad *m_scratchPad;
QMutex m_lock;
};
class KisScratchPadDefaultBounds : public KisDefaultBounds
{
public:
KisScratchPadDefaultBounds(KisScratchPad *scratchPad)
: m_scratchPad(scratchPad)
{
}
~KisScratchPadDefaultBounds() override {}
QRect bounds() const override {
return m_scratchPad->imageBounds();
}
void * sourceCookie() const override {
return m_scratchPad;
}
private:
Q_DISABLE_COPY(KisScratchPadDefaultBounds)
KisScratchPad *m_scratchPad;
};
KisScratchPad::KisScratchPad(QWidget *parent)
: QWidget(parent)
, m_toolMode(HOVERING)
, m_paintLayer(0)
, m_displayProfile(0)
, m_resourceProvider(0)
{
setAutoFillBackground(false);
setMouseTracking(true);
m_cursor = KisCursor::load("tool_freehand_cursor.png", 5, 5);
- m_colorPickerCursor = KisCursor::load("color-picker_image_foreground.xpm", 5, 5);
+ m_colorPickerCursor = KisCursor::load("tool_color_picker_cursor.png", 5, 5);
setCursor(m_cursor);
KisConfig cfg(true);
QImage checkImage = KisCanvasWidgetBase::createCheckersImage(cfg.checkSize());
m_checkBrush = QBrush(checkImage);
// We are not supposed to use updates here,
// so just set the listener to null
m_updateScheduler = new KisUpdateScheduler(0);
m_undoStore = new KisSurrogateUndoStore();
m_undoAdapter = new KisPostExecutionUndoAdapter(m_undoStore, m_updateScheduler);
m_nodeListener = new KisScratchPadNodeListener(this);
connect(this, SIGNAL(sigUpdateCanvas(QRect)), SLOT(slotUpdateCanvas(QRect)), Qt::QueuedConnection);
// filter will be deleted by the QObject hierarchy
m_eventFilter = new KisScratchPadEventFilter(this);
m_infoBuilder = new KisPaintingInformationBuilder();
m_scaleBorderWidth = 1;
}
KisScratchPad::~KisScratchPad()
{
delete m_infoBuilder;
delete m_undoAdapter;
delete m_undoStore;
delete m_updateScheduler;
delete m_nodeListener;
}
KisScratchPad::Mode KisScratchPad::modeFromButton(Qt::MouseButton button) const
{
return
button == Qt::NoButton ? HOVERING :
button == Qt::MidButton ? PANNING :
button == Qt::RightButton ? PICKING :
PAINTING;
}
void KisScratchPad::pointerPress(KoPointerEvent *event)
{
if (isModeManuallySet == false) {
if (m_toolMode != HOVERING) return;
m_toolMode = modeFromButton(event->button());
}
// see if we are pressing down with a button
if (event->button() == Qt::LeftButton ||
event->button() == Qt::MidButton ||
event->button() == Qt::RightButton) {
isMouseDown = true;
} else {
isMouseDown = false;
}
// if mouse is down, we are doing one of three things
if(isMouseDown) {
if (m_toolMode == PAINTING) {
beginStroke(event);
event->accept();
}
else if (m_toolMode == PANNING) {
beginPan(event);
event->accept();
}
else if (m_toolMode == PICKING) {
pick(event);
event->accept();
}
}
}
void KisScratchPad::pointerRelease(KoPointerEvent *event)
{
isMouseDown = false;
if (isModeManuallySet == false) {
if (modeFromButton(event->button()) != m_toolMode) return;
if (m_toolMode == PAINTING) {
endStroke(event);
m_toolMode = HOVERING;
event->accept();
}
else if (m_toolMode == PANNING) {
endPan(event);
m_toolMode = HOVERING;
event->accept();
}
else if (m_toolMode == PICKING) {
event->accept();
m_toolMode = HOVERING;
}
} else {
if (m_toolMode == PAINTING) {
endStroke(event);
}
else if (m_toolMode == PANNING) {
endPan(event);
}
event->accept();
}
}
void KisScratchPad::pointerMove(KoPointerEvent *event)
{
if(event && event->point.isNull() == false) {
m_helper->cursorMoved(documentToWidget().map(event->point));
}
if (isMouseDown) {
if (m_toolMode == PAINTING) {
doStroke(event);
event->accept();
}
else if (m_toolMode == PANNING) {
doPan(event);
event->accept();
}
else if (m_toolMode == PICKING) {
pick(event);
event->accept();
}
}
}
void KisScratchPad::beginStroke(KoPointerEvent *event)
{
m_helper->initPaint(event,
documentToWidget().map(event->point),
0,
0,
m_updateScheduler,
m_paintLayer,
m_paintLayer->paintDevice()->defaultBounds());
}
void KisScratchPad::doStroke(KoPointerEvent *event)
{
m_helper->paintEvent(event);
}
void KisScratchPad::endStroke(KoPointerEvent *event)
{
Q_UNUSED(event);
m_helper->endPaint();
}
void KisScratchPad::beginPan(KoPointerEvent *event)
{
setCursor(QCursor(Qt::ClosedHandCursor));
m_panDocPoint = event->point;
}
void KisScratchPad::doPan(KoPointerEvent *event)
{
QPointF docOffset = event->point - m_panDocPoint;
m_translateTransform.translate(-docOffset.x(), -docOffset.y());
updateTransformations();
update();
}
void KisScratchPad::endPan(KoPointerEvent *event)
{
Q_UNUSED(event);
// the normal brush editor scratchpad reverts back to paint mode when done
if(isModeManuallySet) {
setCursor(QCursor(Qt::OpenHandCursor));
} else {
setCursor(m_cursor);
}
}
void KisScratchPad::pick(KoPointerEvent *event)
{
KoColor color;
if (KisToolUtils::pickColor(color, m_paintLayer->projection(), event->point.toPoint())) {
emit colorSelected(color);
}
}
void KisScratchPad::setOnScreenResolution(qreal scaleX, qreal scaleY)
{
m_scaleBorderWidth = BORDER_SIZE(qMax(scaleX, scaleY));
m_scaleTransform = QTransform::fromScale(scaleX, scaleY);
updateTransformations();
update();
}
QTransform KisScratchPad::documentToWidget() const
{
return m_translateTransform.inverted() * m_scaleTransform;
}
QTransform KisScratchPad::widgetToDocument() const
{
return m_scaleTransform.inverted() * m_translateTransform;
}
void KisScratchPad::updateTransformations()
{
m_eventFilter->setWidgetToDocumentTransform(widgetToDocument());
}
QRect KisScratchPad::imageBounds() const
{
return widgetToDocument().mapRect(rect());
}
void KisScratchPad::imageUpdated(const QRect &rect)
{
emit sigUpdateCanvas(documentToWidget().mapRect(QRectF(rect)).toAlignedRect());
}
void KisScratchPad::slotUpdateCanvas(const QRect &rect)
{
update(rect);
}
void KisScratchPad::paintEvent ( QPaintEvent * event ) {
if(!m_paintLayer) return;
QRectF imageRect = widgetToDocument().mapRect(QRectF(event->rect()));
QRect alignedImageRect =
imageRect.adjusted(-m_scaleBorderWidth, -m_scaleBorderWidth,
m_scaleBorderWidth, m_scaleBorderWidth).toAlignedRect();
QPointF offset = alignedImageRect.topLeft();
m_paintLayer->projectionPlane()->recalculate(alignedImageRect, m_paintLayer);
KisPaintDeviceSP projection = m_paintLayer->projection();
QImage image = projection->convertToQImage(m_displayProfile,
alignedImageRect.x(),
alignedImageRect.y(),
alignedImageRect.width(),
alignedImageRect.height(),
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags());
QPainter gc(this);
gc.fillRect(event->rect(), m_checkBrush);
gc.setRenderHints(QPainter::SmoothPixmapTransform);
gc.drawImage(QRectF(event->rect()), image, imageRect.translated(-offset));
QBrush brush(Qt::lightGray);
QPen pen(brush, 1, Qt::DotLine);
gc.setPen(pen);
if (m_cutoutOverlay.isValid()) {
gc.drawRect(m_cutoutOverlay);
}
if(!isEnabled()) {
QColor color(Qt::lightGray);
color.setAlphaF(0.5);
QBrush disabledBrush(color);
gc.fillRect(event->rect(), disabledBrush);
}
gc.end();
}
void KisScratchPad::setupScratchPad(KisCanvasResourceProvider* resourceProvider,
const QColor &defaultColor)
{
m_resourceProvider = resourceProvider;
KisConfig cfg(true);
setDisplayProfile(cfg.displayProfile(QApplication::desktop()->screenNumber(this)));
connect(m_resourceProvider, SIGNAL(sigDisplayProfileChanged(const KoColorProfile*)),
SLOT(setDisplayProfile(const KoColorProfile*)));
connect(m_resourceProvider, SIGNAL(sigOnScreenResolutionChanged(qreal,qreal)),
SLOT(setOnScreenResolution(qreal,qreal)));
connect(this, SIGNAL(colorSelected(KoColor)),
m_resourceProvider, SLOT(slotSetFGColor(KoColor)));
m_helper.reset(new KisToolFreehandHelper(m_infoBuilder, m_resourceProvider->resourceManager()));
setFillColor(defaultColor);
KisPaintDeviceSP paintDevice =
new KisPaintDevice(m_defaultColor.colorSpace(), "scratchpad");
m_paintLayer = new KisPaintLayer(0, "ScratchPad", OPACITY_OPAQUE_U8, paintDevice);
m_paintLayer->setGraphListener(m_nodeListener);
m_paintLayer->paintDevice()->setDefaultBounds(new KisScratchPadDefaultBounds(this));
fillDefault();
}
void KisScratchPad::setCutoutOverlayRect(const QRect& rc)
{
m_cutoutOverlay = rc;
}
void KisScratchPad::setModeManually(bool value)
{
isModeManuallySet = value;
}
void KisScratchPad::setModeType(QString mode)
{
if (mode.toLower() == "painting") {
m_toolMode = PAINTING;
setCursor(m_cursor);
}
else if (mode.toLower() == "panning") {
m_toolMode = PANNING;
setCursor(Qt::OpenHandCursor);
}
else if (mode.toLower() == "colorpicking") {
m_toolMode = PICKING;
setCursor(m_colorPickerCursor);
}
}
QImage KisScratchPad::cutoutOverlay() const
{
if(!m_paintLayer) return QImage();
KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
QRect rc = widgetToDocument().mapRect(m_cutoutOverlay);
QImage rawImage = paintDevice->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
QImage scaledImage = rawImage.scaled(m_cutoutOverlay.size(),
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
return scaledImage;
}
void KisScratchPad::setPresetImage(const QImage& image)
{
m_presetImage = image;
}
void KisScratchPad::paintCustomImage(const QImage& loadedImage)
{
// this is 99% copied from the normal paintPresetImage()
// we don't want to save over the preset image, so we don't
// want to store it in the m_presetImage
if(!m_paintLayer) return;
KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
QRect overlayRect = widgetToDocument().mapRect(m_cutoutOverlay);
QRect imageRect(QPoint(), overlayRect.size());
QImage scaledImage = loadedImage.scaled(overlayRect.size(),
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
KisPaintDeviceSP device = new KisPaintDevice(paintDevice->colorSpace());
device->convertFromQImage(scaledImage, 0);
KisPainter painter(paintDevice);
painter.beginTransaction();
painter.bitBlt(overlayRect.topLeft(), device, imageRect);
painter.deleteTransaction();
update();
}
void KisScratchPad::loadScratchpadImage(QImage image)
{
if(!m_paintLayer) return;
m_translateTransform.reset(); // image will be loaded at 0,0, so reset panning location
updateTransformations();
fillDefault(); // wipes out whatever was there before
QRect imageSize = image.rect();
KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
KisPaintDeviceSP device = new KisPaintDevice(paintDevice->colorSpace());
device->convertFromQImage(image, 0);
KisPainter painter(paintDevice);
painter.beginTransaction();
painter.bitBlt(imageSize.topLeft(), device, imageSize);
painter.deleteTransaction();
update();
}
QImage KisScratchPad::copyScratchpadImageData()
{
// we can get the image data by resizing the "cut overlay" area to the image bounds and using that as the image
QRect original_cutOverlay_rect = cutoutOverlay().rect();
// calculate what the area of the scratchpad was used
//setCutoutOverlayRect(m_paintLayer->extent()); // this needs to capture everything that has been painted on
setCutoutOverlayRect(m_paintLayer->projection()->extent());
QImage fullImage = cutoutOverlay();
KisScratchPad::setCutoutOverlayRect(original_cutOverlay_rect); // return overlay size back to original
return fullImage;
}
void KisScratchPad::paintPresetImage()
{
if(!m_paintLayer) return;
KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
QRect overlayRect = widgetToDocument().mapRect(m_cutoutOverlay);
QRect imageRect(QPoint(), overlayRect.size());
QImage scaledImage = m_presetImage.scaled(overlayRect.size(),
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
KisPaintDeviceSP device = new KisPaintDevice(paintDevice->colorSpace());
device->convertFromQImage(scaledImage, 0);
KisPainter painter(paintDevice);
painter.beginTransaction();
painter.bitBlt(overlayRect.topLeft(), device, imageRect);
painter.deleteTransaction();
update();
}
void KisScratchPad::setDisplayProfile(const KoColorProfile *colorProfile)
{
if (colorProfile) {
m_displayProfile = colorProfile;
QWidget::update();
}
}
void KisScratchPad::fillDefault()
{
if(!m_paintLayer) return;
KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
KisTransaction t(paintDevice);
paintDevice->setDefaultPixel(m_defaultColor);
paintDevice->clear();
t.end();
update();
}
void KisScratchPad::fillTransparent() {
if(!m_paintLayer) return;
KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
QColor transQColor(0,0,0,0);
KoColor transparentColor(transQColor, KoColorSpaceRegistry::instance()->rgb8());
transparentColor.setOpacity(0.0);
KisTransaction t(paintDevice);
paintDevice->setDefaultPixel(transparentColor);
paintDevice->clear();
t.end();
update();
}
void KisScratchPad::setFillColor(QColor newColor)
{
m_defaultColor = KoColor(newColor, KoColorSpaceRegistry::instance()->rgb8());
}
void KisScratchPad::fillGradient()
{
if(!m_paintLayer) return;
KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
KoAbstractGradientSP gradient = m_resourceProvider->currentGradient();
QRect gradientRect = widgetToDocument().mapRect(rect());
KisTransaction t(paintDevice);
paintDevice->clear();
KisGradientPainter painter(paintDevice);
painter.setGradient(gradient);
painter.setGradientShape(KisGradientPainter::GradientShapeLinear);
painter.paintGradient(gradientRect.topLeft(),
gradientRect.bottomRight(),
KisGradientPainter::GradientRepeatNone,
0.2, false,
gradientRect.left(), gradientRect.top(),
gradientRect.width(), gradientRect.height());
t.end();
update();
}
void KisScratchPad::fillBackground()
{
if(!m_paintLayer) return;
KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
KisTransaction t(paintDevice);
paintDevice->setDefaultPixel(m_resourceProvider->bgColor());
paintDevice->clear();
t.end();
update();
}
void KisScratchPad::fillLayer()
{
if(!m_paintLayer) return;
KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
QRect sourceRect(0, 0, paintDevice->exactBounds().width(), paintDevice->exactBounds().height());
KisPainter painter(paintDevice);
painter.beginTransaction();
painter.bitBlt(QPoint(0, 0), m_resourceProvider->currentImage()->projection(), sourceRect);
painter.deleteTransaction();
update();
}