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?

3 comments:

  1. I'm not sure if this is what you were asking (so maybe you already know this), but QImages are stored "client-side", in the application's memory space, whereas QPixmaps are stored "server-side" -- meaning that if you are using Qt's x11 graphics engine, in the X server, or if you are using opengl, probably in video card memory -- and the application only has a handle to it. (With the raster engine, server-side is the application itself, so there's possibly not much difference.)

    What this means if that if you want to perform some kind of operation on a QPixmap which the "server" doesn't support, you first have to "download" it to the application's memory (by converting to a QImage), perform the operation, and then "upload" it back (by converting back). This can be very slow. On the other hand, for operations which the server does support, it can be hardware-accelerated or have other benefits (with opengl this is obvious, but x11 might do some internal acceleration itself, and other operating systems might have other things). And because the given server is what handles drawing to the screen, drawing pixmaps is fast. On the flipside, on QImages you can perform any kind of operation you want -- this is not accelerated, but done with Qt's raster engine which is generally quite fast (usually even faster than x11). However, if you want to draw a QImage to the screen, you have to upload it to the server, which is slow.

    Executive summary: If you want to paint it to the screen, use a QPixmap. If you want to do other things with it, use a QImage. If you want to do both, sadly you'll have to engage some brain cells and figure out which (or some kind of scheme with both) would suck less.

    ReplyDelete
  2. Also, holy shit it seems Blogger have finally unfucked their commenting system. I rejoice.

    ReplyDelete
  3. Thanks for the first comment! =) I didn't know that.

    ReplyDelete

Followers