You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
355 lines
12 KiB
355 lines
12 KiB
/*
|
|
* kis_tool_brush.cc - 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 <kaction.h>
|
|
#include <kcommand.h>
|
|
#include <klocale.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 );
|
|
Q_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"
|
|
|