ApwFreetypeLibrary

Artifact [7f24eeff87]
Login

Artifact [7f24eeff87]

Artifact 7f24eeff87aaa164ce37bb20fe511663e3cb692d:


/* =====================================================================
 *  This Java implementation is derived from FreeType code
 *  Portions of this software are copyright (C) 2014 The FreeType
 *  Project (www.freetype.org).  All rights reserved.
 *
 *  Copyright (C) of the Java implementation 2014
 *  Arnulf Wiedemann arnulf at wiedemann-pri.de
 *
 *  See the file "license.terms" for information on usage and
 *  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 * =====================================================================
 */

package fttruetype;

  /* ===================================================================== */
  /*    TTInterpInsFuncs2                                                          */
  /*                                                                       */
  /* ===================================================================== */

import ftbase.FTCalc;
import ftbase.FTError;
import ftbase.FTReference;
import ftbase.FTVectorRec;
import ftbase.IUPWorkerRec;

public class TTInterpInsFuncs2 extends TTInterpInsFuncs1 {
    private static int oid = 0;

    private int id;
    private static String TAG = "TTInterpInsFuncs2";

    /* ==================== TTInterpInsFuncs2 ================================== */
    public TTInterpInsFuncs2() {
      oid++;
      id = oid;
    }
    
    /* ==================== mySelf ================================== */
    public String mySelf() {
      return  TAG+"!"+id+"!";
    }
        
    /* ==================== toString ===================================== */
    public String toString() {
      return mySelf()+"!";
    }

    /* ==================== toDebugString ===================================== */
    public String toDebugString() {
      StringBuffer str = new StringBuffer(mySelf()+"\n");
      return str.toString();
    }
 

    /* =====================================================================
     *
     * FLIPRGON[]:   FLIP RanGe ON
     * Opcode range: 0x81
     * Stack:        uint32 uint32 -->
     *
     * =====================================================================
     */
    static void insFLIPRGON() {
      Short I;
      Short K;
      Short L;

      K = (short)cur.stack[cur.numArgs + 1];
      L = (short)cur.stack[cur.numArgs + 0];
      if (BOUNDS((int)K, (int)cur.pts.n_points) || BOUNDS((int)L, (int)cur.pts.n_points)) {
        if (cur.pedantic_hinting) {
          cur.error = FTError.INTERP_INVALID_REFERENCE;
        }
        return;
      }
      for (I = L; I <= K; I++) {
        cur.pts.tags[I] |= FT_CURVE_TAG_ON;
      }
    }

    /* =====================================================================
     *
     * FLIPRGOFF:    FLIP RanGe OFF
     * Opcode range: 0x82
     * Stack:        uint32 uint32 -->
     *
     * =====================================================================
     */
    static void insFLIPRGOFF() {
      Short I;
      Short K;
      Short L;

      K = (short)cur.stack[cur.numArgs + 1];
      L = (short)cur.stack[cur.numArgs + 0];
      if (BOUNDS((int)K, (int)cur.pts.n_points) || BOUNDS((int)L, (int)cur.pts.n_points)) {
        if (cur.pedantic_hinting) {
          cur.error = FTError.INTERP_INVALID_REFERENCE;
        }
        return;
      }
      for (I = L; I <= K; I++) {
        cur.pts.tags[I] &= ~FT_CURVE_TAG_ON;
      }
    }

    private static boolean ComputePointDisplacement(FTReference<Long> x_ref, FTReference<Long> y_ref, FTReference<TTGlyphZoneRec> zone_ref, FTReference<Short> short_ref) {
      TTGlyphZoneRec zp;
      Short p;
      long d;
      Long x;
      Long y;

      if ((cur.opcode & 1) != 0) {
        zp = cur.zp0;
        p  = cur.GS.rp1;
      } else {
        zp = cur.zp1;
        p  = cur.GS.rp2;
      }
      if (BOUNDS((int)p, (int)zp.n_points)) {
        if (cur.pedantic_hinting) {
          cur.error = FTError.INTERP_INVALID_REFERENCE;
        }
        short_ref.Set((short)0);
        return FAILURE;
      }
      zone_ref.Set(zp);
      short_ref.Set(p);
      d = (long)cur.func_project.callClassMethod(zp.cur[zp.cur_idx + p].x - zp.org[zp.org_idx + p].x,
              zp.cur[zp.cur_idx + p].y - zp.org[zp.org_idx + p].y);
      x = FTCalc.FT_MulDiv(d, cur.GS.freeVector.x, cur.F_dot_P);
      x_ref.Set(x);
      y = FTCalc.FT_MulDiv(d, cur.GS.freeVector.y, cur.F_dot_P);
      y_ref.Set(y);
      return SUCCESS;
    }

    static void MoveZp2Point(Short point, Long dx, Long dy, boolean touch ) {
      if (cur.GS.freeVector.x != 0) {
        cur.zp2.cur[cur.zp2.cur_idx + point].x = (cur.zp2.cur[cur.zp2.cur_idx + point].x + dx);
        if (touch) {
          cur.zp2.tags[point] |= FT_CURVE_TAG_TOUCH_X;
        }
      }
      if (cur.GS.freeVector.y != 0) {
        cur.zp2.cur[cur.zp2.cur_idx + point].y = (cur.zp2.cur[cur.zp2.cur_idx + point].y + dy);
        if (touch) {
          cur.zp2.tags[point] |= FT_CURVE_TAG_TOUCH_Y;
        }
      }
    }

    /* =====================================================================
     *
     * SHP[a]:       SHift Point by the last point
     * Opcode range: 0x32-0x33
     * Stack:        uint32... -->
     *
     * =====================================================================
     */
    static void insSHP() {
      TTGlyphZoneRec zp;
      Short ref;
      Long dx;
      Long dy;
      Short point;
      FTReference<Long> dx_ref = new FTReference<Long>();  
      FTReference<Long> dy_ref = new FTReference<Long>();  
      FTReference<TTGlyphZoneRec> zp_ref = new FTReference<TTGlyphZoneRec>();  
      FTReference<Short> short_ref = new FTReference<Short>();  

      if (cur.top < cur.GS.loop) {
        if (cur.pedantic_hinting) {
          cur.error = FTError.INTERP_INVALID_REFERENCE;
        }
        cur.GS.loop = 1;
        cur.new_top = cur.numArgs;
        return;
      }
      if (ComputePointDisplacement(dx_ref, dy_ref, zp_ref, short_ref)) {
        return;
      }
      dx = dx_ref.Get();
      dy = dy_ref.Get();
      while (cur.GS.loop > 0) {
        cur.numArgs--;
        point = (short)cur.stack[cur.numArgs];
        if (BOUNDS((int)point, (int)cur.zp2.n_points)) {
          if (cur.pedantic_hinting) {
            cur.error = FTError.INTERP_INVALID_REFERENCE;
            return;
          }
        } else {
          MoveZp2Point(point, dx, dy, true);
        }
        cur.GS.loop--;
      }
      cur.GS.loop = 1;
      cur.new_top = cur.numArgs;
    }

    /* =====================================================================
     *
     * SHC[a]:       SHift Contour
     * Opcode range: 0x34-35
     * Stack:        uint32 -->
     *
     * UNDOCUMENTED: According to Greg Hitchcock, there is one (virtual)
     *               contour in the twilight zone, namely contour number
     *               zero which includes all points of it.
     *
     * =====================================================================
     */
    static void insSHC() {
      TTGlyphZoneRec zp;
      Short ref;
      Long dx;
      Long dy;
      Short contour;
      Short bounds;
      Short start;
      int limit;
      int i;
      FTReference<Long> dx_ref = new FTReference<Long>();  
      FTReference<Long> dy_ref = new FTReference<Long>();  
      FTReference<TTGlyphZoneRec> zp_ref = new FTReference<TTGlyphZoneRec>();  
      FTReference<Short> short_ref = new FTReference<Short>();  

      contour = (short)cur.stack[cur.numArgs + 0];
      bounds  = (cur.GS.gep2 == 0) ? 1 : cur.zp2.n_contours;
      if (BOUNDS((int)contour, (int)bounds)) {
        if (cur.pedantic_hinting) {
          cur.error = FTError.INTERP_INVALID_REFERENCE;
        }
        return;
      }
      if (ComputePointDisplacement(dx_ref, dy_ref, zp_ref, short_ref)) {
        return;
      }
      dx = dx_ref.Get();
      dy = dy_ref.Get();
      zp = zp_ref.Get();
      ref = short_ref.Get();
      if (contour == 0) {
        start = 0;
      } else {
        start = (short)(cur.zp2.contours[contour - 1] + 1 - cur.zp2.first_point);
      }
      /* we use the number of points if in the twilight zone */
      if (cur.GS.gep2 == 0) {
        limit = cur.zp2.n_points;
      } else {
        limit = (short)(cur.zp2.contours[contour] - cur.zp2.first_point + 1);
      }
      for (i = start; i < limit; i++) {
        if (zp.cur != cur.zp2.cur || ref != i) {
          MoveZp2Point((short)i, dx, dy, true);
        }
      }
    }

    /* =====================================================================
     *
     * SHZ[a]:       SHift Zone
     * Opcode range: 0x36-37
     * Stack:        uint32 -->
     *
     * =====================================================================
     */
    static void insSHZ() {
      TTGlyphZoneRec zp;
      Short ref;
      Long dx;
      Long dy;
      int limit;
      int i;
      FTReference<Long> dx_ref = new FTReference<Long>();  
      FTReference<Long> dy_ref = new FTReference<Long>();  
      FTReference<TTGlyphZoneRec> zp_ref = new FTReference<TTGlyphZoneRec>();  
      FTReference<Short> short_ref = new FTReference<Short>();  

      if (BOUNDS((int)cur.stack[cur.numArgs + 0], 2)) {
        if (cur.pedantic_hinting) {
          cur.error = FTError.INTERP_INVALID_REFERENCE;
        }
        return;
      }
      if (ComputePointDisplacement(dx_ref, dy_ref, zp_ref, short_ref)) {
        return;
      }
      dx = dx_ref.Get();
      dy = dy_ref.Get();
      zp = zp_ref.Get();
      ref = short_ref.Get();
      /* XXX: UNDOCUMENTED! SHZ doesn't move the phantom points.     */
      /*      Twilight zone has no real contours, so use `n_points'. */
      /*      Normal zone's `n_points' includes phantoms, so must    */
      /*      use end of last contour.                               */
      if (cur.GS.gep2 == 0) {
        limit = (short)cur.zp2.n_points;
      } else {
        if (cur.GS.gep2 == 1 && cur.zp2.n_contours > 0) {
          limit = (short)(cur.zp2.contours[cur.zp2.n_contours - 1] + 1);
        } else {
          limit = 0;
        }
      }
      /* XXX: UNDOCUMENTED! SHZ doesn't touch the points */
      for (i = 0; i < limit; i++) {
        if (zp.cur != cur.zp2.cur || ref != i) {
          MoveZp2Point((short)i, dx, dy, false);
        }
      }
    }

    /* =====================================================================
     *
     * SHPIX[]:      SHift points by a PIXel amount
     * Opcode range: 0x38
     * Stack:        f26.6 uint32... -->
     *
     * =====================================================================
     */
    static void insSHPIX() {
      Long dx;
      Long dy;
      Short point;

      if (cur.top < cur.GS.loop + 1) {
        if (cur.pedantic_hinting) {
          cur.error = FTError.INTERP_INVALID_REFERENCE;
        }
        cur.GS.loop = 1;
        cur.new_top = cur.numArgs;
        return;
      }
      dx = TT_MulFix14((long)cur.stack[cur.numArgs + 0], (long)cur.GS.freeVector.x);
      dy = TT_MulFix14((long)cur.stack[cur.numArgs + 0], (long)cur.GS.freeVector.y);
      while (cur.GS.loop > 0) {
        cur.numArgs--;
        point = (short)cur.stack[cur.numArgs];
        if (BOUNDS((int)point, (int)cur.zp2.n_points)) {
          if (cur.pedantic_hinting) {
            cur.error = FTError.INTERP_INVALID_REFERENCE;
            return;
          }
        } else {
          MoveZp2Point(point, dx, dy, true);
        }
        cur.GS.loop--;
      }
      cur.GS.loop = 1;
      cur.new_top = cur.numArgs;
    }

    /* =====================================================================
     *
     * MSIRP[a]:     Move Stack Indirect Relative Position
     * Opcode range: 0x3A-0x3B
     * Stack:        f26.6 uint32 -->
     *
     * =====================================================================
     */
    static void insMSIRP() {
      Short point;
      Long distance;

      point = (short)cur.stack[cur.numArgs + 0];
      if (BOUNDS((int)point, (int)cur.zp1.n_points) || BOUNDS((int)cur.GS.rp0, (int)cur.zp0.n_points)) {
        if (cur.pedantic_hinting) {
          cur.error = FTError.INTERP_INVALID_REFERENCE;
        }
        return;
      }
      /* UNDOCUMENTED!  The MS rasterizer does that with */
      /* twilight points (confirmed by Greg Hitchcock)   */
      if (cur.GS.gep1 == 0) {
        cur.zp1.org[cur.zp1.org_idx + point] = cur.zp0.org[cur.zp0.org_idx + cur.GS.rp0];
        cur.func_move_orig.callClassMethod(cur.zp1, point, (long)cur.stack[cur.numArgs + 1]);
        cur.zp1.cur[cur.zp1.cur_idx + point] = cur.zp1.org[cur.zp1.org_idx + point];
      }
      distance = (long)cur.func_project.callClassMethod(cur.zp1.cur[cur.zp1.cur_idx + point].x - cur.zp0.cur[cur.zp0.cur_idx + cur.GS.rp0].x,
              cur.zp1.cur[cur.zp1.cur_idx + point].y - cur.zp0.cur[cur.zp0.cur_idx + cur.GS.rp0].y);
      cur.func_move.callClassMethod(cur.zp1, point, (long)(cur.stack[cur.numArgs + 1] - distance));
      cur.GS.rp1 = cur.GS.rp0;
      cur.GS.rp2 = point;
      if ((cur.opcode & 1) != 0) {
        cur.GS.rp0 = point;
      }
    }

    /* =====================================================================
     *
     * MDAP[a]:      Move Direct Absolute Point
     * Opcode range: 0x2E-0x2F
     * Stack:        uint32 -->
     *
     * =====================================================================
     */
    static void insMDAP() {
      Short  point;
      long cur_dist;
      long distance;

Debug(0, DBG_INTERP, TAG, String.format("insMDAP: cur.GS.gep0: %d, cur.GS.gep1: %d", cur.GS.gep0, cur.GS.gep1));
      point = (short)cur.stack[cur.numArgs + 0];
      if (BOUNDS((int)point, (int)cur.zp0.n_points)) {
        if (cur.pedantic_hinting) {
          cur.error = FTError.INTERP_INVALID_REFERENCE;
        }
        return;
      }
      if ((cur.opcode & 1) != 0) {
        cur_dist = ((Long)cur.func_project.callClassMethod(cur.zp0.cur[cur.zp0.cur_idx + point].x,
                cur.zp0.cur[cur.zp0.cur_idx + point].y));
        distance = (((Long)cur.func_round.callClassMethod(cur_dist, (long)cur.tt_metrics.compensations[0])) - cur_dist);
Debug(0, DBG_INTERP, TAG, String.format("cur_dist: %d, distance: %d comp: %d \n", cur_dist, distance, cur.tt_metrics.compensations[0]));
      } else {
        distance = 0;
      }
      cur.func_move.callClassMethod(cur.zp0, (short)point, distance);
      cur.GS.rp0 = point;
      cur.GS.rp1 = point;
Debug(0, DBG_INTERP, TAG, String.format("insMDAP end: point: %d distance: %d, cur.GS.rp0: %d, cur.GS.rp1: %d\n", point, distance, cur.GS.rp0, cur.GS.rp1));
    }

    /* =====================================================================
     *
     * MIAP[a]:      Move Indirect Absolute Point
     * Opcode range: 0x3E-0x3F
     * Stack:        uint32 uint32 -->
     *
     * =====================================================================
     */
    static void insMIAP() {
      Long cvtEntry;
      Short point;
      int distance;
      int org_dist;
      Long control_value_cutin;

      control_value_cutin = (long)cur.GS.control_value_cutin;
      cvtEntry = (long)cur.stack[cur.numArgs + 1];
      point = (short)cur.stack[cur.numArgs + 0];
      if (BOUNDS((int)point, (int)cur.zp0.n_points) || BOUNDSL((long)cvtEntry, (long)cur.cvtSize)) {
        if (cur.pedantic_hinting) {
          cur.error = FTError.INTERP_INVALID_REFERENCE;
        }
        cur.GS.rp0 = point;
        cur.GS.rp1 = point;
        return;
      }
      /* UNDOCUMENTED!                                                      */
      /*                                                                    */
      /* The behaviour of an MIAP instruction is quite different when used  */
      /* in the twilight zone.                                              */
      /*                                                                    */
      /* First, no control value cut-in test is performed as it would fail  */
      /* anyway.  Second, the original point, i.e. (org_x,org_y) of         */
      /* zp0.point, is set to the absolute, unrounded distance found in the */
      /* CVT.                                                               */
      /*                                                                    */
      /* This is used in the CVT programs of the Microsoft fonts Arial,     */
      /* Times, etc., in order to re-adjust some key font heights.  It      */
      /* allows the use of the IP instruction in the twilight zone, which   */
      /* otherwise would be invalid according to the specification.         */
      /*                                                                    */
      /* We implement it with a special sequence for the twilight zone.     */
      /* This is a bad hack, but it seems to work.                          */
      /*                                                                    */
      /* Confirmed by Greg Hitchcock.                                       */
      distance = (int)cur.func_read_cvt.callClassMethod(cvtEntry.intValue(), 0);
      if (cur.GS.gep0 == 0) {  /* If in twilight zone */
        cur.zp0.org[cur.zp0.org_idx + point].x = (TT_MulFix14((long)distance, cur.GS.freeVector.x));
        cur.zp0.org[cur.zp0.org_idx + point].y = (TT_MulFix14((long)distance, cur.GS.freeVector.y));
        cur.zp0.cur[cur.zp0.cur_idx + point] = cur.zp0.org[cur.zp0.org_idx + point];
      }
      org_dist = (int)cur.func_project.callClassMethod(cur.zp0.cur[cur.zp0.cur_idx + point].x - cur.zp0.cur[cur.zp0.cur_idx + point].x,
              cur.zp0.cur[cur.zp0.cur_idx + point].y - cur.zp0.cur[cur.zp0.cur_idx + point].y);
      if ((cur.opcode & 1) != 0) {  /* rounding and control cut-in flag */
        if (FTCalc.FT_ABS((long)(distance - org_dist)) > control_value_cutin) {
          distance = org_dist;
        }
        distance = (int)cur.func_round.callClassMethod(distance, cur.tt_metrics.compensations[0]);
      }
      cur.func_move.callClassMethod(cur.zp0, point, distance - org_dist);
      cur.GS.rp0 = point;
      cur.GS.rp1 = point;
    }

    /* =====================================================================
     *
     * MDRP[abcde]:  Move Direct Relative Point
     * Opcode range: 0xC0-0xDF
     * Stack:        uint32 -->
     *
     * =====================================================================
     */
    static void insMDRP() {
      short point;
      long org_dist;
      long distance;
      long minimum_distance;

Debug(0, DBG_INTERP, TAG, String.format("insMDRP: cur.GS.gep0: %d, cur.GS.gep1: %d", cur.GS.gep0, cur.GS.gep1));
      minimum_distance = cur.GS.minimum_distance;
      point = (short)cur.stack[cur.numArgs + 0];
Debug(0, DBG_INTERP, TAG, String.format("insMDRP: minimum_distance: %d point: %d rp0: %d",  minimum_distance, point, cur.GS.rp0));
      if (BOUNDS((int)point, (int)cur.zp1.n_points ) || BOUNDS((int)cur.GS.rp0, (int)cur.zp0.n_points)) {
        if (cur.pedantic_hinting) {
          cur.error = FTError.INTERP_INVALID_REFERENCE;
        }
        cur.GS.rp1 = cur.GS.rp0;
        cur.GS.rp2 = point;
        if ((cur.opcode & 16) != 0) {
          cur.GS.rp0 = point;
        }
        return;
      }
      /* XXX: Is there some undocumented feature while in the */
      /*      twilight zone?                                  */
      /* XXX: UNDOCUMENTED: twilight zone special case */
Debug(0, DBG_INTERP, TAG, String.format("cur.GS.gep0: %d, cur.GS.gep1: %d\n", cur.GS.gep0, cur.GS.gep1));
      if (cur.GS.gep0 == 0 || cur.GS.gep1 == 0) {
        FTVectorRec vec1 = cur.zp1.org[cur.zp1.org_idx + point];
        FTVectorRec vec2 = cur.zp0.org[cur.zp1.org_idx + cur.GS.rp0];

        org_dist = (long)cur.func_dualproj.callClassMethod(vec1.x - vec2.x, vec1.y - vec2.y);
      } else {
        FTVectorRec vec1 = cur.zp1.orus[cur.zp1.orus_idx + point];
        FTVectorRec vec2 = cur.zp0.orus[cur.zp1.orus_idx + cur.GS.rp0];

Debug(0, DBG_INTERP, TAG, String.format("vec1: %d %d, vec2: %d %d\n", vec1.x, vec1.y, vec2.x, vec2.y));
Debug(0, DBG_INTERP, TAG, String.format("x_scale: %d y_scale: %d", cur.metrics.x_scale, cur.metrics.y_scale));
        if (cur.metrics.x_scale == cur.metrics.y_scale) {
          /* this should be faster */
          org_dist = (long)cur.func_dualproj.callClassMethod(vec1.x - vec2.x, vec1.y - vec2.y);
Debug(0, DBG_INTERP, TAG, String.format("d1: %d", org_dist));
          org_dist = FT_MulFix(org_dist, cur.metrics.x_scale);
Debug(0, DBG_INTERP, TAG, String.format("d2: %d", org_dist));
        } else {
          FTVectorRec vec = new FTVectorRec();

          vec.x = FT_MulFix(vec1.x - vec2.x, cur.metrics.x_scale);
          vec.y = FT_MulFix(vec1.y - vec2.y, cur.metrics.y_scale);
          org_dist = (long)cur.func_dualproj.callClassMethod(vec.x, vec.y);
        }
      }
Debug(0, DBG_INTERP, TAG, "org_dist1: "+org_dist);
      /* single width cut-in test */
      if (FTCalc.FT_ABS(org_dist - cur.GS.single_width_value) < cur.GS.single_width_cutin) {
        if (org_dist >= 0) {
          org_dist = (long)cur.GS.single_width_value;
        } else {
          org_dist = -(long)cur.GS.single_width_value;
        }
      }
Debug(0, DBG_INTERP, TAG, "org_dist2: "+org_dist);
      /* round flag */
      if ((cur.opcode & 4) != 0) {
        distance = (long)cur.func_round.callClassMethod(org_dist, (long)(cur.tt_metrics.compensations[cur.opcode & 3]));
      } else {
        distance = RoundNone(org_dist, (long)(cur.tt_metrics.compensations[cur.opcode & 3])).intValue();
      }
Debug(0, DBG_INTERP, TAG, "distance1: "+distance);
      /* minimum distance flag */
      if ((cur.opcode & 8) != 0) {
        if (org_dist >= 0) {
          if (distance < minimum_distance) {
            distance = minimum_distance;
          }
        } else {
          if (distance > -minimum_distance) {
            distance = -minimum_distance;
          }
        }
      }
Debug(0, DBG_INTERP, TAG, "distance2: "+distance);
Debug(0, DBG_INTERP, TAG, String.format("insMDRP 3: cur.GS.rp0: %d, cur.GS.rp1: %d, cur.GS.gep0: %d, cur.GS.gep1: %d", cur.GS.rp0, cur.GS.rp1, cur.GS.gep0, cur.GS.gep1));
      /* now move the point */
      org_dist = (long)cur.func_project.callClassMethod(cur.zp1.cur[cur.zp1.cur_idx + point].x - cur.zp0.cur[cur.zp0.cur_idx + cur.GS.rp0].x,
              cur.zp1.cur[cur.zp1.cur_idx + point].y - cur.zp0.cur[cur.zp0.cur_idx + cur.GS.rp0].y);
Debug(0, DBG_INTERP, TAG, "org_dist3: "+org_dist);
      cur.func_move.callClassMethod(cur.zp1 , point,(long)(distance - org_dist));

Debug(0, DBG_INTERP, TAG, String.format("insMDRP 5: cur.GS.rp0: %d, cur.GS.rp1: %d, cur.GS.rp2: %d", cur.GS.rp0, cur.GS.rp1, cur.GS.rp2));
      cur.GS.rp1 = cur.GS.rp0;
      cur.GS.rp2 = point;
Debug(0, DBG_INTERP, TAG, String.format("insMDRP 6: cur.GS.rp0: %d, cur.GS.rp1: %d, cur.GS.rp2: %d opcode: 0x%04x", cur.GS.rp0, cur.GS.rp1, cur.GS.rp2, cur.opcode));
      if ((cur.opcode & 16) != 0) {
Debug(0, DBG_INTERP, TAG, String.format("insMDRP 7: cur.GS.rp0: %d, cur.GS.rp1: %d, cur.GS.rp2: %d", cur.GS.rp0, cur.GS.rp1, cur.GS.rp2));
        cur.GS.rp0 = point;
      }
Debug(0, DBG_INTERP, TAG, String.format("insMDRP end: cur.GS.rp0: %d, cur.GS.rp1: %d", cur.GS.rp0, cur.GS.rp1));
    }

    /* =====================================================================
     *
     * MIRP[abcde]:  Move Indirect Relative Point
     * Opcode range: 0xE0-0xFF
     * Stack:        int32? uint32 -->
     *
     * =====================================================================
     */
    static void insMIRP() {
      short point;
      long cvtEntry;
      long cvt_dist;
      long distance;
      long cur_dist;
      long org_dist;
      int control_value_cutin;
      long minimum_distance;

Debug(0, DBG_INTERP, TAG, String.format("Ins_MIRP: cur.GS.rp0: %d, cur.GS.rp1: %d", cur.GS.rp0, cur.GS.rp1));
      minimum_distance = cur.GS.minimum_distance;
      control_value_cutin = cur.GS.control_value_cutin;
      point = (short)cur.stack[cur.numArgs + 0];
      cvtEntry = (long)(cur.stack[cur.numArgs + 1] + 1);
Debug(0, DBG_INTERP, TAG, String.format("Ins_MIRP: minimum_distance: %d, control_value_cutin: %d, point: %d, cvtEntry: %d", minimum_distance, control_value_cutin, point, cvtEntry));
      /* XXX: UNDOCUMENTED! cvt[-1] = 0 always */
      if (BOUNDS((int)point, (int)cur.zp1.n_points) ||
           BOUNDSL((long)cvtEntry, (long)cur.cvtSize + 1) ||
           BOUNDS((int)cur.GS.rp0, (int)cur.zp0.n_points)) {
        if (cur.pedantic_hinting) {
          cur.error = FTError.INTERP_INVALID_REFERENCE;
        }
        cur.GS.rp1 = cur.GS.rp0;
        if ((cur.opcode & 16) != 0) {
          cur.GS.rp0 = point;
        }
        cur.GS.rp2 = point;
        return;
      }
      if (cvtEntry == 0) {
        cvt_dist = 0L;
      } else {
        cvt_dist = (long)((int)cur.func_read_cvt.callClassMethod((int)(cvtEntry - 1), 0));
      }
Debug(0, DBG_INTERP, TAG, String.format("cvtEntry: %d, cvt_dist: %d", cvtEntry, cvt_dist));
      /* single width test */
      if (FTCalc.FT_ABS(cvt_dist - cur.GS.single_width_value) < cur.GS.single_width_cutin) {
        if (cvt_dist >= 0) {
          cvt_dist = (long)cur.GS.single_width_value;
        } else {
          cvt_dist = -(long)cur.GS.single_width_value;
        }
      }
Debug(0, DBG_INTERP, TAG, String.format("cvt_dist: %d, cur.GS.gep0: %d, cur.GS.gep1: %d", cvt_dist, cur.GS.gep0, cur.GS.gep1));
      /* UNDOCUMENTED!  The MS rasterizer does that with */
      /* twilight points (confirmed by Greg Hitchcock)   */
      if (cur.GS.gep1 == 0) {
        cur.zp1.org[cur.zp1.org_idx + point].x = (cur.zp0.org[cur.zp0.org_idx + cur.GS.rp0].x + TT_MulFix14(cvt_dist, cur.GS.freeVector.x));
        cur.zp1.org[cur.zp1.org_idx + point].y = (cur.zp0.org[cur.zp0.org_idx + cur.GS.rp0].y + TT_MulFix14(cvt_dist, cur.GS.freeVector.y));
        cur.zp1.cur[cur.zp1.cur_idx + point] = cur.zp1.org[cur.zp1.org_idx + point];
      }
Debug(0, DBG_INTERP, TAG, String.format("dualproj: point: %d, GS.rp0: %d, zp1.org[point].x: %d, zp1.org[point].y: %d, zp0.org[GS.rp0].x: %d, zp0.org[GS.rp0].y: %d", point, cur.GS.rp0, cur.zp1.org[cur.zp1.org_idx + point].x, cur.zp1.org[cur.zp1.org_idx + point].y, cur.zp0.org[cur.zp0.org_idx + cur.GS.rp0].x, cur.zp0.org[cur.zp0.org_idx + cur.GS.rp0].y));
      org_dist = (long)cur.func_dualproj.callClassMethod(cur.zp1.org[cur.zp1.org_idx + point].x - cur.zp0.org[cur.zp0.org_idx + cur.GS.rp0].x,
              cur.zp1.org[cur.zp1.org_idx + point].y - cur.zp0.org[cur.zp0.org_idx + cur.GS.rp0].y);
      cur_dist = (long)cur.func_project.callClassMethod(cur.zp1.cur[cur.zp1.cur_idx + point].x - cur.zp0.cur[cur.zp0.cur_idx + cur.GS.rp0].x,
              cur.zp1.cur[cur.zp1.cur_idx + point].y - cur.zp0.cur[cur.zp0.cur_idx + cur.GS.rp0].y);
      /* auto-flip test */
      if (cur.GS.auto_flip) {
        if ((org_dist ^ cvt_dist) < 0) {
          cvt_dist = -cvt_dist;
        }
      }
      /* control value cut-in and round */
      if ((cur.opcode & 4) != 0) {
        /* XXX: UNDOCUMENTED!  Only perform cut-in test when both points */
        /*      refer to the same zone.                                  */
        if (cur.GS.gep0 == cur.GS.gep1) {
          /* XXX: According to Greg Hitchcock, the following wording is */
          /*      the right one:                                        */
          /*                                                            */
          /*        When the absolute difference between the value in   */
          /*        the table [CVT] and the measurement directly from   */
          /*        the outline is _greater_ than the cut_in value, the */
          /*        outline measurement is used.                        */
          /*                                                            */
          /*      This is from `instgly.doc'.  The description in       */
          /*      `ttinst2.doc', version 1.66, is thus incorrect since  */
          /*      it implies `>=' instead of `>'.                       */
          if (FTCalc.FT_ABS(cvt_dist - org_dist) > control_value_cutin) {
            cvt_dist = (long)org_dist;
          }
        }
        distance = (long)cur.func_round.callClassMethod(cvt_dist, (long)cur.tt_metrics.compensations[cur.opcode & 3]);
      } else {
        distance = RoundNone(cvt_dist, (long)(cur.tt_metrics.compensations[cur.opcode & 3])).intValue();
      }
      /* minimum distance test */
      if ((cur.opcode & 8) != 0) {
        if (org_dist >= 0) {
          if (distance < minimum_distance) {
            distance = minimum_distance;
          }
        } else {
          if (distance > -minimum_distance) {
            distance = -minimum_distance;
          }
        }
      }
      cur.func_move.callClassMethod(cur.zp1, point, (long)(distance - cur_dist));
      cur.GS.rp1 = cur.GS.rp0;
      if ((cur.opcode & 16) != 0) {
        cur.GS.rp0 = point;
      }
      cur.GS.rp2 = point;
    }

    /* =====================================================================
     *
     * ALIGNRP[]:    ALIGN Relative Point
     * Opcode range: 0x3C
     * Stack:        uint32 uint32... -->
     *
     * =====================================================================
     */
    static void insALIGNRP() {
      short point;
      long  distance;

      if (cur.top < cur.GS.loop || BOUNDS((int)cur.GS.rp0, (int)cur.zp0.n_points)) {
        if (cur.pedantic_hinting) {
          cur.error = FTError.INTERP_INVALID_REFERENCE;
        }
        cur.GS.loop = 1;
        cur.new_top = cur.numArgs;
        return;
      }
      while (cur.GS.loop > 0) {
        cur.numArgs--;
        point = (short)cur.stack[cur.numArgs];
        if (BOUNDS((int)point, (int)cur.zp1.n_points)) {
          if (cur.pedantic_hinting) {
            cur.error = FTError.INTERP_INVALID_REFERENCE;
            return;
          }
        } else {
          distance = (long)cur.func_project.callClassMethod(cur.zp1.cur[cur.zp1.cur_idx + point].x - cur.zp0.cur[cur.zp0.cur_idx + cur.GS.rp0].x,
                  cur.zp1.cur[cur.zp1.cur_idx + point].y - cur.zp0.cur[cur.zp0.cur_idx + cur.GS.rp0].y);
Debug(0, DBG_INTERP, TAG, String.format("distance: %d, zp1.cur[point].x: %d, zp1.cur[point].x: %d, zp0.cur[rp0].x: %d, zp0.cur[rp0].y: %d", distance, cur.zp1.cur[cur.zp1.cur_idx + point].x, cur.zp1.cur[cur.zp1.cur_idx + point].y, cur.zp0.cur[cur.zp0.cur_idx + cur.GS.rp0].x, cur.zp0.cur[cur.zp0.cur_idx + cur.GS.rp0].y));
          cur.func_move.callClassMethod(cur.zp1, point, -distance);
        }
        cur.GS.loop--;
      }
      cur.GS.loop = 1;
      cur.new_top = cur.numArgs;
    }

    /* =====================================================================
     *
     * ISECT[]:      moves point to InterSECTion
     * Opcode range: 0x0F
     * Stack:        5 * uint32 -->
     *
     * =====================================================================
     */
    static void insISECT() {
      Short point;
      Short a0;
      Short a1;
      Short b0;
      Short b1;
      Long discriminant;
      Long dotproduct;

      int dx;
      int dy;
      int dax;
      int day;
      int dbx;
      int dby;
      Long val;
      FTVectorRec R = new FTVectorRec();

      point = (short)cur.stack[cur.numArgs + 0];
      a0 = (short)cur.stack[cur.numArgs + 1];
      a1 = (short)cur.stack[cur.numArgs + 2];
      b0 = (short)cur.stack[cur.numArgs + 3];
      b1 = (short)cur.stack[cur.numArgs + 4];

      if (BOUNDS((int)b0, (int)cur.zp0.n_points) ||
           BOUNDS((int)b1, (int)cur.zp0.n_points) ||
           BOUNDS((int)a0, (int)cur.zp1.n_points) ||
           BOUNDS((int)a1, (int)cur.zp1.n_points) ||
           BOUNDS((int)point, (int)cur.zp2.n_points)) {
        if (cur.pedantic_hinting) {
          cur.error = FTError.INTERP_INVALID_REFERENCE;
	}
        return;
      }
      /* Cramer's rule */
      dbx = (int)(cur.zp0.cur[cur.zp0.cur_idx + b1].x - cur.zp0.cur[cur.zp0.cur_idx + b0].x);
      dby = (int)(cur.zp0.cur[cur.zp0.cur_idx + b1].y - cur.zp0.cur[cur.zp0.cur_idx + b0].y);
      dax = (int)(cur.zp1.cur[cur.zp1.cur_idx + a1].x - cur.zp1.cur[cur.zp1.cur_idx + a0].x);
      day = (int)(cur.zp1.cur[cur.zp1.cur_idx + a1].y - cur.zp1.cur[cur.zp1.cur_idx + a0].y);
      dx = (int)(cur.zp0.cur[cur.zp0.cur_idx + b0].x - cur.zp1.cur[cur.zp1.cur_idx + a0].x);
      dy = (int)(cur.zp0.cur[cur.zp0.cur_idx + b0].y - cur.zp1.cur[cur.zp1.cur_idx + a0].y);
      cur.zp2.tags[point] |= (FT_CURVE_TAG_TOUCH_X | FT_CURVE_TAG_TOUCH_Y);
      discriminant = FTCalc.FT_MulDiv((long)dax, -(long)dby, 0x40L) + FTCalc.FT_MulDiv((long)day, (long)dbx, 0x40L);
      dotproduct = FTCalc.FT_MulDiv((long)dax, (long)dbx, 0x40L) + FTCalc.FT_MulDiv((long)day, (long)dby, 0x40L);
      /* The discriminant above is actually a cross product of vectors     */
      /* da and db. Together with the dot product, they can be used as     */
      /* surrogates for sine and cosine of the angle between the vectors.  */
      /* Indeed,                                                           */
      /*       dotproduct   = |da||db|cos(angle)                           */
      /*       discriminant = |da||db|sin(angle)     .                     */
      /* We use these equations to reject grazing intersections by         */
      /* thresholding abs(tan(angle)) at 1/19, corresponding to 3 degrees. */
      if (19 * FTCalc.FT_ABS(discriminant) > FTCalc.FT_ABS(dotproduct)) {
        val = FTCalc.FT_MulDiv((long)dx, -(long)dby, 0x40L) + FTCalc.FT_MulDiv((long)dy, (long)dbx, 0x40L);
        R.x = FTCalc.FT_MulDiv(val, (long)dax, discriminant);
        R.y = FTCalc.FT_MulDiv(val, (long)day, discriminant);
        cur.zp2.cur[cur.zp2.cur_idx + point].x = (cur.zp1.cur[cur.zp1.cur_idx + a0].x + R.x);
        cur.zp2.cur[cur.zp2.cur_idx + point].y = (cur.zp1.cur[cur.zp1.cur_idx + a0].y + R.y);
      } else {
        /* else, take the middle of the middles of A and B */
        cur.zp2.cur[cur.zp2.cur_idx + point].x = ((cur.zp1.cur[cur.zp1.cur_idx + a0].x + cur.zp1.cur[cur.zp1.cur_idx + a1].x +
                                 cur.zp0.cur[cur.zp0.cur_idx + b0].x + cur.zp0.cur[cur.zp0.cur_idx + b1].x) / 4);
        cur.zp2.cur[cur.zp2.cur_idx + point].y = ((cur.zp1.cur[cur.zp1.cur_idx + a0].y + cur.zp1.cur[cur.zp1.cur_idx + a1].y +
                                 cur.zp0.cur[cur.zp0.cur_idx + b0].y + cur.zp0.cur[cur.zp0.cur_idx + b1].y) / 4);
      }
    }

    /* =====================================================================
     *
     * ALIGNPTS[]:   ALIGN PoinTS
     * Opcode range: 0x27
     * Stack:        uint32 uint32 -->
     *
     * =====================================================================
     */
    static void insALIGNPTS() {
      short p1;
      short p2;
      long distance;

      p1 = (short)cur.stack[cur.numArgs + 0];
      p2 = (short)cur.stack[cur.numArgs + 1];
      if (BOUNDS((int)p1, (int)cur.zp1.n_points) || BOUNDS((int)p2, (int)cur.zp0.n_points)) {
        if (cur.pedantic_hinting) {
          cur.error = FTError.INTERP_INVALID_REFERENCE;
	}
        return;
      }
      distance = (int)cur.func_project.callClassMethod(cur.zp0.cur[cur.zp0.cur_idx + p2].x - cur.zp1.cur[cur.zp1.cur_idx + p1].x,
              cur.zp0.cur[cur.zp0.cur_idx + p2].y - cur.zp1.cur[cur.zp1.cur_idx + p1].y) / 2;
      cur.func_move.callClassMethod(cur.zp1, p1, distance);
      cur.func_move.callClassMethod(cur.zp0, p2, -distance);
    }

    /* =====================================================================
     *
     * IP[]:         Interpolate Point
     * Opcode range: 0x39
     * Stack:        uint32... -->
     *
     * SOMETIMES, DUMBER CODE IS BETTER CODE
     * =====================================================================
     */
    static void insIP() {
      long old_range;
      long cur_range;
      FTVectorRec orus_base;
      FTVectorRec cur_base;
      boolean twilight;

      if (cur.top < cur.GS.loop) {
        if (cur.pedantic_hinting) {
          cur.error = FTError.INTERP_INVALID_REFERENCE;
        }
        cur.GS.loop = 1;
        cur.new_top = cur.numArgs;
        return;
      }
      /*
       * We need to deal in a special way with the twilight zone.
       * Otherwise, by definition, the value of cur.twilight.orus[n] is (0,0),
       * for every n.
       */
      twilight = cur.GS.gep0 == 0 || cur.GS.gep1 == 0 || cur.GS.gep2 == 0;

      if (BOUNDS((int)cur.GS.rp1, (int)cur.zp0.n_points)) {
        if (cur.pedantic_hinting) {
          cur.error = FTError.INTERP_INVALID_REFERENCE;
        }
        cur.GS.loop = 1;
        cur.new_top = cur.numArgs;
        return;
      }
Debug(0, DBG_INTERP, TAG, String.format("twilight: %b, rp1: %d", twilight, cur.GS.rp1));
      if (twilight) {
        orus_base = cur.zp0.org[cur.zp0.org_idx + cur.GS.rp1];
      } else {
        orus_base = cur.zp0.orus[cur.zp0.orus_idx + cur.GS.rp1];
Debug(0, DBG_INTERP, TAG, String.format("orus.x: %d, orus.y: %d", orus_base.x,orus_base.y));
Debug(0, DBG_INTERP, TAG, String.format("orus2.x: %d, orus2.y: %d", cur.zp0.orus[cur.zp0.orus_idx + 1].x, cur.zp0.orus[cur.zp0.orus_idx + 1].y));
      }
      cur_base = cur.zp0.cur[cur.zp0.cur_idx + cur.GS.rp1];
      /* XXX: There are some glyphs in some braindead but popular */
      /*      fonts out there (e.g. [aeu]grave in monotype.ttf)   */
      /*      calling IP[] with bad values of rp[12].             */
      /*      Do something sane when this odd thing happens.      */
      if (BOUNDS((int)cur.GS.rp1, (int)cur.zp0.n_points) || BOUNDS((int)cur.GS.rp2, (int)cur.zp1.n_points)) {
        old_range = 0;
        cur_range = 0;
      } else {
        if (twilight) {
          old_range = (long)cur.func_dualproj.callClassMethod(cur.zp1.org[cur.zp1.org_idx + cur.GS.rp2].x - orus_base.x,
                  cur.zp1.org[cur.zp1.org_idx + cur.GS.rp2].y - orus_base.y);
        } else {
        System.out.println(String.format("x_scale: %d,  y_scale:%d", cur.metrics.x_scale, cur.metrics.y_scale));
          if (cur.metrics.x_scale == cur.metrics.y_scale) {
Debug(0, DBG_INTERP, TAG, String.format("rp2: %d, zp1.orus.x: %d, x: %d, zp1.orus.y: %d, y: %d", cur.GS.rp2, cur.zp1.orus[cur.zp1.orus_idx + cur.GS.rp2].x, orus_base.x,
cur.zp1.orus[cur.zp1.orus_idx + cur.GS.rp2].y, orus_base.y));            
            old_range = (long)cur.func_dualproj.callClassMethod(cur.zp1.orus[cur.zp1.orus_idx + cur.GS.rp2].x - orus_base.x,
                    cur.zp1.orus[cur.zp1.orus_idx + cur.GS.rp2].y - orus_base.y);
Debug(0, DBG_INTERP, TAG, String.format("old_range: %d, rp2.x: %d, rp2.y: %d, orus.x: %d, orus.y: %d", old_range, cur.zp1.orus[cur.zp1.orus_idx + cur.GS.rp2].x, cur.zp1.orus[cur.zp1.orus_idx + cur.GS.rp2].y, orus_base.x, orus_base.y));
          } else {
            FTVectorRec vec = new FTVectorRec();

            vec.x = FT_MulFix(cur.zp1.orus[cur.zp1.orus_idx + cur.GS.rp2].x - orus_base.x, cur.metrics.x_scale);
            vec.y = FT_MulFix(cur.zp1.orus[cur.zp1.orus_idx + cur.GS.rp2].y - orus_base.y, cur.metrics.y_scale);
            old_range = (int)cur.func_dualproj.callClassMethod(vec.x, vec.y);
          }
        }
        cur_range = (long)cur.func_project.callClassMethod(cur.zp1.cur[cur.zp1.cur_idx + cur.GS.rp2].x - cur_base.x,
                cur.zp1.cur[cur.zp1.cur_idx + cur.GS.rp2].y - cur_base.y);
      }
Debug(0, DBG_INTERP, TAG, String.format("old_range:%d cur_range: %d", old_range, cur_range));
      for (; cur.GS.loop > 0; --cur.GS.loop) {
        short point = (short)cur.stack[--cur.numArgs];
        long org_dist;
        long cur_dist;
        long new_dist;

        /* check point bounds */
        if (BOUNDS((int)point, (int)cur.zp2.n_points)) {
          if (cur.pedantic_hinting) {
            cur.error = FTError.INTERP_INVALID_REFERENCE;
            return;
          }
          continue;
        }
        if (twilight) {
          org_dist = (long)cur.func_dualproj.callClassMethod(cur.zp2.org[cur.zp2.org_idx + point].x - orus_base.x,
                  cur.zp2.org[cur.zp2.org_idx + point].y - orus_base.y);
        } else {
       	  if (cur.metrics.x_scale == cur.metrics.y_scale) {
            org_dist = (long)cur.func_dualproj.callClassMethod(cur.zp2.orus[cur.zp2.orus_idx + point].x - orus_base.x,
                    cur.zp2.orus[cur.zp2.orus_idx + point].y - orus_base.y);
          } else {
            FTVectorRec vec = new FTVectorRec();

            vec.x = FT_MulFix(cur.zp2.orus[cur.zp2.orus_idx + point].x - orus_base.x,
                               cur.metrics.x_scale);
            vec.y = FT_MulFix( cur.zp2.orus[cur.zp2.orus_idx + point].y - orus_base.y,
                               cur.metrics.y_scale);
            org_dist = (long)cur.func_dualproj.callClassMethod(vec.x, vec.y);
          }
        }
        cur_dist = (long)cur.func_project.callClassMethod(cur.zp2.cur[cur.zp2.cur_idx + point].x - cur_base.x,
                cur.zp2.cur[cur.zp2.cur_idx + point].y - cur_base.y);
        if (org_dist != 0) {
          if (old_range != 0) {
            new_dist = FTCalc.FT_MulDiv((long)org_dist, (long)cur_range, (long)old_range);
          } else {
            /* This is the same as what MS does for the invalid case:  */
            /*                                                         */
            /*   delta = (Original_Pt - Original_RP1) -                */
            /*           (Current_Pt - Current_RP1)                    */
            /*                                                         */
            /* In FreeType speak:                                      */
            /*                                                         */
            /*   new_dist = cur_dist -                                 */
            /*              org_dist - cur_dist;                       */
            new_dist = -org_dist;
          }
        } else {
          new_dist = 0;
        }
        cur.func_move.callClassMethod(cur.zp2, point, new_dist - cur_dist);
      }
      cur.GS.loop = 1;
      cur.new_top = cur.numArgs;
    }

    /* =====================================================================
     *
     * UTP[a]:       UnTouch Point
     * Opcode range: 0x29
     * Stack:        uint32 -->
     *
     * =====================================================================
     */
    static void insUTP() {
      Short point;
      byte mask;

      point = (short)cur.stack[cur.numArgs + 0];
      if (BOUNDS((int)point, (int)cur.zp0.n_points)) {
        if (cur.pedantic_hinting) {
          cur.error = FTError.INTERP_INVALID_REFERENCE;
        }
        return;
      }
      mask = (byte)0xFF;
      if (cur.GS.freeVector.x != 0) {
        mask &= ~FT_CURVE_TAG_TOUCH_X;
      }
      if (cur.GS.freeVector.y != 0) {
        mask &= ~FT_CURVE_TAG_TOUCH_Y;
      }
      cur.zp0.tags[point] &= mask;
    }

    /* ======================== insUNKNOWN ============================== */
    protected static void insUNKNOWN() {
      TTDefRec def = cur.IDefs[0];
      int limit = cur.numIDefs;
      int defIdx;

      for (defIdx = 0; defIdx < limit; defIdx++) {
        if (def.opc == cur.opcode && def.active) {
          TTCallRec call;

          if (cur.callTop >= cur.callSize) {
            cur.error = FTError.INTERP_STACK_OVERFLOW;
            return;
          }
          call = cur.callStack[cur.callTop++];
          call.CallerRange = cur.curRange;
          call.CallerIP = cur.IP + 1;
          call.CurCount = 1;
          call.CurRestart = def.start;
          call.CurEnd = def.end;
          insGotoCodeRange(def.range, (long)def.start);
          cur.step_ins = false;
          return;
        }
      }
      cur.error = FTError.INTERP_INVALID_OPCODE;
    }

    /* =====================================================================
     * _iup_worker_shift
     * =====================================================================
     */
    private static void _iup_worker_shift(FTReference<IUPWorkerRec> worker_ref, Integer p1, Integer p2, Integer p, boolean useX) {
      int i;
      long dx;
      IUPWorkerRec worker = worker_ref.Get();

      if (useX) {
        dx = worker.curs[worker.cur_idx + p].x - worker.orgs[worker.org_idx + p].x;
      } else {
        dx = worker.curs[worker.cur_idx + p].y - worker.orgs[worker.org_idx + p].y;        
      }
      if (dx != 0) {
        for (i = p1; i < p; i++) {
          if (useX) {
            worker.curs[worker.cur_idx + i].x = worker.curs[worker.cur_idx + i].x + dx;
          } else {
            worker.curs[worker.cur_idx + i].y = worker.curs[worker.cur_idx + i].y + dx;            
          }
        }
        for (i = p + 1; i <= p2; i++) {
          if (useX) {
            worker.curs[worker.cur_idx + i].x = worker.curs[worker.cur_idx + i].x + dx;
          } else {
            worker.curs[worker.cur_idx + i].y = worker.curs[worker.cur_idx + i].y + dx;            
          }
        }
      }
      worker_ref.Set(worker);
    }

    /* =====================================================================
     * _iup_worker_interpolate
     * =====================================================================
     */
    private static void _iup_worker_interpolate(FTReference<IUPWorkerRec> worker_ref, int p1, int p2,
           int ref1, int ref2, boolean useX) {
      IUPWorkerRec worker = worker_ref.Get();
      int i;
      long orus1;
      long orus2;
      long org1;
      long org2;
      long delta1;
      long delta2;

Debug(0, DBG_INTERP, TAG, String.format("_iup_worker_interpolate: p1: %d, p2: %d, ref1: %d ref2: %d", p1, p2, ref1, ref2));
      if (p1 > p2) {
        return;
      }
      if (BOUNDS((int)ref1, (int)worker.max_points) ||
           BOUNDS((int)ref2, (int)worker.max_points)) {
        return;
      }
      if (useX) {
        orus1 = worker.orus[worker.orus_idx + ref1].x;
        orus2 = worker.orus[worker.orus_idx + ref2].x;
      } else {
        orus1 = worker.orus[worker.orus_idx + ref1].y;
        orus2 = worker.orus[worker.orus_idx + ref2].y;        
      }
      if (orus1 > orus2) {
        long tmp_o;
        int tmp_r;

        tmp_o = orus1;
        orus1 = orus2;
        orus2 = tmp_o;
        tmp_r = ref1;
        ref1  = ref2;
        ref2  = tmp_r;
      }
      if (useX) {
        org1   = worker.orgs[worker.org_idx + ref1].x;
        org2   = worker.orgs[worker.org_idx + ref2].x;
        delta1 = worker.curs[worker.cur_idx + ref1].x - org1;
        delta2 = worker.curs[worker.org_idx + ref2].x - org2;
      } else {
        org1   = worker.orgs[worker.org_idx + ref1].y;
        org2   = worker.orgs[worker.org_idx + ref2].y;
        delta1 = worker.curs[worker.cur_idx + ref1].y - org1;
        delta2 = worker.curs[worker.org_idx + ref2].y - org2;
      }
Debug(0, DBG_INTERP, TAG, String.format("org1: %d,  org2: %d, delta1: %d, delta2: %d, orus1. %d, orus2: %d", org1, org2, delta1, delta2, orus1, orus2));
      if (orus1 == orus2) {
        /* simple shift of untouched points */
        for (i = p1; i <= p2; i++) {
          long x;

          if (useX) {
            x = worker.orgs[worker.org_idx + i].x;
          } else {
            x = worker.orgs[worker.org_idx + i].y;            
          }
          if (x <= org1) {
            x += delta1;
          } else {
            x += delta2;
          }
Debug(0, DBG_INTERP, TAG, String.format("i: %d curs[i].x: %d x: %d",  i, useX ? worker.curs[worker.cur_idx + i].x : worker.curs[worker.cur_idx + i].y, x));
          if (useX) {
            worker.curs[worker.cur_idx + i].x = x;
          } else {
            worker.curs[worker.cur_idx + i].y = x;            
          }
        }
      } else {
        long scale = 0L;
        boolean scale_valid = false;

Debug(0, DBG_INTERP, TAG, String.format("p1: %d, p2: %d", p1, p2));
        /* interpolation */
        for (i = p1; i <= p2; i++) {
          long x;

          if (useX) {
            x = worker.orgs[worker.org_idx + i].x;
          } else {
            x = worker.orgs[worker.org_idx + i].y;            
          }
Debug(0, DBG_INTERP, TAG, String.format("x: %d org1: %d, org2: %d, delta1: %d", x, org1, org2, delta1));
          if (x <= org1) {
            x += delta1;
          } else {
            if (x >= org2) {
              x += delta2;
            } else {
              if (!scale_valid) {
                scale_valid = true;
                scale = FTCalc.FTDivFix(org2 + delta2 - (org1 + delta1), orus2 - orus1);
              }
Debug(0, DBG_INTERP, TAG, String.format("scale: %d, org1: %d, delta1: %d, worker.orus[i].x: %d, orus1: %d", scale, org1, delta1, useX ?  worker.orus[worker.orus_idx + i].x :  worker.orus[worker.orus_idx + i].y, orus1));
              if (useX) {
                x = (org1 + delta1) + FT_MulFix(worker.orus[worker.orus_idx + i].x - orus1, scale);
              } else {
                x = (org1 + delta1) + FT_MulFix(worker.orus[worker.orus_idx + i].y - orus1, scale);              
              }
Debug(0, DBG_INTERP, TAG, String.format("x: %d", x));
            }
          }
          if (useX) {
            worker.curs[worker.cur_idx + i].x = x;
          } else {
            worker.curs[worker.cur_idx + i].y = x;            
          }
        }
      }
//FTGlyphLoaderRec._showLoaderZone("_iup_worker_interpolate end");
      worker_ref.Set(worker);
    }

    /* =====================================================================
     *
     * IUP[a]:       Interpolate Untouched Points
     * Opcode range: 0x30-0x31
     * Stack:        -->
     *
     * =====================================================================
     */
    protected static void insIUP() {
      IUPWorkerRec V = new IUPWorkerRec();
      FTReference<IUPWorkerRec> worker_ref;
      byte mask;
      int first_point;   /* first point of contour        */
      int end_point;     /* end point (last+1) of contour */
      int first_touched; /* first touched point in contour   */
      int cur_touched;   /* current touched point in contour */
      int point;         /* current point   */
      short contour;     /* current contour */
      boolean useX = true;

Debug(0, DBG_INTERP, TAG, "insIUP");
      /* ignore empty outlines */
      if (cur.pts.n_contours == 0) {
        return;
      }
//FTGlyphLoaderRec._showLoaderZone("insIUP");
      if ((cur.opcode & 1) != 0) {
        useX = true;
        mask = FT_CURVE_TAG_TOUCH_X;
        V.orgs = cur.pts.org;
        V.org_idx = cur.pts.org_idx + 0;
        V.curs = cur.pts.cur;
        V.cur_idx = cur.pts.cur_idx + 0;
        V.orus = cur.pts.orus;
        V.orus_idx = cur.pts.orus_idx + 0;
      } else {
        useX = false;
        mask = FT_CURVE_TAG_TOUCH_Y;
        V.orgs = cur.pts.org;
//        V.org_idx = cur.pts.org_idx + 1;
        V.org_idx = cur.pts.org_idx + 0;
        V.curs = cur.pts.cur;
//        V.cur_idx = cur.pts.cur_idx + 1;
        V.cur_idx = cur.pts.cur_idx + 0;
        V.orus = cur.pts.orus;
//        V.orus_idx = cur.pts.orus_idx + 1;
        V.orus_idx = cur.pts.orus_idx + 0;
      }
      V.max_points = (int)cur.pts.n_points;
      contour = 0;
      point = 0;
      do {
        end_point = cur.pts.contours[contour] - cur.pts.first_point;
        first_point = point;
        if (BOUNDS((int)end_point, (int)cur.pts.n_points)) {
          end_point = cur.pts.n_points - 1;
        }
        while (point <= end_point && (cur.pts.tags[point] & mask) == 0) {
          point++;
        }
        worker_ref = new FTReference<IUPWorkerRec>();
        worker_ref.Set(V);
        if (point <= end_point) {
          first_touched = point;
          cur_touched   = point;
          point++;
          while (point <= end_point) {
            if ((cur.pts.tags[point] & mask) != 0) {
              _iup_worker_interpolate(worker_ref, cur_touched + 1, point - 1, cur_touched, point, useX);
              cur_touched = point;
            }
            point++;
          }
          if (cur_touched == first_touched) {
            _iup_worker_shift(worker_ref, first_point, end_point, cur_touched, useX);
          } else {
            _iup_worker_interpolate(worker_ref, (cur_touched + 1), end_point, 
                    cur_touched, first_touched, useX);
            if (first_touched > 0) {
              _iup_worker_interpolate(worker_ref, first_point, first_touched - 1,
                      cur_touched, first_touched, useX);
            }
          }
          V = worker_ref.Get();            
        }
        contour++;
      } while (contour < cur.pts.n_contours);
//FTGlyphLoaderRec._showLoaderZone("insIUP END");
    }

    /* =====================================================================
     *
     * DELTAPn[]:    DELTA exceptions P1, P2, P3
     * Opcode range: 0x5D,0x71,0x72
     * Stack:        uint32 (2 * uint32)... -->
     *
     * =====================================================================
     */
    protected static void insDELTAP() {
      Long k;
      Long nump;
      Short A;
      Long C;
      Long B;

Debug(0, DBG_INTERP, TAG, "insDELTAP");
      nump = (long)cur.stack[cur.numArgs + 0];   /* some points theoretically may occur more
                                     than once, thus UShort isn't enough */
      for (k = 1L; k <= nump; k++) {
        if (cur.numArgs < 2) {
          if (cur.pedantic_hinting) {
            cur.error = FTError.INTERP_TOO_FEW_ARGUMENTS;
          }
          cur.numArgs = 0;
          cur.new_top = cur.numArgs;
Debug(0, DBG_INTERP, TAG, "insDELTAP end 0");
          return;
        }
        cur.numArgs -= 2;
        A = (short)cur.stack[cur.numArgs + 1];
        B = cur.stack[cur.numArgs];
        /* XXX: Because some popular fonts contain some invalid DeltaP */
        /*      instructions, we simply ignore them when the stacked   */
        /*      point reference is off limit, rather than returning an */
        /*      error.  As a delta instruction doesn't change a glyph  */
        /*      in great ways, this shouldn't be a problem.            */
        if (!BOUNDS((int)A, (int)cur.zp0.n_points)) {
          C = ((long)B & 0xF0) >> 4;
          switch (cur.opcode) {
          case 0x5D:
            break;
          case 0x71:
            C += 16;
            break;
          case 0x72:
            C += 32;
            break;
          }
          C += cur.GS.delta_base;
          if (CurrentPpem() == (long)C) {
            B = ((long)B & 0xF) - 8;
            if (B >= 0) {
              B++;
            }
            B = B * 64 / (1L << cur.GS.delta_shift);
            cur.func_move.callClassMethod(cur.zp0, A.shortValue(), B.longValue());
          }
        } else {
          if (cur.pedantic_hinting) {
            cur.error = FTError.INTERP_INVALID_REFERENCE;
          }
        }
      }
Debug(0, DBG_INTERP, TAG, "insDELTAP end 1");
      cur.new_top = cur.numArgs;
Debug(0, DBG_INTERP, TAG, "insDELTAP end 2");
    }

    /* =====================================================================
     *
     * DELTACn[]:    DELTA exceptions C1, C2, C3
     * Opcode range: 0x73,0x74,0x75
     * Stack:        uint32 (2 * uint32)... -->
     *
     * =====================================================================
     */
    protected static void insDELTAC() {
      Long nump;
      Long k;
      Long A;
      Long C;
      Long B;

      nump = (long)cur.stack[cur.numArgs + 0];
      for (k = 1L; k <= nump; k++) {
        if (cur.numArgs < 2) {
          if (cur.pedantic_hinting) {
            cur.error = FTError.INTERP_TOO_FEW_ARGUMENTS;
          }
          cur.numArgs = 0;
          cur.new_top = cur.numArgs;
          return;
        }
        cur.numArgs -= 2;
        A = (long)cur.stack[cur.numArgs + 1];
        B = cur.stack[cur.numArgs];
        if (BOUNDSL(A, (long)cur.cvtSize)) {
          if (cur.pedantic_hinting) {
            cur.error = FTError.INTERP_INVALID_REFERENCE;
            return;
          }
        } else {
          C = ((long)B & 0xF0) >> 4;
          switch (cur.opcode) {
          case 0x73:
            break;
          case 0x74:
            C += 16;
            break;
          case 0x75:
            C += 32;
            break;
          }
          C += cur.GS.delta_base;
          if (CurrentPpem() == (long)C) {
            B = ((long)B & 0xF) - 8;
            if (B >= 0) {
              B++;
            }
            B = B * 64 / (1L << cur.GS.delta_shift);
            cur.func_move_cvt.callClassMethod(A.intValue(), B.intValue());
          }
        }
      }
      cur.new_top = cur.numArgs;
    }

    /* =====================================================================
     *
     * MISC. INSTRUCTIONS
     *
     * =====================================================================
     */

    /* =====================================================================
     *
     * GETINFO[]:    GET INFOrmation
     * Opcode range: 0x88
     * Stack:        uint32 --> uint32
     *
     * =====================================================================
     */
    protected static void insGETINFO() {
      Long K;

      K = 0L;
      if ((cur.stack[cur.numArgs + 0] & 1) != 0) {
          K = (long)TT_INTERPRETER_VERSION_35;
      }
      /********************************/
      /* GLYPH ROTATED                */
      /* Selector Bit:  1             */
      /* Return Bit(s): 8             */
      /*                              */
      if ((cur.stack[cur.numArgs + 0] & 2) != 0 && cur.tt_metrics.rotated) {
        K |= 0x80;
      }
      /********************************/
      /* GLYPH STRETCHED              */
      /* Selector Bit:  2             */
      /* Return Bit(s): 9             */
      /*                              */
      if ((cur.stack[cur.numArgs + 0] & 4) != 0 && cur.tt_metrics.stretched) {
        K |= 1 << 8;
      }
      /********************************/
      /* HINTING FOR GRAYSCALE        */
      /* Selector Bit:  5             */
      /* Return Bit(s): 12            */
      /*                              */
      if (((cur.stack[cur.numArgs + 0] & 32) != 0) && cur.grayscale) {
        K |= 1 << 12;
      }
      cur.stack[cur.numArgs + 0] = K.intValue();
    }
}