Compare commits

..

241 Commits

Author SHA1 Message Date
Matthias Clasen 5efa3400d9 xxx: Plug a memory leak in GtkPopover 2020-12-13 10:30:22 -05:00
Matthias Clasen 41c6bfec67 Fix gsk_spline_decompose_arc
For short segments (angle < tolerance), we were not
emitting any curve at all when decomposing arc.
2020-12-13 10:30:22 -05:00
Matthias Clasen 5a4d19644f pathbuilder: Misc docs improvements 2020-12-13 10:30:22 -05:00
Matthias Clasen c9e9f6184d path: Misc doc improvements 2020-12-13 10:30:22 -05:00
Matthias Clasen 3a3c16c475 Add gsk_path_measure_get_{path,tolerance}
These are just nice apis to have and avoid having to carry
these around as extra arguments in many places.

This was showing up as inconvenience in writing tests
for the measure apis.
2020-12-13 10:30:22 -05:00
Benjamin Otte dfb25771a7 xxx path)_fill 2020-12-11 01:42:17 +01:00
Benjamin Otte b286b04b7d testsuite: Add tests for the dasher 2020-12-11 01:42:17 +01:00
Benjamin Otte 22aa12ade5 path: Add a foreach function that dashes a path 2020-12-11 01:42:17 +01:00
Benjamin Otte 79ba157664 path: Deal with non-uniformness of progress parameter
The progress is non-uniform, so simple translation of progress doesn't work.
So check if larger and smaller values inch closer towards minimal distance.
2020-12-10 20:18:05 +01:00
Benjamin Otte c21c63c357 path: Always decompose conics into at least 2 segments
Conics are evil in that their parameter skews towards the center, and if
it's a very flat conic (weight almost equal to 0), then we'd approximate
it with a single segment and not subdivide, which would cause the
parameter to be wildly off around 0.25 or 0.75.

And that would cause offset calculations to fail.
2020-12-10 20:18:05 +01:00
Matthias Clasen 78f04ae486 testsuite Add curve tangent tests 2020-12-10 20:18:05 +01:00
Benjamin Otte bd4dcc54fb testsuite: Add a test for the conic that got us segment() 2020-12-10 20:18:05 +01:00
Benjamin Otte 580d3057ce path: Add gsk_curve_segment()
Using split() twice with scaled t values does not work with conics.
2020-12-10 20:18:05 +01:00
Benjamin Otte 0060734103 testsuite: Add a test for gsk_curve_decompose() 2020-12-10 20:18:05 +01:00
Benjamin Otte 95ce8aafb4 testuite: Add tests for gsk_curve_get_tangent() 2020-12-10 20:18:05 +01:00
Matthias Clasen 0603bc2f45 testuite: Add tests for gsk_curve_get_point()
Add a few tests for gsk_curve_get_point().

Since GskCurve is not public api, we add gskcurve.c
as source to the test binary.
2020-12-10 20:18:05 +01:00
Benjamin Otte 07864c3070 curve: Split eval() into get_point() and get_tangent()
That's more in line with the get_start/end_point/tangent() functions.

Plus, those calls are independent and we usually want one or the other.
2020-12-10 20:18:05 +01:00
Matthias Clasen 95d5b9a057 Add gsk_curve_get_{start,end}_tangent
Add a way to get the tangents at the start and end of the curve.
This will be used in stroking.
2020-12-10 20:18:05 +01:00
Benjamin Otte d5fe88a787 testsuite: Add conics to the random paths 2020-12-10 20:18:05 +01:00
Benjamin Otte 343324c205 path: Add GskCurve
GskCurve is an abstraction for path operations. It's essentially a
collection of vfuncs per GskPathOperation.

GskStandardContour has been ported to use it where appropriate.
2020-12-10 20:18:05 +01:00
Benjamin Otte 12dde7b4a1 path: Introduce gskpathop
A gskpathop is a pointer to a graphene_point_t* with the low bits used
to encode the GskPathOperation. It's an easy way to introduce API for
operations.

So far it's just used to replace GskStandardOperation.
2020-12-10 20:18:05 +01:00
Benjamin Otte c07afdf1d7 WIP: css: Replace border rendering code with GskPath
The weight is wrong still, I need to compute the correct one to get real
45deg circle corners and not just roughly correct ones.
2020-12-10 20:18:05 +01:00
Benjamin Otte 7959ed96e2 WIP: pathbuilder: Add gsk_path_builder_add_rounded_rect()
It works, but does not use a custom contour yet.
2020-12-10 20:02:13 +01:00
Benjamin Otte 43360a1cfa path: Add conic curves
So far this just adds the API, if you use it, you'll get lots of
g_warnings().

This will be fixed in future commits.
2020-12-08 21:33:46 +01:00
Benjamin Otte 22753c685e path: Rename to gtk_path_builder_add_segment()
It's about bulding paths, not about measuring them.
2020-12-06 18:54:01 +01:00
Benjamin Otte 40ae74e03d path: Split contours into their own file
I'm not sure I want to keep all contours in one file, but for now that's
how it is.
2020-12-06 18:54:01 +01:00
Benjamin Otte 95b71d47c9 path: Make all private contour APIs take a GskContour
... instead of a path, index tuple.
2020-12-06 18:54:01 +01:00
Benjamin Otte cfd8855676 stroke: Add support for dashes
... and hook it up in the node parser and for Cairo rendering.
2020-12-06 18:54:01 +01:00
Matthias Clasen 7c7bb31d93 gsk: Implement parsing fill and stroke nodes
Make serialization and deserialization work for stroke and
fill nodes.
2020-12-06 18:54:01 +01:00
Benjamin Otte a0c9e33865 path: Add flags to gsk_path_foreach()
This way we can default to the siplest possible foreach() output - like
cairo_copy_path_flat() decomposing everything into lines - and add flags
to get more and more fancy.

This will be useful to have conics automatically decomposed for Cairo
drawing or if we want to add more line types in the future.
2020-12-06 18:54:01 +01:00
Benjamin Otte dccec4ced2 testsuite: Add an in_fill() test 2020-12-06 18:54:01 +01:00
Matthias Clasen 8f3f16d251 Implement gsk_path_measure_in_fill
Implement this in the obvious way, using the decomposed form
of standard contours. Since the decomposed form is part of the
measure object, this api moves from gsk_path_in_fill to
gsk_path_measure_in_fill.
2020-12-06 18:54:01 +01:00
Benjamin Otte d0ce83e853 testsuite: Add a parsing test
This test includes an implementation of a gsk_path_equal() func with
a tolerance that is necessary because parsing does not always work
100% exactly due to floating point rounding, so we can't just
compare the to_string() output.
2020-12-06 18:54:01 +01:00
Matthias Clasen 91dd2e449e path: Special-case rects and circles
Write out the commands for rects and circles in a special
way, and add code in the parser to recognize this, so we
can successfully round-trip these through the SVG path format.

The special way - for people who want to use it for debugging -
for now is that we use uppercase "Z" to close standard paths, but
lowercase "z" to close our special paths.

A test is included, but the random path serializations should take care
of it, too.
2020-12-06 18:54:01 +01:00
Matthias Clasen 6ff7c67c67 path: Fix serialization for circles
The svg A can not do a full circle, since it is a two point
parametrization - if the start and end point are the same,
it draws nothing. So, use two arcs.
2020-12-06 18:54:01 +01:00
Benjamin Otte de1213cbb2 testsuite: Add librsvg path tests 2020-12-06 18:54:01 +01:00
Matthias Clasen 217921ff22 path: Implement gsk_path_parse
Implement the SVG path syntax to read back the strings
that we generate when serializing paths. The tests for
this code are taken from librsvg.

This includes an elliptical arc implementation according
to the SVG spec. The code is mostly taken from librsvg,
but pretty directly follows the SVG spec implementation
notes. We don't export this, since the parametrization
is inconvenient. We do want an arc_to API, but
these are not the arcs we are looking for.
2020-12-06 18:54:01 +01:00
Matthias Clasen 13153e4b30 path: Implement SVG arcs
This is elliptical arc implementation according to the SVG spec.
The code is mostly taken from librsvg, but pretty directly
follows the SVG spec implementation notes.

We don't export this, since the parametrization is inconvenient.
We do want an arc_to API, but these are not the arcs we are
looking for.

It will be used in parsing SVG path syntax.
2020-12-06 18:54:01 +01:00
Matthias Clasen 1eca63e442 stroke: Add miter limit
Add a miter limit to GskStroke. This will be needed to
fully implement line joins.

Also introduce the GSK_LINE_JOIN_MITER_CLIP value,
following SVG 2.0. cairo does not have it, so translate
it to plain miter when using cairo.
2020-12-06 18:54:01 +01:00
Matthias Clasen 2ed55da45c Documentation typo fixes 2020-12-06 18:54:01 +01:00
Benjamin Otte ab77041437 testsuite: Add relative path functions
They're making the paths slightly weirder, but they test public API, so
woohoo!
2020-12-06 18:54:01 +01:00
Benjamin Otte 1ef2a39c4d pathbuilder: Add relative path commands
And gsk_path_builder_get_current_point().

They will be needed by the string parser.
2020-12-06 18:54:01 +01:00
Benjamin Otte bccada91b6 path: Add GSK_CIRCLE_POINT_INIT() to initialize points on the circle
This is just splitting out a commonly done operation into a macro.
2020-12-06 18:54:01 +01:00
Benjamin Otte bed254cc7a pathbuilder: Redo semantics for starting curves
We now always have a "current point" which is either the last point an
operation was made to, or (0, 0) if no drawing operation has
been made yet.

Adding a contour of any kind to the builder will always update the
current point to that contour's end point.
2020-12-06 18:54:00 +01:00
Benjamin Otte bb224682c1 xxx: demo 2020-12-06 18:53:24 +01:00
Benjamin Otte 8e2f90c887 path: Split GskPathBuilder into its own file
... and add missing API docs.
2020-12-06 18:53:17 +01:00
Benjamin Otte 705617a6d5 testsuite: Add a test using get_point() and get_closest_point() 2020-12-06 18:51:55 +01:00
Benjamin Otte bc019bd1fc testsuite: Add a test for get_point() 2020-12-06 18:51:55 +01:00
Benjamin Otte c9edcb1009 testsuite: Update create_random_path()
1. Allow specifying the max number of contours
2. Be smarter about creating the paths:
   With 10% chance, create a "weird" path like the empty one or only
   points or things like that.
   Otherwise create a bunch of contours, with 2/3 a standard contour,
   with 1/3 a predetermined one.
2020-12-06 18:51:55 +01:00
Benjamin Otte 967968fb0b gtk-demo: Add cute maze demo 2020-12-06 18:51:55 +01:00
Benjamin Otte 9e7d3b4473 testsuite: Add tests for gsk_path_measure_get_closest_point() 2020-12-06 18:51:54 +01:00
Benjamin Otte 5819bfeca7 path: Add gsk_path_measure_get_closest_point()
... and gsk_path_measure_get_closest_point_full().

Those 2 functions allow finding the closest point on a path to a given
point.
2020-12-06 18:51:54 +01:00
Benjamin Otte e611becb9f spline: Use Skia's tolerance checks
This avoids measuring being too far off (it's still off, but it's less
than a percent now.
2020-12-06 18:51:54 +01:00
Benjamin Otte 541f7aa2bb testsuite: Add tests for gsk_path_measure_add_segment() 2020-12-06 18:51:54 +01:00
Benjamin Otte 7163015b1a gtk-demo: Add a text-on-path demo 2020-12-06 18:51:54 +01:00
Benjamin Otte 8422d7965f xxx: path_fill demo 2020-12-06 18:51:54 +01:00
Benjamin Otte 0092f55bd1 path: Add gsk_path_measure_get_point()
Allows querying the coordinates and direction of any specific point on a
path.
2020-12-06 18:51:54 +01:00
Matthias Clasen 3e8fcf4831 path: Add gsk_path_add_circle()
Adds a circle contour, too.
2020-12-06 18:51:54 +01:00
Benjamin Otte a0c680011c pathmeasure: Implement support for beziers
Instead of treating bezier curves as lines, we properly decompose them
into line segments now so that we can treat those as lines.
2020-12-06 18:51:54 +01:00
Benjamin Otte b8ddef040d path: Implement gsk_path_to_cairo() using foreach() 2020-12-06 18:51:54 +01:00
Benjamin Otte a1f759336c path: Add gsk_path_foreach() 2020-12-06 18:51:54 +01:00
Benjamin Otte acfcb9da6c path: Collect flags
We don't need them yet, but maybe later.
2020-12-06 18:51:50 +01:00
Benjamin Otte b2fdb10aaa testsuite: Add path tests 2020-12-04 02:58:34 +01:00
Benjamin Otte f92dbb8caa pathmeasure: Add gsk_path_measure_add_segment()
This allows chunking paths, weeee.
2020-12-04 02:58:34 +01:00
Benjamin Otte 484b7dd5f2 path: Add gsk_path_builder_add_path() 2020-12-04 02:58:34 +01:00
Benjamin Otte 307eccc451 gsk: Add GskPathMeasure
An object to do measuring operations on paths - determining their
length, cutting off subpaths, things like that.
2020-12-04 02:58:34 +01:00
Benjamin Otte 57227e8f64 path: Change data structure for standard path
Instead of the Cairo method and imitating cairo_path_data_t, use the
Skia method and keep points and operations separate.

That way we get a points array that includes the starting point -
because it's always the end point of the previous operation.
2020-12-04 02:58:34 +01:00
Benjamin Otte a5b1842acf popover: Use fill and stroke nodes instead of Cairo
... to render the arrow.

The arrow should really be turned into a real thing - maybe an icon?
2020-12-04 02:58:34 +01:00
Benjamin Otte e482dd739f snapshot: Add gtk_snapshot_push_stroke() 2020-12-04 02:58:34 +01:00
Benjamin Otte c1b8eb50ed gsk: Add GskStrokeNode 2020-12-04 02:58:34 +01:00
Benjamin Otte 12c5bb6f8c gsk: Add GskStroke
It's unused in this commit. This just prepares the new object.
2020-12-04 02:58:34 +01:00
Benjamin Otte 16d504a78c demos: Add a simple demo filling a path 2020-12-04 02:58:34 +01:00
Benjamin Otte e22a12bb42 snapshot: Add gtk_snapshot_push_fill() 2020-12-04 02:58:34 +01:00
Benjamin Otte 4fc6ef1a5f gsk: Add GskFillNode
Take a rendernode as source and a GskPath and fill the region in the
path just like cairo_fill() would.
2020-12-04 02:58:31 +01:00
Benjamin Otte 5bda1e0d4b gsk: Add GskPath 2020-12-04 02:56:18 +01:00
Benjamin Otte aca2f15f05 listview: Use the correct scroll policy
Use the horizontal policy for horizontal decisions, not the vertical
one.

This broke in 0011ce949c.
2020-12-04 02:56:18 +01:00
Benjamin Otte b69e956d60 docs: Put render nodes in their own sections 2020-12-04 02:56:17 +01:00
Benjamin Otte feaff8825c roundedbox: Remove unused API 2020-12-04 02:55:57 +01:00
Emmanuele Bassi f1ff8f9aad Merge branch 'wip/chergert/macos-decelerate' into 'master'
macos: fix typo when creating scroll events

Closes #3418

See merge request GNOME/gtk!2916
2020-12-03 22:54:46 +00:00
Christian Hergert 115ea624d9 macos: fix typo when creating scroll events
This fixes an issue where we would ignore events with Y delta
and no X delta while scrolling due to a typo when checking for
any delta.

This fixes deceleration of kinetic scrolling on the macOS backend.

Fixes #3418
2020-12-03 13:46:47 -08:00
Matthias Clasen 6ecae6c5f8 Merge branch 'wip/chergert/gdk-macos-for-master' into 'master'
macos: fix motion event delivery after closing transient window

Closes #3419

See merge request GNOME/gtk!2910
2020-12-03 20:07:16 +00:00
Matthias Clasen 6d76b587cf Merge branch 'wip/chergert/shadertoy-macos-fix' into 'master'
demos: make alienplanet demo work on macOS OpenGL

See merge request GNOME/gtk!2915
2020-12-03 19:00:39 +00:00
Christian Hergert 8e62ff50fe demos: make alienplanet demo work on macOS OpenGL
On the macOS OpenGL implementation, the use of noise2 as a
function within the glsl shader collides with the builtin noise2 of a
different signature.

This changes the name to something similar (noize2) so that we
do not risk colliding names when linking.

With this commit, the shadertoy alienplanet demo works on mac
OpenGL (albeit still with the Cairo renderer).
2020-12-03 10:23:13 -08:00
Matthias Clasen 0daa905a27 Merge branch 'wip/otte/conic' into 'master'
Implement a GL shader for conic gradients

See merge request GNOME/gtk!2913
2020-12-03 17:14:01 +00:00
Emmanuele Bassi d1a4ad3db6 Merge branch 'wip/tintou/gdk-backends-header' into 'master'
build: Use the correct path to the header of the wayland and x11 backends

See merge request GNOME/gtk!2914
2020-12-03 12:32:24 +00:00
Benjamin Otte 2c1bd399d2 glrenderer: Implement a shader for conic gradients 2020-12-03 13:07:17 +01:00
Benjamin Otte fac5fba0dc rendernode: Fix some wrong constants for conic gradients
copy/paste from other nodes left some wrong values in there and that
screwed things up quite a bit.
2020-12-03 13:07:17 +01:00
Benjamin Otte c907ad83b0 glrenderer: Emit more useful error messages
Catch the error when it happens, so that we can emit a specific and more
helpful error message.

Also verify that all branches in the code now do indeed set a proper
GError when they fail, so that the final catch-all is no longer needed.
Instead, assert that the error is set so that we catch future code
additions early that do not set the GError.
2020-12-03 13:07:17 +01:00
Corentin Noël 585ba777c2 build: Use the correct path to the header of the wayland and x11 backends 2020-12-03 10:02:03 +01:00
Matthias Clasen f8ee4cfea5 NEWS: Updates 2020-12-02 23:10:39 -05:00
Matthias Clasen 35d2cbefe6 Merge branch 'wip/otte/conic' into 'master'
Add support for conic gradients

See merge request GNOME/gtk!2911
2020-12-03 03:54:24 +00:00
Christian Hergert 004f0a6596 macos: plug leak of application windows
This ensures that we don't leak window references inside the action muxer.
Otherwise, we risk not disposing the windows upon gtk_window_destroy()
and blocking the main loop from quitting.

Fixes #3419
2020-12-02 19:44:02 -08:00
Christian Hergert b68e0bb3fb muxer: add helper to get group by name
This is useful when you need to read a group back out of the muxer.
2020-12-02 19:44:02 -08:00
Christian Hergert 92f0216605 macos: ensure element is part of queue
We don't want to risk decrementing length field unless this is actually
part of the queue.
2020-12-02 19:44:02 -08:00
Christian Hergert e317b9be00 macos: maintain GList element consistency
We need to keep this consistent so that we can look things up
faster in other places. Therefore, just take the hit here and clear
the entire list ensuring prev/next poniters are cleared.
2020-12-02 19:44:01 -08:00
Christian Hergert 2e52386be5 macos: send focus-out event to windowing 2020-12-02 19:44:01 -08:00
Christian Hergert 9431c70a6a macos: freeze updates until surface is mapped
This more closely matches the X11 backend in terms of freezing
updates on the surface initially until we get mapped.
2020-12-02 19:44:01 -08:00
Matthias Clasen 6e67d44aa3 Merge branch 'matthiasc/for-master' into 'master'
Matthiasc/for master

Closes #3429

See merge request GNOME/gtk!2912
2020-12-03 03:43:19 +00:00
Benjamin Otte e622013f7e css: Add support for conic-gradient()
This comes complete with animation support. For a good time, try:

@keyframes conic {
  100% { background-image: conic-gradient(from 1turn, red, lime, blue, yellow, red); }
}

window {
  background-image: conic-gradient(red, lime, blue, yellow, red);
  animation: conic infinite linear 5s;
}
2020-12-03 04:13:33 +01:00
Benjamin Otte 3886f0c530 Merge branch 'wip/otte/diediedie' into 'master'
Remove GtkFileChooserButton

See merge request GNOME/gtk!2909
2020-12-03 01:31:39 +00:00
Benjamin Otte eb9c204535 gtk: Remove GtkFileChooserButton
... as discussed in the meeting.
2020-12-03 02:02:27 +01:00
Matthias Clasen ab9e99218f docs: Remove mentions of <accelerator>
That is no longer supported, keyboard shortcuts
are done with GtkShortcut now.

Fixes: #3429
2020-12-02 19:59:44 -05:00
Matthias Clasen 714c610d6b gtk-demo: Add a search button
Easter eggs are great, but search shouldn't be one.
2020-12-02 19:53:55 -05:00
Benjamin Otte 8706d69e60 snapshot: Add gsk_snapshot_append_conic_gradient() 2020-12-03 01:15:53 +01:00
Benjamin Otte 55a242bd81 gsk: Add GskConicGradientNode 2020-12-03 00:47:54 +01:00
Matthias Clasen 61b40c47f9 Merge branch 'fix/keyboard-input' into 'master'
Gdk4/Win32: Correct calls to gdk_key_event_new()

See merge request GNOME/gtk!2903
2020-12-02 22:14:27 +00:00
Matthew Jakeman 710d0620a4 Gdk4/Win32: Correct call to gdk_key_event_new()
The keycode and modifier (state) parameters are in the wrong order
for gdk_key_event_new() in the gdk win32 backend, which causes
key up/down events to be populated incorrectly.
2020-12-02 22:14:27 +00:00
Benjamin Otte 71cb7c2063 rendernodeparser: Split out a function 2020-12-02 21:48:35 +01:00
Matthias Clasen a482d870dd Merge branch 'wip/chergert/gdk-macos-for-master' into 'master'
Revert "macos: specify a window level for surfaces"

See merge request GNOME/gtk!2908
2020-12-02 20:15:51 +00:00
Emmanuele Bassi bc4992fef9 Merge branch 'ebassi/doc-fixes' into 'master'
Ebassi/doc fixes

Closes #3428

See merge request GNOME/gtk!2906
2020-12-02 20:08:05 +00:00
Christian Hergert b38d0d7d9e macos: resign main/key when hiding window
This isn't done automatically for us, so we need to synthesize it in
our hide helper.

With this commit, we properly re-focus the new main/key window after
we have closed a transient-for window.
2020-12-02 11:36:38 -08:00
Christian Hergert 853a9c7a8c macos: disconnect frame clock when destroying surface 2020-12-02 11:35:02 -08:00
Christian Hergert 26b9254ac7 macos: track grab serial like other backends
This is what is done elsewhere, so copy that here too now that we actually
generate serials for events.
2020-12-02 11:34:31 -08:00
Benjamin Otte a51f11999a tests: Remove testsvg
That demo lives in gtk-demo now.
2020-12-02 20:17:28 +01:00
Christian Hergert f11c23f407 Revert "macos: specify a window level for surfaces"
This reverts commit ca8b00e871.

This isn't needed and causes issues with other applications getting
stacked beneath our windows.
2020-12-02 11:15:57 -08:00
Emmanuele Bassi 0c8de4e561 Fix transfer of GtkTreeExpander.get_item()
We return a full reference to the item.

Fixes: #3428
2020-12-02 19:10:13 +00:00
Emmanuele Bassi 13f9993007 docs: Add missing fields to GtkTextBufferClass 2020-12-02 19:05:41 +00:00
Emmanuele Bassi b2b451d762 docs: Hide private files/directories in the GTK reference 2020-12-02 19:05:41 +00:00
Emmanuele Bassi 2e146a56f7 docs: Mark GtkModelButton's section as private
The widget is not public any more.
2020-12-02 19:05:41 +00:00
Emmanuele Bassi 34197d4f8a docs: Add more symbols to the GDK reference 2020-12-02 19:05:41 +00:00
Emmanuele Bassi 39dbd91bb0 docs: Add yet another private header 2020-12-02 19:05:41 +00:00
Emmanuele Bassi 901d99d7c5 docs: More ignored files 2020-12-02 19:05:41 +00:00
Emmanuele Bassi 8f6e3848f6 docs: Remove duplicate symbol 2020-12-02 19:05:41 +00:00
Emmanuele Bassi 3548350dfa docs: Add missing GdkDragSurfaceInterface gtk-doc annotation 2020-12-02 19:05:41 +00:00
Emmanuele Bassi caae240bc7 docs: Ignore harder
First of all, we must list *all* ignored headers. Since we have public
headers in the x11 and wayland directories, we must explicitly declare
all headers that we consider private under those directories.

The "quartz" subdirectory was renamed "macos", with the new macOS
backend. The "mir" directory was removed, so there's no need to ignore
it.

We are also missing a bunch of ignored headers in the top-level gdk
directory.

Finally, pass the list of ignored files to gtkdoc-mkdb, so we won't get
missing declaration warnings.
2020-12-02 19:05:41 +00:00
Emmanuele Bassi f5f6ceece5 docs: Remove private symbols from the API reference 2020-12-02 19:05:41 +00:00
Emmanuele Bassi 4caceb5dd4 Remove gtk-doc annotation from private symbols
Keep the documentation, just tweak it so that gtk-doc won't try to find
the declarations of these private symbols.
2020-12-02 19:05:41 +00:00
Matthias Clasen 3da41b6021 Merge branch 'wip/chergert/gdk-macos-for-master' into 'master'
macos: fix window stacking

See merge request GNOME/gtk!2907
2020-12-02 18:38:14 +00:00
Christian Hergert 9b9fb4d7a3 macos: place above transient-for when presenting
We need to re-attach to the transient-for window whenever we present or
we risk getting placed behind the window by the display server. Apparently
that setting does not persist across a hide of the NSWindow.
2020-12-02 10:21:16 -08:00
Christian Hergert ca8b00e871 macos: specify a window level for surfaces 2020-12-02 10:15:43 -08:00
Matthias Clasen 4434889e41 Merge branch 'matthiasc/for-master' into 'master'
Matthiasc/for master

See merge request GNOME/gtk!2905
2020-12-02 15:57:39 +00:00
Bilal Elmoussaoui 475352fbc0 StringList: add missing nullability annotations 2020-12-02 10:36:29 -05:00
Bilal Elmoussaoui ad30ac3932 Shortcut: add missing nullability annotations 2020-12-02 10:29:16 -05:00
Bilal Elmoussaoui 9d52ca1887 StringFilter: get_search fix nullable annotation 2020-12-02 10:28:07 -05:00
Bilal Elmoussaoui ec537d75e4 Text: add missing nullability annotations 2020-12-02 10:28:07 -05:00
Bilal Elmoussaoui b47ea9421f TextView: add missing nullability annotations 2020-12-02 10:28:07 -05:00
Bilal Elmoussaoui 845d2e5433 TreeModel: add missing nullability annotations 2020-12-02 10:28:07 -05:00
Bilal Elmoussaoui 50310f7b14 TreeView: add missing nullability annotations 2020-12-02 10:28:07 -05:00
Matthias Clasen af944aa74c widget: Add a missing nullability annotation 2020-12-02 10:08:58 -05:00
Matthias Clasen b5077d02ed Clarify the docs for gtk_widget_class_set_css_name
Reword, and mention the default css name.
2020-12-02 10:06:01 -05:00
Bilal Elmoussaoui 6a3bd1c4d1 window: default_icon_name can be null 2020-12-02 10:00:18 -05:00
Emmanuele Bassi 2fcd0e21bd Merge branch 'ebassi/for-master' into 'master'
Various documentation fixes

See merge request GNOME/gtk!2900
2020-12-02 13:40:25 +00:00
Daniel Mustieles 5127726bbc Updated Spanish translation 2020-12-02 13:54:32 +01:00
Daniel Mustieles db456a70ee Updated Spanish translation 2020-12-02 09:26:08 +01:00
Emmanuele Bassi 770e848953 Merge branch 'wip/chergert/gdk-macos-for-master' into 'master'
macos: various macos improvements for GTK 4

See merge request GNOME/gtk!2902
2020-12-02 01:14:30 +00:00
Christian Hergert 9e1dd15e31 macos: use Cairo renderer by default
Until the GL renderer is working on macOS OpenGL, we need to default
to the Cairo renderer.
2020-12-01 16:24:15 -08:00
Christian Hergert 27b9a9e7ef macos: glFlush() when switching GL contexts
The Mac OpenGL programming guide suggests that you glFlush() before changing
contexts to ensure that the commands have been submitted.
2020-12-01 16:19:04 -08:00
Christian Hergert a020c901ba macos: treat some toplevel NSWindow like document windows
This makes the window animate in by the window manager like other
application windows on macOS. Currently ignored for transient windows.
2020-12-01 16:19:04 -08:00
Emmanuele Bassi cdad68e858 docs: Ignore generated Wayland protocol file 2020-12-01 18:03:31 +00:00
Emmanuele Bassi ef36028526 docs: Ignore more private files 2020-12-01 18:01:33 +00:00
Emmanuele Bassi 69b1b19315 docs: Annotate missing symbols 2020-12-01 17:28:16 +00:00
Emmanuele Bassi 516f71bbdc Use the appropriate prefix for GtkBuilderClosureFlags
Facilitate the job of glib-mkenums, and ensure that the enumeration
nicknames are computed reliably.
2020-12-01 17:26:34 +00:00
Emmanuele Bassi d820fea7a4 docs: Annotate GtkBuildableParser 2020-12-01 17:21:45 +00:00
Emmanuele Bassi 3e8d157118 docs: Hide GtkBitsetIter fields 2020-12-01 17:18:38 +00:00
Emmanuele Bassi 67f45940ce docs: Annotate GtkAssistant::escape action signal 2020-12-01 17:18:23 +00:00
Emmanuele Bassi 95400e6451 docs: Annotate GtkAppChooserButton::changed 2020-12-01 17:16:05 +00:00
Emmanuele Bassi d46fe31f54 docs: Ignore more files 2020-12-01 15:51:52 +00:00
Emmanuele Bassi 4f67be1c8e docs: Add missing annotations for GtkTextTagTable 2020-12-01 15:51:52 +00:00
Emmanuele Bassi c13e164f25 Make GtkCenterLayout accessors more idiomatic
Add argument validation, and emit a layout-changed signal whenever the
start, center, and end widgets change.
2020-12-01 15:51:52 +00:00
Emmanuele Bassi eba8f36d4f docs: Fix annotations in GtkCenterLayout
Missing ':' separator.
2020-12-01 15:51:52 +00:00
Emmanuele Bassi 37f11ab77d docs: Annotate GskRenderer 2020-12-01 15:51:52 +00:00
Emmanuele Bassi 8b898818c2 docs: Annotate the GL shader objects 2020-12-01 15:51:52 +00:00
Emmanuele Bassi 0f62ab6c42 docs: Annotate all render node types 2020-12-01 15:51:52 +00:00
Emmanuele Bassi c524b72d13 docs: Annotate structure types used by GskRenderNodes 2020-12-01 15:51:52 +00:00
Emmanuele Bassi b5313dbd4d docs: Annotate GdkDeviceTool 2020-12-01 15:51:52 +00:00
Emmanuele Bassi b9a1827da7 docs: Annotate GdkGLTexture 2020-12-01 13:48:07 +00:00
Emmanuele Bassi 1057189ea2 docs: Annotate GdkMemoryTexture 2020-12-01 13:47:56 +00:00
Emmanuele Bassi 879b2845ad docs: Annotate GdkPaintable
The dummy typedef needs a gtk-doc stanza, and the interface
documentation needs to be slightly clarified.
2020-12-01 13:44:51 +00:00
Emmanuele Bassi ecce3756d4 docs: Annotate GdkPopup 2020-12-01 13:42:07 +00:00
Emmanuele Bassi 6e4dd5811d docs: Annotate GdkSnapshot 2020-12-01 13:41:28 +00:00
Emmanuele Bassi 598e1304f6 docs: Annotate the GdkToplevel interface 2020-12-01 13:39:48 +00:00
Emmanuele Bassi e3fbc0ee9c docs: Fix typo in the GdkTolevelLayout annotation 2020-12-01 13:38:33 +00:00
Emmanuele Bassi 4021734047 docs: Annotate the GdkContentProviderClass structure 2020-12-01 13:38:12 +00:00
Emmanuele Bassi 5a307fa7f3 docs: Ignore more private headers 2020-12-01 13:37:59 +00:00
Matthias Clasen 617d12fd77 Merge branch 'sophie-h/gtk-nullables-2'
Merge Sophie's nullable annotations, with some documentation
fixups on top.
2020-11-30 23:43:44 -05:00
Matthias Clasen 837f398a9b columnviewcolumn: Improve docs
Mention what setting sorter to NULL means.
2020-11-30 23:40:03 -05:00
Matthias Clasen e18211deca centerlayout: Improve docs
Explain what passing NULL does.
2020-11-30 23:36:23 -05:00
Jordi Mas 32a547cc3e Update Catalan translation 2020-11-30 22:35:47 +01:00
Matthias Clasen 365afa381a Merge branch 'arnaudb/annotate-set-title' into 'master'
Add "nullable" annotation.

See merge request GNOME/gtk!2895
2020-11-30 21:14:28 +00:00
Matthias Clasen b7d3c073f8 NEWS: Updates 2020-11-30 14:00:26 -05:00
Matthias Clasen 56b0095622 Merge branch 'master' into 'master'
broadway: make header name parsing case-insensitve

Closes #3406

See merge request GNOME/gtk!2894
2020-11-30 17:55:27 +00:00
Matthias Clasen 39f0c48eb2 Apply 1 suggestion(s) to 1 file(s) 2020-11-30 17:44:30 +00:00
Matthias Clasen 7d24e1405e Apply 1 suggestion(s) to 1 file(s) 2020-11-30 17:42:39 +00:00
Matthias Clasen 3680ae220a Merge branch 'matthiasc/for-master' into 'master'
build: Use link_whole for linking our convenience libs

See merge request GNOME/gtk!2899
2020-11-30 15:49:25 +00:00
Matthias Clasen f309fa21c9 build: Use link_whole for linking our convenience libs
Without this, we lose exported symbols from e.g. libgsk
that are not used elsewhere in the code.
2020-11-30 09:56:55 -05:00
Matthias Clasen 61e7326f22 Merge branch 'matthiasc/for-master' into 'master'
Matthiasc/for master

Closes #3364

See merge request GNOME/gtk!2898
2020-11-30 14:44:15 +00:00
Timm Bäder dc13ff97e2 modelbutton: Reset active menu item on pointer leave
We don't want to leave an active menu item behind if the pointer doesn't
enter another menu item.
This matches the old GtkMenu behavior
2020-11-30 09:23:03 -05:00
Timm Bäder af28237569 popovermenu: Connect to right controller signal
leave is emitted before the new contains-pointer value is set, so the
signal handler wasn't working right.
2020-11-30 09:22:57 -05:00
Timm Bäder ff8de0a93f eventcontrollermotion: Notify by pspec 2020-11-30 09:22:49 -05:00
Timm Bäder aefabcc056 popovermenu: Pull variable declaration in closest scope 2020-11-30 09:22:41 -05:00
Timm Bäder 1d6c6fc3b8 shortcutcontroller: Separate trigger/action early returns
We take this early return in update_accel over 36k times when starting
the widget factory and always because the action is not a named action.
2020-11-30 09:22:34 -05:00
Timm Bäder 8645773fc6 shortcutcontroller: Pass the muxer to update_accel()
We only needed the widget to get its action muxer. And this way we don't
have to call gtk_widget_get_action_muxer() dozens of times, just once in
set_widget().
2020-11-30 09:22:29 -05:00
Timm Bäder d663909159 shortcutcontroller: Use priv->shortcuts directly in set_widget()
Instead of treating the shortcut controller itself as a GListModel,
which just passes everything along from priv->shortcuts.
2020-11-30 09:22:21 -05:00
Timm Bäder e3ff92bc87 shortcutcontroller: Save signal handler ID
gtk_shortcut_controller_finalize() used to take ~13.8% when closing the
complex listbox demo in gtk4-demo. Now it takes nothing.
2020-11-30 09:21:57 -05:00
Timm Bäder 2d43ecf8ca widget: root child before emitting child observer signals
Try to avoid the childen_observer signal handlers screwing up internal
state.

Fixes #3364
2020-11-30 09:21:52 -05:00
Timm Bäder 5f4beb711f widgetprivate: Remove unused function 2020-11-30 09:21:42 -05:00
Timm Bäder da514187f5 gl renderer: Make minimal rounded rects more minimal 2020-11-30 09:21:34 -05:00
Matthias Clasen 6bd9ce7b55 docs: Ignore more generated Wayland protocol headers
These inject mysterious words like Airbrush, Lense and
Mouse into the list of undocumented apis. No need for that.
2020-11-30 09:21:24 -05:00
Matthias Clasen ec21d354b9 gsk: doc fixups 2020-11-30 09:21:24 -05:00
Matthias Clasen 8cf4ce5d9e Merge branch 'ebassi/cell-renderer-get-size' into 'master'
Remove GtkCellRendererClass.get_size()

See merge request GNOME/gtk!2896
2020-11-30 14:09:03 +00:00
Emmanuele Bassi 09b6c37e1d Merge branch 'ebassi/for-master' into 'master'
Blend nodes might have NULL top/bottom nodes

See merge request GNOME/gtk!2891
2020-11-30 13:10:25 +00:00
Piotr Drąg 3161dbfff2 Update Polish translation 2020-11-29 12:11:26 +01:00
Yuri Chornoivan 8412928a14 Update Ukrainian translation 2020-11-29 07:53:56 +00:00
Emmanuele Bassi 60a7830ba5 Remove GtkCellRendererClass.get_size()
The get_size() vfuncs is deprecated in GTK3, and only used as a
fallback path for cell renderers that do not implement preferred
size virtual functions.
2020-11-28 19:21:24 +00:00
Emmanuele Bassi ac473282a2 Port CellRendererGraph to preferred size vfuncs
Drop use of GtkCellRendererClass.get_size()
2020-11-28 19:21:24 +00:00
Emmanuele Bassi 20f9e0f144 Port GtkCellRendererProgress to preferred size vfuncs
Drop the use of GtkCellRendererClass.get_size()
2020-11-28 19:21:24 +00:00
Emmanuele Bassi 71a9e5d8c2 Port GtkCellRendererToggle to preferred size vfuncs
Drop the use of GtkCellRendererClass.get_size()
2020-11-28 19:21:24 +00:00
Emmanuele Bassi a605fcc2bd Port GtkCellRendererSpinner to preferred size vfuncs
Drop the use of GtkCellRendererClass.get_size()
2020-11-28 19:21:23 +00:00
Emmanuele Bassi 3eaebf1e0b Port GtkCellRendererPixbuf to preferred size vfuncs
Drop the GtkCellRendererClass.get_size() override.
2020-11-28 19:21:23 +00:00
Аляксей e540ccbd34 Update Belarusian translation 2020-11-28 14:30:43 +00:00
Arnaud Bonatti 55ce61d1a8 Add "nullable" annotation. 2020-11-28 12:15:38 +01:00
Jakub Steiner 8e14f43398 Merge branch 'wip/jimmac/dark-prelights' into 'master'
wip/jimmac/dark prelights

Closes #3380

See merge request GNOME/gtk!2892
2020-11-27 18:14:09 +00:00
Jakub Steiner bbbcc72460 Adwaita,HC: use dark prelights
- increase :hover visibility by going darker rather than lighter
  puppies died.

Fixes https://gitlab.gnome.org/GNOME/gtk/-/issues/3380
2020-11-27 18:17:12 +01:00
Anders Jonsson c2b1b00e52 Update Swedish translation 2020-11-27 13:35:03 +00:00
Anders Jonsson 05c2e35c5d Update Swedish translation 2020-11-27 13:31:30 +00:00
Benjamin Beichler 8efde9c48c broadway: make header name parsing case-insensitve
since http RFC state that the header names should be processed case in-sensitive, broadway should not rely on the actual case. E.g. the go-language libraries tend to rewrite the header, which cause problems with e.g. Caddy

Fixes #3406
2020-11-27 12:05:51 +01:00
Emmanuele Bassi 247fc2e471 Blend nodes might have NULL top/bottom nodes
We have code in place to handle a NULL node in the state when dealing
with blend nodes, but we don't always check for NULL, which leads to
warnings in the CSS Blend modes demo.
2020-11-26 00:21:11 +00:00
Matthias Clasen 9d2ca90b4c popover: Fix a typo
s/Wether/Whether/

Fixes #3393
2020-11-25 13:59:16 -05:00
Emmanuele Bassi 3a5f8b6192 Merge branch 'ebassi/for-master' into 'master'
Ebassi/for master

Closes #3404 and #3403

See merge request GNOME/gtk!2890
2020-11-25 18:43:53 +00:00
Emmanuele Bassi 16b5a88097 a11y: Check before disconnecting Text signals
Use the same initial check for the accessible object type that we use
when connecting the signal, in case we try to disconnect signals on
different widgets. Additionally, check before accessing data that might
have already been removed.

Fixes: #3403
2020-11-25 18:15:04 +00:00
Emmanuele Bassi fdf2e046c3 a11y: Check before disconnecting selection signals
If the selection data has already been cleared we should just bail out.

Fixes: #3404
2020-11-25 18:13:54 +00:00
Jakub Steiner 531d57f30c Merge branch 'wip/jimmac/menus-darker' into 'master'
Adwaita: darker selected menuitem + sidebars

Closes #3390

See merge request GNOME/gtk!2881
2020-11-25 13:42:55 +00:00
Jordi Mas i Hernandez 841bf67bb9 Update Catalan translation 2020-11-25 13:42:11 +00:00
Jakub Steiner 399b457f54 Adwaita: darker selected menuitem + sidebars
- without sacrificing the fg/bg contrast too much, increase
  selected item visibility

Fixes https://gitlab.gnome.org/GNOME/gtk/-/issues/3390
2020-11-25 14:28:19 +01:00
Sophie Herold fed734edfc Fix some nullable return annotations 2 2020-11-24 19:27:33 +01:00
Matthias Clasen c84c014536 Merge branch 'wip/chergert/for-master' into 'master'
textview: fix precondition assertions

Closes #3386

See merge request GNOME/gtk!2886
2020-11-24 17:14:53 +00:00
Christian Hergert 1e994419ee textview: fix precondition assertions
It is a programmer error to call this with a child widget that has not
been placed within the GtkTextView.

Fixes #3386
2020-11-24 08:29:52 -08:00
Matthias Clasen 7b22c44b39 Merge branch 'fix-integer-overflow' into 'master'
gdk/win32: fix integer overflow in monitor refresh rate calculation

Closes #3394

See merge request GNOME/gtk!2884
2020-11-24 16:22:49 +00:00
Anders Jonsson 2e744260e0 Update Swedish translation 2020-11-23 22:09:28 +00:00
Volker Rümelin 7190a31b5f gdk/win32: fix integer overflow in monitor refresh rate calculation
In gdk/win32/gdkmonitor-win32.c in function
populate_monitor_devices_from_display_config() refresh->Numerator * 1000
overflows for refresh->Numerator > 4294976.

Cast the factor 1000 to UINT64 to prevent the overflow.

Fixes #3394
2020-11-23 21:42:58 +01:00
Matthias Clasen 729f007379 Merge branch 'no-more-devel-headers' into 'master'
Drop devel styling from our windows

See merge request GNOME/gtk!2882
2020-11-23 19:01:44 +00:00
Matthias Clasen b75b359f19 Drop devel styling from our windows
We are about to do a stable release. Time to get used
again to plain old, boring header bars.
2020-11-23 12:10:01 -05:00
Emmanuele Bassi d7de720275 Merge branch 'ebassi/for-master' into 'master'
Ebassi/for master

See merge request GNOME/gtk!2876
2020-11-23 15:41:03 +00:00
Emmanuele Bassi ef86e46238 a11y: Cache the accessibility bus address
Just check for it once; doing it every time we failed to create an
ATContext is just going to fill up the logs.
2020-11-23 14:34:46 +00:00
Emmanuele Bassi 0a46baeb56 a11y: Turn critical warnings into debug messages
The accessibility bus might not be available, and if it isn't the case,
it means something has failed at a level where the user can't do much
about it. There's no need to emit a critical warning.
2020-11-23 14:34:46 +00:00
Emmanuele Bassi fd3a6299ce Merge branch 'ebassi/glib-min-version' into 'master'
Ebassi/glib min version

See merge request GNOME/gtk!2880
2020-11-23 14:33:20 +00:00
Emmanuele Bassi 803b147483 Disable deprecation warnings from GLib
We don't want to test the latest changes in GLib in our own build.
2020-11-23 13:04:11 +00:00
Emmanuele Bassi 1f44319588 Drop redundant dependencies
PangoCairo already depends on Pango; Cairo-gobject already depends on
Cairo.
2020-11-23 13:04:11 +00:00
Emmanuele Bassi 505478bc59 Drop GLib dependency from GTK's declared dependencies
We depend on GObject, which already depends on GLib; on older versions
of Meson, this ends up trying to depend on the system copy of GLib,
instead of the sub-project copy, in case the version of GLib we have
installed is too old.
2020-11-23 12:29:32 +00:00
Emmanuele Bassi de7833b6a0 Use the right GLib macros for version checking
There's a typo in the name, and we never noticed.
2020-11-23 12:06:22 +00:00
Emmanuele Bassi 3a1b2083d6 Do not depend on GLib API introduced after 2.66
To avoid bleeding edge deprecations we use GLIB_VERSION_MIN_REQUIRED and
GLIB_VERSION_MAX_ALLOWED. Since we depend on GLib 2.66, we cannot use
API introduced in 2.67, even when conditionally compiled.
2020-11-23 12:06:22 +00:00
183 changed files with 31392 additions and 15322 deletions
+96
View File
@@ -1,3 +1,99 @@
Overview of Changes
===================
* Accessibility:
- Map presentation role according to Core-AAM
- Use presentation role for spacers in list views
- Set proper roles, relations and descriptions on more widgets
- Expand the documentation
- Use a single GTK_A11Y environment variable and document it
- Serveral memory leak and crash fixes
- Initialize AT contexts lazily
- Improve the API for storing lists in relations
- Implement more of the Component interface
- Implement the Cache interface
- Implement ScrollSubstringTo for GtkTextView
* Introspection:
- Add many missing annotations to APIs
- In particular, nullability annotation fixes for rust
* Touch support:
- Fix issues with text handles
- Fix interaction between touch scrolling and focus
- Fix active state updates for touch events
- Allow pressing buttons with multiple fingers
* GtkScrolledWindow:
- Accumulate velocity with kinetic scrolling
* GtkSearchEntry:
- Use a better clear icon
* GtkTreeView:
- Remove the deprecated GtkCellRendererClass.get_size vfunc
* GtkBuilder:
- Be stricter about <requires>
- Make gtk-builder-tool simplify update <requires>
* GtkFileChooser:
- Remove GtkFileChooserButton
* Make the inspector available in non-debug builds
* CSS:
- Support conic gradients
* Adwaita:
- Round all window corners
- Round submenus
- Remove the 'chin' on menus
- Industrial style menuitems
- Improved tooltip styling
- Unified sidebar styles
- Dark prelight
- Don't backdrop labels
* GSK:
- Rename render node apis from peek to get
* Make GLES work in the absence of GL_ARB_timer_query
* Wayland:
- Support gtk_shell1 version 3 (startup notification
and activation)
- Implmement minimization
* OS X:
- Use the cairo renderer by default
- Work towards a working GL renderer
- Search engine updates
- Fix rendering artifacts with hover transitions
* Windows:
- Fix key event generation, making text input work
* Documentation:
- Add missing enumerations, symbols and types
- Lots of smaller fixes
* Demos:
- Make the constraints demo more useful
- Make search more obvious
- Add a suggestion entry demo
- Consistency improvements
- Drop 'devel' styling
* Translation updates:
Belarusian
Catalan
Polish
Spanish
Swedish
Ukrainian
Overview of Changes in 3.99.4
=============================
@@ -1,9 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="ConstraintEditorWindow" parent="GtkApplicationWindow">
<style>
<class name="devel"/>
</style>
<property name="title" translatable="yes">GTK Constraint Editor</property>
<property name="default-width">1024</property>
<property name="default-height">768</property>
+4 -4
View File
@@ -67,7 +67,7 @@ vec2 raySphere(vec3 ro, vec3 rd, vec4 sphere) {
return vec2(t0, t1);
}
float noise1(vec2 p) {
float noize1(vec2 p) {
vec2 n = mod2(p, vec2(cellWidth));
vec2 hh = hash(sqrt(2.0)*(n+1000.0));
hh.x *= hh.y;
@@ -81,7 +81,7 @@ float noise1(vec2 p) {
return h*0.25;
}
float noise2(vec2 p) {
float noize2(vec2 p) {
vec2 n = mod2(p, vec2(cellWidth));
vec2 hh = hash(sqrt(2.0)*(n+1000.0));
hh.x *= hh.y;
@@ -114,7 +114,7 @@ float height(vec2 p, float dd, int mx) {
int i = 0;
for (; i < 4;++i) {
float nn = a*noise2(p);
float nn = a*noize2(p);
s += nn;
d += abs(a);
p += o;
@@ -130,7 +130,7 @@ float height(vec2 p, float dd, int mx) {
mx = int(mix(float(4), float(mx), step(rdd, far)));
for (; i < mx; ++i) {
float nn = a*noise1(p);
float nn = a*noize1(p);
s += nn;
d += abs(a);
p += o;
+6
View File
@@ -319,6 +319,9 @@
<file>paintable_svg.c</file>
<file>panes.c</file>
<file>password_entry.c</file>
<file>path_fill.c</file>
<file>path_maze.c</file>
<file>path_text.c</file>
<file>peg_solitaire.c</file>
<file>pickers.c</file>
<file>printing.c</file>
@@ -403,6 +406,9 @@
<gresource prefix="/fontrendering">
<file>fontrendering.ui</file>
</gresource>
<gresource prefix="/path_text">
<file>path_text.ui</file>
</gresource>
<gresource prefix="/org/gtk/Demo4">
<file>icons/16x16/actions/application-exit.png</file>
<file>icons/16x16/actions/document-new.png</file>
-4
View File
@@ -346,10 +346,6 @@ create_clocks_model (void)
result = g_list_store_new (GTK_TYPE_CLOCK);
#if GLIB_CHECK_VERSION (2, 67, 0)
# define g_time_zone_new g_time_zone_new_identifier
#endif
/* local time */
clock = gtk_clock_new ("local", NULL);
g_list_store_append (result, clock);
+31 -5
View File
@@ -142,16 +142,42 @@ load_file (GtkStringList *list,
}
static void
file_selected_cb (GtkWidget *button,
open_response_cb (GtkWidget *dialog,
int response,
GtkStringList *stringlist)
{
GFile *file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (button));
gtk_widget_hide (dialog);
if (file)
if (response == GTK_RESPONSE_ACCEPT)
{
GFile *file;
file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
load_file (stringlist, file);
g_object_unref (file);
}
gtk_window_destroy (GTK_WINDOW (dialog));
}
static void
file_open_cb (GtkWidget *button,
GtkStringList *stringlist)
{
GtkWidget *dialog;
dialog = gtk_file_chooser_dialog_new ("Open file",
GTK_WINDOW (gtk_widget_get_root (button)),
GTK_FILE_CHOOSER_ACTION_OPEN,
"_Cancel", GTK_RESPONSE_CANCEL,
"_Load", GTK_RESPONSE_ACCEPT,
NULL);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
g_signal_connect (dialog, "response", G_CALLBACK (open_response_cb), stringlist);
gtk_widget_show (dialog);
}
GtkWidget *
@@ -189,8 +215,8 @@ do_listview_words (GtkWidget *do_widget)
header = gtk_header_bar_new ();
gtk_header_bar_set_show_title_buttons (GTK_HEADER_BAR (header), TRUE);
open_button = gtk_file_chooser_button_new ("_Open", GTK_FILE_CHOOSER_ACTION_OPEN);
g_signal_connect (open_button, "file-set", G_CALLBACK (file_selected_cb), stringlist);
open_button = gtk_button_new_with_mnemonic ("_Open");
g_signal_connect (open_button, "clicked", G_CALLBACK (file_open_cb), stringlist);
gtk_header_bar_pack_start (GTK_HEADER_BAR (header), open_button);
gtk_window_set_titlebar (GTK_WINDOW (window), header);
+10 -3
View File
@@ -17,9 +17,6 @@
</section>
</menu>
<object class="GtkApplicationWindow" id="window">
<style>
<class name="devel"/>
</style>
<property name="default-width">800</property>
<property name="default-height">600</property>
<child type="titlebar">
@@ -31,6 +28,16 @@
<property name="label" translatable="yes">Run</property>
</object>
</child>
<child>
<object class="GtkToggleButton">
<property name="icon-name">edit-find-symbolic</property>
<property name="valign">center</property>
<property name="active" bind-source="searchbar" bind-property="search-mode-enabled" bind-flags="bidirectional|sync-create"/>
<accessibility>
<property name="label" translatable="yes">Search</property>
</accessibility>
</object>
</child>
<child type="end">
<object class="GtkMenuButton" id="gear_menu_button">
<property name="valign">center</property>
+3
View File
@@ -68,6 +68,9 @@ demos = files([
'paintable_mediastream.c',
'panes.c',
'password_entry.c',
'path_fill.c',
'path_maze.c',
'path_text.c',
'peg_solitaire.c',
'pickers.c',
'printing.c',
+42 -15
View File
@@ -13,19 +13,50 @@
static void
file_set (GtkFileChooserButton *button,
GtkWidget *picture)
open_response_cb (GtkWidget *dialog,
int response,
GtkPicture *picture)
{
GFile *file;
GdkPaintable *paintable;
gtk_widget_hide (dialog);
file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (button));
if (response == GTK_RESPONSE_ACCEPT)
{
GFile *file;
GdkPaintable *paintable;
paintable = svg_paintable_new (file);
gtk_picture_set_paintable (GTK_PICTURE (picture), paintable);
file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
paintable = svg_paintable_new (file);
gtk_picture_set_paintable (GTK_PICTURE (picture), paintable);
g_object_unref (paintable);
g_object_unref (file);
}
g_object_unref (paintable);
g_object_unref (file);
gtk_window_destroy (GTK_WINDOW (dialog));
}
static void
show_file_open (GtkWidget *button,
GtkPicture *picture)
{
GtkFileFilter *filter;
GtkWidget *dialog;
dialog = gtk_file_chooser_dialog_new ("Open node file",
GTK_WINDOW (gtk_widget_get_root (button)),
GTK_FILE_CHOOSER_ACTION_OPEN,
"_Cancel", GTK_RESPONSE_CANCEL,
"_Load", GTK_RESPONSE_ACCEPT,
NULL);
filter = gtk_file_filter_new ();
gtk_file_filter_add_mime_type (filter, "image/svg+xml");
gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
g_signal_connect (dialog, "response", G_CALLBACK (open_response_cb), picture);
gtk_widget_show (dialog);
}
static GtkWidget *window;
@@ -35,7 +66,6 @@ do_paintable_svg (GtkWidget *do_widget)
{
GtkWidget *header;
GtkWidget *picture;
GtkFileFilter *filter;
GtkWidget *button;
GFile *file;
GdkPaintable *paintable;
@@ -49,17 +79,14 @@ do_paintable_svg (GtkWidget *do_widget)
gtk_window_set_title (GTK_WINDOW (window), "Paintable — SVG");
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
button = gtk_file_chooser_button_new ("Select an SVG file", GTK_FILE_CHOOSER_ACTION_OPEN);
filter = gtk_file_filter_new ();
gtk_file_filter_add_mime_type (filter, "image/svg+xml");
gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (button), filter);
button = gtk_button_new_with_mnemonic ("_Open");
gtk_header_bar_pack_start (GTK_HEADER_BAR (header), button);
picture = gtk_picture_new ();
gtk_picture_set_can_shrink (GTK_PICTURE (picture), TRUE);
gtk_widget_set_size_request (picture, 16, 16);
g_signal_connect (button, "file-set", G_CALLBACK (file_set), picture);
g_signal_connect (button, "clicked", G_CALLBACK (show_file_open), picture);
gtk_window_set_child (GTK_WINDOW (window), picture);
+359
View File
@@ -0,0 +1,359 @@
/* Path/Fill
*
* This demo shows how to use PangoCairo to draw text with more than
* just a single color.
*/
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include "paintable.h"
#include "gsk/gskpathdashprivate.h"
#define GTK_TYPE_PATH_PAINTABLE (gtk_path_paintable_get_type ())
G_DECLARE_FINAL_TYPE (GtkPathPaintable, gtk_path_paintable, GTK, PATH_PAINTABLE, GObject)
struct _GtkPathPaintable
{
GObject parent_instance;
int width;
int height;
GskPath *path;
GdkPaintable *background;
};
struct _GtkPathPaintableClass
{
GObjectClass parent_class;
};
static int
gtk_path_paintable_get_intrinsic_width (GdkPaintable *paintable)
{
GtkPathPaintable *self = GTK_PATH_PAINTABLE (paintable);
if (self->background)
return MAX (gdk_paintable_get_intrinsic_width (self->background), self->width);
else
return self->width;
}
static int
gtk_path_paintable_get_intrinsic_height (GdkPaintable *paintable)
{
GtkPathPaintable *self = GTK_PATH_PAINTABLE (paintable);
if (self->background)
return MAX (gdk_paintable_get_intrinsic_height (self->background), self->height);
else
return self->height;
}
static void
gtk_path_paintable_snapshot (GdkPaintable *paintable,
GdkSnapshot *snapshot,
double width,
double height)
{
GtkPathPaintable *self = GTK_PATH_PAINTABLE (paintable);
#if 0
gtk_snapshot_push_fill (snapshot, self->path, GSK_FILL_RULE_WINDING);
#else
GskStroke *stroke = gsk_stroke_new (2.0);
gtk_snapshot_push_stroke (snapshot, self->path, stroke);
gsk_stroke_free (stroke);
#endif
if (self->background)
{
gdk_paintable_snapshot (self->background, snapshot, width, height);
}
else
{
gtk_snapshot_append_linear_gradient (snapshot,
&GRAPHENE_RECT_INIT (0, 0, width, height),
&GRAPHENE_POINT_INIT (0, 0),
&GRAPHENE_POINT_INIT (width, height),
(GskColorStop[8]) {
{ 0.0, { 1.0, 0.0, 0.0, 1.0 } },
{ 0.2, { 1.0, 0.0, 0.0, 1.0 } },
{ 0.3, { 1.0, 1.0, 0.0, 1.0 } },
{ 0.4, { 0.0, 1.0, 0.0, 1.0 } },
{ 0.6, { 0.0, 1.0, 1.0, 1.0 } },
{ 0.7, { 0.0, 0.0, 1.0, 1.0 } },
{ 0.8, { 1.0, 0.0, 1.0, 1.0 } },
{ 1.0, { 1.0, 0.0, 1.0, 1.0 } }
},
8);
}
gtk_snapshot_pop (snapshot);
}
static GdkPaintableFlags
gtk_path_paintable_get_flags (GdkPaintable *paintable)
{
GtkPathPaintable *self = GTK_PATH_PAINTABLE (paintable);
if (self->background)
return gdk_paintable_get_flags (self->background);
else
return GDK_PAINTABLE_STATIC_CONTENTS | GDK_PAINTABLE_STATIC_SIZE;
}
static void
gtk_path_paintable_paintable_init (GdkPaintableInterface *iface)
{
iface->get_intrinsic_width = gtk_path_paintable_get_intrinsic_width;
iface->get_intrinsic_height = gtk_path_paintable_get_intrinsic_height;
iface->snapshot = gtk_path_paintable_snapshot;
iface->get_flags = gtk_path_paintable_get_flags;
}
/* When defining the GType, we need to implement the GdkPaintable interface */
G_DEFINE_TYPE_WITH_CODE (GtkPathPaintable, gtk_path_paintable, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
gtk_path_paintable_paintable_init))
/* Here's the boilerplate for the GObject declaration.
* We don't need to do anything special here, because we keep no
* data of our own.
*/
static void
gtk_path_paintable_class_init (GtkPathPaintableClass *klass)
{
}
static void
gtk_path_paintable_init (GtkPathPaintable *self)
{
}
/* And finally, we add a simple constructor.
* It is declared in the header so that the other examples
* can use it.
*/
GdkPaintable *
gtk_path_paintable_new (GskPath *path,
GdkPaintable *background,
int width,
int height)
{
GtkPathPaintable *self;
self = g_object_new (GTK_TYPE_PATH_PAINTABLE, NULL);
self->path = path;
self->background = background;
if (self->background)
{
g_signal_connect_swapped (self->background, "invalidate-contents", G_CALLBACK (gdk_paintable_invalidate_contents), self);
g_signal_connect_swapped (self->background, "invalidate-size", G_CALLBACK (gdk_paintable_invalidate_size), self);
}
self->width = width;
self->height = height;
return GDK_PAINTABLE (self);
}
void
gtk_path_paintable_set_path (GtkPathPaintable *self,
GskPath *path)
{
g_clear_pointer (&self->path, gsk_path_unref);
self->path = gsk_path_ref (path);
gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
}
static GskPath *
create_hexagon (GtkWidget *widget)
{
GskPathBuilder *builder;
builder = gsk_path_builder_new ();
gsk_path_builder_move_to (builder, 120, 0);
gsk_path_builder_line_to (builder, 360, 0);
gsk_path_builder_line_to (builder, 480, 208);
gsk_path_builder_line_to (builder, 360, 416);
gsk_path_builder_line_to (builder, 120, 416);
gsk_path_builder_line_to (builder, 0, 208);
gsk_path_builder_close (builder);
return gsk_path_builder_free_to_path (builder);
}
static GskPath *
create_path_from_text (GtkWidget *widget)
{
cairo_surface_t *surface;
cairo_t *cr;
cairo_path_t *path;
PangoLayout *layout;
PangoFontDescription *desc;
GskPath *result;
surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
cr = cairo_create (surface);
layout = gtk_widget_create_pango_layout (widget, "Pango power!\nPango power!\nPango power!");
desc = pango_font_description_from_string ("sans bold 36");
pango_layout_set_font_description (layout, desc);
pango_font_description_free (desc);
pango_cairo_layout_path (cr, layout);
path = cairo_copy_path_flat (cr);
result = gsk_path_new_from_cairo (path);
cairo_path_destroy (path);
g_object_unref (layout);
cairo_destroy (cr);
cairo_surface_destroy (surface);
return result;
}
static gboolean
build_path (GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight,
gpointer user_data)
{
GskPathBuilder *builder = user_data;
switch (op)
{
case GSK_PATH_MOVE:
gsk_path_builder_move_to (builder, pts[0].x, pts[0].y);
break;
case GSK_PATH_CLOSE:
gsk_path_builder_close (builder);
break;
case GSK_PATH_LINE:
gsk_path_builder_line_to (builder, pts[1].x, pts[1].y);
break;
case GSK_PATH_CURVE:
gsk_path_builder_curve_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
break;
case GSK_PATH_CONIC:
gsk_path_builder_conic_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, weight);
break;
default:
g_assert_not_reached ();
break;
}
return TRUE;
}
static gboolean
update_path (GtkWidget *widget,
GdkFrameClock *frame_clock,
gpointer measure)
{
float progress = gdk_frame_clock_get_frame_time (frame_clock) % (60 * G_USEC_PER_SEC) / (float) (30 * G_USEC_PER_SEC);
GskPathBuilder *builder;
GskPath *path;
graphene_point_t pos;
graphene_vec2_t tangent;
GskStroke *stroke;
builder = gsk_path_builder_new ();
gsk_path_builder_add_segment (builder,
measure,
#if 1
0.0, gsk_path_measure_get_length (measure));
#else
progress > 1 ? (progress - 1) * gsk_path_measure_get_length (measure) : 0.0,
(progress < 1 ? progress : 1.0) * gsk_path_measure_get_length (measure));
#endif
path = gsk_path_builder_free_to_path (builder);
stroke = gsk_stroke_new (1);
gsk_stroke_set_dash (stroke, (float[2]) { 10, 5 }, 2);
gsk_stroke_set_dash_offset (stroke, - (gdk_frame_clock_get_frame_time (frame_clock) % G_USEC_PER_SEC) * 15. / G_USEC_PER_SEC);
builder = gsk_path_builder_new ();
gsk_path_dash (path, stroke, 0.2, build_path, builder);
gsk_path_unref (path);
gsk_path_measure_get_point (measure,
(progress > 1 ? (progress - 1) : progress) * gsk_path_measure_get_length (measure),
&pos,
&tangent);
gsk_path_builder_move_to (builder, pos.x + 5 * graphene_vec2_get_x (&tangent), pos.y + 5 * graphene_vec2_get_y (&tangent));
gsk_path_builder_line_to (builder, pos.x + 3 * graphene_vec2_get_y (&tangent), pos.y + 3 * graphene_vec2_get_x (&tangent));
gsk_path_builder_line_to (builder, pos.x - 3 * graphene_vec2_get_y (&tangent), pos.y - 3 * graphene_vec2_get_x (&tangent));
gsk_path_builder_close (builder);
path = gsk_path_builder_free_to_path (builder);
gtk_path_paintable_set_path (GTK_PATH_PAINTABLE (gtk_picture_get_paintable (GTK_PICTURE (widget))),
path);
gsk_path_unref (path);
return G_SOURCE_CONTINUE;
}
GtkWidget *
do_path_fill (GtkWidget *do_widget)
{
static GtkWidget *window = NULL;
if (!window)
{
GtkWidget *picture;
GdkPaintable *paintable;
GtkMediaStream *stream;
GskPath *path;
graphene_rect_t bounds;
GskPathMeasure *measure;
window = gtk_window_new ();
gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
gtk_window_set_title (GTK_WINDOW (window), "Path Fill");
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
#if 0
stream = gtk_media_file_new_for_resource ("/images/gtk-logo.webm");
#else
stream = gtk_nuclear_media_stream_new ();
#endif
gtk_media_stream_play (stream);
gtk_media_stream_set_loop (stream, TRUE);
path = create_hexagon (window);
path = create_path_from_text (window);
gsk_path_get_bounds (path, &bounds);
paintable = gtk_path_paintable_new (path,
GDK_PAINTABLE (stream),
bounds.origin.x + bounds.size.width,
bounds.origin.y + bounds.size.height);
picture = gtk_picture_new_for_paintable (paintable);
measure = gsk_path_measure_new (path);
gtk_widget_add_tick_callback (picture, update_path, measure, (GDestroyNotify) gsk_path_measure_unref);
gtk_picture_set_keep_aspect_ratio (GTK_PICTURE (picture), FALSE);
gtk_picture_set_can_shrink (GTK_PICTURE (picture), FALSE);
g_object_unref (paintable);
gtk_window_set_child (GTK_WINDOW (window), picture);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_window_destroy (GTK_WINDOW (window));
return window;
}
+338
View File
@@ -0,0 +1,338 @@
/* Path/Maze
*
* This demo shows how to use a GskPath to create a maze and use
* gsk_path_measure_get_closest_point() to check the mouse stays
* on the path.
*
* It also shows off the performance of GskPath (or not) as this
* is a rather complex path.
*/
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include "paintable.h"
#define MAZE_GRID_SIZE 20
#define MAZE_STROKE_SIZE_ACTIVE (MAZE_GRID_SIZE - 4)
#define MAZE_STROKE_SIZE_INACTIVE (MAZE_GRID_SIZE - 12)
#define MAZE_WIDTH 31
#define MAZE_HEIGHT 21
#define GTK_TYPE_MAZE (gtk_maze_get_type ())
G_DECLARE_FINAL_TYPE (GtkMaze, gtk_maze, GTK, MAZE, GtkWidget)
struct _GtkMaze
{
GtkWidget parent_instance;
int width;
int height;
GskPath *path;
GskPathMeasure *measure;
GdkPaintable *background;
gboolean active;
};
struct _GtkMazeClass
{
GtkWidgetClass parent_class;
};
G_DEFINE_TYPE (GtkMaze, gtk_maze, GTK_TYPE_WIDGET)
static void
gtk_maze_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkMaze *self = GTK_MAZE (widget);
if (orientation == GTK_ORIENTATION_HORIZONTAL)
*minimum = *natural = self->width;
else
*minimum = *natural = self->height;
}
static void
gtk_maze_snapshot (GtkWidget *widget,
GdkSnapshot *snapshot)
{
GtkMaze *self = GTK_MAZE (widget);
double width = gtk_widget_get_width (widget);
double height = gtk_widget_get_height (widget);
GskStroke *stroke;
stroke = gsk_stroke_new (MAZE_STROKE_SIZE_INACTIVE);
if (self->active)
gsk_stroke_set_line_width (stroke, MAZE_STROKE_SIZE_ACTIVE);
gsk_stroke_set_line_join (stroke, GSK_LINE_JOIN_ROUND);
gsk_stroke_set_line_cap (stroke, GSK_LINE_CAP_ROUND);
gtk_snapshot_push_stroke (snapshot, self->path, stroke);
gsk_stroke_free (stroke);
if (self->background)
{
gdk_paintable_snapshot (self->background, snapshot, width, height);
}
else
{
gtk_snapshot_append_linear_gradient (snapshot,
&GRAPHENE_RECT_INIT (0, 0, width, height),
&GRAPHENE_POINT_INIT (0, 0),
&GRAPHENE_POINT_INIT (width, height),
(GskColorStop[8]) {
{ 0.0, { 1.0, 0.0, 0.0, 1.0 } },
{ 0.2, { 1.0, 0.0, 0.0, 1.0 } },
{ 0.3, { 1.0, 1.0, 0.0, 1.0 } },
{ 0.4, { 0.0, 1.0, 0.0, 1.0 } },
{ 0.6, { 0.0, 1.0, 1.0, 1.0 } },
{ 0.7, { 0.0, 0.0, 1.0, 1.0 } },
{ 0.8, { 1.0, 0.0, 1.0, 1.0 } },
{ 1.0, { 1.0, 0.0, 1.0, 1.0 } }
},
8);
}
gtk_snapshot_pop (snapshot);
}
static void
gtk_maze_dispose (GObject *object)
{
GtkMaze *self = GTK_MAZE (object);
g_clear_pointer (&self->path, gsk_path_unref);
g_clear_pointer (&self->measure, gsk_path_measure_unref);
if (self->background)
{
g_signal_handlers_disconnect_matched (self->background, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
g_clear_object (&self->background);
}
G_OBJECT_CLASS (gtk_maze_parent_class)->dispose (object);
}
static void
gtk_maze_class_init (GtkMazeClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = gtk_maze_dispose;
widget_class->measure = gtk_maze_measure;
widget_class->snapshot = gtk_maze_snapshot;
}
static void
pointer_motion (GtkEventControllerMotion *controller,
double x,
double y,
GtkMaze *self)
{
if (!self->active)
return;
if (gsk_path_measure_get_closest_point (self->measure, &GRAPHENE_POINT_INIT (x, y), NULL) <= MAZE_STROKE_SIZE_ACTIVE / 2.0f)
return;
self->active = FALSE;
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
pointer_leave (GtkEventControllerMotion *controller,
GtkMaze *self)
{
if (!self->active)
{
self->active = TRUE;
gtk_widget_queue_draw (GTK_WIDGET (self));
}
}
static void
gtk_maze_init (GtkMaze *self)
{
GtkEventController *controller;
controller = GTK_EVENT_CONTROLLER (gtk_event_controller_motion_new ());
g_signal_connect (controller, "motion", G_CALLBACK (pointer_motion), self);
g_signal_connect (controller, "leave", G_CALLBACK (pointer_leave), self);
gtk_widget_add_controller (GTK_WIDGET (self), controller);
self->active = TRUE;
}
static void
gtk_maze_set_path (GtkMaze *self,
GskPath *path)
{
g_clear_pointer (&self->path, gsk_path_unref);
g_clear_pointer (&self->measure, gsk_path_measure_unref);
self->path = gsk_path_ref (path);
self->measure = gsk_path_measure_new (path);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
GtkWidget *
gtk_maze_new (GskPath *path,
GdkPaintable *background,
int width,
int height)
{
GtkMaze *self;
self = g_object_new (GTK_TYPE_MAZE, NULL);
gtk_maze_set_path (self, path);
gsk_path_unref (path);
self->background = background;
if (self->background)
{
g_signal_connect_swapped (self->background, "invalidate-contents", G_CALLBACK (gtk_widget_queue_draw), self);
g_signal_connect_swapped (self->background, "invalidate-size", G_CALLBACK (gtk_widget_queue_resize), self);
}
self->width = width;
self->height = height;
return GTK_WIDGET (self);
}
static void
add_point_to_maze (GtkBitset *maze,
GskPathBuilder *builder,
guint x,
guint y)
{
gboolean set[4] = { };
guint dir;
gtk_bitset_add (maze, y * MAZE_WIDTH + x);
while (TRUE)
{
set[0] = set[0] || x == 0 || gtk_bitset_contains (maze, y * MAZE_WIDTH + x - 1);
set[1] = set[1] || y == 0 || gtk_bitset_contains (maze, (y - 1) * MAZE_WIDTH + x);
set[2] = set[2] || x + 1 == MAZE_WIDTH || gtk_bitset_contains (maze, y * MAZE_WIDTH + x + 1);
set[3] = set[3] || y + 1 == MAZE_HEIGHT || gtk_bitset_contains (maze, (y + 1) * MAZE_WIDTH + x);
if (set[0] && set[1] && set[2] && set[3])
return;
do
{
dir = g_random_int_range (0, 4);
}
while (set[dir]);
switch (dir)
{
case 0:
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
gsk_path_builder_line_to (builder, (x - 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
add_point_to_maze (maze, builder, x - 1, y);
break;
case 1:
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
gsk_path_builder_line_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y - 0.5) * MAZE_GRID_SIZE);
add_point_to_maze (maze, builder, x, y - 1);
break;
case 2:
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
gsk_path_builder_line_to (builder, (x + 1.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
add_point_to_maze (maze, builder, x + 1, y);
break;
case 3:
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
gsk_path_builder_line_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 1.5) * MAZE_GRID_SIZE);
add_point_to_maze (maze, builder, x, y + 1);
break;
default:
g_assert_not_reached ();
break;
}
}
}
static GskPath *
create_path_for_maze (GtkWidget *widget)
{
GskPathBuilder *builder;
GtkBitset *maze;
builder = gsk_path_builder_new ();
maze = gtk_bitset_new_empty ();
/* make sure the outer lines are unreachable:
* Set the full range, then remove the center again. */
gtk_bitset_add_range (maze, 0, MAZE_WIDTH * MAZE_HEIGHT);
gtk_bitset_remove_rectangle (maze, MAZE_WIDTH + 1, MAZE_WIDTH - 2, MAZE_HEIGHT - 2, MAZE_WIDTH);
/* Fill the maze */
add_point_to_maze (maze, builder, MAZE_WIDTH / 2, MAZE_HEIGHT / 2);
/* Add start and stop lines */
gsk_path_builder_move_to (builder, 1.5 * MAZE_GRID_SIZE, -0.5 * MAZE_GRID_SIZE);
gsk_path_builder_line_to (builder, 1.5 * MAZE_GRID_SIZE, 1.5 * MAZE_GRID_SIZE);
gsk_path_builder_move_to (builder, (MAZE_WIDTH - 1.5) * MAZE_GRID_SIZE, (MAZE_HEIGHT - 1.5) * MAZE_GRID_SIZE);
gsk_path_builder_line_to (builder, (MAZE_WIDTH - 1.5) * MAZE_GRID_SIZE, (MAZE_HEIGHT + 0.5) * MAZE_GRID_SIZE);
gtk_bitset_unref (maze);
return gsk_path_builder_free_to_path (builder);
}
GtkWidget *
do_path_maze (GtkWidget *do_widget)
{
static GtkWidget *window = NULL;
if (!window)
{
GtkWidget *maze;
GtkMediaStream *stream;
GskPath *path;
window = gtk_window_new ();
gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
gtk_window_set_title (GTK_WINDOW (window), "Follow the maze with the mouse");
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
#if 0
stream = gtk_media_file_new_for_resource ("/images/gtk-logo.webm");
#else
stream = gtk_nuclear_media_stream_new ();
#endif
gtk_media_stream_play (stream);
gtk_media_stream_set_loop (stream, TRUE);
path = create_path_for_maze (window);
maze = gtk_maze_new (path,
GDK_PAINTABLE (stream),
MAZE_WIDTH * MAZE_GRID_SIZE,
MAZE_HEIGHT * MAZE_GRID_SIZE);
gtk_window_set_child (GTK_WINDOW (window), maze);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_window_destroy (GTK_WINDOW (window));
return window;
}
+590
View File
@@ -0,0 +1,590 @@
/* Path/Text
*
* This demo shows how to use GskPath to animate a path along another path.
*/
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#define GTK_TYPE_PATH_WIDGET (gtk_path_widget_get_type ())
G_DECLARE_FINAL_TYPE (GtkPathWidget, gtk_path_widget, GTK, PATH_WIDGET, GtkWidget)
#define POINT_SIZE 8
enum {
PROP_0,
PROP_TEXT,
PROP_EDITABLE,
N_PROPS
};
struct _GtkPathWidget
{
GtkWidget parent_instance;
char *text;
gboolean editable;
graphene_point_t points[4];
guint active_point;
float line_closest;
GskPath *line_path;
GskPathMeasure *line_measure;
GskPath *text_path;
GdkPaintable *background;
};
struct _GtkPathWidgetClass
{
GtkWidgetClass parent_class;
};
static GParamSpec *properties[N_PROPS] = { NULL, };
G_DEFINE_TYPE (GtkPathWidget, gtk_path_widget, GTK_TYPE_WIDGET)
static GskPath *
create_path_from_text (GtkWidget *widget,
const char *text)
{
cairo_surface_t *surface;
cairo_t *cr;
cairo_path_t *path;
PangoLayout *layout;
PangoFontDescription *desc;
GskPath *result;
surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
cr = cairo_create (surface);
layout = gtk_widget_create_pango_layout (widget, text);
desc = pango_font_description_from_string ("sans bold 36");
pango_layout_set_font_description (layout, desc);
pango_font_description_free (desc);
cairo_move_to (cr, 0, - pango_layout_get_baseline (layout) / (double) PANGO_SCALE);
pango_cairo_layout_path (cr, layout);
path = cairo_copy_path_flat (cr);
result = gsk_path_new_from_cairo (path);
cairo_path_destroy (path);
g_object_unref (layout);
cairo_destroy (cr);
cairo_surface_destroy (surface);
return result;
}
typedef struct
{
GskPathMeasure *measure;
GskPathBuilder *builder;
double scale;
} GtkPathTransform;
static void
gtk_path_transform_point (GskPathMeasure *measure,
const graphene_point_t *pt,
float scale,
graphene_point_t *res)
{
graphene_vec2_t tangent;
gsk_path_measure_get_point (measure, pt->x * scale, res, &tangent);
res->x -= pt->y * scale * graphene_vec2_get_y (&tangent);
res->y += pt->y * scale * graphene_vec2_get_x (&tangent);
}
static gboolean
gtk_path_transform_op (GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight,
gpointer data)
{
GtkPathTransform *transform = data;
switch (op)
{
case GSK_PATH_MOVE:
{
graphene_point_t res;
gtk_path_transform_point (transform->measure, &pts[0], transform->scale, &res);
gsk_path_builder_move_to (transform->builder, res.x, res.y);
}
break;
case GSK_PATH_LINE:
{
graphene_point_t res;
gtk_path_transform_point (transform->measure, &pts[1], transform->scale, &res);
gsk_path_builder_line_to (transform->builder, res.x, res.y);
}
break;
case GSK_PATH_CURVE:
{
graphene_point_t res[3];
gtk_path_transform_point (transform->measure, &pts[1], transform->scale, &res[0]);
gtk_path_transform_point (transform->measure, &pts[2], transform->scale, &res[1]);
gtk_path_transform_point (transform->measure, &pts[3], transform->scale, &res[2]);
gsk_path_builder_curve_to (transform->builder, res[0].x, res[0].y, res[1].x, res[1].y, res[2].x, res[2].y);
}
break;
case GSK_PATH_CONIC:
{
graphene_point_t res[2];
gtk_path_transform_point (transform->measure, &pts[1], transform->scale, &res[0]);
gtk_path_transform_point (transform->measure, &pts[2], transform->scale, &res[1]);
gsk_path_builder_conic_to (transform->builder, res[0].x, res[0].y, res[1].x, res[1].y, weight);
}
break;
case GSK_PATH_CLOSE:
gsk_path_builder_close (transform->builder);
break;
default:
g_assert_not_reached();
return FALSE;
}
return TRUE;
}
static GskPath *
gtk_path_transform (GskPathMeasure *measure,
GskPath *path)
{
GtkPathTransform transform = { measure, gsk_path_builder_new () };
graphene_rect_t bounds;
gsk_path_get_bounds (path, &bounds);
if (bounds.origin.x + bounds.size.width > 0)
transform.scale = gsk_path_measure_get_length (measure) / (bounds.origin.x + bounds.size.width);
else
transform.scale = 1.0f;
gsk_path_foreach (path, GSK_PATH_FOREACH_ALLOW_CURVE, gtk_path_transform_op, &transform);
return gsk_path_builder_free_to_path (transform.builder);
}
static void
gtk_path_widget_clear_text_path (GtkPathWidget *self)
{
g_clear_pointer (&self->text_path, gsk_path_unref);
}
static void
gtk_path_widget_clear_paths (GtkPathWidget *self)
{
gtk_path_widget_clear_text_path (self);
g_clear_pointer (&self->line_path, gsk_path_unref);
g_clear_pointer (&self->line_measure, gsk_path_measure_unref);
}
static void
gtk_path_widget_create_text_path (GtkPathWidget *self)
{
GskPath *path;
gtk_path_widget_clear_text_path (self);
if (self->line_measure == NULL)
return;
path = create_path_from_text (GTK_WIDGET (self), self->text);
self->text_path = gtk_path_transform (self->line_measure, path);
gsk_path_unref (path);
}
static void
gtk_path_widget_create_paths (GtkPathWidget *self)
{
double width = gtk_widget_get_width (GTK_WIDGET (self));
double height = gtk_widget_get_height (GTK_WIDGET (self));
GskPathBuilder *builder;
gtk_path_widget_clear_paths (self);
if (width <= 0 || height <= 0)
return;
builder = gsk_path_builder_new ();
gsk_path_builder_move_to (builder,
self->points[0].x * width, self->points[0].y * height);
gsk_path_builder_curve_to (builder,
self->points[1].x * width, self->points[1].y * height,
self->points[2].x * width, self->points[2].y * height,
self->points[3].x * width, self->points[3].y * height);
self->line_path = gsk_path_builder_free_to_path (builder);
self->line_measure = gsk_path_measure_new (self->line_path);
gtk_path_widget_create_text_path (self);
}
static void
gtk_path_widget_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkPathWidget *self = GTK_PATH_WIDGET (widget);
GTK_WIDGET_CLASS (gtk_path_widget_parent_class)->size_allocate (widget, width, height, baseline);
gtk_path_widget_create_paths (self);
}
static void
gtk_path_widget_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
GtkPathWidget *self = GTK_PATH_WIDGET (widget);
double width = gtk_widget_get_width (widget);
double height = gtk_widget_get_height (widget);
GskPath *path;
GskStroke *stroke;
gsize i;
/* frosted glass the background */
gtk_snapshot_push_blur (snapshot, 100);
gdk_paintable_snapshot (self->background, snapshot, width, height);
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 1, 1, 1, 0.6 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
gtk_snapshot_pop (snapshot);
/* draw the text */
if (self->text_path)
{
gtk_snapshot_push_fill (snapshot, self->text_path, GSK_FILL_RULE_WINDING);
gdk_paintable_snapshot (self->background, snapshot, width, height);
/* ... with an emboss effect */
stroke = gsk_stroke_new (2.0);
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT(1, 1));
gtk_snapshot_push_stroke (snapshot, self->text_path, stroke);
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 0, 0.2 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
gsk_stroke_free (stroke);
gtk_snapshot_pop (snapshot);
gtk_snapshot_pop (snapshot);
}
if (self->editable && self->line_path)
{
GskPathBuilder *builder;
/* draw the control line */
stroke = gsk_stroke_new (1.0);
gtk_snapshot_push_stroke (snapshot, self->line_path, stroke);
gsk_stroke_free (stroke);
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 0, 1 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
gtk_snapshot_pop (snapshot);
/* draw the points */
builder = gsk_path_builder_new ();
for (i = 0; i < 4; i++)
{
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (self->points[i].x * width, self->points[i].y * height), POINT_SIZE);
}
path = gsk_path_builder_free_to_path (builder);
gtk_snapshot_push_fill (snapshot, path, GSK_FILL_RULE_WINDING);
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 1, 1, 1, 1 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
gtk_snapshot_pop (snapshot);
stroke = gsk_stroke_new (1.0);
gtk_snapshot_push_stroke (snapshot, path, stroke);
gsk_stroke_free (stroke);
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 0, 1 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
gtk_snapshot_pop (snapshot);
gsk_path_unref (path);
}
if (self->line_closest >= 0)
{
GskPathBuilder *builder;
graphene_point_t closest;
builder = gsk_path_builder_new ();
gsk_path_measure_get_point (self->line_measure, self->line_closest, &closest, NULL);
gsk_path_builder_add_circle (builder, &closest, POINT_SIZE);
path = gsk_path_builder_free_to_path (builder);
gtk_snapshot_push_fill (snapshot, path, GSK_FILL_RULE_WINDING);
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 1, 1 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
gtk_snapshot_pop (snapshot);
gsk_path_unref (path);
}
}
static void
gtk_path_widget_set_text (GtkPathWidget *self,
const char *text)
{
if (g_strcmp0 (self->text, text) == 0)
return;
g_free (self->text);
self->text = g_strdup (text);
gtk_path_widget_create_paths (self);
gtk_widget_queue_draw (GTK_WIDGET (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TEXT]);
}
static void
gtk_path_widget_set_editable (GtkPathWidget *self,
gboolean editable)
{
if (self->editable == editable)
return;
self->editable = editable;
gtk_widget_queue_draw (GTK_WIDGET (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_EDITABLE]);
}
static void
gtk_path_widget_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkPathWidget *self = GTK_PATH_WIDGET (object);
switch (prop_id)
{
case PROP_TEXT:
gtk_path_widget_set_text (self, g_value_get_string (value));
break;
case PROP_EDITABLE:
gtk_path_widget_set_editable (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_path_widget_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkPathWidget *self = GTK_PATH_WIDGET (object);
switch (prop_id)
{
case PROP_TEXT:
g_value_set_string (value, self->text);
break;
case PROP_EDITABLE:
g_value_set_boolean (value, self->editable);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_path_widget_dispose (GObject *object)
{
GtkPathWidget *self = GTK_PATH_WIDGET (object);
gtk_path_widget_clear_paths (self);
G_OBJECT_CLASS (gtk_path_widget_parent_class)->dispose (object);
}
static void
gtk_path_widget_class_init (GtkPathWidgetClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = gtk_path_widget_dispose;
object_class->set_property = gtk_path_widget_set_property;
object_class->get_property = gtk_path_widget_get_property;
widget_class->size_allocate = gtk_path_widget_allocate;
widget_class->snapshot = gtk_path_widget_snapshot;
properties[PROP_TEXT] =
g_param_spec_string ("text",
"text",
"Text transformed along a path",
NULL,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
properties[PROP_EDITABLE] =
g_param_spec_boolean ("editable",
"editable",
"If the path can be edited by the user",
FALSE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
drag_begin (GtkGestureDrag *gesture,
double x,
double y,
GtkPathWidget *self)
{
graphene_point_t mouse = GRAPHENE_POINT_INIT (x, y);
double width = gtk_widget_get_width (GTK_WIDGET (self));
double height = gtk_widget_get_height (GTK_WIDGET (self));
gsize i;
for (i = 0; i < 4; i++)
{
if (graphene_point_distance (&GRAPHENE_POINT_INIT (self->points[i].x * width, self->points[i].y * height), &mouse, NULL, NULL) <= POINT_SIZE)
{
self->active_point = i;
break;
}
}
if (i == 4)
{
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
return;
}
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
drag_update (GtkGestureDrag *drag,
double offset_x,
double offset_y,
GtkPathWidget *self)
{
double width = gtk_widget_get_width (GTK_WIDGET (self));
double height = gtk_widget_get_height (GTK_WIDGET (self));
double start_x, start_y;
gtk_gesture_drag_get_start_point (drag, &start_x, &start_y);
self->points[self->active_point] = GRAPHENE_POINT_INIT ((start_x + offset_x) / width,
(start_y + offset_y) / height);
self->points[self->active_point].x = CLAMP (self->points[self->active_point].x, 0, 1);
self->points[self->active_point].y = CLAMP (self->points[self->active_point].y, 0, 1);
gtk_path_widget_create_paths (self);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
pointer_motion (GtkEventControllerMotion *controller,
double x,
double y,
GtkPathWidget *self)
{
gsk_path_measure_get_closest_point_full (self->line_measure,
&GRAPHENE_POINT_INIT (x, y),
INFINITY,
&self->line_closest,
NULL, NULL, NULL);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
pointer_leave (GtkEventControllerMotion *controller,
GtkPathWidget *self)
{
self->line_closest = -1;
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
gtk_path_widget_init (GtkPathWidget *self)
{
GtkEventController *controller;
controller = GTK_EVENT_CONTROLLER (gtk_gesture_drag_new ());
g_signal_connect (controller, "drag-begin", G_CALLBACK (drag_begin), self);
g_signal_connect (controller, "drag-update", G_CALLBACK (drag_update), self);
g_signal_connect (controller, "drag-end", G_CALLBACK (drag_update), self);
gtk_widget_add_controller (GTK_WIDGET (self), controller);
controller = GTK_EVENT_CONTROLLER (gtk_event_controller_motion_new ());
g_signal_connect (controller, "enter", G_CALLBACK (pointer_motion), self);
g_signal_connect (controller, "motion", G_CALLBACK (pointer_motion), self);
g_signal_connect (controller, "leave", G_CALLBACK (pointer_leave), self);
gtk_widget_add_controller (GTK_WIDGET (self), controller);
self->line_closest = -1;
self->points[0] = GRAPHENE_POINT_INIT (0.1, 0.9);
self->points[1] = GRAPHENE_POINT_INIT (0.3, 0.1);
self->points[2] = GRAPHENE_POINT_INIT (0.7, 0.1);
self->points[3] = GRAPHENE_POINT_INIT (0.9, 0.9);
self->background = GDK_PAINTABLE (gdk_texture_new_from_resource ("/sliding_puzzle/portland-rose.jpg"));
gtk_path_widget_set_text (self, "It's almost working");
}
GtkWidget *
gtk_path_widget_new (void)
{
GtkPathWidget *self;
self = g_object_new (GTK_TYPE_PATH_WIDGET, NULL);
return GTK_WIDGET (self);
}
GtkWidget *
do_path_text (GtkWidget *do_widget)
{
static GtkWidget *window = NULL;
if (!window)
{
GtkBuilder *builder;
g_type_ensure (GTK_TYPE_PATH_WIDGET);
builder = gtk_builder_new_from_resource ("/path_text/path_text.ui");
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
gtk_window_set_display (GTK_WINDOW (window),
gtk_widget_get_display (do_widget));
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *) &window);
g_object_unref (builder);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_window_destroy (GTK_WINDOW (window));
return window;
}
+38
View File
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GtkWindow" id="window">
<property name="title" translatable="yes">Text along a Path</property>
<child type="titlebar">
<object class="GtkHeaderBar">
<child type="end">
<object class="GtkToggleButton" id="edit-toggle">
<property name="icon-name">document-edit-symbolic</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkRevealer">
<property name="reveal-child" bind-source="edit-toggle" bind-property="active" bind-flags="sync-create"></property>
<child>
<object class="GtkEntry" id="text">
<property name="text">Through the looking glass</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkPathWidget" id="view">
<property name="editable" bind-source="edit-toggle" bind-property="active" bind-flags="sync-create"></property>
<property name="text" bind-source="text" bind-property="text" bind-flags="sync-create"></property>
<property name="hexpand">true</property>
<property name="vexpand">true</property>
</object>
</child>
</object>
</child>
</object>
</interface>
-38
View File
@@ -63,9 +63,6 @@ do_pickers (GtkWidget *do_widget)
if (!window)
{
char *dir;
GFile *file;
window = gtk_window_new ();
gtk_window_set_display (GTK_WINDOW (window),
gtk_widget_get_display (do_widget));
@@ -129,41 +126,6 @@ do_pickers (GtkWidget *do_widget)
gtk_widget_set_hexpand (label, TRUE);
gtk_grid_attach (GTK_GRID (table), label, 0, 2, 1, 1);
picker = gtk_file_chooser_button_new ("Pick a File",
GTK_FILE_CHOOSER_ACTION_OPEN);
gtk_grid_attach (GTK_GRID (table), picker, 1, 2, 1, 1);
picker = gtk_file_chooser_button_new ("Pick a File",
GTK_FILE_CHOOSER_ACTION_OPEN);
dir = g_get_current_dir ();
file = g_file_new_for_path (dir);
gtk_file_chooser_add_shortcut_folder (GTK_FILE_CHOOSER (picker), file, NULL);
g_object_unref (file);
g_free (dir);
gtk_file_chooser_add_choice (GTK_FILE_CHOOSER (picker),
"choice",
"Encoding",
(const char *[]) { "option1", "option2", NULL },
(const char *[]) { "UTF-8", "Other Encoding", NULL });
gtk_file_chooser_set_choice (GTK_FILE_CHOOSER (picker), "choice", "option1");
gtk_file_chooser_add_choice (GTK_FILE_CHOOSER (picker),
"check",
"Read backwards",
NULL, NULL);
gtk_file_chooser_set_choice (GTK_FILE_CHOOSER (picker), "check", "false");
gtk_grid_attach (GTK_GRID (table), picker, 2, 2, 1, 1);
label = gtk_label_new ("Folder:");
gtk_widget_set_halign (label, GTK_ALIGN_START);
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
picker = gtk_file_chooser_button_new ("Pick a Folder",
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
gtk_grid_attach (GTK_GRID (table), label, 0, 3, 1, 1);
gtk_grid_attach (GTK_GRID (table), picker, 1, 3, 1, 1);
label = gtk_label_new ("Mail:");
gtk_widget_set_halign (label, GTK_ALIGN_START);
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
-3
View File
@@ -13,9 +13,6 @@
</section>
</menu>
<template class="IconBrowserWindow" parent="GtkApplicationWindow">
<style>
<class name="devel"/>
</style>
<property name="title" translatable="yes">Icon Browser</property>
<property name="default-width">1024</property>
<property name="default-height">768</property>
-3
View File
@@ -1,9 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GtkWindow" id="window">
<style>
<class name="devel"/>
</style>
<property name="title" translatable="yes">Help</property>
<property name="default-width">720</property>
<property name="default-height">520</property>
-3
View File
@@ -94,9 +94,6 @@
</object>
<template class="NodeEditorWindow" parent="GtkApplicationWindow">
<style>
<class name="devel"/>
</style>
<property name="title" translatable="yes">GTK Node Editor</property>
<property name="default-width">1024</property>
<property name="default-height">768</property>
-2
View File
@@ -812,8 +812,6 @@ activate (GApplication *app)
gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW (main_window), TRUE);
update_title (GTK_WINDOW (main_window));
gtk_widget_add_css_class (main_window, "devel");
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_window_set_child (GTK_WINDOW (main_window), box);
-6
View File
@@ -433,9 +433,6 @@ Suspendisse feugiat quam quis dolor accumsan cursus.</property>
</columns>
</object>
<object class="GtkApplicationWindow" id="window">
<style>
<class name="devel"/>
</style>
<property name="title">GTK Widget Factory</property>
<child type="titlebar">
<object class="GtkHeaderBar" id="headerbar1">
@@ -881,9 +878,6 @@ Suspendisse feugiat quam quis dolor accumsan cursus.</property>
<property name="use-alpha">1</property>
</object>
</child>
<child>
<object class="GtkFileChooserButton" id="filechooserbutton1"/>
</child>
<child>
<object class="GtkLinkButton" id="linkbutton1">
<property name="label" translatable="yes">link button</property>
+29 -26
View File
@@ -11,6 +11,11 @@ GDK_WINDOWING_WIN32
GDK_WINDOWING_MACOS
GDK_WINDOWING_WAYLAND
<SUBSECTION>
GDK_MAJOR_VERSION
GDK_MICRO_VERSION
GDK_MINOR_VERSION
<SUBSECTION>
GDK_VERSION_4_0
GDK_VERSION_MIN_REQUIRED
@@ -24,6 +29,8 @@ GDK_TYPE_GRAB_STATUS
GDK_TYPE_STATUS
GdkStatus
GDKVAR
GDK_VERSION_3_92
GDK_VERSION_3_94
gdk_axis_use_get_type
gdk_byte_order_get_type
gdk_crossing_mode_get_type
@@ -827,11 +834,14 @@ gdk_x11_display_error_trap_pop_ignored
gdk_x11_display_set_cursor_theme
gdk_x11_display_set_surface_scale
gdk_x11_display_get_glx_version
gdk_x11_display_get_primary_monitor
gdk_x11_display_get_screen
gdk_x11_monitor_get_output
gdk_x11_monitor_get_workarea
gdk_x11_screen_get_screen_number
gdk_x11_screen_get_xscreen
gdk_x11_screen_get_window_manager_name
gdk_x11_screen_get_monitor_output
gdk_x11_screen_lookup_visual
gdk_x11_screen_supports_net_wm_hint
gdk_x11_screen_get_number_of_desktops
gdk_x11_screen_get_current_desktop
@@ -846,9 +856,9 @@ gdk_x11_surface_set_utf8_property
gdk_x11_surface_set_frame_sync_enabled
gdk_x11_surface_set_group
gdk_x11_surface_get_group
gdk_x11_keymap_get_group_for_state
gdk_x11_keymap_key_is_modifier
gdk_x11_visual_get_xvisual
gdk_x11_surface_set_skip_pager_hint
gdk_x11_surface_set_skip_taskbar_hint
gdk_x11_surface_set_urgency_hint
gdk_x11_get_xatom_by_name_for_display
gdk_x11_get_xatom_name_for_display
gdk_x11_set_sm_client_id
@@ -901,29 +911,18 @@ GDK_X11_DRAG_CONTEXT_CLASS
GDK_IS_X11_DRAG_CONTEXT
GDK_IS_X11_DRAG_CONTEXT_CLASS
GDK_X11_DRAG_CONTEXT_GET_CLASS
GDK_TYPE_X11_KEYMAP
GDK_X11_KEYMAP
GDK_X11_KEYMAP_CLASS
GDK_IS_X11_KEYMAP
GDK_IS_X11_KEYMAP_CLASS
GDK_X11_KEYMAP_GET_CLASS
GDK_TYPE_X11_GL_CONTEXT
GDK_X11_GL_CONTEXT
GDK_X11_GL_CONTEXT_CLASS
GDK_IS_X11_GL_CONTEXT
GDK_IS_X11_GL_CONTEXT_CLASS
GDK_TYPE_X11_SCREEN
GDK_X11_SCREEN
GDK_X11_SCREEN_CLASS
GDK_IS_X11_SCREEN
GDK_IS_X11_SCREEN_CLASS
GDK_X11_SCREEN_GET_CLASS
GDK_TYPE_X11_VISUAL
GDK_X11_VISUAL
GDK_X11_VISUAL_CLASS
GDK_IS_X11_VISUAL
GDK_IS_X11_VISUAL_CLASS
GDK_X11_VISUAL_GET_CLASS
GDK_TYPE_X11_DRAG
GDK_X11_DRAG
GDK_IS_X11_DRAG
GDK_IS_X11_DRAG_CLASS
GDK_TYPE_X11_MONITOR
GDK_X11_MONITOR
GDK_IS_X11_MONITOR
GDK_TYPE_X11_SURFACE
GDK_X11_SURFACE
GDK_X11_SURFACE_CLASS
@@ -941,9 +940,10 @@ gdk_x11_device_xi_get_type
gdk_x11_display_get_type
gdk_x11_display_manager_get_type
gdk_x11_drag_context_get_type
gdk_x11_keymap_get_type
gdk_x11_drag_get_type
gdk_x11_gl_context_get_type
gdk_x11_monitor_get_type
gdk_x11_screen_get_type
gdk_x11_visual_get_type
gdk_x11_surface_get_type
gdk_surface_impl_x11_get_type
</SECTION>
@@ -958,7 +958,6 @@ gdk_wayland_display_query_registry
gdk_wayland_display_set_cursor_theme
gdk_wayland_display_get_startup_notification_id
gdk_wayland_display_set_startup_notification_id
gdk_wayland_display_query_registry
<SUBSECTION Device>
gdk_wayland_seat_get_wl_seat
@@ -1008,8 +1007,12 @@ GDK_IS_WAYLAND_SURFACE_CLASS
gdk_wayland_device_get_type
gdk_wayland_display_get_type
gdk_wayland_display_manager_get_type
gdk_wayland_gl_context_get_type
gdk_wayland_monitor_get_type
gdk_wayland_popup_get_type
gdk_wayland_seat_get_type
gdk_wayland_surface_get_type
gdk_wayland_toplevel_get_type
</SECTION>
<SECTION>
+66 -11
View File
@@ -1,21 +1,22 @@
private_headers = [
'gdkintl.h',
'gdkmarshalers.h',
'gdkkeysyms.h',
'gdkinternals.h',
'gdk-autocleanup.h',
'gdk-private.h',
'gdkapplaunchcontextprivate.h',
'gdkcairocontextprivate.h',
'gdkcairoprivate.h',
'gdkclipboardprivate.h',
'gdkcontentformatsprivate.h',
'gdkcontentproviderprivate.h',
'gdkcursorprivate.h',
'gdkdeviceprivate.h',
'gdkdebug.h',
'gdkdevicepadprivate.h',
'gdkdeviceprivate.h',
'gdkdevicetoolprivate.h',
'gdkdisplaymanagerprivate.h',
'gdkdisplayprivate.h',
'gdkdndprivate.h',
'gdkdragprivate.h',
'gdkdragsurfaceprivate.h',
'gdkdrawcontextprivate.h',
'gdkdropprivate.h',
'gdkeventsprivate.h',
@@ -23,12 +24,17 @@ private_headers = [
'gdkframeclockprivate.h',
'gdkglcontextprivate.h',
'gdkgltextureprivate.h',
'gdkinternals.h',
'gdkintl.h',
'gdkkeysprivate.h',
'gdkmonitorprivate.h',
'gdkkeysyms.h',
'gdkmarshalers.h',
'gdkmemorytextureprivate.h',
'gdkmonitorprivate.h',
'gdkpipeiostreamprivate.h',
'gdkpopupprivate.h',
'gdkprofilerprivate.h',
'gdkrgbaprivate.h',
'gdkscreenprivate.h',
'gdkseatdefaultprivate.h',
'gdkseatprivate.h',
@@ -37,22 +43,68 @@ private_headers = [
'gdksurfaceprivate.h',
'gdktextureprivate.h',
'gdktoplevelprivate.h',
'gdktoplevelsizeprivate.h',
'gdkvulkancontextprivate.h',
'filetransferportalprivate.h',
'keyname-table.h',
'x11/gdkprivate-x11.h',
'x11/gdkeventsource.h',
# gdk/x11
'gdkcairocontext-x11.h',
'gdkclipboard-x11.h',
'gdkdevice-xi2-private.h',
'gdkdevicemanagerprivate-core.h',
'gdkdisplay-x11.h',
'gdkeventsource.h',
'gdkeventtranslator.h',
'gdkglcontext-x11.h',
'gdkkeys-x11.h',
'gdkmonitor-x11.h',
'gdkprivate-x11.h',
'gdkscreen-x11.h',
'gdkselectioninputstream-x11.h',
'gdkselectionoutputstream-x11.h',
'gdksurface-x11.h',
'gdktextlistconverter-x11.h',
'gdkvisual-x11.h',
'gdkvulkancontext-x11.h',
'gdkx-autocleanups.h',
'MwmUtil.h',
'xsettings-client.h',
# gdk/wayland
'gdkcairocontext-wayland.h',
'gdkclipboard-wayland.h',
'gdkdevice-wayland-private.h',
'gdkdisplay-wayland.h',
'gdkglcontext-wayland.h',
'gdkmonitor-wayland.h',
'gdkprimary-wayland.h',
'gdkprivate-wayland.h',
'gdkseat-wayland.h',
'gdksurface-wayland.h',
'gdkvulkancontext-wayland.h',
'wm-button-layout-translation.h',
'gtk-primary-selection-client-protocol.h',
'gtk-shell-client-protocol.h',
'idle-inhibit-unstable-v1-client-protocol.h',
'keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h',
'pointer-gestures-unstable-v1-client-protocol.h',
'primary-selection-unstable-v1-client-protocol.h',
'server-decoration-client-protocol.h',
'tablet-unstable-v2-client-protocol.h',
'xdg-foreign-unstable-v1-client-protocol.h',
'xdg-shell-unstable-v6-client-protocol.h',
'win32',
'quartz',
'xdg-output-unstable-v1-client-protocol.h',
'xdg-shell-client-protocol.h',
'xdg-shell-unstable-v6-client-protocol.h',
'wayland-cursor.h',
'os-compatibility.h',
'xcursor.h',
'broadway',
'mir'
'wayland/cursor',
'macos',
'win32',
]
images = [
@@ -116,6 +168,9 @@ if get_option('gtk_doc')
'--ignore-decorators=_GDK_EXTERN|G_GNUC_WARN_UNUSED_RESULT',
'--ignore-headers=' + ' '.join(private_headers),
],
mkdb_args: [
'--ignore-files=' + ' '.join(private_headers),
],
fixxref_args: [
'--html-dir=@0@'.format(docpath),
'--extra-dir=@0@'.format(join_paths(glib_docpath, 'glib')),
+7
View File
@@ -23,6 +23,13 @@
<xi:include href="xml/GskGLShader.xml" />
</reference>
<part id="paths">
<title>Paths</title>
<xi:include href="xml/GskPath.xml" />
<xi:include href="xml/GskPathBuilder.xml" />
<xi:include href="xml/GskStroke.xml" />
</part>
<index id="api-index-full">
<title>Index of all symbols</title>
<xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
+115 -3
View File
@@ -62,19 +62,22 @@ gsk_render_node_write_to_file
GskScalingFilter
gsk_render_node_get_bounds
<SUBSECTION Nodes>
<SUBSECTION>
gsk_color_node_new
gsk_color_node_get_color
gsk_texture_node_new
gsk_texture_node_get_texture
<SUBSECTION>
GskColorStop
gsk_linear_gradient_node_new
gsk_repeating_linear_gradient_node_new
gsk_linear_gradient_node_get_start
gsk_linear_gradient_node_get_end
gsk_linear_gradient_node_get_n_color_stops
gsk_linear_gradient_node_get_color_stops
gsk_repeating_linear_gradient_node_new
<SUBSECTION>
gsk_radial_gradient_node_new
gsk_repeating_radial_gradient_node_new
gsk_radial_gradient_node_get_n_color_stops
gsk_radial_gradient_node_get_color_stops
gsk_radial_gradient_node_get_start
@@ -82,11 +85,18 @@ gsk_radial_gradient_node_get_end
gsk_radial_gradient_node_get_hradius
gsk_radial_gradient_node_get_vradius
gsk_radial_gradient_node_get_center
gsk_repeating_radial_gradient_node_new
<SUBSECTION>
gsk_conic_gradient_node_new
gsk_conic_gradient_node_get_n_color_stops
gsk_conic_gradient_node_get_color_stops
gsk_conic_gradient_node_get_center
gsk_conic_gradient_node_get_rotation
<SUBSECTION>
gsk_border_node_new
gsk_border_node_get_outline
gsk_border_node_get_widths
gsk_border_node_get_colors
<SUBSECTION>
gsk_inset_shadow_node_new
gsk_inset_shadow_node_get_outline
gsk_inset_shadow_node_get_color
@@ -94,6 +104,7 @@ gsk_inset_shadow_node_get_dx
gsk_inset_shadow_node_get_dy
gsk_inset_shadow_node_get_spread
gsk_inset_shadow_node_get_blur_radius
<SUBSECTION>
gsk_outset_shadow_node_new
gsk_outset_shadow_node_get_outline
gsk_outset_shadow_node_get_color
@@ -101,45 +112,63 @@ gsk_outset_shadow_node_get_dx
gsk_outset_shadow_node_get_dy
gsk_outset_shadow_node_get_spread
gsk_outset_shadow_node_get_blur_radius
<SUBSECTION>
gsk_cairo_node_new
gsk_cairo_node_get_draw_context
gsk_cairo_node_get_surface
<SUBSECTION>
gsk_container_node_new
gsk_container_node_get_n_children
gsk_container_node_get_child
<SUBSECTION>
gsk_transform_node_new
gsk_transform_node_get_child
gsk_transform_node_get_transform
<SUBSECTION>
gsk_opacity_node_new
gsk_opacity_node_get_child
gsk_opacity_node_get_opacity
<SUBSECTION>
gsk_color_matrix_node_new
gsk_color_matrix_node_get_child
gsk_color_matrix_node_get_color_matrix
gsk_color_matrix_node_get_color_offset
<SUBSECTION>
gsk_repeat_node_new
gsk_repeat_node_get_child
gsk_repeat_node_get_child_bounds
<SUBSECTION>
gsk_clip_node_new
gsk_clip_node_get_child
gsk_clip_node_get_clip
<SUBSECTION>
gsk_rounded_clip_node_new
gsk_rounded_clip_node_get_child
gsk_rounded_clip_node_get_clip
<SUBSECTION>
GskFillRule
gsk_fill_node_new
gsk_fill_node_get_child
gsk_fill_node_get_path
gsk_fill_node_get_fill_rule
<SUBSECTION>
GskShadow
gsk_shadow_node_new
gsk_shadow_node_get_shadow
gsk_shadow_node_get_n_shadows
gsk_shadow_node_get_child
<SUBSECTION>
GskBlendMode
gsk_blend_node_new
gsk_blend_node_get_bottom_child
gsk_blend_node_get_top_child
gsk_blend_node_get_blend_mode
<SUBSECTION>
gsk_cross_fade_node_new
gsk_cross_fade_node_get_start_child
gsk_cross_fade_node_get_end_child
gsk_cross_fade_node_get_progress
<SUBSECTION>
gsk_text_node_new
gsk_text_node_get_font
gsk_text_node_get_glyphs
@@ -147,12 +176,15 @@ gsk_text_node_get_color
gsk_text_node_has_color_glyphs
gsk_text_node_get_num_glyphs
gsk_text_node_get_offset
<SUBSECTION>
gsk_blur_node_new
gsk_blur_node_get_child
gsk_blur_node_get_radius
<SUBSECTION>
gsk_debug_node_new
gsk_debug_node_get_child
gsk_debug_node_get_message
<SUBSECTION>
gsk_gl_shader_node_new
gsk_gl_shader_node_get_n_children
gsk_gl_shader_node_get_child
@@ -172,6 +204,7 @@ GSK_TYPE_COLOR_NODE
GSK_TYPE_CONTAINER_NODE
GSK_TYPE_CROSS_FADE_NODE
GSK_TYPE_DEBUG_NODE
GSK_TYPE_FILL_NODE
GSK_TYPE_INSET_SHADOW_NODE
GSK_TYPE_LINEAR_GRADIENT_NODE
GSK_TYPE_OPACITY_NODE
@@ -197,6 +230,7 @@ gsk_color_node_get_type
gsk_container_node_get_type
gsk_cross_fade_node_get_type
gsk_debug_node_get_type
gsk_fill_node_get_type
gsk_inset_shadow_node_get_type
gsk_linear_gradient_node_get_type
gsk_opacity_node_get_type
@@ -234,6 +268,84 @@ gsk_rounded_rect_contains_rect
gsk_rounded_rect_intersects_rect
</SECTION>
<SECTION>
<FILE>GskPath</FILE>
<SUBSECTION>
GskPath
gsk_path_ref
gsk_path_unref
gsk_path_new_rect
gsk_path_new_from_cairo
gsk_path_parse
<SUBSECTION>
gsk_path_print
gsk_path_to_string
gsk_path_to_cairo
<SUBSECTION>
gsk_path_is_empty
gsk_path_get_bounds
<SUBSECTION>
GskPathForeachFlags
gsk_path_foreach
<SUBSECTION Private>
GSK_TYPE_PATH
gsk_path_get_type
</SECTION>
<SECTION>
<FILE>GskPathBuilder</FILE>
GskPathBuilder
gsk_path_builder_new
gsk_path_builder_ref
gsk_path_builder_unref
gsk_path_builder_to_path
gsk_path_builder_free_to_path
<SUBSECTION>
gsk_path_builder_get_current_point
<SUBSECTION>
gsk_path_builder_add_rect
gsk_path_builder_add_rounded_rect
gsk_path_builder_add_circle
<SUBSECTION>
gsk_path_builder_move_to
gsk_path_builder_rel_move_to
gsk_path_builder_line_to
gsk_path_builder_rel_line_to
gsk_path_builder_curve_to
gsk_path_builder_rel_curve_to
gsk_path_builder_conic_to
gsk_path_builder_rel_conic_to
gsk_path_builder_close
<SUBSECTION Private>
GSK_TYPE_PATH_BUILDER
gsk_path_builder_get_type
</SECTION>
<SECTION>
<FILE>GskStroke</FILE>
GskLineCap
GskLineJoin
gsk_stroke_new
gsk_stroke_copy
gsk_stroke_free
gsk_stroke_equal
gsk_stroke_set_line_width
gsk_stroke_get_line_width
gsk_stroke_set_line_join
gsk_stroke_get_line_join
gsk_stroke_set_line_cap
gsk_stroke_get_line_cap
gsk_stroke_set_miter_limit
gsk_stroke_get_miter_limit
gsk_stroke_set_dash
gsk_stroke_get_dash
gsk_stroke_set_dash_offset
gsk_stroke_get_dash_offset
<SUBSECTION Private>
GSK_TYPE_STROKE
gsk_stroke_get_type
</SECTION>
<SECTION>
<FILE>GskTransform</FILE>
GskTransform
-1
View File
@@ -283,7 +283,6 @@
<xi:include href="xml/gtkcolorchooserwidget.xml" />
<xi:include href="xml/gtkcolorchooserdialog.xml" />
<xi:include href="xml/gtkfilechooser.xml" />
<xi:include href="xml/gtkfilechooserbutton.xml" />
<xi:include href="xml/gtkfilechoosernative.xml" />
<xi:include href="xml/gtkfilechooserdialog.xml" />
<xi:include href="xml/gtkfilechooserwidget.xml" />
+3 -25
View File
@@ -1339,31 +1339,6 @@ gtk_file_chooser_widget_get_type
GtkFileChooserWidgetPrivate
</SECTION>
<SECTION>
<FILE>gtkfilechooserbutton</FILE>
<TITLE>GtkFileChooserButton</TITLE>
GtkFileChooserButton
gtk_file_chooser_button_new
gtk_file_chooser_button_new_with_dialog
gtk_file_chooser_button_get_title
gtk_file_chooser_button_set_title
gtk_file_chooser_button_get_width_chars
gtk_file_chooser_button_set_width_chars
gtk_file_chooser_button_get_modal
gtk_file_chooser_button_set_modal
<SUBSECTION Standard>
GTK_FILE_CHOOSER_BUTTON
GTK_IS_FILE_CHOOSER_BUTTON
GTK_TYPE_FILE_CHOOSER_BUTTON
GTK_FILE_CHOOSER_BUTTON_CLASS
GTK_IS_FILE_CHOOSER_BUTTON_CLASS
GTK_FILE_CHOOSER_BUTTON_GET_CLASS
<SUBSECTION Private>
gtk_file_chooser_button_get_type
GtkFileChooserButtonPrivate
</SECTION>
<SECTION>
<FILE>gtkfilefilter</FILE>
GtkFileFilter
@@ -4304,6 +4279,8 @@ gtk_snapshot_push_color_matrix
gtk_snapshot_push_repeat
gtk_snapshot_push_clip
gtk_snapshot_push_rounded_clip
gtk_snapshot_push_fill
gtk_snapshot_push_stroke
gtk_snapshot_push_cross_fade
gtk_snapshot_push_blend
gtk_snapshot_push_blur
@@ -4329,6 +4306,7 @@ gtk_snapshot_append_color
gtk_snapshot_append_layout
gtk_snapshot_append_linear_gradient
gtk_snapshot_append_repeating_linear_gradient
gtk_snapshot_append_conic_gradient
gtk_snapshot_append_border
gtk_snapshot_append_inset_shadow
gtk_snapshot_append_outset_shadow
-1
View File
@@ -87,7 +87,6 @@ gtk_event_controller_motion_get_type
gtk_event_controller_scroll_get_type
gtk_every_filter_get_type
gtk_expander_get_type
gtk_file_chooser_button_get_type
gtk_file_chooser_dialog_get_type
gtk_file_chooser_get_type
gtk_file_chooser_native_get_type
+17 -5
View File
@@ -1,9 +1,6 @@
fs = import('fs')
private_headers = [
'imm-extra.h',
'gtkbitmaskprivateimpl.h',
'gdkpixbufutilsprivate.h',
'gtkaccelgroupprivate.h',
'gtkaccelmapprivate.h',
'gtkaccessibleattributesetprivate.h',
@@ -21,6 +18,7 @@ private_headers = [
'gtkapplicationprivate.h',
'gtkatcontextprivate.h',
'gtkbindingsprivate.h',
'gtkbitmaskprivateimpl.h',
'gtkbitmaskprivate.h',
'gtkbuilderprivate.h',
'gtkbuilderscopeprivate.h',
@@ -146,6 +144,7 @@ private_headers = [
'gtkimageprivate.h',
'gtkimcontextsimpleprivate.h',
'gtkimmoduleprivate.h',
'gtkintl.h',
'gtkkineticscrollingprivate.h',
'gtklabelprivate.h',
'gtklistbaseprivate.h',
@@ -165,6 +164,7 @@ private_headers = [
'gtknativedialogprivate.h',
'gtknomediafileprivate.h',
'gtkpango.h',
'gdkpixbufutilsprivate.h',
'gtkplacessidebarprivate.h',
'gtkplacesviewprivate.h',
'gtkplacesviewrowprivate.h',
@@ -183,6 +183,10 @@ private_headers = [
'gtkrendernodepaintableprivate.h',
'gtkroundedboxprivate.h',
'gtkscalerprivate.h',
'gtksearchengine.h',
'gtksearchenginemodel.h',
'gtksearchenginequartz.h',
'gtksearchenginetracker3.h',
'gtksearchentryprivate.h',
'gtksettingsprivate.h',
'gtkshortcutcontrollerprivate.h',
@@ -222,8 +226,15 @@ private_headers = [
'gtkwin32drawprivate.h',
'gtkwin32themeprivate.h',
'gtkwindowprivate.h',
'gtk-text-input-client-protocol.h',
'roaring.h',
'gtkdbusgenerated.h',
'imm-extra.h',
'text-input-unstable-v3-client-protocol.h',
'a11y',
'inspector',
'roaring',
'timsort',
]
images = [
@@ -471,6 +482,7 @@ if get_option('gtk_doc')
],
mkdb_args: [
'--default-includes=gtk/gtk.h',
'--ignore-files=' + ' '.join(private_headers),
],
fixxref_args: [
'--html-dir=@0@'.format(docpath),
-46
View File
@@ -615,51 +615,6 @@ create_font_button (void)
return new_widget_info ("font-button", vbox, SMALL);
}
static WidgetInfo *
create_file_button (void)
{
GtkWidget *vbox;
GtkWidget *vbox2;
GtkWidget *picker;
char *path;
GFile *file;
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 3);
picker = gtk_file_chooser_button_new ("File Chooser Button",
GTK_FILE_CHOOSER_ACTION_OPEN);
gtk_widget_set_size_request (picker, 150, -1);
gtk_widget_set_halign (picker, GTK_ALIGN_CENTER);
gtk_widget_set_valign (picker, GTK_ALIGN_CENTER);
gtk_box_append (GTK_BOX (vbox2), picker);
gtk_box_append (GTK_BOX (vbox2),
gtk_label_new ("File Button (Files)"));
gtk_box_append (GTK_BOX (vbox),
vbox2);
gtk_box_append (GTK_BOX (vbox),
gtk_separator_new (GTK_ORIENTATION_HORIZONTAL));
vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 3);
picker = gtk_file_chooser_button_new ("File Chooser Button",
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
gtk_widget_set_size_request (picker, 150, -1);
path = g_build_filename (g_get_home_dir (), "Documents", NULL);
file = g_file_new_for_path (path);
gtk_file_chooser_set_file (GTK_FILE_CHOOSER (picker), file, NULL);
g_free (path);
g_object_unref (file);
gtk_widget_set_halign (picker, GTK_ALIGN_CENTER);
gtk_widget_set_valign (picker, GTK_ALIGN_CENTER);
gtk_box_append (GTK_BOX (vbox2), picker);
gtk_box_append (GTK_BOX (vbox2), gtk_label_new ("File Button (Select Folder)"));
gtk_box_append (GTK_BOX (vbox), vbox2);
add_margin (vbox);
return new_widget_info ("file-button", vbox, MEDIUM);
}
static WidgetInfo *
create_editable_label (void)
{
@@ -2144,7 +2099,6 @@ get_all_widgets (void)
retval = g_list_prepend (retval, create_combo_box_entry ());
retval = g_list_prepend (retval, create_combo_box_text ());
retval = g_list_prepend (retval, create_entry ());
retval = g_list_prepend (retval, create_file_button ());
retval = g_list_prepend (retval, create_font_button ());
retval = g_list_prepend (retval, create_frame ());
retval = g_list_prepend (retval, create_icon_view ());
+1 -1
View File
@@ -1022,7 +1022,7 @@ parse_line (const char *line, const char *key)
{
const char *p;
if (!g_str_has_prefix (line, key))
if (g_ascii_strncasecmp (line, key, strlen (key)) != 0)
return NULL;
p = line + strlen (key);
if (*p != ':')
+7
View File
@@ -49,6 +49,12 @@ struct _GdkContentProvider
GObject parent;
};
/**
* GdkContentProviderClass:
* @content_changed: Signal class closure for #GdkContentProvider::content-changed
*
* Class structure for #GdkContentProvider.
*/
struct _GdkContentProviderClass
{
GObjectClass parent_class;
@@ -56,6 +62,7 @@ struct _GdkContentProviderClass
/* signals */
void (* content_changed) (GdkContentProvider *provider);
/*< private >*/
/* vfuncs */
void (* attach_clipboard) (GdkContentProvider *provider,
GdkClipboard *clipboard);
+5
View File
@@ -32,6 +32,11 @@ G_BEGIN_DECLS
#define GDK_DEVICE_TOOL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_DEVICE_TOOL, GdkDeviceTool))
#define GDK_IS_DEVICE_TOOL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_DEVICE_TOOL))
/**
* GdkDeviceTool:
*
* A physical tool associated to a #GdkDevice.
*/
typedef struct _GdkDeviceTool GdkDeviceTool;
/**
+6
View File
@@ -30,6 +30,12 @@
* during a DND operation.
*/
/**
* GdkDragSurfaceInterface:
*
* The #GdkDragSurfaceInterface implementation is private to GDK.
*/
G_DEFINE_INTERFACE (GdkDragSurface, gdk_drag_surface, GDK_TYPE_SURFACE)
static gboolean
+5
View File
@@ -33,6 +33,11 @@ G_BEGIN_DECLS
#define GDK_GL_TEXTURE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_GL_TEXTURE, GdkGLTexture))
#define GDK_IS_GL_TEXTURE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_GL_TEXTURE))
/**
* GdkGLTexture:
*
* A #GdkTexture representing a GL texture object.
*/
typedef struct _GdkGLTexture GdkGLTexture;
typedef struct _GdkGLTextureClass GdkGLTextureClass;
+5
View File
@@ -94,6 +94,11 @@ typedef enum {
#define GDK_MEMORY_TEXTURE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_MEMORY_TEXTURE, GdkMemoryTexture))
#define GDK_IS_MEMORY_TEXTURE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_MEMORY_TEXTURE))
/**
* GdkMemoryTexture:
*
* A #GdkTexture representing image data in memory.
*/
typedef struct _GdkMemoryTexture GdkMemoryTexture;
typedef struct _GdkMemoryTextureClass GdkMemoryTextureClass;
+12 -5
View File
@@ -31,6 +31,11 @@ G_BEGIN_DECLS
#define GDK_TYPE_PAINTABLE (gdk_paintable_get_type ())
/**
* GdkPaintable:
*
* Interface for paintable objects.
*/
GDK_AVAILABLE_IN_ALL
G_DECLARE_INTERFACE (GdkPaintable, gdk_paintable, GDK, PAINTABLE, GObject)
@@ -74,11 +79,13 @@ typedef enum {
* this function should return the aspect ratio computed from those.
*
* The list of functions that can be implemented for the #GdkPaintable interface.
* Note that apart from the first function, no function is mandatory to implement,
* though it is a good idea to implement #GdkPaintableInterface.get_current_image()
* for non-static paintables and #GdkPaintableInterface.get_flags() if the image
* is not dynamic as the default implementation returns no flags and that will
* make the implementation likely quite slow.
*
* Note that apart from the #GdkPaintableInterface.snapshot() function, no virtual
* function of this interface is mandatory to implement, though it is a good idea
* to implement #GdkPaintableInterface.get_current_image() for non-static paintables
* and #GdkPaintableInterface.get_flags() if the image is not dynamic as the default
* implementation returns no flags and that will make the implementation likely
* quite slow.
*/
struct _GdkPaintableInterface
{
+5
View File
@@ -31,6 +31,11 @@ G_BEGIN_DECLS
#define GDK_TYPE_POPUP (gdk_popup_get_type ())
/**
* GdkPopup:
*
* Interface for popup surfaces.
*/
GDK_AVAILABLE_IN_ALL
G_DECLARE_INTERFACE (GdkPopup, gdk_popup, GDK, POPUP, GObject)
+7 -1
View File
@@ -29,7 +29,13 @@
G_BEGIN_DECLS
typedef struct _GdkSnapshotClass GdkSnapshotClass;
/**
* GdkSnapshot:
*
* Base type for snapshot operations.
*/
typedef struct _GdkSnapshotClass GdkSnapshotClass;
#define GDK_TYPE_SNAPSHOT (gdk_snapshot_get_type ())
+5 -1
View File
@@ -120,6 +120,11 @@ typedef enum
#define GDK_TYPE_TOPLEVEL (gdk_toplevel_get_type ())
/**
* GdkToplevel:
*
* An interface for top level surfaces.
*/
GDK_AVAILABLE_IN_ALL
G_DECLARE_INTERFACE (GdkToplevel, gdk_toplevel, GDK, TOPLEVEL, GObject)
@@ -198,7 +203,6 @@ void gdk_toplevel_begin_move (GdkToplevel *toplevel,
double y,
guint32 timestamp);
G_END_DECLS
#endif /* __GDK_TOPLEVEL_H__ */
+1 -1
View File
@@ -30,7 +30,7 @@
G_BEGIN_DECLS
/**
* GdkTopLevelLayout:
* GdkToplevelLayout:
*
* Struct containing information for gdk_toplevel_present()
*/
+9
View File
@@ -320,11 +320,20 @@
-(void)hide
{
BOOL wasKey = [self isKeyWindow];
BOOL wasMain = [self isMainWindow];
inShowOrHide = YES;
[self orderOut:nil];
inShowOrHide = NO;
initialPositionKnown = NO;
if (wasMain)
[self windowDidResignMain:nil];
if (wasKey)
[self windowDidResignKey:nil];
}
-(BOOL)trackManualMove
+1 -1
View File
@@ -122,7 +122,7 @@ gdk_macos_device_ungrab (GdkDevice *device,
grab = _gdk_display_get_last_device_grab (display, device);
if (grab != NULL)
grab->serial_end = 0;
grab->serial_end = grab->serial_start;
_gdk_display_device_grab_update (display, device, 0);
}
+1 -1
View File
@@ -610,7 +610,7 @@ fill_scroll_event (GdkMacosDisplay *self,
sx = [nsevent scrollingDeltaX];
sy = [nsevent scrollingDeltaY];
if (sx != 0.0 || dx != 0.0)
if (sx != 0.0 || sy != 0.0)
ret = gdk_scroll_event_new (GDK_SURFACE (surface),
pointer,
NULL,
+15 -8
View File
@@ -477,7 +477,8 @@ _gdk_macos_display_surface_removed (GdkMacosDisplay *self,
if (self->keyboard_surface == surface)
_gdk_macos_display_surface_resigned_key (self, surface);
g_queue_unlink (&self->sorted_surfaces, &surface->sorted);
if (queue_contains (&self->sorted_surfaces, &surface->sorted))
g_queue_unlink (&self->sorted_surfaces, &surface->sorted);
if (queue_contains (&self->main_surfaces, &surface->main))
_gdk_macos_display_surface_resigned_main (self, surface);
@@ -519,23 +520,30 @@ void
_gdk_macos_display_surface_resigned_key (GdkMacosDisplay *self,
GdkMacosSurface *surface)
{
gboolean was_keyboard_surface;
g_return_if_fail (GDK_IS_MACOS_DISPLAY (self));
g_return_if_fail (GDK_IS_MACOS_SURFACE (surface));
if (self->keyboard_surface == surface)
was_keyboard_surface = self->keyboard_surface == surface;
self->keyboard_surface = NULL;
if (was_keyboard_surface)
{
GdkDevice *keyboard;
GdkEvent *event;
GdkSeat *seat;
GList *node;
seat = gdk_display_get_default_seat (GDK_DISPLAY (self));
keyboard = gdk_seat_get_keyboard (seat);
event = gdk_focus_event_new (GDK_SURFACE (surface), keyboard, FALSE);
_gdk_event_queue_append (GDK_DISPLAY (self), event);
node = _gdk_event_queue_append (GDK_DISPLAY (self), event);
_gdk_windowing_got_event (GDK_DISPLAY (self), node, event,
_gdk_display_get_next_serial (GDK_DISPLAY (self)));
}
self->keyboard_surface = NULL;
_gdk_macos_display_clear_sorting (self);
}
@@ -1049,9 +1057,8 @@ _gdk_macos_display_clear_sorting (GdkMacosDisplay *self)
{
g_return_if_fail (GDK_IS_MACOS_DISPLAY (self));
self->sorted_surfaces.head = NULL;
self->sorted_surfaces.tail = NULL;
self->sorted_surfaces.length = 0;
while (self->sorted_surfaces.head != NULL)
g_queue_unlink (&self->sorted_surfaces, self->sorted_surfaces.head);
}
const GList *
+16 -3
View File
@@ -447,15 +447,28 @@ _gdk_macos_gl_context_new (GdkMacosSurface *surface,
gboolean
_gdk_macos_gl_context_make_current (GdkMacosGLContext *self)
{
NSOpenGLContext *current;
g_return_val_if_fail (GDK_IS_MACOS_GL_CONTEXT (self), FALSE);
if (self->gl_context != nil)
if (self->gl_context == NULL)
return FALSE;
current = [NSOpenGLContext currentContext];
if (self->gl_context != current)
{
/* The OpenGL mac programming guide suggests that glFlush() is called
* before switching current contexts to ensure that the drawing commands
* are submitted.
*/
if (current != NULL)
glFlush ();
[self->gl_context makeCurrentContext];
return TRUE;
}
return FALSE;
return TRUE;
}
G_GNUC_END_IGNORE_DEPRECATIONS
+22 -1
View File
@@ -112,15 +112,21 @@ gdk_macos_surface_hide (GdkSurface *surface)
{
GdkMacosSurface *self = (GdkMacosSurface *)surface;
GdkSeat *seat;
gboolean was_mapped;
g_assert (GDK_IS_MACOS_SURFACE (self));
was_mapped = GDK_SURFACE_IS_MAPPED (GDK_SURFACE (self));
seat = gdk_display_get_default_seat (surface->display);
gdk_seat_ungrab (seat);
[self->window hide];
_gdk_surface_clear_update_area (surface);
if (was_mapped)
gdk_surface_freeze_updates (GDK_SURFACE (self));
}
static int
@@ -361,6 +367,17 @@ gdk_macos_surface_destroy (GdkSurface *surface,
GdkMacosSurface *self = (GdkMacosSurface *)surface;
GdkMacosWindow *window = g_steal_pointer (&self->window);
GdkFrameClock *frame_clock;
if ((frame_clock = gdk_surface_get_frame_clock (GDK_SURFACE (self))))
{
g_signal_handlers_disconnect_by_func (frame_clock,
G_CALLBACK (gdk_macos_surface_before_paint),
self);
g_signal_handlers_disconnect_by_func (frame_clock,
G_CALLBACK (gdk_macos_surface_before_paint),
self);
}
g_clear_pointer (&self->title, g_free);
@@ -533,7 +550,10 @@ _gdk_macos_surface_new (GdkMacosDisplay *display,
}
if (ret != NULL)
_gdk_macos_surface_monitor_changed (ret);
{
gdk_surface_freeze_updates (GDK_SURFACE (ret));
_gdk_macos_surface_monitor_changed (ret);
}
g_object_unref (frame_clock);
@@ -755,6 +775,7 @@ _gdk_macos_surface_show (GdkMacosSurface *self)
{
_gdk_macos_surface_update_position (self);
gdk_surface_invalidate_rect (GDK_SURFACE (self), NULL);
gdk_surface_thaw_updates (GDK_SURFACE (self));
}
}
+4
View File
@@ -100,6 +100,8 @@ _gdk_macos_toplevel_surface_present (GdkToplevel *toplevel,
g_assert (GDK_IS_MACOS_TOPLEVEL_SURFACE (self));
g_assert (GDK_IS_MACOS_WINDOW (nswindow));
_gdk_macos_toplevel_surface_attach_to_parent (self);
style_mask = [nswindow styleMask];
monitor = gdk_display_get_monitor_at_surface (display, surface);
@@ -174,6 +176,8 @@ _gdk_macos_toplevel_surface_present (GdkToplevel *toplevel,
}
else
{
[nswindow setAnimationBehavior:NSWindowAnimationBehaviorDocumentWindow];
if (!self->decorated &&
!GDK_MACOS_SURFACE (self)->did_initial_present &&
GDK_SURFACE (self)->x == 0 &&
+3 -3
View File
@@ -2096,8 +2096,8 @@ gdk_event_translate (MSG *msg,
window,
device_manager_win32->core_keyboard,
_gdk_win32_get_next_tick (msg->time),
state,
keycode,
state,
is_modifier,
&translated,
&translated);
@@ -2170,8 +2170,8 @@ gdk_event_translate (MSG *msg,
window,
device_manager_win32->core_keyboard,
_gdk_win32_get_next_tick (msg->time),
build_key_event_state (key_state),
0,
build_key_event_state (key_state),
FALSE,
&translated,
&translated);
@@ -2183,8 +2183,8 @@ gdk_event_translate (MSG *msg,
window,
device_manager_win32->core_keyboard,
_gdk_win32_get_next_tick (msg->time),
build_key_event_state (key_state),
0,
build_key_event_state (key_state),
FALSE,
&translated,
&translated);
+1 -1
View File
@@ -509,7 +509,7 @@ populate_monitor_devices_from_display_config (GPtrArray *monitors)
refresh = &dispconf_paths[path_index].targetInfo.refreshRate;
gdk_monitor_set_refresh_rate (mon,
refresh->Numerator * 1000 / refresh->Denominator);
refresh->Numerator * (UINT64) 1000 / refresh->Denominator);
}
g_free (dispconf_paths);
+2 -2
View File
@@ -1395,7 +1395,7 @@ gdk_x11_keymap_translate_keyboard_state (GdkKeymap *keymap,
return tmp_keyval != NoSymbol;
}
/**
/*< private >
* gdk_x11_keymap_get_group_for_state:
* @keymap: (type GdkX11Keymap): a #GdkX11Keymap
* @state: raw state returned from X
@@ -1459,7 +1459,7 @@ _gdk_x11_keymap_add_virt_mods (GdkKeymap *keymap,
}
}
/**
/*< private >
* gdk_x11_keymap_key_is_modifier:
* @keymap: (type GdkX11Keymap): a #GdkX11Keymap
* @keycode: the hardware keycode from a key event
+4 -4
View File
@@ -281,7 +281,7 @@ _gdk_x11_screen_init_visuals (GdkX11Screen *x11_screen,
}
}
/**
/*< private >
* gdk_x11_screen_lookup_visual:
* @screen: a #GdkX11Screen.
* @xvisualid: an X Visual ID.
@@ -304,11 +304,11 @@ gdk_x11_screen_lookup_visual (GdkX11Screen *x11_screen,
return NULL;
}
/**
/*< private >
* gdk_x11_visual_get_xvisual:
* @visual: (type GdkX11Visual): a #GdkVisual.
* @visual: a #GdkX11Visual.
*
* Returns the X visual belonging to a #GdkVisual.
* Returns the X visual belonging to a #GdkX11Visual.
*
* Returns: (transfer none): an Xlib Visual*.
**/
+6
View File
@@ -264,10 +264,13 @@ collect_reused_child_nodes (GskRenderer *renderer,
case GSK_RADIAL_GRADIENT_NODE:
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
case GSK_CONIC_GRADIENT_NODE:
case GSK_REPEAT_NODE:
case GSK_BLEND_NODE:
case GSK_CROSS_FADE_NODE:
case GSK_BLUR_NODE:
case GSK_FILL_NODE:
case GSK_STROKE_NODE:
default:
@@ -848,11 +851,14 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer,
case GSK_RADIAL_GRADIENT_NODE:
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
case GSK_CONIC_GRADIENT_NODE:
case GSK_REPEAT_NODE:
case GSK_BLEND_NODE:
case GSK_CROSS_FADE_NODE:
case GSK_BLUR_NODE:
case GSK_GL_SHADER_NODE:
case GSK_FILL_NODE:
case GSK_STROKE_NODE:
default:
break; /* Fallback */
}
+71 -7
View File
@@ -54,6 +54,8 @@
glGetUniformLocation(programs->program_name ## _program.id, "u_" #uniform_basename);\
if (programs->program_name ## _program.program_name.uniform_basename ## _location == -1) \
{ \
g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_LINK_FAILED, \
"Failed to find variable \"u_%s\" in shader program \"%s\"", #uniform_basename, #program_name); \
g_clear_pointer (&programs, gsk_gl_renderer_programs_unref); \
goto out; \
} \
@@ -306,10 +308,10 @@ color_matrix_modifies_alpha (GskRenderNode *node)
static inline void
gsk_rounded_rect_shrink_to_minimum (GskRoundedRect *self)
{
self->bounds.size.width = ceilf (MAX (MAX (self->corner[0].width, self->corner[1].width),
MAX (self->corner[2].width, self->corner[3].width)) * 2);
self->bounds.size.height = ceilf (MAX (MAX (self->corner[0].height, self->corner[1].height),
MAX (self->corner[2].height, self->corner[3].height)) * 2);
self->bounds.size.width = MAX (self->corner[0].width + self->corner[1].width,
self->corner[3].width + self->corner[2].width);
self->bounds.size.height = MAX (self->corner[0].height + self->corner[3].height,
self->corner[1].height + self->corner[2].height);
}
static inline gboolean G_GNUC_PURE
@@ -1481,6 +1483,35 @@ render_radial_gradient_node (GskGLRenderer *self,
}
}
static inline void
render_conic_gradient_node (GskGLRenderer *self,
GskRenderNode *node,
RenderOpBuilder *builder)
{
const int n_color_stops = gsk_conic_gradient_node_get_n_color_stops (node);
if (n_color_stops < GL_MAX_GRADIENT_STOPS)
{
const GskColorStop *stops = gsk_conic_gradient_node_get_color_stops (node, NULL);
const graphene_point_t *center = gsk_conic_gradient_node_get_center (node);
const float rotation = gsk_conic_gradient_node_get_rotation (node);
ops_set_program (builder, &self->programs->conic_gradient_program);
ops_set_conic_gradient (builder,
n_color_stops,
stops,
builder->dx + center->x,
builder->dy + center->y,
rotation);
load_vertex_data (ops_draw (builder, NULL), &node->bounds, builder);
}
else
{
render_fallback_node (self, node, builder);
}
}
static inline gboolean
rounded_inner_rect_contains_rect (const GskRoundedRect *rounded,
const graphene_rect_t *rect)
@@ -3014,6 +3045,23 @@ apply_radial_gradient_op (const Program *program,
glUniform2f (program->radial_gradient.center_location, op->center[0], op->center[1]);
}
static inline void
apply_conic_gradient_op (const Program *program,
const OpConicGradient *op)
{
OP_PRINT (" -> Conic gradient");
if (op->n_color_stops.send)
glUniform1i (program->conic_gradient.num_color_stops_location, op->n_color_stops.value);
if (op->color_stops.send)
glUniform1fv (program->conic_gradient.color_stops_location,
op->n_color_stops.value * 5,
(float *)op->color_stops.value);
glUniform1f (program->conic_gradient.rotation_location, op->rotation);
glUniform2f (program->conic_gradient.center_location, op->center[0], op->center[1]);
}
static inline void
apply_border_op (const Program *program,
const OpBorder *op)
@@ -3248,6 +3296,7 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self,
{ "/org/gtk/libgsk/glsl/inset_shadow.glsl", "inset shadow" },
{ "/org/gtk/libgsk/glsl/linear_gradient.glsl", "linear gradient" },
{ "/org/gtk/libgsk/glsl/radial_gradient.glsl", "radial gradient" },
{ "/org/gtk/libgsk/glsl/conic_gradient.glsl", "conic gradient" },
{ "/org/gtk/libgsk/glsl/outset_shadow.glsl", "outset shadow" },
{ "/org/gtk/libgsk/glsl/repeat.glsl", "repeat" },
{ "/org/gtk/libgsk/glsl/unblurred_outset_shadow.glsl", "unblurred_outset shadow" },
@@ -3310,6 +3359,12 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self,
INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, end);
INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, radius);
/* conic gradient */
INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, color_stops);
INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, num_color_stops);
INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, center);
INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, rotation);
/* blur */
INIT_PROGRAM_UNIFORM_LOCATION (blur, blur_radius);
INIT_PROGRAM_UNIFORM_LOCATION (blur, blur_size);
@@ -3361,9 +3416,8 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self,
out:
gsk_gl_shader_builder_finish (&shader_builder);
if (error && !(*error) && !programs)
g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_COMPILATION_FAILED,
"Failed to compile all shader programs"); /* Probably, eh. */
/* Check we indeed emitted an error if there was one */
g_assert (programs || !error || *error);
return programs;
}
@@ -3662,6 +3716,10 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer *self,
render_radial_gradient_node (self, node, builder);
break;
case GSK_CONIC_GRADIENT_NODE:
render_conic_gradient_node (self, node, builder);
break;
case GSK_CLIP_NODE:
render_clip_node (self, node, builder);
break;
@@ -3723,6 +3781,8 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer *self,
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
case GSK_FILL_NODE:
case GSK_STROKE_NODE:
case GSK_CAIRO_NODE:
default:
{
@@ -4025,6 +4085,10 @@ gsk_gl_renderer_render_ops (GskGLRenderer *self)
apply_radial_gradient_op (program, ptr);
break;
case OP_CHANGE_CONIC_GRADIENT:
apply_conic_gradient_op (program, ptr);
break;
case OP_CHANGE_BLUR:
apply_blur_op (program, ptr);
break;
+24
View File
@@ -1038,3 +1038,27 @@ ops_set_radial_gradient (RenderOpBuilder *self,
op->start = start;
op->end = end;
}
void
ops_set_conic_gradient (RenderOpBuilder *self,
guint n_color_stops,
const GskColorStop *color_stops,
float center_x,
float center_y,
float rotation)
{
const guint real_n_color_stops = MIN (GL_MAX_GRADIENT_STOPS, n_color_stops);
OpConicGradient *op;
/* TODO: State tracking? */
op = ops_begin (self, OP_CHANGE_CONIC_GRADIENT);
op->n_color_stops.value = real_n_color_stops;
op->n_color_stops.send = true;
op->color_stops.value = color_stops;
op->color_stops.send = true;
op->center[0] = center_x;
op->center[1] = center_y;
op->rotation = rotation;
}
+14 -1
View File
@@ -13,7 +13,7 @@
#include "opbuffer.h"
#define GL_N_VERTICES 6
#define GL_N_PROGRAMS 14
#define GL_N_PROGRAMS 15
#define GL_MAX_GRADIENT_STOPS 6
typedef struct
@@ -129,6 +129,12 @@ struct _Program
int end_location;
int radius_location;
} radial_gradient;
struct {
int num_color_stops_location;
int color_stops_location;
int center_location;
int rotation_location;
} conic_gradient;
struct {
int blur_radius_location;
int blur_size_location;
@@ -193,6 +199,7 @@ typedef struct {
Program inset_shadow_program;
Program linear_gradient_program;
Program radial_gradient_program;
Program conic_gradient_program;
Program outset_shadow_program;
Program repeat_program;
Program unblurred_outset_shadow_program;
@@ -327,6 +334,12 @@ void ops_set_radial_gradient (RenderOpBuilder *self,
float end,
float hradius,
float vradius);
void ops_set_conic_gradient (RenderOpBuilder *self,
guint n_color_stops,
const GskColorStop *color_stops,
float center_x,
float center_y,
float rotation);
GskQuadVertex * ops_draw (RenderOpBuilder *builder,
const GskQuadVertex vertex_data[GL_N_VERTICES]);
+1
View File
@@ -33,6 +33,7 @@ static guint op_sizes[OP_LAST] = {
sizeof (OpBlend),
sizeof (OpGLShader),
sizeof (OpExtraTexture),
sizeof (OpConicGradient),
};
void
+9
View File
@@ -41,6 +41,7 @@ typedef enum
OP_CHANGE_BLEND = 27,
OP_CHANGE_GL_SHADER_ARGS = 28,
OP_CHANGE_EXTRA_SOURCE_TEXTURE = 29,
OP_CHANGE_CONIC_GRADIENT = 30,
OP_LAST
} OpKind;
@@ -156,6 +157,14 @@ typedef struct
float center[2];
} OpRadialGradient;
typedef struct
{
ColorStopUniformValue color_stops;
IntUniformValue n_color_stops;
float center[2];
float rotation;
} OpConicGradient;
typedef struct
{
const graphene_matrix_t *matrix;
+2
View File
@@ -22,6 +22,8 @@
#ifndef __GI_SCANNER__
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPath, gsk_path_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPathMeasure, gsk_path_measure_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskRenderer, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskRenderNode, gsk_render_node_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskTransform, gsk_transform_unref)
+5 -1
View File
@@ -21,11 +21,15 @@
#define __GSK_H_INSIDE__
#include <gsk/gskenums.h>
#include <gsk/gskglshader.h>
#include <gsk/gskpath.h>
#include <gsk/gskpathbuilder.h>
#include <gsk/gskpathmeasure.h>
#include <gsk/gskrenderer.h>
#include <gsk/gskrendernode.h>
#include <gsk/gskroundedrect.h>
#include <gsk/gskstroke.h>
#include <gsk/gsktransform.h>
#include <gsk/gskglshader.h>
#include <gsk/gskcairorenderer.h>
+1669
View File
File diff suppressed because it is too large Load Diff
+99
View File
@@ -0,0 +1,99 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GSK_CONTOUR_PRIVATE_H__
#define __GSK_CONTOUR_PRIVATE_H__
#include <gskpath.h>
#include "gskpathopprivate.h"
G_BEGIN_DECLS
typedef enum
{
GSK_PATH_FLAT,
GSK_PATH_CLOSED
} GskPathFlags;
typedef struct _GskContour GskContour;
GskContour * gsk_rect_contour_new (const graphene_rect_t *rect);
GskContour * gsk_circle_contour_new (const graphene_point_t *center,
float radius,
float start_angle,
float end_angle);
GskContour * gsk_standard_contour_new (GskPathFlags flags,
const graphene_point_t *points,
gsize n_points,
const gskpathop *ops,
gsize n_ops,
gssize offset);
void gsk_contour_copy (GskContour * dest,
const GskContour *src);
GskContour * gsk_contour_dup (const GskContour *src);
gsize gsk_contour_get_size (const GskContour *self);
GskPathFlags gsk_contour_get_flags (const GskContour *self);
void gsk_contour_print (const GskContour *self,
GString *string);
gboolean gsk_contour_get_bounds (const GskContour *self,
graphene_rect_t *bounds);
gpointer gsk_contour_init_measure (const GskContour *self,
float tolerance,
float *out_length);
void gsk_contour_free_measure (const GskContour *self,
gpointer data);
gboolean gsk_contour_foreach (const GskContour *self,
float tolerance,
GskPathForeachFunc func,
gpointer user_data);
void gsk_contour_get_start_end (const GskContour *self,
graphene_point_t *start,
graphene_point_t *end);
void gsk_contour_get_point (const GskContour *self,
gpointer measure_data,
float distance,
graphene_point_t *pos,
graphene_vec2_t *tangent);
gboolean gsk_contour_get_closest_point (const GskContour *self,
gpointer measure_data,
float tolerance,
const graphene_point_t *point,
float threshold,
float *out_distance,
graphene_point_t *out_pos,
float *out_offset,
graphene_vec2_t *out_tangent);
int gsk_contour_get_winding (const GskContour *self,
gpointer measure_data,
const graphene_point_t *point,
gboolean *on_edge);
void gsk_contour_add_segment (const GskContour *self,
GskPathBuilder *builder,
gpointer measure_data,
float start,
float end);
G_END_DECLS
#endif /* __GSK_CONTOUR_PRIVATE_H__ */
+978
View File
@@ -0,0 +1,978 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gskcurveprivate.h"
#define MIN_PROGRESS (1/1024.f)
typedef struct _GskCurveClass GskCurveClass;
struct _GskCurveClass
{
void (* init) (GskCurve *curve,
gskpathop op);
void (* init_foreach) (GskCurve *curve,
GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight);
void (* get_point) (const GskCurve *curve,
float t,
graphene_point_t *pos);
void (* get_tangent) (const GskCurve *curve,
float t,
graphene_vec2_t *tangent);
void (* split) (const GskCurve *curve,
float progress,
GskCurve *result1,
GskCurve *result2);
void (* segment) (const GskCurve *curve,
float start,
float end,
GskCurve *segment);
gboolean (* decompose) (const GskCurve *curve,
float tolerance,
GskCurveAddLineFunc add_line_func,
gpointer user_data);
gskpathop (* pathop) (const GskCurve *curve);
const graphene_point_t * (* get_start_point) (const GskCurve *curve);
const graphene_point_t * (* get_end_point) (const GskCurve *curve);
void (* get_start_tangent) (const GskCurve *curve,
graphene_vec2_t *tangent);
void (* get_end_tangent) (const GskCurve *curve,
graphene_vec2_t *tangent);
};
static void
get_tangent (const graphene_point_t *p0,
const graphene_point_t *p1,
graphene_vec2_t *t)
{
graphene_vec2_init (t, p1->x - p0->x, p1->y - p0->y);
graphene_vec2_normalize (t, t);
}
/** LINE **/
static void
gsk_line_curve_init_from_points (GskLineCurve *self,
GskPathOperation op,
const graphene_point_t *start,
const graphene_point_t *end)
{
self->op = op;
self->points[0] = *start;
self->points[1] = *end;
}
static void
gsk_line_curve_init (GskCurve *curve,
gskpathop op)
{
GskLineCurve *self = &curve->line;
const graphene_point_t *pts = gsk_pathop_points (op);
gsk_line_curve_init_from_points (self, gsk_pathop_op (op), &pts[0], &pts[1]);
}
static void
gsk_line_curve_init_foreach (GskCurve *curve,
GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight)
{
GskLineCurve *self = &curve->line;
g_assert (n_pts == 2);
gsk_line_curve_init_from_points (self, op, &pts[0], &pts[1]);
}
static void
gsk_line_curve_get_point (const GskCurve *curve,
float t,
graphene_point_t *pos)
{
const GskLineCurve *self = &curve->line;
graphene_point_interpolate (&self->points[0], &self->points[1], t, pos);
}
static void
gsk_line_curve_get_tangent (const GskCurve *curve,
float t,
graphene_vec2_t *tangent)
{
const GskLineCurve *self = &curve->line;
get_tangent (&self->points[0], &self->points[1], tangent);
}
static void
gsk_line_curve_split (const GskCurve *curve,
float progress,
GskCurve *start,
GskCurve *end)
{
const GskLineCurve *self = &curve->line;
graphene_point_t point;
graphene_point_interpolate (&self->points[0], &self->points[1], progress, &point);
if (start)
gsk_line_curve_init_from_points (&start->line, GSK_PATH_LINE, &self->points[0], &point);
if (end)
gsk_line_curve_init_from_points (&end->line, GSK_PATH_LINE, &point, &self->points[1]);
}
static void
gsk_line_curve_segment (const GskCurve *curve,
float start,
float end,
GskCurve *segment)
{
const GskLineCurve *self = &curve->line;
graphene_point_t start_point, end_point;
graphene_point_interpolate (&self->points[0], &self->points[1], start, &start_point);
graphene_point_interpolate (&self->points[0], &self->points[1], end, &end_point);
gsk_line_curve_init_from_points (&segment->line, GSK_PATH_LINE, &start_point, &end_point);
}
static gboolean
gsk_line_curve_decompose (const GskCurve *curve,
float tolerance,
GskCurveAddLineFunc add_line_func,
gpointer user_data)
{
const GskLineCurve *self = &curve->line;
return add_line_func (&self->points[0], &self->points[1], 0.0f, 1.0f, user_data);
}
static gskpathop
gsk_line_curve_pathop (const GskCurve *curve)
{
const GskLineCurve *self = &curve->line;
return gsk_pathop_encode (self->op, self->points);
}
static const graphene_point_t *
gsk_line_curve_get_start_point (const GskCurve *curve)
{
const GskLineCurve *self = &curve->line;
return &self->points[0];
}
static const graphene_point_t *
gsk_line_curve_get_end_point (const GskCurve *curve)
{
const GskLineCurve *self = &curve->line;
return &self->points[1];
}
static void
gsk_line_curve_get_start_end_tangent (const GskCurve *curve,
graphene_vec2_t *tangent)
{
const GskLineCurve *self = &curve->line;
get_tangent (&self->points[0], &self->points[1], tangent);
}
static const GskCurveClass GSK_LINE_CURVE_CLASS = {
gsk_line_curve_init,
gsk_line_curve_init_foreach,
gsk_line_curve_get_point,
gsk_line_curve_get_tangent,
gsk_line_curve_split,
gsk_line_curve_segment,
gsk_line_curve_decompose,
gsk_line_curve_pathop,
gsk_line_curve_get_start_point,
gsk_line_curve_get_end_point,
gsk_line_curve_get_start_end_tangent,
gsk_line_curve_get_start_end_tangent
};
/** CURVE **/
static void
gsk_curve_curve_init_from_points (GskCurveCurve *self,
const graphene_point_t pts[4])
{
self->op = GSK_PATH_CURVE;
self->has_coefficients = FALSE;
memcpy (self->points, pts, sizeof (graphene_point_t) * 4);
}
static void
gsk_curve_curve_init (GskCurve *curve,
gskpathop op)
{
GskCurveCurve *self = &curve->curve;
gsk_curve_curve_init_from_points (self, gsk_pathop_points (op));
}
static void
gsk_curve_curve_init_foreach (GskCurve *curve,
GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight)
{
GskCurveCurve *self = &curve->curve;
g_assert (n_pts == 4);
gsk_curve_curve_init_from_points (self, pts);
}
static void
gsk_curve_curve_ensure_coefficients (const GskCurveCurve *curve)
{
GskCurveCurve *self = (GskCurveCurve *) curve;
const graphene_point_t *pts = &self->points[0];
if (self->has_coefficients)
return;
self->coeffs[0] = GRAPHENE_POINT_INIT (pts[3].x - 3.0f * pts[2].x + 3.0f * pts[1].x - pts[0].x,
pts[3].y - 3.0f * pts[2].y + 3.0f * pts[1].y - pts[0].y);
self->coeffs[1] = GRAPHENE_POINT_INIT (3.0f * pts[2].x - 6.0f * pts[1].x + 3.0f * pts[0].x,
3.0f * pts[2].y - 6.0f * pts[1].y + 3.0f * pts[0].y);
self->coeffs[2] = GRAPHENE_POINT_INIT (3.0f * pts[1].x - 3.0f * pts[0].x,
3.0f * pts[1].y - 3.0f * pts[0].y);
self->coeffs[3] = pts[0];
self->has_coefficients = TRUE;
}
static void
gsk_curve_curve_get_point (const GskCurve *curve,
float t,
graphene_point_t *pos)
{
const GskCurveCurve *self = &curve->curve;
const graphene_point_t *c = self->coeffs;
gsk_curve_curve_ensure_coefficients (self);
*pos = GRAPHENE_POINT_INIT (((c[0].x * t + c[1].x) * t +c[2].x) * t + c[3].x,
((c[0].y * t + c[1].y) * t +c[2].y) * t + c[3].y);
}
static void
gsk_curve_curve_get_tangent (const GskCurve *curve,
float t,
graphene_vec2_t *tangent)
{
const GskCurveCurve *self = &curve->curve;
const graphene_point_t *c = self->coeffs;
gsk_curve_curve_ensure_coefficients (self);
graphene_vec2_init (tangent,
(3.0f * c[0].x * t + 2.0f * c[1].x) * t + c[2].x,
(3.0f * c[0].y * t + 2.0f * c[1].y) * t + c[2].y);
graphene_vec2_normalize (tangent, tangent);
}
static void
gsk_curve_curve_split (const GskCurve *curve,
float progress,
GskCurve *start,
GskCurve *end)
{
const GskCurveCurve *self = &curve->curve;
const graphene_point_t *pts = self->points;
graphene_point_t ab, bc, cd;
graphene_point_t abbc, bccd;
graphene_point_t final;
graphene_point_interpolate (&pts[0], &pts[1], progress, &ab);
graphene_point_interpolate (&pts[1], &pts[2], progress, &bc);
graphene_point_interpolate (&pts[2], &pts[3], progress, &cd);
graphene_point_interpolate (&ab, &bc, progress, &abbc);
graphene_point_interpolate (&bc, &cd, progress, &bccd);
graphene_point_interpolate (&abbc, &bccd, progress, &final);
if (start)
gsk_curve_curve_init_from_points (&start->curve, (graphene_point_t[4]) { pts[0], ab, abbc, final });
if (end)
gsk_curve_curve_init_from_points (&end->curve, (graphene_point_t[4]) { final, bccd, cd, pts[3] });
}
static void
gsk_curve_curve_segment (const GskCurve *curve,
float start,
float end,
GskCurve *segment)
{
GskCurve tmp;
gsk_curve_curve_split (curve, start, NULL, &tmp);
gsk_curve_curve_split (&tmp, (end - start) / (1.0f - start), segment, NULL);
}
/* taken from Skia, including the very descriptive name */
static gboolean
gsk_curve_curve_too_curvy (const GskCurveCurve *self,
float tolerance)
{
const graphene_point_t *pts = self->points;
graphene_point_t p;
graphene_point_interpolate (&pts[0], &pts[3], 1.0f / 3, &p);
if (ABS (p.x - pts[1].x) + ABS (p.y - pts[1].y) > tolerance)
return TRUE;
graphene_point_interpolate (&pts[0], &pts[3], 2.0f / 3, &p);
if (ABS (p.x - pts[2].x) + ABS (p.y - pts[2].y) > tolerance)
return TRUE;
return FALSE;
}
static gboolean
gsk_curce_curve_decompose_step (const GskCurve *curve,
float start_progress,
float end_progress,
float tolerance,
GskCurveAddLineFunc add_line_func,
gpointer user_data)
{
const GskCurveCurve *self = &curve->curve;
GskCurve left, right;
float mid_progress;
if (!gsk_curve_curve_too_curvy (self, tolerance) || end_progress - start_progress <= MIN_PROGRESS)
return add_line_func (&self->points[0], &self->points[3], start_progress, end_progress, user_data);
gsk_curve_curve_split ((const GskCurve *) self, 0.5, &left, &right);
mid_progress = (start_progress + end_progress) / 2;
return gsk_curce_curve_decompose_step (&left, start_progress, mid_progress, tolerance, add_line_func, user_data)
&& gsk_curce_curve_decompose_step (&right, mid_progress, end_progress, tolerance, add_line_func, user_data);
}
static gboolean
gsk_curve_curve_decompose (const GskCurve *curve,
float tolerance,
GskCurveAddLineFunc add_line_func,
gpointer user_data)
{
return gsk_curce_curve_decompose_step (curve, 0.0, 1.0, tolerance, add_line_func, user_data);
}
static gskpathop
gsk_curve_curve_pathop (const GskCurve *curve)
{
const GskCurveCurve *self = &curve->curve;
return gsk_pathop_encode (self->op, self->points);
}
static const graphene_point_t *
gsk_curve_curve_get_start_point (const GskCurve *curve)
{
const GskCurveCurve *self = &curve->curve;
return &self->points[0];
}
static const graphene_point_t *
gsk_curve_curve_get_end_point (const GskCurve *curve)
{
const GskCurveCurve *self = &curve->curve;
return &self->points[3];
}
static void
gsk_curve_curve_get_start_tangent (const GskCurve *curve,
graphene_vec2_t *tangent)
{
const GskCurveCurve *self = &curve->curve;
get_tangent (&self->points[0], &self->points[1], tangent);
}
static void
gsk_curve_curve_get_end_tangent (const GskCurve *curve,
graphene_vec2_t *tangent)
{
const GskCurveCurve *self = &curve->curve;
get_tangent (&self->points[2], &self->points[3], tangent);
}
static const GskCurveClass GSK_CURVE_CURVE_CLASS = {
gsk_curve_curve_init,
gsk_curve_curve_init_foreach,
gsk_curve_curve_get_point,
gsk_curve_curve_get_tangent,
gsk_curve_curve_split,
gsk_curve_curve_segment,
gsk_curve_curve_decompose,
gsk_curve_curve_pathop,
gsk_curve_curve_get_start_point,
gsk_curve_curve_get_end_point,
gsk_curve_curve_get_start_tangent,
gsk_curve_curve_get_end_tangent
};
/** CONIC **/
static void
gsk_conic_curve_init_from_points (GskConicCurve *self,
const graphene_point_t pts[4])
{
self->op = GSK_PATH_CONIC;
self->has_coefficients = FALSE;
memcpy (self->points, pts, sizeof (graphene_point_t) * 4);
}
static void
gsk_conic_curve_init (GskCurve *curve,
gskpathop op)
{
GskConicCurve *self = &curve->conic;
gsk_conic_curve_init_from_points (self, gsk_pathop_points (op));
}
static void
gsk_conic_curve_init_foreach (GskCurve *curve,
GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight)
{
GskConicCurve *self = &curve->conic;
g_assert (n_pts == 3);
gsk_conic_curve_init_from_points (self,
(graphene_point_t[4]) {
pts[0],
pts[1],
GRAPHENE_POINT_INIT (weight, 0),
pts[2]
});
}
static inline float
gsk_conic_curve_get_weight (const GskConicCurve *self)
{
return self->points[2].x;
}
static void
gsk_conic_curve_ensure_coefficents (const GskConicCurve *curve)
{
GskConicCurve *self = (GskConicCurve *) curve;
float w = gsk_conic_curve_get_weight (self);
const graphene_point_t *pts = self->points;
graphene_point_t pw = GRAPHENE_POINT_INIT (w * pts[1].x, w * pts[1].y);
if (self->has_coefficients)
return;
self->num[2] = pts[0];
self->num[1] = GRAPHENE_POINT_INIT (2 * (pw.x - pts[0].x),
2 * (pw.y - pts[0].y));
self->num[0] = GRAPHENE_POINT_INIT (pts[3].x - 2 * pw.x + pts[0].x,
pts[3].y - 2 * pw.y + pts[0].y);
self->denom[2] = GRAPHENE_POINT_INIT (1, 1);
self->denom[1] = GRAPHENE_POINT_INIT (2 * (w - 1), 2 * (w - 1));
self->denom[0] = GRAPHENE_POINT_INIT (-self->denom[1].x, -self->denom[1].y);
self->has_coefficients = TRUE;
}
static inline void
gsk_curve_eval_quad (const graphene_point_t quad[3],
float progress,
graphene_point_t *result)
{
*result = GRAPHENE_POINT_INIT ((quad[0].x * progress + quad[1].x) * progress + quad[2].x,
(quad[0].y * progress + quad[1].y) * progress + quad[2].y);
}
static inline void
gsk_conic_curve_eval_point (const GskConicCurve *self,
float progress,
graphene_point_t *point)
{
graphene_point_t num, denom;
gsk_curve_eval_quad (self->num, progress, &num);
gsk_curve_eval_quad (self->denom, progress, &denom);
*point = GRAPHENE_POINT_INIT (num.x / denom.x, num.y / denom.y);
}
static void
gsk_conic_curve_get_point (const GskCurve *curve,
float t,
graphene_point_t *pos)
{
const GskConicCurve *self = &curve->conic;
gsk_conic_curve_ensure_coefficents (self);
gsk_conic_curve_eval_point (self, t, pos);
}
static void
gsk_conic_curve_get_tangent (const GskCurve *curve,
float t,
graphene_vec2_t *tangent)
{
const GskConicCurve *self = &curve->conic;
graphene_point_t tmp;
float w = gsk_conic_curve_get_weight (self);
const graphene_point_t *pts = self->points;
/* The tangent will be 0 in these corner cases, just
* treat it like a line here. */
if ((t <= 0.f && graphene_point_equal (&pts[0], &pts[1])) ||
(t >= 1.f && graphene_point_equal (&pts[1], &pts[3])))
{
graphene_vec2_init (tangent, pts[3].x - pts[0].x, pts[3].y - pts[0].y);
return;
}
gsk_curve_eval_quad ((graphene_point_t[3]) {
GRAPHENE_POINT_INIT ((w - 1) * (pts[3].x - pts[0].x),
(w - 1) * (pts[3].y - pts[0].y)),
GRAPHENE_POINT_INIT (pts[3].x - pts[0].x - 2 * w * (pts[1].x - pts[0].x),
pts[3].y - pts[0].y - 2 * w * (pts[1].y - pts[0].y)),
GRAPHENE_POINT_INIT (w * (pts[1].x - pts[0].x),
w * (pts[1].y - pts[0].y))
},
t,
&tmp);
graphene_vec2_init (tangent, tmp.x, tmp.y);
graphene_vec2_normalize (tangent, tangent);
}
static void
split_bezier3d_recurse (const graphene_point3d_t *p,
int l,
float t,
graphene_point3d_t *left,
graphene_point3d_t *right,
int *lpos,
int *rpos)
{
if (l == 1)
{
left[*lpos] = p[0];
right[*rpos] = p[0];
}
else
{
graphene_point3d_t *np;
int i;
np = g_alloca (sizeof (graphene_point3d_t) * (l - 1));
for (i = 0; i < l - 1; i++)
{
if (i == 0)
{
left[*lpos] = p[i];
(*lpos)++;
}
if (i + 1 == l - 1)
{
right[*rpos] = p[i + 1];
(*rpos)--;
}
graphene_point3d_interpolate (&p[i], &p[i + 1], t, &np[i]);
}
split_bezier3d_recurse (np, l - 1, t, left, right, lpos, rpos);
}
}
static void
split_bezier3d (const graphene_point3d_t *p,
int l,
float t,
graphene_point3d_t *left,
graphene_point3d_t *right)
{
int lpos = 0;
int rpos = l - 1;
split_bezier3d_recurse (p, l, t, left, right, &lpos, &rpos);
}
static void
gsk_conic_curve_split (const GskCurve *curve,
float progress,
GskCurve *start,
GskCurve *end)
{
const GskConicCurve *self = &curve->conic;
graphene_point3d_t p[3];
graphene_point3d_t l[3], r[3];
graphene_point_t left[4], right[4];
float w;
/* do de Casteljau in homogeneous coordinates... */
w = self->points[2].x;
p[0] = GRAPHENE_POINT3D_INIT (self->points[0].x, self->points[0].y, 1);
p[1] = GRAPHENE_POINT3D_INIT (self->points[1].x * w, self->points[1].y * w, w);
p[2] = GRAPHENE_POINT3D_INIT (self->points[3].x, self->points[3].y, 1);
split_bezier3d (p, 3, progress, l, r);
/* then project the control points down */
left[0] = GRAPHENE_POINT_INIT (l[0].x / l[0].z, l[0].y / l[0].z);
left[1] = GRAPHENE_POINT_INIT (l[1].x / l[1].z, l[1].y / l[1].z);
left[3] = GRAPHENE_POINT_INIT (l[2].x / l[2].z, l[2].y / l[2].z);
right[0] = GRAPHENE_POINT_INIT (r[0].x / r[0].z, r[0].y / r[0].z);
right[1] = GRAPHENE_POINT_INIT (r[1].x / r[1].z, r[1].y / r[1].z);
right[3] = GRAPHENE_POINT_INIT (r[2].x / r[2].z, r[2].y / r[2].z);
/* normalize the outer weights to be 1 by using
* the fact that weights w_i and c*w_i are equivalent
* for any nonzero constant c
*/
for (int i = 0; i < 3; i++)
{
l[i].z /= l[0].z;
r[i].z /= r[2].z;
}
/* normalize the inner weight to be 1 by using
* the fact that w_0*w_2/w_1^2 is a constant for
* all equivalent weights.
*/
left[2] = GRAPHENE_POINT_INIT (l[1].z / sqrt (l[2].z), 0);
right[2] = GRAPHENE_POINT_INIT (r[1].z / sqrt (r[0].z), 0);
if (start)
gsk_curve_init (start, gsk_pathop_encode (GSK_PATH_CONIC, left));
if (end)
gsk_curve_init (end, gsk_pathop_encode (GSK_PATH_CONIC, right));
}
static void
gsk_conic_curve_segment (const GskCurve *curve,
float start,
float end,
GskCurve *segment)
{
const GskConicCurve *self = &curve->conic;
graphene_point_t start_num, start_denom;
graphene_point_t mid_num, mid_denom;
graphene_point_t end_num, end_denom;
graphene_point_t ctrl_num, ctrl_denom;
float mid;
if (start <= 0.0f)
return gsk_conic_curve_split (curve, end, segment, NULL);
else if (end >= 1.0f)
return gsk_conic_curve_split (curve, start, NULL, segment);
gsk_conic_curve_ensure_coefficents (self);
gsk_curve_eval_quad (self->num, start, &start_num);
gsk_curve_eval_quad (self->denom, start, &start_denom);
mid = (start + end) / 2;
gsk_curve_eval_quad (self->num, mid, &mid_num);
gsk_curve_eval_quad (self->denom, mid, &mid_denom);
gsk_curve_eval_quad (self->num, end, &end_num);
gsk_curve_eval_quad (self->denom, end, &end_denom);
ctrl_num = GRAPHENE_POINT_INIT (2 * mid_num.x - (start_num.x + end_num.x) / 2,
2 * mid_num.y - (start_num.y + end_num.y) / 2);
ctrl_denom = GRAPHENE_POINT_INIT (2 * mid_denom.x - (start_denom.x + end_denom.x) / 2,
2 * mid_denom.y - (start_denom.y + end_denom.y) / 2);
gsk_conic_curve_init_from_points (&segment->conic,
(graphene_point_t[4]) {
GRAPHENE_POINT_INIT (start_num.x / start_denom.x,
start_num.y / start_denom.y),
GRAPHENE_POINT_INIT (ctrl_num.x / ctrl_denom.x,
ctrl_num.y / ctrl_denom.y),
GRAPHENE_POINT_INIT (ctrl_denom.x / sqrtf (start_denom.x * end_denom.x),
0),
GRAPHENE_POINT_INIT (end_num.x / end_denom.x,
end_num.y / end_denom.y)
});
}
/* taken from Skia, including the very descriptive name */
static gboolean
gsk_conic_curve_too_curvy (const graphene_point_t *start,
const graphene_point_t *mid,
const graphene_point_t *end,
float tolerance)
{
return fabs ((start->x + end->x) * 0.5 - mid->x) > tolerance
|| fabs ((start->y + end->y) * 0.5 - mid->y) > tolerance;
}
static gboolean
gsk_conic_curve_decompose_subdivide (const GskConicCurve *self,
float tolerance,
const graphene_point_t *start,
float start_progress,
const graphene_point_t *end,
float end_progress,
GskCurveAddLineFunc add_line_func,
gpointer user_data)
{
graphene_point_t mid;
float mid_progress;
mid_progress = (start_progress + end_progress) / 2;
gsk_conic_curve_eval_point (self, mid_progress, &mid);
if (end_progress - start_progress <= MIN_PROGRESS ||
!gsk_conic_curve_too_curvy (start, &mid, end, tolerance))
{
return add_line_func (start, end, start_progress, end_progress, user_data);
}
return gsk_conic_curve_decompose_subdivide (self, tolerance,
start, start_progress, &mid, mid_progress,
add_line_func, user_data)
&& gsk_conic_curve_decompose_subdivide (self, tolerance,
&mid, mid_progress, end, end_progress,
add_line_func, user_data);
}
static gboolean
gsk_conic_curve_decompose (const GskCurve *curve,
float tolerance,
GskCurveAddLineFunc add_line_func,
gpointer user_data)
{
const GskConicCurve *self = &curve->conic;
graphene_point_t mid;
gsk_conic_curve_ensure_coefficents (self);
gsk_conic_curve_eval_point (self, 0.5, &mid);
return gsk_conic_curve_decompose_subdivide (self,
tolerance,
&self->points[0],
0.0f,
&mid,
0.5f,
add_line_func,
user_data)
&& gsk_conic_curve_decompose_subdivide (self,
tolerance,
&mid,
0.5f,
&self->points[3],
1.0f,
add_line_func,
user_data);
}
static gskpathop
gsk_conic_curve_pathop (const GskCurve *curve)
{
const GskConicCurve *self = &curve->conic;
return gsk_pathop_encode (self->op, self->points);
}
static const graphene_point_t *
gsk_conic_curve_get_start_point (const GskCurve *curve)
{
const GskConicCurve *self = &curve->conic;
return &self->points[0];
}
static const graphene_point_t *
gsk_conic_curve_get_end_point (const GskCurve *curve)
{
const GskConicCurve *self = &curve->conic;
return &self->points[3];
}
static void
gsk_conic_curve_get_start_tangent (const GskCurve *curve,
graphene_vec2_t *tangent)
{
const GskConicCurve *self = &curve->conic;
get_tangent (&self->points[0], &self->points[1], tangent);
}
static void
gsk_conic_curve_get_end_tangent (const GskCurve *curve,
graphene_vec2_t *tangent)
{
const GskConicCurve *self = &curve->conic;
get_tangent (&self->points[1], &self->points[3], tangent);
}
static const GskCurveClass GSK_CONIC_CURVE_CLASS = {
gsk_conic_curve_init,
gsk_conic_curve_init_foreach,
gsk_conic_curve_get_point,
gsk_conic_curve_get_tangent,
gsk_conic_curve_split,
gsk_conic_curve_segment,
gsk_conic_curve_decompose,
gsk_conic_curve_pathop,
gsk_conic_curve_get_start_point,
gsk_conic_curve_get_end_point,
gsk_conic_curve_get_start_tangent,
gsk_conic_curve_get_end_tangent
};
/** API **/
static const GskCurveClass *
get_class (GskPathOperation op)
{
const GskCurveClass *klasses[] = {
[GSK_PATH_CLOSE] = &GSK_LINE_CURVE_CLASS,
[GSK_PATH_LINE] = &GSK_LINE_CURVE_CLASS,
[GSK_PATH_CURVE] = &GSK_CURVE_CURVE_CLASS,
[GSK_PATH_CONIC] = &GSK_CONIC_CURVE_CLASS,
};
g_assert (op < G_N_ELEMENTS (klasses) && klasses[op] != NULL);
return klasses[op];
}
void
gsk_curve_init (GskCurve *curve,
gskpathop op)
{
get_class (gsk_pathop_op (op))->init (curve, op);
}
void
gsk_curve_init_foreach (GskCurve *curve,
GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight)
{
get_class (op)->init_foreach (curve, op, pts, n_pts, weight);
}
void
gsk_curve_get_point (const GskCurve *curve,
float progress,
graphene_point_t *pos)
{
get_class (curve->op)->get_point (curve, progress, pos);
}
void
gsk_curve_get_tangent (const GskCurve *curve,
float progress,
graphene_vec2_t *tangent)
{
get_class (curve->op)->get_tangent (curve, progress, tangent);
}
void
gsk_curve_split (const GskCurve *curve,
float progress,
GskCurve *start,
GskCurve *end)
{
get_class (curve->op)->split (curve, progress, start, end);
}
void
gsk_curve_segment (const GskCurve *curve,
float start,
float end,
GskCurve *segment)
{
if (start <= 0 && end >= 1)
{
*segment = *curve;
return;
}
get_class (curve->op)->segment (curve, start, end, segment);
}
gboolean
gsk_curve_decompose (const GskCurve *curve,
float tolerance,
GskCurveAddLineFunc add_line_func,
gpointer user_data)
{
return get_class (curve->op)->decompose (curve, tolerance, add_line_func, user_data);
}
gskpathop
gsk_curve_pathop (const GskCurve *curve)
{
return get_class (curve->op)->pathop (curve);
}
const graphene_point_t *
gsk_curve_get_start_point (const GskCurve *curve)
{
return get_class (curve->op)->get_start_point (curve);
}
const graphene_point_t *
gsk_curve_get_end_point (const GskCurve *curve)
{
return get_class (curve->op)->get_end_point (curve);
}
void
gsk_curve_get_start_tangent (const GskCurve *curve,
graphene_vec2_t *tangent)
{
get_class (curve->op)->get_start_tangent (curve, tangent);
}
void
gsk_curve_get_end_tangent (const GskCurve *curve,
graphene_vec2_t *tangent)
{
get_class (curve->op)->get_end_tangent (curve, tangent);
}
+123
View File
@@ -0,0 +1,123 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GSK_CURVE_PRIVATE_H__
#define __GSK_CURVE_PRIVATE_H__
#include "gskpathopprivate.h"
G_BEGIN_DECLS
typedef gpointer gskpathop;
typedef union _GskCurve GskCurve;
typedef struct _GskLineCurve GskLineCurve;
typedef struct _GskCurveCurve GskCurveCurve;
typedef struct _GskConicCurve GskConicCurve;
struct _GskLineCurve
{
GskPathOperation op;
gboolean padding;
graphene_point_t points[2];
};
struct _GskCurveCurve
{
GskPathOperation op;
gboolean has_coefficients;
graphene_point_t points[4];
graphene_point_t coeffs[4];
};
struct _GskConicCurve
{
GskPathOperation op;
gboolean has_coefficients;
graphene_point_t points[4];
graphene_point_t num[3];
graphene_point_t denom[3];
};
union _GskCurve
{
GskPathOperation op;
GskLineCurve line;
GskCurveCurve curve;
GskConicCurve conic;
};
typedef gboolean (* GskCurveAddLineFunc) (const graphene_point_t *from,
const graphene_point_t *to,
float from_progress,
float to_progress,
gpointer user_data);
void gsk_curve_init (GskCurve *curve,
gskpathop op);
void gsk_curve_init_foreach (GskCurve *curve,
GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight);
void gsk_curve_get_point (const GskCurve *curve,
float progress,
graphene_point_t *pos);
void gsk_curve_get_tangent (const GskCurve *curve,
float progress,
graphene_vec2_t *tangent);
void gsk_curve_split (const GskCurve *curve,
float progress,
GskCurve *start,
GskCurve *end);
void gsk_curve_segment (const GskCurve *curve,
float start,
float end,
GskCurve *segment);
gboolean gsk_curve_decompose (const GskCurve *curve,
float tolerance,
GskCurveAddLineFunc add_line_func,
gpointer user_data);
gskpathop gsk_curve_pathop (const GskCurve *curve);
#define gsk_curve_builder_to(curve, builder) gsk_path_builder_pathop_to ((builder), gsk_curve_pathop (curve))
const graphene_point_t *gsk_curve_get_start_point (const GskCurve *curve);
const graphene_point_t *gsk_curve_get_end_point (const GskCurve *curve);
void gsk_curve_get_start_tangent (const GskCurve *curve,
graphene_vec2_t *tangent);
void gsk_curve_get_end_tangent (const GskCurve *curve,
graphene_vec2_t *tangent);
G_END_DECLS
#endif /* __GSK_CURVE_PRIVATE_H__ */
+102 -1
View File
@@ -32,6 +32,7 @@
* @GSK_REPEATING_LINEAR_GRADIENT_NODE: A node drawing a repeating linear gradient
* @GSK_RADIAL_GRADIENT_NODE: A node drawing a radial gradient
* @GSK_REPEATING_RADIAL_GRADIENT_NODE: A node drawing a repeating radial gradient
* @GSK_CONIC_GRADIENT_NODE: A node drawing a conic gradient
* @GSK_BORDER_NODE: A node stroking a border around an area
* @GSK_TEXTURE_NODE: A node drawing a #GdkTexture
* @GSK_INSET_SHADOW_NODE: A node drawing an inset shadow
@@ -61,6 +62,7 @@ typedef enum {
GSK_REPEATING_LINEAR_GRADIENT_NODE,
GSK_RADIAL_GRADIENT_NODE,
GSK_REPEATING_RADIAL_GRADIENT_NODE,
GSK_CONIC_GRADIENT_NODE,
GSK_BORDER_NODE,
GSK_TEXTURE_NODE,
GSK_INSET_SHADOW_NODE,
@@ -71,6 +73,8 @@ typedef enum {
GSK_REPEAT_NODE,
GSK_CLIP_NODE,
GSK_ROUNDED_CLIP_NODE,
GSK_FILL_NODE,
GSK_STROKE_NODE,
GSK_SHADOW_NODE,
GSK_BLEND_NODE,
GSK_CROSS_FADE_NODE,
@@ -165,6 +169,104 @@ typedef enum {
GSK_CORNER_BOTTOM_LEFT
} GskCorner;
/**
* GskFillRule:
* @GSK_FILL_RULE_WINDING: If the path crosses the ray from
* left-to-right, counts +1. If the path crosses the ray
* from right to left, counts -1. (Left and right are determined
* from the perspective of looking along the ray from the starting
* point.) If the total count is non-zero, the point will be filled.
* @GSK_FILL_RULE_EVEN_ODD: Counts the total number of
* intersections, without regard to the orientation of the contour. If
* the total number of intersections is odd, the point will be
* filled.
*
* #GskFillRule is used to select how paths are filled, for example in
* gsk_fill_node_new(). Whether or not a point is included in the fill is
* determined by taking a ray from that point to infinity and looking
* at intersections with the path. The ray can be in any direction,
* as long as it doesn't pass through the end point of a segment
* or have a tricky intersection such as intersecting tangent to the path.
* (Note that filling is not actually implemented in this way. This
* is just a description of the rule that is applied.)
*
* New entries may be added in future versions.
**/
typedef enum {
GSK_FILL_RULE_WINDING,
GSK_FILL_RULE_EVEN_ODD
} GskFillRule;
/**
* @GSK_LINE_CAP_BUTT: Start and stop the line exactly at the start
* and end point
* @GSK_LINE_CAP_ROUND: Use a round ending, the center of the circle
* is the start or end point.
* @GSK_LINE_CAP_SQUARE: use squared ending, the center of the square
* is the start or end point.
*
* Specifies how to render the start and end points of contours or
* dashes when stroking.
*
* The default line cap style is %GSK_LINE_CAP_BUTT.
*/
typedef enum {
GSK_LINE_CAP_BUTT,
GSK_LINE_CAP_ROUND,
GSK_LINE_CAP_SQUARE
} GskLineCap;
/**
* GskLineJoin:
* @GSK_LINE_JOIN_MITER: Use a sharp, angled corner
* @GSK_LINE_JOIN_MITER_CLIP: Use a sharp, angled corner, at a distance
* @GSK_LINE_JOIN_ROUND: Use a round join, the center of the circle is
* the joint point
* @GSK_LINE_JOIN_BEVEL: Use a cut-off join, the join is cut off at half
* the line width from the joint point
*
* Specifies how to render the junction of two lines when stroking.
*
* See gsk_stroke_set_miter_limit() for details on the difference between
* @GSK_LINE_JOIN_MITER and @GSK_LINE_JOIN_MITER_CLIP.
*
* The default line join style is %GSK_LINE_JOIN_MITER.
**/
typedef enum {
GSK_LINE_JOIN_MITER,
GSK_LINE_JOIN_MITER_CLIP,
GSK_LINE_JOIN_ROUND,
GSK_LINE_JOIN_BEVEL
} GskLineJoin;
/**
* GskPathOperation:
* @GSK_PATH_MOVE: A move-to operation, with 1 point describing the
* target point.
* @GSK_PATH_LINE: A line-to operation, with 2 points describing the
* start and end point of a straight line.
* @GSK_PATH_CLOSE: A close operation ending the current contour with
* a line back to the starting point. Two points describe the start
* and end of the line.
* @GSK_PATH_CURVE: A curve-to operation describing a cubic Bézier curve
* with 4 points describing the start point, the two control points
* and the end point of the curve.
* @GSK_PATH_CONIC: A weighted quadratic bezier curve with 3 points
* describing the start point, control point and end point of the
* curve. A weight for the curve will be passed, too.
*
* Path operations can be used to approximate a #GskPath.
*
* More values may be added in the future.
**/
typedef enum {
GSK_PATH_MOVE,
GSK_PATH_CLOSE,
GSK_PATH_LINE,
GSK_PATH_CURVE,
GSK_PATH_CONIC,
} GskPathOperation;
/**
* GskSerializationError:
* @GSK_SERIALIZATION_UNSUPPORTED_FORMAT: The format can not be
@@ -247,5 +349,4 @@ typedef enum
GSK_GL_UNIFORM_TYPE_VEC4,
} GskGLUniformType;
#endif /* __GSK_TYPES_H__ */
+1 -1
View File
@@ -1149,7 +1149,7 @@ gsk_shader_args_builder_to_args (GskShaderArgsBuilder *builder)
}
/**
* gdk_shader_args_builder_free_to_args: (skip)
* gsk_shader_args_builder_free_to_args: (skip)
* @builder: a #GskShaderArgsBuilder
*
* Creates a new #GBytes args from the current state of the
+10
View File
@@ -32,10 +32,20 @@ G_BEGIN_DECLS
#define GSK_TYPE_SHADER_ARGS_BUILDER (gsk_shader_args_builder_get_type ())
/**
* GskShaderArgsBuilder:
*
* An object to build the uniforms data for a #GskGLShader.
*/
typedef struct _GskShaderArgsBuilder GskShaderArgsBuilder;
#define GSK_TYPE_GL_SHADER (gsk_gl_shader_get_type ())
/**
* GskGLShader:
*
* An object representing a GL shader program.
*/
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GskGLShader, gsk_gl_shader, GSK, GL_SHADER, GObject)
+1170
View File
File diff suppressed because it is too large Load Diff
+111
View File
@@ -0,0 +1,111 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GSK_PATH_H__
#define __GSK_PATH_H__
#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gsk/gsk.h> can be included directly."
#endif
#include <gsk/gsktypes.h>
G_BEGIN_DECLS
/**
* GskPathForeachFlags:
* @GSK_PATH_FOREACH_ALLOW_CURVE: Allow emission of %GSK_PATH_CURVE
* operations.
* @GSK_PATH_FOREACH_ALLOW_CONIC: Allow emission of %GSK_PATH_CONIC
* operations.
*
* Flags that can be passed to gsk_path_foreach() to enable additional
* features.
*
* By default, gsk_path_foreach() will only emit a path with all operations
* flattened to straight lines to allow for maximum compatibility. The only
* operations emitted will be %GSK_PATH_MOVE, %GSK_PATH_LINE and %GSK_PATH_CLOSE.
*/
typedef enum
{
GSK_PATH_FOREACH_ALLOW_CURVE = (1 << 0),
GSK_PATH_FOREACH_ALLOW_CONIC = (1 << 1)
} GskPathForeachFlags;
/**
* GskPathForeachFunc:
* @op: The operation to perform
* @pts: The points of the operation
* @n_pts: The number of points
* @weight: The weight for conic curves, or unused if not a conic curve.
* @user_data: The user data provided with the function
*
* Prototype of the callback to iterate throught the operations of
* a path.
*
* Returns: %TRUE to continue evaluating the path, %FALSE to
* immediately abort and not call the function again.
*/
typedef gboolean (* GskPathForeachFunc) (GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight,
gpointer user_data);
#define GSK_TYPE_PATH (gsk_path_get_type ())
GDK_AVAILABLE_IN_ALL
GType gsk_path_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_new_from_cairo (const cairo_path_t *path);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_ref (GskPath *self);
GDK_AVAILABLE_IN_ALL
void gsk_path_unref (GskPath *self);
GDK_AVAILABLE_IN_ALL
void gsk_path_print (GskPath *self,
GString *string);
GDK_AVAILABLE_IN_ALL
char * gsk_path_to_string (GskPath *self);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_parse (const char *string);
GDK_AVAILABLE_IN_ALL
void gsk_path_to_cairo (GskPath *self,
cairo_t *cr);
GDK_AVAILABLE_IN_ALL
gboolean gsk_path_is_empty (GskPath *path);
GDK_AVAILABLE_IN_ALL
gboolean gsk_path_get_bounds (GskPath *path,
graphene_rect_t *bounds);
GDK_AVAILABLE_IN_ALL
gboolean gsk_path_foreach (GskPath *path,
GskPathForeachFlags flags,
GskPathForeachFunc func,
gpointer user_data);
G_END_DECLS
#endif /* __GSK_PATH_H__ */
+897
View File
@@ -0,0 +1,897 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gskpathbuilder.h"
#include "gskpathprivate.h"
/**
* SECTION:gskpathbuilder
* @Title: Building paths
* @Short_description: Building paths of lines and curves
* @See_also: #GskPath, #GskPathMeasure
*
* This section describes how to construct #GskPath structures.
*
* A path is constructed like this:
*
* |[<!-- language="C" -->
* GskPath *
* construct_path (void)
* {
* GskPathBuilder *builder;
*
* builder = gsk_path_builder_new ();
*
* // add contours to the path here
*
* return gsk_path_builder_free_to_path (builder);
* ]|
*
* Adding contours to the path can be done in two ways.
* The easiest option is to use the `gsk_path_builder_add_*` group
* of functions that add predefined contours to the current path,
* either common shapes like gsk_path_builder_add_circle()
* or by adding from other paths like gsk_path_builder_add_path().
*
* The other option is to define each line and curve manually with
* the `gsk_path_builder_*_to` group of functions. You start with
* a call to gsk_path_builder_move_to() to set the starting point
* and then use multiple calls to any of the drawing functions to
* move the pen along the plane. Once you are done, you can call
* gsk_path_builder_close() to close the path by connecting it
* back with a line to the starting point.
* This is similar for how paths are drawn in Cairo.
*/
/**
* GskPathBuilder:
*
* A #GskPathBuilder struct is an opaque struct. It is meant to
* not be kept around and only be used to create new #GskPath
* objects.
*/
struct _GskPathBuilder
{
int ref_count;
GSList *contours; /* (reverse) list of already recorded contours */
GskPathFlags flags; /* flags for the current path */
graphene_point_t current_point; /* the point all drawing ops start from */
GArray *ops; /* operations for current contour - size == 0 means no current contour */
GArray *points; /* points for the operations */
};
G_DEFINE_BOXED_TYPE (GskPathBuilder,
gsk_path_builder,
gsk_path_builder_ref,
gsk_path_builder_unref)
/**
* gsk_path_builder_new:
*
* Create a new #GskPathBuilder object. The resulting builder
* would create an empty #GskPath. Use addition functions to add
* types to it.
*
* Returns: a new #GskPathBuilder
**/
GskPathBuilder *
gsk_path_builder_new (void)
{
GskPathBuilder *builder;
builder = g_slice_new0 (GskPathBuilder);
builder->ref_count = 1;
builder->ops = g_array_new (FALSE, FALSE, sizeof (gskpathop));
builder->points = g_array_new (FALSE, FALSE, sizeof (graphene_point_t));
/* Be explicit here */
builder->current_point = GRAPHENE_POINT_INIT (0, 0);
return builder;
}
/**
* gsk_path_builder_ref:
* @builder: a #GskPathBuilder
*
* Acquires a reference on the given @builder.
*
* This function is intended primarily for bindings. #GskPathBuilder objects
* should not be kept around.
*
* Returns: (transfer none): the given #GskPathBuilder with
* its reference count increased
*/
GskPathBuilder *
gsk_path_builder_ref (GskPathBuilder *builder)
{
g_return_val_if_fail (builder != NULL, NULL);
g_return_val_if_fail (builder->ref_count > 0, NULL);
builder->ref_count += 1;
return builder;
}
/* We're cheating here. Out pathops are relative to the NULL pointer,
* so that we can not care about the points GArray reallocating itself
* until we create the contour.
* This does however mean that we need to not use gsk_pathop_get_points()
* without offsetting the returned pointer.
*/
static inline gskpathop
gsk_pathop_encode_index (GskPathOperation op,
gsize index)
{
return gsk_pathop_encode (op, ((graphene_point_t *) NULL) + index);
}
static void
gsk_path_builder_ensure_current (GskPathBuilder *builder)
{
if (builder->ops->len != 0)
return;
builder->flags = GSK_PATH_FLAT;
g_array_append_vals (builder->ops, (gskpathop[1]) { gsk_pathop_encode_index (GSK_PATH_MOVE, 0) }, 1);
g_array_append_val (builder->points, builder->current_point);
}
static void
gsk_path_builder_append_current (GskPathBuilder *builder,
GskPathOperation op,
gsize n_points,
const graphene_point_t *points)
{
gsk_path_builder_ensure_current (builder);
g_array_append_vals (builder->ops, (gskpathop[1]) { gsk_pathop_encode_index (op, builder->points->len - 1) }, 1);
g_array_append_vals (builder->points, points, n_points);
builder->current_point = points[n_points - 1];
}
static void
gsk_path_builder_end_current (GskPathBuilder *builder)
{
GskContour *contour;
if (builder->ops->len == 0)
return;
contour = gsk_standard_contour_new (builder->flags,
(graphene_point_t *) builder->points->data,
builder->points->len,
(gskpathop *) builder->ops->data,
builder->ops->len,
(graphene_point_t *) builder->points->data - (graphene_point_t *) NULL);
g_array_set_size (builder->ops, 0);
g_array_set_size (builder->points, 0);
/* do this at the end to avoid inflooping when add_contour calls back here */
gsk_path_builder_add_contour (builder, contour);
}
static void
gsk_path_builder_clear (GskPathBuilder *builder)
{
gsk_path_builder_end_current (builder);
g_slist_free_full (builder->contours, g_free);
builder->contours = NULL;
}
/**
* gsk_path_builder_unref:
* @builder: a #GskPathBuilder
*
* Releases a reference on the given @builder.
*/
void
gsk_path_builder_unref (GskPathBuilder *builder)
{
g_return_if_fail (builder != NULL);
g_return_if_fail (builder->ref_count > 0);
builder->ref_count -= 1;
if (builder->ref_count > 0)
return;
gsk_path_builder_clear (builder);
g_array_unref (builder->ops);
g_array_unref (builder->points);
g_slice_free (GskPathBuilder, builder);
}
/**
* gsk_path_builder_free_to_path: (skip)
* @builder: a #GskPathBuilder
*
* Creates a new #GskPath from the current state of the
* given @builder, and frees the @builder instance.
*
* Returns: (transfer full): the newly created #GskPath
* with all the contours added to @builder
*/
GskPath *
gsk_path_builder_free_to_path (GskPathBuilder *builder)
{
GskPath *res;
g_return_val_if_fail (builder != NULL, NULL);
res = gsk_path_builder_to_path (builder);
gsk_path_builder_unref (builder);
return res;
}
/**
* gsk_path_builder_to_path:
* @builder: a #GskPathBuilder
*
* Creates a new #GskPath from the given @builder.
*
* The given #GskPathBuilder is reset once this function returns;
* you cannot call this function multiple times on the same @builder instance.
*
* This function is intended primarily for bindings. C code should use
* gsk_path_builder_free_to_path().
*
* Returns: (transfer full): the newly created #GskPath
* with all the contours added to @builder
*/
GskPath *
gsk_path_builder_to_path (GskPathBuilder *builder)
{
GskPath *path;
g_return_val_if_fail (builder != NULL, NULL);
gsk_path_builder_end_current (builder);
builder->contours = g_slist_reverse (builder->contours);
path = gsk_path_new_from_contours (builder->contours);
gsk_path_builder_clear (builder);
return path;
}
void
gsk_path_builder_add_contour (GskPathBuilder *builder,
GskContour *contour)
{
gsk_path_builder_end_current (builder);
builder->contours = g_slist_prepend (builder->contours, contour);
}
/**
* gsk_path_builder_get_current_point:
* @builder: a #GskPathBuilder
*
* Gets the current point. The current point is used for relative
* drawing commands and updated after every operation.
*
* When @builder is created, the default current point is set to (0, 0).
*
* Returns: (transfer none) The current point
**/
const graphene_point_t *
gsk_path_builder_get_current_point (GskPathBuilder *builder)
{
g_return_val_if_fail (builder != NULL, NULL);
return &builder->current_point;
}
/**
* gsk_path_builder_add_path:
* @builder: a #GskPathBuilder
* @path: (transfer none): the path to append
*
* Appends all of @path to @builder.
**/
void
gsk_path_builder_add_path (GskPathBuilder *builder,
GskPath *path)
{
gsize i;
g_return_if_fail (builder != NULL);
g_return_if_fail (path != NULL);
for (i = 0; i < gsk_path_get_n_contours (path); i++)
{
const GskContour *contour = gsk_path_get_contour (path, i);
gsk_path_builder_add_contour (builder, gsk_contour_dup (contour));
}
}
/**
* gsk_path_builder_add_rect:
* @builder: A #GskPathBuilder
* @rect: The rectangle to create a path for
*
* Creates a path representing the given rectangle.
*
* If the width or height of the rectangle is negative, the start
* point will be on the right or bottom, respectively.
*
* If the the width or height are 0, the path will be a closed
* horizontal or vertical line. If both are 0, it'll be a closed dot.
*
* Returns: a new #GskPath representing a rectangle
**/
void
gsk_path_builder_add_rect (GskPathBuilder *builder,
const graphene_rect_t *rect)
{
GskContour *contour;
g_return_if_fail (builder != NULL);
contour = gsk_rect_contour_new (rect);
gsk_path_builder_add_contour (builder, contour);
gsk_contour_get_start_end (contour, NULL, &builder->current_point);
}
/**
* gsk_path_builder_add_rounded_rect:
* @self: a #GskPathBuilder
* @rect: the rounded rect
*
* Adds @rect as a new contour to the path built in @self.
**/
void
gsk_path_builder_add_rounded_rect (GskPathBuilder *self,
const GskRoundedRect *rect)
{
const float weight = sqrt(0.5f);
g_return_if_fail (self != NULL);
g_return_if_fail (rect != NULL);
gsk_path_builder_move_to (self,
rect->bounds.origin.x + rect->corner[GSK_CORNER_TOP_LEFT].width,
rect->bounds.origin.y);
/* top */
gsk_path_builder_line_to (self,
rect->bounds.origin.x + rect->bounds.size.width - rect->corner[GSK_CORNER_TOP_RIGHT].width,
rect->bounds.origin.y);
/* topright corner */
gsk_path_builder_conic_to (self,
rect->bounds.origin.x + rect->bounds.size.width,
rect->bounds.origin.y,
rect->bounds.origin.x + rect->bounds.size.width,
rect->bounds.origin.y + rect->corner[GSK_CORNER_TOP_RIGHT].height,
weight);
/* right */
gsk_path_builder_line_to (self,
rect->bounds.origin.x + rect->bounds.size.width,
rect->bounds.origin.y + rect->bounds.size.height - rect->corner[GSK_CORNER_BOTTOM_RIGHT].height);
/* bottomright corner */
gsk_path_builder_conic_to (self,
rect->bounds.origin.x + rect->bounds.size.width,
rect->bounds.origin.y + rect->bounds.size.height,
rect->bounds.origin.x + rect->bounds.size.width - rect->corner[GSK_CORNER_BOTTOM_RIGHT].width,
rect->bounds.origin.y + rect->bounds.size.height,
weight);
/* bottom */
gsk_path_builder_line_to (self,
rect->bounds.origin.x + rect->corner[GSK_CORNER_BOTTOM_LEFT].width,
rect->bounds.origin.y + rect->bounds.size.height);
/* bottomleft corner */
gsk_path_builder_conic_to (self,
rect->bounds.origin.x,
rect->bounds.origin.y + rect->bounds.size.height,
rect->bounds.origin.x,
rect->bounds.origin.y + rect->bounds.size.height - rect->corner[GSK_CORNER_BOTTOM_LEFT].height,
weight);
/* left */
gsk_path_builder_line_to (self,
rect->bounds.origin.x,
rect->bounds.origin.y + rect->corner[GSK_CORNER_TOP_LEFT].height);
/* topleft corner */
gsk_path_builder_conic_to (self,
rect->bounds.origin.x,
rect->bounds.origin.y,
rect->bounds.origin.x + rect->corner[GSK_CORNER_TOP_LEFT].width,
rect->bounds.origin.y,
weight);
/* done */
gsk_path_builder_close (self);
}
/**
* gsk_path_builder_add_circle:
* @builder: a #GskPathBuilder
* @center: the center of the circle
* @radius: the radius of the circle
*
* Adds a circle with the @center and @radius.
**/
void
gsk_path_builder_add_circle (GskPathBuilder *builder,
const graphene_point_t *center,
float radius)
{
GskContour *contour;
g_return_if_fail (builder != NULL);
g_return_if_fail (center != NULL);
g_return_if_fail (radius > 0);
contour = gsk_circle_contour_new (center, radius, 0, 360);
gsk_path_builder_add_contour (builder, contour);
}
/**
* gsk_path_builder_move_to:
* @builder: a #GskPathBuilder
* @x: x coordinate
* @y: y coordinate
*
* Starts a new contour by placing the pen at @x, @y.
*
* If gsk_path_builder_move_to() is called twice in succession, the first
* call will result in a contour made up of a single point. The second call
* will start a new contour.
**/
void
gsk_path_builder_move_to (GskPathBuilder *builder,
float x,
float y)
{
g_return_if_fail (builder != NULL);
gsk_path_builder_end_current (builder);
builder->current_point = GRAPHENE_POINT_INIT(x, y);
gsk_path_builder_ensure_current (builder);
}
/**
* gsk_path_builder_rel_move_to:
* @builder: a #GskPathBuilder
* @x: x offset
* @y: y offset
*
* Starts a new contour by placing the pen at @x, @y relative to the current
* point.
*
* This is the relative version of gsk_path_builder_move_to().
**/
void
gsk_path_builder_rel_move_to (GskPathBuilder *builder,
float x,
float y)
{
g_return_if_fail (builder != NULL);
gsk_path_builder_move_to (builder,
builder->current_point.x + x,
builder->current_point.y + y);
}
/**
* gsk_path_builder_line_to:
* @builder: a #GskPathBuilder
* @x: x coordinate
* @y: y coordinate
*
* Draws a line from the current point to @x, @y and makes it the new current
* point.
**/
void
gsk_path_builder_line_to (GskPathBuilder *builder,
float x,
float y)
{
g_return_if_fail (builder != NULL);
/* skip the line if it goes to the same point */
if (graphene_point_equal (&builder->current_point,
&GRAPHENE_POINT_INIT (x, y)))
return;
gsk_path_builder_append_current (builder,
GSK_PATH_LINE,
1, (graphene_point_t[1]) {
GRAPHENE_POINT_INIT (x, y)
});
}
/**
* gsk_path_builder_line_to:
* @builder: a #GskPathBuilder
* @x: x offset
* @y: y offset
*
* Draws a line from the current point to a point offset to it by @x, @y
* and makes it the new current point.
*
* This is the relative version of gsk_path_builder_line_to().
**/
void
gsk_path_builder_rel_line_to (GskPathBuilder *builder,
float x,
float y)
{
g_return_if_fail (builder != NULL);
gsk_path_builder_line_to (builder,
builder->current_point.x + x,
builder->current_point.y + y);
}
/**
* gsk_path_builder_curve_to:
* @builder: a #GskPathBuilder
* @x1: x coordinate of first control point
* @y1: y coordinate of first control point
* @x2: x coordinate of second control point
* @y2: y coordinate of second control point
* @x3: x coordinate of the end of the curve
* @y3: y coordinate of the end of the curve
*
* Adds a [cubic Bézier curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve)
* from the current point to @x3, @y3 with @x1, @y1 and @x2, @y2 as the control
* points.
*
* After this, @x3, @y3 will be the new current point.
**/
void
gsk_path_builder_curve_to (GskPathBuilder *builder,
float x1,
float y1,
float x2,
float y2,
float x3,
float y3)
{
g_return_if_fail (builder != NULL);
builder->flags &= ~GSK_PATH_FLAT;
gsk_path_builder_append_current (builder,
GSK_PATH_CURVE,
3, (graphene_point_t[3]) {
GRAPHENE_POINT_INIT (x1, y1),
GRAPHENE_POINT_INIT (x2, y2),
GRAPHENE_POINT_INIT (x3, y3)
});
}
/**
* gsk_path_builder_rel_curve_to:
* @builder: a #GskPathBuilder
* @x1: x offset of first control point
* @y1: y offset of first control point
* @x2: x offset of second control point
* @y2: y offset of second control point
* @x3: x offset of the end of the curve
* @y3: y offset of the end of the curve
*
* Adds a [cubic Bézier curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve)
* from the current point to @x3, @y3 with @x1, @y1 and @x2, @y2 as the control
* points. All coordinates are given relative to the current point.
*
* This is the relative version of gsk_path_builder_curve_to().
**/
void
gsk_path_builder_rel_curve_to (GskPathBuilder *builder,
float x1,
float y1,
float x2,
float y2,
float x3,
float y3)
{
g_return_if_fail (builder != NULL);
gsk_path_builder_curve_to (builder,
builder->current_point.x + x1,
builder->current_point.y + y1,
builder->current_point.x + x2,
builder->current_point.y + y2,
builder->current_point.x + x3,
builder->current_point.y + y3);
}
/**
* gsk_path_builder_conic_to:
* @builder: a #GskPathBuilder
* @x1: x coordinate of control point
* @y1: y coordinate of control point
* @x2: x coordinate of the end of the curve
* @y2: y coordinate of the end of the curve
* @weight: weight of the curve
*
* Adds a [conic curve](https://en.wikipedia.org/wiki/Non-uniform_rational_B-spline)
* from the current point to @x2, @y2 with the given
* @weight and @x1, @y1 as the single control point.
*
* Conic curves can be used to draw ellipses and circles.
*
* After this, @x2, @y2 will be the new current point.
**/
void
gsk_path_builder_conic_to (GskPathBuilder *builder,
float x1,
float y1,
float x2,
float y2,
float weight)
{
g_return_if_fail (builder != NULL);
g_return_if_fail (weight >= 0);
builder->flags &= ~GSK_PATH_FLAT;
gsk_path_builder_append_current (builder,
GSK_PATH_CONIC,
3, (graphene_point_t[3]) {
GRAPHENE_POINT_INIT (x1, y1),
GRAPHENE_POINT_INIT (weight, 0),
GRAPHENE_POINT_INIT (x2, y2)
});
}
/**
* gsk_path_builder_rel_conic_to:
* @builder: a #GskPathBuilder
* @x1: x offset of control point
* @y1: y offset of control point
* @x2: x offset of the end of the curve
* @y2: y offset of the end of the curve
* @weight: weight of the curve
*
* Adds a [conic curve](https://en.wikipedia.org/wiki/Non-uniform_rational_B-spline)
* from the current point to @x2, @y2 with the given
* @weight and @x1, @y1 as the single control point.
*
* This is the relative version of gsk_path_builder_conic_to().
**/
void
gsk_path_builder_rel_conic_to (GskPathBuilder *builder,
float x1,
float y1,
float x2,
float y2,
float weight)
{
g_return_if_fail (builder != NULL);
g_return_if_fail (weight >= 0);
gsk_path_builder_conic_to (builder,
builder->current_point.x + x1,
builder->current_point.y + y1,
builder->current_point.x + x2,
builder->current_point.y + y2,
weight);
}
/**
* gsk_path_builder_close:
* @builder: a #GskPathBuilder
*
* Ends the current contour with a line back to the start point.
*
* Note that this is different from calling gsk_path_builder_line_to()
* with the start point in that the contour will be closed. A closed
* contour behaves different from an open one when stroking its start
* and end point are considered connected, so they will be joined
* via the line join, and not ended with line caps.
**/
void
gsk_path_builder_close (GskPathBuilder *builder)
{
g_return_if_fail (builder != NULL);
if (builder->ops->len == 0)
return;
builder->flags |= GSK_PATH_CLOSED;
gsk_path_builder_append_current (builder,
GSK_PATH_CLOSE,
1, (graphene_point_t[1]) {
g_array_index (builder->points, graphene_point_t, 0)
});
gsk_path_builder_end_current (builder);
}
static void
arc_segment (GskPathBuilder *builder,
double cx,
double cy,
double rx,
double ry,
double sin_phi,
double cos_phi,
double sin_th0,
double cos_th0,
double sin_th1,
double cos_th1,
double t)
{
double x1, y1, x2, y2, x3, y3;
x1 = rx * (cos_th0 - t * sin_th0);
y1 = ry * (sin_th0 + t * cos_th0);
x3 = rx * cos_th1;
y3 = ry * sin_th1;
x2 = x3 + rx * (t * sin_th1);
y2 = y3 + ry * (-t * cos_th1);
gsk_path_builder_curve_to (builder,
cx + cos_phi * x1 - sin_phi * y1,
cy + sin_phi * x1 + cos_phi * y1,
cx + cos_phi * x2 - sin_phi * y2,
cy + sin_phi * x2 + cos_phi * y2,
cx + cos_phi * x3 - sin_phi * y3,
cy + sin_phi * x3 + cos_phi * y3);
}
void
gsk_path_builder_svg_arc_to (GskPathBuilder *builder,
float rx,
float ry,
float x_axis_rotation,
gboolean large_arc,
gboolean positive_sweep,
float x,
float y)
{
graphene_point_t *current;
double x1, y1, x2, y2;
double phi, sin_phi, cos_phi;
double mid_x, mid_y;
double lambda;
double d;
double k;
double x1_, y1_;
double cx_, cy_;
double cx, cy;
double ux, uy, u_len;
double cos_theta1, theta1;
double vx, vy, v_len;
double dp_uv;
double cos_delta_theta, delta_theta;
int i, n_segs;
double d_theta, theta;
double sin_th0, cos_th0;
double sin_th1, cos_th1;
double th_half;
double t;
if (builder->points->len > 0)
{
current = &g_array_index (builder->points, graphene_point_t, builder->points->len - 1);
x1 = current->x;
y1 = current->y;
}
else
{
x1 = 0;
y1 = 0;
}
x2 = x;
y2 = y;
phi = x_axis_rotation * M_PI / 180.0;
sincos (phi, &sin_phi, &cos_phi);
rx = fabs (rx);
ry = fabs (ry);
mid_x = (x1 - x2) / 2;
mid_y = (y1 - y2) / 2;
x1_ = cos_phi * mid_x + sin_phi * mid_y;
y1_ = - sin_phi * mid_x + cos_phi * mid_y;
lambda = (x1_ / rx) * (x1_ / rx) + (y1_ / ry) * (y1_ / ry);
if (lambda > 1)
{
lambda = sqrt (lambda);
rx *= lambda;
ry *= lambda;
}
d = (rx * y1_) * (rx * y1_) + (ry * x1_) * (ry * x1_);
if (d == 0)
return;
k = sqrt (fabs ((rx * ry) * (rx * ry) / d - 1.0));
if (positive_sweep == large_arc)
k = -k;
cx_ = k * rx * y1_ / ry;
cy_ = -k * ry * x1_ / rx;
cx = cos_phi * cx_ - sin_phi * cy_ + (x1 + x2) / 2;
cy = sin_phi * cx_ + cos_phi * cy_ + (y1 + y2) / 2;
ux = (x1_ - cx_) / rx;
uy = (y1_ - cy_) / ry;
u_len = sqrt (ux * ux + uy * uy);
if (u_len == 0)
return;
cos_theta1 = CLAMP (ux / u_len, -1, 1);
theta1 = acos (cos_theta1);
if (uy < 0)
theta1 = - theta1;
vx = (- x1_ - cx_) / rx;
vy = (- y1_ - cy_) / ry;
v_len = sqrt (vx * vx + vy * vy);
if (v_len == 0)
return;
dp_uv = ux * vx + uy * vy;
cos_delta_theta = CLAMP (dp_uv / (u_len * v_len), -1, 1);
delta_theta = acos (cos_delta_theta);
if (ux * vy - uy * vx < 0)
delta_theta = - delta_theta;
if (positive_sweep && delta_theta < 0)
delta_theta += 2 * M_PI;
else if (!positive_sweep && delta_theta > 0)
delta_theta -= 2 * M_PI;
n_segs = ceil (fabs (delta_theta / (M_PI_2 + 0.001)));
d_theta = delta_theta / n_segs;
theta = theta1;
sincos (theta1, &sin_th1, &cos_th1);
th_half = d_theta / 2;
t = (8.0 / 3.0) * sin (th_half / 2) * sin (th_half / 2) / sin (th_half);
for (i = 0; i < n_segs; i++)
{
theta = theta1;
theta1 = theta + d_theta;
sin_th0 = sin_th1;
cos_th0 = cos_th1;
sincos (theta1, &sin_th1, &cos_th1);
arc_segment (builder,
cx, cy, rx, ry,
sin_phi, cos_phi,
sin_th0, cos_th0,
sin_th1, cos_th1,
t);
}
}
+124
View File
@@ -0,0 +1,124 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GSK_PATH_BUILDER_H__
#define __GSK_PATH_BUILDER_H__
#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gsk/gsk.h> can be included directly."
#endif
#include <gsk/gskroundedrect.h>
#include <gsk/gsktypes.h>
G_BEGIN_DECLS
#define GSK_TYPE_PATH_BUILDER (gsk_path_builder_get_type ())
GDK_AVAILABLE_IN_ALL
GType gsk_path_builder_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GskPathBuilder * gsk_path_builder_new (void);
GDK_AVAILABLE_IN_ALL
GskPathBuilder * gsk_path_builder_ref (GskPathBuilder *builder);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_unref (GskPathBuilder *builder);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_builder_free_to_path (GskPathBuilder *builder) G_GNUC_WARN_UNUSED_RESULT;
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_builder_to_path (GskPathBuilder *builder) G_GNUC_WARN_UNUSED_RESULT;
GDK_AVAILABLE_IN_ALL
const graphene_point_t *gsk_path_builder_get_current_point (GskPathBuilder *builder);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_add_path (GskPathBuilder *builder,
GskPath *path);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_add_rect (GskPathBuilder *builder,
const graphene_rect_t *rect);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_add_rounded_rect (GskPathBuilder *builder,
const GskRoundedRect *rect);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_add_circle (GskPathBuilder *builder,
const graphene_point_t *center,
float radius);
/* next function implemented in gskpathmeasure.c */
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_add_segment (GskPathBuilder *builder,
GskPathMeasure *self,
float start,
float end);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_move_to (GskPathBuilder *builder,
float x,
float y);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_rel_move_to (GskPathBuilder *builder,
float x,
float y);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_line_to (GskPathBuilder *builder,
float x,
float y);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_rel_line_to (GskPathBuilder *builder,
float x,
float y);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_curve_to (GskPathBuilder *builder,
float x1,
float y1,
float x2,
float y2,
float x3,
float y3);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_rel_curve_to (GskPathBuilder *builder,
float x1,
float y1,
float x2,
float y2,
float x3,
float y3);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_conic_to (GskPathBuilder *builder,
float x1,
float y1,
float x2,
float y2,
float weight);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_rel_conic_to (GskPathBuilder *builder,
float x1,
float y1,
float x2,
float y2,
float weight);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_close (GskPathBuilder *builder);
G_END_DECLS
#endif /* __GSK_PATH_BUILDER_H__ */
+291
View File
@@ -0,0 +1,291 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gskpathdashprivate.h"
#include "gskcontourprivate.h"
#include "gskcurveprivate.h"
#include "gskpathprivate.h"
#include "gskstrokeprivate.h"
typedef struct
{
float offset; /* how much of the current dash we've spent */
gsize dash_index; /* goes from 0 to n_dash * 2, so we don't have to care about on/off
for uneven dashes */
gboolean on; /* If we're currently dashing or not */
gboolean may_close; /* TRUE if we haven't turned the dash off in this contour */
gboolean needs_move_to; /* If we have emitted the initial move_to() yet */
enum {
NORMAL, /* no special behavior required */
SKIP, /* skip the next dash */
ONLY, /* only do the first dash */
DONE /* done with the first dash */
} first_dash_behavior; /* How to handle the first dash in the loop. We loop closed contours
twice to make sure the first dash and the last dash can get joined */
GskCurve curve; /* Curve we are currently processing */
float collect_start; /* We're collecting multiple line segments when decomposing. */
float collect_length; /* No need to emit a curve for every line segment when the dash is long enough. */
/* from the stroke */
float *dash;
gsize n_dash;
float dash_length;
float dash_offset;
float tolerance;
GskPathForeachFunc func;
gpointer user_data;
} GskPathDash;
static void
gsk_path_dash_setup (GskPathDash *self)
{
self->offset = fmodf (self->dash_offset, 2 * self->dash_length);
self->dash_index = 0;
self->on = TRUE;
self->may_close = TRUE;
while (self->offset > self->dash[self->dash_index % self->n_dash])
{
self->offset -= self->dash[self->dash_index % self->n_dash];
self->dash_index++;
self->on = !self->on;
}
if (self->first_dash_behavior != ONLY)
self->needs_move_to = TRUE;
}
static gboolean
gsk_path_dash_ensure_move_to (GskPathDash *self,
const graphene_point_t *pt)
{
if (!self->needs_move_to)
return TRUE;
if (!self->func (GSK_PATH_MOVE, pt, 1, 0, self->user_data))
return FALSE;
self->needs_move_to = FALSE;
return TRUE;
}
static gboolean
gsk_path_dash_add_line_segment (const graphene_point_t *start,
const graphene_point_t *end,
float t_start,
float t_end,
gpointer user_data)
{
GskPathDash *self = user_data;
float remaining, length, t_step;
length = graphene_point_distance (start, end, NULL, NULL);
if (self->collect_length)
{
t_start = self->collect_start;
length += self->collect_length;
self->collect_length = 0;
}
t_step = t_end - t_start;
remaining = length;
while (remaining)
{
float piece;
if (self->offset + remaining <= self->dash[self->dash_index % self->n_dash])
{
/* try collecting multiple line segments */
if (t_end < 1.0)
{
self->collect_start = t_start + t_step * (length - remaining) / length;
self->collect_length = remaining;
return TRUE;
}
piece = remaining;
}
else
piece = self->dash[self->dash_index % self->n_dash] - self->offset;
if (self->on)
{
if (self->first_dash_behavior != SKIP)
{
GskCurve segment;
if (piece)
{
gsk_curve_segment (&self->curve,
t_start + t_step * (length - remaining) / length,
t_start + t_step * (length - (remaining - piece)) / length,
&segment);
if (!gsk_path_dash_ensure_move_to (self, gsk_curve_get_start_point (&segment)))
return FALSE;
if (!gsk_pathop_foreach (gsk_curve_pathop (&segment), self->func, self->user_data))
return FALSE;
}
else
{
graphene_point_t p;
gsk_curve_get_point (&self->curve, t_start + t_step * (length - remaining) / length, &p);
if (!gsk_path_dash_ensure_move_to (self, &p))
return FALSE;
}
}
}
else
{
self->may_close = FALSE;
if (self->first_dash_behavior == ONLY)
{
self->first_dash_behavior = DONE;
return FALSE;
}
self->first_dash_behavior = NORMAL;
}
if (self->offset + remaining <= self->dash[self->dash_index % self->n_dash])
{
self->offset += remaining;
remaining = 0;
}
else
{
remaining -= piece;
self->offset = 0;
self->dash_index++;
self->dash_index %= 2 * self->n_dash;
self->on = !self->on;
self->needs_move_to = TRUE;
}
}
return TRUE;
}
static gboolean
gsk_path_dash_foreach (GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight,
gpointer user_data)
{
GskPathDash *self = user_data;
switch (op)
{
case GSK_PATH_MOVE:
gsk_path_dash_setup (self);
break;
case GSK_PATH_CLOSE:
if (self->may_close)
{
if (graphene_point_equal (&pts[0], &pts[1]))
return self->func (GSK_PATH_CLOSE, pts, 2, 0, self->user_data);
}
else
op = GSK_PATH_LINE;
G_GNUC_FALLTHROUGH;
case GSK_PATH_LINE:
case GSK_PATH_CURVE:
case GSK_PATH_CONIC:
gsk_curve_init_foreach (&self->curve, op, pts, n_pts, weight);
if (!gsk_curve_decompose (&self->curve, self->tolerance, gsk_path_dash_add_line_segment, self))
return FALSE;
break;
default:
g_assert_not_reached ();
break;
}
return TRUE;
}
gboolean
gsk_contour_dash (const GskContour *contour,
GskStroke *stroke,
float tolerance,
GskPathForeachFunc func,
gpointer user_data)
{
GskPathDash self = {
.offset = 0,
.dash = stroke->dash,
.n_dash = stroke->n_dash,
.dash_length = stroke->dash_length,
.dash_offset = stroke->dash_offset,
.tolerance = tolerance,
.func = func,
.user_data = user_data
};
gboolean is_closed = gsk_contour_get_flags (contour) & GSK_PATH_CLOSED ? TRUE : FALSE;
self.first_dash_behavior = is_closed ? SKIP : NORMAL;
if (!gsk_contour_foreach (contour, tolerance, gsk_path_dash_foreach, &self))
return FALSE;
if (is_closed)
{
if (self.first_dash_behavior == NORMAL)
self.first_dash_behavior = ONLY;
else
self.first_dash_behavior = NORMAL;
self.needs_move_to = !self.on;
if (!gsk_contour_foreach (contour, tolerance, gsk_path_dash_foreach, &self) &&
self.first_dash_behavior != DONE)
return FALSE;
}
return TRUE;
}
gboolean
gsk_path_dash (GskPath *path,
GskStroke *stroke,
float tolerance,
GskPathForeachFunc func,
gpointer user_data)
{
gsize i;
/* Dashing disabled, no need to do any work */
if (stroke->dash_length <= 0)
return gsk_path_foreach (path, -1, func, user_data);
for (i = 0; i < gsk_path_get_n_contours (path); i++)
{
if (!gsk_contour_dash (gsk_path_get_contour (path, i), stroke, tolerance, func, user_data))
return FALSE;
}
return TRUE;
}
+50
View File
@@ -0,0 +1,50 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GSK_PATH_DASH_PRIVATE_H__
#define __GSK_PATH_DASH_PRIVATE_H__
#include <gsk/gskpath.h>
G_BEGIN_DECLS
GDK_AVAILABLE_IN_ALL
gboolean gsk_path_dash (GskPath *path,
GskStroke *stroke,
float tolerance,
GskPathForeachFunc func,
gpointer user_data);
#ifdef GTK_COMPILATION
#include "gskcontourprivate.h"
gboolean gsk_contour_dash (const GskContour *contour,
GskStroke *stroke,
float tolerance,
GskPathForeachFunc func,
gpointer user_data);
#endif /* GTK_COMPILATION */
G_END_DECLS
#endif /* __GSK_PATH_DASH_PRIVATE_H__ */
+506
View File
@@ -0,0 +1,506 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gskpathmeasure.h"
#include "gskpathbuilder.h"
#include "gskpathprivate.h"
/**
* SECTION:gskpathmeasure
* @Title: PathMeasure
* @Short_description: Measuring operations on paths
* @See_also: #GskPath
*
* #GskPathMeasure is an object that allows measuring operations on #GskPaths.
* These operations are useful when implementing animations.
*/
typedef struct _GskContourMeasure GskContourMeasure;
struct _GskContourMeasure
{
float length;
gpointer contour_data;
};
struct _GskPathMeasure
{
/*< private >*/
guint ref_count;
GskPath *path;
float tolerance;
float length;
gsize n_contours;
GskContourMeasure measures[];
};
/**
* GskPathMeasure:
*
* A #GskPathMeasure struct is a reference counted struct
* and should be treated as opaque.
*/
G_DEFINE_BOXED_TYPE (GskPathMeasure, gsk_path_measure,
gsk_path_measure_ref,
gsk_path_measure_unref)
/**
* gsk_path_measure_new:
* @path: the path to measure
*
* Creates a measure object for the given @path.
*
* Returns: a new #GskPathMeasure representing @path
**/
GskPathMeasure *
gsk_path_measure_new (GskPath *path)
{
return gsk_path_measure_new_with_tolerance (path, GSK_PATH_TOLERANCE_DEFAULT);
}
/**
* gsk_path_measure_new_with_tolerance:
* @path: the path to measure
* @tolerance: the tolerance for measuring operations
*
* Creates a measure object for the given @path and @tolerance.
*
* Returns: a new #GskPathMeasure representing @path
**/
GskPathMeasure *
gsk_path_measure_new_with_tolerance (GskPath *path,
float tolerance)
{
GskPathMeasure *self;
gsize i, n_contours;
g_return_val_if_fail (path != NULL, NULL);
g_return_val_if_fail (tolerance > 0, NULL);
n_contours = gsk_path_get_n_contours (path);
self = g_malloc0 (sizeof (GskPathMeasure) + n_contours * sizeof (GskContourMeasure));
self->ref_count = 1;
self->path = gsk_path_ref (path);
self->tolerance = tolerance;
self->n_contours = n_contours;
for (i = 0; i < n_contours; i++)
{
self->measures[i].contour_data = gsk_contour_init_measure (gsk_path_get_contour (path, i),
self->tolerance,
&self->measures[i].length);
self->length += self->measures[i].length;
}
return self;
}
/**
* gsk_path_measure_ref:
* @self: a #GskPathMeasure
*
* Increases the reference count of a #GskPathMeasure by one.
*
* Returns: the passed in #GskPathMeasure.
**/
GskPathMeasure *
gsk_path_measure_ref (GskPathMeasure *self)
{
g_return_val_if_fail (self != NULL, NULL);
self->ref_count++;
return self;
}
/**
* gsk_path_measure_unref:
* @self: a #GskPathMeasure
*
* Decreases the reference count of a #GskPathMeasure by one.
* If the resulting reference count is zero, frees the path_measure.
**/
void
gsk_path_measure_unref (GskPathMeasure *self)
{
gsize i;
g_return_if_fail (self != NULL);
g_return_if_fail (self->ref_count > 0);
self->ref_count--;
if (self->ref_count > 0)
return;
for (i = 0; i < self->n_contours; i++)
{
gsk_contour_free_measure (gsk_path_get_contour (self->path, i),
self->measures[i].contour_data);
}
gsk_path_unref (self->path);
g_free (self);
}
/**
* gsk_path_measure_get_path:
* @self: a #GskPathMeasure
*
* Returns the path that the measure was created for.
*
* Returns: (transfer none): the path of @self
*/
GskPath *
gsk_path_measure_get_path (GskPathMeasure *self)
{
return self->path;
}
/**
* gsk_path_measure_get_tolerance:
* @self: a #GskPathMeasure
*
* Returns the tolerance that the measure was created with.
*
* Returns: the tolerance of @self
*/
float
gsk_path_measure_get_tolerance (GskPathMeasure *self)
{
return self->tolerance;
}
/**
* gsk_path_measure_get_length:
* @self: a #GskPathMeasure
*
* Gets the length of the path being measured.
*
* The length is cached, so this function does not do any work.
*
* Returns: The length of the path measured by @self
**/
float
gsk_path_measure_get_length (GskPathMeasure *self)
{
g_return_val_if_fail (self != NULL, 0);
return self->length;
}
static float
gsk_path_measure_clamp_distance (GskPathMeasure *self,
float distance)
{
if (isnan (distance))
return 0;
return CLAMP (distance, 0, self->length);
}
/**
* gsk_path_measure_get_point:
* @self: a #GskPathMeasure
* @distance: distance into the path
* @pos: (out) (optional) (caller-allocates): The coordinates
* of the position at @distance
* @tangent: (out) (optional) (caller-allocates): The tangent
* to the position at @distance
*
* Calculates the coordinates and tangent of the point @distance
* units into the path. The value will be clamped to the length
* of the path.
*
* If the point is a discontinuous edge in the path, the returned
* point and tangent will describe the line starting at that point
* going forward.
*
* If @self describes an empty path, the returned point will be
* set to `(0, 0)` and the tangent will be the x axis or `(1, 0)`.
**/
void
gsk_path_measure_get_point (GskPathMeasure *self,
float distance,
graphene_point_t *pos,
graphene_vec2_t *tangent)
{
gsize i;
g_return_if_fail (self != NULL);
if (pos == NULL && tangent == NULL)
return;
distance = gsk_path_measure_clamp_distance (self, distance);
for (i = 0; i < self->n_contours; i++)
{
if (distance < self->measures[i].length)
break;
distance -= self->measures[i].length;
}
/* weird corner cases */
if (i == self->n_contours)
{
/* the empty path goes here */
if (self->n_contours == 0)
{
if (pos)
graphene_point_init (pos, 0.f, 0.f);
if (tangent)
graphene_vec2_init (tangent, 1.f, 0.f);
return;
}
/* rounding errors can make this happen */
i = self->n_contours - 1;
distance = self->measures[i].length;
}
gsk_contour_get_point (gsk_path_get_contour (self->path, i),
self->measures[i].contour_data,
distance,
pos,
tangent);
}
/**
* gsk_path_measure_get_closest_point:
* @self: a #GskPathMeasure
* @point: the point to fond the closest point to
* @out_pos: (out) (optional) (caller-allocates): return location
* for the closest point
*
* Gets the point on the path that is closest to @point.
*
* If the path being measured is empty, return 0 and set
* @out_pos to (0, 0).
*
* This is a simpler and slower version of
* gsk_path_measure_get_closest_point_full(). Use that one if you
* need more control.
*
* Returns: The offset into the path of the closest point
**/
float
gsk_path_measure_get_closest_point (GskPathMeasure *self,
const graphene_point_t *point,
graphene_point_t *out_pos)
{
float result;
g_return_val_if_fail (self != NULL, 0.0f);
if (gsk_path_measure_get_closest_point_full (self,
point,
INFINITY,
&result,
out_pos,
NULL,
NULL))
return result;
if (out_pos)
*out_pos = GRAPHENE_POINT_INIT (0, 0);
return 0;
}
/**
* gsk_path_measure_get_closest_point_full:
* @self: a #GskPathMeasure
* @point: the point to fond the closest point to
* @threshold: The maximum allowed distance between the path and @point.
* Use INFINITY to look for any point.
* @out_distance: (out) (optional) (caller-allocates): The
* distance between the found closest point on the path and the given
* @point.
* @out_pos: (out) (optional) (caller-allocates): return location
* for the closest point
* @out_offset: (out) (optional) (caller-allocates): The offset into
* the path of the found point
* @out_tangent: (out) (optional) (caller-allocates): return location for
* the tangent at the closest point
*
* Gets the point on the path that is closest to @point. If no point on
* path is closer to @point than @threshold, return %FALSE.
*
* Returns: %TRUE if a point was found, %FALSE otherwise.
**/
gboolean
gsk_path_measure_get_closest_point_full (GskPathMeasure *self,
const graphene_point_t *point,
float threshold,
float *out_distance,
graphene_point_t *out_pos,
float *out_offset,
graphene_vec2_t *out_tangent)
{
gboolean result;
gsize i;
float distance, length;
g_return_val_if_fail (self != NULL, FALSE);
g_return_val_if_fail (point != NULL, FALSE);
result = FALSE;
length = 0;
for (i = 0; i < self->n_contours; i++)
{
if (gsk_contour_get_closest_point (gsk_path_get_contour (self->path, i),
self->measures[i].contour_data,
self->tolerance,
point,
threshold,
&distance,
out_pos,
out_offset,
out_tangent))
{
result = TRUE;
if (out_offset)
*out_offset += length;
if (distance < self->tolerance)
break;
threshold = distance - self->tolerance;
}
length += self->measures[i].length;
}
if (result && out_distance)
*out_distance = distance;
return result;
}
/**
* gsk_path_measure_in_fill:
* @self: a #GskPathMeasure
* @point: the point to test
* @fill_rule: the fill rule to follow
*
* Returns whether the given point is inside the area that would be
* affected if the path of @self was filled according to @fill_rule.
*
* Returns: %TRUE if @point is inside
*/
gboolean
gsk_path_measure_in_fill (GskPathMeasure *self,
graphene_point_t *point,
GskFillRule fill_rule)
{
int winding = 0;
gboolean on_edge = FALSE;
int i;
for (i = 0; i < self->n_contours; i++)
{
winding += gsk_contour_get_winding (gsk_path_get_contour (self->path, i),
self->measures[i].contour_data,
point,
&on_edge);
if (on_edge)
return TRUE;
}
switch (fill_rule)
{
case GSK_FILL_RULE_EVEN_ODD:
return winding & 1;
case GSK_FILL_RULE_WINDING:
return winding != 0;
default:
g_assert_not_reached ();
}
}
/**
* gsk_path_builder_add_segment:
* @self: a #GskPathBuilder
* @measure: the #GskPathMeasure to take the segment to
* @start: start distance into the path
* @end: end distance into the path
*
* Adds to @self the segment of @measure inbetween @start and @end.
*
* The distances are given relative to the length of @measure's path,
* from 0 for the beginning of the path to
* gsk_path_measure_get_length() for the end of the path. The values
* will be clamped to that range.
*
* If @start >= @end after clamping, no path will be added.
**/
void
gsk_path_builder_add_segment (GskPathBuilder *self,
GskPathMeasure *measure,
float start,
float end)
{
gsize i;
g_return_if_fail (self != NULL);
g_return_if_fail (measure != NULL);
start = gsk_path_measure_clamp_distance (measure, start);
end = gsk_path_measure_clamp_distance (measure, end);
if (start >= end)
return;
for (i = 0; i < measure->n_contours; i++)
{
if (measure->measures[i].length < start)
{
start -= measure->measures[i].length;
end -= measure->measures[i].length;
}
else if (start > 0 || end < measure->measures[i].length)
{
float len = MIN (end, measure->measures[i].length);
gsk_contour_add_segment (gsk_path_get_contour (measure->path, i),
self,
measure->measures[i].contour_data,
start,
len);
end -= len;
start = 0;
if (end <= 0)
break;
}
else
{
end -= measure->measures[i].length;
gsk_path_builder_add_contour (self, gsk_contour_dup (gsk_path_get_contour (measure->path, i)));
}
}
}
+79
View File
@@ -0,0 +1,79 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GSK_PATH_MEASURE_H__
#define __GSK_PATH_MEASURE_H__
#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gsk/gsk.h> can be included directly."
#endif
#include <gsk/gskpath.h>
G_BEGIN_DECLS
#define GSK_TYPE_PATH_MEASURE (gsk_path_measure_get_type ())
GDK_AVAILABLE_IN_ALL
GType gsk_path_measure_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GskPathMeasure * gsk_path_measure_new (GskPath *path);
GDK_AVAILABLE_IN_ALL
GskPathMeasure * gsk_path_measure_new_with_tolerance (GskPath *path,
float tolerance);
GDK_AVAILABLE_IN_ALL
GskPathMeasure * gsk_path_measure_ref (GskPathMeasure *self);
GDK_AVAILABLE_IN_ALL
void gsk_path_measure_unref (GskPathMeasure *self);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_measure_get_path (GskPathMeasure *self);
GDK_AVAILABLE_IN_ALL
float gsk_path_measure_get_tolerance (GskPathMeasure *self);
GDK_AVAILABLE_IN_ALL
float gsk_path_measure_get_length (GskPathMeasure *self);
GDK_AVAILABLE_IN_ALL
void gsk_path_measure_get_point (GskPathMeasure *self,
float distance,
graphene_point_t *pos,
graphene_vec2_t *tangent);
GDK_AVAILABLE_IN_ALL
float gsk_path_measure_get_closest_point (GskPathMeasure *self,
const graphene_point_t *point,
graphene_point_t *out_pos);
GDK_AVAILABLE_IN_ALL
gboolean gsk_path_measure_get_closest_point_full (GskPathMeasure *self,
const graphene_point_t *point,
float threshold,
float *out_distance,
graphene_point_t *out_pos,
float *out_offset,
graphene_vec2_t *out_tangent);
GDK_AVAILABLE_IN_ALL
gboolean gsk_path_measure_in_fill (GskPathMeasure *self,
graphene_point_t *point,
GskFillRule fill_rule);
G_END_DECLS
#endif /* __GSK_PATH_MEASURE_H__ */
+179
View File
@@ -0,0 +1,179 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GSK_PATHOP_PRIVATE_H__
#define __GSK_PATHOP_PRIVATE_H__
#include <gsk/gskpath.h>
#include <gsk/gskpathbuilder.h>
G_BEGIN_DECLS
typedef gpointer gskpathop;
static inline
gskpathop gsk_pathop_encode (GskPathOperation op,
const graphene_point_t *pts);
static inline
const graphene_point_t *gsk_pathop_points (gskpathop pop);
static inline
GskPathOperation gsk_pathop_op (gskpathop pop);
static inline
gboolean gsk_pathop_foreach (gskpathop pop,
GskPathForeachFunc func,
gpointer user_data);
/* included inline so tests can use them */
static inline
void gsk_path_builder_pathop_to (GskPathBuilder *builder,
gskpathop op);
static inline
void gsk_path_builder_pathop_reverse_to (GskPathBuilder *builder,
gskpathop op);
/* IMPLEMENTATION */
#define GSK_PATHOP_OPERATION_MASK (0x7)
static inline gskpathop
gsk_pathop_encode (GskPathOperation op,
const graphene_point_t *pts)
{
/* g_assert (op & GSK_PATHOP_OPERATION_MASK == op); */
g_assert ((GPOINTER_TO_SIZE (pts) & GSK_PATHOP_OPERATION_MASK) == 0);
return GSIZE_TO_POINTER (GPOINTER_TO_SIZE (pts) | op);
}
static inline const graphene_point_t *
gsk_pathop_points (gskpathop pop)
{
return GSIZE_TO_POINTER (GPOINTER_TO_SIZE (pop) & ~GSK_PATHOP_OPERATION_MASK);
}
static inline
GskPathOperation gsk_pathop_op (gskpathop pop)
{
return GPOINTER_TO_SIZE (pop) & GSK_PATHOP_OPERATION_MASK;
}
static inline gboolean
gsk_pathop_foreach (gskpathop pop,
GskPathForeachFunc func,
gpointer user_data)
{
switch (gsk_pathop_op (pop))
{
case GSK_PATH_MOVE:
return func (gsk_pathop_op (pop), gsk_pathop_points (pop), 1, 0, user_data);
case GSK_PATH_CLOSE:
case GSK_PATH_LINE:
return func (gsk_pathop_op (pop), gsk_pathop_points (pop), 2, 0, user_data);
case GSK_PATH_CURVE:
return func (gsk_pathop_op (pop), gsk_pathop_points (pop), 4, 0, user_data);
case GSK_PATH_CONIC:
{
const graphene_point_t *pts = gsk_pathop_points (pop);
return func (gsk_pathop_op (pop), (graphene_point_t[3]) { pts[0], pts[1], pts[3] }, 3, pts[2].x, user_data);
}
default:
g_assert_not_reached ();
return TRUE;
}
}
static inline void
gsk_path_builder_pathop_to (GskPathBuilder *builder,
gskpathop op)
{
const graphene_point_t *pts = gsk_pathop_points (op);
switch (gsk_pathop_op (op))
{
case GSK_PATH_MOVE:
gsk_path_builder_move_to (builder, pts[0].x, pts[0].y);
break;
case GSK_PATH_CLOSE:
gsk_path_builder_close (builder);
break;
case GSK_PATH_LINE:
gsk_path_builder_line_to (builder, pts[1].x, pts[1].y);
break;
case GSK_PATH_CURVE:
gsk_path_builder_curve_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
break;
case GSK_PATH_CONIC:
gsk_path_builder_conic_to (builder, pts[1].x, pts[1].y, pts[3].x, pts[3].y, pts[2].x);
break;
default:
g_assert_not_reached ();
break;
}
}
static inline void
gsk_path_builder_pathop_reverse_to (GskPathBuilder *builder,
gskpathop op)
{
const graphene_point_t *pts = gsk_pathop_points (op);
switch (gsk_pathop_op (op))
{
case GSK_PATH_MOVE:
gsk_path_builder_move_to (builder, pts[0].x, pts[0].y);
break;
case GSK_PATH_CLOSE:
gsk_path_builder_line_to (builder, pts[0].x, pts[0].y);
break;
case GSK_PATH_LINE:
gsk_path_builder_line_to (builder, pts[1].x, pts[1].y);
break;
case GSK_PATH_CURVE:
gsk_path_builder_curve_to (builder, pts[2].x, pts[2].y, pts[1].x, pts[1].y, pts[0].x, pts[0].y);
break;
case GSK_PATH_CONIC:
gsk_path_builder_conic_to (builder, pts[1].x, pts[1].y, pts[0].x, pts[0].y, pts[2].x);
break;
default:
g_assert_not_reached ();
break;
}
}
G_END_DECLS
#endif /* __GSK_PATHOP_PRIVATE_H__ */
+61
View File
@@ -0,0 +1,61 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GSK_PATH_PRIVATE_H__
#define __GSK_PATH_PRIVATE_H__
#include "gskpath.h"
#include "gskcontourprivate.h"
#include "gskpathopprivate.h"
G_BEGIN_DECLS
/* Same as Skia, so looks like a good value. ¯\_(ツ)_/¯ */
#define GSK_PATH_TOLERANCE_DEFAULT (0.5)
GskPath * gsk_path_new_from_contours (const GSList *contours);
gsize gsk_path_get_n_contours (GskPath *path);
const GskContour * gsk_path_get_contour (GskPath *path,
gsize i);
gboolean gsk_path_foreach_with_tolerance (GskPath *self,
GskPathForeachFlags flags,
double tolerance,
GskPathForeachFunc func,
gpointer user_data);
void gsk_path_builder_add_contour (GskPathBuilder *builder,
GskContour *contour);
void gsk_path_builder_svg_arc_to (GskPathBuilder *builder,
float rx,
float ry,
float x_axis_rotation,
gboolean large_arc,
gboolean positive_sweep,
float x,
float y);
G_END_DECLS
#endif /* __GSK_PATH_PRIVATE_H__ */
+2 -1
View File
@@ -567,8 +567,9 @@ get_renderer_for_backend (GdkSurface *surface)
return GSK_TYPE_BROADWAY_RENDERER;
#endif
#ifdef GDK_WINDOWING_MACOS
/* Use Cairo until GL renderer is working */
if (GDK_IS_MACOS_SURFACE (surface))
return GSK_TYPE_GL_RENDERER;
return GSK_TYPE_CAIRO_RENDERER;
#endif
#ifdef GDK_WINDOWING_WIN32
if (GDK_IS_WIN32_SURFACE (surface))
+6
View File
@@ -33,6 +33,12 @@ G_BEGIN_DECLS
#define GSK_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSK_TYPE_RENDERER, GskRenderer))
#define GSK_IS_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSK_TYPE_RENDERER))
/**
* GskRenderer:
*
* Base type for the object managing the rendering pipeline
* for a #GdkSurface.
*/
typedef struct _GskRendererClass GskRendererClass;
GDK_AVAILABLE_IN_ALL
+76
View File
@@ -40,12 +40,28 @@ typedef struct _GskRenderNode GskRenderNode;
typedef struct _GskColorStop GskColorStop;
typedef struct _GskShadow GskShadow;
/**
* GskColorStop:
* @offset: the offset of the color stop
* @color: the color at the given offset
*
* A color stop in a gradient node.
*/
struct _GskColorStop
{
float offset;
GdkRGBA color;
};
/**
* GskShadow:
* @color: the color of the shadow
* @dx: the horizontal offset of the shadow
* @dy: the vertical offset of the shadow
* @radius: the radius of the shadow
*
* The shadow parameters in a shadow node.
*/
struct _GskShadow
{
GdkRGBA color;
@@ -56,6 +72,16 @@ struct _GskShadow
typedef struct _GskParseLocation GskParseLocation;
/**
* GskParseLocation:
* @bytes: the offset of the location in the parse buffer, as bytes
* @chars: the offset of the location in the parse buffer, as characters
* @lines: the line of the location in the parse buffer
* @line_bytes: the position in the line, as bytes
* @line_chars: the position in the line, as characters
*
* A location in a parse buffer.
*/
struct _GskParseLocation
{
gsize bytes;
@@ -120,6 +146,7 @@ GskRenderNode * gsk_render_node_deserialize (GBytes
#define GSK_TYPE_REPEATING_LINEAR_GRADIENT_NODE (gsk_repeating_linear_gradient_node_get_type())
#define GSK_TYPE_RADIAL_GRADIENT_NODE (gsk_radial_gradient_node_get_type())
#define GSK_TYPE_REPEATING_RADIAL_GRADIENT_NODE (gsk_repeating_radial_gradient_node_get_type())
#define GSK_TYPE_CONIC_GRADIENT_NODE (gsk_conic_gradient_node_get_type())
#define GSK_TYPE_BORDER_NODE (gsk_border_node_get_type())
#define GSK_TYPE_INSET_SHADOW_NODE (gsk_inset_shadow_node_get_type())
#define GSK_TYPE_OUTSET_SHADOW_NODE (gsk_outset_shadow_node_get_type())
@@ -131,6 +158,8 @@ GskRenderNode * gsk_render_node_deserialize (GBytes
#define GSK_TYPE_REPEAT_NODE (gsk_repeat_node_get_type())
#define GSK_TYPE_CLIP_NODE (gsk_clip_node_get_type())
#define GSK_TYPE_ROUNDED_CLIP_NODE (gsk_rounded_clip_node_get_type())
#define GSK_TYPE_FILL_NODE (gsk_fill_node_get_type())
#define GSK_TYPE_STROKE_NODE (gsk_stroke_node_get_type())
#define GSK_TYPE_SHADOW_NODE (gsk_shadow_node_get_type())
#define GSK_TYPE_BLEND_NODE (gsk_blend_node_get_type())
#define GSK_TYPE_CROSS_FADE_NODE (gsk_cross_fade_node_get_type())
@@ -145,6 +174,7 @@ typedef struct _GskLinearGradientNode GskLinearGradientNode;
typedef struct _GskRepeatingLinearGradientNode GskRepeatingLinearGradientNode;
typedef struct _GskRadialGradientNode GskRadialGradientNode;
typedef struct _GskRepeatingRadialGradientNode GskRepeatingRadialGradientNode;
typedef struct _GskConicGradientNode GskConicGradientNode;
typedef struct _GskBorderNode GskBorderNode;
typedef struct _GskInsetShadowNode GskInsetShadowNode;
typedef struct _GskOutsetShadowNode GskOutsetShadowNode;
@@ -156,6 +186,8 @@ typedef struct _GskColorMatrixNode GskColorMatrixNode;
typedef struct _GskRepeatNode GskRepeatNode;
typedef struct _GskClipNode GskClipNode;
typedef struct _GskRoundedClipNode GskRoundedClipNode;
typedef struct _GskFillNode GskFillNode;
typedef struct _GskStrokeNode GskStrokeNode;
typedef struct _GskShadowNode GskShadowNode;
typedef struct _GskBlendNode GskBlendNode;
typedef struct _GskCrossFadeNode GskCrossFadeNode;
@@ -216,6 +248,24 @@ GskRenderNode * gsk_repeating_linear_gradient_node_new (const graph
const GskColorStop *color_stops,
gsize n_color_stops);
GDK_AVAILABLE_IN_ALL
GType gsk_conic_gradient_node_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GskRenderNode * gsk_conic_gradient_node_new (const graphene_rect_t *bounds,
const graphene_point_t *center,
float rotation,
const GskColorStop *color_stops,
gsize n_color_stops);
GDK_AVAILABLE_IN_ALL
const graphene_point_t * gsk_conic_gradient_node_get_center (GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
float gsk_conic_gradient_node_get_rotation (GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
gsize gsk_conic_gradient_node_get_n_color_stops (GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
const GskColorStop * gsk_conic_gradient_node_get_color_stops (GskRenderNode *node,
gsize *n_stops);
GDK_AVAILABLE_IN_ALL
GType gsk_radial_gradient_node_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
@@ -397,6 +447,32 @@ GskRenderNode * gsk_rounded_clip_node_get_child (GskRenderNode
GDK_AVAILABLE_IN_ALL
const GskRoundedRect * gsk_rounded_clip_node_get_clip (GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
GType gsk_fill_node_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GskRenderNode * gsk_fill_node_new (GskRenderNode *child,
GskPath *path,
GskFillRule fill_rule);
GDK_AVAILABLE_IN_ALL
GskRenderNode * gsk_fill_node_get_child (GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_fill_node_get_path (GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
GskFillRule gsk_fill_node_get_fill_rule (GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
GType gsk_stroke_node_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GskRenderNode * gsk_stroke_node_new (GskRenderNode *child,
GskPath *path,
const GskStroke *stroke);
GDK_AVAILABLE_IN_ALL
GskRenderNode * gsk_stroke_node_get_child (GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_stroke_node_get_path (GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
const GskStroke * gsk_stroke_node_get_stroke (GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
GType gsk_shadow_node_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
+831
View File
@@ -23,8 +23,10 @@
#include "gskcairoblurprivate.h"
#include "gskdebugprivate.h"
#include "gskdiffprivate.h"
#include "gskpath.h"
#include "gskrendererprivate.h"
#include "gskroundedrectprivate.h"
#include "gskstrokeprivate.h"
#include "gsktransformprivate.h"
#include "gdk/gdktextureprivate.h"
@@ -51,6 +53,11 @@ rectangle_init_from_graphene (cairo_rectangle_int_t *cairo,
/*** GSK_COLOR_NODE ***/
/**
* GskColorNode:
*
* A render node for a solid color.
*/
struct _GskColorNode
{
GskRenderNode render_node;
@@ -134,6 +141,17 @@ gsk_color_node_new (const GdkRGBA *rgba,
/*** GSK_LINEAR_GRADIENT_NODE ***/
/**
* GskRepeatingLinearGradientNode:
*
* A render node for a repeating linear gradient.
*/
/**
* GskLinearGradientNode:
*
* A render node for a linear gradient.
*/
struct _GskLinearGradientNode
{
GskRenderNode render_node;
@@ -392,6 +410,17 @@ gsk_linear_gradient_node_get_color_stops (GskRenderNode *node,
/*** GSK_RADIAL_GRADIENT_NODE ***/
/**
* GskRepeatingRadialGradientNode:
*
* A render node for a repeating radial gradient.
*/
/**
* GskRadialGradientNode:
*
* A render node for a radial gradient.
*/
struct _GskRadialGradientNode
{
GskRenderNode render_node;
@@ -741,8 +770,334 @@ gsk_radial_gradient_node_get_end (GskRenderNode *node)
return self->end;
}
/*** GSK_CONIC_GRADIENT_NODE ***/
struct _GskConicGradientNode
{
GskRenderNode render_node;
graphene_point_t center;
float rotation;
gsize n_stops;
GskColorStop *stops;
};
static void
gsk_conic_gradient_node_finalize (GskRenderNode *node)
{
GskConicGradientNode *self = (GskConicGradientNode *) node;
GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_CONIC_GRADIENT_NODE));
g_free (self->stops);
parent_class->finalize (node);
}
#define DEG_TO_RAD(x) ((x) * (G_PI / 180.f))
static void
_cairo_mesh_pattern_set_corner_rgba (cairo_pattern_t *pattern,
guint corner_num,
const GdkRGBA *rgba)
{
cairo_mesh_pattern_set_corner_color_rgba (pattern, corner_num, rgba->red, rgba->green, rgba->blue, rgba->alpha);
}
static void
project (double angle,
double radius,
double *x_out,
double *y_out)
{
double x, y;
x = radius * cos (angle);
y = radius * sin (angle);
if (copysign (x, 1.0) > copysign (y, 1.0))
{
*x_out = copysign (radius, x);
*y_out = y * radius / copysign (x, 1.0);
}
else
{
*x_out = x * radius / copysign (y, 1.0);
*y_out = copysign (radius, y);
}
}
static void
gsk_conic_gradient_node_add_patch (cairo_pattern_t *pattern,
float radius,
float start_angle,
const GdkRGBA *start_color,
float end_angle,
const GdkRGBA *end_color)
{
double x, y;
cairo_mesh_pattern_begin_patch (pattern);
cairo_mesh_pattern_move_to (pattern, 0, 0);
project (start_angle, radius, &x, &y);
cairo_mesh_pattern_line_to (pattern, x, y);
project (end_angle, radius, &x, &y);
cairo_mesh_pattern_line_to (pattern, x, y);
cairo_mesh_pattern_line_to (pattern, 0, 0);
_cairo_mesh_pattern_set_corner_rgba (pattern, 0, start_color);
_cairo_mesh_pattern_set_corner_rgba (pattern, 1, start_color);
_cairo_mesh_pattern_set_corner_rgba (pattern, 2, end_color);
_cairo_mesh_pattern_set_corner_rgba (pattern, 3, end_color);
cairo_mesh_pattern_end_patch (pattern);
}
static void
gdk_rgba_color_interpolate (GdkRGBA *dest,
const GdkRGBA *src1,
const GdkRGBA *src2,
double progress)
{
double alpha = src1->alpha * (1.0 - progress) + src2->alpha * progress;
dest->alpha = alpha;
if (alpha == 0)
{
dest->red = src1->red * (1.0 - progress) + src2->red * progress;
dest->green = src1->green * (1.0 - progress) + src2->green * progress;
dest->blue = src1->blue * (1.0 - progress) + src2->blue * progress;
}
else
{
dest->red = (src1->red * src1->alpha * (1.0 - progress) + src2->red * src2->alpha * progress) / alpha;
dest->green = (src1->green * src1->alpha * (1.0 - progress) + src2->green * src2->alpha * progress) / alpha;
dest->blue = (src1->blue * src1->alpha * (1.0 - progress) + src2->blue * src2->alpha * progress) / alpha;
}
}
static void
gsk_conic_gradient_node_draw (GskRenderNode *node,
cairo_t *cr)
{
GskConicGradientNode *self = (GskConicGradientNode *) node;
cairo_pattern_t *pattern;
graphene_point_t corner;
float radius;
gsize i;
pattern = cairo_pattern_create_mesh ();
graphene_rect_get_top_right (&node->bounds, &corner);
radius = graphene_point_distance (&self->center, &corner, NULL, NULL);
graphene_rect_get_bottom_right (&node->bounds, &corner);
radius = MAX (radius, graphene_point_distance (&self->center, &corner, NULL, NULL));
graphene_rect_get_bottom_left (&node->bounds, &corner);
radius = MAX (radius, graphene_point_distance (&self->center, &corner, NULL, NULL));
graphene_rect_get_top_left (&node->bounds, &corner);
radius = MAX (radius, graphene_point_distance (&self->center, &corner, NULL, NULL));
for (i = 0; i <= self->n_stops; i++)
{
GskColorStop *stop1 = &self->stops[MAX (i, 1) - 1];
GskColorStop *stop2 = &self->stops[MIN (i, self->n_stops - 1)];
double offset1 = i > 0 ? stop1->offset : 0;
double offset2 = i < self->n_stops ? stop2->offset : 1;
double start_angle, end_angle;
offset1 = offset1 * 360 + self->rotation - 90;
offset2 = offset2 * 360 + self->rotation - 90;
for (start_angle = offset1; start_angle < offset2; start_angle = end_angle)
{
GdkRGBA start_color, end_color;
end_angle = (floor (start_angle / 45) + 1) * 45;
end_angle = MIN (end_angle, offset2);
gdk_rgba_color_interpolate (&start_color,
&stop1->color,
&stop2->color,
(start_angle - offset1) / (offset2 - offset1));
gdk_rgba_color_interpolate (&end_color,
&stop1->color,
&stop2->color,
(end_angle - offset1) / (offset2 - offset1));
gsk_conic_gradient_node_add_patch (pattern,
radius,
DEG_TO_RAD (start_angle),
&start_color,
DEG_TO_RAD (end_angle),
&end_color);
}
}
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
gsk_cairo_rectangle (cr, &node->bounds);
cairo_translate (cr, self->center.x, self->center.y);
cairo_set_source (cr, pattern);
cairo_fill (cr);
cairo_pattern_destroy (pattern);
}
static void
gsk_conic_gradient_node_diff (GskRenderNode *node1,
GskRenderNode *node2,
cairo_region_t *region)
{
GskConicGradientNode *self1 = (GskConicGradientNode *) node1;
GskConicGradientNode *self2 = (GskConicGradientNode *) node2;
gsize i;
if (!graphene_point_equal (&self1->center, &self2->center) ||
self1->rotation != self2->rotation ||
self1->n_stops != self2->n_stops)
{
gsk_render_node_diff_impossible (node1, node2, region);
return;
}
for (i = 0; i < self1->n_stops; i++)
{
GskColorStop *stop1 = &self1->stops[i];
GskColorStop *stop2 = &self2->stops[i];
if (stop1->offset != stop2->offset ||
!gdk_rgba_equal (&stop1->color, &stop2->color))
{
gsk_render_node_diff_impossible (node1, node2, region);
return;
}
}
}
/**
* gsk_conic_gradient_node_new:
* @bounds: the bounds of the node
* @center: the center of the gradient
* @rotation: the rotation of the gradient in degrees
* @color_stops: (array length=n_color_stops): a pointer to an array of #GskColorStop defining the gradient
* The offsets of all color steps must be increasing. The first stop's offset must be >= 0 and the last
* stop's offset must be <= 1.
* @n_color_stops: the number of elements in @color_stops
*
* Creates a #GskRenderNode that draws a conic gradient. The conic gradient
* starts around @center in the direction of @rotation. A rotation of 0 means
* that the gradient points up. Color stops are then added clockwise.
*
* Returns: (transfer full) (type GskConicGradientNode): A new #GskRenderNode
*/
GskRenderNode *
gsk_conic_gradient_node_new (const graphene_rect_t *bounds,
const graphene_point_t *center,
float rotation,
const GskColorStop *color_stops,
gsize n_color_stops)
{
GskConicGradientNode *self;
GskRenderNode *node;
gsize i;
g_return_val_if_fail (bounds != NULL, NULL);
g_return_val_if_fail (center != NULL, NULL);
g_return_val_if_fail (color_stops != NULL, NULL);
g_return_val_if_fail (n_color_stops >= 2, NULL);
g_return_val_if_fail (color_stops[0].offset >= 0, NULL);
for (i = 1; i < n_color_stops; i++)
g_return_val_if_fail (color_stops[i].offset >= color_stops[i - 1].offset, NULL);
g_return_val_if_fail (color_stops[n_color_stops - 1].offset <= 1, NULL);
self = gsk_render_node_alloc (GSK_CONIC_GRADIENT_NODE);
node = (GskRenderNode *) self;
graphene_rect_init_from_rect (&node->bounds, bounds);
graphene_point_init_from_point (&self->center, center);
self->rotation = rotation;
self->n_stops = n_color_stops;
self->stops = g_malloc_n (n_color_stops, sizeof (GskColorStop));
memcpy (self->stops, color_stops, n_color_stops * sizeof (GskColorStop));
return node;
}
/**
* gsk_conic_gradient_node_get_n_color_stops:
* @node: (type GskConicGradientNode): a #GskRenderNode for a conic gradient
*
* Retrieves the number of color stops in the gradient.
*
* Returns: the number of color stops
*/
gsize
gsk_conic_gradient_node_get_n_color_stops (GskRenderNode *node)
{
GskConicGradientNode *self = (GskConicGradientNode *) node;
return self->n_stops;
}
/**
* gsk_conic_gradient_node_get_color_stops:
* @node: (type GskConicGradientNode): a #GskRenderNode for a conic gradient
* @n_stops: (out) (optional): the number of color stops in the returned array
*
* Retrieves the color stops in the gradient.
*
* Returns: (array length=n_stops): the color stops in the gradient
*/
const GskColorStop *
gsk_conic_gradient_node_get_color_stops (GskRenderNode *node,
gsize *n_stops)
{
GskConicGradientNode *self = (GskConicGradientNode *) node;
if (n_stops != NULL)
*n_stops = self->n_stops;
return self->stops;
}
/**
* gsk_conic_gradient_node_get_center:
* @node: (type GskConicGradientNode): a #GskRenderNode for a conic gradient
*
* Retrieves the center pointer for the gradient.
*
* Returns: the center point for the gradient
*/
const graphene_point_t *
gsk_conic_gradient_node_get_center (GskRenderNode *node)
{
GskConicGradientNode *self = (GskConicGradientNode *) node;
return &self->center;
}
/**
* gsk_conic_gradient_node_get_rotation:
* @node: (type GskConicGradientNode): a #GskRenderNode for a conic gradient
*
* Retrieves the rotation for the gradient in degrees.
*
* Returns: the rotation for the gradient
*/
float
gsk_conic_gradient_node_get_rotation (GskRenderNode *node)
{
GskConicGradientNode *self = (GskConicGradientNode *) node;
return self->rotation;
}
/*** GSK_BORDER_NODE ***/
/**
* GskBorderNode:
*
* A render node for a border.
*/
struct _GskBorderNode
{
GskRenderNode render_node;
@@ -1027,6 +1382,11 @@ gsk_border_node_get_uniform (GskRenderNode *self)
/*** GSK_TEXTURE_NODE ***/
/**
* GskTextureNode:
*
* A render node for a #GdkTexture.
*/
struct _GskTextureNode
{
GskRenderNode render_node;
@@ -1138,6 +1498,11 @@ gsk_texture_node_new (GdkTexture *texture,
/*** GSK_INSET_SHADOW_NODE ***/
/**
* GskInsetShadowNode:
*
* A render node for an inset shadow.
*/
struct _GskInsetShadowNode
{
GskRenderNode render_node;
@@ -1697,6 +2062,11 @@ gsk_inset_shadow_node_get_blur_radius (GskRenderNode *node)
/*** GSK_OUTSET_SHADOW_NODE ***/
/**
* GskOutsetShadowNode:
*
* A render node for an outset shadow.
*/
struct _GskOutsetShadowNode
{
GskRenderNode render_node;
@@ -2006,6 +2376,11 @@ gsk_outset_shadow_node_get_blur_radius (GskRenderNode *node)
/*** GSK_CAIRO_NODE ***/
/**
* GskCairoNode:
*
* A render node for a Cairo surface.
*/
struct _GskCairoNode
{
GskRenderNode render_node;
@@ -2137,6 +2512,11 @@ gsk_cairo_node_get_draw_context (GskRenderNode *node)
/**** GSK_CONTAINER_NODE ***/
/**
* GskContainerNode:
*
* A render node that can contain other render nodes.
*/
struct _GskContainerNode
{
GskRenderNode render_node;
@@ -2324,6 +2704,11 @@ gsk_container_node_get_child (GskRenderNode *node,
/*** GSK_TRANSFORM_NODE ***/
/**
* GskTransformNode:
*
* A render node applying a #GskTransform to its single child node.
*/
struct _GskTransformNode
{
GskRenderNode render_node;
@@ -2517,6 +2902,11 @@ gsk_transform_node_get_transform (GskRenderNode *node)
/*** GSK_OPACITY_NODE ***/
/**
* GskOpacityNode:
*
* A render node controlling the opacity of its single child node.
*/
struct _GskOpacityNode
{
GskRenderNode render_node;
@@ -2640,6 +3030,11 @@ gsk_opacity_node_get_opacity (GskRenderNode *node)
/*** GSK_COLOR_MATRIX_NODE ***/
/**
* GskColorMatrixNode:
*
* A render node controlling the color matrix of its single child node.
*/
struct _GskColorMatrixNode
{
GskRenderNode render_node;
@@ -2861,6 +3256,11 @@ gsk_color_matrix_node_get_color_offset (GskRenderNode *node)
/*** GSK_REPEAT_NODE ***/
/**
* GskRepeatNode:
*
* A render node repeating its single child node.
*/
struct _GskRepeatNode
{
GskRenderNode render_node;
@@ -2993,6 +3393,11 @@ gsk_repeat_node_get_child_bounds (GskRenderNode *node)
/*** GSK_CLIP_NODE ***/
/**
* GskClipNode:
*
* A render node applying a rectangular clip to its single child node.
*/
struct _GskClipNode
{
GskRenderNode render_node;
@@ -3123,6 +3528,11 @@ gsk_clip_node_get_clip (GskRenderNode *node)
/*** GSK_ROUNDED_CLIP_NODE ***/
/**
* GskRoundedClipNode:
*
* A render node applying a rounded rectangle clip to its single child.
*/
struct _GskRoundedClipNode
{
GskRenderNode render_node;
@@ -3251,8 +3661,347 @@ gsk_rounded_clip_node_get_clip (GskRenderNode *node)
return &self->clip;
}
/*** GSK_FILL_NODE ***/
struct _GskFillNode
{
GskRenderNode render_node;
GskRenderNode *child;
GskPath *path;
GskFillRule fill_rule;
};
static void
gsk_fill_node_finalize (GskRenderNode *node)
{
GskFillNode *self = (GskFillNode *) node;
GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_FILL_NODE));
gsk_render_node_unref (self->child);
gsk_path_unref (self->path);
parent_class->finalize (node);
}
static void
gsk_fill_node_draw (GskRenderNode *node,
cairo_t *cr)
{
GskFillNode *self = (GskFillNode *) node;
cairo_save (cr);
switch (self->fill_rule)
{
case GSK_FILL_RULE_WINDING:
cairo_set_fill_rule (cr, CAIRO_FILL_RULE_WINDING);
break;
case GSK_FILL_RULE_EVEN_ODD:
cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
break;
default:
g_assert_not_reached ();
break;
}
gsk_path_to_cairo (self->path, cr);
cairo_clip (cr);
gsk_render_node_draw (self->child, cr);
cairo_restore (cr);
}
static void
gsk_fill_node_diff (GskRenderNode *node1,
GskRenderNode *node2,
cairo_region_t *region)
{
GskFillNode *self1 = (GskFillNode *) node1;
GskFillNode *self2 = (GskFillNode *) node2;
if (self1->path == self2->path)
{
cairo_region_t *sub;
cairo_rectangle_int_t clip_rect;
graphene_rect_t rect;
sub = cairo_region_create();
gsk_render_node_diff (self1->child, self2->child, sub);
graphene_rect_union (&node1->bounds, &node2->bounds, &rect);
rectangle_init_from_graphene (&clip_rect, &rect);
cairo_region_intersect_rectangle (sub, &clip_rect);
cairo_region_union (region, sub);
cairo_region_destroy (sub);
}
else
{
gsk_render_node_diff_impossible (node1, node2, region);
}
}
/**
* gsk_fill_node_new:
* @child: The node to fill the area with
* @path: The path describing the area to fill
* @fill_rule: The fill rule to use
*
* Creates a #GskRenderNode that will fill the @child in the area
* given by @path and @fill_rule.
*
* Returns: (transfer none) (type GskFillNode): A new #GskRenderNode
*/
GskRenderNode *
gsk_fill_node_new (GskRenderNode *child,
GskPath *path,
GskFillRule fill_rule)
{
GskFillNode *self;
GskRenderNode *node;
graphene_rect_t path_bounds;
g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
g_return_val_if_fail (path != NULL, NULL);
self = gsk_render_node_alloc (GSK_FILL_NODE);
node = (GskRenderNode *) self;
self->child = gsk_render_node_ref (child);
self->path = gsk_path_ref (path);
self->fill_rule = fill_rule;
if (gsk_path_get_bounds (path, &path_bounds))
graphene_rect_intersection (&path_bounds, &child->bounds, &node->bounds);
else
graphene_rect_init_from_rect (&node->bounds, graphene_rect_zero ());
return node;
}
/**
* gsk_fill_node_get_child:
* @node: (type GskFillNode): a fill #GskRenderNode
*
* Gets the child node that is getting drawn by the given @node.
*
* Returns: (transfer none): The child that is getting drawn
**/
GskRenderNode *
gsk_fill_node_get_child (GskRenderNode *node)
{
GskFillNode *self = (GskFillNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_FILL_NODE), NULL);
return self->child;
}
/**
* gsk_fill_node_get_path:
* @node: (type GskFillNode): a fill #GskRenderNode
*
* Retrievs the path used to describe the area filled with the contents of
* the @node.
*
* Returns: (transfer none): a #GskPath
*/
GskPath *
gsk_fill_node_get_path (GskRenderNode *node)
{
GskFillNode *self = (GskFillNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_FILL_NODE), NULL);
return self->path;
}
/**
* gsk_fill_node_get_fill_rule:
* @node: (type GskFillNode): a fill #GskRenderNode
*
* Retrievs the fill rule used to determine how the path is filled.
*
* Returns: a #GskFillRule
*/
GskFillRule
gsk_fill_node_get_fill_rule (GskRenderNode *node)
{
GskFillNode *self = (GskFillNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_FILL_NODE), GSK_FILL_RULE_WINDING);
return self->fill_rule;
}
/*** GSK_STROKE_NODE ***/
struct _GskStrokeNode
{
GskRenderNode render_node;
GskRenderNode *child;
GskPath *path;
GskStroke stroke;
};
static void
gsk_stroke_node_finalize (GskRenderNode *node)
{
GskStrokeNode *self = (GskStrokeNode *) node;
GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_STROKE_NODE));
gsk_render_node_unref (self->child);
gsk_path_unref (self->path);
gsk_stroke_clear (&self->stroke);
parent_class->finalize (node);
}
static void
gsk_stroke_node_draw (GskRenderNode *node,
cairo_t *cr)
{
GskStrokeNode *self = (GskStrokeNode *) node;
cairo_save (cr);
gsk_cairo_rectangle (cr, &self->child->bounds);
cairo_clip (cr);
cairo_push_group (cr);
gsk_render_node_draw (self->child, cr);
cairo_pop_group_to_source (cr);
gsk_stroke_to_cairo (&self->stroke, cr);
gsk_path_to_cairo (self->path, cr);
cairo_stroke (cr);
cairo_restore (cr);
}
static void
gsk_stroke_node_diff (GskRenderNode *node1,
GskRenderNode *node2,
cairo_region_t *region)
{
GskStrokeNode *self1 = (GskStrokeNode *) node1;
GskStrokeNode *self2 = (GskStrokeNode *) node2;
if (self1->path == self2->path &&
gsk_stroke_equal (&self1->stroke, &self2->stroke))
{
cairo_region_t *sub;
sub = cairo_region_create();
gsk_render_node_diff (self1->child, self2->child, sub);
cairo_region_union (region, sub);
cairo_region_destroy (sub);
}
else
{
gsk_render_node_diff_impossible (node1, node2, region);
}
}
/**
* gsk_stroke_node_new:
* @child: The node to stroke the area with
* @path: (transfer none): The path describing the area to stroke
* @stroke: (transfer none): The stroke attributes to use
*
* Creates a #GskRenderNode that will stroke the @child along the given
* @path using the attributes defined in @stroke.
*
* Returns: (transfer none) (type GskStrokeNode): A new #GskRenderNode
*/
GskRenderNode *
gsk_stroke_node_new (GskRenderNode *child,
GskPath *path,
const GskStroke *stroke)
{
GskStrokeNode *self;
GskRenderNode *node;
g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
g_return_val_if_fail (path != NULL, NULL);
g_return_val_if_fail (stroke != NULL, NULL);
self = gsk_render_node_alloc (GSK_STROKE_NODE);
node = (GskRenderNode *) self;
self->child = gsk_render_node_ref (child);
self->path = gsk_path_ref (path);
gsk_stroke_init_copy (&self->stroke, stroke);
/* XXX: Figure out a way to compute bounds from the path */
graphene_rect_init_from_rect (&node->bounds, &child->bounds);
return node;
}
/**
* gsk_stroke_node_get_child:
* @node: (type GskStrokeNode): a stroke #GskRenderNode
*
* Gets the child node that is getting drawn by the given @node.
*
* Returns: (transfer none): The child that is getting drawn
**/
GskRenderNode *
gsk_stroke_node_get_child (GskRenderNode *node)
{
GskStrokeNode *self = (GskStrokeNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_STROKE_NODE), NULL);
return self->child;
}
/**
* gsk_stroke_node_get_path:
* @node: (type GskStrokeNode): a stroke #GskRenderNode
*
* Retrievs the path that will be stroked with the contents of
* the @node.
*
* Returns: (transfer none): a #GskPath
*/
GskPath *
gsk_stroke_node_get_path (GskRenderNode *node)
{
GskStrokeNode *self = (GskStrokeNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_STROKE_NODE), NULL);
return self->path;
}
/**
* gsk_stroke_node_get_stroke:
* @node: (type GskStrokeNode): a stroke #GskRenderNode
*
* Retrievs the stroke attributes used in this @node.
*
* Returns: a #GskStroke
*/
const GskStroke *
gsk_stroke_node_get_stroke (GskRenderNode *node)
{
GskStrokeNode *self = (GskStrokeNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_STROKE_NODE), NULL);
return &self->stroke;
}
/*** GSK_SHADOW_NODE ***/
/**
* GskShadowNode:
*
* A render node drawing one or more shadows behind its single child node.
*/
struct _GskShadowNode
{
GskRenderNode render_node;
@@ -3492,6 +4241,11 @@ gsk_shadow_node_get_n_shadows (GskRenderNode *node)
/*** GSK_BLEND_NODE ***/
/**
* GskBlendNode:
*
* A render node applying a blending function between its two child nodes.
*/
struct _GskBlendNode
{
GskRenderNode render_node;
@@ -3684,6 +4438,11 @@ gsk_blend_node_get_blend_mode (GskRenderNode *node)
/*** GSK_CROSS_FADE_NODE ***/
/**
* GskCrossFadeNode:
*
* A render node cross fading between two child nodes.
*/
struct _GskCrossFadeNode
{
GskRenderNode render_node;
@@ -3833,6 +4592,11 @@ gsk_cross_fade_node_get_progress (GskRenderNode *node)
/*** GSK_TEXT_NODE ***/
/**
* GskTextNode:
*
* A render node drawing a set of glyphs.
*/
struct _GskTextNode
{
GskRenderNode render_node;
@@ -4098,6 +4862,11 @@ gsk_text_node_get_offset (GskRenderNode *node)
/*** GSK_BLUR_NODE ***/
/**
* GskBlurNode:
*
* A render node applying a blur effect to its single child.
*/
struct _GskBlurNode
{
GskRenderNode render_node;
@@ -4410,6 +5179,12 @@ gsk_blur_node_get_radius (GskRenderNode *node)
/*** GSK_DEBUG_NODE ***/
/**
* GskDebugNode:
*
* A render node that emits a debugging message when drawing its
* child node.
*/
struct _GskDebugNode
{
GskRenderNode render_node;
@@ -4530,6 +5305,11 @@ gsk_debug_node_get_message (GskRenderNode *node)
/*** GSK_GL_SHADER_NODE ***/
/**
* GskGLShaderNode:
*
* A render node using a GL shader when drawing its children nodes.
*/
struct _GskGLShaderNode
{
GskRenderNode render_node;
@@ -4752,6 +5532,7 @@ GSK_DEFINE_RENDER_NODE_TYPE (gsk_linear_gradient_node, GSK_LINEAR_GRADIENT_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_repeating_linear_gradient_node, GSK_REPEATING_LINEAR_GRADIENT_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_radial_gradient_node, GSK_RADIAL_GRADIENT_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_repeating_radial_gradient_node, GSK_REPEATING_RADIAL_GRADIENT_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_conic_gradient_node, GSK_CONIC_GRADIENT_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_border_node, GSK_BORDER_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_texture_node, GSK_TEXTURE_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_inset_shadow_node, GSK_INSET_SHADOW_NODE)
@@ -4762,6 +5543,8 @@ GSK_DEFINE_RENDER_NODE_TYPE (gsk_color_matrix_node, GSK_COLOR_MATRIX_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_repeat_node, GSK_REPEAT_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_clip_node, GSK_CLIP_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_rounded_clip_node, GSK_ROUNDED_CLIP_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_fill_node, GSK_FILL_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_stroke_node, GSK_STROKE_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_shadow_node, GSK_SHADOW_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_blend_node, GSK_BLEND_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_cross_fade_node, GSK_CROSS_FADE_NODE)
@@ -4885,6 +5668,22 @@ gsk_render_node_init_types_once (void)
gsk_render_node_types[GSK_REPEATING_RADIAL_GRADIENT_NODE] = node_type;
}
{
const GskRenderNodeTypeInfo node_info =
{
GSK_CONIC_GRADIENT_NODE,
sizeof (GskConicGradientNode),
NULL,
gsk_conic_gradient_node_finalize,
gsk_conic_gradient_node_draw,
NULL,
gsk_conic_gradient_node_diff,
};
GType node_type = gsk_render_node_type_register_static (I_("GskConicGradientNode"), &node_info);
gsk_render_node_types[GSK_CONIC_GRADIENT_NODE] = node_type;
}
{
const GskRenderNodeTypeInfo node_info =
{
@@ -5045,6 +5844,38 @@ gsk_render_node_init_types_once (void)
gsk_render_node_types[GSK_ROUNDED_CLIP_NODE] = node_type;
}
{
const GskRenderNodeTypeInfo node_info =
{
GSK_FILL_NODE,
sizeof (GskFillNode),
NULL,
gsk_fill_node_finalize,
gsk_fill_node_draw,
NULL,
gsk_fill_node_diff,
};
GType node_type = gsk_render_node_type_register_static (I_("GskFillNode"), &node_info);
gsk_render_node_types[GSK_FILL_NODE] = node_type;
}
{
const GskRenderNodeTypeInfo node_info =
{
GSK_STROKE_NODE,
sizeof (GskStrokeNode),
NULL,
gsk_stroke_node_finalize,
gsk_stroke_node_draw,
NULL,
gsk_stroke_node_diff,
};
GType node_type = gsk_render_node_type_register_static (I_("GskStrokeNode"), &node_info);
gsk_render_node_types[GSK_STROKE_NODE] = node_type;
}
{
const GskRenderNodeTypeInfo node_info =
{
+428 -36
View File
@@ -23,9 +23,13 @@
#include "gskrendernodeparserprivate.h"
#include "gskpath.h"
#include "gskpathbuilder.h"
#include "gskroundedrectprivate.h"
#include "gskrendernodeprivate.h"
#include "gskstroke.h"
#include "gsktransformprivate.h"
#include "gskenumtypes.h"
#include "gdk/gdkrgbaprivate.h"
#include "gdk/gdktextureprivate.h"
@@ -406,7 +410,10 @@ parse_string (GtkCssParser *parser,
token = gtk_css_parser_get_token (parser);
if (!gtk_css_token_is (token, GTK_CSS_TOKEN_STRING))
return FALSE;
{
gtk_css_parser_error_syntax (parser, "Expected a string");
return FALSE;
}
s = g_strdup (token->string.string);
gtk_css_parser_consume_token (parser);
@@ -949,6 +956,26 @@ create_default_render_node (void)
return gsk_color_node_new (&GDK_RGBA("FF00CC"), &GRAPHENE_RECT_INIT (0, 0, 50, 50));
}
static GskPath *
create_default_path (void)
{
GskPathBuilder *builder;
guint i;
builder = gsk_path_builder_new ();
gsk_path_builder_move_to (builder, 25, 0);
for (i = 1; i < 5; i++)
{
gsk_path_builder_line_to (builder,
sin (i * G_PI * 0.8) * 25 + 25,
-cos (i * G_PI * 0.8) * 25 + 25);
}
gsk_path_builder_close (builder);
return gsk_path_builder_free_to_path (builder);
}
static GskRenderNode *
parse_color_node (GtkCssParser *parser)
{
@@ -1070,6 +1097,40 @@ parse_repeating_radial_gradient_node (GtkCssParser *parser)
return parse_radial_gradient_node_internal (parser, TRUE);
}
static GskRenderNode *
parse_conic_gradient_node (GtkCssParser *parser)
{
graphene_rect_t bounds = GRAPHENE_RECT_INIT (0, 0, 50, 50);
graphene_point_t center = GRAPHENE_POINT_INIT (25, 25);
double rotation = 0.0;
GArray *stops = NULL;
const Declaration declarations[] = {
{ "bounds", parse_rect, NULL, &bounds },
{ "center", parse_point, NULL, &center },
{ "rotation", parse_double, NULL, &rotation },
{ "stops", parse_stops, clear_stops, &stops },
};
GskRenderNode *result;
parse_declarations (parser, declarations, G_N_ELEMENTS(declarations));
if (stops == NULL)
{
GskColorStop from = { 0.0, GDK_RGBA("AAFF00") };
GskColorStop to = { 1.0, GDK_RGBA("FF00CC") };
stops = g_array_new (FALSE, FALSE, sizeof (GskColorStop));
g_array_append_val (stops, from);
g_array_append_val (stops, to);
}
result = gsk_conic_gradient_node_new (&bounds, &center, rotation,
(GskColorStop *) stops->data, stops->len);
g_array_free (stops, TRUE);
return result;
}
static GskRenderNode *
parse_inset_shadow_node (GtkCssParser *parser)
{
@@ -1725,6 +1786,208 @@ parse_rounded_clip_node (GtkCssParser *parser)
return result;
}
static gboolean
parse_path (GtkCssParser *parser,
gpointer out_path)
{
GskPath *path;
char *str = NULL;
if (!parse_string (parser, &str))
return FALSE;
path = gsk_path_parse (str);
g_free (str);
if (path == NULL)
{
gtk_css_parser_error_value (parser, "Invalid path");
return FALSE;
}
*((GskPath **) out_path) = path;
return TRUE;
}
static void
clear_path (gpointer inout_path)
{
g_clear_pointer ((GskPath **) inout_path, gsk_path_unref);
}
static gboolean
parse_dash (GtkCssParser *parser,
gpointer out_dash)
{
GArray *dash;
double d;
/* because CSS does this, too */
if (gtk_css_parser_try_ident (parser, "none"))
{
*((GArray **) out_dash) = NULL;
return TRUE;
}
dash = g_array_new (FALSE, FALSE, sizeof (float));
do {
if (!gtk_css_parser_consume_number (parser, &d))
{
g_array_free (dash, TRUE);
return FALSE;
}
g_array_append_vals (dash, (float[1]) { d }, 1);
} while (gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_SIGNLESS_NUMBER) ||
gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_SIGNLESS_INTEGER));
*((GArray **) out_dash) = dash;
return TRUE;
}
static void
clear_dash (gpointer inout_array)
{
g_clear_pointer ((GArray **) inout_array, g_array_unref);
}
static gboolean
parse_enum (GtkCssParser *parser,
GType type,
gpointer out_value)
{
GEnumClass *class;
GEnumValue *v;
const GtkCssToken *token;
token = gtk_css_parser_get_token (parser);
if (!gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT))
{
gtk_css_parser_error_syntax (parser, "Expected a valid identifier");
return FALSE;
}
class = g_type_class_ref (type);
v = g_enum_get_value_by_nick (class, token->string.string);
if (v == NULL)
{
gtk_css_parser_error_value (parser, "\"%s\" is not a valid identifier here", token->string.string);
g_type_class_unref (class);
return FALSE;
}
*(int*)out_value = v->value;
g_type_class_unref (class);
gtk_css_parser_consume_token (parser);
return TRUE;
}
static gboolean
parse_fill_rule (GtkCssParser *parser,
gpointer out_rule)
{
return parse_enum (parser, GSK_TYPE_FILL_RULE, out_rule);
}
static GskRenderNode *
parse_fill_node (GtkCssParser *parser)
{
GskRenderNode *child = NULL;
GskPath *path = NULL;
int rule = GSK_FILL_RULE_WINDING;
const Declaration declarations[] = {
{ "child", parse_node, clear_node, &child },
{ "path", parse_path, clear_path, &path },
{ "fill-rule", parse_fill_rule, NULL, &rule },
};
GskRenderNode *result;
parse_declarations (parser, declarations, G_N_ELEMENTS (declarations));
if (child == NULL)
child = create_default_render_node ();
if (path == NULL)
path = create_default_path ();
result = gsk_fill_node_new (child, path, rule);
gsk_path_unref (path);
gsk_render_node_unref (child);
return result;
}
static gboolean
parse_line_cap (GtkCssParser *parser,
gpointer out)
{
return parse_enum (parser, GSK_TYPE_LINE_CAP, out);
}
static gboolean
parse_line_join (GtkCssParser *parser,
gpointer out)
{
return parse_enum (parser, GSK_TYPE_LINE_JOIN, out);
}
static GskRenderNode *
parse_stroke_node (GtkCssParser *parser)
{
GskRenderNode *child = NULL;
GskPath *path = NULL;
double line_width = 1.0;
int line_cap = GSK_LINE_CAP_BUTT;
int line_join = GSK_LINE_JOIN_MITER;
double miter_limit = 4.0;
GArray *dash = NULL;
double dash_offset = 0.0;
GskStroke *stroke;
const Declaration declarations[] = {
{ "child", parse_node, clear_node, &child },
{ "path", parse_path, clear_path, &path },
{ "line-width", parse_double, NULL, &line_width },
{ "line-cap", parse_line_cap, NULL, &line_cap },
{ "line-join", parse_line_join, NULL, &line_join },
{ "miter-limit", parse_double, NULL, &miter_limit },
{ "dash", parse_dash, clear_dash, &dash },
{ "dash-offset", parse_double, NULL, &dash_offset},
};
GskRenderNode *result;
parse_declarations (parser, declarations, G_N_ELEMENTS (declarations));
if (child == NULL)
child = create_default_render_node ();
if (path == NULL)
path = create_default_path ();
stroke = gsk_stroke_new (line_width);
gsk_stroke_set_line_cap (stroke, line_cap);
gsk_stroke_set_line_join (stroke, line_join);
gsk_stroke_set_miter_limit (stroke, miter_limit);
if (dash)
{
gsk_stroke_set_dash (stroke, (float *) dash->data, dash->len);
g_array_free (dash, TRUE);
}
gsk_stroke_set_dash_offset (stroke, dash_offset);
result = gsk_stroke_node_new (child, path, stroke);
gsk_path_unref (path);
gsk_stroke_free (stroke);
gsk_render_node_unref (child);
return result;
}
static GskRenderNode *
parse_shadow_node (GtkCssParser *parser)
{
@@ -1797,12 +2060,15 @@ parse_node (GtkCssParser *parser,
{ "inset-shadow", parse_inset_shadow_node },
{ "linear-gradient", parse_linear_gradient_node },
{ "radial-gradient", parse_radial_gradient_node },
{ "conic-gradient", parse_conic_gradient_node },
{ "opacity", parse_opacity_node },
{ "outset-shadow", parse_outset_shadow_node },
{ "repeat", parse_repeat_node },
{ "repeating-linear-gradient", parse_repeating_linear_gradient_node },
{ "repeating-radial-gradient", parse_repeating_radial_gradient_node },
{ "rounded-clip", parse_rounded_clip_node },
{ "fill", parse_fill_node },
{ "stroke", parse_stroke_node },
{ "shadow", parse_shadow_node },
{ "text", parse_text_node },
{ "texture", parse_texture_node },
@@ -2059,7 +2325,7 @@ append_float_param (Printer *p,
float value,
float default_value)
{
/* Don't approximate-compare here, better be topo verbose */
/* Don't approximate-compare here, better be too verbose */
if (value == default_value)
return;
@@ -2184,6 +2450,30 @@ append_node_param (Printer *p,
render_node_print (p, node);
}
static void
append_stops_param (Printer *p,
const char *param_name,
const GskColorStop *stops,
gsize n_stops)
{
gsize i;
_indent (p);
g_string_append (p->str, param_name);
g_string_append (p->str, ": ");
for (i = 0; i < n_stops; i ++)
{
if (i > 0)
g_string_append (p->str, ", ");
string_append_double (p->str, stops[i].offset);
g_string_append_c (p->str, ' ');
append_rgba (p->str, &stops[i].color);
}
g_string_append (p->str, ";\n");
}
static cairo_status_t
cairo_write_array (void *closure,
const unsigned char *data,
@@ -2210,8 +2500,11 @@ append_escaping_newlines (GString *str,
len = strcspn (string, "\n");
g_string_append_len (str, string, len);
string += len;
g_string_append (str, "\\\n");
string++;
if (*string)
{
g_string_append (str, "\\\n");
string++;
}
} while (*string);
}
@@ -2245,6 +2538,83 @@ base64_encode_with_linebreaks (const guchar *data,
return out;
}
static const char *
enum_to_nick (GType type,
int value)
{
GEnumClass *class;
GEnumValue *v;
class = g_type_class_ref (type);
v = g_enum_get_value (class, value);
g_type_class_unref (class);
return v->value_nick;
}
static void
append_enum_param (Printer *p,
const char *param_name,
GType type,
int value)
{
_indent (p);
g_string_append_printf (p->str, "%s: ", param_name);
g_string_append (p->str, enum_to_nick (type, value));
g_string_append_c (p->str, ';');
g_string_append_c (p->str, '\n');
}
static void
append_path_param (Printer *p,
const char *param_name,
GskPath *path)
{
char *str, *s;
_indent (p);
g_string_append (p->str, "path: \"\\\n");
str = gsk_path_to_string (path);
/* Put each command on a new line */
for (s = str; *s; s++)
{
if (*s == ' ' &&
(s[1] == 'M' || s[1] == 'C' || s[1] == 'Z' || s[1] == 'L'))
*s = '\n';
}
append_escaping_newlines (p->str, str);
g_string_append (p->str, "\";\n");
g_free (str);
}
static void
append_dash_param (Printer *p,
const char *param_name,
const float *dash,
gsize n_dash)
{
_indent (p);
g_string_append (p->str, "dash: ");
if (n_dash == 0)
{
g_string_append (p->str, "none");
}
else
{
gsize i;
string_append_double (p->str, dash[0]);
for (i = 1; i < n_dash; i++)
{
g_string_append_c (p->str, ' ');
string_append_double (p->str, dash[i]);
}
}
g_string_append (p->str, ";\n");
}
static void
render_node_print (Printer *p,
GskRenderNode *node)
@@ -2294,10 +2664,6 @@ render_node_print (Printer *p,
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
case GSK_LINEAR_GRADIENT_NODE:
{
const gsize n_stops = gsk_linear_gradient_node_get_n_color_stops (node);
const GskColorStop *stops = gsk_linear_gradient_node_get_color_stops (node, NULL);
gsize i;
if (gsk_render_node_get_node_type (node) == GSK_REPEATING_LINEAR_GRADIENT_NODE)
start_node (p, "repeating-linear-gradient");
else
@@ -2306,19 +2672,8 @@ render_node_print (Printer *p,
append_rect_param (p, "bounds", &node->bounds);
append_point_param (p, "end", gsk_linear_gradient_node_get_end (node));
append_point_param (p, "start", gsk_linear_gradient_node_get_start (node));
_indent (p);
g_string_append (p->str, "stops: ");
for (i = 0; i < n_stops; i ++)
{
if (i > 0)
g_string_append (p->str, ", ");
string_append_double (p->str, stops[i].offset);
g_string_append_c (p->str, ' ');
append_rgba (p->str, &stops[i].color);
}
g_string_append (p->str, ";\n");
append_stops_param (p, "stops", gsk_linear_gradient_node_get_color_stops (node, NULL),
gsk_linear_gradient_node_get_n_color_stops (node));
end_node (p);
}
@@ -2327,10 +2682,6 @@ render_node_print (Printer *p,
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
case GSK_RADIAL_GRADIENT_NODE:
{
const gsize n_stops = gsk_radial_gradient_node_get_n_color_stops (node);
const GskColorStop *stops = gsk_radial_gradient_node_get_color_stops (node, NULL);
gsize i;
if (gsk_render_node_get_node_type (node) == GSK_REPEATING_RADIAL_GRADIENT_NODE)
start_node (p, "repeating-radial-gradient");
else
@@ -2343,18 +2694,23 @@ render_node_print (Printer *p,
append_float_param (p, "start", gsk_radial_gradient_node_get_start (node), 0.0f);
append_float_param (p, "end", gsk_radial_gradient_node_get_end (node), 1.0f);
_indent (p);
g_string_append (p->str, "stops: ");
for (i = 0; i < n_stops; i ++)
{
if (i > 0)
g_string_append (p->str, ", ");
append_stops_param (p, "stops", gsk_radial_gradient_node_get_color_stops (node, NULL),
gsk_radial_gradient_node_get_n_color_stops (node));
string_append_double (p->str, stops[i].offset);
g_string_append_c (p->str, ' ');
append_rgba (p->str, &stops[i].color);
}
g_string_append (p->str, ";\n");
end_node (p);
}
break;
case GSK_CONIC_GRADIENT_NODE:
{
start_node (p, "conic-gradient");
append_rect_param (p, "bounds", &node->bounds);
append_point_param (p, "center", gsk_conic_gradient_node_get_center (node));
append_float_param (p, "rotation", gsk_conic_gradient_node_get_rotation (node), 0.0f);
append_stops_param (p, "stops", gsk_conic_gradient_node_get_color_stops (node, NULL),
gsk_conic_gradient_node_get_n_color_stops (node));
end_node (p);
}
@@ -2407,6 +2763,42 @@ render_node_print (Printer *p,
append_node_param (p, "child", gsk_rounded_clip_node_get_child (node));
append_rounded_rect_param (p, "clip", gsk_rounded_clip_node_get_clip (node));
end_node (p);
}
break;
case GSK_FILL_NODE:
{
start_node (p, "fill");
append_node_param (p, "child", gsk_fill_node_get_child (node));
append_path_param (p, "path", gsk_fill_node_get_path (node));
append_enum_param (p, "fill-rule", GSK_TYPE_FILL_RULE, gsk_fill_node_get_fill_rule (node));
end_node (p);
}
break;
case GSK_STROKE_NODE:
{
const GskStroke *stroke;
const float *dash;
gsize n_dash;
start_node (p, "stroke");
append_node_param (p, "child", gsk_stroke_node_get_child (node));
append_path_param (p, "path", gsk_stroke_node_get_path (node));
stroke = gsk_stroke_node_get_stroke (node);
append_float_param (p, "line-width", gsk_stroke_get_line_width (stroke), 0.0f);
append_enum_param (p, "line-cap", GSK_TYPE_LINE_CAP, gsk_stroke_get_line_cap (stroke));
append_enum_param (p, "line-join", GSK_TYPE_LINE_JOIN, gsk_stroke_get_line_join (stroke));
append_float_param (p, "line-width", gsk_stroke_get_miter_limit (stroke), 4.0f);
dash = gsk_stroke_get_dash (stroke, &n_dash);
if (dash)
append_dash_param (p, "dash", dash, n_dash);
append_float_param (p, "dash-offset", gsk_stroke_get_dash_offset (stroke), 0.0f);
end_node (p);
}
+208
View File
@@ -0,0 +1,208 @@
/*
* Copyright © 2002 University of Southern California
* 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
* Carl D. Worth <cworth@cworth.org>
*/
#include "config.h"
#include "gsksplineprivate.h"
#include <math.h>
/* Spline deviation from the circle in radius would be given by:
error = sqrt (x**2 + y**2) - 1
A simpler error function to work with is:
e = x**2 + y**2 - 1
From "Good approximation of circles by curvature-continuous Bezier
curves", Tor Dokken and Morten Daehlen, Computer Aided Geometric
Design 8 (1990) 22-41, we learn:
abs (max(e)) = 4/27 * sin**6(angle/4) / cos**2(angle/4)
and
abs (error) =~ 1/2 * e
Of course, this error value applies only for the particular spline
approximation that is used in _cairo_gstate_arc_segment.
*/
static float
arc_error_normalized (float angle)
{
return 2.0/27.0 * pow (sin (angle / 4), 6) / pow (cos (angle / 4), 2);
}
static float
arc_max_angle_for_tolerance_normalized (float tolerance)
{
float angle, error;
guint i;
/* Use table lookup to reduce search time in most cases. */
struct {
float angle;
float error;
} table[] = {
{ G_PI / 1.0, 0.0185185185185185036127 },
{ G_PI / 2.0, 0.000272567143730179811158 },
{ G_PI / 3.0, 2.38647043651461047433e-05 },
{ G_PI / 4.0, 4.2455377443222443279e-06 },
{ G_PI / 5.0, 1.11281001494389081528e-06 },
{ G_PI / 6.0, 3.72662000942734705475e-07 },
{ G_PI / 7.0, 1.47783685574284411325e-07 },
{ G_PI / 8.0, 6.63240432022601149057e-08 },
{ G_PI / 9.0, 3.2715520137536980553e-08 },
{ G_PI / 10.0, 1.73863223499021216974e-08 },
{ G_PI / 11.0, 9.81410988043554039085e-09 },
};
for (i = 0; i < G_N_ELEMENTS (table); i++)
{
if (table[i].error < tolerance)
return table[i].angle;
}
i++;
do {
angle = G_PI / i++;
error = arc_error_normalized (angle);
} while (error > tolerance);
return angle;
}
static guint
arc_segments_needed (float angle,
float radius,
float tolerance)
{
float max_angle;
/* the error is amplified by at most the length of the
* major axis of the circle; see cairo-pen.c for a more detailed analysis
* of this. */
max_angle = arc_max_angle_for_tolerance_normalized (tolerance / radius);
return ceil (fabs (angle) / max_angle);
}
/* We want to draw a single spline approximating a circular arc radius
R from angle A to angle B. Since we want a symmetric spline that
matches the endpoints of the arc in position and slope, we know
that the spline control points must be:
(R * cos(A), R * sin(A))
(R * cos(A) - h * sin(A), R * sin(A) + h * cos (A))
(R * cos(B) + h * sin(B), R * sin(B) - h * cos (B))
(R * cos(B), R * sin(B))
for some value of h.
"Approximation of circular arcs by cubic polynomials", Michael
Goldapp, Computer Aided Geometric Design 8 (1991) 227-238, provides
various values of h along with error analysis for each.
From that paper, a very practical value of h is:
h = 4/3 * R * tan(angle/4)
This value does not give the spline with minimal error, but it does
provide a very good approximation, (6th-order convergence), and the
error expression is quite simple, (see the comment for
_arc_error_normalized).
*/
static gboolean
gsk_spline_decompose_arc_segment (const graphene_point_t *center,
float radius,
float angle_A,
float angle_B,
GskSplineAddCurveFunc curve_func,
gpointer user_data)
{
float r_sin_A, r_cos_A;
float r_sin_B, r_cos_B;
float h;
r_sin_A = radius * sin (angle_A);
r_cos_A = radius * cos (angle_A);
r_sin_B = radius * sin (angle_B);
r_cos_B = radius * cos (angle_B);
h = 4.0/3.0 * tan ((angle_B - angle_A) / 4.0);
return curve_func ((graphene_point_t[4]) {
GRAPHENE_POINT_INIT (
center->x + r_cos_A,
center->y + r_sin_A
),
GRAPHENE_POINT_INIT (
center->x + r_cos_A - h * r_sin_A,
center->y + r_sin_A + h * r_cos_A
),
GRAPHENE_POINT_INIT (
center->x + r_cos_B + h * r_sin_B,
center->y + r_sin_B - h * r_cos_B
),
GRAPHENE_POINT_INIT (
center->x + r_cos_B,
center->y + r_sin_B
)
},
user_data);
}
gboolean
gsk_spline_decompose_arc (const graphene_point_t *center,
float radius,
float tolerance,
float start_angle,
float end_angle,
GskSplineAddCurveFunc curve_func,
gpointer user_data)
{
float step = start_angle - end_angle;
guint i, n_segments;
/* Recurse if drawing arc larger than pi */
if (ABS (step) > G_PI)
{
float mid_angle = (start_angle + end_angle) / 2.0;
return gsk_spline_decompose_arc (center, radius, tolerance, start_angle, mid_angle, curve_func, user_data)
&& gsk_spline_decompose_arc (center, radius, tolerance, mid_angle, end_angle, curve_func, user_data);
}
else if (ABS (step) < tolerance)
{
return gsk_spline_decompose_arc_segment (center, radius, start_angle, end_angle, curve_func, user_data);
}
n_segments = arc_segments_needed (ABS (step), radius, tolerance);
step = (end_angle - start_angle) / n_segments;
for (i = 0; i < n_segments - 1; i++, start_angle += step)
{
if (!gsk_spline_decompose_arc_segment (center, radius, start_angle, start_angle + step, curve_func, user_data))
return FALSE;
}
return gsk_spline_decompose_arc_segment (center, radius, start_angle, end_angle, curve_func, user_data);
}
+41
View File
@@ -0,0 +1,41 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GSK_SPLINE_PRIVATE_H__
#define __GSK_SPLINE_PRIVATE_H__
#include "gskpath.h"
G_BEGIN_DECLS
typedef gboolean (* GskSplineAddCurveFunc) (const graphene_point_t curve[4],
gpointer user_data);
gboolean gsk_spline_decompose_arc (const graphene_point_t *center,
float radius,
float tolerance,
float start_angle,
float end_angle,
GskSplineAddCurveFunc curve_func,
gpointer user_data);
G_END_DECLS
#endif /* __GSK_SPLINE_PRIVATE_H__ */
+441
View File
@@ -0,0 +1,441 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gskstrokeprivate.h"
/**
* SECTION:gskstroke
* @Title: Stroke
* @Short_description: Properties of a stroke operation
* @See_also: #GskPath, gsk_stroke_node_new()
*
* This section describes the #GskStroke structure that is used to
* describe lines and curves that are more complex than simple rectangles.
*
* #GskStroke is an immutable struct. After creation, you cannot change
* the types it represents. Instead, new #GskStroke have to be created.
* The #GskStrokeBuilder structure is meant to help in this endeavor.
*/
/**
* GskStroke:
*
* A #GskStroke struct is an opaque struct that should be copied
* on use.
*/
G_DEFINE_BOXED_TYPE (GskStroke, gsk_stroke,
gsk_stroke_copy,
gsk_stroke_free)
/**
* gsk_stroke_new:
* @line_width: line width of the stroke. Must be > 0
*
* Creates a new #GskStroke with the given @line_width.
*
* Returns: a new #GskStroke
**/
GskStroke *
gsk_stroke_new (float line_width)
{
GskStroke *self;
g_return_val_if_fail (line_width > 0, NULL);
self = g_new0 (GskStroke, 1);
self->line_width = line_width;
self->line_cap = GSK_LINE_CAP_BUTT;
self->line_join = GSK_LINE_JOIN_MITER;
self->miter_limit = 4.f; /* following svg */
return self;
}
/**
* gsk_stroke_copy:
* @other: #GskStroke to copy
*
* Creates a copy of the given @other stroke.
*
* Returns: a new #GskStroke. Use gsk_stroke_free() to free it.
**/
GskStroke *
gsk_stroke_copy (const GskStroke *other)
{
GskStroke *self;
g_return_val_if_fail (other != NULL, NULL);
self = g_new (GskStroke, 1);
gsk_stroke_init_copy (self, other);
return self;
}
/**
* gsk_stroke_free:
* @self: a #GskStroke
*
* Frees a #GskStroke.
**/
void
gsk_stroke_free (GskStroke *self)
{
if (self == NULL)
return;
gsk_stroke_clear (self);
g_free (self);
}
void
gsk_stroke_to_cairo (const GskStroke *self,
cairo_t *cr)
{
cairo_set_line_width (cr, self->line_width);
/* gcc can optimize that to a direct case. This catches later additions to the enum */
switch (self->line_cap)
{
case GSK_LINE_CAP_BUTT:
cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
break;
case GSK_LINE_CAP_ROUND:
cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
break;
case GSK_LINE_CAP_SQUARE:
cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
break;
default:
g_assert_not_reached ();
break;
}
/* gcc can optimize that to a direct case. This catches later additions to the enum */
switch (self->line_join)
{
case GSK_LINE_JOIN_MITER:
case GSK_LINE_JOIN_MITER_CLIP:
cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
break;
case GSK_LINE_JOIN_ROUND:
cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
break;
case GSK_LINE_JOIN_BEVEL:
cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
break;
default:
g_assert_not_reached ();
break;
}
cairo_set_miter_limit (cr, self->miter_limit);
if (self->dash_length)
{
gsize i;
double *dash = g_newa (double, self->n_dash);
for (i = 0; i < self->n_dash; i++)
{
dash[i] = self->dash[i];
}
cairo_set_dash (cr, dash, self->n_dash, self->dash_offset);
}
else
cairo_set_dash (cr, NULL, 0, 0.0);
}
/**
* gsk_stroke_equal:
* @stroke1: the first #GskStroke
* @stroke2: the second #GskStroke
*
* Checks if 2 strokes are identical.
*
* Returns: %TRUE if the 2 strokes are equal, %FALSE otherwise
**/
gboolean
gsk_stroke_equal (gconstpointer stroke1,
gconstpointer stroke2)
{
const GskStroke *self1 = stroke1;
const GskStroke *self2 = stroke2;
return self1->line_width == self2->line_width;
}
/**
* gsk_stroke_set_line_width:
* @self: a #GskStroke
* @line_width: width of the line in pixels
*
* Sets the line width to be used when stroking. The line width
* must be > 0.
**/
void
gsk_stroke_set_line_width (GskStroke *self,
float line_width)
{
g_return_if_fail (self != NULL);
g_return_if_fail (line_width > 0);
self->line_width = line_width;
}
/**
* gsk_stroke_get_line_width:
* @self: a #GskStroke
*
* Gets the line width used.
*
* Returns: The line width
**/
float
gsk_stroke_get_line_width (const GskStroke *self)
{
g_return_val_if_fail (self != NULL, 0.0);
return self->line_width;
}
/**
* gsk_stroke_set_line_cap:
* @self: a #GskStroke
* @line_cap: the #GskLineCap
*
* Sets the line cap to be used when stroking.
* See #GskLineCap for details.
**/
void
gsk_stroke_set_line_cap (GskStroke *self,
GskLineCap line_cap)
{
g_return_if_fail (self != NULL);
self->line_cap = line_cap;
}
/**
* gsk_stroke_get_line_cap:
* @self: a #GskStroke
*
* Gets the line cap used. See #GskLineCap for details.
*
* Returns: The line cap
**/
GskLineCap
gsk_stroke_get_line_cap (const GskStroke *self)
{
g_return_val_if_fail (self != NULL, 0.0);
return self->line_cap;
}
/**
* gsk_stroke_set_line_join:
* @self: a #GskStroke
* @line_join: The line join to use
*
* Sets the line join to be used when stroking.
* See #GskLineJoin for details.
**/
void
gsk_stroke_set_line_join (GskStroke *self,
GskLineJoin line_join)
{
g_return_if_fail (self != NULL);
self->line_join = line_join;
}
/**
* gsk_stroke_get_line_join:
* @self: a #GskStroke
*
* Gets the line join used. See #GskLineJoin for details.
*
* Returns: The line join
**/
GskLineJoin
gsk_stroke_get_line_join (const GskStroke *self)
{
g_return_val_if_fail (self != NULL, 0.0);
return self->line_join;
}
/**
* gsk_stroke_set_miter_limit:
* @self: a #GskStroke
* @limit: the miter limit, must be non-negative
*
* Sets the limit for the distance from the corner where sharp
* turns of joins get cut off. The miter limit is in units of
* line width.
*
* For joins of type %GSK_LINE_JOIN_MITER that exceed the miter
* limit, the join gets rendered as if it was of type
* %GSK_LINE_JOIN_BEVEL. For joins of type %GSK_LINE_JOIN_MITER_CLIP,
* the miter is clipped at a distance of half the miter limit.
*/
void
gsk_stroke_set_miter_limit (GskStroke *self,
float limit)
{
g_return_if_fail (self != NULL);
g_return_if_fail (limit >= 0);
self->miter_limit = limit;
}
/**
* gsk_stroke_get_miter_limit:
* @self: a #GskStroke
*
* Returns the miter limit of a #GskStroke.
*/
float
gsk_stroke_get_miter_limit (const GskStroke *self)
{
g_return_val_if_fail (self != NULL, 4.f);
return self->miter_limit;
}
/**
* gsk_stroke_set_dash:
* @self: a #GskStroke
* @dash: (array length=n_dash) (transfer none) (allow none):
* the array of dashes
* @n_dash: number of elements in @dash
*
* Sets the dash pattern to use by this stroke. A dash pattern is specified by
* an array of alternating non-negative values. Each value provides the length
* of alternate "on" and "off" portions of the stroke.
*
* Each "on" segment will have caps applied as if the segment were a separate
* contour. In particular, it is valid to use an "on" length of 0 with
* @GSK_LINE_CAP_ROUND or @GSK_LINE_CAP_SQUARE to draw dots or squares along
* a path.
*
* If @n_dash is 0, if all elements in @dash are 0, or if there are negative
* values in @dash, then dashing is disabled.
*
* If @n_dash is 1, an alternating "on" and "off" pattern with the single
* dash length provided is assumed.
*
* If @n_dash is uneven, the dash array will be used with the first element
* in @dash defining an "on" or "off" in alternating passes through the array.
*
* You can specify a starting offset into the dash with
* @gsk_stroke_set_dash_offset().
**/
void
gsk_stroke_set_dash (GskStroke *self,
const float *dash,
gsize n_dash)
{
float dash_length;
gsize i;
g_return_if_fail (self != NULL);
g_return_if_fail (dash != NULL || n_dash == 0);
dash_length = 0;
for (i = 0; i < n_dash; i++)
{
if (!(dash[i] >= 0)) /* should catch NaN */
{
g_critical ("invalid value in dash array at position %zu", i);
return;
}
dash_length += dash[i];
}
self->dash_length = dash_length;
g_free (self->dash);
self->dash = g_memdup (dash, sizeof (gfloat) * n_dash);
self->n_dash = n_dash;
}
/**
* gsk_stroke_get_dash:
* @self: a #GskStroke
* @n_dash: (out) (caller allocates): number of elements
* in the array returned
*
* Gets the dash array in use or %NULL if dashing is disabled.
*
* Returns: (array length=n_dash) (transfer none) (allow none):
* The dash array or %NULL if the dash array is empty.
**/
const float *
gsk_stroke_get_dash (const GskStroke *self,
gsize *n_dash)
{
g_return_val_if_fail (self != NULL, NULL);
g_return_val_if_fail (n_dash != NULL, NULL);
*n_dash = self->n_dash;
return self->dash;
}
/**
* gsk_stroke_set_dash_offset:
* @self: a #GskStroke
* @offset: offset into the dash pattern
*
* Sets the offset into the dash pattern set via gsk_stroke_set_dash() where
* dashing should begin.
*
* This is an offset into the length of the path, not an index into the array values of
* the dash array.
**/
void
gsk_stroke_set_dash_offset (GskStroke *self,
float offset)
{
g_return_if_fail (self != NULL);
self->dash_offset = offset;
}
/**
* gsk_stroke_get_dash_offset:
* @self: a #GskStroke
*
* Returns the dash_offset of a #GskStroke.
*/
float
gsk_stroke_get_dash_offset (const GskStroke *self)
{
g_return_val_if_fail (self != NULL, 4.f);
return self->dash_offset;
}
+87
View File
@@ -0,0 +1,87 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GSK_STROKE_H__
#define __GSK_STROKE_H__
#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gsk/gsk.h> can be included directly."
#endif
#include <gsk/gsktypes.h>
G_BEGIN_DECLS
#define GSK_TYPE_STROKE (gsk_stroke_get_type ())
GDK_AVAILABLE_IN_ALL
GType gsk_stroke_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GskStroke * gsk_stroke_new (float line_width);
GDK_AVAILABLE_IN_ALL
GskStroke * gsk_stroke_copy (const GskStroke *other);
GDK_AVAILABLE_IN_ALL
void gsk_stroke_free (GskStroke *self);
GDK_AVAILABLE_IN_ALL
gboolean gsk_stroke_equal (gconstpointer stroke1,
gconstpointer stroke2);
GDK_AVAILABLE_IN_ALL
void gsk_stroke_set_line_width (GskStroke *self,
float line_width);
GDK_AVAILABLE_IN_ALL
float gsk_stroke_get_line_width (const GskStroke *self);
GDK_AVAILABLE_IN_ALL
void gsk_stroke_set_line_cap (GskStroke *self,
GskLineCap line_cap);
GDK_AVAILABLE_IN_ALL
GskLineCap gsk_stroke_get_line_cap (const GskStroke *self);
GDK_AVAILABLE_IN_ALL
void gsk_stroke_set_line_join (GskStroke *self,
GskLineJoin line_join);
GDK_AVAILABLE_IN_ALL
GskLineJoin gsk_stroke_get_line_join (const GskStroke *self);
GDK_AVAILABLE_IN_ALL
void gsk_stroke_set_miter_limit (GskStroke *self,
float limit);
GDK_AVAILABLE_IN_ALL
float gsk_stroke_get_miter_limit (const GskStroke *self);
GDK_AVAILABLE_IN_ALL
void gsk_stroke_set_dash (GskStroke *self,
const float *dash,
gsize n_dash);
GDK_AVAILABLE_IN_ALL
const float * gsk_stroke_get_dash (const GskStroke *self,
gsize *n_dash);
GDK_AVAILABLE_IN_ALL
void gsk_stroke_set_dash_offset (GskStroke *self,
float offset);
GDK_AVAILABLE_IN_ALL
float gsk_stroke_get_dash_offset (const GskStroke *self);
G_END_DECLS
#endif /* __GSK_STROKE_H__ */
+63
View File
@@ -0,0 +1,63 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GSK_STROKE_PRIVATE_H__
#define __GSK_STROKE_PRIVATE_H__
#include "gskstroke.h"
G_BEGIN_DECLS
struct _GskStroke
{
float line_width;
GskLineCap line_cap;
GskLineJoin line_join;
float miter_limit;
float *dash;
gsize n_dash;
float dash_length; /* sum of all dashes in the array */
float dash_offset;
};
static inline void
gsk_stroke_init_copy (GskStroke *stroke,
const GskStroke *other)
{
*stroke = *other;
stroke->dash = g_memdup (other->dash, stroke->n_dash * sizeof (float));
}
static inline void
gsk_stroke_clear (GskStroke *stroke)
{
g_clear_pointer (&stroke->dash, g_free);
stroke->n_dash = 0; /* better safe than sorry */
}
void gsk_stroke_to_cairo (const GskStroke *self,
cairo_t *cr);
G_END_DECLS
#endif /* __GSK_STROKE_PRIVATE_H__ */
+4
View File
@@ -26,7 +26,11 @@
#include <gdk/gdk.h>
#include <gsk/gskenums.h>
typedef struct _GskPath GskPath;
typedef struct _GskPathBuilder GskPathBuilder;
typedef struct _GskPathMeasure GskPathMeasure;
typedef struct _GskRenderer GskRenderer;
typedef struct _GskStroke GskStroke;
typedef struct _GskTransform GskTransform;
#endif /* __GSK_TYPES_H__ */
+14 -1
View File
@@ -8,6 +8,7 @@ gsk_private_gl_shaders = [
'resources/glsl/color.glsl',
'resources/glsl/linear_gradient.glsl',
'resources/glsl/radial_gradient.glsl',
'resources/glsl/conic_gradient.glsl',
'resources/glsl/color_matrix.glsl',
'resources/glsl/blur.glsl',
'resources/glsl/inset_shadow.glsl',
@@ -20,23 +21,31 @@ gsk_private_gl_shaders = [
]
gsk_public_sources = files([
'gskdiff.c',
'gskcairorenderer.c',
'gskdiff.c',
'gskglshader.c',
'gskpath.c',
'gskpathbuilder.c',
'gskpathdash.c',
'gskpathmeasure.c',
'gskrenderer.c',
'gskrendernode.c',
'gskrendernodeimpl.c',
'gskrendernodeparser.c',
'gskroundedrect.c',
'gskstroke.c',
'gsktransform.c',
'gl/gskglrenderer.c',
])
gsk_private_sources = files([
'gskcairoblur.c',
'gskcontour.c',
'gskcurve.c',
'gskdebug.c',
'gskprivate.c',
'gskprofiler.c',
'gskspline.c',
'gl/gskglshaderbuilder.c',
'gl/gskglprofiler.c',
'gl/gskglglyphcache.c',
@@ -53,9 +62,13 @@ gsk_public_headers = files([
'gskcairorenderer.h',
'gskenums.h',
'gskglshader.h',
'gskpath.h',
'gskpathbuilder.h',
'gskpathmeasure.h',
'gskrenderer.h',
'gskrendernode.h',
'gskroundedrect.h',
'gskstroke.h',
'gsktransform.h',
'gsktypes.h',
'gsk-autocleanup.h',
+69
View File
@@ -0,0 +1,69 @@
// VERTEX_SHADER
uniform vec2 u_center;
uniform float u_rotation;
uniform float u_color_stops[6 * 5];
uniform int u_num_color_stops;
const float PI = 3.1415926535897932384626433832795;
_OUT_ vec2 center;
_OUT_ float rotation;
_OUT_ vec4 color_stops[6];
_OUT_ float color_offsets[6];
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
// The -90 is because conics point to the top by default
rotation = mod (u_rotation - 90, 360.0);
if (rotation < 0)
rotation += 360.0;
rotation = PI / 180.0 * rotation;
center = (u_modelview * vec4(u_center, 0, 1)).xy;
for (int i = 0; i < u_num_color_stops; i ++) {
color_offsets[i] = u_color_stops[(i * 5) + 0];
color_stops[i] = gsk_premultiply(vec4(u_color_stops[(i * 5) + 1],
u_color_stops[(i * 5) + 2],
u_color_stops[(i * 5) + 3],
u_color_stops[(i * 5) + 4]));
}
}
// FRAGMENT_SHADER:
#ifdef GSK_LEGACY
uniform int u_num_color_stops;
#else
uniform highp int u_num_color_stops; // Why? Because it works like this.
#endif
const float PI = 3.1415926535897932384626433832795;
_IN_ vec2 center;
_IN_ float rotation;
_IN_ vec4 color_stops[6];
_IN_ float color_offsets[6];
void main() {
// Position relative to center
vec2 pos = gsk_get_frag_coord() - center;
// direction of point in range [-PI, PI]
float angle = atan (pos.y, pos.x);
// rotate, it's now [-2 * PI, PI]
angle -= rotation;
// fract() does the modulo here, so now we have progress
// into the current conic
float offset = fract (angle / 2 / PI + 2);
vec4 color = color_stops[0];
for (int i = 1; i < u_num_color_stops; i ++) {
if (offset >= color_offsets[i - 1]) {
float o = (offset - color_offsets[i - 1]) / (color_offsets[i] - color_offsets[i - 1]);
color = mix(color_stops[i - 1], color_stops[i], clamp(o, 0.0, 1.0));
}
}
gskSetOutputColor(color * u_alpha);
}
+3
View File
@@ -259,6 +259,9 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self,
case GSK_SHADOW_NODE:
case GSK_RADIAL_GRADIENT_NODE:
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
case GSK_CONIC_GRADIENT_NODE:
case GSK_FILL_NODE:
case GSK_STROKE_NODE:
default:
FALLBACK ("Unsupported node '%s'", g_type_name_from_instance ((GTypeInstance *) node));
+12 -4
View File
@@ -1598,7 +1598,7 @@ get_bus_address_dbus (GdkDisplay *display)
if (error != NULL)
{
g_critical ("Unable to acquire session bus: %s", error->message);
GTK_NOTE (A11Y, g_message ("Unable to acquire session bus: %s", error->message));
g_error_free (error);
return NULL;
}
@@ -1615,8 +1615,8 @@ get_bus_address_dbus (GdkDisplay *display)
&error);
if (error != NULL)
{
g_critical ("Unable to acquire the address of the accessibility bus: %s",
error->message);
GTK_NOTE (A11Y, g_message ("Unable to acquire the address of the accessibility bus: %s",
error->message));
g_error_free (error);
}
@@ -1711,9 +1711,17 @@ gtk_at_spi_create_context (GtkAccessibleRole accessible_role,
g_return_val_if_fail (GTK_IS_ACCESSIBLE (accessible), NULL);
g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
const char *bus_address = get_bus_address (display);
static const char *bus_address;
if (bus_address == NULL)
{
bus_address = get_bus_address (display);
if (bus_address == NULL)
bus_address = "";
}
if (*bus_address == '\0')
return NULL;
#if defined(GDK_WINDOWING_WAYLAND)
+4
View File
@@ -1132,6 +1132,8 @@ gtk_atspi_disconnect_selection_signals (GtkAccessible *accessible)
SelectionChanged *changed;
changed = g_object_get_data (G_OBJECT (accessible), "accessible-selection-data");
if (changed == NULL)
return;
g_signal_handlers_disconnect_by_func (accessible, changed->changed, changed->data);
@@ -1143,6 +1145,8 @@ gtk_atspi_disconnect_selection_signals (GtkAccessible *accessible)
SelectionChanged *changed;
changed = g_object_get_data (G_OBJECT (accessible), "accessible-selection-data");
if (changed == NULL)
return;
g_signal_handlers_disconnect_by_func (notebook, changed->changed, changed->data);
+8
View File
@@ -1526,9 +1526,15 @@ gtk_atspi_connect_text_signals (GtkAccessible *accessible,
void
gtk_atspi_disconnect_text_signals (GtkAccessible *accessible)
{
if (!GTK_IS_EDITABLE (accessible) &&
!GTK_IS_TEXT_VIEW (accessible))
return;
TextChanged *changed;
changed = g_object_get_data (G_OBJECT (accessible), "accessible-text-data");
if (changed == NULL)
return;
if (GTK_IS_EDITABLE (accessible))
{
@@ -1544,6 +1550,7 @@ gtk_atspi_disconnect_text_signals (GtkAccessible *accessible)
else if (GTK_IS_TEXT_VIEW (accessible))
{
g_signal_handlers_disconnect_by_func (accessible, buffer_changed, changed);
if (changed->buffer)
{
g_signal_handlers_disconnect_by_func (changed->buffer, insert_range_cb, changed);
@@ -1551,6 +1558,7 @@ gtk_atspi_disconnect_text_signals (GtkAccessible *accessible)
g_signal_handlers_disconnect_by_func (changed->buffer, delete_range_after_cb, changed);
g_signal_handlers_disconnect_by_func (changed->buffer, mark_set_cb, changed);
}
g_clear_object (&changed->buffer);
}
-1
View File
@@ -66,7 +66,6 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkEntry, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkEntryCompletion, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkEventController, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkExpander, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkFileChooserButton, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkFileChooserDialog, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkFileChooserWidget, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkFileFilter, g_object_unref)
-1
View File
@@ -119,7 +119,6 @@
#include <gtk/gtkfixed.h>
#include <gtk/gtkfixedlayout.h>
#include <gtk/gtkfilechooser.h>
#include <gtk/gtkfilechooserbutton.h>
#include <gtk/gtkfilechooserdialog.h>
#include <gtk/gtkfilechoosernative.h>
#include <gtk/gtkfilechooserwidget.h>
+13
View File
@@ -315,6 +315,19 @@ gtk_action_muxer_find (GtkActionMuxer *muxer,
return NULL;
}
GActionGroup *
gtk_action_muxer_get_group (GtkActionMuxer *muxer,
const char *group_name)
{
Group *group;
group = g_hash_table_lookup (muxer->groups, group_name);
if (group)
return group->group;
return NULL;
}
static inline Action *
find_observers (GtkActionMuxer *muxer,
const char *action_name)
+2
View File
@@ -60,6 +60,8 @@ void gtk_action_muxer_remove (GtkActi
GActionGroup * gtk_action_muxer_find (GtkActionMuxer *muxer,
const char *action_name,
const char **unprefixed_name);
GActionGroup * gtk_action_muxer_get_group (GtkActionMuxer *muxer,
const char *group_name);
GtkActionMuxer * gtk_action_muxer_get_parent (GtkActionMuxer *muxer);
void gtk_action_muxer_set_parent (GtkActionMuxer *muxer,
+7
View File
@@ -715,6 +715,13 @@ gtk_app_chooser_button_class_init (GtkAppChooserButtonClass *klass)
G_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (oclass, NUM_PROPERTIES, properties);
/**
* GtkAppChooserButton::changed:
* @self: the object which received the signal
*
* Emitted when the active application on the #GtkAppChooserButton
* changes.
*/
signals[SIGNAL_CHANGED] =
g_signal_new (I_("changed"),
G_OBJECT_CLASS_TYPE (klass),
+26
View File
@@ -250,12 +250,38 @@ gtk_application_impl_quartz_shutdown (GtkApplicationImpl *impl)
quartz->inhibitors = NULL;
}
static void
on_window_unmap_cb (GtkApplicationImpl *impl,
GtkWindow *window)
{
GtkApplicationImplQuartz *quartz = (GtkApplicationImplQuartz *) impl;
if ((GActionGroup *)window == gtk_action_muxer_get_group (quartz->muxer, "win"))
gtk_action_muxer_remove (quartz->muxer, "win");
}
static void
gtk_application_impl_quartz_active_window_changed (GtkApplicationImpl *impl,
GtkWindow *window)
{
GtkApplicationImplQuartz *quartz = (GtkApplicationImplQuartz *) impl;
/* Track unmapping of the window so we can clear the "win" field.
* Without this, we might hold on to a reference of the window
* preventing it from getting disposed.
*/
if (window != NULL && !g_object_get_data (G_OBJECT (window), "quartz-muxer-umap"))
{
gulong handler_id = g_signal_connect_object (window,
"unmap",
G_CALLBACK (on_window_unmap_cb),
impl,
G_CONNECT_SWAPPED);
g_object_set_data (G_OBJECT (window),
"quartz-muxer-unmap",
GSIZE_TO_POINTER (handler_id));
}
gtk_action_muxer_remove (quartz->muxer, "win");
if (G_IS_ACTION_GROUP (window))
+6
View File
@@ -560,6 +560,12 @@ gtk_assistant_class_init (GtkAssistantClass *class)
NULL,
G_TYPE_NONE, 0);
/**
* GtkAssistant::escape
* @assistant: the #GtkAssistant
*
* The action signal for the Escape binding.
*/
signals[ESCAPE] =
g_signal_new_class_handler (I_("escape"),
G_TYPE_FROM_CLASS (gobject_class),
+1
View File
@@ -143,6 +143,7 @@ typedef struct _GtkBitsetIter GtkBitsetIter;
struct _GtkBitsetIter
{
/*< private >*/
gpointer private_data[10];
};

Some files were not shown because too many files have changed in this diff Show More