Yet Another Brush Proposal
mw_triad at users.sourceforge.net
Tue Mar 25 23:40:43 CET 2008
Here's my attempt at an update based on IRC discussion. I'm sure I've
screwed *something* up, so please tell me what :-).
(Note: I still don't like KisPaintTool, it has nothing to do with the
paint! :-). So I left that KisStrokeTool, but I renamed (or tried!)
everything else to follow the current stuff.)
= Introduction =
== Glossary ==
:Generates/updates one or more KisStroke(s). Currently named KisPaintTool.
:Holds all information generated by a KisStrokeTool, used by KisStamp to
apply paint to the canvas.
:Holds information about the current paint, such as the color and opacity.
:Determines the mask of affected pixels from a KisStroke.
:Determines how the canvas should be updated using the current content
of the canvas, the current KisPaint (likely a subclass specific to the
subclass of this KisPaintOp), and the mask from KisStamp.
:The word "paint" will be used throughout to refer to the material
currently on the canvas, which may be paint in the usual sense, but also
ink, chalk, charcoal, water, etc.
== Justification ==
To implement "natural" painting, it is desired to simulate a number of
real-world tools. Currently, we have a Chinese ink tool, and a sumi-e
tool is planned. Eventually, we would like to support a number of tools,
* Bristle brushes of varying types (e.g. flat, tapered), with varying
paints, such as ink, tempera, oil, powders, etc.
* Airbrushes with inks
* Various types of pens (roller, felt, quill)
* Pencils (charcoal, chalk, others?)
* Brushes with no paint applied to the canvas (i.e. affecting the
"paint" already on the canvas without introducing new "paint")
This proposal is instead to describe a clear separation of the tool from
the physical interaction of the paint. This will enable much greater
code re-use, and also allow artists to make their own decisions about
what combinations of tool and paint are sensible, rather than making the
decision at an architectural level.
The UI will need to be carefully thought out (or else, we don't expose
the guts until we feel we have a usable interface), but I believe that
good presets can make this system work effectively while allowing
tremendous flexibility, and at the same time significantly reducing code
duplication. Combining good presets with a rich, physics-based pigment
algorithm should reduce the number of KisPaintOp types to a very small
number, possibly even one single KisPaintOp that can charcoal, ink, oil
paint, and most other real-world media. At the same time, most tools can
be folded into a full-featured bristle brush KisStamp, a rigid-body
KisStamp to handle quills, felt tips, chalks, and so forth, an airbrush
KisStamp, and a few others. This results in about a half dozen or so
classes that can implement dozens or even hundreds of tool/paint
== Basic Operation ==
KisStrokeTool takes raw input and translates it into a KisStroke. The
KisStroke is a parametric function of t, that can be evaluated for such
things as x, y, pen tilt, pen angle, pen pressure, dx/dt, dy/dt, stroke
angle, etc. Various stroke tools would cover freehand, lines, paths,
Valerie's "double-ended strokes", tablet simulation, etc. KisStamp does
not need to know any of these details.
KisStamp takes the stroke and translates it into a series of pixel masks
where the KisPaint will be applied by the KisPaintOp. This operation
should be designed to work on partial strokes, to allow real-time
updating. Tools include bristle brushes, sponges, buckets, etc.
Finally, KisPaintOp takes the mask from KisStamp and uses it to update
the canvas, and optionally the current KisPaint (for bidi painting, not
necessarily supported by all KisPaintOps).
= Classes =
== KisStroke ==
KisStroke contains information about the current stroke. It represents a
multidimensional, single-parameter parametric function that can be
evaluated by time to determine various information about the requested
point, including x, y, distance along the stroke, pen
pressure/tilt/angle, stroke angle, and any rates-of-change useful to
KisStamp. It is a concrete class and has no subclasses.
Time is used as the parameter as some KisPaintOps may operate on this
bases (e.g. airbrushes).
== KisStrokeTool ==
KisStrokeTool maps user input to generate KisStrokes, creating "fake"
stylus data as needed. KisStrokeTool is responsible for smoothing, etc.
The class is abstract with various implementations including:
* paths (paths are persistent), bezier tool (non-persistent paths)
* freehand tool, line tool
* various shape tools
== KisStamp ==
KisStamp takes a KisStroke (and optionally various data from the canvas)
and generates a series of pixel masks that identify where the paint is
being applied to the canvas. Each mask is linearly distributed along the
time axis. In addition to alpha data, each mask contains an overall
distance-delta (the distance travelled along the stroke since the
previous mask), distance along the path, stroke angle, and some
additional information (per-pixel) conveying what part of the tip
touched the canvas. It may also contain any additional data we decide is
needed by KisPaint or KisPaintOp. The KisStamp specifies if bidi
painting is allowed (since bidi doesn't make sense for some tools).
The class is abstract with various implementations including:
* bristle brush (including bristle stiffness, density, size, brush shape
and size, etc)
* sponge brush; parameters including stiffness, hole size/density, and
shape allow simulating...
** felt tips
** quill tips
** pencils, chalks
** "digital brushes"
* bucket tool; boundary options include...
** impasto (height)
** color similarity (can be broken down by channel or hue/luma/sat/etc)
== KisPaint ==
The base KisPaint produces a color that is constant or based on the
stroke distance, angle, and KisStamp 'part of tip' parameters, or any
combination thereof. Subclasses may ignore these parameters or use
others, and may do other things like bidi transfer. The 'part of tip'
parameters may be combined with the stroke distance, e.g. to simulate a
pencil/chalk with various colors of pigment, stacked.
Currently I only know of one reasonable subclass; 'natural paint'. With
parameters for wetness, viscosity, etc, this simulates:
* paints (oil, tempera, etc)
* inks, watercolors, washes
* pastels, chalks, charcoals
* no paint (pushes paint-on-canvas around, also bidi transfer can happen)
In general, a KisPaint subclass will correspond to a particular
KisPaintOp subclass. KisPaint holds the color information, while
KisPaintOp manipulates the canvas and optionally (bidi painting) the
KisPaint. A KisPaint is always preserved and may optionally be restored
for each new stroke. KisPaint can be thought of as the data portion of a
painting operation, while KisPaintOp is the algorithm portion and may
contain no data of its own. It would thus be reasonable to merge the two
== KisPaintOp ==
KisPaintOp takes the masks from KisStamp and, combined with the KisPaint
and the existing canvas contents, determines how to update the canvas.
The class is a reference implementation that applies the base KisPaint
directly (simple "digital painting"). Subclasses (particularly the
'natural paint' variant) may alter their reaction to the masks, e.g. to
simulate "bleeding" of the paint, and may also update their KisPaint.
Some subclasses (e.g. filter, clone) may not use a KisPaint.
* natural paint (see 'natural paint' under KisPaint)
* clone operation
This results in the canvas content being generically represented as one
or more "natural paints".
ELANG: input is in wrong language (please use English on English lists)
More information about the kimageshop