Files
squeekboard/eek/eek-renderer.c
Dorota Czaplejewicz f9fbd3fb2d rendering: Simplify Cairo context usage, remove unneeded calls.
Moved Cairo context usage to Rust, and rearranged ctx setup (position) to happen in one place.

Removed render calls that were overwritten on each draw call anyway.
2019-12-07 12:47:47 +00:00

839 lines
27 KiB
C

/*
* Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
* Copyright (C) 2010-2011 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include "config.h"
#include <math.h>
#include <string.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "eek-keyboard.h"
#include "eek-renderer.h"
#include "src/style.h"
enum {
PROP_0,
PROP_PCONTEXT,
PROP_LAST
};
typedef struct _EekRendererPrivate
{
LevelKeyboard *keyboard; // unowned
PangoContext *pcontext; // owned
GtkCssProvider *css_provider; // owned
GtkStyleContext *view_context; // owned
GtkStyleContext *button_context; // TODO: maybe move a copy to each button
gdouble border_width;
gdouble allocation_width;
gdouble allocation_height;
gdouble scale;
gint scale_factor; /* the outputs scale factor */
gint origin_x;
gint origin_y;
PangoFontDescription *font; // owned reference
cairo_surface_t *keyboard_surface;
} EekRendererPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (EekRenderer, eek_renderer, G_TYPE_OBJECT)
/* eek-keyboard-drawing.c */
static void eek_renderer_render_button_label (EekRenderer *self, cairo_t *cr, GtkStyleContext *ctx,
const struct squeek_button *button);
static void invalidate (EekRenderer *renderer);
void eek_render_button (EekRenderer *self,
cairo_t *cr, const struct squeek_button *button,
gboolean pressed, gboolean locked);
struct _CreateKeyboardSurfaceCallbackData {
cairo_t *cr;
EekRenderer *renderer;
struct squeek_view *view;
struct squeek_row *row;
};
typedef struct _CreateKeyboardSurfaceCallbackData CreateKeyboardSurfaceCallbackData;
static void
create_keyboard_surface_button_callback (struct squeek_button *button,
gpointer user_data)
{
CreateKeyboardSurfaceCallbackData *data = user_data;
EekBounds bounds = squeek_button_get_bounds(button);
cairo_save (data->cr);
cairo_translate (data->cr, bounds.x, bounds.y);
cairo_rectangle (data->cr,
0.0,
0.0,
bounds.width,
bounds.height);
cairo_clip (data->cr);
eek_render_button (data->renderer, data->cr, button, FALSE, FALSE);
cairo_restore (data->cr);
}
static void
create_keyboard_surface_row_callback (struct squeek_row *row,
gpointer user_data)
{
CreateKeyboardSurfaceCallbackData *data = user_data;
EekBounds bounds = squeek_row_get_bounds(row);
cairo_save (data->cr);
cairo_translate (data->cr, bounds.x, bounds.y);
gint angle = squeek_row_get_angle (row);
cairo_rotate (data->cr, angle * G_PI / 180);
data->row = row;
squeek_row_foreach(row, create_keyboard_surface_button_callback, data);
cairo_restore (data->cr);
}
static void
render_keyboard_surface (EekRenderer *renderer, struct squeek_view *view)
{
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
CreateKeyboardSurfaceCallbackData data = {
.cr = cairo_create (priv->keyboard_surface),
.renderer = renderer,
};
/* Paint the background covering the entire widget area */
gtk_render_background (priv->view_context,
data.cr,
0, 0,
priv->allocation_width, priv->allocation_height);
gtk_render_frame (priv->view_context,
data.cr,
0, 0,
priv->allocation_width, priv->allocation_height);
GdkRGBA color = {0};
gtk_style_context_get_color (priv->view_context, GTK_STATE_FLAG_NORMAL, &color);
cairo_set_source_rgba (data.cr,
color.red,
color.green,
color.blue,
color.alpha);
cairo_save (data.cr);
EekBounds bounds = squeek_view_get_bounds (view);
cairo_translate (data.cr, bounds.x, bounds.y);
/* draw rows */
squeek_view_foreach(view,
create_keyboard_surface_row_callback,
&data);
cairo_restore (data.cr);
cairo_destroy (data.cr);
}
static void
render_outline (cairo_t *cr,
GtkStyleContext *ctx,
EekBounds bounds)
{
GtkBorder margin, border;
gtk_style_context_get_margin(ctx, GTK_STATE_FLAG_NORMAL, &margin);
gtk_style_context_get_border(ctx, GTK_STATE_FLAG_NORMAL, &border);
gdouble x = margin.left + border.left;
gdouble y = margin.top + border.top;
EekBounds position = {
.x = x,
.y = y,
.width = bounds.width - x - (margin.right + border.right),
.height = bounds.height - y - (margin.bottom + border.bottom),
};
gtk_render_background (ctx, cr,
position.x, position.y, position.width, position.height);
gtk_render_frame (ctx, cr,
position.x, position.y, position.width, position.height);
}
static void render_button_in_context(EekRenderer *self,
gint scale_factor,
cairo_t *cr,
GtkStyleContext *ctx,
const struct squeek_button *button) {
/* blank background */
cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.0);
cairo_paint (cr);
EekBounds bounds = squeek_button_get_bounds(button);
render_outline (cr, ctx, bounds);
cairo_paint (cr);
/* render icon (if any) */
const char *icon_name = squeek_button_get_icon_name(button);
if (icon_name) {
cairo_surface_t *icon_surface =
eek_renderer_get_icon_surface (icon_name, 16, scale_factor);
if (icon_surface) {
gint width = cairo_image_surface_get_width (icon_surface);
gint height = cairo_image_surface_get_height (icon_surface);
cairo_save (cr);
cairo_translate (cr,
(bounds.width - (double)width / scale_factor) / 2,
(bounds.height - (double)height / scale_factor) / 2);
cairo_rectangle (cr, 0, 0, width, height);
cairo_clip (cr);
/* Draw the shape of the icon using the foreground color */
GdkRGBA color = {0};
gtk_style_context_get_color (ctx, GTK_STATE_FLAG_NORMAL, &color);
cairo_set_source_rgba (cr, color.red,
color.green,
color.blue,
color.alpha);
cairo_mask_surface (cr, icon_surface, 0.0, 0.0);
cairo_surface_destroy(icon_surface);
cairo_fill (cr);
cairo_restore (cr);
return;
}
}
eek_renderer_render_button_label (self, cr, ctx, button);
}
void
eek_render_button (EekRenderer *self,
cairo_t *cr,
const struct squeek_button *button,
gboolean pressed,
gboolean locked)
{
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
GtkStyleContext *ctx = priv->button_context;
/* Set the name of the button on the widget path, using the name obtained
from the button's symbol. */
g_autoptr (GtkWidgetPath) path = NULL;
path = gtk_widget_path_copy (gtk_style_context_get_path (ctx));
const char *name = squeek_button_get_name(button);
gtk_widget_path_iter_set_name (path, -1, name);
/* Update the style context with the updated widget path. */
gtk_style_context_set_path (ctx, path);
/* Set the state to take into account whether the button is active
(pressed) or normal. */
gtk_style_context_set_state(ctx,
pressed ? GTK_STATE_FLAG_ACTIVE : GTK_STATE_FLAG_NORMAL);
const char *outline_name = squeek_button_get_outline_name(button);
if (locked) {
gtk_style_context_add_class(ctx, "locked");
}
gtk_style_context_add_class(ctx, outline_name);
render_button_in_context(self, priv->scale_factor, cr, ctx, button);
// Save and restore functions don't work if gtk_render_* was used in between
gtk_style_context_set_state(ctx, GTK_STATE_FLAG_NORMAL);
gtk_style_context_remove_class(ctx, outline_name);
if (locked) {
gtk_style_context_remove_class(ctx, "locked");
}
}
/**
* eek_renderer_apply_transformation_for_key:
* @self: The renderer used to render the key
* @cr: The Cairo rendering context used for rendering
* @key: The key to be transformed
* @scale: The factor used to scale the key bounds before rendering
* @rotate: Whether to rotate the key by the angle defined for the key's
* in its section definition
*
* Applies a transformation, consisting of scaling and rotation, to the
* current rendering context using the bounds for the given key. The scale
* factor is separate to the normal scale factor for the keyboard as a whole
* and is applied cumulatively. It is typically used to render larger than
* normal keys for popups.
*/
void
eek_renderer_apply_transformation_for_button (cairo_t *cr,
struct button_place *place,
gdouble scale)
{
EekBounds bounds = squeek_button_get_bounds(place->button);
gdouble s;
gint angle = squeek_row_get_angle (place->row);
cairo_scale (cr, scale, scale);
s = sin (angle * G_PI / 180);
if (s < 0)
cairo_translate (cr, 0, - bounds.width * s);
else
cairo_translate (cr, bounds.height * s, 0);
cairo_rotate (cr, angle * G_PI / 180);
}
static void
eek_renderer_render_button_label (EekRenderer *self,
cairo_t *cr,
GtkStyleContext *ctx,
const struct squeek_button *button)
{
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
const gchar *label = squeek_button_get_label(button);
if (!label) {
return;
}
PangoFontDescription *font;
gdouble scale;
if (!priv->font) {
const PangoFontDescription *base_font;
gdouble size;
base_font = pango_context_get_font_description (priv->pcontext);
// FIXME: Base font size on the same size unit used for button sizing,
// and make the default about 1/3 of the current row height
size = 30000.0;
priv->font = pango_font_description_copy (base_font);
pango_font_description_set_size (priv->font, (gint)round(size * 0.6));
}
EekBounds bounds = squeek_button_get_bounds(button);
scale = MIN((bounds.width - priv->border_width) / bounds.width,
(bounds.height - priv->border_width) / bounds.height);
font = pango_font_description_copy (priv->font);
pango_font_description_set_size (font,
(gint)round(pango_font_description_get_size (font) * scale));
PangoLayout *layout = pango_cairo_create_layout (cr);
pango_layout_set_font_description (layout, font);
pango_font_description_free (font);
pango_layout_set_text (layout, label, -1);
PangoLayoutLine *line = pango_layout_get_line_readonly(layout, 0);
if (line->resolved_dir == PANGO_DIRECTION_RTL) {
pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT);
}
pango_layout_set_width (layout,
PANGO_SCALE * bounds.width * scale);
PangoRectangle extents = { 0, };
pango_layout_get_extents (layout, NULL, &extents);
cairo_save (cr);
cairo_move_to
(cr,
(bounds.width - (double)extents.width / PANGO_SCALE) / 2,
(bounds.height - (double)extents.height / PANGO_SCALE) / 2);
GdkRGBA color = {0};
gtk_style_context_get_color (ctx, GTK_STATE_FLAG_NORMAL, &color);
cairo_set_source_rgba (cr,
color.red,
color.green,
color.blue,
color.alpha);
pango_cairo_show_layout (cr, layout);
cairo_restore (cr);
g_object_unref (layout);
}
/*
* eek_renderer_real_render_key:
* @self: The renderer used to render the key
* @cr: The Cairo rendering context used for rendering
* @key: The key to be transformed
* @scale: The factor used to scale the key bounds before rendering
* @rotate: Whether to rotate the key by the angle defined for the key's
* in its section definition
*
* Renders a key separately from the normal keyboard rendering.
*/
void
eek_renderer_render_button (EekRenderer *self,
cairo_t *cr,
struct button_place *place,
gdouble scale,
gboolean is_pressed,
gboolean is_locked)
{
g_return_if_fail (EEK_IS_RENDERER(self));
g_return_if_fail (place);
g_return_if_fail (scale >= 0.0);
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
EekBounds bounds;
EekBounds view_bounds = squeek_view_get_bounds (level_keyboard_current(priv->keyboard));
eek_renderer_get_button_bounds (view_bounds, place, &bounds);
cairo_save (cr);
/* Because this function is called separately from the keyboard rendering
function, the transformation for the context needs to be set up */
cairo_translate (cr, priv->origin_x, priv->origin_y);
cairo_scale (cr, priv->scale, priv->scale);
cairo_translate (cr, bounds.x, bounds.y);
eek_renderer_apply_transformation_for_button (cr, place, scale);
eek_render_button (
self, cr, place->button,
is_pressed,
is_locked
);
cairo_restore (cr);
}
void
eek_renderer_render_keyboard (EekRenderer *self,
cairo_t *cr)
{
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
g_return_if_fail (priv->keyboard);
g_return_if_fail (priv->allocation_width > 0.0);
g_return_if_fail (priv->allocation_height > 0.0);
cairo_save(cr);
cairo_translate (cr, priv->origin_x, priv->origin_y);
cairo_scale (cr, priv->scale, priv->scale);
if (priv->keyboard_surface)
cairo_surface_destroy (priv->keyboard_surface);
priv->keyboard_surface = cairo_surface_create_for_rectangle (
cairo_get_target (cr), 0, 0,
priv->allocation_width, priv->allocation_height);
render_keyboard_surface (self, squeek_layout_get_current_view(priv->keyboard->layout));
cairo_set_source_surface (cr, priv->keyboard_surface, 0.0, 0.0);
cairo_pattern_t *source = cairo_get_source (cr);
cairo_pattern_set_extend (source, CAIRO_EXTEND_PAD);
cairo_paint (cr);
squeek_layout_draw_all_changed(priv->keyboard->layout, self, cr);
cairo_restore (cr);
}
static void
eek_renderer_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
EekRendererPrivate *priv = eek_renderer_get_instance_private (
EEK_RENDERER(object));
switch (prop_id) {
case PROP_PCONTEXT:
priv->pcontext = g_value_get_object (value);
g_object_ref (priv->pcontext);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
eek_renderer_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
switch (prop_id) {
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
eek_renderer_dispose (GObject *object)
{
EekRenderer *self = EEK_RENDERER (object);
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
if (priv->keyboard) {
priv->keyboard = NULL;
}
if (priv->pcontext) {
g_object_unref (priv->pcontext);
priv->pcontext = NULL;
}
/* this will release all allocated surfaces and font if any */
invalidate (EEK_RENDERER(object));
G_OBJECT_CLASS (eek_renderer_parent_class)->dispose (object);
}
static void
eek_renderer_finalize (GObject *object)
{
EekRenderer *self = EEK_RENDERER(object);
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
g_object_unref(priv->css_provider);
g_object_unref(priv->view_context);
g_object_unref(priv->button_context);
pango_font_description_free (priv->font);
G_OBJECT_CLASS (eek_renderer_parent_class)->finalize (object);
}
static void
eek_renderer_class_init (EekRendererClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GParamSpec *pspec;
gobject_class->set_property = eek_renderer_set_property;
gobject_class->get_property = eek_renderer_get_property;
gobject_class->dispose = eek_renderer_dispose;
gobject_class->finalize = eek_renderer_finalize;
pspec = g_param_spec_object ("pango-context",
"Pango Context",
"Pango Context",
PANGO_TYPE_CONTEXT,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE);
g_object_class_install_property (gobject_class,
PROP_PCONTEXT,
pspec);
}
static GType new_type(char *name) {
GTypeInfo info = {0};
info.class_size = sizeof(GtkWidgetClass);
info.instance_size = sizeof(GtkWidget);
return g_type_register_static(GTK_TYPE_WIDGET, name, &info,
G_TYPE_FLAG_ABSTRACT
);
}
static GType view_type() {
static GType type = 0;
if (!type) {
type = new_type("sq_view");
}
return type;
}
static GType button_type() {
static GType type = 0;
if (!type) {
type = new_type("sq_button");
}
return type;
}
static void
eek_renderer_init (EekRenderer *self)
{
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
priv->keyboard = NULL;
priv->pcontext = NULL;
priv->border_width = 1.0;
priv->allocation_width = 0.0;
priv->allocation_height = 0.0;
priv->scale = 1.0;
priv->scale_factor = 1;
priv->font = NULL;
priv->keyboard_surface = NULL;
GtkIconTheme *theme = gtk_icon_theme_get_default ();
gtk_icon_theme_add_resource_path (theme, "/sm/puri/squeekboard/icons");
priv->css_provider = squeek_load_style();
}
static void
invalidate (EekRenderer *renderer)
{
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
if (priv->keyboard_surface) {
cairo_surface_destroy (priv->keyboard_surface);
priv->keyboard_surface = NULL;
}
}
EekRenderer *
eek_renderer_new (LevelKeyboard *keyboard,
PangoContext *pcontext)
{
EekRenderer *renderer = g_object_new (EEK_TYPE_RENDERER,
"pango-context", pcontext,
NULL);
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
priv->keyboard = keyboard;
/* Create a style context for the layout */
GtkWidgetPath *path = gtk_widget_path_new();
gtk_widget_path_append_type(path, view_type());
priv->view_context = gtk_style_context_new();
gtk_style_context_set_path(priv->view_context, path);
gtk_widget_path_unref(path);
if (squeek_layout_get_kind(priv->keyboard->layout) == ARRANGEMENT_KIND_WIDE) {
gtk_style_context_add_class(priv->view_context, "wide");
}
gtk_style_context_add_provider (priv->view_context,
GTK_STYLE_PROVIDER(priv->css_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER);
/* Create a style context for the buttons */
path = gtk_widget_path_new();
gtk_widget_path_append_type(path, view_type());
if (squeek_layout_get_kind(priv->keyboard->layout) == ARRANGEMENT_KIND_WIDE) {
gtk_widget_path_iter_add_class(path, -1, "wide");
}
gtk_widget_path_append_type(path, button_type());
priv->button_context = gtk_style_context_new ();
gtk_style_context_set_path(priv->button_context, path);
gtk_widget_path_unref(path);
gtk_style_context_set_parent(priv->button_context, priv->view_context);
gtk_style_context_set_state (priv->button_context, GTK_STATE_FLAG_NORMAL);
gtk_style_context_add_provider (priv->button_context,
GTK_STYLE_PROVIDER(priv->css_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER);
return renderer;
}
void
eek_renderer_set_allocation_size (EekRenderer *renderer,
gdouble width,
gdouble height)
{
gdouble scale;
g_return_if_fail (EEK_IS_RENDERER(renderer));
g_return_if_fail (width > 0.0 && height > 0.0);
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
priv->allocation_width = width;
priv->allocation_height = height;
/* Calculate a scale factor to use when rendering the keyboard into the
available space. */
EekBounds bounds = squeek_view_get_bounds (level_keyboard_current(priv->keyboard));
gdouble w = (bounds.x * 2) + bounds.width;
gdouble h = (bounds.y * 2) + bounds.height;
scale = MIN(width / w, height / h);
priv->scale = scale;
/* Set the rendering offset in widget coordinates to center the keyboard */
priv->origin_x = (gint)floor((width - (scale * w)) / 2);
priv->origin_y = (gint)floor((height - (scale * h)) / 2);
invalidate (renderer);
}
void
eek_renderer_get_size (EekRenderer *renderer,
gdouble *width,
gdouble *height)
{
g_return_if_fail (EEK_IS_RENDERER(renderer));
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
EekBounds bounds = squeek_view_get_bounds (level_keyboard_current(priv->keyboard));
if (width)
*width = bounds.width;
if (height)
*height = bounds.height;
}
void
eek_renderer_get_button_bounds (EekBounds view_bounds,
struct button_place *place,
EekBounds *bounds)
{
gint angle = 0;
EekPoint points[4], min, max;
g_return_if_fail (place);
g_return_if_fail (bounds != NULL);
EekBounds button_bounds = squeek_button_get_bounds(place->button);
EekBounds row_bounds = squeek_row_get_bounds (place->row);
points[0].x = button_bounds.x;
points[0].y = button_bounds.y;
points[1].x = points[0].x + button_bounds.width;
points[1].y = points[0].y;
points[2].x = points[1].x;
points[2].y = points[1].y + button_bounds.height;
points[3].x = points[0].x;
points[3].y = points[2].y;
angle = squeek_row_get_angle (place->row);
min = points[2];
max = points[0];
for (unsigned i = 0; i < G_N_ELEMENTS(points); i++) {
eek_point_rotate (&points[i], angle);
if (points[i].x < min.x)
min.x = points[i].x;
if (points[i].x > max.x)
max.x = points[i].x;
if (points[i].y < min.y)
min.y = points[i].y;
if (points[i].y > max.y)
max.y = points[i].y;
}
bounds->x = view_bounds.x + row_bounds.x + min.x;
bounds->y = view_bounds.y + row_bounds.y + min.y;
bounds->width = (max.x - min.x);
bounds->height = (max.y - min.y);
}
gdouble
eek_renderer_get_scale (EekRenderer *renderer)
{
g_return_val_if_fail (EEK_IS_RENDERER(renderer), 0);
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
return priv->scale;
}
void
eek_renderer_set_scale_factor (EekRenderer *renderer, gint scale)
{
g_return_if_fail (EEK_IS_RENDERER(renderer));
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
priv->scale_factor = scale;
}
cairo_surface_t *
eek_renderer_get_icon_surface (const gchar *icon_name,
gint size,
gint scale)
{
GError *error = NULL;
cairo_surface_t *surface = gtk_icon_theme_load_surface (gtk_icon_theme_get_default (),
icon_name,
size,
scale,
NULL,
0,
&error);
if (surface == NULL) {
g_warning ("can't get icon surface for %s: %s",
icon_name,
error->message);
g_error_free (error);
return NULL;
}
return surface;
}
static gboolean
sign (EekPoint *p1, EekPoint *p2, EekPoint *p3)
{
// FIXME: what is this actually checking?
return (p1->x - p3->x) * (p2->y - p3->y) -
(p2->x - p3->x) * (p1->y - p3->y);
}
uint32_t
eek_are_bounds_inside (EekBounds bounds, EekPoint point, EekPoint origin, int32_t angle)
{
EekPoint points[4];
gboolean b1, b2, b3;
points[0].x = bounds.x;
points[0].y = bounds.y;
points[1].x = points[0].x + bounds.width;
points[1].y = points[0].y;
points[2].x = points[1].x;
points[2].y = points[1].y + bounds.height;
points[3].x = points[0].x;
points[3].y = points[2].y;
for (unsigned i = 0; i < G_N_ELEMENTS(points); i++) {
eek_point_rotate (&points[i], angle);
points[i].x += origin.x;
points[i].y += origin.y;
}
b1 = sign (&point, &points[0], &points[1]) < 0.0;
b2 = sign (&point, &points[1], &points[2]) < 0.0;
b3 = sign (&point, &points[2], &points[0]) < 0.0;
if (b1 == b2 && b2 == b3) {
return 1;
}
b1 = sign (&point, &points[2], &points[3]) < 0.0;
b2 = sign (&point, &points[3], &points[0]) < 0.0;
b3 = sign (&point, &points[0], &points[2]) < 0.0;
if (b1 == b2 && b2 == b3) {
return 1;
}
return 0;
}
struct transformation
eek_renderer_get_transformation (EekRenderer *renderer) {
struct transformation failed = {0};
g_return_val_if_fail (EEK_IS_RENDERER(renderer), failed);
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
struct transformation ret = {
.origin_x = priv->origin_x,
.origin_y = priv->origin_y,
.scale = priv->scale,
};
return ret;
}