misc/libfreetype/src/autofit/afloader.c
changeset 5172 88f2e05288ba
equal deleted inserted replaced
5171:f9283dc4860d 5172:88f2e05288ba
       
     1 /***************************************************************************/
       
     2 /*                                                                         */
       
     3 /*  afloader.c                                                             */
       
     4 /*                                                                         */
       
     5 /*    Auto-fitter glyph loading routines (body).                           */
       
     6 /*                                                                         */
       
     7 /*  Copyright 2003-2009, 2011 by                                           */
       
     8 /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
       
     9 /*                                                                         */
       
    10 /*  This file is part of the FreeType project, and may only be used,       */
       
    11 /*  modified, and distributed under the terms of the FreeType project      */
       
    12 /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
       
    13 /*  this file you indicate that you have read the license and              */
       
    14 /*  understand and accept it fully.                                        */
       
    15 /*                                                                         */
       
    16 /***************************************************************************/
       
    17 
       
    18 
       
    19 #include "afloader.h"
       
    20 #include "afhints.h"
       
    21 #include "afglobal.h"
       
    22 #include "aferrors.h"
       
    23 
       
    24 
       
    25   /* Initialize glyph loader. */
       
    26 
       
    27   FT_LOCAL_DEF( FT_Error )
       
    28   af_loader_init( AF_Loader  loader,
       
    29                   FT_Memory  memory )
       
    30   {
       
    31     FT_ZERO( loader );
       
    32 
       
    33     af_glyph_hints_init( &loader->hints, memory );
       
    34 #ifdef FT_DEBUG_AUTOFIT
       
    35     _af_debug_hints = &loader->hints;
       
    36 #endif
       
    37     return FT_GlyphLoader_New( memory, &loader->gloader );
       
    38   }
       
    39 
       
    40 
       
    41   /* Reset glyph loader and compute globals if necessary. */
       
    42 
       
    43   FT_LOCAL_DEF( FT_Error )
       
    44   af_loader_reset( AF_Loader  loader,
       
    45                    FT_Face    face )
       
    46   {
       
    47     FT_Error  error = AF_Err_Ok;
       
    48 
       
    49 
       
    50     loader->face    = face;
       
    51     loader->globals = (AF_FaceGlobals)face->autohint.data;
       
    52 
       
    53     FT_GlyphLoader_Rewind( loader->gloader );
       
    54 
       
    55     if ( loader->globals == NULL )
       
    56     {
       
    57       error = af_face_globals_new( face, &loader->globals );
       
    58       if ( !error )
       
    59       {
       
    60         face->autohint.data =
       
    61           (FT_Pointer)loader->globals;
       
    62         face->autohint.finalizer =
       
    63           (FT_Generic_Finalizer)af_face_globals_free;
       
    64       }
       
    65     }
       
    66 
       
    67     return error;
       
    68   }
       
    69 
       
    70 
       
    71   /* Finalize glyph loader. */
       
    72 
       
    73   FT_LOCAL_DEF( void )
       
    74   af_loader_done( AF_Loader  loader )
       
    75   {
       
    76     af_glyph_hints_done( &loader->hints );
       
    77 
       
    78     loader->face    = NULL;
       
    79     loader->globals = NULL;
       
    80 
       
    81 #ifdef FT_DEBUG_AUTOFIT
       
    82     _af_debug_hints = NULL;
       
    83 #endif
       
    84     FT_GlyphLoader_Done( loader->gloader );
       
    85     loader->gloader = NULL;
       
    86   }
       
    87 
       
    88 
       
    89   /* Load a single glyph component.  This routine calls itself */
       
    90   /* recursively, if necessary, and does the main work of      */
       
    91   /* `af_loader_load_glyph.'                                   */
       
    92 
       
    93   static FT_Error
       
    94   af_loader_load_g( AF_Loader  loader,
       
    95                     AF_Scaler  scaler,
       
    96                     FT_UInt    glyph_index,
       
    97                     FT_Int32   load_flags,
       
    98                     FT_UInt    depth )
       
    99   {
       
   100     FT_Error          error;
       
   101     FT_Face           face     = loader->face;
       
   102     FT_GlyphLoader    gloader  = loader->gloader;
       
   103     AF_ScriptMetrics  metrics  = loader->metrics;
       
   104     AF_GlyphHints     hints    = &loader->hints;
       
   105     FT_GlyphSlot      slot     = face->glyph;
       
   106     FT_Slot_Internal  internal = slot->internal;
       
   107 
       
   108 
       
   109     error = FT_Load_Glyph( face, glyph_index, load_flags );
       
   110     if ( error )
       
   111       goto Exit;
       
   112 
       
   113     loader->transformed = internal->glyph_transformed;
       
   114     if ( loader->transformed )
       
   115     {
       
   116       FT_Matrix  inverse;
       
   117 
       
   118 
       
   119       loader->trans_matrix = internal->glyph_matrix;
       
   120       loader->trans_delta  = internal->glyph_delta;
       
   121 
       
   122       inverse = loader->trans_matrix;
       
   123       FT_Matrix_Invert( &inverse );
       
   124       FT_Vector_Transform( &loader->trans_delta, &inverse );
       
   125     }
       
   126 
       
   127     /* set linear metrics */
       
   128     slot->linearHoriAdvance = slot->metrics.horiAdvance;
       
   129     slot->linearVertAdvance = slot->metrics.vertAdvance;
       
   130 
       
   131     switch ( slot->format )
       
   132     {
       
   133     case FT_GLYPH_FORMAT_OUTLINE:
       
   134       /* translate the loaded glyph when an internal transform is needed */
       
   135       if ( loader->transformed )
       
   136         FT_Outline_Translate( &slot->outline,
       
   137                               loader->trans_delta.x,
       
   138                               loader->trans_delta.y );
       
   139 
       
   140       /* copy the outline points in the loader's current               */
       
   141       /* extra points which is used to keep original glyph coordinates */
       
   142       error = FT_GLYPHLOADER_CHECK_POINTS( gloader,
       
   143                                            slot->outline.n_points + 4,
       
   144                                            slot->outline.n_contours );
       
   145       if ( error )
       
   146         goto Exit;
       
   147 
       
   148       FT_ARRAY_COPY( gloader->current.outline.points,
       
   149                      slot->outline.points,
       
   150                      slot->outline.n_points );
       
   151 
       
   152       FT_ARRAY_COPY( gloader->current.outline.contours,
       
   153                      slot->outline.contours,
       
   154                      slot->outline.n_contours );
       
   155 
       
   156       FT_ARRAY_COPY( gloader->current.outline.tags,
       
   157                      slot->outline.tags,
       
   158                      slot->outline.n_points );
       
   159 
       
   160       gloader->current.outline.n_points   = slot->outline.n_points;
       
   161       gloader->current.outline.n_contours = slot->outline.n_contours;
       
   162 
       
   163       /* compute original horizontal phantom points (and ignore */
       
   164       /* vertical ones)                                         */
       
   165       loader->pp1.x = hints->x_delta;
       
   166       loader->pp1.y = hints->y_delta;
       
   167       loader->pp2.x = FT_MulFix( slot->metrics.horiAdvance,
       
   168                                  hints->x_scale ) + hints->x_delta;
       
   169       loader->pp2.y = hints->y_delta;
       
   170 
       
   171       /* be sure to check for spacing glyphs */
       
   172       if ( slot->outline.n_points == 0 )
       
   173         goto Hint_Metrics;
       
   174 
       
   175       /* now load the slot image into the auto-outline and run the */
       
   176       /* automatic hinting process                                 */
       
   177       if ( metrics->clazz->script_hints_apply )
       
   178         metrics->clazz->script_hints_apply( hints,
       
   179                                             &gloader->current.outline,
       
   180                                             metrics );
       
   181 
       
   182       /* we now need to adjust the metrics according to the change in */
       
   183       /* width/positioning that occurred during the hinting process   */
       
   184       if ( scaler->render_mode != FT_RENDER_MODE_LIGHT )
       
   185       {
       
   186         FT_Pos        old_rsb, old_lsb, new_lsb;
       
   187         FT_Pos        pp1x_uh, pp2x_uh;
       
   188         AF_AxisHints  axis  = &hints->axis[AF_DIMENSION_HORZ];
       
   189         AF_Edge       edge1 = axis->edges;         /* leftmost edge  */
       
   190         AF_Edge       edge2 = edge1 +
       
   191                               axis->num_edges - 1; /* rightmost edge */
       
   192 
       
   193 
       
   194         if ( axis->num_edges > 1 && AF_HINTS_DO_ADVANCE( hints ) )
       
   195         {
       
   196           old_rsb = loader->pp2.x - edge2->opos;
       
   197           old_lsb = edge1->opos;
       
   198           new_lsb = edge1->pos;
       
   199 
       
   200           /* remember unhinted values to later account */
       
   201           /* for rounding errors                       */
       
   202 
       
   203           pp1x_uh = new_lsb    - old_lsb;
       
   204           pp2x_uh = edge2->pos + old_rsb;
       
   205 
       
   206           /* prefer too much space over too little space */
       
   207           /* for very small sizes                        */
       
   208 
       
   209           if ( old_lsb < 24 )
       
   210             pp1x_uh -= 8;
       
   211 
       
   212           if ( old_rsb < 24 )
       
   213             pp2x_uh += 8;
       
   214 
       
   215           loader->pp1.x = FT_PIX_ROUND( pp1x_uh );
       
   216           loader->pp2.x = FT_PIX_ROUND( pp2x_uh );
       
   217 
       
   218           if ( loader->pp1.x >= new_lsb && old_lsb > 0 )
       
   219             loader->pp1.x -= 64;
       
   220 
       
   221           if ( loader->pp2.x <= edge2->pos && old_rsb > 0 )
       
   222             loader->pp2.x += 64;
       
   223 
       
   224           slot->lsb_delta = loader->pp1.x - pp1x_uh;
       
   225           slot->rsb_delta = loader->pp2.x - pp2x_uh;
       
   226         }
       
   227         else
       
   228         {
       
   229           FT_Pos  pp1x = loader->pp1.x;
       
   230           FT_Pos  pp2x = loader->pp2.x;
       
   231 
       
   232 
       
   233           loader->pp1.x = FT_PIX_ROUND( pp1x );
       
   234           loader->pp2.x = FT_PIX_ROUND( pp2x );
       
   235 
       
   236           slot->lsb_delta = loader->pp1.x - pp1x;
       
   237           slot->rsb_delta = loader->pp2.x - pp2x;
       
   238         }
       
   239       }
       
   240       else
       
   241       {
       
   242         FT_Pos  pp1x = loader->pp1.x;
       
   243         FT_Pos  pp2x = loader->pp2.x;
       
   244 
       
   245 
       
   246         loader->pp1.x = FT_PIX_ROUND( pp1x + hints->xmin_delta );
       
   247         loader->pp2.x = FT_PIX_ROUND( pp2x + hints->xmax_delta );
       
   248 
       
   249         slot->lsb_delta = loader->pp1.x - pp1x;
       
   250         slot->rsb_delta = loader->pp2.x - pp2x;
       
   251       }
       
   252 
       
   253       /* good, we simply add the glyph to our loader's base */
       
   254       FT_GlyphLoader_Add( gloader );
       
   255       break;
       
   256 
       
   257     case FT_GLYPH_FORMAT_COMPOSITE:
       
   258       {
       
   259         FT_UInt      nn, num_subglyphs = slot->num_subglyphs;
       
   260         FT_UInt      num_base_subgs, start_point;
       
   261         FT_SubGlyph  subglyph;
       
   262 
       
   263 
       
   264         start_point = gloader->base.outline.n_points;
       
   265 
       
   266         /* first of all, copy the subglyph descriptors in the glyph loader */
       
   267         error = FT_GlyphLoader_CheckSubGlyphs( gloader, num_subglyphs );
       
   268         if ( error )
       
   269           goto Exit;
       
   270 
       
   271         FT_ARRAY_COPY( gloader->current.subglyphs,
       
   272                        slot->subglyphs,
       
   273                        num_subglyphs );
       
   274 
       
   275         gloader->current.num_subglyphs = num_subglyphs;
       
   276         num_base_subgs                 = gloader->base.num_subglyphs;
       
   277 
       
   278         /* now read each subglyph independently */
       
   279         for ( nn = 0; nn < num_subglyphs; nn++ )
       
   280         {
       
   281           FT_Vector  pp1, pp2;
       
   282           FT_Pos     x, y;
       
   283           FT_UInt    num_points, num_new_points, num_base_points;
       
   284 
       
   285 
       
   286           /* gloader.current.subglyphs can change during glyph loading due */
       
   287           /* to re-allocation -- we must recompute the current subglyph on */
       
   288           /* each iteration                                                */
       
   289           subglyph = gloader->base.subglyphs + num_base_subgs + nn;
       
   290 
       
   291           pp1 = loader->pp1;
       
   292           pp2 = loader->pp2;
       
   293 
       
   294           num_base_points = gloader->base.outline.n_points;
       
   295 
       
   296           error = af_loader_load_g( loader, scaler, subglyph->index,
       
   297                                     load_flags, depth + 1 );
       
   298           if ( error )
       
   299             goto Exit;
       
   300 
       
   301           /* recompute subglyph pointer */
       
   302           subglyph = gloader->base.subglyphs + num_base_subgs + nn;
       
   303 
       
   304           if ( subglyph->flags & FT_SUBGLYPH_FLAG_USE_MY_METRICS )
       
   305           {
       
   306             pp1 = loader->pp1;
       
   307             pp2 = loader->pp2;
       
   308           }
       
   309           else
       
   310           {
       
   311             loader->pp1 = pp1;
       
   312             loader->pp2 = pp2;
       
   313           }
       
   314 
       
   315           num_points     = gloader->base.outline.n_points;
       
   316           num_new_points = num_points - num_base_points;
       
   317 
       
   318           /* now perform the transformation required for this subglyph */
       
   319 
       
   320           if ( subglyph->flags & ( FT_SUBGLYPH_FLAG_SCALE    |
       
   321                                    FT_SUBGLYPH_FLAG_XY_SCALE |
       
   322                                    FT_SUBGLYPH_FLAG_2X2      ) )
       
   323           {
       
   324             FT_Vector*  cur   = gloader->base.outline.points +
       
   325                                 num_base_points;
       
   326             FT_Vector*  limit = cur + num_new_points;
       
   327 
       
   328 
       
   329             for ( ; cur < limit; cur++ )
       
   330               FT_Vector_Transform( cur, &subglyph->transform );
       
   331           }
       
   332 
       
   333           /* apply offset */
       
   334 
       
   335           if ( !( subglyph->flags & FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES ) )
       
   336           {
       
   337             FT_Int      k = subglyph->arg1;
       
   338             FT_UInt     l = subglyph->arg2;
       
   339             FT_Vector*  p1;
       
   340             FT_Vector*  p2;
       
   341 
       
   342 
       
   343             if ( start_point + k >= num_base_points         ||
       
   344                                l >= (FT_UInt)num_new_points )
       
   345             {
       
   346               error = AF_Err_Invalid_Composite;
       
   347               goto Exit;
       
   348             }
       
   349 
       
   350             l += num_base_points;
       
   351 
       
   352             /* for now, only use the current point coordinates;    */
       
   353             /* we may consider another approach in the near future */
       
   354             p1 = gloader->base.outline.points + start_point + k;
       
   355             p2 = gloader->base.outline.points + start_point + l;
       
   356 
       
   357             x = p1->x - p2->x;
       
   358             y = p1->y - p2->y;
       
   359           }
       
   360           else
       
   361           {
       
   362             x = FT_MulFix( subglyph->arg1, hints->x_scale ) + hints->x_delta;
       
   363             y = FT_MulFix( subglyph->arg2, hints->y_scale ) + hints->y_delta;
       
   364 
       
   365             x = FT_PIX_ROUND( x );
       
   366             y = FT_PIX_ROUND( y );
       
   367           }
       
   368 
       
   369           {
       
   370             FT_Outline  dummy = gloader->base.outline;
       
   371 
       
   372 
       
   373             dummy.points  += num_base_points;
       
   374             dummy.n_points = (short)num_new_points;
       
   375 
       
   376             FT_Outline_Translate( &dummy, x, y );
       
   377           }
       
   378         }
       
   379       }
       
   380       break;
       
   381 
       
   382     default:
       
   383       /* we don't support other formats (yet?) */
       
   384       error = AF_Err_Unimplemented_Feature;
       
   385     }
       
   386 
       
   387   Hint_Metrics:
       
   388     if ( depth == 0 )
       
   389     {
       
   390       FT_BBox    bbox;
       
   391       FT_Vector  vvector;
       
   392 
       
   393 
       
   394       vvector.x = slot->metrics.vertBearingX - slot->metrics.horiBearingX;
       
   395       vvector.y = slot->metrics.vertBearingY - slot->metrics.horiBearingY;
       
   396       vvector.x = FT_MulFix( vvector.x, metrics->scaler.x_scale );
       
   397       vvector.y = FT_MulFix( vvector.y, metrics->scaler.y_scale );
       
   398 
       
   399       /* transform the hinted outline if needed */
       
   400       if ( loader->transformed )
       
   401       {
       
   402         FT_Outline_Transform( &gloader->base.outline, &loader->trans_matrix );
       
   403         FT_Vector_Transform( &vvector, &loader->trans_matrix );
       
   404       }
       
   405 #if 1
       
   406       /* we must translate our final outline by -pp1.x and compute */
       
   407       /* the new metrics                                           */
       
   408       if ( loader->pp1.x )
       
   409         FT_Outline_Translate( &gloader->base.outline, -loader->pp1.x, 0 );
       
   410 #endif
       
   411       FT_Outline_Get_CBox( &gloader->base.outline, &bbox );
       
   412 
       
   413       bbox.xMin = FT_PIX_FLOOR( bbox.xMin );
       
   414       bbox.yMin = FT_PIX_FLOOR( bbox.yMin );
       
   415       bbox.xMax = FT_PIX_CEIL(  bbox.xMax );
       
   416       bbox.yMax = FT_PIX_CEIL(  bbox.yMax );
       
   417 
       
   418       slot->metrics.width        = bbox.xMax - bbox.xMin;
       
   419       slot->metrics.height       = bbox.yMax - bbox.yMin;
       
   420       slot->metrics.horiBearingX = bbox.xMin;
       
   421       slot->metrics.horiBearingY = bbox.yMax;
       
   422 
       
   423       slot->metrics.vertBearingX = FT_PIX_FLOOR( bbox.xMin + vvector.x );
       
   424       slot->metrics.vertBearingY = FT_PIX_FLOOR( bbox.yMax + vvector.y );
       
   425 
       
   426       /* for mono-width fonts (like Andale, Courier, etc.) we need */
       
   427       /* to keep the original rounded advance width; ditto for     */
       
   428       /* digits if all have the same advance width                 */
       
   429 #if 0
       
   430       if ( !FT_IS_FIXED_WIDTH( slot->face ) )
       
   431         slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x;
       
   432       else
       
   433         slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance,
       
   434                                                x_scale );
       
   435 #else
       
   436       if ( FT_IS_FIXED_WIDTH( slot->face )                              ||
       
   437            ( af_face_globals_is_digit( loader->globals, glyph_index ) &&
       
   438              metrics->digits_have_same_width                          ) )
       
   439       {
       
   440         slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance,
       
   441                                                metrics->scaler.x_scale );
       
   442 
       
   443         /* Set delta values to 0.  Otherwise code that uses them is */
       
   444         /* going to ruin the fixed advance width.                   */
       
   445         slot->lsb_delta = 0;
       
   446         slot->rsb_delta = 0;
       
   447       }
       
   448       else
       
   449       {
       
   450         /* non-spacing glyphs must stay as-is */
       
   451         if ( slot->metrics.horiAdvance )
       
   452           slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x;
       
   453       }
       
   454 #endif
       
   455 
       
   456       slot->metrics.vertAdvance = FT_MulFix( slot->metrics.vertAdvance,
       
   457                                              metrics->scaler.y_scale );
       
   458 
       
   459       slot->metrics.horiAdvance = FT_PIX_ROUND( slot->metrics.horiAdvance );
       
   460       slot->metrics.vertAdvance = FT_PIX_ROUND( slot->metrics.vertAdvance );
       
   461 
       
   462       /* now copy outline into glyph slot */
       
   463       FT_GlyphLoader_Rewind( internal->loader );
       
   464       error = FT_GlyphLoader_CopyPoints( internal->loader, gloader );
       
   465       if ( error )
       
   466         goto Exit;
       
   467 
       
   468       slot->outline = internal->loader->base.outline;
       
   469       slot->format  = FT_GLYPH_FORMAT_OUTLINE;
       
   470     }
       
   471 
       
   472   Exit:
       
   473     return error;
       
   474   }
       
   475 
       
   476 
       
   477   /* Load a glyph. */
       
   478 
       
   479   FT_LOCAL_DEF( FT_Error )
       
   480   af_loader_load_glyph( AF_Loader  loader,
       
   481                         FT_Face    face,
       
   482                         FT_UInt    gindex,
       
   483                         FT_UInt32  load_flags )
       
   484   {
       
   485     FT_Error      error;
       
   486     FT_Size       size = face->size;
       
   487     AF_ScalerRec  scaler;
       
   488 
       
   489 
       
   490     if ( !size )
       
   491       return AF_Err_Invalid_Argument;
       
   492 
       
   493     FT_ZERO( &scaler );
       
   494 
       
   495     scaler.face    = face;
       
   496     scaler.x_scale = size->metrics.x_scale;
       
   497     scaler.x_delta = 0;  /* XXX: TODO: add support for sub-pixel hinting */
       
   498     scaler.y_scale = size->metrics.y_scale;
       
   499     scaler.y_delta = 0;  /* XXX: TODO: add support for sub-pixel hinting */
       
   500 
       
   501     scaler.render_mode = FT_LOAD_TARGET_MODE( load_flags );
       
   502     scaler.flags       = 0;  /* XXX: fix this */
       
   503 
       
   504     error = af_loader_reset( loader, face );
       
   505     if ( !error )
       
   506     {
       
   507       AF_ScriptMetrics  metrics;
       
   508       FT_UInt           options = 0;
       
   509 
       
   510 
       
   511 #ifdef FT_OPTION_AUTOFIT2
       
   512       /* XXX: undocumented hook to activate the latin2 hinter */
       
   513       if ( load_flags & ( 1UL << 20 ) )
       
   514         options = 2;
       
   515 #endif
       
   516 
       
   517       error = af_face_globals_get_metrics( loader->globals, gindex,
       
   518                                            options, &metrics );
       
   519       if ( !error )
       
   520       {
       
   521         loader->metrics = metrics;
       
   522 
       
   523         if ( metrics->clazz->script_metrics_scale )
       
   524           metrics->clazz->script_metrics_scale( metrics, &scaler );
       
   525         else
       
   526           metrics->scaler = scaler;
       
   527 
       
   528         load_flags |=  FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_TRANSFORM;
       
   529         load_flags &= ~FT_LOAD_RENDER;
       
   530 
       
   531         if ( metrics->clazz->script_hints_init )
       
   532         {
       
   533           error = metrics->clazz->script_hints_init( &loader->hints,
       
   534                                                      metrics );
       
   535           if ( error )
       
   536             goto Exit;
       
   537         }
       
   538 
       
   539         error = af_loader_load_g( loader, &scaler, gindex, load_flags, 0 );
       
   540       }
       
   541     }
       
   542   Exit:
       
   543     return error;
       
   544   }
       
   545 
       
   546 
       
   547 /* END */