Week 30: Custom brush works

Creating custom brushes is important for digital painters. A brush mask you can create using procedural dialogs we have (like Auto brush, Softbrush) has advantages of being somehow like a vector – you can easily scale them and rotate them without artefacts. But they are restricted to the shape – we support rectangular or circular shapes only.Recently I had idea of creating vector brushes with QPainterPath. I think we need to create some nice ui for that and it could work. There are problems, like supporting features like hardness/softness, but I did not dig deeper. It is nice area where somebody can play in Krita.

Bitmap brush has advantage of being any shape you want, but when you scale it too much, you see artefacts. Usually you open some image or photo reference or you use your current state of the canvas, make some selection with selection tools and save the selected pixels to your palette of your brush masks. This did not worked in Krita for a long time. We have custom brush dialog in Brush Tip for this in Krita. The custom brush dialog was disabled probably since 2.0.x series, because of it’s buggy behaviour. We decided in the planning phase that we add this on the Action plan and I will work on the fixes.

I started with the exploration of the code. First I fixed cloning of the KisBrush, somebody forgot to copy some important d-pointer members when we create a new clone from KisBrush.
Next problem was that the selection made on canvas was not taken into account. The brush was always created from the whole image you see on canvas. It was fixed quite easy. I just did copy of the projection of the canvas (the composition of all layers) and I applied global selection mask on it. Then I computed bounding box and tada – we have correct brush data.

Cloudy brush in custom brush

Then I was working on improving the workflow. E.g. you setup spacing, we recreated the brush again, so if you tried to paint with the brush, tweak the spacing, you lost the brush. You lost the brush also everytime the dialog of custom brush was shown. Fixed. Use button Use as Brush if you need to recreate the brush. Also you could not give a name to the brush which is shown in the UI. I added this little feature there too. If you use empty name, the brush will be named with the current date&time. The file name of the brush is always random, that might change soon. I would like to thank enkithan for input on the UI of the dialog.

We separated the GUI from the painting of the widgets a long time ago when Sven worked on presets. Custom brush did not painted. The fix for painting involved putting temporary brush into our brush resource server. I had some problems with crashes. KisBrush is KisSharedPointer. You probably know this concept. Short version: you don’t delete the allocated memory manually, the data are deleted automatically thanks to the reference counting in shared pointer. We add the brushes to our resource server based on KoResourceServer and that one is not aware of the shared pointers. I added some new behaviour to survive with shared pointers. The solution is not the best one, it smells like a workaround, but I discussed this with team and we decided that it is good for now. The proper solution would be resource server with support for adding objects that are shared pointers, not just pointer that will be owned by resource server.

I started to fix painting with color masks. You can check option Use color as mask. It means that the mask will be converted to grayscale and will serve as alpha mask. You can select any color for the brush mask. Currently it is broken, I will try to fix it. I worked on it already, but the design is little complicated to read and I’m not finished yet. As I worked on it, I noticed some place for optimization and I made the painting with predefined brushes little faster (speedup is 1.1).