Source code
Revision control
Copy as Markdown
Other Tools
/*
* Copyright © 2022 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
#ifndef HB_GEOMETRY_HH
#define HB_GEOMETRY_HH
#include "hb.hh"
#include "hb-algs.hh"
template <typename Float = float>
struct hb_extents_t
{
hb_extents_t () {}
hb_extents_t (const hb_glyph_extents_t &extents) :
xmin (hb_min (extents.x_bearing, extents.x_bearing + extents.width)),
ymin (hb_min (extents.y_bearing, extents.y_bearing + extents.height)),
xmax (hb_max (extents.x_bearing, extents.x_bearing + extents.width)),
ymax (hb_max (extents.y_bearing, extents.y_bearing + extents.height)) {}
hb_extents_t (Float xmin, Float ymin, Float xmax, Float ymax) :
xmin (xmin), ymin (ymin), xmax (xmax), ymax (ymax) {}
bool is_empty () const { return xmin >= xmax || ymin >= ymax; }
bool is_void () const { return xmin > xmax; }
void union_ (const hb_extents_t &o)
{
if (o.is_empty ()) return;
if (is_empty ())
{
*this = o;
return;
}
xmin = hb_min (xmin, o.xmin);
ymin = hb_min (ymin, o.ymin);
xmax = hb_max (xmax, o.xmax);
ymax = hb_max (ymax, o.ymax);
}
void intersect (const hb_extents_t &o)
{
if (o.is_empty () || is_empty ())
{
*this = hb_extents_t {};
return;
}
xmin = hb_max (xmin, o.xmin);
ymin = hb_max (ymin, o.ymin);
xmax = hb_min (xmax, o.xmax);
ymax = hb_min (ymax, o.ymax);
}
void
add_point (Float x, Float y)
{
if (unlikely (is_void ()))
{
xmin = xmax = x;
ymin = ymax = y;
}
else
{
xmin = hb_min (xmin, x);
ymin = hb_min (ymin, y);
xmax = hb_max (xmax, x);
ymax = hb_max (ymax, y);
}
}
hb_glyph_extents_t to_glyph_extents (bool xneg = false, bool yneg = false) const
{
hb_position_t x0 = (hb_position_t) roundf (xmin);
hb_position_t y0 = (hb_position_t) roundf (ymin);
hb_position_t x1 = (hb_position_t) roundf (xmax);
hb_position_t y1 = (hb_position_t) roundf (ymax);
return hb_glyph_extents_t {xneg ? x1 : x0,
yneg ? y0 : y1,
xneg ? x0 - x1 : x1 - x0,
yneg ? y1 - y0 : y0 - y1};
}
Float xmin = 0;
Float ymin = 0;
Float xmax = -1;
Float ymax = -1;
};
template <typename Float = float>
struct hb_transform_t
{
hb_transform_t () {}
hb_transform_t (Float xx, Float yx,
Float xy, Float yy,
Float x0, Float y0) :
xx (xx), yx (yx), xy (xy), yy (yy), x0 (x0), y0 (y0) {}
bool is_identity () const
{
return xx == 1 && yx == 0 &&
xy == 0 && yy == 1 &&
x0 == 0 && y0 == 0;
}
bool is_translation () const
{
return xx == 1 && yx == 0 &&
xy == 0 && yy == 1;
}
void multiply (const hb_transform_t &o, bool before=false)
{
// Copied from cairo-matrix.c
const hb_transform_t &a = before ? o : *this;
const hb_transform_t &b = before ? *this : o;
*this = {
a.xx * b.xx + a.xy * b.yx,
a.yx * b.xx + a.yy * b.yx,
a.xx * b.xy + a.xy * b.yy,
a.yx * b.xy + a.yy * b.yy,
a.xx * b.x0 + a.xy * b.y0 + a.x0,
a.yx * b.x0 + a.yy * b.y0 + a.y0
};
}
HB_ALWAYS_INLINE
void transform_distance (Float &dx, Float &dy) const
{
Float new_x = xx * dx + xy * dy;
Float new_y = yx * dx + yy * dy;
dx = new_x;
dy = new_y;
}
HB_ALWAYS_INLINE
void transform_point (Float &x, Float &y) const
{
Float new_x = x0 + xx * x + xy * y;
Float new_y = y0 + yx * x + yy * y;
x = new_x;
y = new_y;
}
void transform_extents (hb_extents_t<Float> &extents) const
{
Float quad_x[4], quad_y[4];
quad_x[0] = extents.xmin;
quad_y[0] = extents.ymin;
quad_x[1] = extents.xmin;
quad_y[1] = extents.ymax;
quad_x[2] = extents.xmax;
quad_y[2] = extents.ymin;
quad_x[3] = extents.xmax;
quad_y[3] = extents.ymax;
extents = hb_extents_t<Float> {};
for (unsigned i = 0; i < 4; i++)
{
transform_point (quad_x[i], quad_y[i]);
extents.add_point (quad_x[i], quad_y[i]);
}
}
void transform (const hb_transform_t &o, bool before=false) { multiply (o, before); }
static hb_transform_t translation (Float x, Float y)
{
return {1, 0, 0, 1, x, y};
}
void translate (Float x, Float y, bool before=false)
{
if (before)
{
x0 += x;
y0 += y;
}
else
{
if (x == 0 && y == 0)
return;
x0 += xx * x + xy * y;
y0 += yx * x + yy * y;
}
}
static hb_transform_t scaling (Float scaleX, Float scaleY)
{
return {scaleX, 0, 0, scaleY, 0, 0};
}
void scale (Float scaleX, Float scaleY)
{
if (scaleX == 1 && scaleY == 1)
return;
xx *= scaleX;
yx *= scaleX;
xy *= scaleY;
yy *= scaleY;
}
static hb_transform_t scaling_around_center (Float scaleX, Float scaleY, Float center_x, Float center_y)
{
return {scaleX, 0, 0, scaleY,
center_x ? (1 - scaleX) * center_x : 0,
center_y ? (1 - scaleY) * center_y : 0};
}
void scale_around_center (Float scaleX, Float scaleY, Float center_x, Float center_y)
{
if (scaleX == 1 && scaleY == 1)
return;
transform (scaling_around_center (scaleX, scaleY, center_x, center_y));
}
static hb_transform_t rotation (Float radians)
{
Float c;
Float s;
hb_sincos (radians, s, c);
return {c, s, -s, c, 0, 0};
}
void rotate (Float radians, bool before=false)
{
if (radians == 0)
return;
transform (rotation (radians), before);
}
static hb_transform_t rotation_around_center (Float radians, Float center_x, Float center_y)
{
Float s, c;
hb_sincos (radians, s, c);
return {
c, s, -s, c,
(1 - c) * center_x + s * center_y,
-s * center_x + (1 - c) * center_y
};
}
void rotate_around_center (Float radians, Float center_x, Float center_y, bool before=false)
{
if (radians == 0)
return;
transform (rotation_around_center (radians, center_x, center_y), before);
}
static hb_transform_t skewing (Float skewX, Float skewY)
{
return {1, skewY ? tanf (skewY) : 0, skewX ? tanf (skewX) : 0, 1, 0, 0};
}
void skew (Float skewX, Float skewY)
{
if (skewX == 0 && skewY == 0)
return;
transform (skewing (skewX, skewY));
}
static hb_transform_t skewing_around_center (Float skewX, Float skewY, Float center_x, Float center_y)
{
skewX = skewX ? tanf (skewX) : 0;
skewY = skewY ? tanf (skewY) : 0;
return {
1, skewY, skewX, 1,
center_y ? -skewX * center_y : 0,
center_x ? -skewY * center_x : 0
};
}
void skew_around_center (Float skewX, Float skewY, Float center_x, Float center_y)
{
if (skewX == 0 && skewY == 0)
return;
transform (skewing_around_center (skewX, skewY, center_x, center_y));
}
Float xx = 1;
Float yx = 0;
Float xy = 0;
Float yy = 1;
Float x0 = 0;
Float y0 = 0;
};
#define HB_TRANSFORM_IDENTITY {1, 0, 0, 1, 0, 0}
template <typename Float = float>
struct hb_bounds_t
{
enum status_t {
UNBOUNDED,
BOUNDED,
EMPTY,
};
hb_bounds_t (status_t status = UNBOUNDED) : status (status) {}
hb_bounds_t (const hb_extents_t<Float> &extents) :
status (extents.is_empty () ? EMPTY : BOUNDED), extents (extents) {}
void union_ (const hb_bounds_t &o)
{
if (o.status == UNBOUNDED)
status = UNBOUNDED;
else if (o.status == BOUNDED)
{
if (status == EMPTY)
*this = o;
else if (status == BOUNDED)
extents.union_ (o.extents);
}
}
void intersect (const hb_bounds_t &o)
{
if (o.status == EMPTY)
status = EMPTY;
else if (o.status == BOUNDED)
{
if (status == UNBOUNDED)
*this = o;
else if (status == BOUNDED)
{
extents.intersect (o.extents);
if (extents.is_empty ())
status = EMPTY;
}
}
}
status_t status;
hb_extents_t<Float> extents;
};
template <typename Float = float>
struct hb_transform_decomposed_t
{
Float translateX = 0;
Float translateY = 0;
Float rotation = 0; // in radians, counter-clockwise
Float scaleX = 1;
Float scaleY = 1;
Float skewX = 0; // in radians, counter-clockwise
Float skewY = 0; // in radians, counter-clockwise
Float tCenterX = 0;
Float tCenterY = 0;
operator bool () const
{
return translateX || translateY ||
rotation ||
scaleX != 1 || scaleY != 1 ||
skewX || skewY ||
tCenterX || tCenterY;
}
hb_transform_t<Float> to_transform () const
{
hb_transform_t<Float> t;
t.translate (translateX + tCenterX, translateY + tCenterY);
t.rotate (rotation);
t.scale (scaleX, scaleY);
t.skew (-skewX, skewY);
t.translate (-tCenterX, -tCenterY);
return t;
}
};
#endif /* HB_GEOMETRY_HH */