This week was devoted to bug fixing. I started with line algorithms. We had some various todo’s in our KisPainter’s line rasterization routines. KisPainter is something like QPainter in Qt, but works with our KisPaintDevice. KisPaintDevice is pixel buffer with no boundaries (infinite buffer if you have infinite memory! But Chuck Norris was able to fill it anyway it is bounded from qint32_MIN to qint32_MAX pixels) and can store pixels with various bit depth (8-bit,16-bit,…, various colorspaces supported in Krita).
We had API bug in the rasterization. KisPainter has KisPaintDevice and can operate on it. The rasterization line algorithms used iterators and overwrite pixels with memcpy, which was wrong. Correct way is to respect the actual composite operation. Also opacity of the painter (alpha) was not respected. Guess who fixed it? Yes, me. Now the routines are little slower due to using compositing, but that is unavoidable. What is this good for? E.g. now if you render some brush mask or anything, the lines in the mask can be composited with different composite modes (Alpha blending, Multiply, Add, Divide, etc. ) and every line could have different opacity. That would be usable for hatching brush or new cooked sketch brush I’m working on in my spare time (which actually does not exist much these days) or even in hairy brush somehow. Anyway it is usable and it was a todo to fix
I fixed also DDA algorithm, which we use for rasterization of the aliased lines. Rounding the coordinates is not always correct, this time ceil was needed. This fixed bug in Pen brush when 1 pixel line is used. We could use Bresenham’s code which is known to be faster, but when I did benchmarking last time, the result was that DDA was slightly faster on my laptop. Maybe my implementation was bad. It was long time ago .
For anti-aliased lines with 1 pixel width we use Wu lines. For anti-aliased with variable width we use drawThickLine method, which has ugly endpoints. I decided that we will reuse rasterization routines from QPainter. It has nice lines with nice styles and cool endpoints. But because Krita is using higher bit depth, it has to be done little different. First we render the curve saved in QPainterPath with some style saved in QPen into QImage as black&white mask. We fill our pixel buffer with e.g. 16-bit colorspace pixels and we use QImage mask on it. It is slower 10-times as our routine for painting anti-aliased lines. But it works. It’s on my TODO to fix the performance of it.
Deevad reported the bug about general usability of Krita, brush outlines. I worked on outlines before, so I was assigned to this bug. It was quite complicated before to construct outline as many transformations was needed. In Krita we have pixel coordinates, document coordinates (dpi) and view coordinates (respect zoom, etc.). You had to care about this in your brush engine. I hated that as I wanted to care only about pixels in brush engine. First Sven moved the view coordinates out of the paintop (brush engine) to tool. Now I even moved the document coordinates to tool. Before we painted the brush outline in paintop using QPainter, now the paintop passes QPainterPath in pixels to tool and tool is responsible for painting the outline correctly (conversions, painting, …).
According the bug report, the outlines were invisible as they were black always. Now we use XOR for them, so they are always visible. For Arthur canvas (QPainter based one) we use QPainter::RasterOp_SourceXorDestination which does not work for OpenGL-based canvas in Krita (QGLWidget).You see just Unsupported Composite Mode or something like that. So we use native OpenGL code for rendering the outline with XOR. The rendering of the outline is quite fast. But some brush masks are slow to render. That’s what I will be working on this week. Make bigger brushes painting faster. Sven Langkamp added option for Deevad so that the outline is visible as you paint.
I will try to finish sketch brush engine and blog about it, coz it is quite fun brush engine. See you next week.