1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
/***************************************************************************/
/* */
/* afwarp.c */
/* */
/* Auto-fitter warping algorithm (body). */
/* */
/* Copyright 2006-2017 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
/* modified, and distributed under the terms of the FreeType project */
/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
/* this file you indicate that you have read the license and */
/* understand and accept it fully. */
/* */
/***************************************************************************/
/*
* The idea of the warping code is to slightly scale and shift a glyph
* within a single dimension so that as much of its segments are aligned
* (more or less) on the grid. To find out the optimal scaling and
* shifting value, various parameter combinations are tried and scored.
*/
#include "afwarp.h"
#ifdef AF_CONFIG_OPTION_USE_WARPER
/*************************************************************************/
/* */
/* The macro FT_COMPONENT is used in trace mode. It is an implicit */
/* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
/* messages during execution. */
/* */
#undef FT_COMPONENT
#define FT_COMPONENT trace_afwarp
/* The weights cover the range 0/64 - 63/64 of a pixel. Obviously, */
/* values around a half pixel (which means exactly between two grid */
/* lines) gets the worst weight. */
#if 1
static const AF_WarpScore
af_warper_weights[64] =
{
35, 32, 30, 25, 20, 15, 12, 10, 5, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, -1, -2, -5, -8,-10,-10,-20,-20,-30,-30,
-30,-30,-20,-20,-10,-10, -8, -5, -2, -1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 5, 10, 12, 15, 20, 25, 30, 32,
};
#else
static const AF_WarpScore
af_warper_weights[64] =
{
30, 20, 10, 5, 4, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, -1, -2, -2, -5, -5,-10,-10,-15,-20,
-20,-15,-15,-10,-10, -5, -5, -2, -2, -1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 4, 5, 10, 20,
};
#endif
/* Score segments for a given `scale' and `delta' in the range */
/* `xx1' to `xx2', and store the best result in `warper'. If */
/* the new best score is equal to the old one, prefer the */
/* value with a smaller distortion (around `base_distort'). */
static void
af_warper_compute_line_best( AF_Warper warper,
FT_Fixed scale,
FT_Pos delta,
FT_Pos xx1,
FT_Pos xx2,
AF_WarpScore base_distort,
AF_Segment segments,
FT_Int num_segments )
{
FT_Int idx_min, idx_max, idx0;
FT_Int nn;
AF_WarpScore scores[65];
for ( nn = 0; nn < 65; nn++ )
scores[nn] = 0;
idx0 = xx1 - warper->t1;
/* compute minimum and maximum indices */
{
FT_Pos xx1min = warper->x1min;
FT_Pos xx1max = warper->x1max;
FT_Pos w = xx2 - xx1;
if ( xx1min + w < warper->x2min )
xx1min = warper->x2min - w;
xx1max = warper->x1max;
if ( xx1max + w > warper->x2max )
xx1max = warper->x2max - w;
idx_min = xx1min - warper->t1;
idx_max = xx1max - warper->t1;
if ( idx_min < 0 || idx_min > idx_max || idx_max > 64 )
{
FT_TRACE5(( "invalid indices:\n"
" min=%d max=%d, xx1=%ld xx2=%ld,\n"
" x1min=%ld x1max=%ld, x2min=%ld x2max=%ld\n",
idx_min, idx_max, xx1, xx2,
warper->x1min, warper->x1max,
warper->x2min, warper->x2max ));
return;
}
}
for ( nn = 0; nn < num_segments; nn++ )
{
FT_Pos len = segments[nn].max_coord - segments[nn].min_coord;
FT_Pos y0 = FT_MulFix( segments[nn].pos, scale ) + delta;
FT_Pos y = y0 + ( idx_min - idx0 );
FT_Int idx;
/* score the length of the segments for the given range */
for ( idx = idx_min; idx <= idx_max; idx++, y++ )
scores[idx] += af_warper_weights[y & 63] * len;
}
/* find best score */
{
FT_Int idx;
for ( idx = idx_min; idx <= idx_max; idx++ )
{
AF_WarpScore score = scores[idx];
AF_WarpScore distort = base_distort + ( idx - idx0 );
if ( score > warper->best_score ||
( score == warper->best_score &&
distort < warper->best_distort ) )
{
warper->best_score = score;
warper->best_distort = distort;
warper->best_scale = scale;
warper->best_delta = delta + ( idx - idx0 );
}
}
}
}
/* Compute optimal scaling and delta values for a given glyph and */
/* dimension. */
FT_LOCAL_DEF( void )
af_warper_compute( AF_Warper warper,
AF_GlyphHints hints,
AF_Dimension dim,
FT_Fixed *a_scale,
FT_Pos *a_delta )
{
AF_AxisHints axis;
AF_Point points;
FT_Fixed org_scale;
FT_Pos org_delta;
FT_Int nn, num_points, num_segments;
FT_Int X1, X2;
FT_Int w;
AF_WarpScore base_distort;
AF_Segment segments;
/* get original scaling transformation */
if ( dim == AF_DIMENSION_VERT )
{
org_scale = hints->y_scale;
org_delta = hints->y_delta;
}
else
{
org_scale = hints->x_scale;
org_delta = hints->x_delta;
}
warper->best_scale = org_scale;
warper->best_delta = org_delta;
warper->best_score = FT_INT_MIN;
warper->best_distort = 0;
axis = &hints->axis[dim];
segments = axis->segments;
num_segments = axis->num_segments;
points = hints->points;
num_points = hints->num_points;
*a_scale = org_scale;
*a_delta = org_delta;
/* get X1 and X2, minimum and maximum in original coordinates */
if ( num_segments < 1 )
return;
#if 1
X1 = X2 = points[0].fx;
for ( nn = 1; nn < num_points; nn++ )
{
FT_Int X = points[nn].fx;
if ( X < X1 )
X1 = X;
if ( X > X2 )
X2 = X;
}
#else
X1 = X2 = segments[0].pos;
for ( nn = 1; nn < num_segments; nn++ )
{
FT_Int X = segments[nn].pos;
if ( X < X1 )
X1 = X;
if ( X > X2 )
X2 = X;
}
#endif
if ( X1 >= X2 )
return;
warper->x1 = FT_MulFix( X1, org_scale ) + org_delta;
warper->x2 = FT_MulFix( X2, org_scale ) + org_delta;
warper->t1 = AF_WARPER_FLOOR( warper->x1 );
warper->t2 = AF_WARPER_CEIL( warper->x2 );
/* examine a half pixel wide range around the maximum coordinates */
warper->x1min = warper->x1 & ~31;
warper->x1max = warper->x1min + 32;
warper->x2min = warper->x2 & ~31;
warper->x2max = warper->x2min + 32;
if ( warper->x1max > warper->x2 )
warper->x1max = warper->x2;
if ( warper->x2min < warper->x1 )
warper->x2min = warper->x1;
warper->w0 = warper->x2 - warper->x1;
if ( warper->w0 <= 64 )
{
warper->x1max = warper->x1;
warper->x2min = warper->x2;
}
/* examine (at most) a pixel wide range around the natural width */
warper->wmin = warper->x2min - warper->x1max;
warper->wmax = warper->x2max - warper->x1min;
#if 1
/* some heuristics to reduce the number of widths to be examined */
{
int margin = 16;
if ( warper->w0 <= 128 )
{
margin = 8;
if ( warper->w0 <= 96 )
margin = 4;
}
if ( warper->wmin < warper->w0 - margin )
warper->wmin = warper->w0 - margin;
if ( warper->wmax > warper->w0 + margin )
warper->wmax = warper->w0 + margin;
}
if ( warper->wmin < warper->w0 * 3 / 4 )
warper->wmin = warper->w0 * 3 / 4;
if ( warper->wmax > warper->w0 * 5 / 4 )
warper->wmax = warper->w0 * 5 / 4;
#else
/* no scaling, just translation */
warper->wmin = warper->wmax = warper->w0;
#endif
for ( w = warper->wmin; w <= warper->wmax; w++ )
{
FT_Fixed new_scale;
FT_Pos new_delta;
FT_Pos xx1, xx2;
/* compute min and max positions for given width, */
/* assuring that they stay within the coordinate ranges */
xx1 = warper->x1;
xx2 = warper->x2;
if ( w >= warper->w0 )
{
xx1 -= w - warper->w0;
if ( xx1 < warper->x1min )
{
xx2 += warper->x1min - xx1;
xx1 = warper->x1min;
}
}
else
{
xx1 -= w - warper->w0;
if ( xx1 > warper->x1max )
{
xx2 -= xx1 - warper->x1max;
xx1 = warper->x1max;
}
}
if ( xx1 < warper->x1 )
base_distort = warper->x1 - xx1;
else
base_distort = xx1 - warper->x1;
if ( xx2 < warper->x2 )
base_distort += warper->x2 - xx2;
else
base_distort += xx2 - warper->x2;
/* give base distortion a greater weight while scoring */
base_distort *= 10;
new_scale = org_scale + FT_DivFix( w - warper->w0, X2 - X1 );
new_delta = xx1 - FT_MulFix( X1, new_scale );
af_warper_compute_line_best( warper, new_scale, new_delta, xx1, xx2,
base_distort,
segments, num_segments );
}
{
FT_Fixed best_scale = warper->best_scale;
FT_Pos best_delta = warper->best_delta;
hints->xmin_delta = FT_MulFix( X1, best_scale - org_scale )
+ best_delta;
hints->xmax_delta = FT_MulFix( X2, best_scale - org_scale )
+ best_delta;
*a_scale = best_scale;
*a_delta = best_delta;
}
}
#else /* !AF_CONFIG_OPTION_USE_WARPER */
/* ANSI C doesn't like empty source files */
typedef int _af_warp_dummy;
#endif /* !AF_CONFIG_OPTION_USE_WARPER */
/* END */