Visualization Library 2.0.0-b5

A lightweight C++ OpenGL middleware for 2D/3D graphics

VL     Star     Watch     Fork     Issue

[Download] [Tutorials] [All Classes] [Grouped Classes]
cf2blues.c
Go to the documentation of this file.
1 /***************************************************************************/
2 /* */
3 /* cf2blues.c */
4 /* */
5 /* Adobe's code for handling Blue Zones (body). */
6 /* */
7 /* Copyright 2009-2013 Adobe Systems Incorporated. */
8 /* */
9 /* This software, and all works of authorship, whether in source or */
10 /* object code form as indicated by the copyright notice(s) included */
11 /* herein (collectively, the "Work") is made available, and may only be */
12 /* used, modified, and distributed under the FreeType Project License, */
13 /* LICENSE.TXT. Additionally, subject to the terms and conditions of the */
14 /* FreeType Project License, each contributor to the Work hereby grants */
15 /* to any individual or legal entity exercising permissions granted by */
16 /* the FreeType Project License and this section (hereafter, "You" or */
17 /* "Your") a perpetual, worldwide, non-exclusive, no-charge, */
18 /* royalty-free, irrevocable (except as stated in this section) patent */
19 /* license to make, have made, use, offer to sell, sell, import, and */
20 /* otherwise transfer the Work, where such license applies only to those */
21 /* patent claims licensable by such contributor that are necessarily */
22 /* infringed by their contribution(s) alone or by combination of their */
23 /* contribution(s) with the Work to which such contribution(s) was */
24 /* submitted. If You institute patent litigation against any entity */
25 /* (including a cross-claim or counterclaim in a lawsuit) alleging that */
26 /* the Work or a contribution incorporated within the Work constitutes */
27 /* direct or contributory patent infringement, then any patent licenses */
28 /* granted to You under this License for that Work shall terminate as of */
29 /* the date such litigation is filed. */
30 /* */
31 /* By using, modifying, or distributing the Work you indicate that you */
32 /* have read and understood the terms and conditions of the */
33 /* FreeType Project License as well as those provided in this section, */
34 /* and you accept them fully. */
35 /* */
36 /***************************************************************************/
37 
38 
39 #include "cf2ft.h"
40 #include FT_INTERNAL_DEBUG_H
41 
42 #include "cf2blues.h"
43 #include "cf2hints.h"
44 #include "cf2font.h"
45 
46 
47  /*************************************************************************/
48  /* */
49  /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
50  /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
51  /* messages during execution. */
52  /* */
53 #undef FT_COMPONENT
54 #define FT_COMPONENT trace_cf2blues
55 
56 
57  /*
58  * For blue values, the FreeType parser produces an array of integers,
59  * while the Adobe CFF engine produces an array of fixed.
60  * Define a macro to convert FreeType to fixed.
61  */
62 #define cf2_blueToFixed( x ) cf2_intToFixed( x )
63 
64 
65  FT_LOCAL_DEF( void )
67  CF2_Font font )
68  {
69  /* pointer to parsed font object */
70  CFF_Decoder* decoder = font->decoder;
71 
72  CF2_Fixed zoneHeight;
73  CF2_Fixed maxZoneHeight = 0;
74  CF2_Fixed csUnitsPerPixel;
75 
76  size_t numBlueValues;
77  size_t numOtherBlues;
78  size_t numFamilyBlues;
79  size_t numFamilyOtherBlues;
80 
81  FT_Pos* blueValues;
82  FT_Pos* otherBlues;
83  FT_Pos* familyBlues;
84  FT_Pos* familyOtherBlues;
85 
86  size_t i;
87  CF2_Fixed emBoxBottom, emBoxTop;
88 
89  CF2_Int unitsPerEm = font->unitsPerEm;
90 
91 
92  if ( unitsPerEm == 0 )
93  unitsPerEm = 1000;
94 
95  FT_ZERO( blues );
96  blues->scale = font->innerTransform.d;
97 
98  cf2_getBlueMetrics( decoder,
99  &blues->blueScale,
100  &blues->blueShift,
101  &blues->blueFuzz );
102 
103  cf2_getBlueValues( decoder, &numBlueValues, &blueValues );
104  cf2_getOtherBlues( decoder, &numOtherBlues, &otherBlues );
105  cf2_getFamilyBlues( decoder, &numFamilyBlues, &familyBlues );
106  cf2_getFamilyOtherBlues( decoder, &numFamilyOtherBlues, &familyOtherBlues );
107 
108  /*
109  * synthetic em box hint heuristic
110  *
111  * Apply this when ideographic dictionary (LanguageGroup 1) has no
112  * real alignment zones. Adobe tools generate dummy zones at -250 and
113  * 1100 for a 1000 unit em. Fonts with ICF-based alignment zones
114  * should not enable the heuristic. When the heuristic is enabled,
115  * the font's blue zones are ignored.
116  *
117  */
118 
119  /* get em box from OS/2 typoAscender/Descender */
120  /* TODO: FreeType does not parse these metrics. Skip them for now. */
121 #if 0
122  FCM_getHorizontalLineMetrics( &e,
123  font->font,
124  &ascender,
125  &descender,
126  &linegap );
127  if ( ascender - descender == unitsPerEm )
128  {
129  emBoxBottom = cf2_intToFixed( descender );
130  emBoxTop = cf2_intToFixed( ascender );
131  }
132  else
133 #endif
134  {
135  emBoxBottom = CF2_ICF_Bottom;
136  emBoxTop = CF2_ICF_Top;
137  }
138 
139  if ( cf2_getLanguageGroup( decoder ) == 1 &&
140  ( numBlueValues == 0 ||
141  ( numBlueValues == 4 &&
142  cf2_blueToFixed( blueValues[0] ) < emBoxBottom &&
143  cf2_blueToFixed( blueValues[1] ) < emBoxBottom &&
144  cf2_blueToFixed( blueValues[2] ) > emBoxTop &&
145  cf2_blueToFixed( blueValues[3] ) > emBoxTop ) ) )
146  {
147  /*
148  * Construct hint edges suitable for synthetic ghost hints at top
149  * and bottom of em box. +-CF2_MIN_COUNTER allows for unhinted
150  * features above or below the last hinted edge. This also gives a
151  * net 1 pixel boost to the height of ideographic glyphs.
152  *
153  * Note: Adjust synthetic hints outward by epsilon (0x.0001) to
154  * avoid interference. E.g., some fonts have real hints at
155  * 880 and -120.
156  */
157 
158  blues->emBoxBottomEdge.csCoord = emBoxBottom - CF2_FIXED_EPSILON;
159  blues->emBoxBottomEdge.dsCoord = cf2_fixedRound(
160  FT_MulFix(
161  blues->emBoxBottomEdge.csCoord,
162  blues->scale ) ) -
164  blues->emBoxBottomEdge.scale = blues->scale;
165  blues->emBoxBottomEdge.flags = CF2_GhostBottom |
166  CF2_Locked |
168 
169  blues->emBoxTopEdge.csCoord = emBoxTop + CF2_FIXED_EPSILON +
170  2 * font->darkenY;
171  blues->emBoxTopEdge.dsCoord = cf2_fixedRound(
172  FT_MulFix(
173  blues->emBoxTopEdge.csCoord,
174  blues->scale ) ) +
176  blues->emBoxTopEdge.scale = blues->scale;
177  blues->emBoxTopEdge.flags = CF2_GhostTop |
178  CF2_Locked |
180 
181  blues->doEmBoxHints = TRUE; /* enable the heuristic */
182 
183  return;
184  }
185 
186  /* copy `BlueValues' and `OtherBlues' to a combined array of top and */
187  /* bottom zones */
188  for ( i = 0; i < numBlueValues; i += 2 )
189  {
190  blues->zone[blues->count].csBottomEdge =
191  cf2_blueToFixed( blueValues[i] );
192  blues->zone[blues->count].csTopEdge =
193  cf2_blueToFixed( blueValues[i + 1] );
194 
195  zoneHeight = blues->zone[blues->count].csTopEdge -
196  blues->zone[blues->count].csBottomEdge;
197 
198  if ( zoneHeight < 0 )
199  {
200  FT_TRACE4(( "cf2_blues_init: ignoring negative zone height\n" ));
201  continue; /* reject this zone */
202  }
203 
204  if ( zoneHeight > maxZoneHeight )
205  {
206  /* take maximum before darkening adjustment */
207  /* so overshoot suppression point doesn't change */
208  maxZoneHeight = zoneHeight;
209  }
210 
211  /* adjust both edges of top zone upward by twice darkening amount */
212  if ( i != 0 )
213  {
214  blues->zone[blues->count].csTopEdge += 2 * font->darkenY;
215  blues->zone[blues->count].csBottomEdge += 2 * font->darkenY;
216  }
217 
218  /* first `BlueValue' is bottom zone; others are top */
219  if ( i == 0 )
220  {
221  blues->zone[blues->count].bottomZone =
222  TRUE;
223  blues->zone[blues->count].csFlatEdge =
224  blues->zone[blues->count].csTopEdge;
225  }
226  else
227  {
228  blues->zone[blues->count].bottomZone =
229  FALSE;
230  blues->zone[blues->count].csFlatEdge =
231  blues->zone[blues->count].csBottomEdge;
232  }
233 
234  blues->count += 1;
235  }
236 
237  for ( i = 0; i < numOtherBlues; i += 2 )
238  {
239  blues->zone[blues->count].csBottomEdge =
240  cf2_blueToFixed( otherBlues[i] );
241  blues->zone[blues->count].csTopEdge =
242  cf2_blueToFixed( otherBlues[i + 1] );
243 
244  zoneHeight = blues->zone[blues->count].csTopEdge -
245  blues->zone[blues->count].csBottomEdge;
246 
247  if ( zoneHeight < 0 )
248  {
249  FT_TRACE4(( "cf2_blues_init: ignoring negative zone height\n" ));
250  continue; /* reject this zone */
251  }
252 
253  if ( zoneHeight > maxZoneHeight )
254  {
255  /* take maximum before darkening adjustment */
256  /* so overshoot suppression point doesn't change */
257  maxZoneHeight = zoneHeight;
258  }
259 
260  /* Note: bottom zones are not adjusted for darkening amount */
261 
262  /* all OtherBlues are bottom zone */
263  blues->zone[blues->count].bottomZone =
264  TRUE;
265  blues->zone[blues->count].csFlatEdge =
266  blues->zone[blues->count].csTopEdge;
267 
268  blues->count += 1;
269  }
270 
271  /* Adjust for FamilyBlues */
272 
273  /* Search for the nearest flat edge in `FamilyBlues' or */
274  /* `FamilyOtherBlues'. According to the Black Book, any matching edge */
275  /* must be within one device pixel */
276 
277  csUnitsPerPixel = FT_DivFix( cf2_intToFixed( 1 ), blues->scale );
278 
279  /* loop on all zones in this font */
280  for ( i = 0; i < blues->count; i++ )
281  {
282  size_t j;
283  CF2_Fixed minDiff;
284  CF2_Fixed flatFamilyEdge, diff;
285  /* value for this font */
286  CF2_Fixed flatEdge = blues->zone[i].csFlatEdge;
287 
288 
289  if ( blues->zone[i].bottomZone )
290  {
291  /* In a bottom zone, the top edge is the flat edge. */
292  /* Search `FamilyOtherBlues' for bottom zones; look for closest */
293  /* Family edge that is within the one pixel threshold. */
294 
295  minDiff = CF2_FIXED_MAX;
296 
297  for ( j = 0; j < numFamilyOtherBlues; j += 2 )
298  {
299  /* top edge */
300  flatFamilyEdge = cf2_blueToFixed( familyOtherBlues[j + 1] );
301 
302  diff = cf2_fixedAbs( flatEdge - flatFamilyEdge );
303 
304  if ( diff < minDiff && diff < csUnitsPerPixel )
305  {
306  blues->zone[i].csFlatEdge = flatFamilyEdge;
307  minDiff = diff;
308 
309  if ( diff == 0 )
310  break;
311  }
312  }
313 
314  /* check the first member of FamilyBlues, which is a bottom zone */
315  if ( numFamilyBlues >= 2 )
316  {
317  /* top edge */
318  flatFamilyEdge = cf2_blueToFixed( familyBlues[1] );
319 
320  diff = cf2_fixedAbs( flatEdge - flatFamilyEdge );
321 
322  if ( diff < minDiff && diff < csUnitsPerPixel )
323  blues->zone[i].csFlatEdge = flatFamilyEdge;
324  }
325  }
326  else
327  {
328  /* In a top zone, the bottom edge is the flat edge. */
329  /* Search `FamilyBlues' for top zones; skip first zone, which is a */
330  /* bottom zone; look for closest Family edge that is within the */
331  /* one pixel threshold */
332 
333  minDiff = CF2_FIXED_MAX;
334 
335  for ( j = 2; j < numFamilyBlues; j += 2 )
336  {
337  /* bottom edge */
338  flatFamilyEdge = cf2_blueToFixed( familyBlues[j] );
339 
340  /* adjust edges of top zone upward by twice darkening amount */
341  flatFamilyEdge += 2 * font->darkenY; /* bottom edge */
342 
343  diff = cf2_fixedAbs( flatEdge - flatFamilyEdge );
344 
345  if ( diff < minDiff && diff < csUnitsPerPixel )
346  {
347  blues->zone[i].csFlatEdge = flatFamilyEdge;
348  minDiff = diff;
349 
350  if ( diff == 0 )
351  break;
352  }
353  }
354  }
355  }
356 
357  /* TODO: enforce separation of zones, including BlueFuzz */
358 
359  /* Adjust BlueScale; similar to AdjustBlueScale() in coretype */
360  /* `bcsetup.c'. */
361 
362  if ( maxZoneHeight > 0 )
363  {
364  if ( blues->blueScale > FT_DivFix( cf2_intToFixed( 1 ),
365  maxZoneHeight ) )
366  {
367  /* clamp at maximum scale */
368  blues->blueScale = FT_DivFix( cf2_intToFixed( 1 ),
369  maxZoneHeight );
370  }
371 
372  /*
373  * TODO: Revisit the bug fix for 613448. The minimum scale
374  * requirement catches a number of library fonts. For
375  * example, with default BlueScale (.039625) and 0.4 minimum,
376  * the test below catches any font with maxZoneHeight < 10.1.
377  * There are library fonts ranging from 2 to 10 that get
378  * caught, including e.g., Eurostile LT Std Medium with
379  * maxZoneHeight of 6.
380  *
381  */
382 #if 0
383  if ( blueScale < .4 / maxZoneHeight )
384  {
385  tetraphilia_assert( 0 );
386  /* clamp at minimum scale, per bug 0613448 fix */
387  blueScale = .4 / maxZoneHeight;
388  }
389 #endif
390 
391  }
392 
393  /*
394  * Suppress overshoot and boost blue zones at small sizes. Boost
395  * amount varies linearly from 0.5 pixel near 0 to 0 pixel at
396  * blueScale cutoff.
397  * Note: This boost amount is different from the coretype heuristic.
398  *
399  */
400 
401  if ( blues->scale < blues->blueScale )
402  {
403  blues->suppressOvershoot = TRUE;
404 
405  /* Change rounding threshold for `dsFlatEdge'. */
406  /* Note: constant changed from 0.5 to 0.6 to avoid a problem with */
407  /* 10ppem Arial */
408 
409  blues->boost = FT_MulFix(
410  cf2_floatToFixed( .6 ),
411  ( cf2_intToFixed( 1 ) -
412  FT_DivFix( blues->scale,
413  blues->blueScale ) ) );
414  if ( blues->boost > 0x7FFF )
415  {
416  /* boost must remain less than 0.5, or baseline could go negative */
417  blues->boost = 0x7FFF;
418  }
419  }
420 
421  /* boost and darkening have similar effects; don't do both */
422  if ( font->stemDarkened )
423  blues->boost = 0;
424 
425  /* set device space alignment for each zone; */
426  /* apply boost amount before rounding flat edge */
427 
428  for ( i = 0; i < blues->count; i++ )
429  {
430  if ( blues->zone[i].bottomZone )
431  blues->zone[i].dsFlatEdge = cf2_fixedRound(
432  FT_MulFix(
433  blues->zone[i].csFlatEdge,
434  blues->scale ) -
435  blues->boost );
436  else
437  blues->zone[i].dsFlatEdge = cf2_fixedRound(
438  FT_MulFix(
439  blues->zone[i].csFlatEdge,
440  blues->scale ) +
441  blues->boost );
442  }
443  }
444 
445 
446  /*
447  * Check whether `stemHint' is captured by one of the blue zones.
448  *
449  * Zero, one or both edges may be valid; only valid edges can be
450  * captured. For compatibility with CoolType, search top and bottom
451  * zones in the same pass (see `BlueLock'). If a hint is captured,
452  * return true and position the edge(s) in one of 3 ways:
453  *
454  * 1) If `BlueScale' suppresses overshoot, position the captured edge
455  * at the flat edge of the zone.
456  * 2) If overshoot is not suppressed and `BlueShift' requires
457  * overshoot, position the captured edge a minimum of 1 device pixel
458  * from the flat edge.
459  * 3) If overshoot is not suppressed or required, position the captured
460  * edge at the nearest device pixel.
461  *
462  */
465  CF2_Hint bottomHintEdge,
466  CF2_Hint topHintEdge )
467  {
468  /* TODO: validate? */
469  CF2_Fixed csFuzz = blues->blueFuzz;
470 
471  /* new position of captured edge */
472  CF2_Fixed dsNew;
473 
474  /* amount that hint is moved when positioned */
475  CF2_Fixed dsMove = 0;
476 
477  FT_Bool captured = FALSE;
478  CF2_UInt i;
479 
480 
481  /* assert edge flags are consistent */
482  FT_ASSERT( !cf2_hint_isTop( bottomHintEdge ) &&
483  !cf2_hint_isBottom( topHintEdge ) );
484 
485  /* TODO: search once without blue fuzz for compatibility with coretype? */
486  for ( i = 0; i < blues->count; i++ )
487  {
488  if ( blues->zone[i].bottomZone &&
489  cf2_hint_isBottom( bottomHintEdge ) )
490  {
491  if ( ( blues->zone[i].csBottomEdge - csFuzz ) <=
492  bottomHintEdge->csCoord &&
493  bottomHintEdge->csCoord <=
494  ( blues->zone[i].csTopEdge + csFuzz ) )
495  {
496  /* bottom edge captured by bottom zone */
497 
498  if ( blues->suppressOvershoot )
499  dsNew = blues->zone[i].dsFlatEdge;
500 
501  else if ( ( blues->zone[i].csTopEdge - bottomHintEdge->csCoord ) >=
502  blues->blueShift )
503  {
504  /* guarantee minimum of 1 pixel overshoot */
505  dsNew = FT_MIN(
506  cf2_fixedRound( bottomHintEdge->dsCoord ),
507  blues->zone[i].dsFlatEdge - cf2_intToFixed( 1 ) );
508  }
509 
510  else
511  {
512  /* simply round captured edge */
513  dsNew = cf2_fixedRound( bottomHintEdge->dsCoord );
514  }
515 
516  dsMove = dsNew - bottomHintEdge->dsCoord;
517  captured = TRUE;
518 
519  break;
520  }
521  }
522 
523  if ( !blues->zone[i].bottomZone && cf2_hint_isTop( topHintEdge ) )
524  {
525  if ( ( blues->zone[i].csBottomEdge - csFuzz ) <=
526  topHintEdge->csCoord &&
527  topHintEdge->csCoord <=
528  ( blues->zone[i].csTopEdge + csFuzz ) )
529  {
530  /* top edge captured by top zone */
531 
532  if ( blues->suppressOvershoot )
533  dsNew = blues->zone[i].dsFlatEdge;
534 
535  else if ( ( topHintEdge->csCoord - blues->zone[i].csBottomEdge ) >=
536  blues->blueShift )
537  {
538  /* guarantee minimum of 1 pixel overshoot */
539  dsNew = FT_MAX(
540  cf2_fixedRound( topHintEdge->dsCoord ),
541  blues->zone[i].dsFlatEdge + cf2_intToFixed( 1 ) );
542  }
543 
544  else
545  {
546  /* simply round captured edge */
547  dsNew = cf2_fixedRound( topHintEdge->dsCoord );
548  }
549 
550  dsMove = dsNew - topHintEdge->dsCoord;
551  captured = TRUE;
552 
553  break;
554  }
555  }
556  }
557 
558  if ( captured )
559  {
560  /* move both edges and flag them `locked' */
561  if ( cf2_hint_isValid( bottomHintEdge ) )
562  {
563  bottomHintEdge->dsCoord += dsMove;
564  cf2_hint_lock( bottomHintEdge );
565  }
566 
567  if ( cf2_hint_isValid( topHintEdge ) )
568  {
569  topHintEdge->dsCoord += dsMove;
570  cf2_hint_lock( topHintEdge );
571  }
572  }
573 
574  return captured;
575  }
576 
577 
578 /* END */
#define cf2_fixedRound(x)
Definition: cf2fixed.h:64
#define CF2_Int
Definition: cf2types.h:65
cf2_getFamilyOtherBlues(CFF_Decoder *decoder, size_t *count, FT_Pos **data)
Definition: cf2ft.c:472
#define CF2_MIN_COUNTER
Definition: cf2blues.h:114
FT_DivFix(FT_Long a, FT_Long b)
Definition: ftcalc.c:586
cf2_hint_isBottom(const CF2_Hint hint)
Definition: cf2hints.c:248
FT_BEGIN_HEADER typedef signed long FT_Pos
Definition: ftimage.h:59
cf2_getOtherBlues(CFF_Decoder *decoder, size_t *count, FT_Pos **data)
Definition: cf2ft.c:446
cf2_hint_lock(CF2_Hint hint)
Definition: cf2hints.c:270
#define FT_MIN(a, b)
Definition: ftobjs.h:70
cf2_getFamilyBlues(CFF_Decoder *decoder, size_t *count, FT_Pos **data)
Definition: cf2ft.c:459
cf2_blues_init(CF2_Blues blues, CF2_Font font)
Definition: cf2blues.c:66
cf2_getBlueValues(CFF_Decoder *decoder, size_t *count, FT_Pos **data)
Definition: cf2ft.c:433
png_uint_32 i
Definition: png.h:2640
FT_BEGIN_HEADER typedef unsigned char FT_Bool
Definition: fttypes.h:104
cf2_getLanguageGroup(CFF_Decoder *decoder)
Definition: cf2ft.c:485
cf2_blues_capture(const CF2_Blues blues, CF2_Hint bottomHintEdge, CF2_Hint topHintEdge)
Definition: cf2blues.c:464
#define const
Definition: zconf.h:91
#define FT_ASSERT(condition)
Definition: ftdebug.h:211
#define FT_TRACE4(varformat)
Definition: ftdebug.h:161
#define cf2_blueToFixed(x)
Definition: cf2blues.c:62
cf2_hint_isValid(const CF2_Hint hint)
Definition: cf2hints.c:218
#define CF2_Fixed
Definition: cf2fixed.h:48
#define cf2_intToFixed(i)
Definition: cf2fixed.h:60
#define FT_ZERO(p)
Definition: ftmemory.h:210
#define FT_MAX(a, b)
Definition: ftobjs.h:71
#define FALSE
Definition: ftobjs.h:57
#define CF2_UInt
Definition: cf2types.h:64
cf2_getBlueMetrics(CFF_Decoder *decoder, CF2_Fixed *blueScale, CF2_Fixed *blueShift, CF2_Fixed *blueFuzz)
Definition: cf2ft.c:413
FT_MulFix(FT_Long a, FT_Long b)
Definition: ftcalc.c:485
#define cf2_floatToFixed(f)
Definition: cf2fixed.h:66
#define cf2_fixedAbs(x)
Definition: cf2fixed.h:68
#define CF2_FIXED_MAX
Definition: cf2fixed.h:52
cf2_hint_isTop(const CF2_Hint hint)
Definition: cf2hints.c:240
#define CF2_FIXED_EPSILON
Definition: cf2fixed.h:55
#define TRUE
Definition: ftobjs.h:53
#define FT_LOCAL_DEF(x)
Definition: ftconfig.h:236