/* =====================================================================
* 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();
}
}