Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 24750b6e62 | |||
| 7643791064 | |||
| d8861f403c | |||
| aacd4840dd | |||
| 070e9a3085 | |||
| 6080a55453 | |||
| 752cb95a59 | |||
| d8eb91c7e9 | |||
| fbeb3fa75e | |||
| 7fb1a8bd2c | |||
| bb8730bf59 | |||
| 4782dfebe3 | |||
| cd131081cf | |||
| 1e1de9ff77 | |||
| 57df4b6fe4 | |||
| 16d0f86f6a | |||
| 4cc3ab12ff | |||
| 08b786a984 | |||
| 54bf63259c | |||
| 53b5441e39 | |||
| 3b2ab7ad1e | |||
| 42eaacbab4 | |||
| 6a1660b78e | |||
| 821fc35941 | |||
| af0492ed0c | |||
| dcdbb73702 | |||
| 60b2cf5d4a | |||
| 2fcb20cc4a | |||
| 89567767c5 | |||
| 6577f386b0 | |||
| 6ce8bd6581 |
@@ -1,7 +1,4 @@
|
||||
Overview of Changes in 4.13.2, xx-xx-xxxx
|
||||
=========================================
|
||||
|
||||
Overview of Changes in 4.13.1, 28-09-2023
|
||||
Overview of Changes in 4.13.1, xx-xx-xxxx
|
||||
=========================================
|
||||
|
||||
* GtkTooltip:
|
||||
@@ -10,32 +7,20 @@ Overview of Changes in 4.13.1, 28-09-2023
|
||||
* GtkCenterLayout, GtkEntry, GtkSearchEntry:
|
||||
- Fix some issues with baseline handling
|
||||
|
||||
* GtkColorButton, GtkFontButton:
|
||||
- Propagate focus-on-click
|
||||
|
||||
* GtkFileChooser:
|
||||
- Make "Visit file" scroll to the file
|
||||
|
||||
* GtkSwitch:
|
||||
- Respect text direction
|
||||
|
||||
* GtkWindow:
|
||||
- Don't assume titlebars are GtkHeaderBars
|
||||
|
||||
* Printing:
|
||||
- Fix some problems with the portal implementation
|
||||
- Add a new simple print API: GtkPrintDialog
|
||||
|
||||
* Paths:
|
||||
- GskPathMeasure performance has been improved
|
||||
- Add custom contours for circles, rounded rectangles and rectangles
|
||||
- Simplify GskPathPoint handling
|
||||
- gsk_path_point_get_closest_point now returns the distance as well
|
||||
- Make GskPathBuilder simplify curves
|
||||
|
||||
* Input:
|
||||
- Handle (some) single-key compose sequences
|
||||
- Fix active state tracking with sensitivity changes and grabs
|
||||
|
||||
* GSK:
|
||||
- Make the repeated gradients match between GL and cairo
|
||||
@@ -44,13 +29,6 @@ Overview of Changes in 4.13.1, 28-09-2023
|
||||
- Restrict an optimization to the cases where it is crrect
|
||||
- Fix rendering of shadows with opacity
|
||||
- The Vulkan renderer now requires Vulkan 1.2
|
||||
- GL: Transition gradients unpremultiplied
|
||||
- GL: Fix clipping of shadows
|
||||
- GL: Some optimizations
|
||||
- Broadway: Fix memory leaks in the renderer
|
||||
|
||||
* Wayland:
|
||||
- Make activation more reliable
|
||||
|
||||
* macOS:
|
||||
- Clamp damage regions to the surface size
|
||||
@@ -65,29 +43,20 @@ Overview of Changes in 4.13.1, 28-09-2023
|
||||
* Build:
|
||||
- Fix build problems with C++ compilers
|
||||
|
||||
* Deprecations:
|
||||
- gtk_window_present_with_time
|
||||
|
||||
* Translation updates
|
||||
Brazilian Portuguese
|
||||
British English
|
||||
Catalan
|
||||
Chinese (China)
|
||||
Czech
|
||||
Danish
|
||||
Dutch
|
||||
Esperanto
|
||||
Galician
|
||||
Georgian
|
||||
Italian
|
||||
Korean
|
||||
Latvian
|
||||
Lithuanian
|
||||
Persian
|
||||
Polish
|
||||
Punjabi
|
||||
Slovenian
|
||||
Turkish
|
||||
|
||||
|
||||
Overview of Changes in 4.13.0, 25-08-2023
|
||||
|
||||
@@ -295,6 +295,7 @@
|
||||
<file>gears.c</file>
|
||||
<file>gestures.c</file>
|
||||
<file>glarea.c</file>
|
||||
<file>glyphs.c</file>
|
||||
<file>gltransition.c</file>
|
||||
<file>headerbar.c</file>
|
||||
<file>hypertext.c</file>
|
||||
@@ -339,6 +340,7 @@
|
||||
<file>path_fill.c</file>
|
||||
<file>path_maze.c</file>
|
||||
<file>path_spinner.c</file>
|
||||
<file>path_sweep.c</file>
|
||||
<file>path_walk.c</file>
|
||||
<file>path_text.c</file>
|
||||
<file>peg_solitaire.c</file>
|
||||
@@ -426,6 +428,10 @@
|
||||
<gresource prefix="/fontrendering">
|
||||
<file>fontrendering.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/path_sweep">
|
||||
<file>path_sweep.ui</file>
|
||||
<file compressed="true">path_world.txt</file>
|
||||
</gresource>
|
||||
<gresource prefix="/path_walk">
|
||||
<file>path_walk.ui</file>
|
||||
<file compressed="true">path_world.txt</file>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -34,6 +34,7 @@ demos = files([
|
||||
'gestures.c',
|
||||
'glarea.c',
|
||||
'gltransition.c',
|
||||
'glyphs.c',
|
||||
'headerbar.c',
|
||||
'hypertext.c',
|
||||
'iconscroll.c',
|
||||
@@ -75,6 +76,7 @@ demos = files([
|
||||
'path_fill.c',
|
||||
'path_maze.c',
|
||||
'path_spinner.c',
|
||||
'path_sweep.c',
|
||||
'path_walk.c',
|
||||
'path_text.c',
|
||||
'peg_solitaire.c',
|
||||
|
||||
@@ -2,13 +2,10 @@
|
||||
*
|
||||
* This demo shows how to use GskPath to draw shapes that are (a bit)
|
||||
* more complex than a rounded rectangle.
|
||||
*
|
||||
* It also demonstrates printing to a stream with GtkPrintDialog.
|
||||
*/
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <cairo-pdf.h>
|
||||
|
||||
#include "paintable.h"
|
||||
|
||||
@@ -168,89 +165,6 @@ gtk_logo_paintable_new (void)
|
||||
return GDK_PAINTABLE (self);
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
write_cairo (void *closure,
|
||||
const unsigned char *data,
|
||||
unsigned int length)
|
||||
{
|
||||
GOutputStream *stream = closure;
|
||||
gsize written;
|
||||
GError *error = NULL;
|
||||
|
||||
if (!g_output_stream_write_all (stream, data, length, &written, NULL, &error))
|
||||
{
|
||||
g_print ("Error writing pdf stream: %s\n", error->message);
|
||||
g_error_free (error);
|
||||
return CAIRO_STATUS_WRITE_ERROR;
|
||||
}
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static void
|
||||
print_ready (GObject *source,
|
||||
GAsyncResult *result,
|
||||
gpointer data)
|
||||
{
|
||||
GtkPrintDialog *dialog = GTK_PRINT_DIALOG (source);
|
||||
GError *error = NULL;
|
||||
GOutputStream *stream;
|
||||
GtkSnapshot *snapshot;
|
||||
GdkPaintable *paintable;
|
||||
GskRenderNode *node;
|
||||
cairo_surface_t *surface;
|
||||
cairo_t *cr;
|
||||
|
||||
stream = gtk_print_dialog_print_finish (dialog, result, &error);
|
||||
if (stream == NULL)
|
||||
{
|
||||
g_print ("Failed to get output stream: %s\n", error->message);
|
||||
g_error_free (error);
|
||||
return;
|
||||
}
|
||||
|
||||
snapshot = gtk_snapshot_new ();
|
||||
paintable = gtk_picture_get_paintable (GTK_PICTURE (data));
|
||||
gdk_paintable_snapshot (paintable, snapshot, 100, 100);
|
||||
node = gtk_snapshot_free_to_node (snapshot);
|
||||
|
||||
surface = cairo_pdf_surface_create_for_stream (write_cairo, stream, 100, 100);
|
||||
cr = cairo_create (surface);
|
||||
|
||||
gsk_render_node_draw (node, cr);
|
||||
|
||||
cairo_destroy (cr);
|
||||
cairo_surface_destroy (surface);
|
||||
gsk_render_node_unref (node);
|
||||
|
||||
if (!g_output_stream_close (stream, NULL, &error))
|
||||
{
|
||||
g_print ("Error from close: %s\n", error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
g_object_unref (stream);
|
||||
}
|
||||
|
||||
static void
|
||||
print (GtkButton *button,
|
||||
gpointer data)
|
||||
{
|
||||
GtkWidget *picture = data;
|
||||
GtkPrintDialog *dialog;
|
||||
|
||||
dialog = gtk_print_dialog_new ();
|
||||
|
||||
gtk_print_dialog_print (dialog,
|
||||
GTK_WINDOW (gtk_widget_get_root (picture)),
|
||||
NULL,
|
||||
NULL,
|
||||
print_ready,
|
||||
picture);
|
||||
|
||||
g_object_unref (dialog);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_path_fill (GtkWidget *do_widget)
|
||||
{
|
||||
@@ -258,21 +172,12 @@ do_path_fill (GtkWidget *do_widget)
|
||||
|
||||
if (!window)
|
||||
{
|
||||
GtkWidget *header, *button, *label;
|
||||
GtkWidget *picture;
|
||||
GdkPaintable *paintable;
|
||||
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 100, 100);
|
||||
gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Fill and Stroke");
|
||||
header = gtk_header_bar_new ();
|
||||
button = gtk_button_new_from_icon_name ("printer-symbolic");
|
||||
gtk_header_bar_pack_start (GTK_HEADER_BAR (header), button);
|
||||
label = gtk_label_new ("Fill and Stroke");
|
||||
gtk_widget_add_css_class (label, "title");
|
||||
gtk_header_bar_set_title_widget (GTK_HEADER_BAR (header), label);
|
||||
gtk_window_set_titlebar (GTK_WINDOW (window), header);
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
|
||||
|
||||
paintable = gtk_logo_paintable_new ();
|
||||
@@ -281,8 +186,6 @@ do_path_fill (GtkWidget *do_widget)
|
||||
gtk_picture_set_can_shrink (GTK_PICTURE (picture), FALSE);
|
||||
g_object_unref (paintable);
|
||||
|
||||
g_signal_connect (button, "clicked", G_CALLBACK (print), picture);
|
||||
|
||||
gtk_window_set_child (GTK_WINDOW (window), picture);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,319 @@
|
||||
/* Path/Sweep
|
||||
*
|
||||
* This demo shows how path intersections can be used.
|
||||
*
|
||||
* The world map that is used here is a path with 211 lines and 1569 cubic
|
||||
* Bėzier segments in 121 contours.
|
||||
*/
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define GTK_TYPE_PATH_SWEEP (gtk_path_sweep_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (GtkPathSweep, gtk_path_sweep, GTK, PATH_SWEEP, GtkWidget)
|
||||
|
||||
#define POINT_SIZE 8
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_PATH,
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
struct _GtkPathSweep
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
|
||||
GskPath *path;
|
||||
graphene_rect_t bounds;
|
||||
float y_pos;
|
||||
gboolean in;
|
||||
};
|
||||
|
||||
struct _GtkPathSweepClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
};
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
G_DEFINE_TYPE (GtkPathSweep, gtk_path_sweep, GTK_TYPE_WIDGET)
|
||||
|
||||
static gboolean
|
||||
intersection_cb (GskPath *path1,
|
||||
const GskPathPoint *point1,
|
||||
GskPath *path2,
|
||||
const GskPathPoint *point2,
|
||||
GskPathIntersection kind,
|
||||
gpointer data)
|
||||
{
|
||||
GskPathBuilder *builder = data;
|
||||
graphene_point_t p;
|
||||
|
||||
gsk_path_point_get_position (point1, path1, &p);
|
||||
gsk_path_builder_add_circle (builder, &p, 4);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GskPath *
|
||||
get_intersection_path (GskPath *path1,
|
||||
GskPath *path2)
|
||||
{
|
||||
GskPathBuilder *builder = gsk_path_builder_new ();
|
||||
|
||||
gsk_path_foreach_intersection (path1, path2, intersection_cb, builder);
|
||||
|
||||
return gsk_path_builder_to_path (builder);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_sweep_snapshot (GtkWidget *widget,
|
||||
GtkSnapshot *snapshot)
|
||||
{
|
||||
GtkPathSweep *self = GTK_PATH_SWEEP (widget);
|
||||
GskStroke *stroke;
|
||||
|
||||
if (self->path == NULL)
|
||||
return;
|
||||
|
||||
gtk_snapshot_save (snapshot);
|
||||
|
||||
stroke = gsk_stroke_new (2.0);
|
||||
|
||||
gtk_snapshot_append_stroke (snapshot, self->path, stroke, &(GdkRGBA) { 0, 0, 0, 1 });
|
||||
|
||||
if (self->in)
|
||||
{
|
||||
graphene_rect_t bounds;
|
||||
GskPathBuilder *builder;
|
||||
GskPath *line, *isecs;
|
||||
|
||||
gsk_path_get_stroke_bounds (self->path, stroke, &bounds);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_move_to (builder, bounds.origin.x, bounds.origin.y + self->y_pos);
|
||||
gsk_path_builder_line_to (builder, bounds.origin.x + bounds.size.width, bounds.origin.y + self->y_pos);
|
||||
line = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
gtk_snapshot_append_stroke (snapshot, line, stroke, &(GdkRGBA) { 0, 0, 0, 1 });
|
||||
|
||||
isecs = get_intersection_path (self->path, line);
|
||||
|
||||
gtk_snapshot_append_fill (snapshot, isecs, GSK_FILL_RULE_WINDING, &(GdkRGBA) { 1, 0, 0, 1 });
|
||||
gtk_snapshot_append_stroke (snapshot, isecs, stroke, &(GdkRGBA) { 0, 0, 0, 1 });
|
||||
|
||||
gsk_path_unref (isecs);
|
||||
gsk_path_unref (line);
|
||||
}
|
||||
|
||||
gsk_stroke_free (stroke);
|
||||
|
||||
gtk_snapshot_restore (snapshot);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_sweep_measure (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkPathSweep *self = GTK_PATH_SWEEP (widget);
|
||||
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
*minimum = *natural = (int) ceilf (self->bounds.size.width);
|
||||
else
|
||||
*minimum = *natural = (int) ceilf (self->bounds.size.height);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_sweep_set_path (GtkPathSweep *self,
|
||||
GskPath *path)
|
||||
{
|
||||
if (self->path == path)
|
||||
return;
|
||||
|
||||
g_clear_pointer (&self->path, gsk_path_unref);
|
||||
graphene_rect_init (&self->bounds, 0, 0, 0, 0);
|
||||
if (path)
|
||||
{
|
||||
GskStroke *stroke;
|
||||
|
||||
self->path = gsk_path_ref (path);
|
||||
stroke = gsk_stroke_new (2.0);
|
||||
gsk_path_get_stroke_bounds (path, stroke, &self->bounds);
|
||||
gsk_stroke_free (stroke);
|
||||
}
|
||||
|
||||
gtk_widget_queue_resize (GTK_WIDGET (self));
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PATH]);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_sweep_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
|
||||
{
|
||||
GtkPathSweep *self = GTK_PATH_SWEEP (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_PATH:
|
||||
gtk_path_sweep_set_path (self, g_value_get_boxed (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_sweep_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkPathSweep *self = GTK_PATH_SWEEP (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_PATH:
|
||||
g_value_set_boxed (value, self->path);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_sweep_dispose (GObject *object)
|
||||
{
|
||||
GtkPathSweep *self = GTK_PATH_SWEEP (object);
|
||||
|
||||
g_clear_pointer (&self->path, gsk_path_unref);
|
||||
|
||||
G_OBJECT_CLASS (gtk_path_sweep_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_sweep_class_init (GtkPathSweepClass *klass)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->dispose = gtk_path_sweep_dispose;
|
||||
object_class->set_property = gtk_path_sweep_set_property;
|
||||
object_class->get_property = gtk_path_sweep_get_property;
|
||||
|
||||
widget_class->snapshot = gtk_path_sweep_snapshot;
|
||||
widget_class->measure = gtk_path_sweep_measure;
|
||||
|
||||
properties[PROP_PATH] =
|
||||
g_param_spec_boxed ("path", NULL, NULL,
|
||||
GSK_TYPE_PATH,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (object_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
motion_cb (GtkEventControllerMotion *controller,
|
||||
double x,
|
||||
double y,
|
||||
gpointer data)
|
||||
{
|
||||
GtkPathSweep *self = data;
|
||||
|
||||
self->y_pos = y;
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
enter_cb (GtkEventControllerMotion *controller,
|
||||
double x,
|
||||
double y,
|
||||
gpointer data)
|
||||
{
|
||||
GtkPathSweep *self = data;
|
||||
|
||||
self->in = TRUE;
|
||||
self->y_pos = y;
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
leave_cb (GtkEventControllerMotion *controller,
|
||||
gpointer data)
|
||||
{
|
||||
GtkPathSweep *self = data;
|
||||
|
||||
self->in = FALSE;
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_path_sweep_init (GtkPathSweep *self)
|
||||
{
|
||||
GtkEventController *controller;
|
||||
|
||||
/* Data taken from
|
||||
* https://commons.wikimedia.org/wiki/Maps_of_the_world#/media/File:Simplified_blank_world_map_without_Antartica_(no_borders).svg
|
||||
*/
|
||||
GBytes *data = g_resources_lookup_data ("/path_sweep/path_world.txt", 0, NULL);
|
||||
GskPath *path = gsk_path_parse (g_bytes_get_data (data, NULL));
|
||||
g_bytes_unref (data);
|
||||
gtk_path_sweep_set_path (self, path);
|
||||
gsk_path_unref (path);
|
||||
|
||||
controller = gtk_event_controller_motion_new ();
|
||||
g_signal_connect (controller, "motion", G_CALLBACK (motion_cb), self);
|
||||
g_signal_connect (controller, "enter", G_CALLBACK (enter_cb), self);
|
||||
g_signal_connect (controller, "leave", G_CALLBACK (leave_cb), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), controller);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtk_path_sweep_new (void)
|
||||
{
|
||||
GtkPathSweep *self;
|
||||
|
||||
self = g_object_new (GTK_TYPE_PATH_SWEEP, NULL);
|
||||
|
||||
return GTK_WIDGET (self);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_path_sweep (GtkWidget *do_widget)
|
||||
{
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
if (!window)
|
||||
{
|
||||
GtkBuilder *builder;
|
||||
|
||||
g_type_ensure (GTK_TYPE_PATH_SWEEP);
|
||||
|
||||
builder = gtk_builder_new_from_resource ("/path_sweep/path_sweep.ui");
|
||||
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *) &window);
|
||||
g_object_unref (builder);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_window_present (GTK_WINDOW (window));
|
||||
else
|
||||
gtk_window_destroy (GTK_WINDOW (window));
|
||||
|
||||
return window;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<object class="GtkWindow" id="window">
|
||||
<property name="title" translatable="yes">World Map</property>
|
||||
<property name="child">
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkPathSweep" id="view">
|
||||
<property name="hexpand">true</property>
|
||||
<property name="vexpand">true</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</interface>
|
||||
@@ -1,9 +1,8 @@
|
||||
/* Pickers and Launchers
|
||||
* #Keywords: GtkColorDialog, GtkFontDialog, GtkFileDialog, GtkPrintDialog, GtkFileLauncher, GtkUriLauncher
|
||||
* #Keywords: GtkColorDialog, GtkFontDialog, GtkFileDialog, GtkFileLauncher, GtkUriLauncher
|
||||
*
|
||||
* The dialogs are mainly intended for use in preference dialogs.
|
||||
* They allow to select colors, fonts and files. There is also a
|
||||
* print dialog.
|
||||
* They allow to select colors, fonts and applications.
|
||||
*
|
||||
* The launchers let you open files or URIs in applications that
|
||||
* can handle them.
|
||||
@@ -12,13 +11,11 @@
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
static GtkWidget *app_picker;
|
||||
static GtkWidget *print_button;
|
||||
|
||||
static void
|
||||
set_file (GFile *file,
|
||||
gpointer data)
|
||||
{
|
||||
GFileInfo *info;
|
||||
char *name;
|
||||
|
||||
if (!file)
|
||||
@@ -34,13 +31,6 @@ set_file (GFile *file,
|
||||
|
||||
gtk_widget_set_sensitive (app_picker, TRUE);
|
||||
g_object_set_data_full (G_OBJECT (app_picker), "file", g_object_ref (file), g_object_unref);
|
||||
|
||||
info = g_file_query_info (file, "standard::content-type", 0, NULL, NULL);
|
||||
if (strcmp (g_file_info_get_content_type (info), "application/pdf") == 0)
|
||||
{
|
||||
gtk_widget_set_sensitive (print_button, TRUE);
|
||||
g_object_set_data_full (G_OBJECT (print_button), "file", g_object_ref (file), g_object_unref);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -57,10 +47,6 @@ file_opened (GObject *source,
|
||||
{
|
||||
g_print ("%s\n", error->message);
|
||||
g_error_free (error);
|
||||
gtk_widget_set_sensitive (app_picker, FALSE);
|
||||
g_object_set_data (G_OBJECT (app_picker), "file", NULL);
|
||||
gtk_widget_set_sensitive (print_button, FALSE);
|
||||
g_object_set_data (G_OBJECT (print_button), "file", NULL);
|
||||
}
|
||||
|
||||
set_file (file, data);
|
||||
@@ -128,53 +114,6 @@ open_app (GtkButton *picker)
|
||||
g_object_unref (launcher);
|
||||
}
|
||||
|
||||
static void
|
||||
print_file_done (GObject *source,
|
||||
GAsyncResult *result,
|
||||
gpointer data)
|
||||
{
|
||||
GtkPrintDialog *dialog = GTK_PRINT_DIALOG (source);
|
||||
GError *error = NULL;
|
||||
GCancellable *cancellable;
|
||||
unsigned int id;
|
||||
|
||||
cancellable = g_task_get_cancellable (G_TASK (result));
|
||||
id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (cancellable), "timeout"));
|
||||
if (id)
|
||||
g_source_remove (id);
|
||||
|
||||
if (!gtk_print_dialog_print_file_finish (dialog, result, &error))
|
||||
{
|
||||
g_print ("%s\n", error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_file (GtkButton *picker)
|
||||
{
|
||||
GtkWindow *parent = GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (picker)));
|
||||
GtkPrintDialog *dialog;
|
||||
GCancellable *cancellable;
|
||||
GFile *file;
|
||||
unsigned int id;
|
||||
|
||||
file = G_FILE (g_object_get_data (G_OBJECT (picker), "file"));
|
||||
dialog = gtk_print_dialog_new ();
|
||||
|
||||
cancellable = g_cancellable_new ();
|
||||
|
||||
id = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT,
|
||||
20,
|
||||
abort_mission, g_object_ref (cancellable), g_object_unref);
|
||||
g_object_set_data (G_OBJECT (cancellable), "timeout", GUINT_TO_POINTER (id));
|
||||
|
||||
gtk_print_dialog_print_file (dialog, parent, NULL, file, cancellable, print_file_done, NULL);
|
||||
|
||||
g_object_unref (cancellable);
|
||||
g_object_unref (dialog);
|
||||
}
|
||||
|
||||
static void
|
||||
open_uri_done (GObject *source,
|
||||
GAsyncResult *result,
|
||||
@@ -295,15 +234,9 @@ do_pickers (GtkWidget *do_widget)
|
||||
gtk_widget_set_sensitive (app_picker, FALSE);
|
||||
g_signal_connect (app_picker, "clicked", G_CALLBACK (open_app), NULL);
|
||||
gtk_box_append (GTK_BOX (picker), app_picker);
|
||||
|
||||
print_button = gtk_button_new_from_icon_name ("printer-symbolic");
|
||||
gtk_widget_set_tooltip_text (print_button, "Print file");
|
||||
gtk_widget_set_sensitive (print_button, FALSE);
|
||||
g_signal_connect (print_button, "clicked", G_CALLBACK (print_file), NULL);
|
||||
gtk_box_append (GTK_BOX (picker), print_button);
|
||||
|
||||
gtk_grid_attach (GTK_GRID (table), picker, 1, 2, 1, 1);
|
||||
|
||||
|
||||
label = gtk_label_new ("URI:");
|
||||
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
||||
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
|
||||
|
||||
@@ -12,6 +12,11 @@ SYNOPSIS
|
||||
--------
|
||||
| **gtk4-path-tool** <COMMAND> [OPTIONS...] <PATH>
|
||||
|
|
||||
| **gtk4-path-tool** simplify [OPTIONS...] <PATH>
|
||||
| **gtk4-path-tool** intersection [OPTIONS...] <PATH> <PATH>
|
||||
| **gtk4-path-tool** union [OPTIONS...] <PATH> <PATH>
|
||||
| **gtk4-path-tool** difference [OPTIONS...] <PATH> <PATH>
|
||||
| **gtk4-path-tool** symmetric-difference [OPTIONS...] <PATH> <PATH>
|
||||
| **gtk4-path-tool** decompose [OPTIONS...] <PATH>
|
||||
| **gtk4-path-tool** show [OPTIONS...] <PATH>
|
||||
| **gtk4-path-tool** render [OPTIONS...] <PATH>
|
||||
@@ -195,6 +200,7 @@ The interior of the path is filled.
|
||||
|
||||
The limit at which to clip miters at line joins. The default value is 4.
|
||||
|
||||
<<<<<<< HEAD
|
||||
``--dashes=VALUE``
|
||||
|
||||
The dash pattern to use for this stroke. A dash pattern is specified by
|
||||
@@ -208,6 +214,21 @@ The interior of the path is filled.
|
||||
The offset into the dash pattern where dashing should begin.
|
||||
The default value is 0.
|
||||
|
||||
Boolean Operations
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The ``intersection``, ``union``, ``difference`` and ``symmetric-difference`` commands
|
||||
perform boolean operations on paths. Given two paths, they create a new path which
|
||||
encircles the area that is the intersection, union, difference or symmetric difference
|
||||
of the areas encircled by the paths.
|
||||
|
||||
Simplification
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
The ``simplify`` command removes areas of overlap from a path such that the resulting
|
||||
path encircles the same area, but every edge in the resulting path is a boundary between
|
||||
the inside and the outside.
|
||||
|
||||
Reversing
|
||||
^^^^^^^^^
|
||||
|
||||
|
||||
@@ -263,7 +263,7 @@ gdk_parse_debug_var (const char *variable,
|
||||
if (debug_enabled || keys[i].always_enabled)
|
||||
fprintf (stderr, " %s%*s%s\n", keys[i].key, (int)(max_width - strlen (keys[i].key)), " ", keys[i].help);
|
||||
}
|
||||
fprintf (stderr, " %s%*s%s\n", "all", max_width - 3, " ", "Enable all values. Other given values are subtracted");
|
||||
fprintf (stderr, " %s%*s%s\n", "all", max_width - 3, " ", "Enable all values");
|
||||
fprintf (stderr, " %s%*s%s\n", "help", max_width - 4, " ", "Print this help");
|
||||
fprintf (stderr, "\nMultiple values can be given, separated by : or space.\n");
|
||||
}
|
||||
|
||||
+1
-3
@@ -183,7 +183,7 @@ gdk_array(reserve) (GdkArray *self,
|
||||
return;
|
||||
|
||||
size = gdk_array(get_size) (self);
|
||||
new_size = ((gsize) 1) << g_bit_storage (MAX (GDK_ARRAY_REAL_SIZE (n), 16) - 1);
|
||||
new_size = 1 << g_bit_storage (MAX (GDK_ARRAY_REAL_SIZE (n), 16) - 1);
|
||||
|
||||
#ifdef GDK_ARRAY_PREALLOC
|
||||
if (self->start == self->preallocated)
|
||||
@@ -318,5 +318,3 @@ gdk_array(get) (const GdkArray *self,
|
||||
#undef GDK_ARRAY_TYPE_NAME
|
||||
#undef GDK_ARRAY_NO_MEMSET
|
||||
#endif
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -1715,56 +1715,6 @@ gdk_gl_context_get_version (GdkGLContext *context,
|
||||
*minor = gdk_gl_version_get_minor (&priv->gl_version);
|
||||
}
|
||||
|
||||
const char *
|
||||
gdk_gl_context_get_glsl_version_string (GdkGLContext *self)
|
||||
{
|
||||
GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (self);
|
||||
|
||||
if (priv->api == GDK_GL_API_GL)
|
||||
{
|
||||
if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (4, 6)))
|
||||
return "#version 460";
|
||||
else if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (4, 5)))
|
||||
return "#version 450";
|
||||
else if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (4, 4)))
|
||||
return "#version 440";
|
||||
else if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (4, 3)))
|
||||
return "#version 430";
|
||||
else if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (4, 2)))
|
||||
return "#version 420";
|
||||
else if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (4, 1)))
|
||||
return "#version 410";
|
||||
else if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (4, 0)))
|
||||
return "#version 400";
|
||||
else if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (3, 3)))
|
||||
return "#version 330";
|
||||
else if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (3, 2)))
|
||||
return "#version 150";
|
||||
else if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (3, 1)))
|
||||
return "#version 140";
|
||||
else
|
||||
return "#version 130";
|
||||
}
|
||||
else if (priv->api == GDK_GL_API_GLES)
|
||||
{
|
||||
if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (3, 2)))
|
||||
return "#version 320 es";
|
||||
else if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (3, 1)))
|
||||
return "#version 310 es";
|
||||
else if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (3, 0)))
|
||||
return "#version 300 es";
|
||||
else if (gdk_gl_version_greater_equal (&priv->gl_version, &GDK_GL_VERSION_INIT (3, 0)))
|
||||
return "#version 300 es";
|
||||
else
|
||||
return "#version 100";
|
||||
}
|
||||
else
|
||||
{
|
||||
/* must be realized to be called */
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_gl_context_clear_current:
|
||||
*
|
||||
|
||||
@@ -147,8 +147,6 @@ void gdk_gl_context_label_object_printf (GdkGLContext
|
||||
const char *format,
|
||||
...) G_GNUC_PRINTF (4, 5);
|
||||
|
||||
const char * gdk_gl_context_get_glsl_version_string (GdkGLContext *self);
|
||||
|
||||
gboolean gdk_gl_context_has_debug (GdkGLContext *self) G_GNUC_PURE;
|
||||
|
||||
gboolean gdk_gl_context_use_es_bgra (GdkGLContext *context);
|
||||
|
||||
+2
-5
@@ -24,16 +24,13 @@
|
||||
#include "gdksnapshotprivate.h"
|
||||
#include "gdkprivate.h"
|
||||
|
||||
#include <graphene.h>
|
||||
|
||||
/* HACK: So we don't need to include any (not-yet-created) GSK or GTK headers */
|
||||
GdkSnapshot * gtk_snapshot_new (void);
|
||||
void gtk_snapshot_push_debug (GdkSnapshot *snapshot,
|
||||
const char *message,
|
||||
...) G_GNUC_PRINTF (2, 3);
|
||||
void gtk_snapshot_pop (GdkSnapshot *snapshot);
|
||||
GdkPaintable * gtk_snapshot_free_to_paintable (GdkSnapshot *snapshot,
|
||||
const graphene_size_t *size);
|
||||
GdkPaintable * gtk_snapshot_free_to_paintable (GdkSnapshot *snapshot);
|
||||
|
||||
/**
|
||||
* GdkPaintable:
|
||||
@@ -121,7 +118,7 @@ gdk_paintable_default_get_current_image (GdkPaintable *paintable)
|
||||
|
||||
snapshot = gtk_snapshot_new ();
|
||||
gdk_paintable_snapshot (paintable, snapshot, width, height);
|
||||
return gtk_snapshot_free_to_paintable (snapshot, NULL);
|
||||
return gtk_snapshot_free_to_paintable (snapshot);
|
||||
}
|
||||
|
||||
static GdkPaintableFlags
|
||||
|
||||
+1
-1
@@ -254,7 +254,7 @@ maybe_flip_position (int bounds_pos,
|
||||
*flipped = TRUE;
|
||||
secondary = rect_pos + (1 - rect_sign) * rect_size / 2 - offset - (1 - surface_sign) * surface_size / 2;
|
||||
|
||||
if ((secondary >= bounds_pos && secondary + surface_size <= bounds_pos + bounds_size) || primary > bounds_pos + bounds_size)
|
||||
if (secondary >= bounds_pos && secondary + surface_size <= bounds_pos + bounds_size)
|
||||
return secondary;
|
||||
|
||||
*flipped = FALSE;
|
||||
|
||||
+3
-5
@@ -345,9 +345,7 @@ gdk_texture_init (GdkTexture *self)
|
||||
*
|
||||
* Creates a new texture object representing the surface.
|
||||
*
|
||||
* The @surface must be an image surface with format `CAIRO_FORMAT_ARGB32`.
|
||||
*
|
||||
* The newly created texture will acquire a reference on the @surface.
|
||||
* @surface must be an image surface with format `CAIRO_FORMAT_ARGB32`.
|
||||
*
|
||||
* Returns: a new `GdkTexture`
|
||||
*/
|
||||
@@ -366,7 +364,7 @@ gdk_texture_new_for_surface (cairo_surface_t *surface)
|
||||
* cairo_image_surface_get_stride (surface),
|
||||
(GDestroyNotify) cairo_surface_destroy,
|
||||
cairo_surface_reference (surface));
|
||||
|
||||
|
||||
texture = gdk_memory_texture_new (cairo_image_surface_get_width (surface),
|
||||
cairo_image_surface_get_height (surface),
|
||||
GDK_MEMORY_DEFAULT,
|
||||
@@ -821,7 +819,7 @@ gdk_texture_set_render_data (GdkTexture *self,
|
||||
GDestroyNotify notify)
|
||||
{
|
||||
g_return_val_if_fail (data != NULL, FALSE);
|
||||
|
||||
|
||||
if (self->render_key != NULL)
|
||||
return FALSE;
|
||||
|
||||
|
||||
@@ -453,7 +453,7 @@ get_colorized_texture (GdkTexture *texture,
|
||||
const graphene_matrix_t *color_matrix,
|
||||
const graphene_vec4_t *color_offset)
|
||||
{
|
||||
cairo_surface_t *surface;
|
||||
cairo_surface_t *surface = gdk_texture_download_surface (texture);
|
||||
cairo_surface_t *image_surface;
|
||||
graphene_vec4_t pixel;
|
||||
guint32* pixel_data;
|
||||
@@ -475,7 +475,6 @@ get_colorized_texture (GdkTexture *texture,
|
||||
return g_object_ref (colorized->texture);
|
||||
}
|
||||
|
||||
surface = gdk_texture_download_surface (texture);
|
||||
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);
|
||||
@@ -537,8 +536,6 @@ get_colorized_texture (GdkTexture *texture,
|
||||
colorized_list, (GDestroyNotify)colorized_texture_free_list);
|
||||
}
|
||||
|
||||
cairo_surface_destroy (surface);
|
||||
|
||||
return colorized_texture;
|
||||
}
|
||||
|
||||
@@ -614,7 +611,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer,
|
||||
}
|
||||
|
||||
texture = gdk_texture_new_for_surface (image_surface);
|
||||
g_ptr_array_add (self->node_textures, texture); /* Transfers ownership to node_textures */
|
||||
g_ptr_array_add (self->node_textures, g_object_ref (texture)); /* Transfers ownership to node_textures */
|
||||
texture_id = gdk_broadway_display_ensure_texture (display, texture);
|
||||
|
||||
add_rect (nodes, &node->bounds, offset_x, offset_y);
|
||||
@@ -905,8 +902,6 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer,
|
||||
add_float (nodes, width);
|
||||
add_float (nodes, height);
|
||||
add_uint32 (nodes, texture_id);
|
||||
|
||||
cairo_surface_destroy (surface);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
+338
-76
@@ -35,10 +35,14 @@
|
||||
#include <gsk/gskrectprivate.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"
|
||||
@@ -65,14 +69,6 @@ typedef struct _GskGLRenderClip
|
||||
guint is_fully_contained : 1;
|
||||
} GskGLRenderClip;
|
||||
|
||||
#define GDK_ARRAY_NAME clips
|
||||
#define GDK_ARRAY_TYPE_NAME Clips
|
||||
#define GDK_ARRAY_ELEMENT_TYPE GskGLRenderClip
|
||||
#define GDK_ARRAY_BY_VALUE 1
|
||||
#define GDK_ARRAY_PREALLOC 16
|
||||
#define GDK_ARRAY_NO_MEMSET
|
||||
#include "gdk/gdkarrayimpl.c"
|
||||
|
||||
typedef struct _GskGLRenderModelview
|
||||
{
|
||||
GskTransform *transform;
|
||||
@@ -85,14 +81,6 @@ typedef struct _GskGLRenderModelview
|
||||
graphene_matrix_t matrix;
|
||||
} GskGLRenderModelview;
|
||||
|
||||
#define GDK_ARRAY_NAME modelviews
|
||||
#define GDK_ARRAY_TYPE_NAME Modelviews
|
||||
#define GDK_ARRAY_ELEMENT_TYPE GskGLRenderModelview
|
||||
#define GDK_ARRAY_BY_VALUE 1
|
||||
#define GDK_ARRAY_PREALLOC 16
|
||||
#define GDK_ARRAY_NO_MEMSET
|
||||
#include "gdk/gdkarrayimpl.c"
|
||||
|
||||
struct _GskGLRenderJob
|
||||
{
|
||||
/* The context containing the framebuffer we are drawing to. Generally this
|
||||
@@ -133,12 +121,12 @@ struct _GskGLRenderJob
|
||||
/* An array of GskGLRenderModelview updated as nodes are processed. The
|
||||
* current modelview is the last element.
|
||||
*/
|
||||
Modelviews modelview;
|
||||
GArray *modelview;
|
||||
|
||||
/* An array of GskGLRenderClip updated as nodes are processed. The
|
||||
* current clip is the last element.
|
||||
*/
|
||||
Clips clip;
|
||||
GArray *clip;
|
||||
|
||||
/* Our current alpha state as we process nodes */
|
||||
float alpha;
|
||||
@@ -168,6 +156,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.
|
||||
*/
|
||||
@@ -209,22 +200,6 @@ static gboolean gsk_gl_render_job_visit_node_with_offscreen (GskGLRenderJob
|
||||
const GskRenderNode *node,
|
||||
GskGLRenderOffscreen *offscreen);
|
||||
|
||||
static inline GskGLRenderClip *
|
||||
clips_grow_one (Clips *clips)
|
||||
{
|
||||
guint len = clips_get_size (clips);
|
||||
clips_set_size (clips, len + 1);
|
||||
return clips_get (clips, len);
|
||||
}
|
||||
|
||||
static inline GskGLRenderModelview *
|
||||
modelviews_grow_one (Modelviews *modelviews)
|
||||
{
|
||||
guint len = modelviews_get_size (modelviews);
|
||||
modelviews_set_size (modelviews, len + 1);
|
||||
return modelviews_get (modelviews, len);
|
||||
}
|
||||
|
||||
static inline int
|
||||
get_target_format (GskGLRenderJob *job,
|
||||
const GskRenderNode *node)
|
||||
@@ -270,7 +245,7 @@ gsk_rounded_rect_shrink_to_minimum (GskRoundedRect *self)
|
||||
static inline gboolean G_GNUC_PURE
|
||||
node_supports_2d_transform (const GskRenderNode *node)
|
||||
{
|
||||
switch (GSK_RENDER_NODE_TYPE (node))
|
||||
switch (gsk_render_node_get_node_type (node))
|
||||
{
|
||||
case GSK_COLOR_NODE:
|
||||
case GSK_OPACITY_NODE:
|
||||
@@ -332,7 +307,7 @@ node_supports_transform (const GskRenderNode *node)
|
||||
* opacity or color matrix.
|
||||
*/
|
||||
|
||||
switch (GSK_RENDER_NODE_TYPE (node))
|
||||
switch (gsk_render_node_get_node_type (node))
|
||||
{
|
||||
case GSK_COLOR_NODE:
|
||||
case GSK_OPACITY_NODE:
|
||||
@@ -498,10 +473,15 @@ gsk_gl_render_job_set_modelview (GskGLRenderJob *job,
|
||||
GskGLRenderModelview *modelview;
|
||||
|
||||
g_assert (job != NULL);
|
||||
g_assert (job->modelview != NULL);
|
||||
|
||||
job->driver->stamps[UNIFORM_SHARED_MODELVIEW]++;
|
||||
|
||||
modelview = modelviews_grow_one (&job->modelview);
|
||||
g_array_set_size (job->modelview, job->modelview->len + 1);
|
||||
|
||||
modelview = &g_array_index (job->modelview,
|
||||
GskGLRenderModelview,
|
||||
job->modelview->len - 1);
|
||||
|
||||
modelview->transform = transform;
|
||||
|
||||
@@ -526,17 +506,26 @@ gsk_gl_render_job_push_modelview (GskGLRenderJob *job,
|
||||
GskGLRenderModelview *modelview;
|
||||
|
||||
g_assert (job != NULL);
|
||||
g_assert (job->modelview != NULL);
|
||||
g_assert (transform != NULL);
|
||||
|
||||
job->driver->stamps[UNIFORM_SHARED_MODELVIEW]++;
|
||||
|
||||
modelview = modelviews_grow_one (&job->modelview);
|
||||
g_array_set_size (job->modelview, job->modelview->len + 1);
|
||||
|
||||
if G_LIKELY (modelviews_get_size (&job->modelview) > 1)
|
||||
modelview = &g_array_index (job->modelview,
|
||||
GskGLRenderModelview,
|
||||
job->modelview->len - 1);
|
||||
|
||||
if G_LIKELY (job->modelview->len > 1)
|
||||
{
|
||||
GskGLRenderModelview *last = job->modelview.end - 2;
|
||||
GskGLRenderModelview *last;
|
||||
GskTransform *t = NULL;
|
||||
|
||||
last = &g_array_index (job->modelview,
|
||||
GskGLRenderModelview,
|
||||
job->modelview->len - 2);
|
||||
|
||||
/* Multiply given matrix with our previous modelview */
|
||||
t = gsk_transform_translate (gsk_transform_ref (last->transform),
|
||||
&(graphene_point_t) {
|
||||
@@ -570,7 +559,8 @@ gsk_gl_render_job_pop_modelview (GskGLRenderJob *job)
|
||||
const GskGLRenderModelview *head;
|
||||
|
||||
g_assert (job != NULL);
|
||||
g_assert (modelviews_get_size (&job->modelview) > 0);
|
||||
g_assert (job->modelview);
|
||||
g_assert (job->modelview->len > 0);
|
||||
|
||||
job->driver->stamps[UNIFORM_SHARED_MODELVIEW]++;
|
||||
|
||||
@@ -581,11 +571,11 @@ gsk_gl_render_job_pop_modelview (GskGLRenderJob *job)
|
||||
|
||||
gsk_transform_unref (head->transform);
|
||||
|
||||
job->modelview.end--;
|
||||
job->modelview->len--;
|
||||
|
||||
if (modelviews_get_size (&job->modelview) >= 1)
|
||||
if (job->modelview->len >= 1)
|
||||
{
|
||||
head = job->modelview.end - 1;
|
||||
head = &g_array_index (job->modelview, GskGLRenderModelview, job->modelview->len - 1);
|
||||
|
||||
job->scale_x = head->scale_x;
|
||||
job->scale_y = head->scale_y;
|
||||
@@ -605,12 +595,14 @@ gsk_gl_render_job_push_clip (GskGLRenderJob *job,
|
||||
GskGLRenderClip *clip;
|
||||
|
||||
g_assert (job != NULL);
|
||||
g_assert (job->clip != NULL);
|
||||
g_assert (rect != NULL);
|
||||
|
||||
job->driver->stamps[UNIFORM_SHARED_CLIP_RECT]++;
|
||||
|
||||
clip = clips_grow_one (&job->clip);
|
||||
g_array_set_size (job->clip, job->clip->len + 1);
|
||||
|
||||
clip = &g_array_index (job->clip, GskGLRenderClip, job->clip->len - 1);
|
||||
memcpy (&clip->rect, rect, sizeof *rect);
|
||||
clip->is_rectilinear = gsk_rounded_rect_is_rectilinear (rect);
|
||||
clip->is_fully_contained = FALSE;
|
||||
@@ -625,13 +617,16 @@ gsk_gl_render_job_push_contained_clip (GskGLRenderJob *job)
|
||||
GskGLRenderClip *old_clip;
|
||||
|
||||
g_assert (job != NULL);
|
||||
g_assert (clips_get_size (&job->clip) > 0);
|
||||
g_assert (job->clip != NULL);
|
||||
g_assert (job->clip->len > 0);
|
||||
|
||||
job->driver->stamps[UNIFORM_SHARED_CLIP_RECT]++;
|
||||
|
||||
clip = clips_grow_one (&job->clip);
|
||||
old_clip = clips_get (&job->clip, clips_get_size (&job->clip) - 2);
|
||||
old_clip = &g_array_index (job->clip, GskGLRenderClip, job->clip->len - 1);
|
||||
|
||||
g_array_set_size (job->clip, job->clip->len + 1);
|
||||
|
||||
clip = &g_array_index (job->clip, GskGLRenderClip, job->clip->len - 1);
|
||||
memcpy (&clip->rect.bounds, &old_clip->rect.bounds, sizeof (graphene_rect_t));
|
||||
memset (clip->rect.corner, 0, sizeof clip->rect.corner);
|
||||
clip->is_rectilinear = TRUE;
|
||||
@@ -644,11 +639,12 @@ static void
|
||||
gsk_gl_render_job_pop_clip (GskGLRenderJob *job)
|
||||
{
|
||||
g_assert (job != NULL);
|
||||
g_assert (clips_get_size (&job->clip) > 0);
|
||||
g_assert (job->clip != NULL);
|
||||
g_assert (job->clip->len > 0);
|
||||
|
||||
job->driver->stamps[UNIFORM_SHARED_CLIP_RECT]++;
|
||||
job->current_clip--;
|
||||
job->clip.end--;
|
||||
job->clip->len--;
|
||||
}
|
||||
|
||||
static inline void
|
||||
@@ -730,7 +726,7 @@ gsk_gl_render_job_transform_bounds (GskGLRenderJob *job,
|
||||
GskTransformCategory category;
|
||||
|
||||
g_assert (job != NULL);
|
||||
g_assert (modelviews_get_size (&job->modelview) > 0);
|
||||
g_assert (job->modelview->len > 0);
|
||||
g_assert (rect != NULL);
|
||||
g_assert (out_rect != NULL);
|
||||
|
||||
@@ -1225,12 +1221,12 @@ gsk_gl_render_job_visit_as_fallback (GskGLRenderJob *job,
|
||||
{
|
||||
cairo_move_to (cr, 0, 0);
|
||||
cairo_rectangle (cr, 0, 0, node->bounds.size.width, node->bounds.size.height);
|
||||
if (GSK_RENDER_NODE_TYPE (node) == GSK_CAIRO_NODE)
|
||||
if (gsk_render_node_get_node_type (node) == GSK_CAIRO_NODE)
|
||||
cairo_set_source_rgba (cr, 0.3, 0, 1, 0.25);
|
||||
else
|
||||
cairo_set_source_rgba (cr, 1, 0, 0, 0.25);
|
||||
cairo_fill_preserve (cr);
|
||||
if (GSK_RENDER_NODE_TYPE (node) == GSK_CAIRO_NODE)
|
||||
if (gsk_render_node_get_node_type (node) == GSK_CAIRO_NODE)
|
||||
cairo_set_source_rgba (cr, 0.3, 0, 1, 1);
|
||||
else
|
||||
cairo_set_source_rgba (cr, 1, 0, 0, 1);
|
||||
@@ -1523,7 +1519,7 @@ gsk_gl_render_job_visit_linear_gradient_node (GskGLRenderJob *job,
|
||||
const graphene_point_t *start = gsk_linear_gradient_node_get_start (node);
|
||||
const graphene_point_t *end = gsk_linear_gradient_node_get_end (node);
|
||||
int n_color_stops = gsk_linear_gradient_node_get_n_color_stops (node);
|
||||
gboolean repeat = GSK_RENDER_NODE_TYPE (node) == GSK_REPEATING_LINEAR_GRADIENT_NODE;
|
||||
gboolean repeat = gsk_render_node_get_node_type (node) == GSK_REPEATING_LINEAR_GRADIENT_NODE;
|
||||
float x1 = job->offset_x + start->x;
|
||||
float x2 = job->offset_x + end->x;
|
||||
float y1 = job->offset_y + start->y;
|
||||
@@ -1596,7 +1592,7 @@ gsk_gl_render_job_visit_radial_gradient_node (GskGLRenderJob *job,
|
||||
float end = gsk_radial_gradient_node_get_end (node);
|
||||
float hradius = gsk_radial_gradient_node_get_hradius (node);
|
||||
float vradius = gsk_radial_gradient_node_get_vradius (node);
|
||||
gboolean repeat = GSK_RENDER_NODE_TYPE (node) == GSK_REPEATING_RADIAL_GRADIENT_NODE;
|
||||
gboolean repeat = gsk_render_node_get_node_type (node) == GSK_REPEATING_RADIAL_GRADIENT_NODE;
|
||||
float scale = 1.0f / (end - start);
|
||||
float bias = -start * scale;
|
||||
|
||||
@@ -1741,7 +1737,7 @@ gsk_gl_render_job_visit_rounded_clip_node (GskGLRenderJob *job,
|
||||
* which both have rounded corners.
|
||||
*/
|
||||
|
||||
if (clips_get_size (&job->clip) <= 1)
|
||||
if (job->clip->len <= 1)
|
||||
need_offscreen = FALSE;
|
||||
else if (gsk_rounded_rect_contains_rect (&job->current_clip->rect, &transformed_clip.bounds))
|
||||
need_offscreen = FALSE;
|
||||
@@ -2810,8 +2806,8 @@ static inline gboolean G_GNUC_PURE
|
||||
equal_texture_nodes (const GskRenderNode *node1,
|
||||
const GskRenderNode *node2)
|
||||
{
|
||||
if (GSK_RENDER_NODE_TYPE (node1) != GSK_TEXTURE_NODE ||
|
||||
GSK_RENDER_NODE_TYPE (node2) != GSK_TEXTURE_NODE)
|
||||
if (gsk_render_node_get_node_type (node1) != GSK_TEXTURE_NODE ||
|
||||
gsk_render_node_get_node_type (node2) != GSK_TEXTURE_NODE)
|
||||
return FALSE;
|
||||
|
||||
if (gsk_texture_node_get_texture (node1) !=
|
||||
@@ -2964,10 +2960,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);
|
||||
@@ -3104,6 +3100,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)
|
||||
@@ -3114,7 +3367,7 @@ gsk_gl_render_job_visit_shadow_node (GskGLRenderJob *job,
|
||||
|
||||
/* Shadow nodes recolor every pixel of the source texture, but leave the alpha in tact.
|
||||
* If the child is a color matrix node that doesn't touch the alpha, we can throw that away. */
|
||||
if (GSK_RENDER_NODE_TYPE (shadow_child) == GSK_COLOR_MATRIX_NODE &&
|
||||
if (gsk_render_node_get_node_type (shadow_child) == GSK_COLOR_MATRIX_NODE &&
|
||||
!color_matrix_modifies_alpha (shadow_child))
|
||||
shadow_child = gsk_color_matrix_node_get_child (shadow_child);
|
||||
|
||||
@@ -3134,7 +3387,7 @@ gsk_gl_render_job_visit_shadow_node (GskGLRenderJob *job,
|
||||
continue;
|
||||
|
||||
if (shadow->radius == 0 &&
|
||||
GSK_RENDER_NODE_TYPE (shadow_child) == GSK_TEXT_NODE)
|
||||
gsk_render_node_get_node_type (shadow_child) == GSK_TEXT_NODE)
|
||||
{
|
||||
if (dx != 0 || dy != 0)
|
||||
{
|
||||
@@ -3957,7 +4210,7 @@ gsk_gl_render_job_visit_node (GskGLRenderJob *job,
|
||||
if (!gsk_gl_render_job_update_clip (job, &node->bounds, &has_clip))
|
||||
return;
|
||||
|
||||
switch (GSK_RENDER_NODE_TYPE (node))
|
||||
switch (gsk_render_node_get_node_type (node))
|
||||
{
|
||||
case GSK_BLEND_NODE:
|
||||
gsk_gl_render_job_visit_blend_node (job, node);
|
||||
@@ -4010,12 +4263,12 @@ gsk_gl_render_job_visit_node (GskGLRenderJob *job,
|
||||
|
||||
if (i + 1 < n_children &&
|
||||
job->current_clip->is_fully_contained &&
|
||||
GSK_RENDER_NODE_TYPE (child) == GSK_ROUNDED_CLIP_NODE)
|
||||
gsk_render_node_get_node_type (child) == GSK_ROUNDED_CLIP_NODE)
|
||||
{
|
||||
const GskRenderNode *grandchild = gsk_rounded_clip_node_get_child (child);
|
||||
const GskRenderNode *child2 = children[i + 1];
|
||||
if (GSK_RENDER_NODE_TYPE (grandchild) == GSK_COLOR_NODE &&
|
||||
GSK_RENDER_NODE_TYPE (child2) == GSK_BORDER_NODE &&
|
||||
if (gsk_render_node_get_node_type (grandchild) == GSK_COLOR_NODE &&
|
||||
gsk_render_node_get_node_type (child2) == GSK_BORDER_NODE &&
|
||||
gsk_border_node_get_uniform_color (child2) &&
|
||||
rounded_rect_equal (gsk_rounded_clip_node_get_clip (child),
|
||||
gsk_border_node_get_outline (child2)))
|
||||
@@ -4169,7 +4422,7 @@ gsk_gl_render_job_visit_node_with_offscreen (GskGLRenderJob *job,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (GSK_RENDER_NODE_TYPE (node) == GSK_TEXTURE_NODE &&
|
||||
if (gsk_render_node_get_node_type (node) == GSK_TEXTURE_NODE &&
|
||||
!offscreen->force_offscreen)
|
||||
{
|
||||
GdkTexture *texture = gsk_texture_node_get_texture (node);
|
||||
@@ -4487,6 +4740,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)
|
||||
@@ -4542,8 +4804,8 @@ gsk_gl_render_job_new (GskGLDriver *driver,
|
||||
job = g_new0 (GskGLRenderJob, 1);
|
||||
job->driver = g_object_ref (driver);
|
||||
job->command_queue = job->driver->command_queue;
|
||||
clips_init (&job->clip);
|
||||
modelviews_init (&job->modelview);
|
||||
job->clip = g_array_sized_new (FALSE, FALSE, sizeof (GskGLRenderClip), 16);
|
||||
job->modelview = g_array_sized_new (FALSE, FALSE, sizeof (GskGLRenderModelview), 16);
|
||||
job->framebuffer = framebuffer;
|
||||
job->clear_framebuffer = !!clear_framebuffer;
|
||||
job->default_framebuffer = default_framebuffer;
|
||||
@@ -4593,16 +4855,16 @@ gsk_gl_render_job_free (GskGLRenderJob *job)
|
||||
job->current_modelview = NULL;
|
||||
job->current_clip = NULL;
|
||||
|
||||
while (job->modelview.end > job->modelview.start)
|
||||
while (job->modelview->len > 0)
|
||||
{
|
||||
GskGLRenderModelview *modelview = job->modelview.end-1;
|
||||
GskGLRenderModelview *modelview = &g_array_index (job->modelview, GskGLRenderModelview, job->modelview->len-1);
|
||||
g_clear_pointer (&modelview->transform, gsk_transform_unref);
|
||||
job->modelview.end--;
|
||||
job->modelview->len--;
|
||||
}
|
||||
|
||||
g_clear_object (&job->driver);
|
||||
g_clear_pointer (&job->region, cairo_region_destroy);
|
||||
modelviews_clear (&job->modelview);
|
||||
clips_clear (&job->clip);
|
||||
g_clear_pointer (&job->modelview, g_array_unref);
|
||||
g_clear_pointer (&job->clip, g_array_unref);
|
||||
g_free (job);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
#extension GL_OES_standard_derivatives : enable
|
||||
|
||||
#ifndef GSK_LEGACY
|
||||
precision highp float;
|
||||
#endif
|
||||
|
||||
@@ -1749,6 +1749,19 @@ gsk_circle_contour_new (const graphene_point_t *center,
|
||||
return (GskContour *) self;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_circle_contour_get_params (const GskContour *contour,
|
||||
graphene_point_t *center,
|
||||
float *radius,
|
||||
gboolean *ccw)
|
||||
{
|
||||
const GskCircleContour *self = (const GskCircleContour *) contour;
|
||||
|
||||
*center = self->center;
|
||||
*radius = self->radius;
|
||||
*ccw = self->ccw;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Rectangle */
|
||||
|
||||
|
||||
@@ -36,6 +36,10 @@ GskContour * gsk_standard_contour_new (GskPathFlags
|
||||
|
||||
GskContour * gsk_circle_contour_new (const graphene_point_t *center,
|
||||
float radius);
|
||||
void gsk_circle_contour_get_params (const GskContour *contour,
|
||||
graphene_point_t *center,
|
||||
float *radius,
|
||||
gboolean *ccw);
|
||||
GskContour * gsk_rect_contour_new (const graphene_rect_t *rect);
|
||||
GskContour * gsk_rounded_rect_contour_new (const GskRoundedRect *rounded_rect);
|
||||
|
||||
|
||||
+24
-181
@@ -2330,6 +2330,30 @@ gsk_curve_get_crossing (const GskCurve *curve,
|
||||
return get_class (curve->op)->get_crossing (curve, point);
|
||||
}
|
||||
|
||||
float
|
||||
gsk_curve_get_length_to (const GskCurve *curve,
|
||||
float t)
|
||||
{
|
||||
return get_class (curve->op)->get_length_to (curve, t);
|
||||
}
|
||||
|
||||
float
|
||||
gsk_curve_get_length (const GskCurve *curve)
|
||||
{
|
||||
return gsk_curve_get_length_to (curve, 1);
|
||||
}
|
||||
|
||||
float
|
||||
gsk_curve_at_length (const GskCurve *curve,
|
||||
float length,
|
||||
float epsilon)
|
||||
{
|
||||
return get_class (curve->op)->get_at_length (curve, length, epsilon);
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Closest point */
|
||||
|
||||
static gboolean
|
||||
project_point_onto_line (const GskCurve *curve,
|
||||
const graphene_point_t *point,
|
||||
@@ -2451,187 +2475,6 @@ gsk_curve_get_closest_point (const GskCurve *curve,
|
||||
return find_closest_point (curve, point, threshold, 0, 1, out_dist, out_t);
|
||||
}
|
||||
|
||||
float
|
||||
gsk_curve_get_length_to (const GskCurve *curve,
|
||||
float t)
|
||||
{
|
||||
return get_class (curve->op)->get_length_to (curve, t);
|
||||
}
|
||||
|
||||
float
|
||||
gsk_curve_get_length (const GskCurve *curve)
|
||||
{
|
||||
return gsk_curve_get_length_to (curve, 1);
|
||||
}
|
||||
|
||||
/* Compute the inverse of the arclength using bisection,
|
||||
* to a given precision
|
||||
*/
|
||||
float
|
||||
gsk_curve_at_length (const GskCurve *curve,
|
||||
float length,
|
||||
float epsilon)
|
||||
{
|
||||
return get_class (curve->op)->get_at_length (curve, length, 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 = - atan2f (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 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;
|
||||
}
|
||||
|
||||
/* 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 (fabsf (a) > 0.0001)
|
||||
{
|
||||
if (b*b > 4*a*c)
|
||||
{
|
||||
d = sqrtf (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 (fabsf (b) > 0.0001)
|
||||
{
|
||||
t[n++] = -c / b;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
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; /* FIXME */
|
||||
|
||||
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 &&
|
||||
fabsf (ay * ti * ti + by * ti + cy) < 0.001)
|
||||
t[n++] = ti;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* vim:set foldmethod=marker expandtab: */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -187,6 +187,19 @@ int gsk_curve_get_curvature_points (const GskCurve
|
||||
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,
|
||||
GskPathIntersection *kind,
|
||||
int n);
|
||||
|
||||
int gsk_curve_self_intersect (const GskCurve *curve,
|
||||
float *t1,
|
||||
graphene_point_t *p,
|
||||
int n);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ static const GdkDebugKey gsk_debug_keys[] = {
|
||||
{ "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;
|
||||
|
||||
@@ -19,7 +19,8 @@ typedef enum {
|
||||
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)
|
||||
|
||||
@@ -144,6 +144,79 @@ gboolean gsk_path_foreach (GskPath
|
||||
GskPathForeachFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
/**
|
||||
* GskPathIntersection:
|
||||
* @GSK_PATH_INTERSECTION_NONE: No intersection
|
||||
* @GSK_PATH_INTERSECTION_NORMAL: A normal intersection, where the two paths
|
||||
* cross each other
|
||||
* @GSK_PATH_INTERSECTION_START: The start of a segment where the two paths coincide
|
||||
* @GSK_PATH_INTERSECTION_END: The end of a segment where the two paths coincide
|
||||
*
|
||||
* The values of this enumeration classify intersections
|
||||
* between paths.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
GSK_PATH_INTERSECTION_NONE,
|
||||
GSK_PATH_INTERSECTION_NORMAL,
|
||||
GSK_PATH_INTERSECTION_START,
|
||||
GSK_PATH_INTERSECTION_END,
|
||||
} GskPathIntersection;
|
||||
|
||||
/**
|
||||
* GskPathIntersectionFunc:
|
||||
* @path1: the first path
|
||||
* @point1: the intersection as point on @path1
|
||||
* @path2: the second path
|
||||
* @point2: the intersection as point on @path2
|
||||
* @kind: specify the nature of the intersection
|
||||
* @user_data: user data
|
||||
*
|
||||
* Prototype of the callback to iterate through the
|
||||
* intersections of two paths.
|
||||
*
|
||||
* Returns: %TRUE to continue iterating, %FALSE to
|
||||
* immediately abort and not call the function again
|
||||
*
|
||||
* Since: 4.14
|
||||
*/
|
||||
typedef gboolean (* GskPathIntersectionFunc) (GskPath *path1,
|
||||
const GskPathPoint *point1,
|
||||
GskPath *path2,
|
||||
const GskPathPoint *point2,
|
||||
GskPathIntersection kind,
|
||||
gpointer user_data);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
gboolean gsk_path_foreach_intersection (GskPath *path1,
|
||||
GskPath *path2,
|
||||
GskPathIntersectionFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
GskPath * gsk_path_union (GskPath *first,
|
||||
GskPath *second,
|
||||
GskFillRule fill_rule);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
GskPath * gsk_path_intersection (GskPath *first,
|
||||
GskPath *second,
|
||||
GskFillRule fill_rule);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
GskPath * gsk_path_difference (GskPath *first,
|
||||
GskPath *second,
|
||||
GskFillRule fill_rule);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
GskPath * gsk_path_symmetric_difference (GskPath *first,
|
||||
GskPath *second,
|
||||
GskFillRule fill_rule);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
GskPath * gsk_path_simplify (GskPath *self,
|
||||
GskFillRule fill_rule);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPath, gsk_path_unref)
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -0,0 +1,705 @@
|
||||
/*
|
||||
* 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 "gskpathprivate.h"
|
||||
|
||||
#include "gskcurveprivate.h"
|
||||
#include "gskpathbuilder.h"
|
||||
#include "gskpathpoint.h"
|
||||
#include "gskcontourprivate.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gsize count;
|
||||
gboolean closed;
|
||||
gboolean z_is_empty;
|
||||
} CountCurveData;
|
||||
|
||||
static gboolean
|
||||
count_cb (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer data)
|
||||
{
|
||||
CountCurveData *ccd = data;
|
||||
|
||||
(ccd->count)++;
|
||||
|
||||
if (op ==GSK_PATH_CLOSE)
|
||||
{
|
||||
ccd->closed = TRUE;
|
||||
ccd->z_is_empty = graphene_point_equal (&pts[0], &pts[1]);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gsize
|
||||
count_curves (const GskContour *contour,
|
||||
gboolean *closed,
|
||||
gboolean *z_is_empty)
|
||||
{
|
||||
CountCurveData data;
|
||||
|
||||
data.count = 0;
|
||||
data.closed = FALSE;
|
||||
data.z_is_empty = FALSE;
|
||||
|
||||
gsk_contour_foreach (contour, count_cb, &data);
|
||||
|
||||
*closed = data.closed;
|
||||
*z_is_empty = data.z_is_empty;
|
||||
|
||||
return data.count;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GskPathPoint point1;
|
||||
GskPathPoint point2;
|
||||
GskPathIntersection kind;
|
||||
} Intersection;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GskPath *path1;
|
||||
GskPath *path2;
|
||||
GskPathIntersectionFunc func;
|
||||
gpointer data;
|
||||
|
||||
gsize contour1;
|
||||
gsize contour2;
|
||||
gsize idx1;
|
||||
gsize idx2;
|
||||
|
||||
const GskContour *c1;
|
||||
const GskContour *c2;
|
||||
GskCurve curve1;
|
||||
GskCurve curve2;
|
||||
|
||||
gboolean c1_closed;
|
||||
gboolean c2_closed;
|
||||
gboolean c1_z_is_empty;
|
||||
gboolean c2_z_is_empty;
|
||||
|
||||
gsize c1_count;
|
||||
gsize c2_count;
|
||||
|
||||
GArray *points;
|
||||
GArray *all_points;
|
||||
} PathIntersectData;
|
||||
|
||||
static gboolean
|
||||
intersect_curve2 (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer data)
|
||||
{
|
||||
PathIntersectData *pd = data;
|
||||
float t1[10], t2[10];
|
||||
graphene_point_t p[10];
|
||||
GskPathIntersection kind[10];
|
||||
int n;
|
||||
|
||||
if (op == GSK_PATH_MOVE)
|
||||
{
|
||||
if (gsk_contour_get_n_ops (pd->c2) == 1)
|
||||
{
|
||||
float dist, tt;
|
||||
|
||||
if (gsk_curve_get_closest_point (&pd->curve1, &pts[0], 1, &dist, &tt) && dist == 0)
|
||||
{
|
||||
Intersection is;
|
||||
|
||||
is.kind = GSK_PATH_INTERSECTION_NORMAL;
|
||||
is.point1.contour = pd->contour1;
|
||||
is.point1.idx = pd->idx1;
|
||||
is.point1.t = tt;
|
||||
is.point2.contour = pd->contour2;
|
||||
is.point2.idx = 0;
|
||||
is.point2.t = 1;
|
||||
|
||||
g_array_append_val (pd->points, is);
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (op == GSK_PATH_CLOSE)
|
||||
{
|
||||
if (graphene_point_equal (&pts[0], &pts[1]))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
pd->idx2++;
|
||||
|
||||
gsk_curve_init_foreach (&pd->curve2, op, pts, n_pts, weight);
|
||||
|
||||
n = gsk_curve_intersect (&pd->curve1, &pd->curve2, t1, t2, p, kind, 19);
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
Intersection is;
|
||||
|
||||
is.point1.contour = pd->contour1;
|
||||
is.point2.contour = pd->contour2;
|
||||
is.point1.idx = pd->idx1;
|
||||
is.point2.idx = pd->idx2;
|
||||
is.point1.t = t1[i];
|
||||
is.point2.t = t2[i];
|
||||
is.kind = kind[i];
|
||||
|
||||
#if 0
|
||||
g_print ("append p1 { %lu %lu %f } p2 { %lu %lu %f } %s\n",
|
||||
is.point1.contour, is.point1.idx, is.point1.t,
|
||||
is.point2.contour, is.point2.idx, is.point2.t,
|
||||
kn[is.kind]);
|
||||
#endif
|
||||
g_array_append_val (pd->points, is);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
intersect_curve (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer data)
|
||||
{
|
||||
PathIntersectData *pd = data;
|
||||
GskBoundingBox b1, b2;
|
||||
|
||||
if (op == GSK_PATH_MOVE)
|
||||
{
|
||||
if (gsk_contour_get_n_ops (pd->c1) == 1)
|
||||
{
|
||||
GskPathPoint point;
|
||||
float dist;
|
||||
|
||||
if (gsk_contour_get_closest_point (pd->c2, &pts[0], 1, &point, &dist) && dist == 0)
|
||||
{
|
||||
Intersection is;
|
||||
|
||||
is.kind = GSK_PATH_INTERSECTION_NORMAL;
|
||||
is.point1.contour = pd->contour1;
|
||||
is.point1.idx = 0;
|
||||
is.point1.t = 1;
|
||||
is.point2.contour = pd->contour2;
|
||||
is.point2.idx = point.idx;
|
||||
is.point2.t = point.t;
|
||||
|
||||
g_array_append_val (pd->points, is);
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (op == GSK_PATH_CLOSE)
|
||||
{
|
||||
if (graphene_point_equal (&pts[0], &pts[1]))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
pd->idx1++;
|
||||
|
||||
gsk_curve_init_foreach (&pd->curve1, op, pts, n_pts, weight);
|
||||
gsk_curve_get_bounds (&pd->curve1, &b1);
|
||||
|
||||
gsk_contour_get_bounds (pd->c2, &b2);
|
||||
|
||||
if (gsk_bounding_box_intersection (&b1, &b2, NULL))
|
||||
{
|
||||
pd->idx2 = 0;
|
||||
|
||||
if (!gsk_contour_foreach (pd->c2, intersect_curve2, pd))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_path_point_near (const GskPathPoint *p1,
|
||||
const GskPathPoint *p2,
|
||||
gboolean closed,
|
||||
gsize count,
|
||||
gboolean z_is_empty,
|
||||
float epsilon)
|
||||
{
|
||||
if (p1->idx == p2->idx && fabsf (p1->t - p2->t) < epsilon)
|
||||
return TRUE;
|
||||
|
||||
if (p1->idx + 1 == p2->idx && (1 - p1->t + p2->t < epsilon))
|
||||
return TRUE;
|
||||
|
||||
if (p2->idx + 1 == p1->idx && (1 - p2->t + p1->t < epsilon))
|
||||
return TRUE;
|
||||
|
||||
if (closed)
|
||||
{
|
||||
if (p1->idx == 1 && p2->idx == count - 1 && (1 - p2->t + p1->t < epsilon))
|
||||
return TRUE;
|
||||
|
||||
if (p2->idx == 1 && p1->idx == count - 1 && (1 - p1->t + p2->t < epsilon))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (closed && z_is_empty)
|
||||
{
|
||||
if (p1->idx == 1 && p2->idx == count - 2 && (1 - p2->t + p1->t < epsilon))
|
||||
return TRUE;
|
||||
|
||||
if (p2->idx == 1 && p1->idx == count - 2 && (1 - p1->t + p2->t < epsilon))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int cmp_path1 (gconstpointer p1, gconstpointer p2);
|
||||
|
||||
static void
|
||||
default_contour_collect_intersections (const GskContour *contour1,
|
||||
const GskContour *contour2,
|
||||
PathIntersectData *pd)
|
||||
{
|
||||
pd->idx1 = 0;
|
||||
|
||||
g_array_set_size (pd->points, 0);
|
||||
|
||||
gsk_contour_foreach (contour1, intersect_curve, pd);
|
||||
|
||||
g_array_sort (pd->points, cmp_path1);
|
||||
|
||||
#if 0
|
||||
g_print ("after sorting\n");
|
||||
for (gsize i = 0; i < pd->points->len; i++)
|
||||
{
|
||||
Intersection *is = &g_array_index (pd->points, Intersection, i);
|
||||
const char *kn[] = { "none", "normal", "start", "end" };
|
||||
|
||||
g_print ("p1 { %lu %lu %f } p2 { %lu %lu %f } %s\n",
|
||||
is->point1.contour, is->point1.idx, is->point1.t,
|
||||
is->point2.contour, is->point2.idx, is->point2.t,
|
||||
kn[is->kind]);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (gsize i = 0; i < pd->points->len; i++)
|
||||
{
|
||||
Intersection *is1 = &g_array_index (pd->points, Intersection, i);
|
||||
|
||||
for (gsize j = i + 1; j < pd->points->len; j++)
|
||||
{
|
||||
Intersection *is2 = &g_array_index (pd->points, Intersection, j);
|
||||
|
||||
if (!gsk_path_point_near (&is1->point1, &is2->point1,
|
||||
pd->c1_closed, pd->c1_count, pd->c1_z_is_empty,
|
||||
0.001))
|
||||
continue;
|
||||
|
||||
if (!gsk_path_point_near (&is1->point2, &is2->point2,
|
||||
pd->c2_closed, pd->c2_count, pd->c2_z_is_empty,
|
||||
0.001))
|
||||
continue;
|
||||
|
||||
if (is1->kind == GSK_PATH_INTERSECTION_NORMAL && is2->kind != GSK_PATH_INTERSECTION_NONE)
|
||||
is1->kind = GSK_PATH_INTERSECTION_NONE;
|
||||
else if (is2->kind == GSK_PATH_INTERSECTION_NORMAL && is1->kind != GSK_PATH_INTERSECTION_NONE)
|
||||
is2->kind = GSK_PATH_INTERSECTION_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
g_print ("after collapsing\n");
|
||||
for (gsize i = 0; i < pd->points->len; i++)
|
||||
{
|
||||
Intersection *is = &g_array_index (pd->points, Intersection, i);
|
||||
const char *kn[] = { "none", "normal", "start", "end" };
|
||||
|
||||
g_print ("p1 { %lu %lu %f } p2 { %lu %lu %f } %s\n",
|
||||
is->point1.contour, is->point1.idx, is->point1.t,
|
||||
is->point2.contour, is->point2.idx, is->point2.t,
|
||||
kn[is->kind]);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (gsize i = 0; i < pd->points->len; i++)
|
||||
{
|
||||
Intersection *is1 = &g_array_index (pd->points, Intersection, i);
|
||||
|
||||
for (gsize j = i + 1; j < pd->points->len; j++)
|
||||
{
|
||||
Intersection *is2 = &g_array_index (pd->points, Intersection, j);
|
||||
|
||||
if (!gsk_path_point_near (&is1->point1, &is2->point1, FALSE, 0, FALSE, 0.001))
|
||||
break;
|
||||
|
||||
if (!gsk_path_point_near (&is1->point2, &is2->point2,
|
||||
pd->c2_closed, pd->c2_count, pd->c2_z_is_empty,
|
||||
0.001))
|
||||
break;
|
||||
|
||||
if ((is1->kind == GSK_PATH_INTERSECTION_END &&
|
||||
is2->kind == GSK_PATH_INTERSECTION_START) ||
|
||||
(is1->kind == GSK_PATH_INTERSECTION_START &&
|
||||
is2->kind == GSK_PATH_INTERSECTION_END))
|
||||
{
|
||||
is1->kind = GSK_PATH_INTERSECTION_NONE;
|
||||
is2->kind = GSK_PATH_INTERSECTION_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
g_print ("after merging segments\n");
|
||||
for (gsize i = 0; i < pd->points->len; i++)
|
||||
{
|
||||
Intersection *is = &g_array_index (pd->points, Intersection, i);
|
||||
const char *kn[] = { "none", "normal", "start", "end" };
|
||||
|
||||
g_print ("p1 { %lu %lu %f } p2 { %lu %lu %f } %s\n",
|
||||
is->point1.contour, is->point1.idx, is->point1.t,
|
||||
is->point2.contour, is->point2.idx, is->point2.t,
|
||||
kn[is->kind]);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (gsize j = 0; j < pd->points->len; j++)
|
||||
{
|
||||
Intersection *is = &g_array_index (pd->points, Intersection, j);
|
||||
|
||||
if (is->kind != GSK_PATH_INTERSECTION_NONE)
|
||||
g_array_append_val (pd->all_points, *is);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
circle_intersect (const graphene_point_t *center1,
|
||||
float radius1,
|
||||
const graphene_point_t *center2,
|
||||
float radius2,
|
||||
graphene_point_t points[2])
|
||||
{
|
||||
float d;
|
||||
float a, h;
|
||||
graphene_point_t m;
|
||||
graphene_vec2_t n;
|
||||
|
||||
g_assert (radius1 >= 0);
|
||||
g_assert (radius2 >= 0);
|
||||
|
||||
d = graphene_point_distance (center1, center2, NULL, NULL);
|
||||
|
||||
if (d < fabsf (radius1 - radius2))
|
||||
return 0;
|
||||
|
||||
if (d > radius1 + radius2)
|
||||
return 0;
|
||||
|
||||
if (d == radius1 + radius2)
|
||||
{
|
||||
graphene_point_interpolate (center1, center2, radius1 / (radius1 + radius2), &points[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
a = (radius1*radius1 - radius2*radius2 + d*d)/(2*d);
|
||||
h = sqrtf (radius1*radius1 - a*a);
|
||||
|
||||
graphene_point_interpolate (center1, center2, a/d, &m);
|
||||
graphene_vec2_init (&n, center2->y - center1->y, center1->x - center2->x);
|
||||
graphene_vec2_normalize (&n, &n);
|
||||
|
||||
graphene_point_init (&points[0], m.x + graphene_vec2_get_x (&n) * h,
|
||||
m.y + graphene_vec2_get_y (&n) * h);
|
||||
graphene_point_init (&points[1], m.x - graphene_vec2_get_x (&n) * h,
|
||||
m.y - graphene_vec2_get_y (&n) * h);
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
static void
|
||||
circle_contour_collect_intersections (const GskContour *contour1,
|
||||
const GskContour *contour2,
|
||||
PathIntersectData *pd)
|
||||
{
|
||||
graphene_point_t center1, center2;
|
||||
float radius1, radius2;
|
||||
gboolean ccw1, ccw2;
|
||||
graphene_point_t p[2];
|
||||
int n;
|
||||
Intersection is[2];
|
||||
|
||||
gsk_circle_contour_get_params (contour1, ¢er1, &radius1, &ccw1);
|
||||
gsk_circle_contour_get_params (contour2, ¢er2, &radius2, &ccw2);
|
||||
|
||||
if (graphene_point_equal (¢er1, ¢er2) && radius1 == radius2)
|
||||
{
|
||||
is[0].kind = GSK_PATH_INTERSECTION_START;
|
||||
is[0].point1.contour = pd->contour1;
|
||||
is[0].point1.idx = 1;
|
||||
is[0].point1.t = 0;
|
||||
is[0].point2.contour = pd->contour2;
|
||||
is[0].point2.idx = 1;
|
||||
is[0].point2.t = 0;
|
||||
|
||||
is[1].kind = GSK_PATH_INTERSECTION_END;
|
||||
is[1].point1.contour = pd->contour1;
|
||||
is[1].point1.idx = 1;
|
||||
is[1].point1.t = 1;
|
||||
is[1].point2.contour = pd->contour2;
|
||||
is[1].point2.idx = 1;
|
||||
is[1].point2.t = 1;
|
||||
|
||||
if (ccw1 != ccw2)
|
||||
{
|
||||
is[0].point2.t = 1;
|
||||
is[1].point2.t = 0;
|
||||
}
|
||||
|
||||
g_array_append_val (pd->all_points, is[0]);
|
||||
g_array_append_val (pd->all_points, is[1]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
n = circle_intersect (¢er1, radius1, ¢er2, radius2, p);
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
float d;
|
||||
|
||||
is[i].kind = GSK_PATH_INTERSECTION_NORMAL;
|
||||
is[i].point1.contour = pd->contour1;
|
||||
is[i].point2.contour = pd->contour2;
|
||||
|
||||
gsk_contour_get_closest_point (contour1, &p[i], 1, &is[i].point1, &d);
|
||||
gsk_contour_get_closest_point (contour2, &p[i], 1, &is[i].point2, &d);
|
||||
}
|
||||
|
||||
if (n == 1)
|
||||
{
|
||||
g_array_append_val (pd->all_points, is[0]);
|
||||
}
|
||||
else if (n == 2)
|
||||
{
|
||||
if (gsk_path_point_compare (&is[0].point1, &is[1].point1) < 0)
|
||||
{
|
||||
g_array_append_val (pd->all_points, is[0]);
|
||||
g_array_append_val (pd->all_points, is[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_array_append_val (pd->all_points, is[1]);
|
||||
g_array_append_val (pd->all_points, is[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
contour_collect_intersections (const GskContour *contour1,
|
||||
const GskContour *contour2,
|
||||
PathIntersectData *pd)
|
||||
{
|
||||
if (strcmp (gsk_contour_get_type_name (contour1), "GskCircleContour") == 0 &&
|
||||
strcmp (gsk_contour_get_type_name (contour2), "GskCircleContour") == 0)
|
||||
circle_contour_collect_intersections (contour1, contour2, pd);
|
||||
else
|
||||
default_contour_collect_intersections (contour1, contour2, pd);
|
||||
}
|
||||
|
||||
static int
|
||||
cmp_path1 (gconstpointer p1,
|
||||
gconstpointer p2)
|
||||
{
|
||||
const Intersection *i1 = p1;
|
||||
const Intersection *i2 = p2;
|
||||
int i;
|
||||
|
||||
i = gsk_path_point_compare (&i1->point1, &i2->point1);
|
||||
if (i != 0)
|
||||
return i;
|
||||
|
||||
return gsk_path_point_compare (&i1->point2, &i2->point2);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
contour_foreach_intersection (const GskContour *contour1,
|
||||
PathIntersectData *pd)
|
||||
{
|
||||
GskBoundingBox b1, b2;
|
||||
|
||||
gsk_contour_get_bounds (contour1, &b1);
|
||||
|
||||
g_array_set_size (pd->all_points, 0);
|
||||
|
||||
for (gsize i = 0; i < gsk_path_get_n_contours (pd->path2); i++)
|
||||
{
|
||||
const GskContour *contour2 = gsk_path_get_contour (pd->path2, i);
|
||||
|
||||
gsk_contour_get_bounds (contour1, &b2);
|
||||
|
||||
if (gsk_bounding_box_intersection (&b1, &b2, NULL))
|
||||
{
|
||||
pd->contour2 = i;
|
||||
pd->c2 = contour2;
|
||||
pd->c2_count = count_curves (contour2, &pd->c2_closed, &pd->c2_z_is_empty);
|
||||
|
||||
contour_collect_intersections (contour1, contour2, pd);
|
||||
}
|
||||
}
|
||||
|
||||
g_array_sort (pd->all_points, cmp_path1);
|
||||
|
||||
#if 0
|
||||
g_print ("after sorting\n");
|
||||
for (gsize i = 0; i < pd->all_points->len; i++)
|
||||
{
|
||||
Intersection *is = &g_array_index (pd->all_points, Intersection, i);
|
||||
const char *kn[] = { "none", "normal", "start", "end" };
|
||||
|
||||
g_print ("p1 { %lu %lu %f } p2 { %lu %lu %f } %s\n",
|
||||
is->point1.contour, is->point1.idx, is->point1.t,
|
||||
is->point2.contour, is->point2.idx, is->point2.t,
|
||||
kn[is->kind]);
|
||||
}
|
||||
|
||||
for (gsize i = 0; i + 1 < pd->all_points->len; i++)
|
||||
{
|
||||
Intersection *is1 = &g_array_index (pd->all_points, Intersection, i);
|
||||
Intersection *is2 = &g_array_index (pd->all_points, Intersection, i + 1);
|
||||
|
||||
if (gsk_path_point_equal (&is1->point1, &is2->point1) &&
|
||||
gsk_path_point_equal (&is1->point2, &is2->point2))
|
||||
{
|
||||
if (is1->kind == GSK_PATH_INTERSECTION_END &&
|
||||
is2->kind == GSK_PATH_INTERSECTION_START)
|
||||
{
|
||||
is1->kind = GSK_PATH_INTERSECTION_NONE;
|
||||
is2->kind = GSK_PATH_INTERSECTION_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
is2->kind = MAX (is1->kind, is2->kind);
|
||||
is1->kind = GSK_PATH_INTERSECTION_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_print ("emitting\n");
|
||||
for (gsize i = 0; i < pd->all_points->len; i++)
|
||||
{
|
||||
Intersection *is = &g_array_index (pd->all_points, Intersection, i);
|
||||
const char *kn[] = { "none", "normal", "start", "end" };
|
||||
|
||||
g_print ("p1 { %lu %lu %f } p2 { %lu %lu %f } %s\n",
|
||||
is->point1.contour, is->point1.idx, is->point1.t,
|
||||
is->point2.contour, is->point2.idx, is->point2.t,
|
||||
kn[is->kind]);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (gsize i = 0; i < pd->all_points->len; i++)
|
||||
{
|
||||
Intersection *is = &g_array_index (pd->all_points, Intersection, i);
|
||||
|
||||
if (is->kind != GSK_PATH_INTERSECTION_NONE)
|
||||
{
|
||||
|
||||
if (!pd->func (pd->path1, &is->point1, pd->path2, &is->point2, is->kind, pd->data))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_foreach_intersection:
|
||||
* @path1: the first path
|
||||
* @path2: the second path
|
||||
* @func: (scope call) (closure user_data): the function to call for intersections
|
||||
* @user_data: (nullable): user data passed to @func
|
||||
*
|
||||
* Finds intersections between two paths.
|
||||
*
|
||||
* This function finds intersections between @path1 and @path2,
|
||||
* and calls @func for each of them, in increasing order for @path1.
|
||||
*
|
||||
* When segments of the paths coincide, the callback is called once
|
||||
* for the start of the segment, with @GSK_PATH_INTERSECTION_START, and
|
||||
* once for the end of the segment, with @GSK_PATH_INTERSECTION_END.
|
||||
* Note that other intersections may occur between the start and end
|
||||
* of such a segment.
|
||||
*
|
||||
* If @func returns `FALSE`, the iteration is stopped.
|
||||
*
|
||||
* Returns: `FALSE` if @func returned FALSE`, `TRUE` otherwise.
|
||||
*
|
||||
* Since: 4.14
|
||||
*/
|
||||
gboolean
|
||||
gsk_path_foreach_intersection (GskPath *path1,
|
||||
GskPath *path2,
|
||||
GskPathIntersectionFunc func,
|
||||
gpointer data)
|
||||
{
|
||||
PathIntersectData pd = {
|
||||
.path1 = path1,
|
||||
.path2 = path2,
|
||||
.func = func,
|
||||
.data = data,
|
||||
};
|
||||
gboolean ret;
|
||||
|
||||
pd.points = g_array_new (FALSE, FALSE, sizeof (Intersection));
|
||||
pd.all_points = g_array_new (FALSE, FALSE, sizeof (Intersection));
|
||||
|
||||
ret = TRUE;
|
||||
for (gsize i = 0; i < gsk_path_get_n_contours (path1); i++)
|
||||
{
|
||||
const GskContour *contour1 = gsk_path_get_contour (path1, i);
|
||||
|
||||
pd.contour1 = i;
|
||||
pd.c1 = contour1;
|
||||
pd.c1_count = count_curves (contour1, &pd.c1_closed, &pd.c1_z_is_empty);
|
||||
pd.idx1 = 0;
|
||||
|
||||
if (!contour_foreach_intersection (contour1, &pd))
|
||||
{
|
||||
ret = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_array_unref (pd.points);
|
||||
g_array_unref (pd.all_points);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* vim:set foldmethod=marker expandtab: */
|
||||
+1818
File diff suppressed because it is too large
Load Diff
@@ -56,5 +56,19 @@ gboolean gsk_path_foreach_with_tolerance (GskPath
|
||||
void gsk_path_builder_add_contour (GskPathBuilder *builder,
|
||||
GskContour *contour);
|
||||
|
||||
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
|
||||
|
||||
|
||||
+6
-11
@@ -287,21 +287,15 @@ gsk_render_node_alloc (GskRenderNodeType node_type)
|
||||
* Returns: (transfer full): the `GskRenderNode` with an additional reference
|
||||
*/
|
||||
GskRenderNode *
|
||||
(gsk_render_node_ref) (GskRenderNode *node)
|
||||
gsk_render_node_ref (GskRenderNode *node)
|
||||
{
|
||||
g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
|
||||
|
||||
return _gsk_render_node_ref (node);
|
||||
}
|
||||
g_atomic_ref_count_inc (&node->ref_count);
|
||||
|
||||
void
|
||||
_gsk_render_node_unref (GskRenderNode *node)
|
||||
{
|
||||
if G_UNLIKELY (g_atomic_ref_count_dec (&node->ref_count))
|
||||
GSK_RENDER_NODE_GET_CLASS (node)->finalize (node);
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gsk_render_node_unref:
|
||||
* @node: (transfer full): a `GskRenderNode`
|
||||
@@ -312,11 +306,12 @@ _gsk_render_node_unref (GskRenderNode *node)
|
||||
* freed.
|
||||
*/
|
||||
void
|
||||
(gsk_render_node_unref) (GskRenderNode *node)
|
||||
gsk_render_node_unref (GskRenderNode *node)
|
||||
{
|
||||
g_return_if_fail (GSK_IS_RENDER_NODE (node));
|
||||
|
||||
_gsk_render_node_unref (node);
|
||||
if (g_atomic_ref_count_dec (&node->ref_count))
|
||||
GSK_RENDER_NODE_GET_CLASS (node)->finalize (node);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -21,9 +21,6 @@ extern GType gsk_render_node_types[];
|
||||
#define GSK_IS_RENDER_NODE_TYPE(node,type) \
|
||||
(G_TYPE_INSTANCE_GET_CLASS ((node), GSK_TYPE_RENDER_NODE, GskRenderNodeClass)->node_type == (type))
|
||||
|
||||
#define GSK_RENDER_NODE_TYPE(node) \
|
||||
(G_TYPE_INSTANCE_GET_CLASS ((node), GSK_TYPE_RENDER_NODE, GskRenderNodeClass)->node_type)
|
||||
|
||||
struct _GskRenderNode
|
||||
{
|
||||
GTypeInstance parent_instance;
|
||||
@@ -60,8 +57,6 @@ GType gsk_render_node_type_register_static (const char
|
||||
|
||||
gpointer gsk_render_node_alloc (GskRenderNodeType node_type);
|
||||
|
||||
void _gsk_render_node_unref (GskRenderNode *node);
|
||||
|
||||
gboolean gsk_render_node_can_diff (const GskRenderNode *node1,
|
||||
const GskRenderNode *node2) G_GNUC_PURE;
|
||||
void gsk_render_node_diff (GskRenderNode *node1,
|
||||
@@ -92,15 +87,6 @@ gboolean gsk_container_node_is_disjoint (const GskRenderNode
|
||||
|
||||
gboolean gsk_render_node_use_offscreen_for_opacity (const GskRenderNode *node);
|
||||
|
||||
#define gsk_render_node_ref(node) _gsk_render_node_ref(node)
|
||||
#define gsk_render_node_unref(node) _gsk_render_node_unref(node)
|
||||
|
||||
static inline GskRenderNode *
|
||||
_gsk_render_node_ref (GskRenderNode *node)
|
||||
{
|
||||
g_atomic_ref_count_inc (&node->ref_count);
|
||||
return node;
|
||||
}
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
@@ -20,6 +20,9 @@ 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([
|
||||
@@ -28,7 +31,9 @@ gsk_public_sources = files([
|
||||
'gskglshader.c',
|
||||
'gskpath.c',
|
||||
'gskpathbuilder.c',
|
||||
'gskpathintersect.c',
|
||||
'gskpathmeasure.c',
|
||||
'gskpathops.c',
|
||||
'gskpathparser.c',
|
||||
'gskpathpoint.c',
|
||||
'gskrenderer.c',
|
||||
@@ -45,6 +50,7 @@ gsk_private_sources = files([
|
||||
'gskcairoblur.c',
|
||||
'gskcontour.c',
|
||||
'gskcurve.c',
|
||||
'gskcurveintersect.c',
|
||||
'gskdebug.c',
|
||||
'gskprivate.c',
|
||||
'gskprofiler.c',
|
||||
@@ -54,6 +60,7 @@ gsk_private_sources = files([
|
||||
'gl/gskglcompiler.c',
|
||||
'gl/gskgldriver.c',
|
||||
'gl/gskglglyphlibrary.c',
|
||||
'gl/gskglglyphylibrary.c',
|
||||
'gl/gskgliconlibrary.c',
|
||||
'gl/gskglprogram.c',
|
||||
'gl/gskglrenderjob.c',
|
||||
@@ -194,6 +201,7 @@ gsk_deps = [
|
||||
cairo_dep,
|
||||
cairo_csi_dep,
|
||||
libgdk_dep,
|
||||
libglyphy_dep
|
||||
]
|
||||
|
||||
libgsk_f16c = static_library('gsk_f16c',
|
||||
|
||||
@@ -171,9 +171,6 @@ gtk_accessible_role_to_atspi_role (GtkAccessibleRole role)
|
||||
case GTK_ACCESSIBLE_ROLE_OPTION:
|
||||
return ATSPI_ROLE_OPTION_PANE;
|
||||
|
||||
case GTK_ACCESSIBLE_ROLE_PARAGRAPH:
|
||||
return ATSPI_ROLE_PARAGRAPH;
|
||||
|
||||
case GTK_ACCESSIBLE_ROLE_PRESENTATION:
|
||||
return ATSPI_ROLE_INVALID;
|
||||
|
||||
|
||||
@@ -454,8 +454,7 @@ gtk_dialog_constructed (GObject *object)
|
||||
}
|
||||
g_list_free (children);
|
||||
|
||||
if (GTK_IS_HEADER_BAR (priv->headerbar))
|
||||
_gtk_header_bar_track_default_decoration (GTK_HEADER_BAR (priv->headerbar));
|
||||
_gtk_header_bar_track_default_decoration (GTK_HEADER_BAR (priv->headerbar));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1398,8 +1397,7 @@ gtk_dialog_buildable_add_child (GtkBuildable *buildable,
|
||||
else if (g_str_equal (type, "titlebar"))
|
||||
{
|
||||
priv->headerbar = GTK_WIDGET (child);
|
||||
if (GTK_IS_HEADER_BAR (priv->headerbar))
|
||||
_gtk_header_bar_track_default_decoration (GTK_HEADER_BAR (priv->headerbar));
|
||||
_gtk_header_bar_track_default_decoration (GTK_HEADER_BAR (priv->headerbar));
|
||||
gtk_window_set_titlebar (GTK_WINDOW (buildable), priv->headerbar);
|
||||
}
|
||||
else if (g_str_equal (type, "action"))
|
||||
|
||||
@@ -12833,12 +12833,9 @@ gtk_tree_view_is_blank_at_pos (GtkTreeView *tree_view,
|
||||
*column = real_column;
|
||||
|
||||
gtk_tree_model_get_iter (priv->model, &iter, real_path);
|
||||
_gtk_tree_view_find_node (tree_view, real_path, &tree, &node);
|
||||
if (node == NULL)
|
||||
if (!_gtk_tree_view_find_node (tree_view, real_path, &tree, &node))
|
||||
{
|
||||
if (!path)
|
||||
gtk_tree_path_free (real_path);
|
||||
return TRUE;
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
/* Check if there's an expander arrow at (x, y) */
|
||||
|
||||
@@ -209,7 +209,6 @@
|
||||
#include <gtk/gtkpopover.h>
|
||||
#include <gtk/gtkpopovermenu.h>
|
||||
#include <gtk/gtkpopovermenubar.h>
|
||||
#include <gtk/gtkprintdialog.h>
|
||||
#include <gtk/print/gtkprintcontext.h>
|
||||
#include <gtk/print/gtkprintoperation.h>
|
||||
#include <gtk/print/gtkprintoperationpreview.h>
|
||||
|
||||
@@ -832,7 +832,6 @@ static const char *role_names[] = {
|
||||
[GTK_ACCESSIBLE_ROLE_WINDOW] = NC_("accessibility", "window"),
|
||||
[GTK_ACCESSIBLE_ROLE_TOGGLE_BUTTON] = NC_("accessibility", "toggle button"),
|
||||
[GTK_ACCESSIBLE_ROLE_APPLICATION] = NC_("accessibility", "application"),
|
||||
[GTK_ACCESSIBLE_ROLE_PARAGRAPH] = NC_("accessibility", "paragraph"),
|
||||
};
|
||||
|
||||
/*< private >
|
||||
@@ -913,7 +912,6 @@ static struct {
|
||||
{ GTK_ACCESSIBLE_ROLE_STRUCTURE, GTK_ACCESSIBLE_ROLE_APPLICATION },
|
||||
{ GTK_ACCESSIBLE_ROLE_STRUCTURE, GTK_ACCESSIBLE_ROLE_DOCUMENT },
|
||||
{ GTK_ACCESSIBLE_ROLE_STRUCTURE, GTK_ACCESSIBLE_ROLE_GENERIC },
|
||||
{ GTK_ACCESSIBLE_ROLE_STRUCTURE, GTK_ACCESSIBLE_ROLE_PARAGRAPH },
|
||||
{ GTK_ACCESSIBLE_ROLE_STRUCTURE, GTK_ACCESSIBLE_ROLE_PRESENTATION },
|
||||
{ GTK_ACCESSIBLE_ROLE_STRUCTURE, GTK_ACCESSIBLE_ROLE_RANGE },
|
||||
{ GTK_ACCESSIBLE_ROLE_STRUCTURE, GTK_ACCESSIBLE_ROLE_ROW_GROUP },
|
||||
|
||||
@@ -1103,7 +1103,6 @@ static guint8 naming[] = {
|
||||
[GTK_ACCESSIBLE_ROLE_WINDOW] = NAME_FROM_AUTHOR,
|
||||
[GTK_ACCESSIBLE_ROLE_TOGGLE_BUTTON] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
|
||||
[GTK_ACCESSIBLE_ROLE_APPLICATION] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
|
||||
[GTK_ACCESSIBLE_ROLE_PARAGRAPH] = GTK_ACCESSIBLE_NAME_PROHIBITED,
|
||||
};
|
||||
|
||||
/* < private >
|
||||
|
||||
+1
-4
@@ -1362,8 +1362,6 @@ typedef enum {
|
||||
* @GTK_ACCESSIBLE_ROLE_APPLICATION: A toplevel element of a graphical user interface.
|
||||
* This is the role that GTK uses by default for windows.
|
||||
* Since: 4.12
|
||||
* @GTK_ACCESSIBLE_ROLE_PARAGRAPH: A paragraph of content.
|
||||
* Since: 4.14
|
||||
*
|
||||
* The accessible role for a [iface@Accessible] implementation.
|
||||
*
|
||||
@@ -1450,8 +1448,7 @@ typedef enum {
|
||||
GTK_ACCESSIBLE_ROLE_WIDGET,
|
||||
GTK_ACCESSIBLE_ROLE_WINDOW,
|
||||
GTK_ACCESSIBLE_ROLE_TOGGLE_BUTTON GDK_AVAILABLE_ENUMERATOR_IN_4_10,
|
||||
GTK_ACCESSIBLE_ROLE_APPLICATION GDK_AVAILABLE_ENUMERATOR_IN_4_12,
|
||||
GTK_ACCESSIBLE_ROLE_PARAGRAPH GDK_AVAILABLE_ENUMERATOR_IN_4_14
|
||||
GTK_ACCESSIBLE_ROLE_APPLICATION GDK_AVAILABLE_ENUMERATOR_IN_4_12
|
||||
} GtkAccessibleRole;
|
||||
|
||||
/**
|
||||
|
||||
@@ -229,8 +229,6 @@ update_default_decoration (GtkHeaderBar *bar)
|
||||
void
|
||||
_gtk_header_bar_track_default_decoration (GtkHeaderBar *bar)
|
||||
{
|
||||
g_assert (GTK_IS_HEADER_BAR (bar));
|
||||
|
||||
bar->track_default_decoration = TRUE;
|
||||
|
||||
update_default_decoration (bar);
|
||||
|
||||
+2
-3
@@ -773,9 +773,8 @@ menu_deactivate_cb (GtkMenuButton *self)
|
||||
* [ctor@Gtk.PopoverMenu.new_from_model]. Actions will be connected
|
||||
* as documented for this function.
|
||||
*
|
||||
* If [property@Gtk.MenuButton:popover] was already set, that popover will be
|
||||
* dissociated from the @menu_button, and the property is updated to refer to
|
||||
* the newly created popover.
|
||||
* If [property@Gtk.MenuButton:popover] is already set, it will be
|
||||
* dissociated from the @menu_button, and the property is set to %NULL.
|
||||
*/
|
||||
void
|
||||
gtk_menu_button_set_menu_model (GtkMenuButton *menu_button,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,135 +0,0 @@
|
||||
/* GTK - The GIMP Toolkit
|
||||
*
|
||||
* Copyright (C) 2022 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 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include <gtk/gtkwindow.h>
|
||||
#include <gtk/print/gtkprintsettings.h>
|
||||
#include <gtk/print/gtkpagesetup.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GtkPrintSetup GtkPrintSetup;
|
||||
|
||||
#define GTK_TYPE_PRINT_SETUP (gtk_print_setup_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
GType gtk_print_setup_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
GtkPrintSetup *gtk_print_setup_ref (GtkPrintSetup *setup);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
void gtk_print_setup_unref (GtkPrintSetup *setup);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
GtkPrintSettings *
|
||||
gtk_print_setup_get_print_settings (GtkPrintSetup *setup);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
GtkPageSetup * gtk_print_setup_get_page_setup (GtkPrintSetup *setup);
|
||||
|
||||
|
||||
#define GTK_TYPE_PRINT_DIALOG (gtk_print_dialog_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
G_DECLARE_FINAL_TYPE (GtkPrintDialog, gtk_print_dialog, GTK, PRINT_DIALOG, GObject)
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
GtkPrintDialog *gtk_print_dialog_new (void);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
const char * gtk_print_dialog_get_title (GtkPrintDialog *self);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
void gtk_print_dialog_set_title (GtkPrintDialog *self,
|
||||
const char *title);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
const char * gtk_print_dialog_get_accept_label (GtkPrintDialog *self);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
void gtk_print_dialog_set_accept_label (GtkPrintDialog *self,
|
||||
const char *accept_label);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
gboolean gtk_print_dialog_get_modal (GtkPrintDialog *self);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
void gtk_print_dialog_set_modal (GtkPrintDialog *self,
|
||||
gboolean modal);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
GtkPageSetup * gtk_print_dialog_get_page_setup (GtkPrintDialog *self);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
void gtk_print_dialog_set_page_setup (GtkPrintDialog *self,
|
||||
GtkPageSetup *page_setup);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
GtkPrintSettings * gtk_print_dialog_get_print_settings (GtkPrintDialog *self);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
void gtk_print_dialog_set_print_settings (GtkPrintDialog *self,
|
||||
GtkPrintSettings *print_settings);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
void gtk_print_dialog_setup (GtkPrintDialog *self,
|
||||
GtkWindow *parent,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
GtkPrintSetup *gtk_print_dialog_setup_finish (GtkPrintDialog *self,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
void gtk_print_dialog_print (GtkPrintDialog *self,
|
||||
GtkWindow *parent,
|
||||
GtkPrintSetup *setup,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
GOutputStream * gtk_print_dialog_print_finish (GtkPrintDialog *self,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
void gtk_print_dialog_print_file (GtkPrintDialog *self,
|
||||
GtkWindow *parent,
|
||||
GtkPrintSetup *setup,
|
||||
GFile *file,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
GDK_AVAILABLE_IN_4_14
|
||||
gboolean gtk_print_dialog_print_file_finish (GtkPrintDialog *self,
|
||||
GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
+1
-2
@@ -310,7 +310,6 @@ gtk_public_sources = files([
|
||||
'gtkpopover.c',
|
||||
'gtkpopovermenu.c',
|
||||
'gtkpopovermenubar.c',
|
||||
'gtkprintdialog.c',
|
||||
'gtkprogressbar.c',
|
||||
'gtkpropertylookuplistmodel.c',
|
||||
'gtkrange.c',
|
||||
@@ -550,7 +549,6 @@ gtk_public_headers = files([
|
||||
'gtkpopover.h',
|
||||
'gtkpopovermenu.h',
|
||||
'gtkpopovermenubar.h',
|
||||
'gtkprintdialog.h',
|
||||
'gtkprogressbar.h',
|
||||
'gtkrange.h',
|
||||
'gtkrecentmanager.h',
|
||||
@@ -1019,6 +1017,7 @@ gtk_deps = [
|
||||
epoxy_dep,
|
||||
libm,
|
||||
graphene_dep,
|
||||
libglyphy_dep,
|
||||
]
|
||||
|
||||
if x11_enabled
|
||||
|
||||
@@ -1338,30 +1338,6 @@ gtk_enumerate_printers (GtkPrinterFunc func,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static GtkPrinter *found_printer;
|
||||
|
||||
static gboolean
|
||||
match_printer_name (GtkPrinter *printer,
|
||||
gpointer data)
|
||||
{
|
||||
if (strcmp (gtk_printer_get_name (printer), (const char *)data) == 0)
|
||||
{
|
||||
found_printer = g_object_ref (printer);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GtkPrinter *
|
||||
gtk_printer_find (const char *name)
|
||||
{
|
||||
found_printer = NULL;
|
||||
gtk_enumerate_printers (match_printer_name, (gpointer) name, NULL, TRUE);
|
||||
return g_steal_pointer (&found_printer);
|
||||
}
|
||||
|
||||
GType
|
||||
gtk_print_capabilities_get_type (void)
|
||||
{
|
||||
|
||||
@@ -69,6 +69,4 @@ GDK_AVAILABLE_IN_ALL
|
||||
void gtk_print_job_set_status (GtkPrintJob *job,
|
||||
GtkPrintStatus status);
|
||||
|
||||
GtkPrinter * gtk_printer_find (const char *name);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -699,9 +699,8 @@ gtk_print_job_send (GtkPrintJob *job,
|
||||
g_return_if_fail (job->spool_io != NULL);
|
||||
|
||||
gtk_print_job_set_status (job, GTK_PRINT_STATUS_SENDING_DATA);
|
||||
|
||||
if (g_io_channel_get_flags (job->spool_io) & G_IO_FLAG_IS_SEEKABLE)
|
||||
g_io_channel_seek_position (job->spool_io, 0, G_SEEK_SET, NULL);
|
||||
|
||||
g_io_channel_seek_position (job->spool_io, 0, G_SEEK_SET, NULL);
|
||||
|
||||
gtk_print_backend_print_stream (job->backend, job,
|
||||
job->spool_io,
|
||||
|
||||
@@ -384,8 +384,6 @@ find_file_printer (void)
|
||||
for (l = backends; l; l = l->next)
|
||||
{
|
||||
GtkPrintBackend *backend = l->data;
|
||||
|
||||
/* FIXME: this needs changes for cpdb */
|
||||
if (strcmp (G_OBJECT_TYPE_NAME (backend), "GtkPrintBackendFile") == 0)
|
||||
{
|
||||
printers = gtk_print_backend_get_printer_list (backend);
|
||||
@@ -429,6 +427,11 @@ prepare_print_response (GDBusConnection *connection,
|
||||
GtkPrintSettings *settings;
|
||||
GtkPageSetup *page_setup;
|
||||
GtkPrinter *printer;
|
||||
char *filename;
|
||||
char *uri;
|
||||
int fd;
|
||||
|
||||
portal->result = GTK_PRINT_OPERATION_RESULT_APPLY;
|
||||
|
||||
v = g_variant_lookup_value (options, "settings", G_VARIANT_TYPE_VARDICT);
|
||||
settings = gtk_print_settings_new_from_gvariant (v);
|
||||
@@ -441,28 +444,15 @@ prepare_print_response (GDBusConnection *connection,
|
||||
g_variant_lookup (options, "token", "u", &portal->token);
|
||||
|
||||
printer = find_file_printer ();
|
||||
if (printer)
|
||||
{
|
||||
char *filename;
|
||||
int fd;
|
||||
char *uri;
|
||||
|
||||
fd = g_file_open_tmp ("gtkprintXXXXXX", &filename, NULL);
|
||||
uri = g_filename_to_uri (filename, NULL, NULL);
|
||||
gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_OUTPUT_URI, uri);
|
||||
g_free (uri);
|
||||
close (fd);
|
||||
fd = g_file_open_tmp ("gtkprintXXXXXX", &filename, NULL);
|
||||
uri = g_filename_to_uri (filename, NULL, NULL);
|
||||
gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_OUTPUT_URI, uri);
|
||||
g_free (uri);
|
||||
close (fd);
|
||||
|
||||
finish_print (portal, printer, page_setup, settings);
|
||||
g_free (filename);
|
||||
|
||||
portal->result = GTK_PRINT_OPERATION_RESULT_APPLY;
|
||||
}
|
||||
else
|
||||
{
|
||||
portal->do_print = FALSE;
|
||||
portal->result = GTK_PRINT_OPERATION_RESULT_ERROR;
|
||||
}
|
||||
finish_print (portal, printer, page_setup, settings);
|
||||
g_free (filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1056,7 +1056,6 @@ gtk_print_run_page_setup_dialog_async (GtkWindow *parent,
|
||||
struct _PrinterFinder
|
||||
{
|
||||
gboolean found_printer;
|
||||
gboolean scheduled_callback;
|
||||
GFunc func;
|
||||
gpointer data;
|
||||
char *printer_name;
|
||||
@@ -1089,14 +1088,6 @@ find_printer_idle (gpointer data)
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
schedule_finder_callback (PrinterFinder *finder)
|
||||
{
|
||||
g_assert (!finder->scheduled_callback);
|
||||
g_idle_add (find_printer_idle, finder);
|
||||
finder->scheduled_callback = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
printer_added_cb (GtkPrintBackend *backend,
|
||||
GtkPrinter *printer,
|
||||
@@ -1129,7 +1120,7 @@ printer_added_cb (GtkPrintBackend *backend,
|
||||
}
|
||||
|
||||
if (finder->found_printer)
|
||||
schedule_finder_callback (finder);
|
||||
g_idle_add (find_printer_idle, finder);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1144,11 +1135,8 @@ printer_list_done_cb (GtkPrintBackend *backend,
|
||||
gtk_print_backend_destroy (backend);
|
||||
g_object_unref (backend);
|
||||
|
||||
/* If there are no more backends left after removing ourselves from the list
|
||||
* above, then we're finished.
|
||||
*/
|
||||
if (finder->backends == NULL && !finder->found_printer)
|
||||
schedule_finder_callback (finder);
|
||||
if (finder->backends == NULL)
|
||||
g_idle_add (find_printer_idle, finder);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1174,7 +1162,9 @@ find_printer_init (PrinterFinder *finder,
|
||||
|
||||
if (gtk_print_backend_printer_list_is_done (backend))
|
||||
{
|
||||
printer_list_done_cb (backend, finder);
|
||||
finder->backends = g_list_remove (finder->backends, backend);
|
||||
gtk_print_backend_destroy (backend);
|
||||
g_object_unref (backend);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1236,19 +1226,17 @@ find_printer (const char *printer,
|
||||
if (g_module_supported ())
|
||||
finder->backends = gtk_print_backend_load_modules ();
|
||||
|
||||
if (finder->backends == NULL)
|
||||
{
|
||||
schedule_finder_callback (finder);
|
||||
return;
|
||||
}
|
||||
|
||||
for (node = finder->backends; !finder->found_printer && node != NULL; node = next)
|
||||
{
|
||||
next = node->next;
|
||||
find_printer_init (finder, GTK_PRINT_BACKEND (node->data));
|
||||
}
|
||||
|
||||
if (finder->backends == NULL)
|
||||
g_idle_add (find_printer_idle, finder);
|
||||
}
|
||||
|
||||
|
||||
GtkPrintOperationResult
|
||||
_gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *op,
|
||||
gboolean show_dialog,
|
||||
@@ -1260,7 +1248,6 @@ _gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *op,
|
||||
else
|
||||
return gtk_print_operation_unix_run_dialog (op, show_dialog, parent, do_print);
|
||||
}
|
||||
|
||||
void
|
||||
_gtk_print_operation_platform_backend_run_dialog_async (GtkPrintOperation *op,
|
||||
gboolean show_dialog,
|
||||
|
||||
+6
-2
@@ -1,5 +1,5 @@
|
||||
project('gtk', 'c',
|
||||
version: '4.13.2',
|
||||
version: '4.13.1',
|
||||
default_options: [
|
||||
'buildtype=debugoptimized',
|
||||
'warning_level=1',
|
||||
@@ -14,7 +14,7 @@ project('gtk', 'c',
|
||||
glib_req = '>= 2.76.0'
|
||||
introspection_req = '>= 1.76.0' # keep this in sync with glib
|
||||
pango_req = '>= 1.50.0' # keep this in sync with .gitlab-ci/test-msys.sh
|
||||
harfbuzz_req = '>= 2.6.0'
|
||||
harfbuzz_req = '>= 4.0.0'
|
||||
fribidi_req = '>= 1.0.6'
|
||||
cairo_req = '>= 1.14.0'
|
||||
gdk_pixbuf_req = '>= 2.30.0'
|
||||
@@ -26,6 +26,7 @@ cloudproviders_req = '>= 0.3.1'
|
||||
xkbcommon_req = '>= 0.2.0'
|
||||
sysprof_req = '>= 3.38.0'
|
||||
vulkan_req = '>= 1.2'
|
||||
libglyphy_req = '>= 0.2.0'
|
||||
|
||||
fs = import('fs')
|
||||
gnome = import('gnome')
|
||||
@@ -382,6 +383,9 @@ fribidi_dep = dependency('fribidi', version: fribidi_req,
|
||||
default_options: ['docs=false'])
|
||||
harfbuzz_dep = dependency('harfbuzz', version: harfbuzz_req,
|
||||
default_options: ['coretext=enabled'])
|
||||
libglyphy_dep = dependency('glyphy', version: libglyphy_req,
|
||||
default_options: ['default_library=static', 'demo=disabled'],
|
||||
fallback : ['glyphy', 'libglyphy_dep'])
|
||||
|
||||
# Require PangoFT2 if on X11 or wayland
|
||||
pangoft_dep = dependency('pangoft2', version: pango_req,
|
||||
|
||||
@@ -15,8 +15,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: lt\n"
|
||||
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gtk/-/issues/\n"
|
||||
"POT-Creation-Date: 2023-09-25 12:58+0000\n"
|
||||
"PO-Revision-Date: 2023-09-25 22:57+0300\n"
|
||||
"POT-Creation-Date: 2023-09-03 14:34+0000\n"
|
||||
"PO-Revision-Date: 2023-09-03 21:19+0300\n"
|
||||
"Last-Translator: Aurimas Černius <aurisc4@gmail.com>\n"
|
||||
"Language-Team: Lietuvių <gnome-lt@lists.akl.lt>\n"
|
||||
"Language: lt\n"
|
||||
@@ -59,31 +59,31 @@ msgstr "Nepavyko pateikti turinio kaip %s"
|
||||
msgid "The current backend does not support OpenGL"
|
||||
msgstr "Dabartinė realizacija nepalaiko OpenGL"
|
||||
|
||||
#: gdk/gdkdisplay.c:1244 gdk/gdksurface.c:1252
|
||||
#: gdk/gdkdisplay.c:1245 gdk/gdksurface.c:1252
|
||||
msgid "Vulkan support disabled via GDK_DEBUG"
|
||||
msgstr "Vulkan palaikymas išjungtas naudojant GDK_DEBUG"
|
||||
|
||||
#: gdk/gdkdisplay.c:1276
|
||||
#: gdk/gdkdisplay.c:1277
|
||||
msgid "GL support disabled via GDK_DEBUG"
|
||||
msgstr "GL palaikymas išjungtas naudojant GDK_DEBUG"
|
||||
|
||||
#: gdk/gdkdisplay.c:1574
|
||||
#: gdk/gdkdisplay.c:1575
|
||||
msgid "No EGL configuration available"
|
||||
msgstr "Nėra prieinamos EGL konfigūracijos"
|
||||
|
||||
#: gdk/gdkdisplay.c:1582
|
||||
#: gdk/gdkdisplay.c:1583
|
||||
msgid "Failed to get EGL configurations"
|
||||
msgstr "Nepavyko gauti EGL konfigūracijų"
|
||||
|
||||
#: gdk/gdkdisplay.c:1612
|
||||
#: gdk/gdkdisplay.c:1613
|
||||
msgid "No EGL configuration with required features found"
|
||||
msgstr "Nerasta EGL konfigūracija su reikiamomis savybėmis"
|
||||
|
||||
#: gdk/gdkdisplay.c:1619
|
||||
#: gdk/gdkdisplay.c:1620
|
||||
msgid "No perfect EGL configuration found"
|
||||
msgstr "Nerasta puikiai tinkanti EGL konfigūracija"
|
||||
|
||||
#: gdk/gdkdisplay.c:1661
|
||||
#: gdk/gdkdisplay.c:1662
|
||||
#, c-format
|
||||
msgid "EGL implementation is missing extension %s"
|
||||
msgid_plural "EGL implementation is missing %2$d extensions: %1$s"
|
||||
@@ -91,23 +91,23 @@ msgstr[0] "EGL realizacijai trūksta %2$d plėtinio: %1$s"
|
||||
msgstr[1] "EGL realizacijai trūksta %2$d plėtinių: %1$s"
|
||||
msgstr[2] "EGL realizacijai trūksta %2$d plėtinių: %1$s"
|
||||
|
||||
#: gdk/gdkdisplay.c:1694
|
||||
#: gdk/gdkdisplay.c:1695
|
||||
msgid "libEGL not available in this sandbox"
|
||||
msgstr "libEGL šioje apribotoje veiksenoje neprieinama"
|
||||
|
||||
#: gdk/gdkdisplay.c:1695
|
||||
#: gdk/gdkdisplay.c:1696
|
||||
msgid "libEGL not available"
|
||||
msgstr "libEGL neprieinama"
|
||||
|
||||
#: gdk/gdkdisplay.c:1705
|
||||
#: gdk/gdkdisplay.c:1706
|
||||
msgid "Failed to create EGL display"
|
||||
msgstr "Nepavyko sukurti EGL vaizduoklio"
|
||||
|
||||
#: gdk/gdkdisplay.c:1715
|
||||
#: gdk/gdkdisplay.c:1716
|
||||
msgid "Could not initialize EGL display"
|
||||
msgstr "Nepavyko inicializuoti EGL vaizduoklio"
|
||||
|
||||
#: gdk/gdkdisplay.c:1726
|
||||
#: gdk/gdkdisplay.c:1727
|
||||
#, c-format
|
||||
msgid "EGL version %d.%d is too old. GTK requires %d.%d"
|
||||
msgstr "EGL versija %d.%d yra per sena. GTK reikalauja %d.%d"
|
||||
@@ -1039,18 +1039,18 @@ msgctxt "progress bar label"
|
||||
msgid "%d %%"
|
||||
msgstr "%d %%"
|
||||
|
||||
#: gtk/deprecated/gtkcolorbutton.c:183 gtk/deprecated/gtkcolorbutton.c:314
|
||||
#: gtk/deprecated/gtkcolorbutton.c:183 gtk/deprecated/gtkcolorbutton.c:311
|
||||
#: gtk/gtkcolordialog.c:411
|
||||
msgid "Pick a Color"
|
||||
msgstr "Pasirinkite spalvą"
|
||||
|
||||
#: gtk/deprecated/gtkcolorbutton.c:505 gtk/gtkcolorchooserwidget.c:313
|
||||
#: gtk/deprecated/gtkcolorbutton.c:502 gtk/gtkcolorchooserwidget.c:313
|
||||
#: gtk/gtkcolordialogbutton.c:335
|
||||
#, c-format
|
||||
msgid "Red %d%%, Green %d%%, Blue %d%%, Alpha %d%%"
|
||||
msgstr "Raudona %d%%, Žalia %d%%, Mėlyna %d%%, Alfa %d%%"
|
||||
|
||||
#: gtk/deprecated/gtkcolorbutton.c:511 gtk/gtkcolorchooserwidget.c:319
|
||||
#: gtk/deprecated/gtkcolorbutton.c:508 gtk/gtkcolorchooserwidget.c:319
|
||||
#: gtk/gtkcolordialogbutton.c:341
|
||||
#, c-format
|
||||
msgid "Red %d%%, Green %d%%, Blue %d%%"
|
||||
@@ -1060,17 +1060,17 @@ msgstr "Raudona %d%%, Žalia %d%%, Mėlyna %d%%"
|
||||
msgid "Sans 12"
|
||||
msgstr "Sans 12"
|
||||
|
||||
#: gtk/deprecated/gtkfontbutton.c:507 gtk/deprecated/gtkfontbutton.c:624
|
||||
#: gtk/deprecated/gtkfontbutton.c:507 gtk/deprecated/gtkfontbutton.c:621
|
||||
#: gtk/gtkfontdialog.c:596
|
||||
msgid "Pick a Font"
|
||||
msgstr "Pasirinkite šriftą"
|
||||
|
||||
#: gtk/deprecated/gtkfontbutton.c:600 gtk/gtkfilechooserwidget.c:3871
|
||||
#: gtk/deprecated/gtkfontbutton.c:597 gtk/gtkfilechooserwidget.c:3871
|
||||
#: gtk/gtkfontdialogbutton.c:126 gtk/inspector/visual.ui:169
|
||||
msgid "Font"
|
||||
msgstr "Šriftas"
|
||||
|
||||
#: gtk/deprecated/gtkfontbutton.c:1155 gtk/gtkfontdialogbutton.c:652
|
||||
#: gtk/deprecated/gtkfontbutton.c:1152 gtk/gtkfontdialogbutton.c:652
|
||||
msgctxt "font"
|
||||
msgid "None"
|
||||
msgstr "Nėra"
|
||||
@@ -2134,7 +2134,7 @@ msgstr "_Dešinė:"
|
||||
msgid "Paper Margins"
|
||||
msgstr "Popieriaus paraštės"
|
||||
|
||||
#: gtk/gtkentry.c:3685
|
||||
#: gtk/gtkentry.c:3673
|
||||
msgid "Insert Emoji"
|
||||
msgstr "Įterpti emoji"
|
||||
|
||||
@@ -2292,7 +2292,7 @@ msgid "If you delete an item, it will be permanently lost."
|
||||
msgstr "Jei ištrinsite elementą, jis bus negrįžtamai prarastas."
|
||||
|
||||
#: gtk/gtkfilechooserwidget.c:1185 gtk/gtkfilechooserwidget.c:1815
|
||||
#: gtk/gtklabel.c:5695 gtk/gtktext.c:6147 gtk/gtktextview.c:9018
|
||||
#: gtk/gtklabel.c:5695 gtk/gtktext.c:6145 gtk/gtktextview.c:9018
|
||||
msgid "_Delete"
|
||||
msgstr "Iš_trinti"
|
||||
|
||||
@@ -2630,19 +2630,19 @@ msgstr "Užverti"
|
||||
msgid "Close the infobar"
|
||||
msgstr "Užverti informacijos juostą"
|
||||
|
||||
#: gtk/gtklabel.c:5692 gtk/gtktext.c:6135 gtk/gtktextview.c:9006
|
||||
#: gtk/gtklabel.c:5692 gtk/gtktext.c:6133 gtk/gtktextview.c:9006
|
||||
msgid "Cu_t"
|
||||
msgstr "_Iškirpti"
|
||||
|
||||
#: gtk/gtklabel.c:5693 gtk/gtktext.c:6139 gtk/gtktextview.c:9010
|
||||
#: gtk/gtklabel.c:5693 gtk/gtktext.c:6137 gtk/gtktextview.c:9010
|
||||
msgid "_Copy"
|
||||
msgstr "_Kopijuoti"
|
||||
|
||||
#: gtk/gtklabel.c:5694 gtk/gtktext.c:6143 gtk/gtktextview.c:9014
|
||||
#: gtk/gtklabel.c:5694 gtk/gtktext.c:6141 gtk/gtktextview.c:9014
|
||||
msgid "_Paste"
|
||||
msgstr "Į_dėti"
|
||||
|
||||
#: gtk/gtklabel.c:5700 gtk/gtktext.c:6156 gtk/gtktextview.c:9039
|
||||
#: gtk/gtklabel.c:5700 gtk/gtktext.c:6154 gtk/gtktextview.c:9039
|
||||
msgid "Select _All"
|
||||
msgstr "P_ažymėti viską"
|
||||
|
||||
@@ -3498,7 +3498,7 @@ msgstr "Nepavyko perkelti elemento su URI „%s“ į „%s“"
|
||||
msgid "No registered application with name “%s” for item with URI “%s” found"
|
||||
msgstr "Nerasta registruota programa pavadinimu „%s“ elementui su URI „%s“"
|
||||
|
||||
#: gtk/gtksearchentry.c:767
|
||||
#: gtk/gtksearchentry.c:758
|
||||
msgid "Clear Entry"
|
||||
msgstr "Išvalyti lauką"
|
||||
|
||||
@@ -3589,7 +3589,7 @@ msgctxt "accessibility"
|
||||
msgid "Sidebar"
|
||||
msgstr "Šoninė juosta"
|
||||
|
||||
#: gtk/gtktext.c:6161 gtk/gtktextview.c:9044
|
||||
#: gtk/gtktext.c:6159 gtk/gtktextview.c:9044
|
||||
msgid "Insert _Emoji"
|
||||
msgstr "Įterpti _emoji"
|
||||
|
||||
@@ -3967,8 +3967,8 @@ msgid "Surface"
|
||||
msgstr "Paviršius"
|
||||
|
||||
#: gtk/inspector/misc-info.ui:365 gtk/inspector/misc-info.ui:400
|
||||
#: gtk/inspector/misc-info.ui:435 gtk/inspector/prop-editor.c:1153
|
||||
#: gtk/inspector/prop-editor.c:1536 gtk/inspector/window.ui:396
|
||||
#: gtk/inspector/misc-info.ui:435 gtk/inspector/prop-editor.c:1150
|
||||
#: gtk/inspector/prop-editor.c:1533 gtk/inspector/window.ui:396
|
||||
msgid "Properties"
|
||||
msgstr "Savybės"
|
||||
|
||||
@@ -4020,7 +4020,7 @@ msgstr "Rodyklė: %p"
|
||||
#. Translators: %s is a type name, for example
|
||||
#. * GtkPropertyExpression with value \"2.5\"
|
||||
#.
|
||||
#: gtk/inspector/prop-editor.c:827
|
||||
#: gtk/inspector/prop-editor.c:824
|
||||
#, c-format
|
||||
msgid "%s with value \"%s\""
|
||||
msgstr "%s su verte „%s“"
|
||||
@@ -4028,7 +4028,7 @@ msgstr "%s su verte „%s“"
|
||||
#. Translators: Both %s are type names, for example
|
||||
#. * GtkPropertyExpression with type GObject
|
||||
#.
|
||||
#: gtk/inspector/prop-editor.c:838
|
||||
#: gtk/inspector/prop-editor.c:835
|
||||
#, c-format
|
||||
msgid "%s with type %s"
|
||||
msgstr "%s su tipu %s"
|
||||
@@ -4036,7 +4036,7 @@ msgstr "%s su tipu %s"
|
||||
#. Translators: Both %s are type names, for example
|
||||
#. * GtkObjectExpression for GtkStringObject 0x23456789
|
||||
#.
|
||||
#: gtk/inspector/prop-editor.c:851
|
||||
#: gtk/inspector/prop-editor.c:848
|
||||
#, c-format
|
||||
msgid "%s for %s %p"
|
||||
msgstr "%s %s %p"
|
||||
@@ -4044,71 +4044,71 @@ msgstr "%s %s %p"
|
||||
#. Translators: Both %s are type names, for example
|
||||
#. * GtkPropertyExpression with value type: gchararray
|
||||
#.
|
||||
#: gtk/inspector/prop-editor.c:881
|
||||
#: gtk/inspector/prop-editor.c:878
|
||||
#, c-format
|
||||
msgid "%s with value type %s"
|
||||
msgstr "%s su %s tipo verte"
|
||||
|
||||
#: gtk/inspector/prop-editor.c:1230
|
||||
#: gtk/inspector/prop-editor.c:1227
|
||||
#, c-format
|
||||
msgid "Uneditable property type: %s"
|
||||
msgstr "Neredaguojamas savybės tipas: %s"
|
||||
|
||||
#: gtk/inspector/prop-editor.c:1388
|
||||
#: gtk/inspector/prop-editor.c:1385
|
||||
msgctxt "column number"
|
||||
msgid "None"
|
||||
msgstr "Nėra"
|
||||
|
||||
#: gtk/inspector/prop-editor.c:1425
|
||||
#: gtk/inspector/prop-editor.c:1422
|
||||
msgid "Attribute:"
|
||||
msgstr "Atributas:"
|
||||
|
||||
#: gtk/inspector/prop-editor.c:1428
|
||||
#: gtk/inspector/prop-editor.c:1425
|
||||
msgid "Model"
|
||||
msgstr "Modelis"
|
||||
|
||||
#: gtk/inspector/prop-editor.c:1433
|
||||
#: gtk/inspector/prop-editor.c:1430
|
||||
msgid "Column:"
|
||||
msgstr "Stulpelis:"
|
||||
|
||||
#. Translators: %s is a type name, for example
|
||||
#. * Action from 0x2345678 (GtkApplicationWindow)
|
||||
#.
|
||||
#: gtk/inspector/prop-editor.c:1532
|
||||
#: gtk/inspector/prop-editor.c:1529
|
||||
#, c-format
|
||||
msgid "Action from: %p (%s)"
|
||||
msgstr "Veiksmas iš: %p (%s)"
|
||||
|
||||
#: gtk/inspector/prop-editor.c:1587
|
||||
#: gtk/inspector/prop-editor.c:1584
|
||||
msgid "Reset"
|
||||
msgstr "Atstatyti"
|
||||
|
||||
#: gtk/inspector/prop-editor.c:1595
|
||||
#: gtk/inspector/prop-editor.c:1592
|
||||
msgctxt "GtkSettings source"
|
||||
msgid "Default"
|
||||
msgstr "Numatyta"
|
||||
|
||||
#: gtk/inspector/prop-editor.c:1598
|
||||
#: gtk/inspector/prop-editor.c:1595
|
||||
msgctxt "GtkSettings source"
|
||||
msgid "Theme"
|
||||
msgstr "Tema"
|
||||
|
||||
#: gtk/inspector/prop-editor.c:1601
|
||||
#: gtk/inspector/prop-editor.c:1598
|
||||
msgctxt "GtkSettings source"
|
||||
msgid "XSettings"
|
||||
msgstr "XSettings"
|
||||
|
||||
#: gtk/inspector/prop-editor.c:1605
|
||||
#: gtk/inspector/prop-editor.c:1602
|
||||
msgctxt "GtkSettings source"
|
||||
msgid "Application"
|
||||
msgstr "Programa"
|
||||
|
||||
#: gtk/inspector/prop-editor.c:1608
|
||||
#: gtk/inspector/prop-editor.c:1605
|
||||
msgctxt "GtkSettings source"
|
||||
msgid "Unknown"
|
||||
msgstr "Nežinoma"
|
||||
|
||||
#: gtk/inspector/prop-editor.c:1611
|
||||
#: gtk/inspector/prop-editor.c:1608
|
||||
msgid "Source:"
|
||||
msgstr "Šaltinis:"
|
||||
|
||||
@@ -7162,7 +7162,7 @@ msgstr ""
|
||||
#: tools/gtk-builder-tool-enumerate.c:56 tools/gtk-builder-tool-preview.c:179
|
||||
#: tools/gtk-builder-tool-preview.c:180 tools/gtk-builder-tool-screenshot.c:360
|
||||
#: tools/gtk-builder-tool-simplify.c:2529 tools/gtk-builder-tool-validate.c:261
|
||||
#: tools/gtk-rendernode-tool-info.c:202 tools/gtk-rendernode-tool-show.c:102
|
||||
#: tools/gtk-rendernode-tool-info.c:200 tools/gtk-rendernode-tool-show.c:102
|
||||
msgid "FILE"
|
||||
msgstr "FAILAS"
|
||||
|
||||
@@ -7445,37 +7445,37 @@ msgstr ""
|
||||
" render Padaryti viršūnės atvaizdą\n"
|
||||
"\n"
|
||||
|
||||
#: tools/gtk-rendernode-tool-info.c:179
|
||||
#: tools/gtk-rendernode-tool-info.c:177
|
||||
#, c-format
|
||||
msgid "Number of nodes: %u\n"
|
||||
msgstr "Viršūnių skaičius: %u\n"
|
||||
|
||||
#: tools/gtk-rendernode-tool-info.c:186
|
||||
#: tools/gtk-rendernode-tool-info.c:184
|
||||
#, c-format
|
||||
msgid "Depth: %u\n"
|
||||
msgstr "Gylis: %u\n"
|
||||
|
||||
#: tools/gtk-rendernode-tool-info.c:189
|
||||
#: tools/gtk-rendernode-tool-info.c:187
|
||||
#, c-format
|
||||
msgid "Bounds: %g x %g\n"
|
||||
msgstr "Paraštės: %g x %g\n"
|
||||
|
||||
#: tools/gtk-rendernode-tool-info.c:190
|
||||
#: tools/gtk-rendernode-tool-info.c:188
|
||||
#, c-format
|
||||
msgid "Origin: %g %g\n"
|
||||
msgstr "Pradinė koordinatė: %g %g\n"
|
||||
|
||||
#: tools/gtk-rendernode-tool-info.c:211
|
||||
#: tools/gtk-rendernode-tool-info.c:209
|
||||
msgid "Provide information about the render node."
|
||||
msgstr "Pateikti informaciją apie piešimo viršūnę."
|
||||
|
||||
#: tools/gtk-rendernode-tool-info.c:224 tools/gtk-rendernode-tool-show.c:130
|
||||
#: tools/gtk-rendernode-tool-info.c:222 tools/gtk-rendernode-tool-show.c:130
|
||||
#: tools/gtk-rendernode-tool-render.c:225
|
||||
#, c-format
|
||||
msgid "No .node file specified\n"
|
||||
msgstr "Nenurodytas .node failas\n"
|
||||
|
||||
#: tools/gtk-rendernode-tool-info.c:230
|
||||
#: tools/gtk-rendernode-tool-info.c:228
|
||||
#, c-format
|
||||
msgid "Can only accept a single .node file\n"
|
||||
msgstr "Galima pateikti tik vieną .node failą\n"
|
||||
@@ -7525,11 +7525,6 @@ msgstr "Galima piešti vienintelį .node failą į vienintelį išvesties failą
|
||||
msgid "Error at %s: %s\n"
|
||||
msgstr "Klaida ties %s: %s\n"
|
||||
|
||||
#: tools/gtk-rendernode-tool-utils.c:69
|
||||
#, c-format
|
||||
msgid "Failed to load node file: %s\n"
|
||||
msgstr "Nepavyko įkelti viršūnių failo: %s\n"
|
||||
|
||||
#: tools/updateiconcache.c:1391
|
||||
#, c-format
|
||||
msgid "Failed to write header\n"
|
||||
|
||||
+55
-60
@@ -29,8 +29,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: GTK\n"
|
||||
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gtk/-/issues/\n"
|
||||
"POT-Creation-Date: 2023-09-21 10:15+0000\n"
|
||||
"PO-Revision-Date: 2023-09-25 09:58-0300\n"
|
||||
"POT-Creation-Date: 2023-09-05 19:13+0000\n"
|
||||
"PO-Revision-Date: 2023-09-12 13:31-0300\n"
|
||||
"Last-Translator: Rafael Fontenelle <rafaelff@gnome.org>\n"
|
||||
"Language-Team: Brazilian Portuguese <https://br.gnome.org/traducao>\n"
|
||||
"Language: pt_BR\n"
|
||||
@@ -79,55 +79,55 @@ msgstr "Não foi possível fornecer o conteúdo como %s"
|
||||
msgid "The current backend does not support OpenGL"
|
||||
msgstr "O backend não oferece suporte a OpenGL"
|
||||
|
||||
#: gdk/gdkdisplay.c:1244 gdk/gdksurface.c:1252
|
||||
#: gdk/gdkdisplay.c:1245 gdk/gdksurface.c:1252
|
||||
msgid "Vulkan support disabled via GDK_DEBUG"
|
||||
msgstr "Suporte a Vulkan desabilitado via GDK_DEBUG"
|
||||
|
||||
#: gdk/gdkdisplay.c:1276
|
||||
#: gdk/gdkdisplay.c:1277
|
||||
msgid "GL support disabled via GDK_DEBUG"
|
||||
msgstr "Suporte a GL desabilitado via GDK_DEBUG"
|
||||
|
||||
#: gdk/gdkdisplay.c:1574
|
||||
#: gdk/gdkdisplay.c:1575
|
||||
msgid "No EGL configuration available"
|
||||
msgstr "Nenhuma configuração EGL está disponível"
|
||||
|
||||
#: gdk/gdkdisplay.c:1582
|
||||
#: gdk/gdkdisplay.c:1583
|
||||
msgid "Failed to get EGL configurations"
|
||||
msgstr "Falha ao obter configurações EGL"
|
||||
|
||||
#: gdk/gdkdisplay.c:1612
|
||||
#: gdk/gdkdisplay.c:1613
|
||||
msgid "No EGL configuration with required features found"
|
||||
msgstr ""
|
||||
"Não foi localizada nenhuma configuração EGL com os recursos necessários"
|
||||
|
||||
#: gdk/gdkdisplay.c:1619
|
||||
#: gdk/gdkdisplay.c:1620
|
||||
msgid "No perfect EGL configuration found"
|
||||
msgstr "Nenhum configuração EGL perfeita localizada"
|
||||
|
||||
#: gdk/gdkdisplay.c:1661
|
||||
#: gdk/gdkdisplay.c:1662
|
||||
#, c-format
|
||||
msgid "EGL implementation is missing extension %s"
|
||||
msgid_plural "EGL implementation is missing %2$d extensions: %1$s"
|
||||
msgstr[0] "A implementação de EGL não contém a extensão %s"
|
||||
msgstr[1] "A implementação de EGL não contém %2$d extensões: %1$s"
|
||||
|
||||
#: gdk/gdkdisplay.c:1694
|
||||
#: gdk/gdkdisplay.c:1695
|
||||
msgid "libEGL not available in this sandbox"
|
||||
msgstr "libEGL não disponível nesta caixa de proteção"
|
||||
|
||||
#: gdk/gdkdisplay.c:1695
|
||||
#: gdk/gdkdisplay.c:1696
|
||||
msgid "libEGL not available"
|
||||
msgstr "libEGL não disponível"
|
||||
|
||||
#: gdk/gdkdisplay.c:1705
|
||||
#: gdk/gdkdisplay.c:1706
|
||||
msgid "Failed to create EGL display"
|
||||
msgstr "Falha ao criar tela EGL"
|
||||
|
||||
#: gdk/gdkdisplay.c:1715
|
||||
#: gdk/gdkdisplay.c:1716
|
||||
msgid "Could not initialize EGL display"
|
||||
msgstr "Não foi possível inicializar a tela EGL"
|
||||
|
||||
#: gdk/gdkdisplay.c:1726
|
||||
#: gdk/gdkdisplay.c:1727
|
||||
#, c-format
|
||||
msgid "EGL version %d.%d is too old. GTK requires %d.%d"
|
||||
msgstr "EGL versão %d.%d é muito velha. GTK requer %d.%d"
|
||||
@@ -1092,18 +1092,18 @@ msgctxt "progress bar label"
|
||||
msgid "%d %%"
|
||||
msgstr "%d %%"
|
||||
|
||||
#: gtk/deprecated/gtkcolorbutton.c:183 gtk/deprecated/gtkcolorbutton.c:314
|
||||
#: gtk/deprecated/gtkcolorbutton.c:183 gtk/deprecated/gtkcolorbutton.c:311
|
||||
#: gtk/gtkcolordialog.c:411
|
||||
msgid "Pick a Color"
|
||||
msgstr "Escolha uma cor"
|
||||
|
||||
#: gtk/deprecated/gtkcolorbutton.c:505 gtk/gtkcolorchooserwidget.c:313
|
||||
#: gtk/deprecated/gtkcolorbutton.c:502 gtk/gtkcolorchooserwidget.c:313
|
||||
#: gtk/gtkcolordialogbutton.c:335
|
||||
#, c-format
|
||||
msgid "Red %d%%, Green %d%%, Blue %d%%, Alpha %d%%"
|
||||
msgstr "Vermelho %d%%, Verde %d%%, Azul %d%%, Alfa %d%%"
|
||||
|
||||
#: gtk/deprecated/gtkcolorbutton.c:511 gtk/gtkcolorchooserwidget.c:319
|
||||
#: gtk/deprecated/gtkcolorbutton.c:508 gtk/gtkcolorchooserwidget.c:319
|
||||
#: gtk/gtkcolordialogbutton.c:341
|
||||
#, c-format
|
||||
msgid "Red %d%%, Green %d%%, Blue %d%%"
|
||||
@@ -1113,17 +1113,17 @@ msgstr "Vermelho %d%%, Verde %d%%, Azul %d%%"
|
||||
msgid "Sans 12"
|
||||
msgstr "Sans 12"
|
||||
|
||||
#: gtk/deprecated/gtkfontbutton.c:507 gtk/deprecated/gtkfontbutton.c:624
|
||||
#: gtk/deprecated/gtkfontbutton.c:507 gtk/deprecated/gtkfontbutton.c:621
|
||||
#: gtk/gtkfontdialog.c:596
|
||||
msgid "Pick a Font"
|
||||
msgstr "Selecione uma fonte"
|
||||
|
||||
#: gtk/deprecated/gtkfontbutton.c:600 gtk/gtkfilechooserwidget.c:3871
|
||||
#: gtk/deprecated/gtkfontbutton.c:597 gtk/gtkfilechooserwidget.c:3871
|
||||
#: gtk/gtkfontdialogbutton.c:126 gtk/inspector/visual.ui:169
|
||||
msgid "Font"
|
||||
msgstr "Fonte"
|
||||
|
||||
#: gtk/deprecated/gtkfontbutton.c:1155 gtk/gtkfontdialogbutton.c:652
|
||||
#: gtk/deprecated/gtkfontbutton.c:1152 gtk/gtkfontdialogbutton.c:652
|
||||
msgctxt "font"
|
||||
msgid "None"
|
||||
msgstr "Nenhuma"
|
||||
@@ -2191,7 +2191,7 @@ msgstr "_Direita:"
|
||||
msgid "Paper Margins"
|
||||
msgstr "Margens do papel"
|
||||
|
||||
#: gtk/gtkentry.c:3685
|
||||
#: gtk/gtkentry.c:3673
|
||||
msgid "Insert Emoji"
|
||||
msgstr "Inserir emoji"
|
||||
|
||||
@@ -2350,7 +2350,7 @@ msgid "If you delete an item, it will be permanently lost."
|
||||
msgstr "Se você excluir um item, ele será permanentemente perdido."
|
||||
|
||||
#: gtk/gtkfilechooserwidget.c:1185 gtk/gtkfilechooserwidget.c:1815
|
||||
#: gtk/gtklabel.c:5695 gtk/gtktext.c:6147 gtk/gtktextview.c:9018
|
||||
#: gtk/gtklabel.c:5695 gtk/gtktext.c:6145 gtk/gtktextview.c:9018
|
||||
msgid "_Delete"
|
||||
msgstr "E_xcluir"
|
||||
|
||||
@@ -2693,19 +2693,19 @@ msgstr "Fechar"
|
||||
msgid "Close the infobar"
|
||||
msgstr "Fecha a barra de informações"
|
||||
|
||||
#: gtk/gtklabel.c:5692 gtk/gtktext.c:6135 gtk/gtktextview.c:9006
|
||||
#: gtk/gtklabel.c:5692 gtk/gtktext.c:6133 gtk/gtktextview.c:9006
|
||||
msgid "Cu_t"
|
||||
msgstr "Recor_tar"
|
||||
|
||||
#: gtk/gtklabel.c:5693 gtk/gtktext.c:6139 gtk/gtktextview.c:9010
|
||||
#: gtk/gtklabel.c:5693 gtk/gtktext.c:6137 gtk/gtktextview.c:9010
|
||||
msgid "_Copy"
|
||||
msgstr "_Copiar"
|
||||
|
||||
#: gtk/gtklabel.c:5694 gtk/gtktext.c:6143 gtk/gtktextview.c:9014
|
||||
#: gtk/gtklabel.c:5694 gtk/gtktext.c:6141 gtk/gtktextview.c:9014
|
||||
msgid "_Paste"
|
||||
msgstr "C_olar"
|
||||
|
||||
#: gtk/gtklabel.c:5700 gtk/gtktext.c:6156 gtk/gtktextview.c:9039
|
||||
#: gtk/gtklabel.c:5700 gtk/gtktext.c:6154 gtk/gtktextview.c:9039
|
||||
msgid "Select _All"
|
||||
msgstr "_Selecionar tudo"
|
||||
|
||||
@@ -3569,7 +3569,7 @@ msgstr ""
|
||||
"Não foi encontrado nenhum aplicativo registrado com o nome “%s” para o item "
|
||||
"com URI “%s”"
|
||||
|
||||
#: gtk/gtksearchentry.c:767
|
||||
#: gtk/gtksearchentry.c:758
|
||||
msgid "Clear Entry"
|
||||
msgstr "Limpar entrada"
|
||||
|
||||
@@ -3682,7 +3682,7 @@ msgctxt "accessibility"
|
||||
msgid "Sidebar"
|
||||
msgstr "Barra lateral"
|
||||
|
||||
#: gtk/gtktext.c:6161 gtk/gtktextview.c:9044
|
||||
#: gtk/gtktext.c:6159 gtk/gtktextview.c:9044
|
||||
msgid "Insert _Emoji"
|
||||
msgstr "Inserir _emoji"
|
||||
|
||||
@@ -4075,8 +4075,8 @@ msgid "Surface"
|
||||
msgstr "Superfície"
|
||||
|
||||
#: gtk/inspector/misc-info.ui:365 gtk/inspector/misc-info.ui:400
|
||||
#: gtk/inspector/misc-info.ui:435 gtk/inspector/prop-editor.c:1153
|
||||
#: gtk/inspector/prop-editor.c:1536 gtk/inspector/window.ui:396
|
||||
#: gtk/inspector/misc-info.ui:435 gtk/inspector/prop-editor.c:1150
|
||||
#: gtk/inspector/prop-editor.c:1533 gtk/inspector/window.ui:396
|
||||
msgid "Properties"
|
||||
msgstr "Propriedades"
|
||||
|
||||
@@ -4128,7 +4128,7 @@ msgstr "Ponteiro: %p"
|
||||
#. Translators: %s is a type name, for example
|
||||
#. * GtkPropertyExpression with value \"2.5\"
|
||||
#.
|
||||
#: gtk/inspector/prop-editor.c:827
|
||||
#: gtk/inspector/prop-editor.c:824
|
||||
#, c-format
|
||||
msgid "%s with value \"%s\""
|
||||
msgstr "%s com valor “%s”"
|
||||
@@ -4136,7 +4136,7 @@ msgstr "%s com valor “%s”"
|
||||
#. Translators: Both %s are type names, for example
|
||||
#. * GtkPropertyExpression with type GObject
|
||||
#.
|
||||
#: gtk/inspector/prop-editor.c:838
|
||||
#: gtk/inspector/prop-editor.c:835
|
||||
#, c-format
|
||||
msgid "%s with type %s"
|
||||
msgstr "%s com tipo %s"
|
||||
@@ -4144,7 +4144,7 @@ msgstr "%s com tipo %s"
|
||||
#. Translators: Both %s are type names, for example
|
||||
#. * GtkObjectExpression for GtkStringObject 0x23456789
|
||||
#.
|
||||
#: gtk/inspector/prop-editor.c:851
|
||||
#: gtk/inspector/prop-editor.c:848
|
||||
#, c-format
|
||||
msgid "%s for %s %p"
|
||||
msgstr "%s para %s %p"
|
||||
@@ -4152,71 +4152,71 @@ msgstr "%s para %s %p"
|
||||
#. Translators: Both %s are type names, for example
|
||||
#. * GtkPropertyExpression with value type: gchararray
|
||||
#.
|
||||
#: gtk/inspector/prop-editor.c:881
|
||||
#: gtk/inspector/prop-editor.c:878
|
||||
#, c-format
|
||||
msgid "%s with value type %s"
|
||||
msgstr "%s com tipo de valor %s"
|
||||
|
||||
#: gtk/inspector/prop-editor.c:1230
|
||||
#: gtk/inspector/prop-editor.c:1227
|
||||
#, c-format
|
||||
msgid "Uneditable property type: %s"
|
||||
msgstr "Tipo de propriedade não editável: %s"
|
||||
|
||||
#: gtk/inspector/prop-editor.c:1388
|
||||
#: gtk/inspector/prop-editor.c:1385
|
||||
msgctxt "column number"
|
||||
msgid "None"
|
||||
msgstr "Nenhum"
|
||||
|
||||
#: gtk/inspector/prop-editor.c:1425
|
||||
#: gtk/inspector/prop-editor.c:1422
|
||||
msgid "Attribute:"
|
||||
msgstr "Atributo:"
|
||||
|
||||
#: gtk/inspector/prop-editor.c:1428
|
||||
#: gtk/inspector/prop-editor.c:1425
|
||||
msgid "Model"
|
||||
msgstr "Modelo"
|
||||
|
||||
#: gtk/inspector/prop-editor.c:1433
|
||||
#: gtk/inspector/prop-editor.c:1430
|
||||
msgid "Column:"
|
||||
msgstr "Coluna:"
|
||||
|
||||
#. Translators: %s is a type name, for example
|
||||
#. * Action from 0x2345678 (GtkApplicationWindow)
|
||||
#.
|
||||
#: gtk/inspector/prop-editor.c:1532
|
||||
#: gtk/inspector/prop-editor.c:1529
|
||||
#, c-format
|
||||
msgid "Action from: %p (%s)"
|
||||
msgstr "Ação de: %p (%s)"
|
||||
|
||||
#: gtk/inspector/prop-editor.c:1587
|
||||
#: gtk/inspector/prop-editor.c:1584
|
||||
msgid "Reset"
|
||||
msgstr "Redefinir"
|
||||
|
||||
#: gtk/inspector/prop-editor.c:1595
|
||||
#: gtk/inspector/prop-editor.c:1592
|
||||
msgctxt "GtkSettings source"
|
||||
msgid "Default"
|
||||
msgstr "Padrão"
|
||||
|
||||
#: gtk/inspector/prop-editor.c:1598
|
||||
#: gtk/inspector/prop-editor.c:1595
|
||||
msgctxt "GtkSettings source"
|
||||
msgid "Theme"
|
||||
msgstr "Tema"
|
||||
|
||||
#: gtk/inspector/prop-editor.c:1601
|
||||
#: gtk/inspector/prop-editor.c:1598
|
||||
msgctxt "GtkSettings source"
|
||||
msgid "XSettings"
|
||||
msgstr "XSettings"
|
||||
|
||||
#: gtk/inspector/prop-editor.c:1605
|
||||
#: gtk/inspector/prop-editor.c:1602
|
||||
msgctxt "GtkSettings source"
|
||||
msgid "Application"
|
||||
msgstr "Aplicativo"
|
||||
|
||||
#: gtk/inspector/prop-editor.c:1608
|
||||
#: gtk/inspector/prop-editor.c:1605
|
||||
msgctxt "GtkSettings source"
|
||||
msgid "Unknown"
|
||||
msgstr "Desconhecido"
|
||||
|
||||
#: gtk/inspector/prop-editor.c:1611
|
||||
#: gtk/inspector/prop-editor.c:1608
|
||||
msgid "Source:"
|
||||
msgstr "Fonte:"
|
||||
|
||||
@@ -7294,7 +7294,7 @@ msgstr ""
|
||||
#: tools/gtk-builder-tool-enumerate.c:56 tools/gtk-builder-tool-preview.c:179
|
||||
#: tools/gtk-builder-tool-preview.c:180 tools/gtk-builder-tool-screenshot.c:360
|
||||
#: tools/gtk-builder-tool-simplify.c:2529 tools/gtk-builder-tool-validate.c:261
|
||||
#: tools/gtk-rendernode-tool-info.c:202 tools/gtk-rendernode-tool-show.c:102
|
||||
#: tools/gtk-rendernode-tool-info.c:200 tools/gtk-rendernode-tool-show.c:102
|
||||
msgid "FILE"
|
||||
msgstr "ARQUIVO"
|
||||
|
||||
@@ -7580,37 +7580,37 @@ msgstr ""
|
||||
" show Mostra o nó\n"
|
||||
" render Faz uma captura de tela do nó\n"
|
||||
|
||||
#: tools/gtk-rendernode-tool-info.c:179
|
||||
#: tools/gtk-rendernode-tool-info.c:177
|
||||
#, c-format
|
||||
msgid "Number of nodes: %u\n"
|
||||
msgstr "Número de nós: %u\n"
|
||||
|
||||
#: tools/gtk-rendernode-tool-info.c:186
|
||||
#: tools/gtk-rendernode-tool-info.c:184
|
||||
#, c-format
|
||||
msgid "Depth: %u\n"
|
||||
msgstr "Profundidade: %u\n"
|
||||
|
||||
#: tools/gtk-rendernode-tool-info.c:189
|
||||
#: tools/gtk-rendernode-tool-info.c:187
|
||||
#, c-format
|
||||
msgid "Bounds: %g x %g\n"
|
||||
msgstr "Limites: %g x %g\n"
|
||||
|
||||
#: tools/gtk-rendernode-tool-info.c:190
|
||||
#: tools/gtk-rendernode-tool-info.c:188
|
||||
#, c-format
|
||||
msgid "Origin: %g %g\n"
|
||||
msgstr "Origem: %g %g\n"
|
||||
|
||||
#: tools/gtk-rendernode-tool-info.c:211
|
||||
#: tools/gtk-rendernode-tool-info.c:209
|
||||
msgid "Provide information about the render node."
|
||||
msgstr "Fornece informações sobre o nó de renderização."
|
||||
|
||||
#: tools/gtk-rendernode-tool-info.c:224 tools/gtk-rendernode-tool-show.c:130
|
||||
#: tools/gtk-rendernode-tool-info.c:222 tools/gtk-rendernode-tool-show.c:130
|
||||
#: tools/gtk-rendernode-tool-render.c:225
|
||||
#, c-format
|
||||
msgid "No .node file specified\n"
|
||||
msgstr "Nenhum arquivo .node especificado\n"
|
||||
|
||||
#: tools/gtk-rendernode-tool-info.c:230
|
||||
#: tools/gtk-rendernode-tool-info.c:228
|
||||
#, c-format
|
||||
msgid "Can only accept a single .node file\n"
|
||||
msgstr "Pode aceitar apenas um único arquivo .node\n"
|
||||
@@ -7661,11 +7661,6 @@ msgstr ""
|
||||
msgid "Error at %s: %s\n"
|
||||
msgstr "Erro em %s: %s\n"
|
||||
|
||||
#: tools/gtk-rendernode-tool-utils.c:69
|
||||
#, c-format
|
||||
msgid "Failed to load node file: %s\n"
|
||||
msgstr "Falha ao carregar o arquivo de nó: %s\n"
|
||||
|
||||
#: tools/updateiconcache.c:1391
|
||||
#, c-format
|
||||
msgid "Failed to write header\n"
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
[wrap-git]
|
||||
directory=glyphy
|
||||
url=https://github.com/behdad/glyphy.git
|
||||
revision=master
|
||||
depth=1
|
||||
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define DEMO_TYPE_WIDGET (demo_widget_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (DemoWidget, demo_widget, DEMO, WIDGET, GtkWidget)
|
||||
|
||||
struct _DemoWidget
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
};
|
||||
|
||||
struct _DemoWidgetClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (DemoWidget, demo_widget, GTK_TYPE_WIDGET)
|
||||
|
||||
static void
|
||||
demo_widget_init (DemoWidget *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
demo_widget_snapshot (GtkWidget *widget,
|
||||
GtkSnapshot *snapshot)
|
||||
{
|
||||
DemoWidget *self = DEMO_WIDGET (widget);
|
||||
PangoLayout *layout;
|
||||
int width, height;
|
||||
int pwidth, pheight;
|
||||
PangoFontDescription *desc;
|
||||
int size;
|
||||
double scale;
|
||||
int x, y;
|
||||
GdkRGBA color;
|
||||
|
||||
width = gtk_widget_get_width (widget);
|
||||
height = gtk_widget_get_height (widget);
|
||||
|
||||
gtk_widget_get_color (widget, &color);
|
||||
|
||||
layout = gtk_widget_create_pango_layout (GTK_WIDGET (self), "Best Aa");
|
||||
|
||||
pango_layout_get_pixel_size (layout, &pwidth, &pheight);
|
||||
desc = pango_font_description_copy_static (pango_context_get_font_description (pango_layout_get_context (layout)));
|
||||
size = pango_font_description_get_size (desc);
|
||||
|
||||
scale = MIN (width / (double)pwidth, height / (double)pheight);
|
||||
|
||||
pango_font_description_set_size (desc, size * scale * 0.5);
|
||||
pango_layout_set_font_description (layout, desc);
|
||||
pango_font_description_free (desc);
|
||||
|
||||
pango_layout_get_pixel_size (layout, &pwidth, &pheight);
|
||||
|
||||
x = floor ((width - pwidth) / 2);
|
||||
y = floor ((height - pheight) / 2);
|
||||
|
||||
gtk_snapshot_save (snapshot);
|
||||
|
||||
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (x, y));
|
||||
|
||||
gtk_snapshot_append_layout (snapshot, layout, &color);
|
||||
|
||||
gtk_snapshot_restore (snapshot);
|
||||
|
||||
g_object_unref (layout);
|
||||
}
|
||||
|
||||
static void
|
||||
demo_widget_dispose (GObject *object)
|
||||
{
|
||||
G_OBJECT_CLASS (demo_widget_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
demo_widget_class_init (DemoWidgetClass *class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
|
||||
|
||||
object_class->dispose = demo_widget_dispose;
|
||||
|
||||
widget_class->snapshot = demo_widget_snapshot;
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
demo_widget_new (void)
|
||||
{
|
||||
return g_object_new (DEMO_TYPE_WIDGET, NULL);
|
||||
}
|
||||
|
||||
static const char css[] =
|
||||
"* {\n"
|
||||
" font-family: Cantarell;\n"
|
||||
" font-weight: 520;\n"
|
||||
"}";
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GtkCssProvider *style;
|
||||
GtkWidget *window;
|
||||
|
||||
gtk_init ();
|
||||
|
||||
style = gtk_css_provider_new ();
|
||||
gtk_css_provider_load_from_string (style, css);
|
||||
gtk_style_context_add_provider_for_display (gdk_display_get_default (),
|
||||
GTK_STYLE_PROVIDER (style),
|
||||
800);
|
||||
|
||||
window = gtk_window_new ();
|
||||
|
||||
gtk_window_set_child (GTK_WINDOW (window), demo_widget_new ());
|
||||
|
||||
gtk_window_present (GTK_WINDOW (window));
|
||||
|
||||
while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0)
|
||||
g_main_context_iteration (NULL, TRUE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
gtk_tests = [
|
||||
# testname, optional extra sources
|
||||
['movingtext'],
|
||||
['bigfont'],
|
||||
['testsections'],
|
||||
['testfilelauncher'],
|
||||
['input'],
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define DEMO_TYPE_WIDGET (demo_widget_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (DemoWidget, demo_widget, DEMO, WIDGET, GtkWidget)
|
||||
|
||||
struct _DemoWidget
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
|
||||
guint tick_cb;
|
||||
guint64 start_time;
|
||||
guint64 stop_time;
|
||||
|
||||
char *text;
|
||||
|
||||
float angle;
|
||||
float size;
|
||||
};
|
||||
|
||||
struct _DemoWidgetClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (DemoWidget, demo_widget, GTK_TYPE_WIDGET)
|
||||
|
||||
static gboolean
|
||||
tick_cb (GtkWidget *widget,
|
||||
GdkFrameClock *frame_clock,
|
||||
gpointer user_data)
|
||||
{
|
||||
DemoWidget *self = DEMO_WIDGET (widget);
|
||||
guint64 now;
|
||||
|
||||
now = gdk_frame_clock_get_frame_time (frame_clock);
|
||||
|
||||
if (self->start_time == 0)
|
||||
self->start_time = now;
|
||||
|
||||
self->angle = 360 * (now - self->start_time) / (double)(G_TIME_SPAN_SECOND * 10);
|
||||
self->size = 150 + 130 * sin (2 * G_PI * (now - self->start_time) / (double)(G_TIME_SPAN_SECOND * 5));
|
||||
|
||||
gtk_widget_queue_draw (widget);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
pressed_cb (GtkEventController *controller,
|
||||
guint keyval,
|
||||
guint keycode,
|
||||
GdkModifierType state,
|
||||
gpointer data)
|
||||
{
|
||||
DemoWidget *self = (DemoWidget *)gtk_event_controller_get_widget (controller);
|
||||
|
||||
if (keyval == GDK_KEY_space)
|
||||
{
|
||||
GdkFrameClock *frame_clock;
|
||||
guint64 now;
|
||||
|
||||
frame_clock = gtk_widget_get_frame_clock (GTK_WIDGET (self));
|
||||
now = gdk_frame_clock_get_frame_time (frame_clock);
|
||||
|
||||
if (self->tick_cb)
|
||||
{
|
||||
gtk_widget_remove_tick_callback (GTK_WIDGET (self), self->tick_cb);
|
||||
self->tick_cb = 0;
|
||||
self->stop_time = now;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->start_time += now - self->stop_time;
|
||||
self->tick_cb = gtk_widget_add_tick_callback (GTK_WIDGET (self), tick_cb, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
demo_widget_init (DemoWidget *self)
|
||||
{
|
||||
GtkEventController *controller;
|
||||
|
||||
self->start_time = 0;
|
||||
self->tick_cb = gtk_widget_add_tick_callback (GTK_WIDGET (self), tick_cb, NULL, NULL);
|
||||
|
||||
controller = gtk_event_controller_key_new ();
|
||||
g_signal_connect (controller, "key-pressed", G_CALLBACK (pressed_cb), NULL);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), controller);
|
||||
gtk_widget_set_focusable (GTK_WIDGET (self), TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
demo_widget_snapshot (GtkWidget *widget,
|
||||
GtkSnapshot *snapshot)
|
||||
{
|
||||
DemoWidget *self = DEMO_WIDGET (widget);
|
||||
PangoLayout *layout;
|
||||
int width, height;
|
||||
int pwidth, pheight;
|
||||
PangoFontDescription *desc;
|
||||
GdkRGBA color;
|
||||
|
||||
width = gtk_widget_get_width (widget);
|
||||
height = gtk_widget_get_height (widget);
|
||||
|
||||
gtk_widget_get_color (widget, &color);
|
||||
|
||||
layout = gtk_widget_create_pango_layout (GTK_WIDGET (self), self->text);
|
||||
desc = pango_font_description_new ();
|
||||
pango_font_description_set_family (desc, "Cantarell");
|
||||
pango_font_description_set_weight (desc, 520);
|
||||
pango_font_description_set_size (desc, self->size * PANGO_SCALE);
|
||||
pango_layout_set_font_description (layout, desc);
|
||||
pango_font_description_free (desc);
|
||||
pango_layout_get_pixel_size (layout, &pwidth, &pheight);
|
||||
|
||||
gtk_snapshot_save (snapshot);
|
||||
|
||||
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (0.5 * width, 0.5 * height));
|
||||
gtk_snapshot_rotate (snapshot, self->angle);
|
||||
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (- 0.5 * pwidth, - 0.5 * pheight));
|
||||
|
||||
gtk_snapshot_append_layout (snapshot, layout, &color);
|
||||
|
||||
gtk_snapshot_restore (snapshot);
|
||||
|
||||
g_object_unref (layout);
|
||||
}
|
||||
|
||||
static void
|
||||
demo_widget_dispose (GObject *object)
|
||||
{
|
||||
DemoWidget *self = DEMO_WIDGET (object);
|
||||
|
||||
g_free (self->text);
|
||||
|
||||
G_OBJECT_CLASS (demo_widget_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
demo_widget_class_init (DemoWidgetClass *class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
|
||||
|
||||
object_class->dispose = demo_widget_dispose;
|
||||
|
||||
widget_class->snapshot = demo_widget_snapshot;
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
demo_widget_new (const char *text,
|
||||
gsize length)
|
||||
{
|
||||
DemoWidget *demo;
|
||||
|
||||
demo = g_object_new (DEMO_TYPE_WIDGET, NULL);
|
||||
|
||||
demo->text = g_strndup (text, length);
|
||||
|
||||
return GTK_WIDGET (demo);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GtkWidget *window;
|
||||
GtkWidget *demo;
|
||||
char *text = NULL;
|
||||
gsize length;
|
||||
|
||||
gtk_init ();
|
||||
|
||||
window = gtk_window_new ();
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!g_file_get_contents (argv[1], &text, &length, &error))
|
||||
{
|
||||
g_warning ("%s", error->message);
|
||||
g_error_free (error);
|
||||
text = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!text)
|
||||
{
|
||||
text = g_strdup ("Best Aa");
|
||||
length = strlen (text);
|
||||
}
|
||||
|
||||
demo = demo_widget_new (text, length);
|
||||
gtk_window_set_child (GTK_WINDOW (window), demo);
|
||||
|
||||
g_free (text);
|
||||
|
||||
gtk_window_present (GTK_WINDOW (window));
|
||||
|
||||
gtk_widget_grab_focus (demo);
|
||||
|
||||
while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0)
|
||||
g_main_context_iteration (NULL, TRUE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,551 @@
|
||||
#include <gtk/gtk.h>
|
||||
#include "gsk/gskcurveprivate.h"
|
||||
|
||||
static void
|
||||
test_line_line_intersection (void)
|
||||
{
|
||||
GskCurve c1, c2;
|
||||
graphene_point_t p1[2], p2[2];
|
||||
float t1, t2;
|
||||
graphene_point_t p;
|
||||
GskPathIntersection kind;
|
||||
int n;
|
||||
|
||||
graphene_point_init (&p1[0], 10, 0);
|
||||
graphene_point_init (&p1[1], 10, 100);
|
||||
graphene_point_init (&p2[0], 0, 10);
|
||||
graphene_point_init (&p2[1], 100, 10);
|
||||
|
||||
gsk_curve_init (&c1, gsk_pathop_encode (GSK_PATH_LINE, p1));
|
||||
gsk_curve_init (&c2, gsk_pathop_encode (GSK_PATH_LINE, p2));
|
||||
|
||||
n = gsk_curve_intersect (&c1, &c2, &t1, &t2, &p, &kind, 1);
|
||||
|
||||
g_assert_cmpint (n, ==, 1);
|
||||
g_assert_cmpfloat_with_epsilon (t1, 0.1, 0.0001);
|
||||
g_assert_cmpfloat_with_epsilon (t2, 0.1, 0.0001);
|
||||
g_assert_true (graphene_point_near (&p, &GRAPHENE_POINT_INIT (10, 10), 0.0001));
|
||||
}
|
||||
|
||||
static void
|
||||
test_line_line_end_intersection (void)
|
||||
{
|
||||
GskCurve c1, c2;
|
||||
graphene_point_t p1[2], p2[2];
|
||||
float t1, t2;
|
||||
graphene_point_t p;
|
||||
GskPathIntersection kind;
|
||||
int n;
|
||||
|
||||
graphene_point_init (&p1[0], 10, 0);
|
||||
graphene_point_init (&p1[1], 10, 100);
|
||||
graphene_point_init (&p2[0], 10, 100);
|
||||
graphene_point_init (&p2[1], 100, 10);
|
||||
|
||||
gsk_curve_init (&c1, gsk_pathop_encode (GSK_PATH_LINE, p1));
|
||||
gsk_curve_init (&c2, gsk_pathop_encode (GSK_PATH_LINE, p2));
|
||||
|
||||
n = gsk_curve_intersect (&c1, &c2, &t1, &t2, &p, &kind, 1);
|
||||
|
||||
g_assert_cmpint (n, ==, 1);
|
||||
g_assert_cmpfloat_with_epsilon (t1, 1, 0.0001);
|
||||
g_assert_cmpfloat_with_epsilon (t2, 0, 0.0001);
|
||||
g_assert_true (graphene_point_near (&p, &GRAPHENE_POINT_INIT (10, 100), 0.0001));
|
||||
}
|
||||
|
||||
static void
|
||||
test_line_line_none_intersection (void)
|
||||
{
|
||||
GskCurve c1, c2;
|
||||
graphene_point_t p1[2], p2[2];
|
||||
float t1, t2;
|
||||
graphene_point_t p;
|
||||
GskPathIntersection kind;
|
||||
int n;
|
||||
|
||||
graphene_point_init (&p1[0], 0, 0);
|
||||
graphene_point_init (&p1[1], 10, 0);
|
||||
graphene_point_init (&p2[0], 20, 0);
|
||||
graphene_point_init (&p2[1], 30, 0);
|
||||
|
||||
gsk_curve_init (&c1, gsk_pathop_encode (GSK_PATH_LINE, p1));
|
||||
gsk_curve_init (&c2, gsk_pathop_encode (GSK_PATH_LINE, p2));
|
||||
|
||||
n = gsk_curve_intersect (&c1, &c2, &t1, &t2, &p, &kind, 1);
|
||||
|
||||
g_assert_cmpint (n, ==, 0);
|
||||
|
||||
graphene_point_init (&p1[0], 247.103424, 95.7965317);
|
||||
graphene_point_init (&p1[1], 205.463974, 266.758484);
|
||||
graphene_point_init (&p2[0], 183.735962, 355.968689);
|
||||
graphene_point_init (&p2[1], 121.553253, 611.27655);
|
||||
|
||||
gsk_curve_init (&c1, gsk_pathop_encode (GSK_PATH_LINE, p1));
|
||||
gsk_curve_init (&c2, gsk_pathop_encode (GSK_PATH_LINE, p2));
|
||||
|
||||
n = gsk_curve_intersect (&c1, &c2, &t1, &t2, &p, &kind, 1);
|
||||
|
||||
g_assert_cmpint (n, ==, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
test_line_line_parallel (void)
|
||||
{
|
||||
GskCurve c1, c2;
|
||||
graphene_point_t p1[2], p2[2];
|
||||
float t1[2], t2[2];
|
||||
graphene_point_t p[2];
|
||||
GskPathIntersection kind[2];
|
||||
int n;
|
||||
|
||||
graphene_point_init (&p1[0], 10, 10);
|
||||
graphene_point_init (&p1[1], 110, 10);
|
||||
graphene_point_init (&p2[0], 20, 10);
|
||||
graphene_point_init (&p2[1], 120, 10);
|
||||
|
||||
gsk_curve_init (&c1, gsk_pathop_encode (GSK_PATH_LINE, p1));
|
||||
gsk_curve_init (&c2, gsk_pathop_encode (GSK_PATH_LINE, p2));
|
||||
|
||||
n = gsk_curve_intersect (&c1, &c2, t1, t2, p, kind, 2);
|
||||
|
||||
g_assert_cmpint (n, ==, 2);
|
||||
g_assert_cmpfloat_with_epsilon (t1[0], 0.1f, 0.01);
|
||||
g_assert_cmpfloat_with_epsilon (t1[1], 1.f, 0.01);
|
||||
g_assert_cmpfloat_with_epsilon (t2[0], 0.f, 0.01);
|
||||
g_assert_cmpfloat_with_epsilon (t2[1], 0.9f, 0.01);
|
||||
}
|
||||
|
||||
static void
|
||||
test_line_line_same (void)
|
||||
{
|
||||
GskCurve c1, c2;
|
||||
graphene_point_t p1[2], p2[2];
|
||||
float t1[2], t2[2];
|
||||
graphene_point_t p[2];
|
||||
GskPathIntersection kind[2];
|
||||
int n;
|
||||
|
||||
graphene_point_init (&p1[0], 10, 10);
|
||||
graphene_point_init (&p1[1], 100, 10);
|
||||
graphene_point_init (&p2[0], 10, 10);
|
||||
graphene_point_init (&p2[1], 100, 10);
|
||||
|
||||
gsk_curve_init (&c1, gsk_pathop_encode (GSK_PATH_LINE, p1));
|
||||
gsk_curve_init (&c2, gsk_pathop_encode (GSK_PATH_LINE, p2));
|
||||
|
||||
n = gsk_curve_intersect (&c1, &c2, t1, t2, p, kind, 2);
|
||||
|
||||
g_assert_cmpint (n, ==, 2);
|
||||
g_assert_cmpfloat_with_epsilon (t1[0], 0.f, 0.01);
|
||||
g_assert_cmpfloat_with_epsilon (t1[1], 1.f, 0.01);
|
||||
g_assert_cmpfloat_with_epsilon (t2[0], 0.f, 0.01);
|
||||
g_assert_cmpfloat_with_epsilon (t2[1], 1.f, 0.01);
|
||||
}
|
||||
|
||||
static void
|
||||
test_line_curve_intersection (void)
|
||||
{
|
||||
GskCurve c1, c2;
|
||||
graphene_point_t p1[4], p2[2];
|
||||
float t1[9], t2[9];
|
||||
graphene_point_t p[9];
|
||||
GskPathIntersection kind[9];
|
||||
int n;
|
||||
GskBoundingBox b;
|
||||
|
||||
graphene_point_init (&p1[0], 0, 100);
|
||||
graphene_point_init (&p1[1], 50, 100);
|
||||
graphene_point_init (&p1[2], 50, 0);
|
||||
graphene_point_init (&p1[3], 100, 0);
|
||||
graphene_point_init (&p2[0], 0, 0);
|
||||
graphene_point_init (&p2[1], 100, 100);
|
||||
|
||||
gsk_curve_init (&c1, gsk_pathop_encode (GSK_PATH_CUBIC, p1));
|
||||
gsk_curve_init (&c2, gsk_pathop_encode (GSK_PATH_LINE, p2));
|
||||
|
||||
n = gsk_curve_intersect (&c1, &c2, t1, t2, p, kind, 1);
|
||||
|
||||
g_assert_cmpint (n, ==, 1);
|
||||
g_assert_cmpfloat_with_epsilon (t1[0], 0.5, 0.0001);
|
||||
g_assert_cmpfloat_with_epsilon (t2[0], 0.5, 0.0001);
|
||||
g_assert_true (graphene_point_near (&p[0], &GRAPHENE_POINT_INIT (50, 50), 0.0001));
|
||||
|
||||
gsk_curve_get_tight_bounds (&c1, &b);
|
||||
gsk_bounding_box_contains_point (&b, &p[0]);
|
||||
|
||||
gsk_curve_get_tight_bounds (&c2, &b);
|
||||
gsk_bounding_box_contains_point (&b, &p[0]);
|
||||
}
|
||||
|
||||
static void
|
||||
test_line_curve_multiple_intersection (void)
|
||||
{
|
||||
GskCurve c1, c2;
|
||||
graphene_point_t p1[4], p2[2];
|
||||
float t1[9], t2[9];
|
||||
graphene_point_t p[9];
|
||||
GskPathIntersection kind[9];
|
||||
graphene_point_t pp;
|
||||
int n;
|
||||
GskBoundingBox b1, b2;
|
||||
|
||||
graphene_point_init (&p1[0], 100, 200);
|
||||
graphene_point_init (&p1[1], 350, 100);
|
||||
graphene_point_init (&p1[2], 100, 350);
|
||||
graphene_point_init (&p1[3], 400, 300);
|
||||
|
||||
graphene_point_init (&p2[0], 0, 0);
|
||||
graphene_point_init (&p2[1], 100, 100);
|
||||
|
||||
gsk_curve_init (&c1, gsk_pathop_encode (GSK_PATH_CUBIC, p1));
|
||||
gsk_curve_init (&c2, gsk_pathop_encode (GSK_PATH_LINE, p2));
|
||||
|
||||
n = gsk_curve_intersect (&c1, &c2, t1, t2, p, kind, 3);
|
||||
|
||||
g_assert_cmpint (n, ==, 0);
|
||||
|
||||
graphene_point_init (&p2[0], 0, 0);
|
||||
graphene_point_init (&p2[1], 200, 200);
|
||||
|
||||
gsk_curve_init (&c1, gsk_pathop_encode (GSK_PATH_CUBIC, p1));
|
||||
gsk_curve_init (&c2, gsk_pathop_encode (GSK_PATH_LINE, p2));
|
||||
|
||||
n = gsk_curve_intersect (&c1, &c2, t1, t2, p, kind, 3);
|
||||
|
||||
g_assert_cmpint (n, ==, 1);
|
||||
|
||||
g_assert_cmpfloat_with_epsilon (t1[0], 0.136196628, 0.0001);
|
||||
g_assert_cmpfloat_with_epsilon (t2[0], 0.88487947, 0.0001);
|
||||
g_assert_true (graphene_point_near (&p[0], &GRAPHENE_POINT_INIT (176.975891, 176.975891), 0.001));
|
||||
|
||||
gsk_curve_get_point (&c1, t1[0], &pp);
|
||||
g_assert_true (graphene_point_near (&p[0], &pp, 0.001));
|
||||
|
||||
gsk_curve_get_point (&c2, t2[0], &pp);
|
||||
g_assert_true (graphene_point_near (&p[0], &pp, 0.001));
|
||||
|
||||
gsk_curve_get_tight_bounds (&c1, &b1);
|
||||
gsk_curve_get_tight_bounds (&c2, &b2);
|
||||
|
||||
gsk_bounding_box_contains_point (&b1, &p[0]);
|
||||
gsk_bounding_box_contains_point (&b2, &p[0]);
|
||||
|
||||
graphene_point_init (&p2[0], 0, 0);
|
||||
graphene_point_init (&p2[1], 280, 280);
|
||||
|
||||
gsk_curve_init (&c1, gsk_pathop_encode (GSK_PATH_CUBIC, p1));
|
||||
gsk_curve_init (&c2, gsk_pathop_encode (GSK_PATH_LINE, p2));
|
||||
|
||||
n = gsk_curve_intersect (&c1, &c2, t1, t2, p, kind, 3);
|
||||
|
||||
g_assert_cmpint (n, ==, 2);
|
||||
|
||||
g_assert_cmpfloat_with_epsilon (t1[0], 0.136196628, 0.0001);
|
||||
g_assert_cmpfloat_with_epsilon (t2[0], 0.632056773, 0.0001);
|
||||
g_assert_true (graphene_point_near (&p[0], &GRAPHENE_POINT_INIT (176.975891, 176.975891), 0.001));
|
||||
|
||||
gsk_curve_get_point (&c1, t1[0], &pp);
|
||||
g_assert_true (graphene_point_near (&p[0], &pp, 0.001));
|
||||
|
||||
gsk_curve_get_point (&c2, t2[0], &pp);
|
||||
g_assert_true (graphene_point_near (&p[0], &pp, 0.001));
|
||||
|
||||
g_assert_cmpfloat_with_epsilon (t1[1], 0.499999911, 0.0001);
|
||||
g_assert_cmpfloat_with_epsilon (t2[1], 0.825892806, 0.0001);
|
||||
g_assert_true (graphene_point_near (&p[1], &GRAPHENE_POINT_INIT (231.25, 231.25), 0.001));
|
||||
|
||||
gsk_curve_get_point (&c1, t1[1], &pp);
|
||||
g_assert_true (graphene_point_near (&p[1], &pp, 0.001));
|
||||
|
||||
gsk_curve_get_point (&c2, t2[1], &pp);
|
||||
g_assert_true (graphene_point_near (&p[1], &pp, 0.001));
|
||||
|
||||
gsk_curve_get_tight_bounds (&c1, &b1);
|
||||
gsk_curve_get_tight_bounds (&c2, &b2);
|
||||
|
||||
gsk_bounding_box_contains_point (&b1, &p[0]);
|
||||
gsk_bounding_box_contains_point (&b1, &p[1]);
|
||||
gsk_bounding_box_contains_point (&b2, &p[0]);
|
||||
gsk_bounding_box_contains_point (&b2, &p[1]);
|
||||
|
||||
graphene_point_init (&p2[0], 0, 0);
|
||||
graphene_point_init (&p2[1], 1000, 1000);
|
||||
|
||||
gsk_curve_init (&c1, gsk_pathop_encode (GSK_PATH_CUBIC, p1));
|
||||
gsk_curve_init (&c2, gsk_pathop_encode (GSK_PATH_LINE, p2));
|
||||
|
||||
n = gsk_curve_intersect (&c1, &c2, t1, t2, p, kind, 3);
|
||||
|
||||
g_assert_cmpint (n, ==, 3);
|
||||
|
||||
g_assert_cmpfloat_with_epsilon (t1[0], 0.863803446, 0.0001);
|
||||
g_assert_cmpfloat_with_epsilon (t2[0], 0.305377066, 0.0001);
|
||||
g_assert_true (graphene_point_near (&p[0], &GRAPHENE_POINT_INIT (305.377075, 305.377075), 0.001));
|
||||
|
||||
gsk_curve_get_point (&c1, t1[0], &pp);
|
||||
g_assert_true (graphene_point_near (&p[0], &pp, 0.001));
|
||||
|
||||
gsk_curve_get_point (&c2, t2[0], &pp);
|
||||
g_assert_true (graphene_point_near (&p[0], &pp, 0.001));
|
||||
|
||||
g_assert_cmpfloat_with_epsilon (t1[1], 0.136196628, 0.0001);
|
||||
g_assert_cmpfloat_with_epsilon (t2[1], 0.176975891, 0.0001);
|
||||
g_assert_true (graphene_point_near (&p[1], &GRAPHENE_POINT_INIT (176.975891, 176.975891), 0.001));
|
||||
|
||||
gsk_curve_get_point (&c1, t1[1], &pp);
|
||||
g_assert_true (graphene_point_near (&p[1], &pp, 0.001));
|
||||
|
||||
gsk_curve_get_point (&c2, t2[1], &pp);
|
||||
g_assert_true (graphene_point_near (&p[1], &pp, 0.001));
|
||||
|
||||
g_assert_cmpfloat_with_epsilon (t1[2], 0.5, 0.0001);
|
||||
g_assert_cmpfloat_with_epsilon (t2[2], 0.231249988, 0.0001);
|
||||
g_assert_true (graphene_point_near (&p[2], &GRAPHENE_POINT_INIT (231.249985, 231.249985), 0.001));
|
||||
|
||||
gsk_curve_get_point (&c1, t1[2], &pp);
|
||||
g_assert_true (graphene_point_near (&p[2], &pp, 0.001));
|
||||
|
||||
gsk_curve_get_point (&c2, t2[2], &pp);
|
||||
g_assert_true (graphene_point_near (&p[2], &pp, 0.001));
|
||||
|
||||
gsk_curve_get_tight_bounds (&c1, &b1);
|
||||
gsk_curve_get_tight_bounds (&c2, &b2);
|
||||
|
||||
gsk_bounding_box_contains_point (&b1, &p[0]);
|
||||
gsk_bounding_box_contains_point (&b1, &p[1]);
|
||||
gsk_bounding_box_contains_point (&b1, &p[2]);
|
||||
gsk_bounding_box_contains_point (&b2, &p[0]);
|
||||
gsk_bounding_box_contains_point (&b2, &p[1]);
|
||||
gsk_bounding_box_contains_point (&b2, &p[2]);
|
||||
}
|
||||
|
||||
static void
|
||||
test_line_curve_end_intersection (void)
|
||||
{
|
||||
GskCurve c1, c2;
|
||||
graphene_point_t p1[4], p2[2];
|
||||
float t1[9], t2[9];
|
||||
graphene_point_t p[9];
|
||||
GskPathIntersection kind[9];
|
||||
int n;
|
||||
|
||||
graphene_point_init (&p1[0], 0, 100);
|
||||
graphene_point_init (&p1[1], 50, 100);
|
||||
graphene_point_init (&p1[2], 50, 0);
|
||||
graphene_point_init (&p1[3], 100, 0);
|
||||
graphene_point_init (&p2[0], 100, 0);
|
||||
graphene_point_init (&p2[1], 100, 100);
|
||||
|
||||
gsk_curve_init (&c1, gsk_pathop_encode (GSK_PATH_CUBIC, p1));
|
||||
gsk_curve_init (&c2, gsk_pathop_encode (GSK_PATH_LINE, p2));
|
||||
|
||||
n = gsk_curve_intersect (&c1, &c2, t1, t2, p, kind, 1);
|
||||
|
||||
g_assert_cmpint (n, ==, 1);
|
||||
g_assert_cmpfloat_with_epsilon (t1[0], 1, 0.0001);
|
||||
g_assert_cmpfloat_with_epsilon (t2[0], 0, 0.0001);
|
||||
g_assert_true (graphene_point_near (&p[0], &GRAPHENE_POINT_INIT (100, 0), 0.0001));
|
||||
}
|
||||
|
||||
static void
|
||||
test_line_curve_none_intersection (void)
|
||||
{
|
||||
GskCurve c1, c2;
|
||||
graphene_point_t p1[4], p2[2];
|
||||
float t1[9], t2[9];
|
||||
graphene_point_t p[9];
|
||||
GskPathIntersection kind[9];
|
||||
int n;
|
||||
|
||||
graphene_point_init (&p1[0], 333, 78);
|
||||
graphene_point_init (&p1[1], 415, 78);
|
||||
graphene_point_init (&p1[2], 463, 131);
|
||||
graphene_point_init (&p1[3], 463, 223);
|
||||
|
||||
graphene_point_init (&p2[0], 520, 476);
|
||||
graphene_point_init (&p2[1], 502, 418);
|
||||
|
||||
gsk_curve_init (&c1, gsk_pathop_encode (GSK_PATH_CUBIC, p1));
|
||||
gsk_curve_init (&c2, gsk_pathop_encode (GSK_PATH_LINE, p2));
|
||||
|
||||
n = gsk_curve_intersect (&c1, &c2, t1, t2, p, kind, 1);
|
||||
|
||||
g_assert_cmpint (n, ==, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
test_curve_curve_intersection (void)
|
||||
{
|
||||
GskCurve c1, c2;
|
||||
graphene_point_t p1[4], p2[4];
|
||||
float t1[9], t2[9];
|
||||
graphene_point_t p[9];
|
||||
GskPathIntersection kind[9];
|
||||
int n;
|
||||
GskBoundingBox b;
|
||||
|
||||
graphene_point_init (&p1[0], 0, 0);
|
||||
graphene_point_init (&p1[1], 33.333, 100);
|
||||
graphene_point_init (&p1[2], 66.667, 0);
|
||||
graphene_point_init (&p1[3], 100, 100);
|
||||
graphene_point_init (&p2[0], 0, 50);
|
||||
graphene_point_init (&p2[1], 100, 0);
|
||||
graphene_point_init (&p2[2], 20, 0); // weight 20
|
||||
graphene_point_init (&p2[3], 50, 100);
|
||||
|
||||
gsk_curve_init (&c1, gsk_pathop_encode (GSK_PATH_CUBIC, p1));
|
||||
gsk_curve_init (&c2, gsk_pathop_encode (GSK_PATH_CONIC, p2));
|
||||
|
||||
n = gsk_curve_intersect (&c1, &c2, t1, t2, p, kind, 9);
|
||||
|
||||
g_assert_cmpint (n, ==, 2);
|
||||
g_assert_cmpfloat (t1[0], <, 0.5);
|
||||
g_assert_cmpfloat (t1[1], >, 0.5);
|
||||
g_assert_cmpfloat (t2[0], <, 0.5);
|
||||
g_assert_cmpfloat (t2[1], >, 0.5);
|
||||
|
||||
gsk_curve_get_tight_bounds (&c1, &b);
|
||||
gsk_bounding_box_contains_point (&b, &p[0]);
|
||||
|
||||
gsk_curve_get_tight_bounds (&c2, &b);
|
||||
gsk_bounding_box_contains_point (&b, &p[0]);
|
||||
}
|
||||
|
||||
static void
|
||||
test_curve_curve_end_intersection (void)
|
||||
{
|
||||
GskCurve c1, c2;
|
||||
graphene_point_t p1[4], p2[4];
|
||||
float t1[9], t2[9];
|
||||
graphene_point_t p[9];
|
||||
GskPathIntersection kind[9];
|
||||
int n;
|
||||
|
||||
graphene_point_init (&p1[0], 0, 0);
|
||||
graphene_point_init (&p1[1], 33.333, 100);
|
||||
graphene_point_init (&p1[2], 66.667, 0);
|
||||
graphene_point_init (&p1[3], 100, 100);
|
||||
graphene_point_init (&p2[0], 100, 100);
|
||||
graphene_point_init (&p2[1], 100, 0);
|
||||
graphene_point_init (&p2[2], 20, 0);
|
||||
graphene_point_init (&p2[3], 10, 0);
|
||||
|
||||
gsk_curve_init (&c1, gsk_pathop_encode (GSK_PATH_CUBIC, p1));
|
||||
gsk_curve_init (&c2, gsk_pathop_encode (GSK_PATH_CONIC, p2));
|
||||
|
||||
n = gsk_curve_intersect (&c1, &c2, t1, t2, p, kind, 9);
|
||||
|
||||
g_assert_cmpint (n, ==, 1);
|
||||
g_assert_cmpfloat_with_epsilon (t1[0], 1, 0.0001);
|
||||
g_assert_cmpfloat_with_epsilon (t2[0], 0, 0.0001);
|
||||
}
|
||||
|
||||
static void
|
||||
test_curve_curve_end_intersection2 (void)
|
||||
{
|
||||
GskCurve c, c1, c2;
|
||||
graphene_point_t p1[4];
|
||||
float t1[9], t2[9];
|
||||
graphene_point_t p[9];
|
||||
GskPathIntersection kind[9];
|
||||
int n;
|
||||
|
||||
graphene_point_init (&p1[0], 200, 100);
|
||||
graphene_point_init (&p1[1], 300, 300);
|
||||
graphene_point_init (&p1[2], 100, 300);
|
||||
graphene_point_init (&p1[3], 300, 100);
|
||||
|
||||
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_CUBIC, p1));
|
||||
|
||||
gsk_curve_split (&c, 0.5, &c1, &c2);
|
||||
|
||||
n = gsk_curve_intersect (&c1, &c2, t1, t2, p, kind, 9);
|
||||
|
||||
g_assert_cmpint (n, ==, 2);
|
||||
}
|
||||
|
||||
static void
|
||||
test_curve_curve_max_intersection (void)
|
||||
{
|
||||
GskCurve c1, c2;
|
||||
graphene_point_t p1[4], p2[4];
|
||||
float t1[9], t2[9];
|
||||
graphene_point_t p[9];
|
||||
GskPathIntersection kind[9];
|
||||
int n;
|
||||
|
||||
graphene_point_init (&p1[0], 106, 100);
|
||||
graphene_point_init (&p1[1], 118, 264);
|
||||
graphene_point_init (&p1[2], 129, 4);
|
||||
graphene_point_init (&p1[3], 128, 182);
|
||||
|
||||
graphene_point_init (&p2[0], 54, 135);
|
||||
graphene_point_init (&p2[1], 263, 136);
|
||||
graphene_point_init (&p2[2], 2, 143);
|
||||
graphene_point_init (&p2[3], 141, 150);
|
||||
|
||||
gsk_curve_init (&c1, gsk_pathop_encode (GSK_PATH_CUBIC, p1));
|
||||
gsk_curve_init (&c2, gsk_pathop_encode (GSK_PATH_CUBIC, p2));
|
||||
|
||||
n = gsk_curve_intersect (&c1, &c2, t1, t2, p, kind, 9);
|
||||
|
||||
g_assert_cmpint (n, ==, 9);
|
||||
}
|
||||
|
||||
/* This showed up as artifacts in the stroker when our
|
||||
* intersection code failed to find intersections with
|
||||
* horizontal lines
|
||||
*/
|
||||
static void
|
||||
test_curve_intersection_horizontal_line (void)
|
||||
{
|
||||
GskCurve c1, c2;
|
||||
float t1, t2;
|
||||
graphene_point_t p;
|
||||
GskPathIntersection kind;
|
||||
int n;
|
||||
|
||||
gsk_curve_init (&c1,
|
||||
gsk_pathop_encode (GSK_PATH_CONIC,
|
||||
(const graphene_point_t[4]) {
|
||||
GRAPHENE_POINT_INIT (200.000, 165.000),
|
||||
GRAPHENE_POINT_INIT (220.858, 165.000),
|
||||
GRAPHENE_POINT_INIT (1.4142, 0),
|
||||
GRAPHENE_POINT_INIT (292.929, 92.929),
|
||||
}));
|
||||
gsk_curve_init_foreach (&c2,
|
||||
GSK_PATH_LINE,
|
||||
(const graphene_point_t[2]) {
|
||||
GRAPHENE_POINT_INIT (300, 110),
|
||||
GRAPHENE_POINT_INIT (100, 110),
|
||||
},
|
||||
2,
|
||||
0);
|
||||
|
||||
n = gsk_curve_intersect (&c1, &c2, &t1, &t2, &p, &kind, 1);
|
||||
|
||||
g_assert_true (n == 1);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
(g_test_init) (&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/curve/intersection/line-line", test_line_line_intersection);
|
||||
g_test_add_func ("/curve/intersection/line-line-none", test_line_line_none_intersection);
|
||||
g_test_add_func ("/curve/intersection/line-line-end", test_line_line_end_intersection);
|
||||
g_test_add_func ("/curve/intersection/line-line-parallel", test_line_line_parallel);
|
||||
g_test_add_func ("/curve/intersection/line-line-same", test_line_line_same);
|
||||
g_test_add_func ("/curve/intersection/line-curve", test_line_curve_intersection);
|
||||
g_test_add_func ("/curve/intersection/line-curve-end", test_line_curve_end_intersection);
|
||||
g_test_add_func ("/curve/intersection/line-curve-none", test_line_curve_none_intersection);
|
||||
g_test_add_func ("/curve/intersection/line-curve-multiple", test_line_curve_multiple_intersection);
|
||||
g_test_add_func ("/curve/intersection/curve-curve", test_curve_curve_intersection);
|
||||
g_test_add_func ("/curve/intersection/curve-curve-end", test_curve_curve_end_intersection);
|
||||
g_test_add_func ("/curve/intersection/curve-curve-end2", test_curve_curve_end_intersection2);
|
||||
g_test_add_func ("/curve/intersection/curve-curve-max", test_curve_curve_max_intersection);
|
||||
g_test_add_func ("/curve/intersection/horizontal-line", test_curve_intersection_horizontal_line);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
@@ -374,6 +374,8 @@ tests = [
|
||||
['shader'],
|
||||
['path', [ 'path-utils.c' ] ],
|
||||
['path-special-cases'],
|
||||
['path-intersect'],
|
||||
['path-ops', [ 'path-utils.c' ] ],
|
||||
]
|
||||
|
||||
test_cargs = []
|
||||
@@ -406,6 +408,7 @@ endforeach
|
||||
internal_tests = [
|
||||
[ 'curve' ],
|
||||
[ 'curve-special-cases' ],
|
||||
[ 'curve-intersect' ],
|
||||
[ 'path-private' ],
|
||||
[ 'diff' ],
|
||||
[ 'half-float' ],
|
||||
|
||||
@@ -0,0 +1,806 @@
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define assert_path_point_equal(point,_contour,_idx,_t) \
|
||||
g_assert_cmpint ((point)->contour, ==, (_contour)); \
|
||||
g_assert_cmpint ((point)->idx, ==, (_idx)); \
|
||||
g_assert_cmpfloat_with_epsilon ((point)->t, (_t), 0.0001);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GskPathPoint point1[20];
|
||||
GskPathPoint point2[20];
|
||||
GskPathIntersection kind[20];
|
||||
int found;
|
||||
} CollectData;
|
||||
|
||||
static gboolean
|
||||
collect_cb (GskPath *path1,
|
||||
const GskPathPoint *point1,
|
||||
GskPath *path2,
|
||||
const GskPathPoint *point2,
|
||||
GskPathIntersection kind,
|
||||
gpointer data)
|
||||
{
|
||||
CollectData *res = data;
|
||||
#if 0
|
||||
const char *kn[] = { "none", "normal", "start", "end" };
|
||||
|
||||
g_print ("%s idx1 %lu t1 %f idx2 %lu t2 %f\n",
|
||||
kn[kind],
|
||||
point1->idx, point1->t, point2->idx, point2->t);
|
||||
#endif
|
||||
|
||||
g_assert_true (res->found < 20);
|
||||
|
||||
res->point1[res->found] = *point1;
|
||||
res->point2[res->found] = *point2;
|
||||
res->kind[res->found] = kind;
|
||||
res->found++;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
test_intersect_simple (void)
|
||||
{
|
||||
GskPath *path1, *path2;
|
||||
graphene_point_t p1, p2;
|
||||
CollectData res;
|
||||
|
||||
path1 = gsk_path_parse ("M 100 100 h 200 v 100 h -200 z");
|
||||
path2 = gsk_path_parse ("M 150 150 h 200 v 100 h -200 z");
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path1, path2, collect_cb, &res);
|
||||
g_assert_true (res.found == 2);
|
||||
assert_path_point_equal (&res.point1[0], 0, 2, 0.5);
|
||||
assert_path_point_equal (&res.point1[1], 0, 3, 0.75);
|
||||
assert_path_point_equal (&res.point2[0], 0, 1, 0.75);
|
||||
assert_path_point_equal (&res.point2[1], 0, 4, 0.5);
|
||||
g_assert_true (res.kind[0] == GSK_PATH_INTERSECTION_NORMAL);
|
||||
g_assert_true (res.kind[1] == GSK_PATH_INTERSECTION_NORMAL);
|
||||
|
||||
gsk_path_point_get_position (&res.point1[0], path1, &p1);
|
||||
gsk_path_point_get_position (&res.point2[0], path2, &p2);
|
||||
g_assert_true (graphene_point_equal (&p1, &p2));
|
||||
|
||||
gsk_path_point_get_position (&res.point1[1], path1, &p1);
|
||||
gsk_path_point_get_position (&res.point2[1], path2, &p2);
|
||||
g_assert_true (graphene_point_equal (&p1, &p2));
|
||||
|
||||
gsk_path_unref (path1);
|
||||
gsk_path_unref (path2);
|
||||
}
|
||||
|
||||
static void
|
||||
test_intersect_simple2 (void)
|
||||
{
|
||||
GskPath *path1, *path2;
|
||||
CollectData res;
|
||||
|
||||
path1 = gsk_path_parse ("M 100 100 h 200 v 100 h -200 z");
|
||||
path2 = gsk_path_parse ("M 100 100 h 200 v 100 h -200 z");
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path1, path2, collect_cb, &res);
|
||||
g_assert_true (res.found == 2);
|
||||
g_assert_true (res.kind[0] == GSK_PATH_INTERSECTION_START);
|
||||
g_assert_true (res.kind[1] == GSK_PATH_INTERSECTION_END);
|
||||
|
||||
gsk_path_unref (path1);
|
||||
gsk_path_unref (path2);
|
||||
}
|
||||
|
||||
static void
|
||||
test_intersect_simple3 (void)
|
||||
{
|
||||
GskPath *path1, *path2;
|
||||
CollectData res;
|
||||
|
||||
path1 = gsk_path_parse ("M 100 100 h 200 v 100 h -200 z");
|
||||
path2 = gsk_path_parse ("M 300 100 h -200 v 100 h 200 z");
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path1, path2, collect_cb, &res);
|
||||
g_assert_true (res.found == 2);
|
||||
g_assert_true (res.kind[0] == GSK_PATH_INTERSECTION_START);
|
||||
g_assert_true (res.kind[1] == GSK_PATH_INTERSECTION_END);
|
||||
|
||||
gsk_path_unref (path1);
|
||||
gsk_path_unref (path2);
|
||||
}
|
||||
|
||||
static void
|
||||
test_intersect_reverse (void)
|
||||
{
|
||||
GskPath *path1, *path2;
|
||||
graphene_point_t p1, p2;
|
||||
CollectData res;
|
||||
|
||||
path1 = gsk_path_parse ("M 100 100 h 200 v 100 h -200 z");
|
||||
path2 = gsk_path_parse ("M 150 150 v 100 h 200 v -100 z");
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path1, path2, collect_cb, &res);
|
||||
g_assert_true (res.found == 2);
|
||||
assert_path_point_equal (&res.point1[0], 0, 2, 0.5);
|
||||
assert_path_point_equal (&res.point1[1], 0, 3, 0.75);
|
||||
assert_path_point_equal (&res.point2[0], 0, 4, 0.25);
|
||||
assert_path_point_equal (&res.point2[1], 0, 1, 0.5);
|
||||
g_assert_true (res.kind[0] == GSK_PATH_INTERSECTION_NORMAL);
|
||||
g_assert_true (res.kind[1] == GSK_PATH_INTERSECTION_NORMAL);
|
||||
|
||||
gsk_path_point_get_position (&res.point1[0], path1, &p1);
|
||||
gsk_path_point_get_position (&res.point2[0], path2, &p2);
|
||||
g_assert_true (graphene_point_equal (&p1, &p2));
|
||||
|
||||
gsk_path_point_get_position (&res.point1[1], path1, &p1);
|
||||
gsk_path_point_get_position (&res.point2[1], path2, &p2);
|
||||
g_assert_true (graphene_point_equal (&p1, &p2));
|
||||
|
||||
gsk_path_unref (path1);
|
||||
gsk_path_unref (path2);
|
||||
}
|
||||
|
||||
static void
|
||||
test_intersect_line_box (void)
|
||||
{
|
||||
GskPath *path1, *path2;
|
||||
graphene_point_t p1, p2;
|
||||
CollectData res;
|
||||
|
||||
path1 = gsk_path_parse ("M 50 150 l 300 0");
|
||||
path2 = gsk_path_parse ("M 100 100 h 200 v 100 h -200 z");
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path1, path2, collect_cb, &res);
|
||||
g_assert_true (res.found == 2);
|
||||
assert_path_point_equal (&res.point1[0], 0, 1, 50.f/300.f);
|
||||
assert_path_point_equal (&res.point1[1], 0, 1, 250.f/300.f);
|
||||
assert_path_point_equal (&res.point2[0], 0, 4, 0.5);
|
||||
assert_path_point_equal (&res.point2[1], 0, 2, 0.5);
|
||||
g_assert_true (res.kind[0] == GSK_PATH_INTERSECTION_NORMAL);
|
||||
g_assert_true (res.kind[1] == GSK_PATH_INTERSECTION_NORMAL);
|
||||
|
||||
gsk_path_point_get_position (&res.point1[0], path1, &p1);
|
||||
gsk_path_point_get_position (&res.point2[0], path2, &p2);
|
||||
g_assert_true (graphene_point_equal (&p1, &p2));
|
||||
|
||||
gsk_path_point_get_position (&res.point1[1], path1, &p1);
|
||||
gsk_path_point_get_position (&res.point2[1], path2, &p2);
|
||||
g_assert_true (graphene_point_equal (&p1, &p2));
|
||||
|
||||
gsk_path_unref (path1);
|
||||
gsk_path_unref (path2);
|
||||
}
|
||||
|
||||
static void
|
||||
test_intersect_xplus (void)
|
||||
{
|
||||
GskPath *path1, *path2;
|
||||
CollectData res;
|
||||
|
||||
path1 = gsk_path_parse ("M 0 0 L 100 100 M 0 100 L 100 0");
|
||||
path2 = gsk_path_parse ("M 0 50 L 100 50 M 50 0 L 50 100");
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path1, path2, collect_cb, &res);
|
||||
g_assert_true (res.found == 4);
|
||||
assert_path_point_equal (&res.point1[0], 0, 1, 0.5);
|
||||
assert_path_point_equal (&res.point1[1], 0, 1, 0.5);
|
||||
assert_path_point_equal (&res.point1[2], 1, 1, 0.5);
|
||||
assert_path_point_equal (&res.point1[3], 1, 1, 0.5);
|
||||
|
||||
assert_path_point_equal (&res.point2[0], 0, 1, 0.5);
|
||||
assert_path_point_equal (&res.point2[1], 1, 1, 0.5);
|
||||
assert_path_point_equal (&res.point2[2], 0, 1, 0.5);
|
||||
assert_path_point_equal (&res.point2[3], 1, 1, 0.5);
|
||||
|
||||
gsk_path_unref (path1);
|
||||
gsk_path_unref (path2);
|
||||
}
|
||||
|
||||
static void
|
||||
test_intersect_point (void)
|
||||
{
|
||||
GskPath *path1, *path2;
|
||||
CollectData res;
|
||||
|
||||
path1 = gsk_path_parse ("M 0 50");
|
||||
path2 = gsk_path_parse ("M 0 0 L 0 100");
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path1, path2, collect_cb, &res);
|
||||
|
||||
g_assert_true (res.found == 1);
|
||||
assert_path_point_equal (&res.point1[0], 0, 0, 1);
|
||||
assert_path_point_equal (&res.point2[0], 0, 1, 0.5);
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path2, path1, collect_cb, &res);
|
||||
|
||||
g_assert_true (res.found == 1);
|
||||
assert_path_point_equal (&res.point1[0], 0, 1, 0.5);
|
||||
assert_path_point_equal (&res.point2[0], 0, 0, 1);
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path1, path1, collect_cb, &res);
|
||||
|
||||
g_assert_true (res.found == 1);
|
||||
assert_path_point_equal (&res.point1[0], 0, 0, 1);
|
||||
assert_path_point_equal (&res.point2[0], 0, 0, 1);
|
||||
|
||||
gsk_path_unref (path1);
|
||||
gsk_path_unref (path2);
|
||||
}
|
||||
|
||||
static void
|
||||
test_intersect_contours (void)
|
||||
{
|
||||
GskPath *path1, *path2;
|
||||
CollectData res;
|
||||
|
||||
path1 = gsk_path_parse ("M 0 100 L 200 100");
|
||||
path2 = gsk_path_parse ("M 150 0 150 200 M 50 0 50 200");
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path1, path2, collect_cb, &res);
|
||||
g_assert_true (res.found == 2);
|
||||
assert_path_point_equal (&res.point1[0], 0, 1, 0.25f);
|
||||
assert_path_point_equal (&res.point1[1], 0, 1, 0.75f);
|
||||
assert_path_point_equal (&res.point2[0], 1, 1, 0.5f);
|
||||
assert_path_point_equal (&res.point2[1], 0, 1, 0.5f);
|
||||
g_assert_true (res.kind[0] == GSK_PATH_INTERSECTION_NORMAL);
|
||||
g_assert_true (res.kind[1] == GSK_PATH_INTERSECTION_NORMAL);
|
||||
|
||||
gsk_path_unref (path1);
|
||||
gsk_path_unref (path2);
|
||||
}
|
||||
|
||||
static void
|
||||
test_intersect_contours2 (void)
|
||||
{
|
||||
GskPath *path1, *path2;
|
||||
CollectData res;
|
||||
|
||||
path1 = gsk_path_parse ("M 0 100 L 200 100");
|
||||
path2 = gsk_path_parse ("M 150 0 L 150 200 M 50 0 L 50 200 M 60 100 L 140 100");
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path1, path2, collect_cb, &res);
|
||||
g_assert_true (res.found == 4);
|
||||
assert_path_point_equal (&res.point1[0], 0, 1, 0.25f);
|
||||
assert_path_point_equal (&res.point1[1], 0, 1, 0.3f);
|
||||
assert_path_point_equal (&res.point2[0], 1, 1, 0.5f);
|
||||
assert_path_point_equal (&res.point2[1], 2, 1, 0.f);
|
||||
g_assert_true (res.kind[0] == GSK_PATH_INTERSECTION_NORMAL);
|
||||
g_assert_true (res.kind[1] == GSK_PATH_INTERSECTION_START);
|
||||
g_assert_true (res.kind[2] == GSK_PATH_INTERSECTION_END);
|
||||
g_assert_true (res.kind[3] == GSK_PATH_INTERSECTION_NORMAL);
|
||||
|
||||
gsk_path_unref (path1);
|
||||
gsk_path_unref (path2);
|
||||
}
|
||||
|
||||
static void
|
||||
test_intersect_contours3 (void)
|
||||
{
|
||||
GskPath *path1, *path2;
|
||||
CollectData res;
|
||||
|
||||
path1 = gsk_path_parse ("M 150 0 L 150 200 M 50 0 L 50 200 M 60 100 L 140 100");
|
||||
path2 = gsk_path_parse ("M 0 100 L 200 100");
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path1, path2, collect_cb, &res);
|
||||
g_assert_true (res.found == 4);
|
||||
assert_path_point_equal (&res.point1[0], 0, 1, 0.5f);
|
||||
assert_path_point_equal (&res.point1[1], 1, 1, 0.5f);
|
||||
assert_path_point_equal (&res.point2[0], 0, 1, 0.75f);
|
||||
assert_path_point_equal (&res.point2[1], 0, 1, 0.25f);
|
||||
g_assert_true (res.kind[0] == GSK_PATH_INTERSECTION_NORMAL);
|
||||
g_assert_true (res.kind[1] == GSK_PATH_INTERSECTION_NORMAL);
|
||||
g_assert_true (res.kind[2] == GSK_PATH_INTERSECTION_START);
|
||||
g_assert_true (res.kind[3] == GSK_PATH_INTERSECTION_END);
|
||||
|
||||
gsk_path_unref (path1);
|
||||
gsk_path_unref (path2);
|
||||
}
|
||||
|
||||
static void
|
||||
test_intersect_coincide (void)
|
||||
{
|
||||
GskPath *path1, *path2;
|
||||
CollectData res;
|
||||
|
||||
path1 = gsk_path_parse ("M 100 100 h 200 v 100 h -200 z");
|
||||
path2 = gsk_path_parse ("M 150 100 h 100 v 50 h -100 z");
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path1, path2, collect_cb, &res);
|
||||
g_assert_true (res.found == 2);
|
||||
assert_path_point_equal (&res.point1[0], 0, 1, 0.25);
|
||||
assert_path_point_equal (&res.point1[1], 0, 1, 0.75);
|
||||
assert_path_point_equal (&res.point2[0], 0, 1, 0);
|
||||
assert_path_point_equal (&res.point2[1], 0, 1, 1);
|
||||
g_assert_true (res.kind[0] == GSK_PATH_INTERSECTION_START);
|
||||
g_assert_true (res.kind[1] == GSK_PATH_INTERSECTION_END);
|
||||
|
||||
gsk_path_unref (path1);
|
||||
gsk_path_unref (path2);
|
||||
}
|
||||
|
||||
static void
|
||||
test_intersect_coincide2 (void)
|
||||
{
|
||||
GskPath *path1, *path2;
|
||||
CollectData res;
|
||||
|
||||
path1 = gsk_path_parse ("M 150 100 h 100 v 50 h -100 z");
|
||||
path2 = gsk_path_parse ("M 100 100 h 200 v 100 h -200 z");
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path1, path2, collect_cb, &res);
|
||||
g_assert_true (res.found == 2);
|
||||
assert_path_point_equal (&res.point1[0], 0, 1, 0);
|
||||
assert_path_point_equal (&res.point1[1], 0, 1, 1);
|
||||
assert_path_point_equal (&res.point2[0], 0, 1, 0.25);
|
||||
assert_path_point_equal (&res.point2[1], 0, 1, 0.75);
|
||||
g_assert_true (res.kind[0] == GSK_PATH_INTERSECTION_START);
|
||||
g_assert_true (res.kind[1] == GSK_PATH_INTERSECTION_END);
|
||||
|
||||
gsk_path_unref (path1);
|
||||
gsk_path_unref (path2);
|
||||
}
|
||||
|
||||
static void
|
||||
test_intersect_coincide3 (void)
|
||||
{
|
||||
GskPath *path1, *path2;
|
||||
CollectData res;
|
||||
|
||||
path1 = gsk_path_parse ("M 100 100 h 200 v 100 h -200 z");
|
||||
path2 = gsk_path_parse ("M 150 100 h 100 v 50 h -25 v -50 h -50 v 50 h -25 z");
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path1, path2, collect_cb, &res);
|
||||
g_assert_true (res.found == 4);
|
||||
g_assert_true (res.kind[0] == GSK_PATH_INTERSECTION_START);
|
||||
g_assert_true (res.kind[1] == GSK_PATH_INTERSECTION_START);
|
||||
g_assert_true (res.kind[2] == GSK_PATH_INTERSECTION_END);
|
||||
g_assert_true (res.kind[3] == GSK_PATH_INTERSECTION_END);
|
||||
|
||||
assert_path_point_equal (&res.point1[0], 0, 1, 0.25);
|
||||
assert_path_point_equal (&res.point1[1], 0, 1, 0.375);
|
||||
assert_path_point_equal (&res.point1[2], 0, 1, 0.625);
|
||||
assert_path_point_equal (&res.point1[3], 0, 1, 0.75);
|
||||
|
||||
assert_path_point_equal (&res.point2[0], 0, 1, 0);
|
||||
assert_path_point_equal (&res.point2[1], 0, 5, 1);
|
||||
assert_path_point_equal (&res.point2[2], 0, 5, 0);
|
||||
assert_path_point_equal (&res.point2[3], 0, 1, 1);
|
||||
|
||||
gsk_path_unref (path1);
|
||||
gsk_path_unref (path2);
|
||||
}
|
||||
|
||||
static void
|
||||
test_intersect_coincide4 (void)
|
||||
{
|
||||
GskPath *path1, *path2;
|
||||
CollectData res;
|
||||
|
||||
path1 = gsk_path_parse ("M 100 100 h 200 v 100 h -200 z");
|
||||
path2 = gsk_path_parse ("M 150 100 h 100 v 50 h -25 v -100 h -50 v 100 h -25 z");
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path1, path2, collect_cb, &res);
|
||||
g_assert_true (res.found == 4);
|
||||
g_assert_true (res.kind[0] == GSK_PATH_INTERSECTION_START);
|
||||
g_assert_true (res.kind[1] == GSK_PATH_INTERSECTION_NORMAL);
|
||||
g_assert_true (res.kind[2] == GSK_PATH_INTERSECTION_NORMAL);
|
||||
g_assert_true (res.kind[3] == GSK_PATH_INTERSECTION_END);
|
||||
|
||||
assert_path_point_equal (&res.point1[0], 0, 1, 0.25);
|
||||
assert_path_point_equal (&res.point1[1], 0, 1, 0.375);
|
||||
assert_path_point_equal (&res.point1[2], 0, 1, 0.625);
|
||||
assert_path_point_equal (&res.point1[3], 0, 1, 0.75);
|
||||
|
||||
assert_path_point_equal (&res.point2[0], 0, 1, 0);
|
||||
assert_path_point_equal (&res.point2[1], 0, 6, 0.5);
|
||||
assert_path_point_equal (&res.point2[2], 0, 4, 0.5);
|
||||
assert_path_point_equal (&res.point2[3], 0, 1, 1);
|
||||
|
||||
gsk_path_unref (path1);
|
||||
gsk_path_unref (path2);
|
||||
}
|
||||
|
||||
/* the next few tests explore overlapping segments */
|
||||
static void
|
||||
test_intersect_coincide5 (void)
|
||||
{
|
||||
GskPath *path1, *path2;
|
||||
CollectData res;
|
||||
|
||||
path1 = gsk_path_parse ("M 150 100 h 100 v 100 h -100 z");
|
||||
path2 = gsk_path_parse ("M 100 100 h 200 v 50 h -100 v -50 h 25 v -50 h -100 z");
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path1, path2, collect_cb, &res);
|
||||
g_assert_true (res.found == 5);
|
||||
g_assert_true (res.kind[0] == GSK_PATH_INTERSECTION_START);
|
||||
g_assert_true (res.kind[1] == GSK_PATH_INTERSECTION_START);
|
||||
g_assert_true (res.kind[2] == GSK_PATH_INTERSECTION_END);
|
||||
g_assert_true (res.kind[3] == GSK_PATH_INTERSECTION_END);
|
||||
g_assert_true (res.kind[4] == GSK_PATH_INTERSECTION_NORMAL);
|
||||
|
||||
assert_path_point_equal (&res.point1[0], 0, 1, 0);
|
||||
assert_path_point_equal (&res.point1[1], 0, 1, 0.5);
|
||||
assert_path_point_equal (&res.point1[2], 0, 1, 0.75);
|
||||
assert_path_point_equal (&res.point1[3], 0, 1, 1);
|
||||
assert_path_point_equal (&res.point1[4], 0, 2, 0.5);
|
||||
|
||||
assert_path_point_equal (&res.point2[0], 0, 1, 0.25);
|
||||
assert_path_point_equal (&res.point2[1], 0, 5, 0);
|
||||
assert_path_point_equal (&res.point2[2], 0, 5, 1);
|
||||
assert_path_point_equal (&res.point2[3], 0, 1, 0.75);
|
||||
assert_path_point_equal (&res.point2[4], 0, 3, 0.5);
|
||||
|
||||
gsk_path_unref (path1);
|
||||
gsk_path_unref (path2);
|
||||
}
|
||||
|
||||
static void
|
||||
test_intersect_coincide6 (void)
|
||||
{
|
||||
GskPath *path1, *path2;
|
||||
CollectData res;
|
||||
|
||||
path1 = gsk_path_parse ("M 150 100 h 75 l 25 50 v 50 h -100 z");
|
||||
path2 = gsk_path_parse ("M 100 100 h 200 v 50 h -100 v -50 h 50 v -50 h -125 z");
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path1, path2, collect_cb, &res);
|
||||
g_assert_true (res.found == 5);
|
||||
g_assert_true (res.kind[0] == GSK_PATH_INTERSECTION_START);
|
||||
g_assert_true (res.kind[1] == GSK_PATH_INTERSECTION_START);
|
||||
g_assert_true (res.kind[2] == GSK_PATH_INTERSECTION_END);
|
||||
g_assert_true (res.kind[3] == GSK_PATH_INTERSECTION_END);
|
||||
g_assert_true (res.kind[4] == GSK_PATH_INTERSECTION_NORMAL);
|
||||
|
||||
assert_path_point_equal (&res.point1[0], 0, 1, 0);
|
||||
assert_path_point_equal (&res.point1[1], 0, 1, 2./3.);
|
||||
assert_path_point_equal (&res.point1[2], 0, 1, 1);
|
||||
assert_path_point_equal (&res.point1[3], 0, 1, 1);
|
||||
assert_path_point_equal (&res.point1[4], 0, 3, 0);
|
||||
|
||||
assert_path_point_equal (&res.point2[0], 0, 1, 0.25);
|
||||
assert_path_point_equal (&res.point2[1], 0, 5, 0);
|
||||
assert_path_point_equal (&res.point2[2], 0, 1, 0.625);
|
||||
assert_path_point_equal (&res.point2[3], 0, 5, 0.5);
|
||||
assert_path_point_equal (&res.point2[4], 0, 3, 0.5);
|
||||
|
||||
gsk_path_unref (path1);
|
||||
gsk_path_unref (path2);
|
||||
}
|
||||
|
||||
static int
|
||||
circle_intersect (const graphene_point_t *center1,
|
||||
float radius1,
|
||||
const graphene_point_t *center2,
|
||||
float radius2,
|
||||
graphene_point_t points[2])
|
||||
{
|
||||
float d;
|
||||
float a, h;
|
||||
graphene_point_t m;
|
||||
graphene_vec2_t n;
|
||||
|
||||
g_assert (radius1 >= 0);
|
||||
g_assert (radius2 >= 0);
|
||||
|
||||
d = graphene_point_distance (center1, center2, NULL, NULL);
|
||||
|
||||
if (d < fabsf (radius1 - radius2))
|
||||
return 0;
|
||||
|
||||
if (d > radius1 + radius2)
|
||||
return 0;
|
||||
|
||||
if (d == radius1 + radius2)
|
||||
{
|
||||
graphene_point_interpolate (center1, center2, radius1 / (radius1 + radius2), &points[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
a + b = d;
|
||||
|
||||
a^2 + h^2 = r1^2
|
||||
b^2 + h^2 = r2^2
|
||||
|
||||
a^2 - (d - a)^2 = r1^2 - r2^2
|
||||
a^2 - (d^2 - 2ad + a2) = r1^2 - r2^2
|
||||
|
||||
2ad -d^2 = r1^2 - r2^2
|
||||
2ad = r1^2 - r2^2 + d^2
|
||||
a = (r1^2 - r2^2 + d^2) / (2d)
|
||||
|
||||
p1/2 = c1 + a/d * (c2 - c1) +/- h * n(c1,c2);
|
||||
*/
|
||||
|
||||
a = (radius1*radius1 - radius2*radius2 + d*d)/(2*d);
|
||||
h = sqrtf (radius1*radius1 - a*a);
|
||||
|
||||
graphene_point_interpolate (center1, center2, a/d, &m);
|
||||
graphene_vec2_init (&n, center2->y - center1->y, center1->x - center2->x);
|
||||
graphene_vec2_normalize (&n, &n);
|
||||
|
||||
graphene_point_init (&points[0], m.x + graphene_vec2_get_x (&n) * h,
|
||||
m.y + graphene_vec2_get_y (&n) * h);
|
||||
graphene_point_init (&points[1], m.x - graphene_vec2_get_x (&n) * h,
|
||||
m.y - graphene_vec2_get_y (&n) * h);
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
static void
|
||||
test_intersect_circle (void)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
GskPath *path1, *path2;
|
||||
CollectData res;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (0, 0), 12);
|
||||
path1 = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (1, 1), 10);
|
||||
path2 = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path1, path2, collect_cb, &res);
|
||||
g_assert_true (res.found == 0);
|
||||
|
||||
gsk_path_unref (path1);
|
||||
gsk_path_unref (path2);
|
||||
}
|
||||
|
||||
static void
|
||||
test_intersect_circle2 (void)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
GskPath *path1, *path2;
|
||||
CollectData res;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (0, 0), 12);
|
||||
path1 = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (0, 25), 10);
|
||||
path2 = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path1, path2, collect_cb, &res);
|
||||
g_assert_true (res.found == 0);
|
||||
|
||||
gsk_path_unref (path1);
|
||||
gsk_path_unref (path2);
|
||||
}
|
||||
|
||||
static void
|
||||
test_intersect_circle3 (void)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
GskPath *path1, *path2;
|
||||
CollectData res;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (0, 0), 12);
|
||||
path1 = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (0, 22), 10);
|
||||
path2 = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path1, path2, collect_cb, &res);
|
||||
g_assert_true (res.found == 1);
|
||||
|
||||
gsk_path_unref (path1);
|
||||
gsk_path_unref (path2);
|
||||
}
|
||||
|
||||
static void
|
||||
test_intersect_circle4 (void)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
GskPath *path1, *path2;
|
||||
CollectData res;
|
||||
graphene_point_t p[2], pos;
|
||||
int n;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (0, 0), 12);
|
||||
path1 = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (0, 18), 10);
|
||||
path2 = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path1, path2, collect_cb, &res);
|
||||
g_assert_true (res.found == 2);
|
||||
|
||||
n = circle_intersect (&GRAPHENE_POINT_INIT (0, 0), 12,
|
||||
&GRAPHENE_POINT_INIT (0, 18), 10,
|
||||
p);
|
||||
|
||||
g_assert_true (n == 2);
|
||||
gsk_path_point_get_position (&res.point1[0], path1, &pos);
|
||||
g_assert_true (graphene_point_near (&p[0], &pos, 0.01));
|
||||
gsk_path_point_get_position (&res.point1[1], path1, &pos);
|
||||
g_assert_true (graphene_point_near (&p[1], &pos, 0.01));
|
||||
|
||||
gsk_path_unref (path1);
|
||||
gsk_path_unref (path2);
|
||||
}
|
||||
|
||||
static void
|
||||
test_intersect_circle5 (void)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
GskPath *path1, *path2;
|
||||
CollectData res;
|
||||
graphene_point_t p[2], pos;
|
||||
int n;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (0, 0), 12);
|
||||
path1 = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (10, 10), 10);
|
||||
path2 = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path1, path2, collect_cb, &res);
|
||||
g_assert_true (res.found == 2);
|
||||
|
||||
n = circle_intersect (&GRAPHENE_POINT_INIT (0, 0), 12,
|
||||
&GRAPHENE_POINT_INIT (10, 10), 10,
|
||||
p);
|
||||
|
||||
g_assert_true (n == 2);
|
||||
gsk_path_point_get_position (&res.point1[0], path1, &pos);
|
||||
g_assert_true (graphene_point_near (&p[0], &pos, 0.01));
|
||||
gsk_path_point_get_position (&res.point1[1], path1, &pos);
|
||||
g_assert_true (graphene_point_near (&p[1], &pos, 0.01));
|
||||
|
||||
gsk_path_unref (path1);
|
||||
gsk_path_unref (path2);
|
||||
}
|
||||
|
||||
static void
|
||||
test_intersect_circle6 (void)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
GskPath *path1;
|
||||
CollectData res;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (100, 100), 12);
|
||||
path1 = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path1, path1, collect_cb, &res);
|
||||
g_assert_true (res.found == 2);
|
||||
|
||||
g_assert_true (res.kind[0] == GSK_PATH_INTERSECTION_START);
|
||||
g_assert_true (res.kind[1] == GSK_PATH_INTERSECTION_END);
|
||||
|
||||
gsk_path_unref (path1);
|
||||
}
|
||||
|
||||
static void
|
||||
test_intersect_circle7 (void)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
GskPath *path1, *path2;
|
||||
CollectData res;
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (100, 100), 12);
|
||||
path1 = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_reverse_path (builder, path1);
|
||||
path2 = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path1, path2, collect_cb, &res);
|
||||
g_assert_true (res.found == 2);
|
||||
|
||||
g_assert_true (res.kind[0] == GSK_PATH_INTERSECTION_START);
|
||||
g_assert_true (res.kind[1] == GSK_PATH_INTERSECTION_END);
|
||||
|
||||
gsk_path_unref (path1);
|
||||
gsk_path_unref (path2);
|
||||
}
|
||||
|
||||
static void
|
||||
test_intersect_circle_rounded_rect (void)
|
||||
{
|
||||
GskRoundedRect rr;
|
||||
GskPathBuilder *builder;
|
||||
GskPath *path1, *path2;
|
||||
CollectData res;
|
||||
|
||||
rr.bounds = GRAPHENE_RECT_INIT (10, 10, 100, 100);
|
||||
rr.corner[GSK_CORNER_TOP_LEFT] = GRAPHENE_SIZE_INIT (20, 20);
|
||||
rr.corner[GSK_CORNER_TOP_RIGHT] = GRAPHENE_SIZE_INIT (20, 20);
|
||||
rr.corner[GSK_CORNER_BOTTOM_RIGHT] = GRAPHENE_SIZE_INIT (20, 20);
|
||||
rr.corner[GSK_CORNER_BOTTOM_LEFT] = GRAPHENE_SIZE_INIT (20, 20);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_rounded_rect (builder, &rr);
|
||||
path1 = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (30, 30), 20);
|
||||
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (90, 30), 20);
|
||||
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (90, 90), 20);
|
||||
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (30, 90), 20);
|
||||
path2 = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
res.found = 0;
|
||||
gsk_path_foreach_intersection (path1, path2, collect_cb, &res);
|
||||
g_assert_true (res.found == 8);
|
||||
|
||||
g_assert_true (res.kind[0] == GSK_PATH_INTERSECTION_START);
|
||||
g_assert_true (res.kind[1] == GSK_PATH_INTERSECTION_END);
|
||||
g_assert_true (res.kind[2] == GSK_PATH_INTERSECTION_START);
|
||||
g_assert_true (res.kind[3] == GSK_PATH_INTERSECTION_END);
|
||||
g_assert_true (res.kind[4] == GSK_PATH_INTERSECTION_START);
|
||||
g_assert_true (res.kind[5] == GSK_PATH_INTERSECTION_END);
|
||||
g_assert_true (res.kind[6] == GSK_PATH_INTERSECTION_START);
|
||||
g_assert_true (res.kind[7] == GSK_PATH_INTERSECTION_END);
|
||||
|
||||
gsk_path_unref (path1);
|
||||
gsk_path_unref (path2);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
(g_test_init) (&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/path/intersect/simple", test_intersect_simple);
|
||||
g_test_add_func ("/path/intersect/simple2", test_intersect_simple2);
|
||||
g_test_add_func ("/path/intersect/simple3", test_intersect_simple3);
|
||||
g_test_add_func ("/path/intersect/reverse", test_intersect_reverse);
|
||||
g_test_add_func ("/path/intersect/line-box", test_intersect_line_box);
|
||||
g_test_add_func ("/path/intersect/xplus", test_intersect_xplus);
|
||||
g_test_add_func ("/path/intersect/point", test_intersect_point);
|
||||
g_test_add_func ("/path/intersect/contours", test_intersect_contours);
|
||||
g_test_add_func ("/path/intersect/contours2", test_intersect_contours2);
|
||||
g_test_add_func ("/path/intersect/contours3", test_intersect_contours3);
|
||||
g_test_add_func ("/path/intersect/coincide", test_intersect_coincide);
|
||||
g_test_add_func ("/path/intersect/coincide2", test_intersect_coincide2);
|
||||
g_test_add_func ("/path/intersect/coincide3", test_intersect_coincide3);
|
||||
g_test_add_func ("/path/intersect/coincide4", test_intersect_coincide4);
|
||||
g_test_add_func ("/path/intersect/coincide5", test_intersect_coincide5);
|
||||
g_test_add_func ("/path/intersect/coincide6", test_intersect_coincide6);
|
||||
g_test_add_func ("/path/intersect/circle", test_intersect_circle);
|
||||
g_test_add_func ("/path/intersect/circle2", test_intersect_circle2);
|
||||
g_test_add_func ("/path/intersect/circle3", test_intersect_circle3);
|
||||
g_test_add_func ("/path/intersect/circle4", test_intersect_circle4);
|
||||
g_test_add_func ("/path/intersect/circle5", test_intersect_circle5);
|
||||
g_test_add_func ("/path/intersect/circle6", test_intersect_circle6);
|
||||
g_test_add_func ("/path/intersect/circle7", test_intersect_circle7);
|
||||
g_test_add_func ("/path/intersect/circle-rounded-rect", test_intersect_circle_rounded_rect);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
* Copyright © 2022 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 <gtk/gtk.h>
|
||||
|
||||
#include "path-utils.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
OP_UNION,
|
||||
OP_INTERSECTION,
|
||||
OP_DIFFERENCE,
|
||||
OP_SYMMETRIC_DIFFERENCE,
|
||||
} Op;
|
||||
|
||||
static void
|
||||
test_ops_simple (void)
|
||||
{
|
||||
struct {
|
||||
const char *in1;
|
||||
const char *in2;
|
||||
Op op;
|
||||
const char *out;
|
||||
} tests[] = {
|
||||
/* partially overlapping edge */
|
||||
{ "M 100 100 L 100 200 L 200 200 Z",
|
||||
"M 150 150 L 150 250 L 250 250 Z",
|
||||
OP_UNION,
|
||||
"M 100 100 L 100 200 L 150 200 L 150 250 L 250 250 L 200 200 L 150 150 L 100 100 Z" },
|
||||
{ "M 100 100 L 100 200 L 200 200 Z",
|
||||
"M 150 150 L 150 250 L 250 250 Z",
|
||||
OP_INTERSECTION,
|
||||
"M 150 200 L 200 200 L 150 150 L 150 200 Z" },
|
||||
{ "M 100 100 L 100 200 L 200 200 Z",
|
||||
"M 150 150 L 150 250 L 250 250 Z",
|
||||
OP_DIFFERENCE,
|
||||
"M 100 100 L 100 200 L 150 200 L 150 150 L 100 100 Z" },
|
||||
{ "M 100 100 L 100 200 L 200 200 Z",
|
||||
"M 150 150 L 150 250 L 250 250 Z",
|
||||
OP_SYMMETRIC_DIFFERENCE,
|
||||
"M 100 100 L 100 200 L 150 200 L 150 150 L 100 100 Z M 200 200 L 150 200 L 150 250 "
|
||||
"L 250 250 L 200 200 Z" },
|
||||
/* two triangles in general position */
|
||||
{ "M 100 100 L 100 200 L 200 200 Z",
|
||||
"M 170 120 L 100 240 L 170 240 Z",
|
||||
OP_UNION,
|
||||
"M 100 100 L 100 200 L 123.33333587646484 200 L 100 240 L 170 240 L 170 200 L 200 200 "
|
||||
"L 170 170 L 170 120 L 151.57894897460938 151.57894897460938 L 100 100 Z" },
|
||||
{ "M 100 100 L 100 200 L 200 200 Z",
|
||||
"M 170 120 L 100 240 L 170 240 Z",
|
||||
OP_INTERSECTION,
|
||||
"M 123.33333587646484 200 L 170 200 L 170 170 L 151.57894897460938 151.57894897460938 "
|
||||
"L 123.33332824707031 200 Z" },
|
||||
{ "M 100 100 L 100 200 L 200 200 Z",
|
||||
"M 170 120 L 100 240 L 170 240 Z",
|
||||
OP_DIFFERENCE,
|
||||
"M 100 100 L 100 200 L 123.33333587646484 200 L 151.57894897460938 151.57894897460938 "
|
||||
"L 100 100 Z M 170 200 L 200 200 L 170 170 L 170 200 Z" },
|
||||
{ "M 100 100 L 100 200 L 200 200 Z",
|
||||
"M 170 120 L 100 240 L 170 240 Z",
|
||||
OP_SYMMETRIC_DIFFERENCE,
|
||||
"M 100 100 L 100 200 L 123.33333587646484 200 L 151.57894897460938 151.57894897460938 "
|
||||
"L 100 100 Z M 170 200 L 123.33333587646484 200 L 100 240 L 170 240 L 170 200 Z "
|
||||
"M 170 200 L 200 200 L 170 170 L 170 200 Z M 151.57894897460938 151.57894897460938 "
|
||||
"L 170 170 L 170 120 L 151.57894897460938 151.57894897460938 Z" },
|
||||
/* nested contours, oriented in opposite direction */
|
||||
{ "M 100 100 L 100 200 L 200 200 Z",
|
||||
"M 120 140 L 170 190 L 120 190 Z",
|
||||
OP_UNION,
|
||||
"M 100 100 L 100 200 L 200 200 L 100 100 Z" },
|
||||
{ "M 100 100 L 100 200 L 200 200 Z",
|
||||
"M 120 140 L 170 190 L 120 190 Z",
|
||||
OP_INTERSECTION,
|
||||
"M 170 190 L 120 140 L 120 190 L 170 190 Z" },
|
||||
{ "M 100 100 L 100 200 L 200 200 Z",
|
||||
"M 120 140 L 170 190 L 120 190 Z",
|
||||
OP_DIFFERENCE,
|
||||
"M 100 100 L 100 200 L 200 200 L 100 100 Z M 120 140 L 170 190 L 120 190 L 120 140 Z" },
|
||||
{ "M 100 100 L 100 200 L 200 200 Z",
|
||||
"M 120 140 L 170 190 L 120 190 Z",
|
||||
OP_SYMMETRIC_DIFFERENCE,
|
||||
"M 100 100 L 100 200 L 200 200 L 100 100 Z M 120 140 L 170 190 L 120 190 L 120 140 Z" },
|
||||
/* nested contours, oriented in opposite direction, other way around */
|
||||
{ "M 100 100 L 200 200 L 100 200 Z",
|
||||
"M 120 140 L 120 190 L 170 190 Z",
|
||||
OP_UNION,
|
||||
"M 200 200 L 100 100 L 100 200 L 200 200 Z" },
|
||||
{ "M 100 100 L 200 200 L 100 200 Z",
|
||||
"M 120 140 L 120 190 L 170 190 Z",
|
||||
OP_INTERSECTION,
|
||||
"M 120 140 L 120 190 L 170 190 L 120 140 Z" },
|
||||
{ "M 100 100 L 200 200 L 100 200 Z",
|
||||
"M 120 140 L 120 190 L 170 190 Z",
|
||||
OP_DIFFERENCE,
|
||||
"M 200 200 L 100 100 L 100 200 L 200 200 Z M 120 190 L 120 140 L 170 190 L 120 190 Z" },
|
||||
{ "M 100 100 L 200 200 L 100 200 Z",
|
||||
"M 120 140 L 120 190 L 170 190 Z",
|
||||
OP_SYMMETRIC_DIFFERENCE,
|
||||
"M 200 200 L 100 100 L 100 200 L 200 200 Z M 120 190 L 120 140 L 170 190 L 120 190 Z" },
|
||||
/* nested contours, oriented in the same direction */
|
||||
{ "M 100 100 L 100 200 L 200 200 Z",
|
||||
"M 120 140 L 120 190 L 170 190 Z",
|
||||
OP_UNION,
|
||||
"M 100 100 L 100 200 L 200 200 L 100 100 Z" },
|
||||
{ "M 100 100 L 100 200 L 200 200 Z",
|
||||
"M 120 140 L 120 190 L 170 190 Z",
|
||||
OP_INTERSECTION,
|
||||
"M 120 140 L 120 190 L 170 190 L 120 140 Z" },
|
||||
{ "M 100 100 L 100 200 L 200 200 Z",
|
||||
"M 120 140 L 120 190 L 170 190 Z",
|
||||
OP_DIFFERENCE,
|
||||
"M 100 100 L 100 200 L 200 200 L 100 100 Z M 120 190 L 120 140 L 170 190 L 120 190 Z" },
|
||||
{ "M 100 100 L 100 200 L 200 200 Z",
|
||||
"M 120 140 L 120 190 L 170 190 Z",
|
||||
OP_SYMMETRIC_DIFFERENCE,
|
||||
"M 100 100 L 100 200 L 200 200 L 100 100 Z M 120 190 L 120 140 L 170 190 L 120 190 Z" },
|
||||
/* a 3-way intersection */
|
||||
{ "M 100 200 L 150 104 L 145 104 L 200 200 Z",
|
||||
"M 100 108.571 L 200 108.571 L 200 50 L 100 50 Z",
|
||||
OP_UNION,
|
||||
"M 147.61904907226562 108.57142639160156 L 100 200 L 200 200 "
|
||||
"L 147.61904907226562 108.57142639160156 Z M 100 108.57099914550781 "
|
||||
"L 147.61927795410156 108.57099914550781 L 200 108.57099914550781 L 200 50 "
|
||||
"L 100 50 L 100 108.57099914550781 Z" },
|
||||
{ "M 100 200 L 150 104 L 145 104 L 200 200 Z",
|
||||
"M 100 108.571 L 200 108.571 L 200 50 L 100 50 Z",
|
||||
OP_INTERSECTION,
|
||||
"M 147.61904907226562 108.57142639160156 L 150 104 L 145 104 "
|
||||
"L 147.61904907226562 108.57142639160156 Z" },
|
||||
{ "M 100 200 L 150 104 L 145 104 L 200 200 Z",
|
||||
"M 100 108.571 L 200 108.571 L 200 50 L 100 50 Z",
|
||||
OP_DIFFERENCE,
|
||||
"M 147.61904907226562 108.57142639160156 L 100 200 L 200 200 "
|
||||
"L 147.61904907226562 108.57142639160156 Z" },
|
||||
{ "M 100 200 L 150 104 L 145 104 L 200 200 Z",
|
||||
"M 100 108.571 L 200 108.571 L 200 50 L 100 50 Z",
|
||||
OP_SYMMETRIC_DIFFERENCE,
|
||||
"M 147.61904907226562 108.57142639160156 L 100 200 L 200 200 "
|
||||
"L 147.61904907226562 108.57142639160156 Z M 150 104 "
|
||||
"L 147.61904907226562 108.57142639160156 L 200 108.57099914550781 "
|
||||
"L 200 50 L 100 50 L 100 108.57099914550781 L 147.61927795410156 108.57099914550781 "
|
||||
"L 145 104 L 150 104 Z" },
|
||||
/* touching quadratics */
|
||||
{ "M 100 100 Q 150 200 200 100 Z",
|
||||
"M 100 200 Q 150 100 200 200 Z",
|
||||
OP_UNION,
|
||||
"M 100 100 "
|
||||
"Q 124.987984 149.975967, 149.975967 149.999985 "
|
||||
"Q 174.987976 150.024033, 200 100 "
|
||||
"L 100 100 "
|
||||
"Z "
|
||||
"M 149.975967 150 "
|
||||
"Q 124.987984 150.024033, 100 200 "
|
||||
"L 200 200 "
|
||||
"Q 174.987976 149.975967, 149.975967 150.000015 "
|
||||
"Z" },
|
||||
/* overlapping quadratics, two intersections, different orientations */
|
||||
{ "M 100 100 Q 150 200 200 100 Z",
|
||||
"M 100 180 Q 150 80 200 180 Z",
|
||||
OP_UNION,
|
||||
"M 100 100 "
|
||||
"Q 113.819313 127.638626, 127.638626 139.999374 "
|
||||
"Q 113.819695 152.360611, 100 180 "
|
||||
"L 200 180 "
|
||||
"Q 186.180313 152.360611, 172.360611 139.999939 "
|
||||
"Q 186.180298 127.639389, 200 100 "
|
||||
"L 100 100 "
|
||||
"Z" },
|
||||
{ "M 100 100 Q 150 200 200 100 Z",
|
||||
"M 100 180 Q 150 80 200 180 Z",
|
||||
OP_INTERSECTION,
|
||||
"M 127.638626 139.99939 "
|
||||
"Q 149.999619 160.000275, 172.360611 140.000061 "
|
||||
"Q 150 120.000061, 127.639389 139.999939 "
|
||||
"Z" },
|
||||
{ "M 100 100 Q 150 200 200 100 Z",
|
||||
"M 100 180 Q 150 80 200 180 Z",
|
||||
OP_DIFFERENCE,
|
||||
"M 100 100 "
|
||||
"Q 113.819313 127.638626, 127.638626 139.999374 "
|
||||
"Q 150 120.000061, 172.360611 139.999939 "
|
||||
"Q 186.180298 127.639389, 200 100 "
|
||||
"L 100 100 "
|
||||
"Z" },
|
||||
{ "M 100 100 Q 150 200 200 100 Z",
|
||||
"M 100 180 Q 150 80 200 180 Z",
|
||||
OP_SYMMETRIC_DIFFERENCE,
|
||||
"M 100 100 "
|
||||
"Q 113.819313 127.638626, 127.638626 139.999374 "
|
||||
"Q 150 120.000061, 172.360611 139.999939 "
|
||||
"Q 186.180298 127.639389, 200 100 "
|
||||
"L 100 100 "
|
||||
"Z "
|
||||
"M 172.360611 140.000061 "
|
||||
"Q 149.999619 160.000275, 127.638626 139.999374 "
|
||||
"Q 113.819695 152.360611, 100 180 "
|
||||
"L 200 180 "
|
||||
"Q 186.180313 152.360611, 172.360611 139.999939 "
|
||||
"Z" },
|
||||
/* overlapping quadratics, two intersections, same orientation */
|
||||
{ "M 100 100 Q 150 200 200 100 Z",
|
||||
"M 100 180 L 200 180 Q 150 80 100 180 Z",
|
||||
OP_UNION,
|
||||
"M 100 100 "
|
||||
"Q 113.819313 127.638626, 127.638626 139.999374 "
|
||||
"Q 113.819695 152.360611, 100 180 "
|
||||
"L 200 180 "
|
||||
"Q 186.180695 152.361374, 172.361389 140.000626 "
|
||||
"Q 186.180298 127.639389, 200 100 "
|
||||
"L 100 100 "
|
||||
"Z" },
|
||||
{ "M 100 100 Q 150 200 200 100 Z",
|
||||
"M 100 180 L 200 180 Q 150 80 100 180 Z",
|
||||
OP_INTERSECTION,
|
||||
"M 127.638626 139.99939 "
|
||||
"Q 149.999619 160.000275, 172.360611 140.000061 "
|
||||
"Q 150.000397 119.999725, 127.639397 139.999939 "
|
||||
"Z" },
|
||||
{ "M 100 100 Q 150 200 200 100 Z",
|
||||
"M 100 180 L 200 180 Q 150 80 100 180 Z",
|
||||
OP_DIFFERENCE,
|
||||
"M 100 100 "
|
||||
"Q 113.819313 127.638626, 127.638626 139.999374 "
|
||||
"Q 150.000397 119.999725, 172.361389 140.000626 "
|
||||
"Q 186.180298 127.639389, 200 100 "
|
||||
"L 100 100 "
|
||||
"Z" },
|
||||
{ "M 100 100 Q 150 200 200 100 Z",
|
||||
"M 100 180 L 200 180 Q 150 80 100 180 Z",
|
||||
OP_SYMMETRIC_DIFFERENCE,
|
||||
"M 100 100 "
|
||||
"Q 113.819313 127.638626, 127.638626 139.999374 "
|
||||
"Q 150.000397 119.999725, 172.361389 140.000626 "
|
||||
"Q 186.180298 127.639389, 200 100 "
|
||||
"L 100 100 "
|
||||
"Z "
|
||||
"M 172.360611 140.000061 "
|
||||
"Q 149.999619 160.000275, 127.638626 139.999374 "
|
||||
"Q 113.819695 152.360611, 100 180 "
|
||||
"L 200 180 "
|
||||
"Q 186.180695 152.361374, 172.361389 140.000626 "
|
||||
"Z" },
|
||||
/* two polygons with near edges */
|
||||
{ "M 100 100 L 100 200 L 400 200 L 400 100 Z",
|
||||
"M 150 103 L 250 100 L 300 103 L 250 180 Z",
|
||||
OP_UNION,
|
||||
"M 100 100 L 100 200 L 400 200 L 400 100 L 250 100 L 100 100 Z" },
|
||||
{ "M 100 100 L 100 200 L 400 200 L 400 100 Z",
|
||||
"M 150 103 L 250 100 L 300 103 L 250 180 Z",
|
||||
OP_INTERSECTION,
|
||||
"M 250 100 L 150 103 L 250 180 L 300 103 L 250 100 Z" },
|
||||
{ "M 100 100 L 100 200 L 400 200 L 400 100 Z",
|
||||
"M 150 103 L 250 100 L 300 103 L 250 180 Z",
|
||||
OP_DIFFERENCE,
|
||||
"M 100 100 L 100 200 L 400 200 L 400 100 L 250 100 L 300 103 L 250 180 L 150 103 L 250 100 L 100 100 Z" },
|
||||
{ "M 100 100 L 100 200 L 400 200 L 400 100 Z",
|
||||
"M 150 103 L 250 100 L 300 103 L 250 180 Z",
|
||||
OP_SYMMETRIC_DIFFERENCE,
|
||||
"M 100 100 L 100 200 L 400 200 L 400 100 L 250 100 L 300 103 L 250 180 L 150 103 L 250 100 L 100 100 Z" },
|
||||
/* Collinear line segments */
|
||||
{ "M 100 100 L 200 100 L 250 100 L 100 200 Z",
|
||||
"M 150 100 L 300 100 L 300 200 Z",
|
||||
OP_UNION,
|
||||
"M 150 100 "
|
||||
"L 100 100 "
|
||||
"L 100 200 "
|
||||
"L 200 133.333328 "
|
||||
"L 300 200 "
|
||||
"L 300 100 "
|
||||
"L 250 100 "
|
||||
"L 200 100 "
|
||||
"L 150 100 "
|
||||
"Z" },
|
||||
{ "M 100 100 L 200 100 L 250 100 L 100 200 Z",
|
||||
"M 150 100 L 300 100 L 300 200 Z",
|
||||
OP_INTERSECTION,
|
||||
"M 200 100 "
|
||||
"L 150 100 "
|
||||
"L 200 133.333328 "
|
||||
"L 250 100 "
|
||||
"L 200 100 "
|
||||
"Z" },
|
||||
{ "M 100 100 L 200 100 L 250 100 L 100 200 Z",
|
||||
"M 150 100 L 300 100 L 300 200 Z",
|
||||
OP_DIFFERENCE,
|
||||
"M 150 100 L 100 100 L 100 200 L 200 133.33332824707031 L 150 100 Z" },
|
||||
{ "M 100 100 L 200 100 L 250 100 L 100 200 Z",
|
||||
"M 150 100 L 300 100 L 300 200 Z",
|
||||
OP_SYMMETRIC_DIFFERENCE,
|
||||
"M 150 100 L 100 100 L 100 200 L 200 133.33332824707031 L 150 100 Z "
|
||||
"M 250 100 L 200 133.33332824707031 L 300 200 L 300 100 L 250 100 Z" },
|
||||
/* a complicated union */
|
||||
{ "M 175 100 L 175 400 L 300 400 L 300 100 z",
|
||||
"M 100 100 C 200 200 200 300 100 400 L 0 400 C 233.3333334 300 233.3333334 200 0 100 Z",
|
||||
OP_UNION,
|
||||
"M 175 100 "
|
||||
"L 175 250 "
|
||||
"L 175 400 "
|
||||
"L 300 400 "
|
||||
"L 300 100 "
|
||||
"L 175 100 "
|
||||
"Z "
|
||||
"M 175 250 "
|
||||
"Q 175 175, 100 100 "
|
||||
"L 0 100 "
|
||||
"Q 174.955811 174.981064, 174.999985 249.962112 "
|
||||
"Z "
|
||||
"M 100 400 "
|
||||
"Q 175 325, 175 250 "
|
||||
"Q 175.044189 324.981049, 0 400 "
|
||||
"L 100 400 "
|
||||
"Z" },
|
||||
};
|
||||
|
||||
for (int i = 0; i < G_N_ELEMENTS (tests); i++)
|
||||
{
|
||||
GskPath *p1, *p2, *p3, *p;
|
||||
|
||||
if (g_test_verbose ())
|
||||
{
|
||||
const char *opname[] = { "union", "intersection", "difference", "symmetric-difference" };
|
||||
g_test_message ("testcase %d op %s \"%s\" \"%s\"", i, opname[tests[i].op], tests[i].in1, tests[i].in2);
|
||||
}
|
||||
|
||||
p1 = gsk_path_parse (tests[i].in1);
|
||||
p2 = gsk_path_parse (tests[i].in2);
|
||||
switch (tests[i].op)
|
||||
{
|
||||
case OP_UNION:
|
||||
p = gsk_path_union (p1, p2, GSK_FILL_RULE_WINDING);
|
||||
break;
|
||||
case OP_INTERSECTION:
|
||||
p = gsk_path_intersection (p1, p2, GSK_FILL_RULE_WINDING);
|
||||
break;
|
||||
case OP_DIFFERENCE:
|
||||
p = gsk_path_difference (p1, p2, GSK_FILL_RULE_WINDING);
|
||||
break;
|
||||
case OP_SYMMETRIC_DIFFERENCE:
|
||||
p = gsk_path_symmetric_difference (p1, p2, GSK_FILL_RULE_WINDING);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
g_assert_nonnull (p);
|
||||
p3 = gsk_path_parse (tests[i].out);
|
||||
assert_path_equal_with_epsilon (p, p3, 0.0001);
|
||||
|
||||
gsk_path_unref (p);
|
||||
gsk_path_unref (p1);
|
||||
gsk_path_unref (p2);
|
||||
gsk_path_unref (p3);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
gtk_test_init (&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/ops/simple", test_ops_simple);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/* Copyright 2023 Red Hat, Inc.
|
||||
*
|
||||
* GTK+ is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* GLib 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 GTK+; see the file COPYING. If not,
|
||||
* see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Matthias Clasen
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include "gtk-path-tool.h"
|
||||
|
||||
#include <glib/gi18n-lib.h>
|
||||
|
||||
void
|
||||
do_pathop (const char *op, int *argc, const char ***argv)
|
||||
{
|
||||
GError *error = NULL;
|
||||
const char *fill = "winding";
|
||||
char **args = NULL;
|
||||
GOptionContext *context;
|
||||
GOptionEntry entries[] = {
|
||||
{ "fill-rule", 0, 0, G_OPTION_ARG_STRING, &fill, N_("Fill rule"), N_("RULE") },
|
||||
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &args, NULL, N_("PATH…") },
|
||||
{ NULL, },
|
||||
};
|
||||
GskPath *path1, *path2, *result;
|
||||
GskFillRule fill_rule;
|
||||
char *prgname;
|
||||
char *summary;
|
||||
|
||||
prgname = g_strconcat ("gtk4-path-tool ", op, NULL);
|
||||
summary = g_strdup_printf (_("Apply the %s path operation."), op);
|
||||
g_set_prgname (prgname);
|
||||
|
||||
context = g_option_context_new (NULL);
|
||||
g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
|
||||
g_option_context_add_main_entries (context, entries, NULL);
|
||||
g_option_context_set_summary (context, summary);
|
||||
|
||||
if (!g_option_context_parse (context, argc, (char ***)argv, &error))
|
||||
{
|
||||
g_printerr ("%s\n", error->message);
|
||||
g_error_free (error);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
g_option_context_free (context);
|
||||
|
||||
if (args == NULL)
|
||||
{
|
||||
g_printerr ("%s\n", _("No paths given."));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
path1 = get_path (args[0]);
|
||||
if (args[1] != NULL)
|
||||
path2 = get_path (args[1]);
|
||||
else
|
||||
path2 = NULL;
|
||||
|
||||
fill_rule = get_enum_value (GSK_TYPE_FILL_RULE, _("fill rule"), fill);
|
||||
|
||||
if (strcmp (op, "simplify") == 0)
|
||||
result = gsk_path_simplify (path1, fill_rule);
|
||||
else if (strcmp (op, "union") == 0)
|
||||
result = gsk_path_union (path1, path2, fill_rule);
|
||||
else if (strcmp (op, "intersection") == 0)
|
||||
result = gsk_path_intersection (path1, path2, fill_rule);
|
||||
else if (strcmp (op, "difference") == 0)
|
||||
result = gsk_path_difference (path1, path2, fill_rule);
|
||||
else if (strcmp (op, "symmetric-difference") == 0)
|
||||
result = gsk_path_symmetric_difference (path1, path2, fill_rule);
|
||||
else
|
||||
{
|
||||
char *msg = g_strdup_printf (_("'%s' is not a supported operation."), op);
|
||||
g_printerr ("%s\n", msg);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
char *str = gsk_path_to_string (result);
|
||||
g_print ("%s\n", str);
|
||||
g_free (str);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_printerr ("%s\n", _("That didn't work out."));
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
+29
-11
@@ -32,12 +32,14 @@
|
||||
#include "path-view.h"
|
||||
|
||||
static void
|
||||
show_path_fill (GskPath *path,
|
||||
show_path_fill (GskPath *path1,
|
||||
GskPath *path2,
|
||||
GskFillRule fill_rule,
|
||||
const GdkRGBA *fg_color,
|
||||
const GdkRGBA *bg_color,
|
||||
gboolean show_points,
|
||||
gboolean show_controls,
|
||||
gboolean show_intersections,
|
||||
const GdkRGBA *point_color)
|
||||
{
|
||||
GtkWidget *window, *sw, *child;
|
||||
@@ -52,14 +54,17 @@ show_path_fill (GskPath *path,
|
||||
gtk_scrolled_window_set_propagate_natural_height (GTK_SCROLLED_WINDOW (sw), TRUE);
|
||||
gtk_window_set_child (GTK_WINDOW (window), sw);
|
||||
|
||||
child = path_view_new (path);
|
||||
child = g_object_new (PATH_TYPE_VIEW, NULL);
|
||||
g_object_set (child,
|
||||
"path1", path1,
|
||||
"path2", path2,
|
||||
"do-fill", TRUE,
|
||||
"fill-rule", fill_rule,
|
||||
"fg-color", fg_color,
|
||||
"bg-color", bg_color,
|
||||
"show-points", show_points,
|
||||
"show-controls", show_controls,
|
||||
"show-intersections", show_intersections,
|
||||
"point-color", point_color,
|
||||
NULL);
|
||||
|
||||
@@ -74,12 +79,14 @@ show_path_fill (GskPath *path,
|
||||
}
|
||||
|
||||
static void
|
||||
show_path_stroke (GskPath *path,
|
||||
show_path_stroke (GskPath *path1,
|
||||
GskPath *path2,
|
||||
GskStroke *stroke,
|
||||
const GdkRGBA *fg_color,
|
||||
const GdkRGBA *bg_color,
|
||||
gboolean show_points,
|
||||
gboolean show_controls,
|
||||
gboolean show_intersections,
|
||||
const GdkRGBA *point_color)
|
||||
{
|
||||
GtkWidget *window, *sw, *child;
|
||||
@@ -94,14 +101,17 @@ show_path_stroke (GskPath *path,
|
||||
gtk_scrolled_window_set_propagate_natural_height (GTK_SCROLLED_WINDOW (sw), TRUE);
|
||||
gtk_window_set_child (GTK_WINDOW (window), sw);
|
||||
|
||||
child = path_view_new (path);
|
||||
child = g_object_new (PATH_TYPE_VIEW, NULL);
|
||||
g_object_set (child,
|
||||
"path1", path1,
|
||||
"path2", path2,
|
||||
"do-fill", FALSE,
|
||||
"stroke", stroke,
|
||||
"fg-color", fg_color,
|
||||
"bg-color", bg_color,
|
||||
"show-points", show_points,
|
||||
"show-controls", show_controls,
|
||||
"show-intersections", show_intersections,
|
||||
"point-color", point_color,
|
||||
NULL);
|
||||
|
||||
@@ -123,6 +133,7 @@ do_show (int *argc,
|
||||
gboolean do_stroke = FALSE;
|
||||
gboolean show_points = FALSE;
|
||||
gboolean show_controls = FALSE;
|
||||
gboolean show_intersections = FALSE;
|
||||
const char *fill = "winding";
|
||||
const char *fg_color = "black";
|
||||
const char *bg_color = "white";
|
||||
@@ -141,6 +152,7 @@ do_show (int *argc,
|
||||
{ "stroke", 0, 0, G_OPTION_ARG_NONE, &do_stroke, N_("Stroke the path"), NULL },
|
||||
{ "points", 0, 0, G_OPTION_ARG_NONE, &show_points, N_("Show path points"), NULL },
|
||||
{ "controls", 0, 0, G_OPTION_ARG_NONE, &show_controls, N_("Show control points"), NULL },
|
||||
{ "intersections", 0, 0, G_OPTION_ARG_NONE, &show_intersections, N_("Show intersections"), NULL },
|
||||
{ "fg-color", 0, 0, G_OPTION_ARG_STRING, &fg_color, N_("Foreground color"), N_("COLOR") },
|
||||
{ "bg-color", 0, 0, G_OPTION_ARG_STRING, &bg_color, N_("Background color"), N_("COLOR") },
|
||||
{ "point-color", 0, 0, G_OPTION_ARG_STRING, &point_color, N_("Point color"), N_("COLOR") },
|
||||
@@ -160,7 +172,8 @@ do_show (int *argc,
|
||||
{ "dash-offset", 0, 0, G_OPTION_ARG_DOUBLE, &dash_offset, N_("Dash offset (number)"), N_("VALUE") },
|
||||
{ NULL, }
|
||||
};
|
||||
GskPath *path;
|
||||
GskPath *path1;
|
||||
GskPath *path2;
|
||||
GskFillRule fill_rule;
|
||||
GdkRGBA fg, bg, pc;
|
||||
GskLineCap line_cap;
|
||||
@@ -210,13 +223,17 @@ do_show (int *argc,
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (g_strv_length (args) > 1)
|
||||
if (g_strv_length (args) > 2)
|
||||
{
|
||||
g_printerr ("%s\n", _("Can only show a single path"));
|
||||
g_printerr ("%s\n", _("Can only show one or two paths"));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
path = get_path (args[0]);
|
||||
path1 = get_path (args[0]);
|
||||
if (g_strv_length (args) > 1)
|
||||
path2 = get_path (args[1]);
|
||||
else
|
||||
path2 = NULL;
|
||||
|
||||
fill_rule = get_enum_value (GSK_TYPE_FILL_RULE, _("fill rule"), fill);
|
||||
get_color (&fg, fg_color);
|
||||
@@ -234,11 +251,12 @@ do_show (int *argc,
|
||||
_gsk_stroke_set_dashes (stroke, dashes);
|
||||
|
||||
if (do_stroke)
|
||||
show_path_stroke (path, stroke, &fg, &bg, show_points, show_controls, &pc);
|
||||
show_path_stroke (path1, path2, stroke, &fg, &bg, show_points, show_controls, show_intersections, &pc);
|
||||
else
|
||||
show_path_fill (path, fill_rule, &fg, &bg, show_points, show_controls, &pc);
|
||||
show_path_fill (path1, path2, fill_rule, &fg, &bg, show_points, show_controls, show_intersections, &pc);
|
||||
|
||||
gsk_path_unref (path);
|
||||
g_clear_pointer (&path1, gsk_path_unref);
|
||||
g_clear_pointer (&path2, gsk_path_unref);
|
||||
|
||||
g_strfreev (args);
|
||||
}
|
||||
|
||||
@@ -38,6 +38,12 @@ usage (void)
|
||||
"Perform various tasks on paths.\n"
|
||||
"\n"
|
||||
"Commands:\n"
|
||||
" simplify Simplify the path\n"
|
||||
" intersection Intersect two paths\n"
|
||||
" union Create the union of two paths\n"
|
||||
" difference Create the difference of two paths\n"
|
||||
" symmetric-difference\n"
|
||||
" Create the symmetric difference of two paths\n"
|
||||
" decompose Decompose the path\n"
|
||||
" reverse Reverse the path\n"
|
||||
" restrict Restrict the path to a segment\n"
|
||||
@@ -128,8 +134,12 @@ main (int argc, const char *argv[])
|
||||
|
||||
if (strcmp (argv[0], "decompose") == 0)
|
||||
do_decompose (&argc, &argv);
|
||||
else if (strcmp (argv[0], "difference") == 0)
|
||||
do_pathop (argv[0], &argc, &argv);
|
||||
else if (strcmp (argv[0], "info") == 0)
|
||||
do_info (&argc, &argv);
|
||||
else if (strcmp (argv[0], "intersection") == 0)
|
||||
do_pathop (argv[0], &argc, &argv);
|
||||
else if (strcmp (argv[0], "render") == 0)
|
||||
do_render (&argc, &argv);
|
||||
else if (strcmp (argv[0], "restrict") == 0)
|
||||
@@ -138,6 +148,12 @@ main (int argc, const char *argv[])
|
||||
do_reverse (&argc, &argv);
|
||||
else if (strcmp (argv[0], "show") == 0)
|
||||
do_show (&argc, &argv);
|
||||
else if (strcmp (argv[0], "simplify") == 0)
|
||||
do_pathop (argv[0], &argc, &argv);
|
||||
else if (strcmp (argv[0], "symmetric-difference") == 0)
|
||||
do_pathop (argv[0], &argc, &argv);
|
||||
else if (strcmp (argv[0], "union") == 0)
|
||||
do_pathop (argv[0], &argc, &argv);
|
||||
else
|
||||
usage ();
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ void do_restrict (int *argc, const char ***argv);
|
||||
void do_reverse (int *argc, const char ***argv);
|
||||
void do_render (int *argc, const char ***argv);
|
||||
void do_show (int *argc, const char ***argv);
|
||||
void do_pathop (const char *op, int *argc, const char ***argv);
|
||||
|
||||
GskPath *get_path (const char *arg);
|
||||
int get_enum_value (GType type,
|
||||
|
||||
@@ -26,6 +26,7 @@ gtk_tools = [
|
||||
['gtk4-path-tool', ['gtk-path-tool.c',
|
||||
'gtk-path-tool-decompose.c',
|
||||
'gtk-path-tool-info.c',
|
||||
'gtk-path-tool-pathops.c',
|
||||
'gtk-path-tool-render.c',
|
||||
'gtk-path-tool-restrict.c',
|
||||
'gtk-path-tool-reverse.c',
|
||||
|
||||
+150
-12
@@ -25,6 +25,8 @@ struct _PathView
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
|
||||
GskPath *path1;
|
||||
GskPath *path2;
|
||||
GskPath *path;
|
||||
GskStroke *stroke;
|
||||
graphene_rect_t bounds;
|
||||
@@ -35,13 +37,17 @@ struct _PathView
|
||||
gboolean do_fill;
|
||||
gboolean show_points;
|
||||
gboolean show_controls;
|
||||
gboolean show_intersections;
|
||||
GskPath *line_path;
|
||||
GskPath *point_path;
|
||||
GdkRGBA point_color;
|
||||
GskPath *intersection_line_path;
|
||||
GskPath *intersection_point_path;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_PATH = 1,
|
||||
PROP_PATH1 = 1,
|
||||
PROP_PATH2,
|
||||
PROP_DO_FILL,
|
||||
PROP_STROKE,
|
||||
PROP_FILL_RULE,
|
||||
@@ -50,6 +56,7 @@ enum {
|
||||
PROP_POINT_COLOR,
|
||||
PROP_SHOW_POINTS,
|
||||
PROP_SHOW_CONTROLS,
|
||||
PROP_SHOW_INTERSECTIONS,
|
||||
N_PROPERTIES
|
||||
};
|
||||
|
||||
@@ -79,10 +86,14 @@ path_view_dispose (GObject *object)
|
||||
{
|
||||
PathView *self = PATH_VIEW (object);
|
||||
|
||||
g_clear_pointer (&self->path1, gsk_path_unref);
|
||||
g_clear_pointer (&self->path2, gsk_path_unref);
|
||||
g_clear_pointer (&self->path, gsk_path_unref);
|
||||
g_clear_pointer (&self->stroke, gsk_stroke_free);
|
||||
g_clear_pointer (&self->line_path, gsk_path_unref);
|
||||
g_clear_pointer (&self->point_path, gsk_path_unref);
|
||||
g_clear_pointer (&self->intersection_line_path, gsk_path_unref);
|
||||
g_clear_pointer (&self->intersection_point_path, gsk_path_unref);
|
||||
|
||||
G_OBJECT_CLASS (path_view_parent_class)->dispose (object);
|
||||
}
|
||||
@@ -97,8 +108,12 @@ path_view_get_property (GObject *object,
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_PATH:
|
||||
g_value_set_boxed (value, self->path);
|
||||
case PROP_PATH1:
|
||||
g_value_set_boxed (value, self->path1);
|
||||
break;
|
||||
|
||||
case PROP_PATH2:
|
||||
g_value_set_boxed (value, self->path2);
|
||||
break;
|
||||
|
||||
case PROP_DO_FILL:
|
||||
@@ -129,6 +144,10 @@ path_view_get_property (GObject *object,
|
||||
g_value_set_boolean (value, self->show_controls);
|
||||
break;
|
||||
|
||||
case PROP_SHOW_INTERSECTIONS:
|
||||
g_value_set_boolean (value, self->show_intersections);
|
||||
break;
|
||||
|
||||
case PROP_POINT_COLOR:
|
||||
g_value_set_boxed (value, &self->point_color);
|
||||
break;
|
||||
@@ -268,6 +287,93 @@ update_controls (PathView *self)
|
||||
update_bounds (self);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GskPathBuilder *line_builder;
|
||||
GskPathBuilder *point_builder;
|
||||
GskPathPoint start;
|
||||
int segment;
|
||||
} IntersectionData;
|
||||
|
||||
static gboolean
|
||||
intersection_cb (GskPath *path1,
|
||||
const GskPathPoint *point1,
|
||||
GskPath *path2,
|
||||
const GskPathPoint *point2,
|
||||
GskPathIntersection kind,
|
||||
gpointer data)
|
||||
{
|
||||
IntersectionData *id = data;
|
||||
graphene_point_t pos;
|
||||
|
||||
switch (kind)
|
||||
{
|
||||
case GSK_PATH_INTERSECTION_NORMAL:
|
||||
gsk_path_point_get_position (point1, path1, &pos);
|
||||
gsk_path_builder_add_circle (id->point_builder, &pos, 3);
|
||||
break;
|
||||
|
||||
case GSK_PATH_INTERSECTION_START:
|
||||
if (id->segment == 0)
|
||||
id->start = *point1;
|
||||
id->segment++;
|
||||
break;
|
||||
|
||||
case GSK_PATH_INTERSECTION_END:
|
||||
id->segment--;
|
||||
if (id->segment == 0)
|
||||
gsk_path_builder_add_segment (id->line_builder, path1, &id->start, point1);
|
||||
break;
|
||||
|
||||
case GSK_PATH_INTERSECTION_NONE:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
update_intersections (PathView *self)
|
||||
{
|
||||
IntersectionData id;
|
||||
|
||||
g_clear_pointer (&self->intersection_line_path, gsk_path_unref);
|
||||
g_clear_pointer (&self->intersection_point_path, gsk_path_unref);
|
||||
|
||||
if (!self->show_intersections || !self->path1 || !self->path2)
|
||||
return;
|
||||
|
||||
id.line_builder = gsk_path_builder_new ();
|
||||
id.point_builder = gsk_path_builder_new ();
|
||||
id.segment = 0;
|
||||
|
||||
gsk_path_foreach_intersection (self->path1, self->path2, intersection_cb, &id);
|
||||
|
||||
self->intersection_line_path = gsk_path_builder_free_to_path (id.line_builder);
|
||||
self->intersection_point_path = gsk_path_builder_free_to_path (id.point_builder);
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
update_path (PathView *self)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
|
||||
g_clear_pointer (&self->path, gsk_path_unref);
|
||||
|
||||
builder = gsk_path_builder_new ();
|
||||
if (self->path1)
|
||||
gsk_path_builder_add_path (builder, self->path1);
|
||||
|
||||
if (self->path2)
|
||||
gsk_path_builder_add_path (builder, self->path2);
|
||||
|
||||
self->path = gsk_path_builder_free_to_path (builder);
|
||||
|
||||
update_intersections (self);
|
||||
update_controls (self);
|
||||
}
|
||||
|
||||
static void
|
||||
path_view_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
@@ -278,11 +384,16 @@ path_view_set_property (GObject *object,
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_PATH1:
|
||||
g_clear_pointer (&self->path1, gsk_path_unref);
|
||||
self->path1 = g_value_dup_boxed (value);
|
||||
update_path (self);
|
||||
break;
|
||||
|
||||
case PROP_PATH:
|
||||
g_clear_pointer (&self->path, gsk_path_unref);
|
||||
self->path = g_value_dup_boxed (value);
|
||||
update_controls (self);
|
||||
case PROP_PATH2:
|
||||
g_clear_pointer (&self->path2, gsk_path_unref);
|
||||
self->path2 = g_value_dup_boxed (value);
|
||||
update_path (self);
|
||||
break;
|
||||
|
||||
case PROP_DO_FILL:
|
||||
@@ -321,6 +432,11 @@ path_view_set_property (GObject *object,
|
||||
update_controls (self);
|
||||
break;
|
||||
|
||||
case PROP_SHOW_INTERSECTIONS:
|
||||
self->show_intersections = g_value_get_boolean (value);
|
||||
update_intersections (self);
|
||||
break;
|
||||
|
||||
case PROP_POINT_COLOR:
|
||||
self->point_color = *(GdkRGBA *) g_value_get_boxed (value);
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
@@ -344,9 +460,9 @@ path_view_measure (GtkWidget *widget,
|
||||
PathView *self = PATH_VIEW (widget);
|
||||
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
*minimum = *natural = (int) ceilf (self->bounds.size.width) + 2 * self->padding;
|
||||
*minimum = *natural = (int) ceilf (self->bounds.origin.x + self->bounds.size.width) + 2 * self->padding;
|
||||
else
|
||||
*minimum = *natural = (int) ceilf (self->bounds.size.height) + 2 * self->padding;
|
||||
*minimum = *natural = (int) ceilf (self->bounds.origin.y + self->bounds.size.height) + 2 * self->padding;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -383,6 +499,18 @@ path_view_snapshot (GtkWidget *widget,
|
||||
gtk_snapshot_append_stroke (snapshot, self->point_path, stroke, &self->fg);
|
||||
}
|
||||
|
||||
if (self->intersection_line_path)
|
||||
{
|
||||
GskStroke *stroke = gsk_stroke_new (gsk_stroke_get_line_width (self->stroke));
|
||||
|
||||
gtk_snapshot_append_stroke (snapshot, self->intersection_line_path, stroke, &self->point_color);
|
||||
}
|
||||
|
||||
if (self->intersection_point_path)
|
||||
{
|
||||
gtk_snapshot_append_fill (snapshot, self->intersection_point_path, GSK_FILL_RULE_WINDING, &self->point_color);
|
||||
}
|
||||
|
||||
gtk_snapshot_restore (snapshot);
|
||||
}
|
||||
|
||||
@@ -399,8 +527,13 @@ path_view_class_init (PathViewClass *class)
|
||||
widget_class->measure = path_view_measure;
|
||||
widget_class->snapshot = path_view_snapshot;
|
||||
|
||||
properties[PROP_PATH]
|
||||
= g_param_spec_boxed ("path", NULL, NULL,
|
||||
properties[PROP_PATH1]
|
||||
= g_param_spec_boxed ("path1", NULL, NULL,
|
||||
GSK_TYPE_PATH,
|
||||
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
properties[PROP_PATH2]
|
||||
= g_param_spec_boxed ("path2", NULL, NULL,
|
||||
GSK_TYPE_PATH,
|
||||
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
@@ -440,6 +573,11 @@ path_view_class_init (PathViewClass *class)
|
||||
FALSE,
|
||||
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
properties[PROP_SHOW_INTERSECTIONS]
|
||||
= g_param_spec_boolean ("show-intersections", NULL, NULL,
|
||||
FALSE,
|
||||
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
properties[PROP_POINT_COLOR]
|
||||
= g_param_spec_boxed ("point-color", NULL, NULL,
|
||||
GDK_TYPE_RGBA,
|
||||
@@ -452,6 +590,6 @@ GtkWidget *
|
||||
path_view_new (GskPath *path)
|
||||
{
|
||||
return g_object_new (PATH_TYPE_VIEW,
|
||||
"path", path,
|
||||
"path1", path,
|
||||
NULL);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user