A Tool the Engine Does Not Have
Clone is the tool where any brush paints with the picture itself, taken from a source circle that keeps its distance from the brush. Drag the circle onto a patch of sky, paint over a power line, and the sky arrives under the brush with its texture intact. libmypaint has nothing like it.
The nearest thing it has is smudge, and it is worth seeing exactly why smudge is not a clone tool. When a libmypaint brush smudges, update_smudge_color calls get_color over a radius around the dab. That returns one averaged color, which is mixed into a running smudge color, and the dab is then painted in that single color. Smudge carries a color along the stroke. It never carries the picture: a dab is one color, so the grain of the sky, an edge, the highlight in an eye cannot ride on it. No setting changes that, because it is the shape of the computation.
Split Where From What
Ariandel's answer is to divide the job. The brush decides where and how strongly. The picture decides what. libmypaint is excellent at the first half and is asked to do nothing else.
When a Clone stroke begins, beginStroke paints the clone brush in plain white, with eraser, smudge and alpha lock all at 0 (first code block). It also builds a CloneStroke, which owns a scratch TileStore of its own and a BrushSurface over that store. libmypaint paints into the scratch store as it would paint anywhere: pressure, tilt, jitter, a shaped tip, symmetry, all of it. White means only the alpha matters. What lands in the scratch store is pure coverage, a map of where and how much.
Laying the Picture Through It
After each batch, lay(_:over:of:) (second code block) visits every tile the coverage reached in that batch and does four things:
- Reads the source. It takes the tile's own rectangle, moves it by
offset, and reads that rectangle fromoriginalin fix15. The shifted rectangle usually straddles up to four tiles;fix15(in:)assembles it. - Finds what lies under.
original[key], the same tile as the stroke found it. - Merges through the coverage.
ard_fix15_clone_mergecomputesunder + (source − under) · c / ONEper channel, wherecis the coverage's alpha at that pixel. - Respects alpha lock. On a locked layer,
ard_fix15_keep_alpharestores the original alpha, and a tile that was empty before stays empty.
Notice what the merge is written over: under, the layer as the stroke found it, not the live tile. The coverage keeps accumulating for the whole stroke, so each batch recomputes the tile from scratch as "the original, moved toward the source by everything the brush has laid so far." The result depends only on total coverage, never on how many batches it took to get there. Laying it again is harmless.
The Offset
Painter.cloneOffset is a SIMD2<Int> in whole canvas pixels: source = brush + offset. The app sets it from the circle. When Clone opens with no circle yet, the circle appears left of center (35% across, 45% down the view). The first stroke after the circle moves fixes the offset as source − start, rounded to whole pixels; later strokes keep that distance, and while Dad paints, the circle follows the brush at it. That is an aligned clone: lift, paint somewhere else, and the source moves with you.
Whole pixels are a choice, not an accident. A fractional offset would mean resampling the source on every batch, and every resample softens the picture a little. A whole-pixel offset makes reading the source a straight copy, so what arrives under the brush is what was there.
Never Its Own Fresh Paint
original is taken once, when the stroke begins: layer.tiles.frozenTiles(), every live tile frozen. Thanks to copy-on-write that costs no pixel copies at all; the layer copies a tile only when the stroke first writes it (Track 3).
Why not read the source from the live layer, which would be simpler? Because the brush travels. Paint so the source trails behind the brush along its own path, and the source rectangle soon lands on pixels this very stroke just cloned. Read from the live layer, and you clone your clone, then clone that, and a few pixels repeat across the stroke. The third code block runs both versions on one row: from original, the row reads ABCDEFABCDEFGHIJKLMNOPQR, a true copy; from the live layer, ABCDEFABCDEFABCDEFABCDEF, the first six pixels stamped over and over. The engine suite pins the real thing as aStrokeNeverClonesItsOwnFreshPaint: a red square, an offset of ten pixels back along the stroke, red just past the square, and empty canvas further on.
The same original survives a redraw. When QuickShape snaps a stroke or a taper redraws it at the lift, startAgain builds a fresh CloneStroke with an empty coverage and the same original, so the redrawn clone still copies the layer as the stroke first found it.