Mipmapping for canvas. Ideas and comments.
Boudewijn Rempt
boud at valdyas.org
Wed Jul 22 08:28:27 CEST 2009
On Tuesday 21 July 2009, Dmitry Kazakov wrote:
> * The second - in KisPrescaledProjection. We merge original images,
> then create a pyramid of a projection and scale at last.
It's actually two steps: we have a projection that is optionally (and the
default is on) cached at 100% in one big QImage, and then there is the
prescaled projection.
Your work probably should be done at the projection level, not the prescaled
projection level; the prescaled projection is only used to move pixels as
quickly as possible to the canvas, and it is never bigger than the canvas size
in pixels.
Now there is a second issue: the opengl canvas. The opengl canvas doesn't use
the 100% QImage projection cache, nor the prescaled projection, but divides
the projection into textures and those textures are fed to the gpu.
I actually think you are working at too high a level here: I think a
KisProjectionPyramid should be in krita/image, not krita/ui. Then the
prescaled projection would resquest a QImage for the updated area at a certain
zoom level, and the KMMP would return that, computed from the nearest tile.
Question: have you taken a look at the way Gimp 2.6 has implemented its
display pyramid? It is fairly simple and might be very instructive.
I'd suggest having KisProjectionPyramid in krita/image. So, if an area is
recomposited, KisImage emits an sigImageUpdated(const QRect& rc) signal. It is
connected to KisProjectionPyramid, which starts recomputing the pyramid for
the tiles in the affected area (you have to have tiles here, you cannot cache
the entire image in a QImage for every level). Through KisCanvas2,
KisPrescaledProjection is notified and asks KisProjectionPyramid for the
udpated area at the correct scaling level. KisProjectionPyramid takes the
projection at the right mipmap level and returns a QImage at exactly the right
zoom level, with KisPrescaledPProjection pastes in its cache and then asks the
canvas to update.
>
> 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
Yes, that sucks. But the if we implement region-of-interest in KisProjection,
then we'd only filter the visible area of an image, and if the image isn't
scaled down too much, it won't be too bad. I wonder whether GEGL optimizes for
this.
> 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):
The real task of the prescaledprojection is have a QImage as big as the
canvas, scaled to the current zoom level that can be used to blt to the
QPainter canvas. It doesn't work with the OpenGL canvas (it is debatable
whether it is necessary there, but the OpenGL canvas scaling down is pretty
bad now because it uses simple nearest neighbour).
> ->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<QImage> 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?
> 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 :)
Traditionally, you'd take steps of 50%, so: 100, 50, 25, 12.5, I believe
--
Boudewijn Rempt | http://www.valdyas.org
More information about the kimageshop
mailing list