Compare commits
24 Commits
dmabug-tex
...
glyphy-no-
Author | SHA1 | Date | |
---|---|---|---|
|
e2c7936ceb | ||
|
6846a5aaa7 | ||
|
1cff54ea92 | ||
|
72a704d145 | ||
|
e2969bec04 | ||
|
370c47e595 | ||
|
922daf2ed7 | ||
|
f3b00d075e | ||
|
c1d7a820ab | ||
|
c09f6a8041 | ||
|
6f3ee51371 | ||
|
30c73365dd | ||
|
cc250beadc | ||
|
73a82faad3 | ||
|
a3d82174e6 | ||
|
ad9d916dfd | ||
|
5cadf635ab | ||
|
2d15c5e55f | ||
|
3195a1e6df | ||
|
b21a5d9a06 | ||
|
01d844a994 | ||
|
b41c45ab5b | ||
|
6f97c02c4d | ||
|
fffa490280 |
@@ -32,6 +32,7 @@
|
|||||||
#include "gskglcommandqueueprivate.h"
|
#include "gskglcommandqueueprivate.h"
|
||||||
#include "gskglcompilerprivate.h"
|
#include "gskglcompilerprivate.h"
|
||||||
#include "gskglglyphlibraryprivate.h"
|
#include "gskglglyphlibraryprivate.h"
|
||||||
|
#include "gskglglyphylibraryprivate.h"
|
||||||
#include "gskgliconlibraryprivate.h"
|
#include "gskgliconlibraryprivate.h"
|
||||||
#include "gskglprogramprivate.h"
|
#include "gskglprogramprivate.h"
|
||||||
#include "gskglshadowlibraryprivate.h"
|
#include "gskglshadowlibraryprivate.h"
|
||||||
@@ -273,6 +274,7 @@ gsk_gl_driver_dispose (GObject *object)
|
|||||||
}
|
}
|
||||||
|
|
||||||
g_clear_object (&self->glyphs_library);
|
g_clear_object (&self->glyphs_library);
|
||||||
|
g_clear_object (&self->glyphy_library);
|
||||||
g_clear_object (&self->icons_library);
|
g_clear_object (&self->icons_library);
|
||||||
g_clear_object (&self->shadows_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->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->icons_library = gsk_gl_icon_library_new (self);
|
||||||
self->shadows_library = gsk_gl_shadow_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);
|
self->current_frame_id);
|
||||||
gsk_gl_texture_library_begin_frame (GSK_GL_TEXTURE_LIBRARY (self->glyphs_library),
|
gsk_gl_texture_library_begin_frame (GSK_GL_TEXTURE_LIBRARY (self->glyphs_library),
|
||||||
self->current_frame_id);
|
self->current_frame_id);
|
||||||
|
gsk_gl_texture_library_begin_frame (GSK_GL_TEXTURE_LIBRARY (self->glyphy_library),
|
||||||
|
self->current_frame_id);
|
||||||
|
|
||||||
/* Cleanup old shadows */
|
/* Cleanup old shadows */
|
||||||
gsk_gl_shadow_library_begin_frame (self->shadows_library);
|
gsk_gl_shadow_library_begin_frame (self->shadows_library);
|
||||||
|
@@ -97,6 +97,7 @@ struct _GskGLDriver
|
|||||||
GskGLCommandQueue *command_queue;
|
GskGLCommandQueue *command_queue;
|
||||||
|
|
||||||
GskGLGlyphLibrary *glyphs_library;
|
GskGLGlyphLibrary *glyphs_library;
|
||||||
|
GskGLGlyphyLibrary *glyphy_library;
|
||||||
GskGLIconLibrary *icons_library;
|
GskGLIconLibrary *icons_library;
|
||||||
GskGLShadowLibrary *shadows_library;
|
GskGLShadowLibrary *shadows_library;
|
||||||
|
|
||||||
|
544
gsk/gl/gskglglyphylibrary.c
Normal file
544
gsk/gl/gskglglyphylibrary.c
Normal file
@@ -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;
|
||||||
|
}
|
142
gsk/gl/gskglglyphylibraryprivate.h
Normal file
142
gsk/gl/gskglglyphylibraryprivate.h
Normal file
@@ -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 (1, UNBLURRED_OUTSET_SHADOW_SPREAD, u_spread)
|
||||||
GSK_GL_ADD_UNIFORM (2, UNBLURRED_OUTSET_SHADOW_OFFSET, u_offset)
|
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_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_gl_command_queue_set_profiler (self->command_queue,
|
||||||
gsk_renderer_get_profiler (renderer));
|
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;
|
ret = TRUE;
|
||||||
|
|
||||||
failure:
|
failure:
|
||||||
@@ -307,10 +314,15 @@ gsk_gl_renderer_render (GskRenderer *renderer,
|
|||||||
|
|
||||||
gsk_gl_driver_begin_frame (self->driver, self->command_queue);
|
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);
|
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
|
#ifdef G_ENABLE_DEBUG
|
||||||
if (GSK_RENDERER_DEBUG_CHECK (GSK_RENDERER (self), FALLBACK))
|
if (GSK_RENDERER_DEBUG_CHECK (GSK_RENDERER (self), FALLBACK))
|
||||||
gsk_gl_render_job_set_debug_fallback (job, TRUE);
|
gsk_gl_render_job_set_debug_fallback (job, TRUE);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
gsk_gl_render_job_render (job, root);
|
gsk_gl_render_job_render (job, root);
|
||||||
gsk_gl_driver_end_frame (self->driver);
|
gsk_gl_driver_end_frame (self->driver);
|
||||||
gsk_gl_render_job_free (job);
|
gsk_gl_render_job_free (job);
|
||||||
|
@@ -39,6 +39,7 @@
|
|||||||
#include "gskglcommandqueueprivate.h"
|
#include "gskglcommandqueueprivate.h"
|
||||||
#include "gskgldriverprivate.h"
|
#include "gskgldriverprivate.h"
|
||||||
#include "gskglglyphlibraryprivate.h"
|
#include "gskglglyphlibraryprivate.h"
|
||||||
|
#include "gskglglyphylibraryprivate.h"
|
||||||
#include "gskgliconlibraryprivate.h"
|
#include "gskgliconlibraryprivate.h"
|
||||||
#include "gskglprogramprivate.h"
|
#include "gskglprogramprivate.h"
|
||||||
#include "gskglrenderjobprivate.h"
|
#include "gskglrenderjobprivate.h"
|
||||||
@@ -152,6 +153,9 @@ struct _GskGLRenderJob
|
|||||||
*/
|
*/
|
||||||
guint clear_framebuffer : 1;
|
guint clear_framebuffer : 1;
|
||||||
|
|
||||||
|
/* Allow experimental glyph rendering with glyphy */
|
||||||
|
guint use_glyphy : 1;
|
||||||
|
|
||||||
/* Format we want to use for intermediate textures, determined by
|
/* Format we want to use for intermediate textures, determined by
|
||||||
* looking at the format of the framebuffer we are rendering on.
|
* looking at the format of the framebuffer we are rendering on.
|
||||||
*/
|
*/
|
||||||
@@ -2953,10 +2957,10 @@ compute_phase_and_pos (float value, float *pos)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
gsk_gl_render_job_visit_text_node (GskGLRenderJob *job,
|
gsk_gl_render_job_visit_text_node_legacy (GskGLRenderJob *job,
|
||||||
const GskRenderNode *node,
|
const GskRenderNode *node,
|
||||||
const GdkRGBA *color,
|
const GdkRGBA *color,
|
||||||
gboolean force_color)
|
gboolean force_color)
|
||||||
{
|
{
|
||||||
const PangoFont *font = gsk_text_node_get_font (node);
|
const PangoFont *font = gsk_text_node_get_font (node);
|
||||||
const PangoGlyphInfo *glyphs = gsk_text_node_get_glyphs (node, NULL);
|
const PangoGlyphInfo *glyphs = gsk_text_node_get_glyphs (node, NULL);
|
||||||
@@ -3093,6 +3097,234 @@ 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 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;
|
||||||
|
const PangoMatrix *matrix;
|
||||||
|
|
||||||
|
#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);
|
||||||
|
|
||||||
|
embolden = gsk_text_node_get_font_embolden (node);
|
||||||
|
matrix = gsk_text_node_get_font_matrix (node);
|
||||||
|
|
||||||
|
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
|
static inline void
|
||||||
gsk_gl_render_job_visit_shadow_node (GskGLRenderJob *job,
|
gsk_gl_render_job_visit_shadow_node (GskGLRenderJob *job,
|
||||||
const GskRenderNode *node)
|
const GskRenderNode *node)
|
||||||
@@ -4476,6 +4708,15 @@ gsk_gl_render_job_set_debug_fallback (GskGLRenderJob *job,
|
|||||||
job->debug_fallback = !!debug_fallback;
|
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
|
static int
|
||||||
get_framebuffer_format (GdkGLContext *context,
|
get_framebuffer_format (GdkGLContext *context,
|
||||||
guint framebuffer)
|
guint framebuffer)
|
||||||
|
@@ -35,4 +35,5 @@ void gsk_gl_render_job_render_flipped (GskGLRenderJob *job
|
|||||||
GskRenderNode *root);
|
GskRenderNode *root);
|
||||||
void gsk_gl_render_job_set_debug_fallback (GskGLRenderJob *job,
|
void gsk_gl_render_job_set_debug_fallback (GskGLRenderJob *job,
|
||||||
gboolean debug_fallback);
|
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
|
typedef struct _GskGLTextureAtlas
|
||||||
{
|
{
|
||||||
|
/* Used by Glyph/Icons */
|
||||||
struct stbrp_context context;
|
struct stbrp_context context;
|
||||||
struct stbrp_node *nodes;
|
struct stbrp_node *nodes;
|
||||||
|
|
||||||
|
/* Used by Glyphy */
|
||||||
|
int cursor_x;
|
||||||
|
int cursor_y;
|
||||||
|
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
|
|
||||||
@@ -48,7 +53,6 @@ typedef struct _GskGLTextureAtlas
|
|||||||
* But are now unused.
|
* But are now unused.
|
||||||
*/
|
*/
|
||||||
int unused_pixels;
|
int unused_pixels;
|
||||||
|
|
||||||
} GskGLTextureAtlas;
|
} GskGLTextureAtlas;
|
||||||
|
|
||||||
typedef struct _GskGLTextureAtlasEntry
|
typedef struct _GskGLTextureAtlasEntry
|
||||||
|
@@ -36,6 +36,7 @@ typedef struct _GskGLCompiler GskGLCompiler;
|
|||||||
typedef struct _GskGLDrawVertex GskGLDrawVertex;
|
typedef struct _GskGLDrawVertex GskGLDrawVertex;
|
||||||
typedef struct _GskGLRenderTarget GskGLRenderTarget;
|
typedef struct _GskGLRenderTarget GskGLRenderTarget;
|
||||||
typedef struct _GskGLGlyphLibrary GskGLGlyphLibrary;
|
typedef struct _GskGLGlyphLibrary GskGLGlyphLibrary;
|
||||||
|
typedef struct _GskGLGlyphyLibrary GskGLGlyphyLibrary;
|
||||||
typedef struct _GskGLIconLibrary GskGLIconLibrary;
|
typedef struct _GskGLIconLibrary GskGLIconLibrary;
|
||||||
typedef struct _GskGLProgram GskGLProgram;
|
typedef struct _GskGLProgram GskGLProgram;
|
||||||
typedef struct _GskGLRenderJob GskGLRenderJob;
|
typedef struct _GskGLRenderJob GskGLRenderJob;
|
||||||
|
15
gsk/gl/resources/glyphy.atlas.glsl
Normal file
15
gsk/gl/resources/glyphy.atlas.glsl
Normal file
@@ -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);
|
||||||
|
}
|
62
gsk/gl/resources/glyphy.fs.glsl
Normal file
62
gsk/gl/resources/glyphy.fs.glsl
Normal file
@@ -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);
|
||||||
|
}
|
25
gsk/gl/resources/glyphy.vs.glsl
Normal file
25
gsk/gl/resources/glyphy.vs.glsl
Normal file
@@ -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
|
#ifndef GSK_LEGACY
|
||||||
precision highp float;
|
precision highp float;
|
||||||
#endif
|
#endif
|
||||||
|
205
gsk/gskcurve.c
205
gsk/gskcurve.c
@@ -2330,6 +2330,30 @@ gsk_curve_get_crossing (const GskCurve *curve,
|
|||||||
return get_class (curve->op)->get_crossing (curve, point);
|
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
|
static gboolean
|
||||||
project_point_onto_line (const GskCurve *curve,
|
project_point_onto_line (const GskCurve *curve,
|
||||||
const graphene_point_t *point,
|
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);
|
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: */
|
/* vim:set foldmethod=marker expandtab: */
|
||||||
|
1115
gsk/gskcurveintersect.c
Normal file
1115
gsk/gskcurveintersect.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include "gskpathopprivate.h"
|
#include "gskpathopprivate.h"
|
||||||
#include "gskpath.h"
|
#include "gskpath.h"
|
||||||
|
#include "gskpathprivate.h"
|
||||||
#include "gskboundingboxprivate.h"
|
#include "gskboundingboxprivate.h"
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
@@ -187,6 +188,19 @@ int gsk_curve_get_curvature_points (const GskCurve
|
|||||||
int gsk_curve_get_cusps (const GskCurve *curve,
|
int gsk_curve_get_cusps (const GskCurve *curve,
|
||||||
float t[2]);
|
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
|
G_END_DECLS
|
||||||
|
|
||||||
|
@@ -17,6 +17,7 @@ static const GdkDebugKey gsk_debug_keys[] = {
|
|||||||
{ "full-redraw", GSK_DEBUG_FULL_REDRAW, "Force full redraws" },
|
{ "full-redraw", GSK_DEBUG_FULL_REDRAW, "Force full redraws" },
|
||||||
{ "sync", GSK_DEBUG_SYNC, "Sync after each frame" },
|
{ "sync", GSK_DEBUG_SYNC, "Sync after each frame" },
|
||||||
{ "staging", GSK_DEBUG_STAGING, "Use a staging image for texture upload (Vulkan only)" },
|
{ "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;
|
static guint gsk_debug_flags;
|
||||||
|
@@ -19,7 +19,8 @@ typedef enum {
|
|||||||
GSK_DEBUG_GEOMETRY = 1 << 9,
|
GSK_DEBUG_GEOMETRY = 1 << 9,
|
||||||
GSK_DEBUG_FULL_REDRAW = 1 << 10,
|
GSK_DEBUG_FULL_REDRAW = 1 << 10,
|
||||||
GSK_DEBUG_SYNC = 1 << 11,
|
GSK_DEBUG_SYNC = 1 << 11,
|
||||||
GSK_DEBUG_STAGING = 1 << 12
|
GSK_DEBUG_STAGING = 1 << 12,
|
||||||
|
GSK_DEBUG_NO_GLYPHY = 1 << 13
|
||||||
} GskDebugFlags;
|
} GskDebugFlags;
|
||||||
|
|
||||||
#define GSK_DEBUG_ANY ((1 << 13) - 1)
|
#define GSK_DEBUG_ANY ((1 << 13) - 1)
|
||||||
|
1712
gsk/gskpathops.c
Normal file
1712
gsk/gskpathops.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -56,5 +56,27 @@ gboolean gsk_path_foreach_with_tolerance (GskPath
|
|||||||
void gsk_path_builder_add_contour (GskPathBuilder *builder,
|
void gsk_path_builder_add_contour (GskPathBuilder *builder,
|
||||||
GskContour *contour);
|
GskContour *contour);
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
GSK_PATH_INTERSECTION_NONE,
|
||||||
|
GSK_PATH_INTERSECTION_NORMAL,
|
||||||
|
GSK_PATH_INTERSECTION_START,
|
||||||
|
GSK_PATH_INTERSECTION_END,
|
||||||
|
} GskPathIntersection;
|
||||||
|
|
||||||
|
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
|
G_END_DECLS
|
||||||
|
|
||||||
|
@@ -43,6 +43,10 @@
|
|||||||
#endif
|
#endif
|
||||||
#include <hb-ot.h>
|
#include <hb-ot.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_PANGOFT
|
||||||
|
#include <pango/pangofc-font.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
/* maximal number of rectangles we keep in a diff region before we throw
|
/* maximal number of rectangles we keep in a diff region before we throw
|
||||||
* the towel and just use the bounding box of the parent node.
|
* the towel and just use the bounding box of the parent node.
|
||||||
* Meant to avoid performance corner cases.
|
* Meant to avoid performance corner cases.
|
||||||
@@ -5460,6 +5464,8 @@ struct _GskTextNode
|
|||||||
|
|
||||||
PangoFont *font;
|
PangoFont *font;
|
||||||
gboolean has_color_glyphs;
|
gboolean has_color_glyphs;
|
||||||
|
gboolean embolden;
|
||||||
|
PangoMatrix matrix;
|
||||||
|
|
||||||
GdkRGBA color;
|
GdkRGBA color;
|
||||||
graphene_point_t offset;
|
graphene_point_t offset;
|
||||||
@@ -5468,6 +5474,37 @@ struct _GskTextNode
|
|||||||
PangoGlyphInfo *glyphs;
|
PangoGlyphInfo *glyphs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 void
|
static void
|
||||||
gsk_text_node_finalize (GskRenderNode *node)
|
gsk_text_node_finalize (GskRenderNode *node)
|
||||||
{
|
{
|
||||||
@@ -5593,6 +5630,8 @@ gsk_text_node_new (PangoFont *font,
|
|||||||
self->offset = *offset;
|
self->offset = *offset;
|
||||||
self->has_color_glyphs = FALSE;
|
self->has_color_glyphs = FALSE;
|
||||||
|
|
||||||
|
get_synthetic_font_params (self->font, &self->embolden, &self->matrix);
|
||||||
|
|
||||||
glyph_infos = g_malloc_n (glyphs->num_glyphs, sizeof (PangoGlyphInfo));
|
glyph_infos = g_malloc_n (glyphs->num_glyphs, sizeof (PangoGlyphInfo));
|
||||||
|
|
||||||
n = 0;
|
n = 0;
|
||||||
@@ -5725,6 +5764,22 @@ gsk_text_node_get_offset (const GskRenderNode *node)
|
|||||||
return &self->offset;
|
return &self->offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gsk_text_node_get_font_embolden (const GskRenderNode *node)
|
||||||
|
{
|
||||||
|
const GskTextNode *self = (const GskTextNode *) node;
|
||||||
|
|
||||||
|
return self->embolden;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PangoMatrix *
|
||||||
|
gsk_text_node_get_font_matrix (const GskRenderNode *node)
|
||||||
|
{
|
||||||
|
const GskTextNode *self = (const GskTextNode *) node;
|
||||||
|
|
||||||
|
return &self->matrix;
|
||||||
|
}
|
||||||
|
|
||||||
/* }}} */
|
/* }}} */
|
||||||
/* {{{ GSK_BLUR_NODE */
|
/* {{{ GSK_BLUR_NODE */
|
||||||
|
|
||||||
|
@@ -87,6 +87,11 @@ gboolean gsk_container_node_is_disjoint (const GskRenderNode
|
|||||||
|
|
||||||
gboolean gsk_render_node_use_offscreen_for_opacity (const GskRenderNode *node);
|
gboolean gsk_render_node_use_offscreen_for_opacity (const GskRenderNode *node);
|
||||||
|
|
||||||
|
gboolean gsk_text_node_get_font_embolden (const GskRenderNode *node);
|
||||||
|
|
||||||
|
const PangoMatrix *
|
||||||
|
gsk_text_node_get_font_matrix (const GskRenderNode *node);
|
||||||
|
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
|
@@ -20,6 +20,9 @@ gsk_private_gl_shaders = [
|
|||||||
'gl/resources/custom.glsl',
|
'gl/resources/custom.glsl',
|
||||||
'gl/resources/filled_border.glsl',
|
'gl/resources/filled_border.glsl',
|
||||||
'gl/resources/mask.glsl',
|
'gl/resources/mask.glsl',
|
||||||
|
'gl/resources/glyphy.atlas.glsl',
|
||||||
|
'gl/resources/glyphy.fs.glsl',
|
||||||
|
'gl/resources/glyphy.vs.glsl',
|
||||||
]
|
]
|
||||||
|
|
||||||
gsk_public_sources = files([
|
gsk_public_sources = files([
|
||||||
@@ -29,6 +32,7 @@ gsk_public_sources = files([
|
|||||||
'gskpath.c',
|
'gskpath.c',
|
||||||
'gskpathbuilder.c',
|
'gskpathbuilder.c',
|
||||||
'gskpathmeasure.c',
|
'gskpathmeasure.c',
|
||||||
|
'gskpathops.c',
|
||||||
'gskpathparser.c',
|
'gskpathparser.c',
|
||||||
'gskpathpoint.c',
|
'gskpathpoint.c',
|
||||||
'gskrenderer.c',
|
'gskrenderer.c',
|
||||||
@@ -45,6 +49,7 @@ gsk_private_sources = files([
|
|||||||
'gskcairoblur.c',
|
'gskcairoblur.c',
|
||||||
'gskcontour.c',
|
'gskcontour.c',
|
||||||
'gskcurve.c',
|
'gskcurve.c',
|
||||||
|
'gskcurveintersect.c',
|
||||||
'gskdebug.c',
|
'gskdebug.c',
|
||||||
'gskprivate.c',
|
'gskprivate.c',
|
||||||
'gskprofiler.c',
|
'gskprofiler.c',
|
||||||
@@ -54,6 +59,7 @@ gsk_private_sources = files([
|
|||||||
'gl/gskglcompiler.c',
|
'gl/gskglcompiler.c',
|
||||||
'gl/gskgldriver.c',
|
'gl/gskgldriver.c',
|
||||||
'gl/gskglglyphlibrary.c',
|
'gl/gskglglyphlibrary.c',
|
||||||
|
'gl/gskglglyphylibrary.c',
|
||||||
'gl/gskgliconlibrary.c',
|
'gl/gskgliconlibrary.c',
|
||||||
'gl/gskglprogram.c',
|
'gl/gskglprogram.c',
|
||||||
'gl/gskglrenderjob.c',
|
'gl/gskglrenderjob.c',
|
||||||
@@ -194,6 +200,7 @@ gsk_deps = [
|
|||||||
cairo_dep,
|
cairo_dep,
|
||||||
cairo_csi_dep,
|
cairo_csi_dep,
|
||||||
libgdk_dep,
|
libgdk_dep,
|
||||||
|
libglyphy_dep
|
||||||
]
|
]
|
||||||
|
|
||||||
libgsk_f16c = static_library('gsk_f16c',
|
libgsk_f16c = static_library('gsk_f16c',
|
||||||
|
@@ -1017,6 +1017,7 @@ gtk_deps = [
|
|||||||
epoxy_dep,
|
epoxy_dep,
|
||||||
libm,
|
libm,
|
||||||
graphene_dep,
|
graphene_dep,
|
||||||
|
libglyphy_dep,
|
||||||
]
|
]
|
||||||
|
|
||||||
if x11_enabled
|
if x11_enabled
|
||||||
|
@@ -14,7 +14,7 @@ project('gtk', 'c',
|
|||||||
glib_req = '>= 2.76.0'
|
glib_req = '>= 2.76.0'
|
||||||
introspection_req = '>= 1.76.0' # keep this in sync with glib
|
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
|
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'
|
fribidi_req = '>= 1.0.6'
|
||||||
cairo_req = '>= 1.14.0'
|
cairo_req = '>= 1.14.0'
|
||||||
gdk_pixbuf_req = '>= 2.30.0'
|
gdk_pixbuf_req = '>= 2.30.0'
|
||||||
@@ -26,6 +26,7 @@ cloudproviders_req = '>= 0.3.1'
|
|||||||
xkbcommon_req = '>= 0.2.0'
|
xkbcommon_req = '>= 0.2.0'
|
||||||
sysprof_req = '>= 3.38.0'
|
sysprof_req = '>= 3.38.0'
|
||||||
vulkan_req = '>= 1.2'
|
vulkan_req = '>= 1.2'
|
||||||
|
libglyphy_req = '>= 0.2.0'
|
||||||
|
|
||||||
fs = import('fs')
|
fs = import('fs')
|
||||||
gnome = import('gnome')
|
gnome = import('gnome')
|
||||||
@@ -382,6 +383,9 @@ fribidi_dep = dependency('fribidi', version: fribidi_req,
|
|||||||
default_options: ['docs=false'])
|
default_options: ['docs=false'])
|
||||||
harfbuzz_dep = dependency('harfbuzz', version: harfbuzz_req,
|
harfbuzz_dep = dependency('harfbuzz', version: harfbuzz_req,
|
||||||
default_options: ['coretext=enabled'])
|
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
|
# Require PangoFT2 if on X11 or wayland
|
||||||
pangoft_dep = dependency('pangoft2', version: pango_req,
|
pangoft_dep = dependency('pangoft2', version: pango_req,
|
||||||
|
6
subprojects/glyphy.wrap
Normal file
6
subprojects/glyphy.wrap
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[wrap-git]
|
||||||
|
directory=glyphy
|
||||||
|
url=https://github.com/behdad/glyphy.git
|
||||||
|
revision=master
|
||||||
|
depth=1
|
||||||
|
|
123
tests/bigfont.c
Normal file
123
tests/bigfont.c
Normal file
@@ -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 = [
|
gtk_tests = [
|
||||||
# testname, optional extra sources
|
# testname, optional extra sources
|
||||||
|
['movingtext'],
|
||||||
|
['bigfont'],
|
||||||
['testsections'],
|
['testsections'],
|
||||||
['testfilelauncher'],
|
['testfilelauncher'],
|
||||||
['input'],
|
['input'],
|
||||||
|
211
tests/movingtext.c
Normal file
211
tests/movingtext.c
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
#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;
|
||||||
|
PangoLayout *layout;
|
||||||
|
|
||||||
|
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);
|
||||||
|
int width, height;
|
||||||
|
int pwidth, pheight;
|
||||||
|
GdkRGBA color;
|
||||||
|
|
||||||
|
width = gtk_widget_get_width (widget);
|
||||||
|
height = gtk_widget_get_height (widget);
|
||||||
|
|
||||||
|
gtk_widget_get_color (widget, &color);
|
||||||
|
|
||||||
|
pango_layout_get_pixel_size (self->layout, &pwidth, &pheight);
|
||||||
|
|
||||||
|
gtk_snapshot_save (snapshot);
|
||||||
|
|
||||||
|
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (0.5 * width, 0.5 * height));
|
||||||
|
gtk_snapshot_scale (snapshot, self->size / 150.f, self->size / 150.f);
|
||||||
|
gtk_snapshot_rotate (snapshot, self->angle);
|
||||||
|
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (- 0.5 * pwidth, - 0.5 * pheight));
|
||||||
|
|
||||||
|
gtk_snapshot_append_layout (snapshot, self->layout, &color);
|
||||||
|
|
||||||
|
gtk_snapshot_restore (snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
demo_widget_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
DemoWidget *self = DEMO_WIDGET (object);
|
||||||
|
|
||||||
|
g_free (self->text);
|
||||||
|
g_clear_object (&self->layout);
|
||||||
|
|
||||||
|
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;
|
||||||
|
PangoFontDescription *desc;
|
||||||
|
|
||||||
|
demo = g_object_new (DEMO_TYPE_WIDGET, NULL);
|
||||||
|
|
||||||
|
demo->text = g_strndup (text, length);
|
||||||
|
demo->layout = gtk_widget_create_pango_layout (GTK_WIDGET (demo), demo->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, 150 * PANGO_SCALE);
|
||||||
|
pango_layout_set_font_description (demo->layout, desc);
|
||||||
|
pango_font_description_free (desc);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
551
testsuite/gsk/curve-intersect.c
Normal file
551
testsuite/gsk/curve-intersect.c
Normal file
@@ -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 ();
|
||||||
|
}
|
@@ -406,7 +406,9 @@ endforeach
|
|||||||
internal_tests = [
|
internal_tests = [
|
||||||
[ 'curve' ],
|
[ 'curve' ],
|
||||||
[ 'curve-special-cases' ],
|
[ 'curve-special-cases' ],
|
||||||
|
[ 'curve-intersect' ],
|
||||||
[ 'path-private' ],
|
[ 'path-private' ],
|
||||||
|
['path-ops', [ 'path-utils.c' ] ],
|
||||||
[ 'diff' ],
|
[ 'diff' ],
|
||||||
[ 'half-float' ],
|
[ 'half-float' ],
|
||||||
['rounded-rect'],
|
['rounded-rect'],
|
||||||
|
357
testsuite/gsk/path-ops.c
Normal file
357
testsuite/gsk/path-ops.c
Normal file
@@ -0,0 +1,357 @@
|
|||||||
|
/*
|
||||||
|
* 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 "gsk/gskpathprivate.h"
|
||||||
|
#include "path-utils.h"
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_ops_simple (void)
|
||||||
|
{
|
||||||
|
struct {
|
||||||
|
const char *in1;
|
||||||
|
const char *in2;
|
||||||
|
GskPathOp 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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_OP_XOR,
|
||||||
|
"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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_OP_XOR,
|
||||||
|
"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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_OP_XOR,
|
||||||
|
"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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_OP_XOR,
|
||||||
|
"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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_OP_XOR,
|
||||||
|
"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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_OP_XOR,
|
||||||
|
"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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_OP_XOR,
|
||||||
|
"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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_OP_XOR,
|
||||||
|
"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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_OP_XOR,
|
||||||
|
"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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_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",
|
||||||
|
GSK_PATH_OP_XOR,
|
||||||
|
"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",
|
||||||
|
GSK_PATH_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);
|
||||||
|
p = gsk_path_op (tests[i].op, GSK_FILL_RULE_WINDING, p1, p2);
|
||||||
|
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 ();
|
||||||
|
}
|
Reference in New Issue
Block a user