David Turner | 8ccb455 | 2004-01-16 09:51:00 +0000 | [diff] [blame^] | 1 | #include "afmodule.h" |
| 2 | #include "afhints.h" |
| 3 | #include "afglobal.h" |
| 4 | #include "aflatin.h" |
| 5 | |
| 6 | static FT_Error |
| 7 | af_hinter_load( AF_Hinter hinter, |
| 8 | FT_UInt glyph_index, |
| 9 | FT_Int32 load_flags, |
| 10 | FT_UInt depth ) |
| 11 | { |
| 12 | FT_Face face = hinter->face; |
| 13 | FT_GlyphSlot slot = face->glyph; |
| 14 | FT_Slot_Internal internal = slot->internal; |
| 15 | FT_Fixed x_scale = hinter->globals->x_scale; |
| 16 | FT_Fixed y_scale = hinter->globals->y_scale; |
| 17 | FT_Error error; |
| 18 | AF_Outline outline = hinter->glyph; |
| 19 | AF_Loader gloader = hinter->loader; |
| 20 | |
| 21 | |
| 22 | /* load the glyph */ |
| 23 | error = FT_Load_Glyph( face, glyph_index, load_flags ); |
| 24 | if ( error ) |
| 25 | goto Exit; |
| 26 | |
| 27 | /* Set `hinter->transformed' after loading with FT_LOAD_NO_RECURSE. */ |
| 28 | hinter->transformed = internal->glyph_transformed; |
| 29 | |
| 30 | if ( hinter->transformed ) |
| 31 | { |
| 32 | FT_Matrix imatrix; |
| 33 | |
| 34 | |
| 35 | imatrix = internal->glyph_matrix; |
| 36 | hinter->trans_delta = internal->glyph_delta; |
| 37 | hinter->trans_matrix = imatrix; |
| 38 | |
| 39 | FT_Matrix_Invert( &imatrix ); |
| 40 | FT_Vector_Transform( &hinter->trans_delta, &imatrix ); |
| 41 | } |
| 42 | |
| 43 | /* set linear horizontal metrics */ |
| 44 | slot->linearHoriAdvance = slot->metrics.horiAdvance; |
| 45 | slot->linearVertAdvance = slot->metrics.vertAdvance; |
| 46 | |
| 47 | switch ( slot->format ) |
| 48 | { |
| 49 | case FT_GLYPH_FORMAT_OUTLINE: |
| 50 | |
| 51 | /* translate glyph outline if we need to */ |
| 52 | if ( hinter->transformed ) |
| 53 | { |
| 54 | FT_UInt n = slot->outline.n_points; |
| 55 | FT_Vector* point = slot->outline.points; |
| 56 | |
| 57 | |
| 58 | for ( ; n > 0; point++, n-- ) |
| 59 | { |
| 60 | point->x += hinter->trans_delta.x; |
| 61 | point->y += hinter->trans_delta.y; |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | /* copy the outline points in the loader's current */ |
| 66 | /* extra points which is used to keep original glyph coordinates */ |
| 67 | error = af_loader_check_points( gloader, slot->outline.n_points + 2, |
| 68 | slot->outline.n_contours ); |
| 69 | if ( error ) |
| 70 | goto Exit; |
| 71 | |
| 72 | FT_ARRAY_COPY( gloader->current.extra_points, slot->outline.points, |
| 73 | slot->outline.n_points ); |
| 74 | |
| 75 | FT_ARRAY_COPY( gloader->current.outline.contours, slot->outline.contours, |
| 76 | slot->outline.n_contours ); |
| 77 | |
| 78 | FT_ARRAY_COPY( gloader->current.outline.tags, slot->outline.tags, |
| 79 | slot->outline.n_points ); |
| 80 | |
| 81 | gloader->current.outline.n_points = slot->outline.n_points; |
| 82 | gloader->current.outline.n_contours = slot->outline.n_contours; |
| 83 | |
| 84 | /* compute original phantom points */ |
| 85 | hinter->pp1.x = 0; |
| 86 | hinter->pp1.y = 0; |
| 87 | hinter->pp2.x = FT_MulFix( slot->metrics.horiAdvance, x_scale ); |
| 88 | hinter->pp2.y = 0; |
| 89 | |
| 90 | /* be sure to check for spacing glyphs */ |
| 91 | if ( slot->outline.n_points == 0 ) |
| 92 | goto Hint_Metrics; |
| 93 | |
| 94 | /* now load the slot image into the auto-outline and run the */ |
| 95 | /* automatic hinting process */ |
| 96 | error = af_outline_load( outline, x_scale, y_scale, face ); |
| 97 | if ( error ) |
| 98 | goto Exit; |
| 99 | |
| 100 | /* perform feature detection */ |
| 101 | af_outline_detect_features( outline ); |
| 102 | |
| 103 | if ( hinter->do_vert_hints ) |
| 104 | { |
| 105 | af_outline_compute_blue_edges( outline, hinter->globals ); |
| 106 | af_outline_scale_blue_edges( outline, hinter->globals ); |
| 107 | } |
| 108 | |
| 109 | /* perform alignment control */ |
| 110 | af_hinter_hint_edges( hinter ); |
| 111 | af_hinter_align( hinter ); |
| 112 | |
| 113 | /* now save the current outline into the loader's current table */ |
| 114 | af_outline_save( outline, gloader ); |
| 115 | |
| 116 | /* we now need to hint the metrics according to the change in */ |
| 117 | /* width/positioning that occured during the hinting process */ |
| 118 | { |
| 119 | FT_Pos old_advance, old_rsb, old_lsb, new_lsb; |
| 120 | AF_Edge edge1 = outline->vert_edges; /* leftmost edge */ |
| 121 | AF_Edge edge2 = edge1 + |
| 122 | outline->num_vedges - 1; /* rightmost edge */ |
| 123 | |
| 124 | |
| 125 | old_advance = hinter->pp2.x; |
| 126 | old_rsb = old_advance - edge2->opos; |
| 127 | old_lsb = edge1->opos; |
| 128 | new_lsb = edge1->pos; |
| 129 | |
| 130 | hinter->pp1.x = FT_PIX_ROUND( new_lsb - old_lsb ); |
| 131 | hinter->pp2.x = FT_PIX_ROUND( edge2->pos + old_rsb ); |
| 132 | |
| 133 | #if 0 |
| 134 | /* try to fix certain bad advance computations */ |
| 135 | if ( hinter->pp2.x + hinter->pp1.x == edge2->pos && old_rsb > 4 ) |
| 136 | hinter->pp2.x += 64; |
| 137 | #endif |
| 138 | } |
| 139 | |
| 140 | /* good, we simply add the glyph to our loader's base */ |
| 141 | af_loader_add( gloader ); |
| 142 | break; |
| 143 | |
| 144 | case FT_GLYPH_FORMAT_COMPOSITE: |
| 145 | { |
| 146 | FT_UInt nn, num_subglyphs = slot->num_subglyphs; |
| 147 | FT_UInt num_base_subgs, start_point; |
| 148 | FT_SubGlyph subglyph; |
| 149 | |
| 150 | |
| 151 | start_point = gloader->base.outline.n_points; |
| 152 | |
| 153 | /* first of all, copy the subglyph descriptors in the glyph loader */ |
| 154 | error = af_loader_check_subglyphs( gloader, num_subglyphs ); |
| 155 | if ( error ) |
| 156 | goto Exit; |
| 157 | |
| 158 | FT_ARRAY_COPY( gloader->current.subglyphs, slot->subglyphs, |
| 159 | num_subglyphs ); |
| 160 | |
| 161 | gloader->current.num_subglyphs = num_subglyphs; |
| 162 | num_base_subgs = gloader->base.num_subglyphs; |
| 163 | |
| 164 | /* now, read each subglyph independently */ |
| 165 | for ( nn = 0; nn < num_subglyphs; nn++ ) |
| 166 | { |
| 167 | FT_Vector pp1, pp2; |
| 168 | FT_Pos x, y; |
| 169 | FT_UInt num_points, num_new_points, num_base_points; |
| 170 | |
| 171 | |
| 172 | /* gloader.current.subglyphs can change during glyph loading due */ |
| 173 | /* to re-allocation -- we must recompute the current subglyph on */ |
| 174 | /* each iteration */ |
| 175 | subglyph = gloader->base.subglyphs + num_base_subgs + nn; |
| 176 | |
| 177 | pp1 = hinter->pp1; |
| 178 | pp2 = hinter->pp2; |
| 179 | |
| 180 | num_base_points = gloader->base.outline.n_points; |
| 181 | |
| 182 | error = af_hinter_load( hinter, subglyph->index, |
| 183 | load_flags, depth + 1 ); |
| 184 | if ( error ) |
| 185 | goto Exit; |
| 186 | |
| 187 | /* recompute subglyph pointer */ |
| 188 | subglyph = gloader->base.subglyphs + num_base_subgs + nn; |
| 189 | |
| 190 | if ( subglyph->flags & FT_SUBGLYPH_FLAG_USE_MY_METRICS ) |
| 191 | { |
| 192 | pp1 = hinter->pp1; |
| 193 | pp2 = hinter->pp2; |
| 194 | } |
| 195 | else |
| 196 | { |
| 197 | hinter->pp1 = pp1; |
| 198 | hinter->pp2 = pp2; |
| 199 | } |
| 200 | |
| 201 | num_points = gloader->base.outline.n_points; |
| 202 | num_new_points = num_points - num_base_points; |
| 203 | |
| 204 | /* now perform the transform required for this subglyph */ |
| 205 | |
| 206 | if ( subglyph->flags & ( FT_SUBGLYPH_FLAG_SCALE | |
| 207 | FT_SUBGLYPH_FLAG_XY_SCALE | |
| 208 | FT_SUBGLYPH_FLAG_2X2 ) ) |
| 209 | { |
| 210 | FT_Vector* cur = gloader->base.outline.points + |
| 211 | num_base_points; |
| 212 | FT_Vector* org = gloader->base.extra_points + |
| 213 | num_base_points; |
| 214 | FT_Vector* limit = cur + num_new_points; |
| 215 | |
| 216 | |
| 217 | for ( ; cur < limit; cur++, org++ ) |
| 218 | { |
| 219 | FT_Vector_Transform( cur, &subglyph->transform ); |
| 220 | FT_Vector_Transform( org, &subglyph->transform ); |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | /* apply offset */ |
| 225 | |
| 226 | if ( !( subglyph->flags & FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES ) ) |
| 227 | { |
| 228 | FT_Int k = subglyph->arg1; |
| 229 | FT_UInt l = subglyph->arg2; |
| 230 | FT_Vector* p1; |
| 231 | FT_Vector* p2; |
| 232 | |
| 233 | |
| 234 | if ( start_point + k >= num_base_points || |
| 235 | l >= (FT_UInt)num_new_points ) |
| 236 | { |
| 237 | error = AF_Err_Invalid_Composite; |
| 238 | goto Exit; |
| 239 | } |
| 240 | |
| 241 | l += num_base_points; |
| 242 | |
| 243 | /* for now, only use the current point coordinates; */ |
| 244 | /* we may consider another approach in the near future */ |
| 245 | p1 = gloader->base.outline.points + start_point + k; |
| 246 | p2 = gloader->base.outline.points + start_point + l; |
| 247 | |
| 248 | x = p1->x - p2->x; |
| 249 | y = p1->y - p2->y; |
| 250 | } |
| 251 | else |
| 252 | { |
| 253 | x = FT_MulFix( subglyph->arg1, x_scale ); |
| 254 | y = FT_MulFix( subglyph->arg2, y_scale ); |
| 255 | |
| 256 | x = FT_PIX_ROUND(x); |
| 257 | y = FT_PIX_ROUND(y); |
| 258 | } |
| 259 | |
| 260 | { |
| 261 | FT_Outline dummy = gloader->base.outline; |
| 262 | |
| 263 | |
| 264 | dummy.points += num_base_points; |
| 265 | dummy.n_points = (short)num_new_points; |
| 266 | |
| 267 | FT_Outline_Translate( &dummy, x, y ); |
| 268 | } |
| 269 | } |
| 270 | } |
| 271 | break; |
| 272 | |
| 273 | default: |
| 274 | /* we don't support other formats (yet?) */ |
| 275 | error = AF_Err_Unimplemented_Feature; |
| 276 | } |
| 277 | |
| 278 | Hint_Metrics: |
| 279 | if ( depth == 0 ) |
| 280 | { |
| 281 | FT_BBox bbox; |
| 282 | |
| 283 | |
| 284 | /* transform the hinted outline if needed */ |
| 285 | if ( hinter->transformed ) |
| 286 | FT_Outline_Transform( &gloader->base.outline, &hinter->trans_matrix ); |
| 287 | |
| 288 | /* we must translate our final outline by -pp1.x and compute */ |
| 289 | /* the new metrics */ |
| 290 | if ( hinter->pp1.x ) |
| 291 | FT_Outline_Translate( &gloader->base.outline, -hinter->pp1.x, 0 ); |
| 292 | |
| 293 | FT_Outline_Get_CBox( &gloader->base.outline, &bbox ); |
| 294 | bbox.xMin = FT_PIX_FLOOR( bbox.xMin ); |
| 295 | bbox.yMin = FT_PIX_FLOOR( bbox.yMin ); |
| 296 | bbox.xMax = FT_PIX_CEIL( bbox.xMax ); |
| 297 | bbox.yMax = FT_PIX_CEIL( bbox.yMax ); |
| 298 | |
| 299 | slot->metrics.width = bbox.xMax - bbox.xMin; |
| 300 | slot->metrics.height = bbox.yMax - bbox.yMin; |
| 301 | slot->metrics.horiBearingX = bbox.xMin; |
| 302 | slot->metrics.horiBearingY = bbox.yMax; |
| 303 | |
| 304 | /* for mono-width fonts (like Andale, Courier, etc.) we need */ |
| 305 | /* to keep the original rounded advance width */ |
| 306 | if ( !FT_IS_FIXED_WIDTH( slot->face ) ) |
| 307 | slot->metrics.horiAdvance = hinter->pp2.x - hinter->pp1.x; |
| 308 | else |
| 309 | slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance, |
| 310 | x_scale ); |
| 311 | |
| 312 | slot->metrics.horiAdvance = FT_PIX_ROUND( slot->metrics.horiAdvance ); |
| 313 | |
| 314 | /* now copy outline into glyph slot */ |
| 315 | af_loader_rewind( slot->internal->loader ); |
| 316 | error = af_loader_copy_points( slot->internal->loader, gloader ); |
| 317 | if ( error ) |
| 318 | goto Exit; |
| 319 | |
| 320 | slot->outline = slot->internal->loader->base.outline; |
| 321 | slot->format = FT_GLYPH_FORMAT_OUTLINE; |
| 322 | } |
| 323 | |
| 324 | #ifdef DEBUG_HINTER |
| 325 | af_debug_hinter = hinter; |
| 326 | #endif |
| 327 | |
| 328 | Exit: |
| 329 | return error; |
| 330 | } |
| 331 | |
| 332 | |
| 333 | /* load and hint a given glyph */ |
| 334 | FT_LOCAL_DEF( FT_Error ) |
| 335 | af_hinter_load_glyph( AF_Hinter hinter, |
| 336 | FT_GlyphSlot slot, |
| 337 | FT_Size size, |
| 338 | FT_UInt glyph_index, |
| 339 | FT_Int32 load_flags ) |
| 340 | { |
| 341 | FT_Face face = slot->face; |
| 342 | FT_Error error; |
| 343 | FT_Fixed x_scale = size->metrics.x_scale; |
| 344 | FT_Fixed y_scale = size->metrics.y_scale; |
| 345 | AF_Face_Globals face_globals = FACE_GLOBALS( face ); |
| 346 | FT_Render_Mode hint_mode = FT_LOAD_TARGET_MODE( load_flags ); |
| 347 | |
| 348 | |
| 349 | /* first of all, we need to check that we're using the correct face and */ |
| 350 | /* global hints to load the glyph */ |
| 351 | if ( hinter->face != face || hinter->globals != face_globals ) |
| 352 | { |
| 353 | hinter->face = face; |
| 354 | if ( !face_globals ) |
| 355 | { |
| 356 | error = af_hinter_new_face_globals( hinter, face, 0 ); |
| 357 | if ( error ) |
| 358 | goto Exit; |
| 359 | |
| 360 | } |
| 361 | hinter->globals = FACE_GLOBALS( face ); |
| 362 | face_globals = FACE_GLOBALS( face ); |
| 363 | |
| 364 | } |
| 365 | |
| 366 | #ifdef FT_CONFIG_CHESTER_BLUE_SCALE |
| 367 | |
| 368 | /* try to optimize the y_scale so that the top of non-capital letters |
| 369 | * is aligned on a pixel boundary whenever possible |
| 370 | */ |
| 371 | { |
| 372 | AF_Globals design = &face_globals->design; |
| 373 | FT_Pos shoot = design->blue_shoots[AF_BLUE_SMALL_TOP]; |
| 374 | |
| 375 | |
| 376 | /* the value of 'shoot' will be -1000 if the font doesn't have */ |
| 377 | /* small latin letters; we simply check the sign here... */ |
| 378 | if ( shoot > 0 ) |
| 379 | { |
| 380 | FT_Pos scaled = FT_MulFix( shoot, y_scale ); |
| 381 | FT_Pos fitted = FT_PIX_ROUND( scaled ); |
| 382 | |
| 383 | |
| 384 | if ( scaled != fitted ) |
| 385 | { |
| 386 | /* adjust y_scale |
| 387 | */ |
| 388 | y_scale = FT_MulDiv( y_scale, fitted, scaled ); |
| 389 | |
| 390 | /* adust x_scale |
| 391 | */ |
| 392 | if ( fitted < scaled ) |
| 393 | x_scale -= x_scale / 50; /* x_scale*0.98 with integers */ |
| 394 | } |
| 395 | } |
| 396 | } |
| 397 | |
| 398 | #endif /* FT_CONFIG_CHESTER_BLUE_SCALE */ |
| 399 | |
| 400 | /* now, we must check the current character pixel size to see if we */ |
| 401 | /* need to rescale the global metrics */ |
| 402 | if ( face_globals->x_scale != x_scale || |
| 403 | face_globals->y_scale != y_scale ) |
| 404 | af_hinter_scale_globals( hinter, x_scale, y_scale ); |
| 405 | |
| 406 | af_loader_rewind( hinter->loader ); |
| 407 | |
| 408 | /* reset hinting flags according to load flags and current render target */ |
| 409 | hinter->do_horz_hints = FT_BOOL( !(load_flags & FT_LOAD_NO_AUTOHINT) ); |
| 410 | hinter->do_vert_hints = FT_BOOL( !(load_flags & FT_LOAD_NO_AUTOHINT) ); |
| 411 | |
| 412 | #ifdef DEBUG_HINTER |
| 413 | hinter->do_horz_hints = !af_debug_disable_vert; /* not a bug, the meaning */ |
| 414 | hinter->do_vert_hints = !af_debug_disable_horz; /* of h/v is inverted! */ |
| 415 | #endif |
| 416 | |
| 417 | /* we snap the width of vertical stems for the monochrome and */ |
| 418 | /* horizontal LCD rendering targets only. Corresponds to X snapping. */ |
| 419 | hinter->do_horz_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO || |
| 420 | hint_mode == FT_RENDER_MODE_LCD ); |
| 421 | |
| 422 | /* we snap the width of horizontal stems for the monochrome and */ |
| 423 | /* vertical LCD rendering targets only. Corresponds to Y snapping. */ |
| 424 | hinter->do_vert_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO || |
| 425 | hint_mode == FT_RENDER_MODE_LCD_V ); |
| 426 | |
| 427 | hinter->do_stem_adjust = FT_BOOL( hint_mode != FT_RENDER_MODE_LIGHT ); |
| 428 | |
| 429 | load_flags |= FT_LOAD_NO_SCALE |
| 430 | | FT_LOAD_IGNORE_TRANSFORM; |
| 431 | load_flags &= ~FT_LOAD_RENDER; |
| 432 | |
| 433 | error = af_hinter_load( hinter, glyph_index, load_flags, 0 ); |
| 434 | |
| 435 | Exit: |
| 436 | return error; |
| 437 | } |