Done Porting!

Done Porting!

Technically, we’re done porting Krita to Qt5 and KDE Frameworks 5. That is to say, everything builds, links and Krita runs, and there are no dependencies on deprecated libraries or classes anymore. In the process, the majority of Calligra’s libraries and plugins were also ported. It was not an easy process, and if there hadn’t been sponsorship available for the porting work, it would not have happened. Not yet, in any case. It’s something I’ve heard from other KDE project maintainers, too: without sponsorship to work on the port full-time, projects might have died.

Krita wouldn’t have died, but looking back at the previous month’s work, I wonder I didn’t go crazy, in a loud way. I spent four, five days a week on porting, and fixing the porting documentation, and then one or two days on trying to keep the bug count down for the 2.9 branch. As Kalle noted, porting isn’t hard, it’s very mechanical work that despite all the scripts, still needs to be done by a human, one who can make judgement calls — and one who isn’t afraid of making mistakes. Lots of mistakes: it’s unavoidable. Most of them seem to be fixed now, though. It’s like running a racecourse in blinkers.

So, what were the hardest problems?

The winners, ex-equo are KStandardDirs to QStandardPaths and KUrl to QUrl.

The latter is weird, because, actually, we shouldn’t be using QUrl at all. The reason KUrl was used in KOffice, now Calligra, is for handling network transparent file access. That’s something I do use in Kate or KWrite, when writing blogs (my blog system is a bunch of nineties perl scripts) but which I am sure not a single Krita user is actually using. It’s too slow and dangerous, with big files, to depend on, it’s superseded by dropbox, one-drive, google-drive, owncloud and the rise of the cheap NAS. Not to mention that only Plasma Desktop users have access to it, because on all other platforms we use native file dialogs which don’t give access to remote locations. All the QUrl’s we use get created from local files and end up being translated to local filenames.

KStandardDirs is more interesting. KStandardDirs actually was two things in one: a way to figure out the paths where the system and the application can store stuff like configuration files, and a way to build a kind of resources database. You’d define a resource type, say “brush”, and add a bunch of locations where brushes can be found. For instance, Krita looks for brushes in its own brushes folder, but also in the shared ‘create project’ brushes folder and could even look in gimp’s brushes folder.

The resources part isn’t part of QStandardPaths, but is used really heavily in Calligra. The central place where we load resources, KoResourceServer just couldn’t be ported to QStandardPaths: we’d have to duplicate the code for every resource type. But there’s no problem that cannot be solved with another layer of indirection and a lump of putty, and I created a KoResourcePaths class that can handle the resource aliases. I’m not totally convinced I ironed out all the bugs, but Krita starts and all resources are being loaded.

There were a few more classes that were deprecated, the foremost being KDialog. There’s only a hundred or so places in Calligra where that class was used, and here the best solutions seemed to me to fork KDialog into KoDialog. Problem solved — and honestly, I don’t see why the class had to be deprecated in the first place.

Now all the basic porting has been done, it’s time to figure out what is broken, and why. Here’s a short list:

  • Loading icons. Right now, I need to patch out the use of the icon cache to load icons. But in any case I am still considering putting the icons in the executable as resources, because that makes packaging on Windows much easier.
  • Qt5’s SVG loader had trouble with our svg icons; that was fixed by cleaning up the icons.
  • OpenGL was a huge task, needing a nearly complete rewrite — it works now on my development system, but I’m not sure about others.
  • Qt5’s tablet support is much better, but now that we use that instead of our own tablet support, we’ve lost some performance (work is ongoing) and some things have changed meaning, which means that the scratchpad and the popup palette are broken for tablet users.
  • In general, the user interface feels sluggish: even things like the preferences dialog’s widgets are slow to react.

And when all that is fixed, there is more to do: make new build environments for Windows (hopefully we can start using MSVC 2015 now) and OSX, see about dropping superfluous dependencies on things like DBus and, then…

Testing, testing and testing!

But I am getting confident that Krita 3.0 could be something we can let people try and test this year. And here is, for your delectation, a delectable screenshot:

Porting Krita to OpenGL 3.1/ES 2.0

Krita was the first painting application with an OpenGL accelerated canvas. We had that before Photoshop… Which also meant that the code was getting quite old fashioned. These days, life is supposed to be better. More flexible in any case. However, even though a 2D canvas is a simple thing, once you factor in rotation, zooming, panning and so on, the potential for bugs is quite big, and we’ve been fixing bugs for ages in the old code.

So I didn’t want to throw that away, but have as clean and straightforward as possible a port from the old code to start with. The old code mostly looked like this (for painting the transparency checkers background):

KisCoordinatesConverter *converter = coordinatesConverter();

QTransform textureTransform;
QTransform modelTransform;
QRectF textureRect;
QRectF modelRect;

converter->getOpenGLCheckersInfo(&textureTransform, &modelTransform, &textureRect, &modelRect);

KisConfig cfg;
GLfloat checkSizeScale = KisOpenGLImageTextures::BACKGROUND_TEXTURE_CHECK_SIZE / static_cast(cfg.checkSize());

textureTransform *= QTransform::fromScale(checkSizeScale / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE,
                                            checkSizeScale / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glViewport(0, 0, width(), height());
glOrtho(0, width(), height(), 0, NEAR_VAL, FAR_VAL);

glMatrixMode(GL_TEXTURE);
glLoadIdentity();
loadQTransform(textureTransform);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
loadQTransform(modelTransform);

glBindTexture(GL_TEXTURE_2D, m_d->openGLImageTextures->backgroundTexture());
glEnable(GL_TEXTURE_2D);

glBegin(GL_TRIANGLES);

glTexCoord2f(textureRect.left(), textureRect.bottom());
glVertex2f(modelRect.left(), modelRect.bottom());

glTexCoord2f(textureRect.left(), textureRect.top());
glVertex2f(modelRect.left(), modelRect.top());

glTexCoord2f(textureRect.right(), textureRect.bottom());
glVertex2f(modelRect.right(), modelRect.bottom());

glTexCoord2f(textureRect.left(), textureRect.top());
glVertex2f(modelRect.left(), modelRect.top());

glTexCoord2f(textureRect.right(), textureRect.top();
glVertex2f(modelRect.right(), modelRect.top());

glTexCoord2f(textureRect.right(), textureRect.bottom());
glVertex2f(modelRect.right(), modelRect.bottom());

glEnd();

glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);

In other words, we set a projection, a transformation matrix for the texture and for the model/view and then start drawing the vertices. Pretty simple. I was rather surprised when I did not find any clear tutorial on converting code like this through google. I’ve read a bunch of modern opengl books and tutorials by now, and they pretty much all have the same order of explanation, the same things they emphasize and the same “advanced” topics. But I couldn’t figure out how to draw my checkers or the tiles for my image. Yeah, I’m a linguist, not a mathematician, and I probable read these tutorials wrong or something.

In any case, after going through the Qt OpenGL examples, the tutorials on Wikibooks, the Red, Orange and Blue books, the Matt Gattis’ notes on porting to WebGL and more confused questions and more confusing answers on Stack Overflow than I care to count, I finally got something that works, and is as straight a translation of the old code as possible.

Purists will cavil at my use of attribute arrays and glDrawArrays — but the alternative, as far as I can tell, would be to redo all the matrix calculation and use matrices to place my tiles in the right location or to update and send new vertex buffer objects all the times. This works — and in the future it might even be pretty.

So, for posterity, and because there might be others in the same spot as me (to wit, tasked with porting OpenGL 1.3 code to OpenGL ES 2.0 or OpenGL 3.1 without compatibility profile), here’s a summary of my current code.

The vertex shader:

uniform mat4 modelViewProjection;
uniform mat4 textureMatrix;

attribute highp vec4 a_vertexPosition;
attribute mediump vec4 a_textureCoordinate;

varying vec4 v_textureCoordinate;

void main()
{
    gl_Position = modelViewProjection * a_vertexPosition;
    v_textureCoordinate = textureMatrix * a_textureCoordinate;
}

The fragment shader (needs to be expanded to handle color correction):

uniform sampler2D texture0;

varying mediump vec4 v_textureCoordinate;

void main() {
    gl_FragColor = texture2D(texture0, v_textureCoordinate.st);
}

And finally the code. The shader programs are all done using Qt’s shader classes, and I don’t show that code here — it’s in the calligra git repo anyway.

KisCoordinatesConverter *converter = coordinatesConverter();

QTransform textureTransform;
QTransform modelTransform;
QRectF textureRect;
QRectF modelRect;

converter->getOpenGLCheckersInfo(&textureTransform, &modelTransform, &textureRect, &modelRect);

// XXX: getting a config object every time we draw the checkers is bad for performance!
KisConfig cfg;
GLfloat checkSizeScale = KisOpenGLImageTextures::BACKGROUND_TEXTURE_CHECK_SIZE / static_cast(cfg.checkSize());

textureTransform *= QTransform::fromScale(checkSizeScale / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE,
                                            checkSizeScale / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE);

m_d->checkerShader->bind();

QMatrix4x4 projectionMatrix;
projectionMatrix.setToIdentity();
projectionMatrix.ortho(0, width(), height(), 0, NEAR_VAL, FAR_VAL);

// Set view/projection matrices
QMatrix4x4 modelMatrix(modelTransform);
modelMatrix.optimize();
modelMatrix = projectionMatrix * modelMatrix;
m_d->checkerShader->setUniformValue("modelViewProjection", modelMatrix);

QMatrix4x4 textureMatrix(textureTransform);
m_d->checkerShader->setUniformValue("textureMatrix", textureMatrix);

//Setup the geometry for rendering
QVector vertices;
vertices << QVector3D(modelRect.left(),  modelRect.bottom(), 0.f)
            << QVector3D(modelRect.left(),  modelRect.top(),    0.f)
            << QVector3D(modelRect.right(), modelRect.bottom(), 0.f)
            << QVector3D(modelRect.left(),  modelRect.top(), 0.f)
            << QVector3D(modelRect.right(), modelRect.top(), 0.f)
            << QVector3D(modelRect.right(), modelRect.bottom(), 0.f); m_d->checkerShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE);
m_d->checkerShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, vertices.constData());

QVector texCoords;
texCoords << QVector2D(textureRect.left(), textureRect.bottom())
            << QVector2D(textureRect.left(), textureRect.top())
            << QVector2D(textureRect.right(), textureRect.bottom())
            << QVector2D(textureRect.left(), textureRect.top())
            << QVector2D(textureRect.right(), textureRect.top())
            << QVector2D(textureRect.right(), textureRect.bottom()); m_d->checkerShader->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE);
m_d->checkerShader->setAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE, texCoords.constData());

    // render checkers
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_d->openGLImageTextures->checkerTexture());

glDrawArrays(GL_TRIANGLES, 0, 6);

glBindTexture(GL_TEXTURE_2D, 0);
m_d->checkerShader->release();

For Krita, there are quite a few todo’s left:

  • Restore the opengl outline cursor — now we use the qpainter one
  • Render onto a framebuffer object so this code can be integrated in Krita Sketch
  • Update the image texture tiles in a thread
  • Update the projection in a thread
  • Restore the colormanagement using OCIO
  • Check whether it runs on Windows, OSX (and Android)
  • Maybe move the layer composition to OpenGL using the GPUImage shader code?

Especially the testing on Windows is interesting, since the old opengl canvas never worked on Windows.

More Vacation Fun: smoother lines in Krita!

Ever since sigetch created a smooth inking pen for Gimp Painter, artists have been asking us to implement something similar in Krita. It’s a feature particularly in demand for inking comics, and since comics is one of Krita’s focus areas…

It basicaly works by looking at the history of the stroke — the last twenty or fifty of positions or so, the speed with which you paint and then calculates a new position, one that makes the line smoother. I don’t pretend to understand the mathematics — but it was pretty easy to figure out from Alexia Death’s clean and clear implementation in Gimp 2.8, so I took that as a model. Free software rocks more!

I actually couldn’t believe it when I was done, and Timothee Giet reported that it worked as per spec… But here’s a sketch from his hand that proves it!

A New Krita Feature: Flipbooks

I’m on vacation, but I cannot stop hacking… But instead of soul-destroying Windows build work, detective work implementing PSD export support or some hair-pulling bug-fixing, I wanted to have some clean, innocent fun…

Create a nice new feature. So over the past couple of days, I’ve implemented a flipbook.

It’s quite full-featured already, with flipbooks being available in the recent files list in Krita’s startup screen, a page for creating new flipbooks and finally a docker that shows the images and allows the user to flip between images in the flipbook, or save the current set of images as a flipbook.

There are bound to be some rough edges, and I’m wondering what kind of feature requests our users will think up to make the flipbook even more useful, but really, it’s already quite usable.

KDE on Windows

From Friday to Sunday, I attended the KDE on Windows sprint, hosted by Intevation, in Osnabrück. There were seven attendants, making it a nice and hacking-focussed sprint. Sure, there were discussions about improvements to the system and so on, as you can read on the meeting wiki page.

Important items are changing the versioning of the portage system by removing the version from the package names, improving the sandboxing of KDE applications (so they don’t overwrite each other settings, system configuration cache and so on) and integration of creatsing nsis installers in the emerge system.

We had some non-keyboard time as well, when on Friday, KDAB sponsored a german-style dinner, and on Saturday, KO an Arabian-style dinner, followed by drinks in the “unikeller”. Some pretty obscure German beer was had there, very tasty. And then some of us went back to the office of an all-night hack session…

See also Patrik SPendrin’s blog, with group picture….

Builds

Currently, I’m working with three or four build trees: one with Microsoft Visual C++, one mixed Microsoft Visual C++ and Intel Composer, one with Mingw, all natively on Windows. Dominik Schmidt demonstrated how easy it is to create 32 and 64 bit builds on Linux using Tomahawk as an example. I hadn’t heard of Tomahawk before, since my media player of choice is ogg123… But the build system looks very simple to use, and Tomahawk is pretty cool, too. Dominik spent a large part of the weekend integrating kdelibs, kde-runtime and kdepim into this cross-compiling system. This system depends heavily on the OpenSUSE build system, which I need to investigate anyway to make my CentOS packages easier to generate.

Check the tomahawk wiki for a basic cross-compiling howto: it really is utterly simple, once you realize that you need to do the same trick as tomahawk with a toolchain file for cmake definitions. That needs to be in order; from that point onwards, everything should just work. I’ll give it a try with Krita soon: the only dependencies still missing are exiv2 and eigen2.

This generates NSIS-based installers, but I also heard that other people have had success generating WIX-based MSI installers using mono on Linux and OSX, so that’s worth investigating as well.

Packages

Being a newcomer to the KDE Windows scene, I did some simple stuff, like creating a calligra package for the emerge system. If you put an emerge checkout on your windows system and follow the basic setup steps (really, just get the checkout, install python3, create an etc directory with copy of kdesettings.bat), you should be able to do an “emerge calligra” and get a working calligra installation. Of course, there might be problems along the way, but the nice people in #kde-windows know most of the answers!

I also started on packaging ilmbase, the basic dependency for OpenEXR. This was trickier, since ilmbase is an autotools-based package, so we had to add a CMake-based build system. That basically worked, so I added the package to emerge’s portage tree, and Patrick Spendrin then finished up and added OpenEXR. Maybe we should try to get the CMake system for those libraries upstreamed, though. The upshot of this is that Krita on Windows, once I’ve made new installers, will be able to handle 32-bits floating point/channel images. Not 16 bit floating point, though, since LittleCMS cannot handle that yet. But Marti has promised that for the next release…

Krita in history…

Krita in history…

“Azes is now being associated with the creation of the era of 58 BC that was to be known through the centures as the Krita, Malava or Vikramaditya, samvat, era. Possibly the era was also calculated for use in astronomy as the term krita, created, would suggest, but was given status by association with royalty.” Romila Thapar, The Penguin History of Early India, p. 220

Two worrying bugs

Krita uses OpenGTL, a very, very cool techology. We use it for filters and for colormodel definitions (mainly for floating point colorspaces). OpenGTL uses LLVM internally to compile from its domain specific language to fast native code, on the fly. Ultra-cool stuff, in short.

But recently, I’ve had two bug reports that worry me a lot, and I do not know how to handle them:

It looks like there’s problem having an application that links to llvm running on a system that uses the radeon driver. I’m not sure, llvm being black magic to me… I also don’t know who to approach in the gallium/radeon/llvm communities about this issue, and neither are the reporters. But if this is true, Krita has a big problem, and so have other applications that make use of the cool possibilities of llvm.

If you have any suggestions, please mail me directly!

Third Krita Sprint First Day

While I was forced by circumstances for forego the production of t-shirts for this sprint — and I feel bad about that! — the first day of the Third Krita Sprint has been great. Yesterday, our webmaster Bugsbane, a.k.a. Kubuntiac and Krita hacker Dmitry Kazakov arrived in Deventer already, and this morning we took the train to Amsterdam, to the the Blender studios. We’re crammed around the same glass table where last year selected Krita hackers saw a sneak preview of the Sintel movie. Feels weird!

Assembled today were artists David Revoy, Animtim, Silvio Heinrich and Kubuntiac, as well as hackers Adam, Dmitry, Sven, Lukas, Matus (tomorrow is his birthday!), Dmitry, Silvio (again, this guy puts in a double appearance). We got a surprise visit from a bunch of Danish game designers here for a game jam at the Binger Filmlab, who, of course, got a full demo.

The selection system in Krita was redesigned, bugs were filed and solved, lunch was consumed on a road-side terrace in the wonderful sunshine, dinner was had at the Blender team’s usual Thai restaurant — and we made plans for tomorrow.

We’ll start off around 10 with an artists session: all artists present are going to take about half an hour to work in Krita, a projector connected so we all can follow. We call it the “demo and gripe” sessions. That way the developers can learn about the way different artists really work, and we can spot glitches and issues with usability. We are going to try to record this session, both sound and screencast and make it available afterwards. No promises though, we might run into glitches there!

The afternoon is for the whole team to get a list of highly important issues that hamper artists most so we can set the priorities for the year following this sprint. After that — it’s hacking and discussion time!

Lamented unmaintained features in Krita

Krita 2.3 is really pretty good — although following the release we’ve found a number of rather serious issues, especially when working with images in the 50-100 megapixel range. But on a less important note, there are quite a few features in Krita that never really got any love after they were initially coded.

The pattern creation tool is one of those, and it’s getting more important now since it’s not possible to use patterns as a color source for freehand painting. The pattern creation popup was coded in 2006 by Bart Coppens (who is also responsible for quite a few other features in Krita but who’s too busy with his Phd these days to code for Krita).

As you can see, it’s quite simple:

There’s a preview, an update button, an use-it-now button and an add-to-predefined patterns button. If you try to make your pattern from a big image (like this 6000×4000 image), you get a warning, and the pattern will be downscaled to fit in 1000×1000. That’s because otherwise it’s pretty easy to run out of memory! Oh, and since yesterday you can select whether you want to create your pattern from the current layer or from the whole image.

What’s missing is using the selection, naming the pattern, exporting the pattern and who knows what else — I haven’t yet checked what other apps provide. Managing existing patterns is pretty primitive as well. And the layout of the popup is a bit of an embarrasment!

But there you have it — a nice small feature that’s suddenly become important. It was fine in 2006, but in 2011, we expect more of it. So… If you suddenly feel inspired to hack on this, join us on #krita on irc.freenode.net and we’ll get you started!

Speaking at Fosdem 2011

This year I’ll give a presentation at Fosdem. I’m not sure which day yet, though.. Updates will follow. The presentation will be very technical: I want to show how easy it is to build a new, custom interface on top of the Calligra Suite core engine — using QWidgets, QGraphicsView or even Qt Quick, for viewing and editing of documents.

I don’t think there’s any other office engine/application suite that makes it not just possible, but even relatively easy, to reuse the canvas, the file conversion code and even the tools in applications. Calligra is beginning to become sort of the webkit of the office document world.

There’s a lot of interest and a real need for this sort of possibility: I know for a fact that apart from Nokia’s FreOffice/Calligra Mobile, there are at least four projects working on a new Calligra user interface, using various technologies, some of them generic, some of them focussed on a particular target group, like medical information systems.

I’m really looking forward to it, but it’s going to be a weird kind of weekend for me, though, since Irina will be doing KDE booth duty, like last year, and I don’t think we have ever been away from the kids together for more than a day since they were small and still had grandparents who could care for them for a weekend.