Hacking on selections

Good progress today… I discovered yesterday that I had about ten more holidays left than I thought the day before yesterday, so I took two days off to hack on Krita. (Pity I didn’t know earlier, or I’d have been able to go to either aKademy or to the monastery in Hemelum — both equally attractive propositions.)

Today I got the selection code factored out of the humongous KisView class. If there’s one thing I dislike about Qt coding, it’s the tendency to have a View or Window class that’s unmanageable because all actions, signals & slots are tied together in that single place. KisSelectionManager takes care of everything associated with selecting, cutting and pasting, and one day I’m going to do the same for the other chunks of functionality in KisView, such as the layer handling. Small is beautiful. Small classes, small packages.

Apart from that internal refactoring which users won’t see, I’ve gotten select all, deselect and invert selection to work properly, done the enabling/disabling of selection actions and added actions for all the cool things you know you want to do with selections, like growing, feathering and all that.

Now I just need to implement all those actions, fix the selection tools, make selection-aware iterators, fix blitting so only selected pixels are blitted and link the selection to its layer. And implement loading/saving of selections.

Krita selections, by the way, are not your old-fashioned marching ant selections, but closer to Gimp and Photoshop masks, except that I understand what they do. I feel that Krita’s selection are pretty intuitive and easy to handle. And it’s a win that I didn’t have to figure out how to let ants march along the edges of a discontinuous selection represented by a bytemap.

Shapes…

Clarence Dang has just made Krita capable of drawing not just lines, but ellipses and squares in any brush! In other news, I have somehow messed up the ksnapshotwidget.ui.h file, and now my screenshot plugin is broken… Anyway, enjoy the spectacle provided by Krita’s new features:

Screenshots and scaling

I’ve finally discovered why image resizing didn’t work when scaling: I believe that when scaling the background layer the projection layer is scaled automatically. Scaling it on its own makes it much too small,  hence the blacked-out backgrounds I was having. And I hacked the old KOffice scan plugin and KSnapshot together into a Krita plugin, so I can make snaps of Krita, downscale them — and presto, Krita is usable for maintaining its own website

Pity the scaling uses a simple matrix transform, instead of something
fancy like bicubic scaling, but soit, honi soit qui mal y pense.

Some progress, at least

Progress with Krita has been slow, the past two weeks, considering that I’ve been at home nearly permanently. Still, I’ve been working on some things, and thinking about others.

There’s now an eraser tool, and a line tool, and I made a start porting the stamp tool. At least, I thought that it was a stamp tool, but it turns out it is a tool that paints with patterns instead of brushes, not something that takes the last selected bit of the image, and paints with that. Perhaps I’d better wait for Patrick Julien to checkin the odds and ends for selecting and pasting he says he has lying about, and make a start on a convolve tool.

I also ported (that’s to say, copy, paste, search & replace) a large number of less interesting composition operations to Krita from GraphicsMagick, after I was given the green light by Leonard Rosenthol about the licensing. The more interesting composition operations are harder to port, since they either work on a whole image at a time, instead of on regions, or they use a number of extra functions from GraphicsMagick that I don’t feel the urge to port, too.

A persistent pain in the neck with working on Krita (or C++, perhaps, I am not sure), is the hard-coded nature of everything. With Python I can define a number of classes, perhaps even dynamically, read a list of classnames in a Python list from a configuration file, iterate over the list and instantiate the classes — easy as pie. But that’s because in Python, classes are an object; in C++ classes are — I don’t know what, exactly, but not objects, and no fun either. Look at Python:

class A: pass 
class B: pass
class C: pass
class D: pass

l = []

for clsname in file("classes.cfg").read().split():
	l.append(eval(clsname))	
print l

Of course, this is a very simple-minded example, but image a situation where you have a large number of classes, say — for the sake of argument — composition operators, one class per op, and you have a smaller number of image types, and some comp. ops. are useful for all image types, others only for a few, and others again for only one. The GUI, of course, must show only the relevant composition ops in the various listboxes. And when I add one, I don’t want to have to go through all the code to add those.

But still, I’m prepared to do precisely that: hardcode the instances of the relevant ops in the correct places, Krita, however, currently doesn’t have anything like that. There is a long enumeration in kis_global.h; there are user-visible string somewhere else, and a big switch statement that uses the enum to switch between function calls.

Here, I should create a factory that creates all defined ops, and some kind of a database that ties together all functionality, preferably defined in a configuration file, but hardcoding will do for now. I’m not sure about naming; I need something that forms the basis of all these capabilities that are now described in enums, and inherit from there.

Likewise, KisPainter is big monolith that looks a lot like QPainter. It combines geometric functions with simple paint functions, but that’s unpleasant, because I would prefer to have one function for creating lines that I can feed not only brush types and things, but also paint operations: hard paint, soft paint, airbrush, convolve, erase — so, instead of:

void eraseCircle(Qpoint p1, Q_INT32 radius);
void eraseLine(QPoint p1, QPoint p2);
void eraseAt(QPoint p);

void eraseLine(Qpoint p1, Q_INT32 radius);
void paintLine(QPoint p1, QPoint p2):
void paintAt(QPoint p)

void convolveCircle(...

I’d like:

class PaintOp;
class erase;
class convolve;
class paint;

class KisPainter{
...
void line(const PaintOp & op, QPoint p1, QPoint p2);
void circle(const PaintOp & op, QPoint p1, Q{Point p2);

Etcetera… Add in a CompositeOp class hierarchy, and life will almost seem to be as sweet as Python makes possible again. Of course, there’s more… We already have a KisResource structure that handles brushes, patterns and other bits of artistic licentiousness that are read from disk; here we have a lot of code duplication, even though the process is fairly dynamic.

And I should finish that convolve tool.

No hacking…

Hacking is just as much a necessity of life as breathing, writing or eating. But sometimes, it’s impossible to go on working, even though you’re all in the flow.

Like when Johanna decides that she’ll punish you for putting a row of books on her favourite sleeping spot; she won’t move to the other side of the desk, but in front of kis_strategy_colorspace_rgb.cc

The books have been put back to the left side of the desk, where I prefer to push my monitor so I have some deskspace for reading, but well, one  cannot have everything, and it is nice, having a sleeping ball of black, cuddly fur on your desk while working.

(And the other reason for not working on Krita is that the fever has dropped off a bit, and I’m just too tired right now, so excuse no new commits, I’m  just goofing off and looking through my stack of photos…Here’s a piccy of another cat, Hendrik, reading Dostoievsky, which I think is pretty nice.)

Worrying about composition

Gah! I never had proper maths at school, and I dare say that if someone had tried to teach me maths at the tender age of eleven or twelve, I’d probably have bucked like race horse that’s harnessed in front of a brewery sledge. But the fact that my maths teacher walked out of the class when I was in the second form, to never return, didn’t help either. Not that I ever thought I had missed something essential.

Until now, that is. Graphics == maths, it seems. And not simple algebra eiher. So I am trying to understand what the various composition modes do — reading the literature, starting with the Porter-Duff article that appears to have started it all, and working my way through the GraphicsMagick source code, and I am finding it hard going.

And then there’s the mess that Krita’s UI is, a rich archeological dig where you can find code from 1999 to 2003, all written by different people with different goals and different styles, and practically nothing finished, or even in a working state. I should scrap that GUI and start again, only that’s going to take even more time from that most elusive of goals: a working eraser too.

Anyway, here’s a snippet of GraphicsMagick code, for the ‘in’ composition operation:

	if (source.opacity == TransparentOpacity)
	{
		destination=source;
		break;
	}
	if (destination.opacity == TransparentOpacity)
		break;
	pixel.opacity=(double) (((double) MaxRGB-source.opacity)*
				(MaxRGB-destination.opacity)/MaxRGB);

	destination.red=(Quantum) (((double) MaxRGB-source.opacity)*
	   (MaxRGB-destination.opacity)*source.red/MaxRGB/pixel.opacity+0.5);

	destination.green=(Quantum) (((double) MaxRGB-source.opacity)*
	     (MaxRGB-destination.opacity)*source.green/MaxRGB/pixel.opacity+0.5);

	destination.blue=(Quantum) (((double) MaxRGB-source.opacity)*
	    (MaxRGB-destination.opacity)*source.blue/MaxRGB/pixel.opacity+0.5);
	destination.opacity=(Quantum) (MaxRGB-pixel.opacity+0.5);
	break;

And here’s how I think it should be translated into Krita:

d = dst;
s = src;

if (d[PIXEL_ALPHA] == OPACITY_TRANSPARENT)
	continue;

if (s[PIXEL_ALPHA] == OPACITY_TRANSPARENT) {
	memcpy(d, s, stride * sizeof(QUANTUM));
	continue;

}

alpha = (double) (((double) QUANTUM_MAX - s[PIXEL_ALPHA]) * (QUANTUM_MAX - d[PIXEL_ALPHA]) / QUANTUM_MAX);
d[PIXEL_RED]   = (QUANTUM) (((double) QUANTUM_MAX - s[PIXEL_ALPHA]) * (QUANTUM_MAX - d[PIXEL_ALPHA]) * s[PIXEL_RED] / QUANTUM_MAX / alpha + 0.5);
d[PIXEL_GREEN] = (QUANTUM) (((double) QUANTUM_MAX - s[PIXEL_ALPHA]) * (QUANTUM_MAX - d[PIXEL_ALPHA]) * s[PIXEL_GREEN] / QUANTUM_MAX / alpha + 0.5);
d[PIXEL_BLUE]  = (QUANTUM) (((double) QUANTUM_MAX - s[PIXEL_ALPHA]) * (QUANTUM_MAX - d[PIXEL_ALPHA]) * s[PIXEL_BLUE] / QUANTUM_MAX / alpha + 0.5);
d[PIXEL_ALPHA] = (QUANTUM) (QUANTUM_MAX - alpha + 0.5);

dst += dststride;
src += srcstride;

But it doesn’t seem to work:

I am not completely sure what ‘in’ ought to do, but I am fairly certain that it isn’t ‘paint vertical black lines’ if I’ve selected a purple brush…

Gimp’s pipe brushes

The Gimp nowadays comes with a small set of rather nifty brushes — the so-called pipe brushes, recognizable from the file-extension .gih. The fileformat for these brushes is actually really horrible, a mix between text and binary. The first line contains the brush name, the second the number of brushes the brush contains, a space, and a bit of text detailing the way the brush ought to be used.

Today (and yesterday) I spent time creating a class that could load those brushes, and actually use them. Well, that’s done. I still don’t parse the bit of text — a ‘parasite’ in Gimp parlance — so you just get all the images in the brush in order, and I don’t make a difference between brushes that are really gray-scale masks to be used with the currently selected colour, and the brushes that are really collections of images that can be pasted into the drawing directly.

But on the other hand, I support pressure-sensitivity for every pipe brush:

Pity that pre-computing all those masks takes such a lot of memory. Krita now wants about 256 MB of memory at startup…

Pressure-sensitivity support in Krita

I just completed the first stab at pressure sensitivity support… It’s still slow, and not completely correct in all places, but you can make beautiful galaxies with it and gorgeous blobs that look like you’ve been painting with a brush and thick india-ink.

I’m rather looking forward, too, to re-activating a few of the other tools. I’ve got some ideas about a nice airbrush tool, one that really fakes an airbrush instead of spattering random dots (although there’s a tool like that in real life too — anyone remember the toothbrush and spatterboard from kindergarten). And a line tool is going to be easy now…

Finally a decent brush!

I’ve finally discovered how to do a decent brush in Krita. Turns out that the most time-intensive bit was the redrawing of the picture. This happened a lot in the in-betweening code, that painted a line between the previous and the current mouse position if Krita couldn’t keep up with the mouse.

So, now I update only once per mouse-move event, and all is pretty:

It’s not as good as the Gimp’s brush, and circles tend to come out a bit angular, but I am pretty chuffed with this result anyway.

Learning C++ and achieving a decent brush

I am finally being developing the feeling that I am starting to begin achieving the first step towards a modicum of confidence in my ability to achieve a moderate competence in C++. That’s to say, last night I spent a few hours hacking the KisToolBrush class for Krita. I want to achieve what nearly all paint applications manage to achieve: to draw a beautiful, antialiased line that accurately follows the mouse or stylus and is painted in the right colour, gradient or pattern using the correct brush. And I don’t seem able to figure out how to do that.

However, in the process of trying various approaches, I noticed that I was producing new code, instead of cut & paste jobs, and code that was working after only two compiles (the first to fix the typoes and add any semicolons that had shimmied off, the second to actually compile all the code). And when I say working, I mean that the code did what I wanted it to do. Always excepting my naive implementation of Bresenham’s line drawing algorithm — I hadn’t understood that I needed to implement it four times, one for ever major direction. Anyway, keying in code and seeing it compile and work feels good, even though I still have trouble with the *&->.! pointers and references, and especially with const.

Brushes

A propos of brushes, I have tried the following approaches:

Create a small layer that contains a colored version of the brush shape, and bitBlt it to the target layer on every mouse move event. This left huge gaps where Krita was too busy to allow Qt to push fresh mouse events. It seems that this is a standard feature of GUI toolkits.

Ditto, but copy the old in-betweening code from the early days of Krita. This uses a rather complex KisVector vector class and does a lot of computing to calculate where to place the points in between the positions we get from mouse move events. This was really, really slow; slow enough that when you tried to paint a circle with any celerity, you’d get a straight line from the point where you pressed the mouse button to the point where you released the mouse button.

See the first attempt, but now don’t paint until the mouse button is released. Show the path using a simple QPainter based line, and collect all mouse positions along the way. On mouse-up, fill the path with the brush. This is what I was doing yesterday evening; I still need to do the brush drawing on mouse-up. I suspect that this will also be too slow to be useful.

Mooching code

Of course, when I couldn’t find a way myself, I took a look really hard at other OSS paint applications to see how they manage… I have found the following application that could, or could not teach me the tricks of the trade:

    • Perico, by Peter Jodda. This application has the advantage that it is written using Qt, just like Krita, and that it is well-commented. However, his backend is fairly simple, doesn’t use tiles, and his paint tool doesn’t use brush masks. He paints a simple ellipse on every mouse move event, and inbetweens using an algorithm that I am going to copy tonight.
    • The Gimp. Now nearing version 2.0 (development started in 1994 or 1995, first using Motif, then using a purpose-built toolkit, GTK), the Gimp is everything that Krita still isn’t. And Krita is now in its fifth year of development. I rather suspect that GDK, the underlying layer between the GTK and X11, does a lot of useful work for paint applications. The Gimp takes the whole event loop, picks the most useful and likely events, returns the other events back to the GDK event loop and starts processing the events, in between updating cursor shapes and things. This is complex code, full of special cases and optimizations that I don’t understand, but the most important clues appear to be in the display/gimpdisplayshell-callbacks.c file. To be investigated more fully.
    • GSumi. This application is so weird I just don’t get anything about it…
    • KolourPaint. This uses QPainter/QPaintDevice for its core, and as such is fast, but not really suitable as an example. As you can see from the image above, I already can use QPainter, and it’s not enough.
    • Wet Dreams. This is an example I am going to copy in any case, but interestingly, there doesn’t appear to be any special connect-the-dots code, as you can see from the screenshot:

Showing the code

Oh, well, I knew I wasn’t competent when I started doing this: it’s all a learning exercise… Here’s the source, with all appropriate ifdefs:


/*
 *  Copyright (c) 2003 Boudewijn Rempt <boud@valdyas.org>
 *
 *  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

#include "kis_vec.h"
#include "kis_painter.h"
#include "kis_selection.h"
#include "kis_doc.h"
#include "kis_view.h"
#include "kis_tool_brush.h"
#include "kis_tool_paint.h"
#include "kis_layer.h"
#include "kis_alpha_mask.h"
#include "kis_cursor.h"

KisToolBrush::KisToolBrush()
        : super(),
          m_mode( HOVER ),
	  m_hotSpotX ( 0 ),
	  m_hotSpotY ( 0 ),
	  m_brushWidth ( 0 ),
	  m_brushHeight ( 0 ),
	  m_spacing ( 1 ),
	  m_dragDist ( 0 ),
	  m_usePattern ( false ),
	  m_useGradient ( false )
{
	setCursor(KisCursor::crossCursor());

        m_painter = 0;
	m_dab = 0;
	m_points = 0;
}

KisToolBrush::~KisToolBrush()
{
	if (m_points) delete m_points;
}

void KisToolBrush::update(KisCanvasSubject *subject)
{
	m_subject = subject;
	super::update(m_subject);
}

void KisToolBrush::paint(QPainter& gc)
{
 	if (m_mode == PAINT)
 		paintLine(gc, QRect());
}

void KisToolBrush::paint(QPainter& gc, const QRect& rc)
{
 	if (m_mode == PAINT)
 		paintLine(gc, rc);
}


void KisToolBrush::mousePress(QMouseEvent *e)
{
        if (!m_subject) return;

        if (!m_subject->currentBrush()) return;

        if (e->button() == QMouseEvent::LeftButton) {
		kdDebug() << "mouse press button:" << e->button() << "\n"; m_mode = PAINT; initPaint(); // Remember the startposition of the stroke #if 0 m_dragStart = e -> pos();
		m_dragDist = 0;
#endif
		if (m_points) delete m_points;

		m_points = new QPointArray(1);
		
		m_points -> setPoint(0, translateImageXYtoViewPort( e->pos()));
		
                paint(e->pos(), 128, 0, 0);
         }
}


void KisToolBrush::mouseRelease(QMouseEvent* e)
{
	if (e->button() == QMouseEvent::LeftButton && m_mode == PAINT) {
		endPaint();
        }
}


#if 1
void KisToolBrush::mouseMove(QMouseEvent *e) {
	if (m_mode == PAINT) {
		Q_INT32 s = m_points -> size();
		m_points -> resize(s + 1);
		m_points -> setPoint( s, translateImageXYtoViewPort( e->pos()));
		paintLine( s - 1 );
		return;
	}
}
#endif

#if 0
void KisToolBrush::mouseMove(QMouseEvent *e)
{
	// XXX: Funny, this: the mouse button of a mouse-move event is always 0; this problably means
	// I should be checking the status of every button here.
	// XXX: Even if I accept all events, playing around with the stylus gives two or three spurious
	// mouse-move events if I lift the stylus from the pad.
	if (m_mode == PAINT) {
  		QPoint pos = e -> pos();
#if 0
		paint(pos, 128, 0, 0);
		return;
#endif
		KisVector end(pos.x(), pos.y());
		KisVector start(m_dragStart.x(), m_dragStart.y());
		KisVector dragVec = end - start;
		float savedDist = m_dragDist;
		float newDist = dragVec.length();
		float dist = savedDist + newDist;

		if (static_cast(dist) < m_spacing) { m_dragDist += newDist; m_dragStart = pos; return; } m_dragDist = 0; #if 0 dragVec.normalize(); // XX: enabling this gives a link error, so copied the relevant code below. #endif double length, ilength; double x, y, z; x = dragVec.x(); y = dragVec.y(); z = dragVec.z(); length = x * x + y * y + z * z; length = sqrt (length); if (length) { ilength = 1/length; x *= ilength; y *= ilength; z *= ilength; } dragVec.setX(x); dragVec.setY(y); dragVec.setZ(z); KisVector step = start; while (dist >= m_spacing) {
			if (savedDist > 0) {
				step += dragVec * (m_spacing - savedDist);
				savedDist -= m_spacing;
			}
			else {
				step += dragVec * m_spacing;
			}
			QPoint p(qRound(step.x()), qRound(step.y()));
			paint(p, 128, 0, 0);
			kdDebug() << "paint: (" << p.x() << "," << p.y() << ")\n"; dist -= m_spacing; } if (dist > 0)
			m_dragDist = dist;

		m_dragStart = pos;
         }
}
#endif

void KisToolBrush::tabletEvent(QTabletEvent *e)
{
         if (e->device() == QTabletEvent::Stylus) {
		 if (!m_subject) {
			 e->accept();
			 return;
		 }

		 if (!m_subject->currentBrush()) {
			 e->accept();
			 return;
		 }

		 Q_INT32 pressure = e -> pressure();

		 if (pressure < 5 && m_mode == PAINT_STYLUS) { endPaint(); } else if (pressure >= 5 && m_mode == HOVER) {
			 m_mode = PAINT_STYLUS;
			 initPaint();
			 paint(e->pos(), e->pressure(), e->xTilt(), e->yTilt());
		 }
		 else if (pressure >= 5 && m_mode == PAINT_STYLUS) {
			 kdDebug() << "Tablet: painting " << e->pos().x() << ", " << e -> pos().y() << "\n"; paint(e->pos(), e->pressure(), e->xTilt(), e->yTilt());
		 }
         }
	 e->accept();
}


void KisToolBrush::initPaint()
{
	// Create painter
	KisImageSP currentImage = m_subject -> currentImg();
	KisPaintDeviceSP device;
	if (currentImage && (device = currentImage -> activeDevice())) {
		if (m_painter)
			delete m_painter;
		m_painter = new KisPainter( device );
		m_painter->beginTransaction("brush");
	}

	// Retrieve and cache brush data. XXX: this is not ideal, since it is
	// done for every stroke, even if the brush and colour have not changed.
	// So, more work is done than is necessary.
	KisBrush *brush = m_subject -> currentBrush();
	KisAlphaMask *mask = brush -> mask();
	m_brushWidth = mask -> width();
	m_brushHeight = mask -> height();

	m_hotSpot = brush -> hotSpot();
	m_hotSpotX = m_hotSpot.x();
	m_hotSpotY = m_hotSpot.y();

	m_spacing = brush -> spacing();
	if (m_spacing <= 0) { m_spacing = brush -> width();
	}
	
	// Set the cursor -- ideally. this should be a pixmap created from the brush,
	// now that X11 can handle colored cursors.

#if 0
	// Setting cursors has no effect until the tool is selected again; this
	// should be fixed.
	setCursor(KisCursor::brushCursor());
#endif


	// Create dab
	m_dab = new KisLayer(mask -> width(),
			     mask -> height(),
			     currentImage -> imgType(),
			     "dab");
        m_dab -> opacity(OPACITY_TRANSPARENT);
	for (int y = 0; y < mask -> height(); y++) {
		for (int x = 0; x < mask -> width(); x++) {
                        m_dab -> setPixel(x, y, m_subject -> fgColor(), mask -> alphaAt(x, y));
                }
        }
}

void KisToolBrush::endPaint() 
{
	m_mode = HOVER;
	KisImageSP currentImage = m_subject -> currentImg();
	KisPaintDeviceSP device;
	if (currentImage && (device = currentImage -> activeDevice())) {
		KisUndoAdapter *adapter = currentImage -> undoAdapter();
		if (adapter && m_painter) {
			// If painting in mouse release, make sure painter
			// is destructed or end()ed
			adapter -> addCommand(m_painter->endTransaction());
		}
		delete m_painter;
		m_painter = 0;
		m_dab = 0; // XXX: No need to delete m_dab because shared pointer?
	}
}

void KisToolBrush::paint(const QPoint & pos,
                         const Q_INT32 /*pressure*/,
                         const Q_INT32 /*xTilt*/,
                         const Q_INT32 /*yTilt*/)
{
#if 0
        kdDebug() << "paint: " << pos.x() << ", " << pos.y() << endl; #endif Q_INT32 x = pos.x() - m_hotSpotX; Q_INT32 y = pos.y() - m_hotSpotY; KisImageSP currentImage = m_subject -> currentImg();

        if (!currentImage) return;

        // Blit the temporary KisPaintDevice onto the current layer
        KisPaintDeviceSP device = currentImage -> activeDevice();
        if (device) {
                m_painter->bitBlt( x,  y,  COMPOSITE_NORMAL, m_dab.data() );
        }
        currentImage -> notify(x,
			       y,
			       m_dab -> width(),
			       m_dab -> height());
}


void KisToolBrush::setup(KActionCollection *collection)
{
        KToggleAction *toggle;
        toggle = new KToggleAction(i18n("&Brush"),
				"handdrawn", 0, this,
                                   SLOT(activate()), collection,
                                   "tool_brush");
        toggle -> setExclusiveGroup("tools");
}


void KisToolBrush::paintLine(Q_INT32 s)
{
	if (m_subject) {
		KisCanvasControllerInterface *controller = m_subject -> canvasController();
		QWidget *canvas = controller -> canvas();
		QPainter gc(canvas);
		QRect rc;

		paintLine(gc, rc, s);
	}
}

void KisToolBrush::paintLine(QPainter& gc, const QRect&, Q_INT32 s)
{
	if (m_subject) {

		RasterOp op = gc.rasterOp();
		QPen old = gc.pen();
		QPen pen( Qt::SolidLine );

		gc.setRasterOp(Qt::NotROP);
		gc.setPen(pen);

		gc.drawPolyline(*m_points, s);

		gc.setRasterOp(op);
		gc.setPen(old);
	}
}

QPoint KisToolBrush::translateImageXYtoViewPort(const QPoint& p) {
		KisCanvasControllerInterface *controller = m_subject -> canvasController();
		Q_ASSERT(controller);
		QPoint p2;
		p2 = controller -> viewToWindow(p);

		p2.setX(p2.x() - controller -> horzValue());
		p2.setY(p2.y() - controller -> vertValue());


		p2 *= m_subject -> zoomFactor();

		return p2;
}