Improve coordinate system calculations for font-size, letter-spacing etc.

Fix parsing of transform to account for single parameter scale and translate correctly.
Fix skewX, had opposite of intended effect.
https://www.w3.org/TR/SVG/coords.html
https://www.w3.org/TR/SVG2/coords.html
https://www.w3.org/TR/SVGTiny12/coords.html
https://www.w3.org/TR/SVG/coords.html#TransformAttribute
https://www.w3.org/TR/SVG/coords.html#SkewXDefined
This commit is contained in:
Mikael Sand
2017-06-27 20:45:04 +03:00
parent f7f1909249
commit c7039588f6
6 changed files with 65 additions and 8 deletions

View File

@@ -104,7 +104,7 @@ public class TSpanShadowNode extends TextShadowNode {
} }
private Path getLinePath(Canvas canvas, String line, Paint paint) { private Path getLinePath(Canvas canvas, String line, Paint paint) {
ReadableMap font = applyTextPropertiesToPaint(paint); ReadableMap font = applyTextPropertiesToPaint(paint, canvas);
int length = line.length(); int length = line.length();
Path path = new Path(); Path path = new Path();
@@ -207,7 +207,7 @@ public class TSpanShadowNode extends TextShadowNode {
return path; return path;
} }
private ReadableMap applyTextPropertiesToPaint(Paint paint) { private ReadableMap applyTextPropertiesToPaint(Paint paint, Canvas canvas) {
ReadableMap font = getFontFromContext(); ReadableMap font = getFontFromContext();
paint.setTextAlign(Paint.Align.LEFT); paint.setTextAlign(Paint.Align.LEFT);
@@ -217,6 +217,18 @@ public class TSpanShadowNode extends TextShadowNode {
float ch = getCanvasHeight(); float ch = getCanvasHeight();
float heightScale = height / ch; float heightScale = height / ch;
SvgViewShadowNode svg = getSvgShadowNode();
ReactShadowNode node = this;
while (node != null && !node.equals(svg)) {
if (node instanceof VirtualNode) {
VirtualNode v = ((VirtualNode) node);
heightScale /= v.getScaleY();
}
node = node.getParent();
}
float fontSize = (float)font.getDouble(PROP_FONT_SIZE) * mScale * heightScale; float fontSize = (float)font.getDouble(PROP_FONT_SIZE) * mScale * heightScale;
float letterSpacing = (float)font.getDouble(PROP_LETTER_SPACING) * mScale * heightScale; float letterSpacing = (float)font.getDouble(PROP_LETTER_SPACING) * mScale * heightScale;

View File

@@ -33,6 +33,8 @@ public abstract class VirtualNode extends LayoutShadowNode {
private static final float[] sMatrixData = new float[9]; private static final float[] sMatrixData = new float[9];
private static final float[] sRawMatrix = new float[9]; private static final float[] sRawMatrix = new float[9];
protected float mOpacity = 1f; protected float mOpacity = 1f;
protected float mScaleX = 1f;
protected float mScaleY = 1f;
protected Matrix mMatrix = new Matrix(); protected Matrix mMatrix = new Matrix();
private int mClipRule; private int mClipRule;
@@ -109,6 +111,18 @@ public abstract class VirtualNode extends LayoutShadowNode {
markUpdated(); markUpdated();
} }
@ReactProp(name = "scaleX", defaultFloat = 1f)
public void setScaleX(float scaleX) {
mScaleX = scaleX;
markUpdated();
}
@ReactProp(name = "scaleY", defaultFloat = 1f)
public void setScaleY(float scaleY) {
mScaleY = scaleY;
markUpdated();
}
@ReactProp(name = "matrix") @ReactProp(name = "matrix")
public void setMatrix(@Nullable ReadableArray matrixArray) { public void setMatrix(@Nullable ReadableArray matrixArray) {
if (matrixArray != null) { if (matrixArray != null) {
@@ -134,6 +148,18 @@ public abstract class VirtualNode extends LayoutShadowNode {
markUpdated(); markUpdated();
} }
public Matrix getMatrix() {
return mMatrix;
}
public float getScaleX() {
return mScaleX;
}
public float getScaleY() {
return mScaleY;
}
@ReactProp(name = "responsible", defaultBoolean = false) @ReactProp(name = "responsible", defaultBoolean = false)
public void setResponsible(boolean responsible) { public void setResponsible(boolean responsible) {
mResponsible = responsible; mResponsible = responsible;

View File

@@ -215,7 +215,7 @@ export default class Matrix2D {
// TODO: can this be combined into a single append operation? // TODO: can this be combined into a single append operation?
skewX *= DEG_TO_RAD; skewX *= DEG_TO_RAD;
skewY *= DEG_TO_RAD; skewY *= DEG_TO_RAD;
this.append(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), x, y); this.append(Math.cos(skewY), Math.sin(skewY), Math.sin(skewX), Math.cos(skewX), x, y);
this.append(cos * scaleX, sin * scaleX, -sin * scaleY, cos * scaleY, 0, 0); this.append(cos * scaleX, sin * scaleX, -sin * scaleY, cos * scaleY, 0, 0);
} else { } else {
this.append(cos * scaleX, sin * scaleX, -sin * scaleY, cos * scaleY, x, y); this.append(cos * scaleX, sin * scaleX, -sin * scaleY, cos * scaleY, x, y);

View File

@@ -42,6 +42,8 @@ const NodeAttributes = {
matrix: { matrix: {
diff: arrayDiffer diff: arrayDiffer
}, },
scaleX: true,
scaleY: true,
opacity: true, opacity: true,
clipRule: true, clipRule: true,
clipPath: true, clipPath: true,

View File

@@ -1,6 +1,6 @@
import extractFill from './extractFill'; import extractFill from './extractFill';
import extractStroke from './extractStroke'; import extractStroke from './extractStroke';
import extractTransform from './extractTransform'; import extractTransform, {props2transform, tp} from './extractTransform';
import extractClipPath from './extractClipPath'; import extractClipPath from './extractClipPath';
import extractResponder from './extractResponder'; import extractResponder from './extractResponder';
import extractOpacity from './extractOpacity'; import extractOpacity from './extractOpacity';
@@ -26,6 +26,23 @@ export default function(props, ref) {
extractedProps.matrix = extractTransform(props); extractedProps.matrix = extractTransform(props);
Object.assign(extractedProps, props2transform(props));
let transform = props.transform;
if (transform) {
if (typeof transform === 'string') {
var transformParsed = tp.parse(transform);
if (transformParsed.matrix) {
// TODO: Extract scaling values for coordinate system
// Especially scaleY for calculating scaling of fontSize
} else {
let trans = props2transform(transformParsed);
if (typeof trans === 'object') {
Object.assign(extractedProps, trans);
}
}
}
}
Object.assign(extractedProps, extractResponder(props, ref)); Object.assign(extractedProps, extractResponder(props, ref));
return extractedProps; return extractedProps;

View File

@@ -50,11 +50,11 @@ class TransformParser {
break; break;
case 'translate': case 'translate':
retval.translateX = transLst[i + 1]; retval.translateX = transLst[i + 1];
retval.translateY = (i + 2 <= transLst.length) ? transLst[i + 2] : 0; retval.translateY = (3 === transLst.length) ? transLst[i + 2] : 0;
break; break;
case 'scale': case 'scale':
retval.scaleX = transLst[i + 1]; retval.scaleX = transLst[i + 1];
retval.scaleY = (i + 2 <= transLst.length) ? transLst[i + 2] : retval.scaleX; retval.scaleY = (3 === transLst.length) ? transLst[i + 2] : retval.scaleX;
break; break;
case 'rotate': case 'rotate':
retval.rotation = transLst[i + 1]; retval.rotation = transLst[i + 1];
@@ -85,7 +85,7 @@ class TransformParser {
} }
} }
const tp = new TransformParser(); export const tp = new TransformParser();
function appendTransform(transform) { function appendTransform(transform) {
@@ -147,7 +147,7 @@ function universal2axis(universal, axisX, axisY, defaultValue) {
return [x || defaultValue || 0, y || defaultValue || 0]; return [x || defaultValue || 0, y || defaultValue || 0];
} }
function props2transform(props) { export function props2transform(props) {
if (props && (typeof props === 'string')) { if (props && (typeof props === 'string')) {
return props; return props;
} }