Compare commits

..

118 Commits

Author SHA1 Message Date
Matthias Clasen 805f3bb123 vulkan: Give up on VkResult
This enumeration is not managed in a sane way.
Just report the numbers.
2020-12-28 19:04:35 -05:00
Benjamin Otte 0fcf01ddd5 Merge branch 'gtkmediafile-pixel-aspect-ratio' into 'master'
gtkmediafile: Consider pixel-aspect-ratio for rendering video with the correct aspect ratio

Closes #3516

See merge request GNOME/gtk!3007
2020-12-28 14:54:30 +00:00
Asier Sarasua Garmendia 1119a74ff0 Update Basque translation 2020-12-27 17:56:02 +00:00
Sebastian Dröge 18ea60e235 gtkmediafile: Consider pixel-aspect-ratio for rendering video with the correct aspect ratio
Fixes https://gitlab.gnome.org/GNOME/gtk/-/issues/3516
2020-12-27 19:33:55 +02:00
Matthias Clasen d752828977 Merge branch 'wip/jtojnar/trivial-fix-pc-vulcan' into 'master'
build: Fix vulkan reference in pc file

Closes #3517

See merge request GNOME/gtk!3006
2020-12-27 17:06:52 +00:00
Fran Dieguez 1034271d9a Update Galician translation 2020-12-27 16:56:56 +00:00
Fran Dieguez f013d3b5d6 Update Galician translation 2020-12-27 16:51:38 +00:00
Benjamin Otte 5f41d26abc Merge branch 'gtkgstsink-unmap-only-when-done' into 'master'
gtkmediafile: Only unmap the GstVideoFrame in the GBytes destroy notify

See merge request GNOME/gtk!3003
2020-12-27 06:17:36 +00:00
Jan Tojnar 7d5826ccf7 build: Fix vulkan reference in pc file
A mistake in string concatenation caused the vulcan dependency to be omitted.

Fixes: https://gitlab.gnome.org/GNOME/gtk/-/issues/3517
2020-12-27 06:42:29 +01:00
Matthias Clasen daded2bc86 Merge branch 'matthiasc/for-master' into 'master'
Matthiasc/for master

See merge request GNOME/gtk!3004
2020-12-27 05:21:26 +00:00
Matthias Clasen bc7619abaf Merge branch 'gtkclip' into 'master'
gtk: remove GtkClipboard leftovers

See merge request GNOME/gtk!2991
2020-12-27 04:32:26 +00:00
Matthias Clasen 459d6e1349 Merge branch 'wip/carlosg/for-master' into 'master'
gtk/gesturestylus: Convert motion history from surface coordinates

Closes #3491

See merge request GNOME/gtk!2999
2020-12-27 04:30:38 +00:00
Sebastian Dröge 6e108d310a gtkmediafile: Only unmap the GstVideoFrame in the GBytes destroy notify
The memory pointed to by GstVideoFrame::plane_data becomes invalid after
unmapping causing the GBytes to point at some random memory if the
unmapping is not deferred until its destroy notify.

When the GStreamer buffer is backed by normal system memory this is not
a problem but if it is backed by e.g. an OpenGL texture, dmabuf or some
other hardware-specific memory this will otherwise cause interesting
problems.
2020-12-26 14:26:23 +02:00
Benjamin Otte 9c84fa127a Merge branch 'unused-static-inline' into 'master'
Remove unused static inline function

See merge request GNOME/gtk!3002
2020-12-25 18:05:11 +00:00
Emmanuel Gil Peyrot 84c6708366 Remove unused static inline function
This was causing a warning on AArch64 since __LITTLE_ENDIAN__ and
__BIG_ENDIAN__ weren’t set, but the functions were actually completely
unused.
2020-12-25 16:16:43 +01:00
Benjamin Otte f7e3016949 Merge branch 'gles-gears' into 'master'
Fix gtk4-demo’s gears demo on OpenGL ES

See merge request GNOME/gtk!3000
2020-12-25 01:36:34 +00:00
Emmanuel Gil Peyrot dd1110ca5c Fix some of gtk4-demo’s transitions example on OpenGL ES
Same issue as the previous commit, int+float is error, and pow() only
works on floats.
2020-12-25 01:36:21 +01:00
Emmanuel Gil Peyrot 28c4adac24 Fix gtk4-demo’s gears demo on OpenGL ES
Here is a command to reproduce this testcase:
GDK_DEBUG=gl-gles gtk4-demo --run gears

Without this patch, Mesa throws this compile error:
0:130(13): error: no matching function for call to `mod(error, float)'; candidates are:

This is caused by `u_rotation - 90` being of type error since
`u_rotation` is a float and it’s illegal to subtract it with an integer.
2020-12-25 01:21:08 +01:00
Carlos Garnacho cf20cfd31e gtk/gesturestylus: Convert motion history from surface coordinates
Motion history coordinates are based on GdkSurface coordinates. Transform
the coordinates from the GtkNative they are received on, accounting for the
possible transforms (e.g. due to window borders and shadows).

Fixes: https://gitlab.gnome.org/GNOME/gtk/-/issues/3491
2020-12-24 19:59:51 +01:00
Matthias Clasen 7e579bb59a Merge branch 'wip/otte/for-master' into 'master'
Wip/otte/for master

See merge request GNOME/gtk!2997
2020-12-24 07:13:22 +00:00
Benjamin Otte 96e1b85c2c gdkarray: Add a "stolen" boolean to splice()
If set to TRUE, does not call the free func for the removed items.

This can be used to move items between arrays without having to do the
refcounting dance.
2020-12-24 06:38:45 +01:00
Benjamin Otte 71e616d17e listview: Use the correct scroll policy
Use the horizontal policy for horizontal decisions, not the vertical
one.

This broke in 0011ce949c.
2020-12-24 06:38:45 +01:00
Benjamin Otte f85d939e5e docs: Put render nodes in their own sections 2020-12-24 06:38:45 +01:00
Benjamin Otte 8d014d6cb0 roundedbox: Remove unused API 2020-12-24 06:38:45 +01:00
Benjamin Otte f7f8990f7a picture: Fix copy/paste error in docs 2020-12-24 06:38:45 +01:00
Benjamin Otte d6f288427a video: When autoplaying, start playing once the video is prepared
This fixe video sources with longer loading times not automatically
playing.
2020-12-24 06:38:45 +01:00
Benjamin Otte 2a8f371643 gtk-demo: Don't put the search bar in the scroll area
1. That's a bad idea UI wise as you can't see the search after you
   scrolled.

2. That's a bad idea code-wise because Listviews need to be put into
   a ScrolledWindow or they won't scroll.
2020-12-24 06:38:45 +01:00
Benjamin Otte dee863dbb2 rendernode: Bail if matrix is invalid
Invalid matrices are okay in GSK (and GL), but not in Cairo.

Testcase included.
2020-12-24 06:38:45 +01:00
A S Alam 7d6877ec80 Update Punjabi translation 2020-12-23 19:48:28 +00:00
Danial Behzadi f426d23690 Update Persian translation 2020-12-23 15:53:07 +00:00
Matthias Clasen 6bd0dc3e78 colorbutton: Correct the css docs
Mention the wrapper colorbutton node.
2020-12-23 09:03:07 -05:00
Matthias Clasen 4f0c920a8e fontbutton: Correct the css docs
Mention the wrapper fontbutton node.
2020-12-23 08:59:58 -05:00
Matthias Clasen b0b793dd94 menubutton: Correct the css docs
We are using menubutton instead of button.popup now.
2020-12-23 08:56:18 -05:00
Matthias Clasen efbb649eee modelbutton: Center titles
The centering of titles was broken in b5b81dea7f.
Make it mostly work again by adding an empty end indicator.

Related: #3405
2020-12-23 00:51:51 -05:00
Matthias Clasen e128a77eed Merge branch 'matthiasc/for-master' into 'master'
window: Release application in destroy

See merge request GNOME/gtk!2994
2020-12-23 03:01:55 +00:00
Matthias Clasen 5cd2ee927a window: Release application in destroy
We used to do this; it was lost in some life-cycle
reshuffling.
2020-12-22 20:34:13 -05:00
Marc-André Lureau 90dfb5e138 gtk: remove GtkClipboard leftovers
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
2020-12-22 16:39:54 +04:00
Matthias Clasen e854b90293 Merge branch 'wip/baedert/for-master' into 'master'
Wip/baedert/for master

See merge request GNOME/gtk!2990
2020-12-22 04:53:31 +00:00
Matthias Clasen 6d042d9e61 Merge branch 'matthiasc/for-master' into 'master'
Matthiasc/for master

See merge request GNOME/gtk!2988
2020-12-22 02:29:03 +00:00
Matthias Clasen 4310f24a4e Fix warnings with Vulkan < 162
Tiring to watch the Vulkan people stumble through
ABI and API compat, but what can you do.
2020-12-21 20:00:16 -05:00
Matthias Clasen aebf8e3c29 Merge branch 'mcatanzaro/fix-gtk-builder-tool' into 'master'
Fix buffer overruns in 'gtk-builder-tool simplify'

See merge request GNOME/gtk!2987
2020-12-21 22:49:42 +00:00
Matthias Clasen fcd449f0d4 modelbutton: Remove an unused field
We're not using the centered field anymore.
2020-12-21 17:45:01 -05:00
Michael Catanzaro ab1b117d91 Fix buffer overruns in 'gtk-builder-tool simplify'
Oops, here we allocate only one byte for each pointer we want to store.
We need to multiply by the size of a pointer.
2020-12-21 15:39:34 -06:00
Emmanuele Bassi 1253296387 Merge branch 'fix-printbackends' into 'master'
Fix the value for the print-backends setting

Closes #3486

See merge request GNOME/gtk!2986
2020-12-21 20:04:26 +00:00
Matthias Clasen 466484176f Merge branch 'ebassi/docs-for-master' into 'master'
Ebassi/docs for master

See merge request GNOME/gtk!2983
2020-12-21 19:22:38 +00:00
Matthias Clasen 20289ad8e1 Fix the value for the print-backends setting
This was broken in the meson conversion, causing
us to default to "" instead of the built print
backends.

Fixes: #3486
2020-12-21 14:10:03 -05:00
Matthias Clasen 0abb8c6862 Merge branch 'matthiasc/for-master' into 'master'
Matthiasc/for master

See merge request GNOME/gtk!2984
2020-12-21 19:00:10 +00:00
Matthias Clasen 147386189f inspector: Show glyphs as in node files
Reuse the glyph serialization code from gsk to
show glyphs in the same way here.
2020-12-21 13:11:46 -05:00
Matthias Clasen 9cec9e4ce2 gsk: Export the glyph serialization privately
We want to use the same serialization in the inspector,
so export this function.
2020-12-21 13:11:08 -05:00
Matthias Clasen 26119af7a6 gsk: Improve glyph serialization
The special case for ASCII glyphs is unfortunately not
working very well, because of an oversight in pango:
When I added subpixel positioning, I made pango_shape()
default to not rounding, and make PangoLayout call
pango_shape_with_flags() and pass the rounding information
down. The upshot is that we need to use the _with_flags
variant here and tell it to round position, so it matches
what the text node contains.
2020-12-21 13:08:34 -05:00
Timm Bäder 478d1f71e3 Merge branch 'fix-node-glyphs' into 'master'
gsk: Fix serialization of glyphs

Closes #3496

See merge request GNOME/gtk!2982
2020-12-21 17:16:38 +00:00
Emmanuele Bassi b1d952b8a2 docs: Add examples for GtkDropTarget
Like we did for GtkDragSource.
2020-12-21 17:11:42 +00:00
Emmanuele Bassi b9bcdbbfdc docs: Add examples of GtkDragSource use
The description is a bit terse; we should help out application
developers some more.
2020-12-21 17:03:59 +00:00
Emmanuele Bassi 6c31ed53f4 docs: Fix typo and whitespace 2020-12-21 17:03:42 +00:00
Matthias Clasen aa95ae774f gsk: Fix serialization of glyphs
We were forgetting to put a space between two numbers
in some cases, confusing the parser.

Test included.

Fixes: #3496
2020-12-21 10:49:56 -05:00
Emmanuele Bassi 65adfd9243 Merge branch 'box_docs' into 'master'
box: improve documentation

Closes #3499

See merge request GNOME/gtk!2981
2020-12-21 15:40:07 +00:00
Yetizone c4a8c0893b box: improve documentation
Rephrase a function parameter description and remove "." from all function parameters descriptions

Fixes https://gitlab.gnome.org/GNOME/gtk/-/issues/3499
2020-12-21 17:26:23 +02:00
Timm Bäder e90f5e8a20 gskpango: Don't copy fg color
No need to do that here, we copy it in to the individual render nodes
anyway.
2020-12-21 15:23:25 +01:00
Timm Bäder 526c76181e gl renderer: Check for pointer equality in rounded_rect_equal 2020-12-21 15:22:08 +01:00
Timm Bäder cf352374ae gskpango: Correct a comment
Not all other draw calls will use cairo.
2020-12-21 13:23:28 +01:00
Timm Bäder 98b99201b0 gskpango: Remove bounds rect
We only need this to render shapes and trapezoids, i.e. only when
falling back to cairo. Remove code to measure the layout and convert the
ink_rect to a graphene_rect_t from gtk_snapshot_append_layout() and do
it when drawing shapes and trapezoids instead.
2020-12-21 12:56:38 +01:00
Timm Bäder bce4d669ee gskpango: Fix a potential cairo_t leak
The previous early return did not cairo_destroy() it.
2020-12-21 12:49:06 +01:00
Timm Bäder e7a326e230 label: Refactor get_layout_location()
Rename a few local variables to be clearer about whether they belong to
the widget or to the layout.

Remove a workaround for an old bug that is no longer valid. We don't
underallocate the layout anymore. Aways center vertically, with respect
to the yalign.
2020-12-21 12:44:01 +01:00
Timm Bäder 9eaeaa8452 label: Refactor snapshot()
Use two early-out style if statements to reduce indentation in this
entire function.
2020-12-21 12:32:32 +01:00
Timm Bäder 2031c5bf90 textlayout: Remove dead offset_y code 2020-12-21 10:26:03 +01:00
Timm Bäder 9fefa7b44c rendernode: Different uniformity in border nodes
Means they are different and a diff is impossible.
2020-12-21 10:26:03 +01:00
Timm Bäder a2a70c3b65 rendernode: Sort diff() requirements by complexity 2020-12-21 10:26:03 +01:00
Timm Bäder 0b4ed8d791 renderborder: Avoid 0 width outline color nodes 2020-12-21 10:26:03 +01:00
Timm Bäder c34f98931e layoutoverlay: Avoid adding transparent color nodes
We have a region for the border here, but the color is defined as
transparent black because we don't want to render anything for it.

This way, the generated .node file for the listbox demo in gtk4-demo
with enabled layout borders is 3.5MB instead of 3.8MB.
2020-12-21 10:26:03 +01:00
Timm Bäder 2feac2772f layoutoverlay: Avoid adding 0 sized color nodes
This way, the output node file of the listbox demo in gtk4-demo with
enabled layout borders is only 3.8MB instead of 12MB.
2020-12-21 10:26:03 +01:00
Timm Bäder fdda4e4e99 GskTransform: Add nullable annotations to _equals()
Both can be NULL here.
2020-12-21 10:26:03 +01:00
Timm Bäder e1cf0cff26 gl renderer: Proper state tracking for color matrix ops 2020-12-21 10:26:03 +01:00
Timm Bäder 634fa772c9 gl renderer: Add missing debug output for repeat ops 2020-12-21 10:26:03 +01:00
Timm Bäder 4b3bbe0ecd gl renderer: Create shader-global uniform ops on draw()
This way we can render the first frame of tests/testoutsetshadowdrawing
in 153 ops instead of 183.

And the first frame of gtk4-demo in 260 instead of 300.
2020-12-21 10:26:03 +01:00
Timm Bäder cde43541d0 gl renderer: Unify new clip/viewport rects in blur_texture() 2020-12-21 10:26:03 +01:00
Timm Bäder 186287ecfb GskTransform: Fix identity transform class name 2020-12-21 10:26:02 +01:00
Timm Bäder bd5d1615ac GskTransform: Avoid a ref+unref pair
If gsk_transform_is_identity() returned FALSE for the next transform,
the previous code did a ref + unref pair, even though it was unneeded.
2020-12-21 10:26:02 +01:00
Timm Bäder 21299cc7e4 icontheme: Make some constant values const 2020-12-21 10:26:02 +01:00
Timm Bäder e747ea7dfd pixbufutils: Only convert icon size to string once 2020-12-21 10:26:02 +01:00
Timm Bäder 810cd7881e icontheme: Make icon_paintable_ensure_texture() return an unowned value
It's private, has only one caller and that one doesn't need the extra
ref.
2020-12-21 10:26:02 +01:00
Timm Bäder a8213d7f45 gl renderer: Avoid setting modelview to identical values
The previous comment here was incorrect. We can save the op itself, we
just can't save us the entry in the modelview stack.
2020-12-21 10:26:02 +01:00
Timm Bäder e6f6d1e478 main: Add & use gdk_event_dup_axes()
The way this code is written trips up scan-build. Add
gdk_event_dup_axes() and use it in gtkmain.c.
2020-12-21 10:26:02 +01:00
Rafael Fontenelle 933d241bba Update Brazilian Portuguese translation 2020-12-21 07:02:13 +00:00
Matthias Clasen 7249c3a09b Merge branch 'ebassi/issue-3495' into 'master'
Use full path when calling gtk4-update-icon-cache

Closes #3495

See merge request GNOME/gtk!2979
2020-12-20 22:40:48 +00:00
Emmanuele Bassi d13f8ac3a9 Call the newly installed gtk4-update-icon-cache
Otherwise we rely on whatever it's inside the PATH.
2020-12-20 16:33:41 +00:00
Emmanuele Bassi fa35e3b874 Pass the bindir to the post-install script 2020-12-20 16:33:25 +00:00
Rafael Fontenelle 853883c6e5 Update Brazilian Portuguese translation 2020-12-20 14:06:52 +00:00
sicklylife b467279c7d Update Japanese translation 2020-12-20 08:03:00 +00:00
sicklylife c6a5ac1d16 Update Japanese translation 2020-12-20 07:58:09 +00:00
A S Alam e41288de4f Update Punjabi translation 2020-12-20 06:14:42 +00:00
Jordi Mas i Hernandez b886988a54 Update Catalan translation 2020-12-19 18:29:08 +00:00
Matthias Clasen bb8a459fe2 Merge branch 'matthiasc/for-master' into 'master'
Matthiasc/for master

See merge request GNOME/gtk!2978
2020-12-19 17:20:39 +00:00
Matthias Clasen d5143a1bf0 Revert "gl renderer: Render too big textures to the clipped area"
This reverts commit 7f6608cbed.
2020-12-19 11:43:56 -05:00
Matthias Clasen 12611fa142 Revert "gl renderer: Take a out graphene_rect in blur_node()"
This reverts commit 7eece7e769.
2020-12-19 11:43:43 -05:00
Matthias Clasen 3e319b409b Merge branch 'ebassi/issue-3492' into 'master'
3to4: Remove GtkCheckButton:draw-indicator

Closes #3492

See merge request GNOME/gtk!2976
2020-12-19 16:41:14 +00:00
Emmanuele Bassi f2b16836c2 3to4: Add test for GtkCheckButton:draw-indicator removal 2020-12-19 15:52:22 +00:00
Emmanuele Bassi 14bf45a0d5 3to4: Remove GtkCheckButton:draw-indicator
The property was removed from the widget, so we need to remove it from
UI files too.

Fixes: #3492
2020-12-18 23:14:24 +00:00
Timm Bäder 0c6a6b3370 Merge branch 'doc_backticks' into 'master'
docs: backtick fixes

See merge request GNOME/gtk!2975
2020-12-18 15:57:30 +00:00
wisp3rwind 9521f609d8 docs: backtick fixes
- in the gettext() examples, at least the _() triggers italics
- add missing closing backtick
2020-12-18 16:31:41 +01:00
Matthias Clasen 7533369695 Merge branch 'wip/baedert/for-master' into 'master'
Wip/baedert/for master

See merge request GNOME/gtk!2973
2020-12-18 05:06:35 +00:00
Matthias Clasen f31765d5dd Merge branch 'ebassi/mnemonic-label-remove' into 'master'
a11y: Fix removing mnemonic widgets

See merge request GNOME/gtk!2970
2020-12-18 00:00:48 +00:00
Matthias Clasen d33062795c Merge branch 'ebassi/docs-for-master' into 'master'
Ebassi/docs for master

See merge request GNOME/gtk!2971
2020-12-17 23:59:41 +00:00
Emmanuele Bassi 2da8d9b0ac Do not build gdk-pixbuf's man page
When using it as a sub-project. We don't really need them.
2020-12-17 20:50:16 +00:00
Matthias Clasen 3f56e8191f Merge branch 'shortcuts' into 'master'
docs: Add separation between kinds of shortcuts in Keyboard input

See merge request GNOME/gtk!2964
2020-12-17 19:43:38 +00:00
Chris Mayo bf4c3dc2c5 docs: Fix list of kinds of shortcuts in Keyboard input
Formatting in HTML was mangled.
2020-12-17 19:18:10 +00:00
Emmanuele Bassi a5365ec8cb docs: Add missing GTK types
If we don't specify the get_type() function, gtk-doc will not collect
properties and signals, and will use a different URL for the page.
2020-12-17 18:30:19 +00:00
Emmanuele Bassi ec94ec0286 Merge branch 'ebassi/password-entry-notify' into 'master'
Notify properties on deletion in PasswordEntryBuffer

Closes #3484

See merge request GNOME/gtk!2969
2020-12-17 15:51:04 +00:00
Emmanuele Bassi af0973c847 a11y: Fix removing mnemonic widgets
We must not use the variadic arguments version with GList, as it expects
a pointer to a GtkAccessible.
2020-12-17 15:49:00 +00:00
Emmanuele Bassi e71f6b0bc0 docs: Note the caveat on GtkEditable signals with delegates
We cannot bubble up ::insert-text and ::delete-text signals from the
delegate to the wrapper editable without causing an infinite recursion,
due to how the signals are designed; ideally, we would have decoupled
signal emission from virtual functions *before* releasing GTK4, but now
that we have, we can't break the contract.
2020-12-17 15:13:43 +00:00
Emmanuele Bassi 1738724e4a Notify properties on deletion in PasswordEntryBuffer
The GtkEntryBufferClass.deleted_text() behaviour changed between GTK3
and GTK4, which means any subclass of GtkEntryBuffer is now responsible
for emitting the "notify" signal for the "text" and "length" properties.

Without this, the GtkText delegate widget used by GtkPasswordEntry would
not be able to communicate changes in the contents of its buffer.

Fixes: #3484
2020-12-17 15:01:35 +00:00
Emmanuele Bassi e9e09be37e Merge branch 'issue-3483' into 'master'
Fix URLs for the download.gnome.org locations

Closes #3483

See merge request GNOME/gtk!2968
2020-12-17 13:47:01 +00:00
Matthias Clasen 8a923e02a1 Merge branch 'wip/jimmac/flat-button-tweaks' into 'master'
Adwaita: flat button adjustments

See merge request GNOME/gtk!2967
2020-12-17 12:52:22 +00:00
Emmanuele Bassi 4f12e2f711 Fix URLs for the download.gnome.org locations
Use a trailing slash, otherwise we end up on an out of date mirror.

Fixes: #3483
2020-12-17 12:46:12 +00:00
Jakub Steiner addcf57b4d Adwaita: flat button adjustments
- normally lighter (on bg_color)
- darker for headerbar
- undecorated checked buttons were never style properly (even gtk3)
  see page2 volume buttons

Finetunes https://gitlab.gnome.org/GNOME/gtk/-/issues/3427
2020-12-17 12:20:29 +01:00
Timm Bäder 7eece7e769 gl renderer: Take a out graphene_rect in blur_node() 2020-12-16 09:13:12 +01:00
Timm Bäder 7f6608cbed gl renderer: Render too big textures to the clipped area 2020-12-16 08:26:46 +01:00
Timm Bäder f816a8fb82 gl renderer: Unify viewport setting in add_offscreen_ops()
We use the same rect here a few times.
2020-12-16 08:26:46 +01:00
Timm Bäder 3c2484142d vulkancontext: Remove usage of VK_ERROR_INCOMPATIBLE_VERSION_KHR
According to
https://www.khronos.org/registry/vulkan/specs/1.0-wsi_extensions/html/chap46.html
this has been removed. It also breaks the build on my system
2020-12-16 08:26:46 +01:00
210 changed files with 25091 additions and 44802 deletions
+6 -6
View File
@@ -17,7 +17,7 @@ license fees or royalties.
The official download location
- https://download.gnome.org/sources/gtk+
- https://download.gnome.org/sources/gtk/
The official web site
@@ -49,11 +49,11 @@ In order to build GTK you will need:
You will also need various dependencies, based on the platform you are
building for:
- [GLib](https://download.gnome.org/sources/glib)
- [GdkPixbuf](https://download.gnome.org/sources/gdk-pixbuf)
- [GObject-Introspection](https://download.gnome.org/sources/gobject-introspection)
- [Cairo](https://www.cairographics.org)
- [Pango](https://download.gnome.org/sources/pango)
- [GLib](https://download.gnome.org/sources/glib/)
- [GdkPixbuf](https://download.gnome.org/sources/gdk-pixbuf/)
- [GObject-Introspection](https://download.gnome.org/sources/gobject-introspection/)
- [Cairo](https://www.cairographics.org/)
- [Pango](https://download.gnome.org/sources/pango/)
- [Epoxy](https://github.com/anholt/libepoxy)
- [Graphene](https://github.com/ebassi/graphene)
- [Xkb-common](https://github.com/xkbcommon/libxkbcommon)
+25 -23
View File
@@ -1,20 +1,21 @@
#!/usr/bin/env python3
import os
import sys
import subprocess
if 'DESTDIR' not in os.environ:
gtk_api_version = sys.argv[1]
gtk_abi_version = sys.argv[2]
import os
import sys
import subprocess
if 'DESTDIR' not in os.environ:
gtk_api_version = sys.argv[1]
gtk_abi_version = sys.argv[2]
gtk_libdir = sys.argv[3]
gtk_datadir = sys.argv[4]
gtk_moduledir = os.path.join(gtk_libdir, 'gtk-' + gtk_api_version, gtk_abi_version)
gtk_printmodule_dir = os.path.join(gtk_moduledir, 'printbackends')
gtk_immodule_dir = os.path.join(gtk_moduledir, 'immodules')
print('Compiling GSettings schemas...')
gtk_bindir = sys.argv[5]
gtk_moduledir = os.path.join(gtk_libdir, 'gtk-' + gtk_api_version, gtk_abi_version)
gtk_printmodule_dir = os.path.join(gtk_moduledir, 'printbackends')
gtk_immodule_dir = os.path.join(gtk_moduledir, 'immodules')
print('Compiling GSettings schemas...')
glib_compile_schemas = subprocess.check_output(['pkg-config',
'--variable=glib_compile_schemas',
'gio-2.0']).strip()
@@ -22,13 +23,14 @@ if 'DESTDIR' not in os.environ:
# pkg-config variables only available since GLib 2.62.0.
glib_compile_schemas = 'glib-compile-schemas'
subprocess.call([glib_compile_schemas,
os.path.join(gtk_datadir, 'glib-2.0', 'schemas')])
print('Updating icon cache...')
subprocess.call(['gtk4-update-icon-cache', '-q', '-t' ,'-f',
os.path.join(gtk_datadir, 'icons', 'hicolor')])
print('Updating module cache for print backends...')
os.path.join(gtk_datadir, 'glib-2.0', 'schemas')])
print('Updating icon cache...')
update_icon_cache = os.path.join(gtk_bindir, 'gtk4-update-icon-cache')
subprocess.call([update_icon_cache, '-q', '-t' ,'-f',
os.path.join(gtk_datadir, 'icons', 'hicolor')])
print('Updating module cache for print backends...')
os.makedirs(gtk_printmodule_dir, exist_ok=True)
gio_querymodules = subprocess.check_output(['pkg-config',
'--variable=gio_querymodules',
@@ -37,7 +39,7 @@ if 'DESTDIR' not in os.environ:
# pkg-config variables only available since GLib 2.62.0.
gio_querymodules = 'gio-querymodules'
subprocess.call([gio_querymodules, gtk_printmodule_dir])
print('Updating module cache for input methods...')
print('Updating module cache for input methods...')
os.makedirs(gtk_immodule_dir, exist_ok=True)
subprocess.call([gio_querymodules, gtk_immodule_dir])
+1 -1
View File
@@ -20,7 +20,7 @@ mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv)
float b = 1.0/distance (pos, p2);
float sum = r + g + b;
float alpha = 1.0 - pow (1.0/(sum), 40)*pow (10.0, 40*0.7);
float alpha = 1.0 - pow (1.0/(sum), 40.0)*pow (10.0, 40.0*0.7);
fragColor = vec4 (r*0.5, g*0.5, b*0.5, 1.0) * alpha;
}
+1 -1
View File
@@ -177,7 +177,7 @@ float ccell2(vec2 p, float r) {
float df(vec2 p, float scale, inout vec2 nn) {
p /= scale;
nn = hextile(p);
nn = round(nn);
nn = floor(nn + 0.5);
float r = hash(nn);
float d;;
+1 -1
View File
@@ -179,7 +179,7 @@ float ccell2(vec2 p, float r) {
float df(vec2 p, float scale, inout vec2 nn) {
p /= scale;
nn = hextile(p);
nn = round(nn);
nn = floor(nn + 0.5);
float r = hash(nn);
float d;;
-6
View File
@@ -319,9 +319,6 @@
<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>
@@ -406,9 +403,6 @@
<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>
-3
View File
@@ -68,9 +68,6 @@ 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',
-359
View File
@@ -1,359 +0,0 @@
/* 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
@@ -1,338 +0,0 @@
/* 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
@@ -1,590 +0,0 @@
/* 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
@@ -1,38 +0,0 @@
<?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>
+1 -1
View File
@@ -31,7 +31,7 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec
float propagation_length = time * wave_speed;
float t = (propagation_length - distance_from_center) / wave_length;
float offset_magnitude = 0;
float offset_magnitude = 0.0;
if (t > 0.0)
offset_magnitude = decay(wave_height * sin(t * 2.0 * PI), t);
-8
View File
@@ -23,14 +23,6 @@
<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/GskPathMeasure.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
View File
@@ -182,12 +182,6 @@ 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
@@ -241,7 +235,6 @@ GSK_TYPE_CONTAINER_NODE
GSK_TYPE_CONIC_GRADIENT_NODE
GSK_TYPE_CROSS_FADE_NODE
GSK_TYPE_DEBUG_NODE
GSK_TYPE_FILL_NODE
GSK_TYPE_GL_SHADER_NODE
GSK_TYPE_INSET_SHADOW_NODE
GSK_TYPE_LINEAR_GRADIENT_NODE
@@ -272,7 +265,6 @@ gsk_conic_gradient_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_gl_shader_node_get_type
gsk_inset_shadow_node_get_type
gsk_linear_gradient_node_get_type
@@ -314,113 +306,6 @@ gsk_rounded_rect_intersects_rect
GSK_TYPE_CORNER
</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
gsk_path_get_stroke_bounds
<SUBSECTION>
GskPathOperation
GskPathForeachFlags
GskPathForeachFunc
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
gsk_path_builder_add_path
gtk_path_builder_add_segment
<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>GskPathMeasure</FILE>
GskPathMeasure
gsk_path_measure_new
gsk_path_measure_new_with_tolerance
gsk_path_measure_ref
gsk_path_measure_unref
<SUBSECTION>
gsk_path_measure_get_path
gsk_path_measure_get_tolerance
gsk_path_measure_get_n_contours
gsk_path_measure_restrict_to_contour
<SUBSECTION>
gsk_path_measure_get_length
gsk_path_measure_is_closed
gsk_path_measure_get_point
gsk_path_measure_get_closest_point
gsk_path_measure_get_closest_point_full
gsk_path_measure_in_fill
<SUBSECTION Private>
GSK_TYPE_PATH_MEASURE
gsk_path_measure_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
-2
View File
@@ -4295,8 +4295,6 @@ 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
+8 -3
View File
@@ -19,8 +19,8 @@ gtk_assistant_page_get_type
gtk_at_context_get_type
gtk_bin_layout_get_type
gtk_bitset_get_type
gtk_expression_get_type
gtk_bookmark_list_get_type
gtk_bool_filter_get_type
gtk_box_get_type
gtk_box_layout_get_type
gtk_buildable_get_type
@@ -46,6 +46,7 @@ gtk_cell_renderer_spinner_get_type
gtk_cell_renderer_text_get_type
gtk_cell_renderer_toggle_get_type
gtk_cell_view_get_type
gtk_center_box_get_type
gtk_center_layout_get_type
gtk_check_button_get_type
gtk_closure_expression_get_type
@@ -65,14 +66,16 @@ gtk_constraint_target_get_type
gtk_css_provider_get_type
gtk_custom_filter_get_type
gtk_custom_sorter_get_type
gtk_custom_layout_get_type
gtk_dialog_get_type
gtk_directory_list_get_type
gtk_drag_icon_get_type
gtk_drag_source_get_type
gtk_drawing_area_get_type
gtk_drop_target_get_type
gtk_drop_target_async_get_type
gtk_drop_controller_motion_get_type
gtk_drop_down_get_type
gtk_drop_target_async_get_type
gtk_drop_target_get_type
gtk_editable_get_type
gtk_editable_label_get_type
gtk_emoji_chooser_get_type
@@ -87,6 +90,7 @@ gtk_event_controller_motion_get_type
gtk_event_controller_scroll_get_type
gtk_every_filter_get_type
gtk_expander_get_type
gtk_expression_get_type
gtk_file_chooser_dialog_get_type
gtk_file_chooser_get_type
gtk_file_chooser_native_get_type
@@ -250,6 +254,7 @@ gtk_video_get_type
gtk_viewport_get_type
gtk_volume_button_get_type
gtk_widget_get_type
gtk_widget_paintable_get_type
gtk_window_get_type
gtk_window_controls_get_type
gtk_window_group_get_type
+12 -15
View File
@@ -119,21 +119,18 @@ around or to activate a widget that does not currently have the focus.
GTK has traditionally supported different kinds of shortcuts:
Accelerators
: Accelerators are any other shortcuts that can be activated regardless
of where the focus is, and typically trigger global actions, such as
Ctrl-Q to quit an application.
Mnmemonics
: Mnemonics are usually triggered using Alt as a modifier for a letter.
They are used in places where a label is associated with a control,
and are indicated by underlining the letter in the label. As a special
case, inside menus (i.e. inside #GtkPopoverMenu), mnemonics can be
triggered without the modifier.
Key bindings
: Key bindings are specific to individual widgets, such as Ctrl-C or
Ctrl-V in an entry copy to or paste from the clipboard. They are only
triggered when the widget has focus.
- Accelerators are any other shortcuts that can be activated regardless
of where the focus is, and typically trigger global actions, such as
Ctrl-Q to quit an application.
- Mnemonics are usually triggered using Alt as a modifier for a letter.
They are used in places where a label is associated with a control,
and are indicated by underlining the letter in the label. As a special
case, inside menus (i.e. inside #GtkPopoverMenu), mnemonics can be
triggered without the modifier.
- Key bindings are specific to individual widgets, such as Ctrl-C or
Ctrl-V in an entry copy to or paste from the clipboard. They are only
triggered when the widget has focus.
GTK handles accelerators and mnemonics in a global scope, during the
capture phase, and key bindings locally, during the target phase.
+1 -1
View File
@@ -1062,7 +1062,7 @@ to start a drag manually, call gdk_drag_begin().
The ::drag-data-get signal has been replaced by the #GtkDragSource::prepare
signal, which returns a #GdkContentProvider for the drag operation.
The destination-side Drag-and-Drop apis in GTK 4 have also been changed
The destination-side Drag-and-Drop API in GTK 4 have also been changed
to use an event controller, #GtkDropTarget. Instead of calling
gtk_drag_dest_set() and connecting to #GtkWidget signals, you create
a #GtkDropTarget object, attach it to the widget with
+4 -4
View File
@@ -97,11 +97,11 @@ the question you have, this list is a good place to start.
#define N_(x) x
#define C_(ctx,x) pgettext (ctx, x)
You use N_() (N stands for no-op) to mark a string for translation in
You use `N_()` (N stands for no-op) to mark a string for translation in
a location where a function call to gettext() is not allowed, such as
in an array initializer. You eventually have to call gettext() on the
string to actually fetch the translation. _() both marks the string for
translation and actually translates it. The C_() macro (C stands for
string to actually fetch the translation. `_()` both marks the string for
translation and actually translates it. The `C_()` macro (C stands for
context) adds an additional context to the string that is marked for
translation, which can help to disambiguate short strings that might
need different translations in different parts of your program.
@@ -190,7 +190,7 @@ the question you have, this list is a good place to start.
encode string literals in UTF-8 by using octal or hexadecimal escapes
like `\212` or `\xa8` to encode each byte. This is portable, but
modifying the escaped strings is not very convenient. Be careful when
mixing hexadecimal escapes with ordinary text; `"\xa8abcd" is a string
mixing hexadecimal escapes with ordinary text; `"\xa8abcd"` is a string
of length 1 !
- Runtime conversion
+7 -4
View File
@@ -182,6 +182,7 @@ G_GNUC_UNUSED static inline void
gdk_array(splice) (GdkArray *self,
gsize pos,
gsize removed,
gboolean stolen,
_T_ *additions,
gsize added)
{
@@ -192,8 +193,9 @@ gdk_array(splice) (GdkArray *self,
g_assert (pos + removed <= size);
remaining = size - pos - removed;
gdk_array(free_elements) (gdk_array(index) (self, pos),
gdk_array(index) (self, pos + removed));
if (!stolen)
gdk_array(free_elements) (gdk_array(index) (self, pos),
gdk_array(index) (self, pos + removed));
gdk_array(reserve) (self, size - removed + added);
@@ -225,9 +227,9 @@ gdk_array(set_size) (GdkArray *self,
{
gsize old_size = gdk_array(get_size) (self);
if (new_size > old_size)
gdk_array(splice) (self, old_size, 0, NULL, new_size - old_size);
gdk_array(splice) (self, old_size, 0, FALSE, NULL, new_size - old_size);
else
gdk_array(splice) (self, new_size, old_size - new_size, NULL, 0);
gdk_array(splice) (self, new_size, old_size - new_size, FALSE, NULL, 0);
}
G_GNUC_UNUSED static void
@@ -241,6 +243,7 @@ gdk_array(append) (GdkArray *self,
gdk_array(splice) (self,
gdk_array(get_size) (self),
0,
FALSE,
#ifdef GDK_ARRAY_BY_VALUE
value,
#else
+16
View File
@@ -1138,6 +1138,22 @@ gdk_event_get_axes (GdkEvent *event,
return GDK_EVENT_GET_CLASS (event)->get_axes (event, axes, n_axes);
}
double *
gdk_event_dup_axes (GdkEvent *event)
{
double *axes;
guint n_axes;
if (gdk_event_get_axes (event, &axes, &n_axes))
{
double *axes_copy = g_memdup (axes, n_axes * sizeof (double));
return axes_copy;
}
return NULL;
}
/**
* gdk_event_get_event_type:
* @event: a #GdkEvent
+2
View File
@@ -603,6 +603,8 @@ void _gdk_event_queue_handle_motion_compression (GdkDisplay *display);
void gdk_event_queue_handle_scroll_compression (GdkDisplay *display);
void _gdk_event_queue_flush (GdkDisplay *display);
double * gdk_event_dup_axes (GdkEvent *event);
G_END_DECLS
+6 -140
View File
@@ -95,141 +95,7 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GdkVulkanContext, gdk_vulkan_context, GDK_TYPE
const char *
gdk_vulkan_strerror (VkResult result)
{
/* If your compiler brought you here with a warning about missing
* enumeration values, you're running a newer Vulkan version than
* the GTK developers (or you are a GTK developer) and have
* encountered a newly added Vulkan error message.
* You want to add it to this enum now.
*
* Because the Vulkan people don't make adding this too easy, here's
* the process to manage it:
* 1. go to
* https://github.com/KhronosGroup/Vulkan-Headers/blob/master/include/vulkan/vulkan_core.h
* 2. Find the line where this enum value was added.
* 3. Click the commit that added this line.
* 4. The commit you're looking at now should also change
* VK_HEADER_VERSION, find that number.
* 5. Use that number in the #ifdef when adding the enum value to
* this enum.
* 6. For the error message, look at the specification (the one
* that includes all extensions) at
* https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#VkResult
* 7. If this value has not been added to the specification yet,
* search for the error message in the text of specification.
* Often it will have a description that can be used as an error
* message.
* 8. If that didn't lead to one (or you are lazy), just use the
* literal string of the enum value as the error message. A
* GTK developer will add the correct one once it's added to the
* specification.
*/
switch (result)
{
case VK_SUCCESS:
return "Command successfully completed.";
case VK_NOT_READY:
return "A fence or query has not yet completed.";
case VK_TIMEOUT:
return "A wait operation has not completed in the specified time.";
case VK_EVENT_SET:
return "An event is signaled.";
case VK_EVENT_RESET:
return "An event is unsignaled.";
case VK_INCOMPLETE:
return "A return array was too small for the result.";
case VK_SUBOPTIMAL_KHR:
return "A swapchain no longer matches the surface properties exactly, but can still be used to present to the surface successfully.";
case VK_ERROR_OUT_OF_HOST_MEMORY:
return "A host memory allocation has failed.";
case VK_ERROR_OUT_OF_DEVICE_MEMORY:
return "A device memory allocation has failed.";
case VK_ERROR_INITIALIZATION_FAILED:
return "Initialization of an object could not be completed for implementation-specific reasons.";
case VK_ERROR_DEVICE_LOST:
return "The logical or physical device has been lost.";
case VK_ERROR_MEMORY_MAP_FAILED:
return "Mapping of a memory object has failed.";
case VK_ERROR_LAYER_NOT_PRESENT:
return "A requested layer is not present or could not be loaded.";
case VK_ERROR_EXTENSION_NOT_PRESENT:
return "A requested extension is not supported.";
case VK_ERROR_FEATURE_NOT_PRESENT:
return "A requested feature is not supported.";
case VK_ERROR_INCOMPATIBLE_DRIVER:
return "The requested version of Vulkan is not supported by the driver or is otherwise incompatible for implementation-specific reasons.";
case VK_ERROR_TOO_MANY_OBJECTS:
return "Too many objects of the type have already been created.";
case VK_ERROR_FORMAT_NOT_SUPPORTED:
return "A requested format is not supported on this device.";
#if VK_HEADER_VERSION >= 24
case VK_ERROR_FRAGMENTED_POOL:
return "A requested pool allocation has failed due to fragmentation of the pools memory.";
#endif
case VK_ERROR_SURFACE_LOST_KHR:
return "A surface is no longer available.";
case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR:
return "The requested window is already in use by Vulkan or another API in a manner which prevents it from being used again.";
case VK_ERROR_OUT_OF_DATE_KHR:
return "A surface has changed in such a way that it is no longer compatible with the swapchain.";
case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR:
return "The display used by a swapchain does not use the same presentable image layout, or is incompatible in a way that prevents sharing an image.";
case VK_ERROR_VALIDATION_FAILED_EXT:
return "The application caused the validation layer to fail.";
case VK_ERROR_INVALID_SHADER_NV:
return "One or more shaders failed to compile or link.";
#if VK_HEADER_VERSION >= 39
case VK_ERROR_OUT_OF_POOL_MEMORY_KHR:
return "A pool memory allocation has failed.";
#endif
#if VK_HEADER_VERSION >= 54
case VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR:
return "An external handle is not a valid handle of the specified type.";
#endif
#if VK_HEADER_VERSION >= 64
case VK_ERROR_NOT_PERMITTED_EXT:
return "The caller does not have sufficient privileges.";
#endif
#if VK_HEADER_VERSION >= 72
case VK_ERROR_FRAGMENTATION_EXT:
return "A descriptor pool creation has failed due to fragmentation";
#endif
#if VK_HEADER_VERSION >= 89
case VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT:
return "Invalid DRM format modifier plane layout";
#endif
#if VK_HEADER_VERSION >= 97
case VK_ERROR_INVALID_DEVICE_ADDRESS_EXT:
return "Invalid device address";
#endif
#if VK_HEADER_VERSION >= 105
case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT:
return "An operation on a swapchain created with VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT failed as it did not have exclusive full-screen access.";
#endif
#if VK_HEADER_VERSION >= 131
case VK_ERROR_UNKNOWN:
return "An unknown error has occurred; either the application has provided invalid input, or an implementation failure has occurred.";
#endif
#if VK_HEADER_VERSION >= 135
case VK_ERROR_INCOMPATIBLE_VERSION_KHR:
return "Acceleration structure serialized with version as the version information is not compatible with device.";
case VK_THREAD_IDLE_KHR:
return "A deferred operation is not complete but there is currently no work for this thread to do at the time of this call.";
case VK_THREAD_DONE_KHR:
return "A deferred operation is not complete but there is no work remaining to assign to additional threads.";
case VK_OPERATION_DEFERRED_KHR:
return "A deferred operation was requested and at least some of the work was deferred.";
case VK_OPERATION_NOT_DEFERRED_KHR:
return "A deferred operation was requested and no operations were deferred.";
case VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT:
return "A requested pipeline creation would have required compilation, but the application requested compilation to not be performed.";
#endif
#if VK_HEADER_VERSION < 140
case VK_RESULT_RANGE_SIZE:
#endif
case VK_RESULT_MAX_ENUM:
default:
return "Unknown Vulkan error.";
}
return "Unknown Vulkan error.";
}
static void
@@ -304,7 +170,7 @@ gdk_vulkan_context_check_swapchain (GdkVulkanContext *context,
if (res != VK_SUCCESS)
{
g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE,
"Could not query surface capabilities: %s", gdk_vulkan_strerror (res));
"Could not query surface capabilities: %d", res);
return FALSE;
}
@@ -401,7 +267,7 @@ gdk_vulkan_context_check_swapchain (GdkVulkanContext *context,
else
{
g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE,
"Could not create swapchain for this surface: %s", gdk_vulkan_strerror (res));
"Could not create swapchain for this surface: %d", res);
priv->swapchain = VK_NULL_HANDLE;
return FALSE;
}
@@ -577,7 +443,7 @@ gdk_vulkan_context_real_init (GInitable *initable,
if (res != VK_SUCCESS)
{
g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE,
"Could not create surface for this surface: %s", gdk_vulkan_strerror (res));
"Could not create surface for this surface: %d", res);
return FALSE;
}
@@ -588,7 +454,7 @@ gdk_vulkan_context_real_init (GInitable *initable,
if (res != VK_SUCCESS)
{
g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE,
"Could not check if queue family supports this surface: %s", gdk_vulkan_strerror (res));
"Could not check if queue family supports this surface: %d", res);
}
else if (!supported)
{
@@ -1116,7 +982,7 @@ gdk_display_create_vulkan_instance (GdkDisplay *display,
if (res != VK_SUCCESS)
{
g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_UNSUPPORTED,
"Could not create a Vulkan instance: %s", gdk_vulkan_strerror (res));
"Could not create a Vulkan instance: %d", res);
return FALSE;
}
-4
View File
@@ -269,8 +269,6 @@ collect_reused_child_nodes (GskRenderer *renderer,
case GSK_BLEND_NODE:
case GSK_CROSS_FADE_NODE:
case GSK_BLUR_NODE:
case GSK_FILL_NODE:
case GSK_STROKE_NODE:
default:
@@ -857,8 +855,6 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer,
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 */
}
-172
View File
@@ -1,172 +0,0 @@
#include "config.h"
#include "gskglpathcacheprivate.h"
#include "gskstrokeprivate.h"
#define MAX_UNUSED_FRAMES (16 * 5)
typedef struct
{
GskPath *path;
GskFillRule fill_rule;
const GskStroke *stroke;
float scale_x;
float scale_y;
guint hash;
GskStroke stroke_copy;
graphene_rect_t bounds;
int texture_id;
int unused_frames;
} CacheItem;
static guint
compute_hash (CacheItem *item)
{
guint hash;
hash = GPOINTER_TO_UINT (item->path);
hash += item->fill_rule;
if (item->stroke)
hash += gsk_stroke_hash (item->stroke);
hash += (int)(item->scale_x * 100);
hash += (int)(item->scale_y * 200);
return hash;
}
static guint
cache_key_hash (gconstpointer v)
{
const CacheItem *item = v;
return item->hash;
}
static gboolean
cache_key_equal (gconstpointer v1,
gconstpointer v2)
{
const CacheItem *item1 = v1;
const CacheItem *item2 = v2;
return item1->path == item2->path &&
item1->fill_rule == item2->fill_rule &&
item1->scale_x == item2->scale_x &&
item1->scale_y == item2->scale_y &&
(item1->stroke == item2->stroke ||
(item1->stroke && item2->stroke &&
gsk_stroke_equal (item1->stroke, item2->stroke)));
}
void
gsk_gl_path_cache_init (GskGLPathCache *self)
{
self->textures = g_hash_table_new_full (cache_key_hash,
cache_key_equal,
NULL,
g_free);
}
void
gsk_gl_path_cache_free (GskGLPathCache *self,
GskGLDriver *gl_driver)
{
GHashTableIter iter;
CacheItem *item;
g_hash_table_iter_init (&iter, self->textures);
while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&item))
{
gsk_gl_driver_destroy_texture (gl_driver, item->texture_id);
}
g_hash_table_unref (self->textures);
self->textures = NULL;
}
void
gsk_gl_path_cache_begin_frame (GskGLPathCache *self,
GskGLDriver *gl_driver)
{
GHashTableIter iter;
CacheItem *item;
g_hash_table_iter_init (&iter, self->textures);
while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&item))
{
if (item->unused_frames > MAX_UNUSED_FRAMES)
{
gsk_gl_driver_destroy_texture (gl_driver, item->texture_id);
g_hash_table_iter_remove (&iter);
}
}
}
int
gsk_gl_path_cache_get_texture_id (GskGLPathCache *self,
GskPath *path,
GskFillRule fill_rule,
const GskStroke *stroke,
float scale_x,
float scale_y,
graphene_rect_t *out_bounds)
{
CacheItem key;
CacheItem *item;
key.path = path;
key.fill_rule = fill_rule;
key.stroke = stroke;
key.scale_x = scale_x;
key.scale_y = scale_y;
key.hash = compute_hash (&key);
item = g_hash_table_lookup (self->textures, &key);
if (item == NULL)
return 0;
item->unused_frames = 0;
g_assert (item->texture_id != 0);
*out_bounds = item->bounds;
return item->texture_id;
}
void
gsk_gl_path_cache_commit (GskGLPathCache *self,
GskPath *path,
GskFillRule fill_rule,
const GskStroke *stroke,
float scale_x,
float scale_y,
int texture_id,
const graphene_rect_t *bounds)
{
CacheItem *item;
g_assert (self != NULL);
g_assert (texture_id > 0);
item = g_new0 (CacheItem, 1);
item->path = path;
item->fill_rule = fill_rule;
if (stroke)
{
gsk_stroke_init_copy (&item->stroke_copy, stroke);
item->stroke = &item->stroke_copy;
}
item->scale_x = scale_x;
item->scale_y = scale_y;
item->hash = compute_hash (item);
item->unused_frames = 0;
item->texture_id = texture_id;
item->bounds = *bounds;
g_hash_table_add (self->textures, item);
}
-35
View File
@@ -1,35 +0,0 @@
#ifndef __GSK_GL_PATH_CACHE_H__
#define __GSK_GL_PATH_CACHE_H__
#include <glib.h>
#include "gskgldriverprivate.h"
#include "gskpath.h"
typedef struct
{
GHashTable *textures;
} GskGLPathCache;
void gsk_gl_path_cache_init (GskGLPathCache *self);
void gsk_gl_path_cache_free (GskGLPathCache *self,
GskGLDriver *gl_driver);
void gsk_gl_path_cache_begin_frame (GskGLPathCache *self,
GskGLDriver *gl_driver);
int gsk_gl_path_cache_get_texture_id (GskGLPathCache *self,
GskPath *path,
GskFillRule fill_rule,
const GskStroke *stroke,
float scale_x,
float scale_y,
graphene_rect_t *out_bounds);
void gsk_gl_path_cache_commit (GskGLPathCache *self,
GskPath *path,
GskFillRule fill_rule,
const GskStroke *stroke,
float scale_x,
float scale_y,
int texture_id,
const graphene_rect_t *bounds);
#endif
+25 -320
View File
@@ -16,7 +16,6 @@
#include "gskglrenderopsprivate.h"
#include "gskcairoblurprivate.h"
#include "gskglshadowcacheprivate.h"
#include "gskglpathcacheprivate.h"
#include "gskglnodesampleprivate.h"
#include "gsktransform.h"
#include "glutilsprivate.h"
@@ -543,7 +542,6 @@ struct _GskGLRenderer
GskGLGlyphCache *glyph_cache;
GskGLIconCache *icon_cache;
GskGLShadowCache shadow_cache;
GskGLPathCache path_cache;
#ifdef G_ENABLE_DEBUG
struct {
@@ -1826,6 +1824,7 @@ blur_texture (GskGLRenderer *self,
float blur_radius_x,
float blur_radius_y)
{
const GskRoundedRect new_clip = GSK_ROUNDED_RECT_INIT (0, 0, texture_to_blur_width, texture_to_blur_height);
int pass1_texture_id, pass1_render_target;
int pass2_texture_id, pass2_render_target;
int prev_render_target;
@@ -1852,17 +1851,16 @@ blur_texture (GskGLRenderer *self,
GL_NEAREST, GL_NEAREST,
&pass2_texture_id, &pass2_render_target);
init_projection_matrix (&item_proj,
&GRAPHENE_RECT_INIT (0, 0, texture_to_blur_width, texture_to_blur_height));
init_projection_matrix (&item_proj, &new_clip.bounds);
ops_set_program (builder, &self->programs->blur_program);
prev_projection = ops_set_projection (builder, &item_proj);
ops_set_modelview (builder, NULL);
prev_viewport = ops_set_viewport (builder, &GRAPHENE_RECT_INIT (0, 0, texture_to_blur_width, texture_to_blur_height));
ops_push_clip (builder, &GSK_ROUNDED_RECT_INIT (0, 0, texture_to_blur_width, texture_to_blur_height));
prev_viewport = ops_set_viewport (builder, &new_clip.bounds);
ops_push_clip (builder, &new_clip);
prev_render_target = ops_set_render_target (builder, pass1_render_target);
ops_begin (builder, OP_CLEAR);
ops_set_program (builder, &self->programs->blur_program);
op = ops_begin (builder, OP_CHANGE_BLUR);
op->size.width = texture_to_blur_width;
@@ -1873,7 +1871,7 @@ blur_texture (GskGLRenderer *self,
ops_set_texture (builder, region->texture_id);
load_vertex_data_with_region (ops_draw (builder, NULL),
&GRAPHENE_RECT_INIT (0, 0, texture_to_blur_width, texture_to_blur_height),
&new_clip.bounds,
builder, region,
FALSE);
#if 0
@@ -1895,7 +1893,7 @@ blur_texture (GskGLRenderer *self,
ops_set_render_target (builder, pass2_render_target);
ops_begin (builder, OP_CLEAR);
load_vertex_data_with_region (ops_draw (builder, NULL), /* render pass 2 */
&GRAPHENE_RECT_INIT (0, 0, texture_to_blur_width, texture_to_blur_height),
&new_clip.bounds,
builder, region,
FALSE);
@@ -2718,263 +2716,6 @@ render_cross_fade_node (GskGLRenderer *self,
load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder);
}
static inline void
render_mask_node (GskGLRenderer *self,
GskRenderNode *node,
RenderOpBuilder *builder)
{
GskRenderNode *source = gsk_mask_node_get_source (node);
GskRenderNode *mask = gsk_mask_node_get_mask (node);
TextureRegion source_region;
TextureRegion mask_region;
gboolean is_offscreen1, is_offscreen2;
OpMask *op;
if (!add_offscreen_ops (self, builder,
&node->bounds,
source,
&source_region, &is_offscreen1,
FORCE_OFFSCREEN | RESET_CLIP))
{
gsk_gl_renderer_add_render_ops (self, source, builder);
return;
}
if (!add_offscreen_ops (self, builder,
&node->bounds,
mask,
&mask_region, &is_offscreen2,
FORCE_OFFSCREEN | RESET_CLIP))
{
gsk_gl_renderer_add_render_ops (self, source, builder);
return;
}
ops_set_program (builder, &self->programs->mask_program);
op = ops_begin (builder, OP_CHANGE_MASK);
op->mask = mask_region.texture_id;
op->texture_rect[0] = 0;
op->texture_rect[1] = 0;
op->texture_rect[2] = 1;
op->texture_rect[3] = 1;
ops_set_texture (builder, source_region.texture_id);
load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder);
}
static GdkTexture *
make_path_mask (GskPath *path,
GskFillRule fill_rule,
float scale_x,
float scale_y,
graphene_rect_t *bounds)
{
cairo_surface_t *surface;
cairo_t *cr;
int width;
int height;
int stride;
guchar *buffer;
GBytes *bytes;
GdkTexture *mask;
gsk_path_get_bounds (path, bounds);
width = ceilf (bounds->size.width * scale_x);
height = ceilf (bounds->size.height * scale_y);
stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, width);
buffer = g_malloc0 (stride * height);
surface = cairo_image_surface_create_for_data (buffer,
CAIRO_FORMAT_ARGB32,
width, height,
stride);
cairo_surface_set_device_scale (surface, scale_x, scale_y);
cr = cairo_create (surface);
cairo_translate (cr, 0, bounds->size.height);
cairo_scale (cr, 1, -1);
cairo_translate (cr, - bounds->origin.x, - bounds->origin.y);
switch (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 (path, cr);
cairo_clip (cr);
cairo_set_source_rgb (cr, 0, 0, 0);
cairo_paint (cr);
cairo_destroy (cr);
bytes = g_bytes_new_take (buffer, stride * height);
mask = gdk_memory_texture_new (width, height, GDK_MEMORY_DEFAULT, bytes, stride);
g_bytes_unref (bytes);
cairo_surface_destroy (surface);
return mask;
}
static inline void
render_fill_node (GskGLRenderer *self,
GskRenderNode *node,
RenderOpBuilder *builder)
{
GskRenderNode *child = gsk_fill_node_get_child (node);
TextureRegion child_region;
gboolean is_offscreen1;
GskPath *path;
GskFillRule fill_rule;
graphene_rect_t mask_bounds;
int mask_texture_id;
OpMask *op;
float scale_x = builder->scale_x;
float scale_y = builder->scale_y;
if (!add_offscreen_ops (self, builder,
&node->bounds,
child,
&child_region, &is_offscreen1,
FORCE_OFFSCREEN | RESET_CLIP))
{
gsk_gl_renderer_add_render_ops (self, child, builder);
return;
}
path = gsk_fill_node_get_path (node);
fill_rule = gsk_fill_node_get_fill_rule (node);
mask_texture_id = gsk_gl_path_cache_get_texture_id (&self->path_cache,
path,
fill_rule,
NULL,
scale_x, scale_y,
&mask_bounds);
if (mask_texture_id == 0)
{
GdkTexture *mask;
mask = make_path_mask (path, fill_rule, scale_x, scale_y, &mask_bounds);
mask_texture_id =
gsk_gl_driver_get_texture_for_texture (self->gl_driver,
mask,
GL_LINEAR,
GL_LINEAR);
gsk_gl_driver_mark_texture_permanent (self->gl_driver, mask_texture_id);
gsk_gl_path_cache_commit (&self->path_cache,
path,
fill_rule,
NULL,
scale_x, scale_y,
mask_texture_id,
&mask_bounds);
g_object_unref (mask);
}
ops_set_program (builder, &self->programs->mask_program);
op = ops_begin (builder, OP_CHANGE_MASK);
op->mask = mask_texture_id;
op->texture_rect[0] = (mask_bounds.origin.x - node->bounds.origin.x) / node->bounds.size.width;
op->texture_rect[1] = (mask_bounds.origin.y - node->bounds.origin.y) / node->bounds.size.height;
op->texture_rect[2] = (mask_bounds.origin.x + mask_bounds.size.width - node->bounds.origin.x) / node->bounds.size.width;
op->texture_rect[3] = (mask_bounds.origin.y + mask_bounds.size.height - node->bounds.origin.y) / node->bounds.size.height;
ops_set_texture (builder, child_region.texture_id);
load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder);
}
static inline void
render_stroke_node (GskGLRenderer *self,
GskRenderNode *node,
RenderOpBuilder *builder)
{
GskRenderNode *child = gsk_stroke_node_get_child (node);
TextureRegion child_region;
gboolean is_offscreen1;
GskPath *path;
GskStroke *stroke;
int mask_texture_id;
graphene_rect_t mask_bounds;
OpMask *op;
float scale_x = builder->scale_x;
float scale_y = builder->scale_y;
if (!add_offscreen_ops (self, builder,
&node->bounds,
child,
&child_region, &is_offscreen1,
FORCE_OFFSCREEN | RESET_CLIP))
{
gsk_gl_renderer_add_render_ops (self, child, builder);
return;
}
path = gsk_stroke_node_get_path (node);
stroke = (GskStroke *)gsk_stroke_node_get_stroke (node);
mask_texture_id = gsk_gl_path_cache_get_texture_id (&self->path_cache,
path,
GSK_FILL_RULE_EVEN_ODD,
stroke,
scale_x, scale_y,
&mask_bounds);
if (mask_texture_id == 0)
{
GskPath *stroke_path;
GdkTexture *mask;
stroke_path = gsk_path_stroke (path, stroke);
mask = make_path_mask (stroke_path, GSK_FILL_RULE_EVEN_ODD, scale_x, scale_y, &mask_bounds);
mask_texture_id =
gsk_gl_driver_get_texture_for_texture (self->gl_driver,
mask,
GL_LINEAR,
GL_LINEAR);
gsk_gl_driver_mark_texture_permanent (self->gl_driver, mask_texture_id);
gsk_gl_path_cache_commit (&self->path_cache,
path,
GSK_FILL_RULE_EVEN_ODD,
stroke,
scale_x, scale_y,
mask_texture_id,
&mask_bounds);
g_object_unref (mask);
gsk_path_unref (stroke_path);
}
ops_set_program (builder, &self->programs->mask_program);
op = ops_begin (builder, OP_CHANGE_MASK);
op->mask = mask_texture_id;
op->texture_rect[0] = (mask_bounds.origin.x - node->bounds.origin.x) / node->bounds.size.width;
op->texture_rect[1] = (mask_bounds.origin.y - node->bounds.origin.y) / node->bounds.size.height;
op->texture_rect[2] = (mask_bounds.origin.x + mask_bounds.size.width - node->bounds.origin.x) / node->bounds.size.width;
op->texture_rect[3] = (mask_bounds.origin.y + mask_bounds.size.height - node->bounds.origin.y) / node->bounds.size.height;
ops_set_texture (builder, child_region.texture_id);
load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder);
}
static inline void
render_blend_node (GskGLRenderer *self,
GskRenderNode *node,
@@ -3199,10 +2940,15 @@ static inline void
apply_color_matrix_op (const Program *program,
const OpColorMatrix *op)
{
float mat[16];
OP_PRINT (" -> Color Matrix");
graphene_matrix_to_float (op->matrix, mat);
glUniformMatrix4fv (program->color_matrix.color_matrix_location, 1, GL_FALSE, mat);
OP_PRINT (" -> Color matrix. Send matrix: %d. Send offset: %d.",
op->matrix.send, op->offset.send);
if (op->matrix.send)
{
float mat[16];
graphene_matrix_to_float (op->matrix.value, mat);
glUniformMatrix4fv (program->color_matrix.color_matrix_location, 1, GL_FALSE, mat);
}
if (op->offset.send)
{
@@ -3458,21 +3204,11 @@ static inline void
apply_repeat_op (const Program *program,
const OpRepeat *op)
{
OP_PRINT (" -> Repeat");
glUniform4fv (program->repeat.child_bounds_location, 1, op->child_bounds);
glUniform4fv (program->repeat.texture_rect_location, 1, op->texture_rect);
}
static inline void
apply_mask_op (const Program *program,
const OpMask *op)
{
OP_PRINT (" -> Mask ");
glUniform4fv (program->mask.texture_rect_location, 1, op->texture_rect);
glUniform1i (program->mask.mask_location, 1);
glActiveTexture (GL_TEXTURE0 + 1);
glBindTexture (GL_TEXTURE_2D, op->mask);
}
static void
gsk_gl_renderer_dispose (GObject *gobject)
{
@@ -3596,7 +3332,6 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self,
{ "/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" },
{ "/org/gtk/libgsk/glsl/mask.glsl", "mask" },
};
gsk_gl_shader_builder_init (&shader_builder,
@@ -3692,10 +3427,6 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self,
INIT_PROGRAM_UNIFORM_LOCATION (cross_fade, progress);
INIT_PROGRAM_UNIFORM_LOCATION (cross_fade, source2);
/* mask */
INIT_PROGRAM_UNIFORM_LOCATION (mask, mask);
INIT_PROGRAM_UNIFORM_LOCATION (mask, texture_rect);
/* blend */
INIT_PROGRAM_UNIFORM_LOCATION (blend, source2);
INIT_PROGRAM_UNIFORM_LOCATION (blend, mode);
@@ -3704,6 +3435,7 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self,
INIT_PROGRAM_UNIFORM_LOCATION (repeat, child_bounds);
INIT_PROGRAM_UNIFORM_LOCATION (repeat, texture_rect);
/* We initialize the alpha uniform here, since the default value is important.
* We can't do it in the shader like a reasonable person would because that doesn't
* work in gles. */
@@ -3848,7 +3580,6 @@ gsk_gl_renderer_realize (GskRenderer *renderer,
self->glyph_cache = get_glyph_cache_for_display (gdk_surface_get_display (surface), self->atlases);
self->icon_cache = get_icon_cache_for_display (gdk_surface_get_display (surface), self->atlases);
gsk_gl_shadow_cache_init (&self->shadow_cache);
gsk_gl_path_cache_init (&self->path_cache);
gdk_profiler_end_mark (before, "gl renderer realize", NULL);
@@ -3876,7 +3607,6 @@ gsk_gl_renderer_unrealize (GskRenderer *renderer)
g_clear_pointer (&self->icon_cache, gsk_gl_icon_cache_unref);
g_clear_pointer (&self->atlases, gsk_gl_texture_atlases_unref);
gsk_gl_shadow_cache_free (&self->shadow_cache, self->gl_driver);
gsk_gl_path_cache_free (&self->path_cache, self->gl_driver);
g_clear_object (&self->gl_profiler);
g_clear_object (&self->gl_driver);
@@ -4081,18 +3811,6 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer *self,
render_gl_shader_node (self, node, builder);
break;
case GSK_MASK_NODE:
render_mask_node (self, node, builder);
break;
case GSK_FILL_NODE:
render_fill_node (self, node, builder);
break;
case GSK_STROKE_NODE:
render_stroke_node (self, node, builder);
break;
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
case GSK_CAIRO_NODE:
@@ -4127,6 +3845,7 @@ add_offscreen_ops (GskGLRenderer *self,
int filter;
GskTextureKey key;
int cached_id;
graphene_rect_t viewport;
if (node_is_invisible (child_node))
{
@@ -4212,27 +3931,19 @@ add_offscreen_ops (GskGLRenderer *self,
render_target);
}
init_projection_matrix (&item_proj,
&GRAPHENE_RECT_INIT (
bounds->origin.x * scale_x,
bounds->origin.y * scale_y,
width, height
));
viewport = GRAPHENE_RECT_INIT (bounds->origin.x * scale_x,
bounds->origin.y * scale_y,
width, height);
init_projection_matrix (&item_proj, &viewport);
prev_render_target = ops_set_render_target (builder, render_target);
/* Clear since we use this rendertarget for the first time */
ops_begin (builder, OP_CLEAR);
prev_projection = ops_set_projection (builder, &item_proj);
ops_set_modelview (builder, gsk_transform_scale (NULL, scale_x, scale_y));
prev_viewport = ops_set_viewport (builder,
&GRAPHENE_RECT_INIT (bounds->origin.x * scale_x,
bounds->origin.y * scale_y,
width, height));
prev_viewport = ops_set_viewport (builder, &viewport);
if (flags & RESET_CLIP)
ops_push_clip (builder,
&GSK_ROUNDED_RECT_INIT (bounds->origin.x * scale_x,
bounds->origin.y * scale_y,
width, height));
ops_push_clip (builder, &GSK_ROUNDED_RECT_INIT_FROM_RECT (viewport));
builder->dx = 0;
builder->dy = 0;
@@ -4398,11 +4109,6 @@ gsk_gl_renderer_render_ops (GskGLRenderer *self)
apply_blend_op (program, ptr);
break;
case OP_CHANGE_MASK:
g_assert (program == &self->programs->mask_program);
apply_mask_op (program, ptr);
break;
case OP_CHANGE_LINEAR_GRADIENT:
apply_linear_gradient_op (program, ptr);
break;
@@ -4524,7 +4230,6 @@ gsk_gl_renderer_do_render (GskRenderer *renderer,
gsk_gl_glyph_cache_begin_frame (self->glyph_cache, self->gl_driver, removed);
gsk_gl_icon_cache_begin_frame (self->icon_cache, removed);
gsk_gl_shadow_cache_begin_frame (&self->shadow_cache, self->gl_driver);
gsk_gl_path_cache_begin_frame (&self->path_cache, self->gl_driver);
g_ptr_array_unref (removed);
/* Set up the modelview and projection matrices to fit our viewport */
+75 -157
View File
@@ -14,27 +14,28 @@ rect_equal (const graphene_rect_t *a,
return memcmp (a, b, sizeof (graphene_rect_t)) == 0;
}
static inline gboolean G_GNUC_PURE
static inline bool G_GNUC_PURE
rounded_rect_equal (const GskRoundedRect *r1,
const GskRoundedRect *r2)
{
int i;
if (r1 == r2)
return true;
if (!r1)
return FALSE;
return false;
if (r1->bounds.origin.x != r2->bounds.origin.x ||
r1->bounds.origin.y != r2->bounds.origin.y ||
r1->bounds.size.width != r2->bounds.size.width ||
r1->bounds.size.height != r2->bounds.size.height)
return FALSE;
return false;
for (i = 0; i < 4; i ++)
for (int i = 0; i < 4; i ++)
if (r1->corner[i].width != r2->corner[i].width ||
r1->corner[i].height != r2->corner[i].height)
return FALSE;
return false;
return TRUE;
return true;
}
static inline gboolean G_GNUC_PURE
@@ -208,10 +209,9 @@ ops_free (RenderOpBuilder *builder)
void
ops_set_program (RenderOpBuilder *builder,
Program *program)
Program *program)
{
OpProgram *op;
ProgramState *program_state = NULL;
if (builder->current_program == program)
return;
@@ -220,86 +220,6 @@ ops_set_program (RenderOpBuilder *builder,
op->program = program;
builder->current_program = program;
program_state = &program->state;
if (memcmp (&builder->current_projection, &program_state->projection, sizeof (graphene_matrix_t)) != 0)
{
OpMatrix *opm;
opm = ops_begin (builder, OP_CHANGE_PROJECTION);
opm->matrix = builder->current_projection;
program_state->projection = builder->current_projection;
}
if (program_state->modelview == NULL ||
!gsk_transform_equal (builder->current_modelview, program_state->modelview))
{
OpMatrix *opm;
opm = ops_begin (builder, OP_CHANGE_MODELVIEW);
gsk_transform_to_matrix (builder->current_modelview, &opm->matrix);
gsk_transform_unref (program_state->modelview);
program_state->modelview = gsk_transform_ref (builder->current_modelview);
}
if (!rect_equal (&builder->current_viewport, &program_state->viewport))
{
OpViewport *opv;
opv = ops_begin (builder, OP_CHANGE_VIEWPORT);
opv->viewport = builder->current_viewport;
program_state->viewport = builder->current_viewport;
}
if (!rounded_rect_equal (builder->current_clip, &program_state->clip))
{
OpClip *opc;
opc = ops_begin (builder, OP_CHANGE_CLIP);
opc->clip = *builder->current_clip;
opc->send_corners = !rounded_rect_corners_equal (builder->current_clip, &program_state->clip);
program_state->clip = *builder->current_clip;
}
if (program_state->opacity != builder->current_opacity)
{
OpOpacity *opo;
opo = ops_begin (builder, OP_CHANGE_OPACITY);
opo->opacity = builder->current_opacity;
program_state->opacity = builder->current_opacity;
}
}
static void
ops_set_clip (RenderOpBuilder *builder,
const GskRoundedRect *clip)
{
ProgramState *current_program_state = get_current_program_state (builder);
OpClip *op;
if (current_program_state &&
rounded_rect_equal (&current_program_state->clip, clip))
return;
if (!(op = op_buffer_peek_tail_checked (&builder->render_ops, OP_CHANGE_CLIP)))
{
op = op_buffer_add (&builder->render_ops, OP_CHANGE_CLIP);
op->send_corners = !current_program_state ||
!rounded_rect_corners_equal (&current_program_state->clip, clip);
}
else
{
/* If the op before sent the corners, this one needs, too */
op->send_corners |= !current_program_state ||
!rounded_rect_corners_equal (&current_program_state->clip, clip);
}
op->clip = *clip;
if (current_program_state)
current_program_state->clip = *clip;
}
void
@@ -318,7 +238,6 @@ ops_push_clip (RenderOpBuilder *self,
g_array_append_val (self->clip_stack, entry);
self->current_clip = &g_array_index (self->clip_stack, ClipStackEntry, self->clip_stack->len - 1).rect;
self->clip_is_rectilinear = entry.is_rectilinear;
ops_set_clip (self, clip);
}
void
@@ -336,7 +255,6 @@ ops_pop_clip (RenderOpBuilder *self)
{
self->current_clip = &head->rect;
self->clip_is_rectilinear = head->is_rectilinear;
ops_set_clip (self, &head->rect);
}
else
{
@@ -352,32 +270,6 @@ ops_has_clip (RenderOpBuilder *self)
self->clip_stack->len > 1;
}
static void
ops_set_modelview_internal (RenderOpBuilder *builder,
GskTransform *transform)
{
ProgramState *current_program_state = get_current_program_state (builder);
OpMatrix *op;
#if 0
XXX This is not possible if we want pop() to work.
if (builder->current_program &&
gsk_transform_equal (builder->current_program_state->modelview, transform))
return;
#endif
if (!(op = op_buffer_peek_tail_checked (&builder->render_ops, OP_CHANGE_MODELVIEW)))
op = op_buffer_add (&builder->render_ops, OP_CHANGE_MODELVIEW);
gsk_transform_to_matrix (transform, &op->matrix);
if (builder->current_program != NULL)
{
gsk_transform_unref (current_program_state->modelview);
current_program_state->modelview = gsk_transform_ref (transform);
}
}
/**
* ops_set_modelview:
* @builder
@@ -410,7 +302,6 @@ ops_set_modelview (RenderOpBuilder *builder,
builder->current_modelview = entry->transform;
builder->scale_x = entry->metadata.scale_x;
builder->scale_y = entry->metadata.scale_y;
ops_set_modelview_internal (builder, entry->transform);
}
/* This sets the given modelview to the one we get when multiplying
@@ -456,7 +347,6 @@ ops_push_modelview (RenderOpBuilder *builder,
builder->scale_x = entry->metadata.scale_x;
builder->scale_y = entry->metadata.scale_y;
builder->current_modelview = entry->transform;
ops_set_modelview_internal (builder, entry->transform);
}
void
@@ -480,7 +370,6 @@ ops_pop_modelview (RenderOpBuilder *builder)
builder->scale_x = head->metadata.scale_x;
builder->scale_y = head->metadata.scale_y;
builder->current_modelview = head->transform;
ops_set_modelview_internal (builder, head->transform);
}
else
{
@@ -492,17 +381,7 @@ graphene_matrix_t
ops_set_projection (RenderOpBuilder *builder,
const graphene_matrix_t *projection)
{
ProgramState *current_program_state = get_current_program_state (builder);
graphene_matrix_t prev_mv;
OpMatrix *op;
if (!(op = op_buffer_peek_tail_checked (&builder->render_ops, OP_CHANGE_PROJECTION)))
op = op_buffer_add (&builder->render_ops, OP_CHANGE_PROJECTION);
op->matrix = *projection;
if (builder->current_program != NULL)
current_program_state->projection = *projection;
prev_mv = builder->current_projection;
builder->current_projection = *projection;
@@ -518,9 +397,8 @@ ops_set_viewport (RenderOpBuilder *builder,
OpViewport *op;
graphene_rect_t prev_viewport;
if (current_program_state != NULL &&
rect_equal (&current_program_state->viewport, viewport))
return current_program_state->viewport;
if (rect_equal (&builder->current_viewport, viewport))
return *viewport;
op = ops_begin (builder, OP_CHANGE_VIEWPORT);
op->viewport = *viewport;
@@ -586,24 +464,14 @@ float
ops_set_opacity (RenderOpBuilder *builder,
float opacity)
{
ProgramState *current_program_state = get_current_program_state (builder);
OpOpacity *op;
float prev_opacity;
if (builder->current_opacity == opacity)
return opacity;
if (!(op = op_buffer_peek_tail_checked (&builder->render_ops, OP_CHANGE_OPACITY)))
op = op_buffer_add (&builder->render_ops, OP_CHANGE_OPACITY);
op->opacity = opacity;
prev_opacity = builder->current_opacity;
builder->current_opacity = opacity;
if (builder->current_program != NULL)
current_program_state->opacity = opacity;
return prev_opacity;
}
@@ -666,30 +534,30 @@ ops_set_color_matrix (RenderOpBuilder *builder,
const graphene_vec4_t *offset)
{
ProgramState *current_program_state = get_current_program_state (builder);
const bool offset_equal = graphene_vec4_equal (offset, &current_program_state->color_matrix.offset);
const bool matrix_equal = graphene_matrix_equal_fast (matrix,
&current_program_state->color_matrix.matrix);
OpColorMatrix *op;
bool offset_equal;
offset_equal = memcmp (offset,
&current_program_state->color_matrix.offset,
sizeof (graphene_vec4_t)) == 0;
if (memcmp (matrix,
&current_program_state->color_matrix.matrix,
sizeof (graphene_matrix_t)) == 0 &&
offset_equal)
if (offset_equal && matrix_equal)
return;
current_program_state->color_matrix.matrix = *matrix;
op = ops_begin (builder, OP_CHANGE_COLOR_MATRIX);
op->matrix = matrix;
if (!matrix_equal)
{
current_program_state->color_matrix.matrix = *matrix;
op->matrix.value = matrix;
op->matrix.send = TRUE;
}
else
op->matrix.send = FALSE;
if (!offset_equal)
{
current_program_state->color_matrix.offset = *offset;
op->offset.value = offset;
op->offset.send = TRUE;
current_program_state->color_matrix.offset = *offset;
}
else
op->offset.send = FALSE;
@@ -755,8 +623,58 @@ GskQuadVertex *
ops_draw (RenderOpBuilder *builder,
const GskQuadVertex vertex_data[GL_N_VERTICES])
{
ProgramState *program_state = get_current_program_state (builder);
OpDraw *op;
if (memcmp (&builder->current_projection, &program_state->projection, sizeof (graphene_matrix_t)) != 0)
{
OpMatrix *opm;
opm = ops_begin (builder, OP_CHANGE_PROJECTION);
opm->matrix = builder->current_projection;
program_state->projection = builder->current_projection;
}
if (program_state->modelview == NULL ||
!gsk_transform_equal (builder->current_modelview, program_state->modelview))
{
OpMatrix *opm;
opm = ops_begin (builder, OP_CHANGE_MODELVIEW);
gsk_transform_to_matrix (builder->current_modelview, &opm->matrix);
gsk_transform_unref (program_state->modelview);
program_state->modelview = gsk_transform_ref (builder->current_modelview);
}
if (!rect_equal (&builder->current_viewport, &program_state->viewport))
{
OpViewport *opv;
opv = ops_begin (builder, OP_CHANGE_VIEWPORT);
opv->viewport = builder->current_viewport;
program_state->viewport = builder->current_viewport;
}
if (!rounded_rect_equal (builder->current_clip, &program_state->clip))
{
OpClip *opc;
opc = ops_begin (builder, OP_CHANGE_CLIP);
opc->clip = *builder->current_clip;
opc->send_corners = !rounded_rect_corners_equal (builder->current_clip, &program_state->clip);
program_state->clip = *builder->current_clip;
}
if (program_state->opacity != builder->current_opacity)
{
OpOpacity *opo;
opo = ops_begin (builder, OP_CHANGE_OPACITY);
opo->opacity = builder->current_opacity;
program_state->opacity = builder->current_opacity;
}
/* TODO: Did the additions above break the following optimization? */
if ((op = op_buffer_peek_tail_checked (&builder->render_ops, OP_DRAW)))
{
op->vao_size += GL_N_VERTICES;
+1 -7
View File
@@ -13,7 +13,7 @@
#include "opbuffer.h"
#define GL_N_VERTICES 6
#define GL_N_PROGRAMS 16
#define GL_N_PROGRAMS 15
#define GL_MAX_GRADIENT_STOPS 6
typedef struct
@@ -179,11 +179,6 @@ struct _Program
int texture_locations[4];
GError *compile_error;
} glshader;
struct {
int mask_location;
int child_rect_location;
int texture_rect_location;
} mask;
};
ProgramState state;
};
@@ -208,7 +203,6 @@ typedef struct {
Program outset_shadow_program;
Program repeat_program;
Program unblurred_outset_shadow_program;
Program mask_program;
};
};
GHashTable *custom_programs; /* GskGLShader -> Program* */
-1
View File
@@ -34,7 +34,6 @@ static guint op_sizes[OP_LAST] = {
sizeof (OpGLShader),
sizeof (OpExtraTexture),
sizeof (OpConicGradient),
sizeof (OpMask),
};
void
+2 -8
View File
@@ -42,7 +42,6 @@ typedef enum
OP_CHANGE_GL_SHADER_ARGS = 28,
OP_CHANGE_EXTRA_SOURCE_TEXTURE = 29,
OP_CHANGE_CONIC_GRADIENT = 30,
OP_CHANGE_MASK = 31,
OP_LAST
} OpKind;
@@ -54,6 +53,7 @@ typedef struct { GskRoundedRect value; guint send: 1; guint send_corners: 1; } R
typedef struct { const GdkRGBA *value; guint send: 1; } RGBAUniformValue;
typedef struct { const graphene_vec4_t *value; guint send: 1; } Vec4UniformValue;
typedef struct { const GskColorStop *value; guint send: 1; } ColorStopUniformValue;
typedef struct { const graphene_matrix_t *value; guint send: 1; } MatrixUniformValue;
/* OpNode are allocated within OpBuffer.pos, but we keep
* a secondary index into the locations of that buffer
@@ -168,7 +168,7 @@ typedef struct
typedef struct
{
const graphene_matrix_t *matrix;
MatrixUniformValue matrix;
Vec4UniformValue offset;
} OpColorMatrix;
@@ -223,12 +223,6 @@ typedef struct
const guchar *uniform_data;
} OpGLShader;
typedef struct
{
int mask;
float texture_rect[4];
} OpMask;
void op_buffer_init (OpBuffer *buffer);
void op_buffer_destroy (OpBuffer *buffer);
void op_buffer_clear (OpBuffer *buffer);
-2
View File
@@ -22,8 +22,6 @@
#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)
+1 -5
View File
@@ -21,15 +21,11 @@
#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>
-1859
View File
File diff suppressed because it is too large Load Diff
-109
View File
@@ -1,109 +0,0 @@
/*
* 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,
gboolean emit_move_to,
float start,
float end);
gboolean gsk_contour_get_stroke_bounds (const GskContour *self,
const GskStroke *stroke,
graphene_rect_t *bounds);
void gsk_contour_add_stroke (const GskContour *contour,
GskPathBuilder *builder,
GskStroke *stroke);
void gsk_contour_default_add_stroke (const GskContour *contour,
GskPathBuilder *builder,
GskStroke *stroke);
G_END_DECLS
#endif /* __GSK_CONTOUR_PRIVATE_H__ */
-1585
View File
File diff suppressed because it is too large Load Diff
-459
View File
@@ -1,459 +0,0 @@
/*
* Copyright © 2020 Red Hat, Inc
*
* 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: Matthias Clasen <mclasen@redhat.com>
*/
#include "config.h"
#include "gskcurveprivate.h"
static inline gboolean
acceptable (float t)
{
return 0 <= t && t <= 1;
}
static int
line_intersect (const GskCurve *curve1,
const GskCurve *curve2,
float *t1,
float *t2,
graphene_point_t *p)
{
const graphene_point_t *pts1 = curve1->line.points;
const graphene_point_t *pts2 = curve2->line.points;
float a1 = pts1[0].x - pts1[1].x;
float b1 = pts1[0].y - pts1[1].y;
float a2 = pts2[0].x - pts2[1].x;
float b2 = pts2[0].y - pts2[1].y;
float det = a1 * b2 - b1 * a2;
if (det != 0)
{
float tt = ((pts1[0].x - pts2[0].x) * b2 - (pts1[0].y - pts2[0].y) * a2) / det;
float ss = - ((pts1[0].y - pts2[0].y) * a1 - (pts1[0].x - pts2[0].x) * b1) / det;
if (acceptable (tt) && acceptable (ss))
{
p->x = pts1[0].x + tt * (pts1[1].x - pts1[0].x);
p->y = pts1[0].y + tt * (pts1[1].y - pts1[0].y);
*t1 = tt;
*t2 = ss;
return 1;
}
}
return 0;
}
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);
}
static void
align_points (const graphene_point_t *p,
const graphene_point_t *a,
const graphene_point_t *b,
graphene_point_t *q,
int n)
{
graphene_vec2_t n1;
float angle;
float s, c;
get_tangent (a, b, &n1);
angle = - atan2 (graphene_vec2_get_y (&n1), graphene_vec2_get_x (&n1));
sincosf (angle, &s, &c);
for (int i = 0; i < n; i++)
{
q[i].x = (p[i].x - a->x) * c - (p[i].y - a->y) * s;
q[i].y = (p[i].x - a->x) * s + (p[i].y - a->y) * c;
}
}
static void
find_point_on_line (const graphene_point_t *p1,
const graphene_point_t *p2,
const graphene_point_t *q,
float *t)
{
float tx = p2->x - p1->x;
float ty = p2->y - p1->y;
float sx = q->x - p1->x;
float sy = q->y - p1->y;
*t = (tx*sx + ty*sy) / (tx*tx + ty*ty);
}
static float
cuberoot (float v)
{
if (v < 0)
return -pow (-v, 1.f / 3);
return pow (v, 1.f / 3);
}
/* Solve P = 0 where P is
* P = (1-t)^3*pa + 3*t*(1-t)^2*pb + 3*t^2*(1-t)*pc + t^3*pd
*/
static int
get_cubic_roots (float pa, float pb, float pc, float pd, float roots[3])
{
float a, b, c, d;
float q, q2;
float p, p3;
float discriminant;
float u1, v1, sd;
int n_roots = 0;
d = -pa + 3*pb - 3*pc + pd;
a = 3*pa - 6*pb + 3*pc;
b = -3*pa + 3*pb;
c = pa;
if (fabs (d) < 0.0001)
{
if (fabs (a) < 0.0001)
{
if (fabs (b) < 0.0001)
return 0;
if (acceptable (-c / b))
{
roots[0] = -c / b;
return 1;
}
return 0;
}
q = sqrt (b*b - 4*a*c);
roots[n_roots] = (-b + q) / (2 * a);
if (acceptable (roots[n_roots]))
n_roots++;
roots[n_roots] = (-b - q) / (2 * a);
if (acceptable (roots[n_roots]))
n_roots++;
return n_roots;
}
a /= d;
b /= d;
c /= d;
p = (3*b - a*a)/3;
p3 = p/3;
q = (2*a*a*a - 9*a*b + 27*c)/27;
q2 = q/2;
discriminant = q2*q2 + p3*p3*p3;
if (discriminant < 0)
{
float mp3 = -p/3;
float mp33 = mp3*mp3*mp3;
float r = sqrt (mp33);
float t = -q / (2*r);
float cosphi = t < -1 ? -1 : (t > 1 ? 1 : t);
float phi = acos (cosphi);
float crtr = cuberoot (r);
float t1 = 2*crtr;
roots[n_roots] = t1 * cos (phi/3) - a/3;
if (acceptable (roots[n_roots]))
n_roots++;
roots[n_roots] = t1 * cos ((phi + 2*M_PI) / 3) - a/3;
if (acceptable (roots[n_roots]))
n_roots++;
roots[n_roots] = t1 * cos ((phi + 4*M_PI) / 3) - a/3;
if (acceptable (roots[n_roots]))
n_roots++;
return n_roots;
}
if (discriminant == 0)
{
u1 = q2 < 0 ? cuberoot (-q2) : -cuberoot (q2);
roots[n_roots] = 2*u1 - a/3;
if (acceptable (roots[n_roots]))
n_roots++;
roots[n_roots] = -u1 - a/3;
if (acceptable (roots[n_roots]))
n_roots++;
return n_roots;
}
sd = sqrt (discriminant);
u1 = cuberoot (sd - q2);
v1 = cuberoot (sd + q2);
roots[n_roots] = u1 - v1 - a/3;
if (acceptable (roots[n_roots]))
n_roots++;
return n_roots;
}
static int
line_curve_intersect (const GskCurve *curve1,
const GskCurve *curve2,
float *t1,
float *t2,
graphene_point_t *p,
int n)
{
const graphene_point_t *a = &curve1->line.points[0];
const graphene_point_t *b = &curve1->line.points[1];
graphene_point_t pts[4];
float t[3];
int m, i;
/* Rotate things to place curve1 on the x axis,
* then solve curve2 for y == 0.
*/
align_points (curve2->curve.points, a, b, pts, 4);
m = get_cubic_roots (pts[0].y, pts[1].y, pts[2].y, pts[3].y, t);
m = MIN (m, n);
for (i = 0; i < m; i++)
{
t2[i] = t[i];
gsk_curve_get_point (curve2, t[i], &p[i]);
find_point_on_line (a, b, &p[i], &t1[i]);
}
return m;
}
static void
curve_intersect_recurse (const GskCurve *curve1,
const GskCurve *curve2,
float t1l,
float t1r,
float t2l,
float t2r,
float *t1,
float *t2,
graphene_point_t *p,
int n,
int *pos)
{
GskCurve p11, p12, p21, p22;
graphene_rect_t b1, b2;
float d1, d2;
if (*pos == n)
return;
gsk_curve_get_tight_bounds (curve1, &b1);
gsk_curve_get_tight_bounds (curve2, &b2);
if (!graphene_rect_intersection (&b1, &b2, NULL))
return;
d1 = (t1r - t1l) / 2;
d2 = (t2r - t2l) / 2;
if (b1.size.width < 0.1 && b1.size.height < 0.1 &&
b2.size.width < 0.1 && b2.size.height < 0.1)
{
graphene_point_t c;
t1[*pos] = t1l + d1;
t2[*pos] = t2l + d2;
gsk_curve_get_point (curve1, 0.5, &c);
for (int i = 0; i < *pos; i++)
{
if (graphene_point_near (&c, &p[i], 0.1))
return;
}
p[*pos] = c;
(*pos)++;
return;
}
gsk_curve_split (curve1, 0.5, &p11, &p12);
gsk_curve_split (curve2, 0.5, &p21, &p22);
curve_intersect_recurse (&p11, &p21, t1l, t1l + d1, t2l, t2l + d2, t1, t2, p, n, pos);
curve_intersect_recurse (&p11, &p22, t1l, t1l + d1, t2l + d2, t2r, t1, t2, p, n, pos);
curve_intersect_recurse (&p12, &p21, t1l + d1, t1r, t2l, t2l + d2, t1, t2, p, n, pos);
curve_intersect_recurse (&p12, &p22, t1l + d1, t1r, t2l + d2, t2r, t1, t2, p, n, pos);
}
static int
curve_intersect (const GskCurve *curve1,
const GskCurve *curve2,
float *t1,
float *t2,
graphene_point_t *p,
int n)
{
int pos = 0;
curve_intersect_recurse (curve1, curve2, 0, 1, 0, 1, t1, t2, p, n, &pos);
return pos;
}
static void
get_bounds (const GskCurve *curve,
float tl,
float tr,
graphene_rect_t *bounds)
{
GskCurve c;
gsk_curve_segment (curve, tl, tr, &c);
gsk_curve_get_tight_bounds (&c, bounds);
/* FIXME this is working around inadequacies of
* graphene_rect_t as bounding box
*/
bounds->size.width += 0.0001;
bounds->size.height += 0.0001;
}
static void
general_intersect_recurse (const GskCurve *curve1,
const GskCurve *curve2,
float t1l,
float t1r,
float t2l,
float t2r,
float *t1,
float *t2,
graphene_point_t *p,
int n,
int *pos)
{
graphene_rect_t b1, b2;
float d1, d2;
if (*pos == n)
return;
get_bounds (curve1, t1l, t1r, &b1);
get_bounds (curve2, t2l, t2r, &b2);
if (!graphene_rect_intersection (&b1, &b2, NULL))
return;
d1 = (t1r - t1l) / 2;
d2 = (t2r - t2l) / 2;
if (b1.size.width < 0.1 && b1.size.height < 0.1 &&
b2.size.width < 0.1 && b2.size.height < 0.1)
{
graphene_point_t c;
t1[*pos] = t1l + d1;
t2[*pos] = t2l + d2;
gsk_curve_get_point (curve1, t1[*pos], &c);
for (int i = 0; i < *pos; i++)
{
if (graphene_point_near (&c, &p[i], 0.1))
return;
}
p[*pos] = c;
(*pos)++;
return;
}
/* Note that in the conic case, we cannot just split the curves and
* pass the two halves down, since splitting changes the parametrization,
* and we need the t's to be valid parameters wrt to the original curve.
*
* So, instead, we determine the bounding boxes above by always starting
* from the original curve. That is a bit less efficient, but also works
* for conics.
*/
general_intersect_recurse (curve1, curve2, t1l, t1l + d1, t2l, t2l + d2, t1, t2, p, n, pos);
general_intersect_recurse (curve1, curve2, t1l, t1l + d1, t2l + d2, t2r, t1, t2, p, n, pos);
general_intersect_recurse (curve1, curve2, t1l + d1, t1r, t2l, t2l + d2, t1, t2, p, n, pos);
general_intersect_recurse (curve1, curve2, t1l + d1, t1r, t2l + d2, t2r, t1, t2, p, n, pos);
}
static int
general_intersect (const GskCurve *curve1,
const GskCurve *curve2,
float *t1,
float *t2,
graphene_point_t *p,
int n)
{
int pos = 0;
general_intersect_recurse (curve1, curve2, 0, 1, 0, 1, t1, t2, p, n, &pos);
return pos;
}
/* Place intersections between the curves in p, and their Bezier positions
* in t1 and t2, up to n. Return the number of intersections found.
*
* Note that two cubic Beziers can have up to 9 intersections.
*/
int
gsk_curve_intersect (const GskCurve *curve1,
const GskCurve *curve2,
float *t1,
float *t2,
graphene_point_t *p,
int n)
{
GskPathOperation op1 = curve1->op;
GskPathOperation op2 = curve2->op;
if (op1 == GSK_PATH_CLOSE)
op1 = GSK_PATH_LINE;
if (op2 == GSK_PATH_CLOSE)
op2 = GSK_PATH_LINE;
/* We special-case line-line and line-curve intersections,
* since we can solve them directly.
* Everything else is done via bisection.
*/
if (op1 == GSK_PATH_LINE && op2 == GSK_PATH_LINE)
return line_intersect (curve1, curve2, t1, t2, p);
else if (op1 == GSK_PATH_LINE && op2 == GSK_PATH_CURVE)
return line_curve_intersect (curve1, curve2, t1, t2, p, n);
else if (op1 == GSK_PATH_CURVE && op2 == GSK_PATH_LINE)
return line_curve_intersect (curve2, curve1, t2, t1, p, n);
else if (op1 == GSK_PATH_CURVE && op2 == GSK_PATH_CURVE)
return curve_intersect (curve1, curve2, t1, t2, p, n);
else
return general_intersect (curve1, curve2, t1, t2, p, n);
}
-144
View File
@@ -1,144 +0,0 @@
/*
* 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);
typedef gboolean (* GskCurveAddCurveFunc) (const graphene_point_t points[4],
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);
gboolean gsk_curve_decompose_curve (const GskCurve *curve,
float tolerance,
GskCurveAddCurveFunc add_curve_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);
void gsk_curve_get_bounds (const GskCurve *curve,
graphene_rect_t *bounds);
void gsk_curve_get_tight_bounds (const GskCurve *curve,
graphene_rect_t *bounds);
int gsk_curve_intersect (const GskCurve *curve1,
const GskCurve *curve2,
float *t1,
float *t2,
graphene_point_t *p,
int n);
void gsk_curve_offset (const GskCurve *curve,
float distance,
GskCurve *offset_curve);
void gsk_curve_reverse (const GskCurve *curve,
GskCurve *reverse);
G_END_DECLS
#endif /* __GSK_CURVE_PRIVATE_H__ */
+2 -102
View File
@@ -73,16 +73,13 @@ 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,
GSK_TEXT_NODE,
GSK_BLUR_NODE,
GSK_DEBUG_NODE,
GSK_GL_SHADER_NODE,
GSK_MASK_NODE,
GSK_GL_SHADER_NODE
} GskRenderNodeType;
/**
@@ -170,104 +167,6 @@ 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
@@ -350,4 +249,5 @@ typedef enum
GSK_GL_UNIFORM_TYPE_VEC4,
} GskGLUniformType;
#endif /* __GSK_TYPES_H__ */
-1266
View File
File diff suppressed because it is too large Load Diff
-120
View File
@@ -1,120 +0,0 @@
/*
* 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_get_stroke_bounds (GskPath *path,
const GskStroke *stroke,
graphene_rect_t *bounds);
GDK_AVAILABLE_IN_ALL
gboolean gsk_path_foreach (GskPath *path,
GskPathForeachFlags flags,
GskPathForeachFunc func,
gpointer user_data);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_stroke (GskPath *path,
GskStroke *stroke);
G_END_DECLS
#endif /* __GSK_PATH_H__ */
-897
View File
@@ -1,897 +0,0 @@
/*
* 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
@@ -1,124 +0,0 @@
/*
* 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
@@ -1,291 +0,0 @@
/*
* 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
@@ -1,50 +0,0 @@
/*
* 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__ */
-628
View File
@@ -1,628 +0,0 @@
/*
* 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 "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;
gsize first;
gsize last;
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;
self->first = 0;
self->last = 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)
{
g_return_val_if_fail (self != NULL, NULL);
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)
{
g_return_val_if_fail (self != NULL, 0.f);
return self->tolerance;
}
/**
* gsk_path_measure_get_n_contours:
* @self: a #GskPathMeasure
*
* Returns the number of contours in the path being measured.
*
* The returned value is independent of whether @self if restricted
* or not.
*
* Returns: The number of contours
**/
gsize
gsk_path_measure_get_n_contours (GskPathMeasure *self)
{
g_return_val_if_fail (self != NULL, 0);
return self->n_contours;
}
/**
* gsk_path_measure_restrict_to_contour:
* @self: a #GskPathMeasure
* @contour: contour to restrict to or (gsize) -1 for using the
* whole path
*
* Restricts all functions on the path to just the given @contour.
*
* If @contour >= gsk_path_measure_get_n_contours() - so in
* particular when it is set to -1 - the whole path will be used.
**/
void
gsk_path_measure_restrict_to_contour (GskPathMeasure *self,
gsize contour)
{
if (contour >= self->n_contours)
{
/* use the whole path */
self->first = 0;
self->last = self->n_contours;
}
else
{
/* use just one contour */
self->first = contour;
self->last = contour + 1;
}
self->length = 0;
for (gsize i = self->first; i < self->last; i++)
{
self->length += self->measures[i].length;
}
}
/**
* 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;
}
/**
* gsk_path_measure_is_closed:
* @self: a #GskPathMeasure
*
* Returns if the path being measured represents a single closed
* contour.
*
* Returns: %TRUE if the current path is closed
**/
gboolean
gsk_path_measure_is_closed (GskPathMeasure *self)
{
const GskContour *contour;
g_return_val_if_fail (self != NULL, FALSE);
/* XXX: is the empty path closed? Currently it's not */
if (self->last - self->first != 1)
return FALSE;
contour = gsk_path_get_contour (self->path, self->first);
return gsk_contour_get_flags (contour) & GSK_PATH_CLOSED ? TRUE : FALSE;
}
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 = self->first; i < self->last; i++)
{
if (distance < self->measures[i].length)
break;
distance -= self->measures[i].length;
}
/* weird corner cases */
if (i == self->last)
{
/* the empty path goes here */
if (self->first == self->last)
{
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->last - 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 = self->first; i < self->last; 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 = self->first; i < self->last; 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 ();
}
}
static void
gsk_path_builder_add_segment_chunk (GskPathBuilder *self,
GskPathMeasure *measure,
gboolean emit_move_to,
float start,
float end)
{
g_assert (start < end);
for (gsize i = measure->first; i < measure->last; 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,
emit_move_to,
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)));
}
emit_move_to = TRUE;
}
}
/**
* 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 from @start to @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, the path will first add the segment
* from @start to the end of the path, and then add the segment from
* the beginning to @end. If the path is closed, these segments will
* be connected.
**/
void
gsk_path_builder_add_segment (GskPathBuilder *self,
GskPathMeasure *measure,
float start,
float end)
{
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)
{
gsk_path_builder_add_segment_chunk (self, measure, TRUE, start, end);
}
else
{
/* If the path is closed, we can connect the 2 subpaths. */
gboolean closed = gsk_path_measure_is_closed (measure);
gboolean need_move_to = !closed;
if (start < measure->length)
gsk_path_builder_add_segment_chunk (self, measure,
TRUE,
start, measure->length);
else
need_move_to = TRUE;
if (end > 0)
gsk_path_builder_add_segment_chunk (self, measure,
need_move_to,
0, end);
if (start == end && closed)
gsk_path_builder_close (self);
}
}
-87
View File
@@ -1,87 +0,0 @@
/*
* 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
gsize gsk_path_measure_get_n_contours (GskPathMeasure *self);
GDK_AVAILABLE_IN_ALL
void gsk_path_measure_restrict_to_contour (GskPathMeasure *self,
gsize contour);
GDK_AVAILABLE_IN_ALL
float gsk_path_measure_get_length (GskPathMeasure *self);
GDK_AVAILABLE_IN_ALL
gboolean gsk_path_measure_is_closed (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
@@ -1,179 +0,0 @@
/*
* 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
@@ -1,61 +0,0 @@
/*
* 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__ */
-1148
View File
File diff suppressed because it is too large Load Diff
-42
View File
@@ -158,15 +158,12 @@ 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())
#define GSK_TYPE_TEXT_NODE (gsk_text_node_get_type())
#define GSK_TYPE_BLUR_NODE (gsk_blur_node_get_type())
#define GSK_TYPE_GL_SHADER_NODE (gsk_gl_shader_node_get_type())
#define GSK_TYPE_MASK_NODE (gsk_mask_node_get_type())
typedef struct _GskDebugNode GskDebugNode;
typedef struct _GskColorNode GskColorNode;
@@ -187,15 +184,12 @@ 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;
typedef struct _GskTextNode GskTextNode;
typedef struct _GskBlurNode GskBlurNode;
typedef struct _GskGLShaderNode GskGLShaderNode;
typedef struct _GskMaskNode GskMaskNode;
GDK_AVAILABLE_IN_ALL
GType gsk_debug_node_get_type (void) G_GNUC_CONST;
@@ -449,32 +443,6 @@ 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
@@ -563,16 +531,6 @@ GBytes * gsk_gl_shader_node_get_args (GskRenderNode
GDK_AVAILABLE_IN_ALL
GskGLShader * gsk_gl_shader_node_get_shader (GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
GType gsk_mask_node_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GskRenderNode * gsk_mask_node_new (GskRenderNode *source_child,
GskRenderNode *mask_child);
GDK_AVAILABLE_IN_ALL
GskRenderNode * gsk_mask_node_get_source (GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
GskRenderNode * gsk_mask_node_get_mask (GskRenderNode *node);
G_END_DECLS
#endif /* __GSK_RENDER_NODE_H__ */
+22 -472
View File
@@ -23,10 +23,8 @@
#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"
@@ -1265,15 +1263,22 @@ gsk_border_node_diff (GskRenderNode *node1,
gdk_rgba_equal (&self1->border_color[0], &self2->border_color[0]))
return;
if (gsk_rounded_rect_equal (&self1->outline, &self2->outline) &&
/* Different uniformity -> diff impossible */
if (self1->uniform ^ self2->uniform)
{
gsk_render_node_diff_impossible (node1, node2, region);
return;
}
if (self1->border_width[0] == self2->border_width[0] &&
self1->border_width[1] == self2->border_width[1] &&
self1->border_width[2] == self2->border_width[2] &&
self1->border_width[3] == self2->border_width[3] &&
gdk_rgba_equal (&self1->border_color[0], &self2->border_color[0]) &&
gdk_rgba_equal (&self1->border_color[1], &self2->border_color[1]) &&
gdk_rgba_equal (&self1->border_color[2], &self2->border_color[2]) &&
gdk_rgba_equal (&self1->border_color[3], &self2->border_color[3]) &&
self1->border_width[0] == self2->border_width[0] &&
self1->border_width[1] == self2->border_width[1] &&
self1->border_width[2] == self2->border_width[2] &&
self1->border_width[3] == self2->border_width[3])
gsk_rounded_rect_equal (&self1->outline, &self2->outline))
return;
gsk_render_node_diff_impossible (node1, node2, region);
@@ -2757,6 +2762,16 @@ gsk_transform_node_draw (GskRenderNode *node,
ctm.xx, ctm.yx,
ctm.xy, ctm.yy,
ctm.x0, ctm.y0));
if (xx * yy == xy * yx)
{
/* broken matrix here. This can happen during transitions
* (like when flipping an axis at the point where scale == 0)
* and just means that nothing should be drawn.
* But Cairo thows lots of ugly errors instead of silently
* going on. So We silently go on.
*/
return;
}
cairo_transform (cr, &ctm);
gsk_render_node_draw (self->child, cr);
@@ -3667,343 +3682,6 @@ 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;
graphene_rect_t path_bounds;
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);
if (gsk_path_get_stroke_bounds (path, stroke, &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_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 ***/
/**
@@ -5520,83 +5198,6 @@ gsk_gl_shader_node_get_args (GskRenderNode *node)
return self->args;
}
/*** GSK_MASK_NODE ***/
typedef struct _GskMaskNode GskMaskNode;
struct _GskMaskNode
{
GskRenderNode render_node;
GskRenderNode *mask_child;
GskRenderNode *source_child;
};
static void
gsk_mask_node_finalize (GskRenderNode *node)
{
GskMaskNode *self = (GskMaskNode *) node;
gsk_render_node_unref (self->source_child);
gsk_render_node_unref (self->mask_child);
}
static void
gsk_mask_node_draw (GskRenderNode *node,
cairo_t *cr)
{
GskMaskNode *self = (GskMaskNode *) node;
cairo_pattern_t *mask_pattern;
cairo_push_group (cr);
gsk_render_node_draw (self->source_child, cr);
cairo_pop_group_to_source (cr);
cairo_push_group (cr);
gsk_render_node_draw (self->mask_child, cr);
mask_pattern = cairo_pop_group (cr);
cairo_mask (cr, mask_pattern);
}
GskRenderNode *
gsk_mask_node_new (GskRenderNode *source_child,
GskRenderNode *mask_child)
{
GskMaskNode *self;
g_return_val_if_fail (GSK_IS_RENDER_NODE (source_child), NULL);
g_return_val_if_fail (GSK_IS_RENDER_NODE (mask_child), NULL);
self = gsk_render_node_alloc (GSK_MASK_NODE);
self->source_child = gsk_render_node_ref (source_child);
self->mask_child = gsk_render_node_ref (mask_child);
graphene_rect_union (&source_child->bounds, &mask_child->bounds, &self->render_node.bounds);
return &self->render_node;
}
GskRenderNode *
gsk_mask_node_get_source (GskRenderNode *node)
{
GskMaskNode *self = (GskMaskNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_MASK_NODE), NULL);
return self->source_child;
}
GskRenderNode *
gsk_mask_node_get_mask (GskRenderNode *node)
{
GskMaskNode *self = (GskMaskNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_MASK_NODE), NULL);
return self->mask_child;
}
GType gsk_render_node_types[GSK_RENDER_NODE_TYPE_N_TYPES];
#ifndef I_
@@ -5629,8 +5230,6 @@ 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)
@@ -5638,7 +5237,6 @@ GSK_DEFINE_RENDER_NODE_TYPE (gsk_text_node, GSK_TEXT_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_blur_node, GSK_BLUR_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_gl_shader_node, GSK_GL_SHADER_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_debug_node, GSK_DEBUG_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_mask_node, GSK_MASK_NODE)
static void
gsk_render_node_init_types_once (void)
@@ -5931,38 +5529,6 @@ 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 =
{
@@ -6074,22 +5640,6 @@ gsk_render_node_init_types_once (void)
GType node_type = gsk_render_node_type_register_static (I_("GskDebugNode"), &node_info);
gsk_render_node_types[GSK_DEBUG_NODE] = node_type;
}
{
const GskRenderNodeTypeInfo node_info =
{
GSK_MASK_NODE,
sizeof (GskMaskNode),
NULL,
gsk_mask_node_finalize,
gsk_mask_node_draw,
NULL,
NULL,
};
GType node_type = gsk_render_node_type_register_static (I_("GskMaskNode"), &node_info);
gsk_render_node_types[GSK_MASK_NODE] = node_type;
}
}
/*< private >
* gsk_render_node_init_types:
+83 -439
View File
@@ -23,13 +23,9 @@
#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"
@@ -410,10 +406,7 @@ parse_string (GtkCssParser *parser,
token = gtk_css_parser_get_token (parser);
if (!gtk_css_token_is (token, GTK_CSS_TOKEN_STRING))
{
gtk_css_parser_error_syntax (parser, "Expected a string");
return FALSE;
}
return FALSE;
s = g_strdup (token->string.string);
gtk_css_parser_consume_token (parser);
@@ -695,10 +688,16 @@ create_ascii_glyphs (PangoFont *font)
glyph_string = pango_glyph_string_new ();
for (i = MIN_ASCII_GLYPH; i < MAX_ASCII_GLYPH; i++)
{
pango_shape ((char[2]) { i, 0 },
1,
&not_a_hack,
glyph_string);
const char text[2] = { i, 0 };
pango_shape_with_flags (text,
1,
text,
1,
&not_a_hack,
glyph_string,
PANGO_SHAPE_ROUND_POSITIONS);
if (glyph_string->num_glyphs != 1)
{
pango_glyph_string_free (glyph_string);
@@ -956,26 +955,6 @@ 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)
{
@@ -1592,31 +1571,6 @@ parse_blend_node (GtkCssParser *parser)
return result;
}
static GskRenderNode *
parse_mask_node (GtkCssParser *parser)
{
GskRenderNode *source = NULL;
GskRenderNode *mask = NULL;
const Declaration declarations[] = {
{ "source", parse_node, clear_node, &source },
{ "mask", parse_node, clear_node, &mask },
};
GskRenderNode *result;
parse_declarations (parser, declarations, G_N_ELEMENTS(declarations));
if (source == NULL)
source = create_default_render_node ();
if (mask == NULL)
mask = gsk_color_node_new (&GDK_RGBA("AAFF00"), &GRAPHENE_RECT_INIT (0, 0, 50, 50));
result = gsk_mask_node_new (source, mask);
gsk_render_node_unref (source);
gsk_render_node_unref (mask);
return result;
}
static GskRenderNode *
parse_repeat_node (GtkCssParser *parser)
{
@@ -1811,208 +1765,6 @@ 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)
{
@@ -2092,14 +1844,11 @@ parse_node (GtkCssParser *parser,
{ "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 },
{ "transform", parse_transform_node },
{ "glshader", parse_glshader_node },
{ "mask", parse_mask_node },
};
GskRenderNode **node_p = out_node;
guint i;
@@ -2351,7 +2100,7 @@ append_float_param (Printer *p,
float value,
float default_value)
{
/* Don't approximate-compare here, better be too verbose */
/* Don't approximate-compare here, better be topo verbose */
if (value == default_value)
return;
@@ -2526,11 +2275,8 @@ append_escaping_newlines (GString *str,
len = strcspn (string, "\n");
g_string_append_len (str, string, len);
string += len;
if (*string)
{
g_string_append (str, "\\\n");
string++;
}
g_string_append (str, "\\\n");
string++;
} while (*string);
}
@@ -2564,81 +2310,82 @@ base64_encode_with_linebreaks (const guchar *data,
return out;
}
static const char *
enum_to_nick (GType type,
int value)
void
gsk_text_node_serialize_glyphs (GskRenderNode *node,
GString *p)
{
GEnumClass *class;
GEnumValue *v;
const guint n_glyphs = gsk_text_node_get_num_glyphs (node);
const PangoGlyphInfo *glyphs = gsk_text_node_get_glyphs (node, NULL);
PangoFont *font = gsk_text_node_get_font (node);
GString *str;
guint i, j;
PangoGlyphString *ascii;
class = g_type_class_ref (type);
v = g_enum_get_value (class, value);
g_type_class_unref (class);
ascii = create_ascii_glyphs (font);
str = g_string_new ("");
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++)
for (i = 0; i < n_glyphs; i++)
{
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++)
if (ascii)
{
g_string_append_c (p->str, ' ');
string_append_double (p->str, dash[i]);
for (j = 0; j < ascii->num_glyphs; j++)
{
if (glyphs[i].glyph == ascii->glyphs[j].glyph &&
glyphs[i].geometry.width == ascii->glyphs[j].geometry.width &&
glyphs[i].geometry.x_offset == 0 &&
glyphs[i].geometry.y_offset == 0 &&
glyphs[i].attr.is_cluster_start)
{
g_string_append_c (str, j + MIN_ASCII_GLYPH);
break;
}
else if (glyphs[i].glyph == ascii->glyphs[j].glyph)
{
if (glyphs[i].geometry.width != ascii->glyphs[j].geometry.width)
g_print ("not ascii because of width (%d != %d)\n",
glyphs[i].geometry.width,
ascii->glyphs[j].geometry.width);
if (glyphs[i].geometry.x_offset != 0 ||
glyphs[i].geometry.y_offset != 0)
g_print ("not ascii because of offset\n");
if (!glyphs[i].attr.is_cluster_start)
g_print ("not ascii because of cluster\n");
}
}
if (j != ascii->num_glyphs)
continue;
}
if (str->len)
{
g_string_append_printf (p, "\"%s\", ", str->str);
g_string_set_size (str, 0);
}
g_string_append_printf (p, "%u %g",
glyphs[i].glyph,
(double) glyphs[i].geometry.width / PANGO_SCALE);
if (!glyphs[i].attr.is_cluster_start ||
glyphs[i].geometry.x_offset != 0 ||
glyphs[i].geometry.y_offset != 0)
{
g_string_append_printf (p, " %g %g",
(double) glyphs[i].geometry.x_offset / PANGO_SCALE,
(double) glyphs[i].geometry.y_offset / PANGO_SCALE);
if (!glyphs[i].attr.is_cluster_start)
g_string_append (p, " same-cluster");
}
if (i + 1 < n_glyphs)
g_string_append (p, ", ");
}
g_string_append (p->str, ";\n");
if (str->len)
g_string_append_printf (p, "\"%s\"", str->str);
g_string_free (str, TRUE);
if (ascii)
pango_glyph_string_free (ascii);
}
static void
@@ -2789,42 +2536,6 @@ 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, "miter-limit", 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);
}
@@ -3004,16 +2715,11 @@ render_node_print (Printer *p,
case GSK_TEXT_NODE:
{
const guint n_glyphs = gsk_text_node_get_num_glyphs (node);
const PangoGlyphInfo *glyphs = gsk_text_node_get_glyphs (node, NULL);
const graphene_point_t *offset = gsk_text_node_get_offset (node);
const GdkRGBA *color = gsk_text_node_get_color (node);
PangoFont *font = gsk_text_node_get_font (node);
PangoFontDescription *desc;
char *font_name;
GString *str;
guint i, j;
PangoGlyphString *ascii = create_ascii_glyphs (font);
start_node (p, "text");
@@ -3023,61 +2729,14 @@ render_node_print (Printer *p,
_indent (p);
desc = pango_font_describe (font);
font_name = pango_font_description_to_string (desc);
if (ascii == NULL)
g_print ("\"%s\" has no ascii table\n", font_name);
g_string_append_printf (p->str, "font: \"%s\";\n", font_name);
g_free (font_name);
pango_font_description_free (desc);
_indent (p);
str = g_string_new (NULL);
g_string_append (p->str, "glyphs: ");
for (i = 0; i < n_glyphs; i++)
{
if (ascii)
{
for (j = 0; j < ascii->num_glyphs; j++)
{
if (glyphs[i].glyph == ascii->glyphs[j].glyph &&
glyphs[i].geometry.width == ascii->glyphs[j].geometry.width &&
glyphs[i].geometry.x_offset == 0 &&
glyphs[i].geometry.y_offset == 0 &&
glyphs[i].attr.is_cluster_start)
{
g_string_append_c (str, j + MIN_ASCII_GLYPH);
break;
}
}
if (j != ascii->num_glyphs)
continue;
}
if (str->len)
{
g_string_append_printf (p->str, "\"%s\", ", str->str);
g_string_set_size (str, 0);
}
g_string_append_printf (p->str, "%u %g",
glyphs[i].glyph,
(double) glyphs[i].geometry.width / PANGO_SCALE);
if (!glyphs[i].attr.is_cluster_start ||
glyphs[i].geometry.x_offset != 0 ||
glyphs[i].geometry.y_offset != 0)
{
g_string_append_printf (p->str, "%g %g",
(double) glyphs[i].geometry.x_offset / PANGO_SCALE,
(double) glyphs[i].geometry.y_offset / PANGO_SCALE);
if (!glyphs[i].attr.is_cluster_start)
g_string_append (p->str, " same-cluster");
}
if (i + 1 < n_glyphs)
g_string_append (p->str, ", ");
}
if (str->len)
g_string_append_printf (p->str, "\"%s\"", str->str);
gsk_text_node_serialize_glyphs (node, p->str);
g_string_append_c (p->str, ';');
g_string_append_c (p->str, '\n');
@@ -3086,10 +2745,6 @@ render_node_print (Printer *p,
append_point_param (p, "offset", offset);
end_node (p);
g_string_free (str, TRUE);
if (ascii)
pango_glyph_string_free (ascii);
}
break;
@@ -3284,17 +2939,6 @@ render_node_print (Printer *p,
}
break;
case GSK_MASK_NODE:
{
start_node (p, "mask");
append_node_param (p, "source", gsk_mask_node_get_source (node));
append_node_param (p, "mask", gsk_mask_node_get_mask (node));
end_node (p);
}
break;
case GSK_NOT_A_RENDER_NODE:
g_assert_not_reached ();
break;
+4 -1
View File
@@ -13,7 +13,7 @@ typedef struct _GskRenderNodeClass GskRenderNodeClass;
* We don't add an "n-types" value to avoid having to handle
* it in every single switch.
*/
#define GSK_RENDER_NODE_TYPE_N_TYPES (GSK_MASK_NODE + 1)
#define GSK_RENDER_NODE_TYPE_N_TYPES (GSK_GL_SHADER_NODE + 1)
extern GType gsk_render_node_types[];
@@ -96,6 +96,9 @@ void gsk_render_node_diff_impossible (GskRenderNode
bool gsk_border_node_get_uniform (GskRenderNode *self);
void gsk_text_node_serialize_glyphs (GskRenderNode *self,
GString *str);
G_END_DECLS
#endif /* __GSK_RENDER_NODE_PRIVATE_H__ */
+10
View File
@@ -7,6 +7,16 @@
G_BEGIN_DECLS
#define GSK_ROUNDED_RECT_INIT_FROM_RECT(_r) \
(GskRoundedRect) { .bounds = _r, \
.corner = { \
GRAPHENE_SIZE_INIT(0, 0),\
GRAPHENE_SIZE_INIT(0, 0),\
GRAPHENE_SIZE_INIT(0, 0),\
GRAPHENE_SIZE_INIT(0, 0),\
}}
gboolean gsk_rounded_rect_is_circular (const GskRoundedRect *self);
void gsk_rounded_rect_path (const GskRoundedRect *self,
-208
View File
@@ -1,208 +0,0 @@
/*
* 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
@@ -1,41 +0,0 @@
/*
* 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__ */
-484
View File
@@ -1,484 +0,0 @@
/*
* 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);
}
/**
* gsk_stroke_to_cairo:
* @self: a #GskStroke
* @cr: the cairo context to configure
*
* A helper function that sets the stroke parameters
* of @cr from the values found in @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;
if (self1->line_width != self2->line_width ||
self1->line_cap != self2->line_cap ||
self1->line_join != self2->line_join ||
self1->miter_limit != self2->miter_limit)
return FALSE;
if (self1->n_dash != self2->n_dash)
return FALSE;
for (int i = 0; i < self1->n_dash; i++)
{
if (self1->dash[i] != self2->dash[i])
return FALSE;
}
if (self1->dash_offset != self2->dash_offset)
return FALSE;
return TRUE;
}
/**
* 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;
}
guint
gsk_stroke_hash (const GskStroke *stroke)
{
guint32 h = 5381;
h = (h << 5) + h + (guint) (stroke->line_width * 100);
h = (h << 5) + h + stroke->line_cap;
h = (h << 5) + h + stroke->line_join;
h = (h << 5) + h + (guint) (stroke->miter_limit * 100);
h = (h << 5) + h + stroke->n_dash;
for (int i = 0; i < stroke->n_dash; i++)
h = (h << 5) + h + (guint) (stroke->dash[i] * 100);
h = (h << 5) + h + (guint) (stroke->dash_offset * 100);
return h;
}
-91
View File
@@ -1,91 +0,0 @@
/*
* 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);
GDK_AVAILABLE_IN_ALL
void gsk_stroke_to_cairo (const GskStroke *self,
cairo_t *cr);
G_END_DECLS
#endif /* __GSK_STROKE_H__ */
-62
View File
@@ -1,62 +0,0 @@
/*
* 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 */
}
guint gsk_stroke_hash (const GskStroke *stroke);
G_END_DECLS
#endif /* __GSK_STROKE_PRIVATE_H__ */
+7 -5
View File
@@ -127,8 +127,10 @@ gsk_transform_alloc (const GskTransformClass *transform_class,
self->transform_class = transform_class;
self->category = next ? MIN (category, next->category) : category;
self->next = gsk_transform_is_identity (next) ? NULL : gsk_transform_ref (next);
g_clear_pointer (&next, gsk_transform_unref);
if (gsk_transform_is_identity (next))
gsk_transform_unref (next);
else
self->next = next;
return self;
}
@@ -217,7 +219,7 @@ gsk_identity_transform_equal (GskTransform *first_transform,
static const GskTransformClass GSK_IDENTITY_TRANSFORM_CLASS =
{
sizeof (GskTransform),
"GskIdentityMatrix",
"GskIdentityTransform",
gsk_identity_transform_finalize,
gsk_identity_transform_to_matrix,
gsk_identity_transform_apply_2d,
@@ -1707,8 +1709,8 @@ gsk_transform_invert (GskTransform *self)
/**
* gsk_transform_equal:
* @first: the first transform
* @second: the second transform
* @first: (nullable): the first transform
* @second: (nullable): the second transform
*
* Checks two transforms for equality.
*
-4
View File
@@ -26,11 +26,7 @@
#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__ */
+1 -17
View File
@@ -18,37 +18,26 @@ gsk_private_gl_shaders = [
'resources/glsl/blend.glsl',
'resources/glsl/repeat.glsl',
'resources/glsl/custom.glsl',
'resources/glsl/mask.glsl',
]
gsk_public_sources = files([
'gskcairorenderer.c',
'gskdiff.c',
'gskcairorenderer.c',
'gskglshader.c',
'gskpath.c',
'gskpathbuilder.c',
'gskpathdash.c',
'gskpathmeasure.c',
'gskpathstroke.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',
'gskcurveintersect.c',
'gskdebug.c',
'gskprivate.c',
'gskprofiler.c',
'gskspline.c',
'gl/gskglshaderbuilder.c',
'gl/gskglprofiler.c',
'gl/gskglglyphcache.c',
@@ -59,20 +48,15 @@ gsk_private_sources = files([
'gl/gskgliconcache.c',
'gl/opbuffer.c',
'gl/stb_rect_pack.c',
'gl/gskglpathcache.c',
])
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',
+3 -3
View File
@@ -15,8 +15,8 @@ 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 = mod (u_rotation - 90.0, 360.0);
if (rotation < 0.0)
rotation += 360.0;
rotation = PI / 180.0 * rotation;
@@ -55,7 +55,7 @@ void main() {
angle -= rotation;
// fract() does the modulo here, so now we have progress
// into the current conic
float offset = fract (angle / 2 / PI + 2);
float offset = fract (angle / 2.0 / PI + 2.0);
vec4 color = color_stops[0];
for (int i = 1; i < u_num_color_stops; i ++) {
-45
View File
@@ -1,45 +0,0 @@
// VERTEX_SHADER:
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
vUv = vec2(aUv.x, aUv.y);
}
// FRAGMENT_SHADER:
uniform sampler2D u_mask;
uniform vec4 u_texture_rect;
void main() {
vec4 source = GskTexture(u_source, vUv);
vec2 tc;
if (vUv.x < u_texture_rect.x || vUv.x > u_texture_rect.z)
{
gskSetOutputColor(vec4 (0, 0, 0, 0));
return;
}
tc.x = (vUv.x - u_texture_rect.x) / (u_texture_rect.z - u_texture_rect.x);
if (u_texture_rect.y <= u_texture_rect.w)
{
if (vUv.y < u_texture_rect.y || vUv.y > u_texture_rect.w)
{
gskSetOutputColor(vec4 (0, 0, 0, 0));
return;
}
tc.y = (vUv.y - u_texture_rect.y) / (u_texture_rect.w - u_texture_rect.y);
}
else
{
if (vUv.y < u_texture_rect.w || vUv.y > u_texture_rect.y)
{
gskSetOutputColor(vec4 (0, 0, 0, 0));
return;
}
tc.y = 1.0 - (vUv.y - u_texture_rect.w) / (u_texture_rect.y - u_texture_rect.w);
}
vec4 mask = GskTexture(u_mask, tc);
gskSetOutputColor(vec4 (source * mask.a));
}
-3
View File
@@ -260,9 +260,6 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self,
case GSK_RADIAL_GRADIENT_NODE:
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
case GSK_CONIC_GRADIENT_NODE:
case GSK_FILL_NODE:
case GSK_STROKE_NODE:
case GSK_MASK_NODE:
default:
FALLBACK ("Unsupported node '%s'", g_type_name_from_instance ((GTypeInstance *) node));
+22 -11
View File
@@ -66,11 +66,11 @@ get_color (GskPangoRenderer *crenderer,
rgba->red = color->red / 65535.;
rgba->green = color->green / 65535.;
rgba->blue = color->blue / 65535.;
rgba->alpha = a ? a / 65535. : crenderer->fg_color.alpha;
rgba->alpha = a ? a / 65535. : crenderer->fg_color->alpha;
}
else
{
*rgba = crenderer->fg_color;
*rgba = *crenderer->fg_color;
if (a)
rgba->alpha = a / 65535.;
}
@@ -138,11 +138,19 @@ gsk_pango_renderer_draw_trapezoid (PangoRenderer *renderer,
double x22)
{
GskPangoRenderer *crenderer = (GskPangoRenderer *) (renderer);
PangoLayout *layout;
PangoRectangle ink_rect;
cairo_t *cr;
double x, y;
cr = gtk_snapshot_append_cairo (crenderer->snapshot, &crenderer->bounds);
layout = pango_renderer_get_layout (renderer);
if (!layout)
return;
pango_layout_get_pixel_extents (layout, &ink_rect, NULL);
cr = gtk_snapshot_append_cairo (crenderer->snapshot,
&GRAPHENE_RECT_INIT (ink_rect.x, ink_rect.y,
ink_rect.width, ink_rect.height));
set_color (crenderer, part, cr);
x = y = 0;
@@ -234,7 +242,6 @@ gsk_pango_renderer_draw_shape (PangoRenderer *renderer,
int y)
{
GskPangoRenderer *crenderer = (GskPangoRenderer *) (renderer);
cairo_t *cr;
PangoLayout *layout;
PangoCairoShapeRendererFunc shape_renderer;
gpointer shape_renderer_data;
@@ -263,17 +270,25 @@ gsk_pango_renderer_draw_shape (PangoRenderer *renderer,
if (!handled)
{
cr = gtk_snapshot_append_cairo (crenderer->snapshot, &crenderer->bounds);
cairo_t *cr;
PangoRectangle ink_rect;
layout = pango_renderer_get_layout (renderer);
if (!layout)
return;
pango_layout_get_pixel_extents (layout, &ink_rect, NULL);
cr = gtk_snapshot_append_cairo (crenderer->snapshot,
&GRAPHENE_RECT_INIT (ink_rect.x, ink_rect.y,
ink_rect.width, ink_rect.height));
shape_renderer = pango_cairo_context_get_shape_renderer (pango_layout_get_context (layout),
&shape_renderer_data);
if (!shape_renderer)
return;
{
cairo_destroy (cr);
return;
}
set_color (crenderer, PANGO_RENDER_PART_FOREGROUND, cr);
@@ -480,7 +495,6 @@ gtk_snapshot_append_layout (GtkSnapshot *snapshot,
const GdkRGBA *color)
{
GskPangoRenderer *crenderer;
PangoRectangle ink_rect;
g_return_if_fail (snapshot != NULL);
g_return_if_fail (PANGO_IS_LAYOUT (layout));
@@ -488,10 +502,7 @@ gtk_snapshot_append_layout (GtkSnapshot *snapshot,
crenderer = gsk_pango_renderer_acquire ();
crenderer->snapshot = snapshot;
crenderer->fg_color = *color;
pango_layout_get_pixel_extents (layout, &ink_rect, NULL);
graphene_rect_init (&crenderer->bounds, ink_rect.x, ink_rect.y, ink_rect.width, ink_rect.height);
crenderer->fg_color = color;
pango_renderer_draw_layout (PANGO_RENDERER (crenderer), layout, 0, 0);
+2 -3
View File
@@ -49,7 +49,7 @@ typedef gboolean (*GskPangoShapeHandler) (PangoAttrShape *attr,
/*
* This is a PangoRenderer implementation that translates all the draw calls to
* gsk render nodes, using the GtkSnapshot helper class. Glyphs are translated
* to text nodes, all other draw calls fall back to cairo nodes.
* to text nodes, other draw calls may fall back to cairo nodes.
*/
struct _GskPangoRenderer
@@ -58,8 +58,7 @@ struct _GskPangoRenderer
GtkWidget *widget;
GtkSnapshot *snapshot;
GdkRGBA fg_color;
graphene_rect_t bounds;
const GdkRGBA *fg_color;
/* Error underline color for this widget */
GdkRGBA *error_color;
-1
View File
@@ -52,7 +52,6 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellRendererText, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellRendererToggle, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCellView, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkCheckButton, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkClipboard, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkColorButton, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkColorChooser, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkColorChooserDialog, g_object_unref)
+1 -1
View File
@@ -100,7 +100,7 @@ gtk_accels_remove (GtkAccels *accels,
position = gtk_accels_find (accels, action_and_target);
if (position < gtk_accels_get_size (accels))
gtk_accels_splice (accels, position, 1, NULL, 0);
gtk_accels_splice (accels, position, 1, FALSE, NULL, 0);
}
/*< private >
+3 -3
View File
@@ -317,8 +317,8 @@ gtk_box_buildable_iface_init (GtkBuildableIface *iface)
/**
* gtk_box_new:
* @orientation: the boxs orientation.
* @spacing: the number of pixels to place by default between children.
* @orientation: the boxs orientation
* @spacing: the number of pixels to place by default between children
*
* Creates a new #GtkBox.
*
@@ -479,7 +479,7 @@ gtk_box_get_baseline_position (GtkBox *box)
* gtk_box_insert_child_after:
* @box: a #GtkBox
* @child: the #GtkWidget to insert
* @sibling: (nullable): the sibling to move @child after, or %NULL
* @sibling: (nullable): the sibling after which to insert @child
*
* Inserts @child in the position after @sibling in the list
* of @box children. If @sibling is %NULL, insert @child at
+9 -2
View File
@@ -58,8 +58,15 @@
*
* # CSS nodes
*
* GtkColorButton has a single CSS node with name button. To differentiate
* it from a plain #GtkButton, it gets the .color style class.
* |[<!-- language="plain" -->
* colorbutton
* button.color
* [content]
*]|
*
* GtkColorButton has a single CSS node with name colorbutton which
* contains a button node. To differentiate it from a plain #GtkButton,
* it gets the .color style class.
*/
typedef struct _GtkColorButtonClass GtkColorButtonClass;
+1 -1
View File
@@ -167,7 +167,7 @@ gtk_css_selector_matches_insert_sorted (GtkCssSelectorMatches *matches,
break;
}
gtk_css_selector_matches_splice (matches, i, 0, (gpointer[1]) { data }, 1);
gtk_css_selector_matches_splice (matches, i, 0, FALSE, (gpointer[1]) { data }, 1);
}
static inline gboolean
+51 -3
View File
@@ -55,15 +55,63 @@
* source must be added to a widget as an event controller, using
* gtk_widget_add_controller().
*
* |[<!-- language="C" -->
* static void
* my_widget_init (MyWidget *self)
* {
* GtkDragSource *drag_source = gtk_drag_source_new ();
*
* g_signal_connect (drag_source, "prepare", G_CALLBACK (on_drag_prepare), self);
* g_signal_connect (drag_source, "drag-begin", G_CALLBACK (on_drag_begin), self);
*
* gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (drag_source));
* }
* ]|
*
* Setting up the content provider and icon ahead of time only
* makes sense when the data does not change. More commonly, you
* will want to set them up just in time. To do so, #GtkDragSource
* has #GtkDragSource::prepare and #GtkDragSource::drag-begin signals.
*
* The ::prepare signal is emitted before a drag is started, and
* can be used to set the content provider and actions that the
* drag should be started with. The ::drag-begin signal is emitted
* after the #GdkDrag object has been created, and can be used
* to set up the drag icon.
* drag should be started with.
*
* |[<!-- language="C" -->
* static GdkContentProvider *
* on_drag_prepare (GtkDragSource *source,
* double x,
* double y,
* MyWidget *self)
* {
* // This widget supports two types of content: GFile objects
* // and GdkPixbuf objects; GTK will handle the serialization
* // of these types automatically
* GFile *file = my_widget_get_file (self);
* GdkPixbuf *pixbuf = my_widget_get_pixbuf (self);
*
* return gdk_content_provider_new_union ((GdkContentProvider *[2]) {
* gdk_content_provider_new_typed (G_TYPE_FILE, file),
* gdk_content_provider_new_typed (GDK_TYPE_PIXBUF, pixbuf),
* }, 2);
* }
* ]|
*
* The ::drag-begin signal is emitted after the #GdkDrag object has
* been created, and can be used to set up the drag icon.
*
* |[<!-- language="C" -->
* static void
* on_drag_begin (GtkDragSource *source,
* GtkDrag *drag,
* MyWidget *self)
* {
* // Set the widget as the drag icon
* GdkPaintable *paintable = gtk_widget_paintable_new (GTK_WIDGET (self));
* gtk_drag_source_set_icon (source, paintable, 0, 0);
* g_object_unref (paintable);
* }
* ]|
*
* During the DND operation, GtkDragSource emits signals that
* can be used to obtain updates about the status of the operation,
+40 -1
View File
@@ -47,7 +47,46 @@
* The most basic way to use a #GtkDropTarget to receive drops on a
* widget is to create it via gtk_drop_target_new() passing in the
* #GType of the data you want to receive and connect to the
* GtkDropTarget::drop signal to receive the data.
* GtkDropTarget::drop signal to receive the data:
*
* |[<!-- language="C" -->
* static gboolean
* on_drop (GtkDropTarget *target,
* const GValue *value,
* double x,
* double y,
* gpointer data)
* {
* MyWidget *self = data;
*
* // Call the appropriate setter depending on the type of data
* // that we received
* if (G_VALUE_HOLDS (value, G_TYPE_FILE))
* my_widget_set_file (self, g_value_get_object (value));
* else if (G_VALUE_HOLDS (value, GDK_TYPE_PIXBUF))
* my_widget_set_pixbuf (self, g_value_get_object (value));
* else
* return FALSE;
*
* return TRUE;
* }
*
* static void
* my_widget_init (MyWidget *self)
* {
* GtkDropTarget *target =
* gtk_drop_target_new (G_TYPE_INVALID, GDK_ACTION_COPY);
*
* // This widget accepts two types of drop types: GFile objects
* // and GdkPixbuf objects
* gtk_drop_target_set_gtypes (target, (GTypes [2]) {
* G_TYPE_FILE,
* GDK_TYPE_PIXBUF,
* }, 2);
*
* gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (target));
* }
* ]|
*
* #GtkDropTarget supports more options, such as:
*
+10 -2
View File
@@ -133,8 +133,8 @@
* }
* ]|
*
* Finally, use gtk_editable_delegate_set_property() in your set_property
* function (and similar for get_property), to set the editable properties:
* Finally, use gtk_editable_delegate_set_property() in your `set_property`
* function (and similar for `get_property`), to set the editable properties:
*
* |[
* ...
@@ -144,6 +144,14 @@
* switch (prop_id)
* ...
* ]|
*
* It is important to note that if you create a GtkEditable that uses a delegate,
* the low level #GtkEditable::insert-text and #GtkEditable::delete-text signals
* will be propagated from the "wrapper" editable to the delegate, but they will
* not be propagated from the delegate to the "wrapper" editable, as they would
* cause an infinite recursion. If you wish to connect to the #GtkEditable::insert-text
* and #GtkEditable::delete-text signals, you will need to connect to them on
* the delegate obtained via gtk_editable_get_delegate().
*/
#include "config.h"
+8 -1
View File
@@ -59,7 +59,14 @@
*
* # CSS nodes
*
* GtkFontButton has a single CSS node with name fontbutton.
* |[<!-- language="plain" -->
* fontbutton
* button.font
* [content]
*]|
*
* GtkFontButton has a single CSS node with name fontbutton which
* contains a button node with the .font style class.
*/
typedef struct _GtkFontButtonClass GtkFontButtonClass;
+10 -3
View File
@@ -35,6 +35,7 @@
#include "gtkintl.h"
#include "gtkmarshalers.h"
#include "gtkmain.h"
#include "gtknative.h"
G_DEFINE_TYPE (GtkGestureStylus, gtk_gesture_stylus, GTK_TYPE_GESTURE_SINGLE)
@@ -319,6 +320,8 @@ gtk_gesture_stylus_get_backlog (GtkGestureStylus *gesture,
GArray *backlog_array;
GdkTimeCoord *history = NULL;
guint n_coords = 0, i;
double surf_x, surf_y;
GtkNative *native;
g_return_val_if_fail (GTK_IS_GESTURE_STYLUS (gesture), FALSE);
g_return_val_if_fail (backlog != NULL && n_elems != NULL, FALSE);
@@ -331,6 +334,9 @@ gtk_gesture_stylus_get_backlog (GtkGestureStylus *gesture,
if (!history)
return FALSE;
native = gtk_widget_get_native (gtk_get_event_widget (event));
gtk_native_get_surface_transform (native, &surf_x, &surf_y);
backlog_array = g_array_new (FALSE, FALSE, sizeof (GdkTimeCoord));
for (i = 0; i < n_coords; i++)
{
@@ -339,10 +345,11 @@ gtk_gesture_stylus_get_backlog (GtkGestureStylus *gesture,
g_array_append_val (backlog_array, *time_coord);
time_coord = &g_array_index (backlog_array, GdkTimeCoord, backlog_array->len - 1);
if (gtk_widget_compute_point (gtk_get_event_widget (event),
if (gtk_widget_compute_point (GTK_WIDGET (native),
gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)),
&GRAPHENE_POINT_INIT (time_coord->axes[GDK_AXIS_X],
time_coord->axes[GDK_AXIS_Y]),
&GRAPHENE_POINT_INIT (time_coord->axes[GDK_AXIS_X] - surf_x,
time_coord->axes[GDK_AXIS_Y] - surf_y),
&p))
{
time_coord->axes[GDK_AXIS_X] = p.x;
+5 -8
View File
@@ -3850,7 +3850,7 @@ gtk_icon_paintable_ensure_texture (GtkIconPaintable *self)
icon_ensure_texture__locked (self, FALSE);
texture = g_object_ref (self->texture);
texture = self->texture;
g_mutex_unlock (&self->texture_lock);
@@ -3867,10 +3867,10 @@ init_color_matrix (graphene_matrix_t *color_matrix,
const GdkRGBA *warning_color,
const GdkRGBA *error_color)
{
GdkRGBA fg_default = { 0.7450980392156863, 0.7450980392156863, 0.7450980392156863, 1.0};
GdkRGBA success_default = { 0.3046921492332342,0.6015716792553597, 0.023437857633325704, 1.0};
GdkRGBA warning_default = {0.9570458533607996, 0.47266346227206835, 0.2421911955443656, 1.0 };
GdkRGBA error_default = { 0.796887159533074, 0 ,0, 1.0 };
const GdkRGBA fg_default = { 0.7450980392156863, 0.7450980392156863, 0.7450980392156863, 1.0};
const GdkRGBA success_default = { 0.3046921492332342,0.6015716792553597, 0.023437857633325704, 1.0};
const GdkRGBA warning_default = {0.9570458533607996, 0.47266346227206835, 0.2421911955443656, 1.0 };
const GdkRGBA error_default = { 0.796887159533074, 0 ,0, 1.0 };
const GdkRGBA *fg = foreground_color ? foreground_color : &fg_default;
const GdkRGBA *sc = success_color ? success_color : &success_default;
const GdkRGBA *wc = warning_color ? warning_color : &warning_default;
@@ -3971,11 +3971,8 @@ gtk_icon_paintable_snapshot_with_colors (GtkIconPaintable *icon,
if (symbolic)
gtk_snapshot_pop (snapshot);
g_object_unref (texture);
}
static GdkPaintableFlags
icon_paintable_get_flags (GdkPaintable *paintable)
{
+82 -108
View File
@@ -3199,12 +3199,11 @@ get_layout_location (GtkLabel *self,
int *yp)
{
GtkWidget *widget = GTK_WIDGET (self);
int req_width, x, y;
int req_height;
int layout_width, layout_height, x, y;
float xalign, yalign;
PangoRectangle logical;
int baseline, layout_baseline, baseline_offset;
int label_width, label_height;
int widget_width, widget_height;
xalign = self->xalign;
yalign = self->yalign;
@@ -3212,19 +3211,17 @@ get_layout_location (GtkLabel *self,
if (_gtk_widget_get_direction (widget) != GTK_TEXT_DIR_LTR)
xalign = 1.0 - xalign;
pango_layout_get_extents (self->layout, NULL, &logical);
pango_layout_get_pixel_extents (self->layout, NULL, &logical);
pango_extents_to_pixels (&logical, NULL);
layout_width = logical.width;
layout_height = logical.height;
req_width = logical.width;
req_height = logical.height;
label_width = gtk_widget_get_width (widget);
label_height = gtk_widget_get_height (widget);
widget_width = gtk_widget_get_width (widget);
widget_height = gtk_widget_get_height (widget);
baseline = gtk_widget_get_allocated_baseline (widget);
x = floor ((xalign * (label_width - req_width)) - logical.x);
x = floor ((xalign * (widget_width - layout_width)) - logical.x);
baseline_offset = 0;
if (baseline != -1)
@@ -3234,23 +3231,7 @@ get_layout_location (GtkLabel *self,
yalign = 0.0; /* Can't support yalign while baseline aligning */
}
/* bgo#315462 - For single-line labels, *do* align the requisition with
* respect to the allocation, even if we are under-allocated. For multi-line
* labels, always show the top of the text when they are under-allocated. The
* rationale is this:
*
* - Single-line labels appear in GtkButtons, and it is very easy to get them
* to be smaller than their requisition. The button may clip the label, but
* the label will still be able to show most of itself and the focus
* rectangle. Also, it is fairly easy to read a single line of clipped text.
*
* - Multi-line labels should not be clipped to showing "something in the
* middle". You want to read the first line, at least, to get some context.
*/
if (pango_layout_get_line_count (self->layout) == 1)
y = floor ((label_height - req_height) * yalign) + baseline_offset;
else
y = floor (MAX ((label_height - req_height) * yalign, 0)) + baseline_offset;
y = floor ((widget_height - layout_height) * yalign) + baseline_offset;
if (xp)
*xp = x;
@@ -3440,42 +3421,84 @@ gtk_label_snapshot (GtkWidget *widget,
GtkLabelSelectionInfo *info;
GtkStyleContext *context;
int lx, ly;
int width, height, x;
int width, height;
info = self->select_info;
if (!self->text || (*self->text == '\0'))
return;
gtk_label_ensure_layout (self);
context = _gtk_widget_get_style_context (widget);
get_layout_location (self, &lx, &ly);
gtk_snapshot_render_layout (snapshot, context, lx, ly, self->layout);
info = self->select_info;
if (!info)
return;
width = gtk_widget_get_width (widget);
height = gtk_widget_get_height (widget);
x = 0;
if (self->text && (*self->text != '\0'))
if (info->selection_anchor != info->selection_end)
{
get_layout_location (self, &lx, &ly);
int range[2];
cairo_region_t *range_clip;
cairo_rectangle_int_t clip_rect;
int i;
gtk_snapshot_render_layout (snapshot, context, lx, ly, self->layout);
range[0] = MIN (info->selection_anchor, info->selection_end);
range[1] = MAX (info->selection_anchor, info->selection_end);
if (info && (info->selection_anchor != info->selection_end))
gtk_style_context_save_to_node (context, info->selection_node);
range_clip = gdk_pango_layout_get_clip_region (self->layout, lx, ly, range, 1);
for (i = 0; i < cairo_region_num_rectangles (range_clip); i++)
{
int range[2];
cairo_region_t *range_clip;
cairo_rectangle_int_t clip_rect;
int i;
cairo_region_get_rectangle (range_clip, i, &clip_rect);
range[0] = info->selection_anchor;
range[1] = info->selection_end;
gtk_snapshot_push_clip (snapshot, &GRAPHENE_RECT_FROM_RECT (&clip_rect));
gtk_snapshot_render_background (snapshot, context, 0, 0, width, height);
gtk_snapshot_render_layout (snapshot, context, lx, ly, self->layout);
gtk_snapshot_pop (snapshot);
}
if (range[0] > range[1])
{
int tmp = range[0];
range[0] = range[1];
range[1] = tmp;
}
cairo_region_destroy (range_clip);
gtk_style_context_save_to_node (context, info->selection_node);
gtk_style_context_restore (context);
}
else
{
GtkLabelLink *focus_link;
GtkLabelLink *active_link;
int range[2];
cairo_region_t *range_clip;
cairo_rectangle_int_t clip_rect;
int i;
GdkRectangle rect;
if (info->selectable &&
gtk_widget_has_focus (widget) &&
gtk_widget_is_drawable (widget))
{
PangoDirection cursor_direction;
cursor_direction = get_cursor_direction (self);
gtk_snapshot_render_insertion_cursor (snapshot, context,
lx, ly,
self->layout, self->select_info->selection_end,
cursor_direction);
}
focus_link = gtk_label_get_focus_link (self, NULL);
active_link = info->active_link;
if (active_link)
{
range[0] = active_link->start;
range[1] = active_link->end;
gtk_style_context_save_to_node (context, active_link->cssnode);
range_clip = gdk_pango_layout_get_clip_region (self->layout, lx, ly, range, 1);
for (i = 0; i < cairo_region_num_rectangles (range_clip); i++)
@@ -3483,7 +3506,7 @@ gtk_label_snapshot (GtkWidget *widget,
cairo_region_get_rectangle (range_clip, i, &clip_rect);
gtk_snapshot_push_clip (snapshot, &GRAPHENE_RECT_FROM_RECT (&clip_rect));
gtk_snapshot_render_background (snapshot, context, x, 0, width, height);
gtk_snapshot_render_background (snapshot, context, 0, 0, width, height);
gtk_snapshot_render_layout (snapshot, context, lx, ly, self->layout);
gtk_snapshot_pop (snapshot);
}
@@ -3492,71 +3515,22 @@ gtk_label_snapshot (GtkWidget *widget,
gtk_style_context_restore (context);
}
else if (info)
if (focus_link && gtk_widget_has_visible_focus (widget))
{
GtkLabelLink *focus_link;
GtkLabelLink *active_link;
int range[2];
cairo_region_t *range_clip;
cairo_rectangle_int_t clip_rect;
int i;
GdkRectangle rect;
range[0] = focus_link->start;
range[1] = focus_link->end;
if (info->selectable &&
gtk_widget_has_focus (widget) &&
gtk_widget_is_drawable (widget))
{
PangoDirection cursor_direction;
gtk_style_context_save_to_node (context, focus_link->cssnode);
cursor_direction = get_cursor_direction (self);
gtk_snapshot_render_insertion_cursor (snapshot, context,
lx, ly,
self->layout, self->select_info->selection_end,
cursor_direction);
}
range_clip = gdk_pango_layout_get_clip_region (self->layout, lx, ly, range, 1);
cairo_region_get_extents (range_clip, &rect);
focus_link = gtk_label_get_focus_link (self, NULL);
active_link = info->active_link;
gtk_snapshot_render_focus (snapshot, context, rect.x, rect.y, rect.width, rect.height);
if (active_link)
{
range[0] = active_link->start;
range[1] = active_link->end;
cairo_region_destroy (range_clip);
gtk_style_context_save_to_node (context, active_link->cssnode);
range_clip = gdk_pango_layout_get_clip_region (self->layout, lx, ly, range, 1);
for (i = 0; i < cairo_region_num_rectangles (range_clip); i++)
{
cairo_region_get_rectangle (range_clip, i, &clip_rect);
gtk_snapshot_push_clip (snapshot, &GRAPHENE_RECT_FROM_RECT (&clip_rect));
gtk_snapshot_render_background (snapshot, context, x, 0, width, height);
gtk_snapshot_render_layout (snapshot, context, lx, ly, self->layout);
gtk_snapshot_pop (snapshot);
}
cairo_region_destroy (range_clip);
gtk_style_context_restore (context);
}
if (focus_link && gtk_widget_has_visible_focus (widget))
{
range[0] = focus_link->start;
range[1] = focus_link->end;
gtk_style_context_save_to_node (context, focus_link->cssnode);
range_clip = gdk_pango_layout_get_clip_region (self->layout, lx, ly, range, 1);
cairo_region_get_extents (range_clip, &rect);
gtk_snapshot_render_focus (snapshot, context, rect.x, rect.y, rect.width, rect.height);
cairo_region_destroy (range_clip);
gtk_style_context_restore (context);
}
gtk_style_context_restore (context);
}
}
}
+3 -16
View File
@@ -936,8 +936,6 @@ rewrite_event_for_surface (GdkEvent *event,
GdkEventType type;
double x, y;
double dx, dy;
double *axes = NULL;
guint n_axes = 0;
type = gdk_event_get_event_type (event);
@@ -960,16 +958,6 @@ rewrite_event_for_surface (GdkEvent *event,
break;
}
if (gdk_event_get_axes (event, &axes, &n_axes))
{
double *axes_copy = g_memdup (axes, n_axes * sizeof (double));
/* The newly created event takes ownership of the axes, so
* we need a copy
*/
axes = axes_copy;
}
switch ((guint) type)
{
case GDK_BUTTON_PRESS:
@@ -982,7 +970,7 @@ rewrite_event_for_surface (GdkEvent *event,
gdk_event_get_modifier_state (event),
gdk_button_event_get_button (event),
x, y,
g_steal_pointer (&axes));
gdk_event_dup_axes (event));
case GDK_MOTION_NOTIFY:
return gdk_motion_event_new (new_surface,
gdk_event_get_device (event),
@@ -990,7 +978,7 @@ rewrite_event_for_surface (GdkEvent *event,
gdk_event_get_time (event),
gdk_event_get_modifier_state (event),
x, y,
g_steal_pointer (&axes));
gdk_event_dup_axes (event));
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
@@ -1002,7 +990,7 @@ rewrite_event_for_surface (GdkEvent *event,
gdk_event_get_time (event),
gdk_event_get_modifier_state (event),
x, y,
g_steal_pointer (&axes),
gdk_event_dup_axes (event),
gdk_touch_event_get_emulating_pointer (event));
case GDK_TOUCHPAD_SWIPE:
gdk_touchpad_event_get_deltas (event, &dx, &dy);
@@ -1030,7 +1018,6 @@ rewrite_event_for_surface (GdkEvent *event,
break;
}
g_assert (!axes);
return NULL;
}
+8 -2
View File
@@ -104,8 +104,14 @@
*
* # CSS nodes
*
* GtkMenuButton has a single CSS node with name button. To differentiate
* it from a plain #GtkButton, it gets the .popup style class.
* |[<!-- language="plain" -->
* menubutton
* button.toggle
* [content]
*]|
*
* GtkMenuButton has a single CSS node with name menubutton
* which contains a toggle button node.
*
* # Accessibility
*
+2 -5
View File
@@ -73,7 +73,7 @@
* and #GtkModelButton:icon properties.
*
* The appearance of model buttons can be influenced with the
* #GtkModelButton:centered and #GtkModelButton:iconic properties.
* #GtkModelButton:iconic property.
*
* Model buttons have built-in support for submenus in #GtkPopoverMenu.
* To make a GtkModelButton that opens a submenu when activated, set
@@ -177,7 +177,6 @@ struct _GtkModelButton
GtkEventController *controller;
guint active : 1;
guint centered : 1;
guint iconic : 1;
guint keep_open : 1;
};
@@ -490,7 +489,7 @@ update_node_name (GtkModelButton *self)
{
case GTK_BUTTON_ROLE_TITLE:
start_name = "arrow";
end_name = NULL;
end_name = "";
break;
case GTK_BUTTON_ROLE_NORMAL:
start_name = NULL;
@@ -760,8 +759,6 @@ gtk_model_button_set_iconic (GtkModelButton *self,
gtk_widget_add_css_class (widget, "flat");
}
self->centered = iconic;
if (!iconic)
{
if (self->start_indicator)
+1 -1
View File
@@ -206,7 +206,7 @@ gtk_multi_filter_remove (GtkMultiFilter *self,
filter = gtk_filters_get (&self->filters, position);
g_signal_handlers_disconnect_by_func (filter, gtk_multi_filter_changed_cb, self);
gtk_filters_splice (&self->filters, position, 1, NULL, 0);
gtk_filters_splice (&self->filters, position, 1, FALSE, NULL, 0);
gtk_filter_changed (GTK_FILTER (self),
GTK_MULTI_FILTER_GET_CLASS (self)->removal_change);
+1 -1
View File
@@ -432,7 +432,7 @@ gtk_multi_sorter_remove (GtkMultiSorter *self,
sorter = gtk_sorters_get (&self->sorters, position);
g_signal_handlers_disconnect_by_func (sorter, gtk_multi_sorter_changed_cb, self);
gtk_sorters_splice (&self->sorters, position, 1, NULL, 0);
gtk_sorters_splice (&self->sorters, position, 1, FALSE, NULL, 0);
gtk_sorter_changed_with_keys (GTK_SORTER (self),
GTK_SORTER_CHANGE_LESS_STRICT,
+3
View File
@@ -135,6 +135,9 @@ gtk_password_entry_buffer_real_deleted_text (GtkEntryBuffer *buffer,
memmove (self->text + start, self->text + end, self->text_bytes + 1 - end);
self->text_chars -= n_chars;
self->text_bytes -= (end - start);
g_object_notify (G_OBJECT (buffer), "text");
g_object_notify (G_OBJECT (buffer), "length");
}
static guint
+21 -40
View File
@@ -1204,28 +1204,6 @@ get_border (GtkCssNode *node,
border->left = _gtk_css_number_value_get (style->border->border_left_width, 100);
}
static GskPath *
gtk_popover_get_tail_path (GtkPopover *popover)
{
GskPathBuilder *builder;
int initial_x, initial_y;
int tip_x, tip_y;
int final_x, final_y;
builder = gsk_path_builder_new ();
gtk_popover_get_gap_coords (popover,
&initial_x, &initial_y,
&tip_x, &tip_y,
&final_x, &final_y);
gsk_path_builder_move_to (builder, initial_x, initial_y);
gsk_path_builder_line_to (builder, tip_x, tip_y);
gsk_path_builder_line_to (builder, final_x, final_y);
return gsk_path_builder_free_to_path (builder);
}
static void
gtk_popover_apply_tail_path (GtkPopover *popover,
cairo_t *cr)
@@ -1419,14 +1397,22 @@ create_arrow_render_node (GtkPopover *popover)
GtkWidget *widget = GTK_WIDGET (popover);
GtkStyleContext *context;
GtkBorder border;
cairo_t *cr;
GtkSnapshot *snapshot;
GskPath *path;
snapshot = gtk_snapshot_new ();
cr = gtk_snapshot_append_cairo (snapshot,
&GRAPHENE_RECT_INIT (
0, 0,
gtk_widget_get_width (widget),
gtk_widget_get_height (widget)
));
/* Clip to the arrow shape */
path = gtk_popover_get_tail_path (popover);
gtk_snapshot_push_fill (snapshot, path, GSK_FILL_RULE_WINDING);
cairo_save (cr);
gtk_popover_apply_tail_path (popover, cr);
cairo_clip (cr);
get_border (priv->arrow_node, &border);
@@ -1434,34 +1420,29 @@ create_arrow_render_node (GtkPopover *popover)
gtk_style_context_save_to_node (context, priv->arrow_node);
/* Render the arrow background */
gtk_snapshot_render_background (snapshot, context,
0, 0,
gtk_widget_get_width (widget),
gtk_widget_get_height (widget));
gtk_render_background (context, cr,
0, 0,
gtk_widget_get_width (widget),
gtk_widget_get_height (widget));
/* Render the border of the arrow tip */
if (border.bottom > 0)
{
GtkCssStyle *style;
const GdkRGBA *border_color;
GskStroke *stroke;
graphene_rect_t bounds;
style = gtk_css_node_get_style (priv->arrow_node);
border_color = gtk_css_color_value_get_rgba (style->border->border_left_color ? style->border->border_left_color : style->core->color);
stroke = gsk_stroke_new (border.bottom + 1);
gtk_snapshot_push_stroke (snapshot, path, stroke);
gsk_stroke_free (stroke);
gtk_popover_apply_tail_path (popover, cr);
gdk_cairo_set_source_rgba (cr, border_color);
gsk_path_get_bounds (path, &bounds);
gtk_snapshot_append_color (snapshot, border_color, &bounds);
gtk_snapshot_pop (snapshot);
cairo_set_line_width (cr, border.bottom + 1);
cairo_stroke (cr);
}
gtk_snapshot_pop (snapshot);
gsk_path_unref (path);
cairo_restore (cr);
cairo_destroy (cr);
gtk_style_context_restore (context);
+79 -142
View File
@@ -357,16 +357,16 @@ snapshot_frame_fill (GtkSnapshot *snapshot,
gtk_snapshot_append_border (snapshot, outline, border_width, colors);
}
static GskStroke *
create_stroke_style (double line_width,
GtkBorderStyle style,
double length)
static void
set_stroke_style (cairo_t *cr,
double line_width,
GtkBorderStyle style,
double length)
{
GskStroke *stroke;
float segments[2];
double segments[2];
double n;
stroke = gsk_stroke_new (line_width);
cairo_set_line_width (cr, line_width);
if (style == GTK_BORDER_STYLE_DOTTED)
{
@@ -374,12 +374,12 @@ create_stroke_style (double line_width,
segments[0] = 0;
segments[1] = n ? length / n : 2;
gsk_stroke_set_dash (stroke, segments, 2);
cairo_set_dash (cr, segments, G_N_ELEMENTS (segments), 0);
gsk_stroke_set_line_cap (stroke, GSK_LINE_CAP_ROUND);
gsk_stroke_set_line_join (stroke, GSK_LINE_JOIN_ROUND);
cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
}
else if (style == GTK_BORDER_STYLE_DASHED)
else
{
n = length / line_width;
/* Optimize the common case of an integer-sized rectangle
@@ -397,33 +397,32 @@ create_stroke_style (double line_width,
segments[0] = n ? (1. / 3) * length / n : 1;
segments[1] = 2 * segments[0];
}
gsk_stroke_set_dash (stroke, segments, 2);
cairo_set_dash (cr, segments, G_N_ELEMENTS (segments), 0);
gsk_stroke_set_line_cap (stroke, GSK_LINE_CAP_SQUARE);
gsk_stroke_set_line_join (stroke, GSK_LINE_JOIN_MITER);
cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
}
else
{
g_assert_not_reached ();
}
return stroke;
}
static void
snapshot_frame_stroke (GtkSnapshot *snapshot,
const GskRoundedRect *border_box,
const float border_width[4],
GdkRGBA colors[4],
guint hidden_side,
GtkBorderStyle stroke_style)
render_frame_stroke (cairo_t *cr,
const GskRoundedRect *border_box,
const double border_width[4],
GdkRGBA colors[4],
guint hidden_side,
GtkBorderStyle stroke_style)
{
gboolean different_colors, different_borders;
GskRoundedRect stroke_box;
GskPathBuilder *builder;
GskPath *path;
GskStroke *stroke;
guint i;
different_colors = !gdk_rgba_equal (&colors[0], &colors[1]) ||
!gdk_rgba_equal (&colors[0], &colors[2]) ||
!gdk_rgba_equal (&colors[0], &colors[3]);
different_borders = border_width[0] != border_width[1] ||
border_width[0] != border_width[2] ||
border_width[0] != border_width[3] ;
stroke_box = *border_box;
gsk_rounded_rect_shrink (&stroke_box,
border_width[GTK_CSS_TOP] / 2.0,
@@ -431,36 +430,32 @@ snapshot_frame_stroke (GtkSnapshot *snapshot,
border_width[GTK_CSS_BOTTOM] / 2.0,
border_width[GTK_CSS_LEFT] / 2.0);
if (border_width[0] == border_width[1] &&
border_width[0] == border_width[2] &&
border_width[0] == border_width[3] &&
hidden_side == 0)
if (!different_colors && !different_borders && hidden_side == 0)
{
double length = 0;
/* FAST PATH:
* Mostly expected to trigger for focus rectangles */
for (i = 0; i < 4; i++)
for (i = 0; i < 4; i++)
{
length += _gtk_rounded_box_guess_length (&stroke_box, i);
}
builder = gsk_path_builder_new ();
gsk_path_builder_add_rounded_rect (builder, &stroke_box);
path = gsk_path_builder_free_to_path (builder);
stroke = create_stroke_style (border_width[0],
stroke_style, length);
gtk_snapshot_push_stroke (snapshot, path, stroke);
gsk_stroke_free (stroke);
gsk_path_unref (path);
gtk_snapshot_append_border (snapshot, border_box, border_width, colors);
gtk_snapshot_pop (snapshot);
gsk_rounded_rect_path (&stroke_box, cr);
gdk_cairo_set_source_rgba (cr, &colors[0]);
set_stroke_style (cr, border_width[0], stroke_style, length);
cairo_stroke (cr);
}
else
{
const float weight = sqrtf(2)/2.0;
GskRoundedRect padding_box;
padding_box = *border_box;
gsk_rounded_rect_shrink (&padding_box,
border_width[GTK_CSS_TOP],
border_width[GTK_CSS_RIGHT],
border_width[GTK_CSS_BOTTOM],
border_width[GTK_CSS_LEFT]);
for (i = 0; i < 4; i++)
{
@@ -470,111 +465,49 @@ snapshot_frame_stroke (GtkSnapshot *snapshot,
if (border_width[i] == 0)
continue;
builder = gsk_path_builder_new ();
cairo_save (cr);
if (i == 0)
{
/* top */
gsk_path_builder_move_to (builder,
stroke_box.bounds.origin.x + stroke_box.corner[GSK_CORNER_TOP_LEFT].width / 2,
stroke_box.bounds.origin.y + stroke_box.corner[GSK_CORNER_TOP_LEFT].height / 2);
gsk_path_builder_conic_to (builder,
stroke_box.bounds.origin.x,
stroke_box.bounds.origin.y,
stroke_box.bounds.origin.x + stroke_box.corner[GSK_CORNER_TOP_LEFT].width,
stroke_box.bounds.origin.y,
weight);
gsk_path_builder_line_to (builder,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width - stroke_box.corner[GSK_CORNER_TOP_RIGHT].width,
stroke_box.bounds.origin.y);
gsk_path_builder_conic_to (builder,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width,
stroke_box.bounds.origin.y,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width - stroke_box.corner[GSK_CORNER_TOP_RIGHT].width / 2,
stroke_box.bounds.origin.y + stroke_box.corner[GSK_CORNER_TOP_RIGHT].height / 2,
weight);
}
_gtk_rounded_box_path_top (border_box, &padding_box, cr);
else if (i == 1)
{
/* right */
gsk_path_builder_move_to (builder,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width - stroke_box.corner[GSK_CORNER_TOP_RIGHT].width / 2,
stroke_box.bounds.origin.y + stroke_box.corner[GSK_CORNER_TOP_RIGHT].height / 2);
gsk_path_builder_conic_to (builder,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width,
stroke_box.bounds.origin.y,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width,
stroke_box.bounds.origin.y + stroke_box.corner[GSK_CORNER_TOP_RIGHT].height,
weight);
gsk_path_builder_line_to (builder,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height - stroke_box.corner[GSK_CORNER_BOTTOM_RIGHT].height);
gsk_path_builder_conic_to (builder,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width - stroke_box.corner[GSK_CORNER_BOTTOM_RIGHT].width / 2,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height - stroke_box.corner[GSK_CORNER_BOTTOM_RIGHT].height / 2,
weight);
}
_gtk_rounded_box_path_right (border_box, &padding_box, cr);
else if (i == 2)
{
/* bottom */
gsk_path_builder_move_to (builder,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width - stroke_box.corner[GSK_CORNER_BOTTOM_RIGHT].width / 2,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height - stroke_box.corner[GSK_CORNER_BOTTOM_RIGHT].height / 2);
gsk_path_builder_conic_to (builder,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height,
stroke_box.bounds.origin.x + stroke_box.bounds.size.width - stroke_box.corner[GSK_CORNER_BOTTOM_RIGHT].width,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height,
weight);
gsk_path_builder_line_to (builder,
stroke_box.bounds.origin.x + stroke_box.corner[GSK_CORNER_BOTTOM_LEFT].width,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height);
gsk_path_builder_conic_to (builder,
stroke_box.bounds.origin.x,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height,
stroke_box.bounds.origin.x + stroke_box.corner[GSK_CORNER_BOTTOM_LEFT].width / 2,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height - stroke_box.corner[GSK_CORNER_BOTTOM_LEFT].height / 2,
weight);
}
_gtk_rounded_box_path_bottom (border_box, &padding_box, cr);
else if (i == 3)
{
/* left */
gsk_path_builder_move_to (builder,
stroke_box.bounds.origin.x + stroke_box.corner[GSK_CORNER_BOTTOM_LEFT].width / 2,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height - stroke_box.corner[GSK_CORNER_BOTTOM_LEFT].height / 2);
gsk_path_builder_conic_to (builder,
stroke_box.bounds.origin.x,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height,
stroke_box.bounds.origin.x,
stroke_box.bounds.origin.y + stroke_box.bounds.size.height - stroke_box.corner[GSK_CORNER_BOTTOM_LEFT].height,
weight);
gsk_path_builder_line_to (builder,
stroke_box.bounds.origin.x,
stroke_box.bounds.origin.y + stroke_box.corner[GSK_CORNER_TOP_LEFT].height);
gsk_path_builder_conic_to (builder,
stroke_box.bounds.origin.x,
stroke_box.bounds.origin.y,
stroke_box.bounds.origin.x + stroke_box.corner[GSK_CORNER_TOP_LEFT].width,
stroke_box.bounds.origin.y,
weight);
}
_gtk_rounded_box_path_left (border_box, &padding_box, cr);
cairo_clip (cr);
path = gsk_path_builder_free_to_path (builder);
stroke = create_stroke_style (border_width[i],
stroke_style,
_gtk_rounded_box_guess_length (&stroke_box, i));
gtk_snapshot_push_stroke (snapshot, path, stroke);
gsk_stroke_free (stroke);
gsk_path_unref (path);
_gtk_rounded_box_path_side (&stroke_box, cr, i);
gtk_snapshot_append_border (snapshot, border_box, border_width, colors);
gdk_cairo_set_source_rgba (cr, &colors[i]);
set_stroke_style (cr,
border_width[i],
stroke_style,
_gtk_rounded_box_guess_length (&stroke_box, i));
cairo_stroke (cr);
gtk_snapshot_pop (snapshot);
cairo_restore (cr);
}
}
}
static void
snapshot_frame_stroke (GtkSnapshot *snapshot,
const GskRoundedRect *outline,
const float border_width[4],
GdkRGBA colors[4],
guint hidden_side,
GtkBorderStyle stroke_style)
{
double double_width[4] = { border_width[0], border_width[1], border_width[2], border_width[3] };
cairo_t *cr;
cr = gtk_snapshot_append_cairo (snapshot,
&outline->bounds);
render_frame_stroke (cr, outline, double_width, colors, hidden_side, stroke_style);
cairo_destroy (cr);
}
static void
color_shade (const GdkRGBA *color,
double factor,
@@ -810,8 +743,12 @@ gtk_css_style_snapshot_outline (GtkCssBoxes *boxes,
if (gdk_rgba_is_clear (color))
return;
border_style[1] = border_style[2] = border_style[3] = border_style[0];
border_width[0] = _gtk_css_number_value_get (outline->outline_width, 100);
if (G_APPROX_VALUE (border_width[0], 0, FLT_EPSILON))
return;
border_style[1] = border_style[2] = border_style[3] = border_style[0];
border_width[3] = border_width[2] = border_width[1] = border_width[0];
colors[0] = colors[1] = colors[2] = colors[3] = *color;
+2 -210
View File
@@ -31,7 +31,6 @@
#include "gsktransformprivate.h"
#include "gsk/gskrendernodeprivate.h"
#include "gsk/gskstrokeprivate.h"
#include "gtk/gskpango.h"
@@ -109,14 +108,6 @@ struct _GtkSnapshotState {
struct {
GskRoundedRect bounds;
} rounded_clip;
struct {
GskPath *path;
GskFillRule fill_rule;
} fill;
struct {
GskPath *path;
GskStroke stroke;
} stroke;
struct {
gsize n_shadows;
GskShadow *shadows;
@@ -133,9 +124,6 @@ struct _GtkSnapshotState {
struct {
char *message;
} debug;
struct {
GskRenderNode *source_node;
} mask;
} data;
};
@@ -1114,135 +1102,6 @@ gtk_snapshot_push_rounded_clip (GtkSnapshot *snapshot,
gtk_rounded_rect_scale_affine (&state->data.rounded_clip.bounds, bounds, scale_x, scale_y, dx, dy);
}
static GskRenderNode *
gtk_snapshot_collect_fill (GtkSnapshot *snapshot,
GtkSnapshotState *state,
GskRenderNode **nodes,
guint n_nodes)
{
GskRenderNode *node, *fill_node;
node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
if (node == NULL)
return NULL;
fill_node = gsk_fill_node_new (node,
state->data.fill.path,
state->data.fill.fill_rule);
if (fill_node->bounds.size.width == 0 ||
fill_node->bounds.size.height == 0)
{
gsk_render_node_unref (node);
gsk_render_node_unref (fill_node);
return NULL;
}
gsk_render_node_unref (node);
return fill_node;
}
static void
gtk_snapshot_clear_fill (GtkSnapshotState *state)
{
gsk_path_unref (state->data.fill.path);
}
/**
* gtk_snapshot_push_fill:
* @snapshot: a #GtkSnapshot
* @path: The path describing the area to fill
* @fill_rule: The fill rule to use
*
* Fills the area given by @path and @fill_rule with an image and discards everything
* outside of it.
*
* The image is recorded until the next call to gtk_snapshot_pop().
*/
void
gtk_snapshot_push_fill (GtkSnapshot *snapshot,
GskPath *path,
GskFillRule fill_rule)
{
GtkSnapshotState *state;
/* FIXME: Is it worth calling ensure_affine() and transforming the path here? */
gtk_snapshot_ensure_identity (snapshot);
state = gtk_snapshot_push_state (snapshot,
gtk_snapshot_get_current_state (snapshot)->transform,
gtk_snapshot_collect_fill,
gtk_snapshot_clear_fill);
state->data.fill.path = gsk_path_ref (path);
state->data.fill.fill_rule = fill_rule;
}
static GskRenderNode *
gtk_snapshot_collect_stroke (GtkSnapshot *snapshot,
GtkSnapshotState *state,
GskRenderNode **nodes,
guint n_nodes)
{
GskRenderNode *node, *stroke_node;
node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
if (node == NULL)
return NULL;
stroke_node = gsk_stroke_node_new (node,
state->data.stroke.path,
&state->data.stroke.stroke);
if (stroke_node->bounds.size.width == 0 ||
stroke_node->bounds.size.height == 0)
{
gsk_render_node_unref (node);
gsk_render_node_unref (stroke_node);
return NULL;
}
gsk_render_node_unref (node);
return stroke_node;
}
static void
gtk_snapshot_clear_stroke (GtkSnapshotState *state)
{
gsk_path_unref (state->data.stroke.path);
gsk_stroke_clear (&state->data.stroke.stroke);
}
/**
* gtk_snapshot_push_stroke:
* @snapshot: a #GtkSnapshot
* @path: The path to stroke
* @stroke: The stroke attributes
*
* Strokes the given @path with the attributes given by @stroke and the
* image being recorded until the next call to gtk_snapshot_pop().
*/
void
gtk_snapshot_push_stroke (GtkSnapshot *snapshot,
GskPath *path,
const GskStroke *stroke)
{
GtkSnapshotState *state;
/* FIXME: Is it worth calling ensure_affine() and transforming the path here? */
gtk_snapshot_ensure_identity (snapshot);
state = gtk_snapshot_push_state (snapshot,
gtk_snapshot_get_current_state (snapshot)->transform,
gtk_snapshot_collect_stroke,
gtk_snapshot_clear_stroke);
state->data.stroke.path = gsk_path_ref (path);
gsk_stroke_init_copy (&state->data.stroke.stroke, stroke);
}
static GskRenderNode *
gtk_snapshot_collect_shadow (GtkSnapshot *snapshot,
GtkSnapshotState *state,
@@ -1392,73 +1251,6 @@ gtk_snapshot_push_blend (GtkSnapshot *snapshot,
NULL);
}
static GskRenderNode *
gtk_snapshot_collect_mask_mask (GtkSnapshot *snapshot,
GtkSnapshotState *state,
GskRenderNode **nodes,
guint n_nodes)
{
GskRenderNode *source_child, *mask_child, *mask_node;
mask_child = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
source_child = state->data.mask.source_node;
if (source_child == NULL ||
mask_child == NULL)
return NULL;
mask_node = gsk_mask_node_new (source_child, mask_child);
gsk_render_node_unref (source_child);
gsk_render_node_unref (mask_child);
return mask_node;
}
static GskRenderNode *
gtk_snapshot_collect_mask_source (GtkSnapshot *snapshot,
GtkSnapshotState *state,
GskRenderNode **nodes,
guint n_nodes)
{
GtkSnapshotState *prev_state = gtk_snapshot_get_previous_state (snapshot);
g_assert (prev_state->collect_func == gtk_snapshot_collect_mask_mask);
prev_state->data.mask.source_node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
return NULL;
}
static void
gtk_snapshot_clear_mask_source (GtkSnapshotState *state)
{
g_clear_pointer (&(state->data.mask.source_node), gsk_render_node_unref);
}
/**
* gtk_snapshot_push_mask:
* @snapshot: a #GtkSnapshot
*
* Calling this function requires 2 subsequent calls to gtk_snapshot_pop().
**/
void
gtk_snapshot_push_mask (GtkSnapshot *snapshot)
{
GtkSnapshotState *current_state = gtk_snapshot_get_current_state (snapshot);
GtkSnapshotState *source_state;
source_state = gtk_snapshot_push_state (snapshot,
current_state->transform,
gtk_snapshot_collect_mask_source,
gtk_snapshot_clear_mask_source);
gtk_snapshot_push_state (snapshot,
source_state->transform,
gtk_snapshot_collect_mask_mask,
NULL);
}
static GskRenderNode *
gtk_snapshot_collect_cross_fade_end (GtkSnapshot *snapshot,
GtkSnapshotState *state,
@@ -1594,7 +1386,7 @@ gtk_snapshot_pop_one (GtkSnapshot *snapshot)
/* Remove all the state's nodes from the list of nodes */
g_assert (state->start_node_index + state->n_nodes == gtk_snapshot_nodes_get_size (&snapshot->nodes));
gtk_snapshot_nodes_splice (&snapshot->nodes, state->start_node_index, state->n_nodes, NULL, 0);
gtk_snapshot_nodes_splice (&snapshot->nodes, state->start_node_index, state->n_nodes, FALSE, NULL, 0);
}
else
{
@@ -1608,7 +1400,7 @@ gtk_snapshot_pop_one (GtkSnapshot *snapshot)
g_assert (previous_state->start_node_index + previous_state->n_nodes == gtk_snapshot_nodes_get_size (&snapshot->nodes));
}
gtk_snapshot_states_splice (&snapshot->state_stack, state_index, 1, NULL, 0);
gtk_snapshot_states_splice (&snapshot->state_stack, state_index, 1, FALSE, NULL, 0);
return node;
}
-10
View File
@@ -89,14 +89,6 @@ GDK_AVAILABLE_IN_ALL
void gtk_snapshot_push_rounded_clip (GtkSnapshot *snapshot,
const GskRoundedRect *bounds);
GDK_AVAILABLE_IN_ALL
void gtk_snapshot_push_fill (GtkSnapshot *snapshot,
GskPath *path,
GskFillRule fill_rule);
GDK_AVAILABLE_IN_ALL
void gtk_snapshot_push_stroke (GtkSnapshot *snapshot,
GskPath *path,
const GskStroke *stroke);
GDK_AVAILABLE_IN_ALL
void gtk_snapshot_push_shadow (GtkSnapshot *snapshot,
const GskShadow *shadow,
gsize n_shadows);
@@ -104,8 +96,6 @@ GDK_AVAILABLE_IN_ALL
void gtk_snapshot_push_blend (GtkSnapshot *snapshot,
GskBlendMode blend_mode);
GDK_AVAILABLE_IN_ALL
void gtk_snapshot_push_mask (GtkSnapshot *snapshot);
GDK_AVAILABLE_IN_ALL
void gtk_snapshot_push_cross_fade (GtkSnapshot *snapshot,
double progress);
GDK_AVAILABLE_IN_ALL
+1 -1
View File
@@ -471,7 +471,7 @@ gtk_string_list_splice (GtkStringList *self,
else
n_additions = 0;
objects_splice (&self->items, position, n_removals, NULL, n_additions);
objects_splice (&self->items, position, n_removals, FALSE, NULL, n_additions);
for (i = 0; i < n_additions; i++)
{
+2 -15
View File
@@ -3906,7 +3906,6 @@ gtk_text_layout_after_buffer_delete_range (GtkTextBuffer *textbuffer,
static void
render_para (GskPangoRenderer *crenderer,
int offset_y,
GtkTextLineDisplay *line_display,
int selection_start_index,
int selection_end_index,
@@ -3936,13 +3935,6 @@ render_para (GskPangoRenderer *crenderer,
gtk_style_context_restore (context);
}
if (offset_y)
{
gtk_snapshot_save (crenderer->snapshot);
gtk_snapshot_translate (crenderer->snapshot,
&GRAPHENE_POINT_INIT (0, offset_y));
}
do
{
PangoLayoutLine *line = pango_layout_iter_get_line_readonly (iter);
@@ -4114,9 +4106,6 @@ render_para (GskPangoRenderer *crenderer,
}
while (pango_layout_iter_next_line (iter));
if (offset_y)
gtk_snapshot_restore (crenderer->snapshot);
pango_layout_iter_free (iter);
}
@@ -4175,9 +4164,7 @@ gtk_text_layout_snapshot (GtkTextLayout *layout,
crenderer->widget = widget;
crenderer->snapshot = snapshot;
crenderer->fg_color = color;
graphene_rect_init (&crenderer->bounds, 0, 0, clip->width, clip->height);
crenderer->fg_color = &color;
gtk_text_layout_wrap_loop_start (layout);
@@ -4248,7 +4235,7 @@ gtk_text_layout_snapshot (GtkTextLayout *layout,
if (line_display->node == NULL)
{
gtk_snapshot_push_collect (snapshot);
render_para (crenderer, 0, line_display,
render_para (crenderer, line_display,
selection_start_index, selection_end_index,
cursor_alpha);
-1
View File
@@ -37,7 +37,6 @@ typedef struct _GtkAdjustment GtkAdjustment;
typedef struct _GtkBitset GtkBitset;
typedef struct _GtkBuilder GtkBuilder;
typedef struct _GtkBuilderScope GtkBuilderScope;
typedef struct _GtkClipboard GtkClipboard;
typedef struct _GtkCssStyleChange GtkCssStyleChange;
typedef struct _GtkEventController GtkEventController;
typedef struct _GtkGesture GtkGesture;
+9 -5
View File
@@ -9504,12 +9504,16 @@ gtk_widget_remove_mnemonic_label (GtkWidget *widget,
if (new_list != NULL && new_list->data != NULL)
{
GList *list;
GtkAccessibleRelation relation = GTK_ACCESSIBLE_RELATION_LABELLED_BY;
GValue value = G_VALUE_INIT;
list = gtk_widget_list_mnemonic_labels (widget);
gtk_accessible_update_relation (GTK_ACCESSIBLE (widget),
GTK_ACCESSIBLE_RELATION_LABELLED_BY, list,
-1);
/* The ATContext takes ownership of the GList returned by list_mnemonic_labels(),
* so we don't need to free it
*/
gtk_accessible_relation_init_value (relation, &value);
g_value_set_pointer (&value, gtk_widget_list_mnemonic_labels (widget));
gtk_accessible_update_relation_value (GTK_ACCESSIBLE (widget), 1, &relation, &value);
g_value_unset (&value);
}
else
{
+2
View File
@@ -6480,6 +6480,8 @@ gtk_window_destroy (GtkWindow *window)
g_list_store_remove (toplevel_list, i);
gtk_window_release_application (window);
gtk_widget_unrealize (GTK_WIDGET (window));
g_object_unref (window);
+17 -9
View File
@@ -65,9 +65,9 @@ static void
recurse_child_widgets (GtkWidget *widget,
GtkSnapshot *snapshot)
{
gboolean needs_clip;
int width = gtk_widget_get_width (widget);
int height = gtk_widget_get_height (widget);
gboolean needs_clip;
GtkCssStyle *style;
GtkWidget *child;
GtkBorder boxes[4];
@@ -107,15 +107,23 @@ recurse_child_widgets (GtkWidget *widget,
const GdkRGBA *color = &colors[i];
const GtkBorder *box = &boxes[i];
gtk_snapshot_append_color (snapshot, color,
&GRAPHENE_RECT_INIT ( 0, - box->top, width, box->top));
gtk_snapshot_append_color (snapshot, color,
&GRAPHENE_RECT_INIT (width, 0, box->right, height));
gtk_snapshot_append_color (snapshot, color,
&GRAPHENE_RECT_INIT (0, height, width, box->bottom));
gtk_snapshot_append_color (snapshot, color,
&GRAPHENE_RECT_INIT (- box->left, 0, box->left, height));
if (gdk_rgba_is_clear (color))
goto next;
if (box->top > 0)
gtk_snapshot_append_color (snapshot, color,
&GRAPHENE_RECT_INIT ( 0, - box->top, width, box->top));
if (box->right > 0)
gtk_snapshot_append_color (snapshot, color,
&GRAPHENE_RECT_INIT (width, 0, box->right, height));
if (box->bottom > 0)
gtk_snapshot_append_color (snapshot, color,
&GRAPHENE_RECT_INIT (0, height, width, box->bottom));
if (box->left > 0)
gtk_snapshot_append_color (snapshot, color,
&GRAPHENE_RECT_INIT (- box->left, 0, box->left, height));
next:
/* Grow box + offset */
width += box->left + box->right;
height += box->top + box->bottom;
+5 -74
View File
@@ -161,12 +161,6 @@ create_list_model_for_render_node (GskRenderNode *node)
case GSK_ROUNDED_CLIP_NODE:
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_rounded_clip_node_get_child (node) }, 1);
case GSK_FILL_NODE:
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_fill_node_get_child (node) }, 1);
case GSK_STROKE_NODE:
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_stroke_node_get_child (node) }, 1);
case GSK_SHADOW_NODE:
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_shadow_node_get_child (node) }, 1);
@@ -174,10 +168,6 @@ create_list_model_for_render_node (GskRenderNode *node)
return create_render_node_list_model ((GskRenderNode *[2]) { gsk_blend_node_get_bottom_child (node),
gsk_blend_node_get_top_child (node) }, 2);
case GSK_MASK_NODE:
return create_render_node_list_model ((GskRenderNode *[2]) { gsk_mask_node_get_source (node),
gsk_mask_node_get_mask (node) }, 2);
case GSK_CROSS_FADE_NODE:
return create_render_node_list_model ((GskRenderNode *[2]) { gsk_cross_fade_node_get_start_child (node),
gsk_cross_fade_node_get_end_child (node) }, 2);
@@ -292,10 +282,6 @@ node_type_name (GskRenderNodeType type)
return "Clip";
case GSK_ROUNDED_CLIP_NODE:
return "Rounded Clip";
case GSK_FILL_NODE:
return "Fill";
case GSK_STROKE_NODE:
return "Stroke";
case GSK_SHADOW_NODE:
return "Shadow";
case GSK_BLEND_NODE:
@@ -308,8 +294,6 @@ node_type_name (GskRenderNodeType type)
return "Blur";
case GSK_GL_SHADER_NODE:
return "GL Shader";
case GSK_MASK_NODE:
return "Mask";
}
}
@@ -337,11 +321,8 @@ node_name (GskRenderNode *node)
case GSK_REPEAT_NODE:
case GSK_CLIP_NODE:
case GSK_ROUNDED_CLIP_NODE:
case GSK_FILL_NODE:
case GSK_STROKE_NODE:
case GSK_SHADOW_NODE:
case GSK_BLEND_NODE:
case GSK_MASK_NODE:
case GSK_CROSS_FADE_NODE:
case GSK_TEXT_NODE:
case GSK_BLUR_NODE:
@@ -594,20 +575,6 @@ add_float_row (GtkListStore *store,
g_free (text);
}
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
populate_render_node_properties (GtkListStore *store,
GskRenderNode *node)
@@ -807,13 +774,10 @@ populate_render_node_properties (GtkListStore *store,
case GSK_TEXT_NODE:
{
const PangoFont *font = gsk_text_node_get_font (node);
const PangoGlyphInfo *glyphs = gsk_text_node_get_glyphs (node, NULL);
const GdkRGBA *color = gsk_text_node_get_color (node);
guint num_glyphs = gsk_text_node_get_num_glyphs (node);
const graphene_point_t *offset = gsk_text_node_get_offset (node);
PangoFontDescription *desc;
GString *s;
int i;
desc = pango_font_describe ((PangoFont *)font);
tmp = pango_font_description_to_string (desc);
@@ -821,9 +785,8 @@ populate_render_node_properties (GtkListStore *store,
g_free (tmp);
pango_font_description_free (desc);
s = g_string_sized_new (6 * num_glyphs);
for (i = 0; i < num_glyphs; i++)
g_string_append_printf (s, "%x ", glyphs[i].glyph);
s = g_string_sized_new (0);
gsk_text_node_serialize_glyphs (node, s);
add_text_row (store, "Glyphs", s->str);
g_string_free (s, TRUE);
@@ -874,7 +837,9 @@ populate_render_node_properties (GtkListStore *store,
case GSK_BLEND_NODE:
{
GskBlendMode mode = gsk_blend_node_get_blend_mode (node);
add_text_row (store, "Blendmode", enum_to_nick (GSK_TYPE_BLEND_MODE, mode));
tmp = g_enum_to_string (GSK_TYPE_BLEND_MODE, mode);
add_text_row (store, "Blendmode", tmp);
g_free (tmp);
}
break;
@@ -1107,39 +1072,6 @@ populate_render_node_properties (GtkListStore *store,
}
break;
case GSK_FILL_NODE:
{
GskPath *path = gsk_fill_node_get_path (node);
GskFillRule fill_rule = gsk_fill_node_get_fill_rule (node);
tmp = gsk_path_to_string (path);
add_text_row (store, "Path", tmp);
g_free (tmp);
add_text_row (store, "Fill rule", enum_to_nick (GSK_TYPE_FILL_RULE, fill_rule));
}
break;
case GSK_STROKE_NODE:
{
GskPath *path = gsk_stroke_node_get_path (node);
const GskStroke *stroke = gsk_stroke_node_get_stroke (node);
GskLineCap line_cap = gsk_stroke_get_line_cap (stroke);
GskLineJoin line_join = gsk_stroke_get_line_join (stroke);
tmp = gsk_path_to_string (path);
add_text_row (store, "Path", tmp);
g_free (tmp);
tmp = g_strdup_printf ("%.2f", gsk_stroke_get_line_width (stroke));
add_text_row (store, "Line width", tmp);
g_free (tmp);
add_text_row (store, "Line cap", enum_to_nick (GSK_TYPE_LINE_CAP, line_cap));
add_text_row (store, "Line join", enum_to_nick (GSK_TYPE_LINE_JOIN, line_join));
}
break;
case GSK_CONTAINER_NODE:
tmp = g_strdup_printf ("%d", gsk_container_node_get_n_children (node));
add_text_row (store, "Children", tmp);
@@ -1200,7 +1132,6 @@ populate_render_node_properties (GtkListStore *store,
break;
case GSK_NOT_A_RENDER_NODE:
case GSK_MASK_NODE:
default:
break;
}
+1 -1
View File
@@ -1109,7 +1109,7 @@ libgtk = library('gtk-4',
c_args: gtk_cargs + common_cflags,
include_directories: [confinc, gdkinc, gskinc, gtkinc],
dependencies: gtk_deps + [libgtk_css_dep, libgdk_dep, libgsk_dep],
link_whole: [libgtk_css, libgdk, libgsk, libottie],
link_whole: [libgtk_css, libgdk, libgsk, ],
link_args: common_ldflags,
darwin_versions: darwin_versions,
install: true,
-180
View File
@@ -6310,186 +6310,6 @@ void ra_shift_tail(roaring_array_t *ra, int32_t count, int32_t distance);
#endif
/* end file include/roaring/roaring_array.h */
/* begin file include/roaring/misc/configreport.h */
/*
* configreport.h
*
*/
#ifndef INCLUDE_MISC_CONFIGREPORT_H_
#define INCLUDE_MISC_CONFIGREPORT_H_
#include <stddef.h> // for size_t
#include <stdint.h>
#include <stdio.h>
#ifdef IS_X64
// useful for basic info (0)
static inline void native_cpuid(unsigned int *eax, unsigned int *ebx,
unsigned int *ecx, unsigned int *edx) {
#ifdef ROARING_INLINE_ASM
__asm volatile("cpuid"
: "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx)
: "0"(*eax), "2"(*ecx));
#endif /* not sure what to do when inline assembly is unavailable*/
}
// CPUID instruction takes no parameters as CPUID implicitly uses the EAX
// register.
// The EAX register should be loaded with a value specifying what information to
// return
static inline void cpuinfo(int code, int *eax, int *ebx, int *ecx, int *edx) {
#ifdef ROARING_INLINE_ASM
__asm__ volatile("cpuid;" // call cpuid instruction
: "=a"(*eax), "=b"(*ebx), "=c"(*ecx),
"=d"(*edx) // output equal to "movl %%eax %1"
: "a"(code) // input equal to "movl %1, %%eax"
//:"%eax","%ebx","%ecx","%edx"// clobbered register
);
#endif /* not sure what to do when inline assembly is unavailable*/
}
static inline int computecacheline(void) {
int eax = 0, ebx = 0, ecx = 0, edx = 0;
cpuinfo((int)0x80000006, &eax, &ebx, &ecx, &edx);
return ecx & 0xFF;
}
// this is quite imperfect, but can be handy
static inline const char *guessprocessor(void) {
unsigned eax = 1, ebx = 0, ecx = 0, edx = 0;
native_cpuid(&eax, &ebx, &ecx, &edx);
const char *codename;
switch (eax >> 4) {
case 0x506E:
codename = "Skylake";
break;
case 0x406C:
codename = "CherryTrail";
break;
case 0x306D:
codename = "Broadwell";
break;
case 0x306C:
codename = "Haswell";
break;
case 0x306A:
codename = "IvyBridge";
break;
case 0x206A:
case 0x206D:
codename = "SandyBridge";
break;
case 0x2065:
case 0x206C:
case 0x206F:
codename = "Westmere";
break;
case 0x106E:
case 0x106A:
case 0x206E:
codename = "Nehalem";
break;
case 0x1067:
case 0x106D:
codename = "Penryn";
break;
case 0x006F:
case 0x1066:
codename = "Merom";
break;
case 0x0066:
codename = "Presler";
break;
case 0x0063:
case 0x0064:
codename = "Prescott";
break;
case 0x006D:
codename = "Dothan";
break;
case 0x0366:
codename = "Cedarview";
break;
case 0x0266:
codename = "Lincroft";
break;
case 0x016C:
codename = "Pineview";
break;
default:
codename = "UNKNOWN";
break;
}
return codename;
}
static inline void tellmeall(void) {
printf("Intel processor: %s\t", guessprocessor());
#ifdef __VERSION__
printf(" compiler version: %s\t", __VERSION__);
#endif
printf("\tBuild option USEAVX ");
#ifdef USEAVX
printf("enabled\n");
#else
printf("disabled\n");
#endif
#ifndef __AVX2__
printf("AVX2 is NOT available.\n");
#endif
if ((sizeof(int) != 4) || (sizeof(long) != 8)) {
printf("number of bytes: int = %lu long = %lu \n",
(long unsigned int)sizeof(size_t),
(long unsigned int)sizeof(int));
}
#if defined(__LITTLE_ENDIAN__) && __LITTLE_ENDIAN__
// This is what we expect!
// printf("you have little endian machine");
#endif
#if defined(__BIG_ENDIAN__) && __BIG_ENDIAN__
printf("you have a big endian machine");
#endif
#if __CHAR_BIT__
if (__CHAR_BIT__ != 8) printf("on your machine, chars don't have 8bits???");
#endif
if (computecacheline() != 64)
printf("cache line: %d bytes\n", computecacheline());
}
#else
static inline void tellmeall(void) {
printf("Non-X64 processor\n");
#ifdef __arm__
printf("ARM processor detected\n");
#endif
#ifdef __VERSION__
printf(" compiler version: %s\t", __VERSION__);
#endif
if ((sizeof(int) != 4) || (sizeof(long) != 8)) {
printf("number of bytes: int = %lu long = %lu \n",
(long unsigned int)sizeof(size_t),
(long unsigned int)sizeof(int));
}
#if __LITTLE_ENDIAN__
// This is what we expect!
// printf("you have little endian machine");
#endif
#if __BIG_ENDIAN__
printf("you have a big endian machine");
#endif
#if __CHAR_BIT__
if (__CHAR_BIT__ != 8) printf("on your machine, chars don't have 8bits???");
#endif
}
#endif
#endif /* INCLUDE_MISC_CONFIGREPORT_H_ */
/* end file include/roaring/misc/configreport.h */
/* begin file include/roaring/roaring.h */
/*
An implementation of Roaring Bitmaps in C.
+5 -3
View File
@@ -536,7 +536,8 @@ button {
transition: $button_transition;
transition-duration: 500ms;
}
&:active {
&:active,
&:checked {
@include button(undecorated-active);
transition: $button_transition;
}
@@ -1396,9 +1397,10 @@ windowcontrols {
min-height: 0;
&:hover {
//special case hover colors inside a headerbar
@include button(undecorated-hover,$c:$headerbar_bg_color);
@include button(undecorated-hover,$c:darken($headerbar_bg_color,10%));
}
&:active { @include button(undecorated-active,$c:$headerbar_bg_color); }
&:active,
&:checked { @include button(undecorated-active,$c:darken($headerbar_bg_color,10%)); }
}
}

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