Paint devices and Painters
Paint devices and Painters
Boudewijn Rempt
This is a write-up of my (Boudewijn's) understanding at this moment -- February 17, 2004 (brought up to date a bit to August 25, 2006).
Krita's core consists of paint devices and painters:
- KisPaintDevice
- KisPainter
These classes are (very) loosely modelled on QPaintDevice and QPainter. KisPaintDevice also takes up some of the roles of QImage, but isn't all that efficient at it.
Examples of KisPaintDevices are: KisSelection and KisBackground.
Getting pixel data in and out of a KisPaintDevice.
Krita stores all image data in tiles; the tiles are managed by the aptly named KisTiledDataManager (which is a subclass of KisDataManager, so that it is possible to exchange the tilemanager somewhat easier at compile time). Inside the tiles, we just have raw Q_UINT8s (these are regular, 8 bit unsigned chars), that can be interpreted only with the appropriate colorspace.
Ordinarily, you will change the data inside a KisPaintDevice through the KisPainter. Using KisPainter has the advantage that your changes will be undoable and that the tile manager is hidden. That's especially nice, since you generally don't want to work directly with tiles, not before having bought shares in an aspirin producer.
The other way of messing with the pixels inside a KisPaintDevice is through the colorAt(), pixel() and and setPixel() methods in KisPaintDevice. These methods retrieve and set the specified pixel data using the tiles, but are not undoable. They are also rather ineffcient and slow.
These methods return and get their data in the format of a KisColor. This is a wrapper around a single pixel worth of uninterpreted data, and contains a pointer to the KisColorSpace that needs to be used to interpret the raw data.
KisPainter and KisPaintDevice do the job of hiding the actual image data from the krita hacker, but sometimes it's not enough, sometimes you need to loop over all the pixels in an image, do something, and write the pixels back to this or another image (or sections of images).
Getting easy access to the image data with KisPaintDevice
The best way to get access to individual pixels, is using one of the iterators the KisPaintDevice has to offer. The easiest way is to iterate over the lines with an iterator.
// For each line y:
KisHLineIteratorPixel it = paintdev->createHLineIterator(x, y, width, writable);
while (!it.isDone()) {
Q_UINT8* data = it.rawData();
// you can use the KisColorSpace of the paint device here to change the data
++it;
}
The writable property is important for speed: if you the subsequent accessing of the pixel data will purely be read-only, writable should be set to false. That way, if the iterators passes over a tile that has no data yet, it will not create a new tile, but return a pointer to a default tile. If you do plan to change the pixel data, it is important to set writable to true, since the new tiles should be created.
Each iterator also has an oldRawData() function. This function returns a const Q_UINT8 pointer to the state the pixel was before the last KisMemento was taken. Basically, this is a 'snapshot' of the pixel taken at the moment the most recent undo operation on this pixel was started. It can be important that you can get access to the state a pixel was before it was changed by a function.
Other iterators are KisVLineIteratorPixel and KisRectIteratorPixel. The Vertical iterator is completely analogous to the HLine iterator. The KisRectIteratorPixel iterates over a whole area at one, in the way that is most efficient. This means that the iteration will not go one complete line at a time, but will stay in the same tile as long as possible. This allows for faster iteration, but might be problematic if you rely on the pixels being visited in a deterministic way.
The KOffice Project