Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/base/ttinterp.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2025 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
18
/* Changes after FreeType: cut out the TrueType instruction interpreter. */
19
/* Patented algorithms are replaced with THROW_PATENTED. */
20
21
/*******************************************************************
22
 *
23
 *  ttinterp.c                                              2.3
24
 *
25
 *  TrueType bytecode intepreter.
26
 *
27
 *  Copyright 1996-1998 by
28
 *  David Turner, Robert Wilhelm, and Werner Lemberg
29
 *
30
 *  This file is part of the FreeType project, and may only be used
31
 *  modified and distributed under the terms of the FreeType project
32
 *  license, LICENSE.TXT.  By continuing to use, modify, or distribute
33
 *  this file you indicate that you have read the license and
34
 *  understand and accept it fully.
35
 *
36
 *
37
 *  TODO:
38
 *
39
 *  - Fix the non-square pixel case (or how to manage the CVT to
40
 *    detect horizontal and vertical scaled FUnits ?)
41
 *
42
 *
43
 *  Changes between 2.3 and 2.2:
44
 *
45
 *  - added support for rotation, stretching, instruction control
46
 *
47
 *  - added support for non-square pixels.  However, this doesn't
48
 *    work perfectly yet...
49
 *
50
 *  Changes between 2.2 and 2.1:
51
 *
52
 *  - a small bugfix in the Push opcodes
53
 *
54
 *  Changes between 2.1 and 2.0:
55
 *
56
 *  - created the TTExec component to take care of all execution
57
 *    context management.  The interpreter has now one single
58
 *    function.
59
 *
60
 *  - made some changes to support re-entrancy.  The re-entrant
61
 *    interpreter is smaller!
62
 *
63
 ******************************************************************/
64
65
#include "ttmisc.h"
66
67
#include "ttfoutl.h"
68
#include "tttypes.h"
69
#include "ttcalc.h"
70
#include "ttinterp.h"
71
#include "ttfinp.h"
72
#include "setjmp_.h"
73
74
#ifdef DEBUG
75
#  define DBG_PAINT    CUR.current_face->font->DebugRepaint(CUR.current_face->font);
76
77
#  define DBG_PRT_FUN CUR.current_face->font->DebugPrint
78
#  define DBG_PRT (void)(!DBG_PRT_FUN ? 0 : DBG_PRT_FUN(CUR.current_face->font
79
#  define DBG_PRINT(fmt) DBG_PRT, fmt))
80
#  define DBG_PRINT1(fmt, a) DBG_PRT, fmt, a))
81
#  define DBG_PRINT3(fmt, a, b, c) DBG_PRT, fmt, a, b, c))
82
#  define DBG_PRINT4(fmt, a, b, c, d) DBG_PRT, fmt, a, b, c, d))
83
#else
84
14.8k
#  define DBG_PRT_FUN NULL
85
#  define DBG_PAINT
86
#  define DBG_PRINT(fmt)
87
#  define DBG_PRINT1(fmt, a)
88
#  define DBG_PRINT3(fmt, a, b, c)
89
#  define DBG_PRINT4(fmt, a, b, c, d)
90
#endif
91
92
/* #define COLLECT_STATS_TTINTERP */
93
94
#ifdef COLLECT_STATS_TTINTERP
95
static int nInstrCount=0;
96
#endif
97
98
/* There are two kinds of implementations there:              */
99
/*                                                            */
100
/* a. static implementation:                                  */
101
/*                                                            */
102
/*    The current execution context is a static variable,     */
103
/*    which fields are accessed directly by the interpreter   */
104
/*    during execution.  The context is named 'cur'.          */
105
/*                                                            */
106
/*    This version is non-reentrant, of course.               */
107
/*                                                            */
108
/*                                                            */
109
/* b. indirect implementation:                                */
110
/*                                                            */
111
/*    The current execution context is passed to _each_       */
112
/*    function as its first argument, and each field is       */
113
/*    thus accessed indirectly.                               */
114
/*                                                            */
115
/*    This version is, however, fully re-entrant.             */
116
/*                                                            */
117
/*                                                            */
118
/*  The idea is that an indirect implementation may be        */
119
/*  slower to execute on the low-end processors that are      */
120
/*  used in some systems (like 386s or even 486s).            */
121
/*                                                            */
122
/*  When the interpreter started, we had no idea of the       */
123
/*  time that glyph hinting (i.e. executing instructions)     */
124
/*  could take in the whole process of rendering a glyph,     */
125
/*  and a 10 to 30% performance penalty on low-end systems    */
126
/*  didn't seem much of a good idea.  This question led us    */
127
/*  to provide two distinct builds of the C version from      */
128
/*  a single source, with the use of macros (again).          */
129
/*                                                            */
130
/*  Now that the engine is working (and working really        */
131
/*  well!), it seems that the greatest time-consuming         */
132
/*  factors are: file i/o, glyph loading, rasterizing and     */
133
/*  _then_ glyph hinting!                                     */
134
/*                                                            */
135
/*  Tests performed with two versions of the 'fttimer'        */
136
/*  program seem to indicate that hinting takes less than 5%  */
137
/*  of the rendering process, which is dominated by glyph     */
138
/*  loading and scan-line conversion by an high order of      */
139
/*  magnitude.                                                */
140
/*                                                            */
141
/*  As a consequence, the indirect implementation is now the  */
142
/*  default, as its performance costs can be considered       */
143
/*  negligible in our context. Note, however, that we         */
144
/*  kept the same source with macros because:                 */
145
/*                                                            */
146
/*    - the code is kept very close in design to the          */
147
/*      Pascal one used for development.                      */
148
/*                                                            */
149
/*    - it's much more readable that way!                     */
150
/*                                                            */
151
/*    - it's still open to later experimentation and tuning   */
152
153
#ifndef TT_STATIC_INTERPRETER      /* indirect implementation */
154
155
312M
#define CUR (*exc)                 /* see ttobjs.h */
156
157
#else                              /* static implementation */
158
159
#define CUR cur
160
161
  static TExecution_Context  cur;  /* static exec. context variable */
162
163
  /* apparently, we have a _lot_ of direct indexing when accessing  */
164
  /* the static 'cur', which makes the code bigger (due to all the  */
165
  /* four bytes addresses).                                         */
166
167
#endif
168
169
#define INS_ARG         EXEC_OPS PStorage args  /* see ttexec.h */
170
171
2.69M
#define SKIP_Code()     SkipCode( EXEC_ARG )
172
173
#define GET_ShortIns()  GetShortIns( EXEC_ARG )
174
175
88.5k
#define COMPUTE_Funcs() Compute_Funcs( EXEC_ARG )
176
177
#define NORMalize( x, y, v )  Normalize( EXEC_ARGS x, y, v )
178
179
#define SET_SuperRound( scale, flags ) \
180
                        SetSuperRound( EXEC_ARGS scale, flags )
181
182
#define INS_Goto_CodeRange( range, ip ) \
183
0
                        Ins_Goto_CodeRange( EXEC_ARGS range, ip )
184
185
#define CUR_Func_project( x, y )   CUR.func_project( EXEC_ARGS x, y )
186
#define CUR_Func_move( z, p, d )   CUR.func_move( EXEC_ARGS z, p, d )
187
#define CUR_Func_dualproj( x, y )  CUR.func_dualproj( EXEC_ARGS x, y )
188
#define CUR_Func_freeProj( x, y )  CUR.func_freeProj( EXEC_ARGS x, y )
189
#define CUR_Func_round( d, c )     CUR.func_round( EXEC_ARGS d, c )
190
191
#define CUR_Func_read_cvt( index )       \
192
          CUR.func_read_cvt( EXEC_ARGS index )
193
194
#define CUR_Func_write_cvt( index, val ) \
195
          CUR.func_write_cvt( EXEC_ARGS index, val )
196
197
#define CUR_Func_move_cvt( index, val )  \
198
          CUR.func_move_cvt( EXEC_ARGS index, val )
199
200
#define CURRENT_Ratio()  Current_Ratio( EXEC_ARG )
201
#define CURRENT_Ppem()   Current_Ppem( EXEC_ARG )
202
203
8.69M
#define CALC_Length()  Calc_Length( EXEC_ARG )
204
205
#define INS_SxVTL( a, b, c, d ) Ins_SxVTL( EXEC_ARGS a, b, c, d )
206
207
#define COMPUTE_Point_Displacement( a, b, c, d ) \
208
           Compute_Point_Displacement( EXEC_ARGS a, b, c, d )
209
210
#define MOVE_Zp2_Point( a, b, c, t )  Move_Zp2_Point( EXEC_ARGS a, b, c, t )
211
212
#define CUR_Ppem()  Cur_PPEM( EXEC_ARG )
213
214
  /* Instruction dispatch function, as used by the interpreter */
215
  typedef void  (*TInstruction_Function)( INS_ARG );
216
217
#define BOUNDS(x,n)  ( x < 0 || x >= n )
218
219
#ifndef ABS
220
#define ABS(x)  ( (x) < 0 ? -(x) : (x) )
221
#endif
222
223
/* The following macro is used to disable algorithms,
224
   which could cause Apple's patent infringement. */
225
0
#define THROW_PATENTED longjmp(find_jmp_buf(CUR.trap), TT_Err_Invalid_Engine)
226
227
/*********************************************************************/
228
/*                                                                   */
229
/*  Before an opcode is executed, the interpreter verifies that      */
230
/*  there are enough arguments on the stack, with the help of        */
231
/*  the Pop_Push_Count table.                                        */
232
/*                                                                   */
233
/*  For each opcode, the first column gives the number of arguments  */
234
/*  that are popped from the stack; the second one gives the number  */
235
/*  of those that are pushed in result.                              */
236
/*                                                                   */
237
/*  Note that for opcodes with a varying number of parameters,       */
238
/*  either 0 or 1 arg is verified before execution, depending        */
239
/*  on the nature of the instruction:                                */
240
/*                                                                   */
241
/*   - if the number of arguments is given by the bytecode           */
242
/*     stream or the loop variable, 0 is chosen.                     */
243
/*                                                                   */
244
/*   - if the first argument is a count n that is followed           */
245
/*     by arguments a1..an, then 1 is chosen.                        */
246
/*                                                                   */
247
/*********************************************************************/
248
249
  static unsigned char Pop_Push_Count[512] =
250
  {
251
    /* opcodes are gathered in groups of 16 */
252
    /* please keep the spaces as they are   */
253
254
    /*  SVTCA  y  */  0, 0,
255
    /*  SVTCA  x  */  0, 0,
256
    /*  SPvTCA y  */  0, 0,
257
    /*  SPvTCA x  */  0, 0,
258
    /*  SFvTCA y  */  0, 0,
259
    /*  SFvTCA x  */  0, 0,
260
    /*  SPvTL //  */  2, 0,
261
    /*  SPvTL +   */  2, 0,
262
    /*  SFvTL //  */  2, 0,
263
    /*  SFvTL +   */  2, 0,
264
    /*  SPvFS     */  2, 0,
265
    /*  SFvFS     */  2, 0,
266
    /*  GPV       */  0, 2,
267
    /*  GFV       */  0, 2,
268
    /*  SFvTPv    */  0, 0,
269
    /*  ISECT     */  5, 0,
270
271
    /*  SRP0      */  1, 0,
272
    /*  SRP1      */  1, 0,
273
    /*  SRP2      */  1, 0,
274
    /*  SZP0      */  1, 0,
275
    /*  SZP1      */  1, 0,
276
    /*  SZP2      */  1, 0,
277
    /*  SZPS      */  1, 0,
278
    /*  SLOOP     */  1, 0,
279
    /*  RTG       */  0, 0,
280
    /*  RTHG      */  0, 0,
281
    /*  SMD       */  1, 0,
282
    /*  ELSE      */  0, 0,
283
    /*  JMPR      */  1, 0,
284
    /*  SCvTCi    */  1, 0,
285
    /*  SSwCi     */  1, 0,
286
    /*  SSW       */  1, 0,
287
288
    /*  DUP       */  1, 2,
289
    /*  POP       */  1, 0,
290
    /*  CLEAR     */  0, 0,
291
    /*  SWAP      */  2, 2,
292
    /*  DEPTH     */  0, 1,
293
    /*  CINDEX    */  1, 1,
294
    /*  MINDEX    */  1, 0,
295
    /*  AlignPTS  */  2, 0,
296
    /*  INS_$28   */  0, 0,
297
    /*  UTP       */  1, 0,
298
    /*  LOOPCALL  */  2, 0,
299
    /*  CALL      */  1, 0,
300
    /*  FDEF      */  1, 0,
301
    /*  ENDF      */  0, 0,
302
    /*  MDAP[0]   */  1, 0,
303
    /*  MDAP[1]   */  1, 0,
304
305
    /*  IUP[0]    */  0, 0,
306
    /*  IUP[1]    */  0, 0,
307
    /*  SHP[0]    */  0, 0,
308
    /*  SHP[1]    */  0, 0,
309
    /*  SHC[0]    */  1, 0,
310
    /*  SHC[1]    */  1, 0,
311
    /*  SHZ[0]    */  1, 0,
312
    /*  SHZ[1]    */  1, 0,
313
    /*  SHPIX     */  1, 0,
314
    /*  IP        */  0, 0,
315
    /*  MSIRP[0]  */  2, 0,
316
    /*  MSIRP[1]  */  2, 0,
317
    /*  AlignRP   */  0, 0,
318
    /*  RTDG      */  0, 0,
319
    /*  MIAP[0]   */  2, 0,
320
    /*  MIAP[1]   */  2, 0,
321
322
    /*  NPushB    */  0, 0,
323
    /*  NPushW    */  0, 0,
324
    /*  WS        */  2, 0,
325
    /*  RS        */  1, 1,
326
    /*  WCvtP     */  2, 0,
327
    /*  RCvt      */  1, 1,
328
    /*  GC[0]     */  1, 1,
329
    /*  GC[1]     */  1, 1,
330
    /*  SCFS      */  2, 0,
331
    /*  MD[0]     */  2, 1,
332
    /*  MD[1]     */  2, 1,
333
    /*  MPPEM     */  0, 1,
334
    /*  MPS       */  0, 1,
335
    /*  FlipON    */  0, 0,
336
    /*  FlipOFF   */  0, 0,
337
    /*  DEBUG     */  1, 0,
338
339
    /*  LT        */  2, 1,
340
    /*  LTEQ      */  2, 1,
341
    /*  GT        */  2, 1,
342
    /*  GTEQ      */  2, 1,
343
    /*  EQ        */  2, 1,
344
    /*  NEQ       */  2, 1,
345
    /*  ODD       */  1, 1,
346
    /*  EVEN      */  1, 1,
347
    /*  IF        */  1, 0,
348
    /*  EIF       */  0, 0,
349
    /*  AND       */  2, 1,
350
    /*  OR        */  2, 1,
351
    /*  NOT       */  1, 1,
352
    /*  DeltaP1   */  1, 0,
353
    /*  SDB       */  1, 0,
354
    /*  SDS       */  1, 0,
355
356
    /*  ADD       */  2, 1,
357
    /*  SUB       */  2, 1,
358
    /*  DIV       */  2, 1,
359
    /*  MUL       */  2, 1,
360
    /*  ABS       */  1, 1,
361
    /*  NEG       */  1, 1,
362
    /*  FLOOR     */  1, 1,
363
    /*  CEILING   */  1, 1,
364
    /*  ROUND[0]  */  1, 1,
365
    /*  ROUND[1]  */  1, 1,
366
    /*  ROUND[2]  */  1, 1,
367
    /*  ROUND[3]  */  1, 1,
368
    /*  NROUND[0] */  1, 1,
369
    /*  NROUND[1] */  1, 1,
370
    /*  NROUND[2] */  1, 1,
371
    /*  NROUND[3] */  1, 1,
372
373
    /*  WCvtF     */  2, 0,
374
    /*  DeltaP2   */  1, 0,
375
    /*  DeltaP3   */  1, 0,
376
    /*  DeltaCn[0] */ 1, 0,
377
    /*  DeltaCn[1] */ 1, 0,
378
    /*  DeltaCn[2] */ 1, 0,
379
    /*  SROUND    */  1, 0,
380
    /*  S45Round  */  1, 0,
381
    /*  JROT      */  2, 0,
382
    /*  JROF      */  2, 0,
383
    /*  ROFF      */  0, 0,
384
    /*  INS_$7B   */  0, 0,
385
    /*  RUTG      */  0, 0,
386
    /*  RDTG      */  0, 0,
387
    /*  SANGW     */  1, 0,
388
    /*  AA        */  1, 0,
389
390
    /*  FlipPT    */  0, 0,
391
    /*  FlipRgON  */  2, 0,
392
    /*  FlipRgOFF */  2, 0,
393
    /*  INS_$83   */  0, 0,
394
    /*  INS_$84   */  0, 0,
395
    /*  ScanCTRL  */  1, 0,
396
    /*  SDVPTL[0] */  2, 0,
397
    /*  SDVPTL[1] */  2, 0,
398
    /*  GetINFO   */  1, 1,
399
    /*  IDEF      */  1, 0,
400
    /*  ROLL      */  3, 3,
401
    /*  MAX       */  2, 1,
402
    /*  MIN       */  2, 1,
403
    /*  ScanTYPE  */  1, 0,
404
    /*  InstCTRL  */  2, 0,
405
    /*  INS_$8F   */  0, 0,
406
407
    /*  INS_$90  */   0, 0,
408
    /*  INS_$91  */   0, 0,
409
    /*  INS_$92  */   0, 0,
410
    /*  INS_$93  */   0, 0,
411
    /*  INS_$94  */   0, 0,
412
    /*  INS_$95  */   0, 0,
413
    /*  INS_$96  */   0, 0,
414
    /*  INS_$97  */   0, 0,
415
    /*  INS_$98  */   0, 0,
416
    /*  INS_$99  */   0, 0,
417
    /*  INS_$9A  */   0, 0,
418
    /*  INS_$9B  */   0, 0,
419
    /*  INS_$9C  */   0, 0,
420
    /*  INS_$9D  */   0, 0,
421
    /*  INS_$9E  */   0, 0,
422
    /*  INS_$9F  */   0, 0,
423
424
    /*  INS_$A0  */   0, 0,
425
    /*  INS_$A1  */   0, 0,
426
    /*  INS_$A2  */   0, 0,
427
    /*  INS_$A3  */   0, 0,
428
    /*  INS_$A4  */   0, 0,
429
    /*  INS_$A5  */   0, 0,
430
    /*  INS_$A6  */   0, 0,
431
    /*  INS_$A7  */   0, 0,
432
    /*  INS_$A8  */   0, 0,
433
    /*  INS_$A9  */   0, 0,
434
    /*  INS_$AA  */   0, 0,
435
    /*  INS_$AB  */   0, 0,
436
    /*  INS_$AC  */   0, 0,
437
    /*  INS_$AD  */   0, 0,
438
    /*  INS_$AE  */   0, 0,
439
    /*  INS_$AF  */   0, 0,
440
441
    /*  PushB[0]  */  0, 1,
442
    /*  PushB[1]  */  0, 2,
443
    /*  PushB[2]  */  0, 3,
444
    /*  PushB[3]  */  0, 4,
445
    /*  PushB[4]  */  0, 5,
446
    /*  PushB[5]  */  0, 6,
447
    /*  PushB[6]  */  0, 7,
448
    /*  PushB[7]  */  0, 8,
449
    /*  PushW[0]  */  0, 1,
450
    /*  PushW[1]  */  0, 2,
451
    /*  PushW[2]  */  0, 3,
452
    /*  PushW[3]  */  0, 4,
453
    /*  PushW[4]  */  0, 5,
454
    /*  PushW[5]  */  0, 6,
455
    /*  PushW[6]  */  0, 7,
456
    /*  PushW[7]  */  0, 8,
457
458
    /*  MDRP[00]  */  1, 0,
459
    /*  MDRP[01]  */  1, 0,
460
    /*  MDRP[02]  */  1, 0,
461
    /*  MDRP[03]  */  1, 0,
462
    /*  MDRP[04]  */  1, 0,
463
    /*  MDRP[05]  */  1, 0,
464
    /*  MDRP[06]  */  1, 0,
465
    /*  MDRP[07]  */  1, 0,
466
    /*  MDRP[08]  */  1, 0,
467
    /*  MDRP[09]  */  1, 0,
468
    /*  MDRP[10]  */  1, 0,
469
    /*  MDRP[11]  */  1, 0,
470
    /*  MDRP[12]  */  1, 0,
471
    /*  MDRP[13]  */  1, 0,
472
    /*  MDRP[14]  */  1, 0,
473
    /*  MDRP[15]  */  1, 0,
474
475
    /*  MDRP[16]  */  1, 0,
476
    /*  MDRP[17]  */  1, 0,
477
    /*  MDRP[18]  */  1, 0,
478
    /*  MDRP[19]  */  1, 0,
479
    /*  MDRP[20]  */  1, 0,
480
    /*  MDRP[21]  */  1, 0,
481
    /*  MDRP[22]  */  1, 0,
482
    /*  MDRP[23]  */  1, 0,
483
    /*  MDRP[24]  */  1, 0,
484
    /*  MDRP[25]  */  1, 0,
485
    /*  MDRP[26]  */  1, 0,
486
    /*  MDRP[27]  */  1, 0,
487
    /*  MDRP[28]  */  1, 0,
488
    /*  MDRP[29]  */  1, 0,
489
    /*  MDRP[30]  */  1, 0,
490
    /*  MDRP[31]  */  1, 0,
491
492
    /*  MIRP[00]  */  2, 0,
493
    /*  MIRP[01]  */  2, 0,
494
    /*  MIRP[02]  */  2, 0,
495
    /*  MIRP[03]  */  2, 0,
496
    /*  MIRP[04]  */  2, 0,
497
    /*  MIRP[05]  */  2, 0,
498
    /*  MIRP[06]  */  2, 0,
499
    /*  MIRP[07]  */  2, 0,
500
    /*  MIRP[08]  */  2, 0,
501
    /*  MIRP[09]  */  2, 0,
502
    /*  MIRP[10]  */  2, 0,
503
    /*  MIRP[11]  */  2, 0,
504
    /*  MIRP[12]  */  2, 0,
505
    /*  MIRP[13]  */  2, 0,
506
    /*  MIRP[14]  */  2, 0,
507
    /*  MIRP[15]  */  2, 0,
508
509
    /*  MIRP[16]  */  2, 0,
510
    /*  MIRP[17]  */  2, 0,
511
    /*  MIRP[18]  */  2, 0,
512
    /*  MIRP[19]  */  2, 0,
513
    /*  MIRP[20]  */  2, 0,
514
    /*  MIRP[21]  */  2, 0,
515
    /*  MIRP[22]  */  2, 0,
516
    /*  MIRP[23]  */  2, 0,
517
    /*  MIRP[24]  */  2, 0,
518
    /*  MIRP[25]  */  2, 0,
519
    /*  MIRP[26]  */  2, 0,
520
    /*  MIRP[27]  */  2, 0,
521
    /*  MIRP[28]  */  2, 0,
522
    /*  MIRP[29]  */  2, 0,
523
    /*  MIRP[30]  */  2, 0,
524
    /*  MIRP[31]  */  2, 0
525
  };
526
527
/*******************************************************************
528
 *
529
 *  Function    :  Norm
530
 *
531
 *  Description :  Returns the norm (length) of a vector.
532
 *
533
 *  Input  :  X, Y   vector
534
 *
535
 *  Output :  Returns length in F26dot6.
536
 *
537
 *****************************************************************/
538
539
  static TT_F26Dot6  Norm( TT_F26Dot6  X, TT_F26Dot6  Y )
540
2.06k
  {
541
2.06k
    Int64       T1, T2;
542
543
2.06k
    MUL_64( X, X, T1 );
544
2.06k
    MUL_64( Y, Y, T2 );
545
546
2.06k
    ADD_64( T1, T2, T1 );
547
548
2.06k
    return (TT_F26Dot6)SQRT_64( T1 );
549
2.06k
  }
550
551
/*******************************************************************
552
 *
553
 *  Function    :  FUnits_To_Pixels
554
 *
555
 *  Description :  Scale a distance in FUnits to pixel coordinates.
556
 *
557
 *  Input  :  Distance in FUnits
558
 *
559
 *  Output :  Distance in 26.6 format.
560
 *
561
 *****************************************************************/
562
563
  static TT_F26Dot6  FUnits_To_Pixels( EXEC_OPS  Int  distance )
564
1.79k
  {
565
1.79k
    return MulDiv_Round( distance,
566
1.79k
                         CUR.metrics.scale1,
567
1.79k
                         CUR.metrics.scale2 );
568
1.79k
  }
569
570
/*******************************************************************
571
 *
572
 *  Function    :  Current_Ratio
573
 *
574
 *  Description :  Return the current aspect ratio scaling factor
575
 *                 depending on the projection vector's state and
576
 *                 device resolutions.
577
 *
578
 *  Input  :  None
579
 *
580
 *  Output :  Aspect ratio in 16.16 format, always <= 1.0 .
581
 *
582
 *****************************************************************/
583
584
  static Long  Current_Ratio( EXEC_OP )
585
  {
586
    if ( CUR.metrics.ratio )
587
      return CUR.metrics.ratio;
588
589
    if ( CUR.GS.projVector.y == 0 )
590
      CUR.metrics.ratio = CUR.metrics.x_ratio;
591
592
    else if ( CUR.GS.projVector.x == 0 )
593
      CUR.metrics.ratio = CUR.metrics.y_ratio;
594
595
    else
596
    {
597
      Long  x, y;
598
599
      x = MulDiv_Round( CUR.GS.projVector.x, CUR.metrics.x_ratio, 0x4000 );
600
      y = MulDiv_Round( CUR.GS.projVector.y, CUR.metrics.y_ratio, 0x4000 );
601
      CUR.metrics.ratio = Norm( x, y );
602
    }
603
604
    return CUR.metrics.ratio;
605
  }
606
607
  static Int  Current_Ppem( EXEC_OP )
608
  {
609
    return MulDiv_Round( CUR.metrics.ppem, CURRENT_Ratio(), 0x10000 );
610
  }
611
612
  static TT_F26Dot6  Read_CVT( EXEC_OPS Int  index )
613
  {
614
    return CUR.cvt[index];
615
  }
616
617
  static TT_F26Dot6  Read_CVT_Stretched( EXEC_OPS Int  index )
618
  {
619
    return MulDiv_Round( CUR.cvt[index], CURRENT_Ratio(), 0x10000 );
620
  }
621
622
  static void  Write_CVT( EXEC_OPS Int  index, TT_F26Dot6  value )
623
  {
624
    int ov=CUR.cvt[index];
625
    (void)ov; /* Quiet compiler warning in release build. */
626
    CUR.cvt[index] = value;
627
    DBG_PRINT3(" cvt[%d]%d:=%d", index, ov, CUR.cvt[index]);
628
}
629
630
  static void  Write_CVT_Stretched( EXEC_OPS Int  index, TT_F26Dot6  value )
631
  {
632
    int ov=CUR.cvt[index];
633
    (void)ov; /* Quiet compiler warning in release build. */
634
    CUR.cvt[index] = MulDiv_Round( value, 0x10000, CURRENT_Ratio() );
635
    DBG_PRINT3(" cvt[%d]%d:=%d", index, ov, CUR.cvt[index]);
636
  }
637
638
  static void  Move_CVT( EXEC_OPS  Int index, TT_F26Dot6 value )
639
  {
640
    int ov=CUR.cvt[index];
641
    (void)ov; /* Quiet compiler warning in release build. */
642
    CUR.cvt[index] += value;
643
    DBG_PRINT3(" cvt[%d]%d:=%d", index, ov, CUR.cvt[index]);
644
  }
645
646
  static void  Move_CVT_Stretched( EXEC_OPS  Int index, TT_F26Dot6  value )
647
  {
648
    int ov=CUR.cvt[index];
649
    (void)ov; /* Quiet compiler warning in release build. */
650
    CUR.cvt[index] += MulDiv_Round( value, 0x10000, CURRENT_Ratio() );
651
    DBG_PRINT3(" cvt[%d]%d:=%d", index, ov, CUR.cvt[index]);
652
  }
653
654
/******************************************************************
655
 *
656
 *  Function    :  Calc_Length
657
 *
658
 *  Description :  Computes the length in bytes of current opcode.
659
 *
660
 *****************************************************************/
661
662
  static Bool  Calc_Length( EXEC_OP )
663
13.8M
  {
664
13.8M
    CUR.opcode = CUR.code[CUR.IP];
665
666
13.8M
    switch ( CUR.opcode )
667
13.8M
    {
668
38.0k
    case 0x40:
669
38.0k
      if ( CUR.IP + 1 >= CUR.codeSize )
670
0
        return FAILURE;
671
672
38.0k
      CUR.length = CUR.code[CUR.IP + 1] + 2;
673
38.0k
      break;
674
675
22.7k
    case 0x41:
676
22.7k
      if ( CUR.IP + 1 >= CUR.codeSize )
677
0
        return FAILURE;
678
679
22.7k
      CUR.length = CUR.code[CUR.IP + 1] * 2 + 2;
680
22.7k
      break;
681
682
856k
    case 0xB0:
683
931k
    case 0xB1:
684
1.27M
    case 0xB2:
685
1.33M
    case 0xB3:
686
1.39M
    case 0xB4:
687
1.39M
    case 0xB5:
688
1.40M
    case 0xB6:
689
1.41M
    case 0xB7:
690
1.41M
      CUR.length = CUR.opcode - 0xB0 + 2;
691
1.41M
      break;
692
693
509k
    case 0xB8:
694
572k
    case 0xB9:
695
581k
    case 0xBA:
696
584k
    case 0xBB:
697
586k
    case 0xBC:
698
587k
    case 0xBD:
699
588k
    case 0xBE:
700
589k
    case 0xBF:
701
589k
      CUR.length = (CUR.opcode - 0xB8) * 2 + 3;
702
589k
      break;
703
704
11.8M
    default:
705
11.8M
      CUR.length = 1;
706
11.8M
      break;
707
13.8M
    }
708
709
    /* make sure result is in range */
710
711
13.8M
    if ( CUR.IP + CUR.length > CUR.codeSize )
712
11
      return FAILURE;
713
714
13.8M
    return SUCCESS;
715
13.8M
  }
716
717
/*******************************************************************
718
 *
719
 *  Function    :  GetShortIns
720
 *
721
 *  Description :  Returns a short integer taken from the instruction
722
 *                 stream at address IP.
723
 *
724
 *  Input  :  None
725
 *
726
 *  Output :  Short read at Code^[IP..IP+1]
727
 *
728
 *  Notes  :  This one could become a Macro in the C version.
729
 *
730
 *****************************************************************/
731
732
  static Short  GetShortIns( EXEC_OP )
733
  {
734
    /* Reading a byte stream so there is no endianess (DaveP) */
735
    CUR.IP += 2;
736
    return ( CUR.code[CUR.IP-2] << 8) +
737
             CUR.code[CUR.IP-1];
738
  }
739
740
/*******************************************************************
741
 *
742
 *  Function    :  Ins_Goto_CodeRange
743
 *
744
 *  Description :  Goes to a certain code range in the instruction
745
 *                 stream.
746
 *
747
 *
748
 *  Input  :  aRange
749
 *            aIP
750
 *
751
 *  Output :  SUCCESS or FAILURE.
752
 *
753
 *****************************************************************/
754
755
  static Bool  Ins_Goto_CodeRange( EXEC_OPS Int  aRange, Int  aIP )
756
  {
757
    TCodeRange*  WITH;
758
759
    if ( aRange < 1 || aRange > 3 )
760
    {
761
      CUR.error = TT_Err_Bad_Argument;
762
      return FAILURE;
763
    }
764
765
    WITH = &CUR.codeRangeTable[aRange - 1];
766
767
    if ( WITH->Base == NULL )     /* invalid coderange */
768
    {
769
      CUR.error = TT_Err_Invalid_CodeRange;
770
      return FAILURE;
771
    }
772
773
    /* NOTE: Because the last instruction of a program may be a CALL */
774
    /*       which will return to the first byte *after* the code    */
775
    /*       range, we test for AIP <= Size, instead of AIP < Size.  */
776
777
    if ( aIP > WITH->Size )
778
    {
779
      CUR.error = TT_Err_Code_Overflow;
780
      return FAILURE;
781
    }
782
783
    CUR.code     = WITH->Base;
784
    CUR.codeSize = WITH->Size;
785
    CUR.IP       = aIP;
786
    CUR.curRange = aRange;
787
788
    return SUCCESS;
789
  }
790
791
/*******************************************************************
792
 *
793
 *  Function    :  Direct_Move
794
 *
795
 *  Description :  Moves a point by a given distance along the
796
 *                 freedom vector.
797
 *
798
 *  Input  : Vx, Vy      point coordinates to move
799
 *           touch       touch flag to modify
800
 *           distance
801
 *
802
 *  Output :  None
803
 *
804
 *****************************************************************/
805
806
  static void  Direct_Move( EXEC_OPS PGlyph_Zone zone,
807
                                     Int         point,
808
                                     TT_F26Dot6  distance )
809
  {
810
    TT_F26Dot6 v;
811
812
    v = CUR.GS.freeVector.x;
813
814
    if ( v != 0 )
815
    {
816
      zone->cur_x[point] += MulDiv_Round( distance,
817
                                          v * 0x10000L,
818
                                          CUR.F_dot_P );
819
820
      zone->touch[point] |= TT_Flag_Touched_X;
821
    }
822
823
    v = CUR.GS.freeVector.y;
824
825
    if ( v != 0 )
826
    {
827
      zone->cur_y[point] += MulDiv_Round( distance,
828
                                          v * 0x10000L,
829
                                          CUR.F_dot_P );
830
831
      zone->touch[point] |= TT_Flag_Touched_Y;
832
    }
833
  }
834
835
/******************************************************************/
836
/*                                                                */
837
/* The following versions are used whenever both vectors are both */
838
/* along one of the coordinate unit vectors, i.e. in 90% cases.   */
839
/*                                                                */
840
/******************************************************************/
841
842
/*******************************************************************
843
 * Direct_Move_X
844
 *
845
 *******************************************************************/
846
847
  static void  Direct_Move_X( EXEC_OPS PGlyph_Zone  zone,
848
                                       Int         point,
849
                                       TT_F26Dot6  distance )
850
  { (void)exc;
851
    zone->cur_x[point] += distance;
852
    zone->touch[point] |= TT_Flag_Touched_X;
853
  }
854
855
/*******************************************************************
856
 * Direct_Move_Y
857
 *
858
 *******************************************************************/
859
860
  static void  Direct_Move_Y( EXEC_OPS PGlyph_Zone  zone,
861
                                       Int         point,
862
                                       TT_F26Dot6  distance )
863
  { (void)exc;
864
    zone->cur_y[point] += distance;
865
    zone->touch[point] |= TT_Flag_Touched_Y;
866
  }
867
868
/*******************************************************************
869
 *
870
 *  Function    :  Round_None
871
 *
872
 *  Description :  Does not round, but adds engine compensation.
873
 *
874
 *  Input  :  distance      : distance to round
875
 *            compensation  : engine compensation
876
 *
877
 *  Output :  rounded distance.
878
 *
879
 *  NOTE : The spec says very few about the relationship between
880
 *         rounding and engine compensation.  However, it seems
881
 *         from the description of super round that we should
882
 *         should add the compensation before rounding.
883
 *
884
 ******************************************************************/
885
886
  static TT_F26Dot6  Round_None( EXEC_OPS TT_F26Dot6  distance,
887
                                          TT_F26Dot6  compensation )
888
  {
889
    TT_F26Dot6  val;
890
    (void)exc;
891
892
    if ( distance >= 0 )
893
    {
894
      val = distance + compensation;
895
      if ( val < 0 )
896
        val = 0;
897
    }
898
    else {
899
      val = distance - compensation;
900
      if ( val > 0 )
901
        val = 0;
902
    }
903
904
    return val;
905
  }
906
907
/*******************************************************************
908
 *
909
 *  Function    :  Round_To_Grid
910
 *
911
 *  Description :  Rounds value to grid after adding engine
912
 *                 compensation
913
 *
914
 *  Input  :  distance      : distance to round
915
 *            compensation  : engine compensation
916
 *
917
 *  Output :  Rounded distance.
918
 *
919
 *****************************************************************/
920
921
  static TT_F26Dot6  Round_To_Grid( EXEC_OPS TT_F26Dot6  distance,
922
                                             TT_F26Dot6  compensation )
923
  {
924
    TT_F26Dot6  val;
925
    (void)exc;
926
927
    if ( distance >= 0 )
928
    {
929
      val = (distance + compensation + 32) & (-64);
930
      if ( val < 0 )
931
        val = 0;
932
    }
933
    else
934
    {
935
      val = -( (compensation - distance + 32) & (-64) );
936
      if ( val > 0 )
937
        val = 0;
938
    }
939
940
    return  val;
941
  }
942
943
/*******************************************************************
944
 *
945
 *  Function    :  Round_To_Half_Grid
946
 *
947
 *  Description :  Rounds value to half grid after adding engine
948
 *                 compensation.
949
 *
950
 *  Input  :  distance      : distance to round
951
 *            compensation  : engine compensation
952
 *
953
 *  Output :  Rounded distance.
954
 *
955
 *****************************************************************/
956
957
  static TT_F26Dot6  Round_To_Half_Grid( EXEC_OPS TT_F26Dot6  distance,
958
                                                  TT_F26Dot6  compensation )
959
  {
960
    TT_F26Dot6  val;
961
     (void)exc;
962
963
    if ( distance >= 0 )
964
    {
965
      val = ((distance + compensation) & (-64)) + 32;
966
      if ( val < 0 )
967
        val = 0;
968
    }
969
    else
970
    {
971
      val = -( ((compensation - distance) & (-64)) + 32 );
972
      if ( val > 0 )
973
        val = 0;
974
    }
975
976
    return val;
977
  }
978
979
/*******************************************************************
980
 *
981
 *  Function    :  Round_Down_To_Grid
982
 *
983
 *  Description :  Rounds value down to grid after adding engine
984
 *                 compensation.
985
 *
986
 *  Input  :  distance      : distance to round
987
 *            compensation  : engine compensation
988
 *
989
 *  Output :  Rounded distance.
990
 *
991
 *****************************************************************/
992
993
  static TT_F26Dot6  Round_Down_To_Grid( EXEC_OPS TT_F26Dot6  distance,
994
                                                  TT_F26Dot6  compensation )
995
  {
996
    TT_F26Dot6  val;
997
    (void)exc;
998
999
    if ( distance >= 0 )
1000
    {
1001
      val = (distance + compensation) & (-64);
1002
      if ( val < 0 )
1003
        val = 0;
1004
    }
1005
    else
1006
    {
1007
      val = -( (compensation - distance) & (-64) );
1008
      if ( val > 0 )
1009
        val = 0;
1010
    }
1011
1012
    return val;
1013
  }
1014
1015
/*******************************************************************
1016
 *
1017
 *  Function    :  Round_Up_To_Grid
1018
 *
1019
 *  Description :  Rounds value up to grid after adding engine
1020
 *                 compensation.
1021
 *
1022
 *  Input  :  distance      : distance to round
1023
 *            compensation  : engine compensation
1024
 *
1025
 *  Output :  Rounded distance.
1026
 *
1027
 *****************************************************************/
1028
1029
  static TT_F26Dot6  Round_Up_To_Grid( EXEC_OPS TT_F26Dot6  distance,
1030
                                                TT_F26Dot6  compensation )
1031
  {
1032
    TT_F26Dot6  val;
1033
    (void)exc;
1034
1035
    if ( distance >= 0 )
1036
    {
1037
      val = (distance + compensation + 63) & (-64);
1038
      if ( val < 0 )
1039
        val = 0;
1040
    }
1041
    else
1042
    {
1043
      val = -( (compensation - distance + 63) & (-64) );
1044
      if ( val > 0 )
1045
        val = 0;
1046
    }
1047
1048
    return val;
1049
  }
1050
1051
/*******************************************************************
1052
 *
1053
 *  Function    :  Round_To_Double_Grid
1054
 *
1055
 *  Description :  Rounds value to double grid after adding engine
1056
 *                 compensation.
1057
 *
1058
 *  Input  :  distance      : distance to round
1059
 *            compensation  : engine compensation
1060
 *
1061
 *  Output :  Rounded distance.
1062
 *
1063
 *****************************************************************/
1064
1065
  static TT_F26Dot6  Round_To_Double_Grid( EXEC_OPS TT_F26Dot6  distance,
1066
                                                    TT_F26Dot6  compensation )
1067
  {
1068
    TT_F26Dot6 val;
1069
    (void)exc;
1070
1071
    if ( distance >= 0 )
1072
    {
1073
      val = (distance + compensation + 16) & (-32);
1074
      if ( val < 0 )
1075
        val = 0;
1076
    }
1077
    else
1078
    {
1079
      val = -( (compensation - distance + 16) & (-32) );
1080
      if ( val > 0 )
1081
        val = 0;
1082
    }
1083
1084
    return val;
1085
  }
1086
1087
/*******************************************************************
1088
 *
1089
 *  Function    :  Round_Super
1090
 *
1091
 *  Description :  Super-rounds value to grid after adding engine
1092
 *                 compensation.
1093
 *
1094
 *  Input  :  distance      : distance to round
1095
 *            compensation  : engine compensation
1096
 *
1097
 *  Output :  Rounded distance.
1098
 *
1099
 *  NOTE : The spec says very few about the relationship between
1100
 *         rounding and engine compensation.  However, it seems
1101
 *         from the description of super round that we should
1102
 *         should add the compensation before rounding.
1103
 *
1104
 *****************************************************************/
1105
1106
  static TT_F26Dot6  Round_Super( EXEC_OPS TT_F26Dot6  distance,
1107
                                           TT_F26Dot6  compensation )
1108
  {
1109
    TT_F26Dot6  val;
1110
1111
    if ( distance >= 0 )
1112
    {
1113
      val = (distance - CUR.phase + CUR.threshold + compensation) &
1114
              (-CUR.period);
1115
      if ( val < 0 )
1116
        val = 0;
1117
      val += CUR.phase;
1118
    }
1119
    else
1120
    {
1121
      val = -( (CUR.threshold - CUR.phase - distance + compensation) &
1122
               (-CUR.period) );
1123
      if ( val > 0 )
1124
        val = 0;
1125
      val -= CUR.phase;
1126
    }
1127
1128
    return val;
1129
  }
1130
1131
/*******************************************************************
1132
 *
1133
 *  Function    :  Round_Super_45
1134
 *
1135
 *  Description :  Super-rounds value to grid after adding engine
1136
 *                 compensation.
1137
 *
1138
 *  Input  :  distance      : distance to round
1139
 *            compensation  : engine compensation
1140
 *
1141
 *  Output :  Rounded distance.
1142
 *
1143
 *  NOTE : There is a separate function for Round_Super_45 as we
1144
 *         may need a greater precision.
1145
 *
1146
 *****************************************************************/
1147
1148
  static TT_F26Dot6  Round_Super_45( EXEC_OPS TT_F26Dot6  distance,
1149
                                              TT_F26Dot6  compensation )
1150
  {
1151
    TT_F26Dot6  val;
1152
1153
    if ( distance >= 0 )
1154
    {
1155
      val = ( (distance - CUR.phase + CUR.threshold + compensation) /
1156
                CUR.period ) * CUR.period;
1157
      if ( val < 0 )
1158
        val = 0;
1159
      val += CUR.phase;
1160
    }
1161
    else
1162
    {
1163
      val = -( ( (CUR.threshold - CUR.phase - distance + compensation) /
1164
                   CUR.period ) * CUR.period );
1165
      if ( val > 0 )
1166
        val = 0;
1167
      val -= CUR.phase;
1168
    }
1169
1170
    return val;
1171
  }
1172
1173
/*******************************************************************
1174
 * Compute_Round
1175
 *
1176
 *****************************************************************/
1177
1178
  static void  Compute_Round( EXEC_OPS Byte  round_mode )
1179
  {
1180
    switch ( round_mode )
1181
    {
1182
    case TT_Round_Off:
1183
      CUR.func_round = (TRound_Function)Round_None;
1184
      break;
1185
1186
    case TT_Round_To_Grid:
1187
      CUR.func_round = (TRound_Function)Round_To_Grid;
1188
      break;
1189
1190
    case TT_Round_Up_To_Grid:
1191
      CUR.func_round = (TRound_Function)Round_Up_To_Grid;
1192
      break;
1193
1194
    case TT_Round_Down_To_Grid:
1195
      CUR.func_round = (TRound_Function)Round_Down_To_Grid;
1196
      break;
1197
1198
    case TT_Round_To_Half_Grid:
1199
      CUR.func_round = (TRound_Function)Round_To_Half_Grid;
1200
      break;
1201
1202
    case TT_Round_To_Double_Grid:
1203
      CUR.func_round = (TRound_Function)Round_To_Double_Grid;
1204
      break;
1205
1206
    case TT_Round_Super:
1207
      CUR.func_round = (TRound_Function)Round_Super;
1208
      break;
1209
1210
    case TT_Round_Super_45:
1211
      CUR.func_round = (TRound_Function)Round_Super_45;
1212
      break;
1213
    }
1214
  }
1215
1216
/*******************************************************************
1217
 *
1218
 *  Function    :  SetSuperRound
1219
 *
1220
 *  Description :  Sets Super Round parameters.
1221
 *
1222
 *  Input  :  GridPeriod   Grid period
1223
 *            OpCode       SROUND opcode
1224
 *
1225
 *  Output :  None.
1226
 *
1227
 *****************************************************************/
1228
1229
  static void  SetSuperRound( EXEC_OPS TT_F26Dot6  GridPeriod,
1230
                                       Long        selector )
1231
  {
1232
    switch ( selector & 0xC0 )
1233
    {
1234
      case 0:
1235
        CUR.period = GridPeriod / 2;
1236
        break;
1237
1238
      case 0x40:
1239
        CUR.period = GridPeriod;
1240
        break;
1241
1242
      case 0x80:
1243
        CUR.period = GridPeriod * 2;
1244
        break;
1245
1246
      /* This opcode is reserved, but... */
1247
1248
      case 0xC0:
1249
        CUR.period = GridPeriod;
1250
        break;
1251
    }
1252
1253
    switch ( selector & 0x30 )
1254
    {
1255
    case 0:
1256
      CUR.phase = 0;
1257
      break;
1258
1259
    case 0x10:
1260
      CUR.phase = CUR.period / 4;
1261
      break;
1262
1263
    case 0x20:
1264
      CUR.phase = CUR.period / 2;
1265
      break;
1266
1267
    case 0x30:
1268
      CUR.phase = GridPeriod * 3 / 4;
1269
      break;
1270
    }
1271
1272
    if ( (selector & 0x0F) == 0 )
1273
      CUR.threshold = CUR.period - 1;
1274
    else
1275
      CUR.threshold = ( (Int)(selector & 0x0F) - 4L ) * CUR.period / 8;
1276
1277
    CUR.period    /= 256;
1278
    CUR.phase     /= 256;
1279
    CUR.threshold /= 256;
1280
  }
1281
/*******************************************************************
1282
 *
1283
 *  Function    :  Project
1284
 *
1285
 *  Description :  Computes the projection of (Vx,Vy) along the
1286
 *                 current projection vector.
1287
 *
1288
 *  Input  :  Vx, Vy    input vector
1289
 *
1290
 *  Output :  Returns distance in F26dot6.
1291
 *
1292
 *****************************************************************/
1293
1294
OPTIMIZE_SETJMP
1295
  static TT_F26Dot6  Project( EXEC_OPS TT_F26Dot6  Vx, TT_F26Dot6  Vy )
1296
  {
1297
    THROW_PATENTED;
1298
    return 0;
1299
  }
1300
1301
/*******************************************************************
1302
 *
1303
 *  Function    :  Dual_Project
1304
 *
1305
 *  Description :  Computes the projection of (Vx,Vy) along the
1306
 *                 current dual vector.
1307
 *
1308
 *  Input  :  Vx, Vy    input vector
1309
 *
1310
 *  Output :  Returns distance in F26dot6.
1311
 *
1312
 *****************************************************************/
1313
1314
OPTIMIZE_SETJMP
1315
  static TT_F26Dot6  Dual_Project( EXEC_OPS TT_F26Dot6  Vx, TT_F26Dot6  Vy )
1316
  {
1317
    THROW_PATENTED;
1318
    return 0;
1319
  }
1320
1321
/*******************************************************************
1322
 *
1323
 *  Function    :  Free_Project
1324
 *
1325
 *  Description :  Computes the projection of (Vx,Vy) along the
1326
 *                 current freedom vector.
1327
 *
1328
 *  Input  :  Vx, Vy    input vector
1329
 *
1330
 *  Output :  Returns distance in F26dot6.
1331
 *
1332
 *****************************************************************/
1333
1334
OPTIMIZE_SETJMP
1335
  static TT_F26Dot6  Free_Project( EXEC_OPS TT_F26Dot6  Vx, TT_F26Dot6  Vy )
1336
0
  {
1337
0
    THROW_PATENTED;
1338
0
    return 0;
1339
0
  }
1340
1341
/*******************************************************************
1342
 *
1343
 *  Function    :  Project_x
1344
 *
1345
 *  Input  :  Vx, Vy    input vector
1346
 *
1347
 *  Output :  Returns Vx.
1348
 *
1349
 *  Note :    Used as a dummy function.
1350
 *
1351
 *****************************************************************/
1352
1353
  static TT_F26Dot6  Project_x( EXEC_OPS TT_F26Dot6  Vx, TT_F26Dot6  Vy )
1354
  { (void)exc; (void)Vy;
1355
    return Vx;
1356
  }
1357
1358
/*******************************************************************
1359
 *
1360
 *  Function    :  Project_y
1361
 *
1362
 *  Input  :  Vx, Vy    input vector
1363
 *
1364
 *  Output :  Returns Vy.
1365
 *
1366
 *  Note :    Used as a dummy function.
1367
 *
1368
 *****************************************************************/
1369
1370
  static TT_F26Dot6  Project_y( EXEC_OPS TT_F26Dot6  Vx, TT_F26Dot6  Vy )
1371
  { (void)exc; (void)Vx;
1372
    return Vy;
1373
  }
1374
1375
/*******************************************************************
1376
 *
1377
 *  Function    :  Compute_Funcs
1378
 *
1379
 *  Description :  Computes the projections and movement function
1380
 *                 pointers according to the current graphics state.
1381
 *
1382
 *  Input  :  None
1383
 *
1384
 *****************************************************************/
1385
1386
  static void  Compute_Funcs( EXEC_OP )
1387
  {
1388
    if ( CUR.GS.freeVector.x == 0x4000 )
1389
    {
1390
      CUR.func_freeProj = (TProject_Function)Project_x;
1391
      CUR.F_dot_P       = CUR.GS.projVector.x * 0x10000L;
1392
    }
1393
    else
1394
    {
1395
      if ( CUR.GS.freeVector.y == 0x4000 )
1396
      {
1397
        CUR.func_freeProj = (TProject_Function)Project_y;
1398
        CUR.F_dot_P       = CUR.GS.projVector.y * 0x10000L;
1399
      }
1400
      else
1401
      {
1402
        CUR.func_move     = (TMove_Function)Direct_Move;
1403
        CUR.func_freeProj = (TProject_Function)Free_Project;
1404
        CUR.F_dot_P = (Long)CUR.GS.projVector.x * CUR.GS.freeVector.x * 4 +
1405
                      (Long)CUR.GS.projVector.y * CUR.GS.freeVector.y * 4;
1406
      }
1407
    }
1408
1409
    CUR.cached_metrics = FALSE;
1410
1411
    if ( CUR.GS.projVector.x == 0x4000 )
1412
      CUR.func_project = (TProject_Function)Project_x;
1413
    else
1414
    {
1415
      if ( CUR.GS.projVector.y == 0x4000 )
1416
        CUR.func_project = (TProject_Function)Project_y;
1417
      else
1418
        CUR.func_project = (TProject_Function)Project;
1419
    }
1420
1421
    if ( CUR.GS.dualVector.x == 0x4000 )
1422
      CUR.func_dualproj = (TProject_Function)Project_x;
1423
    else
1424
    {
1425
      if ( CUR.GS.dualVector.y == 0x4000 )
1426
        CUR.func_dualproj = (TProject_Function)Project_y;
1427
      else
1428
        CUR.func_dualproj = (TProject_Function)Dual_Project;
1429
    }
1430
1431
    CUR.func_move = (TMove_Function)Direct_Move;
1432
1433
    if ( CUR.F_dot_P == 0x40000000L )
1434
    {
1435
      if ( CUR.GS.freeVector.x == 0x4000 )
1436
        CUR.func_move = (TMove_Function)Direct_Move_X;
1437
      else
1438
      {
1439
        if ( CUR.GS.freeVector.y == 0x4000 )
1440
          CUR.func_move = (TMove_Function)Direct_Move_Y;
1441
      }
1442
    }
1443
1444
    /* at small sizes, F_dot_P can become too small, resulting   */
1445
    /* in overflows and 'spikes' in a number of glyphs like 'w'. */
1446
1447
    if ( ABS( CUR.F_dot_P ) < 0x4000000L )
1448
      CUR.F_dot_P = 0x40000000L;
1449
1450
    /* Disable cached aspect ratio */
1451
    CUR.metrics.ratio = 0;
1452
  }
1453
1454
/*******************************************************************
1455
 *
1456
 *  Function    :  Normalize
1457
 *
1458
 *  Description :  Norms a vector
1459
 *
1460
 *  Input  :  Vx, Vy    input vector
1461
 *            R         unit vector
1462
 *
1463
 *  Output :  Returns FAILURE if a vector parameter is zero.
1464
 *
1465
 *****************************************************************/
1466
1467
  static Bool  Normalize( EXEC_OPS TT_F26Dot6      Vx,
1468
                                   TT_F26Dot6      Vy,
1469
                                   TT_UnitVector*  R )
1470
  {
1471
    TT_F26Dot6  W;
1472
    Bool        S1, S2;
1473
1474
    if ( ABS( Vx ) < 0x10000L && ABS( Vy ) < 0x10000L )
1475
    {
1476
      Vx *= 0x100;
1477
      Vy *= 0x100;
1478
1479
      W = Norm( Vx, Vy );
1480
1481
      if ( W == 0 )
1482
      {
1483
        /* XXX : Undocumented. It seems that it's possible to try  */
1484
        /*       to normalize the vector (0,0). Return immediately */
1485
        return SUCCESS;
1486
      }
1487
1488
      R->x = (TT_F2Dot14)MulDiv_Round( Vx, 0x4000L, W );
1489
      R->y = (TT_F2Dot14)MulDiv_Round( Vy, 0x4000L, W );
1490
1491
      return SUCCESS;
1492
    }
1493
1494
    W = Norm( Vx, Vy );
1495
1496
    if ( W <= 0 )
1497
    {
1498
      CUR.error = TT_Err_Divide_By_Zero;
1499
      return FAILURE;
1500
    }
1501
1502
    Vx = MulDiv_Round( Vx, 0x4000L, W );
1503
    Vy = MulDiv_Round( Vy, 0x4000L, W );
1504
1505
    W = Vx * Vx + Vy * Vy;
1506
1507
    /* Now, we want that Sqrt( W ) = 0x4000 */
1508
    /* Or 0x10000000 <= W < 0x10004000      */
1509
1510
    if ( Vx < 0 )
1511
    {
1512
      Vx = -Vx;
1513
      S1 = TRUE;
1514
    }
1515
    else
1516
      S1 = FALSE;
1517
1518
    if ( Vy < 0 )
1519
    {
1520
      Vy = -Vy;
1521
      S2 = TRUE;
1522
    }
1523
    else
1524
      S2 = FALSE;
1525
1526
    while ( W < 0x10000000L )
1527
    {
1528
      /* We need to increase W, by a minimal amount */
1529
      if ( Vx < Vy )
1530
        Vx++;
1531
      else
1532
        Vy++;
1533
1534
      W = Vx * Vx + Vy * Vy;
1535
    }
1536
1537
    while ( W >= 0x10004000L )
1538
    {
1539
      /* We need to decrease W, by a minimal amount */
1540
      if ( Vx < Vy )
1541
        Vx--;
1542
      else
1543
        Vy--;
1544
1545
      W = Vx * Vx + Vy * Vy;
1546
    }
1547
1548
    /* Note that in various cases, we can only  */
1549
    /* compute a Sqrt(W) of 0x3FFF, eg. Vx = Vy */
1550
1551
    if ( S1 )
1552
      Vx = -Vx;
1553
1554
    if ( S2 )
1555
      Vy = -Vy;
1556
1557
    R->x = (TT_F2Dot14)Vx;   /* Type conversion */
1558
    R->y = (TT_F2Dot14)Vy;   /* Type conversion */
1559
1560
    return SUCCESS;
1561
  }
1562
1563
/****************************************************************/
1564
/*                                                              */
1565
/* MANAGING THE STACK                                           */
1566
/*                                                              */
1567
/*  Instructions appear in the specs' order.                    */
1568
/*                                                              */
1569
/****************************************************************/
1570
1571
/*******************************************/
1572
/* DUP[]     : Duplicate top stack element */
1573
/* CodeRange : $20                         */
1574
1575
  static void  Ins_DUP( INS_ARG )
1576
  { (void)exc;
1577
    args[1] = args[0];
1578
  }
1579
1580
/*******************************************/
1581
/* POP[]     : POPs the stack's top elt.   */
1582
/* CodeRange : $21                         */
1583
1584
  static void  Ins_POP( INS_ARG )
1585
  { (void)exc; (void)args;
1586
    /* nothing to do */
1587
  }
1588
1589
/*******************************************/
1590
/* CLEAR[]   : Clear the entire stack      */
1591
/* CodeRange : $22                         */
1592
1593
  static void  Ins_CLEAR( INS_ARG )
1594
  { (void)args;
1595
    CUR.new_top = 0;
1596
  }
1597
1598
/*******************************************/
1599
/* SWAP[]    : Swap the top two elements   */
1600
/* CodeRange : $23                         */
1601
1602
  static void  Ins_SWAP( INS_ARG )
1603
  {
1604
    Long  L;
1605
    (void)exc;
1606
1607
    L       = args[0];
1608
    args[0] = args[1];
1609
    args[1] = L;
1610
  }
1611
1612
/*******************************************/
1613
/* DEPTH[]   : return the stack depth      */
1614
/* CodeRange : $24                         */
1615
1616
  static void  Ins_DEPTH( INS_ARG )
1617
  {
1618
    args[0] = CUR.top;
1619
  }
1620
1621
/*******************************************/
1622
/* CINDEX[]  : copy indexed element        */
1623
/* CodeRange : $25                         */
1624
1625
  static void  Ins_CINDEX( INS_ARG )
1626
  {
1627
    Long  L;
1628
1629
    L = args[0];
1630
1631
    if ( L<0 || L > CUR.args )
1632
      CUR.error = TT_Err_Invalid_Reference;
1633
    else
1634
      args[0] = CUR.stack[CUR.args - L];
1635
  }
1636
1637
/*******************************************/
1638
/* MINDEX[]  : move indexed element        */
1639
/* CodeRange : $26                         */
1640
1641
  static void  Ins_MINDEX( INS_ARG )
1642
  {
1643
    Long  L, K;
1644
1645
    L = args[0];
1646
1647
    if (L == 0)
1648
        return;
1649
1650
    if ( L<0 || L > CUR.args )
1651
    {
1652
      CUR.error = TT_Err_Invalid_Reference;
1653
      return;
1654
    }
1655
1656
    K = CUR.stack[CUR.args - L];
1657
1658
    memmove( (&CUR.stack[CUR.args - L    ]),
1659
              (&CUR.stack[CUR.args - L + 1]),
1660
              (L - 1) * sizeof ( Long ) );
1661
1662
    CUR.stack[ CUR.args-1 ] = K;
1663
  }
1664
1665
/*******************************************/
1666
/* ROLL[]    : roll top three elements     */
1667
/* CodeRange : $8A                         */
1668
1669
  static void  Ins_ROLL( INS_ARG )
1670
  {
1671
    Long  A, B, C;
1672
    (void)exc;
1673
1674
    A = args[2];
1675
    B = args[1];
1676
    C = args[0];
1677
1678
    args[2] = C;
1679
    args[1] = A;
1680
    args[0] = B;
1681
  }
1682
1683
/****************************************************************/
1684
/*                                                              */
1685
/* MANAGING THE FLOW OF CONTROL                                 */
1686
/*                                                              */
1687
/*  Instructions appear in the specs' order.                    */
1688
/*                                                              */
1689
/****************************************************************/
1690
1691
  static Bool  SkipCode( EXEC_OP )
1692
  {
1693
    CUR.IP += CUR.length;
1694
1695
    if ( CUR.IP < CUR.codeSize )
1696
      if ( CALC_Length() == SUCCESS )
1697
        return SUCCESS;
1698
1699
    CUR.error = TT_Err_Code_Overflow;
1700
    return FAILURE;
1701
  }
1702
1703
/*******************************************/
1704
/* IF[]      : IF test                     */
1705
/* CodeRange : $58                         */
1706
1707
  static void  Ins_IF( INS_ARG )
1708
  {
1709
    Int   nIfs;
1710
    Bool  Out;
1711
1712
    if ( args[0] != 0 )
1713
      return;
1714
1715
    nIfs = 1;
1716
    Out = 0;
1717
1718
    do
1719
    {
1720
      if ( SKIP_Code() == FAILURE )
1721
        return;
1722
1723
      switch ( CUR.opcode )
1724
      {
1725
      case 0x58:      /* IF */
1726
        nIfs++;
1727
        break;
1728
1729
      case 0x1b:      /* ELSE */
1730
        Out = (nIfs == 1);
1731
        break;
1732
1733
      case 0x59:      /* EIF */
1734
        nIfs--;
1735
        Out = (nIfs == 0);
1736
        break;
1737
      }
1738
    } while ( Out == 0 );
1739
  }
1740
1741
/*******************************************/
1742
/* ELSE[]    : ELSE                        */
1743
/* CodeRange : $1B                         */
1744
1745
  static void  Ins_ELSE( INS_ARG )
1746
  {
1747
    Int  nIfs;
1748
    (void)args;
1749
1750
    nIfs = 1;
1751
1752
    do
1753
    {
1754
      if ( SKIP_Code() == FAILURE )
1755
        return;
1756
1757
      switch ( CUR.opcode )
1758
      {
1759
      case 0x58:    /* IF */
1760
        nIfs++;
1761
        break;
1762
1763
      case 0x59:    /* EIF */
1764
        nIfs--;
1765
        break;
1766
      }
1767
    } while ( nIfs != 0 );
1768
  }
1769
1770
/*******************************************/
1771
/* EIF[]     : End IF                      */
1772
/* CodeRange : $59                         */
1773
1774
  static void  Ins_EIF( INS_ARG )
1775
  { (void)exc; (void)args;
1776
    /* nothing to do */
1777
  }
1778
1779
/*******************************************/
1780
/* JROT[]    : Jump Relative On True       */
1781
/* CodeRange : $78                         */
1782
1783
  static void  Ins_JROT( INS_ARG )
1784
  {
1785
    if ( args[1] != 0 )
1786
    {
1787
      CUR.IP      += (Int)(args[0]);
1788
      CUR.step_ins = FALSE;
1789
1790
      /* If the instruction pointer moves back past the start of the description
1791
       * assume there is a fatal error, and move to the end of the description instead.
1792
       */
1793
      if(CUR.IP < 0)
1794
          CUR.IP = CUR.codeSize;
1795
1796
      /* See JMPR below */
1797
      if(CUR.IP > CUR.codeSize ||
1798
         (CUR.code[CUR.IP] != 0x2D && CUR.code[CUR.IP - 1] == 0x2D))
1799
        CUR.IP -= 1;
1800
    }
1801
  }
1802
1803
/*******************************************/
1804
/* JMPR[]    : JuMP Relative               */
1805
/* CodeRange : $1C                         */
1806
1807
  static void  Ins_JMPR( INS_ARG )
1808
  {
1809
    if ( BOUNDS(CUR.IP + args[0], CUR.codeSize ) )
1810
    {
1811
      CUR.error = TT_Err_Invalid_Reference;
1812
      return;
1813
    }
1814
1815
    CUR.IP      += (Int)(args[0]);
1816
    CUR.step_ins = FALSE;
1817
1818
    /* If the instruction pointer moves back past the start of the description
1819
     * assume there is a fatal error, and move to the end of the description instead.
1820
     */
1821
    if(CUR.IP < 0)
1822
        CUR.IP = CUR.codeSize;
1823
1824
    if(CUR.IP > CUR.codeSize ||
1825
       (CUR.code[CUR.IP] != 0x2D && CUR.code[CUR.IP - 1] == 0x2D))
1826
    /* The JPMR is meant to stop at the ENDF instruction to finish
1827
     * the function. However the programmer made a mistake, and ended
1828
     * up one byte too far. I suspect that some TT interpreters handle this
1829
     * by detecting that the IP has gone off the end of the function. We can
1830
     * allow for simple cases here by just checking the preceding byte.
1831
     * Fonts with this problem are not uncommon.
1832
     */
1833
      CUR.IP -= 1;
1834
  }
1835
1836
/*******************************************/
1837
/* JROF[]    : Jump Relative On False      */
1838
/* CodeRange : $79                         */
1839
1840
  static void  Ins_JROF( INS_ARG )
1841
  {
1842
    if ( args[1] == 0 )
1843
    {
1844
      if ( BOUNDS(CUR.IP + args[0], CUR.codeSize ) )
1845
      {
1846
        CUR.error = TT_Err_Invalid_Reference;
1847
        return;
1848
      }
1849
1850
      CUR.IP      += (Int)(args[0]);
1851
      CUR.step_ins = FALSE;
1852
1853
      /* If the instruction pointer moves back past the start of the description
1854
       * assume there is a fatal error, and move to the end of the description instead.
1855
       */
1856
      if(CUR.IP < 0)
1857
          CUR.IP = CUR.codeSize;
1858
1859
      /* See JMPR above */
1860
      if(CUR.code[CUR.IP] != 0x2D && CUR.code[CUR.IP - 1] == 0x2D)
1861
        CUR.IP -= 1;
1862
    }
1863
  }
1864
1865
/****************************************************************/
1866
/*                                                              */
1867
/* LOGICAL FUNCTIONS                                            */
1868
/*                                                              */
1869
/*  Instructions appear in the specs' order.                    */
1870
/*                                                              */
1871
/****************************************************************/
1872
1873
/*******************************************/
1874
/* LT[]      : Less Than                   */
1875
/* CodeRange : $50                         */
1876
1877
  static void  Ins_LT( INS_ARG )
1878
  { (void)exc;
1879
    if ( args[0] < args[1] )
1880
      args[0] = 1;
1881
    else
1882
      args[0] = 0;
1883
  }
1884
1885
/*******************************************/
1886
/* LTEQ[]    : Less Than or EQual          */
1887
/* CodeRange : $51                         */
1888
1889
  static void  Ins_LTEQ( INS_ARG )
1890
  { (void)exc;
1891
    if ( args[0] <= args[1] )
1892
      args[0] = 1;
1893
    else
1894
      args[0] = 0;
1895
  }
1896
1897
/*******************************************/
1898
/* GT[]      : Greater Than                */
1899
/* CodeRange : $52                         */
1900
1901
  static void  Ins_GT( INS_ARG )
1902
  { (void)exc;
1903
    if ( args[0] > args[1] )
1904
      args[0] = 1;
1905
    else
1906
      args[0] = 0;
1907
  }
1908
1909
/*******************************************/
1910
/* GTEQ[]    : Greater Than or EQual       */
1911
/* CodeRange : $53                         */
1912
1913
  static void  Ins_GTEQ( INS_ARG )
1914
  { (void)exc;
1915
    if ( args[0] >= args[1] )
1916
      args[0] = 1;
1917
    else
1918
      args[0] = 0;
1919
  }
1920
1921
/*******************************************/
1922
/* EQ[]      : EQual                       */
1923
/* CodeRange : $54                         */
1924
1925
  static void  Ins_EQ( INS_ARG )
1926
  { (void)exc;
1927
    if ( args[0] == args[1] )
1928
      args[0] = 1;
1929
    else
1930
      args[0] = 0;
1931
  }
1932
1933
/*******************************************/
1934
/* NEQ[]     : Not EQual                   */
1935
/* CodeRange : $55                         */
1936
1937
  static void  Ins_NEQ( INS_ARG )
1938
  { (void)exc;
1939
    if ( args[0] != args[1] )
1940
      args[0] = 1;
1941
    else
1942
      args[0] = 0;
1943
  }
1944
1945
/*******************************************/
1946
/* ODD[]     : Odd                         */
1947
/* CodeRange : $56                         */
1948
1949
  static void  Ins_ODD( INS_ARG )
1950
  {
1951
    if ( (CUR_Func_round( args[0], 0L ) & 127) == 64 )
1952
      args[0] = 1;
1953
    else
1954
      args[0] = 0;
1955
  }
1956
1957
/*******************************************/
1958
/* EVEN[]    : Even                        */
1959
/* CodeRange : $57                         */
1960
1961
  static void  Ins_EVEN( INS_ARG )
1962
  {
1963
    if ( (CUR_Func_round( args[0], 0L ) & 127) == 0 )
1964
      args[0] = 1;
1965
    else
1966
      args[0] = 0;
1967
  }
1968
1969
/*******************************************/
1970
/* AND[]     : logical AND                 */
1971
/* CodeRange : $5A                         */
1972
1973
  static void  Ins_AND( INS_ARG )
1974
  { (void)exc;
1975
    if ( args[0] != 0 && args[1] != 0 )
1976
      args[0] = 1;
1977
    else
1978
      args[0] = 0;
1979
  }
1980
1981
/*******************************************/
1982
/* OR[]      : logical OR                  */
1983
/* CodeRange : $5B                         */
1984
1985
  static void  Ins_OR( INS_ARG )
1986
  { (void)exc;
1987
    if ( args[0] != 0 || args[1] != 0 )
1988
      args[0] = 1;
1989
    else
1990
      args[0] = 0;
1991
  }
1992
1993
/*******************************************/
1994
/* NOT[]     : logical NOT                 */
1995
/* CodeRange : $5C                         */
1996
1997
  static void  Ins_NOT( INS_ARG )
1998
  { (void)exc;
1999
    if ( args[0] != 0 )
2000
      args[0] = 0;
2001
    else
2002
      args[0] = 1;
2003
  }
2004
2005
/****************************************************************/
2006
/*                                                              */
2007
/* ARITHMETIC AND MATH INSTRUCTIONS                             */
2008
/*                                                              */
2009
/*  Instructions appear in the specs' order.                    */
2010
/*                                                              */
2011
/****************************************************************/
2012
2013
/*******************************************/
2014
/* ADD[]     : ADD                         */
2015
/* CodeRange : $60                         */
2016
2017
  static void  Ins_ADD( INS_ARG )
2018
  { (void)exc;
2019
    args[0] += args[1];
2020
  }
2021
2022
/*******************************************/
2023
/* SUB[]     : SUBstract                   */
2024
/* CodeRange : $61                         */
2025
2026
  static void  Ins_SUB( INS_ARG )
2027
  { (void)exc;
2028
    args[0] -= args[1];
2029
  }
2030
2031
/*******************************************/
2032
/* DIV[]     : DIVide                      */
2033
/* CodeRange : $62                         */
2034
2035
  static void  Ins_DIV( INS_ARG )
2036
  {
2037
    if ( args[1] == 0 )
2038
    {
2039
      CUR.error = TT_Err_Divide_By_Zero;
2040
      return;
2041
    }
2042
2043
    args[0] = MulDiv_Round( args[0], 64L, args[1] );
2044
    DBG_PRINT1(" %d", args[0]);
2045
  }
2046
2047
/*******************************************/
2048
/* MUL[]     : MULtiply                    */
2049
/* CodeRange : $63                         */
2050
2051
  static void  Ins_MUL( INS_ARG )
2052
  { (void)exc;
2053
    args[0] = MulDiv_Round( args[0], args[1], 64L );
2054
  }
2055
2056
/*******************************************/
2057
/* ABS[]     : ABSolute value              */
2058
/* CodeRange : $64                         */
2059
2060
  static void  Ins_ABS( INS_ARG )
2061
  { (void)exc;
2062
    args[0] = ABS( args[0] );
2063
  }
2064
2065
/*******************************************/
2066
/* NEG[]     : NEGate                      */
2067
/* CodeRange : $65                         */
2068
2069
  static void  Ins_NEG( INS_ARG )
2070
  { (void)exc;
2071
    args[0] = -args[0];
2072
  }
2073
2074
/*******************************************/
2075
/* FLOOR[]   : FLOOR                       */
2076
/* CodeRange : $66                         */
2077
2078
  static void  Ins_FLOOR( INS_ARG )
2079
  { (void)exc;
2080
    args[0] &= -64;
2081
  }
2082
2083
/*******************************************/
2084
/* CEILING[] : CEILING                     */
2085
/* CodeRange : $67                         */
2086
2087
  static void  Ins_CEILING( INS_ARG )
2088
  { (void)exc;
2089
    args[0] = (args[0] + 63) & (-64);
2090
  }
2091
2092
/*******************************************/
2093
/* MAX[]     : MAXimum                     */
2094
/* CodeRange : $68                         */
2095
2096
  static void  Ins_MAX( INS_ARG )
2097
  { (void)exc;
2098
    if ( args[1] > args[0] )
2099
      args[0] = args[1];
2100
  }
2101
2102
/*******************************************/
2103
/* MIN[]     : MINimum                     */
2104
/* CodeRange : $69                         */
2105
2106
  static void  Ins_MIN( INS_ARG )
2107
  { (void)exc;
2108
    if ( args[1] < args[0] )
2109
      args[0] = args[1];
2110
  }
2111
2112
/****************************************************************/
2113
/*                                                              */
2114
/* COMPENSATING FOR THE ENGINE CHARACTERISTICS                  */
2115
/*                                                              */
2116
/*  Instructions appear in the specs' order.                    */
2117
/*                                                              */
2118
/****************************************************************/
2119
2120
/*******************************************/
2121
/* ROUND[ab] : ROUND value                 */
2122
/* CodeRange : $68-$6B                     */
2123
2124
  static void  Ins_ROUND( INS_ARG )
2125
  {
2126
    args[0] = CUR_Func_round( args[0],
2127
                              CUR.metrics.compensations[CUR.opcode - 0x68] );
2128
  }
2129
2130
/*******************************************/
2131
/* NROUND[ab]: No ROUNDing of value        */
2132
/* CodeRange : $6C-$6F                     */
2133
2134
  static void  Ins_NROUND( INS_ARG )
2135
  {
2136
    args[0] = Round_None( EXEC_ARGS
2137
                          args[0],
2138
                          CUR.metrics.compensations[CUR.opcode - 0x6C] );
2139
  }
2140
2141
/****************************************************************/
2142
/*                                                              */
2143
/* DEFINING AND USING FUNCTIONS AND INSTRUCTIONS                */
2144
/*                                                              */
2145
/*  Instructions appear in the specs' order.                    */
2146
/*                                                              */
2147
/****************************************************************/
2148
2149
/* Skip the whole function definition. */
2150
  static void skip_FDEF( EXEC_OP )
2151
136k
  {
2152
    /* We don't allow nested IDEFS & FDEFs.    */
2153
2154
2.69M
    while ( SKIP_Code() == SUCCESS )
2155
2.69M
    {
2156
2.69M
      switch ( CUR.opcode )
2157
2.69M
      {
2158
0
      case 0x89:    /* IDEF */
2159
6
      case 0x2c:    /* FDEF */
2160
6
        CUR.error = TT_Err_Nested_DEFS;
2161
6
        return;
2162
2163
136k
      case 0x2d:   /* ENDF */
2164
136k
        return;
2165
2.69M
      }
2166
2.69M
    }
2167
136k
  }
2168
2169
/*******************************************/
2170
/* FDEF[]    : Function DEFinition         */
2171
/* CodeRange : $2C                         */
2172
2173
  static void  Ins_FDEF( INS_ARG )
2174
  {
2175
    PDefRecord  pRec;
2176
2177
    if ( BOUNDS( args[0], CUR.numFDefs ) )
2178
    {
2179
      CUR.error = TT_Err_Invalid_Reference;
2180
      return;
2181
    }
2182
2183
    pRec = &CUR.FDefs[args[0]];
2184
2185
    pRec->Range  = CUR.curRange;
2186
    pRec->Opc    = (Byte)(args[0]);
2187
    pRec->Start  = CUR.IP + 1;
2188
    pRec->Active = TRUE;
2189
2190
    skip_FDEF(EXEC_ARG);
2191
  }
2192
2193
/*******************************************/
2194
/* ENDF[]    : END Function definition     */
2195
/* CodeRange : $2D                         */
2196
2197
  static void  Ins_ENDF( INS_ARG )
2198
  {
2199
    PCallRecord  pRec;
2200
     (void)args;
2201
2202
    if ( CUR.callTop <= 0 )     /* We encountered an ENDF without a call */
2203
    {
2204
      CUR.error = TT_Err_ENDF_In_Exec_Stream;
2205
      return;
2206
    }
2207
2208
    CUR.callTop--;
2209
2210
    pRec = &CUR.callStack[CUR.callTop];
2211
2212
    pRec->Cur_Count--;
2213
2214
    CUR.step_ins = FALSE;
2215
2216
    if ( pRec->Cur_Count > 0 )
2217
    {
2218
      CUR.callTop++;
2219
      CUR.IP = pRec->Cur_Restart;
2220
    }
2221
    else
2222
      /* Loop through the current function */
2223
      INS_Goto_CodeRange( pRec->Caller_Range,
2224
                          pRec->Caller_IP );
2225
2226
    /* Exit the current call frame.                       */
2227
2228
    /* NOTE: When the last intruction of a program        */
2229
    /*       is a CALL or LOOPCALL, the return address    */
2230
    /*       is always out of the code range.  This is    */
2231
    /*       a valid address, and it's why we do not test */
2232
    /*       the result of Ins_Goto_CodeRange() here!     */
2233
  }
2234
2235
/*******************************************/
2236
/* CALL[]    : CALL function               */
2237
/* CodeRange : $2B                         */
2238
2239
  static void  Ins_CALL( INS_ARG )
2240
  {
2241
    PCallRecord  pCrec;
2242
2243
    if ( BOUNDS( args[0], CUR.numFDefs ) || !CUR.FDefs[args[0]].Active )
2244
    {
2245
      CUR.error = TT_Err_Invalid_Reference;
2246
      return;
2247
    }
2248
2249
    if ( CUR.callTop >= CUR.callSize )
2250
    {
2251
      CUR.error = TT_Err_Stack_Overflow;
2252
      return;
2253
    }
2254
2255
    DBG_PRINT1("%d", args[0]);
2256
2257
    pCrec = &CUR.callStack[CUR.callTop];
2258
2259
    pCrec->Caller_Range = CUR.curRange;
2260
    pCrec->Caller_IP    = CUR.IP + 1;
2261
    pCrec->Cur_Count    = 1;
2262
    pCrec->Cur_Restart  = CUR.FDefs[args[0]].Start;
2263
2264
    CUR.callTop++;
2265
2266
    INS_Goto_CodeRange( CUR.FDefs[args[0]].Range,
2267
                        CUR.FDefs[args[0]].Start );
2268
2269
    CUR.step_ins = FALSE;
2270
  }
2271
2272
/*******************************************/
2273
/* LOOPCALL[]: LOOP and CALL function      */
2274
/* CodeRange : $2A                         */
2275
2276
  static void  Ins_LOOPCALL( INS_ARG )
2277
  {
2278
    PCallRecord  pTCR;
2279
2280
    if ( BOUNDS( args[1], CUR.numFDefs ) || !CUR.FDefs[args[1]].Active )
2281
    {
2282
      CUR.error = TT_Err_Invalid_Reference;
2283
      return;
2284
    }
2285
2286
    if ( CUR.callTop >= CUR.callSize )
2287
    {
2288
      CUR.error = TT_Err_Stack_Overflow;
2289
      return;
2290
    }
2291
2292
    if ( args[0] > 0 )
2293
    {
2294
      pTCR = &CUR.callStack[CUR.callTop];
2295
2296
      pTCR->Caller_Range = CUR.curRange;
2297
      pTCR->Caller_IP    = CUR.IP + 1;
2298
      pTCR->Cur_Count    = (Int)(args[0]);
2299
      pTCR->Cur_Restart  = CUR.FDefs[args[1]].Start;
2300
2301
      CUR.callTop++;
2302
2303
      INS_Goto_CodeRange( CUR.FDefs[args[1]].Range,
2304
                          CUR.FDefs[args[1]].Start );
2305
2306
      CUR.step_ins = FALSE;
2307
    }
2308
  }
2309
2310
/*******************************************/
2311
/* IDEF[]    : Instruction DEFinition      */
2312
/* CodeRange : $89                         */
2313
2314
  static void Ins_IDEF( INS_ARG )
2315
  {
2316
    if (CUR.countIDefs >= CUR.numIDefs || args[0] > 255)
2317
        CUR.error = TT_Err_Storage_Overflow;
2318
    else
2319
      {
2320
        PDefRecord  pTDR;
2321
2322
        CUR.IDefPtr[(Byte)(args[0])] = CUR.countIDefs;
2323
        pTDR = &CUR.IDefs[CUR.countIDefs++];
2324
        pTDR->Opc    = (Byte)(args[0]);
2325
        pTDR->Start  = CUR.IP + 1;
2326
        pTDR->Range  = CUR.curRange;
2327
        pTDR->Active = TRUE;
2328
        skip_FDEF(EXEC_ARG);
2329
      }
2330
  }
2331
2332
/****************************************************************/
2333
/*                                                              */
2334
/* PUSHING DATA ONTO THE INTERPRETER STACK                      */
2335
/*                                                              */
2336
/*  Instructions appear in the specs' order.                    */
2337
/*                                                              */
2338
/****************************************************************/
2339
2340
/*******************************************/
2341
/* NPUSHB[]  : PUSH N Bytes                */
2342
/* CodeRange : $40                         */
2343
2344
  static void  Ins_NPUSHB( INS_ARG )
2345
  {
2346
    Int  L, K;
2347
2348
    L = (Int)CUR.code[CUR.IP + 1];
2349
2350
    if ( BOUNDS( L, CUR.stackSize+1-CUR.top )
2351
      || BOUNDS( L, CUR.codeSize+1-CUR.IP))
2352
    {
2353
      CUR.error = TT_Err_Stack_Overflow;
2354
      return;
2355
    }
2356
2357
    for ( K = 1; K <= L; K++ )
2358
      { args[K - 1] = CUR.code[CUR.IP + K + 1];
2359
        DBG_PRINT1(" %d", args[K - 1]);
2360
      }
2361
2362
    CUR.new_top += L;
2363
  }
2364
2365
/*******************************************/
2366
/* NPUSHW[]  : PUSH N Words                */
2367
/* CodeRange : $41                         */
2368
2369
  static void  Ins_NPUSHW( INS_ARG )
2370
  {
2371
    Int  L, K;
2372
2373
    L = (Int)CUR.code[CUR.IP + 1];
2374
2375
    /* GET_ShortIns() reads two values from the execution stream */
2376
    if ( BOUNDS( L, CUR.stackSize+1-CUR.top )
2377
      || BOUNDS( L * 2, CUR.codeSize+1-CUR.IP))
2378
    {
2379
      CUR.error = TT_Err_Stack_Overflow;
2380
      return;
2381
    }
2382
2383
    CUR.IP += 2;
2384
2385
    for ( K = 0; K < L; K++ )
2386
      { args[K] = GET_ShortIns();
2387
        DBG_PRINT1(" %d", args[K]);
2388
      }
2389
2390
    CUR.step_ins = FALSE;
2391
    CUR.new_top += L;
2392
  }
2393
2394
/*******************************************/
2395
/* PUSHB[abc]: PUSH Bytes                  */
2396
/* CodeRange : $B0-$B7                     */
2397
2398
  static void  Ins_PUSHB( INS_ARG )
2399
  {
2400
    Int  L, K;
2401
2402
    L = ((Int)CUR.opcode - 0xB0 + 1);
2403
2404
    if ( BOUNDS( L, CUR.stackSize+1-CUR.top )
2405
      || BOUNDS( CUR.IP + L, CUR.codeSize ))
2406
    {
2407
      CUR.error = TT_Err_Stack_Overflow;
2408
      return;
2409
    }
2410
2411
    for ( K = 1; K <= L; K++ )
2412
      { args[K - 1] = CUR.code[CUR.IP + K];
2413
        DBG_PRINT1(" %d", args[K - 1]);
2414
      }
2415
  }
2416
2417
/*******************************************/
2418
/* PUSHW[abc]: PUSH Words                  */
2419
/* CodeRange : $B8-$BF                     */
2420
2421
  static void  Ins_PUSHW( INS_ARG )
2422
  {
2423
    Int  L, K;
2424
2425
    L = CUR.opcode - 0xB8 + 1;
2426
2427
    if ( BOUNDS( L, CUR.stackSize+1-CUR.top )
2428
      || BOUNDS( CUR.IP + (L * 2), CUR.codeSize ))
2429
    {
2430
      CUR.error = TT_Err_Stack_Overflow;
2431
      return;
2432
    }
2433
2434
    CUR.IP++;
2435
2436
    for ( K = 0; K < L; K++ )
2437
      { args[K] = GET_ShortIns();
2438
        DBG_PRINT1(" %d", args[K]);
2439
      }
2440
2441
    CUR.step_ins = FALSE;
2442
  }
2443
2444
/****************************************************************/
2445
/*                                                              */
2446
/* MANAGING THE STORAGE AREA                                    */
2447
/*                                                              */
2448
/*  Instructions appear in the specs' order.                    */
2449
/*                                                              */
2450
/****************************************************************/
2451
2452
/*******************************************/
2453
/* RS[]      : Read Store                  */
2454
/* CodeRange : $43                         */
2455
2456
  static void  Ins_RS( INS_ARG )
2457
  {
2458
    if ( BOUNDS( args[0], CUR.storeSize ) )
2459
    {
2460
      CUR.error = TT_Err_Invalid_Reference;
2461
      return;
2462
    }
2463
2464
    args[0] = CUR.storage[args[0]];
2465
  }
2466
2467
/*******************************************/
2468
/* WS[]      : Write Store                 */
2469
/* CodeRange : $42                         */
2470
2471
  static void  Ins_WS( INS_ARG )
2472
  {
2473
    if ( BOUNDS( args[0], CUR.storeSize ) )
2474
    {
2475
      CUR.error = TT_Err_Invalid_Reference;
2476
      return;
2477
    }
2478
2479
    CUR.storage[args[0]] = args[1];
2480
  }
2481
2482
/*******************************************/
2483
/* WCVTP[]   : Write CVT in Pixel units    */
2484
/* CodeRange : $44                         */
2485
2486
  static void  Ins_WCVTP( INS_ARG )
2487
  {
2488
    if ( BOUNDS( args[0], CUR.cvtSize ) )
2489
    {
2490
      CUR.error = TT_Err_Invalid_Reference;
2491
      return;
2492
    }
2493
2494
    CUR_Func_write_cvt( args[0], args[1] );
2495
  }
2496
2497
/*******************************************/
2498
/* WCVTF[]   : Write CVT in FUnits         */
2499
/* CodeRange : $70                         */
2500
2501
  static void  Ins_WCVTF( INS_ARG )
2502
  {
2503
#ifdef DEBUG
2504
    int ov;
2505
#endif
2506
2507
    if ( BOUNDS( args[0], CUR.cvtSize ) )
2508
    {
2509
      CUR.error = TT_Err_Invalid_Reference;
2510
      return;
2511
    }
2512
2513
#ifdef DEBUG
2514
    ov = CUR.cvt[args[0]];
2515
#endif
2516
    CUR.cvt[args[0]] = FUnits_To_Pixels( EXEC_ARGS args[1] );
2517
#ifdef DEBUG
2518
    DBG_PRINT3(" cvt[%d]%d:=%d", args[0], ov, CUR.cvt[args[0]]);
2519
#endif
2520
  }
2521
2522
/*******************************************/
2523
/* RCVT[]    : Read CVT                    */
2524
/* CodeRange : $45                         */
2525
2526
  static void  Ins_RCVT( INS_ARG )
2527
  {
2528
    int index;
2529
    if ( BOUNDS( args[0], CUR.cvtSize ) )
2530
    {
2531
      CUR.error = TT_Err_Invalid_Reference;
2532
      return;
2533
    }
2534
    index=args[0];
2535
    args[0] = CUR_Func_read_cvt( index );
2536
    DBG_PRINT3(" cvt[%d]%d:%d", index, CUR.cvt[index], args[0]);
2537
  }
2538
2539
/****************************************************************/
2540
/*                                                              */
2541
/* MANAGING THE GRAPHICS STATE                                  */
2542
/*                                                              */
2543
/*  Instructions appear in the specs' order.                    */
2544
/*                                                              */
2545
/****************************************************************/
2546
2547
/*******************************************/
2548
/* SVTCA[a]  : Set F and P vectors to axis */
2549
/* CodeRange : $00-$01                     */
2550
2551
  static void  Ins_SVTCA( INS_ARG )
2552
62.4k
  {
2553
62.4k
    Short  A, B;
2554
62.4k
    (void)args;
2555
2556
62.4k
    if ( CUR.opcode & 1 )
2557
21.8k
        A = 0x4000;
2558
40.6k
    else
2559
40.6k
        A = 0;
2560
2561
62.4k
    B = A ^ 0x4000;
2562
2563
62.4k
    CUR.GS.freeVector.x = A;
2564
62.4k
    CUR.GS.projVector.x = A;
2565
62.4k
    CUR.GS.dualVector.x = A;
2566
2567
62.4k
    CUR.GS.freeVector.y = B;
2568
62.4k
    CUR.GS.projVector.y = B;
2569
62.4k
    CUR.GS.dualVector.y = B;
2570
2571
62.4k
    COMPUTE_Funcs();
2572
62.4k
  }
2573
2574
/*******************************************/
2575
/* SPVTCA[a] : Set PVector to Axis         */
2576
/* CodeRange : $02-$03                     */
2577
2578
  static void  Ins_SPVTCA( INS_ARG )
2579
10.8k
  {
2580
10.8k
    Short  A, B;
2581
10.8k
    (void)args;
2582
10.8k
    if ( CUR.opcode & 1 )
2583
6.75k
      A = 0x4000;
2584
4.10k
    else
2585
4.10k
      A = 0;
2586
2587
10.8k
    B = A ^ 0x4000;
2588
2589
10.8k
    CUR.GS.projVector.x = A;
2590
10.8k
    CUR.GS.dualVector.x = A;
2591
2592
10.8k
    CUR.GS.projVector.y = B;
2593
10.8k
    CUR.GS.dualVector.y = B;
2594
2595
10.8k
    COMPUTE_Funcs();
2596
10.8k
  }
2597
2598
/*******************************************/
2599
/* SFVTCA[a] : Set FVector to Axis         */
2600
/* CodeRange : $04-$05                     */
2601
2602
  static void  Ins_SFVTCA( INS_ARG )
2603
417
  {
2604
417
    Short  A, B;
2605
417
    (void)args;
2606
2607
417
    if ( CUR.opcode & 1 )
2608
315
      A = 0x4000;
2609
102
    else
2610
102
      A = 0;
2611
2612
417
    B = A ^ 0x4000;
2613
2614
417
    CUR.GS.freeVector.x = A;
2615
417
    CUR.GS.freeVector.y = B;
2616
2617
417
    COMPUTE_Funcs();
2618
417
  }
2619
2620
  static Bool  Ins_SxVTL( EXEC_OPS  Int             aIdx1,
2621
                                    Int             aIdx2,
2622
                                    Int             aOpc,
2623
                                    TT_UnitVector*  Vec )
2624
  {
2625
    Long  A, B, C;
2626
2627
    if ( BOUNDS( aIdx1, CUR.zp2.n_points ) ||
2628
         BOUNDS( aIdx2, CUR.zp1.n_points ) )
2629
    {
2630
      CUR.error = TT_Err_Invalid_Reference;
2631
      return FAILURE;
2632
    }
2633
2634
    A = CUR.zp1.cur_x[aIdx2] - CUR.zp2.cur_x[aIdx1];
2635
    B = CUR.zp1.cur_y[aIdx2] - CUR.zp2.cur_y[aIdx1];
2636
2637
    if ( (aOpc & 1) != 0 )
2638
    {
2639
      C =  B;   /* CounterClockwise rotation */
2640
      B =  A;
2641
      A = -C;
2642
    }
2643
2644
    if ( NORMalize( A, B, Vec ) == FAILURE )
2645
    {
2646
      /* When the vector is too small or zero! */
2647
2648
      CUR.error = TT_Err_Ok;
2649
      Vec->x = 0x4000;
2650
      Vec->y = 0;
2651
    }
2652
2653
    return SUCCESS;
2654
  }
2655
2656
/*******************************************/
2657
/* SPVTL[a]  : Set PVector to Line         */
2658
/* CodeRange : $06-$07                     */
2659
2660
  static void  Ins_SPVTL( INS_ARG )
2661
  {
2662
    if ( INS_SxVTL( args[1],
2663
                    args[0],
2664
                    CUR.opcode,
2665
                    &CUR.GS.projVector) == FAILURE )
2666
      return;
2667
2668
    CUR.GS.dualVector = CUR.GS.projVector;
2669
    COMPUTE_Funcs();
2670
  }
2671
2672
/*******************************************/
2673
/* SFVTL[a]  : Set FVector to Line         */
2674
/* CodeRange : $08-$09                     */
2675
2676
  static void  Ins_SFVTL( INS_ARG )
2677
  {
2678
    if ( INS_SxVTL( (Int)(args[1]),
2679
                    (Int)(args[0]),
2680
                    CUR.opcode,
2681
                    &CUR.GS.freeVector) == FAILURE )
2682
      return;
2683
2684
    COMPUTE_Funcs();
2685
  }
2686
2687
/*******************************************/
2688
/* SFVTPV[]  : Set FVector to PVector      */
2689
/* CodeRange : $0E                         */
2690
2691
  static void  Ins_SFVTPV( INS_ARG )
2692
  { (void)args;
2693
    CUR.GS.freeVector = CUR.GS.projVector;
2694
    COMPUTE_Funcs();
2695
  }
2696
2697
/*******************************************/
2698
/* SDPVTL[a] : Set Dual PVector to Line    */
2699
/* CodeRange : $86-$87                     */
2700
2701
  static void  Ins_SDPVTL( INS_ARG )
2702
  {
2703
    Long  A, B, C;
2704
    Long  p1, p2;   /* was Int in pas type ERROR */
2705
2706
    p1 = args[1];
2707
    p2 = args[0];
2708
2709
    if ( BOUNDS( p2, CUR.zp1.n_points ) ||
2710
         BOUNDS( p1, CUR.zp2.n_points ) )
2711
    {
2712
      CUR.error = TT_Err_Invalid_Reference;
2713
      return;
2714
    }
2715
2716
    A = CUR.zp1.org_x[p2] - CUR.zp2.org_x[p1];
2717
    B = CUR.zp1.org_y[p2] - CUR.zp2.org_y[p1];
2718
2719
    if ( (CUR.opcode & 1) != 0 )
2720
    {
2721
      C =  B;   /* CounterClockwise rotation */
2722
      B =  A;
2723
      A = -C;
2724
    }
2725
2726
    if ( NORMalize( A, B, &CUR.GS.dualVector ) == FAILURE )
2727
      return;
2728
2729
    A = CUR.zp1.cur_x[p2] - CUR.zp2.cur_x[p1];
2730
    B = CUR.zp1.cur_y[p2] - CUR.zp2.cur_y[p1];
2731
2732
    if ( (CUR.opcode & 1) != 0 )
2733
    {
2734
      C =  B;   /* CounterClockwise rotation */
2735
      B =  A;
2736
      A = -C;
2737
    }
2738
2739
    if ( NORMalize( A, B, &CUR.GS.projVector ) == FAILURE )
2740
      return;
2741
2742
    COMPUTE_Funcs();
2743
  }
2744
2745
/*******************************************/
2746
/* SPVFS[]   : Set PVector From Stack      */
2747
/* CodeRange : $0A                         */
2748
2749
  static void  Ins_SPVFS( INS_ARG )
2750
  {
2751
    Short  S;
2752
    Long   X, Y;
2753
2754
    /* Only use low 16bits, then sign extend */
2755
    S = (Short)args[1];
2756
    Y = (Long)S;
2757
    S = (Short)args[0];
2758
    X = (Long)S;
2759
2760
    if ( NORMalize( X, Y, &CUR.GS.projVector ) == FAILURE )
2761
      return;
2762
2763
    CUR.GS.dualVector = CUR.GS.projVector;
2764
2765
    COMPUTE_Funcs();
2766
  }
2767
2768
/*******************************************/
2769
/* SFVFS[]   : Set FVector From Stack      */
2770
/* CodeRange : $0B                         */
2771
2772
  static void  Ins_SFVFS( INS_ARG )
2773
  {
2774
    Short  S;
2775
    Long   X, Y;
2776
2777
    /* Only use low 16bits, then sign extend */
2778
    S = (Short)args[1];
2779
    Y = (Long)S;
2780
    S = (Short)args[0];
2781
    X = S;
2782
2783
    if ( NORMalize( X, Y, &CUR.GS.freeVector ) == FAILURE )
2784
      return;
2785
2786
    COMPUTE_Funcs();
2787
  }
2788
2789
/*******************************************/
2790
/* GPV[]     : Get Projection Vector       */
2791
/* CodeRange : $0C                         */
2792
2793
  static void  Ins_GPV( INS_ARG )
2794
  {
2795
    args[0] = CUR.GS.projVector.x;
2796
    args[1] = CUR.GS.projVector.y;
2797
  }
2798
2799
/*******************************************/
2800
/* GFV[]     : Get Freedom Vector          */
2801
/* CodeRange : $0D                         */
2802
2803
  static void  Ins_GFV( INS_ARG )
2804
  {
2805
    args[0] = CUR.GS.freeVector.x;
2806
    args[1] = CUR.GS.freeVector.y;
2807
  }
2808
2809
/*******************************************/
2810
/* SRP0[]    : Set Reference Point 0       */
2811
/* CodeRange : $10                         */
2812
2813
  static void  Ins_SRP0( INS_ARG )
2814
  {
2815
    CUR.GS.rp0 = (Int)(args[0]);
2816
  }
2817
2818
/*******************************************/
2819
/* SRP1[]    : Set Reference Point 1       */
2820
/* CodeRange : $11                         */
2821
2822
  static void  Ins_SRP1( INS_ARG )
2823
  {
2824
    CUR.GS.rp1 = (Int)(args[0]);
2825
  }
2826
2827
/*******************************************/
2828
/* SRP2[]    : Set Reference Point 2       */
2829
/* CodeRange : $12                         */
2830
2831
  static void  Ins_SRP2( INS_ARG )
2832
  {
2833
    CUR.GS.rp2 = (Int)(args[0]);
2834
  }
2835
2836
/*******************************************/
2837
/* SZP0[]    : Set Zone Pointer 0          */
2838
/* CodeRange : $13                         */
2839
2840
  static void  Ins_SZP0( INS_ARG )
2841
  {
2842
    switch ( args[0] )
2843
    {
2844
    case 0:
2845
      CUR.zp0 = CUR.twilight;
2846
      break;
2847
2848
    case 1:
2849
      CUR.zp0 = CUR.pts;
2850
      break;
2851
2852
    default:
2853
      CUR.error = TT_Err_Invalid_Reference;
2854
      return;
2855
      break;
2856
    }
2857
2858
    CUR.GS.gep0 = (Int)(args[0]);
2859
  }
2860
2861
/*******************************************/
2862
/* SZP1[]    : Set Zone Pointer 1          */
2863
/* CodeRange : $14                         */
2864
2865
  static void  Ins_SZP1( INS_ARG )
2866
  {
2867
    switch ( args[0] )
2868
    {
2869
    case 0:
2870
      CUR.zp1 = CUR.twilight;
2871
      break;
2872
2873
    case 1:
2874
      CUR.zp1 = CUR.pts;
2875
      break;
2876
2877
    default:
2878
      CUR.error = TT_Err_Invalid_Reference;
2879
      return;
2880
    }
2881
2882
    CUR.GS.gep1 = (Int)(args[0]);
2883
  }
2884
2885
/*******************************************/
2886
/* SZP2[]    : Set Zone Pointer 2          */
2887
/* CodeRange : $15                         */
2888
2889
  static void  Ins_SZP2( INS_ARG )
2890
  {
2891
    switch ( args[0] )
2892
    {
2893
    case 0:
2894
      CUR.zp2 = CUR.twilight;
2895
      break;
2896
2897
    case 1:
2898
      CUR.zp2 = CUR.pts;
2899
      break;
2900
2901
    default:
2902
      CUR.error = TT_Err_Invalid_Reference;
2903
      return;
2904
    }
2905
2906
    CUR.GS.gep2 = (Int)(args[0]);
2907
  }
2908
2909
/*******************************************/
2910
/* SZPS[]    : Set Zone Pointers           */
2911
/* CodeRange : $16                         */
2912
2913
  static void  Ins_SZPS( INS_ARG )
2914
  {
2915
    switch ( args[0] )
2916
    {
2917
    case 0:
2918
      CUR.zp0 = CUR.twilight;
2919
      break;
2920
2921
    case 1:
2922
      CUR.zp0 = CUR.pts;
2923
      break;
2924
2925
    default:
2926
      CUR.error = TT_Err_Invalid_Reference;
2927
      return;
2928
    }
2929
2930
    CUR.zp1 = CUR.zp0;
2931
    CUR.zp2 = CUR.zp0;
2932
2933
    CUR.GS.gep0 = (Int)(args[0]);
2934
    CUR.GS.gep1 = (Int)(args[0]);
2935
    CUR.GS.gep2 = (Int)(args[0]);
2936
  }
2937
2938
/*******************************************/
2939
/* RTHG[]    : Round To Half Grid          */
2940
/* CodeRange : $19                         */
2941
2942
  static void  Ins_RTHG( INS_ARG )
2943
  { (void)args;
2944
    CUR.GS.round_state = TT_Round_To_Half_Grid;
2945
2946
    CUR.func_round = (TRound_Function)Round_To_Half_Grid;
2947
  }
2948
2949
/*******************************************/
2950
/* RTG[]     : Round To Grid               */
2951
/* CodeRange : $18                         */
2952
2953
  static void  Ins_RTG( INS_ARG )
2954
  { (void)args;
2955
    CUR.GS.round_state = TT_Round_To_Grid;
2956
2957
    CUR.func_round = (TRound_Function)Round_To_Grid;
2958
  }
2959
2960
/*******************************************/
2961
/* RTDG[]    : Round To Double Grid        */
2962
/* CodeRange : $3D                         */
2963
2964
  static void  Ins_RTDG( INS_ARG )
2965
  { (void)args;
2966
    CUR.GS.round_state = TT_Round_To_Double_Grid;
2967
2968
    CUR.func_round = (TRound_Function)Round_To_Double_Grid;
2969
  }
2970
2971
/*******************************************/
2972
/* RUTG[]    : Round Up To Grid            */
2973
/* CodeRange : $7C                         */
2974
2975
  static void  Ins_RUTG( INS_ARG )
2976
  { (void)args;
2977
    CUR.GS.round_state = TT_Round_Up_To_Grid;
2978
2979
    CUR.func_round = (TRound_Function)Round_Up_To_Grid;
2980
  }
2981
2982
/*******************************************/
2983
/* RDTG[]    : Round Down To Grid          */
2984
/* CodeRange : $7D                         */
2985
2986
  static void  Ins_RDTG( INS_ARG )
2987
  { (void)args;
2988
    CUR.GS.round_state = TT_Round_Down_To_Grid;
2989
2990
    CUR.func_round = (TRound_Function)Round_Down_To_Grid;
2991
  }
2992
2993
/*******************************************/
2994
/* ROFF[]    : Round OFF                   */
2995
/* CodeRange : $7A                         */
2996
2997
  static void  Ins_ROFF( INS_ARG )
2998
  { (void)args;
2999
    CUR.GS.round_state = TT_Round_Off;
3000
3001
    CUR.func_round = (TRound_Function)Round_None;
3002
  }
3003
3004
/*******************************************/
3005
/* SROUND[]  : Super ROUND                 */
3006
/* CodeRange : $76                         */
3007
3008
  static void  Ins_SROUND( INS_ARG )
3009
  {
3010
    SET_SuperRound( 0x4000L, args[0] );
3011
    CUR.GS.round_state = TT_Round_Super;
3012
3013
    CUR.func_round = (TRound_Function)Round_Super;
3014
  }
3015
3016
/*******************************************/
3017
/* S45ROUND[]: Super ROUND 45 degrees      */
3018
/* CodeRange : $77                         */
3019
3020
  static void  Ins_S45ROUND( INS_ARG )
3021
  {
3022
    SET_SuperRound( 0x2D41L, args[0] );
3023
    CUR.GS.round_state = TT_Round_Super_45;
3024
3025
    CUR.func_round = (TRound_Function)Round_Super_45;
3026
  }
3027
3028
/*******************************************/
3029
/* SLOOP[]   : Set LOOP variable           */
3030
/* CodeRange : $17                         */
3031
3032
  static void  Ins_SLOOP( INS_ARG )
3033
  {
3034
    CUR.GS.loop = args[0];
3035
  }
3036
3037
/*******************************************/
3038
/* SMD[]     : Set Minimum Distance        */
3039
/* CodeRange : $1A                         */
3040
3041
  static void  Ins_SMD( INS_ARG )
3042
  {
3043
    CUR.GS.minimum_distance = args[0];
3044
  }
3045
3046
/*******************************************/
3047
/* INSTCTRL[]: INSTruction ConTRol         */
3048
/* CodeRange : $8e                         */
3049
3050
  static void  Ins_INSTCTRL( INS_ARG )
3051
  {
3052
    Long  K, L;
3053
3054
    K = args[1];
3055
    L = args[0];
3056
3057
    if ( K < 0 || K > 3 )
3058
    {
3059
      CUR.error = TT_Err_Invalid_Reference;
3060
      return;
3061
    }
3062
3063
    CUR.GS.instruct_control = (Int)((CUR.GS.instruct_control & (~K)) | (L & K));
3064
  }
3065
3066
/*******************************************/
3067
/* SCANCTRL[]: SCAN ConTRol                */
3068
/* CodeRange : $85                         */
3069
3070
  static void  Ins_SCANCTRL( INS_ARG )
3071
  {
3072
    Int  A;
3073
3074
    /* Get Threshold */
3075
    A = (Int)(args[0] & 0xFF);
3076
3077
    if ( A == 0xFF )
3078
    {
3079
      CUR.GS.scan_control = TRUE;
3080
      return;
3081
    }
3082
    else if ( A == 0 )
3083
    {
3084
      CUR.GS.scan_control = FALSE;
3085
      return;
3086
    }
3087
3088
    A *= 64;
3089
3090
    if ( (args[0] & 0x100) != 0 && CUR.metrics.pointSize <= A )
3091
      CUR.GS.scan_control = TRUE;
3092
3093
    if ( (args[0] & 0x200) != 0 && CUR.metrics.rotated )
3094
      CUR.GS.scan_control = TRUE;
3095
3096
    if ( (args[0] & 0x400) != 0 && CUR.metrics.stretched )
3097
      CUR.GS.scan_control = TRUE;
3098
3099
    if ( (args[0] & 0x800) != 0 && CUR.metrics.pointSize > A )
3100
      CUR.GS.scan_control = FALSE;
3101
3102
    if ( (args[0] & 0x1000) != 0 && CUR.metrics.rotated )
3103
      CUR.GS.scan_control = FALSE;
3104
3105
    if ( (args[0] & 0x2000) != 0 && CUR.metrics.stretched )
3106
      CUR.GS.scan_control = FALSE;
3107
}
3108
3109
/*******************************************/
3110
/* SCANTYPE[]: SCAN TYPE                   */
3111
/* CodeRange : $8D                         */
3112
3113
  static void  Ins_SCANTYPE( INS_ARG )
3114
  {
3115
    /* For compatibility with future enhancements, */
3116
    /* we must ignore new modes                    */
3117
3118
    if ( args[0] >= 0 && args[0] <= 5 )
3119
    {
3120
      if ( args[0] == 3 )
3121
        args[0] = 2;
3122
3123
      CUR.GS.scan_type = (Int)args[0];
3124
    }
3125
  }
3126
3127
/**********************************************/
3128
/* SCVTCI[]  : Set Control Value Table Cut In */
3129
/* CodeRange : $1D                            */
3130
3131
  static void  Ins_SCVTCI( INS_ARG )
3132
  {
3133
    CUR.GS.control_value_cutin = (TT_F26Dot6)args[0];
3134
  }
3135
3136
/**********************************************/
3137
/* SSWCI[]   : Set Single Width Cut In        */
3138
/* CodeRange : $1E                            */
3139
3140
  static void  Ins_SSWCI( INS_ARG )
3141
  {
3142
    CUR.GS.single_width_cutin = (TT_F26Dot6)args[0];
3143
  }
3144
3145
/**********************************************/
3146
/* SSW[]     : Set Single Width               */
3147
/* CodeRange : $1F                            */
3148
3149
  static void  Ins_SSW( INS_ARG )
3150
  {
3151
    /* XXX : Undocumented or bug in the Windows engine ?  */
3152
    /*                                                    */
3153
    /* It seems that the value that is read here is       */
3154
    /* expressed in 16.16 format, rather than in          */
3155
    /* font units..                                       */
3156
    /*                                                    */
3157
    CUR.GS.single_width_value = (TT_F26Dot6)(args[0] >> 10);
3158
  }
3159
3160
/**********************************************/
3161
/* FLIPON[]  : Set Auto_flip to On            */
3162
/* CodeRange : $4D                            */
3163
3164
  static void  Ins_FLIPON( INS_ARG )
3165
  { (void)args;
3166
    CUR.GS.auto_flip = TRUE;
3167
  }
3168
3169
/**********************************************/
3170
/* FLIPOFF[] : Set Auto_flip to Off           */
3171
/* CodeRange : $4E                            */
3172
3173
  static void  Ins_FLIPOFF( INS_ARG )
3174
  { (void)args;
3175
    CUR.GS.auto_flip = FALSE;
3176
  }
3177
3178
/**********************************************/
3179
/* SANGW[]   : Set Angle Weight               */
3180
/* CodeRange : $7E                            */
3181
3182
  static void  Ins_SANGW( INS_ARG )
3183
  { (void)exc; (void)args;
3184
    /* instruction not supported anymore */
3185
  }
3186
3187
/**********************************************/
3188
/* SDB[]     : Set Delta Base                 */
3189
/* CodeRange : $5E                            */
3190
3191
  static void  Ins_SDB( INS_ARG )
3192
  {
3193
    CUR.GS.delta_base = (Int)args[0];
3194
  }
3195
3196
/**********************************************/
3197
/* SDS[]     : Set Delta Shift                */
3198
/* CodeRange : $5F                            */
3199
3200
  static void  Ins_SDS( INS_ARG )
3201
  {
3202
    CUR.GS.delta_shift = (Int)args[0];
3203
  }
3204
3205
/**********************************************/
3206
/* GC[a]     : Get Coordinate projected onto  */
3207
/* CodeRange : $46-$47                        */
3208
3209
/* BULLSHIT: Measures from the original glyph must to be taken */
3210
/*           along the dual projection vector!                 */
3211
3212
  static void  Ins_GC( INS_ARG )
3213
  {
3214
    Long  L;
3215
3216
    L = args[0];
3217
3218
    if ( BOUNDS( L, CUR.zp2.n_points ) )
3219
    {
3220
      CUR.error = TT_Err_Invalid_Reference;
3221
      return;
3222
    }
3223
3224
    switch ( CUR.opcode & 1 )
3225
    {
3226
    case 0:
3227
      L = CUR_Func_project( CUR.zp2.cur_x[L],
3228
                            CUR.zp2.cur_y[L] );
3229
      break;
3230
3231
    case 1:
3232
      L = CUR_Func_dualproj( CUR.zp2.org_x[L],
3233
                             CUR.zp2.org_y[L] );
3234
      break;
3235
    }
3236
3237
    args[0] = L;
3238
  }
3239
3240
/**********************************************/
3241
/* SCFS[]    : Set Coordinate From Stack      */
3242
/* CodeRange : $48                            */
3243
/*                                            */
3244
/* Formula:                                   */
3245
/*                                            */
3246
/*   OA := OA + ( value - OA.p )/( f.p ) * f  */
3247
/*                                            */
3248
3249
  static void  Ins_SCFS( INS_ARG )
3250
  {
3251
    Long  K;
3252
    Int   L;
3253
3254
    L = (Int)args[0];
3255
3256
    if ( BOUNDS( args[0], CUR.zp2.n_points ) )
3257
    {
3258
      CUR.error = TT_Err_Invalid_Reference;
3259
      return;
3260
    }
3261
3262
    K = CUR_Func_project( CUR.zp2.cur_x[L],
3263
                          CUR.zp2.cur_y[L] );
3264
3265
    CUR_Func_move( &CUR.zp2, L, args[1] - K );
3266
3267
    /* not part of the specs, but here for safety */
3268
3269
    if ( CUR.GS.gep2 == 0 )
3270
    {
3271
      CUR.zp2.org_x[L] = CUR.zp2.cur_x[L];
3272
      CUR.zp2.org_y[L] = CUR.zp2.cur_y[L];
3273
    }
3274
  }
3275
3276
/**********************************************/
3277
/* MD[a]     : Measure Distance               */
3278
/* CodeRange : $49-$4A                        */
3279
3280
/* BULLSHIT: Measure taken in the original glyph must be along */
3281
/*           the dual projection vector.                       */
3282
3283
/* Second BULLSHIT: Flag attributes are inverted!                */
3284
/*                  0 => measure distance in original outline    */
3285
/*                  1 => measure distance in grid-fitted outline */
3286
3287
  static void  Ins_MD( INS_ARG )
3288
  {
3289
    Long       K, L;
3290
    TT_F26Dot6 D;
3291
3292
    K = args[1];
3293
    L = args[0];
3294
3295
    if( BOUNDS( args[0], CUR.zp2.n_points ) ||
3296
        BOUNDS( args[1], CUR.zp1.n_points ) )
3297
    {
3298
      CUR.error = TT_Err_Invalid_Reference;
3299
      return;
3300
    }
3301
3302
    if ( CUR.opcode & 1 )
3303
      D = CUR_Func_project( CUR.zp2.cur_x[L] - CUR.zp1.cur_x[K],
3304
                            CUR.zp2.cur_y[L] - CUR.zp1.cur_y[K] );
3305
    else
3306
      D = CUR_Func_dualproj( CUR.zp2.org_x[L] - CUR.zp1.org_x[K],
3307
                             CUR.zp2.org_y[L] - CUR.zp1.org_y[K] );
3308
3309
    args[0] = D;
3310
  }
3311
3312
/**********************************************/
3313
/* MPPEM[]   : Measure Pixel Per EM           */
3314
/* CodeRange : $4B                            */
3315
3316
  static void  Ins_MPPEM( INS_ARG )
3317
  {
3318
    args[0] = CURRENT_Ppem();
3319
    DBG_PRINT1(" %d", args[0]);
3320
  }
3321
3322
/**********************************************/
3323
/* MPS[]     : Measure PointSize              */
3324
/* CodeRange : $4C                            */
3325
3326
  static void  Ins_MPS( INS_ARG )
3327
  {
3328
    args[0] = CUR.metrics.pointSize;
3329
  }
3330
3331
/****************************************************************/
3332
/*                                                              */
3333
/* MANAGING OUTLINES                                            */
3334
/*                                                              */
3335
/*  Instructions appear in the specs' order.                    */
3336
/*                                                              */
3337
/****************************************************************/
3338
3339
/**********************************************/
3340
/* FLIPPT[]  : FLIP PoinT                     */
3341
/* CodeRange : $80                            */
3342
3343
  static void  Ins_FLIPPT( INS_ARG )
3344
  {
3345
    Long  point;
3346
    (void)args;
3347
3348
    if ( CUR.top < CUR.GS.loop )
3349
    {
3350
      CUR.error = TT_Err_Too_Few_Arguments;
3351
      return;
3352
    }
3353
3354
    while ( CUR.GS.loop > 0 )
3355
    {
3356
      CUR.args--;
3357
3358
      point = CUR.stack[CUR.args];
3359
3360
      if ( BOUNDS( point, CUR.pts.n_points ) )
3361
      {
3362
        CUR.error = TT_Err_Invalid_Reference;
3363
       return;
3364
      }
3365
3366
      CUR.pts.touch[point] ^= TT_Flag_On_Curve;
3367
3368
      CUR.GS.loop--;
3369
    }
3370
3371
    CUR.GS.loop = 1;
3372
    CUR.new_top = CUR.args;
3373
  }
3374
3375
/**********************************************/
3376
/* FLIPRGON[]: FLIP RanGe ON                  */
3377
/* CodeRange : $81                            */
3378
3379
  static void  Ins_FLIPRGON( INS_ARG )
3380
  {
3381
    Long  I, K, L;
3382
3383
    K = args[1];
3384
    L = args[0];
3385
3386
    if ( BOUNDS( K, CUR.pts.n_points ) ||
3387
         BOUNDS( L, CUR.pts.n_points ) )
3388
    {
3389
      CUR.error = TT_Err_Invalid_Reference;
3390
      return;
3391
    }
3392
3393
    for ( I = L; I <= K; I++ )
3394
      CUR.pts.touch[I] |= TT_Flag_On_Curve;
3395
  }
3396
3397
/**********************************************/
3398
/* FLIPRGOFF : FLIP RanGe OFF                 */
3399
/* CodeRange : $82                            */
3400
3401
  static void  Ins_FLIPRGOFF( INS_ARG )
3402
  {
3403
    Long  I, K, L;
3404
3405
    K = args[1];
3406
    L = args[0];
3407
3408
    if ( BOUNDS( K, CUR.pts.n_points ) ||
3409
         BOUNDS( L, CUR.pts.n_points ) )
3410
    {
3411
      CUR.error = TT_Err_Invalid_Reference;
3412
      return;
3413
    }
3414
3415
    for ( I = L; I <= K; I++ )
3416
      CUR.pts.touch[I] &= ~TT_Flag_On_Curve;
3417
}
3418
3419
  static Bool  Compute_Point_Displacement( EXEC_OPS
3420
                                           PCoordinates  x,
3421
                                           PCoordinates  y,
3422
                                           PGlyph_Zone   zone,
3423
                                           Int*          refp )
3424
  {
3425
    TGlyph_Zone  zp;
3426
    Int          p;
3427
    TT_F26Dot6   d;
3428
3429
    if ( CUR.opcode & 1 )
3430
    {
3431
      zp = CUR.zp0;
3432
      p  = CUR.GS.rp1;
3433
    }
3434
    else
3435
    {
3436
      zp = CUR.zp1;
3437
      p  = CUR.GS.rp2;
3438
    }
3439
3440
    if ( BOUNDS( p, zp.n_points ) )
3441
    {
3442
      /* Don't set error code, just return. */
3443
      /* Ported from Freetype2 */
3444
      *refp = 0;
3445
      return FAILURE;
3446
    }
3447
3448
    *zone = zp;
3449
    *refp = p;
3450
3451
    d = CUR_Func_project( zp.cur_x[p] - zp.org_x[p],
3452
                          zp.cur_y[p] - zp.org_y[p] );
3453
3454
    *x = MulDiv_Round(d, (Long)CUR.GS.freeVector.x * 0x10000L, CUR.F_dot_P );
3455
    *y = MulDiv_Round(d, (Long)CUR.GS.freeVector.y * 0x10000L, CUR.F_dot_P );
3456
3457
    return SUCCESS;
3458
  }
3459
3460
  static void  Move_Zp2_Point( EXEC_OPS
3461
                               Long        point,
3462
                               TT_F26Dot6  dx,
3463
                               TT_F26Dot6  dy,
3464
                               Bool        touch )
3465
  {
3466
    if (point >= CUR.n_points)
3467
    {
3468
      CUR.error = TT_Err_Invalid_Reference;
3469
    }
3470
    else
3471
    {
3472
      if ( CUR.GS.freeVector.x != 0 )
3473
      {
3474
        CUR.zp2.cur_x[point] += dx;
3475
        if ( touch )
3476
          CUR.zp2.touch[point] |= TT_Flag_Touched_X;
3477
      }
3478
3479
      if ( CUR.GS.freeVector.y != 0 )
3480
      {
3481
        CUR.zp2.cur_y[point] += dy;
3482
        if ( touch )
3483
          CUR.zp2.touch[point] |= TT_Flag_Touched_Y;
3484
      }
3485
    }
3486
  }
3487
3488
/**********************************************/
3489
/* SHP[a]    : SHift Point by the last point  */
3490
/* CodeRange : $32-33                         */
3491
3492
  static void  Ins_SHP( INS_ARG )
3493
  {
3494
    TGlyph_Zone zp;
3495
    Int         refp;
3496
3497
    TT_F26Dot6  dx,
3498
                dy;
3499
    Long        point;
3500
    (void)args;
3501
3502
    if ( CUR.top < CUR.GS.loop )
3503
    {
3504
      CUR.error = TT_Err_Invalid_Reference;
3505
      return;
3506
    }
3507
3508
    if ( COMPUTE_Point_Displacement( &dx, &dy, &zp, &refp ) )
3509
      return;
3510
3511
    while ( CUR.GS.loop > 0 )
3512
    {
3513
      CUR.args--;
3514
      point = CUR.stack[CUR.args];
3515
3516
      if ( BOUNDS( point, CUR.zp2.n_points ) )
3517
      {
3518
        CUR.error = TT_Err_Invalid_Reference;
3519
        return;
3520
      }
3521
3522
      /* undocumented: SHP touches the points */
3523
      MOVE_Zp2_Point( point, dx, dy, TRUE );
3524
3525
      CUR.GS.loop--;
3526
    }
3527
3528
    CUR.GS.loop = 1;
3529
    CUR.new_top = CUR.args;
3530
  }
3531
3532
/**********************************************/
3533
/* SHC[a]    : SHift Contour                  */
3534
/* CodeRange : $34-35                         */
3535
3536
  static void  Ins_SHC( INS_ARG )
3537
  {
3538
    TGlyph_Zone zp;
3539
    Int         refp;
3540
    TT_F26Dot6  dx,
3541
                dy;
3542
3543
    Long        contour, i;
3544
    Int         first_point, last_point;
3545
3546
    contour = args[0];
3547
3548
    if ( BOUNDS( args[0], CUR.pts.n_contours ) )
3549
    {
3550
#if 0
3551
      /* A workaround for the Ghostscript bug 688501.
3552
       *  Ported from FreeType 2
3553
       */
3554
      CUR.error = TT_Err_Invalid_Reference;
3555
#endif
3556
      return;
3557
    }
3558
3559
    if ( COMPUTE_Point_Displacement( &dx, &dy, &zp, &refp ) )
3560
      return;
3561
3562
    if ( contour == 0 )
3563
      first_point = 0;
3564
    else
3565
      first_point = CUR.pts.contours[contour-1] + 1;
3566
3567
    last_point = CUR.pts.contours[contour];
3568
3569
    /* undocumented: SHC doesn't touch the points */
3570
    for ( i = first_point; i <= last_point; i++ )
3571
    {
3572
      if ( zp.cur_x != CUR.zp2.cur_x || refp != i )
3573
        MOVE_Zp2_Point( i, dx, dy, FALSE );
3574
    }
3575
  }
3576
3577
/**********************************************/
3578
/* SHZ[a]    : SHift Zone                     */
3579
/* CodeRange : $36-37                         */
3580
3581
  static void  Ins_SHZ( INS_ARG )
3582
  {
3583
    TGlyph_Zone zp;
3584
    Int         refp;
3585
    TT_F26Dot6  dx,
3586
                dy;
3587
3588
    Int  last_point;
3589
    Long i;
3590
3591
    if ( BOUNDS( args[0], 2 ) )
3592
    {
3593
      CUR.error = TT_Err_Invalid_Reference;
3594
      return;
3595
    }
3596
3597
    if ( COMPUTE_Point_Displacement( &dx, &dy, &zp, &refp ) )
3598
      return;
3599
3600
    last_point = zp.n_points - 1;
3601
3602
    /* undocumented: SHZ doesn't touch the points */
3603
    for ( i = 0; i <= last_point; i++ )
3604
    {
3605
      if ( zp.cur_x != CUR.zp2.cur_x || refp != i )
3606
        MOVE_Zp2_Point( i, dx, dy, FALSE );
3607
    }
3608
  }
3609
3610
/**********************************************/
3611
/* SHPIX[]   : SHift points by a PIXel amount */
3612
/* CodeRange : $38                            */
3613
3614
  static void  Ins_SHPIX( INS_ARG )
3615
  {
3616
    TT_F26Dot6  dx, dy;
3617
    Long        point;
3618
3619
    if ( CUR.top < CUR.GS.loop )
3620
    {
3621
      CUR.error = TT_Err_Invalid_Reference;
3622
      return;
3623
    }
3624
3625
    dx = MulDiv_Round( args[0],
3626
                       (Long)CUR.GS.freeVector.x,
3627
                       0x4000 );
3628
    dy = MulDiv_Round( args[0],
3629
                       (Long)CUR.GS.freeVector.y,
3630
                       0x4000 );
3631
3632
    while ( CUR.GS.loop > 0 )
3633
    {
3634
      CUR.args--;
3635
3636
      point = CUR.stack[CUR.args];
3637
3638
      if ( BOUNDS( point, CUR.zp2.n_points ) )
3639
      {
3640
        CUR.error = TT_Err_Invalid_Reference;
3641
        return;
3642
      }
3643
3644
      MOVE_Zp2_Point( point, dx, dy, TRUE );
3645
3646
      CUR.GS.loop--;
3647
    }
3648
3649
    CUR.GS.loop = 1;
3650
    CUR.new_top = CUR.args;
3651
  }
3652
3653
/**********************************************/
3654
/* MSIRP[a]  : Move Stack Indirect Relative   */
3655
/* CodeRange : $3A-$3B                        */
3656
3657
  static void  Ins_MSIRP( INS_ARG )
3658
  {
3659
    Int         point;
3660
    TT_F26Dot6  distance;
3661
3662
    point = (Int)args[0];
3663
3664
    if ( BOUNDS( args[0], CUR.zp1.n_points )
3665
      || BOUNDS( CUR.GS.rp0, CUR.zp0.n_points) )
3666
    {
3667
      CUR.error = TT_Err_Invalid_Reference;
3668
      return;
3669
    }
3670
3671
    /* XXX: Undocumented behaviour */
3672
3673
    if ( CUR.GS.gep0 == 0 )   /* if in twilight zone */
3674
    {
3675
      CUR.zp1.org_x[point] = CUR.zp0.org_x[CUR.GS.rp0];
3676
      CUR.zp1.org_y[point] = CUR.zp0.org_y[CUR.GS.rp0];
3677
      CUR.zp1.cur_x[point] = CUR.zp1.org_x[point];
3678
      CUR.zp1.cur_y[point] = CUR.zp1.org_y[point];
3679
    }
3680
3681
    distance = CUR_Func_project( CUR.zp1.cur_x[point] -
3682
                                   CUR.zp0.cur_x[CUR.GS.rp0],
3683
                                 CUR.zp1.cur_y[point] -
3684
                                   CUR.zp0.cur_y[CUR.GS.rp0] );
3685
3686
    CUR_Func_move( &CUR.zp1, point, args[1] - distance );
3687
3688
    CUR.GS.rp1 = CUR.GS.rp0;
3689
    CUR.GS.rp2 = point;
3690
3691
    if ( (CUR.opcode & 1) != 0 )
3692
      CUR.GS.rp0 = point;
3693
  }
3694
3695
/**********************************************/
3696
/* MDAP[a]   : Move Direct Absolute Point     */
3697
/* CodeRange : $2E-$2F                        */
3698
3699
  static void  Ins_MDAP( INS_ARG )
3700
  {
3701
    Int         point;
3702
    TT_F26Dot6  cur_dist,
3703
                distance;
3704
3705
    point = (Int)args[0];
3706
3707
    if ( BOUNDS( args[0], CUR.zp0.n_points ) )
3708
    {
3709
      CUR.error = TT_Err_Invalid_Reference;
3710
      return;
3711
    }
3712
3713
    /* XXX: Is there some undocumented feature while in the */
3714
    /*      twilight zone? ?                                */
3715
3716
    if ( (CUR.opcode & 1) != 0 )
3717
    {
3718
      cur_dist = CUR_Func_project( CUR.zp0.cur_x[point],
3719
                                   CUR.zp0.cur_y[point] );
3720
      distance = CUR_Func_round( cur_dist,
3721
                                 CUR.metrics.compensations[0] ) - cur_dist;
3722
    }
3723
    else
3724
      distance = 0;
3725
3726
    CUR_Func_move( &CUR.zp0, point, distance );
3727
3728
    CUR.GS.rp0 = point;
3729
    CUR.GS.rp1 = point;
3730
  }
3731
3732
/**********************************************/
3733
/* MIAP[a]   : Move Indirect Absolute Point   */
3734
/* CodeRange : $3E-$3F                        */
3735
3736
  static void  Ins_MIAP( INS_ARG )
3737
  {
3738
    Int         cvtEntry, point;
3739
    TT_F26Dot6  distance,
3740
                org_dist;
3741
3742
    cvtEntry = (Int)args[1];
3743
    point    = (Int)args[0];
3744
3745
    if ( BOUNDS( args[0], CUR.zp0.n_points ) ||
3746
         BOUNDS( args[1], CUR.cvtSize )      )
3747
    {
3748
        /* Ignore these errors, abort the instruction
3749
         * and continue. This restores the FreeType
3750
         * behaviour when pedantic_hinting is false. For bug
3751
         * #689471, see also Ins_SHC above and bug #688501.
3752
         */
3753
      return;
3754
    }
3755
3756
    /* Undocumented:                                     */
3757
    /*                                                   */
3758
    /* The behaviour of an MIAP instruction is quite     */
3759
    /* different when used in the twilight zone.         */
3760
    /*                                                   */
3761
    /* First, no control value cutin test is performed   */
3762
    /* as it would fail anyway.  Second, the original    */
3763
    /* point, i.e. (org_x,org_y) of zp0.point, is set    */
3764
    /* to the absolute, unrounded distance found in      */
3765
    /* the CVT.                                          */
3766
    /*                                                   */
3767
    /* This is used in the CVT programs of the Microsoft */
3768
    /* fonts Arial, Times, etc., in order to re-adjust   */
3769
    /* some key font heights.  It allows the use of the  */
3770
    /* IP instruction in the twilight zone, which        */
3771
    /* otherwise would be "illegal" according to the     */
3772
    /* specs :)                                          */
3773
    /*                                                   */
3774
    /* We implement it with a special sequence for the   */
3775
    /* twilight zone. This is a bad hack, but it seems   */
3776
    /* to work.                                          */
3777
3778
    distance = CUR_Func_read_cvt( cvtEntry );
3779
3780
    DBG_PRINT3(" cvtEntry=%d point=%d distance=%d", cvtEntry, point, distance);
3781
3782
    if ( CUR.GS.gep0 == 0 )   /* If in twilight zone */
3783
    {
3784
      CUR.zp0.org_x[point] = MulDiv_Round( CUR.GS.freeVector.x,
3785
                                           distance, 0x4000L );
3786
      CUR.zp0.cur_x[point] = CUR.zp0.org_x[point];
3787
3788
      CUR.zp0.org_y[point] = MulDiv_Round( CUR.GS.freeVector.y,
3789
                                           distance, 0x4000L );
3790
      CUR.zp0.cur_y[point] = CUR.zp0.org_y[point];
3791
    }
3792
3793
    org_dist = CUR_Func_project( CUR.zp0.cur_x[point],
3794
                                 CUR.zp0.cur_y[point] );
3795
3796
    if ( (CUR.opcode & 1) != 0 )   /* rounding and control cutin flag */
3797
    {
3798
      if ( ABS(distance - org_dist) > CUR.GS.control_value_cutin )
3799
        distance = org_dist;
3800
3801
      distance = CUR_Func_round( distance, CUR.metrics.compensations[0] );
3802
    }
3803
3804
    CUR_Func_move( &CUR.zp0, point, distance - org_dist );
3805
3806
    CUR.GS.rp0 = point;
3807
    CUR.GS.rp1 = point;
3808
  }
3809
3810
/**********************************************/
3811
/* MDRP[abcde] : Move Direct Relative Point   */
3812
/* CodeRange   : $C0-$DF                      */
3813
3814
  static void  Ins_MDRP( INS_ARG )
3815
  {
3816
    Int         point;
3817
    TT_F26Dot6  distance,
3818
                org_dist;
3819
3820
    point = (Int)args[0];
3821
3822
    if ( BOUNDS( args[0], CUR.zp1.n_points ) ||
3823
         BOUNDS( CUR.GS.rp0, CUR.zp0.n_points) )
3824
    {
3825
        /* Current version of FreeType silently ignores this out of bounds error
3826
         * and drops the instruction, see bug #691121
3827
      CUR.error = TT_Err_Invalid_Reference; */
3828
      return;
3829
    }
3830
3831
    /* XXX: Is there some undocumented feature while in the */
3832
    /*      twilight zone?                                  */
3833
3834
    org_dist = CUR_Func_dualproj( CUR.zp1.org_x[point] -
3835
                                    CUR.zp0.org_x[CUR.GS.rp0],
3836
                                  CUR.zp1.org_y[point] -
3837
                                    CUR.zp0.org_y[CUR.GS.rp0] );
3838
3839
    /* single width cutin test */
3840
3841
    if ( ABS(org_dist) < CUR.GS.single_width_cutin )
3842
    {
3843
      if ( org_dist >= 0 )
3844
        org_dist = CUR.GS.single_width_value;
3845
      else
3846
        org_dist = -CUR.GS.single_width_value;
3847
    }
3848
3849
    /* round flag */
3850
3851
    if ( (CUR.opcode & 4) != 0 )
3852
      distance = CUR_Func_round( org_dist,
3853
                                 CUR.metrics.compensations[CUR.opcode & 3] );
3854
    else
3855
      distance = Round_None( EXEC_ARGS
3856
                             org_dist,
3857
                             CUR.metrics.compensations[CUR.opcode & 3]  );
3858
3859
    /* minimum distance flag */
3860
3861
    if ( (CUR.opcode & 8) != 0 )
3862
    {
3863
      if ( org_dist >= 0 )
3864
      {
3865
        if ( distance < CUR.GS.minimum_distance )
3866
          distance = CUR.GS.minimum_distance;
3867
      }
3868
      else
3869
      {
3870
        if ( distance > -CUR.GS.minimum_distance )
3871
          distance = -CUR.GS.minimum_distance;
3872
      }
3873
    }
3874
3875
    /* now move the point */
3876
3877
    org_dist = CUR_Func_project( CUR.zp1.cur_x[point] -
3878
                                   CUR.zp0.cur_x[CUR.GS.rp0],
3879
                                 CUR.zp1.cur_y[point] -
3880
                                   CUR.zp0.cur_y[CUR.GS.rp0] );
3881
3882
    CUR_Func_move( &CUR.zp1, point, distance - org_dist );
3883
3884
    CUR.GS.rp1 = CUR.GS.rp0;
3885
    CUR.GS.rp2 = point;
3886
3887
    if ( (CUR.opcode & 16) != 0 )
3888
      CUR.GS.rp0 = point;
3889
  }
3890
3891
/**********************************************/
3892
/* MIRP[abcde] : Move Indirect Relative Point */
3893
/* CodeRange   : $E0-$FF                      */
3894
3895
  static void  Ins_MIRP( INS_ARG )
3896
  {
3897
    Int         point,
3898
                cvtEntry;
3899
3900
    TT_F26Dot6  cvt_dist,
3901
                distance,
3902
                cur_dist,
3903
                org_dist;
3904
3905
    point    = (Int)args[0];
3906
    cvtEntry = (Int)args[1];
3907
3908
    /* XXX: UNDOCUMENTED! cvt[-1] = 0 always */
3909
3910
    if ( BOUNDS( args[0],   CUR.zp1.n_points ) ||
3911
         BOUNDS( args[1]+1, CUR.cvtSize+1 )    ||
3912
         BOUNDS(CUR.GS.rp0,  CUR.zp0.n_points) )
3913
    {
3914
      CUR.error = TT_Err_Invalid_Reference;
3915
      return;
3916
    }
3917
3918
    if ( args[1] < 0 )
3919
      cvt_dist = 0;
3920
    else
3921
      cvt_dist = CUR_Func_read_cvt( cvtEntry );
3922
3923
    /* single width test */
3924
3925
    if ( ABS( cvt_dist ) < CUR.GS.single_width_cutin )
3926
    {
3927
      if ( cvt_dist >= 0 )
3928
        cvt_dist =  CUR.GS.single_width_value;
3929
      else
3930
        cvt_dist = -CUR.GS.single_width_value;
3931
    }
3932
3933
    /* XXX : Undocumented - twilight zone */
3934
3935
    if ( CUR.GS.gep1 == 0 )
3936
    {
3937
      CUR.zp1.org_x[point] = CUR.zp0.org_x[CUR.GS.rp0] +
3938
                             MulDiv_Round( cvt_dist,
3939
                                           CUR.GS.freeVector.x,
3940
                                           0x4000 );
3941
3942
      CUR.zp1.org_y[point] = CUR.zp0.org_y[CUR.GS.rp0] +
3943
                             MulDiv_Round( cvt_dist,
3944
                                           CUR.GS.freeVector.y,
3945
                                           0x4000 );
3946
3947
      CUR.zp1.cur_x[point] = CUR.zp1.org_x[point];
3948
      CUR.zp1.cur_y[point] = CUR.zp1.org_y[point];
3949
    }
3950
3951
    org_dist = CUR_Func_dualproj( CUR.zp1.org_x[point] -
3952
                                    CUR.zp0.org_x[CUR.GS.rp0],
3953
                                  CUR.zp1.org_y[point] -
3954
                                    CUR.zp0.org_y[CUR.GS.rp0] );
3955
3956
    cur_dist = CUR_Func_project( CUR.zp1.cur_x[point] -
3957
                                   CUR.zp0.cur_x[CUR.GS.rp0],
3958
                                 CUR.zp1.cur_y[point] -
3959
                                   CUR.zp0.cur_y[CUR.GS.rp0] );
3960
3961
    /* auto-flip test */
3962
3963
    if ( CUR.GS.auto_flip )
3964
    {
3965
      if ( (org_dist ^ cvt_dist) < 0 )
3966
        cvt_dist = -cvt_dist;
3967
    }
3968
3969
    /* control value cutin and round */
3970
3971
    if ( (CUR.opcode & 4) != 0 )
3972
    {
3973
      /* XXX: Undocumented : only perform cut-in test when both points */
3974
      /*      refer to the same zone.                                  */
3975
3976
      if ( CUR.GS.gep0 == CUR.GS.gep1 )
3977
        if ( ABS( cvt_dist - org_dist ) >= CUR.GS.control_value_cutin )
3978
          cvt_dist = org_dist;
3979
3980
      distance = CUR_Func_round( cvt_dist,
3981
                                 CUR.metrics.compensations[CUR.opcode & 3] );
3982
    }
3983
    else
3984
      distance = Round_None( EXEC_ARGS
3985
                             cvt_dist,
3986
                             CUR.metrics.compensations[CUR.opcode & 3] );
3987
3988
    /* minimum distance test */
3989
3990
    if ( (CUR.opcode & 8) != 0 )
3991
    {
3992
      if ( org_dist >= 0 )
3993
      {
3994
        if ( distance < CUR.GS.minimum_distance )
3995
          distance = CUR.GS.minimum_distance;
3996
      }
3997
      else
3998
      {
3999
        if ( distance > -CUR.GS.minimum_distance )
4000
          distance = -CUR.GS.minimum_distance;
4001
      }
4002
    }
4003
4004
    CUR_Func_move( &CUR.zp1, point, distance - cur_dist );
4005
4006
    CUR.GS.rp1 = CUR.GS.rp0;
4007
4008
    if ( (CUR.opcode & 16) != 0 )
4009
      CUR.GS.rp0 = point;
4010
4011
    /* UNDOCUMENTED! */
4012
4013
    CUR.GS.rp2 = point;
4014
  }
4015
4016
/**********************************************/
4017
/* ALIGNRP[]   : ALIGN Relative Point         */
4018
/* CodeRange   : $3C                          */
4019
4020
  static void  Ins_ALIGNRP( INS_ARG )
4021
  {
4022
    Int         point;
4023
    TT_F26Dot6  distance;
4024
    (void)args;
4025
4026
    if ( CUR.top < CUR.GS.loop )
4027
    {
4028
      CUR.error = TT_Err_Invalid_Reference;
4029
      return;
4030
    }
4031
4032
    while ( CUR.GS.loop > 0 )
4033
    {
4034
      CUR.args--;
4035
4036
      point = (Int)CUR.stack[CUR.args];
4037
4038
      if ( BOUNDS( point, CUR.zp1.n_points ) ||
4039
           BOUNDS( CUR.GS.rp0, CUR.zp0.n_points ) )
4040
      {
4041
        CUR.error = TT_Err_Invalid_Reference;
4042
        return;
4043
      }
4044
4045
      distance = CUR_Func_project( CUR.zp1.cur_x[point] -
4046
                                     CUR.zp0.cur_x[CUR.GS.rp0],
4047
                                   CUR.zp1.cur_y[point] -
4048
                                     CUR.zp0.cur_y[CUR.GS.rp0] );
4049
4050
      CUR_Func_move( &CUR.zp1, point, -distance );
4051
      CUR.GS.loop--;
4052
    }
4053
4054
    CUR.GS.loop = 1;
4055
    CUR.new_top = CUR.args;
4056
  }
4057
4058
/**********************************************/
4059
/* AA[]        : Adjust Angle                 */
4060
/* CodeRange   : $7F                          */
4061
4062
  static void  Ins_AA( INS_ARG )
4063
  { (void)exc; (void)args;
4064
    /* Intentional - no longer supported */
4065
  }
4066
4067
/**********************************************/
4068
/* ISECT[]     : moves point to InterSECTion  */
4069
/* CodeRange   : $0F                          */
4070
4071
  static void  Ins_ISECT( INS_ARG )
4072
  {
4073
    Long  point,           /* are these Ints or Longs? */
4074
          a0, a1,
4075
          b0, b1;
4076
4077
    TT_F26Dot6  discriminant;
4078
4079
    TT_F26Dot6  dx,  dy,
4080
                dax, day,
4081
                dbx, dby;
4082
4083
    TT_F26Dot6  val;
4084
4085
    TT_Vector   R;
4086
4087
    point = args[0];
4088
4089
    a0 = args[1];
4090
    a1 = args[2];
4091
    b0 = args[3];
4092
    b1 = args[4];
4093
4094
    if ( BOUNDS( b0, CUR.zp0.n_points ) ||
4095
         BOUNDS( b1, CUR.zp0.n_points ) ||
4096
         BOUNDS( a0, CUR.zp1.n_points ) ||
4097
         BOUNDS( a1, CUR.zp1.n_points ) ||
4098
         BOUNDS( point, CUR.zp2.n_points) )
4099
    {
4100
      CUR.error = TT_Err_Invalid_Reference;
4101
      return;
4102
    }
4103
4104
    dbx = CUR.zp0.cur_x[b1] - CUR.zp0.cur_x[b0];
4105
    dby = CUR.zp0.cur_y[b1] - CUR.zp0.cur_y[b0];
4106
4107
    dax = CUR.zp1.cur_x[a1] - CUR.zp1.cur_x[a0];
4108
    day = CUR.zp1.cur_y[a1] - CUR.zp1.cur_y[a0];
4109
4110
    dx = CUR.zp0.cur_x[b0] - CUR.zp1.cur_x[a0];
4111
    dy = CUR.zp0.cur_y[b0] - CUR.zp1.cur_y[a0];
4112
4113
    CUR.zp2.touch[point] |= TT_Flag_Touched_Both;
4114
4115
    discriminant = MulDiv_Round( dax, -dby, 0x40L ) +
4116
                   MulDiv_Round( day, dbx, 0x40L );
4117
4118
    if ( ABS( discriminant ) >= 0x40 )
4119
    {
4120
      val = MulDiv_Round( dx, -dby, 0x40L ) + MulDiv_Round( dy, dbx, 0x40L );
4121
4122
      R.x = MulDiv_Round( val, dax, discriminant );
4123
      R.y = MulDiv_Round( val, day, discriminant );
4124
4125
      CUR.zp2.cur_x[point] = CUR.zp1.cur_x[a0] + R.x;
4126
      CUR.zp2.cur_y[point] = CUR.zp1.cur_y[a0] + R.y;
4127
    }
4128
    else
4129
    {
4130
      /* else, take the middle of the middles of A and B */
4131
4132
      CUR.zp2.cur_x[point] = ( CUR.zp1.cur_x[a0] +
4133
                               CUR.zp1.cur_x[a1] +
4134
                               CUR.zp0.cur_x[b0] +
4135
                               CUR.zp1.cur_x[b1] ) / 4;
4136
      CUR.zp2.cur_y[point] = ( CUR.zp1.cur_y[a0] +
4137
                               CUR.zp1.cur_y[a1] +
4138
                               CUR.zp0.cur_y[b0] +
4139
                               CUR.zp1.cur_y[b1] ) / 4;
4140
    }
4141
  }
4142
4143
/**********************************************/
4144
/* ALIGNPTS[]  : ALIGN PoinTS                 */
4145
/* CodeRange   : $27                          */
4146
4147
  static void  Ins_ALIGNPTS( INS_ARG )
4148
  {
4149
    Int         p1, p2;
4150
    TT_F26Dot6  distance;
4151
4152
    p1 = (Int)args[0];
4153
   p2 = (Int)args[1];
4154
4155
    if ( BOUNDS( args[0], CUR.zp1.n_points ) ||
4156
         BOUNDS( args[1], CUR.zp0.n_points ) )
4157
    {
4158
      CUR.error = TT_Err_Invalid_Reference;
4159
      return;
4160
    }
4161
4162
    distance = CUR_Func_project( CUR.zp0.cur_x[p2] -
4163
                                   CUR.zp1.cur_x[p1],
4164
                                 CUR.zp0.cur_y[p2] -
4165
                                   CUR.zp1.cur_y[p1] ) / 2;
4166
4167
    CUR_Func_move( &CUR.zp1, p1, distance );
4168
4169
    CUR_Func_move( &CUR.zp0, p2, -distance );
4170
  }
4171
4172
/**********************************************/
4173
/* IP[]        : Interpolate Point            */
4174
/* CodeRange   : $39                          */
4175
4176
  static void  Ins_IP( INS_ARG )
4177
  {
4178
    TT_F26Dot6  org_a, org_b, org_x,
4179
                cur_a, cur_b, cur_x,
4180
                distance;
4181
    Int         point;
4182
    (void)args;
4183
4184
    if ( CUR.top < CUR.GS.loop ||
4185
         BOUNDS(CUR.GS.rp1, CUR.zp0.n_points) ||
4186
         BOUNDS(CUR.GS.rp2, CUR.zp1.n_points))
4187
    {
4188
      CUR.error = TT_Err_Invalid_Reference;
4189
      return;
4190
    }
4191
4192
    org_a = CUR_Func_dualproj( CUR.zp0.org_x[CUR.GS.rp1],
4193
                               CUR.zp0.org_y[CUR.GS.rp1] );
4194
4195
    org_b = CUR_Func_dualproj( CUR.zp1.org_x[CUR.GS.rp2],
4196
                               CUR.zp1.org_y[CUR.GS.rp2] );
4197
4198
    cur_a = CUR_Func_project( CUR.zp0.cur_x[CUR.GS.rp1],
4199
                              CUR.zp0.cur_y[CUR.GS.rp1] );
4200
4201
    cur_b = CUR_Func_project( CUR.zp1.cur_x[CUR.GS.rp2],
4202
                              CUR.zp1.cur_y[CUR.GS.rp2] );
4203
4204
    while ( CUR.GS.loop > 0 )
4205
    {
4206
      CUR.args--;
4207
4208
      point = (Int)CUR.stack[CUR.args];
4209
      if ( BOUNDS( point, CUR.zp2.n_points ) )
4210
      {
4211
        CUR.error = TT_Err_Invalid_Reference;
4212
        return;
4213
      }
4214
4215
      org_x = CUR_Func_dualproj( CUR.zp2.org_x[point],
4216
                                 CUR.zp2.org_y[point] );
4217
4218
      cur_x = CUR_Func_project( CUR.zp2.cur_x[point],
4219
                                CUR.zp2.cur_y[point] );
4220
4221
      if ( ( org_a <= org_b && org_x <= org_a ) ||
4222
           ( org_a >  org_b && org_x >= org_a ) )
4223
4224
        distance = ( cur_a - org_a ) + ( org_x - cur_x );
4225
4226
      else if ( ( org_a <= org_b  &&  org_x >= org_b ) ||
4227
                ( org_a >  org_b  &&  org_x <  org_b ) )
4228
4229
        distance = ( cur_b - org_b ) + ( org_x - cur_x );
4230
4231
      else
4232
         /* note: it seems that rounding this value isn't a good */
4233
         /*       idea (cf. width of capital 'S' in Times)       */
4234
4235
         distance = MulDiv_Round( cur_b - cur_a,
4236
                                  org_x - org_a,
4237
                                  org_b - org_a ) + ( cur_a - cur_x );
4238
4239
      CUR_Func_move( &CUR.zp2, point, distance );
4240
4241
      CUR.GS.loop--;
4242
    }
4243
4244
    CUR.GS.loop = 1;
4245
    CUR.new_top = CUR.args;
4246
  }
4247
4248
/**********************************************/
4249
/* UTP[a]      : UnTouch Point                */
4250
/* CodeRange   : $29                          */
4251
4252
  static void  Ins_UTP( INS_ARG )
4253
  {
4254
    Byte  mask;
4255
4256
    if ( BOUNDS( args[0], CUR.zp0.n_points ) )
4257
    {
4258
      CUR.error = TT_Err_Invalid_Reference;
4259
      return;
4260
    }
4261
4262
    mask = 0xFF;
4263
4264
    if ( CUR.GS.freeVector.x != 0 )
4265
      mask &= ~TT_Flag_Touched_X;
4266
4267
    if ( CUR.GS.freeVector.y != 0 )
4268
      mask &= ~TT_Flag_Touched_Y;
4269
4270
    CUR.zp0.touch[args[0]] &= mask;
4271
  }
4272
4273
  /* Local variables for Ins_IUP: */
4274
  struct LOC_Ins_IUP
4275
  {
4276
    PCoordinates  orgs;   /* original and current coordinate */
4277
    PCoordinates  curs;   /* arrays                          */
4278
  };
4279
4280
  static void  Shift( Int  p1,
4281
                      Int  p2,
4282
                      Int  p,
4283
                      struct LOC_Ins_IUP*  LINK )
4284
1.25k
  {
4285
1.25k
    Int         i;
4286
1.25k
    TT_F26Dot6  x;
4287
4288
1.25k
    x = LINK->curs[p] - LINK->orgs[p];
4289
4290
5.75k
    for ( i = p1; i < p; i++ )
4291
4.50k
      LINK->curs[i] += x;
4292
4293
5.80k
    for ( i = p + 1; i <= p2; i++ )
4294
4.54k
      LINK->curs[i] += x;
4295
1.25k
  }
4296
4297
  static void  Interp( Int  p1, Int  p2,
4298
                       Int  ref1, Int  ref2,
4299
                       struct LOC_Ins_IUP*  LINK )
4300
152k
  {
4301
152k
    Long        i;
4302
152k
    TT_F26Dot6  x, x1, x2, d1, d2;
4303
4304
152k
    if ( p1 > p2 )
4305
35.6k
      return;
4306
4307
117k
    x1 = LINK->orgs[ref1];
4308
117k
    d1 = LINK->curs[ref1] - LINK->orgs[ref1];
4309
117k
    x2 = LINK->orgs[ref2];
4310
117k
    d2 = LINK->curs[ref2] - LINK->orgs[ref2];
4311
4312
117k
    if ( x1 == x2 )
4313
406
    {
4314
1.57k
      for ( i = p1; i <= p2; i++ )
4315
1.16k
      {
4316
1.16k
        x = LINK->orgs[i];
4317
4318
1.16k
        if ( x <= x1 )
4319
432
          x += d1;
4320
733
        else
4321
733
          x += d2;
4322
4323
1.16k
        LINK->curs[i] = x;
4324
1.16k
      }
4325
406
      return;
4326
406
    }
4327
4328
116k
    if ( x1 < x2 )
4329
55.2k
    {
4330
390k
      for ( i = p1; i <= p2; i++ )
4331
334k
      {
4332
334k
        x = LINK->orgs[i];
4333
4334
334k
        if ( x <= x1 )
4335
46.8k
          x += d1;
4336
288k
        else
4337
288k
        {
4338
288k
          if ( x >= x2 )
4339
47.7k
            x += d2;
4340
240k
          else
4341
240k
            x = LINK->curs[ref1] +
4342
240k
                  MulDiv_Round( x - x1,
4343
240k
                                LINK->curs[ref2] - LINK->curs[ref1],
4344
240k
                                x2 - x1 );
4345
288k
        }
4346
334k
        LINK->curs[i] = x;
4347
334k
      }
4348
55.2k
      return;
4349
55.2k
    }
4350
4351
    /* x2 < x1 */
4352
4353
405k
    for ( i = p1; i <= p2; i++ )
4354
343k
    {
4355
343k
      x = LINK->orgs[i];
4356
343k
      if ( x <= x2 )
4357
48.4k
        x += d2;
4358
295k
      else
4359
295k
      {
4360
295k
        if ( x >= x1 )
4361
50.5k
          x += d1;
4362
244k
        else
4363
244k
          x = LINK->curs[ref1] +
4364
244k
                MulDiv_Round( x - x1,
4365
244k
                              LINK->curs[ref2] - LINK->curs[ref1],
4366
244k
                              x2 - x1 );
4367
295k
      }
4368
343k
      LINK->curs[i] = x;
4369
343k
    }
4370
61.5k
  }
4371
4372
/**********************************************/
4373
/* IUP[a]      : Interpolate Untouched Points */
4374
/* CodeRange   : $30-$31                      */
4375
4376
  static void  Ins_IUP( INS_ARG )
4377
  {
4378
    struct LOC_Ins_IUP  V;
4379
    unsigned char       mask;
4380
4381
    Long first_point;   /* first point of contour        */
4382
    Long end_point;     /* end point (last+1) of contour */
4383
4384
    Long first_touched; /* first touched point in contour   */
4385
    Long cur_touched;   /* current touched point in contour */
4386
4387
    Long point;         /* current point   */
4388
    Long contour;       /* current contour */
4389
    (void)args;
4390
4391
    if ( CUR.opcode & 1 )
4392
    {
4393
      mask   = TT_Flag_Touched_X;
4394
      V.orgs = CUR.pts.org_x;
4395
      V.curs = CUR.pts.cur_x;
4396
    }
4397
    else
4398
    {
4399
      mask   = TT_Flag_Touched_Y;
4400
      V.orgs = CUR.pts.org_y;
4401
      V.curs = CUR.pts.cur_y;
4402
    }
4403
4404
    contour = 0;
4405
    point   = 0;
4406
    if (contour > CUR.n_contours - 1)
4407
    {
4408
      CUR.error = TT_Err_Invalid_Reference;
4409
    }
4410
    else
4411
    {
4412
      do
4413
      {
4414
        end_point   = CUR.pts.contours[contour];
4415
        first_point = point;
4416
4417
        while ( point <= end_point && point < CUR.pts.n_points && (CUR.pts.touch[point] & mask) == 0 )
4418
          point++;
4419
4420
        if (BOUNDS(point, CUR.pts.n_points ))
4421
        {
4422
            CUR.error = TT_Err_Invalid_Reference;
4423
            return;
4424
        }
4425
4426
        if ( point <= end_point )
4427
        {
4428
          first_touched = point;
4429
          cur_touched   = point;
4430
4431
          point++;
4432
4433
          while ( point <= end_point )
4434
          {
4435
            if ( (CUR.pts.touch[point] & mask) != 0 )
4436
            {
4437
              if (BOUNDS(cur_touched,  CUR.pts.n_points)
4438
               || BOUNDS(point, CUR.pts.n_points))
4439
              {
4440
                 CUR.error = TT_Err_Invalid_Reference;
4441
                 return;
4442
              }
4443
              else
4444
              {
4445
                Interp( (Int)(cur_touched + 1),
4446
                      (Int)(point - 1),
4447
                      (Int)cur_touched,
4448
                      (Int)point,
4449
                      &V );
4450
                cur_touched = point;
4451
              }
4452
            }
4453
4454
            point++;
4455
          }
4456
4457
          if ( cur_touched == first_touched )
4458
            Shift( (Int)first_point, (Int)end_point, (Int)cur_touched, &V );
4459
          else
4460
          {
4461
            Interp((Int)(cur_touched + 1),
4462
                   (Int)(end_point),
4463
                   (Int)(cur_touched),
4464
                   (Int)(first_touched),
4465
                   &V );
4466
4467
            Interp((Int)(first_point),
4468
                   (Int)(first_touched - 1),
4469
                   (Int)(cur_touched),
4470
                   (Int)(first_touched),
4471
                   &V );
4472
          }
4473
        }
4474
        contour++;
4475
      } while ( contour < CUR.pts.n_contours );
4476
    }
4477
  }
4478
4479
/**********************************************/
4480
/* DELTAPn[]   : DELTA Exceptions P1, P2, P3  */
4481
/* CodeRange   : $5D,$71,$72                  */
4482
4483
  static void  Ins_DELTAP( INS_ARG )
4484
  {
4485
    Int   k;
4486
    Long  A, B, C, nump;
4487
4488
    nump = args[0];
4489
4490
    for ( k = 1; k <= nump; k++ )
4491
    {
4492
      if ( CUR.args < 2 )
4493
      {
4494
        CUR.error = TT_Err_Too_Few_Arguments;
4495
        return;
4496
      }
4497
4498
      CUR.args -= 2;
4499
4500
      A = CUR.stack[CUR.args + 1];
4501
      B = CUR.stack[CUR.args];
4502
4503
#if 0
4504
      if ( BOUNDS( A, CUR.zp0.n_points ) )
4505
#else
4506
      /* igorm changed : allow phantom points (Altona.Page_3.2002-09-27.pdf). */
4507
      if ( BOUNDS( A, CUR.zp0.n_points + 2 ) )
4508
#endif
4509
      {
4510
/*        CUR.error = TT_Err_Invalid_Reference;*/
4511
        return;
4512
      }
4513
4514
      C = (B & 0xF0) >> 4;
4515
4516
      switch ( CUR.opcode )
4517
      {
4518
      case 0x5d:
4519
        break;
4520
4521
      case 0x71:
4522
        C += 16;
4523
        break;
4524
4525
      case 0x72:
4526
        C += 32;
4527
        break;
4528
      }
4529
4530
      C += CUR.GS.delta_base;
4531
4532
      if ( CURRENT_Ppem() == C )
4533
      {
4534
        B = (B & 0xF) - 8;
4535
        if ( B >= 0 )
4536
          B++;
4537
        B = B * 64 / (1L << CUR.GS.delta_shift);
4538
4539
        CUR_Func_move( &CUR.zp0, (Int)A, (Int)B );
4540
      }
4541
    }
4542
4543
    CUR.new_top = CUR.args;
4544
  }
4545
4546
/**********************************************/
4547
/* DELTACn[]   : DELTA Exceptions C1, C2, C3  */
4548
/* CodeRange   : $73,$74,$75                  */
4549
4550
  static void  Ins_DELTAC( INS_ARG )
4551
  {
4552
    Long  nump, k;
4553
    Long  A, B, C;
4554
4555
    nump = args[0];
4556
4557
    for ( k = 1; k <= nump; k++ )
4558
    {
4559
      if ( CUR.args < 2 )
4560
      {
4561
        CUR.error = TT_Err_Too_Few_Arguments;
4562
        return;
4563
      }
4564
4565
      CUR.args -= 2;
4566
4567
      A = CUR.stack[CUR.args + 1];
4568
      B = CUR.stack[CUR.args];
4569
4570
      if ( A >= CUR.cvtSize )
4571
      {
4572
        CUR.error = TT_Err_Invalid_Reference;
4573
        return;
4574
      }
4575
4576
      C = ((unsigned long)(B & 0xF0)) >> 4;
4577
4578
      switch ( CUR.opcode )
4579
      {
4580
      case 0x73:
4581
        break;
4582
4583
      case 0x74:
4584
        C += 16;
4585
        break;
4586
4587
      case 0x75:
4588
        C += 32;
4589
        break;
4590
      }
4591
4592
      C += CUR.GS.delta_base;
4593
4594
      if ( CURRENT_Ppem() == C )
4595
      {
4596
        B = (B & 0xF) - 8;
4597
        if ( B >= 0 )
4598
          B++;
4599
        B = B * 64 / (1L << CUR.GS.delta_shift);
4600
4601
        CUR_Func_move_cvt( A, B );
4602
      }
4603
    }
4604
4605
    CUR.new_top = CUR.args;
4606
  }
4607
4608
/****************************************************************/
4609
/*                                                              */
4610
/* MISC. INSTRUCTIONS                                           */
4611
/*                                                              */
4612
/****************************************************************/
4613
4614
/***********************************************************/
4615
/* DEBUG[]     : DEBUG. Unsupported                        */
4616
/* CodeRange   : $4F                                       */
4617
4618
/* NOTE : The original instruction pops a value from the stack */
4619
4620
  static void  Ins_DEBUG( INS_ARG )
4621
  { (void)args;
4622
    CUR.error = TT_Err_Debug_OpCode;
4623
  }
4624
4625
/**********************************************/
4626
/* GETINFO[]   : GET INFOrmation              */
4627
/* CodeRange   : $88                          */
4628
4629
  static void  Ins_GETINFO( INS_ARG )
4630
  {
4631
    Long  K;
4632
4633
    K = 0;
4634
4635
    /* We return then Windows 3.1 version number */
4636
    /* for the font scaler                       */
4637
    if ( (args[0] & 1) != 0 )
4638
      K = 3;
4639
4640
    /* Has the glyph been rotated ? */
4641
    if ( CUR.metrics.rotated )
4642
      K |= 0x80;
4643
4644
    /* Has the glyph been stretched ? */
4645
    if ( CUR.metrics.stretched )
4646
      K |= 0x100;
4647
4648
    args[0] = K;
4649
  }
4650
4651
  static void  Ins_UNKNOWN( INS_ARG )
4652
  { /* Rewritten by igorm. */
4653
    Byte i;
4654
    TDefRecord*  def;
4655
    PCallRecord  call;
4656
4657
#   if 0     /* The condition below appears always false
4658
                due to limited range of data type
4659
                - skip it to quiet a compiler warning. */
4660
    if (CUR.opcode > sizeof(CUR.IDefPtr) / sizeof(CUR.IDefPtr[0])) {
4661
        CUR.error = TT_Err_Invalid_Opcode;
4662
        return;
4663
    }
4664
#   endif
4665
    i = CUR.IDefPtr[(Byte)CUR.opcode];
4666
4667
    if (i >= CUR.numIDefs)
4668
      {
4669
        CUR.error = TT_Err_Invalid_Opcode;
4670
        return;
4671
      }
4672
    def   = &CUR.IDefs[i];
4673
4674
    if ( CUR.callTop >= CUR.callSize )
4675
    {
4676
      CUR.error = TT_Err_Stack_Overflow;
4677
      return;
4678
    }
4679
4680
    call = CUR.callStack + CUR.callTop++;
4681
4682
    call->Caller_Range = CUR.curRange;
4683
    call->Caller_IP    = CUR.IP+1;
4684
    call->Cur_Count    = 1;
4685
    call->Cur_Restart  = def->Start;
4686
4687
    INS_Goto_CodeRange( def->Range, def->Start );
4688
4689
    CUR.step_ins = FALSE;
4690
    return;
4691
  }
4692
4693
  static struct { const char *sName; TInstruction_Function p; }  Instruct_Dispatch[256] =
4694
  {
4695
    /* Opcodes are gathered in groups of 16. */
4696
    /* Please keep the spaces as they are.   */
4697
4698
     {"  SVTCA  y  ",  Ins_SVTCA  }
4699
    ,{"  SVTCA  x  ",  Ins_SVTCA  }
4700
    ,{"  SPvTCA y  ",  Ins_SPVTCA }
4701
    ,{"  SPvTCA x  ",  Ins_SPVTCA }
4702
    ,{"  SFvTCA y  ",  Ins_SFVTCA }
4703
    ,{"  SFvTCA x  ",  Ins_SFVTCA }
4704
    ,{"  SPvTL //  ",  Ins_SPVTL  }
4705
    ,{"  SPvTL +   ",  Ins_SPVTL  }
4706
    ,{"  SFvTL //  ",  Ins_SFVTL  }
4707
    ,{"  SFvTL +   ",  Ins_SFVTL  }
4708
    ,{"  SPvFS     ",  Ins_SPVFS  }
4709
    ,{"  SFvFS     ",  Ins_SFVFS  }
4710
    ,{"  GPV       ",  Ins_GPV    }
4711
    ,{"  GFV       ",  Ins_GFV    }
4712
    ,{"  SFvTPv    ",  Ins_SFVTPV }
4713
    ,{"  ISECT     ",  Ins_ISECT  }
4714
4715
    ,{"  SRP0      ",  Ins_SRP0   }
4716
    ,{"  SRP1      ",  Ins_SRP1   }
4717
    ,{"  SRP2      ",  Ins_SRP2   }
4718
    ,{"  SZP0      ",  Ins_SZP0   }
4719
    ,{"  SZP1      ",  Ins_SZP1   }
4720
    ,{"  SZP2      ",  Ins_SZP2   }
4721
    ,{"  SZPS      ",  Ins_SZPS   }
4722
    ,{"  SLOOP     ",  Ins_SLOOP  }
4723
    ,{"  RTG       ",  Ins_RTG    }
4724
    ,{"  RTHG      ",  Ins_RTHG   }
4725
    ,{"  SMD       ",  Ins_SMD    }
4726
    ,{"  ELSE      ",  Ins_ELSE   }
4727
    ,{"  JMPR      ",  Ins_JMPR   }
4728
    ,{"  SCvTCi    ",  Ins_SCVTCI }
4729
    ,{"  SSwCi     ",  Ins_SSWCI  }
4730
    ,{"  SSW       ",  Ins_SSW    }
4731
4732
    ,{"  DUP       ",  Ins_DUP    }
4733
    ,{"  POP       ",  Ins_POP    }
4734
    ,{"  CLEAR     ",  Ins_CLEAR  }
4735
    ,{"  SWAP      ",  Ins_SWAP   }
4736
    ,{"  DEPTH     ",  Ins_DEPTH  }
4737
    ,{"  CINDEX    ",  Ins_CINDEX }
4738
    ,{"  MINDEX    ",  Ins_MINDEX }
4739
    ,{"  AlignPTS  ",  Ins_ALIGNPTS}
4740
    ,{"  INS_$28   ",  Ins_UNKNOWN }
4741
    ,{"  UTP       ",  Ins_UTP     }
4742
    ,{"  LOOPCALL  ",  Ins_LOOPCALL}
4743
    ,{"  CALL      ",  Ins_CALL    }
4744
    ,{"  FDEF      ",  Ins_FDEF    }
4745
    ,{"  ENDF      ",  Ins_ENDF    }
4746
    ,{"  MDAP[0]   ",  Ins_MDAP    }
4747
    ,{"  MDAP[1]   ",  Ins_MDAP    }
4748
4749
    ,{"  IUP[0]    ",  Ins_IUP       }
4750
    ,{"  IUP[1]    ",  Ins_IUP       }
4751
    ,{"  SHP[0]    ",  Ins_SHP       }
4752
    ,{"  SHP[1]    ",  Ins_SHP       }
4753
    ,{"  SHC[0]    ",  Ins_SHC       }
4754
    ,{"  SHC[1]    ",  Ins_SHC       }
4755
    ,{"  SHZ[0]    ",  Ins_SHZ       }
4756
    ,{"  SHZ[1]    ",  Ins_SHZ       }
4757
    ,{"  SHPIX     ",  Ins_SHPIX     }
4758
    ,{"  IP        ",  Ins_IP        }
4759
    ,{"  MSIRP[0]  ",  Ins_MSIRP     }
4760
    ,{"  MSIRP[1]  ",  Ins_MSIRP     }
4761
    ,{"  AlignRP   ",  Ins_ALIGNRP   }
4762
    ,{"  RTDG      ",  Ins_RTDG      }
4763
    ,{"  MIAP[0]   ",  Ins_MIAP      }
4764
    ,{"  MIAP[1]   ",  Ins_MIAP      }
4765
4766
    ,{"  NPushB    ",  Ins_NPUSHB     }
4767
    ,{"  NPushW    ",  Ins_NPUSHW     }
4768
    ,{"  WS        ",  Ins_WS         }
4769
    ,{"  RS        ",  Ins_RS         }
4770
    ,{"  WCvtP     ",  Ins_WCVTP      }
4771
    ,{"  RCvt      ",  Ins_RCVT       }
4772
    ,{"  GC[0]     ",  Ins_GC         }
4773
    ,{"  GC[1]     ",  Ins_GC         }
4774
    ,{"  SCFS      ",  Ins_SCFS       }
4775
    ,{"  MD[0]     ",  Ins_MD         }
4776
    ,{"  MD[1]     ",  Ins_MD         }
4777
    ,{"  MPPEM     ",  Ins_MPPEM      }
4778
    ,{"  MPS       ",  Ins_MPS        }
4779
    ,{"  FlipON    ",  Ins_FLIPON     }
4780
    ,{"  FlipOFF   ",  Ins_FLIPOFF    }
4781
    ,{"  DEBUG     ",  Ins_DEBUG      }
4782
4783
    ,{"  LT        ",  Ins_LT         }
4784
    ,{"  LTEQ      ",  Ins_LTEQ       }
4785
    ,{"  GT        ",  Ins_GT         }
4786
    ,{"  GTEQ      ",  Ins_GTEQ       }
4787
    ,{"  EQ        ",  Ins_EQ         }
4788
    ,{"  NEQ       ",  Ins_NEQ        }
4789
    ,{"  ODD       ",  Ins_ODD        }
4790
    ,{"  EVEN      ",  Ins_EVEN       }
4791
    ,{"  IF        ",  Ins_IF         }
4792
    ,{"  EIF       ",  Ins_EIF        }
4793
    ,{"  AND       ",  Ins_AND        }
4794
    ,{"  OR        ",  Ins_OR         }
4795
    ,{"  NOT       ",  Ins_NOT        }
4796
    ,{"  DeltaP1   ",  Ins_DELTAP     }
4797
    ,{"  SDB       ",  Ins_SDB        }
4798
    ,{"  SDS       ",  Ins_SDS        }
4799
4800
    ,{"  ADD       ",  Ins_ADD       }
4801
    ,{"  SUB       ",  Ins_SUB       }
4802
    ,{"  DIV       ",  Ins_DIV       }
4803
    ,{"  MUL       ",  Ins_MUL       }
4804
    ,{"  ABS       ",  Ins_ABS       }
4805
    ,{"  NEG       ",  Ins_NEG       }
4806
    ,{"  FLOOR     ",  Ins_FLOOR     }
4807
    ,{"  CEILING   ",  Ins_CEILING   }
4808
    ,{"  ROUND[0]  ",  Ins_ROUND     }
4809
    ,{"  ROUND[1]  ",  Ins_ROUND     }
4810
    ,{"  ROUND[2]  ",  Ins_ROUND     }
4811
    ,{"  ROUND[3]  ",  Ins_ROUND     }
4812
    ,{"  NROUND[0] ",  Ins_NROUND    }
4813
    ,{"  NROUND[1] ",  Ins_NROUND    }
4814
    ,{"  NROUND[2] ",  Ins_NROUND    }
4815
    ,{"  NROUND[3] ",  Ins_NROUND    }
4816
4817
    ,{"  WCvtF     ",  Ins_WCVTF      }
4818
    ,{"  DeltaP2   ",  Ins_DELTAP     }
4819
    ,{"  DeltaP3   ",  Ins_DELTAP     }
4820
    ,{"  DeltaCn[0] ", Ins_DELTAC     }
4821
    ,{"  DeltaCn[1] ", Ins_DELTAC     }
4822
    ,{"  DeltaCn[2] ", Ins_DELTAC     }
4823
    ,{"  SROUND    ",  Ins_SROUND     }
4824
    ,{"  S45Round  ",  Ins_S45ROUND   }
4825
    ,{"  JROT      ",  Ins_JROT       }
4826
    ,{"  JROF      ",  Ins_JROF       }
4827
    ,{"  ROFF      ",  Ins_ROFF       }
4828
    ,{"  INS_$7B   ",  Ins_UNKNOWN    }
4829
    ,{"  RUTG      ",  Ins_RUTG       }
4830
    ,{"  RDTG      ",  Ins_RDTG       }
4831
    ,{"  SANGW     ",  Ins_SANGW      }
4832
    ,{"  AA        ",  Ins_AA         }
4833
4834
    ,{"  FlipPT    ",  Ins_FLIPPT     }
4835
    ,{"  FlipRgON  ",  Ins_FLIPRGON   }
4836
    ,{"  FlipRgOFF ",  Ins_FLIPRGOFF  }
4837
    ,{"  INS_$83   ",  Ins_UNKNOWN    }
4838
    ,{"  INS_$84   ",  Ins_UNKNOWN    }
4839
    ,{"  ScanCTRL  ",  Ins_SCANCTRL   }
4840
    ,{"  SDPVTL[0] ",  Ins_SDPVTL     }
4841
    ,{"  SDPVTL[1] ",  Ins_SDPVTL     }
4842
    ,{"  GetINFO   ",  Ins_GETINFO    }
4843
    ,{"  IDEF      ",  Ins_IDEF       }
4844
    ,{"  ROLL      ",  Ins_ROLL       }
4845
    ,{"  MAX       ",  Ins_MAX        }
4846
    ,{"  MIN       ",  Ins_MIN        }
4847
    ,{"  ScanTYPE  ",  Ins_SCANTYPE   }
4848
    ,{"  InstCTRL  ",  Ins_INSTCTRL   }
4849
    ,{"  INS_$8F   ",  Ins_UNKNOWN    }
4850
4851
    ,{"  INS_$90  ",   Ins_UNKNOWN   }
4852
    ,{"  INS_$91  ",   Ins_UNKNOWN   }
4853
    ,{"  INS_$92  ",   Ins_UNKNOWN   }
4854
    ,{"  INS_$93  ",   Ins_UNKNOWN   }
4855
    ,{"  INS_$94  ",   Ins_UNKNOWN   }
4856
    ,{"  INS_$95  ",   Ins_UNKNOWN   }
4857
    ,{"  INS_$96  ",   Ins_UNKNOWN   }
4858
    ,{"  INS_$97  ",   Ins_UNKNOWN   }
4859
    ,{"  INS_$98  ",   Ins_UNKNOWN   }
4860
    ,{"  INS_$99  ",   Ins_UNKNOWN   }
4861
    ,{"  INS_$9A  ",   Ins_UNKNOWN   }
4862
    ,{"  INS_$9B  ",   Ins_UNKNOWN   }
4863
    ,{"  INS_$9C  ",   Ins_UNKNOWN   }
4864
    ,{"  INS_$9D  ",   Ins_UNKNOWN   }
4865
    ,{"  INS_$9E  ",   Ins_UNKNOWN   }
4866
    ,{"  INS_$9F  ",   Ins_UNKNOWN   }
4867
4868
    ,{"  INS_$A0  ",   Ins_UNKNOWN   }
4869
    ,{"  INS_$A1  ",   Ins_UNKNOWN   }
4870
    ,{"  INS_$A2  ",   Ins_UNKNOWN   }
4871
    ,{"  INS_$A3  ",   Ins_UNKNOWN   }
4872
    ,{"  INS_$A4  ",   Ins_UNKNOWN   }
4873
    ,{"  INS_$A5  ",   Ins_UNKNOWN   }
4874
    ,{"  INS_$A6  ",   Ins_UNKNOWN   }
4875
    ,{"  INS_$A7  ",   Ins_UNKNOWN   }
4876
    ,{"  INS_$A8  ",   Ins_UNKNOWN   }
4877
    ,{"  INS_$A9  ",   Ins_UNKNOWN   }
4878
    ,{"  INS_$AA  ",   Ins_UNKNOWN   }
4879
    ,{"  INS_$AB  ",   Ins_UNKNOWN   }
4880
    ,{"  INS_$AC  ",   Ins_UNKNOWN   }
4881
    ,{"  INS_$AD  ",   Ins_UNKNOWN   }
4882
    ,{"  INS_$AE  ",   Ins_UNKNOWN   }
4883
    ,{"  INS_$AF  ",   Ins_UNKNOWN   }
4884
4885
    ,{"  PushB[0]  ",  Ins_PUSHB     }
4886
    ,{"  PushB[1]  ",  Ins_PUSHB     }
4887
    ,{"  PushB[2]  ",  Ins_PUSHB     }
4888
    ,{"  PushB[3]  ",  Ins_PUSHB     }
4889
    ,{"  PushB[4]  ",  Ins_PUSHB     }
4890
    ,{"  PushB[5]  ",  Ins_PUSHB     }
4891
    ,{"  PushB[6]  ",  Ins_PUSHB     }
4892
    ,{"  PushB[7]  ",  Ins_PUSHB     }
4893
    ,{"  PushW[0]  ",  Ins_PUSHW     }
4894
    ,{"  PushW[1]  ",  Ins_PUSHW     }
4895
    ,{"  PushW[2]  ",  Ins_PUSHW     }
4896
    ,{"  PushW[3]  ",  Ins_PUSHW     }
4897
    ,{"  PushW[4]  ",  Ins_PUSHW     }
4898
    ,{"  PushW[5]  ",  Ins_PUSHW     }
4899
    ,{"  PushW[6]  ",  Ins_PUSHW     }
4900
    ,{"  PushW[7]  ",  Ins_PUSHW     }
4901
4902
    ,{"  MDRP[00]  ",  Ins_MDRP       }
4903
    ,{"  MDRP[01]  ",  Ins_MDRP       }
4904
    ,{"  MDRP[02]  ",  Ins_MDRP       }
4905
    ,{"  MDRP[03]  ",  Ins_MDRP       }
4906
    ,{"  MDRP[04]  ",  Ins_MDRP       }
4907
    ,{"  MDRP[05]  ",  Ins_MDRP       }
4908
    ,{"  MDRP[06]  ",  Ins_MDRP       }
4909
    ,{"  MDRP[07]  ",  Ins_MDRP       }
4910
    ,{"  MDRP[08]  ",  Ins_MDRP       }
4911
    ,{"  MDRP[09]  ",  Ins_MDRP       }
4912
    ,{"  MDRP[10]  ",  Ins_MDRP       }
4913
    ,{"  MDRP[11]  ",  Ins_MDRP       }
4914
    ,{"  MDRP[12]  ",  Ins_MDRP       }
4915
    ,{"  MDRP[13]  ",  Ins_MDRP       }
4916
    ,{"  MDRP[14]  ",  Ins_MDRP       }
4917
    ,{"  MDRP[15]  ",  Ins_MDRP       }
4918
4919
    ,{"  MDRP[16]  ",  Ins_MDRP       }
4920
    ,{"  MDRP[17]  ",  Ins_MDRP       }
4921
    ,{"  MDRP[18]  ",  Ins_MDRP       }
4922
    ,{"  MDRP[19]  ",  Ins_MDRP       }
4923
    ,{"  MDRP[20]  ",  Ins_MDRP       }
4924
    ,{"  MDRP[21]  ",  Ins_MDRP       }
4925
    ,{"  MDRP[22]  ",  Ins_MDRP       }
4926
    ,{"  MDRP[23]  ",  Ins_MDRP       }
4927
    ,{"  MDRP[24]  ",  Ins_MDRP       }
4928
    ,{"  MDRP[25]  ",  Ins_MDRP       }
4929
    ,{"  MDRP[26]  ",  Ins_MDRP       }
4930
    ,{"  MDRP[27]  ",  Ins_MDRP       }
4931
    ,{"  MDRP[28]  ",  Ins_MDRP       }
4932
    ,{"  MDRP[29]  ",  Ins_MDRP       }
4933
    ,{"  MDRP[30]  ",  Ins_MDRP       }
4934
    ,{"  MDRP[31]  ",  Ins_MDRP       }
4935
4936
    ,{"  MIRP[00]  ",  Ins_MIRP       }
4937
    ,{"  MIRP[01]  ",  Ins_MIRP       }
4938
    ,{"  MIRP[02]  ",  Ins_MIRP       }
4939
    ,{"  MIRP[03]  ",  Ins_MIRP       }
4940
    ,{"  MIRP[04]  ",  Ins_MIRP       }
4941
    ,{"  MIRP[05]  ",  Ins_MIRP       }
4942
    ,{"  MIRP[06]  ",  Ins_MIRP       }
4943
    ,{"  MIRP[07]  ",  Ins_MIRP       }
4944
    ,{"  MIRP[08]  ",  Ins_MIRP       }
4945
    ,{"  MIRP[09]  ",  Ins_MIRP       }
4946
    ,{"  MIRP[10]  ",  Ins_MIRP       }
4947
    ,{"  MIRP[11]  ",  Ins_MIRP       }
4948
    ,{"  MIRP[12]  ",  Ins_MIRP       }
4949
    ,{"  MIRP[13]  ",  Ins_MIRP       }
4950
    ,{"  MIRP[14]  ",  Ins_MIRP       }
4951
    ,{"  MIRP[15]  ",  Ins_MIRP       }
4952
4953
    ,{"  MIRP[16]  ",  Ins_MIRP       }
4954
    ,{"  MIRP[17]  ",  Ins_MIRP       }
4955
    ,{"  MIRP[18]  ",  Ins_MIRP       }
4956
    ,{"  MIRP[19]  ",  Ins_MIRP       }
4957
    ,{"  MIRP[20]  ",  Ins_MIRP       }
4958
    ,{"  MIRP[21]  ",  Ins_MIRP       }
4959
    ,{"  MIRP[22]  ",  Ins_MIRP       }
4960
    ,{"  MIRP[23]  ",  Ins_MIRP       }
4961
    ,{"  MIRP[24]  ",  Ins_MIRP       }
4962
    ,{"  MIRP[25]  ",  Ins_MIRP       }
4963
    ,{"  MIRP[26]  ",  Ins_MIRP       }
4964
    ,{"  MIRP[27]  ",  Ins_MIRP       }
4965
    ,{"  MIRP[28]  ",  Ins_MIRP       }
4966
    ,{"  MIRP[29]  ",  Ins_MIRP       }
4967
    ,{"  MIRP[30]  ",  Ins_MIRP       }
4968
    ,{"  MIRP[31]  ",  Ins_MIRP       }
4969
  };
4970
4971
/****************************************************************/
4972
/*                                                              */
4973
/*                    RUN                                       */
4974
/*                                                              */
4975
/*  This function executes a run of opcodes.  It will exit      */
4976
/*  in the following cases:                                     */
4977
/*                                                              */
4978
/*   - Errors (in which case it returns FALSE)                  */
4979
/*                                                              */
4980
/*   - Reaching the end of the main code range (returns TRUE).  */
4981
/*     Reaching the end of a code range within a function       */
4982
/*     call is an error.                                        */
4983
/*                                                              */
4984
/*   - After executing one single opcode, if the flag           */
4985
/*     'Instruction_Trap' is set to TRUE (returns TRUE).        */
4986
/*                                                              */
4987
/*  On exit whith TRUE, test IP < CodeSize to know wether it    */
4988
/*  comes from a instruction trap or a normal termination.      */
4989
/*                                                              */
4990
/*                                                              */
4991
/*     Note:  The documented DEBUG opcode pops a value from     */
4992
/*            the stack.  This behaviour is unsupported, here   */
4993
/*            a DEBUG opcode is always an error.                */
4994
/*                                                              */
4995
/*                                                              */
4996
/* THIS IS THE INTERPRETER'S MAIN LOOP                          */
4997
/*                                                              */
4998
/*  Instructions appear in the specs' order.                    */
4999
/*                                                              */
5000
/****************************************************************/
5001
5002
OPTIMIZE_SETJMP
5003
  TT_Error  RunIns( PExecution_Context  exc )
5004
14.8k
  {
5005
14.8k
    TT_Error     Result;
5006
14.8k
    Int          A;
5007
14.8k
    PDefRecord   WITH;
5008
14.8k
    PCallRecord  WITH1;
5009
#ifdef COLLECT_STATS_TTINTERP
5010
    bool bFirst;
5011
#endif
5012
14.8k
    bool dbg_prt = (DBG_PRT_FUN != NULL);
5013
#   ifdef DEBUG
5014
        ttfMemory *mem = exc->current_face->font->tti->ttf_memory;
5015
        F26Dot6 *save_ox, *save_oy, *save_cx, *save_cy;
5016
5017
        DBG_PRINT("\n%% *** Entering RunIns ***");
5018
#   endif
5019
5020
14.8k
    (void)dbg_prt; /* Quiet compiler warning in release build. */
5021
5022
    /* set CVT functions */
5023
14.8k
    CUR.metrics.ratio = 0;
5024
14.8k
    if ( CUR.metrics.x_ppem != CUR.metrics.y_ppem )
5025
0
    {
5026
      /* non-square pixels, use the stretched routines */
5027
0
      CUR.func_read_cvt  = Read_CVT_Stretched;
5028
0
      CUR.func_write_cvt = Write_CVT_Stretched;
5029
0
      CUR.func_move_cvt  = Move_CVT_Stretched;
5030
0
    }
5031
14.8k
    else
5032
14.8k
    {
5033
      /* square pixels, use normal routines */
5034
14.8k
      CUR.func_read_cvt  = Read_CVT;
5035
14.8k
      CUR.func_write_cvt = Write_CVT;
5036
14.8k
      CUR.func_move_cvt  = Move_CVT;
5037
14.8k
    }
5038
5039
14.8k
    COMPUTE_Funcs();
5040
14.8k
    Compute_Round( EXEC_ARGS (Byte)exc->GS.round_state );
5041
5042
#   ifdef DEBUG
5043
      if (dbg_prt && CUR.pts.n_points) {
5044
        save_ox = mem->alloc_bytes(mem, CUR.pts.n_points * sizeof(*save_ox), "RunIns");
5045
        save_oy = mem->alloc_bytes(mem, CUR.pts.n_points * sizeof(*save_oy), "RunIns");
5046
        save_cx = mem->alloc_bytes(mem, CUR.pts.n_points * sizeof(*save_cx), "RunIns");
5047
        save_cy = mem->alloc_bytes(mem, CUR.pts.n_points * sizeof(*save_cy), "RunIns");
5048
        if (!save_ox || !save_oy || !save_cx || !save_cy)
5049
          return TT_Err_Out_Of_Memory;
5050
      } else
5051
        save_ox = save_oy = save_cx = save_cy = NULL;
5052
#   endif
5053
5054
14.8k
    Result = setjmp(find_jmp_buf(exc->trap));
5055
14.8k
    if (Result) {
5056
780
        CUR.error = Result;
5057
780
        goto _LExit;
5058
780
    }
5059
#ifdef COLLECT_STATS_TTINTERP
5060
    bFirst = true;
5061
#endif
5062
14.0k
    do
5063
8.69M
    {
5064
8.69M
      CALC_Length();
5065
5066
      /* First, let's check for empty stack and overflow */
5067
5068
8.69M
      CUR.args = CUR.top - Pop_Push_Count[CUR.opcode * 2];
5069
5070
      /* `args' is the top of the stack once arguments have been popped. */
5071
      /* One can also interpret it as the index of the last argument.    */
5072
5073
8.69M
      if ( CUR.args < 0 )
5074
283
      {
5075
283
        CUR.error = TT_Err_Too_Few_Arguments;
5076
283
        goto _LErrorLabel;
5077
283
      }
5078
5079
8.69M
      CUR.new_top = CUR.args + Pop_Push_Count[CUR.opcode * 2 + 1];
5080
5081
      /* `new_top' is the new top of the stack, after the instruction's */
5082
      /* execution.  `top' will be set to `new_top' after the 'switch'  */
5083
      /* statement.                                                     */
5084
5085
8.69M
      if ( CUR.new_top > CUR.stackSize )
5086
8
      {
5087
8
        CUR.error = TT_Err_Stack_Overflow;
5088
8
        goto _LErrorLabel;
5089
8
      }
5090
5091
8.69M
      CUR.step_ins = TRUE;
5092
8.69M
      CUR.error    = TT_Err_Ok;
5093
5094
#ifdef COLLECT_STATS_TTINTERP
5095
        DBG_PRINT3("\n%%n=%5d IP=%5d OP=%s            ", nInstrCount, CUR.IP, Instruct_Dispatch[CUR.opcode].sName);
5096
        /*
5097
        { for(int i=0;i<CUR.top;i++)
5098
            DBG_PRINT1("% %d",CUR.stack[i]);
5099
        }
5100
        */
5101
        if (save_ox != NULL) {
5102
          memcpy(save_ox, CUR.pts.org_x, sizeof(CUR.pts.org_x[0]) * CUR.pts.n_points);
5103
          memcpy(save_oy, CUR.pts.org_y, sizeof(CUR.pts.org_y[0]) * CUR.pts.n_points);
5104
          memcpy(save_cx, CUR.pts.cur_x, sizeof(CUR.pts.cur_x[0]) * CUR.pts.n_points);
5105
          memcpy(save_cy, CUR.pts.cur_y, sizeof(CUR.pts.cur_y[0]) * CUR.pts.n_points);
5106
        }
5107
#endif
5108
5109
8.69M
      Instruct_Dispatch[CUR.opcode].p( EXEC_ARGS &CUR.stack[CUR.args] );
5110
5111
#ifdef COLLECT_STATS_TTINTERP
5112
      if (save_ox != NULL) {
5113
        F26Dot6 *pp[4], *qq[4];
5114
        const char *ss[] = {"org.x", "org.y", "cur.x", "cur.y"};
5115
        int l = 0, i, j;
5116
5117
        pp[0] = save_ox,
5118
        pp[1] = save_oy,
5119
        pp[2] = save_cx,
5120
        pp[3] = save_cy;
5121
        qq[0] = CUR.pts.org_x;
5122
        qq[1] = CUR.pts.org_y;
5123
        qq[2] = CUR.pts.cur_x;
5124
        qq[3] = CUR.pts.cur_y;
5125
5126
        for(i = 0; i < 4; i++)
5127
          for(j = 0;j < CUR.pts.n_points; j++)
5128
            { F26Dot6 *ppi = pp[i], *qqi = qq[i];
5129
              if(ppi[j] != qqi[j] || bFirst)
5130
              {
5131
                DBG_PRINT4("%%  %s[%d]%d:=%d", ss[i], j, pp[i][j], qq[i][j]);
5132
                if(++l > 3)
5133
                  { l=0;
5134
                    DBG_PRINT("\n");
5135
                  }
5136
              }
5137
            }
5138
        nInstrCount++;
5139
        bFirst=FALSE;
5140
      }
5141
#endif
5142
5143
      DBG_PAINT
5144
5145
8.69M
      if ( CUR.error != TT_Err_Ok )
5146
530
      {
5147
530
        switch ( CUR.error )
5148
530
        {
5149
41
        case TT_Err_Invalid_Opcode: /* looking for redefined instructions */
5150
41
          A = 0;
5151
5152
178
          while ( A < CUR.numIDefs )
5153
137
          {
5154
137
            WITH = &CUR.IDefs[A];
5155
5156
137
            if ( WITH->Active && CUR.opcode == WITH->Opc )
5157
0
            {
5158
0
              if ( CUR.callTop >= CUR.callSize )
5159
0
              {
5160
0
                CUR.error = TT_Err_Invalid_Reference;
5161
0
                goto _LErrorLabel;
5162
0
              }
5163
5164
0
              WITH1 = &CUR.callStack[CUR.callTop];
5165
5166
0
              WITH1->Caller_Range = CUR.curRange;
5167
0
              WITH1->Caller_IP    = CUR.IP + 1;
5168
0
              WITH1->Cur_Count    = 1;
5169
0
              WITH1->Cur_Restart  = WITH->Start;
5170
5171
0
              if ( INS_Goto_CodeRange( WITH->Range, WITH->Start ) == FAILURE )
5172
0
                goto _LErrorLabel;
5173
5174
0
              goto _LSuiteLabel;
5175
0
            }
5176
137
            else
5177
137
            {
5178
137
              A++;
5179
137
              continue;
5180
137
            }
5181
137
          }
5182
5183
41
          CUR.error = TT_Err_Invalid_Opcode;
5184
41
          goto _LErrorLabel;
5185
0
          break;
5186
5187
489
        default:
5188
489
          goto _LErrorLabel;
5189
489
          break;
5190
530
        }
5191
530
      }
5192
5193
8.69M
      CUR.top = CUR.new_top;
5194
5195
8.69M
      if ( CUR.step_ins )
5196
6.81M
        CUR.IP += CUR.length;
5197
5198
8.69M
  _LSuiteLabel:
5199
5200
8.69M
      if ( CUR.IP >= CUR.codeSize )
5201
13.2k
      {
5202
13.2k
        if ( CUR.callTop > 0 )
5203
0
        {
5204
0
          CUR.error = TT_Err_Code_Overflow;
5205
0
          goto _LErrorLabel;
5206
0
        }
5207
13.2k
        else
5208
13.2k
          goto _LNo_Error;
5209
13.2k
      }
5210
8.69M
    } while ( !CUR.instruction_trap );
5211
5212
13.2k
  _LNo_Error:
5213
13.2k
    Result = TT_Err_Ok;
5214
13.2k
    goto _LExit;
5215
5216
821
  _LErrorLabel:
5217
821
    Result = CUR.error;
5218
821
    DBG_PRINT1("%%  ERROR=%d", Result);
5219
14.8k
  _LExit:
5220
#   ifdef DEBUG
5221
    if (save_ox != NULL) {
5222
      mem->free(mem, save_ox, "RunIns");
5223
      mem->free(mem, save_oy, "RunIns");
5224
      mem->free(mem, save_cx, "RunIns");
5225
      mem->free(mem, save_cy, "RunIns");
5226
    }
5227
#   endif
5228
5229
14.8k
    return Result;
5230
821
  }