microvg  8.0.3
microvg
vg_outline_box.c
Go to the documentation of this file.
1 /*
2  * C
3  *
4  * Copyright 2026 MicroEJ Corp. All rights reserved.
5  * MicroEJ Corp. PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  *
7  * Build: 7E4D1F7C
8  */
9 
17 // -----------------------------------------------------------------------------
18 // Includes
19 // -----------------------------------------------------------------------------
20 
21 #include "vg_configuration.h"
22 
23 #if defined VG_FEATURE_FONT && \
24  (defined VG_FEATURE_FONT_FREETYPE_VECTOR || defined VG_FEATURE_FONT_FREETYPE_BITMAP) && \
25  (VG_FEATURE_FONT == VG_FEATURE_FONT_FREETYPE_VECTOR || VG_FEATURE_FONT == VG_FEATURE_FONT_FREETYPE_BITMAP)
26 
27 #include <float.h>
28 #include <math.h>
29 
30 #include "vg_outline_box.h"
31 #include "ui_util.h"
32 
33 // -----------------------------------------------------------------------------
34 // Internal functions
35 // -----------------------------------------------------------------------------
36 
37 static void __add_point(VG_OUTLINE_BOX *box, float x, float y) {
38  box->x_min = MIN(box->x_min, x);
39  box->x_max = MAX(box->x_max, x);
40  box->y_min = MIN(box->y_min, y);
41  box->y_max = MAX(box->y_max, y);
42  box->empty = false;
43 }
44 
45 static void __add_axis_value(VG_OUTLINE_BOX *box, float v, bool is_x) {
46  if (is_x) {
47  box->x_min = MIN(box->x_min, v);
48  box->x_max = MAX(box->x_max, v);
49  } else {
50  box->y_min = MIN(box->y_min, v);
51  box->y_max = MAX(box->y_max, v);
52  }
53 }
54 
55 static float __quad_at(float p0, float p1, float p2, float t) {
56  float u = 1.f - t;
57  return (u * u * p0) + (2.f * u * t * p1) + (t * t * p2);
58 }
59 
60 static float __cubic_at(float p0, float p1, float p2, float p3, float t) {
61  float u = 1.f - t;
62  return (u * u * u * p0) + (3.f * u * u * t * p1) + (3.f * u * t * t * p2) + (t * t * t * p3);
63 }
64 
65 static void __quad_axis(VG_OUTLINE_BOX *box, float p0, float p1, float p2, float lo, float hi,
66  bool is_x) {
67  if ((p1 >= lo) && (p1 <= hi)) {
68  return; // a curve stays within the hull of its points, so it cannot leave the box
69  }
70  /*
71  * d is an integer combination of outline coordinates, so it is either exactly 0 or at least 1
72  * in magnitude: comparing it to 0.f is exact, never a near-miss needing a tolerance.
73  */
74  float d = (p0 - (2.f * p1)) + p2;
75  if (0.f == d) {
76  return;
77  }
78  float t = (p0 - p1) / d;
79  if ((t > 0.f) && (t < 1.f)) {
80  __add_axis_value(box, __quad_at(p0, p1, p2, t), is_x);
81  }
82 }
83 
84 static void __cubic_axis(VG_OUTLINE_BOX *box, float p0, float p1, float p2, float p3, float lo,
85  float hi, bool is_x) {
86  if ((p1 >= lo) && (p1 <= hi) && (p2 >= lo) && (p2 <= hi)) {
87  return; // a curve stays within the hull of its points, so it cannot leave the box
88  }
89  float a = ((-p0 + (3.f * p1)) - (3.f * p2)) + p3;
90  float b = ((2.f * p0) - (4.f * p1)) + (2.f * p2);
91  float c = -p0 + p1;
92  float roots[2];
93  int nb = 0;
94  if (0.f == a) {
95  if (0.f != b) {
96  roots[nb] = -c / b;
97  nb++;
98  }
99  } else {
100  float disc = (b * b) - (4.f * a * c);
101  if (disc >= 0.f) {
102  float sq = sqrtf(disc);
103  roots[nb] = (-b + sq) / (2.f * a);
104  nb++;
105  roots[nb] = (-b - sq) / (2.f * a);
106  nb++;
107  }
108  }
109  for (int i = 0; i < nb; i++) {
110  float t = roots[i];
111  if ((t > 0.f) && (t < 1.f)) {
112  __add_axis_value(box, __cubic_at(p0, p1, p2, p3, t), is_x);
113  }
114  }
115 }
116 
117 // -----------------------------------------------------------------------------
118 // FT_Outline_Decompose callbacks
119 // -----------------------------------------------------------------------------
120 
121 struct outline_walk {
122  VG_OUTLINE_BOX *box;
123  FT_Vector current;
124 };
125 
126 static int __move_to(const FT_Vector *to, void *user) {
127  struct outline_walk *w = (struct outline_walk *)user;
128  __add_point(w->box, (float)to->x, (float)to->y);
129  w->current = *to;
130  return 0;
131 }
132 
133 static int __line_to(const FT_Vector *to, void *user) {
134  struct outline_walk *w = (struct outline_walk *)user;
135  __add_point(w->box, (float)to->x, (float)to->y);
136  w->current = *to;
137  return 0;
138 }
139 
140 static int __conic_to(const FT_Vector *control, const FT_Vector *to, void *user) {
141  struct outline_walk *w = (struct outline_walk *)user;
142  __add_point(w->box, (float)to->x, (float)to->y);
143  __quad_axis(w->box, (float)w->current.x, (float)control->x, (float)to->x, w->box->x_min,
144  w->box->x_max, true);
145  __quad_axis(w->box, (float)w->current.y, (float)control->y, (float)to->y, w->box->y_min,
146  w->box->y_max, false);
147  w->current = *to;
148  return 0;
149 }
150 
151 static int __cubic_to(const FT_Vector *control1, const FT_Vector *control2, const FT_Vector *to,
152  void *user) {
153  struct outline_walk *w = (struct outline_walk *)user;
154  __add_point(w->box, (float)to->x, (float)to->y);
155  __cubic_axis(w->box, (float)w->current.x, (float)control1->x, (float)control2->x, (float)to->x,
156  w->box->x_min, w->box->x_max, true);
157  __cubic_axis(w->box, (float)w->current.y, (float)control1->y, (float)control2->y, (float)to->y,
158  w->box->y_min, w->box->y_max, false);
159  w->current = *to;
160  return 0;
161 }
162 
163 // -----------------------------------------------------------------------------
164 // vg_outline_box.h functions
165 // -----------------------------------------------------------------------------
166 
167 // See the header file for the function documentation
168 void VG_OUTLINE_BOX_compute(const FT_Outline *outline, VG_OUTLINE_BOX *box) {
169  box->x_min = FLT_MAX;
170  box->x_max = -FLT_MAX;
171  box->y_min = FLT_MAX;
172  box->y_max = -FLT_MAX;
173  box->empty = true;
174 
175  static const FT_Outline_Funcs funcs = {
176  .move_to = __move_to,
177  .line_to = __line_to,
178  .conic_to = __conic_to,
179  .cubic_to = __cubic_to,
180  .shift = 0,
181  .delta = 0,
182  };
183 
184  struct outline_walk walk;
185  walk.box = box;
186  walk.current.x = 0;
187  walk.current.y = 0;
188  int error = FT_Outline_Decompose((FT_Outline *)outline, &funcs, &walk);
189  if ((0 != error) || box->empty) {
190  // a malformed outline may have pushed points in before FreeType detected it: undo that
191  // partial walk, so the caller sees "nothing drawn" and not a truncated box
192  box->x_min = 0.f;
193  box->x_max = 0.f;
194  box->y_min = 0.f;
195  box->y_max = 0.f;
196  box->empty = true;
197  }
198 }
199 
200 // cppcheck-suppress [misra-c2012-3.2]
201 #endif \
202  // defined VG_FEATURE_FONT && \
203  // (defined VG_FEATURE_FONT_FREETYPE_VECTOR || defined VG_FEATURE_FONT_FREETYPE_BITMAP) && \
204  // (VG_FEATURE_FONT == VG_FEATURE_FONT_FREETYPE_VECTOR || VG_FEATURE_FONT == VG_FEATURE_FONT_FREETYPE_BITMAP)
205 
206 // -----------------------------------------------------------------------------
207 // EOF
208 // -----------------------------------------------------------------------------
The box of the curve an outline draws, control points excluded.
MicroEJ MicroVG library low level API: enable some features according to the hardware capacities.
MicroEJ MicroVG library low level API: box of the curve an outline draws.
void VG_OUTLINE_BOX_compute(const FT_Outline *outline, VG_OUTLINE_BOX *box)
Computes the box of the curve the given outline draws, where the box a font declares also bounds the ...