Werner Lemberg pushed to branch master at FreeType / FreeType
Commits:
-
5499d7bf
by Anuj Verma at 2022-03-05T16:44:23+01:00
-
2600ef63
by Anuj Verma at 2022-03-05T16:53:45+01:00
-
360e2507
by Anuj Verma at 2022-03-05T17:00:10+01:00
3 changed files:
Changes:
... | ... | @@ -3409,6 +3409,44 @@ FT_BEGIN_HEADER |
3409 | 3409 | * }
|
3410 | 3410 | *
|
3411 | 3411 | * ```
|
3412 | + *
|
|
3413 | + * FreeType has two rasterizers for generating SDF, namely:
|
|
3414 | + *
|
|
3415 | + * 1. `sdf` for generating SDF directly from glyph's outline, and
|
|
3416 | + *
|
|
3417 | + * 2. `bsdf` for generating SDF from rasterized bitmaps.
|
|
3418 | + *
|
|
3419 | + * Depending on the glyph type (i.e., outline or bitmap), one of the two
|
|
3420 | + * rasterizers is chosen at runtime and used for generating SDFs. To
|
|
3421 | + * force the use of `bsdf` you should render the glyph with any of the
|
|
3422 | + * FreeType's other rendering modes (e.g., `FT_RENDER_MODE_NORMAL`) and
|
|
3423 | + * then re-render with `FT_RENDER_MODE_SDF`.
|
|
3424 | + *
|
|
3425 | + * There are some issues with stability and possible failures of the SDF
|
|
3426 | + * renderers (specifically `sdf`).
|
|
3427 | + *
|
|
3428 | + * 1. The `sdf` rasterizer is sensitive to really small features (e.g.,
|
|
3429 | + * sharp turns that are less than 1~pixel) and imperfections in the
|
|
3430 | + * glyph's outline, causing artifacts in the final output.
|
|
3431 | + *
|
|
3432 | + * 2. The `sdf` rasterizer has limited support for handling intersecting
|
|
3433 | + * contours and *cannot* handle self-intersecting contours whatsoever.
|
|
3434 | + * Self-intersection happens when a single connected contour intersect
|
|
3435 | + * itself at some point; having these in your font definitely pose a
|
|
3436 | + * problem to the rasterizer and cause artifacts, too.
|
|
3437 | + *
|
|
3438 | + * 3. Generating SDF for really small glyphs may result in undesirable
|
|
3439 | + * output; the pixel grid (which stores distance information) becomes
|
|
3440 | + * too coarse.
|
|
3441 | + *
|
|
3442 | + * 4. Since the output buffer is normalized, precision at smaller spreads
|
|
3443 | + * is greater than precision at larger spread values because the
|
|
3444 | + * output range of [0..255] gets mapped to a smaller SDF range. A
|
|
3445 | + * spread of~2 should be sufficient in most cases.
|
|
3446 | + *
|
|
3447 | + * Points (1) and (2) can be avoided by using the `bsdf` rasterizer,
|
|
3448 | + * which is more stable than the `sdf` rasterizer in general.
|
|
3449 | + *
|
|
3412 | 3450 | */
|
3413 | 3451 | typedef enum FT_Render_Mode_
|
3414 | 3452 | {
|
... | ... | @@ -738,6 +738,18 @@ |
738 | 738 | |
739 | 739 | contour = shape->contours;
|
740 | 740 | |
741 | + /* If the control point coincides with any of the end points */
|
|
742 | + /* then it is a line and should be treated as one to avoid */
|
|
743 | + /* unnecessary complexity later in the algorithm. */
|
|
744 | + if ( ( contour->last_pos.x == control_1->x &&
|
|
745 | + contour->last_pos.y == control_1->y ) ||
|
|
746 | + ( control_1->x == to->x &&
|
|
747 | + control_1->y == to->y ) )
|
|
748 | + {
|
|
749 | + sdf_line_to( to, user );
|
|
750 | + goto Exit;
|
|
751 | + }
|
|
752 | + |
|
741 | 753 | FT_CALL( sdf_edge_new( memory, &edge ) );
|
742 | 754 | |
743 | 755 | edge->edge_type = SDF_EDGE_CONIC;
|
... | ... | @@ -764,9 +776,9 @@ |
764 | 776 | const FT_26D6_Vec* to,
|
765 | 777 | void* user )
|
766 | 778 | {
|
767 | - SDF_Shape* shape = ( SDF_Shape* )user;
|
|
768 | - SDF_Edge* edge = NULL;
|
|
769 | - SDF_Contour* contour = NULL;
|
|
779 | + SDF_Shape* shape = ( SDF_Shape* )user;
|
|
780 | + SDF_Edge* edge = NULL;
|
|
781 | + SDF_Contour* contour = NULL;
|
|
770 | 782 | |
771 | 783 | FT_Error error = FT_Err_Ok;
|
772 | 784 | FT_Memory memory = shape->memory;
|
... | ... | @@ -1137,23 +1149,38 @@ |
1137 | 1149 | FT_Int max_splits,
|
1138 | 1150 | SDF_Edge** out )
|
1139 | 1151 | {
|
1140 | - FT_Error error = FT_Err_Ok;
|
|
1141 | - FT_26D6_Vec cpos[7];
|
|
1142 | - SDF_Edge* left,* right;
|
|
1152 | + FT_Error error = FT_Err_Ok;
|
|
1153 | + FT_26D6_Vec cpos[7];
|
|
1154 | + SDF_Edge* left, *right;
|
|
1155 | + const FT_26D6 threshold = ONE_PIXEL / 4;
|
|
1143 | 1156 | |
1144 | 1157 | |
1145 | - if ( !memory || !out )
|
|
1158 | + if ( !memory || !out )
|
|
1146 | 1159 | {
|
1147 | 1160 | error = FT_THROW( Invalid_Argument );
|
1148 | 1161 | goto Exit;
|
1149 | 1162 | }
|
1150 | 1163 | |
1151 | - /* split the conic */
|
|
1164 | + /* split the cubic */
|
|
1152 | 1165 | cpos[0] = control_points[0];
|
1153 | 1166 | cpos[1] = control_points[1];
|
1154 | 1167 | cpos[2] = control_points[2];
|
1155 | 1168 | cpos[3] = control_points[3];
|
1156 | 1169 | |
1170 | + /* If the segment is flat enough we won't get any benefit by */
|
|
1171 | + /* splitting it further, so we can just stop splitting. */
|
|
1172 | + /* */
|
|
1173 | + /* Check the deviation of the Bezier curve and stop if it is */
|
|
1174 | + /* smaller than the pre-defined `threshold` value. */
|
|
1175 | + if ( FT_ABS( 2 * cpos[0].x - 3 * cpos[1].x + cpos[3].x ) < threshold &&
|
|
1176 | + FT_ABS( 2 * cpos[0].y - 3 * cpos[1].y + cpos[3].y ) < threshold &&
|
|
1177 | + FT_ABS( cpos[0].x - 3 * cpos[2].x + 2 * cpos[3].x ) < threshold &&
|
|
1178 | + FT_ABS( cpos[0].y - 3 * cpos[2].y + 2 * cpos[3].y ) < threshold )
|
|
1179 | + {
|
|
1180 | + split_cubic( cpos );
|
|
1181 | + goto Append;
|
|
1182 | + }
|
|
1183 | + |
|
1157 | 1184 | split_cubic( cpos );
|
1158 | 1185 | |
1159 | 1186 | /* If max number of splits is done */
|
... | ... | @@ -1250,13 +1277,32 @@ |
1250 | 1277 | /* Subdivide the curve and add it to the list. */
|
1251 | 1278 | {
|
1252 | 1279 | FT_26D6_Vec ctrls[3];
|
1280 | + FT_26D6 dx, dy;
|
|
1281 | + FT_UInt num_splits;
|
|
1253 | 1282 | |
1254 | 1283 | |
1255 | 1284 | ctrls[0] = edge->start_pos;
|
1256 | 1285 | ctrls[1] = edge->control_a;
|
1257 | 1286 | ctrls[2] = edge->end_pos;
|
1258 | 1287 | |
1259 | - error = split_sdf_conic( memory, ctrls, 32, &new_edges );
|
|
1288 | + dx = FT_ABS( ctrls[2].x + ctrls[0].x - 2 * ctrls[1].x );
|
|
1289 | + dy = FT_ABS( ctrls[2].y + ctrls[0].y - 2 * ctrls[1].y );
|
|
1290 | + if ( dx < dy )
|
|
1291 | + dx = dy;
|
|
1292 | + |
|
1293 | + /* Calculate the number of necessary bisections. Each */
|
|
1294 | + /* bisection causes a four-fold reduction of the deviation, */
|
|
1295 | + /* hence we bisect the Bezier curve until the deviation */
|
|
1296 | + /* becomes less than 1/8th of a pixel. For more details */
|
|
1297 | + /* check file `ftgrays.c`. */
|
|
1298 | + num_splits = 1;
|
|
1299 | + while ( dx > ONE_PIXEL / 8 )
|
|
1300 | + {
|
|
1301 | + dx >>= 2;
|
|
1302 | + num_splits <<= 1;
|
|
1303 | + }
|
|
1304 | + |
|
1305 | + error = split_sdf_conic( memory, ctrls, num_splits, &new_edges );
|
|
1260 | 1306 | }
|
1261 | 1307 | break;
|
1262 | 1308 | |
... | ... | @@ -3286,6 +3332,7 @@ |
3286 | 3332 | FT_26D6_Vec grid_point = zero_vector;
|
3287 | 3333 | SDF_Signed_Distance dist = max_sdf;
|
3288 | 3334 | FT_UInt index = 0;
|
3335 | + FT_16D16 diff = 0;
|
|
3289 | 3336 | |
3290 | 3337 | |
3291 | 3338 | if ( x < 0 || x >= width )
|
... | ... | @@ -3313,7 +3360,7 @@ |
3313 | 3360 | if ( dist.distance > sp_sq )
|
3314 | 3361 | continue;
|
3315 | 3362 | |
3316 | - /* square_root the values and fit in a 6.10 fixed-point */
|
|
3363 | + /* take the square root of the distance if required */
|
|
3317 | 3364 | if ( USE_SQUARED_DISTANCES )
|
3318 | 3365 | dist.distance = square_root( dist.distance );
|
3319 | 3366 | |
... | ... | @@ -3325,11 +3372,15 @@ |
3325 | 3372 | /* check whether the pixel is set or not */
|
3326 | 3373 | if ( dists[index].sign == 0 )
|
3327 | 3374 | dists[index] = dist;
|
3328 | - else if ( dists[index].distance > dist.distance )
|
|
3329 | - dists[index] = dist;
|
|
3330 | - else if ( FT_ABS( dists[index].distance - dist.distance )
|
|
3331 | - < CORNER_CHECK_EPSILON )
|
|
3332 | - dists[index] = resolve_corner( dists[index], dist );
|
|
3375 | + else
|
|
3376 | + {
|
|
3377 | + diff = FT_ABS( dists[index].distance - dist.distance );
|
|
3378 | + |
|
3379 | + if ( diff <= CORNER_CHECK_EPSILON )
|
|
3380 | + dists[index] = resolve_corner( dists[index], dist );
|
|
3381 | + else if ( dists[index].distance > dist.distance )
|
|
3382 | + dists[index] = dist;
|
|
3383 | + }
|
|
3333 | 3384 | }
|
3334 | 3385 | }
|
3335 | 3386 |
... | ... | @@ -48,6 +48,8 @@ FT_BEGIN_HEADER |
48 | 48 | #define MIN_SPREAD 2
|
49 | 49 | /* maximum spread supported by the renderer */
|
50 | 50 | #define MAX_SPREAD 32
|
51 | + /* pixel size in 26.6 */
|
|
52 | +#define ONE_PIXEL ( 1 << 6 )
|
|
51 | 53 | |
52 | 54 | |
53 | 55 | /**************************************************************************
|