org.jpedal.fonts.tt.hinting.TTGraphicsState Maven / Gradle / Ivy
Show all versions of OpenViewerFX Show documentation
/*
* ===========================================
* Java Pdf Extraction Decoding Access Library
* ===========================================
*
* Project Info: http://www.idrsolutions.com
* Help section for developers at http://www.idrsolutions.com/support/
*
* (C) Copyright 1997-2017 IDRsolutions and Contributors.
*
* This file is part of JPedal/JPDF2HTML5
*
@LICENSE@
*
* ---------------
* TTGraphicsState.java
* ---------------
*/
package org.jpedal.fonts.tt.hinting;
import java.io.Serializable;
/**
* holds the graphics state variables
*/
public class TTGraphicsState implements Cloneable, Serializable {
/**
* Preset vectors -
*
* Vectors are stored as two F2Dot14 numbers stored in an int, with the x component taking up the high 16 bits and
* the y component taking up the bottom 16. The hypotenuse must be 1 in length. (0x4000 in F2Dot14)
*/
public static final int x_axis = 0x40000000;
public static final int y_axis = 0x00004000;
/**
* Preset round states -
*
* The spec defines a way of interpreting an item from the stack to form a round state consisting of period, phase
* and threshold for use in the SROUND instruction. This format is used instead of storing each aspect of the round
* state individually.
*/
public static final int hg = 0x68; //Half Grid
public static final int g = 0x48; //Grid
public static final int dg = 0x08; //Double Grid
public static final int dtg = 0x44; //Down to Grid
public static final int utg = 0x40; //Up to Grid
public static final int off = -1; //None
//Controls whether MIRP will flip CVT entries to match the actual distance
public boolean autoFlip = true;
//The maximum difference between the measured distance and the cvt entry if the entry is to be used for MIRP
public int controlValueTableCutIn = 68;
//Lowest number in relative numbering system used by DELTA instructions
public int deltaBase = 9;
//Determines the magnitude of movements by DELTA instructions
public int deltaShift = 3;
//Vector which defines the direction of movement (for almost all instructions)
public int freedomVector = x_axis;
//Vector which defines the direction of measurement (usually)
public int projectionVector = x_axis;
//Vector which defines the direction of measurement when dealing with points original positions in GC, MD, MDRP and MIRP
public int dualProjectionVector = x_axis;
//Allows you to disable instructions entirely for glyph programs.
public int instructControl;
//Scan conversion not implemented - using java for anti aliased rendering instead
// public boolean scanControl=false;
//Used by some functions to repeat actions. It's always reset to 1 after it's used.
public int loop = 1;
//Sets the minimum distance to which a value will be rounded in MIRP and MDRP
public int minimumDistance = 1;
//Contains the components required to round variables
public int roundState = g;
//Grid period for rounding - 1 usually, sqrt(2)/2 for 45 degrees
public double gridPeriod = 1.0;
//Reference points
public int rp0;
public int rp1;
public int rp2;
//The cutIn is the difference below which a distance will be replaced with the widthValue in MDRP and MIRP
public int singleWidthCutIn;
public int singleWidthValue;
//Zone pointers
public int zp0 = TTVM.GLYPH_ZONE;
public int zp1 = TTVM.GLYPH_ZONE;
public int zp2 = TTVM.GLYPH_ZONE;
/**
* Create a copy of this object.
*
* @return Copy reference
* @throws CloneNotSupportedException
*/
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
/**
* Rounds a double according to the round state variable.
*
* @param n Number to round
* @return Rounded number
*/
public double round(double n) {
if (roundState == off) {
return n;
}
final boolean isPositive = n > 0;
//Get period
int p = (roundState >> 6) & 3;
final double period;
if (p == 0) {
period = gridPeriod / 2;
} else if (p == 1) {
period = gridPeriod;
} else {
period = gridPeriod * 2;
}
//Get phase
p = (roundState >> 4) & 3;
final double phase;
switch (p) {
case 0:
phase = 0;
break;
case 1:
phase = period / 4;
break;
case 2:
phase = period / 2;
break;
default:
phase = (3 * period) / 4;
break;
}
//Get threshold
p = roundState & 15;
//special case - use largest number smaller than period
if (p == 0) {
double result = phase;
while (result < n) {
result += period;
}
return result;
}
final double threshold = ((p - 4) * period) / 8;
//Round
n -= phase;
double lower = 0;
if (n > 0) {
n += threshold;
while (lower + period <= n) {
lower += period;
}
} else {
n -= threshold;
while (lower - period >= n) {
lower -= period;
}
}
n = lower;
n += phase;
//Make sure number doesn't flip from pos to neg/neg to pos
if (isPositive && n < 0) {
n = phase % period;
}
if (!isPositive && n > 0) {
n = (phase - (10 * period)) % period;
}
return n;
}
/**
* Rounds a F26Dot6 number according to the round state variable.
*
* @param f26dot6 Number to round
* @return Rounded number
*/
public int round(final int f26dot6) {
double d = TTVM.getDoubleFromF26Dot6(f26dot6);
d = round(d);
return TTVM.storeDoubleAsF26Dot6(d);
}
/**
* Takes an F26Dot6 distance along the projection vector and calculates the shift along the freedom vector IN ACTUAL
* SPACE required.
*
* @param distance Move required along PV
* @return F26Dot6 shifts needed in x and y
*/
public int[] getFVMoveforPVDistance(final int distance) {
if (distance == 0) {
return new int[]{0, 0};
}
final int[] fv = getVectorComponents(freedomVector);
fv[0] = TTVM.storeDoubleAsF26Dot6(TTVM.getDoubleFromF2Dot14(fv[0]));
fv[1] = TTVM.storeDoubleAsF26Dot6(TTVM.getDoubleFromF2Dot14(fv[1]));
final double fvWorth = TTVM.getDoubleFromF26Dot6(getCoordsOnVector(projectionVector, fv[0], fv[1]));
if (fvWorth != 0) {
final double mul = TTVM.getDoubleFromF26Dot6(distance);
fv[0] = (int) ((fv[0] * mul) / fvWorth);
fv[1] = (int) ((fv[1] * mul) / fvWorth);
} else {
fv[0] = 0;
fv[1] = 0;
}
return fv;
}
/**
* Get the F2Dot14 components of a vector.
*
* @param vector vector to get
* @return array of x and y components
*/
static int[] getVectorComponents(final int vector) {
return new int[]{(vector >> 16), ((vector << 16) >> 16)};
}
/**
* Create a vector from two F2Dot14 numbers.
*
* @param x x component
* @param y y component
* @return newly created vector
*/
static int createVector(final int x, final int y) {
return ((x & 0xFFFF) << 16) + (y & 0xFFFF);
}
/**
* Get the coordinates of a point on a vector.
*
* @param vector Vector to measure against
* @param x F26Dot6 x coordinate
* @param y F26Dot6 y coordinate
* @return F26Dot6 coordinate on vector
*/
static int getCoordsOnVector(final int vector, final int x, final int y) {
final int[] pv = getVectorComponents(vector);
// double xProj = TTVM.getDoubleFromF2Dot14(pv[0]) * TTVM.getDoubleFromF26Dot6(x);
// double yProj = TTVM.getDoubleFromF2Dot14(pv[1]) * TTVM.getDoubleFromF26Dot6(y);
// return TTVM.storeDoubleAsF26Dot6(xProj + yProj);
final long xProj = pv[0] * (long) x;
final long yProj = pv[1] * (long) y;
long bigResult = xProj + yProj;
final boolean roundUp = (bigResult & 0x3FFF) >= 0x7F;
bigResult >>= 14;
if (roundUp) {
bigResult++;
}
return (int) bigResult;
}
/**
* Resets various variables for each glyph.
*/
public void resetForGlyph() {
zp0 = TTVM.GLYPH_ZONE;
zp1 = TTVM.GLYPH_ZONE;
zp2 = TTVM.GLYPH_ZONE;
projectionVector = x_axis;
dualProjectionVector = x_axis;
freedomVector = x_axis;
roundState = g;
loop = 1;
controlValueTableCutIn = 68;
}
private static String getVectorAsString(final int vector) {
final int[] v = getVectorComponents(vector);
return TTVM.getDoubleFromF2Dot14(v[0]) + ", " + TTVM.getDoubleFromF2Dot14(v[1]);
}
public String getRoundStateAsString() {
switch (roundState) {
case g:
return "Grid";
case hg:
return "HalfGrid";
case dg:
return "DoubleGrid";
case dtg:
return "DownToGrid";
case utg:
return "UpToGrid";
case off:
return "Off";
}
//Get period
int p = (roundState >> 6) & 3;
final String period;
if (p == 0) {
period = "Period:HalfPixel";
} else if (p == 1) {
period = "Period:OnePixel";
} else {
period = "Period:TwoPixels";
}
//Get phase
p = (roundState >> 4) & 3;
final String phase;
switch (p) {
case 0:
phase = "Phase:0";
break;
case 1:
phase = "Phase:Period/4";
break;
case 2:
phase = "Phase:Period/2";
break;
default:
phase = "Phase:Period*(3/4)";
break;
}
//Get threshold
p = roundState & 15;
final String threshold;
if (p == 0) {
threshold = "Threshold:Period-1";
} else {
threshold = "(" + (p - 4) + "/8)" + "*Period";
}
return '(' + period + ',' + phase + ',' + threshold + ')';
}
/**
* Returns a string containing details of the graphics state.
*
* @return TTGraphicsState details
*/
@Override
public String toString() {
return "org.jpedal.fonts.tt.hinting.TTGraphicsState[" +
"zp0=" + (zp0 == TTVM.GLYPH_ZONE ? "GLYPH" : "TWILIGHT") +
",zp1=" + (zp1 == TTVM.GLYPH_ZONE ? "GLYPH" : "TWILIGHT") +
",zp2=" + (zp2 == TTVM.GLYPH_ZONE ? "GLYPH" : "TWILIGHT") +
",rp0=" + rp0 +
",rp1=" + rp1 +
",rp2=" + rp2 +
",freedomVector=(" + getVectorAsString(freedomVector) + ')' +
",projectionVector=(" + getVectorAsString(projectionVector) + ')' +
",dualProjectionVector=(" + getVectorAsString(dualProjectionVector) + ')' +
",instructControl=" + instructControl +
",autoFlip=" + autoFlip +
",deltaBase=" + deltaBase +
",deltaShift=" + deltaShift +
",loop=" + loop +
",roundState=" + getRoundStateAsString() +
",minimumDistance=" + minimumDistance +
",controlValueTableCutIn=" + controlValueTableCutIn +
",singleWidthCutIn=" + singleWidthCutIn +
",singleWidthValue=" + singleWidthValue + ']';
}
}