您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
1175 行
46 KiB
1175 行
46 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Unity.UIWidgets.foundation;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
|
|
namespace Unity.UIWidgets.ui {
|
|
public class CommandBufferCanvas : Canvas {
|
|
readonly RenderTexture _renderTexture;
|
|
readonly float _fringeWidth;
|
|
readonly float _devicePixelRatio;
|
|
readonly MeshPool _meshPool;
|
|
readonly List<RenderLayer> _layers = new List<RenderLayer>();
|
|
int _saveCount;
|
|
|
|
static readonly List<int> _imageTriangles = new List<int>(12) {
|
|
0, 1, 2, 0, 2, 1,
|
|
0, 2, 3, 0, 3, 2,
|
|
};
|
|
|
|
static readonly List<int> _imageNineTriangles = new List<int>(12 * 9) {
|
|
0, 4, 1, 1, 4, 5,
|
|
0, 1, 4, 1, 5, 4,
|
|
1, 5, 2, 2, 5, 6,
|
|
1, 2, 5, 2, 6, 5,
|
|
2, 6, 3, 3, 6, 7,
|
|
2, 3, 6, 3, 7, 6,
|
|
4, 8, 5, 5, 8, 9,
|
|
4, 5, 8, 5, 9, 8,
|
|
5, 9, 6, 6, 9, 10,
|
|
5, 6, 9, 6, 10, 9,
|
|
6, 10, 7, 7, 10, 11,
|
|
6, 7, 10, 7, 11, 10,
|
|
8, 12, 9, 9, 12, 13,
|
|
8, 9, 12, 9, 13, 12,
|
|
9, 13, 10, 10, 13, 14,
|
|
9, 10, 13, 10, 14, 13,
|
|
10, 14, 11, 11, 14, 15,
|
|
10, 11, 14, 11, 15, 14,
|
|
};
|
|
|
|
public CommandBufferCanvas(RenderTexture renderTexture, float devicePixelRatio, MeshPool meshPool) {
|
|
D.assert(renderTexture);
|
|
|
|
this._renderTexture = renderTexture;
|
|
this._fringeWidth = 1.0f / devicePixelRatio;
|
|
this._devicePixelRatio = devicePixelRatio;
|
|
this._meshPool = meshPool;
|
|
|
|
this.reset();
|
|
}
|
|
|
|
RenderLayer _getLayer() {
|
|
D.assert(this._layers.Count > 0);
|
|
return this._layers.Last();
|
|
}
|
|
|
|
State _getState() {
|
|
var layer = this._getLayer();
|
|
D.assert(layer.states.Count > 0);
|
|
return layer.states.Last();
|
|
}
|
|
|
|
public int getSaveCount() {
|
|
return this._saveCount;
|
|
}
|
|
|
|
public void save() {
|
|
this._saveCount++;
|
|
|
|
var layer = this._getLayer();
|
|
layer.states.Add(this._getState().copy());
|
|
layer.clipStack.save();
|
|
}
|
|
|
|
public void saveLayer(Rect bounds, Paint paint) {
|
|
this._saveCount++;
|
|
|
|
var state = this._getState();
|
|
var textureWidth = Mathf.CeilToInt(
|
|
(float) bounds.width * XformUtils.getScaleX(state.xform) * this._devicePixelRatio);
|
|
var textureHeight = Mathf.CeilToInt(
|
|
(float) bounds.height * XformUtils.getScaleY(state.xform) * this._devicePixelRatio);
|
|
|
|
var parentLayer = this._getLayer();
|
|
var layer = new RenderLayer {
|
|
rtID = Shader.PropertyToID("_rtID_" + this._layers.Count + "_" + parentLayer.layers.Count),
|
|
width = textureWidth,
|
|
height = textureHeight,
|
|
layerBounds = bounds,
|
|
layerPaint = new Paint {
|
|
color = paint.color,
|
|
blendMode = paint.blendMode,
|
|
}
|
|
};
|
|
|
|
parentLayer.layers.Add(layer);
|
|
this._layers.Add(layer);
|
|
|
|
state = this._getState();
|
|
XformUtils.transformTranslate(state.xform, (float) -bounds.left, (float) -bounds.top);
|
|
}
|
|
|
|
public void restore() {
|
|
this._saveCount--;
|
|
|
|
var layer = this._getLayer();
|
|
D.assert(layer.states.Count > 0);
|
|
if (layer.states.Count > 1) {
|
|
layer.states.RemoveAt(layer.states.Count - 1);
|
|
layer.clipStack.restore();
|
|
return;
|
|
}
|
|
|
|
this._layers.RemoveAt(this._layers.Count - 1);
|
|
var state = this._getState();
|
|
|
|
var vertices = new List<Vector3>(4);
|
|
var uv = new List<Vector2>(4);
|
|
|
|
float uvx0 = 0.0f;
|
|
float uvx1 = 1.0f;
|
|
float uvy0 = 1.0f;
|
|
float uvy1 = 0.0f;
|
|
|
|
float x, y;
|
|
var bounds = layer.layerBounds;
|
|
PathUtils.transformPoint(out x, out y, state.xform,
|
|
(float) bounds.left, (float) bounds.top);
|
|
vertices.Add(new Vector2(x, y));
|
|
uv.Add(new Vector2(uvx0, uvy0));
|
|
PathUtils.transformPoint(out x, out y, state.xform,
|
|
(float) bounds.left, (float) bounds.bottom);
|
|
vertices.Add(new Vector2(x, y));
|
|
uv.Add(new Vector2(uvx0, uvy1));
|
|
PathUtils.transformPoint(out x, out y, state.xform,
|
|
(float) bounds.right, (float) bounds.bottom);
|
|
vertices.Add(new Vector2(x, y));
|
|
uv.Add(new Vector2(uvx1, uvy1));
|
|
PathUtils.transformPoint(out x, out y, state.xform,
|
|
(float) bounds.right, (float) bounds.top);
|
|
vertices.Add(new Vector2(x, y));
|
|
uv.Add(new Vector2(uvx1, uvy0));
|
|
|
|
var mesh = new MeshMesh(vertices, _imageTriangles, uv);
|
|
|
|
if (!this._applyClip(mesh.bounds)) {
|
|
return;
|
|
}
|
|
|
|
var mat = this._getMat(layer.layerPaint);
|
|
var properties = this._getMatPropsForImage(null, layer.layerPaint);
|
|
|
|
this._getLayer().draws.Add(
|
|
new RenderDraw {
|
|
mesh = mesh,
|
|
pass = CanvasShaderPass.texrtPass0,
|
|
material = mat,
|
|
properties = properties,
|
|
layer = layer
|
|
}
|
|
);
|
|
}
|
|
|
|
public void translate(double dx, double dy) {
|
|
var state = this._getState();
|
|
float[] t = new float[6];
|
|
XformUtils.transformTranslate(t, (float) dx, (float) dy);
|
|
XformUtils.transformPremultiply(state.xform, t);
|
|
}
|
|
|
|
public void scale(double sx, double? sy = null) {
|
|
var state = this._getState();
|
|
float[] t = new float[6];
|
|
XformUtils.transformScale(t, (float) sx, (float) (sy ?? sx));
|
|
XformUtils.transformPremultiply(state.xform, t);
|
|
}
|
|
|
|
public void rotate(double radians, Offset offset = null) {
|
|
var state = this._getState();
|
|
float[] t = new float[6];
|
|
|
|
if (offset != null) {
|
|
XformUtils.transformTranslate(t, (float) -offset.dx, (float) -offset.dy);
|
|
XformUtils.transformPremultiply(state.xform, t);
|
|
}
|
|
|
|
XformUtils.transformRotate(t, (float) radians);
|
|
XformUtils.transformPremultiply(state.xform, t);
|
|
|
|
if (offset != null) {
|
|
XformUtils.transformTranslate(t, (float) offset.dx, (float) offset.dy);
|
|
XformUtils.transformPremultiply(state.xform, t);
|
|
}
|
|
}
|
|
|
|
public void skew(double sx, double sy) {
|
|
var state = this._getState();
|
|
float[] t = new float[6];
|
|
XformUtils.transformSkew(t, (float) sx, (float) sy);
|
|
XformUtils.transformPremultiply(state.xform, t);
|
|
}
|
|
|
|
public void concat(Matrix3 matrix) {
|
|
var state = this._getState();
|
|
float[] t = XformUtils.fromMatrix3(matrix);
|
|
XformUtils.transformPremultiply(state.xform, t);
|
|
}
|
|
|
|
public Matrix3 getTotalMatrix() {
|
|
var state = this._getState();
|
|
return XformUtils.toMatrix3(state.xform);
|
|
}
|
|
|
|
public void resetMatrix() {
|
|
var state = this._getState();
|
|
XformUtils.transformIdentity(state.xform);
|
|
}
|
|
|
|
public void setMatrix(Matrix3 matrix) {
|
|
var state = this._getState();
|
|
state.xform = XformUtils.fromMatrix3(matrix);
|
|
}
|
|
|
|
public float getDevicePixelRatio() {
|
|
return this._devicePixelRatio;
|
|
}
|
|
|
|
public void clipRect(Rect rect) {
|
|
var path = new Path();
|
|
path.addRect(rect);
|
|
this.clipPath(path);
|
|
}
|
|
|
|
public void clipRRect(RRect rrect) {
|
|
var path = new Path();
|
|
path.addRRect(rrect);
|
|
this.clipPath(path);
|
|
}
|
|
|
|
public void clipPath(Path path) {
|
|
var layer = this._getLayer();
|
|
var state = this._getState();
|
|
layer.clipStack.clipPath(path, state.xform, this._devicePixelRatio);
|
|
}
|
|
|
|
public void drawLine(Offset from, Offset to, Paint paint) {
|
|
var path = new Path();
|
|
path.moveTo(from.dx, from.dy);
|
|
path.lineTo(to.dx, to.dy);
|
|
this.drawPath(path, paint);
|
|
}
|
|
|
|
public void drawRect(Rect rect, Paint paint) {
|
|
var path = new Path();
|
|
path.addRect(rect);
|
|
this.drawPath(path, paint);
|
|
}
|
|
|
|
public void drawRRect(RRect rrect, Paint paint) {
|
|
var path = new Path();
|
|
path.addRRect(rrect);
|
|
this.drawPath(path, paint);
|
|
}
|
|
|
|
public void drawDRRect(RRect outer, RRect inner, Paint paint) {
|
|
var path = new Path();
|
|
path.addRRect(outer);
|
|
path.addRRect(inner);
|
|
path.winding(PathWinding.clockwise);
|
|
this.drawPath(path, paint);
|
|
}
|
|
|
|
public void drawOval(Rect rect, Paint paint) {
|
|
var w = rect.width / 2;
|
|
var h = rect.height / 2;
|
|
var path = new Path();
|
|
path.addEllipse(rect.left + w, rect.top + h, w, h);
|
|
this.drawPath(path, paint);
|
|
}
|
|
|
|
public void drawCircle(Offset c, double radius, Paint paint) {
|
|
var path = new Path();
|
|
path.addCircle(c.dx, c.dy, radius);
|
|
this.drawPath(path, paint);
|
|
}
|
|
|
|
public void drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint) {
|
|
//var path = new Path();
|
|
}
|
|
|
|
static Vector4 _premulColor(Color c) {
|
|
return new Vector4(
|
|
c.red * c.alpha / (255f * 255f),
|
|
c.green * c.alpha / (255f * 255f),
|
|
c.blue * c.alpha / (255f * 255f),
|
|
c.alpha / 255f
|
|
);
|
|
}
|
|
|
|
static Vector4 _color(Color c) {
|
|
return new Vector4(
|
|
c.red / 255f,
|
|
c.green / 255f,
|
|
c.blue / 255f,
|
|
c.alpha / 255f
|
|
);
|
|
}
|
|
|
|
MaterialPropertyBlock _getMatProps(Paint paint, float alpha = 1.0f) {
|
|
var properties = new MaterialPropertyBlock();
|
|
properties.SetVector("_viewSize", this._getViewSize());
|
|
|
|
if (paint.shader is Gradient) {
|
|
var gradient = (Gradient) paint.shader;
|
|
|
|
var innerCol = gradient.innerColor;
|
|
var outerCol = gradient.outerColor;
|
|
|
|
if (alpha != 1.0f) {
|
|
innerCol = innerCol.withOpacity(innerCol.opacity * alpha);
|
|
outerCol = outerCol.withOpacity(outerCol.opacity * alpha);
|
|
}
|
|
|
|
var matrix = Matrix4x4.zero;
|
|
matrix[0, 0] = gradient.invXform[0];
|
|
matrix[1, 0] = gradient.invXform[1];
|
|
matrix[0, 1] = gradient.invXform[2];
|
|
matrix[1, 1] = gradient.invXform[3];
|
|
matrix[0, 2] = gradient.invXform[4];
|
|
matrix[1, 2] = gradient.invXform[5];
|
|
|
|
properties.SetMatrix("_paintMat", matrix);
|
|
properties.SetVector("_innerCol", _premulColor(innerCol));
|
|
properties.SetVector("_outerCol", _premulColor(outerCol));
|
|
properties.SetVector("_extent", new Vector2(gradient.extent[0], gradient.extent[1]));
|
|
properties.SetFloat("_radius", gradient.radius);
|
|
properties.SetFloat("_feather", gradient.feather);
|
|
} else {
|
|
var innerCol = paint.color;
|
|
var outerCol = paint.color;
|
|
|
|
if (alpha != 1.0f) {
|
|
innerCol = innerCol.withOpacity(innerCol.opacity * alpha);
|
|
outerCol = outerCol.withOpacity(outerCol.opacity * alpha);
|
|
}
|
|
|
|
properties.SetMatrix("_paintMat", Matrix4x4.identity);
|
|
properties.SetVector("_innerCol", _premulColor(innerCol));
|
|
properties.SetVector("_outerCol", _premulColor(outerCol));
|
|
properties.SetVector("_extent", new Vector2(0.0f, 0.0f));
|
|
properties.SetFloat("_radius", 0.0f);
|
|
properties.SetFloat("_feather", 1.0f);
|
|
}
|
|
|
|
return properties;
|
|
}
|
|
|
|
bool _applyClip(Rect queryBounds) {
|
|
var layer = this._getLayer();
|
|
var layerBounds = Rect.fromLTRB(0, 0, layer.layerBounds.width, layer.layerBounds.height);
|
|
ReducedClip reducedClip = new ReducedClip(layer.clipStack, layerBounds, queryBounds);
|
|
if (reducedClip.isEmpty()) {
|
|
return false;
|
|
}
|
|
|
|
var scissor = reducedClip.scissor;
|
|
var deviceScissor = Rect.fromLTRB(
|
|
scissor.left, layerBounds.height - scissor.bottom,
|
|
scissor.right, layerBounds.height - scissor.top
|
|
).scale(layer.width / layerBounds.width, layer.height / layerBounds.height);
|
|
|
|
layer.draws.Add(new RenderScissor {
|
|
deviceScissor = deviceScissor,
|
|
});
|
|
|
|
if (this._mustRenderClip(reducedClip.maskGenID(), reducedClip.scissor)) {
|
|
var mat = this._getMat(null);
|
|
var properties = new MaterialPropertyBlock();
|
|
properties.SetVector("_viewSize", this._getViewSize());
|
|
|
|
var boundsMesh = new MeshMesh(reducedClip.scissor);
|
|
this._getLayer().draws.Add(new RenderDraw {
|
|
mesh = boundsMesh,
|
|
pass = CanvasShaderPass.stencilClear,
|
|
material = mat,
|
|
properties = properties,
|
|
});
|
|
|
|
for (var i = 0; i < reducedClip.maskElements.Count; i++) {
|
|
var maskElement = reducedClip.maskElements[i];
|
|
|
|
this._getLayer().draws.Add(new RenderDraw {
|
|
mesh = maskElement.mesh,
|
|
pass = CanvasShaderPass.stencilIntersect0,
|
|
material = mat,
|
|
properties = properties,
|
|
});
|
|
this._getLayer().draws.Add(new RenderDraw {
|
|
mesh = boundsMesh,
|
|
pass = CanvasShaderPass.stencilIntersect1,
|
|
material = mat,
|
|
properties = properties,
|
|
});
|
|
}
|
|
|
|
this._setLastClipGenId(reducedClip.maskGenID(), reducedClip.scissor);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void drawPath(Path path, Paint paint) {
|
|
D.assert(path != null);
|
|
D.assert(paint != null);
|
|
|
|
if (paint.style == PaintingStyle.fill) {
|
|
var state = this._getState();
|
|
var cache = path.flatten(state.xform, this._devicePixelRatio);
|
|
|
|
bool convex;
|
|
var mesh = cache.getFillMesh(out convex);
|
|
|
|
if (!this._applyClip(mesh.bounds)) {
|
|
return;
|
|
}
|
|
|
|
var mat = this._getMat(paint);
|
|
var properties = this._getMatProps(paint);
|
|
if (convex) {
|
|
this._getLayer().draws.Add(new RenderDraw {
|
|
mesh = mesh,
|
|
pass = CanvasShaderPass.convexFill,
|
|
material = mat,
|
|
properties = properties,
|
|
});
|
|
} else {
|
|
this._getLayer().draws.Add(new RenderDraw {
|
|
mesh = mesh,
|
|
pass = CanvasShaderPass.fillPass0,
|
|
material = mat,
|
|
properties = properties,
|
|
});
|
|
this._getLayer().draws.Add(new RenderDraw {
|
|
mesh = mesh.boundsMesh,
|
|
pass = CanvasShaderPass.fillPass1,
|
|
material = mat,
|
|
properties = properties,
|
|
});
|
|
}
|
|
} else {
|
|
var state = this._getState();
|
|
float scale = XformUtils.getAverageScale(state.xform);
|
|
float strokeWidth = ((float) paint.strokeWidth * scale).clamp(0, 200.0f);
|
|
float alpha = 1.0f;
|
|
|
|
if (strokeWidth == 0) {
|
|
strokeWidth = this._fringeWidth;
|
|
} else if (strokeWidth < this._fringeWidth) {
|
|
// If the stroke width is less than pixel size, use alpha to emulate coverage.
|
|
// Since coverage is area, scale by alpha*alpha.
|
|
alpha = (strokeWidth / this._fringeWidth).clamp(0.0f, 1.0f);
|
|
alpha *= alpha;
|
|
strokeWidth = this._fringeWidth;
|
|
}
|
|
|
|
var cache = path.flatten(state.xform, this._devicePixelRatio);
|
|
var mesh = cache.getStrokeMesh(
|
|
strokeWidth * 0.5f,
|
|
paint.strokeCap,
|
|
paint.strokeJoin,
|
|
(float) paint.strokeMiterLimit);
|
|
|
|
if (!this._applyClip(mesh.bounds)) {
|
|
return;
|
|
}
|
|
|
|
var mat = this._getMat(paint);
|
|
var properties = this._getMatProps(paint, alpha);
|
|
this._getLayer().draws.Add(new RenderDraw {
|
|
mesh = mesh,
|
|
pass = CanvasShaderPass.strokePass0,
|
|
material = mat,
|
|
properties = properties,
|
|
});
|
|
this._getLayer().draws.Add(new RenderDraw {
|
|
mesh = mesh,
|
|
pass = CanvasShaderPass.strokePass1,
|
|
material = mat,
|
|
properties = properties,
|
|
});
|
|
}
|
|
}
|
|
|
|
public void drawImage(Image image, Offset offset, Paint paint) {
|
|
D.assert(image != null);
|
|
D.assert(offset != null);
|
|
D.assert(paint != null);
|
|
|
|
this.drawImageRect(image,
|
|
null,
|
|
Rect.fromLTWH(offset.dx, offset.dy, image.width / this._devicePixelRatio,
|
|
image.height / this._devicePixelRatio),
|
|
paint);
|
|
}
|
|
|
|
Vector2 _getViewSize() {
|
|
var layer = this._getLayer();
|
|
return new Vector2((float) layer.layerBounds.width, (float) layer.layerBounds.height);
|
|
}
|
|
|
|
static Dictionary<BlendMode, Material> _materials = new Dictionary<BlendMode, Material>();
|
|
static readonly int _srcBlend = Shader.PropertyToID("_SrcBlend");
|
|
static readonly int _dstBlend = Shader.PropertyToID("_DstBlend");
|
|
|
|
Material _getMat(Paint paint) {
|
|
var op = paint == null ? BlendMode.srcOver : paint.blendMode;
|
|
|
|
Material mat;
|
|
if (_materials.TryGetValue(op, out mat) && mat) {
|
|
return mat;
|
|
}
|
|
|
|
var canvasShader = Shader.Find("UIWidgets/canvas");
|
|
if (canvasShader == null) {
|
|
throw new Exception("UIWidgets/canvas not found");
|
|
}
|
|
|
|
mat = new Material(canvasShader);
|
|
mat.hideFlags = HideFlags.HideAndDontSave;
|
|
_materials[op] = mat;
|
|
|
|
if (op == BlendMode.srcOver) {
|
|
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.One);
|
|
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
|
|
} else if (op == BlendMode.srcIn) {
|
|
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.DstAlpha);
|
|
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.Zero);
|
|
} else if (op == BlendMode.srcOut) {
|
|
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.OneMinusDstAlpha);
|
|
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.Zero);
|
|
} else if (op == BlendMode.srcATop) {
|
|
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.DstAlpha);
|
|
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
|
|
} else if (op == BlendMode.dstOver) {
|
|
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.OneMinusDstAlpha);
|
|
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.One);
|
|
} else if (op == BlendMode.dstIn) {
|
|
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.Zero);
|
|
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.SrcAlpha);
|
|
} else if (op == BlendMode.dstOut) {
|
|
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.Zero);
|
|
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
|
|
} else if (op == BlendMode.dstATop) {
|
|
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.OneMinusDstAlpha);
|
|
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.SrcAlpha);
|
|
} else if (op == BlendMode.plus) {
|
|
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.One);
|
|
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.One);
|
|
} else if (op == BlendMode.src) {
|
|
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.One);
|
|
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.Zero);
|
|
} else if (op == BlendMode.dst) {
|
|
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.Zero);
|
|
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.One);
|
|
} else if (op == BlendMode.xor) {
|
|
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.OneMinusDstAlpha);
|
|
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
|
|
} else if (op == BlendMode.clear) {
|
|
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.Zero);
|
|
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.Zero);
|
|
} else {
|
|
Debug.LogWarning("Not supported BlendMode: " + op + ". Defaults to srcOver");
|
|
mat.SetInt(_srcBlend, (int) UnityEngine.Rendering.BlendMode.One);
|
|
mat.SetInt(_dstBlend, (int) UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
|
|
}
|
|
|
|
return mat;
|
|
}
|
|
|
|
MaterialPropertyBlock _getMatPropsForImage(Texture image, Paint paint) {
|
|
var properties = new MaterialPropertyBlock();
|
|
properties.SetVector("_viewSize", this._getViewSize());
|
|
properties.SetVector("_innerCol", _color(paint.color));
|
|
if (image != null) {
|
|
image.filterMode = paint.filterMode;
|
|
properties.SetTexture("_tex", image);
|
|
}
|
|
|
|
return properties;
|
|
}
|
|
|
|
public void drawImageRect(Image image, Rect dst, Paint paint) {
|
|
this.drawImageRect(image, null, dst, paint);
|
|
}
|
|
|
|
public void drawImageRect(Image image, Rect src, Rect dst, Paint paint) {
|
|
D.assert(image != null);
|
|
D.assert(dst != null);
|
|
D.assert(paint != null);
|
|
|
|
if (src == null) {
|
|
src = Rect.fromLTWH(0, 0, image.width, image.height);
|
|
}
|
|
|
|
var vertices = new List<Vector3>(4);
|
|
var uv = new List<Vector2>(4);
|
|
|
|
float uvx0 = (float) src.left / image.width;
|
|
float uvx1 = (float) src.right / image.width;
|
|
float uvy0 = 1.0f - (float) src.top / image.height;
|
|
float uvy1 = 1.0f - (float) src.bottom / image.height;
|
|
|
|
var state = this._getState();
|
|
float x, y;
|
|
PathUtils.transformPoint(out x, out y, state.xform, (float) dst.left, (float) dst.top);
|
|
vertices.Add(new Vector2(x, y));
|
|
uv.Add(new Vector2(uvx0, uvy0));
|
|
PathUtils.transformPoint(out x, out y, state.xform, (float) dst.left, (float) dst.bottom);
|
|
vertices.Add(new Vector2(x, y));
|
|
uv.Add(new Vector2(uvx0, uvy1));
|
|
PathUtils.transformPoint(out x, out y, state.xform, (float) dst.right, (float) dst.bottom);
|
|
vertices.Add(new Vector2(x, y));
|
|
uv.Add(new Vector2(uvx1, uvy1));
|
|
PathUtils.transformPoint(out x, out y, state.xform, (float) dst.right, (float) dst.top);
|
|
vertices.Add(new Vector2(x, y));
|
|
uv.Add(new Vector2(uvx1, uvy0));
|
|
|
|
var mesh = new MeshMesh(vertices, _imageTriangles, uv);
|
|
if (!this._applyClip(mesh.bounds)) {
|
|
return;
|
|
}
|
|
|
|
var mat = this._getMat(paint);
|
|
var properties = this._getMatPropsForImage(image.texture, paint);
|
|
var isRT = image.texture is RenderTexture;
|
|
this._getLayer().draws.Add(new RenderDraw {
|
|
mesh = mesh,
|
|
pass = isRT ? CanvasShaderPass.texrtPass0 : CanvasShaderPass.texPass0,
|
|
material = mat,
|
|
image = image, // to keep a reference to avoid GC.
|
|
properties = properties,
|
|
});
|
|
}
|
|
|
|
public void drawImageNine(Image image, Rect center, Rect dst, Paint paint) {
|
|
this.drawImageNine(image, null, center, dst, paint);
|
|
}
|
|
|
|
public void drawImageNine(Image image, Rect src, Rect center, Rect dst, Paint paint) {
|
|
D.assert(image != null);
|
|
D.assert(center != null);
|
|
D.assert(dst != null);
|
|
D.assert(paint != null);
|
|
|
|
if (src == null) {
|
|
src = Rect.fromLTWH(0, 0, image.width, image.height);
|
|
}
|
|
|
|
float x0 = (float) dst.left;
|
|
float x3 = (float) dst.right;
|
|
float x1 = x0 + (float) center.left / this._devicePixelRatio;
|
|
float x2 = x3 - (float) (src.width - center.right) / this._devicePixelRatio;
|
|
|
|
float y0 = (float) dst.top;
|
|
float y3 = (float) dst.bottom;
|
|
float y1 = y0 + (float) center.top / this._devicePixelRatio;
|
|
float y2 = y3 - (float) (src.height - center.bottom) / this._devicePixelRatio;
|
|
|
|
float tx0 = (float) src.left / image.width;
|
|
float tx1 = (float) (src.left + center.left) / image.width;
|
|
float tx2 = (float) (src.left + center.right) / image.width;
|
|
float tx3 = (float) src.right / image.width;
|
|
float ty0 = 1 - (float) src.top / image.height;
|
|
float ty1 = 1 - (float) (src.top + center.top) / image.height;
|
|
float ty2 = 1 - (float) (src.top + center.bottom) / image.height;
|
|
float ty3 = 1 - (float) src.bottom / image.height;
|
|
|
|
var vertices = new List<Vector3>(16);
|
|
var uv = new List<Vector2>(16);
|
|
|
|
var state = this._getState();
|
|
float x, y;
|
|
PathUtils.transformPoint(out x, out y, state.xform, x0, y0);
|
|
vertices.Add(new Vector2(x, y));
|
|
uv.Add(new Vector2(tx0, ty0));
|
|
PathUtils.transformPoint(out x, out y, state.xform, x1, y0);
|
|
vertices.Add(new Vector2(x, y));
|
|
uv.Add(new Vector2(tx1, ty0));
|
|
PathUtils.transformPoint(out x, out y, state.xform, x2, y0);
|
|
vertices.Add(new Vector2(x, y));
|
|
uv.Add(new Vector2(tx2, ty0));
|
|
PathUtils.transformPoint(out x, out y, state.xform, x3, y0);
|
|
vertices.Add(new Vector2(x, y));
|
|
uv.Add(new Vector2(tx3, ty0));
|
|
PathUtils.transformPoint(out x, out y, state.xform, x0, y1);
|
|
vertices.Add(new Vector2(x, y));
|
|
uv.Add(new Vector2(tx0, ty1));
|
|
PathUtils.transformPoint(out x, out y, state.xform, x1, y1);
|
|
vertices.Add(new Vector2(x, y));
|
|
uv.Add(new Vector2(tx1, ty1));
|
|
PathUtils.transformPoint(out x, out y, state.xform, x2, y1);
|
|
vertices.Add(new Vector2(x, y));
|
|
uv.Add(new Vector2(tx2, ty1));
|
|
PathUtils.transformPoint(out x, out y, state.xform, x3, y1);
|
|
vertices.Add(new Vector2(x, y));
|
|
uv.Add(new Vector2(tx3, ty1));
|
|
PathUtils.transformPoint(out x, out y, state.xform, x0, y2);
|
|
vertices.Add(new Vector2(x, y));
|
|
uv.Add(new Vector2(tx0, ty2));
|
|
PathUtils.transformPoint(out x, out y, state.xform, x1, y2);
|
|
vertices.Add(new Vector2(x, y));
|
|
uv.Add(new Vector2(tx1, ty2));
|
|
PathUtils.transformPoint(out x, out y, state.xform, x2, y2);
|
|
vertices.Add(new Vector2(x, y));
|
|
uv.Add(new Vector2(tx2, ty2));
|
|
PathUtils.transformPoint(out x, out y, state.xform, x3, y2);
|
|
vertices.Add(new Vector2(x, y));
|
|
uv.Add(new Vector2(tx3, ty2));
|
|
PathUtils.transformPoint(out x, out y, state.xform, x0, y3);
|
|
vertices.Add(new Vector2(x, y));
|
|
uv.Add(new Vector2(tx0, ty3));
|
|
PathUtils.transformPoint(out x, out y, state.xform, x1, y3);
|
|
vertices.Add(new Vector2(x, y));
|
|
uv.Add(new Vector2(tx1, ty3));
|
|
PathUtils.transformPoint(out x, out y, state.xform, x2, y3);
|
|
vertices.Add(new Vector2(x, y));
|
|
uv.Add(new Vector2(tx2, ty3));
|
|
PathUtils.transformPoint(out x, out y, state.xform, x3, y3);
|
|
vertices.Add(new Vector2(x, y));
|
|
uv.Add(new Vector2(tx3, ty3));
|
|
|
|
var mesh = new MeshMesh(vertices, _imageNineTriangles, uv);
|
|
if (!this._applyClip(mesh.bounds)) {
|
|
return;
|
|
}
|
|
|
|
var mat = this._getMat(paint);
|
|
var properties = this._getMatPropsForImage(image.texture, paint);
|
|
this._getLayer().draws.Add(new RenderDraw {
|
|
mesh = mesh,
|
|
pass = CanvasShaderPass.texPass0,
|
|
material = mat,
|
|
image = image, // to keep a reference to avoid GC.
|
|
properties = properties,
|
|
});
|
|
}
|
|
|
|
public void drawPicture(Picture picture) {
|
|
this.save();
|
|
|
|
int saveCount = 0;
|
|
|
|
var drawCmds = picture.drawCmds;
|
|
foreach (var drawCmd in drawCmds) {
|
|
if (drawCmd is DrawSave) {
|
|
saveCount++;
|
|
this.save();
|
|
} else if (drawCmd is DrawSaveLayer) {
|
|
saveCount++;
|
|
var drawSaveLayer = (DrawSaveLayer) drawCmd;
|
|
this.saveLayer(drawSaveLayer.rect, drawSaveLayer.paint);
|
|
} else if (drawCmd is DrawRestore) {
|
|
saveCount--;
|
|
if (saveCount < 0) {
|
|
throw new Exception("unmatched save/restore in picture");
|
|
}
|
|
|
|
this.restore();
|
|
} else if (drawCmd is DrawTranslate) {
|
|
var drawTranslate = (DrawTranslate) drawCmd;
|
|
this.translate(drawTranslate.dx, drawTranslate.dy);
|
|
} else if (drawCmd is DrawScale) {
|
|
var drawScale = (DrawScale) drawCmd;
|
|
this.scale(drawScale.sx, drawScale.sy);
|
|
} else if (drawCmd is DrawRotate) {
|
|
var drawRotate = (DrawRotate) drawCmd;
|
|
this.rotate(drawRotate.radians, drawRotate.offset);
|
|
} else if (drawCmd is DrawSkew) {
|
|
var drawSkew = (DrawSkew) drawCmd;
|
|
this.skew(drawSkew.sx, drawSkew.sy);
|
|
} else if (drawCmd is DrawConcat) {
|
|
var drawConcat = (DrawConcat) drawCmd;
|
|
this.concat(drawConcat.matrix);
|
|
} else if (drawCmd is DrawResetMatrix) {
|
|
this.resetMatrix();
|
|
} else if (drawCmd is DrawSetMatrix) {
|
|
var drawSetMatrix = (DrawSetMatrix) drawCmd;
|
|
this.setMatrix(drawSetMatrix.matrix);
|
|
} else if (drawCmd is DrawClipRect) {
|
|
var drawClipRect = (DrawClipRect) drawCmd;
|
|
this.clipRect(drawClipRect.rect);
|
|
} else if (drawCmd is DrawClipRRect) {
|
|
var drawClipRRect = (DrawClipRRect) drawCmd;
|
|
this.clipRRect(drawClipRRect.rrect);
|
|
} else if (drawCmd is DrawClipPath) {
|
|
var drawClipPath = (DrawClipPath) drawCmd;
|
|
this.clipPath(drawClipPath.path);
|
|
} else if (drawCmd is DrawPath) {
|
|
var drawPath = (DrawPath) drawCmd;
|
|
this.drawPath(drawPath.path, drawPath.paint);
|
|
} else if (drawCmd is DrawImage) {
|
|
var drawImage = (DrawImage) drawCmd;
|
|
this.drawImage(drawImage.image, drawImage.offset, drawImage.paint);
|
|
} else if (drawCmd is DrawImageRect) {
|
|
var drawImageRect = (DrawImageRect) drawCmd;
|
|
this.drawImageRect(drawImageRect.image, drawImageRect.src, drawImageRect.dst, drawImageRect.paint);
|
|
} else if (drawCmd is DrawImageNine) {
|
|
var drawImageNine = (DrawImageNine) drawCmd;
|
|
this.drawImageNine(drawImageNine.image, drawImageNine.src, drawImageNine.center, drawImageNine.dst,
|
|
drawImageNine.paint);
|
|
} else if (drawCmd is DrawPicture) {
|
|
var drawPicture = (DrawPicture) drawCmd;
|
|
this.drawPicture(drawPicture.picture);
|
|
} else if (drawCmd is DrawTextBlob) {
|
|
var drawTextBlob = (DrawTextBlob) drawCmd;
|
|
this.drawTextBlob(drawTextBlob.textBlob, drawTextBlob.offset, drawTextBlob.paint);
|
|
} else {
|
|
throw new Exception("unknown drawCmd: " + drawCmd);
|
|
}
|
|
}
|
|
|
|
if (saveCount != 0) {
|
|
throw new Exception("unmatched save/restore in picture");
|
|
}
|
|
|
|
this.restore();
|
|
}
|
|
|
|
public void drawTextBlob(TextBlob textBlob, Offset offset, Paint paint) {
|
|
var state = this._getState();
|
|
|
|
var xform = new float[6];
|
|
XformUtils.transformTranslate(xform, (float) offset.dx, (float) offset.dy);
|
|
XformUtils.transformMultiply(xform, state.xform); // xform = state.xform * xform
|
|
|
|
var scale = XformUtils.getAverageScale(xform) * this._devicePixelRatio;
|
|
var mesh = MeshGenerator.generateMesh(textBlob, scale).transform(xform);
|
|
|
|
if (!this._applyClip(mesh.bounds)) {
|
|
return;
|
|
}
|
|
|
|
var mat = this._getMat(paint);
|
|
var font = FontManager.instance.getOrCreate(textBlob.style.fontFamily).font;
|
|
var properties = this._getMatPropsForImage(font.material.mainTexture, paint);
|
|
this._getLayer().draws.Add(new RenderDraw {
|
|
mesh = mesh,
|
|
pass = CanvasShaderPass.texfontPass0,
|
|
material = mat,
|
|
properties = properties,
|
|
});
|
|
}
|
|
|
|
public void flush() {
|
|
if (this._saveCount > 0) {
|
|
throw new Exception("unmatched save/restore");
|
|
}
|
|
|
|
D.assert(this._layers.Count == 1);
|
|
D.assert(this._layers[0].states.Count == 1);
|
|
|
|
var layer = this._getLayer();
|
|
if (layer.draws.Count == 0) {
|
|
D.assert(layer.layers.Count == 0);
|
|
return;
|
|
}
|
|
|
|
using (var cmdBuf = new CommandBuffer()) {
|
|
cmdBuf.name = "CommandBufferCanvas";
|
|
this._drawLayer(layer, cmdBuf);
|
|
Graphics.ExecuteCommandBuffer(cmdBuf);
|
|
}
|
|
|
|
this._clearLayer(layer);
|
|
}
|
|
|
|
public void reset() {
|
|
foreach (var layer in this._layers) {
|
|
this._clearLayer(layer);
|
|
}
|
|
|
|
this._saveCount = 0;
|
|
|
|
RenderLayer firstLayer;
|
|
if (this._layers.Count == 0) {
|
|
var bounds = Rect.fromLTWH(0, 0,
|
|
this._renderTexture.width / this._devicePixelRatio,
|
|
this._renderTexture.height / this._devicePixelRatio);
|
|
firstLayer = new RenderLayer {
|
|
width = this._renderTexture.width,
|
|
height = this._renderTexture.height,
|
|
layerBounds = bounds,
|
|
};
|
|
} else {
|
|
D.assert(this._layers.Count > 0);
|
|
firstLayer = this._layers[0];
|
|
firstLayer = new RenderLayer {
|
|
width = firstLayer.width,
|
|
height = firstLayer.height,
|
|
layerBounds = firstLayer.layerBounds,
|
|
};
|
|
}
|
|
|
|
this._layers.Clear();
|
|
this._layers.Add(firstLayer);
|
|
}
|
|
|
|
void _drawLayer(RenderLayer layer, CommandBuffer cmdBuf) {
|
|
foreach (var subLayer in layer.layers) {
|
|
cmdBuf.GetTemporaryRT(subLayer.rtID, new RenderTextureDescriptor(
|
|
subLayer.width, subLayer.height,
|
|
RenderTextureFormat.Default, 24) {
|
|
msaaSamples = QualitySettings.antiAliasing,
|
|
useMipMap = false,
|
|
autoGenerateMips = false,
|
|
});
|
|
this._drawLayer(subLayer, cmdBuf);
|
|
}
|
|
|
|
if (layer.rtID == 0) {
|
|
cmdBuf.SetRenderTarget(this._renderTexture,
|
|
RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store);
|
|
cmdBuf.ClearRenderTarget(true, true, UnityEngine.Color.clear);
|
|
} else {
|
|
cmdBuf.SetRenderTarget(layer.rtID,
|
|
RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store);
|
|
cmdBuf.ClearRenderTarget(true, true, UnityEngine.Color.clear);
|
|
}
|
|
|
|
foreach (var draw in layer.draws) {
|
|
draw.onExecute(this, cmdBuf);
|
|
}
|
|
|
|
foreach (var subLayer in layer.layers) {
|
|
cmdBuf.ReleaseTemporaryRT(subLayer.rtID);
|
|
}
|
|
}
|
|
|
|
void _clearLayer(RenderLayer layer) {
|
|
foreach (var draw in layer.draws) {
|
|
draw.onDestroy(this);
|
|
}
|
|
layer.draws.Clear();
|
|
|
|
foreach (var subLayer in layer.layers) {
|
|
this._clearLayer(subLayer);
|
|
}
|
|
layer.layers.Clear();
|
|
}
|
|
|
|
void _setLastClipGenId(uint clipGenId, Rect clipBounds) {
|
|
var layer = this._getLayer();
|
|
layer.lastClipGenId = clipGenId;
|
|
layer.lastClipBounds = clipBounds;
|
|
}
|
|
|
|
bool _mustRenderClip(uint clipGenId, Rect clipBounds) {
|
|
var layer = this._getLayer();
|
|
return layer.lastClipGenId != clipGenId || layer.lastClipBounds != clipBounds;
|
|
}
|
|
|
|
class State {
|
|
public float[] xform = {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f};
|
|
|
|
public State copy() {
|
|
return new State {
|
|
xform = (float[]) this.xform.Clone(),
|
|
};
|
|
}
|
|
}
|
|
|
|
class RenderLayer {
|
|
public int rtID;
|
|
public int width;
|
|
public int height;
|
|
public Rect layerBounds;
|
|
public Paint layerPaint;
|
|
public readonly List<RenderCmd> draws = new List<RenderCmd>();
|
|
public readonly List<RenderLayer> layers = new List<RenderLayer>();
|
|
public readonly List<State> states = new List<State> {new State()};
|
|
public readonly ClipStack clipStack = new ClipStack();
|
|
public uint lastClipGenId;
|
|
public Rect lastClipBounds;
|
|
}
|
|
|
|
interface RenderCmd {
|
|
void onExecute(CommandBufferCanvas canvas, CommandBuffer cmdBuf);
|
|
void onDestroy(CommandBufferCanvas canvas);
|
|
}
|
|
|
|
class RenderDraw : RenderCmd {
|
|
public MeshMesh mesh;
|
|
public int pass;
|
|
public MaterialPropertyBlock properties;
|
|
public RenderLayer layer;
|
|
public Material material;
|
|
public Image image; // just to keep a reference to avoid GC.
|
|
public Mesh meshObj;
|
|
|
|
public void onExecute(CommandBufferCanvas canvas, CommandBuffer cmdBuf) {
|
|
if (this.layer != null) {
|
|
cmdBuf.SetGlobalTexture("_tex", this.layer.rtID);
|
|
}
|
|
|
|
if (!this.meshObj) {
|
|
this.meshObj = canvas._meshPool.getMesh();
|
|
|
|
// clear triangles first in order to bypass validation in SetVertices.
|
|
this.meshObj.SetTriangles((int[]) null, 0, false);
|
|
|
|
this.meshObj.SetVertices(this.mesh.vertices);
|
|
this.meshObj.SetTriangles(this.mesh.triangles, 0, false);
|
|
this.meshObj.SetUVs(0, this.mesh.uv);
|
|
}
|
|
|
|
cmdBuf.DrawMesh(this.meshObj, Matrix4x4.identity, this.material, 0, this.pass, this.properties);
|
|
if (this.layer != null) {
|
|
cmdBuf.SetGlobalTexture("_tex", BuiltinRenderTextureType.None);
|
|
}
|
|
}
|
|
|
|
public void onDestroy(CommandBufferCanvas canvas) {
|
|
if (this.meshObj) {
|
|
canvas._meshPool.returnMesh(this.meshObj);
|
|
this.meshObj = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
class RenderScissor : RenderCmd {
|
|
public Rect deviceScissor;
|
|
|
|
public void onExecute(CommandBufferCanvas canvas, CommandBuffer cmdBuf) {
|
|
cmdBuf.EnableScissorRect(this.deviceScissor.toRect());
|
|
}
|
|
|
|
public void onDestroy(CommandBufferCanvas canvas) {
|
|
}
|
|
}
|
|
}
|
|
|
|
static class CanvasShaderPass {
|
|
public const int fillPass0 = 0;
|
|
public const int fillPass1 = 1;
|
|
public const int convexFill = 2;
|
|
public const int strokePass0 = 3;
|
|
public const int strokePass1 = 4;
|
|
public const int texPass0 = 5;
|
|
public const int texrtPass0 = 6;
|
|
public const int stencilClear = 7;
|
|
public const int stencilIntersect0 = 8;
|
|
public const int stencilIntersect1 = 9;
|
|
public const int texfontPass0 = 10;
|
|
}
|
|
|
|
static class XformUtils {
|
|
public static void transformIdentity(float[] t) {
|
|
t[0] = 1.0f;
|
|
t[1] = 0.0f;
|
|
t[2] = 0.0f;
|
|
t[3] = 1.0f;
|
|
t[4] = 0.0f;
|
|
t[5] = 0.0f;
|
|
}
|
|
|
|
public static void transformTranslate(float[] t, float tx, float ty) {
|
|
t[0] = 1.0f;
|
|
t[1] = 0.0f;
|
|
t[2] = 0.0f;
|
|
t[3] = 1.0f;
|
|
t[4] = tx;
|
|
t[5] = ty;
|
|
}
|
|
|
|
public static void transformScale(float[] t, float sx, float sy) {
|
|
t[0] = sx;
|
|
t[1] = 0.0f;
|
|
t[2] = 0.0f;
|
|
t[3] = sy;
|
|
t[4] = 0.0f;
|
|
t[5] = 0.0f;
|
|
}
|
|
|
|
public static void transformRotate(float[] t, float a) {
|
|
float cs = Mathf.Cos(a), sn = Mathf.Sin(a);
|
|
t[0] = cs;
|
|
t[1] = sn;
|
|
t[2] = -sn;
|
|
t[3] = cs;
|
|
t[4] = 0.0f;
|
|
t[5] = 0.0f;
|
|
}
|
|
|
|
public static void transformSkew(float[] t, float sx, float sy) {
|
|
t[0] = 1.0f;
|
|
t[1] = Mathf.Tan(sy);
|
|
t[2] = Mathf.Tan(sx);
|
|
t[3] = 1.0f;
|
|
t[4] = 0.0f;
|
|
t[5] = 0.0f;
|
|
}
|
|
|
|
|
|
// t = s * t;
|
|
public static void transformMultiply(float[] t, float[] s) {
|
|
float t0 = t[0] * s[0] + t[1] * s[2];
|
|
float t2 = t[2] * s[0] + t[3] * s[2];
|
|
float t4 = t[4] * s[0] + t[5] * s[2] + s[4];
|
|
t[1] = t[0] * s[1] + t[1] * s[3];
|
|
t[3] = t[2] * s[1] + t[3] * s[3];
|
|
t[5] = t[4] * s[1] + t[5] * s[3] + s[5];
|
|
t[0] = t0;
|
|
t[2] = t2;
|
|
t[4] = t4;
|
|
}
|
|
|
|
public static void transformPremultiply(float[] t, float[] s) {
|
|
float[] s2 = {s[0], s[1], s[2], s[3], s[4], s[5]};
|
|
transformMultiply(s2, t);
|
|
t[0] = s2[0];
|
|
t[1] = s2[1];
|
|
t[2] = s2[2];
|
|
t[3] = s2[3];
|
|
t[4] = s2[4];
|
|
t[5] = s2[5];
|
|
}
|
|
|
|
public static int transformInverse(float[] inv, float[] t) {
|
|
double det = (double) t[0] * t[3] - (double) t[2] * t[1];
|
|
if (det > -1e-6 && det < 1e-6) {
|
|
transformIdentity(inv);
|
|
return 0;
|
|
}
|
|
|
|
double invdet = 1.0 / det;
|
|
inv[0] = (float) (t[3] * invdet);
|
|
inv[2] = (float) (-t[2] * invdet);
|
|
inv[4] = (float) (((double) t[2] * t[5] - (double) t[3] * t[4]) * invdet);
|
|
inv[1] = (float) (-t[1] * invdet);
|
|
inv[3] = (float) (t[0] * invdet);
|
|
inv[5] = (float) (((double) t[1] * t[4] - (double) t[0] * t[5]) * invdet);
|
|
return 1;
|
|
}
|
|
|
|
public static float getAverageScale(float[] t) {
|
|
return (getScaleX(t) + getScaleY(t)) * 0.5f;
|
|
}
|
|
|
|
public static float getScaleX(float[] t) {
|
|
return Mathf.Sqrt(t[0] * t[0] + t[2] * t[2]);
|
|
}
|
|
|
|
public static float getScaleY(float[] t) {
|
|
return Mathf.Sqrt(t[1] * t[1] + t[3] * t[3]);
|
|
}
|
|
|
|
public static float[] fromMatrix3(Matrix3 matrix) {
|
|
return new[] {
|
|
matrix.getScaleX(), matrix.getSkewY(),
|
|
matrix.getSkewX(), matrix.getScaleY(),
|
|
matrix.getTranslateX(), matrix.getTranslateY(),
|
|
};
|
|
}
|
|
|
|
public static Matrix3 toMatrix3(float[] xform) {
|
|
return Matrix3.makeAll(
|
|
xform[0], xform[2], xform[4],
|
|
xform[1], xform[3], xform[5],
|
|
0, 0, 1
|
|
);
|
|
}
|
|
}
|
|
}
|