diff options
author | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
---|---|---|
committer | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
commit | 554fd8c5195424bdbcabf5de30fdc183aba391bd (patch) | |
tree | 976dc5ab7fddf506dadce60ae936f43f58787092 /libjava/classpath/gnu/java/awt/font/opentype/truetype/VirtualMachine.java | |
download | cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.bz2 cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.xz |
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig;
imported gcc-4.6.4 source tree from verified upstream tarball.
downloading a git-generated archive based on the 'upstream' tag
should provide you with a source tree that is binary identical
to the one extracted from the above tarball.
if you have obtained the source via the command 'git clone',
however, do note that line-endings of files in your working
directory might differ from line-endings of the respective
files in the upstream repository.
Diffstat (limited to 'libjava/classpath/gnu/java/awt/font/opentype/truetype/VirtualMachine.java')
-rw-r--r-- | libjava/classpath/gnu/java/awt/font/opentype/truetype/VirtualMachine.java | 1815 |
1 files changed, 1815 insertions, 0 deletions
diff --git a/libjava/classpath/gnu/java/awt/font/opentype/truetype/VirtualMachine.java b/libjava/classpath/gnu/java/awt/font/opentype/truetype/VirtualMachine.java new file mode 100644 index 000000000..512c39ffd --- /dev/null +++ b/libjava/classpath/gnu/java/awt/font/opentype/truetype/VirtualMachine.java @@ -0,0 +1,1815 @@ +/* VirtualMachine.java -- Virtual machine for TrueType bytecodes. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.java.awt.font.opentype.truetype; + +import gnu.java.lang.CPStringBuilder; + +import java.awt.FontFormatException; +import java.awt.geom.AffineTransform; +import java.nio.ByteBuffer; +import java.nio.ShortBuffer; + + +/** + * A virtual machine for interpreting TrueType bytecodes. + * + * <p><b>Lack of Thread Safety:</b> The virtual machine is + * intentionally <i>not</i> safe to access from multiple concurrent + * threads. Synchronization needs to be performed externally. Usually, + * the font has already obtained a lock before calling the scaler, + * which in turn calls the VM. It would be wasteful to acquire + * additional locks for the VM. + * + * <p><b>Implementation Status:</b> The current implementation can + * execute pre-programs of fonts, but it does not yet actually move + * any points. Control flow and arithmeti instructions are + * implemented, but most geometric instructions are not working + * yet. So, the VirtualMachine class is currently a no-op. However, + * not very much is missing. You are more than welcome to complete the + * implementation. + * + * <p><b>Patents:</b> Apple Computer holds three United States Patents + * for the mathematical algorithms that are used by TrueType + * instructions. The monopoly granted by these patents will expire in + * October 2009. Before the expiration date, a license must be + * obtained from Apple Computer to use the patented technology inside + * the United States. For other countries, different dates might + * apply, or no license might be needed. + * + * <p>The default build of this class does not use the patented + * algorithms. If you have obtained a license from Apple, or if the + * patent protection has expired, or if no license is required for + * your contry, you can set a flag in the source file which will + * enable the use of the patented mathematical algorithms.</p> + * + * <p>The relevant patents are listed subsequently.</p> + * + * <p><ol><li>United States Patent 5155805, <i>Method and Apparatus + * for Moving Control Points in Displaying Digital Typeface on Raster + * Output Devices,</i> invented by Sampo Kaasila, assigned to Apple + * Computer. Filing date: May 8, 1989. Date of patent: October 13, + * 1992.</li> + * + * <li>United States Patent 5159668, <i>Method and Apparatus for + * Manipulating Outlines in Improving Digital Typeface on Raster + * Output Devices,</i> invented by Sampo Kaasila, assigned to Apple + * Computer. Filing date: May 8, 1989. Date of patent: October 27, + * 1992.</li> + * + * <li>United States Patent 5325479, <i>Method and Apparatus for + * Moving Control Points in Displaying Digital Typeface on Raster + * Output Devices,</i> invented by Sampo Kaasila, assigned to Apple + * Computer. Filing date: May 28, 1989. Date of patent: June 28, 1994 + * (with a statement that “[t]he portion of the term of this + * patent subsequent to Oct. 13, 2009 has been + * disclaimed”).</li></ol> + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +class VirtualMachine +{ + /** + * Indicates whether or not to perform hinting operations that are + * protected by a number of US patents, two of which will expire on + * October 13, 2009, and one of which will expire on October 27, + * 2009. + */ + private final static boolean PATENTED_HINTING = false; + + + /** + * Indicates whether the execution of the Virtual Machine is traced + * to System.out. + */ + private final static boolean TRACE_EXECUTION = false; + + + /** + * The value 1 in 2-dot-14 fixed notation. + */ + private static final short ONE_214 = 0x4000; // 1 << 14 + + + /** + * The storage area of the virtual machine. + */ + private final int[] storage; + + + /** + * The stack. The stack grows from bottom to top, so + * <code>sp[0]</code> gets used before <code>sp[1]</code>. + */ + private int[] stack; + + + /** + * The maximum number of stack elements. + */ + private final int maxStackElements; + + + /** + * The current stack pointer of the virtual machine. + */ + private int sp; + + + /** + * fdefBuffer[i] is the buffer that contains the TrueType + * instructions of function #i. Most of the time, functions are + * defined in the font program, but a font may also re-define + * functions in its CVT program. + */ + private ByteBuffer[] fdefBuffer; + + + /** + * fdefEntryPoint[i] is the position in fdefBuffer[i] where the + * first TrueType instruction after the FDEF is located. + */ + private int[] fdefEntryPoint; + + + /** + * The original Control Value Table, sometimes abbreviated as CVT. + * The table contains signed 16-bit FUnits. Some fonts have no CVT, + * in which case the field will be <code>null</code>. + */ + private ShortBuffer controlValueTable; + + + /** + * The scaled values inside the control value table. + */ + private int[] cvt; + + + /** + * A value that is used by rounding operations to compensate for dot + * gain. + */ + private int engineCompensation = 0; + + + /** + * The contents of the font’s <code>fpgm</code> table, or + * <code>null</code> after the font program has been executed once. + */ + private ByteBuffer fontProgram; + + + /** + * The <code>prep</code> table of the font, which contains a program + * that is executed whenever the point size or the device transform + * have changed. This program is called pre-program because it gets + * executed before the instructions of the individual glyphs. If + * the font does not contain a pre-program, the value of this field + * is <code>null</code>. + */ + private ByteBuffer preProgram; + + + /** + * The number of points in the Twilight Zone. + */ + private int numTwilightPoints; + + + /** + * The current point size of the scaled font. The value is in Fixed + * 26.6 notation. + */ + private int pointSize; // 26.6 + + private AffineTransform deviceTransform; + + private int scaleX, scaleY, shearX, shearY; // 26.6 + + + /** + * Indicates whether or not scan-line conversion will use + * anti-aliasing (with gray levels). Font programs can ask for this + * value with the <code>GETINFO</code> instruction, and some + * programs may behave differently according to this setting. + */ + private boolean antialiased; + + + /* Graphics State. FIXME: Move this to its own class? Some + * documentation would not hurt, either. + */ + private int cvtCutIn; // 26.6 + private int deltaBase; // uint32 + private int deltaShift; // uint32 + private short freeX; // 2.14 + private short freeY; // 2.14 + private int loop; // int + private int minimumDistance; // 26.6 + private short projX; // 2.14 + private short projY; // 2.14 + private short dualX; // 2.14 + private short dualY; // 2.14 + private int rp0, rp1, rp2; // point numbers + private boolean scanControl; + private int scanType; + private int singleWidthValue; // 26.6 + private Zone zp0, zp1, zp2; + + private Zone twilightZone; + private Zone glyphZone; + + + /** + * Indicates whether or not the instructions that are associated + * with individual glyphs shall be executed. Set as a side effect + * of executing the pre-program when the point size, device + * transform or some other relevant parameter have changed. + */ + private boolean executeGlyphInstructions; + + + /** + * Indicates whether to ignore any modifications to the control + * value table that the font’s pre-program might have + * performed. Set as a side effect of executing the pre-program + * when the point size, device transform or some other relevant + * parameter have changed. + */ + private boolean ignoreCVTProgram; + + + /** + * The length of the space between rounded values. A value + * of zero means that rounding has been switched off. + */ + private int roundPeriod; // 26.6 + + + /** + * The offset of the rounded values from multiples of + * <code>roundPeriod</code>. + */ + private int roundPhase; // 26.6 + + + private int roundThreshold; // 26.6 + + + /** + * A cache for the number of pixels per EM. The value is a normal + * integer, not a fixed point notation. + * + * @see #getPixelsPerEM() + */ + private int cachedPixelsPerEM; + + + /** + * The number of font units per EM. + */ + private int unitsPerEm; + + + /** + * Constructs a new Virtual Machine for executing TrueType + * instructions. + * + * @param unitsPerEm the number of font units in one typographic + * em. + * + * @param preProgram the <code>prep</code> table of the font, which + * contains a program that is executed whenever the point size or + * the device transform have changed. This program is called + * pre-program because it gets executed before the instructions of + * the individual glyphs. If the font does not contain a + * pre-program, pass <code>null</code>. + */ + VirtualMachine(int unitsPerEm, + ByteBuffer maxp, + ByteBuffer controlValueTable, + ByteBuffer fontProgram, + ByteBuffer preProgram) + throws FontFormatException + { + int maxStorage, numFunctionDefs, maxInstructionDefs; + + if (maxp.getInt(0) != 0x00010000) + throw new FontFormatException("unsupported maxp version"); + + this.unitsPerEm = unitsPerEm; + maxStorage = maxp.getChar(18); + + /* FreeType says that there exist some broken fonts (like + * "Keystrokes MT") that contain function defs, but have a zero + * value in their maxp table. + */ + numFunctionDefs = maxp.getChar(20); + if (numFunctionDefs == 0) + numFunctionDefs = 64; + fdefBuffer = new ByteBuffer[numFunctionDefs]; + fdefEntryPoint = new int[numFunctionDefs]; + + /* Read the contents of the Control Value Table. */ + if (controlValueTable != null) + this.controlValueTable = controlValueTable.asShortBuffer(); + + maxInstructionDefs = maxp.getChar(22); + maxStackElements = maxp.getChar(24); + storage = new int[maxStorage]; + this.fontProgram = fontProgram; + this.preProgram = preProgram; + numTwilightPoints = maxp.getChar(16); + } + + + /** + * Sets the graphics state to default values. + */ + private void resetGraphicsState() + { + /* The freedom, projection and dual vector default to the x axis. */ + freeX = projX = dualX = ONE_214; + freeY = projY = dualX = 0; + cachedPixelsPerEM = 0; + + cvtCutIn = 68; // 17/16 in 26.6 notation + deltaBase = 9; + deltaShift = 3; + loop = 1; + minimumDistance = Fixed.ONE; + singleWidthValue = 0; + rp0 = rp1 = rp2 = 0; + scanControl = false; + scanType = 2; + zp0 = zp1 = zp2 = getZone(1); + + setRoundingMode(Fixed.ONE, 0x48); // round to grid + } + + + /** + * Reloads the control value table and scales each entry from font + * units to pixel values. + */ + private void reloadControlValueTable() + { + /* Some TrueType fonts have no control value table. */ + if (controlValueTable == null) + return; + + /* Read in the Control Value Table. */ + if (cvt == null) + cvt = new int[controlValueTable.capacity()]; + + /* Scale the entries. */ + for (int i = 0; i < cvt.length; i++) + cvt[i] = funitsToPixels(controlValueTable.get(i)); + } + + + /** + * Scales a value from font unites to pixels. + * + * @return the scaled value. + */ + private int funitsToPixels(int funits) + { + return (int) (((long) funits * scaleY + (unitsPerEm>>1)) + / unitsPerEm); + } + + + /** + * Sets up the virtual machine for the specified parameters. If + * there is no change to the last set-up, the method will quickly + * return. Otherwise, the font’s pre-program will be + * executed. + * + * @param pointSize the point size of the scaled font. + * + * @param deviceTransform an affine transformation which gets + * applied in addition to scaling by <code>pointSize</code>. Font + * programs can separately inquire about the point size. For this + * reason, it is not recommended to pre-multiply the point size to + * the device transformation. + * + * @param antialiased <code>true</code> if the scan-line conversion + * algorithm will use gray levels to give a smoother appearance, + * <code>false</code> otherwise. Font programs can ask for this + * value with the <code>GETINFO</code> instruction, and some + * programs may behave differently according to this setting. + */ + public boolean setup(double pointSize, + AffineTransform deviceTransform, + boolean antialiased) + { + boolean changeCTM; + int pointSize_Fixed; + + if (stack == null) + stack = new int[maxStackElements]; + + if (twilightZone == null) + twilightZone = new Zone(numTwilightPoints); + + /* If the font program has not yet been executed, do so. */ + if (fontProgram != null) + { + resetGraphicsState(); + sp = -1; + execute(fontProgram, 0); + fontProgram = null; // prevent further execution + } + + /* Determine whether the transformation matrix has changed. */ + pointSize_Fixed = Fixed.valueOf(pointSize); + changeCTM = ((pointSize_Fixed != this.pointSize) + || !deviceTransform.equals(this.deviceTransform) + || (antialiased != this.antialiased)); + + if (changeCTM) + { + this.pointSize = pointSize_Fixed; + this.deviceTransform = deviceTransform; + this.antialiased = antialiased; + scaleX = (int) (deviceTransform.getScaleX() * pointSize * 64); + scaleY = (int) (deviceTransform.getScaleY() * pointSize * 64); + shearX = (int) (deviceTransform.getShearX() * pointSize * 64); + shearY = (int) (deviceTransform.getShearY() * pointSize * 64); + + resetGraphicsState(); + reloadControlValueTable(); + executeGlyphInstructions = true; + ignoreCVTProgram = false; + + if (preProgram != null) + { + sp = -1; + execute(preProgram, 0); + if (ignoreCVTProgram) + reloadControlValueTable(); + } + } + + return executeGlyphInstructions; + } + + + /** + * Executes a stream of TrueType instructions. + */ + private void execute(ByteBuffer instructions, int pos) + { + instructions.position(pos); + + // FIXME: SECURITY: Possible denial-of-service attack + // via instructions that have an endless loop. + while (instructions.hasRemaining() + && executeInstruction(instructions)) + ; + } + + + /** + * Writes a textual description of the current TrueType instruction, + * including the top stack elements, to <code>System.out</code>. + * This is useful for debugging. + * + * @param inst the instruction stream, positioned at the current + * instruction. + */ + private void dumpInstruction(ByteBuffer inst) + { + CPStringBuilder sbuf = new CPStringBuilder(40); + int pc = inst.position(); + int bcode = inst.get(pc) & 0xff; + int count; + int delta; + + char pcPrefix = 'c'; + for (int i = 0; i < fdefBuffer.length; i++) + { + if (fdefBuffer[i] == inst) + { + pcPrefix = 'f'; + break; + } + } + sbuf.append(pcPrefix); + + + sbuf.append(getHex((short) inst.position())); + sbuf.append(": "); + sbuf.append(getHex((byte) bcode)); + sbuf.append(" "); + sbuf.append(INST_NAME[bcode]); + + if (bcode == 0x40) // NPUSHB + { + count = inst.get(pc + 1) & 0xff; + sbuf.append(" ("); + sbuf.append(count); + sbuf.append(") "); + for (int i = 0; i < count; i++) + { + if (i > 0) + sbuf.append(" "); + sbuf.append('$'); + sbuf.append(getHex(inst.get(pc + 2 + i))); + } + } + if (bcode == 0x41) // NPUSHW + { + count = inst.get(pc + 1) & 0xff; + sbuf.append(" ("); + sbuf.append(count); + sbuf.append(") "); + for (int i = 0; i < count; i++) + { + if (i > 0) + sbuf.append(' '); + sbuf.append('$'); + sbuf.append(getHex(inst.getShort(pc + 2 + 2*i))); + } + } + else + { + count = getInstructionLength(bcode) - 1; + for (int i = 0; i < count; i++) + { + sbuf.append(" $"); + sbuf.append(getHex(inst.get(pc + 1 + i))); + } + } + + while (sbuf.length() < 30) + sbuf.append(' '); + sbuf.append('|'); + sbuf.append(sp + 1); + sbuf.append("| "); + for (int i = sp; i >= Math.max(0, sp - 5); i = i - 1) + { + if (i < sp) + sbuf.append(" "); + if ((stack[i] >> 16) != 0) + sbuf.append(getHex((short) (stack[i] >> 16))); + sbuf.append(getHex((short) stack[i])); + } + System.out.println(sbuf); + } + + + private static char getNibble(int i, int rightShift) + { + i = (i >> rightShift) & 15; + if (i < 10) + return (char) (i + '0'); + else + return (char) (i + 'a' - 10); + } + + + private static String getHex(byte b) + { + char[] a = new char[2]; + a[0] = getNibble(b, 4); + a[1] = getNibble(b, 0); + return new String(a); + } + + + private static String getHex(short b) + { + char[] a = new char[4]; + a[0] = getNibble(b, 12); + a[1] = getNibble(b, 8); + a[2] = getNibble(b, 4); + a[3] = getNibble(b, 0); + return new String(a); + } + + + /** + * Skips any instructions until the specified opcode has been + * encoutered. + * + * @param inst the current instruction stream. After the call, + * the position of <code>inst</code> is right after the first + * occurence of <code>opcode</code>. + * + * @param opcode1 the opcode for which to look. + * + * @param opcode2 another opcode for which to look. Pass -1 + * if only <code>opcode1</code> would terminate skipping. + * + * @param illegalCode1 an opcode that must not be encountered + * while skipping. Pass -1 if any opcode is acceptable. + * + * @param illegalCode2 another opcode that must not be encountered + * while skipping. Pass -1 to perform no check. + * + * @param handleNestedIfClauses <code>true</code> to handle + * nested <code>IF [ELSE] EIF</code> clauses, <code>false</code> + * to ignore them. From the TrueType specification document, + * one would think that nested if clauses would not be valid, + * but they do appear in some fonts. + * + * @throws IllegalStateException if <code>illegalCode1</code> or + * <code>illegalCode2</code> has been encountered while skipping. + */ + private static void skipAfter(ByteBuffer inst, + int opcode1, int opcode2, + int illegalCode1, int illegalCode2, + boolean handleNestedIfClauses) + { + int pos = inst.position(); + int curOpcode; + int instLen; + int nestingLevel = 0; // increased inside IF [ELSE] EIF sequences + + while (true) + { + curOpcode = inst.get(pos) & 0xff; + instLen = getInstructionLength(curOpcode); + + if (false && TRACE_EXECUTION) + { + for (int i = 0; i < nestingLevel; i++) + System.out.print("--"); + System.out.print("--" + pos + "-" + INST_NAME[curOpcode]); + if (nestingLevel > 0) + System.out.print(", ifNestingLevel=" + nestingLevel); + System.out.println(); + } + + if (curOpcode == 0x40) // NPUSHB + pos += 1 + (inst.get(pos + 1) & 0xff); + else if (curOpcode == 0x41) // NPUSHW + pos += 1 + 2 * (inst.get(pos + 1) & 0xff); + else + pos += instLen; + + if ((nestingLevel == 0) + && ((curOpcode == opcode1) || (curOpcode == opcode2))) + break; + + if (handleNestedIfClauses) + { + if (curOpcode == /* IF */ 0x58) + ++nestingLevel; + else if (curOpcode == /* EIF */ 0x59) + --nestingLevel; + } + + if ((nestingLevel < 0) + || (curOpcode == illegalCode1) + || (curOpcode == illegalCode2)) + throw new IllegalStateException(); + } + + inst.position(pos); + } + + + /** + * Returns the number of bytes that a TrueType instruction occupies. + * + * @param opcode the instruction. + * + * @return the number of bytes occupied by the instructions and its + * operands. For <code>NPUSHB</code> and <code>NPUSHW</code>, where + * the instruction length depends on the first operand byte, the + * result is -1. + */ + private static int getInstructionLength(int opcode) + { + /* NPUSHB, NPUSHW --> see following byte */ + if ((opcode == 0x40) || (opcode == 0x41)) + return -1; + + /* PUSHB[0] .. PUSHB[7] --> 2, 3, 4, 5, 6, 7, 8, 9 */ + if ((opcode >= 0xb0) && (opcode <= 0xb7)) + return opcode - 0xae; + + /* PUSHW[0] .. PUSHW[7] --> 3, 5, 6, 7, 11, 13, 15, 17*/ + if ((opcode >= 0xb8) && (opcode <= 0xbf)) + return 1 + ((opcode - 0xb7) << 1); + + return 1; + } + + + /** + * Executes a single TrueType instruction. This is the core + * routine of the Virtual Machine. + * + * @return <code>true</code> if another instruction shall be + * executed in the same call frame; <code>false</code> if the + * current call frame shall be popped. + */ + private boolean executeInstruction(ByteBuffer inst) + { + if (TRACE_EXECUTION) + dumpInstruction(inst); + + int i, count, e1, e2, e3, e4, x, y; + int bcode = inst.get() & 0xff; + + switch (bcode) + { + case 0x00: // SVTCA[0], Set freedom and proj. Vectors To Coord. Axis [y] + setFreedomVector((short) 0, ONE_214); + setProjectionVector((short) 0, ONE_214); + break; + + case 0x01: // SVTCA[1], Set freedom and proj. Vectors To Coord. Axis [x] + setFreedomVector(ONE_214, (short) 0); + setProjectionVector(ONE_214, (short) 0); + break; + + case 0x02: // SPVTCA[0], Set Projection Vector To Coordinate Axis [y] + setProjectionVector((short) 0, ONE_214); + break; + + case 0x03: // SPVTCA[1], Set Projection Vector To Coordinate Axis [x] + setProjectionVector(ONE_214, (short) 0); + break; + + case 0x0c: // GPV, Get Projection Vector + stack[++sp] = projX; + stack[++sp] = projY; + break; + + case 0x0d: // GPV, Get Freedom Vector + stack[++sp] = freeX; + stack[++sp] = freeY; + break; + + case 0x0F: // ISECT, move point p to the InterSECTION of two lines + sp -= 4; + handleISECT(stack[sp], stack[sp+1], stack[sp+2], + stack[sp+3], stack[sp+4]); + break; + + case 0x10: // SRP0, Set Reference Point 0 + rp0 = stack[sp--]; + break; + + case 0x11: // SRP1, Set Reference Point 1 + rp1 = stack[sp--]; + break; + + case 0x12: // SRP2, Set Reference Point 2 + rp2 = stack[sp--]; + break; + + case 0x13: // SZP0, Set Zone Pointer 0 + zp0 = getZone(stack[sp--]); + break; + + case 0x14: // SZP1, Set Zone Pointer 1 + zp1 = getZone(stack[sp--]); + break; + + case 0x15: // SZP2, Set Zone Pointer 2 + zp2 = getZone(stack[sp--]); + break; + + case 0x16: // SZPS, Set Zone PointerS + zp0 = zp1 = zp2 = getZone(stack[sp--]); + break; + + case 0x17: // SLOOP, Set LOOP variable + loop = stack[sp--]; + break; + + case 0x18: // RTG, Round To Grid + setRoundingMode(Fixed.ONE, 0x48); + break; + + case 0x19: // RTHG, Round To Half Grid + setRoundingMode(Fixed.ONE, 0x68); + break; + + case 0x1a: // SMD, Set Minimum Distance + minimumDistance = stack[sp--]; + break; + + case 0x1B: // ELSE, ELSE clause + skipAfter(inst, + /* look for: EIF, -- */ 0x59, -1, + /* illegal: --, -- */ -1, -1, + /* handle nested if clauses */ true); + break; + + case 0x1C: // JMPR, JuMP Relative + inst.position(inst.position() - 1 + stack[sp--]); + break; + + case 0x1D: // SCVTCI, Set Control Value Table Cut-In + cvtCutIn = stack[sp--]; + break; + + case 0x1F: // SSW, Set Single Width + singleWidthValue = stack[sp--]; + break; + + case 0x20: // DUP, DUPlicate top stack element + e1 = stack[sp]; + stack[++sp] = e1; + break; + + case 0x21: // POP, POP top stack element + sp--; + break; + + case 0x22: // CLEAR, CLEAR the stack + sp = -1; + break; + + case 0x23: // SWAP, SWAP the top two elements on the stack + e1 = stack[sp--]; + e2 = stack[sp]; + stack[sp] = e1; + stack[++sp] = e2; + break; + + case 0x24: // DEPTH, DEPTH of the stack + stack[++sp] = sp + 1; + break; + + case 0x25: // CINDEX, Copy the INDEXed element to the top of the stack + stack[sp] = stack[sp - stack[sp]]; + break; + + case 0x26: // MINDEX, Move the INDEXed element to the top of the stack + i = stack[sp]; + e1 = stack[sp - i]; + System.arraycopy(/* src */ stack, /* srcPos */ sp - i + 1, + /* dest */ stack, /* destPos*/ sp - i, + /* length */ i - 1); + --sp; + stack[sp] = e1; + break; + + case 0x2a: // LOOPCALL, LOOP and CALL function + i = stack[sp--]; + count = stack[sp--]; + e1 = inst.position(); + e2 = sp; + for (int j = 0; j < count; j++) + execute(fdefBuffer[i], fdefEntryPoint[i]); + inst.position(e1); + break; + + case 0x2B: // CALL, CALL function + i = stack[sp--]; + e1 = inst.position(); + e2 = sp; + execute(fdefBuffer[i], fdefEntryPoint[i]); + inst.position(e1); + break; + + case 0x2C: // FDEF, Function DEFinition + i = stack[sp--]; + fdefBuffer[i] = inst; + fdefEntryPoint[i] = inst.position(); + skipAfter(inst, + /* look for: ENDF */ 0x2d, + /* look for: --- */ -1, + /* illegal: IDEF */ 0x89, + /* illegal: FDEF */ 0x2c, + /* do not handle nested if clauses */ false); + break; + + case 0x2D: // ENDF, END Function definition + /* Pop the current stack frame. */ + return false; + + case 0x2e: // MDAP[0], Move Direct Absolute Point + handleMDAP(stack[sp--], /* round */ false); + break; + + case 0x2f: // MDAP[1], Move Direct Absolute Point + handleMDAP(stack[sp--], /* round */ true); + break; + + case 0x39: // IP, Interpolate Point by the last relative stretch + handleIP(); + break; + + case 0x3d: // RTDG, Round To Double Grid + setRoundingMode(Fixed.ONE, 0x08); + roundThreshold = roundThreshold / 64; // period/128 + break; + + case 0x3e: // MIAP[0], Move Indirect Absolute Point + e1 = stack[sp--]; + handleMIAP(e1, stack[sp--], /* round */ false); + break; + + case 0x3f: // MIAP[1], Move Indirect Absolute Point + e1 = stack[sp--]; + handleMIAP(e1, stack[sp--], /* round */ true); + break; + + case 0x40: // NPUSHB + count = inst.get() & 0xff; + for (i = 0; i < count; i++) + stack[++sp] = inst.get() & 0xff; + break; + + case 0x41: // NPUSHW + count = inst.get() & 0xff; + for (i = 0; i < count; i++) + stack[++sp] = inst.getShort(); + break; + + case 0x42: // WS, Write Store + e1 = stack[sp--]; i = stack[sp--]; + storage[i] = e1; + break; + + case 0x43: // RS, Read Store + stack[sp] = storage[stack[sp]]; + break; + + case 0x44: // WCVTP, Write Control Value Table in Pixel units + e1 = stack[sp--]; + i = stack[sp--]; + if (i < cvt.length) + cvt[i] = e1; + break; + + case 0x45: // RCVT, Read Control Value Table entry + if (stack[sp] < cvt.length) + stack[sp] = cvt[stack[sp]]; + else + stack[sp] = 0; + break; + + case 0x46: // GC[0], Get Coordinate projected onto the projection vector + stack[sp] = getProjection(zp2, stack[sp]); + break; + + case 0x47: // GC[1], Get Coordinate projected onto the projection vector + stack[sp] = getOriginalProjection(zp2, stack[sp]); + break; + + case 0x4B: // MPPEM, Measure Pixels Per EM + stack[++sp] = getPixelsPerEM(); + break; + + case 0x4c: // MPS, Measure Point Size + /* FreeType2 returns pixels per em here, because they think that + * the point size would be irrelevant in a given font program. + * This is extremely surprising, because the appearance of good + * fonts _should_ change with point size. For example, a good + * font should be wider at small point sizes, and the holes + * inside glyphs ("Punzen" in German, I do not know the correct + * English expression) should be larger. Note that this change + * of appearance is dependent on point size, _not_ the + * resolution of the display device. + */ + stack[++sp] = pointSize; + break; + + case 0x4f: // DEBUG, DEBUG call + sp--; + break; + + case 0x50: // LT, Less Than + e1 = stack[sp--]; + stack[sp] = (stack[sp] < e1) ? 1 : 0; + break; + + case 0x51: // LTEQ, Greater Than or EQual + e1 = stack[sp--]; + stack[sp] = (stack[sp] <= e1) ? 1 : 0; + break; + + case 0x52: // GT, Greater Than + e1 = stack[sp--]; + stack[sp] = (stack[sp] > e1) ? 1 : 0; + break; + + case 0x53: // GTEQ, Greater Than or EQual + e1 = stack[sp--]; + stack[sp] = (stack[sp] >= e1) ? 1 : 0; + break; + + case 0x54: // EQ, EQual + e1 = stack[sp--]; + stack[sp] = (stack[sp] == e1) ? 1 : 0; + break; + + case 0x55: // NEQ, Not EQual + e1 = stack[sp--]; + stack[sp] = (stack[sp] != e1) ? 1 : 0; + break; + + case 0x58: // IF, IF test + if (stack[sp--] == 0) + skipAfter(inst, + /* look for: ELSE */ 0x1B, + /* look for: EIF */ 0x59, + /* illegal: -- */ -1, + /* illegal: -- */ -1, + /* handle nested if clauses */ true); + break; + + case 0x59: // EIF, End IF + // Do nothing. + break; + + case 0x5A: // AND + e1 = stack[sp--]; + stack[sp] = ((e1 != 0) && (stack[sp] != 0)) ? 1 : 0; + break; + + case 0x5B: // OR + e1 = stack[sp--]; + stack[sp] = ((e1 != 0) || (stack[sp] != 0)) ? 1 : 0; + break; + + case 0x5C: // NOT + stack[sp] = (stack[sp] != 0) ? 0 : 1; + break; + + case 0x5e: // SDB, Set Delta Base in the graphics state + deltaBase = stack[sp--]; + break; + + case 0x5f: // SDS, Set Delta Shift in the graphics state + deltaShift = stack[sp--]; + break; + + case 0x60: // ADD + e1 = stack[sp--]; + stack[sp] += e1; + break; + + case 0x61: // SUB, SUBtract + e1 = stack[sp--]; + stack[sp] -= e1; + break; + + case 0x62: // DIV, DIVide + e1 = stack[sp--]; + stack[sp] = Fixed.div(e1, stack[sp]); + break; + + case 0x63: // MUL, MULtiply + e1 = stack[sp--]; + stack[sp] = Fixed.mul(e1, stack[sp]); + break; + + case 0x64: // ABS, ABSolute value + stack[sp] = Math.abs(stack[sp]); + break; + + case 0x65: // NEG, NEGate + stack[sp] = -stack[sp]; + break; + + case 0x66: // FLOOR + stack[sp] = Fixed.floor(stack[sp]); + break; + + case 0x67: // CEILING + stack[sp] = Fixed.ceil(stack[sp]); + break; + + case 0x68: // ROUND[0] -- round grey distance + stack[sp] = round(stack[sp], /* no engine compensation */ 0); + break; + + case 0x69: // ROUND[1] -- round black distance + stack[sp] = round(stack[sp], -engineCompensation); + break; + + case 0x6a: // ROUND[2] -- round white distance + stack[sp] = round(stack[sp], engineCompensation); + break; + + case 0x6b: // ROUND[3] -- round distance (not yet defined) + stack[sp] = round(stack[sp], /* no engine compensation */ 0); + break; + + case 0x6c: // NROUND[0] -- compensate grey distance + stack[sp] = nround(stack[sp], 0); + break; + + case 0x6d: // NROUND[1] -- compensate black distance + stack[sp] = nround(stack[sp], -engineCompensation); + break; + + case 0x6e: // NROUND[2] -- compensate white distance + stack[sp] = nround(stack[sp], engineCompensation); + break; + + case 0x6f: // NROUND[3] -- compensate distance (not yet defined) + stack[sp] = nround(stack[sp], 0); + break; + + case 0x70: // WCVTF, Write Control Value Table in Funits + e1 = stack[sp--]; + cvt[stack[sp--]] = e1 * getPixelsPerEM(); + break; + + case 0x73: // DELTAC1, DELTA exception C1 + count = stack[sp--]; + sp -= 2 * count; + deltaC(stack, sp + 1, count, 0); + break; + + case 0x74: // DELTAC2, DELTA exception C2 + count = stack[sp--]; + sp -= 2 * count; + deltaC(stack, sp + 1, count, 16); + break; + + case 0x75: // DELTAC3, DELTA exception C3 + count = stack[sp--]; + sp -= 2 * count; + deltaC(stack, sp + 1, count, 32); + break; + + case 0x76: // SROUND, Super ROUND + setRoundingMode(Fixed.ONE, stack[sp--]); + break; + + case 0x77: // S45ROUND, Super ROUND 45 degrees + setRoundingMode(/* sqrt(2)/2 */ 0x2d, stack[sp--]); + break; + + case 0x78: // JROT, Jump Relative On True + e1 = stack[sp--]; + i = inst.position() - 1 + stack[sp--]; + if (e1 != 0) + inst.position(i); + break; + + case 0x79: // JROF, Jump Relative On False + e1 = stack[sp--]; + i = inst.position() - 1 + stack[sp--]; + if (e1 == 0) + inst.position(i); + break; + + case 0x7a: // ROFF, Round OFF + roundPeriod = 0; + break; + + case 0x7c: // RUTG, Round Up To Grid + setRoundingMode(Fixed.ONE, 0x40); + break; + + case 0x7d: // RDTG, Round Down To Grid + setRoundingMode(Fixed.ONE, 0x40); + roundThreshold = 0; + break; + + case 0x7e: // SANGW, Set ANGle Weight (no-op according to TrueType spec) + case 0x7f: // AA, Adjust Angle (no-op according to TrueType spec) + sp--; + break; + + case 0x85: // SCANCTRL, SCAN conversion ConTRoL + e1 = stack[sp--]; + int ppemThreshold = e1 & 255; + scanControl = false; + boolean ppemCondition = (ppemThreshold == 255) + || ((ppemThreshold != 0) && (getPixelsPerEM() > ppemThreshold)); + if (((e1 & (1<<8)) != 0) && ppemCondition) + scanControl = true; + if (((e1 & (1<<9)) != 0) && isRotated()) + scanControl = true; + if (((e1 & (1<<10)) != 0) && isStretched()) + scanControl = true; + if (((e1 & (1<<11)) != 0) && !ppemCondition) + scanControl = false; + if (((e1 & (1<<12)) != 0) && !isRotated()) + scanControl = false; + if (((e1 & (1<<13)) != 0) && !isStretched()) + scanControl = false; + break; + + case 0x88: // GETINFO, GET INFOrmation + e1 = 0; + if ((stack[sp] & 1) != 0) // ask for rasterizer version + e1 |= 35; // "Microsoft Rasterizer version 1.7" (grayscale-capable) + if (((stack[sp] & 2) != 0) && isRotated()) + e1 |= 1 << 8; // bit 8: glyph has been rotated + if (((stack[sp] & 4) != 0) && isStretched()) + e1 |= 1 << 9; // bit 9: glyph has been stretched + if (((stack[sp] & 32) != 0) && antialiased) + e1 |= 1 << 12; // bit 12: antialiasing is active + stack[sp] = e1; + break; + + case 0x8a: // ROLL, ROLL the top three stack elements + e1 = stack[sp - 2]; + stack[sp - 2] = stack[sp - 1]; + stack[sp - 1] = stack[sp]; + stack[sp] = e1; + break; + + case 0x8b: // MAX, MAXimum of top two stack elements + e1 = stack[sp--]; + stack[sp] = Math.max(e1, stack[sp]); + break; + + case 0x8c: // MIN, MINimum of top two stack elements + e1 = stack[sp--]; + stack[sp] = Math.min(e1, stack[sp]); + break; + + case 0x8d: // SCANTYPE + scanType = stack[sp--]; + break; + + case 0x8e: // INSTCTRL, INSTRuction execution ConTRoL + e1 = stack[sp--]; // selector + e2 = stack[sp--]; // value + switch (e1) + { + case 1: + executeGlyphInstructions = (e2 == 0); + break; + + case 2: + ignoreCVTProgram = (e2 != 0); + break; + } + break; + + case 0xb0: // PUSHB[0] + case 0xb1: // PUSHB[1] + case 0xb2: // PUSHB[2] + case 0xb3: // PUSHB[3] + case 0xb4: // PUSHB[4] + case 0xb5: // PUSHB[5] + case 0xb6: // PUSHB[6] + case 0xb7: // PUSHB[7] + count = bcode - 0xb0 + 1; + for (i = 0; i < count; i++) + stack[++sp] = inst.get() & 0xff; + break; + + case 0xb8: // PUSHW[0] + case 0xb9: // PUSHW[1] + case 0xba: // PUSHW[2] + case 0xbb: // PUSHW[3] + case 0xbc: // PUSHW[4] + case 0xbd: // PUSHW[5] + case 0xbe: // PUSHW[6] + case 0xbf: // PUSHW[7] + count = bcode - 0xb8 + 1; + for (i = 0; i < count; i++) + stack[++sp] = inst.getShort(); + break; + + // MIRPxxxx, Move Indirect Relative Point + case 0xe0: case 0xe1: case 0xe2: case 0xe3: + case 0xe4: case 0xe5: case 0xe6: case 0xe7: + case 0xe8: case 0xe9: case 0xea: case 0xeb: + case 0xec: case 0xed: case 0xee: case 0xef: + case 0xf0: case 0xf1: case 0xf2: case 0xf3: + case 0xf4: case 0xf5: case 0xf6: case 0xf7: + case 0xf8: case 0xf9: case 0xfa: case 0xfb: + case 0xfc: case 0xfd: case 0xfe: case 0xff: + e1 = stack[sp--]; + handleMIRP(bcode, /* point */ e1, /* cvtIndex */ stack[sp--]); + break; + + default: + throw new IllegalStateException(); + } + + return true; + } + + + /** + * Sets the rounding mode. + * + * @param period the grid period in fixed-point notation, such as + * {@link Fixed#ONE} for the <code>SROUND</code> instruction or + * <code>sqrt(2)/2</code> for the <code>S45ROUND</code> instruction. + * + * @param mode a byte whose bits are set according to the TrueType + * specification for SROUND and S45ROUND parameters. + */ + private void setRoundingMode(int period, int mode) + { + /* Set the period. */ + switch ((mode & 0xc0) >> 6) + { + case 0: + roundPeriod = period / 2; + break; + + case 2: + roundPeriod = period * 2; + break; + + default: + roundPeriod = period; + break; + } + + /* Set the phase. */ + switch ((mode & 0x30) >> 4) + { + case 0: + roundPhase = 0; + break; + + case 1: + roundPhase = roundPeriod >> 2; // period/4 + break; + + case 2: + roundPhase = roundPeriod >> 1; // period/2 + break; + + case 3: + roundPhase = (roundPeriod >> 1) + (roundPeriod >> 2); // period * 3/4 + break; + } + + /* Set the threshold. */ + int threshold = mode & 0x0f; + if (threshold == 0) + roundThreshold = roundPeriod - Fixed.ONE; + else + roundThreshold = ((threshold - 4) * roundPeriod) / 8; + } + + + + /** + * Implements the DELTAC instructions. These instructions check + * whether the current number of pixels per em is contained in an + * exception table. If it is, a delta value is determined, and the + * specified entry in the Control Value Table is modified according + * to the delta. + * + * @param pairs the delta table. Because the delta table is on + * the stack, callers usually just want to pass the stack array. + * + * @param offset the offset of the first pair in <code>pairs</code>. + * + * @param numPairs the number of pairs. + * + * @param base 0 for <code>DELTAC1</code>, 16 for <code>DELTAC2</code>, + * or 32 for <code>DELTAC2</code>. + * + * @see <a href= + * "http://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html#DELTAC1" + * >Apple’s documentation for <code>DELTAC1</code></a>, <a href= + * "http://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html#DELTAC2" + * ><code>DELTAC2</code></a>, and <a href= + * "http://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html#DELTAC3" + * ><code>DELTAC3</code></a> + */ + private void deltaC(int[] pairs, int offset, int numPairs, int base) + { + int arg, relativePpem; + int ppemTrigger = getPixelsPerEM() - (deltaBase + base); + int delta, cvtIndex, rightShift; + for (int i = 0; i < numPairs; i++) + { + arg = pairs[offset + 2 * i]; + relativePpem = (arg >> 4) & 15; + if (relativePpem == ppemTrigger) + { + delta = (arg & 15) - 8; + if (delta >= 0) + ++delta; + + rightShift = deltaShift - 6; + if (rightShift > 0) + delta = delta >> rightShift; + else if (rightShift < 0) + delta = delta << (-rightShift); + cvt[pairs[offset + 2 * i + 1]] += delta; + + break; + } + } + } + + + private Zone getZone(int zoneNumber) + { + return (zoneNumber == 0) ? twilightZone : glyphZone; + } + + + /** + * Projects the specified vector along the current projection + * vector. + * + * @param x the x component of the input vector, in 26.6 fixed-point + * notation. + * + * @param y the y component of the input vector, in 26.6 fixed-point + * notation. + * + * @return the projected distance, in 26.6 fixed-point notation. + */ + private int getProjection(int x, int y) + { + return (int) (((((long) x) * projX + ((long) y) * projY)) >> 14); + } + + + /** + * Projects the specified vector along the current dual projection + * vector. + * + * @param x the x component of the input vector, in 26.6 fixed-point + * notation. + * + * @param y the y component of the input vector, in 26.6 fixed-point + * notation. + * + * @return the projected distance, in 26.6 fixed-point notation. + */ + private int getDualProjection(int x, int y) + { + return (int) (((((long) x) * dualX + ((long) y) * dualY)) >> 14); + } + + + private int getProjection(Zone zone, int point) + { + return getProjection(zone.getX(point), zone.getY(point)); + } + + + private int getOriginalProjection(Zone zone, int point) + { + return getDualProjection(zone.getOriginalX(point), + zone.getOriginalY(point)); + } + + + private void handleISECT(int a0, int a1, int b0, int b1, int p) + { + System.out.println("FIXME: Unimplemented ISECT " + p); + } + + + private static int muldiv(int a, int b, int c) + { + int s; + s = a; a = Math.abs(a); + s ^= b; b = Math.abs(b); + s ^= c; c = Math.abs(c); + a = (int) ((((long) a) * b + (c>>1)) / c); + return (s < 0) ? -a : a; + } + + + private int getFreeDotProj() + { + int result; + + result = ((((int) projX) * freeX) << 2) + + ((((int) projY) * freeY) << 2); + + /* FIXME: This seems somewhat bogus. Need to contact the + * developers of FreeType. + */ + if (Math.abs(result) < 0x4000000) + result = 0x40000000; + return result; + } + + + private void movePoint(Zone zone, int point, int distance) + { + int freeDotProj = getFreeDotProj(); + int c; + + if (freeX != 0) + { + c = zone.getX(point); + c += muldiv(distance, freeX << 16, freeDotProj); + zone.setX(point, c, /* touch */ true); + } + + if (freeY != 0) + { + c = zone.getY(point); + c += muldiv(distance, freeY << 16, freeDotProj); + zone.setY(point, c, /* touch */ true); + } + + if (TRACE_EXECUTION) + { + System.out.println("point[" + point + "] moved to " + + Fixed.toString(zone.getX(point), + zone.getY(point))); + dumpVectors(); + } + } + + private void dumpVectors() + { + System.out.println(" proj=" + Fixed.toString(projX>>8, projY>>8) + + ", free=" + Fixed.toString(freeX>>8, freeY>>8)); + } + + + private void handleIP() + { + // Implementation taken from FreeType. + int p, org_a, org_b, org_x, cur_a, cur_b, cur_x, distance; + int freeDotProj; + + org_a = getOriginalProjection(zp0, rp1); + cur_a = getProjection(zp0, rp1); + + org_b = getOriginalProjection(zp1, rp2); + cur_b = getProjection(zp1, rp2); + + while (--loop >= 0) + { + p = stack[sp--]; + org_x = getOriginalProjection(zp2, p); + cur_x = getProjection(zp2, p); + + if (((org_a <= org_b) && (org_x <= org_a)) + || ((org_a > org_b) && (org_x >= org_a))) + distance = (cur_a - org_a) + (org_x - cur_x); + else if (((org_a <= org_b) && (org_x >= org_b)) + || ((org_a > org_b) && (org_x < org_b))) + distance = (cur_b - org_b) + (org_x - cur_x); + else + distance = muldiv(cur_b - cur_a, org_x - org_a, org_b - org_a) + + (cur_a - cur_x); + movePoint(zp2, p, distance); + } + loop = 1; + } + + + private void handleMDAP(int point, boolean round) + { + System.out.println("FIXME: Unimplemented MDAP: point " + + point + "/" + zp0); + } + + + private void handleMIAP(int cvtIndex, int point, boolean round) + { + int previousPos, pos; + + previousPos = getProjection(zp0, point); + pos = cvt[cvtIndex]; + + if (round) + { + if (Math.abs(pos - previousPos) > cvtCutIn) + pos = previousPos; + pos = round(pos, /* no engine compensation */ 0); + } + movePoint(zp0, point, pos - previousPos); + rp0 = rp1 = point; + } + + + private void handleMIRP(int bcode, int point, int cvtIndex) + { + System.out.println("FIXME: Unimplemented mirp " + point + ", " + cvtIndex); + } + + + + private int round(int distance, int compensation) + { + int result; + + if (roundPeriod == 0) + return nround(distance, compensation); + + if (distance >= 0) + { + result = distance + compensation - roundPhase + roundThreshold; + result &= -roundPeriod; // truncate to the next lowest periodic value + return Math.max(result, 0) + roundPhase; + } + else + { + result = compensation - roundPhase + roundThreshold - distance; + result &= -roundPeriod; + return Math.max(-result, 0) - roundPhase; + } + } + + + private static int nround(int distance, int compensation) + { + if (distance >= 0) + return Math.max(distance + compensation, 0); + else + return Math.min(distance - compensation, 0); + } + + + /** + * Determines whether the current glyph is rotated. + * + * @return <code>false</code> if the shearing factors for the + * <i>x</i> and <i>y</i> axes are zero; <code>true</code> if they + * are non-zero. + */ + private boolean isRotated() + { + return (shearX != 0) || (shearY != 0); + } + + + /** + * Determines whether the current glyph is stretched. + * + * @return <code>false</code> if the scaling factors for the + * <i>x</i> and <i>y</i> axes are are equal; <code>true</code> if + * they differ. + */ + private boolean isStretched() + { + return scaleX != scaleY; + } + + + /** + * Returns how many pixels there are per EM, in direction of the + * current projection vector. The result is a normal integer, + * not a Fixed. + */ + private int getPixelsPerEM() + { + if (cachedPixelsPerEM == 0) + { + cachedPixelsPerEM = Fixed.intValue(Fixed.vectorLength( + applyCTM_x(projX >> 8, projY >> 8), + applyCTM_y(projX >> 8, projY >> 8))); + } + + return cachedPixelsPerEM; + } + + + private void setProjectionVector(short x, short y) + { + if (PATENTED_HINTING) + { + if ((x != projX) || (y != projY)) + cachedPixelsPerEM = 0; + + projX = x; + projY = y; + } + } + + + private void setFreedomVector(short x, short y) + { + if (PATENTED_HINTING) + { + freeX = x; + freeY = y; + } + } + + + private void setDualVector(short x, short y) + { + if (PATENTED_HINTING) + { + dualX = x; + dualY = y; + } + } + + + private int applyCTM_x(int x, int y) + { + return (int) (((long) scaleX * x + (long) shearX * y) >> 6); + } + + private int applyCTM_y(int x, int y) + { + return (int) (((long) shearY * x + (long) scaleY * y) >> 6); + } + + + private static final String[] INST_NAME = + { + /* 00 */ "SVTCA[0]", "SVTCA[1]", "SPVTCA[0]", "SPVTCA[1]", + /* 04 */ "INST_04", "INST_05", "INST_06", "INST_07", + /* 08 */ "INST_08", "INST_09", "INST_0A", "INST_0B", + /* 0c */ "GPV", "GFV", "INST_0E", "ISECT", + /* 10 */ "SRP0", "SRP1", "SRP2", "SZP0", + /* 14 */ "SZP1", "SZP2", "SZPS", "SLOOP", + /* 18 */ "RTG", "RTHG", "SMD", "ELSE", + /* 1c */ "JMPR", "SCVTCI", "INST_1E", "SSW", + /* 20 */ "DUP", "POP", "CLEAR", "SWAP", + /* 24 */ "DEPTH", "CINDEX", "MINDEX", "INST_27", + /* 28 */ "INST_28", "INST_29", "LOOPCALL", "CALL", + /* 2c */ "FDEF", "ENDF", "MDAP[0]", "MDAP[1]", + /* 30 */ "IUP[0]", "IUP[1]", "SHP[0]", "SHP[1]", + /* 34 */ "INST_34", "INST_35", "INST_36", "INST_37", + /* 38 */ "INST_38", "IP", "INST_3A", "INST_3B", + /* 3c */ "INST_3C", "RTDG", "MIAP[0]", "MIAP[1]", + /* 40 */ "NPUSHB", "NPUSHW", "WS", "RS", + /* 44 */ "WCVTP", "RCVT", "GC[0]", "GC[1]", + /* 48 */ "INST_48", "INST_49", "INST_4A", "MPPEM", + /* 4c */ "MPS", "FLIPON", "FLIPOFF", "DEBUG", + /* 50 */ "LT", "LTEQ", "GT", "GTEQ", + /* 54 */ "EQ", "NEQ", "INST_56", "INST_57", + /* 58 */ "IF", "EIF", "AND", "OR", + /* 5c */ "NOT", "INST_5D", "SDB", "SDS", + /* 60 */ "ADD", "SUB", "DIV", "MUL", + /* 64 */ "ABS", "NEG", "FLOOR", "CEILING", + /* 68 */ "ROUND[0]", "ROUND[1]", "ROUND[2]", "ROUND[3]", + /* 6c */ "NROUND[0]", "NROUND[1]", "NROUND[2]", "NROUND[3]", + /* 70 */ "WCVTF", "INST_71", "INST_72", "DELTAC1", + /* 74 */ "DELTAC2", "DELTAC3", "SROUND", "S45ROUND", + /* 78 */ "JROT", "JROF", "ROFF", "INST_7B", + /* 7c */ "RUTG", "RDTG", "SANGW", "AA", + /* 80 */ "FLIPPT", "FLIPRGON", "FLIPRGOFF", "INST_83", + /* 84 */ "INST_84", "SCANCTRL", "INST_86", "INST_87", + /* 88 */ "GETINFO", "INST_89", "ROLL", "MAX", + /* 8c */ "MIN", "SCANTYPE", "INSTCTRL", "INST_8F", + /* 90 */ "INST_90", "INST_91", "INST_92", "INST_93", + /* 94 */ "INST_94", "INST_95", "INST_96", "INST_97", + /* 98 */ "INST_98", "INST_99", "INST_9A", "INST_9B", + /* 9c */ "INST_9C", "INST_9D", "INST_9E", "INST_9F", + /* a0 */ "INST_A0", "INST_A1", "INST_A2", "INST_A3", + /* a4 */ "INST_A4", "INST_A5", "INST_A6", "INST_A7", + /* a8 */ "INST_A8", "INST_A9", "INST_AA", "INST_AB", + /* ac */ "INST_AC", "INST_AD", "INST_AE", "INST_AF", + /* b0 */ "PUSHB[0]", "PUSHB[1]", "PUSHB[2]", "PUSHB[3]", + /* b4 */ "PUSHB[4]", "PUSHB[5]", "PUSHB[6]", "PUSHB[7]", + /* b8 */ "PUSHW[0]", "PUSHW[1]", "PUSHW[2]", "PUSHW[3]", + /* bc */ "PUSHW[4]", "PUSHW[5]", "PUSHW[6]", "PUSHW[7]", + /* c0 */ "INST_C0", "INST_C1", "INST_C2", "INST_C3", + /* c4 */ "INST_C4", "INST_C5", "INST_C6", "INST_C7", + /* c8 */ "INST_C8", "INST_C9", "INST_CA", "INST_CB", + /* cc */ "INST_CC", "INST_CD", "INST_CE", "INST_CF", + /* d0 */ "INST_D0", "INST_D1", "INST_D2", "INST_D3", + /* d4 */ "INST_D4", "INST_D5", "INST_D6", "INST_D7", + /* d8 */ "INST_D8", "INST_D9", "INST_DA", "INST_DB", + /* dc */ "INST_DC", "INST_DD", "INST_DE", "INST_DF", + /* e0 */ "MIRP00000", "MIRP00001", "MIRP00010", "MIRP00011", + /* e4 */ "MIRP00100", "MIRP00101", "MIRP00110", "MIRP00111", + /* e8 */ "MIRP01000", "MIRP01001", "MIRP01010", "MIRP01011", + /* ec */ "MIRP01100", "MIRP01101", "MIRP01110", "MIRP01111", + /* f0 */ "MIRP10000", "MIRP10001", "MIRP10010", "MIRP10011", + /* f4 */ "MIRP10100", "MIRP10101", "MIRP10110", "MIRP10111", + /* f8 */ "MIRP11000", "MIRP11001", "MIRP11010", "MIRP11011", + /* fc */ "MIRP11100", "MIRP11101", "MIRP11110", "MIRP11111" + }; +} |