Tuesday, November 27, 2012

Krita Sketch is: Rounded

A quick recap of the main topic: My employer, KO GmbH, has been working on a touch version of Krita, called Krita Sketch, over the last few months, and is gearing up towards a first release. So, i'm doing a bit of writing, talking about some of the work i've been doing as a part of this project.


There are two things in Krita Sketch which are rounded, and i will be covering the first of these in this entry. The first is the simple rounded corners in various places around the application, which are done in three ways (that i will get back to in a moment), and the other is the colour selector, which is an entirely different matter. That one will be covered in the next entry.

So, rounded corners in QML, how do you do that? Well, in reality it really is quite simple, but i thought i would spend a little while talking about it anyway. In addition to just using a set of Images manually, lining them op as appropriate (which we did in a lot of places), you have two options when making them, depending on precisely what you are trying to do.

Pure QML painting

The first option is to use the radius option on a Rectangle item. A very simple approach, which does however come with one caveat, which is not the radius itself, but rather a problem of how borders work in Qt in general (it will also be known to people who work with WebKit, which behaves in the same way). Shortly, borders are painted on the centre of the element's edge, rather than on either side of it. If you are aware of this, it is easy enough to work with, but you must be aware of the behaviour for it all to make sense.

Using the radius property means that all four corners on the element will be rounded a number of pixels equal to that radius. Easy to work with. Except in the case where you only want some of your corners to be rounded. We have not used this trick in Krita Sketch, but it's a useful trick to know: Use the Item item's clipping property, fill it with your rounded-cornered Rectangle, and make that Rectangle larger than the Item by way of adding negative margins in the appropriate places. For example, the following code will give you a blue rectangle with the two top corners rounded:

BlueRectangle.qml:


import QtQuick 1.0
Item {
    Item {
        width: 50;
        height: 50;
        anchors.centerIn: parent;
        clip: true;
        Rectangle {
            anchors {
                fill: parent;
                bottomMargin: -radius;
            }
            radius: 10;
            color: "blue";
        }
    }
}


A total hack? Yes, absolutely. But it works a treat :)

The BorderImage Compoment

The solution we should have used in Krita Sketch in the majority of cases was the BorderImage component. A very simple but extremely powerful component, which shortly takes an image, cuts it into nine slices according to what's set in your border property, and arranges those slices according to your desire (scaling and tiling according to the settings you choose). The documentation for this component is extensive and well written, so i won't talk about this other than saying it is very powerful.

An important thing, however, is to make sure that you know it exists. It is a non-obvious thing to have available, and if you do not know it's there you'll find yourself reinventing a lot of things and doing a lot of manual cutting which, on mobile devices in particular, means that you are using more texture space than you should. In our case this is not any serious cost (we have a LOT of images in memory already, and not using this component does not seriously impact on this), but if you are targeting mobile devices, in particular the smaller variety (smartphones), make sure you're not reinventing the wheel here.


The word of the day is: Squircle

Labels: , ,

Friday, November 09, 2012

Krita Sketch is: Layered

A quick recap of the main topic: My employer, KO GmbH, has been working on a touch version of Krita, called Krita Sketch, over the last few months, and is gearing up towards a first release. So, i'm doing a bit of writing, talking about some of the work i've been doing as a part of this project.

One of the most vital features of Krita is its powerful layer system. Powered by the Calligra Flake library, which forms the base of the document structure in the majority of Calligra's applications, it allows the user to nest and group layers in all sorts of interesting ways, and while this is all fun and games in a desktop application, where tree structures are shown as, well, trees, in a touch based application where the concept of trees has been all but banished and the only available data structure for multiple-item data is a list, well, something had to be worked out for this. This blog entry, then, is all about the odd beast that is a flattened tree structure with retained structural information. In other words, how do you fake a tree when you only have a list?

On the left of this text you can see the current version of our tree-list, and below what this looks like on the desktop. You might also notice some interesting details such as the change in where certain controls are (specifically the movement controls). In QML this sort of thing, that is, putting controls on top of something else, spreading outside of the perceived borders of an item, is very easy to do.

Some of you working on the desktop will be aware of how you sometimes fake a listview using Qt's powerful QTreeView widget. Well, in QML you haven't got a tree view. What you have instead is an incredibly powerful ListView component.

As such things go, what we ended up doing to the model was to cheat. When building this type of structure you really have two options: Either you do it the "proper" way and do the conversion on the fly, or you store a flattened version of the data structure. They both have advantages and disadvantages: Option 1 will give you the least amount of data duplication, but it will be programmatically expensive at times. Option 2 will cause some amount of data duplication, but reading each item will be cheaper at all times. As we have a data structure here with what will always be a computer-sciencey version of a small number (unlikely to have hundreds of layers in an image), and the fact that reading the data rapidly is more important than saving the few hundred bytes of memory that storing a list of pointers would cause, we decided that doing it this way would be most sensible of the two.

So, with that in mind, we can start to discuss what's needed to take a tree and flatten it into a list model usable from QML, while retaining some of the information.

A list of general things to remember when building a model for use with QML is below. I will be going into each of these in larger detail below, but as a convenient check list, here you go:

  • Inherit QAbstractListModel. This allows for the highest degree of control, and at the same time gives you a large number of safety checks for free. Don't use a QAbstractItemModel unless you really, really have to (and i'd like to know when that becomes a need, i've yet to find one ;) ).
  • Role names. Make sure you use role names when building your data reimplementation. This allows you register names for use on the QML side, and pair them with a field name on the C++ side. Specifically you will need to use setRoleNames() in your constructor. Create an enum inside your class, with the value of the first entry set to Qt::UserRole + 1, and set the equivalent names for each of these using a QHash in your constructor.
  • Reimplement data() using the role names you created above, by setting up a switch statement over the role argument, using your roles as the only entries. (note that the standard entries in Qt are still good to have, if you are sharing the model with a QWidget based application as well). Note, you must remember to check validity of your QModelIndex before you try using it (see below).
  • Reimplement rowCount() as simply as you can. In our case, as we have a QList of entries, check whether the parent is a valid QModelIndex. If the parent is valid, you're a list and any real entry has no children. If the parent is not valid, it is the virtual root of the data structure, which as its children has all the entries in the list, and as such you in that case return the number of entries.
However, in addition to this, we also have a couple of other items to worry about. In our case, our model is acting essentially as a facade to another model, which comes with a set of specific oddities which are not quite modelley, but more like view information. In particular, we need to convey information about which is the currently active layer, and further need an ability to request to change to a different layer. To do this, we have two things going on: One private slot which listens to the node activation and update requests from the KisNodeManager instance of the view of the canvas for the current image, which keeps track of all this information, and a role which tells us which is the current layer. The dataChanged function is then called on both the indices corresponding to the previously active layer and the new active layer, and the view acts accordingly, updating the data for only those two items.

QAbstractListModel vs QAbstractItemModel

In some ways the choice here is really straight forward: In QML you only have lists available, you have no views which will handle a tree structure. However, if you have built models yourself in the past, you may have found yourself limited by the list model, and just defaulted to using the abstract one. Stay your code finger and really think before you do this again. QAbstractListModel has all the power you need when dealing with QML, where any item in a list is really just a pseudo-object with properties, so you don't need the columns or anything like that (in fact, you could want to map your columns to a name to access it from QML, and that translates pretty directly to using the roleNames functionality). Short, but i hope that's clear enough without me grinding on about it ;)

Role Names

Rather than using columns or any such thing, in QML you access the fields in your model by using names. What this means is that you need to register the names you want to be able to use using the setRoleNames() call in your model's constructor. A very short example would work as follows, where you have two roles (a name and a subtitle, just as examples)

YourModel.h:

class YourModel : public QAbstractListModel
{
    Q_OBJECT
public:
    enum LayerRoles {
        NameRole = Qt::UserRole + 1,
        SubtitleRole
    }
    YourModel(QObject* parent = 0);
    ~YourModel();
private:
    class Private;
    Private* d;
};

YourModel.cpp:
YourModel::Private {
Public:
    Private() {};
    QList<YourItem> list;
};

LayerModel::LayerModel(QObject* parent)
    : QAbstractListModel(parent)
    , d(new Private)
{
    QHash<int, QByteArray> roles;
    roles[NameRole] = "name";
    roles[SubtitleNameRole] = "subtitle";
    setRoleNames(roles);
}

QVariant LayerModel::data(const QModelIndex& index, int role) const
{
    QVariant data;
    if (index.isValid())
    {
        switch(role)
        {
        case IconRole:
            data = d->list[index.row()].name();
            break;
        case NameRole:
            data = d->list[index.row()].subtitle();
            break;
        default:
            break;
        }
    }
    return data;
}

int YourModel::rowCount(const QModelIndex& parent) const
{
    if ( parent.isValid() )
        return 0;
    return d->items.count();
}

What you see above is all that's needed to be able to call model.name and model.subtitle in your ListView item's delegate inside QML. This particular part is documented well in the ListView documentation, but the C++ side is lacking just a little. Note that you still, of course, need to somehow expose the C++ class to QML somehow. There's two ways of doing this. Either you return an instance from some class registered elsewhere, in which case you don't need to do anything special (since it's a QObject type), or you allow for the creation of instances of the model inside QML. To do this, all you need to do is, somewhere in your code (likely your main function):

qmlRegisterType<YourModel>("your.application", 1, 0, "YourModel");

The your.application part is what you then import in your QML file, 1 and 0 is the version numbers (so you would do import your.application 1.0) and YourModel is the name of the component you can instantiate (just as you would any other QML component).

Flattening

The final part of this entry is about what we did about the tree of layers. So, having shown the most basic structure above, here is the function which creates the list of layers when a layer is added or removed (and of course on start-up). Yes, i am aware that we could splice new layers in and such, but quite frankly, this is so fast that you in reality don't notice it. If it becomes a performance problem, then we can fix it, but for now, the act of actually creating a layer or removing it (in other words, repainting the image) vastly outweighs the price of this function.

    void rebuildLayerList(KisNodeSP layer = 0)
    {
        if(layer == 0)
        {
            layers.clear();
            layer = image->rootLayer();
        }
        // implementation note: The root node is not a visible node, and so
        // is never added to the list of layers
        QList<KisNodeSP> children = layer->childNodes(layerClassNames(), KoProperties());
        if(children.count() == 0)
            return;
        for(quint32 i = children.count(); i > 0; --i)
        {
            layers << children.at(i-1);
            rebuildLayerList(children.at(i-1));
        }
    }

The logic is very simple here: If no layer is passed, we clear out the list and start working on the root. From there, we go through all children as appropriate (the layerClassNames function simply returns a list of the names of the layer types we're interested in), and then cycle through them all, adding each children, and its children, to the list. You will recognise this as a depth first search, and if you don't, that's what it's called when you go through a tree structure and list all children as soon as you find a new child to list (rather than a breath first search, where you list all children at one layer before going down one layer).

This means that when we ask the model at which depth some item can be found, all we have to do is traverse all the way up through the tree until we reach the root node (that is, until we reach a layer which has no parent node). That particular bit of code, found in the data() function (indicated above) is extremely simple, but just so you don't have to go digging, here's the relevant part of said function:

KisNodeSP parent
KisNodeSP node = d->layers[index.row()];
int depth = -1;
switch(role)
{
case DepthRole:
    parent = node;
    while(parent)
    {
        ++depth;
        parent = parent->parent();
    }
    data = depth;
    break;
...

And that's really all there is to it. Just remember to call rebuildLayerList() every time the tree model you are watching changes. In our case, that means we have this in the constructor, but of course it would depend on when you get the tree model.


    connect(d->nodeModel, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)),
            this, SLOT(source_rowsAboutToBeInserted(QModelIndex, int, int)));
    connect(d->nodeModel, SIGNAL(rowsInserted(QModelIndex, int, int)),
            this, SLOT(source_rowsInserted(QModelIndex, int, int)));

    connect(d->nodeModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)),
            this, SLOT(source_rowsAboutToBeRemoved(QModelIndex, int, int)));
    connect(d->nodeModel, SIGNAL(rowsRemoved(QModelIndex, int, int)),
            this, SLOT(source_rowsRemoved(QModelIndex, int, int)));

    connect(d->nodeModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
            this, SLOT(source_dataChanged(QModelIndex,QModelIndex)));
    connect(d->nodeModel, SIGNAL(modelReset()),
            this, SLOT(source_modelReset()));


I hope this has been of some use to someone out there, and i will see you soon in the next instalment of Krita Sketch Is! ;)


The word of the day is: Flattened

Labels: , ,

Monday, November 05, 2012

Krita Sketch is: Inspiring

The Krita Sketch icon by Animtim

Just under two years ago, i started working at this little company called KO GmbH, a small outfit based in Magdeburg, Germany, but which as many other KDE related companies is spread out through the world, primarily Europe. The company was originally set up so work could be done on what is now Calligra, KDE's powerful and sleek office suite.

One of the applications in this suite is the impressive painting tool Krita, and recently KO found a sponsor which has asked to remain anonymous for now, and who had taken an interest and decided that Krita needed some more exposure. They wondered whether Krita would perhaps be useful as a base for a touch based painting application, and as Qt and KDE is of course extremely modular, for us as coders, that would mostly mean creating a new shell around the painting tools and the various models inside Krita, so we were happy to be able to tell them that yes, Krita would be quite suitable for such a project.

The best part of this project has been that we have been able to talk about it, but of course as with all such things, we have just been so busy producing the work that blogging about our progress hasn't happened ;) Now that we're getting to the release of the first version of Krita Sketch, we are getting to that wonderful point where there's a little more time for communication, and so, we will be writing a bit about the work we have been doing.

This first post works as an introduction to the sub-project of Krita Sketch, the next posts will talk about more specific parts of the application and the work which was done to create it.

Effect on me

The next paragraph is not really about the project, but i felt i had to say it. So, feel free to skip it ;)

For me, personally, one thing that has happened as a direct result of being moved onto the Sketch project inside KO is how my inspiration level has changed. Due to its nature as a closed project, i cannot say much about what i worked on previously, other than the fact that i didn't enjoy it much and that it was based on tech which i similarly am not a big fan of. So, to suddenly be moved onto an entirely open project, based on a technology which i thoroughly enjoy working with, has meant that i went from feeling like a wage worker to feeling like someone who was making a real difference. I know that, practically speaking, there is no technical difference (both are writing a piece of software for a customer), but the difference in feeling between the two projects is just incredible. Working on open source software, using a framework i enjoy, just makes life so much more pleasant. Thank you, dear sponsor, for making this possible :D

The Project

So, about the project itself. You can start reading again ;)

Now, due to other obligations, i joined the project once it had already got under way to some degree, and i'll leave it to Boud to talk about that period. What happened when i joined was that two projects ran in parallel, one in which work was happening on the underlying technology, and one in which design work was being undertaken. This is one of those delightful things that can be done, when there is an agreement on the communication layer between the two parts, and in Qt, and particularly in QML, this is provided by models. In essence, as a coder you can view a QML application as a big set of views onto your data models (in the form of Qt's QAbstractListModel, since QML is all about lists, not trees). As an artist, you don't need to worry about all that technical mumbo-jumbo, and can simply concentrate on designing and building the UI, using the building blocks that QML gives you, safe in the knowledge that you just have some lists with some data in them, and that you get to decide what data that is (since adding a new field to a list model is really quite easy, this makes life easier for the coder as well).

Before caption removal
After caption removal
All in all, this worked out really quite well for us. Some things could have worked better, as we ended up with the two applications (the running technical implementation, and the high fidelity mockup that Animtim and myself worked on) diverging at some point, but this was quickly caught up again. The mockup slowly became less important, as the application itself formed out of the technical implementation, now called Sketch, but something which worked spectacularly well was the ability to share the models between the two applications. That is, the same code which was used to test things in the technical implementation was used in the mockup as well, so that it was not simply lists of dummy information, but actual, real information from the application layer. This meant we were able to catch a couple of issues (as an example, one thing was the fact that colours in most palettes did not, in fact, have names, and so the design had to be changed to accommodate that fact, or we'd have had a very odd looking panel with unnamed colours).

Over time the mockup became less and less important, but not before it had served its purpose, letting myself and Animtim work on the design, without being encumbered by the of course unavoidable instabilities in the running technical implementation. Even during a two week period in which the technical implementation crashed on startup due to some deep-down work which Arjen Hiemstra was doing on the canvas implementation, we could steam ahead and get the user interface in tip-top shape, working as it needed to.

In conclusion, this approach really worked for us, and i personally believe that it is an approach which will work for others in a similar situation; that is, you have a design ready for implementation and a technical implementation of an underlying system which is in a state of flux. Another thing is, of course, that it means i must reiterate what many have said before me: Abstract your UI from your data. Please. And in Qt, that means use the model classes. I know they seem scary, but they really don't bite that hard. Especially if you stick to QAbstractListModel, which is very simple to work with (no trees, but as you will see in later entries here, you are perfectly able to fake that).

You want it, you say?

For binary releases, sorry, we've not currently got anything there (though at least for Windows this will change very shortly... yes, i said Windows ;) ). However, the code for Krita Sketch at the moment sits in a branch of the Calligra repository, as the application had to be released outside the normal release schedule of the suite in its first release (due to a customer requirement), so if you want to have a look, for now you will have to clone the Calligra repository and install it from source: https://projects.kde.org/projects/calligra/repository/show?rev=krita-sketch-rempt is the current home. If you want to only use sketch, the easiest is to build it with the cmake option -DCREATIVEONLY=On, which will cause Calligra to only be built with Karbon and Krita turned on). Another important setting note when working with Krita compiled from source is that full debug will severely impede the drawing performance, so unless you have a good reason, just configure with RelWithDebInfo instead :)

Oh what's that? OK, then, a screenshot. Now, this is just me poking about, and please note: my art is code, not a visual artist ;)


The word of the day is: Shiny

Labels: , ,

Friday, November 02, 2012

Qt Contributors Day 2012 Berlin - KDE Edition



As you may remember from last year, the Qt Developer Days in both Munich and San Francisco, on the first day of the conference there were two options: Training and the Qt Contributors Day. This year will be no different, and so, this year we will have the Qt Contributors Day: KDE Edition at Qt Developer Days 2012 in Berlin! The event is made possible by the strong, long standing and i'm sure you'll agree fruitful partnership between KDE and KDAB, one of the patrons of KDE.
So, i hear you asking, what's this thing about? Well, this year we've decided to focus on KDE's ability to help out Qt in various ways, the way we have all benefited from Qt through the years. There is of course already a whole lot of work going on in this area, and well, this day is then just another step towards that goal. Think of it as a sort of mini-sprint.
Your to-do list
1. Register
2. Figure out some good topics for discussions
3. Join us on the day

ps: yes, i'm aware this is getting really quite close, but it was announced on twitter and the Qt Project forums earlier this week, and now also here. So even if it's announced late, i would hope a not inconsiderable number of you were already planning on attending already.... let's make it epic, people :)

The word of the day is: Open Governance. (ok, that's two words, but they're important ;) )

Labels: ,