147 Commits

Author SHA1 Message Date
Walter Werner SCHNEIDER 0c145b3815 op/clip: add bounds expansion after move
After moving the pen, the next action should update the bounds for the start position.

References: https://todo.sr.ht/~eliasnaur/gio/451
Signed-off-by: Walter Werner SCHNEIDER <contact@schnwalter.eu>
2025-07-16 09:46:32 +02:00
Walter Werner SCHNEIDER d76b4272aa f32: replace Affine2D{} with AffineId() for identity transformations
Reduces ambiguity by introducing AffineId() for representing identity transformation matrices.

References: https://todo.sr.ht/~eliasnaur/gio/655
Signed-off-by: Walter Werner SCHNEIDER <contact@schnwalter.eu>
2025-07-09 13:35:03 +02:00
Admin f73287be87 all: clean up code, upgrade to modern Go
Signed-off-by: ddkwork
2025-05-05 19:46:39 +02:00
Egon Elbre 3b5148a64e op/clip: add note about Path.End
Signed-off-by: Egon Elbre <egonelbre@gmail.com>
2024-04-15 10:18:25 +02:00
Elias Naur c515b7804e all: replace InvalidateOp with InvalidateCmd command
Curiously, InvalidateCmd is probably the only command that is appropriate
to call during layout.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2024-02-05 11:09:36 +00:00
Egon Elbre 5fa94ff67b op/paint: add nearest neighbor scaling
This adds support for nearest neighbor filtering,
which can be useful in quite a few scenarios.

  img := paint.NewImageOp(m)
  img.Filter = paint.FilterNearest
  img.Add(gtx.Ops)

Fixes: https://todo.sr.ht/~eliasnaur/gio/414
Signed-off-by: Egon Elbre <egonelbre@gmail.com>
2023-11-15 10:38:02 -06:00
Egon Elbre 49296bd0ca internal/ops: use uint32 for pc, version, macroID
4GB of render data should be sufficient for anyone.

By replacing an int with uint32, it allows for a smaller memory
footprint and faster caching. Example impact on rendering static
labels.

                                     │  before.txt  │             after.txt              │
                                     │    sec/op    │   sec/op     vs base               │
LabelStatic/1000runes-RTL-arabic-32     98.08µ ± 3%   88.17µ ± 1%  -10.10% (p=0.002 n=6)
LabelStatic/1000runes-RTL-complex-32    103.9µ ± 2%   101.9µ ± 1%   -1.84% (p=0.009 n=6)
LabelStatic/1000runes-RTL-emoji-32      113.3µ ± 2%   100.7µ ± 3%  -11.11% (p=0.002 n=6)
LabelStatic/1000runes-RTL-latin-32     100.01µ ± 1%   92.31µ ± 1%   -7.69% (p=0.002 n=6)
LabelStatic/1000runes-LTR-arabic-32     97.90µ ± 2%   87.92µ ± 2%  -10.19% (p=0.002 n=6)
LabelStatic/1000runes-LTR-complex-32   102.63µ ± 2%   99.81µ ± 1%   -2.75% (p=0.002 n=6)
LabelStatic/1000runes-LTR-emoji-32     106.56µ ± 2%   98.47µ ± 1%   -7.59% (p=0.002 n=6)
LabelStatic/1000runes-LTR-latin-32      97.51µ ± 1%   92.60µ ± 3%   -5.03% (p=0.002 n=6)
geomean                                 102.4µ        95.09µ        -7.10%

Signed-off-by: Egon Elbre <egonelbre@gmail.com>
2023-11-09 15:18:46 -05:00
Chris Waldon 27193ae8e8 op/clip: prevent no-op path segments
This commit prevents the insertion of LineTo and QuadTo path segments that have
no visible effect on the path (because the path's pen is already at their end state).
This eliminates whisker artifacts from some stroked paths. Thanks to Morlay for the
bug report leading to this fix.

Fixes: https://todo.sr.ht/~eliasnaur/gio/535
Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2023-09-18 09:28:11 -05:00
Elias Naur ae3bd2a1e1 op/paint: add opacity operation
The new paint.PushOpacity allows for adjusting the opacity of a group
of drawing operations.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2023-09-08 11:46:17 -05:00
Elias Naur 80196f3c3e op: tolerate incomplete macros
Before this change, a macro not Stop'ed would result in an endless
loop during op decoding.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-10-04 17:11:35 -06:00
Elias Naur 61b2e37691 all: format comments with go fmt ./... using Go 1.19
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-08-06 12:26:03 +02:00
Elias Naur b5f12c5f26 f32: [API] unexport Rectangle
There are no public API that uses f32.Rectangle anymore. Move Rectangle
to an internal package for internal use.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-05-31 10:24:09 +02:00
Elias Naur 48a8540a68 all: [API] change clip.RRect and UniformRRect to take integer coordinates
Like the change to op.Offset before this, clip.RRect and UniformRRect
is usually used with integer coordinates. Change to integer coordinates
to eliminate many useless conversions to float32.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-05-31 10:24:09 +02:00
Elias Naur a63e0cb44a all: [API] change op.Offset to take integer coordinates
op.Offset is a convenience function most often used by layouts. Layouts
usually operate in integer coordinates, and the float32 version of op.Offset
needlessly force conversions from int to float32. This change makes op.Offset
take integer coordinates, to better match its intended use.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-05-31 10:24:09 +02:00
Elias Naur 1f11a5a17b f32,gpu,op/clip: add f32.Rectangle method for converting to image.Rectangle
Creating an image.Rectangle from a f32.Rectangle is used by two packages in Gio
and about to be used for a third. Add a Round method to f32.Rectangle to avoid
duplicating the implementation.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-03-30 22:20:27 +02:00
Egon Elbre 3fd231367f internal/stroke: optimize arc drawing
Arc with a small angle doesn't need many segments.

Updates: https://todo.sr.ht/~eliasnaur/gio/313
Signed-off-by: Egon Elbre <egonelbre@gmail.com>
2022-03-14 11:42:59 +01:00
Egon Elbre 4172566aad io/pointer: [API] make cursor name into a byte
Add most of the common cursors defined by different systems.

Normalize cursor names to match CSS.

This is API change: some cursor names have changed, and the
underlying type is no longer a string.

Signed-off-by: Egon Elbre <egonelbre@gmail.com>
2022-03-01 14:04:21 +01:00
Elias Naur 773a7e48a0 op/clip: don't panic when stroking empty path
Fixes: https://todo.sr.ht/~eliasnaur/gio/367
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-02-22 12:04:10 +01:00
Elias Naur ce116abded op/clip: specify path for stroked Rects
Fixes: https://todo.sr.ht/~eliasnaur/gio/358
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-02-10 19:01:08 +01:00
Elias Naur 65199a2274 op,internal/ops: support CallOps without macros
A recorded macro is prefixed with an internal macro op that stores
the end of the macro. The end is used to efficiently skip the macro
when not calling it. The call a macro, the CallOp stores the start
position of the macro.

To support seamless wrapping of Ops lists, this change removes the
dependency on the macro op prefix from CallOp. Internal code can
now call an Ops like this:

    var ops op.Ops
    var wrapper op.Ops

    ops.AddCall(&wrapper.Internal, &ops.Internal, ops.PC{}, ops.PCFor(&ops.Internal))

References: https://todo.sr.ht/~eliasnaur/gio/318
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2022-01-20 09:56:30 +01:00
Pierre Curto 9a2fee1c0e op, internal/ops: cleanups
Remove unnecessary fill when starting a recording in op.Record.
Have the exact number of possible stack kinds in ops.Ops.

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2022-01-18 15:49:43 +01:00
Pierre Curto 2ce9ad36af clip: check Path ops are not mixed with others
Multiple operations Op, such as clip.Path, cannot
be interleaved with other ops. This patch adds a
mechanism to ensure that is the case by starting
multi ops with ops.BeginMulti and ending them with
ops.EndMulti while operations are written to op.Ops
with ops.WriteMulti.
This mechanism is applied to clip.Path.

Fixes: https://todo.sr.ht/~eliasnaur/gio/336
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2022-01-15 19:16:07 +01:00
Pierre Curto 11bb86166a op/clip: automatically close Path in Outlines
Unclosed path segments in Path will be automatically
closed by a line.

Fixes: https://todo.sr.ht/~eliasnaur/gio/320
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2021-12-21 10:13:20 +01:00
Elias Naur 6534639276 io/pointer,op/clip: remove clip.Circle, pointer.Rect, pointer.Ellipse
They've been deprecated for a while, and gio-x is updated to not use
them.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-12-20 16:23:16 +01:00
Pierre Curto 872b4ba41b op/clip: fix Path open state
If a Move/MoveTo did not move the pen, the Path
was still set as open.

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2021-12-02 11:50:01 +01:00
Pierre Curto 227c5a132b op/clip: don't panic when Path is used without an initial MoveTo
A Path initialized with Begin should be ready to use with its pen at (0,
0). Make it so.

Updates gio#311

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2021-11-26 22:34:25 +01:00
Pierre Curto 5fd4ecb011 clip: add Path method to Ellipse
Useful when drawing ellipses with Stroke.
Also, this makes it consistent with the other clip operations.

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2021-11-26 22:23:03 +01:00
Pierre Curto 6bfd980044 op/clip: don't panic on zero-sized Ellipse
When the rectangle used in an Ellipse has no width
or height, the path would panic with "path not closed".
Use an empty rectangle path in that case instead.

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2021-11-16 15:50:53 +01:00
Pierre Curto bcf3ff77ff op/paint: remove unnecessary check in ImageOp
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2021-11-10 17:31:50 +01:00
Pierre Curto 3f25a49c75 clip: add Path method to Rect
Useful when drawing non-rounded rectangles with Stroke.
Also, this makes it consistent with RRect and Circle which
also have a Path method.

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
2021-11-07 11:02:52 +01:00
Elias Naur 29cea1db49 io/pointer,io/router: replace AreaOp with clip.Op
Pointer hit areas and paint clip areas are separate concepts, but
similar enough to warrant merging. This change replaces pointer hit
areas with clip areas, so Gio is left with just one area concept (in
package op/clip).

The reason for separating the concepts in the original Gio release was
because of my being unsure general path/stroke hit areas would ever be
implemented, let alone efficient.

This change represents a change of mind, in the sense that it's better
to have an incomplete API than two separate area concepts.

Leave the deprecated pointer.Rect, pointer.Ellipse for temporary
backwards compatibility.

This is an API change. Most existing programs should continue to build
with this change, but may have to adjust to having all clip.Ops participate
in InputOp hit areas.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-11-03 14:12:31 +01:00
Elias Naur 28fc08a508 op/clip: introduce axis-aligned Ellipse shape
And deprecate (but not yet remove) Circle.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-11-03 14:12:31 +01:00
Elias Naur 563eb60382 op: remove op.Save
op.Save, op.SaveStack were compatibility stop-gaps.

API change. Most op.Save/Loads can safely be removed altogether, or
replaced with

trans := op.Offset(f32.Pt(0, 0).Push(gtx.Ops)
...
trans.Pop()
cl := clip.Rect(image.Rect(-1e6, -1e6, 1e6, 1e6)).Push(gtx.Ops)
...
cl.Pop()

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-10-26 12:13:11 +02:00
Elias Naur e14e33b87e op/clip: remove Op.Add
The Add method was a compatibility stop-gap.

API change. Use clip.Op.Push and Pop the return value to explicitly mark
the region affected by the clip operation.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-10-26 12:13:11 +02:00
Elias Naur 0048f7be1d internal/ops: hide Ops methods by converting them to package functions
Ops is in the internal package ops, but external clients can reach its
method through op.Ops.Internal. Hide them by converting them to internal
package functions.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-10-12 14:50:15 +02:00
Elias Naur bd1ef92dc4 op/clip: remove complex stroke support
In a discussion with Raph Levien, the author of our compute renderer
implementation, it became clear to me that it's not at all certain that
complex strokes will ever be efficiently supported by a GPU renderer.

At the same time, the machinery for converting a complex stroke to a
GPU-friendly outline has a significant maintenance cost. Further, it is
surprising to users that complex strokes are significantly slower and
allocate memory.

This change removes support for complex strokes, leaving only
round-capped, round-joined strokes supported by the compute renderer.
The default renderer still converts all strokes to outline, but it also
caches the result.

This is an API change. The complex stroke conversion code has been moved
to the external gioui.org/x/stroke package, with a similar API.

Updats gio#282 (Inkeliz brought up the allocation issue)

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-10-08 18:10:47 +02:00
Elias Naur 391725b9d0 op: move Ops internal methods and state to internal package ops
Merge package opconsts into ops as well; it only existed to break
import cycles.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-10-08 17:21:56 +02:00
Elias Naur 936c266b03 all: [API] split operation stack into per-state stacks
The op.Save and Load methods exist to support the need for
transformation, clip, pointer area state to behave as stacks. For
example, layout needs to apply an offset to its children but not
subsequent operations.

Before this change, op.Save and Load were used to save and restore the
state:

    ops := new(op.Ops)
    // Save state.
    state := op.Save(ops)
    // Apply offset.
    op.Offset(...).Add(ops)
    // Draw with offset applied.
    draw(ops)
    // Restore state.
    state.Load()

A drawback with the op.Save mechanism is that there is no direct
connection between the state change and the saving and loading of state.
This causes confusion as to when a Save/Load is needed and who is
responsible for performing them, which leads to subtle bugs and over-use
of Save/Loads.

This change gets rid of the general state stack and replaces it with
per-state stacks. There is now a stack for transformation, clip, pointer
areas, and they can only be restored by the code pushing state to them.
The example above now becomes:

    ops := new(op.Ops)
    // Push offset to the transformation stack.
    stack := op.Offset(...).Push(ops)
    // Draw with offset applied.
    draw(ops)
    // Restore state.
    stack.Pop()

For convenience, transformation also be Add'ed if the stack operation is
not required.

Simple state such as the current material no longer has a way to be
restored; it is assumed the client of a PaintOp adds their desired
material operation before it.

API change: replace op.Save/Load with explicit Push/Pop scopes for
op.TransformOps, pointer.AreaOps, clip.Ops.

To ease porting, this change retains a version of op.Save/Load that
saves and restores the transformation and clip stacks. It also retains
an Add method for clip.Op.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-10-08 17:21:56 +02:00
Elias Naur 62daadb37c op/clip: add absolute Path.ArcTo for completeness
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-10-07 17:36:36 +02:00
Elias Naur 414be0a0b3 gpu: Merge GPU.Collect and GPU.Frame
There's no meaningful reason to have them separate. The intention was to
enable rendering concurrent with other processing, but that's gaining
framerate at the expense of input latency and complicating ImageOp
semantics.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-08-24 08:30:52 +02:00
Petr Karmashev 1efe68c154 op/paint: remove duplicate word
Signed-off-by: Petr Karmashev <smonkl@bk.ru>
2021-08-04 12:58:05 +02:00
Elias Naur 5197f637a7 gpu: [compute] compute and store clipping path hashes during construction
The hash of the clipping paths that affect drawing operations are computed
and used to quickly determine that two operations are not equal, the
most likely outcome of a comparison.

However, for paths that are constructed once and cached computing the
hash at every frame is wasteful. This is especially true for text, which
is both cached and also among the largest paths in a frame.

This change moves the hashing to op/clip.Path construction time, and
stores the hash in the ops list so it won't be re-computed at every use.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-07-27 14:34:18 +02:00
Elias Naur 75c9736708 op/paint: skip zero images
Updates gio#235

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-06-16 12:01:42 +02:00
Inkeliz b4e6d3efba internal/stroke, op/clip: remove unused variables
Fixes gio#233

Signed-off-by: Inkeliz <inkeliz@inkeliz.com>
2021-06-07 17:02:48 +02:00
Elias Naur 06ce077436 op/clip: compute bounds during Path build
The current renderer transforms and processes paths before sending them
to the GPU. It can compute bounds during processing.

The new renderer passes paths verbatim to the GPU, but needs the bounds
for constructing clip bounds.

This change computes the bounds during construction, so it is available
at use. As a bonus for storing the bounds with the path, path caches
(such as for storing text fragments) automatically reuse the bounds
calculations as well.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-04-27 16:49:06 +02:00
Elias Naur bc2c3db43e op/clip,gpu: move approximation of complex strokes to op/clip.Op.Add
Before this change, the two renderers both had special case code for
approximating strokes they don't support natively. This change moves
that conversion to clip.Op.Add, for several reasons:

- The compute renderer no longer need fallback logic and caches for
  strokes it doesn't support.
- The approximation logic is slow. Moving it to clip.Op.Add will not
  speed it up, but will make the cost easier to spot in profiles. Until all
  strokes are supported natively, users can use macros to cache
  expensive strokes.
- Reduced garbage: Op.Add takes an op.Ops anyway, and can use that for
  storing the approximated stroke outline.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-03-23 19:37:45 +01:00
Elias Naur 7825bda8f8 internal/stroke,op/clip: don't import op/clip from internal/stroke
To avoid an import cycle in a future change, internal/stroke can no
longer import op/clip. Move required op/clip functionality to
internal/stroke and duplicate the remaining types.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-03-23 15:28:52 +01:00
Elias Naur f9cf6ff20a op/clip: separate function for approximating arcs with quadratic curves
Both Path.Arc and the internal stroke package needs to support arcs;
this change isolates the approximation computation into a function we
can move to internal/stroke in a follow-up.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-03-23 15:01:00 +01:00
Elias Naur 13da40f601 gpu,op/clip: [compute] get rid of stroke vs fill flags
The fill mode is now controlled by a SetFillMode command, not by flags
on each path segment and fill command.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2021-03-19 21:46:31 +01:00
pierre ac800a9d8f op/clip: optimize zero corner radius in RRect if pixel-aligned
Signed-off-by: pierre <pierre.curto@gmail.com>
2021-03-16 19:00:47 +01:00