/*
* Copyright ( C ) 2026 Behdad Esfahbod
*
* This is part of HarfBuzz , a text shaping library .
*
* Cubic - to - quadratic B é zier conversion .
* Ported from fonttools cu2qu :
* https : //github.com/fonttools/fonttools/blob/main/Lib/fontTools/cu2qu/cu2qu.py
*
* 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 .
*
* Author ( s ) : Behdad Esfahbod
*/
#ifndef HB_GPU_CU2QU_HH
#define HB_GPU_CU2QU_HH
#include "hb-gpu-draw.hh"
#include <cmath>
/* Maximum approximation error in font units.
* 0.5 is well below one unit in any reasonable coordinate system. */
#ifndef HB_GPU_CU2QU_TOLERANCE
#define HB_GPU_CU2QU_TOLERANCE 0 .5
#endif
/* Maximum subdivision depth. Each level halves the parameter interval;
* at depth 10 the sub - curve spans 1 / 1024 of the original , so the
* quadratic error (O(dt^4)) is reduced by a factor of ~10^12. */
#define HB_GPU_CU2QU_MAX_DEPTH 10
struct hb_gpu_cu2qu_point_t
{
double x, y;
};
static inline hb_gpu_cu2qu_point_t
hb_gpu_cu2qu_lerp (hb_gpu_cu2qu_point_t a, hb_gpu_cu2qu_point_t b, double t)
{
return {a.x + (b.x - a.x) * t,
a.y + (b.y - a.y) * t};
}
/*
* Check whether a cubic error curve stays within ` tolerance ` of the origin .
*/
static bool
hb_gpu_cubic_farthest_fit_inside (hb_gpu_cu2qu_point_t p0,
hb_gpu_cu2qu_point_t p1,
hb_gpu_cu2qu_point_t p2,
hb_gpu_cu2qu_point_t p3,
double tolerance, unsigned depth)
{
if (hypot (p1.x, p1.y) <= tolerance &&
hypot (p2.x, p2.y) <= tolerance)
return true ;
if (depth >= 8 )
return false ;
hb_gpu_cu2qu_point_t mid = {(p0.x + 3 * (p1.x + p2.x) + p3.x) * 0 .125 ,
(p0.y + 3 * (p1.y + p2.y) + p3.y) * 0 .125 };
if (hypot (mid.x, mid.y) > tolerance)
return false ;
hb_gpu_cu2qu_point_t d3 = {(p3.x + p2.x - p1.x - p0.x) * 0 .125 ,
(p3.y + p2.y - p1.y - p0.y) * 0 .125 };
return hb_gpu_cubic_farthest_fit_inside (
p0,
hb_gpu_cu2qu_lerp (p0, p1, 0 .5 ),
{mid.x - d3.x, mid.y - d3.y},
mid,
tolerance, depth + 1
) && hb_gpu_cubic_farthest_fit_inside (
mid,
{mid.x + d3.x, mid.y + d3.y},
hb_gpu_cu2qu_lerp (p2, p3, 0 .5 ),
p3,
tolerance, depth + 1
);
}
/*
* Try to approximate a cubic B é zier with a single quadratic .
*/
static bool
hb_gpu_cubic_approx_quadratic (hb_gpu_cu2qu_point_t c0,
hb_gpu_cu2qu_point_t c1,
hb_gpu_cu2qu_point_t c2,
hb_gpu_cu2qu_point_t c3,
double tolerance,
hb_gpu_cu2qu_point_t *q1)
{
double ax = c1.x - c0.x, ay = c1.y - c0.y;
double dx = c3.x - c2.x, dy = c3.y - c2.y;
double px = -ay, py = ax;
double denom = px * dx + py * dy;
if (fabs (denom) < 1 e-12 )
return false ;
double h = (px * (c0.x - c2.x) + py * (c0.y - c2.y)) / denom;
q1->x = c2.x + dx * h;
q1->y = c2.y + dy * h;
hb_gpu_cu2qu_point_t err1 = {c0.x + (q1->x - c0.x) * (2 .0 / 3 .0 ) - c1.x,
c0.y + (q1->y - c0.y) * (2 .0 / 3 .0 ) - c1.y};
hb_gpu_cu2qu_point_t err2 = {c3.x + (q1->x - c3.x) * (2 .0 / 3 .0 ) - c2.x,
c3.y + (q1->y - c3.y) * (2 .0 / 3 .0 ) - c2.y};
return hb_gpu_cubic_farthest_fit_inside ({0 , 0 }, err1, err2, {0 , 0 }, tolerance, 0 );
}
/*
* Split a cubic at t = 0 . 5 ( de Casteljau ) .
*/
static void
hb_gpu_split_cubic_half (hb_gpu_cu2qu_point_t c0,
hb_gpu_cu2qu_point_t c1,
hb_gpu_cu2qu_point_t c2,
hb_gpu_cu2qu_point_t c3,
hb_gpu_cu2qu_point_t out[7 ])
{
hb_gpu_cu2qu_point_t m01 = hb_gpu_cu2qu_lerp (c0, c1, 0 .5 );
hb_gpu_cu2qu_point_t m12 = hb_gpu_cu2qu_lerp (c1, c2, 0 .5 );
hb_gpu_cu2qu_point_t m23 = hb_gpu_cu2qu_lerp (c2, c3, 0 .5 );
hb_gpu_cu2qu_point_t m012 = hb_gpu_cu2qu_lerp (m01, m12, 0 .5 );
hb_gpu_cu2qu_point_t m123 = hb_gpu_cu2qu_lerp (m12, m23, 0 .5 );
hb_gpu_cu2qu_point_t mid = hb_gpu_cu2qu_lerp (m012, m123, 0 .5 );
out[0 ] = c0; out[1 ] = m01; out[2 ] = m012;
out[3 ] = mid;
out[4 ] = m123; out[5 ] = m23; out[6 ] = c3;
}
/*
* Recursively convert a cubic into quadratic segments .
*/
static void
hb_gpu_cubic_to_quadratics (hb_gpu_draw_t *g,
hb_gpu_cu2qu_point_t c0,
hb_gpu_cu2qu_point_t c1,
hb_gpu_cu2qu_point_t c2,
hb_gpu_cu2qu_point_t c3,
double tolerance, unsigned depth)
{
hb_gpu_cu2qu_point_t q1;
if (hb_gpu_cubic_approx_quadratic (c0, c1, c2, c3, tolerance, &q1))
{
g->acc_conic_to (q1.x, q1.y, c3.x, c3.y);
return ;
}
if (depth >= HB_GPU_CU2QU_MAX_DEPTH)
{
g->acc_line_to (c3.x, c3.y);
return ;
}
hb_gpu_cu2qu_point_t pts[7 ];
hb_gpu_split_cubic_half (c0, c1, c2, c3, pts);
hb_gpu_cubic_to_quadratics (g, pts[0 ], pts[1 ], pts[2 ], pts[3 ], tolerance, depth + 1 );
hb_gpu_cubic_to_quadratics (g, pts[3 ], pts[4 ], pts[5 ], pts[6 ], tolerance, depth + 1 );
}
#endif /* HB_GPU_CU2QU_HH */
Messung V0.5 in Prozent C=96 H=100 G=97
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-09-30)
¤
*© Formatika GbR, Deutschland