Tuesday, June 22, 2010

Krita: transactions, swapping and new fastBitBlt for tiled data manager

It was almost two weeks ago when I blogged last time. It was very tough those weeks because I had to merge two processes at the same time: preparations for an examination at the university and coding on Krita. But nevertheless, I'm glad announce much progress in my project for Krita!

Transactions refactoring

I've finally commited my patchset for transactions in Krita [1]. So now there is almost nothing common between Qt's QUndoCommand and Krita's KisTransaction (from the public side :)). It was needed to allow pooler (and upcoming swapper) threads to work efficiently. Now every transaction has the beginning and the end. So when you end a stroke on the canvas you finish a transaction as well. The pooler recognizes this signal and starts doing his routine job to optimize your next stroke (while you are still thinking about it ;)).

There are big changes from a developer point of view as well. Now you can create a transaction in a stack instead of a heap, that makes the code much simplier. And the most curious artefact of Krita's past was removed. There was a "global variable" in every KisDoc2 that used to disable saving undo information while loading the document. This variable had to be checked thoughout all the code of Krita: in every tool, in every action. Now it has gone. New tile engine does initial document loading in a cheap and fast way.

If you are interested in details of the new transactions system you can read a design document here. Examples are given!

Region() interface for paint devices

After finishing with transaction I continued my work on preparation to Filter API redesign. I've added a feature to a paint device for getting a QRegion of non-transparent (non-default) pixels. Of course it is based on tiles, and the region is rounded by 64. It is intended to be used as optimization of filter's application. You can read the details in Filter API Discussion Notes, that happend between Cyrille Berger and me in Deventer.

FastBitBlt interface for paint devices

This is part of Filter API redesign as well. The interface simply shares tiles between different paint devices. It allows us to create clones of devices really fast! It is intended to be used in layer merging subsystem and during filter application. There are limitation in this interface as well: it cannot be used when paint devices are shifted and when their color spaces do not coincide.

Framework for tile compression

After some discussion with Cyrille and his experiments with LZF algorithm, I started implementing tile compression framework inside the tile engine (Cyrille has already added nessecary features into KoStore to allow delegating compression to it). I'm going to kill two rabbits with one shoot, by doing this. The first "rabbit": Krita will be able to work with various formats of saved tiles. It means that we will be able to modernize the tile format without losing back compatibility. And the second: this framework will be the first stone in the swapper subsystem's building!

Thursday, June 10, 2010

Krita: New Transaction System For Review

Today the new API for transactions has been finished!

It turned out that Krita can't fully rely on QUndoCommand functionality, so on Deventer sprint it was suggested to modify our transactions [1]. Now I'm glad to show you results of the work done.

Shortly, the new system is based on two classes, the first represents the boundaries of the transaction, while the second defines the lifetime of the undo information. Touching base transaction classes caused much code to be revised, but now it is done.

So now, if you want to get more details about new system, you can read a complete description on our wiki page.

You can even take part in testing of the new system by applying the patch.

Wednesday, May 19, 2010

Krita: canvas will not flicker anymore!

Many have already seen this annoying bug in Krita canvas update system. It was seen when you painted with big and complex brushes like Hairy Brush on a multilayer image. Now I'm glad to announce that the patch is produced! And it will be in trunk soon!
The bug was caused by a race condition between Image and UI threads. The latter one tried to read the data from the merged projection while the former one was preparing it. I have fixed this by splitting the update process into two parts. The first part reads data from the image. It is executed in the context of the Image thread. Then the thread saves update information got from the canvas to the external structure called KisUpdateInformation and passes it to the UI thread, that continues updating doing rescaling and other slow operations.

This patch touches much code in canvas subsystem, so there will be a short delay for reviewing and testing the patch. And if you want to see it in trunk faster, you can help testing it! [0]

There are some technical details of implementation in our wiki [1]

[0] - http://dimula73.narod.ru/canvas-split-single-v2.patch
[1] - http://wiki.koffice.org/index.php?title=Krita/KisCanvas2_Update_Split_Reasoning

Tuesday, May 4, 2010

Creative Monday in Mocsow

We have just had three days of holidays in Russia. They were all due to a "prolonged" Labour Day. :)

On Sunday I was happily coding a Krita's [0] canvas system and almost done with the splitting [1], so I decided to get a day off and organize a "Creative Monday" on the next day. I went to the center of Moscow and got some photos [2].

Here you can see some results =)

[0] - http://www.krita.org/
[1] - http://wiki.koffice.org/index.php?title=Krita/KisCanvas2_Update_Split_Reasoning
[2] - http://maps.google.com/maps/ms?ie=UTF&msa=0&msid=107532280042317408794.000485c8770230c310ee3

About cars...

Cars, pt2

Cars, pt1


About windscreens...

Windscreen, pt2

Windscreen, pt1


About rice, fish, restaurant and abstractions...

Abstraction


And, of course, about love...

About Love, pt1

About Love, pt3

About Love, pt3

Friday, August 21, 2009

GSoC Krita tile engine wrap-up

The summer has almost gone, GSoC has finished..

I had two aims for my project: the first was to make a new, good, able to swap and compress tile engine for Krita and the second was to make a mipmapping subsystem. They are in trunk now, but they both are in testing state.

Features

Let's take a look what features do they suggest:

Tile engine

I had to refactor most of tile-subsystem code. New tiles can be easily shared, that helps much during saving undo information. Speaking strictly, now we don't have to save this undo information just before painting. This is done in deferred way, in a separate thread. It means that new engine has a very good perspective on multiprocessor systems.

Unfortunately i didn't manage to finish swapper thread in time. Too much time was spent on the second task. That is one of the resons why this engine is still in a "testing" state.

Mipmapping

This task was quite difficult to perform. Adding new canvas caching and prescaling engine caused much code of KisPrescaledProjection to be refactored. So i spent most of the time on that. Now mipmapping stuff itself is finished, but much of Krita merging and threading code must be revised. We've faced with many paint artefacts caused by race conditions in a merging mechanism. That is why at the moment i'm working on that part of Krita. After solving all the issues with threading mipmapping will be a very good option for users to optimize their zooming.

How to activate

As i said these features are being tested for now. This means they are not turned on by default, but if you are interested, you can always try them out!

New tile engine activation HOWTO:

This feature needs recompilation of Krita.

1) svn co svn://anonsvn.kde.org/home/kde/trunk/koffice
2) cd koffice
3) .. edit a file ./krita/image/CMakeLists.txt ..
.. change a line ..
set(USE_TILESYSTEM 1)
.. to ..
set(USE_TILESYSTEM 3)
4) make -j3 install

Mipmapping activation HOWTO:

For mipmapping you needn't recompile anything :)
Just set an option in your kritarc configuration file

useMipmapping=true

This file is usually stored in ~/.kde4/share/config/kritarc

Wednesday, July 22, 2009

We are in trunk!

New Krita tile engine is in trunk now on! You can try, test, see, change it! =)

Just checkout a trunk and switch engine in krita/image/CMakeLists.txt file
To activate a new one set USE_TILESYSTEM to '3'

Tuesday, July 21, 2009

Mipmapping for Krita's canvas subsys

Hi, all!

The tile engine has already come over to a quite stable state by this moment. I think today i'll publish patches for trunk. So now i'm publishing my ideas about mipmapping feature of viewing mechanism.

Well, mipmapping could be added in two parts of krita: the first - before actual merging and the second - after merging.

* The first could be done inside KisPaintDevice class and all merging would be done with prescaled images.

Pros:
+ we merge only small images, not fullsized big ones
+ as a consequence, filter layers and masks are applied much
faster

Cons:
- too much overhead, in memory and in cpu time. Actually, a huge
piece of work is done for nothing. Parts of image pyramids
will never be used at all.
- complexity of the system: alignment between layers
- probable artefacts caused by merging after scaling


* The second - in KisPrescaledProjection. We merge original images, then create a pyramid of a projection and scale at last.

Pros:
+ not so much overhead as we create only one pyramid for a view
+ zooming works fast thanks to the pyramid
+ no scale-merge artefacts.
+ no problems with alignment

Cons:
- no help to filter layers


I think the second variant is better. At least for the beginning. So i'm going to add it to KisPrescaledProjection. More than that, i could try to make this image pyramid as encapsulated of the canvas subsystem as possible, so in fueature it could be ported to a paint device if needed, or even made as a template.

As i investigated, KisPrescaledProjection have three main entry points (input methods):

->updateCanvasProjection(rect)
->preScale()
->resizePrescaledImage(size)

These methods fit for image pyramid very well:

updateCanvasProjection() would start a thread that would eventually update pyramid

preScale() would request scaled image from pyramid.
It would look like:
QPainter gc(m_prescaledQImage);
...
m_imagePyramid.drawScaledImage(gc, ..., ..., scale);


Here is a mockup of an interface of a KisImagePyramidClass:


class KisImagePyramid {
public:
void updateCanvasProjection(QRect rect);

/**
* Here are some problems with scaleX/scaleY
* as for image pyramid they should be equal
*/
void drawScaledImage(QPainter gc,
QPointF topLeftScaled /* target topLeft point */,
QRect rectUnscaled /* source rect in image */,
qreal scale);
private:
QVector m_pyramid;

KisProjectionCache /* or QImage */ m_original;
QThreadPool m_pyramidUpdater;
};


What do you think about this idea? Maybe i've missed something?
As always, comments are welcome! =)

PS:
There are some points where i'm in doubt now:

1) Should we use KisProjectionCache for storing original image or simple QImage? I guess not, because it could be used much in drawScaledImage much.

2) Which zoom-levels should be stored inside m_pyramid? I saw that when you press Ctrl+'+'/'-' Krita switches across some finite number of levels. Which part of Krita/Koffice decides, which levels to use? I guess, these levels and levels in m_pyramid should agree :)

3) KisPresceledProjection::Private::prescaledQImage vs
KisPresceledProjection::Private::prescaledQPixmap?
What is the difference and where are they mostly used?

Followers