Compare commits
304 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 127685ef22 | |||
| be81f6b4ab | |||
| 716e0b97bd | |||
| ff862dc926 | |||
| 17c2a1cb4e | |||
| 895e640fd0 | |||
| e8f5f86ad5 | |||
| 5055b41ee7 | |||
| 7efd08ca2e | |||
| 76b421e064 | |||
| 063e6baa0a | |||
| 9e5d412a8b | |||
| fa9b634d8f | |||
| d76379428d | |||
| 46509b6dd2 | |||
| 89dbf9cc81 | |||
| c138aaabf3 | |||
| 7cef454c86 | |||
| 5cb8d15505 | |||
| 2aab55983d | |||
| 31714e5c1d | |||
| 96c351e792 | |||
| edd57004d3 | |||
| 234d20641c | |||
| dd802f21e7 | |||
| dbbc990c72 | |||
| 3a6e772cba | |||
| c9fa16fcfa | |||
| 07b04fbea9 | |||
| 1c633cbea2 | |||
| 2f685d5d2a | |||
| 0fdf2cc195 | |||
| 4f7d18a28f | |||
| c77272a7d7 | |||
| 7a1004df73 | |||
| 0e86d2b345 | |||
| 2441bdb900 | |||
| 57679b7b7f | |||
| 37063e7a05 | |||
| 791dc7b9be | |||
| ff24dfb2e7 | |||
| 4db60fa5a8 | |||
| 5301367630 | |||
| 9409b7ef7d | |||
| afeb7f668b | |||
| 38b8da0f5f | |||
| d029b62d23 | |||
| 1e9bdb4647 | |||
| 1d72024605 | |||
| 06570443b7 | |||
| 77f7caf18d | |||
| 8767ffde2f | |||
| d58b7fa779 | |||
| dcbf3f8879 | |||
| 30d8c8e17c | |||
| b803bb5edb | |||
| 985a39d41f | |||
| f846eec894 | |||
| e7fc8ad1f5 | |||
| 95169ad54b | |||
| ddb2e91a42 | |||
| 90357193c9 | |||
| a336fe2850 | |||
| fcb8e4cf37 | |||
| b7b6c147f9 | |||
| 35ee82ca07 | |||
| 2dc56a6e9b | |||
| e12ef76de5 | |||
| 536b05e35b | |||
| 1354854d23 | |||
| 2b062d60f2 | |||
| c8bdb4c7fb | |||
| 80328e8a4f | |||
| 1138e3770b | |||
| ec58013b22 | |||
| 70cb61fb71 | |||
| 8e6a0ec23d | |||
| e2ab334636 | |||
| dd0effe957 | |||
| ac210c1765 | |||
| c58e48e648 | |||
| acdadab617 | |||
| aa19194f7b | |||
| 9319a6e39b | |||
| b41206abab | |||
| 07c3dc6b6f | |||
| fa71a2a993 | |||
| b997d1e892 | |||
| 4b71fba540 | |||
| 48d39c0a57 | |||
| 0aad053507 | |||
| 6ed14b2a5f | |||
| c7df5ef957 | |||
| a9013febcf | |||
| 815c430ba1 | |||
| e94d5bf006 | |||
| a00480f4a1 | |||
| 6da952100c | |||
| df8588e9b7 | |||
| fce9b35e4d | |||
| 6fd4421add | |||
| 7149bfd100 | |||
| 344ad0355e | |||
| e7871fbc43 | |||
| 99d8dd751e | |||
| 22b1abb36d | |||
| aa289d1023 | |||
| ef51e02767 | |||
| 7118127139 | |||
| 2ce2afa036 | |||
| 5803dd765d | |||
| 6a310b5069 | |||
| 2caab68be9 | |||
| 4e2dbc1258 | |||
| 40eca1a68e | |||
| 98f937ba15 | |||
| a70988ecd5 | |||
| 6f2ff620bd | |||
| bd772610b1 | |||
| de42b5bfae | |||
| fcdd5173bd | |||
| c419799313 | |||
| c5973a630b | |||
| e5a88b64b1 | |||
| 74f58a49b9 | |||
| cff9d9f5eb | |||
| ddd64f2918 | |||
| 9f06f53a59 | |||
| 4c00d7a306 | |||
| 88726e12f7 | |||
| 248bb148af | |||
| 49589e1da1 | |||
| 552267b93d | |||
| 855357f871 | |||
| c83cba2322 | |||
| 20dcc31d19 | |||
| 29e6cc5808 | |||
| 4e6ee28bcb | |||
| 4b3247576a | |||
| 69edf17c2a | |||
| d91a4ad1dd | |||
| 9b750ef69f | |||
| 0bf22ee3ce | |||
| 981ed22dff | |||
| d40321ef63 | |||
| c94996e8e8 | |||
| 4b19dd46dd | |||
| 942e841cbc | |||
| e0a595273a | |||
| 92ca52822c | |||
| f89dbce93c | |||
| 28f0e2eb2a | |||
| 47ac080565 | |||
| afdf5cfde9 | |||
| a4760bcff7 | |||
| 72e571a3de | |||
| 484c0fdd15 | |||
| 2636fb7c8d | |||
| 99c2936e90 | |||
| 66c74d6091 | |||
| a43ba245e2 | |||
| 810d734eda | |||
| 687d6c5dc4 | |||
| 5e090c1fac | |||
| ceb61e6600 | |||
| ae60293c24 | |||
| e411081c84 | |||
| 0682a5e45e | |||
| 4f751aa53d | |||
| 27fa51cfa6 | |||
| 2772ff624f | |||
| 69b160cfe8 | |||
| ee7541c032 | |||
| abf6068d91 | |||
| 9d5f3e787d | |||
| ab2b9ba444 | |||
| 6ab1aff531 | |||
| 6012276093 | |||
| 9648cf226b | |||
| 56532a505d | |||
| 6bb2e5625a | |||
| d57a5dffa1 | |||
| 1028ca0841 | |||
| 6dbe6b42c2 | |||
| 7611c3ea03 | |||
| 2fa9f934b6 | |||
| e9fd7b7ed6 | |||
| 075e954b71 | |||
| 3f7122b3d2 | |||
| 35251c6d9c | |||
| 173594365c | |||
| 2fc44fb27d | |||
| 0264630c90 | |||
| 208769f70f | |||
| c5bffb9fb5 | |||
| c7afa5452b | |||
| c7c6e83779 | |||
| fcb6adaaaa | |||
| 9c12b58e32 | |||
| 6c8b505f93 | |||
| 8b228e7471 | |||
| 4ce07f41ca | |||
| 1bfd0e5e38 | |||
| b962d37f79 | |||
| c6ecf02a07 | |||
| 8b7d4b423c | |||
| 83dc126565 | |||
| f2d3d7e710 | |||
| f991428cb9 | |||
| e2b4108377 | |||
| c9cf7b1e08 | |||
| 4d865cd7ba | |||
| a55458a84a | |||
| 66d8631c23 | |||
| 5c1ad88137 | |||
| e230c9c6f0 | |||
| 6d24a2c942 | |||
| 5222dc0cd1 | |||
| 02c484e844 | |||
| 5face79cd0 | |||
| 51a72a9c53 | |||
| f609d9cd59 | |||
| 3901c6ab91 | |||
| da6f86bd79 | |||
| 42fd499af4 | |||
| de5b88477a | |||
| cd49a7f9e9 | |||
| df025fcb88 | |||
| bbb1404bd3 | |||
| af2a172197 | |||
| 252b9fc39c | |||
| 6fc5e04d7c | |||
| 66f1fef083 | |||
| 645d4807c3 | |||
| e2c360ada0 | |||
| a463ead739 | |||
| dbe5e57b8e | |||
| 99b99d7b23 | |||
| 787111a145 | |||
| 9a872f059f | |||
| eadc94e0a1 | |||
| f8855e892a | |||
| 27d05014e3 | |||
| ebb58b7cbc | |||
| 7f6895a4bb | |||
| 80ae4c1a69 | |||
| e72df9cd5f | |||
| 80ba529fb9 | |||
| fd6b3ef5a0 | |||
| 4cd0a39794 | |||
| 5fbc510f94 | |||
| 600ab5ba5f | |||
| ae92181d02 | |||
| 66910ed998 | |||
| 5371e4403e | |||
| 79375dd7dd | |||
| 6889609ab9 | |||
| 0bf87281e0 | |||
| bc7bed7517 | |||
| cfac6fd752 | |||
| 97c09c827b | |||
| 0370672886 | |||
| 2d062fedd9 | |||
| c6a68f3de2 | |||
| d6181b2335 | |||
| d74e62886c | |||
| d8505f09eb | |||
| 935c6aade0 | |||
| a35c35f849 | |||
| 57518a2bb4 | |||
| 99a8202015 | |||
| 17c903e246 | |||
| 4de5d225db | |||
| 7741df9963 | |||
| 7ef54e9c53 | |||
| 12bb700c62 | |||
| e3ba7250f1 | |||
| 54842095c3 | |||
| d1aec0c3f1 | |||
| 00d5f72d6e | |||
| 15d01d4315 | |||
| 348e34f221 | |||
| ce7b0656c0 | |||
| fd0d360f9b | |||
| f26cae3838 | |||
| b8468af411 | |||
| 9fd7e319f3 | |||
| 80a8b59f24 | |||
| b9d4da9cfe | |||
| 5bf5b58eb3 | |||
| b2c227e9c5 | |||
| d854228d58 | |||
| a0c09bc2a9 | |||
| aca3b2da58 | |||
| 604541863c | |||
| fd9e0dd13a | |||
| da535164b4 | |||
| 82d9570ed4 | |||
| a619e8af4a | |||
| 0579220546 | |||
| 031c37c7b0 | |||
| 860821114a | |||
| 73bba62d82 | |||
| 42249ce28e |
+1
-1
@@ -167,7 +167,7 @@ macos:
|
||||
needs: []
|
||||
before_script:
|
||||
- bash .gitlab-ci/show-info-osx.sh
|
||||
- pip3 install --user meson==0.56
|
||||
- pip3 install --user meson==0.59
|
||||
- pip3 install --user ninja
|
||||
- export PATH=/Users/gitlabrunner/Library/Python/3.7/bin:$PATH
|
||||
- export MESON_FORCE_BACKTRACE=1
|
||||
|
||||
@@ -5,7 +5,7 @@ call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliar
|
||||
@echo on
|
||||
|
||||
:: FIXME: make warnings fatal
|
||||
pip3 install --upgrade --user meson==0.56.2 || goto :error
|
||||
pip3 install --upgrade --user meson==0.59 || goto :error
|
||||
meson -Dmedia-gstreamer=disabled _build || goto :error
|
||||
ninja -C _build || goto :error
|
||||
|
||||
|
||||
@@ -31,7 +31,8 @@ pacman --noconfirm -S --needed \
|
||||
mingw-w64-$MSYS2_ARCH-pango \
|
||||
mingw-w64-$MSYS2_ARCH-fribidi \
|
||||
mingw-w64-$MSYS2_ARCH-gst-plugins-bad \
|
||||
mingw-w64-$MSYS2_ARCH-shared-mime-info
|
||||
mingw-w64-$MSYS2_ARCH-shared-mime-info \
|
||||
mingw-w64-$MSYS2_ARCH-python-gobject
|
||||
|
||||
mkdir -p _ccache
|
||||
export CCACHE_BASEDIR="$(pwd)"
|
||||
|
||||
@@ -1,8 +1,127 @@
|
||||
Overview of Changes
|
||||
===================
|
||||
|
||||
* GtkProgressBar:
|
||||
- Fix handling of "inverted"
|
||||
|
||||
* GtkLabel:
|
||||
- Add a "natural wrap mode" property to influence how
|
||||
natural width is determined
|
||||
|
||||
* GtkTextView
|
||||
- Scroll insertion on-screen after undo / redo
|
||||
|
||||
* gsk:
|
||||
- Abort region diffing when changes are too complex
|
||||
|
||||
* gdk:
|
||||
- Avoid compressing discrete scroll events
|
||||
- Fix problems with hiding windows
|
||||
- Improve GL and GLES version checks
|
||||
|
||||
* Wayland:
|
||||
- Support new high-contrast setting
|
||||
|
||||
* Inspector:
|
||||
- Add DND inspection support
|
||||
|
||||
* build:
|
||||
- Avoid deprecated meson apis
|
||||
|
||||
* Translation updates
|
||||
Galician
|
||||
Portuguese
|
||||
Ukrainian
|
||||
|
||||
|
||||
Overview of Changes in 4.5.1, 16-12-2021
|
||||
========================================
|
||||
|
||||
* GtkWidget sizing has been rewritten to implement
|
||||
width-for-height more properly. This had some fallout,
|
||||
and some widgets may still not react kindly to the
|
||||
new way of doing things.
|
||||
|
||||
See https://blog.gtk.org/2021/12/03/sizable-news/
|
||||
for details, and please file issues if you notice fallout.
|
||||
|
||||
* Rename git `master` branch to `main`
|
||||
|
||||
* Css:
|
||||
- Fully support font-variant-caps
|
||||
- Fix a crash with gradients
|
||||
|
||||
* Make various widgets activatable:
|
||||
- GtkComboBox
|
||||
- GtkDropDown
|
||||
|
||||
* GtkPopover:
|
||||
- Make focus indicators not disappear
|
||||
|
||||
* GtkTextView:
|
||||
- Don't leave embedded children stranded when scrolling
|
||||
- Don't insert Emoji into non-editable textviews
|
||||
- Fix Emoji chooser positioning
|
||||
- Fix problems with pasting text
|
||||
- Improve scroll-to-mark behavior
|
||||
- Support right-aligned, centered and decimal tabs
|
||||
- Make child anchor replacement character settable
|
||||
- Provide more context to input methods
|
||||
|
||||
* GtkDragIcon:
|
||||
- Provide default icons for paintables and files
|
||||
|
||||
* GtkBuilder:
|
||||
- Speed up template precompilation
|
||||
|
||||
* Actions:
|
||||
- Reduce allocations during signal emissions
|
||||
- Avoid duplication and unnecessary recursion
|
||||
|
||||
* Inspector:
|
||||
- Show the selected im-module in the General tab
|
||||
- Add a clipboard viewer
|
||||
- Make the recorder record events too
|
||||
- Add a graph visualizing gtk_widget_measure()
|
||||
|
||||
* Gsk:
|
||||
- Fix hexbox rendering
|
||||
- Fix transformed linear gradient rendering
|
||||
|
||||
* Printing:
|
||||
- Fix dialog-less printing
|
||||
|
||||
* Windows:
|
||||
- Use the common EGL setup code
|
||||
- Respect GDK_DEBUG=gl-egl
|
||||
- Fix AeroSnap indicator and positioning
|
||||
|
||||
* X11:
|
||||
- Improve behavior of windows drags on headerbar controls
|
||||
- Trap errors for RANDR changes
|
||||
- Fix problems with drag icons
|
||||
|
||||
* Wayland:
|
||||
- Ensure we prefer the Wayland im-module over others
|
||||
|
||||
* Translation updates
|
||||
Basque
|
||||
Catalan
|
||||
Croatian
|
||||
Friulian
|
||||
Galician
|
||||
Hebrew
|
||||
Icelandic
|
||||
Italian
|
||||
Latvian
|
||||
Lithuanian
|
||||
Occitan
|
||||
Persian
|
||||
Portuguese
|
||||
Spanish
|
||||
Swedish
|
||||
Ukrainian
|
||||
|
||||
Overview of Changes in 4.5.0
|
||||
============================
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ executable('gtk4-constraint-editor',
|
||||
c_args: common_cflags,
|
||||
dependencies: libgtk_dep,
|
||||
include_directories: confinc,
|
||||
gui_app: true,
|
||||
win_subsystem: 'windows',
|
||||
link_args: extra_demo_ldflags,
|
||||
install: false,
|
||||
)
|
||||
|
||||
+316
-127
@@ -1,12 +1,10 @@
|
||||
/* Clipboard
|
||||
*
|
||||
* GdkClipboard is used for clipboard handling. This demo shows how to
|
||||
* copy and paste text to and from the clipboard.
|
||||
* copy and paste text, images, colors or files to and from the clipboard.
|
||||
*
|
||||
* It also shows how to transfer images via the clipboard or via
|
||||
* drag-and-drop, and how to make clipboard contents persist after
|
||||
* the application exits. Clipboard persistence requires a clipboard
|
||||
* manager to run.
|
||||
* You can also use Drag-And-Drop to copy the data from the source to the
|
||||
* target.
|
||||
*/
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
@@ -14,22 +12,103 @@
|
||||
#include <string.h>
|
||||
#include "demoimage.h"
|
||||
|
||||
static GtkWidget *window = NULL;
|
||||
static void
|
||||
copy_button_clicked (GtkStack *source_stack,
|
||||
gpointer user_data)
|
||||
{
|
||||
GdkClipboard *clipboard;
|
||||
const char *visible_child_name;
|
||||
GtkWidget *visible_child;
|
||||
|
||||
clipboard = gtk_widget_get_clipboard (GTK_WIDGET (source_stack));
|
||||
|
||||
visible_child = gtk_stack_get_visible_child (source_stack);
|
||||
visible_child_name = gtk_stack_get_visible_child_name (source_stack);
|
||||
|
||||
if (strcmp (visible_child_name, "Text") == 0)
|
||||
{
|
||||
gdk_clipboard_set_text (clipboard, gtk_editable_get_text (GTK_EDITABLE (visible_child)));
|
||||
}
|
||||
else if (strcmp (visible_child_name, "Image") == 0)
|
||||
{
|
||||
GtkWidget *child;
|
||||
|
||||
for (child = gtk_widget_get_first_child (visible_child); child; child = gtk_widget_get_next_sibling (child))
|
||||
{
|
||||
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (child)))
|
||||
{
|
||||
GtkWidget *image = gtk_widget_get_first_child (child);
|
||||
GdkPaintable *paintable = gtk_image_get_paintable (GTK_IMAGE (image));
|
||||
|
||||
if (GDK_IS_TEXTURE (paintable))
|
||||
gdk_clipboard_set (clipboard, GDK_TYPE_TEXTURE, paintable);
|
||||
else
|
||||
gdk_clipboard_set (clipboard, GDK_TYPE_PAINTABLE, paintable);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (strcmp (visible_child_name, "Color") == 0)
|
||||
{
|
||||
GdkRGBA color;
|
||||
|
||||
gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (visible_child), &color);
|
||||
gdk_clipboard_set (clipboard, GDK_TYPE_RGBA, &color);
|
||||
}
|
||||
else if (strcmp (visible_child_name, "File") == 0)
|
||||
{
|
||||
gdk_clipboard_set (clipboard, G_TYPE_FILE, g_object_get_data (G_OBJECT (visible_child), "file"), NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_print ("TODO");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
copy_button_clicked (GtkWidget *button,
|
||||
gpointer user_data)
|
||||
present_value (GtkStack *dest_stack,
|
||||
const GValue *value)
|
||||
{
|
||||
GtkWidget *entry;
|
||||
GdkClipboard *clipboard;
|
||||
GtkWidget *child;
|
||||
|
||||
entry = GTK_WIDGET (user_data);
|
||||
if (G_VALUE_HOLDS (value, G_TYPE_FILE))
|
||||
{
|
||||
GFile *file;
|
||||
|
||||
/* Get the clipboard object */
|
||||
clipboard = gtk_widget_get_clipboard (entry);
|
||||
gtk_stack_set_visible_child_name (dest_stack, "File");
|
||||
child = gtk_stack_get_visible_child (dest_stack);
|
||||
|
||||
/* Set clipboard text */
|
||||
gdk_clipboard_set_text (clipboard, gtk_editable_get_text (GTK_EDITABLE (entry)));
|
||||
file = g_value_get_object (value);
|
||||
g_object_set (child, "label", g_file_peek_path (file), NULL);
|
||||
}
|
||||
else if (G_VALUE_HOLDS (value, GDK_TYPE_RGBA))
|
||||
{
|
||||
GdkRGBA *color;
|
||||
|
||||
gtk_stack_set_visible_child_name (dest_stack, "Color");
|
||||
child = gtk_widget_get_first_child (gtk_stack_get_visible_child (dest_stack));
|
||||
|
||||
color = g_value_get_boxed (value);
|
||||
g_object_set (child, "rgba", color, NULL);
|
||||
}
|
||||
else if (G_VALUE_HOLDS (value, GDK_TYPE_TEXTURE) ||
|
||||
G_VALUE_HOLDS (value, GDK_TYPE_PAINTABLE))
|
||||
{
|
||||
GdkPaintable *paintable;
|
||||
|
||||
gtk_stack_set_visible_child_name (dest_stack, "Image");
|
||||
child = gtk_stack_get_visible_child (dest_stack);
|
||||
|
||||
paintable = g_value_get_object (value);
|
||||
g_object_set (child, "paintable", paintable, NULL);
|
||||
}
|
||||
else if (G_VALUE_HOLDS (value, G_TYPE_STRING))
|
||||
{
|
||||
gtk_stack_set_visible_child_name (dest_stack, "Text");
|
||||
child = gtk_stack_get_visible_child (dest_stack);
|
||||
|
||||
gtk_label_set_label (GTK_LABEL (child), g_value_get_string (value));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -37,149 +116,259 @@ paste_received (GObject *source_object,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkStack *dest_stack = user_data;
|
||||
GdkClipboard *clipboard;
|
||||
GtkWidget *entry;
|
||||
char *text;
|
||||
const GValue *value;
|
||||
GError *error = NULL;
|
||||
|
||||
clipboard = GDK_CLIPBOARD (source_object);
|
||||
entry = GTK_WIDGET (user_data);
|
||||
|
||||
/* Get the resulting text of the read operation */
|
||||
text = gdk_clipboard_read_text_finish (clipboard, result, &error);
|
||||
|
||||
if (text)
|
||||
value = gdk_clipboard_read_value_finish (clipboard, result, &error);
|
||||
if (value)
|
||||
{
|
||||
/* Set the entry text */
|
||||
gtk_editable_set_text (GTK_EDITABLE (entry), text);
|
||||
g_free (text);
|
||||
present_value (dest_stack, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
|
||||
/* Show an error about why pasting failed.
|
||||
* Usually you probably want to ignore such failures,
|
||||
* but for demonstration purposes, we show the error.
|
||||
*/
|
||||
dialog = gtk_message_dialog_new (GTK_WINDOW (window),
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
|
||||
GTK_MESSAGE_ERROR,
|
||||
GTK_BUTTONS_CLOSE,
|
||||
"Could not paste text: %s",
|
||||
error->message);
|
||||
g_signal_connect (dialog, "response",
|
||||
G_CALLBACK (gtk_window_destroy), NULL);
|
||||
gtk_widget_show (dialog);
|
||||
|
||||
g_print ("%s\n", error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
paste_button_clicked (GtkWidget *button,
|
||||
gpointer user_data)
|
||||
paste_button_clicked (GtkStack *dest_stack,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkWidget *entry;
|
||||
GdkClipboard *clipboard;
|
||||
GdkContentFormats *formats;
|
||||
|
||||
entry = GTK_WIDGET (user_data);
|
||||
clipboard = gtk_widget_get_clipboard (GTK_WIDGET (dest_stack));
|
||||
formats = gdk_clipboard_get_formats (clipboard);
|
||||
|
||||
/* Get the clipboard object */
|
||||
clipboard = gtk_widget_get_clipboard (entry);
|
||||
if (gdk_content_formats_contain_gtype (formats, GDK_TYPE_TEXTURE))
|
||||
gdk_clipboard_read_value_async (clipboard, GDK_TYPE_TEXTURE, 0, NULL, paste_received, dest_stack);
|
||||
else if (gdk_content_formats_contain_gtype (formats, GDK_TYPE_PAINTABLE))
|
||||
gdk_clipboard_read_value_async (clipboard, GDK_TYPE_PAINTABLE, 0, NULL, paste_received, dest_stack);
|
||||
else if (gdk_content_formats_contain_gtype (formats, GDK_TYPE_RGBA))
|
||||
gdk_clipboard_read_value_async (clipboard, GDK_TYPE_RGBA, 0, NULL, paste_received, dest_stack);
|
||||
else if (gdk_content_formats_contain_gtype (formats, G_TYPE_FILE))
|
||||
gdk_clipboard_read_value_async (clipboard, G_TYPE_FILE, 0, NULL, paste_received, dest_stack);
|
||||
else if (gdk_content_formats_contain_gtype (formats, G_TYPE_STRING))
|
||||
gdk_clipboard_read_value_async (clipboard, G_TYPE_STRING, 0, NULL, paste_received, dest_stack);
|
||||
}
|
||||
|
||||
/* Request the contents of the clipboard, contents_received will be
|
||||
called when we do get the contents.
|
||||
*/
|
||||
gdk_clipboard_read_text_async (clipboard, NULL, paste_received, entry);
|
||||
static void
|
||||
update_copy_button_sensitivity (GtkWidget *source_stack)
|
||||
{
|
||||
GtkButton *copy_button;
|
||||
const char *visible_child_name;
|
||||
GtkWidget *visible_child;
|
||||
gboolean sensitive;
|
||||
|
||||
copy_button = GTK_BUTTON (g_object_get_data (G_OBJECT (source_stack), "copy-button"));
|
||||
|
||||
visible_child = gtk_stack_get_visible_child (GTK_STACK (source_stack));
|
||||
visible_child_name = gtk_stack_get_visible_child_name (GTK_STACK (source_stack));
|
||||
if (strcmp (visible_child_name, "Text") == 0)
|
||||
{
|
||||
sensitive = strlen (gtk_editable_get_text (GTK_EDITABLE (visible_child))) > 0;
|
||||
}
|
||||
else if (strcmp (visible_child_name, "Color") == 0 ||
|
||||
strcmp (visible_child_name, "Image") == 0)
|
||||
{
|
||||
sensitive = TRUE;
|
||||
}
|
||||
else if (strcmp (visible_child_name, "File") == 0)
|
||||
{
|
||||
sensitive = g_object_get_data (G_OBJECT (visible_child), "file") != NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
sensitive = FALSE;
|
||||
}
|
||||
|
||||
gtk_widget_set_sensitive (GTK_WIDGET (copy_button), sensitive);
|
||||
}
|
||||
|
||||
static void
|
||||
source_changed_cb (GtkButton *copy_button,
|
||||
GParamSpec *pspec,
|
||||
GtkWidget *source_stack)
|
||||
{
|
||||
update_copy_button_sensitivity (source_stack);
|
||||
}
|
||||
|
||||
static void
|
||||
text_changed_cb (GtkButton *copy_button,
|
||||
GParamSpec *pspec,
|
||||
GtkWidget *entry)
|
||||
{
|
||||
update_copy_button_sensitivity (gtk_widget_get_ancestor (entry, GTK_TYPE_STACK));
|
||||
}
|
||||
|
||||
static void
|
||||
file_button_set_file (GtkButton *button,
|
||||
GFile *file)
|
||||
{
|
||||
gtk_label_set_label (GTK_LABEL (gtk_button_get_child (button)), g_file_peek_path (file));
|
||||
g_object_set_data_full (G_OBJECT (button), "file", g_object_ref (file), g_object_unref);
|
||||
}
|
||||
|
||||
static void
|
||||
file_chooser_response (GtkNativeDialog *dialog,
|
||||
int response,
|
||||
GtkButton *button)
|
||||
{
|
||||
gtk_native_dialog_hide (dialog);
|
||||
|
||||
if (response == GTK_RESPONSE_ACCEPT)
|
||||
{
|
||||
GFile *file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
|
||||
file_button_set_file (button, file);
|
||||
g_object_unref (file);
|
||||
|
||||
update_copy_button_sensitivity (gtk_widget_get_ancestor (GTK_WIDGET (button), GTK_TYPE_STACK));
|
||||
}
|
||||
|
||||
gtk_native_dialog_destroy (dialog);
|
||||
}
|
||||
|
||||
static void
|
||||
open_file_cb (GtkWidget *button)
|
||||
{
|
||||
GtkFileChooserNative *chooser;
|
||||
|
||||
chooser = gtk_file_chooser_native_new ("Choose a file",
|
||||
GTK_WINDOW (gtk_widget_get_ancestor (button, GTK_TYPE_WINDOW)),
|
||||
GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||
"_Open",
|
||||
"_Cancel");
|
||||
|
||||
g_signal_connect (chooser, "response", G_CALLBACK (file_chooser_response), button);
|
||||
gtk_native_dialog_show (GTK_NATIVE_DIALOG (chooser));
|
||||
}
|
||||
|
||||
static void
|
||||
update_paste_button_sensitivity (GdkClipboard *clipboard,
|
||||
GtkWidget *paste_button)
|
||||
{
|
||||
GdkContentFormats *formats;
|
||||
gboolean sensitive = FALSE;
|
||||
|
||||
formats = gdk_clipboard_get_formats (clipboard);
|
||||
|
||||
if (gdk_content_formats_contain_gtype (formats, G_TYPE_FILE) ||
|
||||
gdk_content_formats_contain_gtype (formats, GDK_TYPE_RGBA) ||
|
||||
gdk_content_formats_contain_gtype (formats, GDK_TYPE_TEXTURE) ||
|
||||
gdk_content_formats_contain_gtype (formats, GDK_TYPE_PAINTABLE) ||
|
||||
gdk_content_formats_contain_gtype (formats, G_TYPE_STRING))
|
||||
sensitive = TRUE;
|
||||
|
||||
gtk_widget_set_sensitive (paste_button, sensitive);
|
||||
}
|
||||
|
||||
static void
|
||||
unset_clipboard_handler (gpointer data)
|
||||
{
|
||||
GdkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (data));
|
||||
|
||||
g_signal_handlers_disconnect_by_func (clipboard, update_paste_button_sensitivity, data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
on_drop (GtkStack *dest_stack,
|
||||
const GValue *value,
|
||||
double x,
|
||||
double y,
|
||||
gpointer data)
|
||||
{
|
||||
present_value (dest_stack, value);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GdkContentProvider *
|
||||
drag_prepare (GtkDragSource *drag_source,
|
||||
double x,
|
||||
double y,
|
||||
gpointer data)
|
||||
{
|
||||
GtkWidget *button;
|
||||
GValue value = G_VALUE_INIT;
|
||||
|
||||
button = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (drag_source));
|
||||
|
||||
if (GTK_IS_TOGGLE_BUTTON (button))
|
||||
{
|
||||
GtkWidget *image = gtk_widget_get_first_child (button);
|
||||
GdkPaintable *paintable = gtk_image_get_paintable (GTK_IMAGE (image));
|
||||
|
||||
if (GDK_IS_TEXTURE (paintable))
|
||||
{
|
||||
g_value_init (&value, GDK_TYPE_TEXTURE);
|
||||
g_value_set_object (&value, paintable);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_value_init (&value, GDK_TYPE_PAINTABLE);
|
||||
g_value_set_object (&value, paintable);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GFile *file = g_object_get_data (G_OBJECT (button), "file");
|
||||
|
||||
if (file)
|
||||
{
|
||||
g_value_init (&value, G_TYPE_FILE);
|
||||
g_value_set_object (&value, file);
|
||||
}
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return gdk_content_provider_new_for_value (&value);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_clipboard (GtkWidget *do_widget)
|
||||
{
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
if (!window)
|
||||
{
|
||||
GtkWidget *vbox, *hbox;
|
||||
GtkWidget *label;
|
||||
GtkWidget *entry, *button;
|
||||
GtkWidget *image;
|
||||
GtkBuilderScope *scope;
|
||||
GtkBuilder *builder;
|
||||
GtkWidget *button;
|
||||
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Clipboard");
|
||||
scope = gtk_builder_cscope_new ();
|
||||
gtk_builder_cscope_add_callback_symbols (GTK_BUILDER_CSCOPE (scope),
|
||||
"copy_button_clicked", G_CALLBACK (copy_button_clicked),
|
||||
"paste_button_clicked", G_CALLBACK (paste_button_clicked),
|
||||
"source_changed_cb", G_CALLBACK (source_changed_cb),
|
||||
"text_changed_cb", G_CALLBACK (text_changed_cb),
|
||||
"open_file_cb", G_CALLBACK (open_file_cb),
|
||||
"on_drop", G_CALLBACK (on_drop),
|
||||
"drag_prepare", G_CALLBACK (drag_prepare),
|
||||
NULL);
|
||||
|
||||
builder = gtk_builder_new ();
|
||||
gtk_builder_set_scope (builder, scope);
|
||||
gtk_builder_add_from_resource (builder, "/clipboard/clipboard.ui", NULL);
|
||||
|
||||
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
|
||||
gtk_window_set_display (GTK_WINDOW (window), gtk_widget_get_display (do_widget));
|
||||
|
||||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
||||
gtk_widget_set_margin_start (vbox, 8);
|
||||
gtk_widget_set_margin_end (vbox, 8);
|
||||
gtk_widget_set_margin_top (vbox, 8);
|
||||
gtk_widget_set_margin_bottom (vbox, 8);
|
||||
button = GTK_WIDGET (gtk_builder_get_object (builder, "copy_button"));
|
||||
g_object_set_data (gtk_builder_get_object (builder, "source_stack"), "copy-button", button);
|
||||
|
||||
gtk_window_set_child (GTK_WINDOW (window), vbox);
|
||||
button = GTK_WIDGET (gtk_builder_get_object (builder, "paste_button"));
|
||||
g_signal_connect (gtk_widget_get_clipboard (button), "changed",
|
||||
G_CALLBACK (update_paste_button_sensitivity), button);
|
||||
g_object_set_data_full (G_OBJECT (button), "clipboard-handler", button, unset_clipboard_handler);
|
||||
|
||||
label = gtk_label_new ("\"Copy\" will copy the text\nin the entry to the clipboard");
|
||||
|
||||
gtk_box_append (GTK_BOX (vbox), label);
|
||||
|
||||
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
|
||||
gtk_widget_set_margin_start (hbox, 8);
|
||||
gtk_widget_set_margin_end (hbox, 8);
|
||||
gtk_widget_set_margin_top (hbox, 8);
|
||||
gtk_widget_set_margin_bottom (hbox, 8);
|
||||
gtk_box_append (GTK_BOX (vbox), hbox);
|
||||
|
||||
/* Create the first entry */
|
||||
entry = gtk_entry_new ();
|
||||
gtk_box_append (GTK_BOX (hbox), entry);
|
||||
|
||||
/* Create the button */
|
||||
button = gtk_button_new_with_mnemonic (_("_Copy"));
|
||||
gtk_box_append (GTK_BOX (hbox), button);
|
||||
g_signal_connect (button, "clicked",
|
||||
G_CALLBACK (copy_button_clicked), entry);
|
||||
|
||||
label = gtk_label_new ("\"Paste\" will paste the text from the clipboard to the entry");
|
||||
gtk_box_append (GTK_BOX (vbox), label);
|
||||
|
||||
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
|
||||
gtk_widget_set_margin_start (hbox, 8);
|
||||
gtk_widget_set_margin_end (hbox, 8);
|
||||
gtk_widget_set_margin_top (hbox, 8);
|
||||
gtk_widget_set_margin_bottom (hbox, 8);
|
||||
gtk_box_append (GTK_BOX (vbox), hbox);
|
||||
|
||||
/* Create the second entry */
|
||||
entry = gtk_entry_new ();
|
||||
gtk_box_append (GTK_BOX (hbox), entry);
|
||||
|
||||
/* Create the button */
|
||||
button = gtk_button_new_with_mnemonic (_("_Paste"));
|
||||
gtk_box_append (GTK_BOX (hbox), button);
|
||||
g_signal_connect (button, "clicked",
|
||||
G_CALLBACK (paste_button_clicked), entry);
|
||||
|
||||
label = gtk_label_new ("Images can be transferred via the clipboard, too");
|
||||
gtk_box_append (GTK_BOX (vbox), label);
|
||||
|
||||
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
|
||||
gtk_widget_set_margin_start (hbox, 8);
|
||||
gtk_widget_set_margin_end (hbox, 8);
|
||||
gtk_widget_set_margin_top (hbox, 8);
|
||||
gtk_widget_set_margin_bottom (hbox, 8);
|
||||
gtk_box_append (GTK_BOX (vbox), hbox);
|
||||
|
||||
/* Create the first image */
|
||||
image = demo_image_new ("dialog-warning");
|
||||
gtk_box_append (GTK_BOX (hbox), image);
|
||||
|
||||
/* Create the second image */
|
||||
image = demo_image_new ("process-stop");
|
||||
gtk_box_append (GTK_BOX (hbox), image);
|
||||
|
||||
/* Create the third image */
|
||||
image = demo_image_new ("weather-clear");
|
||||
gtk_box_append (GTK_BOX (hbox), image);
|
||||
g_object_unref (builder);
|
||||
g_object_unref (scope);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
|
||||
@@ -0,0 +1,288 @@
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<object class="GtkWindow" id="window">
|
||||
<property name="resizable">1</property>
|
||||
<property name="title">Clipboard</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="margin-start">12</property>
|
||||
<property name="margin-end">12</property>
|
||||
<property name="margin-top">12</property>
|
||||
<property name="margin-bottom">12</property>
|
||||
<property name="spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label">“Copy” will copy the selected data the clipboard, “Paste” will show the current clipboard contents. You can also drag the data to the bottom.</property>
|
||||
<property name="wrap">1</property>
|
||||
<property name="max-width-chars">40</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkDropDown" id="source_chooser">
|
||||
<property name="valign">center</property>
|
||||
<property name="model">
|
||||
<object class="GtkStringList">
|
||||
<items>
|
||||
<item>Text</item>
|
||||
<item>Color</item>
|
||||
<item>Image</item>
|
||||
<item>File</item>
|
||||
</items>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkStack" id="source_stack">
|
||||
<signal name="notify::visible-child" handler="source_changed_cb" object="copy_button"/>
|
||||
<property name="vexpand">1</property>
|
||||
<binding name="visible-child-name">
|
||||
<lookup name="string" type="GtkStringObject">
|
||||
<lookup name="selected-item">
|
||||
source_chooser
|
||||
</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
<child>
|
||||
<object class="GtkStackPage">
|
||||
<property name="name">Text</property>
|
||||
<property name="child">
|
||||
<object class="GtkEntry" id="source_text">
|
||||
<property name="valign">center</property>
|
||||
<signal name="notify::text" handler="text_changed_cb" object="copy_button"/>
|
||||
<property name="text">Copy this!</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkStackPage">
|
||||
<property name="name">Color</property>
|
||||
<property name="child">
|
||||
<object class="GtkColorButton" id="source_color">
|
||||
<property name="valign">center</property>
|
||||
<property name="rgba">purple</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkStackPage">
|
||||
<property name="name">Image</property>
|
||||
<property name="child">
|
||||
<object class="GtkBox">
|
||||
<property name="valign">center</property>
|
||||
<style>
|
||||
<class name="linked"/>
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="image_rose">
|
||||
<property name="active">1</property>
|
||||
<child>
|
||||
<object class="GtkDragSource">
|
||||
<signal name="prepare" handler="drag_prepare"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<style>
|
||||
<class name="large-icons"/>
|
||||
</style>
|
||||
<property name="paintable">resource:///transparent/portland-rose.jpg</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="image_floppy">
|
||||
<property name="group">image_rose</property>
|
||||
<child>
|
||||
<object class="GtkDragSource">
|
||||
<signal name="prepare" handler="drag_prepare"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<style>
|
||||
<class name="large-icons"/>
|
||||
</style>
|
||||
<property name="paintable">resource:///images/floppybuddy.gif</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="image_logo">
|
||||
<property name="group">image_floppy</property>
|
||||
<child>
|
||||
<object class="GtkDragSource">
|
||||
<signal name="prepare" handler="drag_prepare"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<style>
|
||||
<class name="large-icons"/>
|
||||
</style>
|
||||
<property name="paintable">resource:///images/org.gtk.Demo4.svg</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkStackPage">
|
||||
<property name="name">File</property>
|
||||
<property name="child">
|
||||
<object class="GtkButton" id="source_file">
|
||||
<child>
|
||||
<object class="GtkDragSource">
|
||||
<property name="propagation-phase">capture</property>
|
||||
<signal name="prepare" handler="drag_prepare"/>
|
||||
</object>
|
||||
</child>
|
||||
<property name="valign">center</property>
|
||||
<property name="child">
|
||||
<object class="GtkLabel">
|
||||
<property name="label">—</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="ellipsize">start</property>
|
||||
</object>
|
||||
</property>
|
||||
<signal name="clicked" handler="open_file_cb"/>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="copy_button">
|
||||
<property name="valign">center</property>
|
||||
<property name="label" translatable="yes">_Copy</property>
|
||||
<signal name="clicked" handler="copy_button_clicked" object="source_stack"/>
|
||||
<property name="use-underline">1</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparator">
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkDropTarget">
|
||||
<property name="actions">copy</property>
|
||||
<property name="formats">GdkTexture GdkPaintable GFile GdkRGBA gchararray</property>
|
||||
<signal name="drop" handler="on_drop" object="dest_stack"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="paste_button">
|
||||
<property name="label" translatable="yes">_Paste</property>
|
||||
<signal name="clicked" handler="paste_button_clicked" object="dest_stack"/>
|
||||
<property name="use-underline">1</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="xalign">0</property>
|
||||
<binding name="label">
|
||||
<lookup name="visible-child-name" type="GtkStack">
|
||||
dest_stack
|
||||
</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkStack" id="dest_stack">
|
||||
<property name="halign">end</property>
|
||||
<property name="valign">center</property>
|
||||
<child>
|
||||
<object class="GtkStackPage">
|
||||
<property name="name"></property>
|
||||
<property name="child">
|
||||
<object class="GtkLabel">
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkStackPage">
|
||||
<property name="name">Text</property>
|
||||
<property name="child">
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">end</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="ellipsize">end</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkStackPage">
|
||||
<property name="name">Image</property>
|
||||
<property name="child">
|
||||
<object class="GtkImage">
|
||||
<property name="halign">end</property>
|
||||
<property name="valign">center</property>
|
||||
<style>
|
||||
<class name="large-icons"/>
|
||||
</style>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkStackPage">
|
||||
<property name="name">Color</property>
|
||||
<property name="child">
|
||||
<object class="GtkBox">
|
||||
<property name="halign">end</property>
|
||||
<property name="valign">center</property>
|
||||
<child>
|
||||
<object class="GtkColorSwatch">
|
||||
<property name="accessible-role">img</property>
|
||||
<property name="can-focus">0</property>
|
||||
<property name="selectable">0</property>
|
||||
<property name="has-menu">0</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkStackPage">
|
||||
<property name="name">File</property>
|
||||
<property name="child">
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">end</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="hexpand">1</property>
|
||||
<property name="ellipsize">start</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
@@ -24,7 +24,6 @@ do_cursors (GtkWidget *do_widget)
|
||||
|
||||
builder = gtk_builder_new_from_resource ("/cursors/cursors.ui");
|
||||
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
g_signal_connect (window, "destroy",
|
||||
@@ -35,7 +34,9 @@ do_cursors (GtkWidget *do_widget)
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_window_destroy (GTK_WINDOW (window));
|
||||
{
|
||||
gtk_window_destroy (GTK_WINDOW (window));
|
||||
}
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,36 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define CURVE_TYPE_EDITOR (curve_editor_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (CurveEditor, curve_editor, CURVE, EDITOR, GtkWidget)
|
||||
|
||||
GtkWidget * curve_editor_new (void);
|
||||
|
||||
void curve_editor_set_edit (CurveEditor *self,
|
||||
gboolean edit);
|
||||
|
||||
void curve_editor_set_path (CurveEditor *self,
|
||||
GskPath *path);
|
||||
|
||||
GskPath * curve_editor_get_path (CurveEditor *self);
|
||||
|
||||
void curve_editor_set_stroke (CurveEditor *self,
|
||||
GskStroke *stroke);
|
||||
|
||||
const GskStroke * curve_editor_get_stroke (CurveEditor *self);
|
||||
|
||||
|
||||
void curve_editor_set_color (CurveEditor *self,
|
||||
GdkRGBA *color);
|
||||
|
||||
const GdkRGBA * curve_editor_get_color (CurveEditor *self);
|
||||
|
||||
gboolean curve_editor_get_show_outline (CurveEditor *self);
|
||||
|
||||
void curve_editor_set_show_outline (CurveEditor *self,
|
||||
gboolean show_outline);
|
||||
|
||||
G_END_DECLS
|
||||
@@ -1,272 +0,0 @@
|
||||
/* Path/Curve Editor
|
||||
*
|
||||
* This demo shows an elaborate curve editor that you would expect to find
|
||||
* in a vector graphics editor. It is built on top of GTK's path APIs.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include "curve-editor.h"
|
||||
|
||||
|
||||
static GskPath *
|
||||
make_circle_path (void)
|
||||
{
|
||||
float w = 310;
|
||||
float h = 310;
|
||||
float cx = w / 2;
|
||||
float cy = h / 2;
|
||||
float pad = 20;
|
||||
float r = (w - 2 * pad) / 2;
|
||||
float k = 0.55228;
|
||||
float kr = k * r;
|
||||
GskPathBuilder *builder;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
|
||||
gsk_path_builder_move_to (builder, cx, pad);
|
||||
gsk_path_builder_curve_to (builder, cx + kr, pad,
|
||||
w - pad, cy - kr,
|
||||
w - pad, cy);
|
||||
gsk_path_builder_curve_to (builder, w - pad, cy + kr,
|
||||
cx + kr, h - pad,
|
||||
cx, h - pad);
|
||||
gsk_path_builder_curve_to (builder, cx - kr, h - pad,
|
||||
pad, cy + kr,
|
||||
pad, cy);
|
||||
gsk_path_builder_curve_to (builder, pad, cy - kr,
|
||||
cx - kr, pad,
|
||||
cx, pad);
|
||||
gsk_path_builder_close (builder);
|
||||
|
||||
return gsk_path_builder_free_to_path (builder);
|
||||
}
|
||||
|
||||
static void
|
||||
edit_changed (GtkToggleButton *button,
|
||||
GParamSpec *pspec,
|
||||
CurveEditor *editor)
|
||||
{
|
||||
curve_editor_set_edit (editor, gtk_toggle_button_get_active (button));
|
||||
}
|
||||
|
||||
static void
|
||||
reset (GtkButton *button,
|
||||
CurveEditor *editor)
|
||||
{
|
||||
GskPath *path;
|
||||
|
||||
path = make_circle_path ();
|
||||
curve_editor_set_path (editor, path);
|
||||
gsk_path_unref (path);
|
||||
}
|
||||
|
||||
static void
|
||||
line_width_changed (GtkSpinButton *spin,
|
||||
CurveEditor *editor)
|
||||
{
|
||||
GskStroke *stroke;
|
||||
|
||||
stroke = gsk_stroke_copy (curve_editor_get_stroke (editor));
|
||||
gsk_stroke_set_line_width (stroke, gtk_spin_button_get_value (spin));
|
||||
curve_editor_set_stroke (editor, stroke);
|
||||
gsk_stroke_free (stroke);
|
||||
}
|
||||
|
||||
static void
|
||||
cap_changed (GtkDropDown *combo,
|
||||
GParamSpec *pspec,
|
||||
CurveEditor *editor)
|
||||
{
|
||||
GskStroke *stroke;
|
||||
|
||||
stroke = gsk_stroke_copy (curve_editor_get_stroke (editor));
|
||||
gsk_stroke_set_line_cap (stroke, (GskLineCap)gtk_drop_down_get_selected (combo));
|
||||
curve_editor_set_stroke (editor, stroke);
|
||||
gsk_stroke_free (stroke);
|
||||
}
|
||||
|
||||
static void
|
||||
join_changed (GtkDropDown *combo,
|
||||
GParamSpec *pspec,
|
||||
CurveEditor *editor)
|
||||
{
|
||||
GskStroke *stroke;
|
||||
|
||||
stroke = gsk_stroke_copy (curve_editor_get_stroke (editor));
|
||||
gsk_stroke_set_line_join (stroke, (GskLineJoin)gtk_drop_down_get_selected (combo));
|
||||
curve_editor_set_stroke (editor, stroke);
|
||||
gsk_stroke_free (stroke);
|
||||
}
|
||||
|
||||
static void
|
||||
color_changed (GtkColorChooser *chooser,
|
||||
GParamSpec *pspec,
|
||||
CurveEditor *editor)
|
||||
{
|
||||
GdkRGBA color;
|
||||
|
||||
gtk_color_chooser_get_rgba (chooser, &color);
|
||||
curve_editor_set_color (editor, &color);
|
||||
}
|
||||
|
||||
static void
|
||||
stroke_toggled (GtkCheckButton *button,
|
||||
CurveEditor *editor)
|
||||
{
|
||||
curve_editor_set_show_outline (editor, gtk_check_button_get_active (button));
|
||||
gtk_widget_queue_draw (GTK_WIDGET (editor));
|
||||
}
|
||||
|
||||
static void
|
||||
limit_changed (GtkSpinButton *spin,
|
||||
CurveEditor *editor)
|
||||
{
|
||||
GskStroke *stroke;
|
||||
|
||||
stroke = gsk_stroke_copy (curve_editor_get_stroke (editor));
|
||||
gsk_stroke_set_miter_limit (stroke, gtk_spin_button_get_value (spin));
|
||||
curve_editor_set_stroke (editor, stroke);
|
||||
gsk_stroke_free (stroke);
|
||||
}
|
||||
|
||||
static void
|
||||
dashes_changed (GtkEntry *entry,
|
||||
GParamSpec *spec,
|
||||
CurveEditor *editor)
|
||||
{
|
||||
const char *text;
|
||||
char **split;
|
||||
GArray *dash;
|
||||
GskStroke *stroke;
|
||||
|
||||
text = gtk_editable_get_text (GTK_EDITABLE (entry));
|
||||
split = g_strsplit (text, " ", 0);
|
||||
|
||||
dash = g_array_new (FALSE, FALSE, sizeof (float));
|
||||
for (int i = 0; split[i] != NULL; i++)
|
||||
{
|
||||
double d;
|
||||
char *endp = 0;
|
||||
|
||||
d = g_strtod (split[i], &endp);
|
||||
if (*endp == '\0')
|
||||
g_array_append_vals (dash, (float[1]) { d }, 1);
|
||||
}
|
||||
|
||||
g_strfreev (split);
|
||||
|
||||
stroke = gsk_stroke_copy (curve_editor_get_stroke (editor));
|
||||
gsk_stroke_set_dash (stroke, (const float *)dash->data, dash->len);
|
||||
curve_editor_set_stroke (editor, stroke);
|
||||
gsk_stroke_free (stroke);
|
||||
|
||||
g_array_free (dash, TRUE);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_curve (GtkWidget *do_widget)
|
||||
{
|
||||
static GtkWidget *window = NULL;
|
||||
GtkWidget *demo;
|
||||
GtkWidget *edit_toggle;
|
||||
GtkWidget *reset_button;
|
||||
GtkWidget *titlebar;
|
||||
GtkWidget *stroke_toggle;
|
||||
GtkWidget *line_width_spin;
|
||||
GtkWidget *stroke_button;
|
||||
GtkWidget *popover;
|
||||
GtkWidget *grid;
|
||||
GtkWidget *cap_combo;
|
||||
GtkWidget *join_combo;
|
||||
GtkWidget *color_button;
|
||||
GtkWidget *limit_spin;
|
||||
GtkWidget *dash_entry;
|
||||
|
||||
if (!window)
|
||||
{
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Curve Editor");
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 310, 350);
|
||||
|
||||
edit_toggle = gtk_toggle_button_new ();
|
||||
gtk_button_set_icon_name (GTK_BUTTON (edit_toggle), "document-edit-symbolic");
|
||||
|
||||
reset_button = gtk_button_new_from_icon_name ("edit-undo-symbolic");
|
||||
|
||||
stroke_button = gtk_menu_button_new ();
|
||||
gtk_menu_button_set_icon_name (GTK_MENU_BUTTON (stroke_button), "open-menu-symbolic");
|
||||
popover = gtk_popover_new ();
|
||||
gtk_menu_button_set_popover (GTK_MENU_BUTTON (stroke_button), popover);
|
||||
|
||||
grid = gtk_grid_new ();
|
||||
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
|
||||
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
|
||||
gtk_popover_set_child (GTK_POPOVER (popover), grid);
|
||||
|
||||
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Color:"), 0, 0, 1, 1);
|
||||
color_button = gtk_color_button_new_with_rgba (&(GdkRGBA){ 0., 0., 0., 1.});
|
||||
gtk_grid_attach (GTK_GRID (grid), color_button, 1, 0, 1, 1);
|
||||
|
||||
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Line width:"), 0, 1, 1, 1);
|
||||
line_width_spin = gtk_spin_button_new_with_range (1, 20, 1);
|
||||
gtk_spin_button_set_value (GTK_SPIN_BUTTON (line_width_spin), 1);
|
||||
gtk_grid_attach (GTK_GRID (grid), line_width_spin, 1, 1, 1, 1);
|
||||
|
||||
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Line cap:"), 0, 2, 1, 1);
|
||||
cap_combo = gtk_drop_down_new_from_strings ((const char *[]){"Butt", "Round", "Square", NULL});
|
||||
gtk_grid_attach (GTK_GRID (grid), cap_combo, 1, 2, 1, 1);
|
||||
|
||||
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Line join:"), 0, 3, 1, 1);
|
||||
join_combo = gtk_drop_down_new_from_strings ((const char *[]){"Miter", "Miter-clip", "Round", "Bevel", "Arcs", NULL});
|
||||
gtk_grid_attach (GTK_GRID (grid), join_combo, 1, 3, 1, 1);
|
||||
|
||||
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Miter limit:"), 0, 4, 1, 1);
|
||||
limit_spin = gtk_spin_button_new_with_range (0, 10, 1);
|
||||
gtk_spin_button_set_digits (GTK_SPIN_BUTTON (limit_spin), 1);
|
||||
gtk_spin_button_set_value (GTK_SPIN_BUTTON (limit_spin), 4);
|
||||
gtk_grid_attach (GTK_GRID (grid), limit_spin, 1, 4, 1, 1);
|
||||
|
||||
gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Dashes:"), 0, 5, 1, 1);
|
||||
dash_entry = gtk_entry_new ();
|
||||
gtk_grid_attach (GTK_GRID (grid), dash_entry, 1, 5, 1, 1);
|
||||
|
||||
stroke_toggle = gtk_check_button_new_with_label ("Show outline");
|
||||
gtk_grid_attach (GTK_GRID (grid), stroke_toggle, 1, 6, 1, 1);
|
||||
|
||||
titlebar = gtk_header_bar_new ();
|
||||
gtk_header_bar_pack_start (GTK_HEADER_BAR (titlebar), edit_toggle);
|
||||
gtk_header_bar_pack_start (GTK_HEADER_BAR (titlebar), reset_button);
|
||||
gtk_header_bar_pack_start (GTK_HEADER_BAR (titlebar), stroke_button);
|
||||
|
||||
gtk_window_set_titlebar (GTK_WINDOW (window), titlebar);
|
||||
|
||||
demo = curve_editor_new ();
|
||||
|
||||
g_signal_connect (stroke_toggle, "toggled", G_CALLBACK (stroke_toggled), demo);
|
||||
g_signal_connect (edit_toggle, "notify::active", G_CALLBACK (edit_changed), demo);
|
||||
g_signal_connect (reset_button, "clicked", G_CALLBACK (reset), demo);
|
||||
g_signal_connect (cap_combo, "notify::selected", G_CALLBACK (cap_changed), demo);
|
||||
g_signal_connect (join_combo, "notify::selected", G_CALLBACK (join_changed), demo);
|
||||
g_signal_connect (color_button, "notify::rgba", G_CALLBACK (color_changed), demo);
|
||||
g_signal_connect (line_width_spin, "value-changed", G_CALLBACK (line_width_changed), demo);
|
||||
g_signal_connect (limit_spin, "value-changed", G_CALLBACK (limit_changed), demo);
|
||||
g_signal_connect (dash_entry, "notify::text", G_CALLBACK (dashes_changed), demo);
|
||||
|
||||
reset (NULL, CURVE_EDITOR (demo));
|
||||
|
||||
gtk_spin_button_set_value (GTK_SPIN_BUTTON (line_width_spin), 6);
|
||||
gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (color_button), &(GdkRGBA) { 1, 0, 0, 1 });
|
||||
gtk_drop_down_set_selected (GTK_DROP_DOWN (cap_combo), GSK_LINE_CAP_ROUND);
|
||||
gtk_editable_set_text (GTK_EDITABLE (dash_entry), "0 8");
|
||||
|
||||
gtk_window_set_child (GTK_WINDOW (window), demo);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_window_destroy (GTK_WINDOW (window));
|
||||
|
||||
return window;
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
<file>demo.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/clipboard">
|
||||
<file>clipboard.ui</file>
|
||||
<file>demoimage.c</file>
|
||||
<file>demoimage.h</file>
|
||||
</gresource>
|
||||
@@ -249,10 +250,6 @@
|
||||
<gresource prefix="/video-player">
|
||||
<file>bbb.png</file>
|
||||
</gresource>
|
||||
<gresource prefix="/curve">
|
||||
<file>curve-editor.c</file>
|
||||
<file>curve-editor.h</file>
|
||||
</gresource>
|
||||
<gresource prefix="/sources">
|
||||
<file>application_demo.c</file>
|
||||
<file>assistant.c</file>
|
||||
@@ -270,7 +267,6 @@
|
||||
<file>css_pixbufs.c</file>
|
||||
<file>css_shadows.c</file>
|
||||
<file>cursors.c</file>
|
||||
<file>curve.c</file>
|
||||
<file>dialog.c</file>
|
||||
<file>drawingarea.c</file>
|
||||
<file>dropdown.c</file>
|
||||
@@ -291,7 +287,6 @@
|
||||
<file>gears.c</file>
|
||||
<file>gestures.c</file>
|
||||
<file>glarea.c</file>
|
||||
<file>glyphs.c</file>
|
||||
<file>gltransition.c</file>
|
||||
<file>headerbar.c</file>
|
||||
<file>hypertext.c</file>
|
||||
@@ -330,9 +325,6 @@
|
||||
<file>paintable_symbolic.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>
|
||||
@@ -417,9 +409,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>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,7 +21,7 @@
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Save _As...</attribute>
|
||||
<attribute name="action">app.save-as</attribute>
|
||||
<attribute name="accel"><Control>s</attribute>
|
||||
<attribute name="accel"><Control><Shift>s</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
|
||||
@@ -17,7 +17,6 @@ demos = files([
|
||||
'css_pixbufs.c',
|
||||
'css_shadows.c',
|
||||
'cursors.c',
|
||||
'curve.c',
|
||||
'dialog.c',
|
||||
'drawingarea.c',
|
||||
'dnd.c',
|
||||
@@ -35,7 +34,6 @@ demos = files([
|
||||
'gestures.c',
|
||||
'glarea.c',
|
||||
'gltransition.c',
|
||||
'glyphs.c',
|
||||
'headerbar.c',
|
||||
'hypertext.c',
|
||||
'iconscroll.c',
|
||||
@@ -72,9 +70,6 @@ demos = files([
|
||||
'paintable_symbolic.c',
|
||||
'panes.c',
|
||||
'password_entry.c',
|
||||
'path_fill.c',
|
||||
'path_maze.c',
|
||||
'path_text.c',
|
||||
'peg_solitaire.c',
|
||||
'pickers.c',
|
||||
'printing.c',
|
||||
@@ -133,7 +128,6 @@ extra_demo_sources = files([
|
||||
'script-names.c',
|
||||
'unicode-names.c',
|
||||
'suggestionentry.c',
|
||||
'curve-editor.c',
|
||||
])
|
||||
|
||||
if harfbuzz_dep.found() and pangoft_dep.found()
|
||||
@@ -248,7 +242,7 @@ executable('gtk4-demo',
|
||||
c_args: gtkdemo_args + demo_cflags,
|
||||
dependencies: gtkdemo_deps,
|
||||
include_directories: confinc,
|
||||
gui_app: true,
|
||||
win_subsystem: 'windows',
|
||||
link_args: extra_demo_ldflags,
|
||||
install: true,
|
||||
)
|
||||
@@ -258,7 +252,7 @@ executable('gtk4-demo-application',
|
||||
c_args: gtkdemo_args + common_cflags,
|
||||
dependencies: gtkdemo_deps,
|
||||
include_directories: confinc,
|
||||
gui_app: true,
|
||||
win_subsystem: 'windows',
|
||||
link_args: extra_demo_ldflags,
|
||||
install: true,
|
||||
)
|
||||
|
||||
@@ -1,348 +0,0 @@
|
||||
/* Path/Text 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)
|
||||
{
|
||||
PangoLayout *layout;
|
||||
PangoFontDescription *desc;
|
||||
GskPathBuilder *builder;
|
||||
|
||||
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);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
|
||||
gsk_path_builder_add_layout (builder, layout);
|
||||
|
||||
return gsk_path_builder_free_to_path (builder);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -1,339 +0,0 @@
|
||||
/* Path/Maze
|
||||
* #Keywords: game, mouse
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
@@ -1,594 +0,0 @@
|
||||
/* Path/Curved 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.5 }, &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)
|
||||
{
|
||||
/* 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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (self->editable && self->line_path)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
NULL, NULL,
|
||||
&self->line_closest,
|
||||
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;
|
||||
}
|
||||
@@ -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>
|
||||
@@ -16,7 +16,7 @@ executable('gtk4-icon-browser',
|
||||
c_args: common_cflags,
|
||||
dependencies: [ libgtk_dep, demo_conf_h ],
|
||||
include_directories: confinc,
|
||||
gui_app: true,
|
||||
win_subsystem: 'windows',
|
||||
link_args: extra_demo_ldflags,
|
||||
install: true,
|
||||
)
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ demo_profile = get_option('profile')
|
||||
|
||||
demo_conf_h = declare_dependency(
|
||||
sources: custom_target('demo-header',
|
||||
command: [gen_demo_header, meson.source_root(), demo_profile],
|
||||
command: [gen_demo_header, meson.project_source_root(), demo_profile],
|
||||
capture: true,
|
||||
output: 'demo_conf.h',
|
||||
build_by_default: true,
|
||||
|
||||
@@ -17,7 +17,7 @@ executable('gtk4-node-editor',
|
||||
c_args: [
|
||||
'-DNODE_EDITOR_SOURCE_DIR="@0@/../../testsuite/gsk/compare/"'.format(meson.current_source_dir())
|
||||
] + common_cflags,
|
||||
gui_app: true,
|
||||
win_subsystem: 'windows',
|
||||
link_args: extra_demo_ldflags,
|
||||
install: false,
|
||||
)
|
||||
|
||||
@@ -3,7 +3,7 @@ executable('gtk4-print-editor',
|
||||
c_args: common_cflags,
|
||||
dependencies: [ libgtk_dep, demo_conf_h ],
|
||||
include_directories: confinc,
|
||||
gui_app: true,
|
||||
win_subsystem: 'windows',
|
||||
link_args: extra_demo_ldflags,
|
||||
install: true,
|
||||
)
|
||||
|
||||
@@ -74,7 +74,7 @@ executable('gtk4-widget-factory',
|
||||
c_args: common_cflags,
|
||||
dependencies: [ libgtk_dep, demo_conf_h ],
|
||||
include_directories: confinc,
|
||||
gui_app: true,
|
||||
win_subsystem: 'windows',
|
||||
link_args: extra_demo_ldflags,
|
||||
install: true,
|
||||
)
|
||||
|
||||
@@ -1927,6 +1927,12 @@ microphone-sensitivity-medium-symbolic</property>
|
||||
<property name="tooltip-text" translatable="1">Insert something</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkColorButton">
|
||||
<property name="rgba">#9141AC</property>
|
||||
<property name="tooltip-text" translatable="1">Select a color</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
|
||||
@@ -1276,6 +1276,15 @@ is provided in the form of a `GtkIconPaintable` (this can be checked with
|
||||
[method@Gtk.IconPaintable.is_symbolic]), you have to call
|
||||
[method@Gtk.IconPaintable.get_icon_name] and set the icon name on a `GtkImage`.
|
||||
|
||||
### Adapt to GtkImage changes
|
||||
`GtkPicture`'s behaviour was "split out" of `GtkImage` as the latter was covering
|
||||
too many use cases; if you're loading an icon, [class@Gtk.Image] in GTK3 and GTK4 are
|
||||
perfectly equivalent. If you are loading a more complex image asset, like a picture
|
||||
or a thumbnail, then [class@Gtk.Picture] is the appropriate widget.
|
||||
|
||||
One noteworthy distinction is that while `GtkImage` has its size computed by
|
||||
GTK, `GtkPicture` lets you decide about the size.
|
||||
|
||||
### Update to GtkFileChooser API changes
|
||||
|
||||
`GtkFileChooser` moved to a GFile-based API. If you need to convert a path
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
|
||||
typedef struct _Deserializer Deserializer;
|
||||
|
||||
struct _Deserializer
|
||||
struct _Deserializer
|
||||
{
|
||||
const char * mime_type; /* interned */
|
||||
GType type;
|
||||
@@ -264,7 +264,7 @@ gdk_content_deserializer_get_priority (GdkContentDeserializer *deserializer)
|
||||
*
|
||||
* This is the `GCancellable` that was passed to [func@Gdk.content_deserialize_async].
|
||||
*
|
||||
* Returns: (transfer none): the cancellable for the current operation
|
||||
* Returns: (transfer none) (nullable): the cancellable for the current operation
|
||||
*/
|
||||
GCancellable *
|
||||
gdk_content_deserializer_get_cancellable (GdkContentDeserializer *deserializer)
|
||||
@@ -934,25 +934,6 @@ init (void)
|
||||
|
||||
formats = gdk_pixbuf_get_formats ();
|
||||
|
||||
/* Make sure png comes first */
|
||||
for (f = formats; f; f = f->next)
|
||||
{
|
||||
GdkPixbufFormat *fmt = f->data;
|
||||
char *name;
|
||||
|
||||
name = gdk_pixbuf_format_get_name (fmt);
|
||||
if (g_str_equal (name, "png"))
|
||||
{
|
||||
formats = g_slist_delete_link (formats, f);
|
||||
formats = g_slist_prepend (formats, fmt);
|
||||
|
||||
g_free (name);
|
||||
break;
|
||||
}
|
||||
|
||||
g_free (name);
|
||||
}
|
||||
|
||||
for (f = formats; f; f = f->next)
|
||||
{
|
||||
GdkPixbufFormat *fmt = f->data;
|
||||
|
||||
+26
-12
@@ -718,19 +718,33 @@ gdk_content_formats_builder_to_formats (GdkContentFormatsBuilder *builder)
|
||||
|
||||
g_return_val_if_fail (builder != NULL, NULL);
|
||||
|
||||
gtypes = g_new (GType, builder->n_gtypes + 1);
|
||||
i = builder->n_gtypes;
|
||||
gtypes[i--] = G_TYPE_INVALID;
|
||||
/* add backwards because most important type is last in the list */
|
||||
for (l = builder->gtypes; l; l = l->next)
|
||||
gtypes[i--] = GPOINTER_TO_SIZE (l->data);
|
||||
if (builder->n_gtypes > 0)
|
||||
{
|
||||
gtypes = g_new (GType, builder->n_gtypes + 1);
|
||||
i = builder->n_gtypes;
|
||||
gtypes[i--] = G_TYPE_INVALID;
|
||||
/* add backwards because most important type is last in the list */
|
||||
for (l = builder->gtypes; l; l = l->next)
|
||||
gtypes[i--] = GPOINTER_TO_SIZE (l->data);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtypes = NULL;
|
||||
}
|
||||
|
||||
mime_types = g_new (const char *, builder->n_mime_types + 1);
|
||||
i = builder->n_mime_types;
|
||||
mime_types[i--] = NULL;
|
||||
/* add backwards because most important type is last in the list */
|
||||
for (l = builder->mime_types; l; l = l->next)
|
||||
mime_types[i--] = l->data;
|
||||
if (builder->n_mime_types > 0)
|
||||
{
|
||||
mime_types = g_new (const char *, builder->n_mime_types + 1);
|
||||
i = builder->n_mime_types;
|
||||
mime_types[i--] = NULL;
|
||||
/* add backwards because most important type is last in the list */
|
||||
for (l = builder->mime_types; l; l = l->next)
|
||||
mime_types[i--] = l->data;
|
||||
}
|
||||
else
|
||||
{
|
||||
mime_types = NULL;
|
||||
}
|
||||
|
||||
result = gdk_content_formats_new_take (gtypes, builder->n_gtypes,
|
||||
mime_types, builder->n_mime_types);
|
||||
|
||||
@@ -342,7 +342,7 @@ gdk_content_provider_write_mime_type_finish (GdkContentProvider *provider,
|
||||
/**
|
||||
* gdk_content_provider_get_value:
|
||||
* @provider: a `GdkContentProvider`
|
||||
* @value: the `GValue` to fill
|
||||
* @value: (out caller-allocates): the `GValue` to fill
|
||||
* @error: a `GError` location to store the error occurring
|
||||
*
|
||||
* Gets the contents of @provider stored in @value.
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
|
||||
typedef struct _Serializer Serializer;
|
||||
|
||||
struct _Serializer
|
||||
struct _Serializer
|
||||
{
|
||||
const char * mime_type; /* interned */
|
||||
GType type;
|
||||
@@ -270,7 +270,7 @@ gdk_content_serializer_get_priority (GdkContentSerializer *serializer)
|
||||
*
|
||||
* This is the `GCancellable` that was passed to [func@content_serialize_async].
|
||||
*
|
||||
* Returns: (transfer none): the cancellable for the current operation
|
||||
* Returns: (transfer none) (nullable): the cancellable for the current operation
|
||||
*/
|
||||
GCancellable *
|
||||
gdk_content_serializer_get_cancellable (GdkContentSerializer *serializer)
|
||||
@@ -446,7 +446,7 @@ lookup_serializer (const char *mime_type,
|
||||
serializer->type == type)
|
||||
return serializer;
|
||||
}
|
||||
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -630,7 +630,7 @@ pixbuf_serializer (GdkContentSerializer *serializer)
|
||||
const GValue *value;
|
||||
GdkPixbuf *pixbuf;
|
||||
const char *name;
|
||||
|
||||
|
||||
name = gdk_content_serializer_get_user_data (serializer);
|
||||
value = gdk_content_serializer_get_value (serializer);
|
||||
|
||||
@@ -651,7 +651,7 @@ pixbuf_serializer (GdkContentSerializer *serializer)
|
||||
gdk_pixbuf_save_to_stream_async (pixbuf,
|
||||
gdk_content_serializer_get_output_stream (serializer),
|
||||
name,
|
||||
gdk_content_serializer_get_cancellable (serializer),
|
||||
gdk_content_serializer_get_cancellable (serializer),
|
||||
pixbuf_serializer_finish,
|
||||
serializer,
|
||||
g_str_equal (name, "png") ? "compression" : NULL, "2",
|
||||
@@ -823,7 +823,7 @@ file_uri_serializer (GdkContentSerializer *serializer)
|
||||
else if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST))
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
|
||||
for (l = g_value_get_boxed (value); l; l = l->next)
|
||||
{
|
||||
uri = g_file_get_uri (l->data);
|
||||
@@ -867,7 +867,7 @@ file_text_serializer (GdkContentSerializer *serializer)
|
||||
{
|
||||
GString *str;
|
||||
GSList *l;
|
||||
|
||||
|
||||
str = g_string_new (NULL);
|
||||
|
||||
for (l = g_value_get_boxed (value); l; l = l->next)
|
||||
@@ -966,25 +966,6 @@ init (void)
|
||||
|
||||
formats = gdk_pixbuf_get_formats ();
|
||||
|
||||
/* Make sure png comes first */
|
||||
for (f = formats; f; f = f->next)
|
||||
{
|
||||
GdkPixbufFormat *fmt = f->data;
|
||||
char *name;
|
||||
|
||||
name = gdk_pixbuf_format_get_name (fmt);
|
||||
if (g_str_equal (name, "png"))
|
||||
{
|
||||
formats = g_slist_delete_link (formats, f);
|
||||
formats = g_slist_prepend (formats, fmt);
|
||||
|
||||
g_free (name);
|
||||
break;
|
||||
}
|
||||
|
||||
g_free (name);
|
||||
}
|
||||
|
||||
for (f = formats; f; f = f->next)
|
||||
{
|
||||
GdkPixbufFormat *fmt = f->data;
|
||||
|
||||
+1
-2
@@ -47,8 +47,7 @@
|
||||
* Cursors by themselves are not very interesting: they must be bound to a
|
||||
* window for users to see them. This is done with [method@Gdk.Surface.set_cursor]
|
||||
* or [method@Gdk.Surface.set_device_cursor]. Applications will typically
|
||||
* use higher-level GTK functions such as [method@Gtk.Widget.set_cursor]`
|
||||
* instead.
|
||||
* use higher-level GTK functions such as [method@Gtk.Widget.set_cursor] instead.
|
||||
*
|
||||
* Cursors are not bound to a given [class@Gdk.Display], so they can be shared.
|
||||
* However, the appearance of cursors may vary when used on different
|
||||
|
||||
@@ -132,6 +132,8 @@ gdk_device_class_init (GdkDeviceClass *klass)
|
||||
* GdkDevice:source: (attributes org.gtk.Property.get=gdk_device_get_source)
|
||||
*
|
||||
* Source type for the device.
|
||||
*
|
||||
* Deprecated: 4.6: Use GdkDeviceTool:tool-type instead
|
||||
*/
|
||||
device_props[PROP_SOURCE] =
|
||||
g_param_spec_enum ("source",
|
||||
@@ -596,6 +598,8 @@ gdk_device_get_has_cursor (GdkDevice *device)
|
||||
* Determines the type of the device.
|
||||
*
|
||||
* Returns: a `GdkInputSource`
|
||||
*
|
||||
* Deprecated: 4.6: Use gdk_device_tool_get_tool_type() instead
|
||||
*/
|
||||
GdkInputSource
|
||||
gdk_device_get_source (GdkDevice *device)
|
||||
|
||||
+1
-1
@@ -92,7 +92,7 @@ GDK_AVAILABLE_IN_ALL
|
||||
GdkDisplay * gdk_device_get_display (GdkDevice *device);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GdkSeat * gdk_device_get_seat (GdkDevice *device);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GDK_DEPRECATED_IN_4_6_FOR(gdk_device_tool_get_tool_type)
|
||||
GdkDeviceTool * gdk_device_get_device_tool (GdkDevice *device);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
|
||||
+135
-62
@@ -19,7 +19,7 @@
|
||||
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
|
||||
* file for a list of people on the GTK+ Team. See the ChangeLog
|
||||
* files for a list of changes. These files are distributed with
|
||||
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
|
||||
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
@@ -522,7 +522,9 @@ _gdk_event_queue_find_first (GdkDisplay *display)
|
||||
if (pending_motion)
|
||||
return pending_motion;
|
||||
|
||||
if (event->event_type == GDK_MOTION_NOTIFY && (event->flags & GDK_EVENT_FLUSHED) == 0)
|
||||
if ((event->event_type == GDK_MOTION_NOTIFY ||
|
||||
(event->event_type == GDK_SCROLL && gdk_scroll_event_get_direction (event) == GDK_SCROLL_SMOOTH)) &&
|
||||
(event->flags & GDK_EVENT_FLUSHED) == 0)
|
||||
pending_motion = tmp_list;
|
||||
else
|
||||
return tmp_list;
|
||||
@@ -596,6 +598,9 @@ _gdk_event_unqueue (GdkDisplay *display)
|
||||
/*
|
||||
* If the last N events in the event queue are smooth scroll events
|
||||
* for the same surface and device, combine them into one.
|
||||
*
|
||||
* We give the remaining event a history with N items, and deltas
|
||||
* that are the sum over the history entries.
|
||||
*/
|
||||
void
|
||||
gdk_event_queue_handle_scroll_compression (GdkDisplay *display)
|
||||
@@ -605,7 +610,6 @@ gdk_event_queue_handle_scroll_compression (GdkDisplay *display)
|
||||
GdkDevice *device = NULL;
|
||||
GdkEvent *last_event = NULL;
|
||||
GList *scrolls = NULL;
|
||||
double delta_x, delta_y;
|
||||
GArray *history = NULL;
|
||||
GdkTimeCoord hist;
|
||||
|
||||
@@ -640,35 +644,42 @@ gdk_event_queue_handle_scroll_compression (GdkDisplay *display)
|
||||
l = l->prev;
|
||||
}
|
||||
|
||||
delta_x = delta_y = 0;
|
||||
|
||||
while (scrolls && scrolls->next != NULL)
|
||||
{
|
||||
GdkEvent *event = scrolls->data;
|
||||
GList *next = scrolls->next;
|
||||
double dx, dy;
|
||||
gboolean inherited = FALSE;
|
||||
|
||||
if (!history && ((GdkScrollEvent *)event)->history)
|
||||
{
|
||||
history = ((GdkScrollEvent *)event)->history;
|
||||
((GdkScrollEvent *)event)->history = NULL;
|
||||
inherited = TRUE;
|
||||
}
|
||||
|
||||
if (!history)
|
||||
history = g_array_new (FALSE, TRUE, sizeof (GdkTimeCoord));
|
||||
|
||||
gdk_scroll_event_get_deltas (event, &dx, &dy);
|
||||
delta_x += dx;
|
||||
delta_y += dy;
|
||||
if (!inherited)
|
||||
{
|
||||
gdk_scroll_event_get_deltas (event, &dx, &dy);
|
||||
|
||||
memset (&hist, 0, sizeof (GdkTimeCoord));
|
||||
hist.time = gdk_event_get_time (event);
|
||||
hist.flags = GDK_AXIS_FLAG_DELTA_X | GDK_AXIS_FLAG_DELTA_Y;
|
||||
hist.axes[GDK_AXIS_DELTA_X] = dx;
|
||||
hist.axes[GDK_AXIS_DELTA_Y] = dy;
|
||||
memset (&hist, 0, sizeof (GdkTimeCoord));
|
||||
hist.time = gdk_event_get_time (event);
|
||||
hist.flags = GDK_AXIS_FLAG_DELTA_X | GDK_AXIS_FLAG_DELTA_Y;
|
||||
hist.axes[GDK_AXIS_DELTA_X] = dx;
|
||||
hist.axes[GDK_AXIS_DELTA_Y] = dy;
|
||||
|
||||
g_array_append_val (history, hist);
|
||||
g_array_append_val (history, hist);
|
||||
}
|
||||
|
||||
gdk_event_unref (event);
|
||||
g_queue_delete_link (&display->queued_events, scrolls);
|
||||
scrolls = next;
|
||||
}
|
||||
|
||||
if (scrolls)
|
||||
if (scrolls && history)
|
||||
{
|
||||
GdkEvent *old_event, *event;
|
||||
double dx, dy;
|
||||
@@ -676,13 +687,29 @@ gdk_event_queue_handle_scroll_compression (GdkDisplay *display)
|
||||
old_event = scrolls->data;
|
||||
|
||||
gdk_scroll_event_get_deltas (old_event, &dx, &dy);
|
||||
|
||||
memset (&hist, 0, sizeof (GdkTimeCoord));
|
||||
hist.time = gdk_event_get_time (old_event);
|
||||
hist.flags = GDK_AXIS_FLAG_DELTA_X | GDK_AXIS_FLAG_DELTA_Y;
|
||||
hist.axes[GDK_AXIS_DELTA_X] = dx;
|
||||
hist.axes[GDK_AXIS_DELTA_Y] = dy;
|
||||
g_array_append_val (history, hist);
|
||||
|
||||
dx = dy = 0;
|
||||
for (int i = 0; i < history->len; i++)
|
||||
{
|
||||
GdkTimeCoord *val = &g_array_index (history, GdkTimeCoord, i);
|
||||
dx += val->axes[GDK_AXIS_DELTA_X];
|
||||
dy += val->axes[GDK_AXIS_DELTA_Y];
|
||||
}
|
||||
|
||||
event = gdk_scroll_event_new (surface,
|
||||
device,
|
||||
gdk_event_get_device_tool (old_event),
|
||||
gdk_event_get_time (old_event),
|
||||
gdk_event_get_modifier_state (old_event),
|
||||
delta_x + dx,
|
||||
delta_y + dy,
|
||||
dx,
|
||||
dy,
|
||||
gdk_scroll_event_is_stop (old_event));
|
||||
|
||||
((GdkScrollEvent *)event)->history = history;
|
||||
@@ -714,24 +741,41 @@ gdk_motion_event_push_history (GdkEvent *event,
|
||||
g_assert (GDK_IS_EVENT_TYPE (event, GDK_MOTION_NOTIFY));
|
||||
g_assert (GDK_IS_EVENT_TYPE (history_event, GDK_MOTION_NOTIFY));
|
||||
|
||||
if (!self->tool)
|
||||
return;
|
||||
if (G_UNLIKELY (!self->history))
|
||||
self->history = g_array_new (FALSE, TRUE, sizeof (GdkTimeCoord));
|
||||
|
||||
if (((GdkMotionEvent *)history_event)->history)
|
||||
{
|
||||
GArray *history = ((GdkMotionEvent *)history_event)->history;
|
||||
g_array_append_vals (self->history, history->data, history->len);
|
||||
}
|
||||
|
||||
tool = gdk_event_get_device_tool (history_event);
|
||||
|
||||
memset (&hist, 0, sizeof (GdkTimeCoord));
|
||||
hist.time = gdk_event_get_time (history_event);
|
||||
hist.flags = gdk_device_tool_get_axes (tool);
|
||||
|
||||
for (i = GDK_AXIS_X; i < GDK_AXIS_LAST; i++)
|
||||
gdk_event_get_axis (history_event, i, &hist.axes[i]);
|
||||
|
||||
if (G_UNLIKELY (!self->history))
|
||||
self->history = g_array_new (FALSE, TRUE, sizeof (GdkTimeCoord));
|
||||
if (tool)
|
||||
{
|
||||
hist.flags = gdk_device_tool_get_axes (tool);
|
||||
for (i = GDK_AXIS_X; i < GDK_AXIS_LAST; i++)
|
||||
gdk_event_get_axis (history_event, i, &hist.axes[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
hist.flags = GDK_AXIS_FLAG_X | GDK_AXIS_FLAG_Y;
|
||||
gdk_event_get_position (history_event, &hist.axes[GDK_AXIS_X], &hist.axes[GDK_AXIS_Y]);
|
||||
}
|
||||
|
||||
g_array_append_val (self->history, hist);
|
||||
}
|
||||
|
||||
/* If the last N events in the event queue are motion notify
|
||||
* events for the same surface, drop all but the last.
|
||||
*
|
||||
* If a button is held down or the device has a tool, then
|
||||
* we give the remaining events a history containing the N-1
|
||||
* dropped events.
|
||||
*/
|
||||
void
|
||||
_gdk_event_queue_handle_motion_compression (GdkDisplay *display)
|
||||
{
|
||||
@@ -741,9 +785,6 @@ _gdk_event_queue_handle_motion_compression (GdkDisplay *display)
|
||||
GdkDevice *pending_motion_device = NULL;
|
||||
GdkEvent *last_motion = NULL;
|
||||
|
||||
/* If the last N events in the event queue are motion notify
|
||||
* events for the same surface, drop all but the last */
|
||||
|
||||
tmp_list = g_queue_peek_tail_link (&display->queued_events);
|
||||
|
||||
while (tmp_list)
|
||||
@@ -780,12 +821,11 @@ _gdk_event_queue_handle_motion_compression (GdkDisplay *display)
|
||||
|
||||
if (last_motion != NULL)
|
||||
{
|
||||
GdkModifierType state = gdk_event_get_modifier_state (last_motion);
|
||||
|
||||
if (state &
|
||||
(GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK |
|
||||
GDK_BUTTON4_MASK | GDK_BUTTON5_MASK))
|
||||
gdk_motion_event_push_history (last_motion, pending_motions->data);
|
||||
if ((gdk_event_get_modifier_state (last_motion) &
|
||||
(GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK |
|
||||
GDK_BUTTON4_MASK | GDK_BUTTON5_MASK)) ||
|
||||
gdk_event_get_device_tool (last_motion) != NULL)
|
||||
gdk_motion_event_push_history (last_motion, pending_motions->data);
|
||||
}
|
||||
|
||||
gdk_event_unref (pending_motions->data);
|
||||
@@ -903,6 +943,9 @@ gdk_event_get_pointer_emulated (GdkEvent *event)
|
||||
* Extract the axis value for a particular axis use from
|
||||
* an event structure.
|
||||
*
|
||||
* To find out which axes are used, use [method@Gdk.DeviceTool.get_axes]
|
||||
* on the device tool returned by [method@Gdk.Event.get_device_tool].
|
||||
*
|
||||
* Returns: %TRUE if the specified axis was found, otherwise %FALSE
|
||||
*/
|
||||
gboolean
|
||||
@@ -1129,6 +1172,9 @@ G_DEFINE_BOXED_TYPE (GdkEventSequence, gdk_event_sequence,
|
||||
*
|
||||
* Extracts all axis values from an event.
|
||||
*
|
||||
* To find out which axes are used, use [method@Gdk.DeviceTool.get_axes]
|
||||
* on the device tool returned by [method@Gdk.Event.get_device_tool].
|
||||
*
|
||||
* Returns: %TRUE on success, otherwise %FALSE
|
||||
*/
|
||||
gboolean
|
||||
@@ -1179,7 +1225,7 @@ gdk_event_get_event_type (GdkEvent *event)
|
||||
*
|
||||
* Extracts the surface associated with an event.
|
||||
*
|
||||
* Returns: (transfer none): The `GdkSurface` associated with the event
|
||||
* Returns: (transfer none) (nullable): The `GdkSurface` associated with the event
|
||||
*/
|
||||
GdkSurface *
|
||||
gdk_event_get_surface (GdkEvent *event)
|
||||
@@ -2431,6 +2477,14 @@ gdk_touchpad_event_get_state (GdkEvent *event)
|
||||
return self->state;
|
||||
}
|
||||
|
||||
static GdkEventSequence *
|
||||
gdk_touchpad_event_get_sequence (GdkEvent *event)
|
||||
{
|
||||
GdkTouchpadEvent *self = (GdkTouchpadEvent *) event;
|
||||
|
||||
return self->sequence;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gdk_touchpad_event_get_position (GdkEvent *event,
|
||||
double *x,
|
||||
@@ -2450,7 +2504,7 @@ static const GdkEventTypeInfo gdk_touchpad_event_info = {
|
||||
NULL,
|
||||
gdk_touchpad_event_get_state,
|
||||
gdk_touchpad_event_get_position,
|
||||
NULL,
|
||||
gdk_touchpad_event_get_sequence,
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
@@ -2461,19 +2515,28 @@ GDK_DEFINE_EVENT_TYPE (GdkTouchpadEvent, gdk_touchpad_event,
|
||||
GDK_EVENT_TYPE_SLOT (GDK_TOUCHPAD_PINCH))
|
||||
|
||||
GdkEvent *
|
||||
gdk_touchpad_event_new_swipe (GdkSurface *surface,
|
||||
GdkDevice *device,
|
||||
guint32 time,
|
||||
GdkModifierType state,
|
||||
GdkTouchpadGesturePhase phase,
|
||||
double x,
|
||||
double y,
|
||||
int n_fingers,
|
||||
double dx,
|
||||
double dy)
|
||||
gdk_touchpad_event_new_swipe (GdkSurface *surface,
|
||||
GdkEventSequence *sequence,
|
||||
GdkDevice *device,
|
||||
guint32 time,
|
||||
GdkModifierType state,
|
||||
GdkTouchpadGesturePhase phase,
|
||||
double x,
|
||||
double y,
|
||||
int n_fingers,
|
||||
double dx,
|
||||
double dy)
|
||||
{
|
||||
GdkTouchpadEvent *self = gdk_event_alloc (GDK_TOUCHPAD_SWIPE, surface, device, time);
|
||||
GdkTouchpadEvent *self;
|
||||
|
||||
g_return_val_if_fail (phase == GDK_TOUCHPAD_GESTURE_PHASE_BEGIN ||
|
||||
phase == GDK_TOUCHPAD_GESTURE_PHASE_END ||
|
||||
phase == GDK_TOUCHPAD_GESTURE_PHASE_UPDATE ||
|
||||
phase == GDK_TOUCHPAD_GESTURE_PHASE_CANCEL, NULL);
|
||||
|
||||
self = gdk_event_alloc (GDK_TOUCHPAD_SWIPE, surface, device, time);
|
||||
|
||||
self->sequence = sequence;
|
||||
self->state = state;
|
||||
self->phase = phase;
|
||||
self->x = x;
|
||||
@@ -2486,21 +2549,30 @@ gdk_touchpad_event_new_swipe (GdkSurface *surface,
|
||||
}
|
||||
|
||||
GdkEvent *
|
||||
gdk_touchpad_event_new_pinch (GdkSurface *surface,
|
||||
GdkDevice *device,
|
||||
guint32 time,
|
||||
GdkModifierType state,
|
||||
GdkTouchpadGesturePhase phase,
|
||||
double x,
|
||||
double y,
|
||||
int n_fingers,
|
||||
double dx,
|
||||
double dy,
|
||||
double scale,
|
||||
double angle_delta)
|
||||
gdk_touchpad_event_new_pinch (GdkSurface *surface,
|
||||
GdkEventSequence *sequence,
|
||||
GdkDevice *device,
|
||||
guint32 time,
|
||||
GdkModifierType state,
|
||||
GdkTouchpadGesturePhase phase,
|
||||
double x,
|
||||
double y,
|
||||
int n_fingers,
|
||||
double dx,
|
||||
double dy,
|
||||
double scale,
|
||||
double angle_delta)
|
||||
{
|
||||
GdkTouchpadEvent *self = gdk_event_alloc (GDK_TOUCHPAD_PINCH, surface, device, time);
|
||||
GdkTouchpadEvent *self;
|
||||
|
||||
g_return_val_if_fail (phase == GDK_TOUCHPAD_GESTURE_PHASE_BEGIN ||
|
||||
phase == GDK_TOUCHPAD_GESTURE_PHASE_END ||
|
||||
phase == GDK_TOUCHPAD_GESTURE_PHASE_UPDATE ||
|
||||
phase == GDK_TOUCHPAD_GESTURE_PHASE_CANCEL, NULL);
|
||||
|
||||
self = gdk_event_alloc (GDK_TOUCHPAD_PINCH, surface, device, time);
|
||||
|
||||
self->sequence = sequence;
|
||||
self->state = state;
|
||||
self->phase = phase;
|
||||
self->x = x;
|
||||
@@ -2907,7 +2979,8 @@ gdk_motion_event_new (GdkSurface *surface,
|
||||
* to the application because they occurred in the same frame as @event.
|
||||
*
|
||||
* Note that only motion and scroll events record history, and motion
|
||||
* events do it only if one of the mouse buttons is down.
|
||||
* events do it only if one of the mouse buttons is down, or the device
|
||||
* has a tool.
|
||||
*
|
||||
* Returns: (transfer container) (array length=out_n_coords) (nullable): an
|
||||
* array of time and coordinates
|
||||
|
||||
+14
-11
@@ -209,7 +209,7 @@ struct _GdkTouchEvent
|
||||
* @pointer_emulated: whether the scroll event was the result of
|
||||
* a pointer emulation
|
||||
* @tool: a `GdkDeviceTool`
|
||||
* @history: (element-type GdkScrollHistory): array of times and deltas
|
||||
* @history: (element-type GdkTimeCoord): array of times and deltas
|
||||
* for other scroll events that were compressed before delivering the
|
||||
* current event
|
||||
*
|
||||
@@ -233,7 +233,7 @@ struct _GdkScrollEvent
|
||||
gboolean pointer_emulated;
|
||||
gboolean is_stop;
|
||||
GdkDeviceTool *tool;
|
||||
GArray *history; /* <GdkScrollHistory> */
|
||||
GArray *history; /* <GdkTimeCoord> */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -402,6 +402,7 @@ struct _GdkTouchpadEvent
|
||||
{
|
||||
GdkEvent parent_instance;
|
||||
|
||||
GdkEventSequence *sequence;
|
||||
GdkModifierType state;
|
||||
gint8 phase;
|
||||
gint8 n_fingers;
|
||||
@@ -506,18 +507,20 @@ GdkEvent * gdk_touch_event_new (GdkEventType type,
|
||||
double *axes,
|
||||
gboolean emulating);
|
||||
|
||||
GdkEvent * gdk_touchpad_event_new_swipe (GdkSurface *surface,
|
||||
GdkDevice *device,
|
||||
guint32 time,
|
||||
GdkModifierType state,
|
||||
GdkEvent * gdk_touchpad_event_new_swipe (GdkSurface *surface,
|
||||
GdkEventSequence *sequence,
|
||||
GdkDevice *device,
|
||||
guint32 time,
|
||||
GdkModifierType state,
|
||||
GdkTouchpadGesturePhase phase,
|
||||
double x,
|
||||
double y,
|
||||
int n_fingers,
|
||||
double dx,
|
||||
double dy);
|
||||
double x,
|
||||
double y,
|
||||
int n_fingers,
|
||||
double dx,
|
||||
double dy);
|
||||
|
||||
GdkEvent * gdk_touchpad_event_new_pinch (GdkSurface *surface,
|
||||
GdkEventSequence *sequence,
|
||||
GdkDevice *device,
|
||||
guint32 time,
|
||||
GdkModifierType state,
|
||||
|
||||
+69
-11
@@ -151,6 +151,12 @@ unmask_context (MaskedContext *mask)
|
||||
return GDK_GL_CONTEXT (GSIZE_TO_POINTER (GPOINTER_TO_SIZE (mask) & ~(gsize) 1));
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
mask_is_surfaceless (MaskedContext *mask)
|
||||
{
|
||||
return GPOINTER_TO_SIZE (mask) & (gsize) 1;
|
||||
}
|
||||
|
||||
static void
|
||||
unref_unmasked (gpointer data)
|
||||
{
|
||||
@@ -574,8 +580,8 @@ gdk_gl_context_real_begin_frame (GdkDrawContext *draw_context,
|
||||
glViewport (0, 0, ww, wh);
|
||||
|
||||
#ifdef HAVE_EGL
|
||||
if (priv->egl_context)
|
||||
glDrawBuffers (1, (GLenum[1]) { GL_BACK_LEFT });
|
||||
if (priv->egl_context && gdk_gl_context_check_version (context, 0, 0, 3, 0))
|
||||
glDrawBuffers (1, (GLenum[1]) { gdk_gl_context_get_use_es (context) ? GL_BACK : GL_BACK_LEFT });
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -997,16 +1003,33 @@ gdk_gl_context_set_required_version (GdkGLContext *context,
|
||||
}
|
||||
|
||||
gboolean
|
||||
gdk_gl_context_check_version (GdkGLContext *context,
|
||||
int required_major,
|
||||
int required_minor)
|
||||
gdk_gl_context_check_version (GdkGLContext *self,
|
||||
int required_gl_major,
|
||||
int required_gl_minor,
|
||||
int required_gles_major,
|
||||
int required_gles_minor)
|
||||
{
|
||||
GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
|
||||
GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);
|
||||
|
||||
g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);
|
||||
g_return_val_if_fail (required_minor < 10, FALSE);
|
||||
g_return_val_if_fail (GDK_IS_GL_CONTEXT (self), FALSE);
|
||||
g_return_val_if_fail (required_gl_minor < 10, FALSE);
|
||||
g_return_val_if_fail (required_gles_minor < 10, FALSE);
|
||||
|
||||
return priv->gl_version >= required_major * 10 + required_minor;
|
||||
if (!gdk_gl_context_is_realized (self))
|
||||
return FALSE;
|
||||
|
||||
switch (priv->api)
|
||||
{
|
||||
case GDK_GL_API_GL:
|
||||
return priv->gl_version >= required_gl_major * 10 + required_gl_minor;
|
||||
|
||||
case GDK_GL_API_GLES:
|
||||
return priv->gl_version >= required_gles_major * 10 + required_gles_minor;
|
||||
|
||||
default:
|
||||
g_return_val_if_reached (FALSE);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1323,6 +1346,7 @@ gl_debug_message_callback (GLenum source,
|
||||
const char *message_source;
|
||||
const char *message_type;
|
||||
const char *message_severity;
|
||||
GLogLevelFlags log_level;
|
||||
|
||||
if (severity == GL_DEBUG_SEVERITY_NOTIFICATION)
|
||||
return;
|
||||
@@ -1384,22 +1408,31 @@ gl_debug_message_callback (GLenum source,
|
||||
{
|
||||
case GL_DEBUG_SEVERITY_HIGH:
|
||||
message_severity = "High";
|
||||
log_level = G_LOG_LEVEL_CRITICAL;
|
||||
break;
|
||||
case GL_DEBUG_SEVERITY_MEDIUM:
|
||||
message_severity = "Medium";
|
||||
log_level = G_LOG_LEVEL_WARNING;
|
||||
break;
|
||||
case GL_DEBUG_SEVERITY_LOW:
|
||||
message_severity = "Low";
|
||||
log_level = G_LOG_LEVEL_MESSAGE;
|
||||
break;
|
||||
case GL_DEBUG_SEVERITY_NOTIFICATION:
|
||||
message_severity = "Notification";
|
||||
log_level = G_LOG_LEVEL_INFO;
|
||||
break;
|
||||
default:
|
||||
message_severity = "Unknown";
|
||||
log_level = G_LOG_LEVEL_MESSAGE;
|
||||
}
|
||||
|
||||
g_warning ("OPENGL:\n Source: %s\n Type: %s\n Severity: %s\n Message: %s",
|
||||
message_source, message_type, message_severity, message);
|
||||
/* There's no higher level function taking a log level argument... */
|
||||
g_log_structured_standard (G_LOG_DOMAIN, log_level,
|
||||
__FILE__, G_STRINGIFY (__LINE__),
|
||||
G_STRFUNC,
|
||||
"OPENGL:\n Source: %s\n Type: %s\n Severity: %s\n Message: %s",
|
||||
message_source, message_type, message_severity, message);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1655,6 +1688,31 @@ gdk_gl_context_clear_current (void)
|
||||
}
|
||||
}
|
||||
|
||||
/*<private>
|
||||
* gdk_gl_context_clear_current_if_surface:
|
||||
* @surface: surface to clear for
|
||||
*
|
||||
* Does a gdk_gl_context_clear_current() if the current context is attached
|
||||
* to @surface, leaves the current context alone otherwise.
|
||||
**/
|
||||
void
|
||||
gdk_gl_context_clear_current_if_surface (GdkSurface *surface)
|
||||
{
|
||||
MaskedContext *current;
|
||||
|
||||
current = g_private_get (&thread_current_context);
|
||||
if (current != NULL && !mask_is_surfaceless (current))
|
||||
{
|
||||
GdkGLContext *context = unmask_context (current);
|
||||
|
||||
if (gdk_gl_context_get_surface (context) != surface)
|
||||
return;
|
||||
|
||||
if (GDK_GL_CONTEXT_GET_CLASS (context)->clear_current (context))
|
||||
g_private_replace (&thread_current_context, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_gl_context_get_current:
|
||||
*
|
||||
|
||||
@@ -99,6 +99,8 @@ gboolean gdk_gl_backend_can_be_used (GdkGLBackend
|
||||
GError **error);
|
||||
void gdk_gl_backend_use (GdkGLBackend backend_type);
|
||||
|
||||
void gdk_gl_context_clear_current_if_surface (GdkSurface *surface);
|
||||
|
||||
GdkGLContext * gdk_gl_context_new (GdkDisplay *display,
|
||||
GdkSurface *surface);
|
||||
|
||||
@@ -109,8 +111,10 @@ void gdk_gl_context_set_is_legacy (GdkGLContext
|
||||
gboolean is_legacy);
|
||||
|
||||
gboolean gdk_gl_context_check_version (GdkGLContext *context,
|
||||
int required_major,
|
||||
int required_minor);
|
||||
int required_gl_major,
|
||||
int required_gl_minor,
|
||||
int required_gles_major,
|
||||
int required_gles_minor);
|
||||
|
||||
gboolean gdk_gl_context_has_unpack_subimage (GdkGLContext *context);
|
||||
void gdk_gl_context_push_debug_group (GdkGLContext *context,
|
||||
|
||||
+5
-2
@@ -21,6 +21,7 @@
|
||||
#include "gdkgltextureprivate.h"
|
||||
|
||||
#include "gdkdisplayprivate.h"
|
||||
#include "gdkglcontextprivate.h"
|
||||
#include "gdkmemoryformatprivate.h"
|
||||
#include "gdkmemorytextureprivate.h"
|
||||
#include "gdktextureprivate.h"
|
||||
@@ -305,9 +306,11 @@ gdk_gl_texture_determine_format (GdkGLTexture *self)
|
||||
GLint active_texture;
|
||||
GLint internal_format;
|
||||
|
||||
if (self->context != gdk_gl_context_get_current ())
|
||||
/* Abort if somebody else is GL-ing here... */
|
||||
if (self->context != gdk_gl_context_get_current () ||
|
||||
/* ... or glGetTexLevelParameter() isn't supported */
|
||||
!gdk_gl_context_check_version (self->context, 0, 0, 3, 1))
|
||||
{
|
||||
/* Somebody else is GL-ing here, abort! */
|
||||
texture->format = GDK_MEMORY_DEFAULT;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2,3 +2,4 @@ BOOLEAN:BOXED
|
||||
BOOLEAN:OBJECT
|
||||
BOOLEAN:POINTER
|
||||
VOID:POINTER,POINTER,BOOLEAN,BOOLEAN
|
||||
VOID:INT,INT
|
||||
|
||||
+1
-1
@@ -187,7 +187,7 @@ gdk_popup_get_rect_anchor (GdkPopup *popup)
|
||||
*
|
||||
* Returns the parent surface of a popup.
|
||||
*
|
||||
* Returns: (transfer none): the parent surface
|
||||
* Returns: (transfer none) (nullable): the parent surface
|
||||
*/
|
||||
GdkSurface *
|
||||
gdk_popup_get_parent (GdkPopup *popup)
|
||||
|
||||
+6
-1
@@ -605,11 +605,14 @@ gdk_surface_class_init (GdkSurfaceClass *klass)
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
_gdk_marshal_VOID__INT_INT,
|
||||
G_TYPE_NONE,
|
||||
2,
|
||||
G_TYPE_INT,
|
||||
G_TYPE_INT);
|
||||
g_signal_set_va_marshaller (signals[LAYOUT],
|
||||
G_OBJECT_CLASS_TYPE (object_class),
|
||||
_gdk_marshal_VOID__INT_INTv);
|
||||
|
||||
/**
|
||||
* GdkSurface::render:
|
||||
@@ -1092,6 +1095,7 @@ gdk_surface_set_egl_native_window (GdkSurface *self,
|
||||
|
||||
if (priv->egl_surface != NULL)
|
||||
{
|
||||
gdk_gl_context_clear_current_if_surface (self);
|
||||
eglDestroySurface (gdk_surface_get_display (self), priv->egl_surface);
|
||||
priv->egl_surface = NULL;
|
||||
}
|
||||
@@ -1120,6 +1124,7 @@ gdk_surface_ensure_egl_surface (GdkSurface *self,
|
||||
priv->egl_surface != NULL &&
|
||||
gdk_display_get_egl_config_high_depth (display) != gdk_display_get_egl_config (display))
|
||||
{
|
||||
gdk_gl_context_clear_current_if_surface (self);
|
||||
eglDestroySurface (gdk_surface_get_display (self), priv->egl_surface);
|
||||
priv->egl_surface = NULL;
|
||||
}
|
||||
|
||||
+1
-1
@@ -454,7 +454,7 @@ gdk_texture_new_from_resource (const char *resource_path)
|
||||
texture = NULL;
|
||||
|
||||
if (texture == NULL)
|
||||
g_error ("Resource path %s s not a valid image: %s", resource_path, error->message);
|
||||
g_error ("Resource path %s is not a valid image: %s", resource_path, error->message);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
@@ -89,10 +89,6 @@ GDK_AVAILABLE_IN_ALL
|
||||
void gdk_texture_download (GdkTexture *texture,
|
||||
guchar *data,
|
||||
gsize stride);
|
||||
GDK_AVAILABLE_IN_4_6
|
||||
void gdk_texture_download_float (GdkTexture *texture,
|
||||
float *data,
|
||||
gsize stride);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gdk_texture_save_to_png (GdkTexture *texture,
|
||||
const char *filename);
|
||||
|
||||
@@ -240,7 +240,7 @@ static const FormatData format_data[] = {
|
||||
[GDK_MEMORY_A8R8G8B8] = { GDK_MEMORY_R8G8B8A8, 8, 4, SAMPLEFORMAT_UINT, EXTRASAMPLE_UNASSALPHA },
|
||||
[GDK_MEMORY_R8G8B8A8] = { GDK_MEMORY_R8G8B8A8, 8, 4, SAMPLEFORMAT_UINT, EXTRASAMPLE_UNASSALPHA },
|
||||
[GDK_MEMORY_A8B8G8R8] = { GDK_MEMORY_R8G8B8A8, 8, 4, SAMPLEFORMAT_UINT, EXTRASAMPLE_UNASSALPHA },
|
||||
[GDK_MEMORY_R8G8B8] = { GDK_MEMORY_R8G8B8, 8, 3, SAMPLEFORMAT_UINT, 0 },
|
||||
[GDK_MEMORY_R8G8B8] = { GDK_MEMORY_R8G8B8, 8, 3, SAMPLEFORMAT_UINT, 0 },
|
||||
[GDK_MEMORY_B8G8R8] = { GDK_MEMORY_R8G8B8, 8, 3, SAMPLEFORMAT_UINT, 0 },
|
||||
[GDK_MEMORY_R16G16B16] = { GDK_MEMORY_R16G16B16, 16, 3, SAMPLEFORMAT_UINT, 0 },
|
||||
[GDK_MEMORY_R16G16B16A16_PREMULTIPLIED] = { GDK_MEMORY_R16G16B16A16_PREMULTIPLIED, 16, 4, SAMPLEFORMAT_UINT, EXTRASAMPLE_ASSOCALPHA },
|
||||
@@ -376,6 +376,13 @@ gdk_load_tiff (GBytes *input_bytes,
|
||||
G_GNUC_UNUSED gint64 before = GDK_PROFILER_CURRENT_TIME;
|
||||
|
||||
tif = tiff_open_read (input_bytes);
|
||||
if (!tif)
|
||||
{
|
||||
g_set_error_literal (error,
|
||||
GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_CORRUPT_IMAGE,
|
||||
_("Could not load TIFF data"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TIFFSetDirectory (tif, 0);
|
||||
|
||||
|
||||
@@ -537,6 +537,7 @@ fill_pinch_event (GdkMacosDisplay *display,
|
||||
seat = gdk_display_get_default_seat (GDK_DISPLAY (display));
|
||||
|
||||
return gdk_touchpad_event_new_pinch (GDK_SURFACE (surface),
|
||||
NULL, /* FIXME make up sequences */
|
||||
gdk_seat_get_pointer (seat),
|
||||
get_time_from_ns_event (nsevent),
|
||||
get_keyboard_modifiers_from_ns_event (nsevent),
|
||||
|
||||
@@ -137,6 +137,7 @@ struct _GdkWaylandPointerData {
|
||||
guint cursor_timeout_id;
|
||||
guint cursor_image_index;
|
||||
guint cursor_image_delay;
|
||||
guint touchpad_event_sequence;
|
||||
|
||||
guint current_output_scale;
|
||||
GSList *pointer_surface_outputs;
|
||||
@@ -1127,7 +1128,7 @@ data_offer_source_actions (void *data,
|
||||
seat->pending_source_actions = gdk_wayland_actions_to_gdk_actions (source_actions);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (seat->drop == NULL)
|
||||
return;
|
||||
|
||||
@@ -1152,7 +1153,7 @@ data_offer_action (void *data,
|
||||
seat->pending_action = gdk_wayland_actions_to_gdk_actions (action);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (seat->drop == NULL)
|
||||
return;
|
||||
|
||||
@@ -2164,7 +2165,7 @@ deliver_key_event (GdkWaylandSeat *seat,
|
||||
key,
|
||||
device_get_modifiers (seat->logical_pointer),
|
||||
_gdk_wayland_keymap_key_is_modifier (keymap, key),
|
||||
&translated,
|
||||
&translated,
|
||||
&no_lock);
|
||||
|
||||
_gdk_wayland_display_deliver_event (seat->display, event);
|
||||
@@ -2667,7 +2668,11 @@ emit_gesture_swipe_event (GdkWaylandSeat *seat,
|
||||
|
||||
seat->pointer_info.time = _time;
|
||||
|
||||
if (phase == GDK_TOUCHPAD_GESTURE_PHASE_BEGIN)
|
||||
seat->pointer_info.touchpad_event_sequence++;
|
||||
|
||||
event = gdk_touchpad_event_new_swipe (seat->pointer_info.focus,
|
||||
GDK_SLOT_TO_EVENT_SEQUENCE (seat->pointer_info.touchpad_event_sequence),
|
||||
seat->logical_pointer,
|
||||
_time,
|
||||
device_get_modifiers (seat->logical_pointer),
|
||||
@@ -2763,7 +2768,11 @@ emit_gesture_pinch_event (GdkWaylandSeat *seat,
|
||||
|
||||
seat->pointer_info.time = _time;
|
||||
|
||||
if (phase == GDK_TOUCHPAD_GESTURE_PHASE_BEGIN)
|
||||
seat->pointer_info.touchpad_event_sequence++;
|
||||
|
||||
event = gdk_touchpad_event_new_pinch (seat->pointer_info.focus,
|
||||
GDK_SLOT_TO_EVENT_SEQUENCE (seat->pointer_info.touchpad_event_sequence),
|
||||
seat->logical_pointer,
|
||||
_time,
|
||||
device_get_modifiers (seat->logical_pointer),
|
||||
@@ -4088,7 +4097,7 @@ tablet_pad_strip_handle_frame (void *data,
|
||||
event = gdk_pad_event_new_strip (seat->keyboard_focus,
|
||||
pad->device,
|
||||
time,
|
||||
g_list_index (pad->mode_groups, group),
|
||||
g_list_index (pad->mode_groups, group),
|
||||
g_list_index (pad->strips, wp_tablet_pad_strip),
|
||||
group->current_mode,
|
||||
group->axis_tmp_info.value);
|
||||
|
||||
@@ -1662,6 +1662,7 @@ static TranslationEntry translations[] = {
|
||||
{ FALSE, "org.gnome.desktop.wm.preferences", "action-middle-click-titlebar", "gtk-titlebar-middle-click", G_TYPE_STRING, { .s = "none" } },
|
||||
{ FALSE, "org.gnome.desktop.wm.preferences", "action-right-click-titlebar", "gtk-titlebar-right-click", G_TYPE_STRING, { .s = "menu" } },
|
||||
{ FALSE, "org.gnome.desktop.a11y", "always-show-text-caret", "gtk-keynav-use-caret", G_TYPE_BOOLEAN, { .b = FALSE } },
|
||||
{ FALSE, "org.gnome.desktop.a11y.interface", "high-contrast", "high-contast", G_TYPE_NONE, { .b = FALSE } },
|
||||
/* Note, this setting doesn't exist, the portal and gsd fake it */
|
||||
{ FALSE, "org.gnome.fontconfig", "serial", "gtk-fontconfig-timestamp", G_TYPE_NONE, { .i = 0 } },
|
||||
};
|
||||
@@ -1711,6 +1712,13 @@ find_translation_entry_by_setting (const char *setting)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
high_contrast_changed (GdkDisplay *display)
|
||||
{
|
||||
gdk_display_setting_changed (display, "gtk-theme-name");
|
||||
gdk_display_setting_changed (display, "gtk-icon-theme-name");
|
||||
}
|
||||
|
||||
static void
|
||||
settings_changed (GSettings *settings,
|
||||
const char *key,
|
||||
@@ -1724,6 +1732,8 @@ settings_changed (GSettings *settings,
|
||||
{
|
||||
if (entry->type != G_TYPE_NONE)
|
||||
gdk_display_setting_changed (display, entry->setting);
|
||||
else if (strcmp (key, "high-contrast") == 0)
|
||||
high_contrast_changed (display);
|
||||
else
|
||||
update_xft_settings (display);
|
||||
}
|
||||
@@ -2090,6 +2100,36 @@ set_decoration_layout_from_entry (GdkDisplay *display,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
set_theme_from_entry (GdkDisplay *display,
|
||||
TranslationEntry *entry,
|
||||
GValue *value)
|
||||
{
|
||||
GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
|
||||
GSettings *settings = NULL;
|
||||
GSettingsSchema *schema = NULL;
|
||||
gboolean hc = FALSE;
|
||||
|
||||
if (display_wayland->settings_portal == NULL)
|
||||
{
|
||||
settings = (GSettings *)g_hash_table_lookup (display_wayland->settings,
|
||||
"org.gnome.desktop.a11y.interface");
|
||||
}
|
||||
|
||||
if (settings)
|
||||
g_object_get (settings, "settings-schema", &schema, NULL);
|
||||
|
||||
if (schema && g_settings_schema_has_key (schema, "high-contrast"))
|
||||
hc = g_settings_get_boolean (settings, "high-contrast");
|
||||
|
||||
g_clear_pointer (&schema, g_settings_schema_unref);
|
||||
|
||||
if (hc)
|
||||
g_value_set_static_string (value, "HighContrast");
|
||||
else
|
||||
set_value_from_entry (display, entry, value);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
set_capability_setting (GdkDisplay *display,
|
||||
GValue *value,
|
||||
@@ -2121,6 +2161,9 @@ gdk_wayland_display_get_setting (GdkDisplay *display,
|
||||
{
|
||||
if (strcmp (name, "gtk-decoration-layout") == 0)
|
||||
set_decoration_layout_from_entry (display, entry, value);
|
||||
else if (strcmp (name, "gtk-theme-name") == 0 ||
|
||||
strcmp (name, "gtk-icon-theme-name") == 0)
|
||||
set_theme_from_entry (display, entry, value);
|
||||
else
|
||||
set_value_from_entry (display, entry, value);
|
||||
return TRUE;
|
||||
|
||||
@@ -1,406 +0,0 @@
|
||||
/* GDK - The GIMP Drawing Kit
|
||||
* Copyright (C) 2001 Stefan Ondrejicka
|
||||
*
|
||||
* 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 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/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
int id;
|
||||
char *bitmap;
|
||||
int hotx;
|
||||
int hoty;
|
||||
} font_info_t;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
int id;
|
||||
int width;
|
||||
int height;
|
||||
int hotx;
|
||||
int hoty;
|
||||
char *data;
|
||||
} cursor_info_t;
|
||||
|
||||
static GSList *fonts = NULL;
|
||||
static GSList *cursors = NULL;
|
||||
|
||||
static int dw,dh;
|
||||
|
||||
static gboolean debug = FALSE;
|
||||
|
||||
#define HEX(c) (((c) >= '0' && (c) <= '9') ? \
|
||||
((c) - '0') : (toupper(c) - 'A' + 10))
|
||||
|
||||
static void print_font(fi)
|
||||
font_info_t *fi;
|
||||
{
|
||||
int x,y;
|
||||
|
||||
for (y = 0; y < dh; y++)
|
||||
{
|
||||
for (x = 0; x < dw; x++)
|
||||
{
|
||||
printf(fi->bitmap[y*dw+x]? "X" : " ");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void print_cursor(ci)
|
||||
cursor_info_t *ci;
|
||||
{
|
||||
int x,y;
|
||||
|
||||
for (y = 0; y < ci->height; y++)
|
||||
{
|
||||
printf("/* ");
|
||||
for (x = 0; x < ci->width; x++)
|
||||
{
|
||||
if (ci->hotx == x && ci->hoty == y)
|
||||
printf("o");
|
||||
else
|
||||
switch (ci->data[y*ci->width+x])
|
||||
{
|
||||
case 0:
|
||||
printf(" ");
|
||||
break;
|
||||
case 1:
|
||||
printf(".");
|
||||
break;
|
||||
case 2:
|
||||
printf("X");
|
||||
break;
|
||||
}
|
||||
}
|
||||
printf(" */\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int read_bdf_font(fname)
|
||||
char *fname;
|
||||
{
|
||||
FILE *f;
|
||||
char line[2048];
|
||||
int rv = 0;
|
||||
gboolean startchar = FALSE, startbitmap = FALSE;
|
||||
char *charname,*p,*bitmap;
|
||||
int dx = 0,dy = 0;
|
||||
int w,h,x,y,py;
|
||||
int id,tmp;
|
||||
|
||||
dw = 0;
|
||||
dh = 0;
|
||||
|
||||
if (!(f = fopen(fname, "r")))
|
||||
{
|
||||
perror(fname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fgets(line, sizeof(line), f) && strncasecmp("STARTFONT ", line, 10))
|
||||
{
|
||||
printf("!BDF font file\n");
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
p = line;
|
||||
while (fgets(line, sizeof(line), f))
|
||||
{
|
||||
if (!startchar)
|
||||
{
|
||||
if (!strncasecmp("STARTCHAR ", line, 10))
|
||||
{
|
||||
startchar = TRUE;
|
||||
charname = g_strndup(p + 10,
|
||||
strcspn(p+10, "\r\n"));
|
||||
}
|
||||
else if (!strncasecmp("FONTBOUNDINGBOX ", line, 16))
|
||||
sscanf(p+16, "%d %d %d %d", &dw, &dh, &dx, &dy);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!strncasecmp("ENDCHAR", line, 7))
|
||||
{
|
||||
font_info_t *nfi;
|
||||
|
||||
if (debug)
|
||||
printf(" %*s*/\n", dw, "");
|
||||
startchar = FALSE;
|
||||
startbitmap = FALSE;
|
||||
|
||||
nfi = g_malloc(sizeof(font_info_t));
|
||||
memset(nfi, '\0', sizeof(font_info_t));
|
||||
|
||||
nfi->name = charname;
|
||||
nfi->id = id;
|
||||
nfi->bitmap = bitmap;
|
||||
nfi->hotx = 0 - dx;
|
||||
nfi->hoty = 0 - dy;
|
||||
|
||||
fonts = g_slist_append(fonts, nfi);
|
||||
}
|
||||
else if (startbitmap)
|
||||
{
|
||||
int px,cx;
|
||||
guchar mask;
|
||||
|
||||
px = x - dx + py * dw;
|
||||
for (cx = 0; cx < w; cx++)
|
||||
{
|
||||
mask = 1 << (3 - (cx % 4));
|
||||
|
||||
bitmap[px+cx] =
|
||||
(mask & HEX(line[cx/4])) != 0;
|
||||
|
||||
if (debug)
|
||||
printf(bitmap[px+cx] ? "X" : " ");
|
||||
}
|
||||
py++;
|
||||
if (debug)
|
||||
printf(" %*s*/\n/* %*s", dw-w, "", dw+dx, "");
|
||||
}
|
||||
else if (!strncasecmp("BBX ", line, 4))
|
||||
{
|
||||
sscanf(p+4, "%d %d %d %d", &w, &h, &x, &y);
|
||||
if (debug)
|
||||
printf("/* %s: */\n/* %*s", charname, dw+dx, "");
|
||||
}
|
||||
else if (!strncasecmp("ENCODING ", line, 9))
|
||||
{
|
||||
if (sscanf(p+9, "%d %d", &tmp, &id) != 2)
|
||||
id = tmp;
|
||||
}
|
||||
else if (!strncasecmp("BITMAP", line, 6))
|
||||
{
|
||||
py = y - dy;
|
||||
startbitmap = TRUE;
|
||||
bitmap = g_malloc(dw*dh);
|
||||
memset(bitmap, '\0', dw*dh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (strncasecmp("ENDFONT", line, 7))
|
||||
rv = -1;
|
||||
|
||||
fclose(f);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int font_info_compare(fi, name)
|
||||
font_info_t *fi;
|
||||
char *name;
|
||||
{
|
||||
return strcmp(name, fi->name);
|
||||
}
|
||||
|
||||
static cursor_info_t *gen_cursor(bmap, mask)
|
||||
font_info_t *bmap;
|
||||
font_info_t *mask;
|
||||
{
|
||||
cursor_info_t *ci;
|
||||
int bx = dw,by = dh,ex = 0,ey = 0;
|
||||
int i,j;
|
||||
|
||||
for (j = 0; j < dh; j++)
|
||||
{
|
||||
gboolean havep = FALSE;
|
||||
|
||||
for (i = 0; i < dw; i++)
|
||||
{
|
||||
if (bmap->bitmap[j*dw+i] || mask->bitmap[j*dw+i])
|
||||
{
|
||||
havep = TRUE;
|
||||
bx = MIN(bx, i);
|
||||
ex = MAX(i+1, ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (havep)
|
||||
{
|
||||
by = MIN(by, j);
|
||||
ey = MAX(ey, j+1);
|
||||
}
|
||||
}
|
||||
|
||||
ci = g_malloc(sizeof(cursor_info_t));
|
||||
ci->name = g_strdup(bmap->name);
|
||||
ci->id = bmap->id;
|
||||
|
||||
ci->width = ex - bx;
|
||||
ci->height = ey - by;
|
||||
|
||||
ci->hotx = bmap->hotx - bx;
|
||||
ci->hoty = ci->height - (bmap->hoty - by);
|
||||
|
||||
ci->data = g_malloc(ci->width * ci->height);
|
||||
memset(ci->data, '\0', ci->width * ci->height);
|
||||
|
||||
for (j = 0; j < ci->height; j++)
|
||||
{
|
||||
for (i = 0; i < ci->width; i++)
|
||||
{
|
||||
int ofs = (by + j) * dw + bx + i;
|
||||
|
||||
ci->data[j*ci->width + i] = mask->bitmap[ofs] *
|
||||
(1 + bmap->bitmap[ofs]);
|
||||
}
|
||||
}
|
||||
|
||||
return ci;
|
||||
}
|
||||
|
||||
static void compose_cursors_from_fonts()
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
for (l = g_slist_copy (fonts); l; l = g_slist_delete_link (l,l))
|
||||
{
|
||||
font_info_t *fi = l->data;
|
||||
char *name;
|
||||
GSList *ml;
|
||||
|
||||
name = g_strconcat(fi->name, "_mask", NULL);
|
||||
|
||||
if ((ml = g_slist_find_custom(fonts, name,
|
||||
(GCompareFunc) font_info_compare)))
|
||||
{
|
||||
cursors = g_slist_append(cursors, gen_cursor(l->data, ml->data));
|
||||
fonts = g_slist_remove(fonts, l->data);
|
||||
fonts = g_slist_remove(fonts, ml->data);
|
||||
}
|
||||
|
||||
g_free(name);
|
||||
}
|
||||
}
|
||||
|
||||
static char *dump_cursor(ci, id)
|
||||
cursor_info_t *ci;
|
||||
int id;
|
||||
{
|
||||
static char cdata[8192];
|
||||
char *p;
|
||||
int i;
|
||||
int c;
|
||||
gboolean flushed;
|
||||
|
||||
sprintf(cdata, " { \"%s\", %d, %d, %d, %d, %d, \n \"",
|
||||
ci->name, ci->id, ci->width, ci->height, ci->hotx, ci->hoty);
|
||||
p = cdata + strlen(cdata);
|
||||
|
||||
for (i = 0; i < ci->width * ci->height; i++)
|
||||
{
|
||||
flushed = FALSE;
|
||||
|
||||
if (!(i%4))
|
||||
c = 0;
|
||||
|
||||
c = c << 2;
|
||||
|
||||
c += ci->data[i];
|
||||
|
||||
if ((i % 4) == 3)
|
||||
{
|
||||
flushed = TRUE;
|
||||
sprintf(p, "\\%03o", c);
|
||||
p += strlen(p);
|
||||
}
|
||||
|
||||
if (i > 0 && !(i % 64))
|
||||
{
|
||||
strcpy(p ,"\"\n \"");
|
||||
p += strlen(p);
|
||||
}
|
||||
}
|
||||
if (!flushed)
|
||||
{
|
||||
sprintf(p, "\\%03o", c);
|
||||
p += strlen(p);
|
||||
}
|
||||
|
||||
strcpy(p, "\" }");
|
||||
|
||||
return cdata;
|
||||
}
|
||||
|
||||
static int dump_cursors()
|
||||
{
|
||||
GSList *ptr;
|
||||
FILE *f = stdout;
|
||||
|
||||
fprintf(f, "static const struct { const char *name; int type; guchar width; guchar height; guchar hotx; guchar hoty; guchar *data; } cursors[] = {\n");
|
||||
|
||||
for (ptr = cursors; ptr; ptr = ptr->next)
|
||||
{
|
||||
if (debug)
|
||||
print_cursor(ptr->data);
|
||||
fprintf(f, "%s, \n", dump_cursor(ptr->data));
|
||||
}
|
||||
|
||||
fprintf(f, " { NULL, 0, 0, 0, 0, 0, NULL },\n};\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(argc, argv)
|
||||
int argc;
|
||||
char **argv;
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
printf("missing parameters !\n");
|
||||
printf("Usage: %s [BDF cursor file]\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (g_getenv ("BDFCURSOR_DEBUG") != NULL)
|
||||
debug = TRUE;
|
||||
|
||||
if (read_bdf_font(argv[1]) || !fonts)
|
||||
{
|
||||
printf("Error reading font\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
compose_cursors_from_fonts();
|
||||
|
||||
if (!cursors)
|
||||
{
|
||||
printf("failed to generate cursors from font!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
dump_cursors();
|
||||
|
||||
if (fonts)
|
||||
{
|
||||
printf("some fonts remained unconverted!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -157,7 +157,6 @@ static HKL latin_locale = NULL;
|
||||
|
||||
static gboolean in_ime_composition = FALSE;
|
||||
static UINT modal_timer;
|
||||
static UINT sync_timer = 0;
|
||||
|
||||
static int debug_indent = 0;
|
||||
|
||||
@@ -1453,23 +1452,6 @@ _gdk_win32_end_modal_call (GdkWin32ModalOpKind kind)
|
||||
}
|
||||
}
|
||||
|
||||
static VOID CALLBACK
|
||||
sync_timer_proc (HWND hwnd,
|
||||
UINT msg,
|
||||
UINT_PTR id,
|
||||
DWORD time)
|
||||
{
|
||||
MSG message;
|
||||
if (PeekMessageW (&message, hwnd, WM_PAINT, WM_PAINT, PM_REMOVE))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RedrawWindow (hwnd, NULL, NULL, RDW_INVALIDATE|RDW_UPDATENOW|RDW_ALLCHILDREN);
|
||||
|
||||
KillTimer (hwnd, sync_timer);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_nchittest (HWND hwnd,
|
||||
GdkSurface *window,
|
||||
@@ -2854,12 +2836,6 @@ gdk_event_translate (MSG *msg,
|
||||
*ret_valp = 1;
|
||||
break;
|
||||
|
||||
case WM_SYNCPAINT:
|
||||
sync_timer = SetTimer (GDK_SURFACE_HWND (window),
|
||||
1,
|
||||
200, sync_timer_proc);
|
||||
break;
|
||||
|
||||
case WM_PAINT:
|
||||
handle_wm_paint (msg, window);
|
||||
break;
|
||||
|
||||
+140
-140
@@ -653,6 +653,7 @@ _gdk_win32_display_create_surface (GdkDisplay *display,
|
||||
|
||||
g_object_unref (frame_clock);
|
||||
impl->hdc = GetDC (impl->handle);
|
||||
impl->inhibit_configure = TRUE;
|
||||
|
||||
return surface;
|
||||
}
|
||||
@@ -786,26 +787,6 @@ show_window_internal (GdkSurface *window,
|
||||
if (!unminimize && !already_mapped && IsWindowVisible (GDK_SURFACE_HWND (window)))
|
||||
return;
|
||||
|
||||
/* Other cases */
|
||||
|
||||
exstyle = GetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE);
|
||||
|
||||
/* Use SetWindowPos to show transparent windows so automatic redraws
|
||||
* in other windows can be suppressed.
|
||||
*/
|
||||
if (exstyle & WS_EX_TRANSPARENT)
|
||||
{
|
||||
UINT flags = SWP_SHOWWINDOW | SWP_NOREDRAW | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER;
|
||||
|
||||
if (GDK_IS_DRAG_SURFACE (window))
|
||||
flags |= SWP_NOACTIVATE;
|
||||
|
||||
SetWindowPos (GDK_SURFACE_HWND (window),
|
||||
SWP_NOZORDER_SPECIFIED, 0, 0, 0, 0, flags);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* For initial map of "normal" windows we want to emulate WM window
|
||||
* positioning behaviour, which means:
|
||||
* + default to the initial CW_USEDEFAULT placement,
|
||||
@@ -952,6 +933,8 @@ show_window_internal (GdkSurface *window,
|
||||
GtkShowWindow (window, SW_SHOW);
|
||||
}
|
||||
|
||||
exstyle = GetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE);
|
||||
|
||||
/* Sync STATE_ABOVE to TOPMOST */
|
||||
if (!GDK_IS_DRAG_SURFACE (window) &&
|
||||
(((window->state & GDK_TOPLEVEL_STATE_ABOVE) &&
|
||||
@@ -988,22 +971,7 @@ gdk_win32_surface_hide (GdkSurface *window)
|
||||
|
||||
_gdk_surface_clear_update_area (window);
|
||||
|
||||
if (GDK_IS_TOPLEVEL (window))
|
||||
ShowOwnedPopups (GDK_SURFACE_HWND (window), FALSE);
|
||||
|
||||
/* Use SetWindowPos to hide transparent windows so automatic redraws
|
||||
* in other windows can be suppressed.
|
||||
*/
|
||||
if (GetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE) & WS_EX_TRANSPARENT)
|
||||
{
|
||||
SetWindowPos (GDK_SURFACE_HWND (window), SWP_NOZORDER_SPECIFIED,
|
||||
0, 0, 0, 0,
|
||||
SWP_HIDEWINDOW | SWP_NOREDRAW | SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
GtkShowWindow (window, SW_HIDE);
|
||||
}
|
||||
GtkShowWindow (window, SW_HIDE);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1042,14 +1010,15 @@ gdk_win32_surface_do_move (GdkSurface *window,
|
||||
}
|
||||
|
||||
void
|
||||
gdk_win32_surface_resize (GdkSurface *window,
|
||||
int width, int height)
|
||||
gdk_win32_surface_resize (GdkSurface *surface,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
RECT outer_rect;
|
||||
|
||||
g_return_if_fail (GDK_IS_SURFACE (window));
|
||||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||||
|
||||
if (GDK_SURFACE_DESTROYED (window))
|
||||
if (GDK_SURFACE_DESTROYED (surface))
|
||||
return;
|
||||
|
||||
if (width < 1)
|
||||
@@ -1058,28 +1027,29 @@ gdk_win32_surface_resize (GdkSurface *window,
|
||||
height = 1;
|
||||
|
||||
GDK_NOTE (MISC, g_print ("gdk_win32_surface_resize: %p: %dx%d\n",
|
||||
GDK_SURFACE_HWND (window), width, height));
|
||||
GDK_SURFACE_HWND (surface), width, height));
|
||||
|
||||
if (window->state & GDK_TOPLEVEL_STATE_FULLSCREEN)
|
||||
if (surface->state & GDK_TOPLEVEL_STATE_FULLSCREEN)
|
||||
return;
|
||||
|
||||
get_outer_rect (window, width, height, &outer_rect);
|
||||
get_outer_rect (surface, width, height, &outer_rect);
|
||||
|
||||
GDK_NOTE (MISC, g_print ("... SetWindowPos(%p,NULL,0,0,%ld,%ld,"
|
||||
"NOACTIVATE|NOMOVE|NOZORDER)\n",
|
||||
GDK_SURFACE_HWND (window),
|
||||
GDK_SURFACE_HWND (surface),
|
||||
outer_rect.right - outer_rect.left,
|
||||
outer_rect.bottom - outer_rect.top));
|
||||
|
||||
API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window),
|
||||
API_CALL (SetWindowPos, (GDK_SURFACE_HWND (surface),
|
||||
SWP_NOZORDER_SPECIFIED,
|
||||
0, 0,
|
||||
outer_rect.right - outer_rect.left,
|
||||
outer_rect.bottom - outer_rect.top,
|
||||
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER));
|
||||
window->resize_count += 1;
|
||||
surface->resize_count += 1;
|
||||
|
||||
gdk_surface_request_layout (window);
|
||||
if (!GDK_WIN32_SURFACE (surface)->force_recompute_size)
|
||||
gdk_surface_request_layout (surface);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1333,33 +1303,22 @@ gdk_win32_surface_set_urgency_hint (GdkSurface *window,
|
||||
gboolean urgent)
|
||||
{
|
||||
FLASHWINFO flashwinfo;
|
||||
typedef BOOL (WINAPI *PFN_FlashWindowEx) (FLASHWINFO*);
|
||||
PFN_FlashWindowEx flashWindowEx = NULL;
|
||||
|
||||
g_return_if_fail (GDK_IS_SURFACE (window));
|
||||
|
||||
if (GDK_SURFACE_DESTROYED (window))
|
||||
return;
|
||||
|
||||
flashWindowEx = (PFN_FlashWindowEx) GetProcAddress (GetModuleHandle ("user32.dll"), "FlashWindowEx");
|
||||
|
||||
if (flashWindowEx)
|
||||
{
|
||||
flashwinfo.cbSize = sizeof (flashwinfo);
|
||||
flashwinfo.hwnd = GDK_SURFACE_HWND (window);
|
||||
if (urgent)
|
||||
flashwinfo.dwFlags = FLASHW_ALL | FLASHW_TIMER;
|
||||
else
|
||||
flashwinfo.dwFlags = FLASHW_STOP;
|
||||
flashwinfo.uCount = 0;
|
||||
flashwinfo.dwTimeout = 0;
|
||||
|
||||
flashWindowEx (&flashwinfo);
|
||||
}
|
||||
flashwinfo.cbSize = sizeof (flashwinfo);
|
||||
flashwinfo.hwnd = GDK_SURFACE_HWND (window);
|
||||
if (urgent)
|
||||
flashwinfo.dwFlags = FLASHW_ALL | FLASHW_TIMER;
|
||||
else
|
||||
{
|
||||
FlashWindow (GDK_SURFACE_HWND (window), urgent);
|
||||
}
|
||||
flashwinfo.dwFlags = FLASHW_STOP;
|
||||
flashwinfo.uCount = 0;
|
||||
flashwinfo.dwTimeout = 0;
|
||||
|
||||
FlashWindowEx (&flashwinfo);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -2495,35 +2454,41 @@ _gdk_win32_surface_handle_aerosnap (GdkSurface *window,
|
||||
}
|
||||
|
||||
static void
|
||||
apply_snap (GdkSurface *window,
|
||||
GdkWin32AeroSnapState snap)
|
||||
apply_snap (GdkSurface *surface,
|
||||
GdkWin32AeroSnapState snap)
|
||||
{
|
||||
GdkMonitor *monitor;
|
||||
GdkDisplay *display;
|
||||
|
||||
display = gdk_surface_get_display (window);
|
||||
monitor = gdk_display_get_monitor_at_surface (display, window);
|
||||
display = gdk_surface_get_display (surface);
|
||||
monitor = gdk_display_get_monitor_at_surface (display, surface);
|
||||
|
||||
switch (snap)
|
||||
{
|
||||
case GDK_WIN32_AEROSNAP_STATE_UNDETERMINED:
|
||||
break;
|
||||
case GDK_WIN32_AEROSNAP_STATE_MAXIMIZE:
|
||||
unsnap (window, monitor);
|
||||
gdk_win32_surface_maximize (window);
|
||||
unsnap (surface, monitor);
|
||||
gdk_win32_surface_maximize (surface);
|
||||
break;
|
||||
case GDK_WIN32_AEROSNAP_STATE_HALFLEFT:
|
||||
unsnap (window, monitor);
|
||||
snap_left (window, monitor, monitor);
|
||||
unsnap (surface, monitor);
|
||||
snap_left (surface, monitor, monitor);
|
||||
break;
|
||||
case GDK_WIN32_AEROSNAP_STATE_HALFRIGHT:
|
||||
unsnap (window, monitor);
|
||||
snap_right (window, monitor, monitor);
|
||||
unsnap (surface, monitor);
|
||||
snap_right (surface, monitor, monitor);
|
||||
break;
|
||||
case GDK_WIN32_AEROSNAP_STATE_FULLUP:
|
||||
snap_up (window);
|
||||
snap_up (surface);
|
||||
break;
|
||||
}
|
||||
|
||||
if (snap != GDK_WIN32_AEROSNAP_STATE_UNDETERMINED)
|
||||
{
|
||||
GDK_WIN32_SURFACE (surface)->inhibit_configure = TRUE;
|
||||
GDK_WIN32_SURFACE (surface)->force_recompute_size = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Registers a dumb window class. This window
|
||||
@@ -3403,10 +3368,10 @@ get_cursor_name_from_op (GdkW32WindowDragOp op,
|
||||
}
|
||||
|
||||
static void
|
||||
setup_drag_move_resize_context (GdkSurface *window,
|
||||
setup_drag_move_resize_context (GdkSurface *surface,
|
||||
GdkW32DragMoveResizeContext *context,
|
||||
GdkW32WindowDragOp op,
|
||||
GdkSurfaceEdge edge,
|
||||
GdkSurfaceEdge edge,
|
||||
GdkDevice *device,
|
||||
int button,
|
||||
double x,
|
||||
@@ -3415,12 +3380,13 @@ setup_drag_move_resize_context (GdkSurface *window,
|
||||
{
|
||||
RECT rect;
|
||||
const char *cursor_name;
|
||||
GdkSurface *pointer_window;
|
||||
GdkWin32Surface *impl = GDK_WIN32_SURFACE (window);
|
||||
gboolean maximized = gdk_toplevel_get_state (GDK_TOPLEVEL (window)) & GDK_TOPLEVEL_STATE_MAXIMIZED;
|
||||
GdkSurface *pointer_surface;
|
||||
GdkWin32Surface *impl = GDK_WIN32_SURFACE (surface);
|
||||
gboolean maximized = gdk_toplevel_get_state (GDK_TOPLEVEL (surface)) & GDK_TOPLEVEL_STATE_MAXIMIZED;
|
||||
int root_x, root_y;
|
||||
gboolean restore_configure = FALSE;
|
||||
|
||||
gdk_win32_surface_get_root_coords (window, x, y, &root_x, &root_y);
|
||||
gdk_win32_surface_get_root_coords (surface, x, y, &root_x, &root_y);
|
||||
|
||||
/* Before we drag, we need to undo any maximization or snapping.
|
||||
* AeroSnap behaviour:
|
||||
@@ -3443,7 +3409,7 @@ setup_drag_move_resize_context (GdkSurface *window,
|
||||
* resize
|
||||
* don't unsnap
|
||||
* apply new width and x position to unsnapped cache,
|
||||
* so that unsnapped window only regains its height
|
||||
* so that unsnapped surface only regains its height
|
||||
* and y position, but inherits x and width from
|
||||
* the fullup snapped state
|
||||
* vertical resize:
|
||||
@@ -3458,7 +3424,7 @@ setup_drag_move_resize_context (GdkSurface *window,
|
||||
*
|
||||
* TODO: make this implementation behave as AeroSnap on resizes?
|
||||
* There's also the case where
|
||||
* a halfleft/halfright window isn't unsnapped when it's
|
||||
* a halfleft/halfright surface isn't unsnapped when it's
|
||||
* being moved horizontally, but it's more difficult to implement.
|
||||
*/
|
||||
if (op == GDK_WIN32_DRAGOP_RESIZE &&
|
||||
@@ -3466,7 +3432,8 @@ setup_drag_move_resize_context (GdkSurface *window,
|
||||
impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFLEFT ||
|
||||
impl->snap_state == GDK_WIN32_AEROSNAP_STATE_FULLUP))
|
||||
{
|
||||
discard_snapinfo (window);
|
||||
discard_snapinfo (surface);
|
||||
restore_configure = TRUE;
|
||||
}
|
||||
else if (maximized ||
|
||||
(impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFRIGHT ||
|
||||
@@ -3476,24 +3443,25 @@ setup_drag_move_resize_context (GdkSurface *window,
|
||||
GdkMonitor *monitor;
|
||||
int wx, wy, wwidth, wheight;
|
||||
int swx, swy, swwidth, swheight;
|
||||
gboolean pointer_outside_of_window;
|
||||
gboolean pointer_outside_of_surface;
|
||||
int offsetx, offsety;
|
||||
gboolean left_half;
|
||||
GdkDisplay *display;
|
||||
|
||||
display = gdk_surface_get_display (window);
|
||||
monitor = gdk_display_get_monitor_at_surface (display, window);
|
||||
gdk_surface_get_geometry (window, &wx, &wy, &wwidth, &wheight);
|
||||
restore_configure = TRUE;
|
||||
display = gdk_surface_get_display (surface);
|
||||
monitor = gdk_display_get_monitor_at_surface (display, surface);
|
||||
gdk_surface_get_geometry (surface, &wx, &wy, &wwidth, &wheight);
|
||||
|
||||
swx = wx;
|
||||
swy = wy;
|
||||
swwidth = wwidth;
|
||||
swheight = wheight;
|
||||
|
||||
/* Subtract window shadow. We don't want pointer to go outside of
|
||||
* the visible window during drag-move. For drag-resize it's OK.
|
||||
* Don't take shadow into account if the window is maximized -
|
||||
* maximized windows don't have shadows.
|
||||
/* Subtract surface shadow. We don't want pointer to go outside of
|
||||
* the visible surface during drag-move. For drag-resize it's OK.
|
||||
* Don't take shadow into account if the surface is maximized -
|
||||
* maximized surfaces don't have shadows.
|
||||
*/
|
||||
if (op == GDK_WIN32_DRAGOP_MOVE && !maximized)
|
||||
{
|
||||
@@ -3503,16 +3471,16 @@ setup_drag_move_resize_context (GdkSurface *window,
|
||||
swheight -= impl->shadow_y;
|
||||
}
|
||||
|
||||
pointer_outside_of_window = root_x < swx || root_x > swx + swwidth ||
|
||||
pointer_outside_of_surface = root_x < swx || root_x > swx + swwidth ||
|
||||
root_y < swy || root_y > swy + swheight;
|
||||
/* Calculate the offset of the pointer relative to the window */
|
||||
/* Calculate the offset of the pointer relative to the surface */
|
||||
offsetx = root_x - swx;
|
||||
offsety = root_y - swy;
|
||||
|
||||
/* Figure out in which half of the window the pointer is.
|
||||
/* Figure out in which half of the surface the pointer is.
|
||||
* The code currently only concerns itself with horizontal
|
||||
* dimension (left/right halves).
|
||||
* There's no upper/lower half, because usually window
|
||||
* There's no upper/lower half, because usually surface
|
||||
* is dragged by its upper half anyway. If that changes, adjust
|
||||
* accordingly.
|
||||
*/
|
||||
@@ -3522,26 +3490,26 @@ setup_drag_move_resize_context (GdkSurface *window,
|
||||
if (!left_half)
|
||||
offsetx = swwidth - offsetx;
|
||||
|
||||
GDK_NOTE (MISC, g_print ("Pointer at %d : %d, this is %d : %d relative to the window's %s\n",
|
||||
GDK_NOTE (MISC, g_print ("Pointer at %d : %d, this is %d : %d relative to the surface's %s\n",
|
||||
root_x, root_y, offsetx, offsety,
|
||||
left_half ? "left half" : "right half"));
|
||||
|
||||
/* Move window in such a way that on unmaximization/unsnapping the pointer
|
||||
* is still pointing at the appropriate half of the window,
|
||||
/* Move surface in such a way that on unmaximization/unsnapping the pointer
|
||||
* is still pointing at the appropriate half of the surface,
|
||||
* with the same offset from the left or right edge. If the new
|
||||
* window size is too small, and adding that offset puts the pointer
|
||||
* surface size is too small, and adding that offset puts the pointer
|
||||
* into the other half or even beyond, move the pointer to the middle.
|
||||
*/
|
||||
if (!pointer_outside_of_window && maximized)
|
||||
if (!pointer_outside_of_surface && maximized)
|
||||
{
|
||||
WINDOWPLACEMENT placement;
|
||||
int unmax_width, unmax_height;
|
||||
int shadow_unmax_width, shadow_unmax_height;
|
||||
|
||||
placement.length = sizeof (placement);
|
||||
API_CALL (GetWindowPlacement, (GDK_SURFACE_HWND (window), &placement));
|
||||
API_CALL (GetWindowPlacement, (GDK_SURFACE_HWND (surface), &placement));
|
||||
|
||||
GDK_NOTE (MISC, g_print ("W32 WM unmaximized window placement is %ld x %ld @ %ld : %ld\n",
|
||||
GDK_NOTE (MISC, g_print ("W32 WM unmaximized surface placement is %ld x %ld @ %ld : %ld\n",
|
||||
placement.rcNormalPosition.right - placement.rcNormalPosition.left,
|
||||
placement.rcNormalPosition.bottom - placement.rcNormalPosition.top,
|
||||
placement.rcNormalPosition.left,
|
||||
@@ -3587,9 +3555,9 @@ setup_drag_move_resize_context (GdkSurface *window,
|
||||
placement.rcNormalPosition.left,
|
||||
placement.rcNormalPosition.top));
|
||||
|
||||
API_CALL (SetWindowPlacement, (GDK_SURFACE_HWND (window), &placement));
|
||||
API_CALL (SetWindowPlacement, (GDK_SURFACE_HWND (surface), &placement));
|
||||
}
|
||||
else if (!pointer_outside_of_window && impl->snap_stash_int)
|
||||
else if (!pointer_outside_of_surface && impl->snap_stash_int)
|
||||
{
|
||||
GdkRectangle new_pos;
|
||||
GdkRectangle snew_pos;
|
||||
@@ -3619,22 +3587,22 @@ setup_drag_move_resize_context (GdkSurface *window,
|
||||
new_pos.y = root_y - new_pos.height / 2;
|
||||
}
|
||||
|
||||
GDK_NOTE (MISC, g_print ("Unsnapped window to %d : %d\n",
|
||||
GDK_NOTE (MISC, g_print ("Unsnapped surface to %d : %d\n",
|
||||
new_pos.x, new_pos.y));
|
||||
discard_snapinfo (window);
|
||||
gdk_win32_surface_move_resize (window, new_pos.x, new_pos.y,
|
||||
discard_snapinfo (surface);
|
||||
gdk_win32_surface_move_resize (surface, new_pos.x, new_pos.y,
|
||||
new_pos.width, new_pos.height);
|
||||
}
|
||||
|
||||
|
||||
if (maximized)
|
||||
gdk_win32_surface_unmaximize (window);
|
||||
gdk_win32_surface_unmaximize (surface);
|
||||
else
|
||||
unsnap (window, monitor);
|
||||
unsnap (surface, monitor);
|
||||
|
||||
if (pointer_outside_of_window)
|
||||
if (pointer_outside_of_surface)
|
||||
{
|
||||
/* Pointer outside of the window, move pointer into window */
|
||||
/* Pointer outside of the surface, move pointer into surface */
|
||||
GDK_NOTE (MISC, g_print ("Pointer at %d : %d is outside of %d x %d @ %d : %d, move it to %d : %d\n",
|
||||
root_x, root_y, wwidth, wheight, wx, wy, wx + wwidth / 2, wy + wheight / 2));
|
||||
root_x = wx + wwidth / 2;
|
||||
@@ -3647,26 +3615,29 @@ setup_drag_move_resize_context (GdkSurface *window,
|
||||
}
|
||||
}
|
||||
|
||||
_gdk_win32_get_window_rect (window, &rect);
|
||||
if (restore_configure)
|
||||
impl->inhibit_configure = FALSE;
|
||||
|
||||
_gdk_win32_get_window_rect (surface, &rect);
|
||||
|
||||
cursor_name = get_cursor_name_from_op (op, edge);
|
||||
|
||||
context->cursor = gdk_cursor_new_from_name (cursor_name, NULL);
|
||||
|
||||
pointer_window = window;
|
||||
pointer_surface = surface;
|
||||
|
||||
/* Note: This triggers a WM_CAPTURECHANGED, which will trigger
|
||||
* gdk_win32_surface_end_move_resize_drag(), which will end
|
||||
* our op before it even begins, but only if context->op is not NONE.
|
||||
* This is why we first do the grab, *then* set the op.
|
||||
*/
|
||||
gdk_device_grab (device, pointer_window,
|
||||
gdk_device_grab (device, pointer_surface,
|
||||
FALSE,
|
||||
GDK_ALL_EVENTS_MASK,
|
||||
context->cursor,
|
||||
timestamp);
|
||||
|
||||
context->window = g_object_ref (window);
|
||||
context->window = g_object_ref (surface);
|
||||
context->op = op;
|
||||
context->edge = edge;
|
||||
context->device = device;
|
||||
@@ -3686,10 +3657,10 @@ setup_drag_move_resize_context (GdkSurface *window,
|
||||
calculate_aerosnap_regions (context);
|
||||
|
||||
GDK_NOTE (EVENTS,
|
||||
g_print ("begin drag moveresize: window %p, toplevel %p, "
|
||||
g_print ("begin drag moveresize: surface %p, toplevel %p, "
|
||||
"op %u, edge %d, device %p, "
|
||||
"button %d, coord %d:%d, time %u\n",
|
||||
pointer_window, window,
|
||||
pointer_surface, surface,
|
||||
context->op, context->edge, context->device,
|
||||
context->button, context->start_root_x,
|
||||
context->start_root_y, context->timestamp));
|
||||
@@ -4084,45 +4055,61 @@ gdk_win32_surface_minimize (GdkSurface *window)
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_win32_surface_maximize (GdkSurface *window)
|
||||
gdk_win32_surface_maximize (GdkSurface *surface)
|
||||
{
|
||||
g_return_if_fail (GDK_IS_SURFACE (window));
|
||||
GdkWin32Surface *impl;
|
||||
|
||||
if (GDK_SURFACE_DESTROYED (window))
|
||||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||||
|
||||
if (GDK_SURFACE_DESTROYED (surface))
|
||||
return;
|
||||
|
||||
GDK_NOTE (MISC, g_print ("gdk_surface_maximize: %p: %s\n",
|
||||
GDK_SURFACE_HWND (window),
|
||||
_gdk_win32_surface_state_to_string (window->state)));
|
||||
GDK_SURFACE_HWND (surface),
|
||||
_gdk_win32_surface_state_to_string (surface->state)));
|
||||
|
||||
if (GDK_SURFACE_IS_MAPPED (window))
|
||||
GtkShowWindow (window, SW_MAXIMIZE);
|
||||
impl = GDK_WIN32_SURFACE (surface);
|
||||
impl->inhibit_configure = TRUE;
|
||||
impl->force_recompute_size = FALSE;
|
||||
|
||||
if (GDK_SURFACE_IS_MAPPED (surface))
|
||||
GtkShowWindow (surface, SW_MAXIMIZE);
|
||||
else
|
||||
gdk_synthesize_surface_state (window,
|
||||
gdk_synthesize_surface_state (surface,
|
||||
0,
|
||||
GDK_TOPLEVEL_STATE_MAXIMIZED);
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_win32_surface_unmaximize (GdkSurface *window)
|
||||
gdk_win32_surface_unmaximize (GdkSurface *surface)
|
||||
{
|
||||
g_return_if_fail (GDK_IS_SURFACE (window));
|
||||
GdkWin32Surface *impl;
|
||||
|
||||
if (GDK_SURFACE_DESTROYED (window))
|
||||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||||
|
||||
if (GDK_SURFACE_DESTROYED (surface))
|
||||
return;
|
||||
|
||||
GDK_NOTE (MISC, g_print ("gdk_surface_unmaximize: %p: %s\n",
|
||||
GDK_SURFACE_HWND (window),
|
||||
_gdk_win32_surface_state_to_string (window->state)));
|
||||
GDK_SURFACE_HWND (surface),
|
||||
_gdk_win32_surface_state_to_string (surface->state)));
|
||||
|
||||
_gdk_win32_surface_invalidate_egl_framebuffer (window);
|
||||
_gdk_win32_surface_invalidate_egl_framebuffer (surface);
|
||||
|
||||
if (GDK_SURFACE_IS_MAPPED (window))
|
||||
GtkShowWindow (window, SW_RESTORE);
|
||||
if (GDK_SURFACE_IS_MAPPED (surface))
|
||||
GtkShowWindow (surface, SW_RESTORE);
|
||||
else
|
||||
gdk_synthesize_surface_state (window,
|
||||
gdk_synthesize_surface_state (surface,
|
||||
GDK_TOPLEVEL_STATE_MAXIMIZED,
|
||||
0);
|
||||
|
||||
impl = GDK_WIN32_SURFACE (surface);
|
||||
|
||||
if (impl->inhibit_configure)
|
||||
{
|
||||
impl->inhibit_configure = FALSE;
|
||||
impl->force_recompute_size = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -4547,6 +4534,9 @@ _gdk_win32_surface_request_layout (GdkSurface *surface)
|
||||
&surface->x, &surface->y,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
if (!impl->inhibit_configure)
|
||||
impl->force_recompute_size = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4561,8 +4551,18 @@ _gdk_win32_surface_compute_size (GdkSurface *surface)
|
||||
|
||||
if (!impl->drag_move_resize_context.native_move_resize_pending)
|
||||
{
|
||||
surface->width = impl->next_layout.configured_width;
|
||||
surface->height = impl->next_layout.configured_height;
|
||||
if (GDK_IS_TOPLEVEL (surface) && impl->force_recompute_size)
|
||||
{
|
||||
surface->width = width;
|
||||
surface->height = height;
|
||||
gdk_win32_surface_resize (surface, width, height);
|
||||
impl->force_recompute_size = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
surface->width = impl->next_layout.configured_width;
|
||||
surface->height = impl->next_layout.configured_height;
|
||||
}
|
||||
|
||||
_gdk_surface_update_size (surface);
|
||||
}
|
||||
|
||||
@@ -337,6 +337,7 @@ struct _GdkWin32Surface
|
||||
int configured_height;
|
||||
RECT configured_rect;
|
||||
} next_layout;
|
||||
gboolean force_recompute_size;
|
||||
|
||||
#ifdef HAVE_EGL
|
||||
guint egl_force_redraw_all : 1;
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
# libwntab32x.la - a libtool library file
|
||||
# Generated by hand, compatible with libtool
|
||||
# Just a wrapper for libwntab32x.a, which is just a copy of wntab32x.lib
|
||||
#
|
||||
# The name that we can dlopen(3).
|
||||
dlname=''
|
||||
|
||||
# Names of this library.
|
||||
library_names=''
|
||||
|
||||
# The name of the static archive.
|
||||
old_library='libwntab32x.a'
|
||||
|
||||
# Libraries that this one depends upon.
|
||||
dependency_libs=''
|
||||
|
||||
# Version information
|
||||
current=0
|
||||
age=0
|
||||
revision=0
|
||||
|
||||
# Is this an already installed library?
|
||||
installed=no
|
||||
|
||||
# Files to dlopen/dlpreopen
|
||||
dlopen=''
|
||||
dlpreopen=''
|
||||
|
||||
# Directory that this library needs to be installed in:
|
||||
libdir=''
|
||||
@@ -76,11 +76,30 @@ print_atoms (GdkX11Clipboard *cb,
|
||||
});
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_x11_clipboard_default_output_closed (GObject *stream,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!g_output_stream_close_finish (G_OUTPUT_STREAM (stream), result, &error))
|
||||
{
|
||||
GDK_NOTE (CLIPBOARD,
|
||||
g_printerr ("-------: failed to close stream: %s\n",
|
||||
error->message));
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
g_object_unref (stream);
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_x11_clipboard_default_output_done (GObject *clipboard,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
GOutputStream *stream = user_data;
|
||||
GError *error = NULL;
|
||||
|
||||
if (!gdk_clipboard_write_finish (GDK_CLIPBOARD (clipboard), result, &error))
|
||||
@@ -90,6 +109,12 @@ gdk_x11_clipboard_default_output_done (GObject *clipboard,
|
||||
GDK_X11_CLIPBOARD (clipboard)->selection, error->message));
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
g_output_stream_close_async (stream,
|
||||
G_PRIORITY_DEFAULT,
|
||||
NULL,
|
||||
gdk_x11_clipboard_default_output_closed,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -103,8 +128,7 @@ gdk_x11_clipboard_default_output_handler (GOutputStream *stream,
|
||||
G_PRIORITY_DEFAULT,
|
||||
NULL,
|
||||
gdk_x11_clipboard_default_output_done,
|
||||
NULL);
|
||||
g_object_unref (stream);
|
||||
stream);
|
||||
}
|
||||
|
||||
static GInputStream *
|
||||
|
||||
@@ -1665,7 +1665,7 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
|
||||
_gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group),
|
||||
direction,
|
||||
FALSE);
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1799,7 +1799,7 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
|
||||
_gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group),
|
||||
x, y,
|
||||
axes);
|
||||
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1838,7 +1838,7 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
|
||||
|
||||
x = (double) xev->event_x / scale;
|
||||
y = (double) xev->event_y / scale;
|
||||
|
||||
|
||||
event = gdk_touch_event_new (ev->evtype == XI_TouchBegin
|
||||
? GDK_TOUCH_BEGIN
|
||||
: GDK_TOUCH_END,
|
||||
@@ -1946,6 +1946,7 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
|
||||
y = (double) xev->event_y / scale;
|
||||
|
||||
event = gdk_touchpad_event_new_pinch (surface,
|
||||
NULL, /* FIXME make up sequences */
|
||||
device,
|
||||
xev->time,
|
||||
state,
|
||||
@@ -2006,6 +2007,7 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator,
|
||||
y = (double) xev->event_y / scale;
|
||||
|
||||
event = gdk_touchpad_event_new_swipe (surface,
|
||||
NULL, /* FIXME make up sequences */
|
||||
device,
|
||||
xev->time,
|
||||
state,
|
||||
|
||||
+26
-2
@@ -1612,11 +1612,30 @@ gdk_x11_drag_set_hotspot (GdkDrag *drag,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_x11_drag_default_output_closed (GObject *stream,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!g_output_stream_close_finish (G_OUTPUT_STREAM (stream), result, &error))
|
||||
{
|
||||
GDK_NOTE (DND,
|
||||
g_printerr ("failed to close stream: %s\n",
|
||||
error->message));
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
g_object_unref (stream);
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_x11_drag_default_output_done (GObject *drag,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
GOutputStream *stream = user_data;
|
||||
GError *error = NULL;
|
||||
|
||||
if (!gdk_drag_write_finish (GDK_DRAG (drag), result, &error))
|
||||
@@ -1624,6 +1643,12 @@ gdk_x11_drag_default_output_done (GObject *drag,
|
||||
GDK_DISPLAY_NOTE (gdk_drag_get_display (GDK_DRAG (drag)), DND, g_printerr ("failed to write stream: %s\n", error->message));
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
g_output_stream_close_async (stream,
|
||||
G_PRIORITY_DEFAULT,
|
||||
NULL,
|
||||
gdk_x11_drag_default_output_closed,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1637,8 +1662,7 @@ gdk_x11_drag_default_output_handler (GOutputStream *stream,
|
||||
G_PRIORITY_DEFAULT,
|
||||
NULL,
|
||||
gdk_x11_drag_default_output_done,
|
||||
NULL);
|
||||
g_object_unref (stream);
|
||||
stream);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
||||
@@ -71,6 +71,8 @@ gdk_x11_surface_destroy_glx_drawable (GdkX11Surface *self)
|
||||
if (self->glx_drawable == None)
|
||||
return;
|
||||
|
||||
gdk_gl_context_clear_current_if_surface (GDK_SURFACE (self));
|
||||
|
||||
glXDestroyWindow (gdk_x11_display_get_xdisplay (gdk_surface_get_display (GDK_SURFACE (self))),
|
||||
self->glx_drawable);
|
||||
|
||||
|
||||
@@ -57,7 +57,8 @@ struct _GdkX11SelectionOutputStreamPrivate {
|
||||
GTask *pending_task;
|
||||
|
||||
guint incr : 1;
|
||||
guint delete_pending : 1;
|
||||
guint sent_end_of_stream : 1;
|
||||
guint delete_pending : 1; /* owns a reference */
|
||||
};
|
||||
|
||||
struct _GdkX11PendingSelectionNotify
|
||||
@@ -176,12 +177,16 @@ gdk_x11_selection_output_stream_needs_flush_unlocked (GdkX11SelectionOutputStrea
|
||||
{
|
||||
GdkX11SelectionOutputStreamPrivate *priv = gdk_x11_selection_output_stream_get_instance_private (stream);
|
||||
|
||||
if (priv->data->len == 0 && priv->notify == NULL)
|
||||
if (priv->sent_end_of_stream)
|
||||
return FALSE;
|
||||
|
||||
if (g_output_stream_is_closing (G_OUTPUT_STREAM (stream)))
|
||||
if (g_output_stream_is_closing (G_OUTPUT_STREAM (stream)) ||
|
||||
g_output_stream_is_closed (G_OUTPUT_STREAM (stream)))
|
||||
return TRUE;
|
||||
|
||||
if (priv->data->len == 0 && priv->notify == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (priv->flush_requested)
|
||||
return TRUE;
|
||||
|
||||
@@ -284,6 +289,8 @@ gdk_x11_selection_output_stream_perform_flush (GdkX11SelectionOutputStream *stre
|
||||
g_byte_array_remove_range (priv->data, 0, n_elements * element_size);
|
||||
if (priv->data->len < element_size)
|
||||
priv->flush_requested = FALSE;
|
||||
if (!priv->incr || n_elements == 0)
|
||||
priv->sent_end_of_stream = TRUE;
|
||||
}
|
||||
|
||||
if (priv->notify)
|
||||
@@ -292,6 +299,7 @@ gdk_x11_selection_output_stream_perform_flush (GdkX11SelectionOutputStream *stre
|
||||
priv->notify = NULL;
|
||||
}
|
||||
|
||||
g_object_ref (stream);
|
||||
priv->delete_pending = TRUE;
|
||||
g_cond_broadcast (&priv->cond);
|
||||
g_mutex_unlock (&priv->mutex);
|
||||
@@ -494,60 +502,6 @@ gdk_x11_selection_output_stream_flush_finish (GOutputStream *stream,
|
||||
return g_task_propagate_boolean (G_TASK (result), error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gdk_x11_selection_output_stream_invoke_close (gpointer stream)
|
||||
{
|
||||
GdkX11SelectionOutputStreamPrivate *priv = gdk_x11_selection_output_stream_get_instance_private (stream);
|
||||
|
||||
GDK_X11_DISPLAY (priv->display)->streams = g_slist_remove (GDK_X11_DISPLAY (priv->display)->streams, stream);
|
||||
g_signal_handlers_disconnect_by_func (priv->display,
|
||||
gdk_x11_selection_output_stream_xevent,
|
||||
stream);
|
||||
g_object_unref (stream);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gdk_x11_selection_output_stream_close (GOutputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_main_context_invoke (NULL, gdk_x11_selection_output_stream_invoke_close, g_object_ref (stream));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_x11_selection_output_stream_close_async (GOutputStream *stream,
|
||||
int io_priority,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
|
||||
task = g_task_new (stream, cancellable, callback, user_data);
|
||||
g_task_set_source_tag (task, gdk_x11_selection_output_stream_close_async);
|
||||
g_task_set_priority (task, io_priority);
|
||||
|
||||
gdk_x11_selection_output_stream_invoke_close (stream);
|
||||
g_task_return_boolean (task, TRUE);
|
||||
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gdk_x11_selection_output_stream_close_finish (GOutputStream *stream,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (g_task_is_valid (result, stream), FALSE);
|
||||
g_return_val_if_fail (g_async_result_is_tagged (result, gdk_x11_selection_output_stream_close_async), FALSE);
|
||||
|
||||
return g_task_propagate_boolean (G_TASK (result), error);
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_x11_selection_output_stream_finalize (GObject *object)
|
||||
{
|
||||
@@ -557,6 +511,13 @@ gdk_x11_selection_output_stream_finalize (GObject *object)
|
||||
/* not sending a notify is terrible */
|
||||
g_assert (priv->notify == NULL);
|
||||
|
||||
GDK_DISPLAY_NOTE (priv->display, SELECTION, g_printerr ("%s:%s: finalizing\n",
|
||||
priv->selection, priv->target));
|
||||
GDK_X11_DISPLAY (priv->display)->streams = g_slist_remove (GDK_X11_DISPLAY (priv->display)->streams, stream);
|
||||
g_signal_handlers_disconnect_by_func (priv->display,
|
||||
gdk_x11_selection_output_stream_xevent,
|
||||
stream);
|
||||
|
||||
g_byte_array_unref (priv->data);
|
||||
g_cond_clear (&priv->cond);
|
||||
g_mutex_clear (&priv->mutex);
|
||||
@@ -579,14 +540,11 @@ gdk_x11_selection_output_stream_class_init (GdkX11SelectionOutputStreamClass *kl
|
||||
|
||||
output_stream_class->write_fn = gdk_x11_selection_output_stream_write;
|
||||
output_stream_class->flush = gdk_x11_selection_output_stream_flush;
|
||||
output_stream_class->close_fn = gdk_x11_selection_output_stream_close;
|
||||
|
||||
output_stream_class->write_async = gdk_x11_selection_output_stream_write_async;
|
||||
output_stream_class->write_finish = gdk_x11_selection_output_stream_write_finish;
|
||||
output_stream_class->flush_async = gdk_x11_selection_output_stream_flush_async;
|
||||
output_stream_class->flush_finish = gdk_x11_selection_output_stream_flush_finish;
|
||||
output_stream_class->close_async = gdk_x11_selection_output_stream_close_async;
|
||||
output_stream_class->close_finish = gdk_x11_selection_output_stream_close_finish;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -628,6 +586,7 @@ gdk_x11_selection_output_stream_xevent (GdkDisplay *display,
|
||||
if (gdk_x11_selection_output_stream_needs_flush (stream) &&
|
||||
gdk_x11_selection_output_stream_can_flush (stream))
|
||||
gdk_x11_selection_output_stream_perform_flush (stream);
|
||||
g_object_unref (stream); /* from unsetting the delete_pending */
|
||||
return FALSE;
|
||||
|
||||
default:
|
||||
|
||||
@@ -3535,7 +3535,7 @@ gdk_x11_surface_unfullscreen (GdkSurface *surface)
|
||||
*
|
||||
* Returns the group this surface belongs to.
|
||||
*
|
||||
* Returns: (transfer none): The group of this surface;
|
||||
* Returns: (transfer none) (nullable): The group of this surface;
|
||||
*/
|
||||
GdkSurface *
|
||||
gdk_x11_surface_get_group (GdkSurface *surface)
|
||||
|
||||
@@ -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 */
|
||||
}
|
||||
|
||||
@@ -952,9 +952,9 @@ gsk_gl_command_queue_execute (GskGLCommandQueue *self,
|
||||
guint program = 0;
|
||||
guint width = 0;
|
||||
guint height = 0;
|
||||
guint n_binds = 0;
|
||||
G_GNUC_UNUSED guint n_binds = 0;
|
||||
guint n_fbos = 0;
|
||||
guint n_uniforms = 0;
|
||||
G_GNUC_UNUSED guint n_uniforms = 0;
|
||||
guint n_programs = 0;
|
||||
guint vao_id;
|
||||
guint vbo_id;
|
||||
@@ -1396,7 +1396,7 @@ gsk_gl_command_queue_do_upload_texture (GskGLCommandQueue *self,
|
||||
glTexImage2D (GL_TEXTURE_2D, 0, gl_internalformat, width, height, 0, gl_format, gl_type, data);
|
||||
}
|
||||
else if (stride % bpp == 0 &&
|
||||
(!use_es || gdk_gl_context_check_version (context, 3, 0) || gdk_gl_context_has_unpack_subimage (context)))
|
||||
(gdk_gl_context_check_version (context, 0, 0, 3, 0) || gdk_gl_context_has_unpack_subimage (context)))
|
||||
{
|
||||
glPixelStorei (GL_UNPACK_ROW_LENGTH, stride / bpp);
|
||||
|
||||
|
||||
@@ -147,7 +147,8 @@ gsk_gl_compiler_new (GskGLDriver *driver,
|
||||
|
||||
gdk_gl_context_get_version (context, &maj, &min);
|
||||
|
||||
if (maj == 3)
|
||||
/* On Windows, legacy contexts can give us a GL 4.x context */
|
||||
if (maj >= 3)
|
||||
self->glsl_version = SHADER_VERSION_GL3_LEGACY;
|
||||
else
|
||||
self->glsl_version = SHADER_VERSION_GL2_LEGACY;
|
||||
|
||||
+74
-23
@@ -245,6 +245,47 @@ gsk_rounded_rect_shrink_to_minimum (GskRoundedRect *self)
|
||||
self->corner[1].height + self->corner[2].height);
|
||||
}
|
||||
|
||||
static inline gboolean G_GNUC_PURE
|
||||
node_supports_2d_transform (const GskRenderNode *node)
|
||||
{
|
||||
switch ((int)gsk_render_node_get_node_type (node))
|
||||
{
|
||||
case GSK_COLOR_NODE:
|
||||
case GSK_OPACITY_NODE:
|
||||
case GSK_COLOR_MATRIX_NODE:
|
||||
case GSK_TEXTURE_NODE:
|
||||
case GSK_CROSS_FADE_NODE:
|
||||
case GSK_LINEAR_GRADIENT_NODE:
|
||||
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
|
||||
case GSK_CONIC_GRADIENT_NODE:
|
||||
case GSK_RADIAL_GRADIENT_NODE:
|
||||
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
|
||||
case GSK_DEBUG_NODE:
|
||||
case GSK_TEXT_NODE:
|
||||
case GSK_CAIRO_NODE:
|
||||
case GSK_BLEND_NODE:
|
||||
case GSK_BLUR_NODE:
|
||||
return TRUE;
|
||||
|
||||
case GSK_SHADOW_NODE:
|
||||
return node_supports_2d_transform (gsk_shadow_node_get_child (node));
|
||||
|
||||
case GSK_TRANSFORM_NODE:
|
||||
return node_supports_2d_transform (gsk_transform_node_get_child (node));
|
||||
|
||||
case GSK_CONTAINER_NODE:
|
||||
for (guint i = 0, p = gsk_container_node_get_n_children (node); i < p; i++)
|
||||
{
|
||||
if (!node_supports_2d_transform (gsk_container_node_get_child (node, i)))
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static inline gboolean G_GNUC_PURE
|
||||
node_supports_transform (const GskRenderNode *node)
|
||||
{
|
||||
@@ -257,24 +298,26 @@ node_supports_transform (const GskRenderNode *node)
|
||||
|
||||
switch ((int)gsk_render_node_get_node_type (node))
|
||||
{
|
||||
case GSK_COLOR_NODE:
|
||||
case GSK_OPACITY_NODE:
|
||||
case GSK_COLOR_MATRIX_NODE:
|
||||
case GSK_TEXTURE_NODE:
|
||||
case GSK_CROSS_FADE_NODE:
|
||||
case GSK_LINEAR_GRADIENT_NODE:
|
||||
case GSK_DEBUG_NODE:
|
||||
case GSK_TEXT_NODE:
|
||||
return TRUE;
|
||||
case GSK_COLOR_NODE:
|
||||
case GSK_OPACITY_NODE:
|
||||
case GSK_COLOR_MATRIX_NODE:
|
||||
case GSK_TEXTURE_NODE:
|
||||
case GSK_CROSS_FADE_NODE:
|
||||
case GSK_DEBUG_NODE:
|
||||
case GSK_TEXT_NODE:
|
||||
case GSK_CAIRO_NODE:
|
||||
case GSK_BLEND_NODE:
|
||||
case GSK_BLUR_NODE:
|
||||
return TRUE;
|
||||
|
||||
case GSK_SHADOW_NODE:
|
||||
return node_supports_transform (gsk_shadow_node_get_child (node));
|
||||
case GSK_SHADOW_NODE:
|
||||
return node_supports_transform (gsk_shadow_node_get_child (node));
|
||||
|
||||
case GSK_TRANSFORM_NODE:
|
||||
return node_supports_transform (gsk_transform_node_get_child (node));
|
||||
case GSK_TRANSFORM_NODE:
|
||||
return node_supports_transform (gsk_transform_node_get_child (node));
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2017,6 +2060,14 @@ gsk_gl_render_job_visit_transform_node (GskGLRenderJob *job,
|
||||
break;
|
||||
|
||||
case GSK_TRANSFORM_CATEGORY_2D:
|
||||
if (node_supports_2d_transform (child))
|
||||
{
|
||||
gsk_gl_render_job_push_modelview (job, transform);
|
||||
gsk_gl_render_job_visit_node (job, child);
|
||||
gsk_gl_render_job_pop_modelview (job);
|
||||
return;
|
||||
}
|
||||
G_GNUC_FALLTHROUGH;
|
||||
case GSK_TRANSFORM_CATEGORY_3D:
|
||||
case GSK_TRANSFORM_CATEGORY_ANY:
|
||||
case GSK_TRANSFORM_CATEGORY_UNKNOWN:
|
||||
@@ -3767,11 +3818,6 @@ gsk_gl_render_job_visit_node (GskGLRenderJob *job,
|
||||
gsk_gl_render_job_visit_as_fallback (job, node);
|
||||
break;
|
||||
|
||||
case GSK_FILL_NODE:
|
||||
case GSK_STROKE_NODE:
|
||||
gsk_gl_render_job_visit_as_fallback (job, node);
|
||||
break;
|
||||
|
||||
case GSK_NOT_A_RENDER_NODE:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
@@ -4071,14 +4117,19 @@ gsk_gl_render_job_set_debug_fallback (GskGLRenderJob *job,
|
||||
}
|
||||
|
||||
static int
|
||||
get_framebuffer_format (guint framebuffer)
|
||||
get_framebuffer_format (GdkGLContext *context,
|
||||
guint framebuffer)
|
||||
{
|
||||
int size;
|
||||
|
||||
if (!gdk_gl_context_check_version (context, 0, 0, 3, 0))
|
||||
return GL_RGBA8;
|
||||
|
||||
glBindFramebuffer (GL_FRAMEBUFFER, framebuffer);
|
||||
glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER,
|
||||
framebuffer ? GL_COLOR_ATTACHMENT0
|
||||
: GL_BACK_LEFT,
|
||||
: gdk_gl_context_get_use_es (context) ? GL_BACK
|
||||
: GL_BACK_LEFT,
|
||||
GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE, &size);
|
||||
|
||||
if (size > 16)
|
||||
@@ -4115,7 +4166,7 @@ gsk_gl_render_job_new (GskGLDriver *driver,
|
||||
job->scale_x = scale_factor;
|
||||
job->scale_y = scale_factor;
|
||||
job->viewport = *viewport;
|
||||
job->target_format = get_framebuffer_format (framebuffer);
|
||||
job->target_format = get_framebuffer_format (job->command_queue->context, framebuffer);
|
||||
|
||||
gsk_gl_render_job_set_alpha (job, 1.0f);
|
||||
gsk_gl_render_job_set_projection_from_rect (job, viewport, NULL);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskboundingboxprivate.h"
|
||||
|
||||
GskBoundingBox *
|
||||
gsk_bounding_box_init (GskBoundingBox *self,
|
||||
const graphene_point_t *a,
|
||||
const graphene_point_t *b)
|
||||
{
|
||||
self->min.x = MIN (a->x, b->x);
|
||||
self->min.y = MIN (a->y, b->y);
|
||||
self->max.x = MAX (a->x, b->x);
|
||||
self->max.y = MAX (a->y, b->y);
|
||||
return self;
|
||||
}
|
||||
|
||||
GskBoundingBox *
|
||||
gsk_bounding_box_init_copy (GskBoundingBox *self,
|
||||
const GskBoundingBox *src)
|
||||
{
|
||||
self->min = src->min;
|
||||
self->max = src->max;
|
||||
return self;
|
||||
}
|
||||
|
||||
GskBoundingBox *
|
||||
gsk_bounding_box_init_from_rect (GskBoundingBox *self,
|
||||
const graphene_rect_t *bounds)
|
||||
{
|
||||
self->min = bounds->origin;
|
||||
self->max.x = bounds->origin.x + bounds->size.width;
|
||||
self->max.y = bounds->origin.y + bounds->size.height;
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_bounding_box_expand (GskBoundingBox *self,
|
||||
const graphene_point_t *p)
|
||||
{
|
||||
self->min.x = MIN (self->min.x, p->x);
|
||||
self->min.y = MIN (self->min.y, p->y);
|
||||
self->max.x = MAX (self->max.x, p->x);
|
||||
self->max.y = MAX (self->max.y, p->y);
|
||||
}
|
||||
|
||||
graphene_rect_t *
|
||||
gsk_bounding_box_to_rect (const GskBoundingBox *self,
|
||||
graphene_rect_t *rect)
|
||||
{
|
||||
rect->origin = self->min;
|
||||
rect->size.width = self->max.x - self->min.x;
|
||||
rect->size.height = self->max.y - self->min.y;
|
||||
return rect;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gsk_bounding_box_contains_point (const GskBoundingBox *self,
|
||||
const graphene_point_t *p)
|
||||
{
|
||||
return self->min.x <= p->x && p->x <= self->max.x &&
|
||||
self->min.y <= p->y && p->y <= self->max.y;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gsk_bounding_box_intersection (const GskBoundingBox *a,
|
||||
const GskBoundingBox *b,
|
||||
GskBoundingBox *res)
|
||||
{
|
||||
graphene_point_t min, max;
|
||||
|
||||
min.x = MAX (a->min.x, b->min.x);
|
||||
min.y = MAX (a->min.y, b->min.y);
|
||||
max.x = MIN (a->max.x, b->max.x);
|
||||
max.y = MIN (a->max.y, b->max.y);
|
||||
|
||||
if (res)
|
||||
gsk_bounding_box_init (res, &min, &max);
|
||||
|
||||
return min.x <= max.x && min.y <= max.y;
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <gsk/gsktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GskBoundingBox GskBoundingBox;
|
||||
|
||||
struct _GskBoundingBox {
|
||||
graphene_point_t min;
|
||||
graphene_point_t max;
|
||||
};
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskBoundingBox * gsk_bounding_box_init (GskBoundingBox *self,
|
||||
const graphene_point_t *a,
|
||||
const graphene_point_t *b);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskBoundingBox * gsk_bounding_box_init_copy (GskBoundingBox *self,
|
||||
const GskBoundingBox *src);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskBoundingBox * gsk_bounding_box_init_from_rect (GskBoundingBox *self,
|
||||
const graphene_rect_t *bounds);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_bounding_box_expand (GskBoundingBox *self,
|
||||
const graphene_point_t *p);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
graphene_rect_t *gsk_bounding_box_to_rect (const GskBoundingBox *self,
|
||||
graphene_rect_t *rect);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gsk_bounding_box_contains_point (const GskBoundingBox *self,
|
||||
const graphene_point_t *p);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gsk_bounding_box_intersection (const GskBoundingBox *a,
|
||||
const GskBoundingBox *b,
|
||||
GskBoundingBox *res);
|
||||
|
||||
G_END_DECLS
|
||||
-2099
File diff suppressed because it is too large
Load Diff
@@ -1,132 +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
|
||||
|
||||
/* A path is flat if it contains no cubic or conic segments.
|
||||
* A path is closed if all its contours end with a GSK_PATH_CLOSE
|
||||
* operation.
|
||||
*/
|
||||
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);
|
||||
|
||||
GskContour * gsk_contour_reverse (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);
|
||||
float gsk_contour_get_curvature (const GskContour *self,
|
||||
gpointer measure_data,
|
||||
float distance,
|
||||
graphene_point_t *center);
|
||||
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);
|
||||
|
||||
void gsk_contour_offset (const GskContour *contour,
|
||||
GskPathBuilder *builder,
|
||||
float distance,
|
||||
GskLineJoin line_join,
|
||||
float miter_limit);
|
||||
void gsk_contour_default_offset (const GskContour *contour,
|
||||
GskPathBuilder *builder,
|
||||
float distance,
|
||||
GskLineJoin line_join,
|
||||
float miter_limit);
|
||||
|
||||
gboolean gsk_contour_is_convex (const GskContour *contour);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_CONTOUR_PRIVATE_H__ */
|
||||
|
||||
@@ -1,286 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskconvexityprivate.h"
|
||||
#include "gskcontourprivate.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GSK_DIR_CHANGE_UNKNOWN,
|
||||
GSK_DIR_CHANGE_LEFT,
|
||||
GSK_DIR_CHANGE_RIGHT,
|
||||
GSK_DIR_CHANGE_STRAIGHT,
|
||||
GSK_DIR_CHANGE_REVERSE,
|
||||
GSK_DIR_CHANGE_INVALID,
|
||||
} GskDirChange;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GSK_PATH_DIRECTION_UNKNOWN,
|
||||
GSK_PATH_DIRECTION_CLOCKWISE,
|
||||
GSK_PATH_DIRECTION_COUNTERCLOCKWISE,
|
||||
} GskPathDirection;
|
||||
|
||||
typedef struct _GskConvexityChecker GskConvexityChecker;
|
||||
struct _GskConvexityChecker
|
||||
{
|
||||
graphene_point_t first_point;
|
||||
graphene_vec2_t first_vec;
|
||||
graphene_point_t last_point;
|
||||
graphene_vec2_t last_vec;
|
||||
GskDirChange expected_direction;
|
||||
GskPathDirection first_direction;
|
||||
int reversals;
|
||||
gboolean finite;
|
||||
GskConvexity convexity;
|
||||
int xsign;
|
||||
int ysign;
|
||||
int dx;
|
||||
int dy;
|
||||
};
|
||||
|
||||
static float
|
||||
cross_product (graphene_vec2_t *a,
|
||||
graphene_vec2_t *b)
|
||||
{
|
||||
float fa[2];
|
||||
float fb[2];
|
||||
graphene_vec2_to_float (a, fa);
|
||||
graphene_vec2_to_float (b, fb);
|
||||
return fa[0] * fb[1] - fa[1] * fb[0];
|
||||
}
|
||||
|
||||
static GskDirChange
|
||||
direction_change (GskConvexityChecker *c,
|
||||
graphene_vec2_t *v)
|
||||
{
|
||||
float cross = cross_product (&(c->last_vec), v);
|
||||
if (!finite (cross))
|
||||
return GSK_DIR_CHANGE_UNKNOWN;
|
||||
|
||||
if (cross == 0)
|
||||
return graphene_vec2_dot (&(c->last_vec), v) < 0
|
||||
? GSK_DIR_CHANGE_REVERSE
|
||||
: GSK_DIR_CHANGE_STRAIGHT;
|
||||
|
||||
return cross > 0
|
||||
? GSK_DIR_CHANGE_RIGHT
|
||||
: GSK_DIR_CHANGE_LEFT;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
add_vec (GskConvexityChecker *c,
|
||||
graphene_vec2_t *v)
|
||||
{
|
||||
GskDirChange dir;
|
||||
int sign;
|
||||
|
||||
dir = direction_change (c, v);
|
||||
switch (dir)
|
||||
{
|
||||
case GSK_DIR_CHANGE_LEFT:
|
||||
case GSK_DIR_CHANGE_RIGHT:
|
||||
if (c->expected_direction == GSK_DIR_CHANGE_INVALID)
|
||||
{
|
||||
c->expected_direction = dir;
|
||||
c->first_direction = (dir == GSK_DIR_CHANGE_RIGHT)
|
||||
? GSK_PATH_DIRECTION_CLOCKWISE
|
||||
: GSK_PATH_DIRECTION_COUNTERCLOCKWISE;
|
||||
}
|
||||
else if (c->expected_direction != dir)
|
||||
{
|
||||
c->first_direction = GSK_PATH_DIRECTION_UNKNOWN;
|
||||
c->convexity = GSK_CONVEXITY_CONCAVE;
|
||||
return FALSE;
|
||||
}
|
||||
graphene_vec2_init_from_vec2 (&c->last_vec, v);
|
||||
break;
|
||||
|
||||
case GSK_DIR_CHANGE_STRAIGHT:
|
||||
break;
|
||||
|
||||
case GSK_DIR_CHANGE_REVERSE:
|
||||
graphene_vec2_init_from_vec2 (&c->last_vec, v);
|
||||
c->reversals++;
|
||||
if (c->reversals > 2)
|
||||
c->convexity = GSK_CONVEXITY_CONCAVE;
|
||||
return c->reversals < 3;
|
||||
|
||||
case GSK_DIR_CHANGE_UNKNOWN:
|
||||
c->finite = FALSE;
|
||||
return FALSE;
|
||||
|
||||
case GSK_DIR_CHANGE_INVALID:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
if (graphene_vec2_get_x (v) > 0)
|
||||
sign = 1;
|
||||
else if (graphene_vec2_get_x (v) < 0)
|
||||
sign = -1;
|
||||
else
|
||||
sign = 0;
|
||||
|
||||
if (sign != 0)
|
||||
{
|
||||
if (c->xsign != 42)
|
||||
{
|
||||
if (c->xsign != sign)
|
||||
c->dx++;
|
||||
|
||||
if (c->dx > 2)
|
||||
{
|
||||
c->convexity = GSK_CONVEXITY_CONCAVE;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
c->xsign = sign;
|
||||
}
|
||||
|
||||
if (graphene_vec2_get_y (v) > 0)
|
||||
sign = 1;
|
||||
else if (graphene_vec2_get_y (v) < 0)
|
||||
sign = -1;
|
||||
else
|
||||
sign = 0;
|
||||
|
||||
if (sign != 0)
|
||||
{
|
||||
if (c->ysign != 42)
|
||||
{
|
||||
if (c->ysign != sign)
|
||||
c->dy++;
|
||||
|
||||
if (c->dy > 2)
|
||||
{
|
||||
c->convexity = GSK_CONVEXITY_CONCAVE;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
c->ysign = sign;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_convexity_checker_init (GskConvexityChecker *c)
|
||||
{
|
||||
c->first_point = GRAPHENE_POINT_INIT(0,0);
|
||||
c->last_point = GRAPHENE_POINT_INIT(0,0);
|
||||
graphene_vec2_init (&c->first_vec, 0, 0);
|
||||
graphene_vec2_init (&c->last_vec, 0, 0);
|
||||
c->expected_direction = GSK_DIR_CHANGE_INVALID;
|
||||
c->first_direction = GSK_PATH_DIRECTION_UNKNOWN;
|
||||
c->reversals = 0;
|
||||
c->finite = TRUE;
|
||||
c->convexity = GSK_CONVEXITY_UNKNOWN;
|
||||
c->xsign = 42;
|
||||
c->ysign = 42;
|
||||
c->dx = 0;
|
||||
c->dy = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_convexity_checker_move (GskConvexityChecker *c,
|
||||
const graphene_point_t *p)
|
||||
{
|
||||
c->first_point = c->last_point = *p;
|
||||
c->expected_direction = GSK_DIR_CHANGE_INVALID;
|
||||
c->convexity = GSK_CONVEXITY_CONVEX;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_convexity_checker_add_point (GskConvexityChecker *c,
|
||||
const graphene_point_t *p)
|
||||
{
|
||||
graphene_vec2_t v;
|
||||
|
||||
if (graphene_point_equal (&c->last_point, p))
|
||||
return TRUE;
|
||||
|
||||
graphene_vec2_init (&v,
|
||||
p->x - c->last_point.x,
|
||||
p->y - c->last_point.y);
|
||||
|
||||
if (graphene_point_equal (&c->first_point, &c->last_point) &&
|
||||
c->expected_direction == GSK_DIR_CHANGE_INVALID)
|
||||
{
|
||||
graphene_vec2_init_from_vec2 (&c->last_vec, &v);
|
||||
graphene_vec2_init_from_vec2 (&c->first_vec, &v);
|
||||
}
|
||||
else if (!add_vec (c, &v))
|
||||
{
|
||||
c->convexity = GSK_CONVEXITY_CONCAVE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
c->last_point = *p;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_convexity_checker_close (GskConvexityChecker *c)
|
||||
{
|
||||
if (!(gsk_convexity_checker_add_point (c, &c->first_point) &&
|
||||
add_vec (c, &c->first_vec)))
|
||||
{
|
||||
c->convexity = GSK_CONVEXITY_CONCAVE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
convex_cb (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer user_data)
|
||||
{
|
||||
GskConvexityChecker *c = user_data;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case GSK_PATH_MOVE:
|
||||
gsk_convexity_checker_move (c, &pts[0]);
|
||||
break;
|
||||
case GSK_PATH_CLOSE:
|
||||
if (!gsk_convexity_checker_close (c))
|
||||
return FALSE;
|
||||
break;
|
||||
case GSK_PATH_LINE:
|
||||
if (!gsk_convexity_checker_add_point (c, &pts[1]))
|
||||
return FALSE;
|
||||
break;
|
||||
case GSK_PATH_CURVE:
|
||||
if (!gsk_convexity_checker_add_point (c, &pts[1]) ||
|
||||
!gsk_convexity_checker_add_point (c, &pts[2]) ||
|
||||
!gsk_convexity_checker_add_point (c, &pts[3]))
|
||||
return FALSE;
|
||||
break;
|
||||
case GSK_PATH_CONIC:
|
||||
if (!gsk_convexity_checker_add_point (c, &pts[1]) ||
|
||||
!gsk_convexity_checker_add_point (c, &pts[3]))
|
||||
return FALSE;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GskConvexity
|
||||
gsk_contour_compute_convexity (const GskContour *contour)
|
||||
{
|
||||
GskConvexityChecker c;
|
||||
|
||||
gsk_convexity_checker_init (&c);
|
||||
gsk_contour_foreach (contour, 0.001, convex_cb, &c);
|
||||
|
||||
g_assert (c.convexity != GSK_CONVEXITY_UNKNOWN);
|
||||
|
||||
return c.convexity;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "gskcontourprivate.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GSK_CONVEXITY_UNKNOWN,
|
||||
GSK_CONVEXITY_CONVEX,
|
||||
GSK_CONVEXITY_CONCAVE,
|
||||
} GskConvexity;
|
||||
|
||||
GskConvexity gsk_contour_compute_convexity (const GskContour *contour);
|
||||
|
||||
|
||||
-2003
File diff suppressed because it is too large
Load Diff
@@ -1,621 +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 - FLT_EPSILON <= t && t <= 1 + FLT_EPSILON;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
line_intersect (const GskCurve *curve1,
|
||||
const GskCurve *curve2,
|
||||
float *t1,
|
||||
float *t2,
|
||||
graphene_point_t *p,
|
||||
int n)
|
||||
{
|
||||
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 (fabs(det) > 0.01)
|
||||
{
|
||||
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[0].x = pts1[0].x + tt * (pts1[1].x - pts1[0].x);
|
||||
p[0].y = pts1[0].y + tt * (pts1[1].y - pts1[0].y);
|
||||
|
||||
t1[0] = tt;
|
||||
t2[0] = ss;
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else /* parallel lines */
|
||||
{
|
||||
float r = a1 * (pts1[1].y - pts2[0].y) - (pts1[1].x - pts2[0].x) * b1;
|
||||
float dist = (r * r) / (a1 * a1 + b1 * b1);
|
||||
float t, s, tt, ss;
|
||||
|
||||
if (dist > 0.01)
|
||||
return 0;
|
||||
|
||||
if (pts1[1].x != pts1[0].x)
|
||||
{
|
||||
t = (pts2[0].x - pts1[0].x) / (pts1[1].x - pts1[0].x);
|
||||
s = (pts2[1].x - pts1[0].x) / (pts1[1].x - pts1[0].x);
|
||||
}
|
||||
else
|
||||
{
|
||||
t = (pts2[0].y - pts1[0].y) / (pts1[1].y - pts1[0].y);
|
||||
s = (pts2[1].y - pts1[0].y) / (pts1[1].y - pts1[0].y);
|
||||
}
|
||||
|
||||
if ((t < 0 && s < 0) || (t > 1 && s > 1))
|
||||
return 0;
|
||||
|
||||
if (acceptable (t))
|
||||
{
|
||||
t1[0] = t;
|
||||
t2[0] = 0;
|
||||
p[0] = pts2[0];
|
||||
}
|
||||
else if (t < 0)
|
||||
{
|
||||
if (pts2[1].x != pts2[0].x)
|
||||
tt = (pts1[0].x - pts2[0].x) / (pts2[1].x - pts2[0].x);
|
||||
else
|
||||
tt = (pts1[0].y - pts2[0].y) / (pts2[1].y - pts2[0].y);
|
||||
|
||||
t1[0] = 0;
|
||||
t2[0] = tt;
|
||||
p[0] = pts1[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pts2[1].x != pts2[0].x)
|
||||
tt = (pts1[1].x - pts2[0].x) / (pts2[1].x - pts2[0].x);
|
||||
else
|
||||
tt = (pts1[1].y - pts2[0].y) / (pts2[1].y - pts2[0].y);
|
||||
|
||||
t1[0] = 1;
|
||||
t2[0] = tt;
|
||||
p[0] = pts1[1];
|
||||
}
|
||||
|
||||
if (acceptable (s))
|
||||
{
|
||||
if (t2[0] == 1)
|
||||
return 1;
|
||||
|
||||
t1[1] = s;
|
||||
t2[1] = 1;
|
||||
p[1] = pts2[1];
|
||||
}
|
||||
else if (s < 0)
|
||||
{
|
||||
if (t1[0] == 0)
|
||||
return 1;
|
||||
|
||||
if (pts2[1].x != pts2[0].x)
|
||||
ss = (pts1[0].x - pts2[0].x) / (pts2[1].x - pts2[0].x);
|
||||
else
|
||||
ss = (pts1[0].y - pts2[0].y) / (pts2[1].y - pts2[0].y);
|
||||
|
||||
t1[1] = 0;
|
||||
t2[1] = ss;
|
||||
p[1] = pts1[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (t1[0] == 1)
|
||||
return 1;
|
||||
|
||||
if (pts2[1].x != pts2[0].x)
|
||||
ss = (pts1[1].x - pts2[0].x) / (pts2[1].x - pts2[0].x);
|
||||
else
|
||||
ss = (pts1[1].y - pts2[0].y) / (pts2[1].y - pts2[0].y);
|
||||
|
||||
t1[1] = 1;
|
||||
t2[1] = ss;
|
||||
p[1] = pts1[1];
|
||||
}
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
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;
|
||||
float dist;
|
||||
|
||||
get_tangent (a, b, &n1);
|
||||
angle = - atan2 (graphene_vec2_get_y (&n1), graphene_vec2_get_x (&n1));
|
||||
sincosf (angle, &s, &c);
|
||||
|
||||
dist = sqrtf ((a->x - b->x)*(a->x - b->x) + (a->y - b->y)*(a->y - b->y));
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
q[i].x = ((p[i].x - a->x) * c - (p[i].y - a->y) * s) / dist;
|
||||
q[i].y = ((p[i].x - a->x) * s + (p[i].y - a->y) * c) / dist;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
find_point_on_line (const graphene_point_t *p1,
|
||||
const graphene_point_t *p2,
|
||||
const graphene_point_t *q,
|
||||
float *t)
|
||||
{
|
||||
if (p2->x != p1->x)
|
||||
*t = (q->x - p1->x) / (p2->x - p1->x);
|
||||
else
|
||||
*t = (q->y - p1->y) / (p2->y - p1->y);
|
||||
}
|
||||
|
||||
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, j;
|
||||
|
||||
/* 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);
|
||||
|
||||
j = 0;
|
||||
for (i = 0; i < m; i++)
|
||||
{
|
||||
t2[j] = t[i];
|
||||
gsk_curve_get_point (curve2, t2[j], &p[j]);
|
||||
find_point_on_line (a, b, &p[j], &t1[j]);
|
||||
if (acceptable (t1[j]))
|
||||
j++;
|
||||
if (j == n)
|
||||
break;
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
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,
|
||||
float tolerance)
|
||||
{
|
||||
GskCurve p11, p12, p21, p22;
|
||||
GskBoundingBox b1, b2;
|
||||
float d1, d2;
|
||||
|
||||
if (*pos == n)
|
||||
return;
|
||||
|
||||
gsk_curve_get_tight_bounds (curve1, &b1);
|
||||
gsk_curve_get_tight_bounds (curve2, &b2);
|
||||
|
||||
if (!gsk_bounding_box_intersection (&b1, &b2, NULL))
|
||||
return;
|
||||
|
||||
d1 = (t1r - t1l) / 2;
|
||||
d2 = (t2r - t2l) / 2;
|
||||
|
||||
if (b1.max.x - b1.min.x < tolerance && b1.max.y - b1.min.y < tolerance &&
|
||||
b2.max.x - b2.min.x < tolerance && b2.max.y - b2.min.y < tolerance)
|
||||
{
|
||||
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, tolerance);
|
||||
curve_intersect_recurse (&p11, &p22, t1l, t1l + d1, t2l + d2, t2r, t1, t2, p, n, pos, tolerance);
|
||||
curve_intersect_recurse (&p12, &p21, t1l + d1, t1r, t2l, t2l + d2, t1, t2, p, n, pos, tolerance);
|
||||
curve_intersect_recurse (&p12, &p22, t1l + d1, t1r, t2l + d2, t2r, t1, t2, p, n, pos, tolerance);
|
||||
}
|
||||
|
||||
static int
|
||||
curve_intersect (const GskCurve *curve1,
|
||||
const GskCurve *curve2,
|
||||
float *t1,
|
||||
float *t2,
|
||||
graphene_point_t *p,
|
||||
int n,
|
||||
float tolerance)
|
||||
{
|
||||
int pos = 0;
|
||||
|
||||
curve_intersect_recurse (curve1, curve2, 0, 1, 0, 1, t1, t2, p, n, &pos, tolerance);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void
|
||||
get_bounds (const GskCurve *curve,
|
||||
float tl,
|
||||
float tr,
|
||||
GskBoundingBox *bounds)
|
||||
{
|
||||
GskCurve c;
|
||||
|
||||
gsk_curve_segment (curve, tl, tr, &c);
|
||||
gsk_curve_get_tight_bounds (&c, bounds);
|
||||
}
|
||||
|
||||
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,
|
||||
float tolerance)
|
||||
{
|
||||
GskBoundingBox b1, b2;
|
||||
float d1, d2;
|
||||
|
||||
if (*pos == n)
|
||||
return;
|
||||
|
||||
get_bounds (curve1, t1l, t1r, &b1);
|
||||
get_bounds (curve2, t2l, t2r, &b2);
|
||||
|
||||
if (!gsk_bounding_box_intersection (&b1, &b2, NULL))
|
||||
return;
|
||||
|
||||
d1 = (t1r - t1l) / 2;
|
||||
d2 = (t2r - t2l) / 2;
|
||||
|
||||
if (b1.max.x - b1.min.x < tolerance && b1.max.y - b1.min.y < tolerance &&
|
||||
b2.max.x - b2.min.x < tolerance && b2.max.y - b2.min.y < tolerance)
|
||||
{
|
||||
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], tolerance))
|
||||
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, tolerance);
|
||||
general_intersect_recurse (curve1, curve2, t1l, t1l + d1, t2l + d2, t2r, t1, t2, p, n, pos, tolerance);
|
||||
general_intersect_recurse (curve1, curve2, t1l + d1, t1r, t2l, t2l + d2, t1, t2, p, n, pos, tolerance);
|
||||
general_intersect_recurse (curve1, curve2, t1l + d1, t1r, t2l + d2, t2r, t1, t2, p, n, pos, tolerance);
|
||||
}
|
||||
|
||||
static int
|
||||
general_intersect (const GskCurve *curve1,
|
||||
const GskCurve *curve2,
|
||||
float *t1,
|
||||
float *t2,
|
||||
graphene_point_t *p,
|
||||
int n,
|
||||
float tolerance)
|
||||
{
|
||||
int pos = 0;
|
||||
|
||||
general_intersect_recurse (curve1, curve2, 0, 1, 0, 1, t1, t2, p, n, &pos, tolerance);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
static int
|
||||
curve_self_intersect (const GskCurve *curve,
|
||||
float *t1,
|
||||
float *t2,
|
||||
graphene_point_t *p,
|
||||
int n)
|
||||
{
|
||||
float tt[3], ss[3], s;
|
||||
graphene_point_t pp[3];
|
||||
int m;
|
||||
GskCurve cs, ce;
|
||||
|
||||
if (curve->op != GSK_PATH_CURVE)
|
||||
return 0;
|
||||
|
||||
s = 0.5;
|
||||
m = gsk_curve_get_curvature_points (curve, tt);
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
if (gsk_curve_get_curvature (curve, tt[i], NULL) == 0)
|
||||
{
|
||||
s = tt[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gsk_curve_split (curve, s, &cs, &ce);
|
||||
|
||||
m = curve_intersect (&cs, &ce, tt, ss, pp, 3, 0.001);
|
||||
|
||||
if (m > 1)
|
||||
{
|
||||
/* One of the (at most 2) intersections we found
|
||||
* must be the common point where we split the curve.
|
||||
* It will have a t value of 1 and an s value of 0.
|
||||
*/
|
||||
if (fabs (tt[0] - 1) > 1e-3)
|
||||
{
|
||||
t1[0] = t2[0] = tt[0] * s;
|
||||
p[0] = pp[0];
|
||||
}
|
||||
else if (fabs (tt[1] - 1) > 1e-3)
|
||||
{
|
||||
t1[0] = t2[0] = tt[1] * s;
|
||||
p[0] = pp[1];
|
||||
}
|
||||
if (fabs (ss[0]) > 1e-3)
|
||||
{
|
||||
t1[1] = t2[1] = s + ss[0] * (1 - s);
|
||||
p[1] = pp[0];
|
||||
}
|
||||
else if (fabs (ss[1]) > 1e-3)
|
||||
{
|
||||
t1[1] = t2[1] = s + ss[1] * (1 - s);
|
||||
p[1] = pp[1];
|
||||
}
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
if (memcmp (curve1, curve2, sizeof (GskCurve)) == 0)
|
||||
return curve_self_intersect (curve1, t1, t2, p, n);
|
||||
|
||||
/* 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, n);
|
||||
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, 0.001);
|
||||
else
|
||||
return general_intersect (curve1, curve2, t1, t2, p, n, 0.001);
|
||||
|
||||
}
|
||||
@@ -1,164 +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"
|
||||
#include "gskboundingboxprivate.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;
|
||||
|
||||
/* points[0], points[1], points[3] are the control points,
|
||||
* points[2].x is the weight
|
||||
*/
|
||||
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_get_normal (const GskCurve *curve,
|
||||
float progress,
|
||||
graphene_vec2_t *normal);
|
||||
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,
|
||||
GskBoundingBox *bounds);
|
||||
void gsk_curve_get_tight_bounds (const GskCurve *curve,
|
||||
GskBoundingBox *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);
|
||||
|
||||
float gsk_curve_get_curvature (const GskCurve *curve,
|
||||
float t,
|
||||
graphene_point_t *center);
|
||||
|
||||
int gsk_curve_get_curvature_points (const GskCurve *curve,
|
||||
float t[3]);
|
||||
|
||||
int gsk_curve_get_cusps (const GskCurve *curve,
|
||||
float t[2]);
|
||||
|
||||
void gsk_curve_print (const GskCurve *curve);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_CURVE_PRIVATE_H__ */
|
||||
|
||||
@@ -10,7 +10,6 @@ static const GdkDebugKey gsk_debug_keys[] = {
|
||||
{ "surface", GSK_DEBUG_SURFACE, "Information about surfaces" },
|
||||
{ "fallback", GSK_DEBUG_FALLBACK, "Information about fallbacks" },
|
||||
{ "glyphcache", GSK_DEBUG_GLYPH_CACHE, "Information about glyph caching" },
|
||||
{ "paths", GSK_DEBUG_PATHS, "Information about path processing" },
|
||||
{ "geometry", GSK_DEBUG_GEOMETRY, "Show borders (when using cairo)" },
|
||||
{ "full-redraw", GSK_DEBUG_FULL_REDRAW, "Force full redraws" },
|
||||
{ "sync", GSK_DEBUG_SYNC, "Sync after each frame" },
|
||||
|
||||
@@ -14,7 +14,6 @@ typedef enum {
|
||||
GSK_DEBUG_VULKAN = 1 << 5,
|
||||
GSK_DEBUG_FALLBACK = 1 << 6,
|
||||
GSK_DEBUG_GLYPH_CACHE = 1 << 7,
|
||||
GSK_DEBUG_PATHS = 1 << 8,
|
||||
/* flags below may affect behavior */
|
||||
GSK_DEBUG_GEOMETRY = 1 << 9,
|
||||
GSK_DEBUG_FULL_REDRAW = 1 << 10,
|
||||
|
||||
+14
-5
@@ -356,6 +356,8 @@ compare (gconstpointer *elem1,
|
||||
const GskDiffSettings *settings,
|
||||
gpointer data)
|
||||
{
|
||||
GskDiffResult res;
|
||||
|
||||
/*
|
||||
* Shrink the box by walking through each diagonal snake (SW and NE).
|
||||
*/
|
||||
@@ -364,7 +366,9 @@ compare (gconstpointer *elem1,
|
||||
if (settings->compare_func (elem1[off1], elem2[off2], data) != 0)
|
||||
break;
|
||||
|
||||
settings->keep_func (elem1[off1], elem2[off2], data);
|
||||
res = settings->keep_func (elem1[off1], elem2[off2], data);
|
||||
if (res != GSK_DIFF_OK)
|
||||
return res;
|
||||
}
|
||||
|
||||
for (; off1 < lim1 && off2 < lim2; lim1--, lim2--)
|
||||
@@ -372,7 +376,9 @@ compare (gconstpointer *elem1,
|
||||
if (settings->compare_func (elem1[lim1 - 1], elem2[lim2 - 1], data) != 0)
|
||||
break;
|
||||
|
||||
settings->keep_func (elem1[lim1 - 1], elem2[lim2 - 1], data);
|
||||
res = settings->keep_func (elem1[lim1 - 1], elem2[lim2 - 1], data);
|
||||
if (res != GSK_DIFF_OK)
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -383,20 +389,23 @@ compare (gconstpointer *elem1,
|
||||
{
|
||||
for (; off2 < lim2; off2++)
|
||||
{
|
||||
settings->insert_func (elem2[off2], off2, data);
|
||||
res = settings->insert_func (elem2[off2], off2, data);
|
||||
if (res != GSK_DIFF_OK)
|
||||
return res;
|
||||
}
|
||||
}
|
||||
else if (off2 == lim2)
|
||||
{
|
||||
for (; off1 < lim1; off1++)
|
||||
{
|
||||
settings->delete_func (elem1[off1], off1, data);
|
||||
res = settings->delete_func (elem1[off1], off1, data);
|
||||
if (res != GSK_DIFF_OK)
|
||||
return res;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SplitResult spl = { 0, };
|
||||
GskDiffResult res;
|
||||
|
||||
/*
|
||||
* Divide ...
|
||||
|
||||
@@ -29,9 +29,9 @@ typedef enum {
|
||||
GSK_DIFF_ABORTED,
|
||||
} GskDiffResult;
|
||||
|
||||
typedef void (* GskKeepFunc) (gconstpointer elem1, gconstpointer elem2, gpointer data);
|
||||
typedef void (* GskDeleteFunc) (gconstpointer elem, gsize idx, gpointer data);
|
||||
typedef void (* GskInsertFunc) (gconstpointer elem, gsize idx, gpointer data);
|
||||
typedef GskDiffResult (* GskKeepFunc) (gconstpointer elem1, gconstpointer elem2, gpointer data);
|
||||
typedef GskDiffResult (* GskDeleteFunc) (gconstpointer elem, gsize idx, gpointer data);
|
||||
typedef GskDiffResult (* GskInsertFunc) (gconstpointer elem, gsize idx, gpointer data);
|
||||
|
||||
typedef struct _GskDiffSettings GskDiffSettings;
|
||||
|
||||
|
||||
+3
-105
@@ -1,5 +1,5 @@
|
||||
/* GSK - The GTK Scene Kit
|
||||
* Copyright 2016 Endless
|
||||
* Copyright 2016 Endless
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@@ -50,7 +50,7 @@
|
||||
* @GSK_BLUR_NODE: A node that applies a blur
|
||||
* @GSK_DEBUG_NODE: Debug information that does not affect the rendering
|
||||
* @GSK_GL_SHADER_NODE: A node that uses OpenGL fragment shaders to render
|
||||
|
||||
|
||||
* The type of a node determines what the node is rendering.
|
||||
*/
|
||||
typedef enum {
|
||||
@@ -73,8 +73,6 @@ 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,
|
||||
@@ -169,107 +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;
|
||||
|
||||
/**
|
||||
* GskLineCap:
|
||||
* @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
|
||||
* @GSK_LINE_JOIN_ARCS: Use a sharp angled corner made from circles
|
||||
*
|
||||
* Specifies how to render the junction of two lines when stroking.
|
||||
*
|
||||
* See [method@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,
|
||||
GSK_LINE_JOIN_ARCS
|
||||
} 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 Bézier 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 identified
|
||||
@@ -354,4 +251,5 @@ typedef enum
|
||||
GSK_GL_UNIFORM_TYPE_VEC4,
|
||||
} GskGLUniformType;
|
||||
|
||||
|
||||
#endif /* __GSK_TYPES_H__ */
|
||||
|
||||
-1516
File diff suppressed because it is too large
Load Diff
-154
@@ -1,154 +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 [method@Gsk.Path.foreach] to enable additional
|
||||
* features.
|
||||
*
|
||||
* By default, [method@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 *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gsk_path_get_bounds (GskPath *self,
|
||||
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_is_convex (GskPath *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gsk_path_foreach (GskPath *self,
|
||||
GskPathForeachFlags flags,
|
||||
GskPathForeachFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_path_reverse (GskPath *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_path_transform (GskPath *self,
|
||||
GskTransform *transform);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_path_stroke (GskPath *self,
|
||||
GskStroke *stroke);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_path_offset (GskPath *self,
|
||||
float distance,
|
||||
GskLineJoin line_join,
|
||||
float miter_limit);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_path_union (GskPath *first,
|
||||
GskPath *second);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_path_intersection (GskPath *first,
|
||||
GskPath *second);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_path_difference (GskPath *first,
|
||||
GskPath *second);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_path_symmetric_difference (GskPath *first,
|
||||
GskPath *second);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_path_simplify (GskPath *self);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_PATH_H__ */
|
||||
@@ -1,964 +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"
|
||||
|
||||
/**
|
||||
* GskPathBuilder:
|
||||
*
|
||||
* A `GskPathBuilder` is an auxiliary object that is used to
|
||||
* create new `GskPath` objects.
|
||||
*
|
||||
* A path is constructed like this:
|
||||
*
|
||||
* ```
|
||||
* 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 [method@Gsk.PathBuilder.add_circle]
|
||||
* or by adding from other paths like [method@Gsk.PathBuilder.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 [method@Gsk.PathBuilder.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
|
||||
* [method@Gsk.PathBuilder.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.
|
||||
*/
|
||||
|
||||
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 add to @builder
|
||||
*
|
||||
* Adds 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.
|
||||
**/
|
||||
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:
|
||||
* @builder: a `GskPathBuilder`
|
||||
* @rect: the rounded rect
|
||||
*
|
||||
* Adds @rect as a new contour to the path built in @builder.
|
||||
**/
|
||||
void
|
||||
gsk_path_builder_add_rounded_rect (GskPathBuilder *builder,
|
||||
const GskRoundedRect *rect)
|
||||
{
|
||||
const float weight = sqrt(0.5f);
|
||||
|
||||
g_return_if_fail (builder != NULL);
|
||||
g_return_if_fail (rect != NULL);
|
||||
|
||||
gsk_path_builder_move_to (builder,
|
||||
rect->bounds.origin.x + rect->corner[GSK_CORNER_TOP_LEFT].width,
|
||||
rect->bounds.origin.y);
|
||||
/* top */
|
||||
gsk_path_builder_line_to (builder,
|
||||
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 (builder,
|
||||
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 (builder,
|
||||
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 (builder,
|
||||
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 (builder,
|
||||
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 (builder,
|
||||
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 (builder,
|
||||
rect->bounds.origin.x,
|
||||
rect->bounds.origin.y + rect->corner[GSK_CORNER_TOP_LEFT].height);
|
||||
/* topleft corner */
|
||||
gsk_path_builder_conic_to (builder,
|
||||
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 (builder);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_add_ellipse:
|
||||
* @builder: a `GskPathBuilder`
|
||||
* @center: the center point of the ellipse
|
||||
* @radius: the radius of the ellipse in x/y direction
|
||||
*
|
||||
* Adds an ellipse with the given @center and the @radius in
|
||||
* x/y direction.
|
||||
**/
|
||||
void
|
||||
gsk_path_builder_add_ellipse (GskPathBuilder *builder,
|
||||
const graphene_point_t *center,
|
||||
const graphene_size_t *radius)
|
||||
{
|
||||
const float weight = sqrt(0.5f);
|
||||
graphene_point_t pts[8];
|
||||
|
||||
g_return_if_fail (builder != NULL);
|
||||
g_return_if_fail (center != NULL);
|
||||
g_return_if_fail (radius != NULL);
|
||||
|
||||
pts[0] = GRAPHENE_POINT_INIT (center->x + radius->width / 2,
|
||||
center->y);
|
||||
pts[1] = GRAPHENE_POINT_INIT (center->x + radius->width / 2,
|
||||
center->y + radius->height / 2);
|
||||
pts[2] = GRAPHENE_POINT_INIT (center->x,
|
||||
center->y + radius->height / 2);
|
||||
pts[3] = GRAPHENE_POINT_INIT (center->x - radius->width / 2,
|
||||
center->y + radius->height / 2);
|
||||
pts[4] = GRAPHENE_POINT_INIT (center->x - radius->width / 2,
|
||||
center->y);
|
||||
pts[5] = GRAPHENE_POINT_INIT (center->x - radius->width / 2,
|
||||
center->y - radius->height / 2);
|
||||
pts[6] = GRAPHENE_POINT_INIT (center->x,
|
||||
center->y - radius->height / 2);
|
||||
pts[7] = GRAPHENE_POINT_INIT (center->x + radius->width / 2,
|
||||
center->y - radius->height / 2);
|
||||
|
||||
gsk_path_builder_move_to (builder, pts[0].x, pts[0].y);
|
||||
gsk_path_builder_conic_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, weight);
|
||||
gsk_path_builder_conic_to (builder, pts[3].x, pts[3].y, pts[4].x, pts[4].y, weight);
|
||||
gsk_path_builder_conic_to (builder, pts[5].x, pts[5].y, pts[6].x, pts[6].y, weight);
|
||||
gsk_path_builder_conic_to (builder, pts[7].x, pts[7].y, pts[0].x, pts[0].y, weight);
|
||||
gsk_path_builder_close (builder);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 [method@Gsk.PathBuilder.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_rel_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 [method@Gsk.PathBuilder.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 [method@Gsk.PathBuilder.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 [method@Gsk.PathBuilder.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 [method@Gsk.PathBuilder.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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_builder_add_layout:
|
||||
* @builder: a `GskPathBuilder`
|
||||
* @layout: the pango layout to add
|
||||
*
|
||||
* Adds the outlines for the glyphs in @layout to @builder.
|
||||
*/
|
||||
void
|
||||
gsk_path_builder_add_layout (GskPathBuilder *builder,
|
||||
PangoLayout *layout)
|
||||
{
|
||||
cairo_surface_t *surface;
|
||||
cairo_t *cr;
|
||||
cairo_path_t *cairo_path;
|
||||
GskPath *path;
|
||||
|
||||
surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
|
||||
cr = cairo_create (surface);
|
||||
|
||||
pango_cairo_layout_path (cr, layout);
|
||||
cairo_path = cairo_copy_path_flat (cr);
|
||||
path = gsk_path_new_from_cairo (cairo_path);
|
||||
|
||||
gsk_path_builder_add_path (builder, path);
|
||||
|
||||
gsk_path_unref (path);
|
||||
|
||||
cairo_path_destroy (cairo_path);
|
||||
cairo_destroy (cr);
|
||||
cairo_surface_destroy (surface);
|
||||
}
|
||||
@@ -1,132 +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);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_add_ellipse (GskPathBuilder *builder,
|
||||
const graphene_point_t *center,
|
||||
const graphene_size_t *radius);
|
||||
/* next function implemented in gskpathmeasure.c */
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_add_segment (GskPathBuilder *builder,
|
||||
GskPathMeasure *measure,
|
||||
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);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_add_layout (GskPathBuilder *builder,
|
||||
PangoLayout *layout);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_PATH_BUILDER_H__ */
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
@@ -1,677 +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"
|
||||
|
||||
/**
|
||||
* GskPathMeasure:
|
||||
*
|
||||
* `GskPathMeasure` is an object that allows measuring operations
|
||||
* on `GskPath` objects, to determine quantities like the arc
|
||||
* length of the path, its curvature, or closest points.
|
||||
*
|
||||
* 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[];
|
||||
};
|
||||
|
||||
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 with a
|
||||
* default tolerance.
|
||||
*
|
||||
* 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 object.
|
||||
**/
|
||||
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: (optional) (out caller-allocates): The coordinates
|
||||
* of the position at @distance
|
||||
* @tangent: (optional) (out 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_curvature:
|
||||
* @self: a `GskPathMeasure`
|
||||
* @distance: distance into the path
|
||||
* @center: (optional) (out caller-allocates): The center
|
||||
* of the osculating circle at the point
|
||||
*
|
||||
* Calculates the curvature at the point @distance units into
|
||||
* the path.
|
||||
*
|
||||
* Optionally, returns the center of the osculating circle as well.
|
||||
*
|
||||
* If the curvature is infinite (at line segments), or does
|
||||
* not exist (at sharp turns), zero is returned, and @center
|
||||
* is not modified.
|
||||
*
|
||||
* Returns: The curvature of the path at the given point
|
||||
*/
|
||||
float
|
||||
gsk_path_measure_get_curvature (GskPathMeasure *self,
|
||||
float distance,
|
||||
graphene_point_t *center)
|
||||
{
|
||||
gsize i;
|
||||
|
||||
g_return_val_if_fail (self != NULL, 0);
|
||||
|
||||
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)
|
||||
return 0;
|
||||
|
||||
/* rounding errors can make this happen */
|
||||
i = self->last - 1;
|
||||
distance = self->measures[i].length;
|
||||
}
|
||||
|
||||
return gsk_contour_get_curvature (gsk_path_get_contour (self->path, i),
|
||||
self->measures[i].contour_data,
|
||||
distance,
|
||||
center);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_measure_get_closest_point:
|
||||
* @self: a `GskPathMeasure`
|
||||
* @point: the point to find the closest point to
|
||||
* @out_pos: (optional) (out 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
|
||||
* [method@Gsk.PathMeasure.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,
|
||||
NULL,
|
||||
out_pos,
|
||||
&result,
|
||||
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 find the closest point to
|
||||
* @threshold: The maximum allowed distance between the path and @point.
|
||||
* Use INFINITY to look for any point.
|
||||
* @out_distance: (optional) (out caller-allocates): The
|
||||
* distance between the found closest point on the path and the given
|
||||
* @point.
|
||||
* @out_pos: (optional) (out caller-allocates): return location
|
||||
* for the closest point
|
||||
* @out_offset: (optional) (out caller-allocates): The offset into
|
||||
* the path of the found point
|
||||
* @out_tangent: (optional) (out 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:
|
||||
* @builder: a `GskPathBuilder`
|
||||
* @measure: the `GskPathMeasure` to take the segment to
|
||||
* @start: start distance into the path
|
||||
* @end: end distance into the path
|
||||
*
|
||||
* Adds to @builder 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 [method@Gsk.PathMeasure.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 *builder,
|
||||
GskPathMeasure *measure,
|
||||
float start,
|
||||
float end)
|
||||
{
|
||||
g_return_if_fail (builder != 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 (builder, 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 (builder, measure,
|
||||
TRUE,
|
||||
start, measure->length);
|
||||
else
|
||||
need_move_to = TRUE;
|
||||
|
||||
if (end > 0)
|
||||
gsk_path_builder_add_segment_chunk (builder, measure,
|
||||
need_move_to,
|
||||
0, end);
|
||||
if (start == end && closed)
|
||||
gsk_path_builder_close (builder);
|
||||
}
|
||||
}
|
||||
@@ -1,92 +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) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
float gsk_path_measure_get_tolerance (GskPathMeasure *self) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gsize gsk_path_measure_get_n_contours (GskPathMeasure *self) G_GNUC_PURE;
|
||||
|
||||
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_curvature (GskPathMeasure *self,
|
||||
float distance,
|
||||
graphene_point_t *center);
|
||||
|
||||
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__ */
|
||||
@@ -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__ */
|
||||
|
||||
-1427
File diff suppressed because it is too large
Load Diff
@@ -1,78 +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);
|
||||
|
||||
GskPathFlags gsk_path_get_flags (GskPath *self);
|
||||
|
||||
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);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GSK_PATH_OP_SIMPLIFY,
|
||||
GSK_PATH_OP_UNION,
|
||||
GSK_PATH_OP_INTERSECTION,
|
||||
GSK_PATH_OP_DIFFERENCE,
|
||||
GSK_PATH_OP_XOR
|
||||
} GskPathOp;
|
||||
|
||||
GskPath * gsk_path_op (GskPathOp operation,
|
||||
GskPath *first,
|
||||
GskPath *second);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_PATH_PRIVATE_H__ */
|
||||
|
||||
-2073
File diff suppressed because it is too large
Load Diff
@@ -158,8 +158,6 @@ 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())
|
||||
@@ -186,8 +184,6 @@ 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;
|
||||
@@ -449,32 +445,6 @@ GskRenderNode * gsk_rounded_clip_node_get_child (const GskRender
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const GskRoundedRect * gsk_rounded_clip_node_get_clip (const GskRenderNode *node) G_GNUC_PURE;
|
||||
|
||||
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 (const GskRenderNode *node);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_fill_node_get_path (const GskRenderNode *node);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskFillRule gsk_fill_node_get_fill_rule (const 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 (const GskRenderNode *node);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskPath * gsk_stroke_node_get_path (const GskRenderNode *node);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const GskStroke * gsk_stroke_node_get_stroke (const GskRenderNode *node);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gsk_shadow_node_get_type (void) G_GNUC_CONST;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
|
||||
+22
-386
@@ -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"
|
||||
@@ -35,6 +33,12 @@
|
||||
|
||||
#include <hb-ot.h>
|
||||
|
||||
/* maximal number of rectangles we keep in a diff region before we throw
|
||||
* the towel and just use the bounding box of the parent node.
|
||||
* Meant to avoid performance corner cases.
|
||||
*/
|
||||
#define MAX_RECTS_IN_DIFF 30
|
||||
|
||||
static inline void
|
||||
gsk_cairo_rectangle (cairo_t *cr,
|
||||
const graphene_rect_t *rect)
|
||||
@@ -2603,32 +2607,35 @@ gsk_container_node_draw (GskRenderNode *node,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_render_node_add_to_region (GskRenderNode *node,
|
||||
cairo_region_t *region)
|
||||
{
|
||||
cairo_rectangle_int_t rect;
|
||||
|
||||
rectangle_init_from_graphene (&rect, &node->bounds);
|
||||
cairo_region_union_rectangle (region, &rect);
|
||||
}
|
||||
|
||||
static int
|
||||
gsk_container_node_compare_func (gconstpointer elem1, gconstpointer elem2, gpointer data)
|
||||
{
|
||||
return gsk_render_node_can_diff ((const GskRenderNode *) elem1, (const GskRenderNode *) elem2) ? 0 : 1;
|
||||
}
|
||||
|
||||
static void
|
||||
static GskDiffResult
|
||||
gsk_container_node_keep_func (gconstpointer elem1, gconstpointer elem2, gpointer data)
|
||||
{
|
||||
gsk_render_node_diff ((GskRenderNode *) elem1, (GskRenderNode *) elem2, data);
|
||||
if (cairo_region_num_rectangles (data) > MAX_RECTS_IN_DIFF)
|
||||
return GSK_DIFF_ABORTED;
|
||||
|
||||
return GSK_DIFF_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
static GskDiffResult
|
||||
gsk_container_node_change_func (gconstpointer elem, gsize idx, gpointer data)
|
||||
{
|
||||
gsk_render_node_add_to_region ((GskRenderNode *) elem, data);
|
||||
const GskRenderNode *node = elem;
|
||||
cairo_region_t *region = data;
|
||||
cairo_rectangle_int_t rect;
|
||||
|
||||
rectangle_init_from_graphene (&rect, &node->bounds);
|
||||
cairo_region_union_rectangle (region, &rect);
|
||||
if (cairo_region_num_rectangles (region) > MAX_RECTS_IN_DIFF)
|
||||
return GSK_DIFF_ABORTED;
|
||||
|
||||
return GSK_DIFF_OK;
|
||||
}
|
||||
|
||||
static GskDiffSettings *
|
||||
@@ -3763,343 +3770,6 @@ gsk_rounded_clip_node_get_clip (const 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 (const GskRenderNode *node)
|
||||
{
|
||||
const GskFillNode *self = (const 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 (const GskRenderNode *node)
|
||||
{
|
||||
const GskFillNode *self = (const 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 (const GskRenderNode *node)
|
||||
{
|
||||
const GskFillNode *self = (const 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 (const GskRenderNode *node)
|
||||
{
|
||||
const GskStrokeNode *self = (const 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 (const GskRenderNode *node)
|
||||
{
|
||||
const GskStrokeNode *self = (const 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 (const GskRenderNode *node)
|
||||
{
|
||||
const GskStrokeNode *self = (const GskStrokeNode *) node;
|
||||
|
||||
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_STROKE_NODE), NULL);
|
||||
|
||||
return &self->stroke;
|
||||
}
|
||||
|
||||
/*** GSK_SHADOW_NODE ***/
|
||||
|
||||
/**
|
||||
@@ -5622,8 +5292,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)
|
||||
@@ -5923,38 +5591,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 =
|
||||
{
|
||||
|
||||
+6
-354
@@ -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"
|
||||
@@ -403,10 +399,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);
|
||||
@@ -962,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)
|
||||
{
|
||||
@@ -1129,7 +1102,7 @@ parse_conic_gradient_node (GtkCssParser *parser)
|
||||
g_array_append_val (stops, to);
|
||||
}
|
||||
|
||||
result = gsk_conic_gradient_node_new (&bounds, ¢er, rotation,
|
||||
result = gsk_conic_gradient_node_new (&bounds, ¢er, rotation,
|
||||
(GskColorStop *) stops->data, stops->len);
|
||||
|
||||
g_array_free (stops, TRUE);
|
||||
@@ -1415,7 +1388,7 @@ parse_cairo_node (GtkCssParser *parser)
|
||||
parse_declarations (parser, declarations, G_N_ELEMENTS(declarations));
|
||||
|
||||
node = gsk_cairo_node_new (&bounds);
|
||||
|
||||
|
||||
if (surface != NULL)
|
||||
{
|
||||
cairo_t *cr = gsk_cairo_node_get_draw_context (node);
|
||||
@@ -1792,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)
|
||||
{
|
||||
@@ -2063,7 +1834,6 @@ parse_node (GtkCssParser *parser,
|
||||
{ "container", parse_container_node },
|
||||
{ "cross-fade", parse_cross_fade_node },
|
||||
{ "debug", parse_debug_node },
|
||||
{ "fill", parse_fill_node },
|
||||
{ "inset-shadow", parse_inset_shadow_node },
|
||||
{ "linear-gradient", parse_linear_gradient_node },
|
||||
{ "radial-gradient", parse_radial_gradient_node },
|
||||
@@ -2075,7 +1845,6 @@ parse_node (GtkCssParser *parser,
|
||||
{ "repeating-radial-gradient", parse_repeating_radial_gradient_node },
|
||||
{ "rounded-clip", parse_rounded_clip_node },
|
||||
{ "shadow", parse_shadow_node },
|
||||
{ "stroke", parse_stroke_node },
|
||||
{ "text", parse_text_node },
|
||||
{ "texture", parse_texture_node },
|
||||
{ "transform", parse_transform_node },
|
||||
@@ -2331,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;
|
||||
|
||||
@@ -2506,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);
|
||||
}
|
||||
|
||||
@@ -2625,83 +2391,6 @@ gsk_text_node_serialize_glyphs (GskRenderNode *node,
|
||||
pango_glyph_string_free (ascii);
|
||||
}
|
||||
|
||||
static const char *
|
||||
enum_to_nick (GType type,
|
||||
int value)
|
||||
{
|
||||
GEnumClass *class;
|
||||
GEnumValue *v;
|
||||
|
||||
class = g_type_class_ref (type);
|
||||
v = g_enum_get_value (class, value);
|
||||
g_type_class_unref (class);
|
||||
|
||||
return v->value_nick;
|
||||
}
|
||||
|
||||
static void
|
||||
append_enum_param (Printer *p,
|
||||
const char *param_name,
|
||||
GType type,
|
||||
int value)
|
||||
{
|
||||
_indent (p);
|
||||
g_string_append_printf (p->str, "%s: ", param_name);
|
||||
g_string_append (p->str, enum_to_nick (type, value));
|
||||
g_string_append_c (p->str, ';');
|
||||
g_string_append_c (p->str, '\n');
|
||||
}
|
||||
|
||||
static void
|
||||
append_path_param (Printer *p,
|
||||
const char *param_name,
|
||||
GskPath *path)
|
||||
{
|
||||
char *str, *s;
|
||||
|
||||
_indent (p);
|
||||
g_string_append (p->str, "path: \"\\\n");
|
||||
str = gsk_path_to_string (path);
|
||||
/* Put each command on a new line */
|
||||
for (s = str; *s; s++)
|
||||
{
|
||||
if (*s == ' ' &&
|
||||
(s[1] == 'M' || s[1] == 'C' || s[1] == 'Z' || s[1] == 'L' || s[1] == 'O'))
|
||||
*s = '\n';
|
||||
}
|
||||
append_escaping_newlines (p->str, str);
|
||||
g_string_append (p->str, "\";\n");
|
||||
g_free (str);
|
||||
}
|
||||
|
||||
static void
|
||||
append_dash_param (Printer *p,
|
||||
const char *param_name,
|
||||
const float *dash,
|
||||
gsize n_dash)
|
||||
{
|
||||
_indent (p);
|
||||
g_string_append (p->str, "dash: ");
|
||||
|
||||
if (n_dash == 0)
|
||||
{
|
||||
g_string_append (p->str, "none");
|
||||
}
|
||||
else
|
||||
{
|
||||
gsize i;
|
||||
|
||||
string_append_double (p->str, dash[0]);
|
||||
for (i = 1; i < n_dash; i++)
|
||||
{
|
||||
g_string_append_c (p->str, ' ');
|
||||
string_append_double (p->str, dash[i]);
|
||||
}
|
||||
}
|
||||
|
||||
g_string_append (p->str, ";\n");
|
||||
}
|
||||
|
||||
static void
|
||||
render_node_print (Printer *p,
|
||||
GskRenderNode *node)
|
||||
@@ -2850,42 +2539,6 @@ render_node_print (Printer *p,
|
||||
append_rounded_rect_param (p, "clip", gsk_rounded_clip_node_get_clip (node));
|
||||
append_node_param (p, "child", gsk_rounded_clip_node_get_child (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);
|
||||
}
|
||||
@@ -3392,4 +3045,3 @@ gsk_render_node_serialize (GskRenderNode *node)
|
||||
|
||||
return g_string_free_to_bytes (p.str);
|
||||
}
|
||||
|
||||
|
||||
-208
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
-458
@@ -1,458 +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"
|
||||
|
||||
/**
|
||||
* GskStroke:
|
||||
*
|
||||
* A `GskStroke` describes the attributes that are used when stroking a path.
|
||||
*/
|
||||
|
||||
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 [method@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:
|
||||
case GSK_LINE_JOIN_ARCS:
|
||||
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 two strokes are identical.
|
||||
*
|
||||
* Returns: %TRUE if the two 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 [method@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 [method@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;
|
||||
}
|
||||
@@ -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__ */
|
||||
@@ -1,60 +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 */
|
||||
}
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_STROKE_PRIVATE_H__ */
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user