diff --git a/ChangeLog b/ChangeLog index 63403ee7c..42f93b03e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2016-02-07 Werner Lemberg + + [cff] Implement missing operators in new engine (except `random'). + + * src/cff/cf2font.h (CF2_STORAGE_SIZE): New macro. + + * src/cff/cf2intrp.c (cf2_interpT2CharString): Implement the + following operators: abs, add, and, div, drop, dup, eq, exch, get, + ifelse, index, mul, neg, not, or, put, roll, sqrt, sub. + + * src/cff/cf2stack.h, src/cff/cf2stack.c (cf2_stack_roll): New + auxiliary function for `roll' operator. + 2016-02-06 Werner Lemberg [cff] Fix some Type 2 operators in old CFF engine. diff --git a/src/cff/cf2font.h b/src/cff/cf2font.h index 8e7df672f..bd05e69e7 100644 --- a/src/cff/cf2font.h +++ b/src/cff/cf2font.h @@ -54,6 +54,7 @@ FT_BEGIN_HEADER /* (Hiragino Kaku Gothic ProN W3; */ /* 8.2d6e1; 2014-12-19) that exceed */ /* this limit */ +#define CF2_STORAGE_SIZE 32 /* typedef is in `cf2glue.h' */ diff --git a/src/cff/cf2intrp.c b/src/cff/cf2intrp.c index c02a3bf56..7d663dd0e 100644 --- a/src/cff/cf2intrp.c +++ b/src/cff/cf2intrp.c @@ -447,6 +447,8 @@ CF2_Stack opStack = NULL; FT_Byte op1; /* first opcode byte */ + CF2_F16Dot16 storage[CF2_STORAGE_SIZE]; /* for `put' and `get' */ + /* instruction limit; 20,000,000 matches Avalon */ FT_UInt32 instructionLimit = 20000000UL; @@ -834,84 +836,189 @@ break; - /* TODO: should these operators be supported? */ - case cf2_escAND: /* in spec */ - FT_TRACE4(( " and\n" )); + case cf2_escAND: + { + CF2_F16Dot16 arg1; + CF2_F16Dot16 arg2; - CF2_FIXME; - break; - case cf2_escOR: /* in spec */ - FT_TRACE4(( " or\n" )); + FT_TRACE4(( " and\n" )); - CF2_FIXME; - break; + arg2 = cf2_stack_popFixed( opStack ); + arg1 = cf2_stack_popFixed( opStack ); - case cf2_escNOT: /* in spec */ - FT_TRACE4(( " not\n" )); + cf2_stack_pushInt( opStack, arg1 && arg2 ); + } + continue; /* do not clear the stack */ - CF2_FIXME; - break; + case cf2_escOR: + { + CF2_F16Dot16 arg1; + CF2_F16Dot16 arg2; - case cf2_escABS: /* in spec */ - FT_TRACE4(( " abs\n" )); - CF2_FIXME; - break; + FT_TRACE4(( " or\n" )); - case cf2_escADD: /* in spec */ - FT_TRACE4(( " add\n" )); + arg2 = cf2_stack_popFixed( opStack ); + arg1 = cf2_stack_popFixed( opStack ); - CF2_FIXME; - break; + cf2_stack_pushInt( opStack, arg1 || arg2 ); + } + continue; /* do not clear the stack */ - case cf2_escSUB: /* in spec */ - FT_TRACE4(( " sub\n" )); + case cf2_escNOT: + { + CF2_F16Dot16 arg; - CF2_FIXME; - break; - case cf2_escDIV: /* in spec */ - FT_TRACE4(( " div\n" )); + FT_TRACE4(( " not\n" )); - CF2_FIXME; - break; + arg = cf2_stack_popFixed( opStack ); - case cf2_escNEG: /* in spec */ - FT_TRACE4(( " neg\n" )); + cf2_stack_pushInt( opStack, !arg ); + } + continue; /* do not clear the stack */ - CF2_FIXME; - break; + case cf2_escABS: + { + CF2_F16Dot16 arg; - case cf2_escEQ: /* in spec */ - FT_TRACE4(( " eq\n" )); - CF2_FIXME; - break; + FT_TRACE4(( " abs\n" )); - case cf2_escDROP: /* in spec */ + arg = cf2_stack_popFixed( opStack ); + + cf2_stack_pushFixed( opStack, FT_ABS( arg ) ); + } + continue; /* do not clear the stack */ + + case cf2_escADD: + { + CF2_F16Dot16 summand1; + CF2_F16Dot16 summand2; + + + FT_TRACE4(( " add\n" )); + + summand2 = cf2_stack_popFixed( opStack ); + summand1 = cf2_stack_popFixed( opStack ); + + cf2_stack_pushFixed( opStack, summand1 + summand2 ); + } + continue; /* do not clear the stack */ + + case cf2_escSUB: + { + CF2_F16Dot16 minuend; + CF2_F16Dot16 subtrahend; + + + FT_TRACE4(( " sub\n" )); + + subtrahend = cf2_stack_popFixed( opStack ); + minuend = cf2_stack_popFixed( opStack ); + + cf2_stack_pushFixed( opStack, minuend - subtrahend ); + } + continue; /* do not clear the stack */ + + case cf2_escDIV: + { + CF2_F16Dot16 dividend; + CF2_F16Dot16 divisor; + + + FT_TRACE4(( " div\n" )); + + divisor = cf2_stack_popFixed( opStack ); + dividend = cf2_stack_popFixed( opStack ); + + cf2_stack_pushFixed( opStack, FT_DivFix( dividend, divisor ) ); + } + continue; /* do not clear the stack */ + + case cf2_escNEG: + { + CF2_F16Dot16 arg; + + + FT_TRACE4(( " neg\n" )); + + arg = cf2_stack_popFixed( opStack ); + + cf2_stack_pushFixed( opStack, -arg ); + } + continue; /* do not clear the stack */ + + case cf2_escEQ: + { + CF2_F16Dot16 arg1; + CF2_F16Dot16 arg2; + + + FT_TRACE4(( " eq\n" )); + + arg2 = cf2_stack_popFixed( opStack ); + arg1 = cf2_stack_popFixed( opStack ); + + cf2_stack_pushInt( opStack, arg1 == arg2 ); + } + continue; /* do not clear the stack */ + + case cf2_escDROP: FT_TRACE4(( " drop\n" )); - CF2_FIXME; - break; + (void)cf2_stack_popFixed( opStack ); + continue; /* do not clear the stack */ - case cf2_escPUT: /* in spec */ - FT_TRACE4(( " put\n" )); + case cf2_escPUT: + { + CF2_F16Dot16 val; + CF2_Int idx; - CF2_FIXME; - break; - case cf2_escGET: /* in spec */ - FT_TRACE4(( " get\n" )); + FT_TRACE4(( " put\n" )); - CF2_FIXME; - break; + idx = cf2_stack_popInt( opStack ); + val = cf2_stack_popFixed( opStack ); - case cf2_escIFELSE: /* in spec */ - FT_TRACE4(( " ifelse\n" )); + if ( idx >= 0 && idx < CF2_STORAGE_SIZE ) + storage[idx] = val; + } + continue; /* do not clear the stack */ - CF2_FIXME; - break; + case cf2_escGET: + { + CF2_Int idx; + + + FT_TRACE4(( " get\n" )); + + idx = cf2_stack_popInt( opStack ); + + if ( idx >= 0 && idx < CF2_STORAGE_SIZE ) + cf2_stack_pushFixed( opStack, storage[idx] ); + } + continue; /* do not clear the stack */ + + case cf2_escIFELSE: + { + CF2_F16Dot16 arg1; + CF2_F16Dot16 arg2; + CF2_F16Dot16 cond1; + CF2_F16Dot16 cond2; + + + FT_TRACE4(( " ifelse\n" )); + + cond2 = cf2_stack_popFixed( opStack ); + cond1 = cf2_stack_popFixed( opStack ); + arg2 = cf2_stack_popFixed( opStack ); + arg1 = cf2_stack_popFixed( opStack ); + + cf2_stack_pushFixed( opStack, cond1 <= cond2 ? arg1 : arg2 ); + } + continue; /* do not clear the stack */ case cf2_escRANDOM: /* in spec */ FT_TRACE4(( " random\n" )); @@ -919,41 +1026,126 @@ CF2_FIXME; break; - case cf2_escMUL: /* in spec */ - FT_TRACE4(( " mul\n" )); + case cf2_escMUL: + { + CF2_F16Dot16 factor1; + CF2_F16Dot16 factor2; - CF2_FIXME; - break; - case cf2_escSQRT: /* in spec */ - FT_TRACE4(( " sqrt\n" )); + FT_TRACE4(( " mul\n" )); - CF2_FIXME; - break; + factor2 = cf2_stack_popFixed( opStack ); + factor1 = cf2_stack_popFixed( opStack ); - case cf2_escDUP: /* in spec */ - FT_TRACE4(( " dup\n" )); + cf2_stack_pushFixed( opStack, FT_MulFix( factor1, factor2 ) ); + } + continue; /* do not clear the stack */ - CF2_FIXME; - break; + case cf2_escSQRT: + { + CF2_F16Dot16 arg; - case cf2_escEXCH: /* in spec */ - FT_TRACE4(( " exch\n" )); - CF2_FIXME; - break; + FT_TRACE4(( " sqrt\n" )); - case cf2_escINDEX: /* in spec */ - FT_TRACE4(( " index\n" )); + arg = cf2_stack_popFixed( opStack ); + if ( arg > 0 ) + { + FT_Fixed root = arg; + FT_Fixed new_root; - CF2_FIXME; - break; - case cf2_escROLL: /* in spec */ - FT_TRACE4(( " roll\n" )); + /* Babylonian method */ + for (;;) + { + new_root = ( root + FT_DivFix( arg, root ) + 1 ) >> 1; + if ( new_root == root ) + break; + root = new_root; + } + arg = new_root; + } + else + arg = 0; - CF2_FIXME; - break; + cf2_stack_pushFixed( opStack, arg ); + } + continue; /* do not clear the stack */ + + case cf2_escDUP: + { + CF2_F16Dot16 arg; + + + FT_TRACE4(( " dup\n" )); + + arg = cf2_stack_popFixed( opStack ); + + cf2_stack_pushFixed( opStack, arg ); + cf2_stack_pushFixed( opStack, arg ); + } + continue; /* do not clear the stack */ + + case cf2_escEXCH: + { + CF2_F16Dot16 arg1; + CF2_F16Dot16 arg2; + + + FT_TRACE4(( " exch\n" )); + + arg2 = cf2_stack_popFixed( opStack ); + arg1 = cf2_stack_popFixed( opStack ); + + cf2_stack_pushFixed( opStack, arg2 ); + cf2_stack_pushFixed( opStack, arg1 ); + } + continue; /* do not clear the stack */ + + case cf2_escINDEX: + { + CF2_Int idx; + CF2_UInt size; + + + FT_TRACE4(( " index\n" )); + + idx = cf2_stack_popInt( opStack ); + size = cf2_stack_count( opStack ); + + if ( size > 0 ) + { + /* for `cf2_stack_getReal', index 0 is bottom of stack */ + CF2_UInt gr_idx; + + + if ( idx < 0 ) + gr_idx = size - 1; + else if ( (CF2_UInt)idx >= size ) + gr_idx = 0; + else + gr_idx = size - 1 - (CF2_UInt)idx; + + cf2_stack_pushFixed( opStack, + cf2_stack_getReal( opStack, gr_idx ) ); + } + } + continue; /* do not clear the stack */ + + case cf2_escROLL: + { + CF2_Int idx; + CF2_Int count; + + + FT_TRACE4(( " roll\n" )); + + idx = cf2_stack_popInt( opStack ); + count = cf2_stack_popInt( opStack ); + + cf2_stack_roll( opStack, count, idx ); + } + continue; /* do not clear the stack */ case cf2_escHFLEX: { diff --git a/src/cff/cf2stack.c b/src/cff/cf2stack.c index 8332b5d91..6fafd901f 100644 --- a/src/cff/cf2stack.c +++ b/src/cff/cf2stack.c @@ -145,7 +145,7 @@ /* Note: type mismatch is silently cast */ - /* TODO: check this */ + /* TODO: check this */ FT_LOCAL_DEF( CF2_Fixed ) cf2_stack_popFixed( CF2_Stack stack ) { @@ -170,7 +170,7 @@ /* Note: type mismatch is silently cast */ - /* TODO: check this */ + /* TODO: check this */ FT_LOCAL_DEF( CF2_Fixed ) cf2_stack_getReal( CF2_Stack stack, CF2_UInt idx ) @@ -195,6 +195,86 @@ } + FT_LOCAL( void ) + cf2_stack_roll( CF2_Stack stack, + CF2_Int count, + CF2_Int shift ) + { + /* we initialize this variable to avoid compiler warnings */ + CF2_StackNumber last = { { 0 }, CF2_NumberInt }; + + CF2_Int start_idx, idx, i; + + + if ( count < 2 ) + return; /* nothing to do (values 0 and 1), or undefined value */ + + if ( (CF2_UInt)count > cf2_stack_count( stack ) ) + { + CF2_SET_ERROR( stack->error, Stack_Overflow ); + return; + } + + if ( shift < 0 ) + shift = -( ( -shift ) % count ); + else + shift %= count; + + if ( shift == 0 ) + return; /* nothing to do */ + + /* We use the following algorithm to do the rolling, */ + /* which needs two temporary variables only. */ + /* */ + /* Example: */ + /* */ + /* count = 8 */ + /* shift = 2 */ + /* */ + /* stack indices before roll: 7 6 5 4 3 2 1 0 */ + /* stack indices after roll: 1 0 7 6 5 4 3 2 */ + /* */ + /* The value of index 0 gets moved to index 2, while */ + /* the old value of index 2 gets moved to index 4, */ + /* and so on. We thus have the following copying */ + /* chains for shift value 2. */ + /* */ + /* 0 -> 2 -> 4 -> 6 -> 0 */ + /* 1 -> 3 -> 5 -> 7 -> 1 */ + /* */ + /* If `count' and `shift' are incommensurable, we */ + /* have a single chain only. Otherwise, increase */ + /* the start index by 1 after the first chain, then */ + /* do the next chain until all elements in all */ + /* chains are handled. */ + + start_idx = -1; + idx = -1; + for ( i = 0; i < count; i++ ) + { + CF2_StackNumber tmp; + + + if ( start_idx == idx ) + { + start_idx++; + idx = start_idx; + last = stack->buffer[idx]; + } + + idx += shift; + if ( idx >= count ) + idx -= count; + else if ( idx < 0 ) + idx += count; + + tmp = stack->buffer[idx]; + stack->buffer[idx] = last; + last = tmp; + } + } + + FT_LOCAL_DEF( void ) cf2_stack_clear( CF2_Stack stack ) { diff --git a/src/cff/cf2stack.h b/src/cff/cf2stack.h index 8c7861dab..e740a7ac4 100644 --- a/src/cff/cf2stack.h +++ b/src/cff/cf2stack.h @@ -93,6 +93,11 @@ FT_BEGIN_HEADER cf2_stack_getReal( CF2_Stack stack, CF2_UInt idx ); + FT_LOCAL( void ) + cf2_stack_roll( CF2_Stack stack, + CF2_Int count, + CF2_Int idx ); + FT_LOCAL( void ) cf2_stack_clear( CF2_Stack stack );