diff --git a/libs/ui/widgets/kis_scratch_pad.cpp b/libs/ui/widgets/kis_scratch_pad.cpp index d6258e008b..76fa967d16 100644 --- a/libs/ui/widgets/kis_scratch_pad.cpp +++ b/libs/ui/widgets/kis_scratch_pad.cpp @@ -1,644 +1,642 @@ /* 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) + , isModeManuallySet(false) + , isMouseDown(false) , 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("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() { const QRect paintingBounds = m_paintLayer.data()->exactBounds(); QImage imageData = m_paintLayer->paintDevice()->convertToQImage(0, paintingBounds.x(), paintingBounds.y(), paintingBounds.width(), paintingBounds.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); return imageData; } 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(); } diff --git a/libs/ui/widgets/kis_scratch_pad.h b/libs/ui/widgets/kis_scratch_pad.h index 34c1168919..7cc8452e1f 100644 --- a/libs/ui/widgets/kis_scratch_pad.h +++ b/libs/ui/widgets/kis_scratch_pad.h @@ -1,204 +1,204 @@ /* 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. */ #ifndef KIS_SCRATCH_PAD_H #define KIS_SCRATCH_PAD_H #include #include #include #include #include #include #include class QColor; class KoColorProfile; class KoPointerEvent; class KisCanvasResourceProvider; class KisUpdateScheduler; class KisUndoStore; class KisPostExecutionUndoAdapter; class KisScratchPadEventFilter; class KisPaintingInformationBuilder; class KisToolFreehandHelper; class KisNodeGraphListener; /** * A scratchpad is a painting canvas with only one zoomlevel and based on * a paint layer, not on a KisImage. It can have a blank, tiled background or * a gradient background. */ class KRITAUI_EXPORT KisScratchPad : public QWidget { Q_OBJECT public: void setupScratchPad(KisCanvasResourceProvider* resourceProvider, const QColor &defaultColor); KisScratchPad(QWidget *parent = 0); ~KisScratchPad() override; /// set the specified rect as the area taken for @see cutoutOverlay void setCutoutOverlayRect(const QRect&rc); /** * keep track of if our scratchpad is in paint, pan, or color pick mode * Set to true if there is a GUI controlling current mode * If this is false, the modes are only changed with various mouse click shortcuts */ void setModeManually(bool value); /** * @brief change the mode explicitly to paint, mix, or pan * @param what mode to change it to */ void setModeType(QString modeName); /// return the contents of the area under the cutoutOverlay rect QImage cutoutOverlay() const; // A callback for our own node graph listener void imageUpdated(const QRect &rect); // A callback for scratch pad default bounds QRect imageBounds() const; // Called by the event filter void pointerPress(KoPointerEvent *event); void pointerRelease(KoPointerEvent *event); void pointerMove(KoPointerEvent *event); public Q_SLOTS: void fillDefault(); void fillGradient(); void fillBackground(); void fillTransparent(); void setFillColor(QColor newColor); /// Fill the area with what is on your current canvas void fillLayer(); /** * Set the icon of the current preset */ void setPresetImage(const QImage& image); /** * Paint the icon of the current preset inside the * cutout overlay * * \see setPresetImage */ void paintPresetImage(); /** * Paint the icon of a custom image that is being loaded * */ void paintCustomImage(const QImage & loadedImage); void loadScratchpadImage(QImage image); QImage copyScratchpadImageData(); private Q_SLOTS: void setOnScreenResolution(qreal scaleX, qreal scaleY); void setDisplayProfile(const KoColorProfile* colorProfile); void slotUpdateCanvas(const QRect &rect); Q_SIGNALS: void colorSelected(const KoColor& color); void sigUpdateCanvas(const QRect &rect); protected: void paintEvent ( QPaintEvent * event ) override; private: void beginStroke(KoPointerEvent *event); void doStroke(KoPointerEvent *event); void endStroke(KoPointerEvent *event); void beginPan(KoPointerEvent *event); void doPan(KoPointerEvent *event); void endPan(KoPointerEvent *event); void pick(KoPointerEvent *event); void updateTransformations(); QTransform documentToWidget() const; QTransform widgetToDocument() const; private: enum Mode { PAINTING, HOVERING, PANNING, PICKING }; Mode modeFromButton(Qt::MouseButton button) const; private: KoColor m_defaultColor; Mode m_toolMode; - bool isModeManuallySet = false; - bool isMouseDown = false; + bool isModeManuallySet; + bool isMouseDown; KisPaintLayerSP m_paintLayer; const KoColorProfile* m_displayProfile; QCursor m_cursor; QCursor m_colorPickerCursor; QRect m_cutoutOverlay; QBrush m_checkBrush; KisCanvasResourceProvider* m_resourceProvider; KisUpdateScheduler *m_updateScheduler; KisUndoStore *m_undoStore; KisPostExecutionUndoAdapter *m_undoAdapter; KisNodeGraphListener *m_nodeListener; KisScratchPadEventFilter *m_eventFilter; QScopedPointer m_helper; KisPaintingInformationBuilder *m_infoBuilder; QTransform m_scaleTransform; QTransform m_translateTransform; QPointF m_panDocPoint; int m_scaleBorderWidth; QImage m_presetImage; }; #endif // KIS_SCRATCH_PAD_H