/*
* 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"
#include <cmath>
template <
typename Float =
float >
struct hb_extents_t
{
hb_extents_t () {}
hb_extents_t (
const hb_glyph_extents_t &extents)
{
double x0 = (
double ) extents.x_bearing;
double y0 = (
double ) extents.y_bearing;
double x1 = x0 + (
double ) extents.width;
double y1 = y0 + (
double ) extents.height;
xmin = (
Float ) hb_min (x0, x1);
ymin = (
Float ) hb_min (y0, y1);
xmax = (
Float ) hb_max (x0, x1);
ymax = (
Float ) hb_max (y0, y1);
}
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
{
double x0 = round ((
double ) xmin);
double y0 = round ((
double ) ymin);
double x1 = round ((
double ) xmax);
double y1 = round ((
double ) ymax);
if (unlikely (!std::isfinite (x0) ||
!std::isfinite (y0) ||
!std::isfinite (x1) ||
!std::isfinite (y1)))
return hb_glyph_extents_t {
0 ,
0 ,
0 ,
0 };
return hb_glyph_extents_t {
clamp_to_hb_position (xneg ? x1 : x0),
clamp_to_hb_position (yneg ? y0 : y1),
clamp_to_hb_position (xneg ? x0 - x1 : x1 - x0),
clamp_to_hb_position (yneg ? y1 - y0 : y0 - y1)
};
}
Float xmin =
0 ;
Float ymin =
0 ;
Float xmax = -
1 ;
Float ymax = -
1 ;
static hb_position_t
clamp_to_hb_position (
double v)
{
return (hb_position_t) hb_clamp (v,
(
double ) hb_int_min (hb_position_t),
(
double ) hb_int_max (hb_position_t));
}
};
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)
{
// https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L240
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 cente
r_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 */
Messung V0.5 in Prozent C=97 H=92 G=94
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland