Compare commits
179 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 568ae041d8 | |||
| d5a6cface7 | |||
| 6c9c9ad6c3 | |||
| de08441d4d | |||
| 8a438c46ae | |||
| 5fb1a572c6 | |||
| 1ab45331c0 | |||
| 23e36c9245 | |||
| 0beb416b53 | |||
| 3d27f67107 | |||
| 9574e49df6 | |||
| 0347e4cd76 | |||
| 14a8321b02 | |||
| 5bb4846442 | |||
| 4715afc822 | |||
| 9f58740486 | |||
| 7088acb88b | |||
| 732a607127 | |||
| e8ef875a7b | |||
| 56b17715f1 | |||
| 6775a22cff | |||
| 380ac5804d | |||
| 241c6811f9 | |||
| ba442fc24e | |||
| 9d5c23f342 | |||
| 568ac6b7f6 | |||
| a94cc20067 | |||
| 638a91451a | |||
| 769679d111 | |||
| 306f524e59 | |||
| e86281d81c | |||
| 2e6b7b5b78 | |||
| 90e933a6aa | |||
| e7549f3359 | |||
| fee497f9e1 | |||
| ef4930723b | |||
| a8ff291a12 | |||
| f366ccc0b2 | |||
| ca69fd2b7a | |||
| db2029d931 | |||
| 6f2fd001a0 | |||
| 3327a6ba08 | |||
| 4954e6962f | |||
| 93db1cc89e | |||
| 68b337d457 | |||
| 0d5e54986a | |||
| 822641c161 | |||
| 385ab74922 | |||
| fcf65c7caa | |||
| ece4e59e99 | |||
| 6eea08ff99 | |||
| 372dcba9c9 | |||
| af817a3362 | |||
| 0edd7547c1 | |||
| ce042f7ba1 | |||
| bb2cd7225e | |||
| 5e1fd56345 | |||
| c72588748b | |||
| 48e1d48e7f | |||
| 70a12c4efb | |||
| 6f76c37fed | |||
| cc5cab65a1 | |||
| f3823eff87 | |||
| 13d6e691c2 | |||
| 0bf16d738e | |||
| 2aba50efa0 | |||
| 63ad234391 | |||
| 21d2372396 | |||
| c0b185bee9 | |||
| da4a4f6a25 | |||
| d7764cc6b3 | |||
| a6b2bcbf24 | |||
| 7fa159e94a | |||
| 05c9f3442c | |||
| cef87b102c | |||
| 70c9521cae | |||
| 1abcf3d48a | |||
| 6363f27f95 | |||
| f35053b837 | |||
| d669e3ab6a | |||
| 5707551b79 | |||
| a7c247bccd | |||
| d86d4c5597 | |||
| 73f1dfc762 | |||
| a621bd066b | |||
| 143ca0e17d | |||
| 58c318a4dc | |||
| 210cb3eecd | |||
| 6e6fa3daed | |||
| 2d6ebbb4d5 | |||
| efa4cae949 | |||
| 5e72914c48 | |||
| f3aab662c3 | |||
| c598fa9147 | |||
| b2296a1918 | |||
| 594595d9cd | |||
| 9ac36aeb42 | |||
| a61fe61318 | |||
| 99085605a8 | |||
| 9da1055575 | |||
| b049990356 | |||
| 8207c548cc | |||
| 83960622e3 | |||
| 7cf7870254 | |||
| 7763e883d6 | |||
| 8bacfad171 | |||
| 53e7277584 | |||
| e43b7902be | |||
| f1d81bb7df | |||
| 84cf6de36d | |||
| aaa219497b | |||
| 8515224921 | |||
| f53da409e5 | |||
| b45a2025d9 | |||
| 0946b0b333 | |||
| 1d9ad55c54 | |||
| 94a64329c2 | |||
| ba502a5009 | |||
| e1d2477485 | |||
| 2fef53b154 | |||
| 742ef96748 | |||
| 8d928ad340 | |||
| 32e123fa67 | |||
| df0dd296e9 | |||
| 34e13556b4 | |||
| bdbb1398db | |||
| b00ca0d699 | |||
| 8c4245da2f | |||
| 7e7aa17484 | |||
| 4e6a04152b | |||
| fb56929791 | |||
| c9022ac2d5 | |||
| 0b6392c0a8 | |||
| 526c0404c2 | |||
| cdacfc5a21 | |||
| ab809d1dc3 | |||
| 6dd9048c6d | |||
| 3523d56122 | |||
| 5c601b673e | |||
| 465a34e6b0 | |||
| aa82a400df | |||
| 1f3db35271 | |||
| 18cce46ed2 | |||
| e78148bae5 | |||
| 41237509ad | |||
| 576e8a2090 | |||
| d0f77c1db4 | |||
| e28ff79bec | |||
| dccf6b55bd | |||
| d5ea376e7b | |||
| 40707a6af0 | |||
| 9b71c9dfc6 | |||
| 64308317f8 | |||
| f920723eae | |||
| c581f4c96f | |||
| 4998c90b10 | |||
| 412b23a146 | |||
| c179013790 | |||
| c6eb7fd483 | |||
| 7c58370673 | |||
| 84737a5159 | |||
| 48804c81f3 | |||
| c79ec355af | |||
| ce5d74d7df | |||
| 5bcc943ec3 | |||
| f5d68bb586 | |||
| 6d1537647c | |||
| ae2c10996a | |||
| 81e9de3778 | |||
| 42a704fefb | |||
| e57eaf16b4 | |||
| 9aabb0e98d | |||
| 9590a5f45e | |||
| 32a3690a3c | |||
| 43af0ee514 | |||
| 3912d6aba9 | |||
| 871685e271 | |||
| 5e9daa9728 | |||
| 412bc1713a |
@@ -1,4 +1,7 @@
|
||||
Overview of Changes in 4.11.4, xx-xx-xxxx
|
||||
Overview of Changes in 4.11.5, xx-xx-xxxx
|
||||
=========================================
|
||||
|
||||
Overview of Changes in 4.11.4, 03-07-2023
|
||||
=========================================
|
||||
|
||||
* GtkFileChooser:
|
||||
@@ -26,6 +29,9 @@ Overview of Changes in 4.11.4, xx-xx-xxxx
|
||||
* GtkDropDown:
|
||||
- Update on expression changes
|
||||
|
||||
* GtkMapListModel:
|
||||
- Implement GtkSectionModel
|
||||
|
||||
* Accessibility:
|
||||
- Improvements all over the place: GtkButton, GtkPasswordEntry,
|
||||
GtkFontChooserDialog, GtkColorChooserDialog, GtkShortcutsWindow,
|
||||
@@ -72,6 +78,7 @@ Overview of Changes in 4.11.4, xx-xx-xxxx
|
||||
* Build:
|
||||
- Require GLib 2.76
|
||||
- Make asan builds work again
|
||||
- Fix the build if ld is not ld.bdf
|
||||
|
||||
* Translation updates:
|
||||
Brazilian Portuguese
|
||||
|
||||
@@ -30,12 +30,12 @@ Views display data from a **_model_**. Models implement the [`iface@Gio.ListMode
|
||||
interface and can be provided in a variety of ways:
|
||||
|
||||
* List model implementations for many specific types of data already exist, for
|
||||
example `GtkDirectoryList` or `GtkStringList`.
|
||||
example [`class@Gtk.DirectoryList`] or [`class@Gtk.StringList`].
|
||||
|
||||
* There are generic list model implementations like`GListStore` that allow building
|
||||
* There are generic list model implementations like [`class@Gio.ListStore`] that allow building
|
||||
lists of arbitrary objects.
|
||||
|
||||
* Wrapping list models like `GtkFilterListModel` or `GtkSortListModel`
|
||||
* Wrapping list models like [`class@Gtk.FilterListModel`] or [`class@Gtk.SortListModel`]
|
||||
modify, adapt or combine other models.
|
||||
|
||||
* Last but not least, developers are encouraged to create their own `GListModel`
|
||||
@@ -133,8 +133,8 @@ tradeoffs of those and experiment with them.
|
||||
|
||||
GTK offers a wide variety of wrapping models which change or supplement an
|
||||
existing model (or models) in some way. But when it comes to storing your
|
||||
actual data, there are only a few ready-made choices available: [`class@Gio.ListStore`]
|
||||
and [`class@Gtk.StringList`].
|
||||
actual data, there are only a few ready-made choices available:
|
||||
[`class@Gio.ListStore`], [`class@Gtk.StringList`], and [`class@Gtk.DirectoryList`].
|
||||
|
||||
`GListStore` is backed by a balanced tree and has performance characteristics
|
||||
that are expected for that data structure. It works reasonably well for dataset
|
||||
@@ -147,6 +147,10 @@ that are expected for that data structure. `GtkStringList` is a good fit for any
|
||||
place where you would otherwise use `char*[]` and works best if the dataset
|
||||
is not very dynamic.
|
||||
|
||||
`GtkDirectoryList` is a list model that wraps [`method@Gio.File.enumerate_children_async`].
|
||||
It presents a `GListModel` and fills it asynchronously with the [`iface@Gio.File`]s
|
||||
returned from that function.
|
||||
|
||||
If these models don't fit your use case or scalability requirements, you
|
||||
should make a custom `GListModel` implementation. It is a small interface and
|
||||
not very hard to implement.
|
||||
@@ -199,7 +203,7 @@ the `.data-table` style class.
|
||||
## Sections
|
||||
|
||||
List models can optionally group their items into **_sections_**, by implementing
|
||||
the `GtkSectionModel` interface. Both `GtkListView` and `GtkGridView` can
|
||||
the `GtkSectionModel` interface. `GtkListView` can
|
||||
display headers for sections, by installing a separate **_header factory_**.
|
||||
|
||||
Many GTK list models support section inherently, or they pass through the
|
||||
|
||||
@@ -104,6 +104,7 @@ struct _GdkDisplay
|
||||
gsize vk_pipeline_cache_size;
|
||||
char *vk_pipeline_cache_etag;
|
||||
guint vk_save_pipeline_cache_source;
|
||||
GHashTable *vk_shader_modules;
|
||||
|
||||
guint vulkan_refcount;
|
||||
#endif /* GDK_RENDERING_VULKAN */
|
||||
|
||||
+1
-1
@@ -1552,7 +1552,7 @@ gdk_gl_context_check_extensions (GdkGLContext *context)
|
||||
|
||||
priv->has_sync = gdk_gl_context_check_version (context, "3.2", "3.0") ||
|
||||
epoxy_has_gl_extension ("GL_ARB_sync") ||
|
||||
epoxy_has_gl_extension ("GK_APPLE_sync");
|
||||
epoxy_has_gl_extension ("GL_APPLE_sync");
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
{
|
||||
|
||||
+2
-1
@@ -25,7 +25,8 @@
|
||||
* multiple frames, and will be used for a long time.
|
||||
*
|
||||
* There are various ways to create `GdkTexture` objects from a
|
||||
* [class@GdkPixbuf.Pixbuf], or a Cairo surface, or other pixel data.
|
||||
* [class@GdkPixbuf.Pixbuf], or from bytes stored in memory, a file, or a
|
||||
* [struct@Gio.Resource].
|
||||
*
|
||||
* The ownership of the pixel data is transferred to the `GdkTexture`
|
||||
* instance; you can only make a copy of it, via [method@Gdk.Texture.download].
|
||||
|
||||
@@ -383,6 +383,13 @@ gdk_vulkan_context_check_swapchain (GdkVulkanContext *context,
|
||||
|
||||
device = gdk_vulkan_context_get_device (context);
|
||||
|
||||
/*
|
||||
* Wait for device to be idle because this function is also called in window resizes.
|
||||
* And if we destroy old swapchain it also destroy the old VkImages, those images could
|
||||
* be in use by a vulkan render.
|
||||
*/
|
||||
vkDeviceWaitIdle (device);
|
||||
|
||||
res = GDK_VK_CHECK (vkGetPhysicalDeviceSurfaceCapabilitiesKHR, gdk_vulkan_context_get_physical_device (context),
|
||||
priv->surface,
|
||||
&capabilities);
|
||||
@@ -1593,6 +1600,8 @@ gdk_display_create_vulkan_instance (GdkDisplay *display,
|
||||
|
||||
gdk_display_create_pipeline_cache (display);
|
||||
|
||||
display->vk_shader_modules = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -1614,6 +1623,9 @@ gdk_display_ref_vulkan (GdkDisplay *display,
|
||||
void
|
||||
gdk_display_unref_vulkan (GdkDisplay *display)
|
||||
{
|
||||
GHashTableIter iter;
|
||||
gpointer key, value;
|
||||
|
||||
g_return_if_fail (GDK_IS_DISPLAY (display));
|
||||
g_return_if_fail (display->vulkan_refcount > 0);
|
||||
|
||||
@@ -1621,6 +1633,16 @@ gdk_display_unref_vulkan (GdkDisplay *display)
|
||||
if (display->vulkan_refcount > 0)
|
||||
return;
|
||||
|
||||
display->vk_shader_modules = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
g_hash_table_iter_init (&iter, display->vk_shader_modules);
|
||||
while (g_hash_table_iter_next (&iter, &key, &value))
|
||||
{
|
||||
g_free (key);
|
||||
vkDestroyShaderModule (display->vk_device,
|
||||
value,
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (display->vk_save_pipeline_cache_source)
|
||||
{
|
||||
gdk_vulkan_save_pipeline_cache_cb (display);
|
||||
@@ -1647,6 +1669,47 @@ gdk_display_unref_vulkan (GdkDisplay *display)
|
||||
display->vk_instance = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VkShaderModule
|
||||
gdk_display_get_vk_shader_module (GdkDisplay *self,
|
||||
const char *resource_name)
|
||||
{
|
||||
VkShaderModule shader;
|
||||
GError *error = NULL;
|
||||
GBytes *bytes;
|
||||
|
||||
shader = g_hash_table_lookup (self->vk_shader_modules, resource_name);
|
||||
if (shader)
|
||||
return shader;
|
||||
|
||||
bytes = g_resources_lookup_data (resource_name, 0, &error);
|
||||
if (bytes == NULL)
|
||||
{
|
||||
GDK_DEBUG (VULKAN, "Error loading shader data: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (GDK_VK_CHECK (vkCreateShaderModule, self->vk_device,
|
||||
&(VkShaderModuleCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
||||
.codeSize = g_bytes_get_size (bytes),
|
||||
.pCode = (uint32_t *) g_bytes_get_data (bytes, NULL),
|
||||
},
|
||||
NULL,
|
||||
&shader) == VK_SUCCESS)
|
||||
{
|
||||
g_hash_table_insert (self->vk_shader_modules, g_strdup (resource_name), shader);
|
||||
}
|
||||
else
|
||||
{
|
||||
shader = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
#else /* GDK_RENDERING_VULKAN */
|
||||
|
||||
static void
|
||||
|
||||
@@ -73,6 +73,9 @@ gboolean gdk_display_ref_vulkan (GdkDisp
|
||||
GError **error);
|
||||
void gdk_display_unref_vulkan (GdkDisplay *display);
|
||||
|
||||
VkShaderModule gdk_display_get_vk_shader_module (GdkDisplay *display,
|
||||
const char *resource_name);
|
||||
|
||||
VkPipelineCache gdk_vulkan_context_get_pipeline_cache (GdkVulkanContext *self);
|
||||
void gdk_vulkan_context_pipeline_cache_updated (GdkVulkanContext *self);
|
||||
|
||||
|
||||
@@ -3064,9 +3064,9 @@ tablet_tool_handle_button (void *data,
|
||||
tablet->pointer_info.press_serial = serial;
|
||||
|
||||
if (button == BTN_STYLUS)
|
||||
n_button = GDK_BUTTON_SECONDARY;
|
||||
else if (button == BTN_STYLUS2)
|
||||
n_button = GDK_BUTTON_MIDDLE;
|
||||
else if (button == BTN_STYLUS2)
|
||||
n_button = GDK_BUTTON_SECONDARY;
|
||||
else if (button == BTN_STYLUS3)
|
||||
n_button = 8; /* Back */
|
||||
else
|
||||
|
||||
+15
-15
@@ -139,7 +139,6 @@ static GSourceFuncs event_funcs = {
|
||||
|
||||
static GdkSurface *mouse_window = NULL;
|
||||
static GdkSurface *mouse_window_ignored_leave = NULL;
|
||||
static int current_x, current_y;
|
||||
static int current_root_x, current_root_y;
|
||||
|
||||
static UINT got_gdk_events_message;
|
||||
@@ -1522,14 +1521,15 @@ generate_button_event (GdkEventType type,
|
||||
GdkEvent *event;
|
||||
GdkDeviceManagerWin32 *device_manager;
|
||||
GdkWin32Surface *impl = GDK_WIN32_SURFACE (window);
|
||||
double x, y;
|
||||
|
||||
if (_gdk_input_ignore_core > 0)
|
||||
return;
|
||||
|
||||
device_manager = GDK_DEVICE_MANAGER_WIN32 (_gdk_device_manager);
|
||||
|
||||
current_x = (gint16) GET_X_LPARAM (msg->lParam) / impl->surface_scale;
|
||||
current_y = (gint16) GET_Y_LPARAM (msg->lParam) / impl->surface_scale;
|
||||
x = (double) GET_X_LPARAM (msg->lParam) / impl->surface_scale;
|
||||
y = (double) GET_Y_LPARAM (msg->lParam) / impl->surface_scale;
|
||||
|
||||
_gdk_device_virtual_set_active (_gdk_device_manager->core_pointer,
|
||||
_gdk_device_manager->system_pointer);
|
||||
@@ -1541,10 +1541,10 @@ generate_button_event (GdkEventType type,
|
||||
_gdk_win32_get_next_tick (msg->time),
|
||||
build_pointer_event_state (msg),
|
||||
button,
|
||||
current_x,
|
||||
current_y,
|
||||
x,
|
||||
y,
|
||||
NULL);
|
||||
|
||||
|
||||
_gdk_win32_append_event (event);
|
||||
}
|
||||
|
||||
@@ -2350,19 +2350,19 @@ gdk_event_translate (MSG *msg,
|
||||
* sends WM_MOUSEMOVE messages after a new window is shown under
|
||||
* the mouse, even if the mouse hasn't moved. This disturbs gtk.
|
||||
*/
|
||||
if (msg->pt.x / impl->surface_scale == current_root_x &&
|
||||
msg->pt.y / impl->surface_scale == current_root_y)
|
||||
if (msg->pt.x == current_root_x &&
|
||||
msg->pt.y == current_root_y)
|
||||
break;
|
||||
|
||||
current_root_x = msg->pt.x / impl->surface_scale;
|
||||
current_root_y = msg->pt.y / impl->surface_scale;
|
||||
current_root_x = msg->pt.x;
|
||||
current_root_y = msg->pt.y;
|
||||
|
||||
if (impl->drag_move_resize_context.op != GDK_WIN32_DRAGOP_NONE)
|
||||
gdk_win32_surface_do_move_resize_drag (window, current_root_x, current_root_y);
|
||||
gdk_win32_surface_do_move_resize_drag (window, msg->pt.x, msg->pt.y);
|
||||
else if (_gdk_input_ignore_core == 0)
|
||||
{
|
||||
current_x = (gint16) GET_X_LPARAM (msg->lParam) / impl->surface_scale;
|
||||
current_y = (gint16) GET_Y_LPARAM (msg->lParam) / impl->surface_scale;
|
||||
double x = (double) GET_X_LPARAM (msg->lParam) / impl->surface_scale;
|
||||
double y = (double) GET_Y_LPARAM (msg->lParam) / impl->surface_scale;
|
||||
|
||||
_gdk_device_virtual_set_active (_gdk_device_manager->core_pointer,
|
||||
_gdk_device_manager->system_pointer);
|
||||
@@ -2372,8 +2372,8 @@ gdk_event_translate (MSG *msg,
|
||||
NULL,
|
||||
_gdk_win32_get_next_tick (msg->time),
|
||||
build_pointer_event_state (msg),
|
||||
current_x,
|
||||
current_y,
|
||||
x,
|
||||
y,
|
||||
NULL);
|
||||
|
||||
_gdk_win32_append_event (event);
|
||||
|
||||
@@ -271,9 +271,10 @@ winpointer_make_event (GdkDeviceWinpointer *device,
|
||||
y /= impl->surface_scale;
|
||||
|
||||
state = 0;
|
||||
if (info->dwKeyStates & POINTER_MOD_CTRL)
|
||||
/* Note that info->dwKeyStates is not reliable, use GetKeyState() */
|
||||
if (GetKeyState (VK_CONTROL) < 0)
|
||||
state |= GDK_CONTROL_MASK;
|
||||
if (info->dwKeyStates & POINTER_MOD_SHIFT)
|
||||
if (GetKeyState (VK_SHIFT) < 0)
|
||||
state |= GDK_SHIFT_MASK;
|
||||
if (GetKeyState (VK_MENU) < 0)
|
||||
state |= GDK_ALT_MASK;
|
||||
|
||||
@@ -3535,6 +3535,8 @@ setup_drag_move_resize_context (GdkSurface *surface,
|
||||
context->button = button;
|
||||
context->start_root_x = root_x;
|
||||
context->start_root_y = root_y;
|
||||
context->current_root_x = root_x;
|
||||
context->current_root_y = root_y;
|
||||
context->timestamp = timestamp;
|
||||
context->start_rect = rect;
|
||||
|
||||
@@ -3650,6 +3652,16 @@ gdk_win32_surface_do_move_resize_drag (GdkSurface *window,
|
||||
if (!_gdk_win32_get_window_rect (window, &rect))
|
||||
return;
|
||||
|
||||
x /= impl->surface_scale;
|
||||
y /= impl->surface_scale;
|
||||
|
||||
if (context->current_root_x == x &&
|
||||
context->current_root_y == y)
|
||||
return;
|
||||
|
||||
context->current_root_x = x;
|
||||
context->current_root_y = y;
|
||||
|
||||
new_rect = context->start_rect;
|
||||
diffx = (x - context->start_root_x) * impl->surface_scale;
|
||||
diffy = (y - context->start_root_y) * impl->surface_scale;
|
||||
|
||||
@@ -137,6 +137,12 @@ struct _GdkW32DragMoveResizeContext
|
||||
int start_root_x;
|
||||
int start_root_y;
|
||||
|
||||
/* Last processed cursor position. Values are divided by the window
|
||||
* scale.
|
||||
*/
|
||||
int current_root_x;
|
||||
int current_root_y;
|
||||
|
||||
/* Initial window rectangle (position and size).
|
||||
* The window is resized/moved relative to this (see start_root_*).
|
||||
*/
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "gskglcommandqueueprivate.h"
|
||||
#include "gskglcompilerprivate.h"
|
||||
#include "gskglglyphlibraryprivate.h"
|
||||
#include "gskglglyphylibraryprivate.h"
|
||||
#include "gskgliconlibraryprivate.h"
|
||||
#include "gskglprogramprivate.h"
|
||||
#include "gskglshadowlibraryprivate.h"
|
||||
@@ -273,6 +274,7 @@ gsk_gl_driver_dispose (GObject *object)
|
||||
}
|
||||
|
||||
g_clear_object (&self->glyphs_library);
|
||||
g_clear_object (&self->glyphy_library);
|
||||
g_clear_object (&self->icons_library);
|
||||
g_clear_object (&self->shadows_library);
|
||||
|
||||
@@ -463,6 +465,7 @@ gsk_gl_driver_new (GskGLCommandQueue *command_queue,
|
||||
}
|
||||
|
||||
self->glyphs_library = gsk_gl_glyph_library_new (self);
|
||||
self->glyphy_library = gsk_gl_glyphy_library_new (self);
|
||||
self->icons_library = gsk_gl_icon_library_new (self);
|
||||
self->shadows_library = gsk_gl_shadow_library_new (self);
|
||||
|
||||
@@ -573,6 +576,8 @@ gsk_gl_driver_begin_frame (GskGLDriver *self,
|
||||
self->current_frame_id);
|
||||
gsk_gl_texture_library_begin_frame (GSK_GL_TEXTURE_LIBRARY (self->glyphs_library),
|
||||
self->current_frame_id);
|
||||
gsk_gl_texture_library_begin_frame (GSK_GL_TEXTURE_LIBRARY (self->glyphy_library),
|
||||
self->current_frame_id);
|
||||
|
||||
/* Cleanup old shadows */
|
||||
gsk_gl_shadow_library_begin_frame (self->shadows_library);
|
||||
|
||||
@@ -97,6 +97,7 @@ struct _GskGLDriver
|
||||
GskGLCommandQueue *command_queue;
|
||||
|
||||
GskGLGlyphLibrary *glyphs_library;
|
||||
GskGLGlyphyLibrary *glyphy_library;
|
||||
GskGLIconLibrary *icons_library;
|
||||
GskGLShadowLibrary *shadows_library;
|
||||
|
||||
|
||||
@@ -0,0 +1,544 @@
|
||||
/* gskglglyphylibrary.c
|
||||
*
|
||||
* Copyright 2020 Christian Hergert <chergert@redhat.com>
|
||||
*
|
||||
* 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
/* Some of the glyphy cache is based upon the original glyphy code.
|
||||
* It's license is provided below.
|
||||
*/
|
||||
/*
|
||||
* Copyright 2012 Google, Inc. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Google Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gdk/gdkglcontextprivate.h>
|
||||
#include <gdk/gdkmemoryformatprivate.h>
|
||||
#include <gdk/gdkprofilerprivate.h>
|
||||
|
||||
#include "gskglcommandqueueprivate.h"
|
||||
#include "gskgldriverprivate.h"
|
||||
#include "gskglglyphylibraryprivate.h"
|
||||
#include "gskdebugprivate.h"
|
||||
|
||||
#include "gskpathprivate.h"
|
||||
|
||||
#include <glyphy.h>
|
||||
|
||||
#define TOLERANCE (1/2048.)
|
||||
#define MIN_FONT_SIZE 14
|
||||
#define GRID_SIZE 20 /* Per EM */
|
||||
#define ENLIGHTEN_MAX .01 /* Per EM */
|
||||
#define EMBOLDEN_MAX .024 /* Per EM */
|
||||
|
||||
/* We split the atlas into cells of size 64x8, so the minimum number of
|
||||
* bytes we store per glyph is 2048, and an atlas of size 2048x1024 can
|
||||
* hold at most 4096 glyphs. We need 5 and 7 bits to store the position
|
||||
* of a glyph in the atlas.
|
||||
*
|
||||
* We allocate each glyph a column of as many vertically adjacent cells
|
||||
* as it needs.
|
||||
*/
|
||||
#define ITEM_W 64
|
||||
#define ITEM_H_QUANTUM 8
|
||||
|
||||
G_DEFINE_TYPE (GskGLGlyphyLibrary, gsk_gl_glyphy_library, GSK_TYPE_GL_TEXTURE_LIBRARY)
|
||||
|
||||
GskGLGlyphyLibrary *
|
||||
gsk_gl_glyphy_library_new (GskGLDriver *driver)
|
||||
{
|
||||
g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), NULL);
|
||||
|
||||
return g_object_new (GSK_TYPE_GL_GLYPHY_LIBRARY,
|
||||
"driver", driver,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static guint
|
||||
gsk_gl_glyphy_key_hash (gconstpointer data)
|
||||
{
|
||||
const GskGLGlyphyKey *key = data;
|
||||
|
||||
/* malloc()'d pointers already guarantee 3 bits from the LSB on 64-bit and
|
||||
* 2 bits from the LSB on 32-bit. Shift by enough to give us 256 entries
|
||||
* in our front cache for the glyph since languages will naturally cluster
|
||||
* for us.
|
||||
*/
|
||||
|
||||
return (key->font << 8) ^ key->glyph;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_gl_glyphy_key_equal (gconstpointer v1,
|
||||
gconstpointer v2)
|
||||
{
|
||||
return memcmp (v1, v2, sizeof (GskGLGlyphyKey)) == 0;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_glyphy_key_free (gpointer data)
|
||||
{
|
||||
GskGLGlyphyKey *key = data;
|
||||
|
||||
g_slice_free (GskGLGlyphyKey, key);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_glyphy_value_free (gpointer data)
|
||||
{
|
||||
g_slice_free (GskGLGlyphyValue, data);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_glyphy_library_clear_cache (GskGLTextureLibrary *library)
|
||||
{
|
||||
GskGLGlyphyLibrary *self = (GskGLGlyphyLibrary *)library;
|
||||
|
||||
g_assert (GSK_IS_GL_GLYPHY_LIBRARY (self));
|
||||
|
||||
memset (self->front, 0, sizeof self->front);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_glyphy_library_init_atlas (GskGLTextureLibrary *library,
|
||||
GskGLTextureAtlas *atlas)
|
||||
{
|
||||
g_assert (GSK_IS_GL_GLYPHY_LIBRARY (library));
|
||||
g_assert (atlas != NULL);
|
||||
|
||||
atlas->cursor_x = 0;
|
||||
atlas->cursor_y = 0;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_gl_glyphy_library_allocate (GskGLTextureLibrary *library,
|
||||
GskGLTextureAtlas *atlas,
|
||||
int width,
|
||||
int height,
|
||||
int *out_x,
|
||||
int *out_y)
|
||||
{
|
||||
GskGLGlyphyLibrary *self = (GskGLGlyphyLibrary *)library;
|
||||
int cursor_save_x;
|
||||
int cursor_save_y;
|
||||
|
||||
g_assert (GSK_IS_GL_GLYPHY_LIBRARY (self));
|
||||
g_assert (atlas != NULL);
|
||||
|
||||
cursor_save_x = atlas->cursor_x;
|
||||
cursor_save_y = atlas->cursor_y;
|
||||
|
||||
if ((height & (self->item_h_q-1)) != 0)
|
||||
height = (height + self->item_h_q - 1) & ~(self->item_h_q - 1);
|
||||
|
||||
/* Require allocations in columns of 64 and rows of 8 */
|
||||
g_assert (width == self->item_w);
|
||||
g_assert ((height % self->item_h_q) == 0);
|
||||
|
||||
if (atlas->cursor_y + height > atlas->height)
|
||||
{
|
||||
/* Go to next column */
|
||||
atlas->cursor_x += self->item_w;
|
||||
atlas->cursor_y = 0;
|
||||
}
|
||||
|
||||
if (atlas->cursor_x + width <= atlas->width &&
|
||||
atlas->cursor_y + height <= atlas->height)
|
||||
{
|
||||
*out_x = atlas->cursor_x;
|
||||
*out_y = atlas->cursor_y;
|
||||
atlas->cursor_y += height;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
atlas->cursor_x = cursor_save_x;
|
||||
atlas->cursor_y = cursor_save_y;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_glyphy_library_finalize (GObject *object)
|
||||
{
|
||||
GskGLGlyphyLibrary *self = (GskGLGlyphyLibrary *)object;
|
||||
|
||||
g_clear_pointer (&self->acc, glyphy_arc_accumulator_destroy);
|
||||
g_clear_pointer (&self->acc_endpoints, g_array_unref);
|
||||
|
||||
G_OBJECT_CLASS (gsk_gl_glyphy_library_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
GQuark quark_glyphy_font_key;
|
||||
|
||||
static void
|
||||
gsk_gl_glyphy_library_class_init (GskGLGlyphyLibraryClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GskGLTextureLibraryClass *library_class = GSK_GL_TEXTURE_LIBRARY_CLASS (klass);
|
||||
|
||||
quark_glyphy_font_key = g_quark_from_static_string ("glyphy-font-key");
|
||||
|
||||
object_class->finalize = gsk_gl_glyphy_library_finalize;
|
||||
|
||||
library_class->allocate = gsk_gl_glyphy_library_allocate;
|
||||
library_class->clear_cache = gsk_gl_glyphy_library_clear_cache;
|
||||
library_class->init_atlas = gsk_gl_glyphy_library_init_atlas;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_glyphy_library_init (GskGLGlyphyLibrary *self)
|
||||
{
|
||||
GskGLTextureLibrary *tl = (GskGLTextureLibrary *)self;
|
||||
|
||||
tl->max_entry_size = 0;
|
||||
tl->max_frame_age = 512;
|
||||
tl->atlas_width = 2048;
|
||||
tl->atlas_height = 1024;
|
||||
gsk_gl_texture_library_set_funcs (tl,
|
||||
gsk_gl_glyphy_key_hash,
|
||||
gsk_gl_glyphy_key_equal,
|
||||
gsk_gl_glyphy_key_free,
|
||||
gsk_gl_glyphy_value_free);
|
||||
|
||||
self->acc = glyphy_arc_accumulator_create ();
|
||||
self->acc_endpoints = g_array_new (FALSE, FALSE, sizeof (glyphy_arc_endpoint_t));
|
||||
self->item_w = ITEM_W;
|
||||
self->item_h_q = ITEM_H_QUANTUM;
|
||||
}
|
||||
|
||||
static glyphy_bool_t
|
||||
accumulate_endpoint (glyphy_arc_endpoint_t *endpoint,
|
||||
GArray *endpoints)
|
||||
{
|
||||
g_array_append_vals (endpoints, endpoint, 1);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
move_to (hb_draw_funcs_t *dfuncs,
|
||||
GskPathBuilder *builder,
|
||||
hb_draw_state_t *st,
|
||||
float x,
|
||||
float y,
|
||||
void *data)
|
||||
{
|
||||
gsk_path_builder_move_to (builder, x, y);
|
||||
}
|
||||
|
||||
static void
|
||||
line_to (hb_draw_funcs_t *dfuncs,
|
||||
GskPathBuilder *builder,
|
||||
hb_draw_state_t *st,
|
||||
float x,
|
||||
float y,
|
||||
void *data)
|
||||
{
|
||||
gsk_path_builder_line_to (builder, x, y);
|
||||
}
|
||||
|
||||
static void
|
||||
cubic_to (hb_draw_funcs_t *dfuncs,
|
||||
GskPathBuilder *builder,
|
||||
hb_draw_state_t *st,
|
||||
float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2,
|
||||
float x3,
|
||||
float y3,
|
||||
void *data)
|
||||
{
|
||||
gsk_path_builder_cubic_to (builder, x1, y1, x2, y2, x3, y3);
|
||||
}
|
||||
|
||||
static void
|
||||
close_path (hb_draw_funcs_t *dfuncs,
|
||||
GskPathBuilder *builder,
|
||||
hb_draw_state_t *st,
|
||||
void *data)
|
||||
{
|
||||
gsk_path_builder_close (builder);
|
||||
}
|
||||
|
||||
static hb_draw_funcs_t *
|
||||
gsk_path_get_draw_funcs (void)
|
||||
{
|
||||
static hb_draw_funcs_t *funcs = NULL;
|
||||
|
||||
if (!funcs)
|
||||
{
|
||||
funcs = hb_draw_funcs_create ();
|
||||
|
||||
hb_draw_funcs_set_move_to_func (funcs, (hb_draw_move_to_func_t) move_to, NULL, NULL);
|
||||
hb_draw_funcs_set_line_to_func (funcs, (hb_draw_line_to_func_t) line_to, NULL, NULL);
|
||||
hb_draw_funcs_set_cubic_to_func (funcs, (hb_draw_cubic_to_func_t) cubic_to, NULL, NULL);
|
||||
hb_draw_funcs_set_close_path_func (funcs, (hb_draw_close_path_func_t) close_path, NULL, NULL);
|
||||
|
||||
hb_draw_funcs_make_immutable (funcs);
|
||||
}
|
||||
|
||||
return funcs;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
acc_callback (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer user_data)
|
||||
{
|
||||
glyphy_arc_accumulator_t *acc = user_data;
|
||||
glyphy_point_t p0, p1, p2, p3;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case GSK_PATH_MOVE:
|
||||
p0.x = pts[0].x; p0.y = pts[0].y;
|
||||
glyphy_arc_accumulator_move_to (acc, &p0);
|
||||
break;
|
||||
case GSK_PATH_CLOSE:
|
||||
glyphy_arc_accumulator_close_path (acc);
|
||||
break;
|
||||
case GSK_PATH_LINE:
|
||||
p1.x = pts[1].x; p1.y = pts[1].y;
|
||||
glyphy_arc_accumulator_line_to (acc, &p1);
|
||||
break;
|
||||
case GSK_PATH_QUAD:
|
||||
p1.x = pts[1].x; p1.y = pts[1].y;
|
||||
p2.x = pts[2].x; p2.y = pts[2].y;
|
||||
/* This glyphy api is mis-named */
|
||||
glyphy_arc_accumulator_conic_to (acc, &p1, &p2);
|
||||
break;
|
||||
case GSK_PATH_CUBIC:
|
||||
p1.x = pts[1].x; p1.y = pts[1].y;
|
||||
p2.x = pts[2].x; p2.y = pts[2].y;
|
||||
p3.x = pts[3].x; p3.y = pts[3].y;
|
||||
glyphy_arc_accumulator_cubic_to (acc, &p1, &p2, &p3);
|
||||
break;
|
||||
case GSK_PATH_CONIC:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
encode_glyph (GskGLGlyphyLibrary *self,
|
||||
hb_font_t *font,
|
||||
unsigned int glyph_index,
|
||||
double tolerance_per_em,
|
||||
glyphy_rgba_t *buffer,
|
||||
guint buffer_len,
|
||||
guint *output_len,
|
||||
guint *nominal_width,
|
||||
guint *nominal_height,
|
||||
glyphy_extents_t *extents)
|
||||
{
|
||||
hb_face_t *face = hb_font_get_face (font);
|
||||
guint upem = hb_face_get_upem (face);
|
||||
double tolerance = upem * tolerance_per_em;
|
||||
double faraway = (double)upem / (MIN_FONT_SIZE * M_SQRT2);
|
||||
double unit_size = (double) upem / GRID_SIZE;
|
||||
double enlighten_max = (double) upem * ENLIGHTEN_MAX;
|
||||
double embolden_max = (double) upem * EMBOLDEN_MAX;
|
||||
double avg_fetch_achieved;
|
||||
GskPathBuilder *builder;
|
||||
GskPath *path, *simplified;
|
||||
|
||||
self->acc_endpoints->len = 0;
|
||||
|
||||
glyphy_arc_accumulator_reset (self->acc);
|
||||
glyphy_arc_accumulator_set_tolerance (self->acc, tolerance);
|
||||
glyphy_arc_accumulator_set_callback (self->acc,
|
||||
(glyphy_arc_endpoint_accumulator_callback_t)accumulate_endpoint,
|
||||
self->acc_endpoints);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
#if HB_VERSION_ATLEAST (7, 0, 0)
|
||||
hb_font_draw_glyph (font, glyph_index, gsk_path_get_draw_funcs (), builder);
|
||||
#else
|
||||
hb_font_get_glyph_shape (font, glyph_index, gsk_path_get_draw_funcs (), builder);
|
||||
#endif
|
||||
path = gsk_path_builder_free_to_path (builder);
|
||||
simplified = gsk_path_op (GSK_PATH_OP_SIMPLIFY, GSK_FILL_RULE_WINDING, path, NULL);
|
||||
gsk_path_foreach (simplified, -1, acc_callback, self->acc);
|
||||
gsk_path_unref (simplified);
|
||||
gsk_path_unref (path);
|
||||
|
||||
if (!glyphy_arc_accumulator_successful (self->acc))
|
||||
return FALSE;
|
||||
|
||||
g_assert (glyphy_arc_accumulator_get_error (self->acc) <= tolerance);
|
||||
|
||||
if (self->acc_endpoints->len > 0)
|
||||
glyphy_outline_winding_from_even_odd ((gpointer)self->acc_endpoints->data,
|
||||
self->acc_endpoints->len,
|
||||
FALSE);
|
||||
|
||||
if (!glyphy_arc_list_encode_blob2 ((gpointer)self->acc_endpoints->data,
|
||||
self->acc_endpoints->len,
|
||||
buffer,
|
||||
buffer_len,
|
||||
faraway,
|
||||
unit_size,
|
||||
enlighten_max,
|
||||
embolden_max,
|
||||
&avg_fetch_achieved,
|
||||
output_len,
|
||||
nominal_width,
|
||||
nominal_height,
|
||||
extents))
|
||||
return FALSE;
|
||||
|
||||
glyphy_extents_scale (extents, 1./upem, 1./upem);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static inline hb_font_t *
|
||||
get_nominal_size_hb_font (PangoFont *font)
|
||||
{
|
||||
hb_font_t *hbfont;
|
||||
const float *coords;
|
||||
unsigned int length;
|
||||
|
||||
hbfont = (hb_font_t *) g_object_get_data ((GObject *)font, "glyph-nominal-size-font");
|
||||
if (hbfont == NULL)
|
||||
{
|
||||
hbfont = hb_font_create (hb_font_get_face (pango_font_get_hb_font (font)));
|
||||
coords = hb_font_get_var_coords_design (pango_font_get_hb_font (font), &length);
|
||||
if (length > 0)
|
||||
hb_font_set_var_coords_design (hbfont, coords, length);
|
||||
|
||||
g_object_set_data_full ((GObject *)font, "glyphy-nominal-size-font",
|
||||
hbfont, (GDestroyNotify)hb_font_destroy);
|
||||
}
|
||||
|
||||
return hbfont;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gsk_gl_glyphy_library_add (GskGLGlyphyLibrary *self,
|
||||
GskGLGlyphyKey *key,
|
||||
PangoFont *font,
|
||||
const GskGLGlyphyValue **out_value)
|
||||
{
|
||||
static glyphy_rgba_t buffer[4096 * 16];
|
||||
GskGLTextureLibrary *tl = (GskGLTextureLibrary *)self;
|
||||
GskGLGlyphyValue *value;
|
||||
glyphy_extents_t extents;
|
||||
hb_font_t *hbfont;
|
||||
guint packed_x;
|
||||
guint packed_y;
|
||||
guint nominal_w, nominal_h;
|
||||
guint output_len = 0;
|
||||
guint texture_id;
|
||||
guint width, height;
|
||||
|
||||
g_assert (GSK_IS_GL_GLYPHY_LIBRARY (self));
|
||||
g_assert (key != NULL);
|
||||
g_assert (font != NULL);
|
||||
g_assert (out_value != NULL);
|
||||
|
||||
hbfont = get_nominal_size_hb_font (font);
|
||||
|
||||
/* Convert the glyph to a list of arcs */
|
||||
if (!encode_glyph (self, hbfont, key->glyph, TOLERANCE,
|
||||
buffer, sizeof buffer, &output_len,
|
||||
&nominal_w, &nominal_h, &extents))
|
||||
return FALSE;
|
||||
|
||||
/* Allocate space for list within atlas */
|
||||
width = self->item_w;
|
||||
height = (output_len + width - 1) / width;
|
||||
GSK_DEBUG (GLYPH_CACHE, "font %u glyph %u: %u bytes (%u x %u)", key->font, key->glyph, output_len * 4, width, height);
|
||||
|
||||
value = gsk_gl_texture_library_pack (tl, key, sizeof *value,
|
||||
width, height, 0,
|
||||
&packed_x, &packed_y);
|
||||
|
||||
g_assert (packed_x % ITEM_W == 0);
|
||||
g_assert (packed_y % ITEM_H_QUANTUM == 0);
|
||||
|
||||
/* Make sure we found space to pack */
|
||||
texture_id = GSK_GL_TEXTURE_ATLAS_ENTRY_TEXTURE (value);
|
||||
if (texture_id == 0)
|
||||
return FALSE;
|
||||
|
||||
if (!glyphy_extents_is_empty (&extents))
|
||||
{
|
||||
/* Connect the texture for data upload */
|
||||
glActiveTexture (GL_TEXTURE0);
|
||||
glBindTexture (GL_TEXTURE_2D, texture_id);
|
||||
|
||||
g_assert (width > 0);
|
||||
g_assert (height > 0);
|
||||
|
||||
/* Upload the arc list */
|
||||
if (width * height == output_len)
|
||||
{
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
packed_x, packed_y,
|
||||
width, height,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
packed_x, packed_y,
|
||||
width, height - 1,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
buffer);
|
||||
/* Upload the last row separately */
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
packed_x, packed_y + height - 1,
|
||||
output_len - (width * (height - 1)), 1,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
buffer + (width * (height - 1)));
|
||||
}
|
||||
}
|
||||
|
||||
value->extents.min_x = extents.min_x;
|
||||
value->extents.min_y = extents.min_y;
|
||||
value->extents.max_x = extents.max_x;
|
||||
value->extents.max_y = extents.max_y;
|
||||
value->nominal_w = nominal_w;
|
||||
value->nominal_h = nominal_h;
|
||||
value->atlas_x = packed_x / self->item_w;
|
||||
value->atlas_y = packed_y / self->item_h_q;
|
||||
|
||||
*out_value = value;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/* gskglglyphylibraryprivate.h
|
||||
*
|
||||
* Copyright 2020-2022 Christian Hergert <chergert@redhat.com>
|
||||
*
|
||||
* 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#ifndef __GSK_GL_GLYPHY_LIBRARY_PRIVATE_H__
|
||||
#define __GSK_GL_GLYPHY_LIBRARY_PRIVATE_H__
|
||||
|
||||
#include <glyphy.h>
|
||||
#include <pango/pango.h>
|
||||
|
||||
#include "gskgltexturelibraryprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GSK_TYPE_GL_GLYPHY_LIBRARY (gsk_gl_glyphy_library_get_type())
|
||||
|
||||
typedef guint FontKey;
|
||||
|
||||
extern GQuark quark_glyphy_font_key;
|
||||
|
||||
static inline FontKey
|
||||
gsk_gl_glyphy_library_get_font_key (PangoFont *font)
|
||||
{
|
||||
FontKey key;
|
||||
|
||||
key = (FontKey) GPOINTER_TO_UINT (g_object_get_qdata ((GObject *)font, quark_glyphy_font_key));
|
||||
if (key == 0)
|
||||
{
|
||||
PangoFontDescription *desc = pango_font_describe (font);
|
||||
pango_font_description_set_size (desc, 10 * PANGO_SCALE);
|
||||
key = (FontKey) pango_font_description_hash (desc);
|
||||
pango_font_description_free (desc);
|
||||
g_object_set_qdata ((GObject *)font, quark_glyphy_font_key, GUINT_TO_POINTER (key));
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
static inline float
|
||||
gsk_gl_glyphy_library_get_font_scale (PangoFont *font)
|
||||
{
|
||||
hb_font_t *hbfont;
|
||||
int x_scale, y_scale;
|
||||
|
||||
hbfont = pango_font_get_hb_font (font);
|
||||
hb_font_get_scale (hbfont, &x_scale, &y_scale);
|
||||
|
||||
return MAX (x_scale, y_scale) / 1000.0;
|
||||
}
|
||||
|
||||
|
||||
typedef struct _GskGLGlyphyKey
|
||||
{
|
||||
FontKey font;
|
||||
PangoGlyph glyph;
|
||||
} GskGLGlyphyKey;
|
||||
|
||||
typedef struct _GskGLGlyphyValue
|
||||
{
|
||||
GskGLTextureAtlasEntry entry;
|
||||
struct {
|
||||
float min_x;
|
||||
float min_y;
|
||||
float max_x;
|
||||
float max_y;
|
||||
} extents;
|
||||
guint nominal_w;
|
||||
guint nominal_h;
|
||||
guint atlas_x;
|
||||
guint atlas_y;
|
||||
} GskGLGlyphyValue;
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GskGLGlyphyLibrary, gsk_gl_glyphy_library, GSK, GL_GLYPHY_LIBRARY, GskGLTextureLibrary)
|
||||
|
||||
struct _GskGLGlyphyLibrary
|
||||
{
|
||||
GskGLTextureLibrary parent_instance;
|
||||
glyphy_arc_accumulator_t *acc;
|
||||
GArray *acc_endpoints;
|
||||
guint item_w;
|
||||
guint item_h_q;
|
||||
struct {
|
||||
GskGLGlyphyKey key;
|
||||
const GskGLGlyphyValue *value;
|
||||
} front[256];
|
||||
};
|
||||
|
||||
GskGLGlyphyLibrary *gsk_gl_glyphy_library_new (GskGLDriver *driver);
|
||||
gboolean gsk_gl_glyphy_library_add (GskGLGlyphyLibrary *self,
|
||||
GskGLGlyphyKey *key,
|
||||
PangoFont *font,
|
||||
const GskGLGlyphyValue **out_value);
|
||||
|
||||
static inline guint
|
||||
gsk_gl_glyphy_library_lookup_or_add (GskGLGlyphyLibrary *self,
|
||||
const GskGLGlyphyKey *key,
|
||||
PangoFont *font,
|
||||
const GskGLGlyphyValue **out_value)
|
||||
{
|
||||
GskGLTextureAtlasEntry *entry;
|
||||
guint front_index = key->glyph & 0xFF;
|
||||
|
||||
if (memcmp (key, &self->front[front_index], sizeof *key) == 0)
|
||||
{
|
||||
*out_value = self->front[front_index].value;
|
||||
}
|
||||
else if (gsk_gl_texture_library_lookup ((GskGLTextureLibrary *)self, key, &entry))
|
||||
{
|
||||
*out_value = (GskGLGlyphyValue *)entry;
|
||||
self->front[front_index].key = *key;
|
||||
self->front[front_index].value = *out_value;
|
||||
}
|
||||
else
|
||||
{
|
||||
GskGLGlyphyKey *k = g_slice_copy (sizeof *key, key);
|
||||
gsk_gl_glyphy_library_add (self, k, font, out_value);
|
||||
self->front[front_index].key = *key;
|
||||
self->front[front_index].value = *out_value;
|
||||
}
|
||||
|
||||
return GSK_GL_TEXTURE_ATLAS_ENTRY_TEXTURE (*out_value);
|
||||
}
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_GL_GLYPHY_LIBRARY_PRIVATE_H__ */
|
||||
@@ -87,3 +87,21 @@ GSK_GL_DEFINE_PROGRAM (unblurred_outset_shadow,
|
||||
GSK_GL_ADD_UNIFORM (1, UNBLURRED_OUTSET_SHADOW_SPREAD, u_spread)
|
||||
GSK_GL_ADD_UNIFORM (2, UNBLURRED_OUTSET_SHADOW_OFFSET, u_offset)
|
||||
GSK_GL_ADD_UNIFORM (3, UNBLURRED_OUTSET_SHADOW_OUTLINE_RECT, u_outline_rect))
|
||||
|
||||
GSK_GL_DEFINE_PROGRAM (glyphy,
|
||||
GSK_GL_SHADER_JOINED (VERTEX,
|
||||
GSK_GL_SHADER_RESOURCE ("glyphy.vs.glsl"),
|
||||
NULL)
|
||||
GSK_GL_SHADER_JOINED (FRAGMENT,
|
||||
GSK_GL_SHADER_RESOURCE ("glyphy.atlas.glsl"),
|
||||
GSK_GL_SHADER_STRING (glyphy_common_shader_source ()),
|
||||
GSK_GL_SHADER_STRING ("#define GLYPHY_SDF_PSEUDO_DISTANCE 1\n"),
|
||||
GSK_GL_SHADER_STRING (glyphy_sdf_shader_source ()),
|
||||
GSK_GL_SHADER_RESOURCE ("glyphy.fs.glsl"),
|
||||
NULL),
|
||||
GSK_GL_ADD_UNIFORM (0, GLYPHY_CONTRAST, u_contrast)
|
||||
GSK_GL_ADD_UNIFORM (1, GLYPHY_GAMMA_ADJUST, u_gamma_adjust)
|
||||
GSK_GL_ADD_UNIFORM (2, GLYPHY_OUTLINE_THICKNESS, u_outline_thickness)
|
||||
GSK_GL_ADD_UNIFORM (3, GLYPHY_OUTLINE, u_outline)
|
||||
GSK_GL_ADD_UNIFORM (4, GLYPHY_BOLDNESS, u_boldness)
|
||||
GSK_GL_ADD_UNIFORM (6, GLYPHY_ATLAS_INFO, u_atlas_info))
|
||||
|
||||
@@ -150,6 +150,13 @@ gsk_gl_renderer_realize (GskRenderer *renderer,
|
||||
gsk_gl_command_queue_set_profiler (self->command_queue,
|
||||
gsk_renderer_get_profiler (renderer));
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
if (gsk_renderer_get_debug_flags (renderer) & GSK_DEBUG_NO_GLYPHY)
|
||||
GSK_RENDERER_DEBUG (renderer, RENDERER, "GL Renderer will use cairo for glyph rendering");
|
||||
else
|
||||
GSK_RENDERER_DEBUG (renderer, RENDERER, "GL Renderer will use glyphy for glyph rendering");
|
||||
#endif
|
||||
|
||||
ret = TRUE;
|
||||
|
||||
failure:
|
||||
@@ -307,10 +314,15 @@ gsk_gl_renderer_render (GskRenderer *renderer,
|
||||
|
||||
gsk_gl_driver_begin_frame (self->driver, self->command_queue);
|
||||
job = gsk_gl_render_job_new (self->driver, &viewport, scale, render_region, 0, clear_framebuffer);
|
||||
|
||||
if ((gsk_renderer_get_debug_flags (renderer) & GSK_DEBUG_NO_GLYPHY) == 0)
|
||||
gsk_gl_render_job_set_use_glyphy (job, TRUE);
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
if (GSK_RENDERER_DEBUG_CHECK (GSK_RENDERER (self), FALLBACK))
|
||||
gsk_gl_render_job_set_debug_fallback (job, TRUE);
|
||||
#endif
|
||||
|
||||
gsk_gl_render_job_render (job, root);
|
||||
gsk_gl_driver_end_frame (self->driver);
|
||||
gsk_gl_render_job_free (job);
|
||||
|
||||
+277
-4
@@ -34,10 +34,14 @@
|
||||
#include <gsk/gskroundedrectprivate.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_PANGOFT
|
||||
#include <pango/pangofc-font.h>
|
||||
#endif
|
||||
|
||||
#include "gskglcommandqueueprivate.h"
|
||||
#include "gskgldriverprivate.h"
|
||||
#include "gskglglyphlibraryprivate.h"
|
||||
#include "gskglglyphylibraryprivate.h"
|
||||
#include "gskgliconlibraryprivate.h"
|
||||
#include "gskglprogramprivate.h"
|
||||
#include "gskglrenderjobprivate.h"
|
||||
@@ -149,6 +153,9 @@ struct _GskGLRenderJob
|
||||
*/
|
||||
guint clear_framebuffer : 1;
|
||||
|
||||
/* Allow experimental glyph rendering with glyphy */
|
||||
guint use_glyphy : 1;
|
||||
|
||||
/* Format we want to use for intermediate textures, determined by
|
||||
* looking at the format of the framebuffer we are rendering on.
|
||||
*/
|
||||
@@ -2959,10 +2966,10 @@ compute_phase_and_pos (float value, float *pos)
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_gl_render_job_visit_text_node (GskGLRenderJob *job,
|
||||
const GskRenderNode *node,
|
||||
const GdkRGBA *color,
|
||||
gboolean force_color)
|
||||
gsk_gl_render_job_visit_text_node_legacy (GskGLRenderJob *job,
|
||||
const GskRenderNode *node,
|
||||
const GdkRGBA *color,
|
||||
gboolean force_color)
|
||||
{
|
||||
const PangoFont *font = gsk_text_node_get_font (node);
|
||||
const PangoGlyphInfo *glyphs = gsk_text_node_get_glyphs (node, NULL);
|
||||
@@ -3098,6 +3105,263 @@ gsk_gl_render_job_visit_text_node (GskGLRenderJob *job,
|
||||
}
|
||||
}
|
||||
|
||||
/* Keep this in sync with glyph_vertex_transcode in glyphy.vs.glsl */
|
||||
typedef struct
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
float g16hi;
|
||||
float g16lo;
|
||||
} EncodedGlyph;
|
||||
|
||||
static inline unsigned int
|
||||
glyph_encode (guint atlas_x , /* 7 bits */
|
||||
guint atlas_y, /* 7 bits */
|
||||
guint corner_x, /* 1 bit */
|
||||
guint corner_y, /* 1 bit */
|
||||
guint nominal_w, /* 6 bits */
|
||||
guint nominal_h) /* 6 bits */
|
||||
{
|
||||
guint x, y;
|
||||
|
||||
g_assert (0 == (atlas_x & ~0x7F));
|
||||
g_assert (0 == (atlas_y & ~0x7F));
|
||||
g_assert (0 == (corner_x & ~1));
|
||||
g_assert (0 == (corner_y & ~1));
|
||||
g_assert (0 == (nominal_w & ~0x3F));
|
||||
g_assert (0 == (nominal_h & ~0x3F));
|
||||
|
||||
x = (((atlas_x << 6) | nominal_w) << 1) | corner_x;
|
||||
y = (((atlas_y << 6) | nominal_h) << 1) | corner_y;
|
||||
|
||||
return (x << 16) | y;
|
||||
}
|
||||
|
||||
static inline void
|
||||
encoded_glyph_init (EncodedGlyph *eg,
|
||||
float x,
|
||||
float y,
|
||||
guint corner_x,
|
||||
guint corner_y,
|
||||
const GskGLGlyphyValue *gi)
|
||||
{
|
||||
guint encoded = glyph_encode (gi->atlas_x, gi->atlas_y, corner_x, corner_y, gi->nominal_w, gi->nominal_h);
|
||||
|
||||
eg->x = x;
|
||||
eg->y = y;
|
||||
eg->g16hi = encoded >> 16;
|
||||
eg->g16lo = encoded & 0xFFFF;
|
||||
}
|
||||
|
||||
static inline void
|
||||
add_encoded_glyph (GskGLDrawVertex *vertices,
|
||||
const EncodedGlyph *eg,
|
||||
const guint16 c[4])
|
||||
{
|
||||
*vertices = (GskGLDrawVertex) { .position = { eg->x, eg->y}, .uv = { eg->g16hi, eg->g16lo}, .color = { c[0], c[1], c[2], c[3] } };
|
||||
}
|
||||
|
||||
static void
|
||||
get_synthetic_font_params (PangoFont *font,
|
||||
gboolean *embolden,
|
||||
PangoMatrix *matrix)
|
||||
{
|
||||
*embolden = FALSE;
|
||||
*matrix = (PangoMatrix) PANGO_MATRIX_INIT;
|
||||
|
||||
#ifdef HAVE_PANGOFT
|
||||
if (PANGO_IS_FC_FONT (font))
|
||||
{
|
||||
FcPattern *pattern = pango_fc_font_get_pattern (PANGO_FC_FONT (font));
|
||||
FcBool b;
|
||||
FcMatrix mat;
|
||||
FcMatrix *m;
|
||||
|
||||
if (FcPatternGetBool (pattern, FC_EMBOLDEN, 0, &b) == FcResultMatch)
|
||||
*embolden = b;
|
||||
|
||||
FcMatrixInit (&mat);
|
||||
for (int i = 0; FcPatternGetMatrix (pattern, FC_MATRIX, i, &m) == FcResultMatch; i++)
|
||||
FcMatrixMultiply (&mat, &mat, m);
|
||||
|
||||
matrix->xx = mat.xx;
|
||||
matrix->xy = mat.xy;
|
||||
matrix->yx = mat.yx;
|
||||
matrix->yy = mat.yy;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_gl_render_job_visit_text_node_glyphy (GskGLRenderJob *job,
|
||||
const GskRenderNode *node,
|
||||
const GdkRGBA *color)
|
||||
{
|
||||
const graphene_point_t *offset;
|
||||
const PangoGlyphInfo *glyphs;
|
||||
const PangoGlyphInfo *gi;
|
||||
GskGLGlyphyLibrary *library;
|
||||
GskGLCommandBatch *batch;
|
||||
PangoFont *font;
|
||||
GskGLDrawVertex *vertices;
|
||||
const guint16 *c;
|
||||
GskGLGlyphyKey lookup;
|
||||
guint16 cc[4];
|
||||
float x;
|
||||
float y;
|
||||
guint last_texture = 0;
|
||||
guint num_glyphs;
|
||||
guint used = 0;
|
||||
guint i;
|
||||
int x_position = 0;
|
||||
float font_scale;
|
||||
gboolean embolden;
|
||||
PangoMatrix matrix = PANGO_MATRIX_INIT;
|
||||
|
||||
#define GRID_SIZE 20
|
||||
|
||||
g_assert (!gsk_text_node_has_color_glyphs (node));
|
||||
|
||||
if (!(num_glyphs = gsk_text_node_get_num_glyphs (node)))
|
||||
return;
|
||||
|
||||
if (RGBA_IS_CLEAR (color))
|
||||
return;
|
||||
|
||||
font = (PangoFont *)gsk_text_node_get_font (node);
|
||||
get_synthetic_font_params (font, &embolden, &matrix);
|
||||
|
||||
glyphs = gsk_text_node_get_glyphs (node, NULL);
|
||||
library = job->driver->glyphy_library;
|
||||
offset = gsk_text_node_get_offset (node);
|
||||
x = offset->x + job->offset_x;
|
||||
y = offset->y + job->offset_y;
|
||||
|
||||
rgba_to_half (color, cc);
|
||||
c = cc;
|
||||
|
||||
gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, glyphy));
|
||||
|
||||
batch = gsk_gl_command_queue_get_batch (job->command_queue);
|
||||
vertices = gsk_gl_command_queue_add_n_vertices (job->command_queue, num_glyphs);
|
||||
|
||||
lookup.font = gsk_gl_glyphy_library_get_font_key (font);
|
||||
font_scale = gsk_gl_glyphy_library_get_font_scale (font);
|
||||
|
||||
for (i = 0, gi = glyphs; i < num_glyphs; i++, gi++)
|
||||
{
|
||||
const GskGLGlyphyValue *glyph;
|
||||
float cx = 0, cy = 0;
|
||||
guint texture_id;
|
||||
|
||||
lookup.glyph = gi->glyph;
|
||||
texture_id = gsk_gl_glyphy_library_lookup_or_add (library, &lookup, font, &glyph);
|
||||
|
||||
if G_UNLIKELY (texture_id == 0)
|
||||
continue;
|
||||
|
||||
if G_UNLIKELY (last_texture != texture_id || batch->draw.vbo_count + GSK_GL_N_VERTICES > 0xffff)
|
||||
{
|
||||
if G_LIKELY (last_texture != 0)
|
||||
{
|
||||
guint vbo_offset = batch->draw.vbo_offset + batch->draw.vbo_count;
|
||||
|
||||
/* Since we have batched added our VBO vertices to avoid repeated
|
||||
* calls to the buffer, we need to manually tweak the vbo offset
|
||||
* of the new batch as otherwise it will point at the end of our
|
||||
* vbo array.
|
||||
*/
|
||||
gsk_gl_render_job_split_draw (job);
|
||||
batch = gsk_gl_command_queue_get_batch (job->command_queue);
|
||||
batch->draw.vbo_offset = vbo_offset;
|
||||
}
|
||||
|
||||
gsk_gl_program_set_uniform4i (job->current_program,
|
||||
UNIFORM_GLYPHY_ATLAS_INFO, 0,
|
||||
GSK_GL_TEXTURE_LIBRARY (library)->atlas_width,
|
||||
GSK_GL_TEXTURE_LIBRARY (library)->atlas_height,
|
||||
library->item_w,
|
||||
library->item_h_q);
|
||||
gsk_gl_program_set_uniform_texture (job->current_program,
|
||||
UNIFORM_SHARED_SOURCE, 0,
|
||||
GL_TEXTURE_2D,
|
||||
GL_TEXTURE0,
|
||||
texture_id);
|
||||
gsk_gl_program_set_uniform1f (job->current_program,
|
||||
UNIFORM_GLYPHY_GAMMA_ADJUST, 0,
|
||||
1.0);
|
||||
gsk_gl_program_set_uniform1f (job->current_program,
|
||||
UNIFORM_GLYPHY_CONTRAST, 0,
|
||||
1.0);
|
||||
|
||||
/* 0.0208 is the value used by freetype for synthetic emboldening */
|
||||
gsk_gl_program_set_uniform1f (job->current_program,
|
||||
UNIFORM_GLYPHY_BOLDNESS, 0,
|
||||
embolden ? 0.0208 * GRID_SIZE : 0.0);
|
||||
|
||||
#if 0
|
||||
gsk_gl_program_set_uniform1f (job->current_program,
|
||||
UNIFORM_GLYPHY_OUTLINE_THICKNESS, 0,
|
||||
1.0);
|
||||
gsk_gl_program_set_uniform1f (job->current_program,
|
||||
UNIFORM_GLYPHY_OUTLINE, 0,
|
||||
1.0);
|
||||
#endif
|
||||
|
||||
last_texture = texture_id;
|
||||
}
|
||||
|
||||
cx = (float)(x_position + gi->geometry.x_offset) / PANGO_SCALE;
|
||||
if G_UNLIKELY (gi->geometry.y_offset != 0)
|
||||
cy = (float)(gi->geometry.y_offset) / PANGO_SCALE;
|
||||
|
||||
x_position += gi->geometry.width;
|
||||
|
||||
EncodedGlyph encoded[4];
|
||||
#define ENCODE_CORNER(_cx, _cy) \
|
||||
G_STMT_START { \
|
||||
float _dx = _cx * (glyph->extents.max_x - glyph->extents.min_x); \
|
||||
float _dy = _cy * (glyph->extents.max_y - glyph->extents.min_y); \
|
||||
float _vx = x + cx + font_scale * (glyph->extents.min_x + matrix.xx * _dx + matrix.xy * _dy); \
|
||||
float _vy = y + cy - font_scale * (glyph->extents.min_y + matrix.yx * _dx + matrix.yy * _dy); \
|
||||
encoded_glyph_init (&encoded[_cx * 2 + _cy], _vx, _vy, _cx, _cy, glyph); \
|
||||
} G_STMT_END
|
||||
ENCODE_CORNER (0, 0);
|
||||
ENCODE_CORNER (0, 1);
|
||||
ENCODE_CORNER (1, 0);
|
||||
ENCODE_CORNER (1, 1);
|
||||
#undef ENCODE_CORNER
|
||||
|
||||
add_encoded_glyph (vertices++, &encoded[0], c);
|
||||
add_encoded_glyph (vertices++, &encoded[1], c);
|
||||
add_encoded_glyph (vertices++, &encoded[2], c);
|
||||
|
||||
add_encoded_glyph (vertices++, &encoded[1], c);
|
||||
add_encoded_glyph (vertices++, &encoded[2], c);
|
||||
add_encoded_glyph (vertices++, &encoded[3], c);
|
||||
|
||||
batch->draw.vbo_count += GSK_GL_N_VERTICES;
|
||||
used++;
|
||||
}
|
||||
|
||||
if (used != num_glyphs)
|
||||
gsk_gl_command_queue_retract_n_vertices (job->command_queue, num_glyphs - used);
|
||||
|
||||
gsk_gl_render_job_end_draw (job);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_gl_render_job_visit_text_node (GskGLRenderJob *job,
|
||||
const GskRenderNode *node,
|
||||
const GdkRGBA *color,
|
||||
gboolean force_color)
|
||||
{
|
||||
if (job->use_glyphy && !gsk_text_node_has_color_glyphs (node))
|
||||
gsk_gl_render_job_visit_text_node_glyphy (job, node, color);
|
||||
else
|
||||
gsk_gl_render_job_visit_text_node_legacy (job, node, color, force_color);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_gl_render_job_visit_shadow_node (GskGLRenderJob *job,
|
||||
const GskRenderNode *node)
|
||||
@@ -4463,6 +4727,15 @@ gsk_gl_render_job_set_debug_fallback (GskGLRenderJob *job,
|
||||
job->debug_fallback = !!debug_fallback;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_render_job_set_use_glyphy (GskGLRenderJob *job,
|
||||
gboolean use_glyphy)
|
||||
{
|
||||
g_return_if_fail (job != NULL);
|
||||
|
||||
job->use_glyphy = !!use_glyphy;
|
||||
}
|
||||
|
||||
static int
|
||||
get_framebuffer_format (GdkGLContext *context,
|
||||
guint framebuffer)
|
||||
|
||||
@@ -35,4 +35,5 @@ void gsk_gl_render_job_render_flipped (GskGLRenderJob *job
|
||||
GskRenderNode *root);
|
||||
void gsk_gl_render_job_set_debug_fallback (GskGLRenderJob *job,
|
||||
gboolean debug_fallback);
|
||||
|
||||
void gsk_gl_render_job_set_use_glyphy (GskGLRenderJob *job,
|
||||
gboolean use_glyphy);
|
||||
|
||||
@@ -36,9 +36,14 @@ G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GskGLTextureAtlas
|
||||
{
|
||||
/* Used by Glyph/Icons */
|
||||
struct stbrp_context context;
|
||||
struct stbrp_node *nodes;
|
||||
|
||||
/* Used by Glyphy */
|
||||
int cursor_x;
|
||||
int cursor_y;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
|
||||
@@ -48,7 +53,6 @@ typedef struct _GskGLTextureAtlas
|
||||
* But are now unused.
|
||||
*/
|
||||
int unused_pixels;
|
||||
|
||||
} GskGLTextureAtlas;
|
||||
|
||||
typedef struct _GskGLTextureAtlasEntry
|
||||
|
||||
@@ -36,6 +36,7 @@ typedef struct _GskGLCompiler GskGLCompiler;
|
||||
typedef struct _GskGLDrawVertex GskGLDrawVertex;
|
||||
typedef struct _GskGLRenderTarget GskGLRenderTarget;
|
||||
typedef struct _GskGLGlyphLibrary GskGLGlyphLibrary;
|
||||
typedef struct _GskGLGlyphyLibrary GskGLGlyphyLibrary;
|
||||
typedef struct _GskGLIconLibrary GskGLIconLibrary;
|
||||
typedef struct _GskGLProgram GskGLProgram;
|
||||
typedef struct _GskGLRenderJob GskGLRenderJob;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
uniform ivec4 u_atlas_info;
|
||||
|
||||
#define GLYPHY_TEXTURE1D_EXTRA_DECLS , sampler2D _tex, ivec4 _atlas_info, ivec2 _atlas_pos
|
||||
#define GLYPHY_TEXTURE1D_EXTRA_ARGS , _tex, _atlas_info, _atlas_pos
|
||||
#define GLYPHY_DEMO_EXTRA_ARGS , u_source, u_atlas_info, gi.atlas_pos
|
||||
|
||||
vec4
|
||||
glyphy_texture1D_func (int offset GLYPHY_TEXTURE1D_EXTRA_DECLS)
|
||||
{
|
||||
ivec2 item_geom = _atlas_info.zw;
|
||||
vec2 pos = (vec2 (_atlas_pos.xy * item_geom +
|
||||
ivec2 (mod (float (offset), float (item_geom.x)), offset / item_geom.x)) +
|
||||
+ vec2 (.5, .5)) / vec2(_atlas_info.xy);
|
||||
return GskTexture (_tex, pos);
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// FRAGMENT_SHADER:
|
||||
// glyphy.fs.glsl
|
||||
|
||||
uniform float u_contrast;
|
||||
uniform float u_gamma_adjust;
|
||||
uniform float u_outline_thickness;
|
||||
uniform bool u_outline;
|
||||
uniform float u_boldness;
|
||||
|
||||
_IN_ vec4 v_glyph;
|
||||
_IN_ vec4 final_color;
|
||||
|
||||
#define SQRT2 1.4142135623730951
|
||||
#define SQRT2_INV 0.70710678118654757 /* 1 / sqrt(2.) */
|
||||
|
||||
struct glyph_info_t {
|
||||
ivec2 nominal_size;
|
||||
ivec2 atlas_pos;
|
||||
};
|
||||
|
||||
glyph_info_t
|
||||
glyph_info_decode (vec4 v)
|
||||
{
|
||||
glyph_info_t gi;
|
||||
gi.nominal_size = (ivec2 (mod (v.zw, 256.)) + 2) / 4;
|
||||
gi.atlas_pos = ivec2 (v_glyph.zw) / 256;
|
||||
return gi;
|
||||
}
|
||||
|
||||
float
|
||||
antialias (float d)
|
||||
{
|
||||
return smoothstep (-.75, +.75, d);
|
||||
}
|
||||
|
||||
void
|
||||
main()
|
||||
{
|
||||
vec2 p = v_glyph.xy;
|
||||
glyph_info_t gi = glyph_info_decode (v_glyph);
|
||||
|
||||
/* isotropic antialiasing */
|
||||
vec2 dpdx = dFdx (p);
|
||||
vec2 dpdy = dFdy (p);
|
||||
float m = length (vec2 (length (dpdx), length (dpdy))) * SQRT2_INV;
|
||||
|
||||
float gsdist = glyphy_sdf (p, gi.nominal_size GLYPHY_DEMO_EXTRA_ARGS);
|
||||
gsdist -= u_boldness;
|
||||
float sdist = gsdist / m * u_contrast;
|
||||
|
||||
if (u_outline)
|
||||
sdist = abs (sdist) - u_outline_thickness * .5;
|
||||
|
||||
if (sdist > 1.)
|
||||
discard;
|
||||
|
||||
float alpha = antialias (-sdist);
|
||||
if (u_gamma_adjust != 1.)
|
||||
alpha = pow (alpha, 1./u_gamma_adjust);
|
||||
|
||||
gskSetOutputColor(final_color * alpha);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// VERTEX_SHADER:
|
||||
// glyphy.vs.glsl
|
||||
|
||||
_OUT_ vec4 v_glyph;
|
||||
_OUT_ vec4 final_color;
|
||||
|
||||
// Keep this in sync with glyph_encode in gskglrenderjob.c
|
||||
vec4
|
||||
glyph_vertex_transcode (vec2 v)
|
||||
{
|
||||
ivec2 g = ivec2 (v);
|
||||
ivec2 corner = ivec2 (mod (v, 2.));
|
||||
g /= 2;
|
||||
ivec2 nominal_size = ivec2 (mod (vec2(g), 64.));
|
||||
return vec4 (corner * nominal_size, g * 4);
|
||||
}
|
||||
|
||||
void
|
||||
main()
|
||||
{
|
||||
v_glyph = glyph_vertex_transcode(aUv);
|
||||
vUv = v_glyph.zw;
|
||||
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
|
||||
final_color = gsk_scaled_premultiply(aColor, u_alpha);
|
||||
}
|
||||
@@ -13,6 +13,12 @@ void main() {
|
||||
uniform int u_mode;
|
||||
uniform sampler2D u_mask;
|
||||
|
||||
float
|
||||
luminance (vec3 color)
|
||||
{
|
||||
return dot (vec3 (0.2126, 0.7152, 0.0722), color);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 source = GskTexture(u_source, vUv);
|
||||
vec4 mask = GskTexture(u_mask, vUv);
|
||||
@@ -23,9 +29,9 @@ void main() {
|
||||
else if (u_mode == 1)
|
||||
mask_value = 1.0 - mask.a;
|
||||
else if (u_mode == 2)
|
||||
mask_value = (0.2126 * mask.r + 0.7152 * mask.g + 0.0722 * mask.b) * mask.a;
|
||||
mask_value = luminance (mask.rgb);
|
||||
else if (u_mode == 3)
|
||||
mask_value = 1.0 - (0.2126 * mask.r + 0.7152 * mask.g + 0.0722 * mask.b) * mask.a;
|
||||
mask_value = mask.a - luminance (mask.rgb);
|
||||
else
|
||||
mask_value = 0.0;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#extension GL_OES_standard_derivatives : enable
|
||||
|
||||
#ifndef GSK_LEGACY
|
||||
precision highp float;
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
#include <gsk/gsktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GskBoundingBox GskBoundingBox;
|
||||
|
||||
struct _GskBoundingBox {
|
||||
graphene_point_t min;
|
||||
graphene_point_t max;
|
||||
};
|
||||
|
||||
static inline 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;
|
||||
}
|
||||
|
||||
static inline GskBoundingBox *
|
||||
gsk_bounding_box_init_copy (GskBoundingBox *self,
|
||||
const GskBoundingBox *src)
|
||||
{
|
||||
self->min = src->min;
|
||||
self->max = src->max;
|
||||
return self;
|
||||
}
|
||||
|
||||
static inline 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;
|
||||
}
|
||||
|
||||
static inline 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);
|
||||
}
|
||||
|
||||
static inline 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;
|
||||
}
|
||||
|
||||
static inline 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;
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
gsk_bounding_box_contains_point_with_epsilon (const GskBoundingBox *self,
|
||||
const graphene_point_t *p,
|
||||
float epsilon)
|
||||
{
|
||||
return self->min.x - epsilon <= p->x && p->x <= self->max.x + epsilon &&
|
||||
self->min.y - epsilon <= p->y && p->y <= self->max.y + epsilon;
|
||||
}
|
||||
|
||||
static inline 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;
|
||||
}
|
||||
|
||||
G_END_DECLS
|
||||
+2436
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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>
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gskpath.h"
|
||||
|
||||
#include "gskpathopprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GSK_PATH_FLAT,
|
||||
GSK_PATH_CLOSED
|
||||
} GskPathFlags;
|
||||
|
||||
typedef struct _GskContour GskContour;
|
||||
|
||||
GskContour * gsk_rect_contour_new (const graphene_rect_t *rect);
|
||||
GskContour * gsk_rounded_rect_contour_new (const GskRoundedRect *rounded_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,
|
||||
GskPathDirection direction,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent);
|
||||
gboolean gsk_contour_get_closest_point (const GskContour *self,
|
||||
gpointer measure_data,
|
||||
float tolerance,
|
||||
const graphene_point_t *point,
|
||||
float threshold,
|
||||
float *out_distance,
|
||||
graphene_point_t *out_pos,
|
||||
float *out_offset,
|
||||
graphene_vec2_t *out_tangent);
|
||||
int gsk_contour_get_winding (const GskContour *self,
|
||||
gpointer measure_data,
|
||||
const graphene_point_t *point);
|
||||
void gsk_contour_add_segment (const GskContour *self,
|
||||
GskPathBuilder *builder,
|
||||
gpointer measure_data,
|
||||
gboolean emit_move_to,
|
||||
float start,
|
||||
float end);
|
||||
float gsk_contour_get_curvature (const GskContour *self,
|
||||
gpointer measure_data,
|
||||
float distance,
|
||||
graphene_point_t *center);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
+2034
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,879 @@
|
||||
/*
|
||||
* 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 <math.h>
|
||||
|
||||
#include "gskcurveprivate.h"
|
||||
|
||||
/* {{{ Utilities */
|
||||
|
||||
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 inline gboolean
|
||||
acceptable (float t)
|
||||
{
|
||||
return 0 - FLT_EPSILON <= t && t <= 1 + FLT_EPSILON;
|
||||
}
|
||||
|
||||
static inline void
|
||||
_sincosf (float angle,
|
||||
float *out_s,
|
||||
float *out_c)
|
||||
{
|
||||
#ifdef HAVE_SINCOSF
|
||||
sincosf (angle, out_s, out_c);
|
||||
#else
|
||||
*out_s = sinf (angle);
|
||||
*out_c = cosf (angle);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
align_points (const graphene_point_t *p,
|
||||
const graphene_point_t *a,
|
||||
const graphene_point_t *b,
|
||||
graphene_point_t *q,
|
||||
int n)
|
||||
{
|
||||
graphene_vec2_t n1;
|
||||
float angle;
|
||||
float s, c;
|
||||
|
||||
get_tangent (a, b, &n1);
|
||||
angle = - atan2 (graphene_vec2_get_y (&n1), graphene_vec2_get_x (&n1));
|
||||
_sincosf (angle, &s, &c);
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
q[i].x = (p[i].x - a->x) * c - (p[i].y - a->y) * s;
|
||||
q[i].y = (p[i].x - a->x) * s + (p[i].y - a->y) * c;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
find_point_on_line (const graphene_point_t *p1,
|
||||
const graphene_point_t *p2,
|
||||
const graphene_point_t *q,
|
||||
float *t)
|
||||
{
|
||||
if (p2->x != p1->x)
|
||||
*t = (q->x - p1->x) / (p2->x - p1->x);
|
||||
else
|
||||
*t = (q->y - p1->y) / (p2->y - p1->y);
|
||||
}
|
||||
|
||||
/* find solutions for at^2 + bt + c = 0 */
|
||||
static int
|
||||
solve_quadratic (float a, float b, float c, float t[2])
|
||||
{
|
||||
float d;
|
||||
int n = 0;
|
||||
|
||||
if (fabs (a) > 0.0001)
|
||||
{
|
||||
if (b*b > 4*a*c)
|
||||
{
|
||||
d = sqrt (b*b - 4*a*c);
|
||||
t[n++] = (-b + d)/(2*a);
|
||||
t[n++] = (-b - d)/(2*a);
|
||||
}
|
||||
else
|
||||
{
|
||||
t[n++] = -b / (2*a);
|
||||
}
|
||||
}
|
||||
else if (fabs (b) > 0.0001)
|
||||
{
|
||||
t[n++] = -c / b;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static int
|
||||
filter_allowable (float t[3],
|
||||
int n)
|
||||
{
|
||||
float g[3];
|
||||
int j = 0;
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
if (0 < t[i] && t[i] < 1)
|
||||
g[j++] = t[i];
|
||||
for (int i = 0; i < j; i++)
|
||||
t[i] = g[i];
|
||||
return j;
|
||||
}
|
||||
|
||||
/* Solve P = 0 where P is
|
||||
* P = (1-t)^2*pa + 2*t*(1-t)*pb + t^2*pc
|
||||
*/
|
||||
static int
|
||||
get_quadratic_roots (float pa, float pb, float pc, float roots[2])
|
||||
{
|
||||
float a, b, c, d;
|
||||
int n_roots = 0;
|
||||
|
||||
a = pa - 2 * pb + pc;
|
||||
b = 2 * (pb - pa);
|
||||
c = pa;
|
||||
|
||||
d = b*b - 4*a*c;
|
||||
|
||||
if (d > 0.0001)
|
||||
{
|
||||
float q = sqrt (d);
|
||||
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++;
|
||||
}
|
||||
else if (fabs (d) < 0.0001)
|
||||
{
|
||||
roots[n_roots] = -b / (2 * a);
|
||||
if (acceptable (roots[n_roots]))
|
||||
n_roots++;
|
||||
}
|
||||
|
||||
return n_roots;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Cusps and inflections */
|
||||
|
||||
/* Get the points where the curvature of curve is
|
||||
* zero, or a maximum or minimum, inside the open
|
||||
* interval from 0 to 1.
|
||||
*/
|
||||
int
|
||||
gsk_curve_get_curvature_points (const GskCurve *curve,
|
||||
float t[3])
|
||||
{
|
||||
const graphene_point_t *pts = curve->cubic.points;
|
||||
graphene_point_t p[4];
|
||||
float a, b, c, d;
|
||||
float x, y, z;
|
||||
int n;
|
||||
|
||||
if (curve->op != GSK_PATH_CUBIC)
|
||||
return 0;
|
||||
|
||||
align_points (pts, &pts[0], &pts[3], p, 4);
|
||||
|
||||
a = p[2].x * p[1].y;
|
||||
b = p[3].x * p[1].y;
|
||||
c = p[1].x * p[2].y;
|
||||
d = p[3].x * p[2].y;
|
||||
|
||||
x = - 3*a + 2*b + 3*c - d;
|
||||
y = 3*a - b - 3*c;
|
||||
z = c - a;
|
||||
|
||||
n = solve_quadratic (x, y, z, t);
|
||||
return filter_allowable (t, n);
|
||||
}
|
||||
|
||||
/* Find cusps inside the open interval from 0 to 1.
|
||||
*
|
||||
* According to Stone & deRose, A Geometric Characterization
|
||||
* of Parametric Cubic curves, a necessary and sufficient
|
||||
* condition is that the first derivative vanishes.
|
||||
*/
|
||||
int
|
||||
gsk_curve_get_cusps (const GskCurve *curve,
|
||||
float t[2])
|
||||
{
|
||||
const graphene_point_t *pts = curve->cubic.points;
|
||||
graphene_point_t p[3];
|
||||
float ax, bx, cx;
|
||||
float ay, by, cy;
|
||||
float tx[3];
|
||||
int nx;
|
||||
int n = 0;
|
||||
|
||||
if (curve->op != GSK_PATH_CUBIC)
|
||||
return 0;
|
||||
|
||||
p[0].x = 3 * (pts[1].x - pts[0].x);
|
||||
p[0].y = 3 * (pts[1].y - pts[0].y);
|
||||
p[1].x = 3 * (pts[2].x - pts[1].x);
|
||||
p[1].y = 3 * (pts[2].y - pts[1].y);
|
||||
p[2].x = 3 * (pts[3].x - pts[2].x);
|
||||
p[2].y = 3 * (pts[3].y - pts[2].y);
|
||||
|
||||
ax = p[0].x - 2 * p[1].x + p[2].x;
|
||||
bx = - 2 * p[0].x + 2 * p[1].x;
|
||||
cx = p[0].x;
|
||||
|
||||
nx = solve_quadratic (ax, bx, cx, tx);
|
||||
nx = filter_allowable (tx, nx);
|
||||
|
||||
ay = p[0].y - 2 * p[1].y + p[2].y;
|
||||
by = - 2 * p[0].y + 2 * p[1].y;
|
||||
cy = p[0].y;
|
||||
|
||||
for (int i = 0; i < nx; i++)
|
||||
{
|
||||
float ti = tx[i];
|
||||
|
||||
if (0 < ti && ti < 1 &&
|
||||
fabs (ay * ti * ti + by * ti + cy) < 0.001)
|
||||
t[n++] = ti;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Intersection */
|
||||
|
||||
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 int
|
||||
line_quad_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[2];
|
||||
int m, i, j;
|
||||
|
||||
/* Rotate things to place curve1 on the x axis,
|
||||
* then solve curve2 for y == 0.
|
||||
*/
|
||||
align_points (curve2->quad.points, a, b, pts, 3);
|
||||
|
||||
m = get_quadratic_roots (pts[0].y, pts[1].y, pts[2].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 int
|
||||
line_cubic_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->cubic.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;
|
||||
}
|
||||
|
||||
#define MAX_LEVEL 25
|
||||
#define TOLERANCE 0.001
|
||||
|
||||
static void
|
||||
cubic_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,
|
||||
int level)
|
||||
{
|
||||
GskCurve p11, p12, p21, p22;
|
||||
GskBoundingBox b1, b2;
|
||||
float d1, d2;
|
||||
|
||||
if (*pos == n)
|
||||
return;
|
||||
|
||||
if (level == MAX_LEVEL)
|
||||
return;
|
||||
|
||||
gsk_curve_get_bounds (curve1, &b1);
|
||||
gsk_curve_get_bounds (curve2, &b2);
|
||||
if (!gsk_bounding_box_intersection (&b1, &b2, NULL))
|
||||
return;
|
||||
|
||||
gsk_curve_get_tight_bounds (curve1, &b1);
|
||||
if (!gsk_bounding_box_intersection (&b1, &b2, NULL))
|
||||
return;
|
||||
|
||||
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);
|
||||
|
||||
cubic_intersect_recurse (&p11, &p21, t1l, t1l + d1, t2l, t2l + d2, t1, t2, p, n, pos, level + 1);
|
||||
cubic_intersect_recurse (&p11, &p22, t1l, t1l + d1, t2l + d2, t2r, t1, t2, p, n, pos, level + 1);
|
||||
cubic_intersect_recurse (&p12, &p21, t1l + d1, t1r, t2l, t2l + d2, t1, t2, p, n, pos, level + 1);
|
||||
cubic_intersect_recurse (&p12, &p22, t1l + d1, t1r, t2l + d2, t2r, t1, t2, p, n, pos, level + 1);
|
||||
}
|
||||
|
||||
static int
|
||||
cubic_intersect (const GskCurve *curve1,
|
||||
const GskCurve *curve2,
|
||||
float *t1,
|
||||
float *t2,
|
||||
graphene_point_t *p,
|
||||
int n)
|
||||
{
|
||||
int pos = 0;
|
||||
|
||||
cubic_intersect_recurse (curve1, curve2, 0, 1, 0, 1, t1, t2, p, n, &pos, 0);
|
||||
|
||||
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,
|
||||
int level)
|
||||
{
|
||||
GskBoundingBox b1, b2;
|
||||
float d1, d2;
|
||||
|
||||
if (*pos == n)
|
||||
return;
|
||||
|
||||
if (level == MAX_LEVEL)
|
||||
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], 0.1))
|
||||
return;
|
||||
}
|
||||
|
||||
p[*pos] = c;
|
||||
(*pos)++;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Note that in the conic case, we cannot just split the curves and
|
||||
* pass the two halves down, since splitting changes the parametrization,
|
||||
* and we need the t's to be valid parameters wrt to the original curve.
|
||||
*
|
||||
* So, instead, we determine the bounding boxes above by always starting
|
||||
* from the original curve. That is a bit less efficient, but also works
|
||||
* for conics.
|
||||
*/
|
||||
general_intersect_recurse (curve1, curve2, t1l, t1l + d1, t2l, t2l + d2, t1, t2, p, n, pos, level + 1);
|
||||
general_intersect_recurse (curve1, curve2, t1l, t1l + d1, t2l + d2, t2r, t1, t2, p, n, pos, level + 1);
|
||||
general_intersect_recurse (curve1, curve2, t1l + d1, t1r, t2l, t2l + d2, t1, t2, p, n, pos, level + 1);
|
||||
general_intersect_recurse (curve1, curve2, t1l + d1, t1r, t2l + d2, t2r, t1, t2, p, n, pos, level + 1);
|
||||
}
|
||||
|
||||
static int
|
||||
general_intersect (const GskCurve *curve1,
|
||||
const GskCurve *curve2,
|
||||
float *t1,
|
||||
float *t2,
|
||||
graphene_point_t *p,
|
||||
int n)
|
||||
{
|
||||
int pos = 0;
|
||||
|
||||
general_intersect_recurse (curve1, curve2, 0, 1, 0, 1, t1, t2, p, n, &pos, 0);
|
||||
|
||||
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_CUBIC)
|
||||
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 = cubic_intersect (&cs, &ce, tt, ss, pp, 3);
|
||||
|
||||
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 (n == 1)
|
||||
return 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;
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
curve_equal (const GskCurve *c1,
|
||||
const GskCurve *c2)
|
||||
{
|
||||
gsize curve_size[] = {
|
||||
sizeof (GskLineCurve),
|
||||
sizeof (GskLineCurve),
|
||||
sizeof (GskLineCurve),
|
||||
sizeof (GskQuadCurve),
|
||||
sizeof (GskCubicCurve),
|
||||
sizeof (GskConicCurve)
|
||||
};
|
||||
|
||||
return c1->op == c2->op && memcmp (c1, c2, curve_size[c1->op]) == 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 (curve_equal (curve1, curve2))
|
||||
return curve_self_intersect (curve1, t1, t2, p, n);
|
||||
|
||||
/* We special-case line-line and line-cubic 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_QUAD)
|
||||
return line_quad_intersect (curve1, curve2, t1, t2, p, n);
|
||||
else if (op1 == GSK_PATH_QUAD && op2 == GSK_PATH_LINE)
|
||||
return line_quad_intersect (curve2, curve1, t2, t1, p, n);
|
||||
else if (op1 == GSK_PATH_LINE && op2 == GSK_PATH_CUBIC)
|
||||
return line_cubic_intersect (curve1, curve2, t1, t2, p, n);
|
||||
else if (op1 == GSK_PATH_CUBIC && op2 == GSK_PATH_LINE)
|
||||
return line_cubic_intersect (curve2, curve1, t2, t1, p, n);
|
||||
else if ((op1 == GSK_PATH_QUAD || op1 == GSK_PATH_CUBIC) &&
|
||||
(op2 == GSK_PATH_QUAD || op2 == GSK_PATH_CUBIC))
|
||||
return cubic_intersect (curve1, curve2, t1, t2, p, n);
|
||||
else
|
||||
return general_intersect (curve1, curve2, t1, t2, p, n);
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* vim:set foldmethod=marker expandtab: */
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* 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>
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gskpathopprivate.h"
|
||||
#include "gskpath.h"
|
||||
#include "gskboundingboxprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef gpointer gskpathop;
|
||||
|
||||
typedef union _GskCurve GskCurve;
|
||||
|
||||
typedef struct _GskLineCurve GskLineCurve;
|
||||
typedef struct _GskQuadCurve GskQuadCurve;
|
||||
typedef struct _GskCubicCurve GskCubicCurve;
|
||||
typedef struct _GskConicCurve GskConicCurve;
|
||||
|
||||
struct _GskLineCurve
|
||||
{
|
||||
GskPathOperation op;
|
||||
|
||||
gboolean padding;
|
||||
|
||||
graphene_point_t points[2];
|
||||
};
|
||||
|
||||
struct _GskQuadCurve
|
||||
{
|
||||
GskPathOperation op;
|
||||
|
||||
gboolean has_coefficients;
|
||||
|
||||
graphene_point_t points[3];
|
||||
|
||||
graphene_point_t coeffs[3];
|
||||
};
|
||||
|
||||
struct _GskCubicCurve
|
||||
{
|
||||
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;
|
||||
GskQuadCurve quad;
|
||||
GskCubicCurve cubic;
|
||||
GskConicCurve conic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
GSK_CURVE_LINE_REASON_STRAIGHT,
|
||||
GSK_CURVE_LINE_REASON_SHORT
|
||||
} GskCurveLineReason;
|
||||
|
||||
typedef gboolean (* GskCurveAddLineFunc) (const graphene_point_t *from,
|
||||
const graphene_point_t *to,
|
||||
float from_progress,
|
||||
float to_progress,
|
||||
GskCurveLineReason reason,
|
||||
gpointer user_data);
|
||||
|
||||
typedef gboolean (* GskCurveAddCurveFunc) (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
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_print (const GskCurve *curve,
|
||||
GString *string);
|
||||
|
||||
char * gsk_curve_to_string (const GskCurve *curve);
|
||||
|
||||
gskpathop gsk_curve_pathop (const GskCurve *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_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_reverse (const GskCurve *curve,
|
||||
GskCurve *reverse);
|
||||
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,
|
||||
GskPathForeachFlags flags,
|
||||
float tolerance,
|
||||
GskCurveAddCurveFunc add_curve_func,
|
||||
gpointer user_data);
|
||||
|
||||
#define gsk_curve_builder_to(curve, builder) gsk_path_builder_pathop_to ((builder), gsk_curve_pathop (curve))
|
||||
|
||||
float gsk_curve_get_curvature (const GskCurve *curve,
|
||||
float t,
|
||||
graphene_point_t *center);
|
||||
void gsk_curve_get_bounds (const GskCurve *curve,
|
||||
GskBoundingBox *bounds);
|
||||
void gsk_curve_get_tight_bounds (const GskCurve *curve,
|
||||
GskBoundingBox *bounds);
|
||||
|
||||
int gsk_curve_get_curvature_points (const GskCurve *curve,
|
||||
float t[3]);
|
||||
|
||||
int gsk_curve_get_cusps (const GskCurve *curve,
|
||||
float t[2]);
|
||||
|
||||
int gsk_curve_intersect (const GskCurve *curve1,
|
||||
const GskCurve *curve2,
|
||||
float *t1,
|
||||
float *t2,
|
||||
graphene_point_t *p,
|
||||
int n);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -12,10 +12,12 @@ 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" },
|
||||
{ "verbose", GSK_DEBUG_VERBOSE, "Print verbose output while rendering" },
|
||||
{ "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" },
|
||||
{ "staging", GSK_DEBUG_STAGING, "Use a staging image for texture upload (Vulkan only)" },
|
||||
{ "no-glyphy", GSK_DEBUG_NO_GLYPHY, "Don't use GPU for glyph rendering (OpenGL only)", TRUE },
|
||||
};
|
||||
|
||||
static guint gsk_debug_flags;
|
||||
|
||||
@@ -14,11 +14,13 @@ typedef enum {
|
||||
GSK_DEBUG_VULKAN = 1 << 5,
|
||||
GSK_DEBUG_FALLBACK = 1 << 6,
|
||||
GSK_DEBUG_GLYPH_CACHE = 1 << 7,
|
||||
GSK_DEBUG_VERBOSE = 1 << 8,
|
||||
/* flags below may affect behavior */
|
||||
GSK_DEBUG_GEOMETRY = 1 << 9,
|
||||
GSK_DEBUG_FULL_REDRAW = 1 << 10,
|
||||
GSK_DEBUG_SYNC = 1 << 11,
|
||||
GSK_DEBUG_STAGING = 1 << 12
|
||||
GSK_DEBUG_STAGING = 1 << 12,
|
||||
GSK_DEBUG_NO_GLYPHY = 1 << 13
|
||||
} GskDebugFlags;
|
||||
|
||||
#define GSK_DEBUG_ANY ((1 << 13) - 1)
|
||||
|
||||
+43
-1
@@ -170,6 +170,49 @@ typedef enum {
|
||||
GSK_CORNER_BOTTOM_LEFT
|
||||
} GskCorner;
|
||||
|
||||
/**
|
||||
* GskPathOperation:
|
||||
* @GSK_PATH_MOVE: A move-to operation, with 1 point describing the target point.
|
||||
* @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_LINE: A line-to operation, with 2 points describing the start and
|
||||
* end point of a straight line.
|
||||
* @GSK_PATH_QUAD: A curve-to operation describing a quadratic Bézier curve
|
||||
* with 3 points describing the start point, the control point and the end
|
||||
* point of the curve.
|
||||
* @GSK_PATH_CUBIC: 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_QUAD,
|
||||
GSK_PATH_CUBIC,
|
||||
GSK_PATH_CONIC,
|
||||
} GskPathOperation;
|
||||
|
||||
/**
|
||||
* GskPathDirection:
|
||||
* @GSK_PATH_START: The side that leads to the start of the path
|
||||
* @GSK_PATH_END: The side that leads to the end of the path
|
||||
*
|
||||
* The values of the `GskPathDirection` enum are used to pick one
|
||||
* of the two sides of the path that at a given point on the path.
|
||||
*/
|
||||
typedef enum {
|
||||
GSK_PATH_START,
|
||||
GSK_PATH_END
|
||||
} GskPathDirection;
|
||||
|
||||
/**
|
||||
* GskSerializationError:
|
||||
* @GSK_SERIALIZATION_UNSUPPORTED_FORMAT: The format can not be identified
|
||||
@@ -274,4 +317,3 @@ typedef enum
|
||||
GSK_MASK_MODE_LUMINANCE,
|
||||
GSK_MASK_MODE_INVERTED_LUMINANCE
|
||||
} GskMaskMode;
|
||||
|
||||
|
||||
+1187
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#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
|
||||
|
||||
typedef enum {
|
||||
GSK_FILL_RULE_WINDING,
|
||||
GSK_FILL_RULE_EVEN_ODD
|
||||
} GskFillRule;
|
||||
|
||||
/**
|
||||
* GskPathForeachFlags:
|
||||
* @GSK_PATH_FOREACH_ALLOW_QUAD: Allow emission of `GSK_PATH_QUAD` operations
|
||||
* @GSK_PATH_FOREACH_ALLOW_CUBIC: Allow emission of `GSK_PATH_CUBIC` operations.
|
||||
* @GSK_PATH_FOREACH_ALLOW_CONIC: Allow emission of `GSK_PATH_CONIC` operations.
|
||||
*
|
||||
* Flags that can be passed to gsk_path_foreach() to enable additional
|
||||
* features.
|
||||
*
|
||||
* By default, [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_QUAD = (1 << 0),
|
||||
GSK_PATH_FOREACH_ALLOW_CUBIC = (1 << 1),
|
||||
GSK_PATH_FOREACH_ALLOW_CONIC = (1 << 2),
|
||||
} 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 ())
|
||||
|
||||
GType gsk_path_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GskPath * gsk_path_ref (GskPath *self);
|
||||
void gsk_path_unref (GskPath *self);
|
||||
void gsk_path_print (GskPath *self,
|
||||
GString *string);
|
||||
char * gsk_path_to_string (GskPath *self);
|
||||
GskPath * gsk_path_parse (const char *string);
|
||||
void gsk_path_to_cairo (GskPath *self,
|
||||
cairo_t *cr);
|
||||
gboolean gsk_path_is_empty (GskPath *self);
|
||||
gboolean gsk_path_get_bounds (GskPath *self,
|
||||
graphene_rect_t *bounds);
|
||||
gboolean gsk_path_foreach (GskPath *self,
|
||||
GskPathForeachFlags flags,
|
||||
GskPathForeachFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPath, gsk_path_unref)
|
||||
|
||||
G_END_DECLS
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#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 ())
|
||||
|
||||
GType gsk_path_builder_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GskPathBuilder * gsk_path_builder_new (void);
|
||||
GskPathBuilder * gsk_path_builder_ref (GskPathBuilder *self);
|
||||
void gsk_path_builder_unref (GskPathBuilder *self);
|
||||
GskPath * gsk_path_builder_free_to_path (GskPathBuilder *self) G_GNUC_WARN_UNUSED_RESULT;
|
||||
GskPath * gsk_path_builder_to_path (GskPathBuilder *self) G_GNUC_WARN_UNUSED_RESULT;
|
||||
|
||||
const graphene_point_t *gsk_path_builder_get_current_point (GskPathBuilder *self);
|
||||
|
||||
void gsk_path_builder_add_path (GskPathBuilder *self,
|
||||
GskPath *path);
|
||||
void gsk_path_builder_add_reverse_path (GskPathBuilder *self,
|
||||
GskPath *path);
|
||||
void gsk_path_builder_add_cairo_path (GskPathBuilder *self,
|
||||
const cairo_path_t *path);
|
||||
void gsk_path_builder_add_layout (GskPathBuilder *self,
|
||||
PangoLayout *layout);
|
||||
void gsk_path_builder_add_rect (GskPathBuilder *self,
|
||||
const graphene_rect_t *rect);
|
||||
void gsk_path_builder_add_rounded_rect (GskPathBuilder *self,
|
||||
const GskRoundedRect *rect);
|
||||
void gsk_path_builder_add_circle (GskPathBuilder *self,
|
||||
const graphene_point_t *center,
|
||||
float radius);
|
||||
void gsk_path_builder_add_ellipse (GskPathBuilder *self,
|
||||
const graphene_point_t *center,
|
||||
const graphene_size_t *radius);
|
||||
void gsk_path_builder_add_segment (GskPathBuilder *self,
|
||||
GskPathMeasure *measure,
|
||||
float start,
|
||||
float end);
|
||||
void gsk_path_builder_move_to (GskPathBuilder *self,
|
||||
float x,
|
||||
float y);
|
||||
void gsk_path_builder_rel_move_to (GskPathBuilder *self,
|
||||
float x,
|
||||
float y);
|
||||
void gsk_path_builder_line_to (GskPathBuilder *self,
|
||||
float x,
|
||||
float y);
|
||||
void gsk_path_builder_rel_line_to (GskPathBuilder *self,
|
||||
float x,
|
||||
float y);
|
||||
void gsk_path_builder_quad_to (GskPathBuilder *self,
|
||||
float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2);
|
||||
void gsk_path_builder_rel_quad_to (GskPathBuilder *self,
|
||||
float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2);
|
||||
void gsk_path_builder_cubic_to (GskPathBuilder *self,
|
||||
float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2,
|
||||
float x3,
|
||||
float y3);
|
||||
void gsk_path_builder_rel_cubic_to (GskPathBuilder *self,
|
||||
float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2,
|
||||
float x3,
|
||||
float y3);
|
||||
void gsk_path_builder_conic_to (GskPathBuilder *self,
|
||||
float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2,
|
||||
float weight);
|
||||
void gsk_path_builder_rel_conic_to (GskPathBuilder *self,
|
||||
float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2,
|
||||
float weight);
|
||||
void gsk_path_builder_close (GskPathBuilder *self);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPathBuilder, gsk_path_builder_unref)
|
||||
|
||||
G_END_DECLS
|
||||
@@ -0,0 +1,489 @@
|
||||
/*
|
||||
* 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 "gskpathpointprivate.h"
|
||||
#include "gskpathprivate.h"
|
||||
|
||||
/*< private >
|
||||
* `GskPathMeasure` is an object that allows measurements
|
||||
* on `GskPath`s such as determining the length of the path.
|
||||
*
|
||||
* Many measuring operations require approximating the path
|
||||
* with simpler shapes. Therefore, a `GskPathMeasure` has
|
||||
* a tolerance that determines what amount is required
|
||||
* for such approximations.
|
||||
*
|
||||
* A `GskPathMeasure` struct is a reference counted struct
|
||||
* and should be treated as opaque.
|
||||
*/
|
||||
|
||||
typedef struct _GskContourMeasure GskContourMeasure;
|
||||
|
||||
struct _GskContourMeasure
|
||||
{
|
||||
float length;
|
||||
gpointer contour_data;
|
||||
};
|
||||
|
||||
struct _GskPathMeasure
|
||||
{
|
||||
/*< private >*/
|
||||
guint ref_count;
|
||||
|
||||
GskPath *path;
|
||||
float tolerance;
|
||||
|
||||
float length;
|
||||
gsize n_contours;
|
||||
GskContourMeasure measures[];
|
||||
};
|
||||
|
||||
G_DEFINE_BOXED_TYPE (GskPathMeasure, gsk_path_measure,
|
||||
gsk_path_measure_ref,
|
||||
gsk_path_measure_unref)
|
||||
|
||||
/*< private >
|
||||
* gsk_path_measure_new:
|
||||
* @path: the path to measure
|
||||
*
|
||||
* Creates a measure object for the given @path.
|
||||
*
|
||||
* Returns: a new `GskPathMeasure` representing @path
|
||||
*/
|
||||
GskPathMeasure *
|
||||
gsk_path_measure_new (GskPath *path)
|
||||
{
|
||||
return gsk_path_measure_new_with_tolerance (path, GSK_PATH_TOLERANCE_DEFAULT);
|
||||
}
|
||||
|
||||
/*< private >
|
||||
* gsk_path_measure_new_with_tolerance:
|
||||
* @path: the path to measure
|
||||
* @tolerance: the tolerance for measuring operations
|
||||
*
|
||||
* Creates a measure object for the given @path and @tolerance.
|
||||
*
|
||||
* Returns: a new `GskPathMeasure` representing @path
|
||||
*/
|
||||
GskPathMeasure *
|
||||
gsk_path_measure_new_with_tolerance (GskPath *path,
|
||||
float tolerance)
|
||||
{
|
||||
GskPathMeasure *self;
|
||||
gsize i, n_contours;
|
||||
|
||||
g_return_val_if_fail (path != NULL, NULL);
|
||||
g_return_val_if_fail (tolerance > 0, NULL);
|
||||
|
||||
n_contours = gsk_path_get_n_contours (path);
|
||||
|
||||
self = g_malloc0 (sizeof (GskPathMeasure) + n_contours * sizeof (GskContourMeasure));
|
||||
|
||||
self->ref_count = 1;
|
||||
self->path = gsk_path_ref (path);
|
||||
self->tolerance = tolerance;
|
||||
self->n_contours = n_contours;
|
||||
|
||||
for (i = 0; i < n_contours; i++)
|
||||
{
|
||||
self->measures[i].contour_data = gsk_contour_init_measure (gsk_path_get_contour (path, i),
|
||||
self->tolerance,
|
||||
&self->measures[i].length);
|
||||
self->length += self->measures[i].length;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*< private >
|
||||
* 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;
|
||||
}
|
||||
|
||||
/*< private >
|
||||
* 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);
|
||||
}
|
||||
|
||||
/*< private >
|
||||
* gsk_path_measure_get_path:
|
||||
* @self: a `GskPathMeasure`
|
||||
*
|
||||
* Returns the path that the measure was created for.
|
||||
*
|
||||
* Returns: (transfer none): the path of @self
|
||||
*/
|
||||
GskPath *
|
||||
gsk_path_measure_get_path (GskPathMeasure *self)
|
||||
{
|
||||
return self->path;
|
||||
}
|
||||
|
||||
/*< private >
|
||||
* gsk_path_measure_get_tolerance:
|
||||
* @self: a `GskPathMeasure`
|
||||
*
|
||||
* Returns the tolerance that the measure was created with.
|
||||
*
|
||||
* Returns: the tolerance of @self
|
||||
*/
|
||||
float
|
||||
gsk_path_measure_get_tolerance (GskPathMeasure *self)
|
||||
{
|
||||
return self->tolerance;
|
||||
}
|
||||
|
||||
/*< private >
|
||||
* 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;
|
||||
}
|
||||
|
||||
/*< private >
|
||||
* 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->n_contours != 1)
|
||||
return FALSE;
|
||||
|
||||
contour = gsk_path_get_contour (self->path, 0);
|
||||
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);
|
||||
}
|
||||
|
||||
/*< private >
|
||||
* 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 was filled according
|
||||
* to @fill_rule.
|
||||
*
|
||||
* Returns: `TRUE` if @point is inside
|
||||
*/
|
||||
gboolean
|
||||
gsk_path_measure_in_fill (GskPathMeasure *self,
|
||||
const graphene_point_t *point,
|
||||
GskFillRule fill_rule)
|
||||
{
|
||||
int winding = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < self->n_contours; i++)
|
||||
winding += gsk_contour_get_winding (gsk_path_get_contour (self->path, i),
|
||||
self->measures[i].contour_data,
|
||||
point);
|
||||
|
||||
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 = 0; i < measure->n_contours; i++)
|
||||
{
|
||||
if (measure->measures[i].length < start)
|
||||
{
|
||||
start -= measure->measures[i].length;
|
||||
end -= measure->measures[i].length;
|
||||
}
|
||||
else if (start > 0 || end < measure->measures[i].length)
|
||||
{
|
||||
float len = MIN (end, measure->measures[i].length);
|
||||
gsk_contour_add_segment (gsk_path_get_contour (measure->path, i),
|
||||
self,
|
||||
measure->measures[i].contour_data,
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*< private >
|
||||
* gsk_path_builder_add_segment:
|
||||
* @self: a `GskPathBuilder`
|
||||
* @measure: the `GskPathMeasure` to take the segment to
|
||||
* @start: start distance into the path
|
||||
* @end: end distance into the path
|
||||
*
|
||||
* Adds to @self the segment of @measure from @start to @end.
|
||||
*
|
||||
* The distances are given relative to the length of @measure's path,
|
||||
* from 0 for the beginning of the path to its length for the end
|
||||
* of the path. The values will be clamped to that range. The length
|
||||
* can be obtained with [method@Gsk.PathMeasure.get_length].
|
||||
*
|
||||
* If @start >= @end after clamping, the path will first add the segment
|
||||
* from @start to the end of the path, and then add the segment from
|
||||
* the beginning to @end. If the path is closed, these segments will
|
||||
* be connected.
|
||||
*/
|
||||
void
|
||||
gsk_path_builder_add_segment (GskPathBuilder *self,
|
||||
GskPathMeasure *measure,
|
||||
float start,
|
||||
float end)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (measure != NULL);
|
||||
|
||||
start = gsk_path_measure_clamp_distance (measure, start);
|
||||
end = gsk_path_measure_clamp_distance (measure, end);
|
||||
|
||||
if (start < end)
|
||||
{
|
||||
gsk_path_builder_add_segment_chunk (self, measure, TRUE, start, end);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If the path is closed, we can connect the 2 subpaths. */
|
||||
gboolean closed = gsk_path_measure_is_closed (measure);
|
||||
gboolean need_move_to = !closed;
|
||||
|
||||
if (start < measure->length)
|
||||
gsk_path_builder_add_segment_chunk (self, measure,
|
||||
TRUE,
|
||||
start, measure->length);
|
||||
else
|
||||
need_move_to = TRUE;
|
||||
|
||||
if (end > 0)
|
||||
gsk_path_builder_add_segment_chunk (self, measure,
|
||||
need_move_to,
|
||||
0, end);
|
||||
if (start == end && closed)
|
||||
gsk_path_builder_close (self);
|
||||
}
|
||||
}
|
||||
|
||||
/*< private >
|
||||
* gsk_path_measure_get_point:
|
||||
* @self: a `GskPathMeasure`
|
||||
* @distance: the distance
|
||||
*
|
||||
* Returns a `GskPathPoint` representing the point at the given
|
||||
* distance into the path.
|
||||
*
|
||||
* An empty path has no points, so `NULL` is returned in that case.
|
||||
*
|
||||
* Returns: (transfer full) (nullable): a newly allocated `GskPathPoint`
|
||||
*/
|
||||
GskPathPoint *
|
||||
gsk_path_measure_get_point (GskPathMeasure *self,
|
||||
float distance)
|
||||
{
|
||||
gsize i;
|
||||
float contour_offset;
|
||||
float offset;
|
||||
const GskContour *contour;
|
||||
|
||||
g_return_val_if_fail (self != NULL, NULL);
|
||||
|
||||
if (self->n_contours == 0)
|
||||
return NULL;
|
||||
|
||||
contour_offset = 0;
|
||||
offset = gsk_path_measure_clamp_distance (self, distance);
|
||||
|
||||
for (i = 0; i < self->n_contours - 1; i++)
|
||||
{
|
||||
if (offset < self->measures[i].length)
|
||||
break;
|
||||
|
||||
contour_offset += self->measures[i].length;
|
||||
offset -= self->measures[i].length;
|
||||
}
|
||||
|
||||
g_assert (0 <= i && i < self->n_contours);
|
||||
|
||||
offset = CLAMP (offset, 0, self->measures[i].length);
|
||||
|
||||
contour = gsk_path_get_contour (self->path, i);
|
||||
|
||||
return gsk_path_point_new (self,
|
||||
contour, self->measures[i].contour_data,
|
||||
contour_offset, offset);
|
||||
}
|
||||
|
||||
/*< private >
|
||||
* gsk_path_measure_get_closest_point:
|
||||
* @self: a `GskPathMeasure`
|
||||
* @point: the point to fond the closest point to
|
||||
* @threshold: The maximum allowed distance between the path and @point.
|
||||
* Use `INFINITY` to look for any point.
|
||||
*
|
||||
* Returns a `GskPathPoint` representing the point on the path
|
||||
* that is closest to the given point.
|
||||
*
|
||||
* If no point on the path is closer than @threshold, `NULL` is returned.
|
||||
*
|
||||
* Returns: (transfer full) (nullable): a newly allocated `GskPathPoint`
|
||||
*/
|
||||
GskPathPoint *
|
||||
gsk_path_measure_get_closest_point (GskPathMeasure *self,
|
||||
const graphene_point_t *point,
|
||||
float threshold)
|
||||
{
|
||||
gssize best_idx;
|
||||
float best_offset;
|
||||
float best_contour_offset;
|
||||
float contour_offset;
|
||||
|
||||
contour_offset = 0;
|
||||
best_idx = -1;
|
||||
|
||||
for (gsize i = 0; i < self->n_contours; i++)
|
||||
{
|
||||
float distance, offset;
|
||||
|
||||
if (gsk_contour_get_closest_point (gsk_path_get_contour (self->path, i),
|
||||
self->measures[i].contour_data,
|
||||
self->tolerance,
|
||||
point,
|
||||
threshold,
|
||||
&distance,
|
||||
NULL,
|
||||
&offset,
|
||||
NULL))
|
||||
{
|
||||
best_idx = i;
|
||||
best_offset = offset;
|
||||
best_contour_offset = contour_offset;
|
||||
|
||||
if (distance < self->tolerance)
|
||||
break;
|
||||
|
||||
threshold = distance - self->tolerance;
|
||||
}
|
||||
|
||||
contour_offset += self->measures[i].length;
|
||||
}
|
||||
|
||||
if (best_idx != -1)
|
||||
return gsk_path_point_new (self,
|
||||
gsk_path_get_contour (self->path, best_idx),
|
||||
self->measures[best_idx].contour_data,
|
||||
best_contour_offset, best_offset);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gsk/gsk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
|
||||
#include <gsk/gskpath.h>
|
||||
#include <gsk/gskpathpoint.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GSK_TYPE_PATH_MEASURE (gsk_path_measure_get_type ())
|
||||
|
||||
GType gsk_path_measure_get_type (void) G_GNUC_CONST;
|
||||
GskPathMeasure * gsk_path_measure_new (GskPath *path);
|
||||
GskPathMeasure * gsk_path_measure_new_with_tolerance (GskPath *path,
|
||||
float tolerance);
|
||||
GskPathMeasure * gsk_path_measure_ref (GskPathMeasure *self);
|
||||
void gsk_path_measure_unref (GskPathMeasure *self);
|
||||
GskPath * gsk_path_measure_get_path (GskPathMeasure *self) G_GNUC_PURE;
|
||||
float gsk_path_measure_get_tolerance (GskPathMeasure *self) G_GNUC_PURE;
|
||||
float gsk_path_measure_get_length (GskPathMeasure *self);
|
||||
gboolean gsk_path_measure_is_closed (GskPathMeasure *self);
|
||||
gboolean gsk_path_measure_in_fill (GskPathMeasure *self,
|
||||
const graphene_point_t *point,
|
||||
GskFillRule fill_rule);
|
||||
GskPathPoint * gsk_path_measure_get_point (GskPathMeasure *self,
|
||||
float distance);
|
||||
GskPathPoint * gsk_path_measure_get_closest_point (GskPathMeasure *self,
|
||||
const graphene_point_t *point,
|
||||
float threshold);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPathMeasure, gsk_path_measure_unref)
|
||||
|
||||
G_END_DECLS
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* 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>
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gskpath.h"
|
||||
#include "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_QUAD:
|
||||
return func (gsk_pathop_op (pop), gsk_pathop_points (pop), 3, 0, user_data);
|
||||
|
||||
case GSK_PATH_CUBIC:
|
||||
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_QUAD:
|
||||
gsk_path_builder_quad_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CUBIC:
|
||||
gsk_path_builder_cubic_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_QUAD:
|
||||
gsk_path_builder_quad_to (builder, pts[1].x, pts[1].y, pts[0].x, pts[0].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CUBIC:
|
||||
gsk_path_builder_cubic_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
|
||||
+1726
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,195 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskpathpointprivate.h"
|
||||
|
||||
#include "gskcontourprivate.h"
|
||||
#include "gskpathmeasure.h"
|
||||
|
||||
#include "gdk/gdkprivate.h"
|
||||
|
||||
|
||||
/**
|
||||
* GskPathPoint:
|
||||
*
|
||||
* `GskPathPoint` is an opaque, immutable type representing a point on a path.
|
||||
*
|
||||
* It can be queried for properties of the path at that point, such as its
|
||||
* tangent or its curvature.
|
||||
*
|
||||
* To obtain a `GskPathPoint`, use [method@Gsk.PathMeasure.get_path_point]
|
||||
* or [method@Gsk.PathMeasure.get_closest_point].
|
||||
*/
|
||||
|
||||
struct _GskPathPoint
|
||||
{
|
||||
guint ref_count;
|
||||
|
||||
GskPathMeasure *measure;
|
||||
const GskContour *contour;
|
||||
gpointer measure_data;
|
||||
float contour_offset; /* distance from beginning of path to contour */
|
||||
float offset; /* offset of point inside contour */
|
||||
};
|
||||
|
||||
G_DEFINE_BOXED_TYPE (GskPathPoint, gsk_path_point,
|
||||
gsk_path_point_ref,
|
||||
gsk_path_point_unref)
|
||||
|
||||
GskPathPoint *
|
||||
gsk_path_point_new (GskPathMeasure *measure,
|
||||
const GskContour *contour,
|
||||
gpointer measure_data,
|
||||
float contour_offset,
|
||||
float offset)
|
||||
{
|
||||
GskPathPoint *self;
|
||||
|
||||
self = g_new0 (GskPathPoint, 1);
|
||||
|
||||
self->ref_count = 1;
|
||||
|
||||
self->measure = gsk_path_measure_ref (measure);
|
||||
self->contour = contour;
|
||||
self->measure_data = measure_data;
|
||||
self->contour_offset = contour_offset;
|
||||
self->offset = offset;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_point_ref:
|
||||
* @self: a `GskPathPoint`
|
||||
*
|
||||
* Increases the reference count of a `GskPathPoint` by one.
|
||||
*
|
||||
* Returns: the passed in `GskPathPoint`
|
||||
*/
|
||||
GskPathPoint *
|
||||
gsk_path_point_ref (GskPathPoint *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, NULL);
|
||||
|
||||
self->ref_count++;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_point_unref:
|
||||
* @self: a `GskPathPoint`
|
||||
*
|
||||
* Decreases the reference count of a `GskPathPoint` by one.
|
||||
*
|
||||
* If the resulting reference count is zero, frees the path_measure.
|
||||
*/
|
||||
void
|
||||
gsk_path_point_unref (GskPathPoint *self)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (self->ref_count > 0);
|
||||
|
||||
self->ref_count--;
|
||||
if (self->ref_count > 0)
|
||||
return;
|
||||
|
||||
gsk_path_measure_unref (self->measure);
|
||||
|
||||
g_free (self);
|
||||
}
|
||||
|
||||
GskPathMeasure *
|
||||
gsk_path_point_get_measure (GskPathPoint *self)
|
||||
{
|
||||
return self->measure;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_point_get_distance:
|
||||
* @self: a `GskPathPoint`
|
||||
*
|
||||
* Returns the distance of the given point from the start of the path.
|
||||
*
|
||||
* This is the length of the contour from the beginning of the path
|
||||
* to the point.
|
||||
*
|
||||
* Returns: The offset of point in path
|
||||
*/
|
||||
float
|
||||
gsk_path_point_get_distance (GskPathPoint *self)
|
||||
{
|
||||
return self->contour_offset + self->offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_point_get_position:
|
||||
* @self: a `GskPathPoint`
|
||||
* @position: (out caller-allocates): Return location for
|
||||
* the coordinates of the point
|
||||
*
|
||||
* Gets the position of the point.
|
||||
*/
|
||||
void
|
||||
gsk_path_point_get_position (GskPathPoint *self,
|
||||
graphene_point_t *position)
|
||||
{
|
||||
gsk_contour_get_point (self->contour,
|
||||
self->measure_data,
|
||||
self->offset,
|
||||
GSK_PATH_END,
|
||||
position, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_point_get_tangent:
|
||||
* @self: a `GskPathPoint`
|
||||
* @direction: the direction for which to return the tangent
|
||||
* @tangent: (out caller-allocates): Return location for
|
||||
* the tangent at the point
|
||||
*
|
||||
* Gets the tangent of the path at the point.
|
||||
*
|
||||
* Note that certain points on a path may not have a single
|
||||
* tangent, such as sharp turns. At such points, there are
|
||||
* two tangents -- the direction of the path going into the
|
||||
* point, and the direction coming out of it.
|
||||
*
|
||||
* The @direction argument lets you choose which one to get.
|
||||
*/
|
||||
void
|
||||
gsk_path_point_get_tangent (GskPathPoint *self,
|
||||
GskPathDirection direction,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
gsk_contour_get_point (self->contour,
|
||||
self->measure_data,
|
||||
self->offset,
|
||||
direction,
|
||||
NULL, tangent);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_point_get_curvature:
|
||||
* @self: a `GskPathPoint`
|
||||
* @center: (out caller-allocates): Return location for
|
||||
* the center of the osculating circle
|
||||
*
|
||||
* 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), zero is returned,
|
||||
* and @center is not modified.
|
||||
*
|
||||
* Returns: The curvature of the path at the given point
|
||||
*/
|
||||
float
|
||||
gsk_path_point_get_curvature (GskPathPoint *self,
|
||||
graphene_point_t *center)
|
||||
{
|
||||
return gsk_contour_get_curvature (self->contour,
|
||||
self->measure_data,
|
||||
self->offset,
|
||||
center);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#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_PATH_POINT (gsk_path_point_get_type ())
|
||||
|
||||
GType gsk_path_point_get_type (void) G_GNUC_CONST;
|
||||
GskPathPoint * gsk_path_point_ref (GskPathPoint *self);
|
||||
void gsk_path_point_unref (GskPathPoint *self);
|
||||
GskPathMeasure * gsk_path_point_get_measure (GskPathPoint *self);
|
||||
float gsk_path_point_get_distance (GskPathPoint *self);
|
||||
void gsk_path_point_get_position (GskPathPoint *self,
|
||||
graphene_point_t *position);
|
||||
void gsk_path_point_get_tangent (GskPathPoint *self,
|
||||
GskPathDirection direction,
|
||||
graphene_vec2_t *tangent);
|
||||
float gsk_path_point_get_curvature (GskPathPoint *self,
|
||||
graphene_point_t *center);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPathPoint, gsk_path_point_unref)
|
||||
|
||||
G_END_DECLS
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "gskpathpoint.h"
|
||||
#include "gskcontourprivate.h"
|
||||
#include "gskpathmeasure.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GskPathPoint * gsk_path_point_new (GskPathMeasure *measure,
|
||||
const GskContour *contour,
|
||||
gpointer measure_data,
|
||||
float contour_offset,
|
||||
float offset);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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>
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#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,
|
||||
GskFillRule fill_rule,
|
||||
GskPath *first,
|
||||
GskPath *second);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -7,8 +7,5 @@ G_BEGIN_DECLS
|
||||
|
||||
void gsk_ensure_resources (void);
|
||||
|
||||
typedef struct _GskVulkanRender GskVulkanRender;
|
||||
typedef struct _GskVulkanRenderPass GskVulkanRenderPass;
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ G_BEGIN_DECLS
|
||||
|
||||
#define GSK_TYPE_RENDER_NODE (gsk_render_node_get_type ())
|
||||
|
||||
#define GSK_IS_RENDER_NODE(obj) ((obj) != NULL)
|
||||
#define GSK_IS_RENDER_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSK_TYPE_RENDER_NODE))
|
||||
|
||||
#define GSK_SERIALIZATION_ERROR (gsk_serialization_error_quark ())
|
||||
|
||||
|
||||
+65
-29
@@ -2421,6 +2421,7 @@ gsk_inset_shadow_node_new (const GskRoundedRect *outline,
|
||||
|
||||
g_return_val_if_fail (outline != NULL, NULL);
|
||||
g_return_val_if_fail (color != NULL, NULL);
|
||||
g_return_val_if_fail (blur_radius >= 0, NULL);
|
||||
|
||||
self = gsk_render_node_alloc (GSK_INSET_SHADOW_NODE);
|
||||
node = (GskRenderNode *) self;
|
||||
@@ -2696,7 +2697,7 @@ gsk_outset_shadow_node_diff (GskRenderNode *node1,
|
||||
|
||||
static void
|
||||
gsk_outset_shadow_node_class_init (gpointer g_class,
|
||||
gpointer class_data)
|
||||
gpointer class_data)
|
||||
{
|
||||
GskRenderNodeClass *node_class = g_class;
|
||||
|
||||
@@ -2734,6 +2735,7 @@ gsk_outset_shadow_node_new (const GskRoundedRect *outline,
|
||||
|
||||
g_return_val_if_fail (outline != NULL, NULL);
|
||||
g_return_val_if_fail (color != NULL, NULL);
|
||||
g_return_val_if_fail (blur_radius >= 0, NULL);
|
||||
|
||||
self = gsk_render_node_alloc (GSK_OUTSET_SHADOW_NODE);
|
||||
node = (GskRenderNode *) self;
|
||||
@@ -3700,8 +3702,7 @@ gsk_color_matrix_node_finalize (GskRenderNode *node)
|
||||
static void
|
||||
apply_color_matrix_to_pattern (cairo_pattern_t *pattern,
|
||||
const graphene_matrix_t *color_matrix,
|
||||
const graphene_vec4_t *color_offset,
|
||||
gboolean multiply_alpha)
|
||||
const graphene_vec4_t *color_offset)
|
||||
{
|
||||
cairo_surface_t *surface, *image_surface;
|
||||
guchar *data;
|
||||
@@ -3739,13 +3740,6 @@ apply_color_matrix_to_pattern (cairo_pattern_t *pattern,
|
||||
graphene_matrix_transform_vec4 (color_matrix, &pixel, &pixel);
|
||||
}
|
||||
|
||||
if (multiply_alpha)
|
||||
graphene_vec4_init (&pixel,
|
||||
graphene_vec4_get_x (&pixel),
|
||||
graphene_vec4_get_y (&pixel),
|
||||
graphene_vec4_get_z (&pixel),
|
||||
alpha * graphene_vec4_get_w (&pixel));
|
||||
|
||||
graphene_vec4_add (&pixel, color_offset, &pixel);
|
||||
|
||||
alpha = graphene_vec4_get_w (&pixel);
|
||||
@@ -3768,6 +3762,8 @@ apply_color_matrix_to_pattern (cairo_pattern_t *pattern,
|
||||
|
||||
cairo_surface_mark_dirty (image_surface);
|
||||
cairo_surface_unmap_image (surface, image_surface);
|
||||
/* https://gitlab.freedesktop.org/cairo/cairo/-/merge_requests/487 */
|
||||
cairo_surface_mark_dirty (surface);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -3789,7 +3785,7 @@ gsk_color_matrix_node_draw (GskRenderNode *node,
|
||||
|
||||
pattern = cairo_pop_group (cr);
|
||||
|
||||
apply_color_matrix_to_pattern (pattern, &self->color_matrix, &self->color_offset, FALSE);
|
||||
apply_color_matrix_to_pattern (pattern, &self->color_matrix, &self->color_offset);
|
||||
|
||||
cairo_set_source (cr, pattern);
|
||||
cairo_paint (cr);
|
||||
@@ -5650,6 +5646,50 @@ gsk_mask_node_finalize (GskRenderNode *node)
|
||||
parent_class->finalize (node);
|
||||
}
|
||||
|
||||
static void
|
||||
apply_luminance_to_pattern (cairo_pattern_t *pattern,
|
||||
gboolean invert_luminance)
|
||||
{
|
||||
cairo_surface_t *surface, *image_surface;
|
||||
guchar *data;
|
||||
gsize x, y, width, height, stride;
|
||||
int red, green, blue, alpha, luminance;
|
||||
guint32* pixel_data;
|
||||
|
||||
cairo_pattern_get_surface (pattern, &surface);
|
||||
image_surface = cairo_surface_map_to_image (surface, NULL);
|
||||
|
||||
data = cairo_image_surface_get_data (image_surface);
|
||||
width = cairo_image_surface_get_width (image_surface);
|
||||
height = cairo_image_surface_get_height (image_surface);
|
||||
stride = cairo_image_surface_get_stride (image_surface);
|
||||
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
pixel_data = (guint32 *) data;
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
alpha = (pixel_data[x] >> 24) & 0xFF;
|
||||
red = (pixel_data[x] >> 16) & 0xFF;
|
||||
green = (pixel_data[x] >> 8) & 0xFF;
|
||||
blue = (pixel_data[x] >> 0) & 0xFF;
|
||||
|
||||
luminance = 2126 * red + 7152 * green + 722 * blue;
|
||||
if (invert_luminance)
|
||||
luminance = 10000 * alpha - luminance;
|
||||
luminance = (luminance + 5000) / 10000;
|
||||
|
||||
pixel_data[x] = luminance * 0x1010101;
|
||||
}
|
||||
data += stride;
|
||||
}
|
||||
|
||||
cairo_surface_mark_dirty (image_surface);
|
||||
cairo_surface_unmap_image (surface, image_surface);
|
||||
/* https://gitlab.freedesktop.org/cairo/cairo/-/merge_requests/487 */
|
||||
cairo_surface_mark_dirty (surface);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_mask_node_draw (GskRenderNode *node,
|
||||
cairo_t *cr)
|
||||
@@ -5672,28 +5712,18 @@ gsk_mask_node_draw (GskRenderNode *node,
|
||||
case GSK_MASK_MODE_ALPHA:
|
||||
break;
|
||||
case GSK_MASK_MODE_INVERTED_ALPHA:
|
||||
graphene_matrix_init_from_float (&color_matrix, (float[]){ 1, 0, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, -1 });
|
||||
graphene_vec4_init (&color_offset, 0, 0, 0, 1);
|
||||
apply_color_matrix_to_pattern (mask_pattern, &color_matrix, &color_offset, FALSE);
|
||||
graphene_matrix_init_from_float (&color_matrix, (float[]){ 0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
-1, -1, -1, -1 });
|
||||
graphene_vec4_init (&color_offset, 1, 1, 1, 1);
|
||||
apply_color_matrix_to_pattern (mask_pattern, &color_matrix, &color_offset);
|
||||
break;
|
||||
case GSK_MASK_MODE_LUMINANCE:
|
||||
graphene_matrix_init_from_float (&color_matrix, (float[]){ 1, 0, 0, 0.2126,
|
||||
0, 1, 0, 0.7152,
|
||||
0, 0, 1, 0.0722,
|
||||
0, 0, 0, 0 });
|
||||
graphene_vec4_init (&color_offset, 0, 0, 0, 0);
|
||||
apply_color_matrix_to_pattern (mask_pattern, &color_matrix, &color_offset, TRUE);
|
||||
apply_luminance_to_pattern (mask_pattern, FALSE);
|
||||
break;
|
||||
case GSK_MASK_MODE_INVERTED_LUMINANCE:
|
||||
graphene_matrix_init_from_float (&color_matrix, (float[]){ 1, 0, 0, -0.2126,
|
||||
0, 1, 0, -0.7152,
|
||||
0, 0, 1, -0.0722,
|
||||
0, 0, 0, 0 });
|
||||
graphene_vec4_init (&color_offset, 0, 0, 0, 1);
|
||||
apply_color_matrix_to_pattern (mask_pattern, &color_matrix, &color_offset, TRUE);
|
||||
apply_luminance_to_pattern (mask_pattern, TRUE);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
@@ -5715,6 +5745,12 @@ gsk_mask_node_diff (GskRenderNode *node1,
|
||||
GskMaskNode *self1 = (GskMaskNode *) node1;
|
||||
GskMaskNode *self2 = (GskMaskNode *) node2;
|
||||
|
||||
if (self1->mask_mode != self2->mask_mode)
|
||||
{
|
||||
gsk_render_node_diff_impossible (node1, node2, region);
|
||||
return;
|
||||
}
|
||||
|
||||
gsk_render_node_diff (self1->source, self2->source, region);
|
||||
gsk_render_node_diff (self1->mask, self2->mask, region);
|
||||
}
|
||||
|
||||
@@ -453,6 +453,21 @@ parse_double (GtkCssParser *parser,
|
||||
return gtk_css_parser_consume_number (parser, out_double);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_positive_double (GtkCssParser *parser,
|
||||
Context *context,
|
||||
gpointer out_double)
|
||||
{
|
||||
if (gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_SIGNED_NUMBER)
|
||||
|| gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_SIGNED_INTEGER))
|
||||
{
|
||||
gtk_css_parser_error_syntax (parser, "Expected a positive number");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return gtk_css_parser_consume_number (parser, out_double);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_point (GtkCssParser *parser,
|
||||
Context *context,
|
||||
@@ -1242,10 +1257,10 @@ parse_radial_gradient_node_internal (GtkCssParser *parser,
|
||||
const Declaration declarations[] = {
|
||||
{ "bounds", parse_rect, NULL, &bounds },
|
||||
{ "center", parse_point, NULL, ¢er },
|
||||
{ "hradius", parse_double, NULL, &hradius },
|
||||
{ "vradius", parse_double, NULL, &vradius },
|
||||
{ "start", parse_double, NULL, &start },
|
||||
{ "end", parse_double, NULL, &end },
|
||||
{ "hradius", parse_positive_double, NULL, &hradius },
|
||||
{ "vradius", parse_positive_double, NULL, &vradius },
|
||||
{ "start", parse_positive_double, NULL, &start },
|
||||
{ "end", parse_positive_double, NULL, &end },
|
||||
{ "stops", parse_stops, clear_stops, &stops },
|
||||
};
|
||||
GskRenderNode *result;
|
||||
@@ -1335,7 +1350,7 @@ parse_inset_shadow_node (GtkCssParser *parser,
|
||||
{ "dx", parse_double, NULL, &dx },
|
||||
{ "dy", parse_double, NULL, &dy },
|
||||
{ "spread", parse_double, NULL, &spread },
|
||||
{ "blur", parse_double, NULL, &blur }
|
||||
{ "blur", parse_positive_double, NULL, &blur }
|
||||
};
|
||||
|
||||
parse_declarations (parser, context, declarations, G_N_ELEMENTS (declarations));
|
||||
@@ -1737,7 +1752,7 @@ parse_outset_shadow_node (GtkCssParser *parser,
|
||||
{ "dx", parse_double, NULL, &dx },
|
||||
{ "dy", parse_double, NULL, &dy },
|
||||
{ "spread", parse_double, NULL, &spread },
|
||||
{ "blur", parse_double, NULL, &blur }
|
||||
{ "blur", parse_positive_double, NULL, &blur }
|
||||
};
|
||||
|
||||
parse_declarations (parser, context, declarations, G_N_ELEMENTS (declarations));
|
||||
@@ -2017,7 +2032,7 @@ parse_blur_node (GtkCssParser *parser,
|
||||
GskRenderNode *child = NULL;
|
||||
double blur_radius = 1.0;
|
||||
const Declaration declarations[] = {
|
||||
{ "blur", parse_double, NULL, &blur_radius },
|
||||
{ "blur", parse_positive_double, NULL, &blur_radius },
|
||||
{ "child", parse_node, clear_node, &child },
|
||||
};
|
||||
GskRenderNode *result;
|
||||
|
||||
@@ -945,3 +945,44 @@ gsk_rounded_rect_to_string (const GskRoundedRect *self)
|
||||
self->corner[3].width,
|
||||
self->corner[3].height);
|
||||
}
|
||||
|
||||
/*
|
||||
* gsk_rounded_rect_get_largest_cover:
|
||||
* @self: the rounded rect to intersect with
|
||||
* @rect: the rectangle to intersect
|
||||
* @result: (out caller-allocates): The resulting rectangle
|
||||
*
|
||||
* Computes the largest rectangle that is fully covered by both
|
||||
* the given rect and the rounded rect.
|
||||
* In particular, this function respects corners, so
|
||||
* gsk_rounded_rect_get_largest_cover(self, &self->bounds, &rect)
|
||||
* can be used to compute a decomposition for a rounded rect itself.
|
||||
**/
|
||||
void
|
||||
gsk_rounded_rect_get_largest_cover (const GskRoundedRect *self,
|
||||
const graphene_rect_t *rect,
|
||||
graphene_rect_t *result)
|
||||
{
|
||||
graphene_rect_t wide, high;
|
||||
double start, end;
|
||||
|
||||
wide = self->bounds;
|
||||
start = MAX(self->corner[GSK_CORNER_TOP_LEFT].height, self->corner[GSK_CORNER_TOP_RIGHT].height);
|
||||
end = MAX(self->corner[GSK_CORNER_BOTTOM_LEFT].height, self->corner[GSK_CORNER_BOTTOM_RIGHT].height);
|
||||
wide.size.height -= MIN (wide.size.height, start + end);
|
||||
wide.origin.y += start;
|
||||
graphene_rect_intersection (&wide, rect, &wide);
|
||||
|
||||
high = self->bounds;
|
||||
start = MAX(self->corner[GSK_CORNER_TOP_LEFT].width, self->corner[GSK_CORNER_BOTTOM_LEFT].width);
|
||||
end = MAX(self->corner[GSK_CORNER_TOP_RIGHT].width, self->corner[GSK_CORNER_BOTTOM_RIGHT].width);
|
||||
high.size.width -= MIN (high.size.width, start + end);
|
||||
high.origin.x += start;
|
||||
graphene_rect_intersection (&high, rect, &high);
|
||||
|
||||
if (wide.size.width * wide.size.height > high.size.width * high.size.height)
|
||||
*result = wide;
|
||||
else
|
||||
*result = high;
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,10 @@ gboolean gsk_rounded_rect_equal (gconstpointer
|
||||
gconstpointer rect2) G_GNUC_PURE;
|
||||
char * gsk_rounded_rect_to_string (const GskRoundedRect *self) G_GNUC_MALLOC;
|
||||
|
||||
void gsk_rounded_rect_get_largest_cover (const GskRoundedRect *self,
|
||||
const graphene_rect_t *rect,
|
||||
graphene_rect_t *result);
|
||||
|
||||
typedef enum {
|
||||
GSK_INTERSECTION_EMPTY,
|
||||
GSK_INTERSECTION_NONEMPTY,
|
||||
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright © 2002 University of Southern California
|
||||
* 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
* Carl D. Worth <cworth@cworth.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gsksplineprivate.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/* Spline deviation from the circle in radius would be given by:
|
||||
|
||||
error = sqrt (x**2 + y**2) - 1
|
||||
|
||||
A simpler error function to work with is:
|
||||
|
||||
e = x**2 + y**2 - 1
|
||||
|
||||
From "Good approximation of circles by curvature-continuous Bezier
|
||||
curves", Tor Dokken and Morten Daehlen, Computer Aided Geometric
|
||||
Design 8 (1990) 22-41, we learn:
|
||||
|
||||
abs (max(e)) = 4/27 * sin**6(angle/4) / cos**2(angle/4)
|
||||
|
||||
and
|
||||
abs (error) =~ 1/2 * e
|
||||
|
||||
Of course, this error value applies only for the particular spline
|
||||
approximation that is used in _cairo_gstate_arc_segment.
|
||||
*/
|
||||
static float
|
||||
arc_error_normalized (float angle)
|
||||
{
|
||||
return 2.0/27.0 * pow (sin (angle / 4), 6) / pow (cos (angle / 4), 2);
|
||||
}
|
||||
|
||||
static float
|
||||
arc_max_angle_for_tolerance_normalized (float tolerance)
|
||||
{
|
||||
float angle, error;
|
||||
guint i;
|
||||
|
||||
/* Use table lookup to reduce search time in most cases. */
|
||||
struct {
|
||||
float angle;
|
||||
float error;
|
||||
} table[] = {
|
||||
{ G_PI / 1.0, 0.0185185185185185036127 },
|
||||
{ G_PI / 2.0, 0.000272567143730179811158 },
|
||||
{ G_PI / 3.0, 2.38647043651461047433e-05 },
|
||||
{ G_PI / 4.0, 4.2455377443222443279e-06 },
|
||||
{ G_PI / 5.0, 1.11281001494389081528e-06 },
|
||||
{ G_PI / 6.0, 3.72662000942734705475e-07 },
|
||||
{ G_PI / 7.0, 1.47783685574284411325e-07 },
|
||||
{ G_PI / 8.0, 6.63240432022601149057e-08 },
|
||||
{ G_PI / 9.0, 3.2715520137536980553e-08 },
|
||||
{ G_PI / 10.0, 1.73863223499021216974e-08 },
|
||||
{ G_PI / 11.0, 9.81410988043554039085e-09 },
|
||||
};
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (table); i++)
|
||||
{
|
||||
if (table[i].error < tolerance)
|
||||
return table[i].angle;
|
||||
}
|
||||
|
||||
i++;
|
||||
do {
|
||||
angle = G_PI / i++;
|
||||
error = arc_error_normalized (angle);
|
||||
} while (error > tolerance);
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
static guint
|
||||
arc_segments_needed (float angle,
|
||||
float radius,
|
||||
float tolerance)
|
||||
{
|
||||
float max_angle;
|
||||
|
||||
/* the error is amplified by at most the length of the
|
||||
* major axis of the circle; see cairo-pen.c for a more detailed analysis
|
||||
* of this. */
|
||||
max_angle = arc_max_angle_for_tolerance_normalized (tolerance / radius);
|
||||
|
||||
return ceil (fabs (angle) / max_angle);
|
||||
}
|
||||
|
||||
/* We want to draw a single spline approximating a circular arc radius
|
||||
R from angle A to angle B. Since we want a symmetric spline that
|
||||
matches the endpoints of the arc in position and slope, we know
|
||||
that the spline control points must be:
|
||||
|
||||
(R * cos(A), R * sin(A))
|
||||
(R * cos(A) - h * sin(A), R * sin(A) + h * cos (A))
|
||||
(R * cos(B) + h * sin(B), R * sin(B) - h * cos (B))
|
||||
(R * cos(B), R * sin(B))
|
||||
|
||||
for some value of h.
|
||||
|
||||
"Approximation of circular arcs by cubic polynomials", Michael
|
||||
Goldapp, Computer Aided Geometric Design 8 (1991) 227-238, provides
|
||||
various values of h along with error analysis for each.
|
||||
|
||||
From that paper, a very practical value of h is:
|
||||
|
||||
h = 4/3 * R * tan(angle/4)
|
||||
|
||||
This value does not give the spline with minimal error, but it does
|
||||
provide a very good approximation, (6th-order convergence), and the
|
||||
error expression is quite simple, (see the comment for
|
||||
_arc_error_normalized).
|
||||
*/
|
||||
static gboolean
|
||||
gsk_spline_decompose_arc_segment (const graphene_point_t *center,
|
||||
float radius,
|
||||
float angle_A,
|
||||
float angle_B,
|
||||
GskSplineAddCurveFunc curve_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
float r_sin_A, r_cos_A;
|
||||
float r_sin_B, r_cos_B;
|
||||
float h;
|
||||
|
||||
r_sin_A = radius * sin (angle_A);
|
||||
r_cos_A = radius * cos (angle_A);
|
||||
r_sin_B = radius * sin (angle_B);
|
||||
r_cos_B = radius * cos (angle_B);
|
||||
|
||||
h = 4.0/3.0 * tan ((angle_B - angle_A) / 4.0);
|
||||
|
||||
return curve_func ((graphene_point_t[4]) {
|
||||
GRAPHENE_POINT_INIT (
|
||||
center->x + r_cos_A,
|
||||
center->y + r_sin_A
|
||||
),
|
||||
GRAPHENE_POINT_INIT (
|
||||
center->x + r_cos_A - h * r_sin_A,
|
||||
center->y + r_sin_A + h * r_cos_A
|
||||
),
|
||||
GRAPHENE_POINT_INIT (
|
||||
center->x + r_cos_B + h * r_sin_B,
|
||||
center->y + r_sin_B - h * r_cos_B
|
||||
),
|
||||
GRAPHENE_POINT_INIT (
|
||||
center->x + r_cos_B,
|
||||
center->y + r_sin_B
|
||||
)
|
||||
},
|
||||
user_data);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gsk_spline_decompose_arc (const graphene_point_t *center,
|
||||
float radius,
|
||||
float tolerance,
|
||||
float start_angle,
|
||||
float end_angle,
|
||||
GskSplineAddCurveFunc curve_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
float step = start_angle - end_angle;
|
||||
guint i, n_segments;
|
||||
|
||||
/* Recurse if drawing arc larger than pi */
|
||||
if (ABS (step) > G_PI)
|
||||
{
|
||||
float mid_angle = (start_angle + end_angle) / 2.0;
|
||||
|
||||
return gsk_spline_decompose_arc (center, radius, tolerance, start_angle, mid_angle, curve_func, user_data)
|
||||
&& gsk_spline_decompose_arc (center, radius, tolerance, mid_angle, end_angle, curve_func, user_data);
|
||||
}
|
||||
else if (ABS (step) < tolerance)
|
||||
{
|
||||
return gsk_spline_decompose_arc_segment (center, radius, start_angle, end_angle, curve_func, user_data);
|
||||
}
|
||||
|
||||
n_segments = arc_segments_needed (ABS (step), radius, tolerance);
|
||||
step = (end_angle - start_angle) / n_segments;
|
||||
|
||||
for (i = 0; i < n_segments - 1; i++, start_angle += step)
|
||||
{
|
||||
if (!gsk_spline_decompose_arc_segment (center, radius, start_angle, start_angle + step, curve_func, user_data))
|
||||
return FALSE;
|
||||
}
|
||||
return gsk_spline_decompose_arc_segment (center, radius, start_angle, end_angle, curve_func, user_data);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright © 2020 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GSK_SPLINE_PRIVATE_H__
|
||||
#define __GSK_SPLINE_PRIVATE_H__
|
||||
|
||||
#include "gskpath.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef gboolean (* GskSplineAddCurveFunc) (const graphene_point_t curve[4],
|
||||
gpointer user_data);
|
||||
gboolean gsk_spline_decompose_arc (const graphene_point_t *center,
|
||||
float radius,
|
||||
float tolerance,
|
||||
float start_angle,
|
||||
float end_angle,
|
||||
GskSplineAddCurveFunc curve_func,
|
||||
gpointer user_data);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_SPLINE_PRIVATE_H__ */
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
#include <gdk/gdk.h>
|
||||
#include <gsk/gskenums.h>
|
||||
|
||||
typedef struct _GskPath GskPath;
|
||||
typedef struct _GskPathBuilder GskPathBuilder;
|
||||
typedef struct _GskPathMeasure GskPathMeasure;
|
||||
typedef struct _GskPathPoint GskPathPoint;
|
||||
typedef struct _GskRenderer GskRenderer;
|
||||
typedef struct _GskTransform GskTransform;
|
||||
|
||||
|
||||
+35
-15
@@ -20,11 +20,14 @@ gsk_private_gl_shaders = [
|
||||
'gl/resources/custom.glsl',
|
||||
'gl/resources/filled_border.glsl',
|
||||
'gl/resources/mask.glsl',
|
||||
'gl/resources/glyphy.atlas.glsl',
|
||||
'gl/resources/glyphy.fs.glsl',
|
||||
'gl/resources/glyphy.vs.glsl',
|
||||
]
|
||||
|
||||
gsk_public_sources = files([
|
||||
'gskdiff.c',
|
||||
'gskcairorenderer.c',
|
||||
'gskdiff.c',
|
||||
'gskglshader.c',
|
||||
'gskrenderer.c',
|
||||
'gskrendernode.c',
|
||||
@@ -37,15 +40,25 @@ gsk_public_sources = files([
|
||||
|
||||
gsk_private_sources = files([
|
||||
'gskcairoblur.c',
|
||||
'gskcontour.c',
|
||||
'gskcurve.c',
|
||||
'gskcurveintersect.c',
|
||||
'gskdebug.c',
|
||||
'gskpath.c',
|
||||
'gskpathbuilder.c',
|
||||
'gskpathmeasure.c',
|
||||
'gskpathops.c',
|
||||
'gskpathpoint.c',
|
||||
'gskprivate.c',
|
||||
'gskprofiler.c',
|
||||
'gskspline.c',
|
||||
'gl/gskglattachmentstate.c',
|
||||
'gl/gskglbuffer.c',
|
||||
'gl/gskglcommandqueue.c',
|
||||
'gl/gskglcompiler.c',
|
||||
'gl/gskgldriver.c',
|
||||
'gl/gskglglyphlibrary.c',
|
||||
'gl/gskglglyphylibrary.c',
|
||||
'gl/gskgliconlibrary.c',
|
||||
'gl/gskglprogram.c',
|
||||
'gl/gskglrenderjob.c',
|
||||
@@ -107,29 +120,35 @@ gsk_private_vulkan_shader_headers = []
|
||||
|
||||
if have_vulkan
|
||||
gsk_private_sources += files([
|
||||
'vulkan/gskvulkanblendmodepipeline.c',
|
||||
'vulkan/gskvulkanblurpipeline.c',
|
||||
'vulkan/gskvulkanborderpipeline.c',
|
||||
'vulkan/gskvulkanboxshadowpipeline.c',
|
||||
'vulkan/gskvulkanblendmodeop.c',
|
||||
'vulkan/gskvulkanblurop.c',
|
||||
'vulkan/gskvulkanborderop.c',
|
||||
'vulkan/gskvulkanbuffer.c',
|
||||
'vulkan/gskvulkanclearop.c',
|
||||
'vulkan/gskvulkanclip.c',
|
||||
'vulkan/gskvulkancolorpipeline.c',
|
||||
'vulkan/gskvulkancolortextpipeline.c',
|
||||
'vulkan/gskvulkancrossfadepipeline.c',
|
||||
'vulkan/gskvulkancolormatrixop.c',
|
||||
'vulkan/gskvulkancolorop.c',
|
||||
'vulkan/gskvulkancommandpool.c',
|
||||
'vulkan/gskvulkaneffectpipeline.c',
|
||||
'vulkan/gskvulkancrossfadeop.c',
|
||||
'vulkan/gskvulkandownloadop.c',
|
||||
'vulkan/gskvulkanglyphcache.c',
|
||||
'vulkan/gskvulkanlineargradientpipeline.c',
|
||||
'vulkan/gskvulkanglyphop.c',
|
||||
'vulkan/gskvulkanimage.c',
|
||||
'vulkan/gskvulkantextpipeline.c',
|
||||
'vulkan/gskvulkantexturepipeline.c',
|
||||
'vulkan/gskvulkaninsetshadowop.c',
|
||||
'vulkan/gskvulkanlineargradientop.c',
|
||||
'vulkan/gskvulkanmaskop.c',
|
||||
'vulkan/gskvulkanmemory.c',
|
||||
'vulkan/gskvulkanpipeline.c',
|
||||
'vulkan/gskvulkanpushconstants.c',
|
||||
'vulkan/gskvulkanop.c',
|
||||
'vulkan/gskvulkanoutsetshadowop.c',
|
||||
'vulkan/gskvulkanpushconstantsop.c',
|
||||
'vulkan/gskvulkanrender.c',
|
||||
'vulkan/gskvulkanrenderer.c',
|
||||
'vulkan/gskvulkanrenderpass.c',
|
||||
'vulkan/gskvulkanshader.c',
|
||||
'vulkan/gskvulkanrenderpassop.c',
|
||||
'vulkan/gskvulkanscissorop.c',
|
||||
'vulkan/gskvulkanshaderop.c',
|
||||
'vulkan/gskvulkantextureop.c',
|
||||
'vulkan/gskvulkanuploadop.c',
|
||||
])
|
||||
|
||||
subdir('vulkan/resources')
|
||||
@@ -179,6 +198,7 @@ gsk_deps = [
|
||||
cairo_csi_dep,
|
||||
pixbuf_dep,
|
||||
libgdk_dep,
|
||||
libglyphy_dep
|
||||
]
|
||||
|
||||
libgsk_f16c = static_library('gsk_f16c',
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskvulkanblendmodeopprivate.h"
|
||||
|
||||
#include "gskvulkanprivate.h"
|
||||
#include "gskvulkanshaderopprivate.h"
|
||||
|
||||
#include "vulkan/resources/blend-mode.vert.h"
|
||||
|
||||
typedef struct _GskVulkanBlendModeOp GskVulkanBlendModeOp;
|
||||
|
||||
struct _GskVulkanBlendModeOp
|
||||
{
|
||||
GskVulkanShaderOp op;
|
||||
|
||||
graphene_rect_t bounds;
|
||||
GskBlendMode blend_mode;
|
||||
|
||||
struct {
|
||||
graphene_rect_t rect;
|
||||
graphene_rect_t tex_rect;
|
||||
guint32 image_descriptor;
|
||||
} top, bottom;
|
||||
};
|
||||
|
||||
static void
|
||||
gsk_vulkan_blend_mode_op_print (GskVulkanOp *op,
|
||||
GString *string,
|
||||
guint indent)
|
||||
{
|
||||
GskVulkanBlendModeOp *self = (GskVulkanBlendModeOp *) op;
|
||||
|
||||
print_indent (string, indent);
|
||||
print_rect (string, &self->bounds);
|
||||
g_string_append_printf (string, "blend-mode %d%% ", self->blend_mode);
|
||||
print_newline (string);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_blend_mode_op_collect_vertex_data (GskVulkanOp *op,
|
||||
guchar *data)
|
||||
{
|
||||
GskVulkanBlendModeOp *self = (GskVulkanBlendModeOp *) op;
|
||||
GskVulkanBlendModeInstance *instance = (GskVulkanBlendModeInstance *) (data + ((GskVulkanShaderOp *) op)->vertex_offset);
|
||||
|
||||
gsk_vulkan_rect_to_float (&self->bounds, instance->rect);
|
||||
gsk_vulkan_rect_to_float (&self->top.rect, instance->top_rect);
|
||||
gsk_vulkan_rect_to_float (&self->bottom.rect, instance->bottom_rect);
|
||||
gsk_vulkan_rect_to_float (&self->top.tex_rect, instance->top_tex_rect);
|
||||
gsk_vulkan_rect_to_float (&self->bottom.tex_rect, instance->bottom_tex_rect);
|
||||
|
||||
instance->top_tex_id = self->top.image_descriptor;
|
||||
instance->bottom_tex_id = self->bottom.image_descriptor;
|
||||
instance->blend_mode = self->blend_mode;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_blend_mode_op_reserve_descriptor_sets (GskVulkanOp *op,
|
||||
GskVulkanRender *render)
|
||||
{
|
||||
GskVulkanBlendModeOp *self = (GskVulkanBlendModeOp *) op;
|
||||
GskVulkanShaderOp *shader = (GskVulkanShaderOp *) op;
|
||||
|
||||
self->top.image_descriptor = gsk_vulkan_render_get_image_descriptor (render,
|
||||
shader->images[0],
|
||||
GSK_VULKAN_SAMPLER_DEFAULT);
|
||||
self->bottom.image_descriptor = gsk_vulkan_render_get_image_descriptor (render,
|
||||
shader->images[1],
|
||||
GSK_VULKAN_SAMPLER_DEFAULT);
|
||||
}
|
||||
|
||||
static const GskVulkanShaderOpClass GSK_VULKAN_BLEND_MODE_OP_CLASS = {
|
||||
{
|
||||
GSK_VULKAN_OP_SIZE (GskVulkanBlendModeOp),
|
||||
GSK_VULKAN_STAGE_SHADER,
|
||||
gsk_vulkan_shader_op_finish,
|
||||
gsk_vulkan_blend_mode_op_print,
|
||||
gsk_vulkan_shader_op_count_vertex_data,
|
||||
gsk_vulkan_blend_mode_op_collect_vertex_data,
|
||||
gsk_vulkan_blend_mode_op_reserve_descriptor_sets,
|
||||
gsk_vulkan_shader_op_command
|
||||
},
|
||||
"blend-mode",
|
||||
2,
|
||||
&gsk_vulkan_blend_mode_info,
|
||||
};
|
||||
|
||||
void
|
||||
gsk_vulkan_blend_mode_op (GskVulkanRender *render,
|
||||
GskVulkanShaderClip clip,
|
||||
const graphene_rect_t *bounds,
|
||||
const graphene_point_t *offset,
|
||||
GskBlendMode blend_mode,
|
||||
GskVulkanImage *top_image,
|
||||
const graphene_rect_t *top_rect,
|
||||
const graphene_rect_t *top_tex_rect,
|
||||
GskVulkanImage *bottom_image,
|
||||
const graphene_rect_t *bottom_rect,
|
||||
const graphene_rect_t *bottom_tex_rect)
|
||||
{
|
||||
GskVulkanBlendModeOp *self;
|
||||
|
||||
self = (GskVulkanBlendModeOp *) gsk_vulkan_shader_op_alloc (render,
|
||||
&GSK_VULKAN_BLEND_MODE_OP_CLASS,
|
||||
clip,
|
||||
(GskVulkanImage *[2]) {
|
||||
top_image,
|
||||
bottom_image
|
||||
});
|
||||
|
||||
graphene_rect_offset_r (bounds, offset->x, offset->y, &self->bounds);
|
||||
self->blend_mode = blend_mode;
|
||||
|
||||
graphene_rect_offset_r (top_rect, offset->x, offset->y, &self->top.rect);
|
||||
gsk_vulkan_normalize_tex_coords (&self->top.tex_rect, bounds, top_tex_rect);
|
||||
|
||||
graphene_rect_offset_r (bottom_rect, offset->x, offset->y, &self->bottom.rect);
|
||||
gsk_vulkan_normalize_tex_coords (&self->bottom.tex_rect, bounds, bottom_tex_rect);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "gskvulkanopprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
void gsk_vulkan_blend_mode_op (GskVulkanRender *render,
|
||||
GskVulkanShaderClip clip,
|
||||
const graphene_rect_t *bounds,
|
||||
const graphene_point_t *offset,
|
||||
GskBlendMode blend_mode,
|
||||
GskVulkanImage *top_image,
|
||||
const graphene_rect_t *top_rect,
|
||||
const graphene_rect_t *top_tex_rect,
|
||||
GskVulkanImage *bottom_image,
|
||||
const graphene_rect_t *bottom_rect,
|
||||
const graphene_rect_t *bottom_tex_rect);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskvulkanblendmodepipelineprivate.h"
|
||||
|
||||
#include "vulkan/resources/blend-mode.vert.h"
|
||||
|
||||
struct _GskVulkanBlendModePipeline
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
|
||||
typedef struct _GskVulkanBlendModeInstance GskVulkanBlendModeInstance;
|
||||
|
||||
G_DEFINE_TYPE (GskVulkanBlendModePipeline, gsk_vulkan_blend_mode_pipeline, GSK_TYPE_VULKAN_PIPELINE)
|
||||
|
||||
static const VkPipelineVertexInputStateCreateInfo *
|
||||
gsk_vulkan_blend_mode_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
|
||||
{
|
||||
return &gsk_vulkan_blend_mode_info;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_blend_mode_pipeline_finalize (GObject *gobject)
|
||||
{
|
||||
//GskVulkanBlendModePipeline *self = GSK_VULKAN_BLUR_PIPELINE (gobject);
|
||||
|
||||
G_OBJECT_CLASS (gsk_vulkan_blend_mode_pipeline_parent_class)->finalize (gobject);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_blend_mode_pipeline_class_init (GskVulkanBlendModePipelineClass *klass)
|
||||
{
|
||||
GskVulkanPipelineClass *pipeline_class = GSK_VULKAN_PIPELINE_CLASS (klass);
|
||||
|
||||
G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_blend_mode_pipeline_finalize;
|
||||
|
||||
pipeline_class->get_input_state_create_info = gsk_vulkan_blend_mode_pipeline_get_input_state_create_info;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_blend_mode_pipeline_init (GskVulkanBlendModePipeline *self)
|
||||
{
|
||||
}
|
||||
|
||||
GskVulkanPipeline *
|
||||
gsk_vulkan_blend_mode_pipeline_new (GdkVulkanContext *context,
|
||||
VkPipelineLayout layout,
|
||||
const char *shader_name,
|
||||
VkRenderPass render_pass)
|
||||
{
|
||||
return gsk_vulkan_pipeline_new (GSK_TYPE_VULKAN_BLEND_MODE_PIPELINE, context, layout, shader_name, render_pass);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_vulkan_blend_mode_pipeline_collect_vertex_data (GskVulkanBlendModePipeline *pipeline,
|
||||
guchar *data,
|
||||
guint32 top_tex_id[2],
|
||||
guint32 bottom_tex_id[2],
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *bounds,
|
||||
const graphene_rect_t *top_bounds,
|
||||
const graphene_rect_t *bottom_bounds,
|
||||
const graphene_rect_t *top_tex_rect,
|
||||
const graphene_rect_t *bottom_tex_rect,
|
||||
GskBlendMode blend_mode)
|
||||
{
|
||||
GskVulkanBlendModeInstance *instance = (GskVulkanBlendModeInstance *) data;
|
||||
|
||||
instance->rect[0] = bounds->origin.x + offset->x;
|
||||
instance->rect[1] = bounds->origin.y + offset->y;
|
||||
instance->rect[2] = bounds->size.width;
|
||||
instance->rect[3] = bounds->size.height;
|
||||
|
||||
instance->top_rect[0] = top_bounds->origin.x + offset->x;
|
||||
instance->top_rect[1] = top_bounds->origin.y + offset->y;
|
||||
instance->top_rect[2] = top_bounds->size.width;
|
||||
instance->top_rect[3] = top_bounds->size.height;
|
||||
|
||||
instance->bottom_rect[0] = bottom_bounds->origin.x + offset->x;
|
||||
instance->bottom_rect[1] = bottom_bounds->origin.y + offset->y;
|
||||
instance->bottom_rect[2] = bottom_bounds->size.width;
|
||||
instance->bottom_rect[3] = bottom_bounds->size.height;
|
||||
|
||||
instance->top_tex_rect[0] = top_tex_rect->origin.x;
|
||||
instance->top_tex_rect[1] = top_tex_rect->origin.y;
|
||||
instance->top_tex_rect[2] = top_tex_rect->size.width;
|
||||
instance->top_tex_rect[3] = top_tex_rect->size.height;
|
||||
|
||||
instance->bottom_tex_rect[0] = bottom_tex_rect->origin.x;
|
||||
instance->bottom_tex_rect[1] = bottom_tex_rect->origin.y;
|
||||
instance->bottom_tex_rect[2] = bottom_tex_rect->size.width;
|
||||
instance->bottom_tex_rect[3] = bottom_tex_rect->size.height;
|
||||
|
||||
instance->top_tex_id[0] = top_tex_id[0];
|
||||
instance->top_tex_id[1] = top_tex_id[1];
|
||||
instance->bottom_tex_id[0] = bottom_tex_id[0];
|
||||
instance->bottom_tex_id[1] = bottom_tex_id[1];
|
||||
instance->blend_mode = blend_mode;
|
||||
}
|
||||
|
||||
gsize
|
||||
gsk_vulkan_blend_mode_pipeline_draw (GskVulkanBlendModePipeline *pipeline,
|
||||
VkCommandBuffer command_buffer,
|
||||
gsize offset,
|
||||
gsize n_commands)
|
||||
{
|
||||
vkCmdDraw (command_buffer,
|
||||
6, n_commands,
|
||||
0, offset);
|
||||
|
||||
return n_commands;
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <graphene.h>
|
||||
|
||||
#include "gskvulkanpipelineprivate.h"
|
||||
#include "gskenums.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GskVulkanBlendModePipelineLayout GskVulkanBlendModePipelineLayout;
|
||||
|
||||
#define GSK_TYPE_VULKAN_BLEND_MODE_PIPELINE (gsk_vulkan_blend_mode_pipeline_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GskVulkanBlendModePipeline, gsk_vulkan_blend_mode_pipeline, GSK, VULKAN_BLEND_MODE_PIPELINE, GskVulkanPipeline)
|
||||
|
||||
GskVulkanPipeline * gsk_vulkan_blend_mode_pipeline_new (GdkVulkanContext *context,
|
||||
VkPipelineLayout layout,
|
||||
const char *shader_name,
|
||||
VkRenderPass render_pass);
|
||||
|
||||
void gsk_vulkan_blend_mode_pipeline_collect_vertex_data (GskVulkanBlendModePipeline *pipeline,
|
||||
guchar *data,
|
||||
guint32 top_tex_id[2],
|
||||
guint32 bottom_tex_id[2],
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *bounds,
|
||||
const graphene_rect_t *top_bounds,
|
||||
const graphene_rect_t *bottom_bounds,
|
||||
const graphene_rect_t *top_tex_rect,
|
||||
const graphene_rect_t *bottom_tex_rect,
|
||||
GskBlendMode blend_mode);
|
||||
gsize gsk_vulkan_blend_mode_pipeline_draw (GskVulkanBlendModePipeline *pipeline,
|
||||
VkCommandBuffer command_buffer,
|
||||
gsize offset,
|
||||
gsize n_commands);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskvulkanbluropprivate.h"
|
||||
|
||||
#include "gskvulkanprivate.h"
|
||||
#include "gskvulkanshaderopprivate.h"
|
||||
|
||||
#include "vulkan/resources/blur.vert.h"
|
||||
|
||||
typedef struct _GskVulkanBlurOp GskVulkanBlurOp;
|
||||
|
||||
struct _GskVulkanBlurOp
|
||||
{
|
||||
GskVulkanShaderOp op;
|
||||
|
||||
graphene_rect_t rect;
|
||||
graphene_rect_t tex_rect;
|
||||
float radius;
|
||||
|
||||
guint32 image_descriptor;
|
||||
};
|
||||
|
||||
static void
|
||||
gsk_vulkan_blur_op_print (GskVulkanOp *op,
|
||||
GString *string,
|
||||
guint indent)
|
||||
{
|
||||
GskVulkanBlurOp *self = (GskVulkanBlurOp *) op;
|
||||
|
||||
print_indent (string, indent);
|
||||
print_rect (string, &self->rect);
|
||||
g_string_append_printf (string, "blur %g",
|
||||
self->radius);
|
||||
print_newline (string);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_blur_op_collect_vertex_data (GskVulkanOp *op,
|
||||
guchar *data)
|
||||
{
|
||||
GskVulkanBlurOp *self = (GskVulkanBlurOp *) op;
|
||||
GskVulkanBlurInstance *instance = (GskVulkanBlurInstance *) (data + ((GskVulkanShaderOp *) op)->vertex_offset);
|
||||
|
||||
gsk_vulkan_rect_to_float (&self->rect, instance->rect);
|
||||
gsk_vulkan_rect_to_float (&self->tex_rect, instance->tex_rect);
|
||||
instance->tex_id = self->image_descriptor;
|
||||
instance->radius = self->radius;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_blur_op_reserve_descriptor_sets (GskVulkanOp *op,
|
||||
GskVulkanRender *render)
|
||||
{
|
||||
GskVulkanBlurOp *self = (GskVulkanBlurOp *) op;
|
||||
GskVulkanShaderOp *shader = (GskVulkanShaderOp *) op;
|
||||
|
||||
self->image_descriptor = gsk_vulkan_render_get_image_descriptor (render,
|
||||
shader->images[0],
|
||||
GSK_VULKAN_SAMPLER_DEFAULT);
|
||||
}
|
||||
|
||||
static const GskVulkanShaderOpClass GSK_VULKAN_BLUR_OP_CLASS = {
|
||||
{
|
||||
GSK_VULKAN_OP_SIZE (GskVulkanBlurOp),
|
||||
GSK_VULKAN_STAGE_SHADER,
|
||||
gsk_vulkan_shader_op_finish,
|
||||
gsk_vulkan_blur_op_print,
|
||||
gsk_vulkan_shader_op_count_vertex_data,
|
||||
gsk_vulkan_blur_op_collect_vertex_data,
|
||||
gsk_vulkan_blur_op_reserve_descriptor_sets,
|
||||
gsk_vulkan_shader_op_command
|
||||
},
|
||||
"blur",
|
||||
1,
|
||||
&gsk_vulkan_blur_info,
|
||||
};
|
||||
|
||||
void
|
||||
gsk_vulkan_blur_op (GskVulkanRender *render,
|
||||
GskVulkanShaderClip clip,
|
||||
GskVulkanImage *image,
|
||||
const graphene_rect_t *rect,
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *tex_rect,
|
||||
float radius)
|
||||
{
|
||||
GskVulkanBlurOp *self;
|
||||
|
||||
g_assert (radius > 0);
|
||||
|
||||
self = (GskVulkanBlurOp *) gsk_vulkan_shader_op_alloc (render, &GSK_VULKAN_BLUR_OP_CLASS, clip, &image);
|
||||
|
||||
graphene_rect_offset_r (rect, offset->x, offset->y, &self->rect);
|
||||
gsk_vulkan_normalize_tex_coords (&self->tex_rect, rect, tex_rect);
|
||||
self->radius = radius;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "gskvulkanopprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
void gsk_vulkan_blur_op (GskVulkanRender *render,
|
||||
GskVulkanShaderClip clip,
|
||||
GskVulkanImage *image,
|
||||
const graphene_rect_t *rect,
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *tex_rect,
|
||||
float radius);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskvulkanblurpipelineprivate.h"
|
||||
|
||||
#include "vulkan/resources/blur.vert.h"
|
||||
|
||||
struct _GskVulkanBlurPipeline
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GskVulkanBlurPipeline, gsk_vulkan_blur_pipeline, GSK_TYPE_VULKAN_PIPELINE)
|
||||
|
||||
static const VkPipelineVertexInputStateCreateInfo *
|
||||
gsk_vulkan_blur_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
|
||||
{
|
||||
return &gsk_vulkan_blur_info;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_blur_pipeline_finalize (GObject *gobject)
|
||||
{
|
||||
//GskVulkanBlurPipeline *self = GSK_VULKAN_BLUR_PIPELINE (gobject);
|
||||
|
||||
G_OBJECT_CLASS (gsk_vulkan_blur_pipeline_parent_class)->finalize (gobject);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_blur_pipeline_class_init (GskVulkanBlurPipelineClass *klass)
|
||||
{
|
||||
GskVulkanPipelineClass *pipeline_class = GSK_VULKAN_PIPELINE_CLASS (klass);
|
||||
|
||||
G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_blur_pipeline_finalize;
|
||||
|
||||
pipeline_class->get_input_state_create_info = gsk_vulkan_blur_pipeline_get_input_state_create_info;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_blur_pipeline_init (GskVulkanBlurPipeline *self)
|
||||
{
|
||||
}
|
||||
|
||||
GskVulkanPipeline *
|
||||
gsk_vulkan_blur_pipeline_new (GdkVulkanContext *context,
|
||||
VkPipelineLayout layout,
|
||||
const char *shader_name,
|
||||
VkRenderPass render_pass)
|
||||
{
|
||||
return gsk_vulkan_pipeline_new (GSK_TYPE_VULKAN_BLUR_PIPELINE, context, layout, shader_name, render_pass);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_vulkan_blur_pipeline_collect_vertex_data (GskVulkanBlurPipeline *pipeline,
|
||||
guchar *data,
|
||||
guint32 tex_id[2],
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *rect,
|
||||
const graphene_rect_t *tex_rect,
|
||||
double radius)
|
||||
{
|
||||
GskVulkanBlurInstance *instance = (GskVulkanBlurInstance *) data;
|
||||
|
||||
instance->rect[0] = rect->origin.x + offset->x;
|
||||
instance->rect[1] = rect->origin.y + offset->y;
|
||||
instance->rect[2] = rect->size.width;
|
||||
instance->rect[3] = rect->size.height;
|
||||
instance->tex_rect[0] = tex_rect->origin.x;
|
||||
instance->tex_rect[1] = tex_rect->origin.y;
|
||||
instance->tex_rect[2] = tex_rect->size.width;
|
||||
instance->tex_rect[3] = tex_rect->size.height;
|
||||
instance->radius = radius;
|
||||
instance->tex_id[0] = tex_id[0];
|
||||
instance->tex_id[1] = tex_id[1];
|
||||
}
|
||||
|
||||
gsize
|
||||
gsk_vulkan_blur_pipeline_draw (GskVulkanBlurPipeline *pipeline,
|
||||
VkCommandBuffer command_buffer,
|
||||
gsize offset,
|
||||
gsize n_commands)
|
||||
{
|
||||
vkCmdDraw (command_buffer,
|
||||
6, n_commands,
|
||||
0, offset);
|
||||
|
||||
return n_commands;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <graphene.h>
|
||||
|
||||
#include "gskvulkanpipelineprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GskVulkanBlurPipelineLayout GskVulkanBlurPipelineLayout;
|
||||
|
||||
#define GSK_TYPE_VULKAN_BLUR_PIPELINE (gsk_vulkan_blur_pipeline_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GskVulkanBlurPipeline, gsk_vulkan_blur_pipeline, GSK, VULKAN_BLUR_PIPELINE, GskVulkanPipeline)
|
||||
|
||||
GskVulkanPipeline * gsk_vulkan_blur_pipeline_new (GdkVulkanContext *context,
|
||||
VkPipelineLayout layout,
|
||||
const char *shader_name,
|
||||
VkRenderPass render_pass);
|
||||
|
||||
void gsk_vulkan_blur_pipeline_collect_vertex_data (GskVulkanBlurPipeline *pipeline,
|
||||
guchar *data,
|
||||
guint32 tex_id[2],
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *rect,
|
||||
const graphene_rect_t *tex_rect,
|
||||
double radius);
|
||||
gsize gsk_vulkan_blur_pipeline_draw (GskVulkanBlurPipeline *pipeline,
|
||||
VkCommandBuffer command_buffer,
|
||||
gsize offset,
|
||||
gsize n_commands);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskvulkanborderopprivate.h"
|
||||
|
||||
#include "gskvulkanprivate.h"
|
||||
#include "gskvulkanshaderopprivate.h"
|
||||
#include "gsk/gskroundedrectprivate.h"
|
||||
|
||||
#include "vulkan/resources/border.vert.h"
|
||||
|
||||
typedef struct _GskVulkanBorderOp GskVulkanBorderOp;
|
||||
|
||||
struct _GskVulkanBorderOp
|
||||
{
|
||||
GskVulkanShaderOp op;
|
||||
|
||||
GskRoundedRect outline;
|
||||
float widths[4];
|
||||
GdkRGBA colors[4];
|
||||
};
|
||||
|
||||
static void
|
||||
gsk_vulkan_border_op_finish (GskVulkanOp *op)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_border_op_print (GskVulkanOp *op,
|
||||
GString *string,
|
||||
guint indent)
|
||||
{
|
||||
GskVulkanBorderOp *self = (GskVulkanBorderOp *) op;
|
||||
|
||||
print_indent (string, indent);
|
||||
print_rounded_rect (string, &self->outline);
|
||||
g_string_append (string, "border ");
|
||||
print_rgba (string, &self->colors[0]);
|
||||
if (!gdk_rgba_equal (&self->colors[3], &self->colors[0]) ||
|
||||
!gdk_rgba_equal (&self->colors[2], &self->colors[0]) ||
|
||||
!gdk_rgba_equal (&self->colors[1], &self->colors[0]))
|
||||
{
|
||||
print_rgba (string, &self->colors[1]);
|
||||
print_rgba (string, &self->colors[2]);
|
||||
print_rgba (string, &self->colors[3]);
|
||||
}
|
||||
g_string_append_printf (string, "%g ", self->widths[0]);
|
||||
if (self->widths[0] != self->widths[1] ||
|
||||
self->widths[0] != self->widths[2] ||
|
||||
self->widths[0] != self->widths[3])
|
||||
g_string_append_printf (string, "%g %g %g ", self->widths[1], self->widths[2], self->widths[3]);
|
||||
|
||||
print_newline (string);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_border_op_collect_vertex_data (GskVulkanOp *op,
|
||||
guchar *data)
|
||||
{
|
||||
GskVulkanBorderOp *self = (GskVulkanBorderOp *) op;
|
||||
GskVulkanBorderInstance *instance = (GskVulkanBorderInstance *) (data + ((GskVulkanShaderOp *) op)->vertex_offset);
|
||||
guint i;
|
||||
|
||||
gsk_rounded_rect_to_float (&self->outline, graphene_point_zero (), instance->rect);
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
instance->border_widths[i] = self->widths[i];
|
||||
gsk_vulkan_rgba_to_float (&self->colors[i], (gpointer) &instance->border_colors[4 * i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_border_op_reserve_descriptor_sets (GskVulkanOp *op,
|
||||
GskVulkanRender *render)
|
||||
{
|
||||
}
|
||||
|
||||
static GskVulkanOp *
|
||||
gsk_vulkan_border_op_command (GskVulkanOp *op,
|
||||
GskVulkanRender *render,
|
||||
VkRenderPass render_pass,
|
||||
VkCommandBuffer command_buffer)
|
||||
{
|
||||
return gsk_vulkan_shader_op_command_n (op, render, render_pass, command_buffer, 8);
|
||||
}
|
||||
|
||||
static const GskVulkanShaderOpClass GSK_VULKAN_BORDER_OP_CLASS = {
|
||||
{
|
||||
GSK_VULKAN_OP_SIZE (GskVulkanBorderOp),
|
||||
GSK_VULKAN_STAGE_SHADER,
|
||||
gsk_vulkan_border_op_finish,
|
||||
gsk_vulkan_border_op_print,
|
||||
gsk_vulkan_shader_op_count_vertex_data,
|
||||
gsk_vulkan_border_op_collect_vertex_data,
|
||||
gsk_vulkan_border_op_reserve_descriptor_sets,
|
||||
gsk_vulkan_border_op_command
|
||||
},
|
||||
"border",
|
||||
0,
|
||||
&gsk_vulkan_border_info,
|
||||
};
|
||||
|
||||
void
|
||||
gsk_vulkan_border_op (GskVulkanRender *render,
|
||||
GskVulkanShaderClip clip,
|
||||
const GskRoundedRect *outline,
|
||||
const graphene_point_t *offset,
|
||||
const float widths[4],
|
||||
const GdkRGBA colors[4])
|
||||
{
|
||||
GskVulkanBorderOp *self;
|
||||
guint i;
|
||||
|
||||
self = (GskVulkanBorderOp *) gsk_vulkan_shader_op_alloc (render, &GSK_VULKAN_BORDER_OP_CLASS, clip, NULL);
|
||||
|
||||
self->outline = *outline;
|
||||
gsk_rounded_rect_offset (&self->outline, offset->x, offset->y);
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
self->widths[i] = widths[i];
|
||||
self->colors[i] = colors[i];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "gskvulkanopprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
void gsk_vulkan_border_op (GskVulkanRender *render,
|
||||
GskVulkanShaderClip clip,
|
||||
const GskRoundedRect *outline,
|
||||
const graphene_point_t *offset,
|
||||
const float widths[4],
|
||||
const GdkRGBA colors[4]);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskvulkanborderpipelineprivate.h"
|
||||
|
||||
#include "gskroundedrectprivate.h"
|
||||
|
||||
#include "vulkan/resources/border.vert.h"
|
||||
|
||||
struct _GskVulkanBorderPipeline
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GskVulkanBorderPipeline, gsk_vulkan_border_pipeline, GSK_TYPE_VULKAN_PIPELINE)
|
||||
|
||||
static const VkPipelineVertexInputStateCreateInfo *
|
||||
gsk_vulkan_border_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
|
||||
{
|
||||
return &gsk_vulkan_border_info;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_border_pipeline_finalize (GObject *gobject)
|
||||
{
|
||||
//GskVulkanBorderPipeline *self = GSK_VULKAN_BORDER_PIPELINE (gobject);
|
||||
|
||||
G_OBJECT_CLASS (gsk_vulkan_border_pipeline_parent_class)->finalize (gobject);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_border_pipeline_class_init (GskVulkanBorderPipelineClass *klass)
|
||||
{
|
||||
GskVulkanPipelineClass *pipeline_class = GSK_VULKAN_PIPELINE_CLASS (klass);
|
||||
|
||||
G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_border_pipeline_finalize;
|
||||
|
||||
pipeline_class->get_input_state_create_info = gsk_vulkan_border_pipeline_get_input_state_create_info;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_border_pipeline_init (GskVulkanBorderPipeline *self)
|
||||
{
|
||||
}
|
||||
|
||||
GskVulkanPipeline *
|
||||
gsk_vulkan_border_pipeline_new (GdkVulkanContext *context,
|
||||
VkPipelineLayout layout,
|
||||
const char *shader_name,
|
||||
VkRenderPass render_pass)
|
||||
{
|
||||
return gsk_vulkan_pipeline_new (GSK_TYPE_VULKAN_BORDER_PIPELINE, context, layout, shader_name, render_pass);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_vulkan_border_pipeline_collect_vertex_data (GskVulkanBorderPipeline *pipeline,
|
||||
guchar *data,
|
||||
const graphene_point_t *offset,
|
||||
const GskRoundedRect *rect,
|
||||
const float widths[4],
|
||||
const GdkRGBA colors[4])
|
||||
{
|
||||
GskVulkanBorderInstance *instance = (GskVulkanBorderInstance *) data;
|
||||
guint i;
|
||||
|
||||
gsk_rounded_rect_to_float (rect, offset, instance->rect);
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
instance->border_widths[i] = widths[i];
|
||||
instance->border_colors[4 * i + 0] = colors[i].red;
|
||||
instance->border_colors[4 * i + 1] = colors[i].green;
|
||||
instance->border_colors[4 * i + 2] = colors[i].blue;
|
||||
instance->border_colors[4 * i + 3] = colors[i].alpha;
|
||||
}
|
||||
}
|
||||
|
||||
gsize
|
||||
gsk_vulkan_border_pipeline_draw (GskVulkanBorderPipeline *pipeline,
|
||||
VkCommandBuffer command_buffer,
|
||||
gsize offset,
|
||||
gsize n_commands)
|
||||
{
|
||||
vkCmdDraw (command_buffer,
|
||||
6 * 8, n_commands,
|
||||
0, offset);
|
||||
|
||||
return n_commands;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <graphene.h>
|
||||
|
||||
#include "gskvulkanpipelineprivate.h"
|
||||
#include "gskroundedrect.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GskVulkanBorderPipelineLayout GskVulkanBorderPipelineLayout;
|
||||
|
||||
#define GSK_TYPE_VULKAN_BORDER_PIPELINE (gsk_vulkan_border_pipeline_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GskVulkanBorderPipeline, gsk_vulkan_border_pipeline, GSK, VULKAN_BORDER_PIPELINE, GskVulkanPipeline)
|
||||
|
||||
GskVulkanPipeline * gsk_vulkan_border_pipeline_new (GdkVulkanContext *context,
|
||||
VkPipelineLayout layout,
|
||||
const char *shader_name,
|
||||
VkRenderPass render_pass);
|
||||
|
||||
void gsk_vulkan_border_pipeline_collect_vertex_data (GskVulkanBorderPipeline *pipeline,
|
||||
guchar *data,
|
||||
const graphene_point_t *offset,
|
||||
const GskRoundedRect *rect,
|
||||
const float widths[4],
|
||||
const GdkRGBA colors[4]);
|
||||
gsize gsk_vulkan_border_pipeline_draw (GskVulkanBorderPipeline *pipeline,
|
||||
VkCommandBuffer command_buffer,
|
||||
gsize offset,
|
||||
gsize n_commands);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskvulkanboxshadowpipelineprivate.h"
|
||||
|
||||
#include "vulkan/resources/inset-shadow.vert.h"
|
||||
|
||||
#include "gskroundedrectprivate.h"
|
||||
|
||||
struct _GskVulkanBoxShadowPipeline
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GskVulkanBoxShadowPipeline, gsk_vulkan_box_shadow_pipeline, GSK_TYPE_VULKAN_PIPELINE)
|
||||
|
||||
static const VkPipelineVertexInputStateCreateInfo *
|
||||
gsk_vulkan_box_shadow_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
|
||||
{
|
||||
return &gsk_vulkan_inset_shadow_info;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_box_shadow_pipeline_finalize (GObject *gobject)
|
||||
{
|
||||
//GskVulkanBoxShadowPipeline *self = GSK_VULKAN_BOX_SHADOW_PIPELINE (gobject);
|
||||
|
||||
G_OBJECT_CLASS (gsk_vulkan_box_shadow_pipeline_parent_class)->finalize (gobject);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_box_shadow_pipeline_class_init (GskVulkanBoxShadowPipelineClass *klass)
|
||||
{
|
||||
GskVulkanPipelineClass *pipeline_class = GSK_VULKAN_PIPELINE_CLASS (klass);
|
||||
|
||||
G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_box_shadow_pipeline_finalize;
|
||||
|
||||
pipeline_class->get_input_state_create_info = gsk_vulkan_box_shadow_pipeline_get_input_state_create_info;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_box_shadow_pipeline_init (GskVulkanBoxShadowPipeline *self)
|
||||
{
|
||||
}
|
||||
|
||||
GskVulkanPipeline *
|
||||
gsk_vulkan_box_shadow_pipeline_new (GdkVulkanContext *context,
|
||||
VkPipelineLayout layout,
|
||||
const char *shader_name,
|
||||
VkRenderPass render_pass)
|
||||
{
|
||||
return gsk_vulkan_pipeline_new (GSK_TYPE_VULKAN_BOX_SHADOW_PIPELINE, context, layout, shader_name, render_pass);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_vulkan_box_shadow_pipeline_collect_vertex_data (GskVulkanBoxShadowPipeline *pipeline,
|
||||
guchar *data,
|
||||
const graphene_point_t *offset,
|
||||
const GskRoundedRect *outline,
|
||||
const GdkRGBA *color,
|
||||
float dx,
|
||||
float dy,
|
||||
float spread,
|
||||
float blur_radius)
|
||||
{
|
||||
GskVulkanInsetShadowInstance *instance = (GskVulkanInsetShadowInstance *) data;
|
||||
|
||||
gsk_rounded_rect_to_float (outline, offset, instance->outline);
|
||||
instance->color[0] = color->red;
|
||||
instance->color[1] = color->green;
|
||||
instance->color[2] = color->blue;
|
||||
instance->color[3] = color->alpha;
|
||||
instance->offset[0] = dx;
|
||||
instance->offset[1] = dy;
|
||||
instance->spread = spread;
|
||||
instance->blur_radius = blur_radius;
|
||||
}
|
||||
|
||||
gsize
|
||||
gsk_vulkan_box_shadow_pipeline_draw (GskVulkanBoxShadowPipeline *pipeline,
|
||||
VkCommandBuffer command_buffer,
|
||||
gsize offset,
|
||||
gsize n_commands)
|
||||
{
|
||||
vkCmdDraw (command_buffer,
|
||||
6 * 8, n_commands,
|
||||
0, offset);
|
||||
|
||||
return n_commands;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <graphene.h>
|
||||
|
||||
#include "gskvulkanpipelineprivate.h"
|
||||
#include "gskroundedrect.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GskVulkanBoxShadowPipelineLayout GskVulkanBoxShadowPipelineLayout;
|
||||
|
||||
#define GSK_TYPE_VULKAN_BOX_SHADOW_PIPELINE (gsk_vulkan_box_shadow_pipeline_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GskVulkanBoxShadowPipeline, gsk_vulkan_box_shadow_pipeline, GSK, VULKAN_BOX_SHADOW_PIPELINE, GskVulkanPipeline)
|
||||
|
||||
GskVulkanPipeline * gsk_vulkan_box_shadow_pipeline_new (GdkVulkanContext *context,
|
||||
VkPipelineLayout layout,
|
||||
const char *shader_name,
|
||||
VkRenderPass render_pass);
|
||||
|
||||
void gsk_vulkan_box_shadow_pipeline_collect_vertex_data (GskVulkanBoxShadowPipeline *pipeline,
|
||||
guchar *data,
|
||||
const graphene_point_t *offset,
|
||||
const GskRoundedRect *outline,
|
||||
const GdkRGBA *color,
|
||||
float dx,
|
||||
float dy,
|
||||
float spread,
|
||||
float blur_radius);
|
||||
|
||||
gsize gsk_vulkan_box_shadow_pipeline_draw (GskVulkanBoxShadowPipeline *pipeline,
|
||||
VkCommandBuffer command_buffer,
|
||||
gsize offset,
|
||||
gsize n_commands);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskvulkanbufferprivate.h"
|
||||
|
||||
#include "gskvulkanmemoryprivate.h"
|
||||
#include "gskvulkanpipelineprivate.h"
|
||||
#include "gskvulkanprivate.h"
|
||||
|
||||
struct _GskVulkanBuffer
|
||||
{
|
||||
@@ -102,6 +103,12 @@ gsk_vulkan_buffer_get_buffer (GskVulkanBuffer *self)
|
||||
return self->vk_buffer;
|
||||
}
|
||||
|
||||
gsize
|
||||
gsk_vulkan_buffer_get_size (GskVulkanBuffer *self)
|
||||
{
|
||||
return self->size;
|
||||
}
|
||||
|
||||
guchar *
|
||||
gsk_vulkan_buffer_map (GskVulkanBuffer *self)
|
||||
{
|
||||
|
||||
@@ -23,6 +23,7 @@ GskVulkanBuffer * gsk_vulkan_buffer_new_map (GdkVulk
|
||||
void gsk_vulkan_buffer_free (GskVulkanBuffer *buffer);
|
||||
|
||||
VkBuffer gsk_vulkan_buffer_get_buffer (GskVulkanBuffer *self);
|
||||
gsize gsk_vulkan_buffer_get_size (GskVulkanBuffer *self);
|
||||
|
||||
guchar * gsk_vulkan_buffer_map (GskVulkanBuffer *self);
|
||||
void gsk_vulkan_buffer_unmap (GskVulkanBuffer *self);
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskvulkanclearopprivate.h"
|
||||
|
||||
#include "gskvulkanprivate.h"
|
||||
|
||||
typedef struct _GskVulkanClearOp GskVulkanClearOp;
|
||||
|
||||
struct _GskVulkanClearOp
|
||||
{
|
||||
GskVulkanOp op;
|
||||
|
||||
cairo_rectangle_int_t rect;
|
||||
GdkRGBA color;
|
||||
};
|
||||
|
||||
static void
|
||||
gsk_vulkan_clear_op_finish (GskVulkanOp *op)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_clear_op_print (GskVulkanOp *op,
|
||||
GString *string,
|
||||
guint indent)
|
||||
{
|
||||
GskVulkanClearOp *self = (GskVulkanClearOp *) op;
|
||||
|
||||
print_indent (string, indent);
|
||||
print_int_rect (string, &self->rect);
|
||||
g_string_append_printf (string, "clear ");
|
||||
print_rgba (string, &self->color);
|
||||
print_newline (string);
|
||||
}
|
||||
|
||||
static gsize
|
||||
gsk_vulkan_clear_op_count_vertex_data (GskVulkanOp *op,
|
||||
gsize n_bytes)
|
||||
{
|
||||
return n_bytes;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_clear_op_collect_vertex_data (GskVulkanOp *op,
|
||||
guchar *data)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_clear_op_reserve_descriptor_sets (GskVulkanOp *op,
|
||||
GskVulkanRender *render)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_init_clear_value (VkClearValue *value,
|
||||
const GdkRGBA *rgba)
|
||||
{
|
||||
gsk_vulkan_rgba_to_float (rgba, value->color.float32);
|
||||
}
|
||||
|
||||
static GskVulkanOp *
|
||||
gsk_vulkan_clear_op_command (GskVulkanOp *op,
|
||||
GskVulkanRender *render,
|
||||
VkRenderPass render_pass,
|
||||
VkCommandBuffer command_buffer)
|
||||
{
|
||||
GskVulkanClearOp *self = (GskVulkanClearOp *) op;
|
||||
VkClearValue clear_value;
|
||||
|
||||
gsk_vulkan_init_clear_value (&clear_value, &self->color);
|
||||
|
||||
vkCmdClearAttachments (command_buffer,
|
||||
1,
|
||||
&(VkClearAttachment) {
|
||||
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
0,
|
||||
clear_value,
|
||||
},
|
||||
1,
|
||||
&(VkClearRect) {
|
||||
{
|
||||
{ self->rect.x, self->rect.y },
|
||||
{ self->rect.width, self->rect.height },
|
||||
},
|
||||
0,
|
||||
1
|
||||
});
|
||||
|
||||
return op->next;
|
||||
}
|
||||
|
||||
static const GskVulkanOpClass GSK_VULKAN_SCISSOR_OP_CLASS = {
|
||||
GSK_VULKAN_OP_SIZE (GskVulkanClearOp),
|
||||
GSK_VULKAN_STAGE_COMMAND,
|
||||
gsk_vulkan_clear_op_finish,
|
||||
gsk_vulkan_clear_op_print,
|
||||
gsk_vulkan_clear_op_count_vertex_data,
|
||||
gsk_vulkan_clear_op_collect_vertex_data,
|
||||
gsk_vulkan_clear_op_reserve_descriptor_sets,
|
||||
gsk_vulkan_clear_op_command
|
||||
};
|
||||
|
||||
void
|
||||
gsk_vulkan_clear_op (GskVulkanRender *render,
|
||||
const cairo_rectangle_int_t *rect,
|
||||
const GdkRGBA *color)
|
||||
{
|
||||
GskVulkanClearOp *self;
|
||||
|
||||
self = (GskVulkanClearOp *) gsk_vulkan_op_alloc (render, &GSK_VULKAN_SCISSOR_OP_CLASS);
|
||||
|
||||
self->rect = *rect;
|
||||
self->color = *color;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "gskvulkanopprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
void gsk_vulkan_clear_op (GskVulkanRender *render,
|
||||
const cairo_rectangle_int_t *rect,
|
||||
const GdkRGBA *color);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -236,9 +236,9 @@ gsk_vulkan_clip_transform (GskVulkanClip *dest,
|
||||
}
|
||||
|
||||
gboolean
|
||||
gsk_vulkan_clip_intersects_rect (const GskVulkanClip *self,
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *rect)
|
||||
gsk_vulkan_clip_may_intersect_rect (const GskVulkanClip *self,
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *rect)
|
||||
{
|
||||
graphene_rect_t r = *rect;
|
||||
r.origin.x += offset->x;
|
||||
@@ -252,13 +252,9 @@ gsk_vulkan_clip_intersects_rect (const GskVulkanClip *self,
|
||||
return FALSE;
|
||||
|
||||
case GSK_VULKAN_CLIP_NONE:
|
||||
return r.size.width > 0 && r.size.height > 0;
|
||||
|
||||
case GSK_VULKAN_CLIP_RECT:
|
||||
return graphene_rect_intersection (&self->rect.bounds, &r, NULL);
|
||||
|
||||
case GSK_VULKAN_CLIP_ROUNDED:
|
||||
return gsk_rounded_rect_intersects_rect (&self->rect, &r);
|
||||
return graphene_rect_intersection (&self->rect.bounds, &r, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,3 +284,17 @@ gsk_vulkan_clip_contains_rect (const GskVulkanClip *self,
|
||||
return gsk_rounded_rect_contains_rect (&self->rect, &r);
|
||||
}
|
||||
}
|
||||
|
||||
GskVulkanShaderClip
|
||||
gsk_vulkan_clip_get_shader_clip (const GskVulkanClip *self,
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *rect)
|
||||
{
|
||||
if (gsk_vulkan_clip_contains_rect (self, offset, rect))
|
||||
return GSK_VULKAN_SHADER_CLIP_NONE;
|
||||
else if (self->type == GSK_VULKAN_CLIP_RECT)
|
||||
return GSK_VULKAN_SHADER_CLIP_RECT;
|
||||
else
|
||||
return GSK_VULKAN_SHADER_CLIP_ROUNDED;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,12 @@
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef enum {
|
||||
GSK_VULKAN_SHADER_CLIP_NONE,
|
||||
GSK_VULKAN_SHADER_CLIP_RECT,
|
||||
GSK_VULKAN_SHADER_CLIP_ROUNDED
|
||||
} GskVulkanShaderClip;
|
||||
|
||||
typedef enum {
|
||||
/* The whole area is clipped, no drawing is necessary.
|
||||
* This can't be handled by return values because for return
|
||||
@@ -55,9 +61,12 @@ gboolean gsk_vulkan_clip_transform (GskVulk
|
||||
gboolean gsk_vulkan_clip_contains_rect (const GskVulkanClip *self,
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *rect) G_GNUC_WARN_UNUSED_RESULT;
|
||||
gboolean gsk_vulkan_clip_intersects_rect (const GskVulkanClip *self,
|
||||
gboolean gsk_vulkan_clip_may_intersect_rect (const GskVulkanClip *self,
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *rect) G_GNUC_WARN_UNUSED_RESULT;
|
||||
GskVulkanShaderClip gsk_vulkan_clip_get_shader_clip (const GskVulkanClip *self,
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *rect);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskvulkancolormatrixopprivate.h"
|
||||
|
||||
#include "gskvulkanprivate.h"
|
||||
#include "gskvulkanshaderopprivate.h"
|
||||
|
||||
#include "vulkan/resources/color-matrix.vert.h"
|
||||
|
||||
typedef struct _GskVulkanColorMatrixOp GskVulkanColorMatrixOp;
|
||||
|
||||
struct _GskVulkanColorMatrixOp
|
||||
{
|
||||
GskVulkanShaderOp op;
|
||||
|
||||
graphene_matrix_t color_matrix;
|
||||
graphene_vec4_t color_offset;
|
||||
graphene_rect_t rect;
|
||||
graphene_rect_t tex_rect;
|
||||
|
||||
guint32 image_descriptor;
|
||||
};
|
||||
|
||||
static void
|
||||
gsk_vulkan_color_matrix_op_print (GskVulkanOp *op,
|
||||
GString *string,
|
||||
guint indent)
|
||||
{
|
||||
GskVulkanColorMatrixOp *self = (GskVulkanColorMatrixOp *) op;
|
||||
|
||||
print_indent (string, indent);
|
||||
print_rect (string, &self->rect);
|
||||
g_string_append (string, "color-matrix ");
|
||||
print_newline (string);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_color_matrix_op_collect_vertex_data (GskVulkanOp *op,
|
||||
guchar *data)
|
||||
{
|
||||
GskVulkanColorMatrixOp *self = (GskVulkanColorMatrixOp *) op;
|
||||
GskVulkanColorMatrixInstance *instance = (GskVulkanColorMatrixInstance *) (data + ((GskVulkanShaderOp *) op)->vertex_offset);
|
||||
|
||||
instance->rect[0] = self->rect.origin.x;
|
||||
instance->rect[1] = self->rect.origin.y;
|
||||
instance->rect[2] = self->rect.size.width;
|
||||
instance->rect[3] = self->rect.size.height;
|
||||
instance->tex_rect[0] = self->tex_rect.origin.x;
|
||||
instance->tex_rect[1] = self->tex_rect.origin.y;
|
||||
instance->tex_rect[2] = self->tex_rect.size.width;
|
||||
instance->tex_rect[3] = self->tex_rect.size.height;
|
||||
graphene_matrix_to_float (&self->color_matrix, instance->color_matrix);
|
||||
graphene_vec4_to_float (&self->color_offset, instance->color_offset);
|
||||
instance->tex_id = self->image_descriptor;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_color_matrix_op_reserve_descriptor_sets (GskVulkanOp *op,
|
||||
GskVulkanRender *render)
|
||||
{
|
||||
GskVulkanColorMatrixOp *self = (GskVulkanColorMatrixOp *) op;
|
||||
GskVulkanShaderOp *shader = (GskVulkanShaderOp *) op;
|
||||
|
||||
self->image_descriptor = gsk_vulkan_render_get_image_descriptor (render,
|
||||
shader->images[0],
|
||||
GSK_VULKAN_SAMPLER_DEFAULT);
|
||||
}
|
||||
|
||||
static const GskVulkanShaderOpClass GSK_VULKAN_COLOR_MATRIX_OP_CLASS = {
|
||||
{
|
||||
GSK_VULKAN_OP_SIZE (GskVulkanColorMatrixOp),
|
||||
GSK_VULKAN_STAGE_SHADER,
|
||||
gsk_vulkan_shader_op_finish,
|
||||
gsk_vulkan_color_matrix_op_print,
|
||||
gsk_vulkan_shader_op_count_vertex_data,
|
||||
gsk_vulkan_color_matrix_op_collect_vertex_data,
|
||||
gsk_vulkan_color_matrix_op_reserve_descriptor_sets,
|
||||
gsk_vulkan_shader_op_command
|
||||
},
|
||||
"color-matrix",
|
||||
1,
|
||||
&gsk_vulkan_color_matrix_info,
|
||||
};
|
||||
|
||||
void
|
||||
gsk_vulkan_color_matrix_op (GskVulkanRender *render,
|
||||
GskVulkanShaderClip clip,
|
||||
GskVulkanImage *image,
|
||||
const graphene_rect_t *rect,
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *tex_rect,
|
||||
const graphene_matrix_t *color_matrix,
|
||||
const graphene_vec4_t *color_offset)
|
||||
{
|
||||
GskVulkanColorMatrixOp *self;
|
||||
|
||||
self = (GskVulkanColorMatrixOp *) gsk_vulkan_shader_op_alloc (render, &GSK_VULKAN_COLOR_MATRIX_OP_CLASS, clip, &image);
|
||||
|
||||
graphene_rect_offset_r (rect, offset->x, offset->y, &self->rect);
|
||||
gsk_vulkan_normalize_tex_coords (&self->tex_rect, rect, tex_rect);
|
||||
self->color_matrix = *color_matrix;
|
||||
self->color_offset = *color_offset;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_vulkan_color_matrix_op_opacity (GskVulkanRender *render,
|
||||
GskVulkanShaderClip clip,
|
||||
GskVulkanImage *image,
|
||||
const graphene_rect_t *rect,
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *tex_rect,
|
||||
float opacity)
|
||||
{
|
||||
graphene_matrix_t color_matrix;
|
||||
graphene_vec4_t color_offset;
|
||||
|
||||
graphene_matrix_init_from_float (&color_matrix,
|
||||
(float[16]) {
|
||||
1.0, 0.0, 0.0, 0.0,
|
||||
0.0, 1.0, 0.0, 0.0,
|
||||
0.0, 0.0, 1.0, 0.0,
|
||||
0.0, 0.0, 0.0, opacity
|
||||
});
|
||||
graphene_vec4_init (&color_offset, 0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
gsk_vulkan_color_matrix_op (render,
|
||||
clip,
|
||||
image,
|
||||
rect,
|
||||
offset,
|
||||
tex_rect,
|
||||
&color_matrix,
|
||||
&color_offset);
|
||||
}
|
||||
|
||||
+14
-22
@@ -1,34 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <graphene.h>
|
||||
|
||||
#include "gskvulkanpipelineprivate.h"
|
||||
#include "gskvulkanopprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GskVulkanEffectPipelineLayout GskVulkanEffectPipelineLayout;
|
||||
|
||||
#define GSK_TYPE_VULKAN_EFFECT_PIPELINE (gsk_vulkan_effect_pipeline_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GskVulkanEffectPipeline, gsk_vulkan_effect_pipeline, GSK, VULKAN_EFFECT_PIPELINE, GskVulkanPipeline)
|
||||
|
||||
GskVulkanPipeline * gsk_vulkan_effect_pipeline_new (GdkVulkanContext *context,
|
||||
VkPipelineLayout layout,
|
||||
const char *shader_name,
|
||||
VkRenderPass render_pass);
|
||||
|
||||
void gsk_vulkan_effect_pipeline_collect_vertex_data (GskVulkanEffectPipeline *pipeline,
|
||||
guchar *data,
|
||||
guint32 tex_id[2],
|
||||
const graphene_point_t *offset,
|
||||
void gsk_vulkan_color_matrix_op (GskVulkanRender *render,
|
||||
GskVulkanShaderClip clip,
|
||||
GskVulkanImage *image,
|
||||
const graphene_rect_t *rect,
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *tex_rect,
|
||||
const graphene_matrix_t *color_matrix,
|
||||
const graphene_vec4_t *color_offset);
|
||||
gsize gsk_vulkan_effect_pipeline_draw (GskVulkanEffectPipeline *pipeline,
|
||||
VkCommandBuffer command_buffer,
|
||||
gsize offset,
|
||||
gsize n_commands);
|
||||
|
||||
void gsk_vulkan_color_matrix_op_opacity (GskVulkanRender *render,
|
||||
GskVulkanShaderClip clip,
|
||||
GskVulkanImage *image,
|
||||
const graphene_rect_t *rect,
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *tex_rect,
|
||||
float opacity);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskvulkancoloropprivate.h"
|
||||
|
||||
#include "gskvulkanprivate.h"
|
||||
#include "gskvulkanshaderopprivate.h"
|
||||
|
||||
#include "vulkan/resources/color.vert.h"
|
||||
|
||||
typedef struct _GskVulkanColorOp GskVulkanColorOp;
|
||||
|
||||
struct _GskVulkanColorOp
|
||||
{
|
||||
GskVulkanShaderOp op;
|
||||
|
||||
graphene_rect_t rect;
|
||||
GdkRGBA color;
|
||||
};
|
||||
|
||||
static void
|
||||
gsk_vulkan_color_op_finish (GskVulkanOp *op)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_color_op_print (GskVulkanOp *op,
|
||||
GString *string,
|
||||
guint indent)
|
||||
{
|
||||
GskVulkanColorOp *self = (GskVulkanColorOp *) op;
|
||||
|
||||
print_indent (string, indent);
|
||||
print_rect (string, &self->rect);
|
||||
g_string_append (string, "color ");
|
||||
print_rgba (string, &self->color);
|
||||
print_newline (string);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_color_op_collect_vertex_data (GskVulkanOp *op,
|
||||
guchar *data)
|
||||
{
|
||||
GskVulkanColorOp *self = (GskVulkanColorOp *) op;
|
||||
GskVulkanColorInstance *instance = (GskVulkanColorInstance *) (data + ((GskVulkanShaderOp *) op)->vertex_offset);
|
||||
|
||||
instance->rect[0] = self->rect.origin.x;
|
||||
instance->rect[1] = self->rect.origin.y;
|
||||
instance->rect[2] = self->rect.size.width;
|
||||
instance->rect[3] = self->rect.size.height;
|
||||
instance->color[0] = self->color.red;
|
||||
instance->color[1] = self->color.green;
|
||||
instance->color[2] = self->color.blue;
|
||||
instance->color[3] = self->color.alpha;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_color_op_reserve_descriptor_sets (GskVulkanOp *op,
|
||||
GskVulkanRender *render)
|
||||
{
|
||||
}
|
||||
|
||||
static const GskVulkanShaderOpClass GSK_VULKAN_COLOR_OP_CLASS = {
|
||||
{
|
||||
GSK_VULKAN_OP_SIZE (GskVulkanColorOp),
|
||||
GSK_VULKAN_STAGE_SHADER,
|
||||
gsk_vulkan_color_op_finish,
|
||||
gsk_vulkan_color_op_print,
|
||||
gsk_vulkan_shader_op_count_vertex_data,
|
||||
gsk_vulkan_color_op_collect_vertex_data,
|
||||
gsk_vulkan_color_op_reserve_descriptor_sets,
|
||||
gsk_vulkan_shader_op_command
|
||||
},
|
||||
"color",
|
||||
0,
|
||||
&gsk_vulkan_color_info,
|
||||
};
|
||||
|
||||
void
|
||||
gsk_vulkan_color_op (GskVulkanRender *render,
|
||||
GskVulkanShaderClip clip,
|
||||
const graphene_rect_t *rect,
|
||||
const graphene_point_t *offset,
|
||||
const GdkRGBA *color)
|
||||
{
|
||||
GskVulkanColorOp *self;
|
||||
|
||||
self = (GskVulkanColorOp *) gsk_vulkan_shader_op_alloc (render, &GSK_VULKAN_COLOR_OP_CLASS, clip, NULL);
|
||||
|
||||
graphene_rect_offset_r (rect, offset->x, offset->y, &self->rect);
|
||||
self->color = *color;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "gskvulkanopprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
void gsk_vulkan_color_op (GskVulkanRender *render,
|
||||
GskVulkanShaderClip clip,
|
||||
const graphene_rect_t *rect,
|
||||
const graphene_point_t *offset,
|
||||
const GdkRGBA *color);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskvulkancolorpipelineprivate.h"
|
||||
|
||||
#include "vulkan/resources/color.vert.h"
|
||||
|
||||
struct _GskVulkanColorPipeline
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GskVulkanColorPipeline, gsk_vulkan_color_pipeline, GSK_TYPE_VULKAN_PIPELINE)
|
||||
|
||||
static const VkPipelineVertexInputStateCreateInfo *
|
||||
gsk_vulkan_color_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
|
||||
{
|
||||
return &gsk_vulkan_color_info;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_color_pipeline_finalize (GObject *gobject)
|
||||
{
|
||||
//GskVulkanColorPipeline *self = GSK_VULKAN_COLOR_PIPELINE (gobject);
|
||||
|
||||
G_OBJECT_CLASS (gsk_vulkan_color_pipeline_parent_class)->finalize (gobject);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_color_pipeline_class_init (GskVulkanColorPipelineClass *klass)
|
||||
{
|
||||
GskVulkanPipelineClass *pipeline_class = GSK_VULKAN_PIPELINE_CLASS (klass);
|
||||
|
||||
G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_color_pipeline_finalize;
|
||||
|
||||
pipeline_class->get_input_state_create_info = gsk_vulkan_color_pipeline_get_input_state_create_info;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_color_pipeline_init (GskVulkanColorPipeline *self)
|
||||
{
|
||||
}
|
||||
|
||||
GskVulkanPipeline *
|
||||
gsk_vulkan_color_pipeline_new (GdkVulkanContext *context,
|
||||
VkPipelineLayout layout,
|
||||
const char *shader_name,
|
||||
VkRenderPass render_pass)
|
||||
{
|
||||
return gsk_vulkan_pipeline_new (GSK_TYPE_VULKAN_COLOR_PIPELINE, context, layout, shader_name, render_pass);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_vulkan_color_pipeline_collect_vertex_data (GskVulkanColorPipeline *pipeline,
|
||||
guchar *data,
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *rect,
|
||||
const GdkRGBA *color)
|
||||
{
|
||||
GskVulkanColorInstance *instance = (GskVulkanColorInstance *) data;
|
||||
|
||||
instance->rect[0] = rect->origin.x + offset->x;
|
||||
instance->rect[1] = rect->origin.y + offset->y;
|
||||
instance->rect[2] = rect->size.width;
|
||||
instance->rect[3] = rect->size.height;
|
||||
instance->color[0] = color->red;
|
||||
instance->color[1] = color->green;
|
||||
instance->color[2] = color->blue;
|
||||
instance->color[3] = color->alpha;
|
||||
}
|
||||
|
||||
gsize
|
||||
gsk_vulkan_color_pipeline_draw (GskVulkanColorPipeline *pipeline,
|
||||
VkCommandBuffer command_buffer,
|
||||
gsize offset,
|
||||
gsize n_commands)
|
||||
{
|
||||
vkCmdDraw (command_buffer,
|
||||
6, n_commands,
|
||||
0, offset);
|
||||
|
||||
return n_commands;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <graphene.h>
|
||||
|
||||
#include "gskvulkanpipelineprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GskVulkanColorPipelineLayout GskVulkanColorPipelineLayout;
|
||||
|
||||
#define GSK_TYPE_VULKAN_COLOR_PIPELINE (gsk_vulkan_color_pipeline_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GskVulkanColorPipeline, gsk_vulkan_color_pipeline, GSK, VULKAN_COLOR_PIPELINE, GskVulkanPipeline)
|
||||
|
||||
GskVulkanPipeline * gsk_vulkan_color_pipeline_new (GdkVulkanContext *context,
|
||||
VkPipelineLayout layout,
|
||||
const char *shader_name,
|
||||
VkRenderPass render_pass);
|
||||
|
||||
void gsk_vulkan_color_pipeline_collect_vertex_data (GskVulkanColorPipeline *pipeline,
|
||||
guchar *data,
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *rect,
|
||||
const GdkRGBA *color);
|
||||
gsize gsk_vulkan_color_pipeline_draw (GskVulkanColorPipeline *pipeline,
|
||||
VkCommandBuffer command_buffer,
|
||||
gsize offset,
|
||||
gsize n_commands);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskvulkancolortextpipelineprivate.h"
|
||||
|
||||
#include "vulkan/resources/texture.vert.h"
|
||||
|
||||
struct _GskVulkanColorTextPipeline
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GskVulkanColorTextPipeline, gsk_vulkan_color_text_pipeline, GSK_TYPE_VULKAN_PIPELINE)
|
||||
|
||||
static const VkPipelineVertexInputStateCreateInfo *
|
||||
gsk_vulkan_color_text_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
|
||||
{
|
||||
return &gsk_vulkan_texture_info;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_color_text_pipeline_finalize (GObject *gobject)
|
||||
{
|
||||
//GskVulkanColorTextPipeline *self = GSK_VULKAN_COLOR_TEXT_PIPELINE (gobject);
|
||||
|
||||
G_OBJECT_CLASS (gsk_vulkan_color_text_pipeline_parent_class)->finalize (gobject);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_color_text_pipeline_class_init (GskVulkanColorTextPipelineClass *klass)
|
||||
{
|
||||
GskVulkanPipelineClass *pipeline_class = GSK_VULKAN_PIPELINE_CLASS (klass);
|
||||
|
||||
G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_color_text_pipeline_finalize;
|
||||
|
||||
pipeline_class->get_input_state_create_info = gsk_vulkan_color_text_pipeline_get_input_state_create_info;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_color_text_pipeline_init (GskVulkanColorTextPipeline *self)
|
||||
{
|
||||
}
|
||||
|
||||
GskVulkanPipeline *
|
||||
gsk_vulkan_color_text_pipeline_new (GdkVulkanContext *context,
|
||||
VkPipelineLayout layout,
|
||||
const char *shader_name,
|
||||
VkRenderPass render_pass)
|
||||
{
|
||||
return gsk_vulkan_pipeline_new (GSK_TYPE_VULKAN_COLOR_TEXT_PIPELINE, context, layout, shader_name, render_pass);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_vulkan_color_text_pipeline_collect_vertex_data (GskVulkanColorTextPipeline *pipeline,
|
||||
guchar *data,
|
||||
GskVulkanRenderer *renderer,
|
||||
const graphene_rect_t *rect,
|
||||
guint tex_id[2],
|
||||
PangoFont *font,
|
||||
guint total_glyphs,
|
||||
const PangoGlyphInfo *glyphs,
|
||||
const graphene_point_t *offset,
|
||||
guint start_glyph,
|
||||
guint num_glyphs,
|
||||
float scale)
|
||||
{
|
||||
GskVulkanTextureInstance *instances = (GskVulkanTextureInstance *) data;
|
||||
int i;
|
||||
int count = 0;
|
||||
int x_position = 0;
|
||||
|
||||
for (i = 0; i < start_glyph; i++)
|
||||
x_position += glyphs[i].geometry.width;
|
||||
|
||||
for (; i < total_glyphs && count < num_glyphs; i++)
|
||||
{
|
||||
const PangoGlyphInfo *gi = &glyphs[i];
|
||||
|
||||
if (gi->glyph != PANGO_GLYPH_EMPTY)
|
||||
{
|
||||
double cx = (x_position + gi->geometry.x_offset) / PANGO_SCALE;
|
||||
double cy = gi->geometry.y_offset / PANGO_SCALE;
|
||||
GskVulkanTextureInstance *instance = &instances[count];
|
||||
GskVulkanCachedGlyph *glyph;
|
||||
|
||||
glyph = gsk_vulkan_renderer_get_cached_glyph (renderer,
|
||||
font,
|
||||
gi->glyph,
|
||||
x_position + gi->geometry.x_offset,
|
||||
gi->geometry.y_offset,
|
||||
scale);
|
||||
|
||||
instance->rect[0] = offset->x + cx + glyph->draw_x;
|
||||
instance->rect[1] = offset->y + cy + glyph->draw_y;
|
||||
instance->rect[2] = glyph->draw_width;
|
||||
instance->rect[3] = glyph->draw_height;
|
||||
|
||||
instance->tex_rect[0] = glyph->tx;
|
||||
instance->tex_rect[1] = glyph->ty;
|
||||
instance->tex_rect[2] = glyph->tw;
|
||||
instance->tex_rect[3] = glyph->th;
|
||||
|
||||
instance->tex_id[0] = tex_id[0];
|
||||
instance->tex_id[1] = tex_id[1];
|
||||
|
||||
count++;
|
||||
}
|
||||
x_position += gi->geometry.width;
|
||||
}
|
||||
}
|
||||
|
||||
gsize
|
||||
gsk_vulkan_color_text_pipeline_draw (GskVulkanColorTextPipeline *pipeline,
|
||||
VkCommandBuffer command_buffer,
|
||||
gsize offset,
|
||||
gsize n_commands)
|
||||
{
|
||||
vkCmdDraw (command_buffer,
|
||||
6, n_commands,
|
||||
0, offset);
|
||||
|
||||
return n_commands;
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <graphene.h>
|
||||
|
||||
#include "gskvulkanpipelineprivate.h"
|
||||
#include "gskvulkanrendererprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GskVulkanColorTextPipelineLayout GskVulkanColorTextPipelineLayout;
|
||||
|
||||
#define GSK_TYPE_VULKAN_COLOR_TEXT_PIPELINE (gsk_vulkan_color_text_pipeline_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GskVulkanColorTextPipeline, gsk_vulkan_color_text_pipeline, GSK, VULKAN_COLOR_TEXT_PIPELINE, GskVulkanPipeline)
|
||||
|
||||
GskVulkanPipeline * gsk_vulkan_color_text_pipeline_new (GdkVulkanContext *context,
|
||||
VkPipelineLayout layout,
|
||||
const char *shader_name,
|
||||
VkRenderPass render_pass);
|
||||
|
||||
void gsk_vulkan_color_text_pipeline_collect_vertex_data (GskVulkanColorTextPipeline *pipeline,
|
||||
guchar *data,
|
||||
GskVulkanRenderer *renderer,
|
||||
const graphene_rect_t *rect,
|
||||
guint tex_id[2],
|
||||
PangoFont *font,
|
||||
guint total_glyphs,
|
||||
const PangoGlyphInfo *glyphs,
|
||||
const graphene_point_t *offset,
|
||||
guint start_glyph,
|
||||
guint num_glyphs,
|
||||
float scale);
|
||||
gsize gsk_vulkan_color_text_pipeline_draw (GskVulkanColorTextPipeline *pipeline,
|
||||
VkCommandBuffer command_buffer,
|
||||
gsize offset,
|
||||
gsize n_commands);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskvulkancommandpoolprivate.h"
|
||||
#include "gskvulkanpipelineprivate.h"
|
||||
#include "gskvulkanprivate.h"
|
||||
|
||||
struct _GskVulkanCommandPool
|
||||
{
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskvulkancrossfadeopprivate.h"
|
||||
|
||||
#include "gskvulkanprivate.h"
|
||||
#include "gskvulkanshaderopprivate.h"
|
||||
|
||||
#include "vulkan/resources/cross-fade.vert.h"
|
||||
|
||||
typedef struct _GskVulkanCrossFadeOp GskVulkanCrossFadeOp;
|
||||
|
||||
struct _GskVulkanCrossFadeOp
|
||||
{
|
||||
GskVulkanShaderOp op;
|
||||
|
||||
graphene_rect_t bounds;
|
||||
float progress;
|
||||
|
||||
struct {
|
||||
graphene_rect_t rect;
|
||||
graphene_rect_t tex_rect;
|
||||
guint32 image_descriptor;
|
||||
} start, end;
|
||||
};
|
||||
|
||||
static void
|
||||
gsk_vulkan_cross_fade_op_print (GskVulkanOp *op,
|
||||
GString *string,
|
||||
guint indent)
|
||||
{
|
||||
GskVulkanCrossFadeOp *self = (GskVulkanCrossFadeOp *) op;
|
||||
|
||||
print_indent (string, indent);
|
||||
print_rect (string, &self->bounds);
|
||||
g_string_append_printf (string, "cross-fade %d%% ", (int) (self->progress * 100 + 0.5));
|
||||
print_newline (string);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_cross_fade_op_collect_vertex_data (GskVulkanOp *op,
|
||||
guchar *data)
|
||||
{
|
||||
GskVulkanCrossFadeOp *self = (GskVulkanCrossFadeOp *) op;
|
||||
GskVulkanCrossFadeInstance *instance = (GskVulkanCrossFadeInstance *) (data + ((GskVulkanShaderOp *) op)->vertex_offset);
|
||||
|
||||
gsk_vulkan_rect_to_float (&self->bounds, instance->rect);
|
||||
gsk_vulkan_rect_to_float (&self->start.rect, instance->start_rect);
|
||||
gsk_vulkan_rect_to_float (&self->end.rect, instance->end_rect);
|
||||
gsk_vulkan_rect_to_float (&self->start.tex_rect, instance->start_tex_rect);
|
||||
gsk_vulkan_rect_to_float (&self->end.tex_rect, instance->end_tex_rect);
|
||||
|
||||
instance->start_tex_id = self->start.image_descriptor;
|
||||
instance->end_tex_id = self->end.image_descriptor;
|
||||
instance->progress = self->progress;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_cross_fade_op_reserve_descriptor_sets (GskVulkanOp *op,
|
||||
GskVulkanRender *render)
|
||||
{
|
||||
GskVulkanCrossFadeOp *self = (GskVulkanCrossFadeOp *) op;
|
||||
GskVulkanShaderOp *shader = (GskVulkanShaderOp *) op;
|
||||
|
||||
self->start.image_descriptor = gsk_vulkan_render_get_image_descriptor (render,
|
||||
shader->images[0],
|
||||
GSK_VULKAN_SAMPLER_DEFAULT);
|
||||
self->end.image_descriptor = gsk_vulkan_render_get_image_descriptor (render,
|
||||
shader->images[1],
|
||||
GSK_VULKAN_SAMPLER_DEFAULT);
|
||||
}
|
||||
|
||||
static const GskVulkanShaderOpClass GSK_VULKAN_CROSS_FADE_OP_CLASS = {
|
||||
{
|
||||
GSK_VULKAN_OP_SIZE (GskVulkanCrossFadeOp),
|
||||
GSK_VULKAN_STAGE_SHADER,
|
||||
gsk_vulkan_shader_op_finish,
|
||||
gsk_vulkan_cross_fade_op_print,
|
||||
gsk_vulkan_shader_op_count_vertex_data,
|
||||
gsk_vulkan_cross_fade_op_collect_vertex_data,
|
||||
gsk_vulkan_cross_fade_op_reserve_descriptor_sets,
|
||||
gsk_vulkan_shader_op_command
|
||||
},
|
||||
"cross-fade",
|
||||
2,
|
||||
&gsk_vulkan_cross_fade_info,
|
||||
};
|
||||
|
||||
void
|
||||
gsk_vulkan_cross_fade_op (GskVulkanRender *render,
|
||||
GskVulkanShaderClip clip,
|
||||
const graphene_rect_t *bounds,
|
||||
const graphene_point_t *offset,
|
||||
float progress,
|
||||
GskVulkanImage *start_image,
|
||||
const graphene_rect_t *start_rect,
|
||||
const graphene_rect_t *start_tex_rect,
|
||||
GskVulkanImage *end_image,
|
||||
const graphene_rect_t *end_rect,
|
||||
const graphene_rect_t *end_tex_rect)
|
||||
{
|
||||
GskVulkanCrossFadeOp *self;
|
||||
|
||||
self = (GskVulkanCrossFadeOp *) gsk_vulkan_shader_op_alloc (render,
|
||||
&GSK_VULKAN_CROSS_FADE_OP_CLASS,
|
||||
clip,
|
||||
(GskVulkanImage *[2]) {
|
||||
start_image,
|
||||
end_image
|
||||
});
|
||||
|
||||
graphene_rect_offset_r (bounds, offset->x, offset->y, &self->bounds);
|
||||
self->progress = progress;
|
||||
|
||||
graphene_rect_offset_r (start_rect, offset->x, offset->y, &self->start.rect);
|
||||
gsk_vulkan_normalize_tex_coords (&self->start.tex_rect, bounds, start_tex_rect);
|
||||
|
||||
graphene_rect_offset_r (end_rect, offset->x, offset->y, &self->end.rect);
|
||||
gsk_vulkan_normalize_tex_coords (&self->end.tex_rect, bounds, end_tex_rect);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "gskvulkanopprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
void gsk_vulkan_cross_fade_op (GskVulkanRender *render,
|
||||
GskVulkanShaderClip clip,
|
||||
const graphene_rect_t *bounds,
|
||||
const graphene_point_t *offset,
|
||||
float progress,
|
||||
GskVulkanImage *start_image,
|
||||
const graphene_rect_t *start_rect,
|
||||
const graphene_rect_t *start_tex_rect,
|
||||
GskVulkanImage *end_image,
|
||||
const graphene_rect_t *end_rect,
|
||||
const graphene_rect_t *end_tex_rect);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskvulkancrossfadepipelineprivate.h"
|
||||
|
||||
#include "vulkan/resources/cross-fade.vert.h"
|
||||
|
||||
struct _GskVulkanCrossFadePipeline
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GskVulkanCrossFadePipeline, gsk_vulkan_cross_fade_pipeline, GSK_TYPE_VULKAN_PIPELINE)
|
||||
|
||||
static const VkPipelineVertexInputStateCreateInfo *
|
||||
gsk_vulkan_cross_fade_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
|
||||
{
|
||||
return &gsk_vulkan_cross_fade_info;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_cross_fade_pipeline_finalize (GObject *gobject)
|
||||
{
|
||||
//GskVulkanCrossFadePipeline *self = GSK_VULKAN_BLUR_PIPELINE (gobject);
|
||||
|
||||
G_OBJECT_CLASS (gsk_vulkan_cross_fade_pipeline_parent_class)->finalize (gobject);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_cross_fade_pipeline_class_init (GskVulkanCrossFadePipelineClass *klass)
|
||||
{
|
||||
GskVulkanPipelineClass *pipeline_class = GSK_VULKAN_PIPELINE_CLASS (klass);
|
||||
|
||||
G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_cross_fade_pipeline_finalize;
|
||||
|
||||
pipeline_class->get_input_state_create_info = gsk_vulkan_cross_fade_pipeline_get_input_state_create_info;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_cross_fade_pipeline_init (GskVulkanCrossFadePipeline *self)
|
||||
{
|
||||
}
|
||||
|
||||
GskVulkanPipeline *
|
||||
gsk_vulkan_cross_fade_pipeline_new (GdkVulkanContext *context,
|
||||
VkPipelineLayout layout,
|
||||
const char *shader_name,
|
||||
VkRenderPass render_pass)
|
||||
{
|
||||
return gsk_vulkan_pipeline_new (GSK_TYPE_VULKAN_CROSS_FADE_PIPELINE, context, layout, shader_name, render_pass);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_vulkan_cross_fade_pipeline_collect_vertex_data (GskVulkanCrossFadePipeline *pipeline,
|
||||
guchar *data,
|
||||
guint32 start_tex_id[2],
|
||||
guint32 end_tex_id[2],
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *bounds,
|
||||
const graphene_rect_t *start_bounds,
|
||||
const graphene_rect_t *end_bounds,
|
||||
const graphene_rect_t *start_tex_rect,
|
||||
const graphene_rect_t *end_tex_rect,
|
||||
double progress)
|
||||
{
|
||||
GskVulkanCrossFadeInstance *instance = (GskVulkanCrossFadeInstance *) data;
|
||||
|
||||
instance->rect[0] = bounds->origin.x + offset->x;
|
||||
instance->rect[1] = bounds->origin.y + offset->y;
|
||||
instance->rect[2] = bounds->size.width;
|
||||
instance->rect[3] = bounds->size.height;
|
||||
|
||||
instance->start_rect[0] = start_bounds->origin.x + offset->x;
|
||||
instance->start_rect[1] = start_bounds->origin.y + offset->y;
|
||||
instance->start_rect[2] = start_bounds->size.width;
|
||||
instance->start_rect[3] = start_bounds->size.height;
|
||||
|
||||
instance->end_rect[0] = end_bounds->origin.x + offset->x;
|
||||
instance->end_rect[1] = end_bounds->origin.y + offset->y;
|
||||
instance->end_rect[2] = end_bounds->size.width;
|
||||
instance->end_rect[3] = end_bounds->size.height;
|
||||
|
||||
instance->start_tex_rect[0] = start_tex_rect->origin.x;
|
||||
instance->start_tex_rect[1] = start_tex_rect->origin.y;
|
||||
instance->start_tex_rect[2] = start_tex_rect->size.width;
|
||||
instance->start_tex_rect[3] = start_tex_rect->size.height;
|
||||
|
||||
instance->end_tex_rect[0] = end_tex_rect->origin.x;
|
||||
instance->end_tex_rect[1] = end_tex_rect->origin.y;
|
||||
instance->end_tex_rect[2] = end_tex_rect->size.width;
|
||||
instance->end_tex_rect[3] = end_tex_rect->size.height;
|
||||
|
||||
instance->start_tex_id[0] = start_tex_id[0];
|
||||
instance->start_tex_id[1] = start_tex_id[1];
|
||||
instance->end_tex_id[0] = end_tex_id[0];
|
||||
instance->end_tex_id[1] = end_tex_id[1];
|
||||
instance->progress = progress;
|
||||
}
|
||||
|
||||
gsize
|
||||
gsk_vulkan_cross_fade_pipeline_draw (GskVulkanCrossFadePipeline *pipeline,
|
||||
VkCommandBuffer command_buffer,
|
||||
gsize offset,
|
||||
gsize n_commands)
|
||||
{
|
||||
vkCmdDraw (command_buffer,
|
||||
6, n_commands,
|
||||
0, offset);
|
||||
|
||||
return n_commands;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <graphene.h>
|
||||
|
||||
#include "gskvulkanpipelineprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GskVulkanCrossFadePipelineLayout GskVulkanCrossFadePipelineLayout;
|
||||
|
||||
#define GSK_TYPE_VULKAN_CROSS_FADE_PIPELINE (gsk_vulkan_cross_fade_pipeline_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GskVulkanCrossFadePipeline, gsk_vulkan_cross_fade_pipeline, GSK, VULKAN_CROSS_FADE_PIPELINE, GskVulkanPipeline)
|
||||
|
||||
GskVulkanPipeline * gsk_vulkan_cross_fade_pipeline_new (GdkVulkanContext *context,
|
||||
VkPipelineLayout layout,
|
||||
const char *shader_name,
|
||||
VkRenderPass render_pass);
|
||||
|
||||
void gsk_vulkan_cross_fade_pipeline_collect_vertex_data (GskVulkanCrossFadePipeline *pipeline,
|
||||
guchar *data,
|
||||
guint32 start_tex_id[2],
|
||||
guint32 end_tex_id[2],
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *bounds,
|
||||
const graphene_rect_t *start_bounds,
|
||||
const graphene_rect_t *end_bounds,
|
||||
const graphene_rect_t *start_tex_rect,
|
||||
const graphene_rect_t *end_tex_rect,
|
||||
double progress);
|
||||
gsize gsk_vulkan_cross_fade_pipeline_draw (GskVulkanCrossFadePipeline *pipeline,
|
||||
VkCommandBuffer command_buffer,
|
||||
gsize offset,
|
||||
gsize n_commands);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskvulkandownloadopprivate.h"
|
||||
|
||||
#include "gskvulkanprivate.h"
|
||||
|
||||
#include "gdk/gdkmemoryformatprivate.h"
|
||||
|
||||
static gsize
|
||||
gsk_vulkan_download_op_count_vertex_data (GskVulkanOp *op,
|
||||
gsize n_bytes)
|
||||
{
|
||||
return n_bytes;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_download_op_collect_vertex_data (GskVulkanOp *op,
|
||||
guchar *data)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_download_op_reserve_descriptor_sets (GskVulkanOp *op,
|
||||
GskVulkanRender *render)
|
||||
{
|
||||
}
|
||||
|
||||
static GskVulkanOp *
|
||||
gsk_vulkan_download_op_command_with_area (GskVulkanOp *op,
|
||||
GskVulkanRender *render,
|
||||
VkCommandBuffer command_buffer,
|
||||
GskVulkanImage *image,
|
||||
const cairo_rectangle_int_t *area,
|
||||
GskVulkanBuffer **buffer)
|
||||
{
|
||||
gsize stride;
|
||||
|
||||
stride = area->width * gdk_memory_format_bytes_per_pixel (gsk_vulkan_image_get_format (image));
|
||||
*buffer = gsk_vulkan_buffer_new_map (gsk_vulkan_render_get_context (render),
|
||||
area->height * stride,
|
||||
GSK_VULKAN_READ);
|
||||
|
||||
gsk_vulkan_image_transition (image,
|
||||
command_buffer,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
VK_ACCESS_TRANSFER_READ_BIT);
|
||||
|
||||
vkCmdCopyImageToBuffer (command_buffer,
|
||||
gsk_vulkan_image_get_vk_image (image),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
gsk_vulkan_buffer_get_buffer (*buffer),
|
||||
1,
|
||||
(VkBufferImageCopy[1]) {
|
||||
{
|
||||
.bufferOffset = 0,
|
||||
.bufferRowLength = area->width,
|
||||
.bufferImageHeight = area->height,
|
||||
.imageSubresource = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.mipLevel = 0,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1
|
||||
},
|
||||
.imageOffset = {
|
||||
.x = area->x,
|
||||
.y = area->y,
|
||||
.z = 0
|
||||
},
|
||||
.imageExtent = {
|
||||
.width = area->width,
|
||||
.height = area->height,
|
||||
.depth = 1
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
vkCmdPipelineBarrier (command_buffer,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_HOST_BIT,
|
||||
0,
|
||||
0, NULL,
|
||||
1, &(VkBufferMemoryBarrier) {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
|
||||
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_HOST_READ_BIT,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.buffer = gsk_vulkan_buffer_get_buffer (*buffer),
|
||||
.offset = 0,
|
||||
.size = VK_WHOLE_SIZE,
|
||||
},
|
||||
0, NULL);
|
||||
|
||||
return op->next;
|
||||
}
|
||||
|
||||
typedef struct _GskVulkanDownloadOp GskVulkanDownloadOp;
|
||||
|
||||
struct _GskVulkanDownloadOp
|
||||
{
|
||||
GskVulkanOp op;
|
||||
|
||||
GskVulkanImage *image;
|
||||
GskVulkanDownloadFunc func;
|
||||
gpointer user_data;
|
||||
|
||||
GskVulkanBuffer *buffer;
|
||||
};
|
||||
|
||||
static void
|
||||
gsk_vulkan_download_op_finish (GskVulkanOp *op)
|
||||
{
|
||||
GskVulkanDownloadOp *self = (GskVulkanDownloadOp *) op;
|
||||
guchar *data;
|
||||
gsize stride;
|
||||
|
||||
data = gsk_vulkan_buffer_map (self->buffer);
|
||||
stride = gsk_vulkan_image_get_width (self->image) *
|
||||
gdk_memory_format_bytes_per_pixel (gsk_vulkan_image_get_format (self->image));
|
||||
self->func (self->user_data,
|
||||
gsk_vulkan_image_get_format (self->image),
|
||||
data,
|
||||
gsk_vulkan_image_get_width (self->image),
|
||||
gsk_vulkan_image_get_height (self->image),
|
||||
stride);
|
||||
gsk_vulkan_buffer_unmap (self->buffer);
|
||||
|
||||
g_object_unref (self->image);
|
||||
g_clear_pointer (&self->buffer, gsk_vulkan_buffer_free);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_download_op_print (GskVulkanOp *op,
|
||||
GString *string,
|
||||
guint indent)
|
||||
{
|
||||
GskVulkanDownloadOp *self = (GskVulkanDownloadOp *) op;
|
||||
|
||||
print_indent (string, indent);
|
||||
g_string_append (string, "download ");
|
||||
print_image (string, self->image);
|
||||
print_newline (string);
|
||||
}
|
||||
|
||||
static GskVulkanOp *
|
||||
gsk_vulkan_download_op_command (GskVulkanOp *op,
|
||||
GskVulkanRender *render,
|
||||
VkRenderPass render_pass,
|
||||
VkCommandBuffer command_buffer)
|
||||
{
|
||||
GskVulkanDownloadOp *self = (GskVulkanDownloadOp *) op;
|
||||
|
||||
return gsk_vulkan_download_op_command_with_area (op,
|
||||
render,
|
||||
command_buffer,
|
||||
self->image,
|
||||
&(cairo_rectangle_int_t) {
|
||||
0, 0,
|
||||
gsk_vulkan_image_get_width (self->image),
|
||||
gsk_vulkan_image_get_height (self->image)
|
||||
},
|
||||
&self->buffer);
|
||||
}
|
||||
|
||||
static const GskVulkanOpClass GSK_VULKAN_DOWNLOAD_OP_CLASS = {
|
||||
GSK_VULKAN_OP_SIZE (GskVulkanDownloadOp),
|
||||
GSK_VULKAN_STAGE_COMMAND,
|
||||
gsk_vulkan_download_op_finish,
|
||||
gsk_vulkan_download_op_print,
|
||||
gsk_vulkan_download_op_count_vertex_data,
|
||||
gsk_vulkan_download_op_collect_vertex_data,
|
||||
gsk_vulkan_download_op_reserve_descriptor_sets,
|
||||
gsk_vulkan_download_op_command
|
||||
};
|
||||
|
||||
void
|
||||
gsk_vulkan_download_op (GskVulkanRender *render,
|
||||
GskVulkanImage *image,
|
||||
GskVulkanDownloadFunc func,
|
||||
gpointer user_data)
|
||||
{
|
||||
GskVulkanDownloadOp *self;
|
||||
|
||||
self = (GskVulkanDownloadOp *) gsk_vulkan_op_alloc (render, &GSK_VULKAN_DOWNLOAD_OP_CLASS);
|
||||
|
||||
self->image = g_object_ref (image);
|
||||
self->func = func,
|
||||
self->user_data = user_data;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_download_save_png_cb (gpointer filename,
|
||||
GdkMemoryFormat format,
|
||||
const guchar *data,
|
||||
int width,
|
||||
int height,
|
||||
gsize stride)
|
||||
{
|
||||
GdkTexture *texture;
|
||||
GBytes *bytes;
|
||||
|
||||
bytes = g_bytes_new_static (data, stride * height);
|
||||
texture = gdk_memory_texture_new (width, height,
|
||||
format,
|
||||
bytes,
|
||||
stride);
|
||||
gdk_texture_save_to_png (texture, filename);
|
||||
|
||||
g_object_unref (texture);
|
||||
g_bytes_unref (bytes);
|
||||
g_free (filename);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_vulkan_download_png_op (GskVulkanRender *render,
|
||||
GskVulkanImage *image,
|
||||
const char *filename_format,
|
||||
...)
|
||||
{
|
||||
va_list args;
|
||||
char *filename;
|
||||
|
||||
va_start (args, filename_format);
|
||||
filename = g_strdup_vprintf (filename_format, args);
|
||||
va_end (args);
|
||||
|
||||
gsk_vulkan_download_op (render,
|
||||
image,
|
||||
gsk_vulkan_download_save_png_cb,
|
||||
filename);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "gskvulkanopprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
void gsk_vulkan_download_op (GskVulkanRender *render,
|
||||
GskVulkanImage *image,
|
||||
GskVulkanDownloadFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
void gsk_vulkan_download_png_op (GskVulkanRender *render,
|
||||
GskVulkanImage *image,
|
||||
const char *filename_format,
|
||||
...) G_GNUC_PRINTF(3, 4);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskvulkaneffectpipelineprivate.h"
|
||||
|
||||
#include "vulkan/resources/color-matrix.vert.h"
|
||||
|
||||
struct _GskVulkanEffectPipeline
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GskVulkanEffectPipeline, gsk_vulkan_effect_pipeline, GSK_TYPE_VULKAN_PIPELINE)
|
||||
|
||||
static const VkPipelineVertexInputStateCreateInfo *
|
||||
gsk_vulkan_effect_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
|
||||
{
|
||||
return &gsk_vulkan_color_matrix_info;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_effect_pipeline_finalize (GObject *gobject)
|
||||
{
|
||||
//GskVulkanEffectPipeline *self = GSK_VULKAN_EFFECT_PIPELINE (gobject);
|
||||
|
||||
G_OBJECT_CLASS (gsk_vulkan_effect_pipeline_parent_class)->finalize (gobject);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_effect_pipeline_class_init (GskVulkanEffectPipelineClass *klass)
|
||||
{
|
||||
GskVulkanPipelineClass *pipeline_class = GSK_VULKAN_PIPELINE_CLASS (klass);
|
||||
|
||||
G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_effect_pipeline_finalize;
|
||||
|
||||
pipeline_class->get_input_state_create_info = gsk_vulkan_effect_pipeline_get_input_state_create_info;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_effect_pipeline_init (GskVulkanEffectPipeline *self)
|
||||
{
|
||||
}
|
||||
|
||||
GskVulkanPipeline *
|
||||
gsk_vulkan_effect_pipeline_new (GdkVulkanContext *context,
|
||||
VkPipelineLayout layout,
|
||||
const char *shader_name,
|
||||
VkRenderPass render_pass)
|
||||
{
|
||||
return gsk_vulkan_pipeline_new (GSK_TYPE_VULKAN_EFFECT_PIPELINE, context, layout, shader_name, render_pass);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_vulkan_effect_pipeline_collect_vertex_data (GskVulkanEffectPipeline *pipeline,
|
||||
guchar *data,
|
||||
guint32 tex_id[2],
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *rect,
|
||||
const graphene_rect_t *tex_rect,
|
||||
const graphene_matrix_t *color_matrix,
|
||||
const graphene_vec4_t *color_offset)
|
||||
{
|
||||
GskVulkanColorMatrixInstance *instance = (GskVulkanColorMatrixInstance *) data;
|
||||
|
||||
instance->rect[0] = rect->origin.x + offset->x;
|
||||
instance->rect[1] = rect->origin.y + offset->y;
|
||||
instance->rect[2] = rect->size.width;
|
||||
instance->rect[3] = rect->size.height;
|
||||
instance->tex_rect[0] = tex_rect->origin.x;
|
||||
instance->tex_rect[1] = tex_rect->origin.y;
|
||||
instance->tex_rect[2] = tex_rect->size.width;
|
||||
instance->tex_rect[3] = tex_rect->size.height;
|
||||
graphene_matrix_to_float (color_matrix, instance->color_matrix);
|
||||
graphene_vec4_to_float (color_offset, instance->color_offset);
|
||||
instance->tex_id[0] = tex_id[0];
|
||||
instance->tex_id[1] = tex_id[1];
|
||||
}
|
||||
|
||||
gsize
|
||||
gsk_vulkan_effect_pipeline_draw (GskVulkanEffectPipeline *pipeline,
|
||||
VkCommandBuffer command_buffer,
|
||||
gsize offset,
|
||||
gsize n_commands)
|
||||
{
|
||||
vkCmdDraw (command_buffer,
|
||||
6, n_commands,
|
||||
0, offset);
|
||||
|
||||
return n_commands;
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
#include "gskvulkanglyphcacheprivate.h"
|
||||
|
||||
#include "gskvulkanimageprivate.h"
|
||||
#include "gskvulkanuploadopprivate.h"
|
||||
|
||||
#include "gskdebugprivate.h"
|
||||
#include "gskprivate.h"
|
||||
#include "gskrendererprivate.h"
|
||||
@@ -29,7 +31,6 @@ typedef struct {
|
||||
int width, height;
|
||||
int x, y, y0;
|
||||
int num_glyphs;
|
||||
GList *dirty_glyphs;
|
||||
guint old_pixels;
|
||||
} Atlas;
|
||||
|
||||
@@ -56,7 +57,6 @@ static gboolean glyph_cache_equal (gconstpointer v1,
|
||||
gconstpointer v2);
|
||||
static void glyph_cache_key_free (gpointer v);
|
||||
static void glyph_cache_value_free (gpointer v);
|
||||
static void dirty_glyph_free (gpointer v);
|
||||
|
||||
static Atlas *
|
||||
create_atlas (GskVulkanGlyphCache *cache)
|
||||
@@ -71,7 +71,8 @@ create_atlas (GskVulkanGlyphCache *cache)
|
||||
atlas->x = 0;
|
||||
atlas->image = NULL;
|
||||
atlas->num_glyphs = 0;
|
||||
atlas->dirty_glyphs = NULL;
|
||||
|
||||
atlas->image = gsk_vulkan_image_new_for_atlas (cache->vulkan, atlas->width, atlas->height);
|
||||
|
||||
return atlas;
|
||||
}
|
||||
@@ -82,7 +83,6 @@ free_atlas (gpointer v)
|
||||
Atlas *atlas = v;
|
||||
|
||||
g_clear_object (&atlas->image);
|
||||
g_list_free_full (atlas->dirty_glyphs, dirty_glyph_free);
|
||||
g_free (atlas);
|
||||
}
|
||||
|
||||
@@ -157,30 +157,14 @@ glyph_cache_value_free (gpointer v)
|
||||
g_free (v);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GlyphCacheKey *key;
|
||||
GskVulkanCachedGlyph *value;
|
||||
cairo_surface_t *surface;
|
||||
} DirtyGlyph;
|
||||
|
||||
static void
|
||||
dirty_glyph_free (gpointer v)
|
||||
{
|
||||
DirtyGlyph *glyph = v;
|
||||
|
||||
if (glyph->surface)
|
||||
cairo_surface_destroy (glyph->surface);
|
||||
g_free (glyph);
|
||||
}
|
||||
|
||||
static void
|
||||
add_to_cache (GskVulkanGlyphCache *cache,
|
||||
GskVulkanRender *render,
|
||||
GlyphCacheKey *key,
|
||||
GskVulkanCachedGlyph *value)
|
||||
{
|
||||
Atlas *atlas;
|
||||
int i;
|
||||
DirtyGlyph *dirty;
|
||||
int width = ceil (value->draw_width * key->scale / 1024.0);
|
||||
int height = ceil (value->draw_height * key->scale / 1024.0);
|
||||
int width_with_padding = width + 2 * PADDING;
|
||||
@@ -217,6 +201,7 @@ add_to_cache (GskVulkanGlyphCache *cache,
|
||||
g_ptr_array_add (cache->atlases, atlas);
|
||||
}
|
||||
|
||||
value->atlas_image = atlas->image;
|
||||
value->atlas_x = atlas->x;
|
||||
value->atlas_y = atlas->y0;
|
||||
|
||||
@@ -225,28 +210,38 @@ add_to_cache (GskVulkanGlyphCache *cache,
|
||||
value->tw = (float)width / atlas->width;
|
||||
value->th = (float)height / atlas->height;
|
||||
|
||||
value->texture_index = i;
|
||||
|
||||
dirty = g_new (DirtyGlyph, 1);
|
||||
dirty->key = key;
|
||||
dirty->value = value;
|
||||
atlas->dirty_glyphs = g_list_prepend (atlas->dirty_glyphs, dirty);
|
||||
|
||||
atlas->x = atlas->x + width_with_padding;
|
||||
atlas->y = MAX (atlas->y, atlas->y0 + height_with_padding);
|
||||
|
||||
atlas->num_glyphs++;
|
||||
|
||||
gsk_vulkan_upload_glyph_op (render,
|
||||
atlas->image,
|
||||
&(cairo_rectangle_int_t) {
|
||||
.x = value->atlas_x,
|
||||
.y = value->atlas_y,
|
||||
.width = width_with_padding,
|
||||
.height = height_with_padding
|
||||
},
|
||||
key->font,
|
||||
&(PangoGlyphInfo) {
|
||||
.glyph = key->glyph,
|
||||
.geometry.width = value->draw_width * PANGO_SCALE,
|
||||
.geometry.x_offset = (0.25 * key->xshift - value->draw_x) * PANGO_SCALE,
|
||||
.geometry.y_offset = (0.25 * key->yshift - value->draw_y) * PANGO_SCALE
|
||||
},
|
||||
(float) key->scale / PANGO_SCALE);
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
if (GSK_RENDERER_DEBUG_CHECK (cache->renderer, GLYPH_CACHE))
|
||||
if (GSK_DEBUG_CHECK (GLYPH_CACHE))
|
||||
{
|
||||
g_print ("Glyph cache:\n");
|
||||
for (i = 0; i < cache->atlases->len; i++)
|
||||
{
|
||||
atlas = g_ptr_array_index (cache->atlases, i);
|
||||
g_print ("\tAtlas %d (%dx%d): %d glyphs (%d dirty), %.2g%% old pixels, filled to %d, %d / %d\n",
|
||||
g_print ("\tAtlas %d (%dx%d): %d glyphs, %.2g%% old pixels, filled to %d, %d / %d\n",
|
||||
i, atlas->width, atlas->height,
|
||||
atlas->num_glyphs, g_list_length (atlas->dirty_glyphs),
|
||||
atlas->num_glyphs,
|
||||
100.0 * (double)atlas->old_pixels / (double)(atlas->width * atlas->height),
|
||||
atlas->x, atlas->y0, atlas->y);
|
||||
}
|
||||
@@ -254,91 +249,12 @@ add_to_cache (GskVulkanGlyphCache *cache,
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
render_glyph (Atlas *atlas,
|
||||
DirtyGlyph *glyph,
|
||||
GskImageRegion *region)
|
||||
{
|
||||
GlyphCacheKey *key = glyph->key;
|
||||
GskVulkanCachedGlyph *value = glyph->value;
|
||||
cairo_surface_t *surface;
|
||||
cairo_t *cr;
|
||||
PangoGlyphString glyphs;
|
||||
PangoGlyphInfo gi;
|
||||
int surface_height;
|
||||
int surface_width;
|
||||
|
||||
surface_width = ceil (value->draw_width * key->scale / 1024.0) + 2 * PADDING;
|
||||
surface_height = ceil (value->draw_height * key->scale / 1024.0) + 2 * PADDING;
|
||||
|
||||
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, surface_width, surface_height);
|
||||
cairo_surface_set_device_scale (surface, key->scale / 1024.0, key->scale / 1024.0);
|
||||
|
||||
cr = cairo_create (surface);
|
||||
|
||||
/* Make sure the entire surface is initialized to black */
|
||||
cairo_set_source_rgba (cr, 0, 0, 0, 0);
|
||||
cairo_rectangle (cr, 0.0, 0.0, surface_width, surface_width);
|
||||
cairo_fill (cr);
|
||||
|
||||
/* Draw glyph */
|
||||
cairo_set_source_rgba (cr, 1, 1, 1, 1);
|
||||
|
||||
gi.glyph = key->glyph;
|
||||
gi.geometry.width = value->draw_width * 1024;
|
||||
gi.geometry.x_offset = (0.25 * key->xshift - value->draw_x) * 1024;
|
||||
gi.geometry.y_offset = (0.25 * key->yshift - value->draw_y) * 1024;
|
||||
|
||||
glyphs.num_glyphs = 1;
|
||||
glyphs.glyphs = &gi;
|
||||
|
||||
pango_cairo_show_glyph_string (cr, key->font, &glyphs);
|
||||
|
||||
cairo_destroy (cr);
|
||||
|
||||
glyph->surface = surface;
|
||||
|
||||
region->data = cairo_image_surface_get_data (surface);
|
||||
region->width = cairo_image_surface_get_width (surface);
|
||||
region->height = cairo_image_surface_get_height (surface);
|
||||
region->stride = cairo_image_surface_get_stride (surface);
|
||||
region->x = value->atlas_x;
|
||||
region->y = value->atlas_y;
|
||||
}
|
||||
|
||||
static void
|
||||
upload_dirty_glyphs (GskVulkanGlyphCache *cache,
|
||||
Atlas *atlas,
|
||||
GskVulkanUploader *uploader)
|
||||
{
|
||||
GList *l;
|
||||
guint num_regions;
|
||||
GskImageRegion *regions;
|
||||
int i;
|
||||
|
||||
num_regions = g_list_length (atlas->dirty_glyphs);
|
||||
regions = alloca (sizeof (GskImageRegion) * num_regions);
|
||||
|
||||
for (l = atlas->dirty_glyphs, i = 0; l; l = l->next, i++)
|
||||
render_glyph (atlas, (DirtyGlyph *)l->data, ®ions[i]);
|
||||
|
||||
GSK_RENDERER_DEBUG (cache->renderer, GLYPH_CACHE,
|
||||
"uploading %d glyphs to cache", num_regions);
|
||||
|
||||
gsk_vulkan_image_upload_regions (atlas->image, uploader, num_regions, regions);
|
||||
|
||||
g_list_free_full (atlas->dirty_glyphs, dirty_glyph_free);
|
||||
atlas->dirty_glyphs = NULL;
|
||||
}
|
||||
|
||||
GskVulkanGlyphCache *
|
||||
gsk_vulkan_glyph_cache_new (GskRenderer *renderer,
|
||||
GdkVulkanContext *vulkan)
|
||||
gsk_vulkan_glyph_cache_new (GdkVulkanContext *vulkan)
|
||||
{
|
||||
GskVulkanGlyphCache *cache;
|
||||
|
||||
cache = GSK_VULKAN_GLYPH_CACHE (g_object_new (GSK_TYPE_VULKAN_GLYPH_CACHE, NULL));
|
||||
cache->renderer = renderer;
|
||||
cache->vulkan = vulkan;
|
||||
g_ptr_array_add (cache->atlases, create_atlas (cache));
|
||||
|
||||
@@ -349,7 +265,7 @@ gsk_vulkan_glyph_cache_new (GskRenderer *renderer,
|
||||
|
||||
GskVulkanCachedGlyph *
|
||||
gsk_vulkan_glyph_cache_lookup (GskVulkanGlyphCache *cache,
|
||||
gboolean create,
|
||||
GskVulkanRender *render,
|
||||
PangoFont *font,
|
||||
PangoGlyph glyph,
|
||||
int x,
|
||||
@@ -383,7 +299,7 @@ gsk_vulkan_glyph_cache_lookup (GskVulkanGlyphCache *cache,
|
||||
}
|
||||
}
|
||||
|
||||
if (create && value == NULL)
|
||||
if (value == NULL)
|
||||
{
|
||||
GlyphCacheKey *key;
|
||||
PangoRectangle ink_rect;
|
||||
@@ -412,7 +328,7 @@ gsk_vulkan_glyph_cache_lookup (GskVulkanGlyphCache *cache,
|
||||
key->scale = (guint)(scale * 1024);
|
||||
|
||||
if (ink_rect.width > 0 && ink_rect.height > 0)
|
||||
add_to_cache (cache, key, value);
|
||||
add_to_cache (cache, render, key, value);
|
||||
|
||||
g_hash_table_insert (cache->hash_table, key, value);
|
||||
}
|
||||
@@ -420,26 +336,6 @@ gsk_vulkan_glyph_cache_lookup (GskVulkanGlyphCache *cache,
|
||||
return value;
|
||||
}
|
||||
|
||||
GskVulkanImage *
|
||||
gsk_vulkan_glyph_cache_get_glyph_image (GskVulkanGlyphCache *cache,
|
||||
GskVulkanUploader *uploader,
|
||||
guint index)
|
||||
{
|
||||
Atlas *atlas;
|
||||
|
||||
g_return_val_if_fail (index < cache->atlases->len, NULL);
|
||||
|
||||
atlas = g_ptr_array_index (cache->atlases, index);
|
||||
|
||||
if (atlas->image == NULL)
|
||||
atlas->image = gsk_vulkan_image_new_for_atlas (cache->vulkan, atlas->width, atlas->height);
|
||||
|
||||
if (atlas->dirty_glyphs)
|
||||
upload_dirty_glyphs (cache, atlas, uploader);
|
||||
|
||||
return atlas->image;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_vulkan_glyph_cache_begin_frame (GskVulkanGlyphCache *cache)
|
||||
{
|
||||
@@ -489,9 +385,9 @@ gsk_vulkan_glyph_cache_begin_frame (GskVulkanGlyphCache *cache)
|
||||
|
||||
if (atlas->old_pixels > MAX_OLD * atlas->width * atlas->height)
|
||||
{
|
||||
GSK_RENDERER_DEBUG (cache->renderer, GLYPH_CACHE,
|
||||
"Dropping atlas %d (%g.2%% old)",
|
||||
i, 100.0 * (double)atlas->old_pixels / (double)(atlas->width * atlas->height));
|
||||
GSK_DEBUG (GLYPH_CACHE,
|
||||
"Dropping atlas %d (%g.2%% old)",
|
||||
i, 100.0 * (double)atlas->old_pixels / (double)(atlas->width * atlas->height));
|
||||
g_ptr_array_remove_index (cache->atlases, i);
|
||||
|
||||
drops[i] = 1;
|
||||
@@ -520,5 +416,5 @@ gsk_vulkan_glyph_cache_begin_frame (GskVulkanGlyphCache *cache)
|
||||
}
|
||||
}
|
||||
|
||||
GSK_RENDERER_DEBUG (cache->renderer, GLYPH_CACHE, "Dropped %d glyphs", dropped);
|
||||
GSK_DEBUG (GLYPH_CACHE, "Dropped %d glyphs", dropped);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <pango/pango.h>
|
||||
#include "gskvulkanrendererprivate.h"
|
||||
#include "gskvulkanimageprivate.h"
|
||||
#include "gskvulkanprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -10,15 +10,31 @@ G_BEGIN_DECLS
|
||||
|
||||
G_DECLARE_FINAL_TYPE(GskVulkanGlyphCache, gsk_vulkan_glyph_cache, GSK, VULKAN_GLYPH_CACHE, GObject)
|
||||
|
||||
GskVulkanGlyphCache *gsk_vulkan_glyph_cache_new (GskRenderer *renderer,
|
||||
GdkVulkanContext *vulkan);
|
||||
typedef struct
|
||||
{
|
||||
guint texture_index;
|
||||
|
||||
GskVulkanImage * gsk_vulkan_glyph_cache_get_glyph_image (GskVulkanGlyphCache *cache,
|
||||
GskVulkanUploader *uploader,
|
||||
guint index);
|
||||
float tx;
|
||||
float ty;
|
||||
float tw;
|
||||
float th;
|
||||
|
||||
int draw_x;
|
||||
int draw_y;
|
||||
int draw_width;
|
||||
int draw_height;
|
||||
|
||||
GskVulkanImage *atlas_image;
|
||||
int atlas_x;
|
||||
int atlas_y;
|
||||
|
||||
guint64 timestamp;
|
||||
} GskVulkanCachedGlyph;
|
||||
|
||||
GskVulkanGlyphCache *gsk_vulkan_glyph_cache_new (GdkVulkanContext *vulkan);
|
||||
|
||||
GskVulkanCachedGlyph *gsk_vulkan_glyph_cache_lookup (GskVulkanGlyphCache *cache,
|
||||
gboolean create,
|
||||
GskVulkanRender *render,
|
||||
PangoFont *font,
|
||||
PangoGlyph glyph,
|
||||
int x,
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskvulkanglyphopprivate.h"
|
||||
|
||||
#include "gskvulkanprivate.h"
|
||||
#include "gskvulkanshaderopprivate.h"
|
||||
|
||||
#include "vulkan/resources/glyph.vert.h"
|
||||
|
||||
typedef struct _GskVulkanGlyphOp GskVulkanGlyphOp;
|
||||
|
||||
struct _GskVulkanGlyphOp
|
||||
{
|
||||
GskVulkanShaderOp op;
|
||||
|
||||
GskVulkanImage *image;
|
||||
graphene_rect_t rect;
|
||||
graphene_rect_t tex_rect;
|
||||
GdkRGBA color;
|
||||
|
||||
guint32 image_descriptor;
|
||||
};
|
||||
|
||||
static void
|
||||
gsk_vulkan_glyph_op_print (GskVulkanOp *op,
|
||||
GString *string,
|
||||
guint indent)
|
||||
{
|
||||
GskVulkanGlyphOp *self = (GskVulkanGlyphOp *) op;
|
||||
|
||||
print_indent (string, indent);
|
||||
print_rect (string, &self->rect);
|
||||
g_string_append (string, "glyph ");
|
||||
print_rgba (string, &self->color);
|
||||
print_newline (string);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_glyph_op_collect_vertex_data (GskVulkanOp *op,
|
||||
guchar *data)
|
||||
{
|
||||
GskVulkanGlyphOp *self = (GskVulkanGlyphOp *) op;
|
||||
GskVulkanGlyphInstance *instance = (GskVulkanGlyphInstance *) (data + ((GskVulkanShaderOp *) op)->vertex_offset);
|
||||
|
||||
gsk_vulkan_rect_to_float (&self->rect, instance->rect);
|
||||
gsk_vulkan_rect_to_float (&self->tex_rect, instance->tex_rect);
|
||||
instance->tex_id = self->image_descriptor;
|
||||
gsk_vulkan_rgba_to_float (&self->color, instance->color);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_glyph_op_reserve_descriptor_sets (GskVulkanOp *op,
|
||||
GskVulkanRender *render)
|
||||
{
|
||||
GskVulkanGlyphOp *self = (GskVulkanGlyphOp *) op;
|
||||
GskVulkanShaderOp *shader = (GskVulkanShaderOp *) op;
|
||||
|
||||
self->image_descriptor = gsk_vulkan_render_get_image_descriptor (render, shader->images[0], GSK_VULKAN_SAMPLER_DEFAULT);
|
||||
}
|
||||
|
||||
static const GskVulkanShaderOpClass GSK_VULKAN_GLYPH_OP_CLASS = {
|
||||
{
|
||||
GSK_VULKAN_OP_SIZE (GskVulkanGlyphOp),
|
||||
GSK_VULKAN_STAGE_SHADER,
|
||||
gsk_vulkan_shader_op_finish,
|
||||
gsk_vulkan_glyph_op_print,
|
||||
gsk_vulkan_shader_op_count_vertex_data,
|
||||
gsk_vulkan_glyph_op_collect_vertex_data,
|
||||
gsk_vulkan_glyph_op_reserve_descriptor_sets,
|
||||
gsk_vulkan_shader_op_command
|
||||
},
|
||||
"glyph",
|
||||
1,
|
||||
&gsk_vulkan_glyph_info,
|
||||
};
|
||||
|
||||
void
|
||||
gsk_vulkan_glyph_op (GskVulkanRender *render,
|
||||
GskVulkanShaderClip clip,
|
||||
GskVulkanImage *image,
|
||||
const graphene_rect_t *rect,
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *tex_rect,
|
||||
const GdkRGBA *color)
|
||||
{
|
||||
GskVulkanGlyphOp *self;
|
||||
|
||||
self = (GskVulkanGlyphOp *) gsk_vulkan_shader_op_alloc (render, &GSK_VULKAN_GLYPH_OP_CLASS, clip, &image);
|
||||
|
||||
graphene_rect_offset_r (rect, offset->x, offset->y, &self->rect);
|
||||
gsk_vulkan_normalize_tex_coords (&self->tex_rect, rect, tex_rect);
|
||||
self->color = *color;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "gskvulkanopprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
void gsk_vulkan_glyph_op (GskVulkanRender *render,
|
||||
GskVulkanShaderClip clip,
|
||||
GskVulkanImage *image,
|
||||
const graphene_rect_t *rect,
|
||||
const graphene_point_t *offset,
|
||||
const graphene_rect_t *tex_rect,
|
||||
const GdkRGBA *color);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
+152
-479
@@ -4,30 +4,12 @@
|
||||
|
||||
#include "gskvulkanbufferprivate.h"
|
||||
#include "gskvulkanmemoryprivate.h"
|
||||
#include "gskvulkanpipelineprivate.h"
|
||||
#include "gskvulkanprivate.h"
|
||||
|
||||
#include "gdk/gdkmemoryformatprivate.h"
|
||||
|
||||
#include "gskrendernodeprivate.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
struct _GskVulkanUploader
|
||||
{
|
||||
GdkVulkanContext *vulkan;
|
||||
|
||||
GskVulkanCommandPool *command_pool;
|
||||
|
||||
GArray *before_buffer_barriers;
|
||||
GArray *before_image_barriers;
|
||||
VkCommandBuffer copy_buffer;
|
||||
GArray *after_buffer_barriers;
|
||||
GArray *after_image_barriers;
|
||||
|
||||
GSList *staging_image_free_list;
|
||||
GSList *staging_buffer_free_list;
|
||||
};
|
||||
|
||||
struct _GskVulkanImage
|
||||
{
|
||||
GObject parent_instance;
|
||||
@@ -41,6 +23,9 @@ struct _GskVulkanImage
|
||||
VkImageUsageFlags vk_usage;
|
||||
VkImage vk_image;
|
||||
VkImageView vk_image_view;
|
||||
VkFramebuffer vk_framebuffer;
|
||||
|
||||
VkPipelineStageFlags vk_pipeline_stage;
|
||||
VkImageLayout vk_image_layout;
|
||||
VkAccessFlags vk_access;
|
||||
|
||||
@@ -49,159 +34,6 @@ struct _GskVulkanImage
|
||||
|
||||
G_DEFINE_TYPE (GskVulkanImage, gsk_vulkan_image, G_TYPE_OBJECT)
|
||||
|
||||
GskVulkanUploader *
|
||||
gsk_vulkan_uploader_new (GdkVulkanContext *context,
|
||||
GskVulkanCommandPool *command_pool)
|
||||
{
|
||||
GskVulkanUploader *self;
|
||||
|
||||
self = g_new0 (GskVulkanUploader, 1);
|
||||
|
||||
self->vulkan = g_object_ref (context);
|
||||
self->command_pool = command_pool;
|
||||
|
||||
self->before_buffer_barriers = g_array_new (FALSE, FALSE, sizeof (VkBufferMemoryBarrier));
|
||||
self->after_buffer_barriers = g_array_new (FALSE, FALSE, sizeof (VkBufferMemoryBarrier));
|
||||
|
||||
self->before_image_barriers = g_array_new (FALSE, FALSE, sizeof (VkImageMemoryBarrier));
|
||||
self->after_image_barriers = g_array_new (FALSE, FALSE, sizeof (VkImageMemoryBarrier));
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_vulkan_uploader_free (GskVulkanUploader *self)
|
||||
{
|
||||
gsk_vulkan_uploader_reset (self);
|
||||
|
||||
g_array_unref (self->after_buffer_barriers);
|
||||
g_array_unref (self->before_buffer_barriers);
|
||||
g_array_unref (self->after_image_barriers);
|
||||
g_array_unref (self->before_image_barriers);
|
||||
|
||||
g_object_unref (self->vulkan);
|
||||
|
||||
g_free (self);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_uploader_add_image_barrier (GskVulkanUploader *self,
|
||||
gboolean after,
|
||||
GskVulkanImage *image,
|
||||
VkImageLayout new_layout,
|
||||
VkAccessFlags new_access)
|
||||
{
|
||||
GArray *array;
|
||||
VkImageMemoryBarrier barrier = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.srcAccessMask = image->vk_access,
|
||||
.dstAccessMask = new_access,
|
||||
.oldLayout = image->vk_image_layout,
|
||||
.newLayout = new_layout,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = image->vk_image,
|
||||
.subresourceRange = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1
|
||||
}
|
||||
};
|
||||
|
||||
if (after)
|
||||
array = self->after_image_barriers;
|
||||
else
|
||||
array = self->before_image_barriers;
|
||||
|
||||
g_array_append_val (array, barrier);
|
||||
|
||||
image->vk_image_layout = new_layout;
|
||||
image->vk_access = new_access;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_uploader_add_buffer_barrier (GskVulkanUploader *self,
|
||||
gboolean after,
|
||||
const VkBufferMemoryBarrier *barrier)
|
||||
{
|
||||
GArray *array;
|
||||
|
||||
if (after)
|
||||
array = self->after_buffer_barriers;
|
||||
else
|
||||
array = self->before_buffer_barriers;
|
||||
|
||||
g_array_append_val (array, *barrier);
|
||||
}
|
||||
|
||||
static VkCommandBuffer
|
||||
gsk_vulkan_uploader_get_copy_buffer (GskVulkanUploader *self)
|
||||
{
|
||||
if (self->copy_buffer == VK_NULL_HANDLE)
|
||||
self->copy_buffer = gsk_vulkan_command_pool_get_buffer (self->command_pool);
|
||||
|
||||
return self->copy_buffer;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_vulkan_uploader_upload (GskVulkanUploader *self)
|
||||
{
|
||||
VkPipelineStageFlagBits host_and_transfer_bits = VK_PIPELINE_STAGE_HOST_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
|
||||
if (self->before_buffer_barriers->len > 0 || self->before_image_barriers->len > 0)
|
||||
{
|
||||
VkCommandBuffer command_buffer;
|
||||
|
||||
command_buffer = gsk_vulkan_command_pool_get_buffer (self->command_pool);
|
||||
vkCmdPipelineBarrier (command_buffer,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | host_and_transfer_bits,
|
||||
VK_PIPELINE_STAGE_HOST_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
0,
|
||||
0, NULL,
|
||||
self->before_buffer_barriers->len, (VkBufferMemoryBarrier *) self->before_buffer_barriers->data,
|
||||
self->before_image_barriers->len, (VkImageMemoryBarrier *) self->before_image_barriers->data);
|
||||
gsk_vulkan_command_pool_submit_buffer (self->command_pool, command_buffer, 0, NULL, 0, NULL, VK_NULL_HANDLE);
|
||||
g_array_set_size (self->before_buffer_barriers, 0);
|
||||
g_array_set_size (self->before_image_barriers, 0);
|
||||
}
|
||||
|
||||
/* append these to existing buffer */
|
||||
if (self->after_buffer_barriers->len > 0 || self->after_image_barriers->len > 0)
|
||||
{
|
||||
VkCommandBuffer command_buffer = gsk_vulkan_uploader_get_copy_buffer (self);
|
||||
vkCmdPipelineBarrier (command_buffer,
|
||||
host_and_transfer_bits,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||
0,
|
||||
0, NULL,
|
||||
self->after_buffer_barriers->len, (VkBufferMemoryBarrier *) self->after_buffer_barriers->data,
|
||||
self->after_image_barriers->len, (VkImageMemoryBarrier *) self->after_image_barriers->data);
|
||||
g_array_set_size (self->after_buffer_barriers, 0);
|
||||
g_array_set_size (self->after_image_barriers, 0);
|
||||
}
|
||||
|
||||
if (self->copy_buffer != VK_NULL_HANDLE)
|
||||
{
|
||||
gsk_vulkan_command_pool_submit_buffer (self->command_pool, self->copy_buffer, 0, NULL, 0, NULL, VK_NULL_HANDLE);
|
||||
self->copy_buffer = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gsk_vulkan_uploader_reset (GskVulkanUploader *self)
|
||||
{
|
||||
g_array_set_size (self->before_image_barriers, 0);
|
||||
self->copy_buffer = VK_NULL_HANDLE;
|
||||
g_array_set_size (self->after_image_barriers, 0);
|
||||
|
||||
g_slist_free_full (self->staging_image_free_list, g_object_unref);
|
||||
self->staging_image_free_list = NULL;
|
||||
g_slist_free_full (self->staging_buffer_free_list, (GDestroyNotify) gsk_vulkan_buffer_free);
|
||||
self->staging_buffer_free_list = NULL;
|
||||
}
|
||||
|
||||
typedef struct _GskMemoryFormatInfo GskMemoryFormatInfo;
|
||||
|
||||
struct _GskMemoryFormatInfo
|
||||
@@ -535,6 +367,7 @@ gsk_vulkan_image_new (GdkVulkanContext *context,
|
||||
gsize height,
|
||||
VkImageTiling tiling,
|
||||
VkImageUsageFlags usage,
|
||||
VkPipelineStageFlags stage,
|
||||
VkImageLayout layout,
|
||||
VkAccessFlags access,
|
||||
VkMemoryPropertyFlags memory)
|
||||
@@ -568,6 +401,7 @@ gsk_vulkan_image_new (GdkVulkanContext *context,
|
||||
self->width = width;
|
||||
self->height = height;
|
||||
self->vk_usage = usage;
|
||||
self->vk_pipeline_stage = stage;
|
||||
self->vk_image_layout = layout;
|
||||
self->vk_access = access;
|
||||
|
||||
@@ -609,72 +443,55 @@ gsk_vulkan_image_new (GdkVulkanContext *context,
|
||||
}
|
||||
|
||||
GskVulkanImage *
|
||||
gsk_vulkan_image_new_from_texture (GskVulkanUploader *uploader,
|
||||
GdkTexture *texture)
|
||||
{
|
||||
GdkTextureDownloader *downloader;
|
||||
GskVulkanImage *result;
|
||||
GskVulkanImageMap map;
|
||||
|
||||
downloader = gdk_texture_downloader_new (texture);
|
||||
result = gsk_vulkan_image_new_for_upload (uploader,
|
||||
gdk_texture_get_format (texture),
|
||||
gdk_texture_get_width (texture),
|
||||
gdk_texture_get_height (texture));
|
||||
gdk_texture_downloader_set_format (downloader, result->format);
|
||||
gsk_vulkan_image_map_memory (result, uploader, GSK_VULKAN_WRITE, &map);
|
||||
gdk_texture_downloader_download_into (downloader, map.data, map.stride);
|
||||
gsk_vulkan_image_unmap_memory (result, uploader, &map);
|
||||
gdk_texture_downloader_free (downloader);
|
||||
return result;
|
||||
}
|
||||
|
||||
GskVulkanImage *
|
||||
gsk_vulkan_image_new_for_upload (GskVulkanUploader *uploader,
|
||||
gsk_vulkan_image_new_for_upload (GdkVulkanContext *context,
|
||||
GdkMemoryFormat format,
|
||||
gsize width,
|
||||
gsize height)
|
||||
{
|
||||
GskVulkanImage *self;
|
||||
|
||||
self = gsk_vulkan_image_new (uploader->vulkan,
|
||||
self = gsk_vulkan_image_new (context,
|
||||
format,
|
||||
width,
|
||||
height,
|
||||
VK_IMAGE_TILING_LINEAR,
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_IMAGE_LAYOUT_PREINITIALIZED,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_image_map_memory_direct (GskVulkanImage *self,
|
||||
GskVulkanUploader *uploader,
|
||||
GskVulkanMapMode mode,
|
||||
GskVulkanImageMap *map)
|
||||
static gboolean
|
||||
gsk_vulkan_image_can_map (GskVulkanImage *self)
|
||||
{
|
||||
if (GSK_DEBUG_CHECK (STAGING))
|
||||
return FALSE;
|
||||
|
||||
if (self->vk_image_layout != VK_IMAGE_LAYOUT_PREINITIALIZED &&
|
||||
self->vk_image_layout != VK_IMAGE_LAYOUT_GENERAL)
|
||||
return FALSE;
|
||||
|
||||
return gsk_vulkan_memory_can_map (self->memory, TRUE);
|
||||
}
|
||||
|
||||
guchar *
|
||||
gsk_vulkan_image_try_map (GskVulkanImage *self,
|
||||
gsize *out_stride)
|
||||
{
|
||||
VkImageSubresource image_res;
|
||||
VkSubresourceLayout image_layout;
|
||||
guchar *result;
|
||||
|
||||
if (self->vk_image_layout != VK_IMAGE_LAYOUT_PREINITIALIZED)
|
||||
{
|
||||
gsk_vulkan_uploader_add_image_barrier (uploader,
|
||||
FALSE,
|
||||
self,
|
||||
VK_IMAGE_LAYOUT_GENERAL,
|
||||
(mode & GSK_VULKAN_READ ? VK_ACCESS_MEMORY_READ_BIT : 0) |
|
||||
(mode & GSK_VULKAN_WRITE ? VK_ACCESS_MEMORY_WRITE_BIT : 0));
|
||||
if (!gsk_vulkan_image_can_map (self))
|
||||
return NULL;
|
||||
|
||||
if (mode & GSK_VULKAN_READ)
|
||||
{
|
||||
gsk_vulkan_uploader_upload (uploader);
|
||||
GSK_VK_CHECK (vkQueueWaitIdle, gdk_vulkan_context_get_queue (self->vulkan));
|
||||
}
|
||||
}
|
||||
result = gsk_vulkan_memory_map (self->memory);
|
||||
if (result == NULL)
|
||||
return NULL;
|
||||
|
||||
image_res.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
image_res.mipLevel = 0;
|
||||
@@ -683,164 +500,15 @@ gsk_vulkan_image_map_memory_direct (GskVulkanImage *self,
|
||||
vkGetImageSubresourceLayout (gdk_vulkan_context_get_device (self->vulkan),
|
||||
self->vk_image, &image_res, &image_layout);
|
||||
|
||||
map->mode = mode;
|
||||
map->staging_buffer = NULL;
|
||||
map->data = gsk_vulkan_memory_map (self->memory) + image_layout.offset;
|
||||
map->stride = image_layout.rowPitch;
|
||||
*out_stride = image_layout.rowPitch;
|
||||
|
||||
return result + image_layout.offset;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_image_unmap_memory_direct (GskVulkanImage *self,
|
||||
GskVulkanUploader *uploader,
|
||||
GskVulkanImageMap *map)
|
||||
void
|
||||
gsk_vulkan_image_unmap (GskVulkanImage *self)
|
||||
{
|
||||
gsk_vulkan_memory_unmap (self->memory);
|
||||
|
||||
gsk_vulkan_uploader_add_image_barrier (uploader,
|
||||
TRUE,
|
||||
self,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_ACCESS_SHADER_READ_BIT);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_image_map_memory_indirect (GskVulkanImage *self,
|
||||
GskVulkanUploader *uploader,
|
||||
GskVulkanMapMode mode,
|
||||
GskVulkanImageMap *map)
|
||||
{
|
||||
map->mode = mode;
|
||||
map->stride = self->width * gdk_memory_format_bytes_per_pixel (self->format);
|
||||
map->staging_buffer = gsk_vulkan_buffer_new_map (uploader->vulkan, self->height * map->stride, mode);
|
||||
|
||||
if (self->vk_image_layout != VK_IMAGE_LAYOUT_PREINITIALIZED)
|
||||
{
|
||||
if (mode & GSK_VULKAN_READ)
|
||||
{
|
||||
gsk_vulkan_uploader_add_image_barrier (uploader,
|
||||
FALSE,
|
||||
self,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
VK_ACCESS_TRANSFER_READ_BIT);
|
||||
|
||||
vkCmdCopyImageToBuffer (gsk_vulkan_uploader_get_copy_buffer (uploader),
|
||||
self->vk_image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
gsk_vulkan_buffer_get_buffer (map->staging_buffer),
|
||||
1,
|
||||
(VkBufferImageCopy[1]) {
|
||||
{
|
||||
.bufferOffset = 0,
|
||||
.imageSubresource = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.mipLevel = 0,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1
|
||||
},
|
||||
.imageOffset = { 0, 0, 0 },
|
||||
.imageExtent = {
|
||||
.width = self->width,
|
||||
.height = self->height,
|
||||
.depth = 1
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
gsk_vulkan_uploader_upload (uploader);
|
||||
GSK_VK_CHECK (vkQueueWaitIdle, gdk_vulkan_context_get_queue (self->vulkan));
|
||||
}
|
||||
}
|
||||
|
||||
map->data = gsk_vulkan_buffer_map (map->staging_buffer);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_image_unmap_memory_indirect (GskVulkanImage *self,
|
||||
GskVulkanUploader *uploader,
|
||||
GskVulkanImageMap *map)
|
||||
{
|
||||
gsk_vulkan_buffer_unmap (map->staging_buffer);
|
||||
|
||||
if (map->mode & GSK_VULKAN_WRITE)
|
||||
{
|
||||
gsk_vulkan_uploader_add_buffer_barrier (uploader,
|
||||
FALSE,
|
||||
&(VkBufferMemoryBarrier) {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
|
||||
.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.buffer = gsk_vulkan_buffer_get_buffer (map->staging_buffer),
|
||||
.offset = 0,
|
||||
.size = VK_WHOLE_SIZE,
|
||||
});
|
||||
|
||||
gsk_vulkan_uploader_add_image_barrier (uploader,
|
||||
FALSE,
|
||||
self,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT);
|
||||
|
||||
vkCmdCopyBufferToImage (gsk_vulkan_uploader_get_copy_buffer (uploader),
|
||||
gsk_vulkan_buffer_get_buffer (map->staging_buffer),
|
||||
self->vk_image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
1,
|
||||
(VkBufferImageCopy[1]) {
|
||||
{
|
||||
.bufferOffset = 0,
|
||||
.imageSubresource = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.mipLevel = 0,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1
|
||||
},
|
||||
.imageOffset = { 0, 0, 0 },
|
||||
.imageExtent = {
|
||||
.width = self->width,
|
||||
.height = self->height,
|
||||
.depth = 1
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
uploader->staging_buffer_free_list = g_slist_prepend (uploader->staging_buffer_free_list,
|
||||
map->staging_buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
gsk_vulkan_buffer_free (map->staging_buffer);
|
||||
}
|
||||
|
||||
gsk_vulkan_uploader_add_image_barrier (uploader,
|
||||
TRUE,
|
||||
self,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_ACCESS_SHADER_READ_BIT);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_vulkan_image_map_memory (GskVulkanImage *self,
|
||||
GskVulkanUploader *uploader,
|
||||
GskVulkanMapMode mode,
|
||||
GskVulkanImageMap *map)
|
||||
{
|
||||
if (!GSK_DEBUG_CHECK (STAGING) && gsk_vulkan_memory_can_map (self->memory, TRUE))
|
||||
gsk_vulkan_image_map_memory_direct (self, uploader, mode, map);
|
||||
else
|
||||
gsk_vulkan_image_map_memory_indirect (self, uploader, mode, map);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_vulkan_image_unmap_memory (GskVulkanImage *self,
|
||||
GskVulkanUploader *uploader,
|
||||
GskVulkanImageMap *map)
|
||||
{
|
||||
if (map->staging_buffer)
|
||||
gsk_vulkan_image_unmap_memory_indirect (self, uploader, map);
|
||||
else
|
||||
gsk_vulkan_image_unmap_memory_direct (self, uploader, map);
|
||||
}
|
||||
|
||||
GskVulkanImage *
|
||||
@@ -859,6 +527,9 @@ gsk_vulkan_image_new_for_swapchain (GdkVulkanContext *context,
|
||||
self->height = height;
|
||||
self->vk_image = image;
|
||||
self->vk_format = format;
|
||||
self->vk_pipeline_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
self->vk_image_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
self->vk_access = 0;
|
||||
|
||||
gsk_vulkan_image_create_view (self,
|
||||
&(GskMemoryFormatInfo) {
|
||||
@@ -886,6 +557,7 @@ gsk_vulkan_image_new_for_atlas (GdkVulkanContext *context,
|
||||
height,
|
||||
VK_IMAGE_TILING_OPTIMAL,
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
0,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
@@ -909,6 +581,7 @@ gsk_vulkan_image_new_for_offscreen (GdkVulkanContext *context,
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
@@ -916,126 +589,26 @@ gsk_vulkan_image_new_for_offscreen (GdkVulkanContext *context,
|
||||
return self;
|
||||
}
|
||||
|
||||
GdkTexture *
|
||||
gsk_vulkan_image_download (GskVulkanImage *self,
|
||||
GskVulkanUploader *uploader)
|
||||
{
|
||||
GskVulkanImageMap map;
|
||||
GdkTexture *texture;
|
||||
GBytes *bytes;
|
||||
|
||||
gsk_vulkan_image_map_memory (self, uploader, GSK_VULKAN_READ, &map);
|
||||
bytes = g_bytes_new (map.data, map.stride * self->height);
|
||||
texture = gdk_memory_texture_new (self->width, self->height,
|
||||
self->format,
|
||||
bytes,
|
||||
map.stride);
|
||||
g_bytes_unref (bytes);
|
||||
gsk_vulkan_image_unmap_memory (self, uploader, &map);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_vulkan_image_upload_regions (GskVulkanImage *self,
|
||||
GskVulkanUploader *uploader,
|
||||
guint num_regions,
|
||||
GskImageRegion *regions)
|
||||
{
|
||||
GskVulkanBuffer *staging;
|
||||
guchar *mem;
|
||||
guchar *m;
|
||||
gsize size;
|
||||
gsize offset;
|
||||
VkBufferImageCopy *bufferImageCopy;
|
||||
|
||||
size = 0;
|
||||
for (int i = 0; i < num_regions; i++)
|
||||
size += regions[i].width * regions[i].height * 4;
|
||||
|
||||
staging = gsk_vulkan_buffer_new_map (uploader->vulkan, size, GSK_VULKAN_WRITE);
|
||||
mem = gsk_vulkan_buffer_map (staging);
|
||||
|
||||
bufferImageCopy = alloca (sizeof (VkBufferImageCopy) * num_regions);
|
||||
memset (bufferImageCopy, 0, sizeof (VkBufferImageCopy) * num_regions);
|
||||
|
||||
offset = 0;
|
||||
for (int i = 0; i < num_regions; i++)
|
||||
{
|
||||
m = mem + offset;
|
||||
if (regions[i].stride == regions[i].width * 4)
|
||||
{
|
||||
memcpy (m, regions[i].data, regions[i].stride * regions[i].height);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (gsize r = 0; r < regions[i].height; r++)
|
||||
memcpy (m + r * regions[i].width * 4, regions[i].data + r * regions[i].stride, regions[i].width * 4);
|
||||
}
|
||||
|
||||
bufferImageCopy[i].bufferOffset = offset;
|
||||
bufferImageCopy[i].bufferRowLength = regions[i].width;
|
||||
bufferImageCopy[i].bufferImageHeight = regions[i].height;
|
||||
bufferImageCopy[i].imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
bufferImageCopy[i].imageSubresource.mipLevel = 0;
|
||||
bufferImageCopy[i].imageSubresource.baseArrayLayer = 0;
|
||||
bufferImageCopy[i].imageSubresource.layerCount = 1;
|
||||
bufferImageCopy[i].imageOffset.x = regions[i].x;
|
||||
bufferImageCopy[i].imageOffset.y = regions[i].y;
|
||||
bufferImageCopy[i].imageOffset.z = 0;
|
||||
bufferImageCopy[i].imageExtent.width = regions[i].width;
|
||||
bufferImageCopy[i].imageExtent.height = regions[i].height;
|
||||
bufferImageCopy[i].imageExtent.depth = 1;
|
||||
|
||||
offset += regions[i].width * regions[i].height * 4;
|
||||
}
|
||||
|
||||
gsk_vulkan_buffer_unmap (staging);
|
||||
|
||||
gsk_vulkan_uploader_add_image_barrier (uploader,
|
||||
FALSE,
|
||||
self,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT);
|
||||
|
||||
vkCmdCopyBufferToImage (gsk_vulkan_uploader_get_copy_buffer (uploader),
|
||||
gsk_vulkan_buffer_get_buffer (staging),
|
||||
self->vk_image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
num_regions,
|
||||
bufferImageCopy);
|
||||
|
||||
gsk_vulkan_uploader_add_image_barrier (uploader,
|
||||
TRUE,
|
||||
self,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_ACCESS_SHADER_READ_BIT);
|
||||
|
||||
uploader->staging_buffer_free_list = g_slist_prepend (uploader->staging_buffer_free_list, staging);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_vulkan_image_finalize (GObject *object)
|
||||
{
|
||||
GskVulkanImage *self = GSK_VULKAN_IMAGE (object);
|
||||
VkDevice device;
|
||||
|
||||
device = gdk_vulkan_context_get_device (self->vulkan);
|
||||
|
||||
if (self->vk_framebuffer != VK_NULL_HANDLE)
|
||||
vkDestroyFramebuffer (device, self->vk_framebuffer, NULL);
|
||||
|
||||
if (self->vk_image_view != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroyImageView (gdk_vulkan_context_get_device (self->vulkan),
|
||||
self->vk_image_view,
|
||||
NULL);
|
||||
}
|
||||
vkDestroyImageView (device, self->vk_image_view, NULL);
|
||||
|
||||
/* memory is NULL for for_swapchain() images, where we don't own
|
||||
* the VkImage */
|
||||
if (self->memory)
|
||||
{
|
||||
vkDestroyImage (gdk_vulkan_context_get_device (self->vulkan),
|
||||
self->vk_image,
|
||||
NULL);
|
||||
vkDestroyImage (device, self->vk_image, NULL);
|
||||
|
||||
gsk_vulkan_memory_free (self->memory);
|
||||
}
|
||||
g_clear_pointer (&self->memory, gsk_vulkan_memory_free);
|
||||
|
||||
g_object_unref (self->vulkan);
|
||||
|
||||
@@ -1053,6 +626,31 @@ gsk_vulkan_image_init (GskVulkanImage *self)
|
||||
{
|
||||
}
|
||||
|
||||
VkFramebuffer
|
||||
gsk_vulkan_image_get_framebuffer (GskVulkanImage *self,
|
||||
VkRenderPass render_pass)
|
||||
{
|
||||
if (self->vk_framebuffer)
|
||||
return self->vk_framebuffer;
|
||||
|
||||
GSK_VK_CHECK (vkCreateFramebuffer, gdk_vulkan_context_get_device (self->vulkan),
|
||||
&(VkFramebufferCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||
.renderPass = render_pass,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = (VkImageView[1]) {
|
||||
self->vk_image_view,
|
||||
},
|
||||
.width = self->width,
|
||||
.height = self->height,
|
||||
.layers = 1
|
||||
},
|
||||
NULL,
|
||||
&self->vk_framebuffer);
|
||||
|
||||
return self->vk_framebuffer;
|
||||
}
|
||||
|
||||
gsize
|
||||
gsk_vulkan_image_get_width (GskVulkanImage *self)
|
||||
{
|
||||
@@ -1066,7 +664,7 @@ gsk_vulkan_image_get_height (GskVulkanImage *self)
|
||||
}
|
||||
|
||||
VkImage
|
||||
gsk_vulkan_image_get_image (GskVulkanImage *self)
|
||||
gsk_vulkan_image_get_vk_image (GskVulkanImage *self)
|
||||
{
|
||||
return self->vk_image;
|
||||
}
|
||||
@@ -1077,8 +675,83 @@ gsk_vulkan_image_get_image_view (GskVulkanImage *self)
|
||||
return self->vk_image_view;
|
||||
}
|
||||
|
||||
VkPipelineStageFlags
|
||||
gsk_vulkan_image_get_vk_pipeline_stage (GskVulkanImage *self)
|
||||
{
|
||||
return self->vk_pipeline_stage;
|
||||
}
|
||||
|
||||
VkImageLayout
|
||||
gsk_vulkan_image_get_vk_image_layout (GskVulkanImage *self)
|
||||
{
|
||||
return self->vk_image_layout;
|
||||
}
|
||||
|
||||
VkAccessFlags
|
||||
gsk_vulkan_image_get_vk_access (GskVulkanImage *self)
|
||||
{
|
||||
return self->vk_access;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_vulkan_image_set_vk_image_layout (GskVulkanImage *self,
|
||||
VkPipelineStageFlags stage,
|
||||
VkImageLayout image_layout,
|
||||
VkAccessFlags access)
|
||||
{
|
||||
self->vk_pipeline_stage = stage;
|
||||
self->vk_image_layout = image_layout;
|
||||
self->vk_access = access;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_vulkan_image_transition (GskVulkanImage *self,
|
||||
VkCommandBuffer command_buffer,
|
||||
VkPipelineStageFlags stage,
|
||||
VkImageLayout image_layout,
|
||||
VkAccessFlags access)
|
||||
{
|
||||
if (self->vk_pipeline_stage == stage &&
|
||||
self->vk_image_layout == image_layout &&
|
||||
self->vk_access == access)
|
||||
return;
|
||||
|
||||
vkCmdPipelineBarrier (command_buffer,
|
||||
self->vk_pipeline_stage,
|
||||
stage,
|
||||
0,
|
||||
0, NULL,
|
||||
0, NULL,
|
||||
1, &(VkImageMemoryBarrier) {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.srcAccessMask = self->vk_access,
|
||||
.dstAccessMask = access,
|
||||
.oldLayout = self->vk_image_layout,
|
||||
.newLayout = image_layout,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = self->vk_image,
|
||||
.subresourceRange = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1
|
||||
},
|
||||
});
|
||||
|
||||
gsk_vulkan_image_set_vk_image_layout (self, stage, image_layout, access);
|
||||
}
|
||||
|
||||
VkFormat
|
||||
gsk_vulkan_image_get_vk_format (GskVulkanImage *self)
|
||||
{
|
||||
return self->vk_format;
|
||||
}
|
||||
|
||||
GdkMemoryFormat
|
||||
gsk_vulkan_image_get_format (GskVulkanImage *self)
|
||||
{
|
||||
return self->format;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user