This document describes the brush engine design for Krita 2.0. For Krita 2.1, certain changes are needed in certain areas, which will also be noted here.
Krita is designed to accommodate pluggable brush engines. The core principle of the brush engine framework is to enforce as little policy as possible: a brush engine does not need to fit into a framework of cooperating brush engines (as proposed by Matthew Woehlke), rather, a set of brush engines can implemented to cooperate with each other. A brush engine can also be completely independent. Brush engines can be as simple as the eraser engine and as complex as the dynamic paintop.
Brush engines modify paint device pixel data destructively, although it is possible to record brush strokes for replay, which means that brush engines need to save pertinent data.
Brush engines can make use of KisBrush-derived brush or not, or not.
The brush engine framework is found in krita/image/brushengine and contains the following classes:
Note about 'incremental' : there are two kinds of "incremental": one must be renamed to "rate" and is a property of paintops that determine whether the paintop action should continue even when the pointer doesn't move. This should be (2.1) replaced with 'rate' and a customizable interval for the timer that calls KisPaintOp::paintAt(). The other type is about the paint deposition method: either 'direct', which means we work directly on the node's paint device, or 'indirect', which means we paint on a temporary paint device with the ALPHA_DARKEN composite op and on stroke end composite the temporary device with the node's paint device. Also see [An architecure for "natural" brush types in Gimp 1+] by Raph Levien: the Gimp makes exactly the same distinction.
In 2.1 we must make this more flexible, it would be nice, for instance, to allow a filter to modify the stroke so we get gradients, or wet fringes etc. This is mainly the responsibility of the KisPaintActionTypeOption in the libpaintop library.
Many brush engines are similar in one or more respects: a lot need access to KisBrush based brushes, a lot need custom pressure-response curves, and so on. libpaintop is a convenience shared library brush engines can use to construct consistent-looking brush engines.
(Note: in 2.0, the widget to modify a setting, the data representing that setting and often the algorithm to apply the setting are coalesced into one class, look for instance at KisPressureDarkenOption. This makes it impossible to untangle the settings and the settings widget, which leads to all kinds of problems. This must be redesigned in a responsible mvc way for 2.1. The current situation is caused by historical reasons.)
A brush engine plugin consists of the following parts:
* factory * settings widget * settings object * brush engine
The factory is responsible for creating a settings widget. The single settings widget is shared by all views and documents in a single Krita process (Note: in 2.1, refactor this to have one widget per view). The factory also creates the paintop itself from a settings instance.
Your settings widget can either be derived from KisPaintOpOptionsWidget in libpaintop or from KisPaintOpSettingsWidget. Take care about initializing the widget from the data in the paintop settings instance, and saving changes in the widget to the settings.
Note: actually, the widget implements KisConfigWidget, which means that changes in the widget should lead to a sigConfigChanged signal, which in turn should be implemented in KisPaintOpSettingsWidget to write the widget state to the settings object, using the sigPleaseUpdatePreview signal that is emitted.
Note: and then, this should lead to an update of the preset preview (which is already almost implemented).
The gui design right now is quite simple: we have a combobox to select a certain brush engine. Next to that is a combobox that can be used to select a preset. The list with presets is empty right now, except for the default preset. Next to that is a widget that (should) show a preview of the a stroke made with the preset, and that, when clicked, expands to preset settings popup.
The preset settings popup (should have) has two tabs: one with the current settings, one with an overview of all the presets for this paintop. This overview is disabled for now, but will be similar to the brushes overview. However, this overview widget should be expanded (2.1) to allow a listview, an iconview etc. of the preset previews.
We have the following data model for presets:
* KoInputDevice 1 * paintopid1, current settings * paintopid2, current settings * paintopid3, current settings * KoInputDevice 2 (like stylus with id 1) * paintopid1, current settings * paintopid2, current settings * paintopid3, current settings * KoInputDevice 3 (like stylus with id 2) * paintopid1, current settings * paintopid2, current settings * paintopid3, current settings
So, whenever the current input device or current paintop changes, we need to make sure the current settings are saved with the right input device and paintop, and make a different settings object active.
* krita/ui/kis_paintop_box.h * krita/ui/widgets/kis_paintop_presets_popup.h