Rotatable, Scalable patterns
Closed, ResolvedPublic

Description

"Patterns are used in many places, for many purposes: to fill layers, to fill geometric strokes, to fill vector objects. It would be really useful to have an easy way to rotate, translate and scale the patterns. It's going to need a lot of deep hacking, even after the vector tools work has been done."

Implies:

  • Rotate, scale and translate for vector fill, geometric fill, general fill and fill layers.

Notes:

  • All this for the brush texture will be more tricky.
  • SVG supports rotate and resize for patterns.
  • Vector patterns were discussed but don't fall under this stretchgoal.
woltherav added a comment.EditedOct 20 2019, 12:38 PM

Okay, having slept on it, and looking at the code again, Cairo is not doing a per pixel transformation, but rather is just passing on the matrix to... wherever, and I for the live of me cannot find the place where the transform is applied to the pixel. I do suspect the that per-pixel transform might be a strategy to pursue. (so, pixel to paint x, invert transform on x, what do we find in the original tiled pattern at that location, paint that at x), but I am not smart enough to understand how we'd apply transform filters here.

EDIT:

Thinking about it further, this bug in inkscape (which doesn't link to the actual fix :| ), seems to imply that cairo rotates the pattern and then paints it staggered. The bug then seems to be caused by 'fractional' widths are heights?

PoC
https://invent.kde.org/snippets/928

ok, so...

There's three functions dealing with filling with pattern inside the fillpainter.

  1. fillRect(const QRect &rc, const KoPatternSP pattern, const QPoint &offset)
  2. fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KoPatternSP pattern, const QPoint &offset)
  3. fillRect(qint32 x1, qint32 y1, qint32 w, qint32 h, const KisPaintDeviceSP device, const QRect& deviceRect)

We should keep 3 as it is currently, as it blits and therefore keeps composition mode in check. The pattern rotation will use the accessors instead.

Fill layer, fill tool and layerstyle all call function 1.
Brush texture option calls function 3.
Geometric drawing tools I cannot find right now???
Vector shapes use patternbackground, which draws the pattern with QPainter. Either I implement the exact same function as in fillpainter to use here somehow, or I try to use the function to figure out how to get a rectangle of the new transformed pattern and use that to paint it with qpainter?

Plan:

  • Get a kiswraparounddefaultboundswrapper inside kisdefaultbounds, which is a wrapper which wraps a defaultboundsbase, with the appropriate bounding rect and turns on wraparound mode.
  • Allow KoPatterns to carry a QTransform, this is necessary here because svg patterns will have their own transforms, so we should load those in and use them as default values for pattern transforms.
  • Implement a second version of Function 3 that is called from Function 2 when the qtransform is not identity. This second function will be using the wraparound and random accessors.
    • @dkazakov suggested to use the KisPerspectiveTransformWorker for doing this alternate transform, but it requires a progressupdater, and I am not sure how to access that from fillpainter? Do I need to implement progressupdater inside fillpainter?
  • Get generator/fill pattern layers to work first and hash out a UI here. Everything else can come later?

Allow KoPatterns to carry a QTransform, this is necessary here because svg patterns will have their own transforms, so we should load those in and use them as default values for pattern transforms.

I'm not sure about this point. Right now the transfromation/modification of the pattern is a property of the "user" of the pattern. It means that it is stored in the brush, layer style or vector background. I don't think we should move it into KoPattern. If we do, then allthe places supporting patterns will have to be refactored to support rotations. I'm not sure this refactoring is in the scope of this task. There will also be problems with file-compatibility: you cannot store a transformation in a PNG/JPEG/PAT image easily.

KisPerspectiveTransformWorker... but it requires a progressupdater, and I am not sure how to access that from fillpainter?

Progress updater is present in the fill painter, just call this->progressUpdater() and pass it to the transform worker

Get generator/fill pattern layers to work first and hash out a UI here. Everything else can come later?

Fill Layers is the easiest place for rotated patterns, because there are no performance concerns. Brush texturing will be the most complicated part.

I did some tests just now regarding rotation in the brush texture:

using the perspective transform worker+10° rotation: works on small sizes (40), slows down significantly on big sizes (200). Interestingly, the perspectivetransformworker+identity transform is faster than the current blitted method, as it uses the kispainter::copyAreaOptimizedOldData function instead of blitting. Might be that the brush texture option would indeed be limited by the limited amount of rotations that can be rendered to a sensible pattern square...

Anyhow, we should proly use the new function without a transform just for the speed up :)