File indexing completed on 2024-12-22 04:13:13

0001 /* This file is part of the KDE project
0002  * Copyright 2010 (C) Boudewijn Rempt <boud@valdyas.org>
0003  * Copyright 2011 (C) Dmitry Kazakov <dimula73@gmail.com>
0004  *
0005  * SPDX-License-Identifier: LGPL-2.0-or-later
0006  */
0007 #include "kis_scratch_pad.h"
0008 
0009 #include <QApplication>
0010 #include <QDesktopWidget>
0011 #include <QMutex>
0012 
0013 #include <KoColorSpace.h>
0014 #include <KoColorProfile.h>
0015 #include <KoColorSpaceRegistry.h>
0016 #include <KoPointerEvent.h>
0017 #include <resources/KoAbstractGradient.h>
0018 
0019 #include <kis_cursor.h>
0020 #include <kis_tool_utils.h>
0021 #include <kis_paint_layer.h>
0022 #include <kis_paint_device.h>
0023 #include <kis_gradient_painter.h>
0024 #include <kis_default_bounds.h>
0025 #include <kis_canvas_resource_provider.h>
0026 
0027 #include "kis_config.h"
0028 #include "kis_image.h"
0029 #include "kis_undo_stores.h"
0030 #include "kis_update_scheduler.h"
0031 #include "kis_post_execution_undo_adapter.h"
0032 #include "kis_scratch_pad_event_filter.h"
0033 #include "kis_painting_information_builder.h"
0034 #include "kis_tool_freehand_helper.h"
0035 #include "kis_image_patch.h"
0036 #include "kis_canvas_widget_base.h"
0037 #include "kis_layer_projection_plane.h"
0038 #include "kis_node_graph_listener.h"
0039 #include "kis_transaction.h"
0040 
0041 class KisScratchPadNodeListener : public KisNodeGraphListener
0042 {
0043 public:
0044     KisScratchPadNodeListener(KisScratchPad *scratchPad)
0045         : m_scratchPad(scratchPad)
0046     {
0047     }
0048 
0049     void requestProjectionUpdate(KisNode *node, const QVector<QRect> &rects, bool resetAnimationCache) override {
0050         KisNodeGraphListener::requestProjectionUpdate(node, rects, resetAnimationCache);
0051 
0052         QMutexLocker locker(&m_lock);
0053 
0054         Q_FOREACH (const QRect &rc, rects) {
0055             m_scratchPad->imageUpdated(rc);
0056         }
0057     }
0058 
0059 private:
0060     KisScratchPad *m_scratchPad;
0061     QMutex m_lock;
0062 };
0063 
0064 
0065 class KisScratchPadDefaultBounds : public KisDefaultBounds
0066 {
0067 public:
0068 
0069     KisScratchPadDefaultBounds(KisScratchPad *scratchPad)
0070         : m_scratchPad(scratchPad)
0071     {
0072     }
0073 
0074     ~KisScratchPadDefaultBounds() override {}
0075 
0076     QRect bounds() const override {
0077         return m_scratchPad->imageBounds();
0078     }
0079 
0080     void * sourceCookie() const override {
0081         return m_scratchPad;
0082     }
0083 
0084 private:
0085     Q_DISABLE_COPY(KisScratchPadDefaultBounds)
0086 
0087     KisScratchPad *m_scratchPad;
0088 };
0089 
0090 class KisScratchPadPaintingInformationBuilder : public KisPaintingInformationBuilder
0091 {
0092     Q_OBJECT
0093 
0094 public:
0095     KisScratchPadPaintingInformationBuilder(KisScratchPad *scratchPad)
0096         : m_scratchPad(scratchPad)
0097     {
0098     }
0099 
0100 protected:
0101     QPointF imageToView(const QPointF &point) override {
0102         return m_scratchPad->documentToWidget().map(point);
0103     }
0104 
0105 private:
0106     const KisScratchPad *m_scratchPad;
0107 };
0108 
0109 
0110 KisScratchPad::KisScratchPad(QWidget *parent)
0111     : QWidget(parent)
0112     , m_toolMode(HOVERING)
0113     , isModeManuallySet(false)
0114     , isMouseDown(false)
0115     , linkCanvasZoomLevel(true)
0116     , m_paintLayer(0)
0117     , m_displayProfile(0)
0118     , m_resourceProvider(0)
0119 {
0120     setAutoFillBackground(false);
0121     setMouseTracking(true);
0122 
0123     m_cursor = KisCursor::load("tool_freehand_cursor.xpm", 2, 2);
0124     m_colorSamplerCursor = KisCursor::load("tool_color_sampler_cursor.xpm", 2, 2);
0125     setCursor(m_cursor);
0126 
0127 
0128     KisConfig cfg(true);
0129     QImage checkImage = KisCanvasWidgetBase::createCheckersImage(cfg.checkSize());
0130     m_checkBrush = QBrush(checkImage);
0131 
0132 
0133     // We are not supposed to use updates here,
0134     // so just set the listener to null
0135     m_updateScheduler = new KisUpdateScheduler(0);
0136     m_undoStore = new KisSurrogateUndoStore();
0137     m_undoAdapter = new KisPostExecutionUndoAdapter(m_undoStore, m_updateScheduler);
0138     m_nodeListener = new KisScratchPadNodeListener(this);
0139 
0140     connect(this, SIGNAL(sigUpdateCanvas(QRect)), SLOT(slotUpdateCanvas(QRect)), Qt::QueuedConnection);
0141 
0142     // filter will be deleted by the QObject hierarchy
0143     m_eventFilter = new KisScratchPadEventFilter(this);
0144 
0145     m_infoBuilder = new KisScratchPadPaintingInformationBuilder(this);
0146 
0147     m_scaleBorderWidth = 1;
0148 }
0149 
0150 KisScratchPad::~KisScratchPad()
0151 {
0152     delete m_infoBuilder;
0153 
0154     delete m_undoAdapter;
0155     delete m_undoStore;
0156     delete m_updateScheduler;
0157     delete m_nodeListener;
0158 }
0159 
0160 KisScratchPad::Mode KisScratchPad::modeFromButton(Qt::MouseButton button) const
0161 {
0162     return
0163         button == Qt::NoButton ? HOVERING :
0164         button == Qt::MiddleButton ? PANNING :
0165         button == Qt::RightButton ? SAMPLING :
0166         PAINTING;
0167 }
0168 
0169 void KisScratchPad::pointerPress(KoPointerEvent *event)
0170 {
0171     if(!isEnabled()) return;
0172 
0173     if (isModeManuallySet == false) {
0174         m_toolMode = modeFromButton(event->button());
0175     }
0176 
0177     // see if we are pressing down with a button
0178     if (event->button() == Qt::LeftButton ||
0179         event->button() == Qt::MiddleButton ||
0180         event->button() == Qt::RightButton) {
0181         isMouseDown = true;
0182     } else {
0183         isMouseDown = false;
0184     }
0185 
0186     // if mouse is down, we are doing one of three things
0187     if(isMouseDown) {
0188         if (m_toolMode == PAINTING) {
0189             beginStroke(event);
0190             event->accept();
0191         }
0192         else if (m_toolMode == PANNING) {
0193             beginPan(event);
0194             event->accept();
0195         }
0196         else if (m_toolMode == SAMPLING) {
0197             sample(event);
0198             event->accept();
0199         }
0200     }
0201 
0202 }
0203 
0204 void KisScratchPad::pointerRelease(KoPointerEvent *event)
0205 {
0206     if(!isEnabled()) return;
0207     isMouseDown = false;
0208 
0209     if (isModeManuallySet == false) {
0210         if (modeFromButton(event->button()) != m_toolMode) return;
0211 
0212         if (m_toolMode == PAINTING) {
0213             endStroke(event);
0214             m_toolMode = HOVERING;
0215             event->accept();
0216         }
0217         else if (m_toolMode == PANNING) {
0218             endPan(event);
0219             m_toolMode = HOVERING;
0220             event->accept();
0221         }
0222         else if (m_toolMode == SAMPLING) {
0223             event->accept();
0224             m_toolMode = HOVERING;
0225         }
0226 
0227     } else {
0228         if (m_toolMode == PAINTING) {
0229             endStroke(event);
0230         }
0231         else if (m_toolMode == PANNING) {
0232             endPan(event);
0233         }
0234 
0235         event->accept();
0236     }
0237 
0238 
0239 }
0240 
0241 void KisScratchPad::pointerMove(KoPointerEvent *event)
0242 {
0243     if(!isEnabled()) return;
0244     KIS_SAFE_ASSERT_RECOVER_RETURN(event);
0245 
0246     if(event->point.isNull() == false) {
0247         m_helper->cursorMoved(documentToWidget().map(event->point));
0248     }
0249 
0250 
0251     if (isMouseDown) {
0252         if (m_toolMode == PAINTING) {
0253             doStroke(event);
0254             event->accept();
0255         }
0256         else if (m_toolMode == PANNING) {
0257             doPan(event);
0258             event->accept();
0259         }
0260         else if (m_toolMode == SAMPLING) {
0261             sample(event);
0262             event->accept();
0263         }
0264     }
0265 }
0266 
0267 void KisScratchPad::beginStroke(KoPointerEvent *event)
0268 {
0269 
0270     m_helper->initPaint(event,
0271                         documentToWidget().map(event->point),
0272                         0,
0273                         0,
0274                         m_updateScheduler,
0275                         m_paintLayer,
0276                         m_paintLayer->paintDevice()->defaultBounds());
0277 
0278 
0279 }
0280 
0281 void KisScratchPad::doStroke(KoPointerEvent *event)
0282 {
0283     m_helper->paintEvent(event);
0284 }
0285 
0286 void KisScratchPad::endStroke(KoPointerEvent *event)
0287 {
0288     Q_UNUSED(event);
0289     m_helper->endPaint();
0290 }
0291 
0292 void KisScratchPad::beginPan(KoPointerEvent *event)
0293 {
0294     setCursor(QCursor(Qt::ClosedHandCursor));
0295     m_panDocPoint = event->point;
0296 }
0297 
0298 void KisScratchPad::doPan(KoPointerEvent *event)
0299 {
0300     QPointF docOffset = event->point - m_panDocPoint;
0301 
0302     m_translateTransform.translate(-docOffset.x(), -docOffset.y());
0303     updateTransformations();
0304     update();
0305 }
0306 
0307 void KisScratchPad::endPan(KoPointerEvent *event)
0308 {
0309     Q_UNUSED(event);
0310 
0311     // the normal brush editor scratchpad reverts back to paint mode when done
0312     if(isModeManuallySet) {
0313         setCursor(QCursor(Qt::OpenHandCursor));
0314     } else {
0315         setCursor(m_cursor);
0316     }
0317 
0318 }
0319 
0320 void KisScratchPad::sample(KoPointerEvent *event)
0321 {
0322     KoColor color;
0323     if (KisToolUtils::sampleColor(color, m_paintLayer->projection(), event->point.toPoint())) {
0324         emit colorSelected(color);
0325     }
0326 }
0327 
0328 void KisScratchPad::setOnScreenResolution(qreal scaleX, qreal scaleY)
0329 {
0330     m_scaleBorderWidth = BORDER_SIZE(qMax(scaleX, scaleY));
0331 
0332     // the scratchpad will use the canvas zoom level...or not
0333     if(linkCanvasZoomLevel) {
0334         m_scaleTransform = QTransform::fromScale(scaleX, scaleY);
0335     } else {
0336         m_scaleTransform = QTransform::fromScale(1, 1);
0337     }
0338 
0339     updateTransformations();
0340     update();
0341 }
0342 
0343 QTransform KisScratchPad::documentToWidget() const
0344 {
0345     return m_translateTransform.inverted() * m_scaleTransform;
0346 }
0347 
0348 QTransform KisScratchPad::widgetToDocument() const
0349 {
0350     return m_scaleTransform.inverted() * m_translateTransform;
0351 }
0352 
0353 void KisScratchPad::updateTransformations()
0354 {
0355     m_eventFilter->setWidgetToDocumentTransform(widgetToDocument());
0356 }
0357 
0358 QRect KisScratchPad::imageBounds() const
0359 {
0360     return widgetToDocument().mapRect(rect());
0361 }
0362 
0363 void KisScratchPad::imageUpdated(const QRect &rect)
0364 {
0365     emit sigUpdateCanvas(documentToWidget().mapRect(QRectF(rect)).toAlignedRect());
0366 }
0367 
0368 void KisScratchPad::slotUpdateCanvas(const QRect &rect)
0369 {
0370     update(rect);
0371 }
0372 
0373 void KisScratchPad::paintEvent ( QPaintEvent * event ) {
0374     if(!m_paintLayer) return;
0375 
0376     QRectF imageRect = widgetToDocument().mapRect(QRectF(event->rect()));
0377 
0378     QRect alignedImageRect =
0379         imageRect.adjusted(-m_scaleBorderWidth, -m_scaleBorderWidth,
0380                            m_scaleBorderWidth, m_scaleBorderWidth).toAlignedRect();
0381 
0382     QPointF offset = alignedImageRect.topLeft();
0383 
0384     m_paintLayer->projectionPlane()->recalculate(alignedImageRect, m_paintLayer);
0385     KisPaintDeviceSP projection = m_paintLayer->projection();
0386 
0387 
0388 
0389     QImage image = projection->convertToQImage(m_displayProfile,
0390                                                alignedImageRect.x(),
0391                                                alignedImageRect.y(),
0392                                                alignedImageRect.width(),
0393                                                alignedImageRect.height(),
0394                                                KoColorConversionTransformation::internalRenderingIntent(),
0395                                                KoColorConversionTransformation::internalConversionFlags());
0396 
0397 
0398     QPainter gc(this);
0399     gc.fillRect(event->rect(), m_checkBrush);
0400 
0401     // if we scale down, it should use Smooth
0402     // if we scale up, it should use Fast (nearest Neighbour) to show pixels
0403     if (event->rect().width() < image.rect().width()) {
0404         gc.setRenderHints(QPainter::SmoothPixmapTransform);
0405     } else {
0406         gc.setRenderHints(0); // that will use NN
0407     }
0408 
0409     gc.drawImage(QRectF(event->rect()), image, imageRect.translated(-offset));
0410 
0411     QBrush brush(Qt::lightGray);
0412     QPen pen(brush, 1, Qt::DotLine);
0413     gc.setPen(pen);
0414     if (m_cutoutOverlay.isValid()) {
0415         gc.drawRect(m_cutoutOverlay);
0416     }
0417 
0418     if(!isEnabled()) {
0419         QColor color(Qt::lightGray);
0420         color.setAlphaF(0.5);
0421         QBrush disabledBrush(color);
0422         gc.fillRect(event->rect(), disabledBrush);
0423     }
0424     gc.end();
0425 }
0426 
0427 void KisScratchPad::resetState()
0428 {
0429     if (m_helper->isRunning()) {
0430         m_helper->endPaint();
0431     }
0432 
0433     m_toolMode = HOVERING;
0434     setCursor(m_cursor);
0435 }
0436 
0437 void KisScratchPad::setupScratchPad(KisCanvasResourceProvider* resourceProvider,
0438                                     const QColor &defaultColor)
0439 {
0440     m_resourceProvider = resourceProvider;
0441     KisConfig cfg(true);
0442     setDisplayProfile(cfg.displayProfile(QApplication::desktop()->screenNumber(QApplication::activeWindow())));
0443     connect(m_resourceProvider, SIGNAL(sigDisplayProfileChanged(const KoColorProfile*)),
0444             SLOT(setDisplayProfile(const KoColorProfile*)));
0445 
0446     connect(m_resourceProvider, SIGNAL(sigOnScreenResolutionChanged(qreal,qreal)),
0447             SLOT(setOnScreenResolution(qreal,qreal)));
0448     connect(this, SIGNAL(colorSelected(KoColor)),
0449             m_resourceProvider, SLOT(slotSetFGColor(KoColor)));
0450 
0451     m_helper.reset(new KisToolFreehandHelper(m_infoBuilder, m_resourceProvider->resourceManager()));
0452 
0453     setFillColor(defaultColor);
0454 
0455     KisPaintDeviceSP paintDevice =
0456         new KisPaintDevice(m_defaultColor.colorSpace(), "scratchpad");
0457 
0458     m_paintLayer = new KisPaintLayer(0, "ScratchPad", OPACITY_OPAQUE_U8, paintDevice);
0459     m_paintLayer->setGraphListener(m_nodeListener);
0460     m_paintLayer->paintDevice()->setDefaultBounds(new KisScratchPadDefaultBounds(this));
0461 
0462     fillDefault();
0463 }
0464 
0465 void KisScratchPad::setCutoutOverlayRect(const QRect& rc)
0466 {
0467     m_cutoutOverlay = rc;
0468 }
0469 
0470 void KisScratchPad::setModeManually(bool value)
0471 {
0472     isModeManuallySet = value;
0473 }
0474 
0475 void KisScratchPad::setModeType(QString mode)
0476 {
0477     if (mode.toLower() == "painting") {
0478         m_toolMode = PAINTING;
0479         setCursor(m_cursor);
0480     }
0481     else if (mode.toLower() == "panning") {
0482         m_toolMode = PANNING;
0483         setCursor(Qt::OpenHandCursor);
0484     }
0485     else if (mode.toLower() == "colorsampling") {
0486         m_toolMode = SAMPLING;
0487         setCursor(m_colorSamplerCursor);
0488     }
0489 }
0490 
0491 void KisScratchPad::linkCanvasToZoomLevel(bool value)
0492 {
0493     linkCanvasZoomLevel = value;
0494 }
0495 
0496 QImage KisScratchPad::cutoutOverlay() const
0497 {
0498     if(!m_paintLayer) return QImage();
0499     KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
0500 
0501 
0502     QRect rc = widgetToDocument().mapRect(m_cutoutOverlay);
0503     QImage rawImage = paintDevice->convertToQImage(0, rc.x(), rc.y(), rc.width(), rc.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
0504 
0505     QImage scaledImage = rawImage.scaled(m_cutoutOverlay.size(),
0506                                          Qt::IgnoreAspectRatio,
0507                                          Qt::SmoothTransformation);
0508 
0509     return scaledImage;
0510 }
0511 
0512 void KisScratchPad::setPresetImage(const QImage& image)
0513 {
0514     m_presetImage = image;
0515 }
0516 
0517 
0518 void KisScratchPad::paintCustomImage(const QImage& loadedImage)
0519 {
0520     // this is 99% copied from the normal paintPresetImage()
0521     // we don't want to save over the preset image, so we don't
0522     // want to store it in the m_presetImage
0523     if(!m_paintLayer) return;
0524     KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
0525 
0526 
0527     QRect overlayRect = widgetToDocument().mapRect(m_cutoutOverlay);
0528     QRect imageRect(QPoint(), overlayRect.size());
0529 
0530     QImage scaledImage = loadedImage.scaled(overlayRect.size(),
0531                                               Qt::IgnoreAspectRatio,
0532                                               Qt::SmoothTransformation);
0533     KisPaintDeviceSP device = new KisPaintDevice(paintDevice->colorSpace());
0534     device->convertFromQImage(scaledImage, 0);
0535 
0536     KisPainter painter(paintDevice);
0537     painter.beginTransaction();
0538     painter.bitBlt(overlayRect.topLeft(), device, imageRect);
0539     painter.deleteTransaction();
0540     update();
0541 }
0542 
0543 void KisScratchPad::loadScratchpadImage(QImage image)
0544 {
0545     if(!m_paintLayer) return;
0546 
0547     m_translateTransform.reset(); // image will be loaded at 0,0, so reset panning location
0548     updateTransformations();
0549 
0550     fillDefault(); // wipes out whatever was there before
0551 
0552     QRect imageSize = image.rect();
0553     KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
0554 
0555     KisPaintDeviceSP device = new KisPaintDevice(paintDevice->colorSpace());
0556     device->convertFromQImage(image, 0);
0557 
0558     KisPainter painter(paintDevice);
0559     painter.beginTransaction();
0560     painter.bitBlt(imageSize.topLeft(), device, imageSize);
0561     painter.deleteTransaction();
0562     update();
0563 }
0564 
0565 QImage KisScratchPad::copyScratchpadImageData()
0566 {
0567     const QRect paintingBounds = m_paintLayer.data()->exactBounds();
0568     QImage imageData = m_paintLayer->paintDevice()->convertToQImage(0, paintingBounds.x(), paintingBounds.y(), paintingBounds.width(), paintingBounds.height(),
0569                             KoColorConversionTransformation::internalRenderingIntent(),
0570                             KoColorConversionTransformation::internalConversionFlags());
0571     return imageData;
0572 }
0573 
0574 void KisScratchPad::paintPresetImage()
0575 {
0576     if(!m_paintLayer) return;
0577     KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
0578 
0579 
0580     QRect overlayRect = widgetToDocument().mapRect(m_cutoutOverlay);
0581     QRect imageRect(QPoint(), overlayRect.size());
0582 
0583     QImage scaledImage = m_presetImage.scaled(overlayRect.size(),
0584                                               Qt::IgnoreAspectRatio,
0585                                               Qt::SmoothTransformation);
0586     KisPaintDeviceSP device = new KisPaintDevice(paintDevice->colorSpace());
0587     device->convertFromQImage(scaledImage, 0);
0588 
0589     KisPainter painter(paintDevice);
0590     painter.beginTransaction();
0591     painter.bitBlt(overlayRect.topLeft(), device, imageRect);
0592     painter.deleteTransaction();
0593     update();
0594 }
0595 
0596 void KisScratchPad::setDisplayProfile(const KoColorProfile *colorProfile)
0597 {
0598     if (colorProfile) {
0599         m_displayProfile = colorProfile;
0600         QWidget::update();
0601     }
0602 }
0603 
0604 void KisScratchPad::fillDefault()
0605 {
0606     if(!m_paintLayer) return;
0607     KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
0608 
0609     KisTransaction t(paintDevice);
0610     paintDevice->setDefaultPixel(m_defaultColor);
0611     paintDevice->clear();
0612     t.end();
0613     update();
0614 }
0615 
0616 void KisScratchPad::fillTransparent() {
0617     if(!m_paintLayer) return;
0618     KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
0619 
0620     QColor transQColor(0,0,0,0);
0621     KoColor transparentColor(transQColor, KoColorSpaceRegistry::instance()->rgb8());
0622     transparentColor.setOpacity(0.0);
0623 
0624     KisTransaction t(paintDevice);
0625     paintDevice->setDefaultPixel(transparentColor);
0626     paintDevice->clear();
0627     t.end();
0628     update();
0629 }
0630 
0631 void KisScratchPad::setFillColor(QColor newColor)
0632 {
0633     m_defaultColor = KoColor(newColor, KoColorSpaceRegistry::instance()->rgb8());
0634 }
0635 
0636 void KisScratchPad::fillGradient()
0637 {
0638     if(!m_paintLayer) return;
0639     KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
0640 
0641     KoAbstractGradientSP gradient = m_resourceProvider->currentGradient();
0642     QRect gradientRect = widgetToDocument().mapRect(rect());
0643 
0644     KisTransaction t(paintDevice);
0645 
0646     paintDevice->clear();
0647 
0648     KisGradientPainter painter(paintDevice);
0649     painter.setGradient(gradient);
0650     painter.setGradientShape(KisGradientPainter::GradientShapeLinear);
0651     painter.paintGradient(gradientRect.topLeft(),
0652                           gradientRect.bottomRight(),
0653                           KisGradientPainter::GradientRepeatNone,
0654                           0.2, false,
0655                           gradientRect.left(), gradientRect.top(),
0656                           gradientRect.width(), gradientRect.height());
0657 
0658     t.end();
0659     update();
0660 }
0661 
0662 void KisScratchPad::fillBackground()
0663 {
0664     if(!m_paintLayer) return;
0665     KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
0666 
0667     KisTransaction t(paintDevice);
0668     paintDevice->setDefaultPixel(m_resourceProvider->bgColor());
0669     paintDevice->clear();
0670     t.end();
0671     update();
0672 }
0673 
0674 void KisScratchPad::fillLayer()
0675 {
0676     if(!m_paintLayer) return;
0677     KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
0678 
0679     QRect sourceRect(0, 0, paintDevice->exactBounds().width(), paintDevice->exactBounds().height());
0680 
0681     KisPainter painter(paintDevice);
0682     painter.beginTransaction();
0683     painter.bitBlt(QPoint(0, 0), m_resourceProvider->currentImage()->projection(), sourceRect);
0684     painter.deleteTransaction();
0685     update();
0686 }
0687 
0688 #include "kis_scratch_pad.moc"