|
|
|
|
|
|
readonly float _fringeWidth; |
|
|
|
readonly float _devicePixelRatio; |
|
|
|
readonly MeshPool _meshPool; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public PictureFlusher(RenderTexture renderTexture, float devicePixelRatio, MeshPool meshPool) { |
|
|
|
D.assert(renderTexture); |
|
|
|
D.assert(devicePixelRatio > 0); |
|
|
|
|
|
|
this._devicePixelRatio = devicePixelRatio; |
|
|
|
this._meshPool = meshPool; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
RenderLayer firstLayer; |
|
|
|
if (this._layers.Count == 0) { |
|
|
|
var width = this._renderTexture.width; |
|
|
|
|
|
|
height = height, |
|
|
|
layerBounds = bounds, |
|
|
|
}; |
|
|
|
} else { |
|
|
|
} |
|
|
|
else { |
|
|
|
D.assert(this._layers.Count > 0); |
|
|
|
firstLayer = this._layers[0]; |
|
|
|
firstLayer = new RenderLayer { |
|
|
|
|
|
|
var blurMesh = ImageMeshGenerator.imageMesh(null, Rect.one, bounds); |
|
|
|
layer.draws.Add(CanvasShader.texRT(layer, paint, blurMesh, blurLayer)); |
|
|
|
} |
|
|
|
} else if (paint.backdrop is _MatrixImageFilter) { |
|
|
|
} |
|
|
|
else if (paint.backdrop is _MatrixImageFilter) { |
|
|
|
|
|
|
|
|
|
|
|
var points = new[] {bounds.topLeft, bounds.bottomLeft, bounds.bottomRight, bounds.topRight}; |
|
|
|
state.matrix.mapPoints(points); |
|
|
|
|
|
|
|
|
|
|
var matrix = Matrix3.makeTrans(-bounds.left, -bounds.top); |
|
|
|
matrix.postConcat(filter.transform); |
|
|
|
matrix.postTranslate(bounds.left, bounds.top); |
|
|
|
|
|
|
|
|
|
|
|
var mesh = ImageMeshGenerator.imageMesh( |
|
|
|
matrix, |
|
|
|
points[0], points[1], points[2], points[3], |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void _restore() { |
|
|
|
var layer = this._currentLayer; |
|
|
|
D.assert(layer.states.Count > 0); |
|
|
|
|
|
|
var renderDraw = CanvasShader.texRT(currentLayer, layer.layerPaint, mesh, layer); |
|
|
|
currentLayer.draws.Add(renderDraw); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void _translate(float dx, float dy) { |
|
|
|
var state = this._currentLayer.currentState; |
|
|
|
var matrix = Matrix3.makeTrans(dx, dy); |
|
|
|
|
|
|
var matrix = Matrix3.makeRotate(radians); |
|
|
|
matrix.postConcat(state.matrix); |
|
|
|
state.matrix = matrix; |
|
|
|
} else { |
|
|
|
} |
|
|
|
else { |
|
|
|
var matrix = Matrix3.makeRotate(radians, offset.dx, offset.dy); |
|
|
|
matrix.postConcat(state.matrix); |
|
|
|
state.matrix = matrix; |
|
|
|
|
|
|
void _skew(float sx, float sy) { |
|
|
|
var state = this._currentLayer.currentState; |
|
|
|
var matrix = Matrix3.makeSkew( sx, sy); |
|
|
|
var matrix = Matrix3.makeSkew(sx, sy); |
|
|
|
matrix.postConcat(state.matrix); |
|
|
|
state.matrix = matrix; |
|
|
|
} |
|
|
|
|
|
|
var state = layer.currentState; |
|
|
|
layer.clipStack.clipPath(path, state.matrix, state.scale * this._devicePixelRatio); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void _tryAddScissor(RenderLayer layer, Rect scissor) { |
|
|
|
if (scissor == this._lastScissor) { |
|
|
|
return; |
|
|
|
|
|
|
}); |
|
|
|
this._lastScissor = scissor; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool _applyClip(Rect queryBounds) { |
|
|
|
if (queryBounds == null || queryBounds.isEmpty) { |
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
|
if (scissor == layerBounds) { |
|
|
|
this._tryAddScissor(layer, null); |
|
|
|
} else { |
|
|
|
} |
|
|
|
else { |
|
|
|
var deviceScissor = Rect.fromLTRB( |
|
|
|
scissor.left - layerBounds.left, layerBounds.bottom - scissor.bottom, |
|
|
|
scissor.right - layerBounds.left, layerBounds.bottom - scissor.top |
|
|
|
|
|
|
if (deviceScissor.isEmpty) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this._tryAddScissor(layer, deviceScissor); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
layer.ignoreClip = true; |
|
|
|
} else { |
|
|
|
} |
|
|
|
else { |
|
|
|
layer.ignoreClip = false; |
|
|
|
|
|
|
|
// need to inflate a bit to make sure all area is cleared.
|
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void _setLastClipGenId(uint clipGenId, Rect clipBounds) { |
|
|
|
var layer = this._currentLayer; |
|
|
|
layer.lastClipGenId = clipGenId; |
|
|
|
|
|
|
return layer.lastClipGenId != clipGenId || layer.lastClipBounds != clipBounds; |
|
|
|
} |
|
|
|
|
|
|
|
RenderLayer _createMaskLayer(RenderLayer parentLayer, Rect maskBounds, Action<Paint> drawCallback, Paint paint) { |
|
|
|
RenderLayer _createMaskLayer(RenderLayer parentLayer, Rect maskBounds, Action<Paint> drawCallback, |
|
|
|
Paint paint) { |
|
|
|
var textureWidth = Mathf.CeilToInt(maskBounds.width * this._devicePixelRatio); |
|
|
|
if (textureWidth < 1) { |
|
|
|
textureWidth = 1; |
|
|
|
|
|
|
if (textureWidth < 1) { |
|
|
|
textureWidth = 1; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var textureHeight = Mathf.CeilToInt((float) maskLayer.height / scaleFactorY); |
|
|
|
if (textureHeight < 1) { |
|
|
|
textureHeight = 1; |
|
|
|
|
|
|
void _drawPath(Path path, Paint paint) { |
|
|
|
D.assert(path != null); |
|
|
|
D.assert(paint != null); |
|
|
|
|
|
|
|
|
|
|
|
if (paint.style == PaintingStyle.fill) { |
|
|
|
var state = this._currentLayer.currentState; |
|
|
|
var cache = path.flatten(state.scale * this._devicePixelRatio); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Action<Paint> drawMesh = p => { |
|
|
|
if (!this._applyClip(mesh.bounds)) { |
|
|
|
return; |
|
|
|
|
|
|
this._drawWithMaskFilter(mesh.bounds, drawMesh, paint, paint.maskFilter); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
drawMesh(paint); |
|
|
|
} |
|
|
|
else { |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
var layer = this._currentLayer; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
drawMesh(paint); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
if (src == null) { |
|
|
|
src = Rect.one; |
|
|
|
} else { |
|
|
|
} |
|
|
|
else { |
|
|
|
src = src.scale(1f / image.width, 1f / image.height); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
var scaleY = 1f / image.height; |
|
|
|
if (src == null) { |
|
|
|
src = Rect.one; |
|
|
|
} else { |
|
|
|
src = src.scale(scaleX, scaleY); |
|
|
|
} |
|
|
|
else { |
|
|
|
src = src.scale(scaleX, scaleY); |
|
|
|
} |
|
|
|
|
|
|
|
center = center.scale(scaleX, scaleY); |
|
|
|
|
|
|
|
|
|
|
var mesh = ImageMeshGenerator.imageNineMesh(state.matrix, src, center, image.width, image.height, dst); |
|
|
|
var mesh = ImageMeshGenerator.imageNineMesh(state.matrix, src, center, image.width, image.height, dst); |
|
|
|
if (!this._applyClip(mesh.bounds)) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
this._saveLayer(cmd.rect, cmd.paint); |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
case DrawRestore _: { |
|
|
|
saveCount--; |
|
|
|
if (saveCount < 0) { |
|
|
|
|
|
|
this._restore(); |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case DrawResetMatrix _: |
|
|
|
this._resetMatrix(); |
|
|
|
break; |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
default: |
|
|
|
throw new Exception("unknown drawCmd: " + drawCmd); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
var state = this._currentLayer.currentState; |
|
|
|
var scale = state.scale * this._devicePixelRatio; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// request font texture so text mesh could be generated correctly
|
|
|
|
var style = textBlob.style; |
|
|
|
var font = FontManager.instance.getOrCreate(style.fontFamily, style.fontWeight, style.fontStyle).font; |
|
|
|
|
|
|
Texture tex = null; |
|
|
|
bool alpha = !EmojiUtils.isSurrogatePairStart(subText[0]); |
|
|
|
if(alpha) { |
|
|
|
bool alpha = !char.IsHighSurrogate(subText[0]); |
|
|
|
if (alpha) { |
|
|
|
font.RequestCharactersInTextureSafe(subText, fontSizeToLoad, style.UnityFontStyle); |
|
|
|
tex = font.material.mainTexture; |
|
|
|
} |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
var layer = this._currentLayer; |
|
|
|
|
|
|
|
if(alpha) layer.draws.Add(CanvasShader.texAlpha(layer, p, mesh, tex)); |
|
|
|
|
|
|
|
if (alpha) { |
|
|
|
layer.draws.Add(CanvasShader.texAlpha(layer, p, mesh, tex)); |
|
|
|
} |
|
|
|
Paint paintWithWhite= new Paint(p); |
|
|
|
Paint paintWithWhite = new Paint(p); |
|
|
|
layer.draws.Add(CanvasShader.tex(layer, paintWithWhite, mesh.resovleMesh(), EmojiUtils.image)); |
|
|
|
layer.draws.Add(CanvasShader.tex(layer, paintWithWhite, mesh.resolveMesh(), EmojiUtils.image)); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
this._lastRtID = rtID; |
|
|
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
} |
|
|
|
else { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void _drawLayer(RenderLayer layer, CommandBuffer cmdBuf) { |
|
|
|
bool toClear = true; |
|
|
|
|
|
|
|
|
|
|
break; |
|
|
|
case CmdDraw cmd: |
|
|
|
this._setRenderTarget(cmdBuf, layer.rtID, ref toClear); |
|
|
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
} |
|
|
|
else { |
|
|
|
cmdBuf.SetGlobalTexture(CmdDraw.texId, cmd.layer.rtID); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
MeshMesh mesh = cmd.mesh; |
|
|
|
if (cmd.textMesh != null) { |
|
|
|
mesh = cmd.textMesh.resovleMesh(); |
|
|
|
mesh = cmd.textMesh.resolveMesh(); |
|
|
|
} |
|
|
|
|
|
|
|
if (mesh == null) { |
|
|
|
|
|
|
|
|
|
|
if (mesh.matrix == null) { |
|
|
|
cmd.properties.SetFloatArray(CmdDraw.matId, CmdDraw.idMat3.fMat); |
|
|
|
} else { |
|
|
|
} |
|
|
|
else { |
|
|
|
cmd.properties.SetFloatArray(CmdDraw.matId, mesh.matrix.fMat); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
} |
|
|
|
else { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
D.assert(!toClear); |
|
|
|
|
|
|
|
foreach (var subLayer in layer.layers) { |
|
|
|
|
|
|
cmd.meshObj = null; |
|
|
|
cmd.meshObjCreated = false; |
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
this.layerBounds.width, |
|
|
|
this.layerBounds.height); |
|
|
|
} |
|
|
|
|
|
|
|
return this._viewport.Value; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
internal class State { |
|
|
|
static readonly Matrix3 _id = Matrix3.I(); |
|
|
|
|
|
|
|
|
|
|
|
Matrix3 _matrix; |
|
|
|
float? _scale; |
|
|
|
Matrix3 _invMatrix; |
|
|
|
|
|
|
this._scale = scale; |
|
|
|
this._invMatrix = invMatrix; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public Matrix3 matrix { |
|
|
|
get { return this._matrix; } |
|
|
|
set { |
|
|
|
|
|
|
if (this._scale == null) { |
|
|
|
this._scale = XformUtils.getScale(this._matrix); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public Matrix3 invMatrix { |
|
|
|
get { |
|
|
|
if (this._invMatrix == null) { |
|
|
|
|
|
|
|
|
|
|
return this._invMatrix; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
internal class CmdDraw { |
|
|
|
public MeshMesh mesh; |
|
|
|
public TextBlobMesh textMesh; |
|
|
|
|
|
|
|
|
|
|
public class CommandBufferCanvas : RecorderCanvas { |
|
|
|
readonly PictureFlusher _flusher; |
|
|
|
|
|
|
|
public CommandBufferCanvas(RenderTexture renderTexture, float devicePixelRatio, MeshPool meshPool) |
|
|
|
|
|
|
|
public CommandBufferCanvas(RenderTexture renderTexture, float devicePixelRatio, MeshPool meshPool) |
|
|
|
: base(new PictureRecorder()) { |
|
|
|
this._flusher = new PictureFlusher(renderTexture, devicePixelRatio, meshPool); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
public override void flush() { |
|
|
|
var picture = this._recorder.endRecording(); |
|
|
|
this._recorder.reset(); |
|
|
|
this._recorder.reset(); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
static class XformUtils { |
|
|
|
public static float getAverageScale(Matrix3 matrix) { |
|
|
|
return (getScaleX(matrix) + getScaleY(matrix)) * 0.5f; |
|
|
|
|
|
|
if (matrix.getSkewY() == 0) { |
|
|
|
return matrix.getScaleX(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return Mathf.Sqrt(x * x + y * y); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
// geometric mean of len0 and len1.
|
|
|
|
return Mathf.Sqrt(scaleX * scaleY); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public static float mapRadius(Matrix3 matrix, float radius) { |
|
|
|
return getScale(matrix) * radius; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
return new MeshMesh(matrix, vertices, _imageTriangles, uv); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public static MeshMesh imageMesh(Matrix3 matrix, Rect src, Rect dst) { |
|
|
|
var vertices = new List<Vector3>(4); |
|
|
|
var uv = new List<Vector2>(4); |
|
|
|
|
|
|
return new MeshMesh(matrix, vertices, _imageTriangles, uv); |
|
|
|
} |
|
|
|
|
|
|
|
public static MeshMesh imageNineMesh(Matrix3 matrix, Rect src, Rect center, int srcWidth, int srcHeight, Rect dst) { |
|
|
|
public static MeshMesh imageNineMesh(Matrix3 matrix, Rect src, Rect center, int srcWidth, int srcHeight, |
|
|
|
Rect dst) { |
|
|
|
float x0 = dst.left; |
|
|
|
float x3 = dst.right; |
|
|
|
float x1 = x0 + ((center.left - src.left) * srcWidth); |
|
|
|
|
|
|
uv.Add(new Vector2(tx2, ty3)); |
|
|
|
vertices.Add(new Vector2(x3, y3)); |
|
|
|
uv.Add(new Vector2(tx3, ty3)); |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |