diff options
Diffstat (limited to 'chalk/ui/kis_tool_freehand.cpp')
-rw-r--r-- | chalk/ui/kis_tool_freehand.cpp | 354 |
1 files changed, 354 insertions, 0 deletions
diff --git a/chalk/ui/kis_tool_freehand.cpp b/chalk/ui/kis_tool_freehand.cpp new file mode 100644 index 00000000..74eec406 --- /dev/null +++ b/chalk/ui/kis_tool_freehand.cpp @@ -0,0 +1,354 @@ +/* + * kis_tool_brush.cpp - part of Chalk + * + * Copyright (c) 2003-2004 Boudewijn Rempt <boud@valdyas.org> + * Copyright (c) 2004 Bart Coppens <kde@bartcoppens.be> + * + * 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 <tqevent.h> +#include <tqlabel.h> +#include <tqlayout.h> +#include <tqwidget.h> +#include <tqrect.h> + +#include <kdebug.h> +#include <tdeaction.h> +#include <kcommand.h> +#include <tdelocale.h> + +#include "kis_canvas_subject.h" +#include "kis_undo_adapter.h" +#include "kis_selection.h" +#include "kis_painter.h" +#include "kis_fill_painter.h" +#include "kis_tool_freehand.h" +#include "kis_cursor.h" +#include "kis_button_press_event.h" +#include "kis_button_release_event.h" +#include "kis_move_event.h" +#include "kis_layer.h" +#include "kis_group_layer.h" +#include "kis_paint_layer.h" +#include "kis_canvas.h" +#include "kis_canvas_painter.h" +#include "kis_boundary_painter.h" +#include "kis_brush.h" + +KisToolFreehand::KisToolFreehand(TQString transactionText) + : super(transactionText), + m_dragDist ( 0 ), + m_transactionText(transactionText), + m_mode( HOVER ) +{ + m_painter = 0; + m_currentImage = 0; + m_tempLayer = 0; + m_paintIncremental = true; + m_paintOnSelection = false; + m_paintedOutline = false; +} + +KisToolFreehand::~KisToolFreehand() +{ +} + +void KisToolFreehand::update(KisCanvasSubject *subject) +{ + super::update(subject); + m_currentImage = m_subject->currentImg(); +} + +void KisToolFreehand::buttonPress(KisButtonPressEvent *e) +{ + if (!m_subject) return; + + if (!m_subject->currentBrush()) return; + + if (!m_currentImage || !m_currentImage->activeDevice()) return; + + if (e->button() == Qt::LeftButton) { + + m_currentImage->activeDevice()->lock( true ); + kdDebug() << ">>>>>>>>>>>>>>>>>>>Locking paint device\n"; + + // People complain that they can't start brush strokes outside of the image boundaries. + // This makes sense, especially when combined with BUG:132759, so commenting out the + // next line makes sense. + //if (!m_currentImage->bounds().contains(e->pos().floorTQPoint())) return; + + initPaint(e); + paintAt(e->pos(), e->pressure(), e->xTilt(), e->yTilt()); + + m_prevPos = e->pos(); + m_prevPressure = e->pressure(); + m_prevXTilt = e->xTilt(); + m_prevYTilt = e->yTilt(); + + TQRect r = m_painter->dirtyRect(); + if ( r.isValid() ) { + m_dirtyRect = r; + + r = TQRect(r.left()-1, r.top()-1, r.width()+2, r.height()+2); //needed to update selectionvisualization + if (!m_paintOnSelection) { + m_currentImage->activeLayer()->setDirty(r); + } + else { + m_target->setDirty(r); + // 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. + m_subject->canvasController()->updateCanvas( r ); + } + } + } +} + +void KisToolFreehand::buttonRelease(KisButtonReleaseEvent* e) +{ + if (e->button() == Qt::LeftButton && m_mode == PAINT) { + endPaint(); + m_currentImage->activeDevice()->lock( false ); + kdDebug() << ">>>>>>>>>>>>>>>>>>>UNLocking paint device\n"; + + } + KisToolPaint::buttonRelease(e); +} + +void KisToolFreehand::move(KisMoveEvent *e) +{ + if (m_mode == PAINT) { + + paintLine(m_prevPos, m_prevPressure, m_prevXTilt, m_prevYTilt, e->pos(), e->pressure(), e->xTilt(), e->yTilt()); + + m_prevPos = e->pos(); + m_prevPressure = e->pressure(); + m_prevXTilt = e->xTilt(); + m_prevYTilt = e->yTilt(); + + TQRect r = m_painter->dirtyRect(); + + if (r.isValid()) { + m_dirtyRect |= r; + + if (!m_paintOnSelection) { + m_currentImage->activeLayer()->setDirty(r); + } + else { + // Just update the canvas + r = TQRect(r.left()-1, r.top()-1, r.width()+2, r.height()+2); //needed to update selectionvisualization + m_target->setDirty(r); + m_subject->canvasController()->updateCanvas( r ); + } + } + } +} + +void KisToolFreehand::initPaint(KisEvent *) +{ + if (!m_currentImage || !m_currentImage->activeDevice()) return; + + m_mode = PAINT; + m_dragDist = 0; + + // Create painter + KisPaintDeviceSP device; + if (m_currentImage && (device = m_currentImage->activeDevice())) { + + if (m_painter) + delete m_painter; + + if (!m_paintIncremental) { + if (m_currentImage->undo()) + m_currentImage->undoAdapter()->beginMacro(m_transactionText); + + KisLayerSupportsIndirectPainting* layer; + if ((layer = dynamic_cast<KisLayerSupportsIndirectPainting*>( + m_currentImage->activeLayer().data()))) { + + // Hack for the painting of single-layered layers using indirect painting, + // because the group layer would not have a correctly synched cache ( + // because of an optimization that would happen, having this layer as + // projection). + KisLayer* l = layer->layer(); + KisPaintLayer* pl = dynamic_cast<KisPaintLayer*>(l); + if (l->parent() && (l->parent()->parent() == 0) + && (l->parent()->childCount() == 1) + && l->parent()->paintLayerInducesProjectionOptimization(pl)) { + // If there's a mask, device could've been the mask. The induce function + // should catch this, but better safe than sorry + l->parent()->resetProjection(pl->paintDevice()); + } + + m_target = new KisPaintDevice(m_currentImage->activeLayer(), + device->colorSpace()); + layer->setTemporaryTarget(m_target); + layer->setTemporaryCompositeOp(m_compositeOp); + layer->setTemporaryOpacity(m_opacity); + + if (device->hasSelection()) + m_target->setSelection(device->selection()); + } + } else { + m_target = device; + } + if(m_target->hasSelection()) m_target->selection()->startCachingExactRect(); + m_painter = new KisPainter( m_target ); + TQ_CHECK_PTR(m_painter); + m_source = device; + if (currentImage()->undo()) m_painter->beginTransaction(m_transactionText); + } + + m_painter->setPaintColor(m_subject->fgColor()); + m_painter->setBackgroundColor(m_subject->bgColor()); + m_painter->setBrush(m_subject->currentBrush()); + + + // if you're drawing on a temporary layer, the layer already sets this + if (m_paintIncremental) { + m_painter->setCompositeOp(m_compositeOp); + m_painter->setOpacity(m_opacity); + } else { + m_painter->setCompositeOp(COMPOSITE_ALPHA_DARKEN); + m_painter->setOpacity( OPACITY_OPAQUE ); + + } + +/* kdDebug() << "target: " << m_target << "( " << m_target->name() << " )" + << " source: " << m_source << "( " << m_source->name() << " )" + << ", incremental " << m_paintIncremental + << ", paint on selection: " << m_paintOnSelection + << ", active device has selection: " << device->hasSelection() + << ", target has selection: " << m_target->hasSelection() + << endl; +*/ +} + +void KisToolFreehand::endPaint() +{ + m_mode = HOVER; + if (m_currentImage) { + + if (m_painter) { + // If painting in mouse release, make sure painter + // is destructed or end()ed + if (!m_paintIncremental) { + if (m_currentImage->undo()) + m_painter->endTransaction(); + KisPainter painter( m_source ); + painter.setCompositeOp(m_compositeOp); + if (m_currentImage->undo()) + painter.beginTransaction(m_transactionText); + painter.bitBlt(m_dirtyRect.x(), m_dirtyRect.y(), m_compositeOp, m_target, + m_opacity, + m_dirtyRect.x(), m_dirtyRect.y(), + m_dirtyRect.width(), m_dirtyRect.height()); + + KisLayerSupportsIndirectPainting* layer = + dynamic_cast<KisLayerSupportsIndirectPainting*>(m_source->parentLayer()); + layer->setTemporaryTarget(0); + m_source->parentLayer()->setDirty(m_dirtyRect); + + if (m_currentImage->undo()) { + m_currentImage->undoAdapter()->addCommand(painter.endTransaction()); + m_currentImage->undoAdapter()->endMacro(); + } + } else { + if (m_currentImage->undo()) + m_currentImage->undoAdapter()->addCommand(m_painter->endTransaction()); + } + } + delete m_painter; + m_painter = 0; + notifyModified(); + if(m_target->hasSelection()) m_target->selection()->stopCachingExactRect(); + } +} + +void KisToolFreehand::paintAt(const KisPoint &pos, + const double pressure, + const double xTilt, + const double yTilt) +{ + painter()->paintAt(pos, pressure, xTilt, yTilt); +} + +void KisToolFreehand::paintLine(const KisPoint & pos1, + const double pressure1, + const double xtilt1, + const double ytilt1, + const KisPoint & pos2, + const double pressure2, + const double xtilt2, + const double ytilt2) +{ + m_dragDist = painter()->paintLine(pos1, pressure1, xtilt1, ytilt1, pos2, pressure2, xtilt2, ytilt2, m_dragDist); +} + + +KisImageSP KisToolFreehand::currentImage() +{ + return m_currentImage; +} + + +void KisToolFreehand::paintOutline(const KisPoint& point) { + if (!m_subject) { + return; + } + + KisCanvasController *controller = m_subject->canvasController(); + + if (currentImage() && !currentImage()->bounds().contains(point.floorTQPoint())) { + if (m_paintedOutline) { + controller->kiscanvas()->update(); + m_paintedOutline = false; + } + return; + } + + KisCanvas *canvas = controller->kiscanvas(); + canvas->repaint(); + + KisBrush *brush = m_subject->currentBrush(); + // There may not be a brush present, and we shouldn't crash in that case + if (brush) { + KisCanvasPainter gc(canvas); + TQPen pen(TQt::SolidLine); + + KisPoint hotSpot = brush->hotSpot(); + + gc.setRasterOp(TQt::NotROP); + gc.setPen(pen); + gc.setViewport(0, 0, static_cast<TQ_INT32>(canvas->width() * m_subject->zoomFactor()), + static_cast<TQ_INT32>(canvas->height() * m_subject->zoomFactor())); + gc.translate((- controller->horzValue()) / m_subject->zoomFactor(), + (- controller->vertValue()) / m_subject->zoomFactor()); + + KisPoint topLeft = point - hotSpot; + + if (m_subject->currentPaintop().id() == "pen") { + // Pen paints on whole pixels only. + topLeft = topLeft.roundTQPoint(); + } + + gc.translate(topLeft.x(), topLeft.y()); + + KisBoundaryPainter::paint(brush->boundary(), gc); + m_paintedOutline = true; + } +} + + +#include "kis_tool_freehand.moc" + |