krita

kis_tool_freehand.cc

00001 /*
00002  *  kis_tool_brush.cc - part of Krita
00003  *
00004  *  Copyright (c) 2003-2004 Boudewijn Rempt <boud@valdyas.org>
00005  *  Copyright (c) 2004 Bart Coppens <kde@bartcoppens.be>
00006  *
00007  *  This program is free software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; either version 2 of the License, or
00010  *  (at your option) any later version.
00011  *
00012  *  This program is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *  GNU General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU General Public License
00018  *  along with this program; if not, write to the Free Software
00019  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  */
00021 #include <qevent.h>
00022 #include <qlabel.h>
00023 #include <qlayout.h>
00024 #include <qwidget.h>
00025 #include <qrect.h>
00026 
00027 #include <kdebug.h>
00028 #include <kaction.h>
00029 #include <kcommand.h>
00030 #include <klocale.h>
00031 
00032 #include "kis_canvas_subject.h"
00033 #include "kis_undo_adapter.h"
00034 #include "kis_selection.h"
00035 #include "kis_painter.h"
00036 #include "kis_fill_painter.h"
00037 #include "kis_tool_freehand.h"
00038 #include "kis_cursor.h"
00039 #include "kis_button_press_event.h"
00040 #include "kis_button_release_event.h"
00041 #include "kis_move_event.h"
00042 #include "kis_layer.h"
00043 #include "kis_group_layer.h"
00044 #include "kis_paint_layer.h"
00045 #include "kis_canvas.h"
00046 #include "kis_canvas_painter.h"
00047 #include "kis_boundary_painter.h"
00048 #include "kis_brush.h"
00049 
00050 KisToolFreehand::KisToolFreehand(QString transactionText)
00051         : super(transactionText),
00052         m_dragDist ( 0 ),
00053         m_transactionText(transactionText),
00054         m_mode( HOVER )
00055 {
00056     m_painter = 0;
00057     m_currentImage = 0;
00058     m_tempLayer = 0;
00059     m_paintIncremental = true;
00060     m_paintOnSelection = false;
00061     m_paintedOutline = false;
00062 }
00063 
00064 KisToolFreehand::~KisToolFreehand()
00065 {
00066 }
00067 
00068 void KisToolFreehand::update(KisCanvasSubject *subject)
00069 {
00070     super::update(subject);
00071     m_currentImage = m_subject->currentImg();
00072 }
00073 
00074 void KisToolFreehand::buttonPress(KisButtonPressEvent *e)
00075 {
00076     if (!m_subject) return;
00077 
00078     if (!m_subject->currentBrush()) return;
00079 
00080     if (!m_currentImage || !m_currentImage->activeDevice()) return;
00081 
00082     if (e->button() == QMouseEvent::LeftButton) {
00083 
00084         m_currentImage->activeDevice()->lock( true );
00085         kdDebug() << ">>>>>>>>>>>>>>>>>>>Locking paint device\n";
00086 
00087         // People complain that they can't start brush strokes outside of the image boundaries.
00088         // This makes sense, especially when combined with BUG:132759, so commenting out the
00089         // next line makes sense.
00090         //if (!m_currentImage->bounds().contains(e->pos().floorQPoint())) return;
00091 
00092         initPaint(e);
00093         paintAt(e->pos(), e->pressure(), e->xTilt(), e->yTilt());
00094 
00095         m_prevPos = e->pos();
00096         m_prevPressure = e->pressure();
00097         m_prevXTilt = e->xTilt();
00098         m_prevYTilt = e->yTilt();
00099 
00100         QRect r = m_painter->dirtyRect();
00101         if ( r.isValid() ) {
00102             m_dirtyRect = r;
00103 
00104             r = QRect(r.left()-1, r.top()-1, r.width()+2, r.height()+2); //needed to update selectionvisualization
00105             if (!m_paintOnSelection) {
00106                     m_currentImage->activeLayer()->setDirty(r);
00107             }
00108             else {
00109                 m_target->setDirty(r);
00110                 // Just update the canvas. XXX: After 1.5, find a better way to make sure tools don't set dirty what they didn't touch.
00111                 m_subject->canvasController()->updateCanvas( r );
00112             }
00113         }
00114     }
00115 }
00116 
00117 void KisToolFreehand::buttonRelease(KisButtonReleaseEvent* e)
00118 {
00119     if (e->button() == QMouseEvent::LeftButton && m_mode == PAINT) {
00120         endPaint();
00121         m_currentImage->activeDevice()->lock( false );
00122         kdDebug() << ">>>>>>>>>>>>>>>>>>>UNLocking paint device\n";
00123 
00124     }
00125     KisToolPaint::buttonRelease(e);
00126 }
00127 
00128 void KisToolFreehand::move(KisMoveEvent *e)
00129 {
00130     if (m_mode == PAINT) {
00131 
00132         paintLine(m_prevPos, m_prevPressure, m_prevXTilt, m_prevYTilt, e->pos(), e->pressure(), e->xTilt(), e->yTilt());
00133 
00134         m_prevPos = e->pos();
00135         m_prevPressure = e->pressure();
00136         m_prevXTilt = e->xTilt();
00137         m_prevYTilt = e->yTilt();
00138 
00139         QRect r = m_painter->dirtyRect();
00140 
00141         if (r.isValid()) {
00142             m_dirtyRect |= r;
00143 
00144             if (!m_paintOnSelection) {
00145                     m_currentImage->activeLayer()->setDirty(r);
00146             }
00147             else {
00148                 // Just update the canvas
00149                 r = QRect(r.left()-1, r.top()-1, r.width()+2, r.height()+2); //needed to update selectionvisualization
00150                 m_target->setDirty(r);
00151                 m_subject->canvasController()->updateCanvas( r );
00152             }
00153         }
00154     }
00155 }
00156 
00157 void KisToolFreehand::initPaint(KisEvent *)
00158 {
00159     if (!m_currentImage || !m_currentImage->activeDevice()) return;
00160 
00161     m_mode = PAINT;
00162     m_dragDist = 0;
00163 
00164     // Create painter
00165     KisPaintDeviceSP device;
00166     if (m_currentImage && (device = m_currentImage->activeDevice())) {
00167 
00168         if (m_painter)
00169             delete m_painter;
00170 
00171         if (!m_paintIncremental) {
00172             if (m_currentImage->undo())
00173                 m_currentImage->undoAdapter()->beginMacro(m_transactionText);
00174 
00175             KisLayerSupportsIndirectPainting* layer;
00176             if ((layer = dynamic_cast<KisLayerSupportsIndirectPainting*>(
00177                     m_currentImage->activeLayer().data()))) {
00178 
00179                 // Hack for the painting of single-layered layers using indirect painting,
00180                 // because the group layer would not have a correctly synched cache (
00181                 // because of an optimization that would happen, having this layer as
00182                 // projection).
00183                 KisLayer* l = layer->layer();
00184                 KisPaintLayer* pl = dynamic_cast<KisPaintLayer*>(l);
00185                 if (l->parent() && (l->parent()->parent() == 0)
00186                     && (l->parent()->childCount() == 1)
00187                     && l->parent()->paintLayerInducesProjectionOptimization(pl)) {
00188                     // If there's a mask, device could've been the mask. The induce function
00189                     // should catch this, but better safe than sorry
00190                     l->parent()->resetProjection(pl->paintDevice());
00191                 }
00192 
00193                 m_target = new KisPaintDevice(m_currentImage->activeLayer(),
00194                                               device->colorSpace());
00195                 layer->setTemporaryTarget(m_target);
00196                 layer->setTemporaryCompositeOp(m_compositeOp);
00197                 layer->setTemporaryOpacity(m_opacity);
00198 
00199                 if (device->hasSelection())
00200                     m_target->setSelection(device->selection());
00201             }
00202         } else {
00203             m_target = device;
00204         }
00205         if(m_target->hasSelection()) m_target->selection()->startCachingExactRect();
00206         m_painter = new KisPainter( m_target );
00207         Q_CHECK_PTR(m_painter);
00208         m_source = device;
00209         if (currentImage()->undo()) m_painter->beginTransaction(m_transactionText);
00210     }
00211 
00212     m_painter->setPaintColor(m_subject->fgColor());
00213     m_painter->setBackgroundColor(m_subject->bgColor());
00214     m_painter->setBrush(m_subject->currentBrush());
00215 
00216 
00217     // if you're drawing on a temporary layer, the layer already sets this
00218     if (m_paintIncremental) {
00219         m_painter->setCompositeOp(m_compositeOp);
00220         m_painter->setOpacity(m_opacity);
00221     } else {
00222         m_painter->setCompositeOp(COMPOSITE_ALPHA_DARKEN);
00223         m_painter->setOpacity( OPACITY_OPAQUE );
00224 
00225     }
00226 
00227 /*    kdDebug() << "target: " << m_target << "( " << m_target->name() << " )"
00228             << " source: " << m_source << "( " << m_source->name() << " )"
00229             << ", incremental " << m_paintIncremental
00230             << ", paint on selection: " << m_paintOnSelection
00231             << ", active device has selection: " << device->hasSelection()
00232             << ", target has selection: " << m_target->hasSelection()
00233             << endl;
00234 */
00235 }
00236 
00237 void KisToolFreehand::endPaint()
00238 {
00239     m_mode = HOVER;
00240     if (m_currentImage) {
00241 
00242         if (m_painter) {
00243             // If painting in mouse release, make sure painter
00244             // is destructed or end()ed
00245             if (!m_paintIncremental) {
00246                 if (m_currentImage->undo())
00247                     m_painter->endTransaction();
00248                 KisPainter painter( m_source );
00249                 painter.setCompositeOp(m_compositeOp);
00250                 if (m_currentImage->undo())
00251                     painter.beginTransaction(m_transactionText);
00252                 painter.bitBlt(m_dirtyRect.x(), m_dirtyRect.y(), m_compositeOp, m_target,
00253                                m_opacity,
00254                                m_dirtyRect.x(), m_dirtyRect.y(),
00255                                m_dirtyRect.width(), m_dirtyRect.height());
00256 
00257                 KisLayerSupportsIndirectPainting* layer =
00258                     dynamic_cast<KisLayerSupportsIndirectPainting*>(m_source->parentLayer());
00259                 layer->setTemporaryTarget(0);
00260                 m_source->parentLayer()->setDirty(m_dirtyRect);
00261 
00262                 if (m_currentImage->undo()) {
00263                     m_currentImage->undoAdapter()->addCommand(painter.endTransaction());
00264                     m_currentImage->undoAdapter()->endMacro();
00265                 }
00266             } else {
00267                 if (m_currentImage->undo())
00268                     m_currentImage->undoAdapter()->addCommand(m_painter->endTransaction());
00269             }
00270         }
00271         delete m_painter;
00272         m_painter = 0;
00273         notifyModified();
00274         if(m_target->hasSelection()) m_target->selection()->stopCachingExactRect();
00275     }
00276 }
00277 
00278 void KisToolFreehand::paintAt(const KisPoint &pos,
00279                const double pressure,
00280                const double xTilt,
00281                const double yTilt)
00282 {
00283     painter()->paintAt(pos, pressure, xTilt, yTilt);
00284 }
00285 
00286 void KisToolFreehand::paintLine(const KisPoint & pos1,
00287                  const double pressure1,
00288                  const double xtilt1,
00289                  const double ytilt1,
00290                  const KisPoint & pos2,
00291                  const double pressure2,
00292                  const double xtilt2,
00293                  const double ytilt2)
00294 {
00295     m_dragDist = painter()->paintLine(pos1, pressure1, xtilt1, ytilt1, pos2, pressure2, xtilt2, ytilt2, m_dragDist);
00296 }
00297 
00298 
00299 KisImageSP KisToolFreehand::currentImage()
00300 {
00301     return m_currentImage;
00302 }
00303 
00304 
00305 void KisToolFreehand::paintOutline(const KisPoint& point) {
00306     if (!m_subject) {
00307         return;
00308     }
00309 
00310     KisCanvasController *controller = m_subject->canvasController();
00311 
00312     if (currentImage() && !currentImage()->bounds().contains(point.floorQPoint())) {
00313         if (m_paintedOutline) {
00314             controller->kiscanvas()->update();
00315             m_paintedOutline = false;
00316         }
00317         return;
00318     }
00319 
00320     KisCanvas *canvas = controller->kiscanvas();
00321     canvas->repaint();
00322 
00323     KisBrush *brush = m_subject->currentBrush();
00324     // There may not be a brush present, and we shouldn't crash in that case
00325     if (brush) {
00326         KisCanvasPainter gc(canvas);
00327         QPen pen(Qt::SolidLine);
00328 
00329         KisPoint hotSpot = brush->hotSpot();
00330 
00331         gc.setRasterOp(Qt::NotROP);
00332         gc.setPen(pen);
00333         gc.setViewport(0, 0, static_cast<Q_INT32>(canvas->width() * m_subject->zoomFactor()),
00334                        static_cast<Q_INT32>(canvas->height() * m_subject->zoomFactor()));
00335         gc.translate((- controller->horzValue()) / m_subject->zoomFactor(),
00336                         (- controller->vertValue()) / m_subject->zoomFactor());
00337 
00338         KisPoint topLeft = point - hotSpot;
00339 
00340         if (m_subject->currentPaintop().id() == "pen") {
00341             // Pen paints on whole pixels only.
00342             topLeft = topLeft.roundQPoint();
00343         }
00344 
00345         gc.translate(topLeft.x(), topLeft.y());
00346 
00347         KisBoundaryPainter::paint(brush->boundary(), gc);
00348         m_paintedOutline = true;
00349     }
00350 }
00351 
00352 
00353 #include "kis_tool_freehand.moc"
00354 
KDE Home | KDE Accessibility Home | Description of Access Keys