Compare commits

...

3 Commits

Author SHA1 Message Date
Matthias Clasen
bdc6ef5b8b gsk: Start using 2d types 2024-03-12 23:31:57 -04:00
Matthias Clasen
81da31d662 Add an simd implementation
This is strongly based on graphenes simd plumbing.
All three types get represented as graphene_simf4d_t,
scale as {x,y,0,0}, point as {x,y,0,0} and box as
{x0,y0,x1,y1}.
2024-03-12 22:49:08 -04:00
Matthias Clasen
f3c2cdb94d Add private Scale, Point, Box types
This is the most straightforward, plain C implementation one could
come up with, to start with something obviously correct.
2024-03-12 22:44:57 -04:00
5 changed files with 1027 additions and 44 deletions

497
gsk/boxprivate.h Normal file
View File

@@ -0,0 +1,497 @@
#pragma once
#include <graphene.h>
#include <math.h>
#include "gsktypesprivate.h"
#include "scaleprivate.h"
#include "pointprivate.h"
#ifndef USE_SIMD
struct _Box
{
float x0, y0, x1, y1;
};
static inline float
box_x0 (const Box box)
{
return box.x0;
}
static inline float
box_y0 (const Box box)
{
return box.y0;
}
static inline float
box_x1 (const Box box)
{
return box.x1;
}
static inline float
box_y1 (const Box box)
{
return box.y1;
}
static inline float
box_width (const Box box)
{
return box.x1 - box.x0;
}
static inline float
box_height (const Box box)
{
return box.y1 - box.y0;
}
/* Assumes x0 <= x1 && y0 <= y1 */
static inline Box
box (float x0,
float y0,
float x1,
float y1)
{
return (Box) { .x0 = x0, .y0 = y0, .x1 = x1, .y1 = y1 };
}
static inline Box
box_from_rect (float x,
float y,
float w,
float h)
{
return box (x, y, x + w, y + h);
}
static inline Box
box_from_graphene (const graphene_rect_t *rect)
{
return box_from_rect (rect->origin.x,
rect->origin.y,
rect->size.width,
rect->size.height);
}
static inline void
box_to_graphene (const Box box,
graphene_rect_t *rect)
{
graphene_rect_init (rect, box.x0, box.y0, box.x1 - box.x0, box.y1 - box.y0);
}
/* Assumes p0.x <= p1.x && p0.y <= p1.y */
static inline Box
box_from_points (Point p0,
Point p1)
{
return box (p0.x, p0.y, p1.x, p1.y);
}
static inline Point
box_origin (const Box box)
{
return point (box.x0, box.y0);
}
static inline Point
box_opposite (const Box box)
{
return point (box.x1, box.y1);
}
static inline void
box_to_float (const Box box,
float v[4])
{
v[0] = box.x0;
v[1] = box.y0;
v[2] = box.x1 - box.x0;
v[3] = box.y1 - box.y0;
}
static inline Box
box_inset (const Box box,
float dx,
float dy)
{
return (Box) { .x0 = box.x0 + dx, .y0 = box.y0 + dy,
.x1 = box.x1 - dx, .y1 = box.y1 - dy };
}
static inline gboolean
box_intersect (const Box box1,
const Box box2,
Box *box)
{
Box b;
b.x0 = MAX (box1.x0, box2.x0);
b.y0 = MAX (box1.y0, box2.y0);
b.x1 = MIN (box1.x1, box2.x1);
b.y1 = MIN (box1.y1, box2.y1);
if (b.x0 <= b.x1 && b.y0 <= b.x1)
{
if (box)
*box = b;
return TRUE;
}
return FALSE;
}
static inline gboolean
box_equal (const Box box1,
const Box box2)
{
return memcmp (&box1, &box2, sizeof (Box)) == 0;
}
static inline gboolean
box_contains (const Box box1,
const Box box2)
{
Box box;
if (box_intersect (box1, box2, &box))
return box_equal (box, box2);
return FALSE;
}
static inline gboolean
box_empty (const Box box)
{
return box.x0 == box.x1 || box.y0 == box.y1;
}
static inline Box
box_add (const Box box,
const Point offset)
{
return (Box) { .x0 = box.x0 + offset.x, .y0 = box.y0 + offset.y,
.x1 = box.x1 + offset.x, .y1 = box.y1 + offset.y };
}
static inline Box
box_sub (const Box box,
const Point offset)
{
return (Box) { .x0 = box.x0 - offset.x, .y0 = box.y0 - offset.y,
.x1 = box.x1 - offset.x, .y1 = box.y1 - offset.y };
}
static inline Box
box_mul (const Box box,
const Scale scale)
{
Box b = (Box) { .x0 = box.x0 * scale.x, .y0 = box.y0 * scale.y,
.x1 = box.x1 * scale.x, .y1 = box.y1 * scale.y };
if (G_UNLIKELY (scale.x < 0 || scale.y < 0))
return (Box) { .x0 = MIN (b.x0, b.x1), .y0 = MIN (b.y0, b.y1),
.x1 = MAX (b.x0, b.x1), .y1 = MAX (b.y0, b.y1) };
return b;
}
static inline Box
box_div (const Box box,
const Scale scale)
{
return box_mul (box, scale_inv (scale));
}
static inline void
box_offset_to_float (const Box box,
const Point offset,
float v[4])
{
box_to_float (box_add (box, offset), v);
}
static inline Box
box_round_larger (const Box box)
{
return (Box) { .x0 = floorf (box.x0), .y0 = floorf (box.y0),
.x1 = ceilf (box.x1), .y1 = ceilf (box.y1) };
}
static inline Box
box_round_to_pixels (const Box box,
const Scale scale,
const Point offset)
{
return box_sub (box_div (box_round_larger (box_mul (box_add (box, offset), scale)), scale), offset);
}
#else /* USE_SIMD */
struct _Box
{
GRAPHENE_ALIGNED_DECL (graphene_simd4f_t v, 16);
};
static inline float
box_x0 (const Box box)
{
return graphene_simd4f_get_x (box.v);
}
static inline float
box_y0 (const Box box)
{
return graphene_simd4f_get_y (box.v);
}
static inline float
box_x1 (const Box box)
{
return graphene_simd4f_get_z (box.v);
}
static inline float
box_y1 (const Box box)
{
return graphene_simd4f_get_w (box.v);
}
static inline float
box_width (const Box box)
{
return box_x1 (box) - box_x0 (box);
}
static inline float
box_height (const Box box)
{
return box_y1 (box) - box_y0 (box);
}
static inline Box
box (float x0,
float y0,
float x1,
float y1)
{
return (Box) { .v = graphene_simd4f_init (x0, y0, x1, y1) };
}
static inline Box
box_from_rect (float x,
float y,
float w,
float h)
{
return box (x, y, x + w, y + h);
}
static inline Box
box_from_graphene (const graphene_rect_t *rect)
{
return box_from_rect (rect->origin.x,
rect->origin.y,
rect->size.width,
rect->size.height);
}
static inline void
box_to_graphene (const Box box,
graphene_rect_t *rect)
{
graphene_rect_init (rect, box_x0 (box),
box_y0 (box),
box_x1 (box) - box_x0 (box),
box_y1 (box) - box_y0 (box));
}
/* Assumes p0.x <= p1.x && p0.y <= p1.y */
/* { a[0], a[1], b[0], b[1] } */
# define graphene_simd4f_splat_xyxy(a,b) \
(__extension__ ({ \
(graphene_simd4f_t) _mm_shuffle_ps ((a), (b), _MM_SHUFFLE (1, 0, 1, 0)); \
}))
static inline Box
box_from_points (Point p0,
Point p1)
{
return (Box) { .v = graphene_simd4f_splat_xyxy (p0.v, p1.v) };
}
static inline Point
box_origin (const Box box)
{
return (Point) { .v = graphene_simd4f_zero_zw (box.v) };
}
static inline Point
box_opposite (const Box box)
{
return (Point) { .v = graphene_simd4f_zero_zw (graphene_simd4f_shuffle_zwxy (box.v)) };
}
static inline void
box_to_float (const Box box,
float v[4])
{
graphene_simd4f_dup_4f (box.v, v);
v[2] -= v[0];
v[3] -= v[1];
}
static inline Box
box_inset (const Box box,
float dx,
float dy)
{
return (Box) { .v = graphene_simd4f_add (box.v, graphene_simd4f_init (dx, dy, -dx, -dy)) };
}
/* return a[0] < b[0] && a[1] < b[1] */
#ifndef graphene_simd4f_cmple_xy
# define graphene_simd4f_cmple_xy(a,b) \
(__extension__ ({ \
__m128i __res = (__m128i) _mm_cmple_ps ((a), (b)); \
(bool) ((_mm_movemask_epi8 (__res) & 0xff) == 0xff); \
}))
#endif
static inline gboolean
box_intersect (const Box box1,
const Box box2,
Box *box)
{
graphene_simd4f_t s, t, t1;
s = graphene_simd4f_max (box1.v, box2.v);
t = graphene_simd4f_min (box1.v, box2.v);
t1 = graphene_simd4f_shuffle_zwxy (t);
if (graphene_simd4f_cmple_xy (s, t1))
{
if (box)
box->v = graphene_simd4f_splat_xyxy (s, t);
return TRUE;
}
return FALSE;
}
static inline gboolean
box_equal (const Box box1,
const Box box2)
{
return (gboolean) !!graphene_simd4f_cmp_eq (box1.v, box2.v);
}
static inline gboolean
box_contains (const Box box1,
const Box box2)
{
Box box;
if (box_intersect (box1, box2, &box))
return box_equal (box, box2);
return FALSE;
}
static inline gboolean
box_empty (const Box box)
{
/* FIXME simd */
return box_x0 (box) == box_x1 (box) || box_y0 (box) == box_y1 (box);
}
/* a splat variation */
#ifndef graphene_simd4f_shuffle_xyxy
# define graphene_simd4f_shuffle_xyxy(v) \
(__extension__ ({ \
(graphene_simd4f_t) _mm_shuffle_ps ((v), (v), _MM_SHUFFLE (1, 0, 1, 0)); \
}))
#endif
static inline Box
box_add (const Box box,
const Point offset)
{
return (Box) { .v = graphene_simd4f_add (box.v, graphene_simd4f_shuffle_xyxy (offset.v)) };
}
static inline Box
box_sub (const Box box,
const Point offset)
{
return (Box) { .v = graphene_simd4f_sub (box.v, graphene_simd4f_shuffle_xyxy (offset.v)) };
}
static inline Box
box_mul (const Box box,
const Scale scale)
{
Box b = (Box) { .v = graphene_simd4f_mul (box.v, graphene_simd4f_shuffle_xyxy (scale.v)) };
if (G_UNLIKELY (!graphene_simd4f_cmple_xy (graphene_simd4f_init (0, 0, 0, 0), scale.v)))
{
graphene_simd4f_t v = graphene_simd4f_shuffle_zwxy (b.v);
graphene_simd4f_t s = graphene_simd4f_min (b.v, v);
graphene_simd4f_t t = graphene_simd4f_max (b.v, v);
return (Box) { .v = graphene_simd4f_splat_xyxy (s, t) };
}
return b;
}
static inline Box
box_div (const Box box,
const Scale scale)
{
return box_mul (box, scale_inv (scale));
}
static inline void
box_offset_to_float (const Box box,
const Point offset,
float v[4])
{
box_to_float (box_add (box, offset), v);
}
#ifdef __SSE4_1__
static inline Box
box_round_larger (const Box box)
{
return { (Box) .v = graphene_simd4f_splat_xyxy (graphene_simd4f_floor (b.v), graphene_simd4f_ceil (b.v)) };
}
#else
static inline Box
box_round_larger (const Box b)
{
return box (floorf (box_x0 (b)),
floorf (box_y0 (b)),
ceilf (box_x1 (b)),
ceilf (box_y1 (b)));
}
#endif
static inline Box
box_round_to_pixels (const Box box,
const Scale scale,
const Point offset)
{
return box_sub (box_div (box_round_larger (box_mul (box_add (box, offset), scale)), scale), offset);
}
#endif

View File

@@ -45,6 +45,10 @@
#include "gdk/gdkrgbaprivate.h"
#include "gdk/gdksubsurfaceprivate.h"
#include "gsk/scaleprivate.h"
#include "gsk/pointprivate.h"
#include "gsk/boxprivate.h"
/* the epsilon we allow pixels to be off due to rounding errors.
* Chosen rather randomly.
*/
@@ -2996,12 +3000,13 @@ gsk_gpu_node_processor_add_glyph_node (GskGpuNodeProcessor *self,
GskGpuDevice *device;
const PangoGlyphInfo *glyphs;
PangoFont *font;
graphene_point_t offset;
Point offset;
Scale s, pango_scale, s4, ss;
guint i, num_glyphs;
float scale, inv_scale;
GdkRGBA color;
gboolean glyph_align;
gboolean hinting;
unsigned int mask;
if (self->opacity < 1.0 &&
gsk_text_node_has_color_glyphs (node))
@@ -3017,72 +3022,66 @@ gsk_gpu_node_processor_add_glyph_node (GskGpuNodeProcessor *self,
num_glyphs = gsk_text_node_get_num_glyphs (node);
glyphs = gsk_text_node_get_glyphs (node, NULL);
font = gsk_text_node_get_font (node);
offset = *gsk_text_node_get_offset (node);
offset.x += self->offset.x;
offset.y += self->offset.y;
scale = MAX (graphene_vec2_get_x (&self->scale), graphene_vec2_get_y (&self->scale));
inv_scale = 1.f / scale;
offset = point_add (point_from_graphene (gsk_text_node_get_offset (node)),
point_from_graphene (&self->offset));
s = scale_max (scale_from_graphene (&self->scale));
s4 = scale_mul (s, scale_from_float (4));
pango_scale = scale_from_float (PANGO_SCALE);
glyph_align = gsk_gpu_frame_should_optimize (self->frame, GSK_GPU_OPTIMIZE_GLYPH_ALIGN);
hinting = gsk_font_get_hint_style (font) != CAIRO_HINT_STYLE_NONE;
if (hinting && glyph_align)
{
ss = scale (scale_x (s4), scale_y (s));
mask = 3;
}
else if (glyph_align)
{
ss = s4;
mask = 15;
}
else
{
ss = s;
mask = 0;
}
for (i = 0; i < num_glyphs; i++)
{
GskGpuImage *image;
Point origin;
graphene_rect_t glyph_bounds, glyph_tex_rect;
graphene_point_t glyph_offset, glyph_origin;
guint32 descriptor;
GskGpuGlyphLookupFlags flags;
glyph_origin = GRAPHENE_POINT_INIT (offset.x + (float) glyphs[i].geometry.x_offset / PANGO_SCALE,
offset.y + (float) glyphs[i].geometry.y_offset / PANGO_SCALE);
origin = point_add (offset, point_div (point (glyphs[i].geometry.x_offset, glyphs[i].geometry.y_offset), pango_scale));
if (hinting && glyph_align)
{
/* Force glyph_origin.y to be device pixel aligned.
* The hinter expects that.
*/
glyph_origin.x = floor (glyph_origin.x * scale * 4 + .5);
flags = ((int) glyph_origin.x & 3);
glyph_origin.x = 0.25 * inv_scale * glyph_origin.x;
glyph_origin.y = floor (glyph_origin.y * scale + .5) * inv_scale;
}
else if (glyph_align)
{
glyph_origin.x = floor (glyph_origin.x * scale * 4 + .5);
glyph_origin.y = floor (glyph_origin.y * scale * 4 + .5);
flags = ((int) glyph_origin.x & 3) |
(((int) glyph_origin.y & 3) << 2);
glyph_origin.x = 0.25 * inv_scale * glyph_origin.x;
glyph_origin.y = 0.25 * inv_scale * glyph_origin.y;
}
else
{
glyph_origin.x = floor (glyph_origin.x * scale + .5) * inv_scale;
glyph_origin.y = floor (glyph_origin.y * scale + .5) * inv_scale;
flags = 0;
}
origin = point_round (point_mul (origin, ss));
flags = (((int) point_x (origin) & 3) | (((int) point_y (origin) & 3) << 2)) & mask;
origin = point_div (origin, ss);
image = gsk_gpu_device_lookup_glyph_image (device,
self->frame,
font,
glyphs[i].glyph,
flags,
scale,
scale_x (s),
&glyph_bounds,
&glyph_offset);
glyph_tex_rect = GRAPHENE_RECT_INIT (-glyph_bounds.origin.x * inv_scale,
-glyph_bounds.origin.y * inv_scale,
gsk_gpu_image_get_width (image) * inv_scale,
gsk_gpu_image_get_height (image) * inv_scale);
glyph_tex_rect = GRAPHENE_RECT_INIT (-glyph_bounds.origin.x / scale_x (s),
-glyph_bounds.origin.y / scale_y (s),
gsk_gpu_image_get_width (image) / scale_x (s),
gsk_gpu_image_get_height (image) / scale_y (s));
glyph_bounds = GRAPHENE_RECT_INIT (0,
0,
glyph_bounds.size.width * inv_scale,
glyph_bounds.size.height * inv_scale);
glyph_origin = GRAPHENE_POINT_INIT (glyph_origin.x - glyph_offset.x * inv_scale,
glyph_origin.y - glyph_offset.y * inv_scale);
glyph_bounds.size.width / scale_x (s),
glyph_bounds.size.height / scale_y (s));
point_to_graphene (point_sub (origin, point_div (point_from_graphene (&glyph_offset), s)),
&glyph_origin);
descriptor = gsk_gpu_node_processor_add_image (self, image, GSK_GPU_SAMPLER_DEFAULT);

22
gsk/gsktypesprivate.h Normal file
View File

@@ -0,0 +1,22 @@
/* GSK - The GTK Scene Kit
* Copyright 2024 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
typedef struct _Scale Scale;
typedef struct _Point Point;
typedef struct _Box Box;

268
gsk/pointprivate.h Normal file
View File

@@ -0,0 +1,268 @@
#pragma once
#include "gsktypesprivate.h"
#include <graphene.h>
#include <math.h>
#include <smmintrin.h>
#include "scaleprivate.h"
#ifndef USE_SIMD
struct _Point
{
float x, y;
};
static inline float
point_x (const Point p)
{
return p.x;
}
static inline float
point_y (const Point p)
{
return p.y;
}
static inline Point
point (float x,
float y)
{
return (Point) { .x = x, .y = y };
}
static inline Point
point_from_graphene (const graphene_point_t *p)
{
return point (p->x, p->y);
}
static inline void
point_to_graphene (const Point p,
graphene_point_t *v)
{
v->x = p.x;
v->y = p.y;
}
static inline void
point_to_float (const Point p,
float v[2])
{
v[0] = p.x;
v[1] = p.y;
}
static inline Point
point_zero (void)
{
return point (0, 0);
}
static inline Point
point_neg (const Point p)
{
return (Point) { .x = -p.x, .y = -p.y };
}
static inline Point
point_mul (const Point p,
const Scale s)
{
return (Point) { .x = p.x * s.x, .y = p.y * s.y };
}
static inline Point
point_div (const Point p,
const Scale s)
{
return (Point) { .x = p.x / s.x, .y = p.y / s.y };
}
static inline Point
point_add (const Point p1,
const Point p2)
{
return (Point) { .x = p1.x + p2.x, .y = p1.y + p2.y };
}
static inline Point
point_sub (const Point p1,
const Point p2)
{
return (Point) { .x = p1.x - p2.x, .y = p1.y - p2.y };
}
static inline Point
point_floor (const Point p)
{
return (Point) { .x = floorf (p.x), .y = floorf (p.y) };
}
static inline Point
point_ceil (const Point p)
{
return (Point) { .x = ceilf (p.x), .y = ceilf (p.y) };
}
static inline Point
point_round (const Point p)
{
return (Point) { .x = roundf (p.x), .y = roundf (p.y) };
}
#else /* USE_SIMD */
#include <smmintrin.h>
struct _Point
{
GRAPHENE_ALIGNED_DECL (graphene_simd4f_t v, 16);
};
static inline float
point_x (const Point p)
{
return graphene_simd4f_get_x (p.v);
}
static inline float
point_y (const Point p)
{
return graphene_simd4f_get_y (p.v);
}
static inline Point
point (float x,
float y)
{
return (Point) { .v = graphene_simd4f_init (x, y, 0.f, 0.f) };
}
static inline Point
point_from_graphene (const graphene_point_t *p)
{
return point (p->x, p->y);
}
static inline void
point_to_graphene (const Point p,
graphene_point_t *v)
{
v->x = p.x;
v->y = p.y;
}
static inline void
point_to_float (const Point p,
float v[2])
{
graphene_simd4f_dup_2f (p.v, v);
}
static inline Point
point_zero (void)
{
return point (0, 0);
}
static inline Point
point_neg (const Point p)
{
return (Point) { .v = graphene_simd4f_neg (p.v) };
}
static inline Point
point_mul (const Point p,
const Scale s)
{
return (Point) { .v = graphene_simd4f_mul (p.v, s.v) };
}
static inline Point
point_div (const Point p,
const Scale s)
{
return (Point) { .v = graphene_simd4f_div (p.v, s.v) };
}
static inline Point
point_add (const Point p1,
const Point p2)
{
return (Point) { .v = graphene_simd4f_add (p1.v, p2.v) };
}
static inline Point
point_sub (const Point p1,
const Point p2)
{
return (Point) { .v = graphene_simd4f_sub (p1.v, p2.v) };
}
#ifdef __SSE4_1__
#ifndef graphene_simd4f_floor
# define graphene_simd4f_floor(v) \
(__extension__ ({ \
(graphene_simd4f_t) _mm_floor_ps ((v)); \
}))
#endif
#ifndef graphene_simd4f_ceil
# define graphene_simd4f_ceil(v) \
(__extension__ ({ \
(graphene_simd4f_t) _mm_ceil_ps ((v)); \
}))
#endif
#ifndef graphene_simd4f_round
# define graphene_simd4f_round(v) \
(__extension__ ({ \
(graphene_simd4f_t) _mm_round_ps ((v)); \
}))
#endif
static inline Point
point_floor (const Point p)
{
return (Point) { .v = graphene_simd4f_floor (p.v) };
}
static inline Point
point_ceil (const Point p)
{
return (Point) { .v = graphene_simd4f_ceil (p.v) };
}
static inline Point
point_round (const Point p)
{
return (Point) { .v = graphene_simd4f_round (p.v) };
}
#else
static inline Point
point_floor (const Point p)
{
return point (floorf (point_x (p)), floorf (point_y (p)));
}
static inline Point
point_ceil (const Point p)
{
return point (ceilf (point_x (p)), ceilf (point_y (p)));
}
static inline Point
point_round (const Point p)
{
return point (roundf (point_x (p)), roundf (point_y (p)));
}
#endif
#endif

197
gsk/scaleprivate.h Normal file
View File

@@ -0,0 +1,197 @@
#pragma once
#include "gsktypesprivate.h"
#include <graphene.h>
#include <math.h>
#ifndef USE_SIMD
struct _Scale
{
float x, y;
};
static inline float
scale_x (const Scale s)
{
return s.x;
}
static inline float
scale_y (const Scale s)
{
return s.y;
}
static inline Scale
scale (float x,
float y)
{
return (Scale) { .x = x, .y = y };
}
static inline Scale
scale_from_float (float s)
{
return scale (s, s);
}
static inline Scale
scale_from_graphene (const graphene_vec2_t *v)
{
return (Scale) { .x = graphene_vec2_get_x (v), .y = graphene_vec2_get_y (v) };
}
static inline void
scale_to_graphene (const Scale s,
graphene_vec2_t *v)
{
graphene_vec2_init (v, s.x, s.y);
}
static inline void
scale_to_float (const Scale s,
float v[2])
{
v[0] = s.x;
v[1] = s.y;
}
static inline gboolean
scale_equal (const Scale s1,
const Scale s2)
{
return (gboolean) (s1.x == s2.x && s1.y == s2.y);
}
static inline Scale
scale_one (void)
{
return scale (1, 1);
}
static inline Scale
scale_inv (const Scale s)
{
return (Scale) { .x = 1 / s.x, .y = 1 / s.y };
}
static inline Scale
scale_mul (const Scale s1,
const Scale s2)
{
return (Scale) { .x = s1.x * s2.x, .y = s1.y * s2.y };
}
static inline Scale
scale_div (const Scale s1,
const Scale s2)
{
return (Scale) { .x = s1.x / s2.x, .y = s1.y / s2.y };
}
static inline Scale
scale_max (const Scale s)
{
return (Scale) { .x = MAX (s.x, s.y), .y = MAX (s.x, s.y) };
}
#else /* USE_SIMD */
struct _Scale
{
GRAPHENE_ALIGNED_DECL (graphene_simd4f_t v, 16);
};
static inline float
scale_x (const Scale s)
{
return graphene_simd4f_get_x (s.v);
}
static inline float
scale_y (const Scale s)
{
return graphene_simd4f_get_y (s.v);
}
static inline Scale
scale (float x,
float y)
{
return (Scale) { .v = graphene_simd4f_init (x, y, 0.f, 0.f) };
}
static inline Scale
scale_from_float (float s)
{
return scale (s, s);
}
static inline Scale
scale_from_graphene (const graphene_vec2_t *v)
{
return (Scale) { .v = v->__graphene_private_value };
}
static inline void
scale_to_graphene (const Scale s,
graphene_vec2_t *v)
{
v->__graphene_private_value = s.v;
}
static inline void
scale_to_float (const Scale s,
float v[2])
{
graphene_simd4f_dup_2f (s.v, v);
}
static inline gboolean
scale_equal (const Scale s1,
const Scale s2)
{
return (gboolean) graphene_simd4f_cmp_eq (s1.v, s2.v);
}
static inline Scale
scale_one (void)
{
return scale (1, 1);
}
static inline Scale
scale_inv (const Scale s)
{
return (Scale) { .v = graphene_simd4f_reciprocal (s.v) };
}
static inline Scale
scale_mul (const Scale s1,
const Scale s2)
{
return (Scale) { .v = graphene_simd4f_mul (s1.v, s2.v) };
}
static inline Scale
scale_div (const Scale s1,
const Scale s2)
{
return (Scale) { .v = graphene_simd4f_div (s1.v, s2.v) };
}
#ifndef graphene_simd4f_shuffle_yxzw
# define graphene_simd4f_shuffle_yxzw(v) \
(__extension__ ({ \
(graphene_simd4f_t) _mm_shuffle_ps ((v), (v), _MM_SHUFFLE (3, 2, 0, 1)); \
}))
#endif
static inline Scale
scale_max (const Scale s)
{
return (Scale) { .v = graphene_simd4f_max (graphene_simd4f_shuffle_yxzw (s.v), s.v) };
}
#endif