void _drawImageRect(Image image, uiRect?src, uiRect dst, uiPaint paint) { D.assert(image != null && image.valid); if (image == null || !image.valid) { return; } if (src == null) { src = uiRectHelper.one; } else { src = uiRectHelper.scale(src.Value, 1f / image.width, 1f / image.height); } var layer = this._currentLayer; var state = layer.currentState; var mesh = ImageMeshGenerator.imageMesh(state.matrix, src.Value, dst); if (!this._applyClip(mesh.bounds)) { ObjectPool <uiMeshMesh> .release(mesh); return; } layer.draws.Add(CanvasShader.tex(layer, paint, mesh, image)); }
void _drawPathDrawMeshCallback(uiPaint p, uiMeshMesh fillMesh, uiMeshMesh strokeMesh, bool convex, float alpha, float strokeMult, Texture tex, uiRect textBlobBounds, TextBlobMesh textMesh, bool notEmoji) { if (!this._applyClip(fillMesh.bounds)) { ObjectPool <uiMeshMesh> .release(fillMesh); ObjectPool <uiMeshMesh> .release(strokeMesh); return; } var layer = this._currentLayer; if (convex) { layer.draws.Add(CanvasShader.convexFill(layer, p, fillMesh)); } else { layer.draws.Add(CanvasShader.fill0(layer, fillMesh)); layer.draws.Add(CanvasShader.fill1(layer, p, fillMesh.boundsMesh)); } if (strokeMesh != null) { layer.draws.Add(CanvasShader.strokeAlpha(layer, p, alpha, strokeMult, strokeMesh)); layer.draws.Add(CanvasShader.stroke1(layer, strokeMesh.duplicate())); } }
void _drawImageNine(Image image, uiRect?src, uiRect center, uiRect dst, uiPaint paint) { D.assert(image != null); var scaleX = 1f / image.width; var scaleY = 1f / image.height; if (src == null) { src = uiRectHelper.one; } else { src = uiRectHelper.scale(src.Value, scaleX, scaleY); } center = uiRectHelper.scale(center, scaleX, scaleY); var layer = this._currentLayer; var state = layer.currentState; var mesh = ImageMeshGenerator.imageNineMesh(state.matrix, src.Value, center, image.width, image.height, dst); if (!this._applyClip(mesh.bounds)) { ObjectPool <uiMeshMesh> .release(mesh); return; } layer.draws.Add(CanvasShader.tex(layer, paint, mesh, image)); }
void _drawWithMaskFilter(uiRect meshBounds, uiPaint paint, uiMaskFilter maskFilter, uiMeshMesh fillMesh, uiMeshMesh strokeMesh, bool convex, float alpha, float strokeMult, Texture tex, uiRect texBound, TextBlobMesh textMesh, bool notEmoji, _drawPathDrawMeshCallbackDelegate drawCallback) { var layer = this._currentLayer; var clipBounds = layer.layerBounds; uiRect?stackBounds; bool iior; layer.clipStack.getBounds(out stackBounds, out iior); if (stackBounds != null) { clipBounds = uiRectHelper.intersect(clipBounds, stackBounds.Value); } if (clipBounds.isEmpty) { this._drawPathDrawMeshQuit(fillMesh, strokeMesh, textMesh); return; } var state = layer.currentState; float sigma = state.scale * maskFilter.sigma; if (sigma <= 0) { this._drawPathDrawMeshQuit(fillMesh, strokeMesh, textMesh); return; } float sigma3 = 3 * sigma; var maskBounds = uiRectHelper.inflate(meshBounds, sigma3); maskBounds = uiRectHelper.intersect(maskBounds, uiRectHelper.inflate(clipBounds, sigma3)); if (maskBounds.isEmpty) { this._drawPathDrawMeshQuit(fillMesh, strokeMesh, textMesh); return; } var maskLayer = this._createMaskLayer(layer, maskBounds, drawCallback, paint, convex, alpha, strokeMult, tex, texBound, textMesh, fillMesh, strokeMesh, notEmoji); var blurLayer = this._createBlurLayer(maskLayer, sigma, sigma, layer); var blurMesh = ImageMeshGenerator.imageMesh(null, uiRectHelper.one, maskBounds); if (!this._applyClip(blurMesh.bounds)) { ObjectPool <uiMeshMesh> .release(blurMesh); return; } layer.draws.Add(CanvasShader.texRT(layer, paint, blurMesh, blurLayer)); }
void _drawTextDrawMeshCallback(uiPaint p, uiMeshMesh fillMesh, uiMeshMesh strokeMesh, bool convex, float alpha, float strokeMult, Texture tex, uiRect textBlobBounds, TextBlobMesh textMesh, bool notEmoji) { if (!this._applyClip(textBlobBounds)) { ObjectPool <TextBlobMesh> .release(textMesh); return; } var layer = this._currentLayer; if (notEmoji) { layer.draws.Add(CanvasShader.texAlpha(layer, p, textMesh, tex)); } else { uiPaint paintWithWhite = new uiPaint(p); paintWithWhite.color = uiColor.white; if (EmojiUtils.image == null) { ObjectPool <TextBlobMesh> .release(textMesh); return; } var raw_mesh = textMesh.resolveMesh(); var meshmesh = raw_mesh.duplicate(); ObjectPool <TextBlobMesh> .release(textMesh); layer.draws.Add(CanvasShader.tex(layer, paintWithWhite, meshmesh, EmojiUtils.image)); } }
public static uiPaint shapeOnly(uiPaint paint) { return(new uiPaint( style: paint.style, strokeWidth: paint.strokeWidth, strokeCap: paint.strokeCap, strokeJoin: paint.strokeJoin, strokeMiterLimit: paint.strokeMiterLimit )); }
void _drawImage(Image image, uiOffset offset, uiPaint paint) { D.assert(image != null); this._drawImageRect(image, null, uiRectHelper.fromLTWH( offset.dx, offset.dy, image.width / this._devicePixelRatio, image.height / this._devicePixelRatio), paint); }
void _drawPathDrawMeshCallback2(uiPaint p, uiMeshMesh mesh, bool convex, float alpha, Texture tex, uiRect textBlobBounds, TextBlobMesh textMesh, bool notEmoji) { if (!this._applyClip(mesh.bounds)) { ObjectPool <uiMeshMesh> .release(mesh); return; } var layer = this._currentLayer; layer.draws.Add(CanvasShader.stroke0(layer, p, alpha, mesh)); layer.draws.Add(CanvasShader.stroke1(layer, mesh.duplicate())); }
RenderLayer _createMaskLayer(RenderLayer parentLayer, uiRect maskBounds, _drawPathDrawMeshCallbackDelegate drawCallback, uiPaint paint, bool convex, float alpha, float strokeMult, Texture tex, uiRect texBound, TextBlobMesh textMesh, uiMeshMesh fillMesh, uiMeshMesh strokeMesh, bool notEmoji) { var textureWidth = Mathf.CeilToInt(maskBounds.width * this._devicePixelRatio); if (textureWidth < 1) { textureWidth = 1; } var textureHeight = Mathf.CeilToInt(maskBounds.height * this._devicePixelRatio); if (textureHeight < 1) { textureHeight = 1; } var maskLayer = RenderLayer.create( rtID: Shader.PropertyToID(this._getNewRenderTextureKey()), width: textureWidth, height: textureHeight, layerBounds: maskBounds, filterMode: FilterMode.Bilinear, noMSAA: true ); parentLayer.addLayer(maskLayer); this._layers.Add(maskLayer); this._currentLayer = maskLayer; var parentState = parentLayer.states[parentLayer.states.Count - 1]; var maskState = maskLayer.states[maskLayer.states.Count - 1]; maskState.matrix = parentState.matrix; drawCallback.Invoke(uiPaint.shapeOnly(paint), fillMesh, strokeMesh, convex, alpha, strokeMult, tex, texBound, textMesh, notEmoji); var removed = this._layers.removeLast(); D.assert(removed == maskLayer); this._currentLayer = this._layers[this._layers.Count - 1]; return(maskLayer); }
public uiPaint(uiPaint paint) { this.color = paint.color; this.blendMode = paint.blendMode; this.style = paint.style; this.strokeWidth = paint.strokeWidth; this.strokeCap = paint.strokeCap; this.strokeJoin = paint.strokeJoin; this.strokeMiterLimit = paint.strokeMiterLimit; this.filterMode = paint.filterMode; this.colorFilter = paint.colorFilter; this.maskFilter = paint.maskFilter; this.backdrop = paint.backdrop; this.shader = paint.shader; this.invertColors = paint.invertColors; }
public static PictureFlusher.CmdDraw texRT(PictureFlusher.RenderLayer layer, uiPaint paint, uiMeshMesh mesh, PictureFlusher.RenderLayer renderLayer) { var mat = _texMat.getMaterial(paint.blendMode, layer.ignoreClip); _getShaderPassAndProps(layer, paint, mesh.matrix, 1.0f, 0.0f, out var pass, out var props); props.SetInt(_texModeId, 1); // pre alpha return(PictureFlusher.CmdDraw.create( mesh: mesh, pass: pass, material: mat, properties: props, layerId: renderLayer.rtID )); }
void _drawImage(Image image, uiOffset offset, uiPaint paint) { D.assert(image != null && image.valid); if (image == null || !image.valid) { return; } this._drawImageRect(image, null, uiRectHelper.fromLTWH( offset.dx, offset.dy, image.width / this._devicePixelRatio, image.height / this._devicePixelRatio), paint); }
public static PictureFlusher.CmdDraw fill1(PictureFlusher.RenderLayer layer, uiPaint paint, uiMeshMesh mesh) { var mat = _fill1Mat.getMaterial(paint.blendMode); _getShaderPassAndProps(layer, paint, mesh.matrix, 1.0f, 0.0f, out var pass, out var props); var ret = PictureFlusher.CmdDraw.create( mesh: mesh.boundsMesh, pass: pass, material: mat, properties: props ); ObjectPool <uiMeshMesh> .release(mesh); return(ret); }
public static PictureFlusher.CmdDraw tex(PictureFlusher.RenderLayer layer, uiPaint paint, uiMeshMesh mesh, Image image) { var mat = _texMat.getMaterial(paint.blendMode, layer.ignoreClip); _getShaderPassAndProps(layer, paint, mesh.matrix, 1.0f, 0.0f, out var pass, out var props); image.texture.filterMode = paint.filterMode; props.SetTexture(_texId, image.texture); props.SetInt(_texModeId, image.texture is RenderTexture || image.isDynamic ? 1 : 0); // pre alpha if RT else post alpha return(PictureFlusher.CmdDraw.create( mesh: mesh, pass: pass, material: mat, properties: props, image: image // keep a reference to avoid GC. )); }
void _drawPathDrawMeshCallback(uiPaint p, uiMeshMesh mesh, bool convex, float alpha, Texture tex, uiRect textBlobBounds, TextBlobMesh textMesh, bool notEmoji) { if (!this._applyClip(mesh.bounds)) { ObjectPool <uiMeshMesh> .release(mesh); return; } var layer = this._currentLayer; if (convex) { layer.draws.Add(CanvasShader.convexFill(layer, p, mesh)); } else { layer.draws.Add(CanvasShader.fill0(layer, mesh)); layer.draws.Add(CanvasShader.fill1(layer, p, mesh.boundsMesh)); } }
void _drawTextBlob(TextBlob?textBlob, uiOffset offset, uiPaint paint) { D.assert(textBlob != null); var state = this._currentLayer.currentState; var scale = state.scale * this._devicePixelRatio; var matrix = new uiMatrix3(state.matrix.Value); matrix.preTranslate(offset.dx, offset.dy); var mesh = TextBlobMesh.create(textBlob.Value, scale, matrix); var textBlobBounds = matrix.mapRect(uiRectHelper.fromRect(textBlob.Value.boundsInText)); // request font texture so text mesh could be generated correctly var style = textBlob.Value.style; var font = FontManager.instance.getOrCreate(style.fontFamily, style.fontWeight, style.fontStyle).font; var fontSizeToLoad = Mathf.CeilToInt(style.UnityFontSize * scale); var subText = textBlob.Value.text.Substring(textBlob.Value.textOffset, textBlob.Value.textSize); Texture tex = null; bool notEmoji = !char.IsHighSurrogate(subText[0]) && !EmojiUtils.isSingleCharEmoji(subText[0]); if (notEmoji) { font.RequestCharactersInTextureSafe(subText, fontSizeToLoad, style.UnityFontStyle); tex = font.material.mainTexture; } if (paint.maskFilter != null && paint.maskFilter.Value.sigma != 0) { this._drawWithMaskFilter(textBlobBounds, paint, paint.maskFilter.Value, null, null, false, 0, 0, tex, textBlobBounds, mesh, notEmoji, this.___drawTextDrawMeshCallback); return; } this._drawTextDrawMeshCallback(paint, null, null, false, 0, 0, tex, textBlobBounds, mesh, notEmoji); }
public static PictureFlusher.CmdDraw texAlpha(PictureFlusher.RenderLayer layer, uiPaint paint, uiMeshMesh mesh, TextBlobMesh textMesh, Texture tex) { var mat = _texMat.getMaterial(paint.blendMode, layer.ignoreClip); var meshMatrix = mesh != null ? mesh.matrix : textMesh.matrix; _getShaderPassAndProps(layer, paint, meshMatrix, 1.0f, 0.0f, out var pass, out var props); tex.filterMode = paint.filterMode; props.SetTexture(_texId, tex); props.SetInt(_texModeId, 2); // alpha only return(PictureFlusher.CmdDraw.create( mesh: mesh, textMesh: textMesh, pass: pass, material: mat, properties: props )); }
public static PictureFlusher.CmdDraw texAlpha(PictureFlusher.RenderLayer layer, uiPaint paint, TextBlobMesh textMesh, Texture tex) { return(texAlpha(layer, paint, null, textMesh, tex)); }
public static PictureFlusher.CmdDraw strokeAlpha(PictureFlusher.RenderLayer layer, uiPaint paint, float alpha, float strokeMult, uiMeshMesh mesh) { var mat = _strokeAlphaMat.getMaterial(paint.blendMode, layer.ignoreClip); _getShaderPassAndProps(layer, paint, mesh.matrix, alpha, strokeMult, out var pass, out var props); return(PictureFlusher.CmdDraw.create( mesh: mesh, pass: pass, material: mat, properties: props )); }
public static PictureFlusher.CmdDraw convexFill(PictureFlusher.RenderLayer layer, uiPaint paint, uiMeshMesh mesh) { var mat = _convexFillMat.getMaterial(paint.blendMode, layer.ignoreClip); _getShaderPassAndProps(layer, paint, mesh.matrix, 1.0f, 0.0f, out var pass, out var props); return(PictureFlusher.CmdDraw.create( mesh: mesh, pass: pass, material: mat, properties: props )); }
static void _getShaderPassAndProps( PictureFlusher.RenderLayer layer, uiPaint paint, uiMatrix3?meshMatrix, float alpha, float strokeMult, out int pass, out MaterialPropertyBlockWrapper props) { Vector4 viewport = layer.viewport; props = ObjectPool <MaterialPropertyBlockWrapper> .alloc(); props.SetVector(_viewportId, viewport); props.SetFloat(_alphaId, alpha); props.SetFloat(_strokeMultId, strokeMult); switch (paint.shader) { case null: pass = 0; props.SetVector(_colorId, _colorToVector4(paint.color)); return; case _LinearGradient linear: pass = 1; props.SetMatrix(_shaderMatId, linear.getGradientMat( _getShaderMatBase(layer.currentState, meshMatrix)).toMatrix4x4()); props.SetTexture(_shaderTexId, linear.gradientTex.texture); props.SetVector(_leftColorId, _colorToVector4(linear.leftColor)); props.SetVector(_rightColorId, _colorToVector4(linear.rightColor)); props.SetInt(_tileModeId, (int)linear.tileMode); return; case _RadialGradient radial: pass = 2; props.SetMatrix(_shaderMatId, radial.getGradientMat( _getShaderMatBase(layer.currentState, meshMatrix)).toMatrix4x4()); props.SetTexture(_shaderTexId, radial.gradientTex.texture); props.SetVector(_leftColorId, _colorToVector4(radial.leftColor)); props.SetVector(_rightColorId, _colorToVector4(radial.rightColor)); props.SetInt(_tileModeId, (int)radial.tileMode); return; case _SweepGradient sweep: pass = 3; props.SetMatrix(_shaderMatId, sweep.getGradientMat( _getShaderMatBase(layer.currentState, meshMatrix)).toMatrix4x4()); props.SetTexture(_shaderTexId, sweep.gradientTex.texture); props.SetVector(_leftColorId, _colorToVector4(sweep.leftColor)); props.SetVector(_rightColorId, _colorToVector4(sweep.rightColor)); props.SetInt(_tileModeId, (int)sweep.tileMode); props.SetFloat(_biasId, sweep.bias); props.SetFloat(_scaleId, sweep.scale); return; case ImageShader image: pass = 4; props.SetMatrix(_shaderMatId, image.getShaderMat( _getShaderMatBase(layer.currentState, meshMatrix)).toMatrix4x4()); props.SetTexture(_shaderTexId, image.image.texture); props.SetInt(_tileModeId, (int)image.tileMode); return; default: throw new Exception("Unknown paint.shader: " + paint.shader); } }
void _drawRRectShadow(uiPath path, uiPaint paint) { D.assert(path.isNaiveRRect, () => "Cannot draw fast Shadow for non-NaiveRRect shapes"); D.assert(paint.style == PaintingStyle.fill, () => "Cannot draw fast Shadow for stroke lines"); var layer = this._currentLayer; var state = layer.currentState; var cache = path.flatten(state.scale * this._devicePixelRatio); bool convex; cache.computeFillMesh(this._fringeWidth, out convex); var fillMesh = cache.fillMesh; var meshBounds = fillMesh.transform(state.matrix); var clipBounds = layer.layerBounds; uiRect?stackBounds; bool iior; layer.clipStack.getBounds(out stackBounds, out iior); if (stackBounds != null) { clipBounds = uiRectHelper.intersect(clipBounds, stackBounds.Value); } if (clipBounds.isEmpty) { ObjectPool <uiMeshMesh> .release(meshBounds); return; } var maskBounds = meshBounds.bounds; maskBounds = uiRectHelper.intersect(maskBounds, clipBounds); if (maskBounds.isEmpty) { ObjectPool <uiMeshMesh> .release(meshBounds); return; } var blurMesh = ImageMeshGenerator.imageMesh(null, uiRectHelper.one, maskBounds); if (!this._applyClip(blurMesh.bounds)) { ObjectPool <uiMeshMesh> .release(meshBounds); ObjectPool <uiMeshMesh> .release(blurMesh); return; } var bound = path.getBounds(); var sigma = state.scale * paint.maskFilter.Value.sigma / 3f; var vertices = ObjectPool <uiList <Vector3> > .alloc(); vertices.SetCapacity(4); vertices.Add(new Vector2(0, 0)); vertices.Add(new Vector2(1, 0)); vertices.Add(new Vector2(0, 1)); vertices.Add(new Vector2(1, 1)); var _triangles = ObjectPool <uiList <int> > .alloc(); _triangles.SetCapacity(6); _triangles.Add(0); _triangles.Add(1); _triangles.Add(2); _triangles.Add(2); _triangles.Add(1); _triangles.Add(3); ObjectPool <uiMeshMesh> .release(meshBounds); ObjectPool <uiMeshMesh> .release(blurMesh); var mesh = uiMeshMesh.create(state.matrix, vertices, _triangles); var shadowColor = paint.color.withAlpha(128); layer.draws.Add(CanvasShader.fastShadow(layer, mesh, sigma, path.isRect, path.isCircle, path.rRectCorner, new Vector4(bound.left, bound.top, bound.right, bound.bottom), shadowColor)); }
void _drawPath(uiPath path, uiPaint paint) { D.assert(path != null); //draw fast shadow if (paint.maskFilter != null && paint.maskFilter.Value.style == BlurStyle.fast_shadow) { this._drawRRectShadow(path, paint); return; } if (paint.style == PaintingStyle.fill) { var state = this._currentLayer.currentState; var cache = path.flatten(state.scale * this._devicePixelRatio); bool convex; cache.computeFillMesh(this._fringeWidth, out convex); var fillMesh = cache.fillMesh; var strokeMesh = cache.strokeMesh; var fmesh = fillMesh.transform(state.matrix); var smesh = strokeMesh?.transform(state.matrix); float strokeMult = 1.0f; if (paint.maskFilter != null && paint.maskFilter.Value.sigma != 0) { this._drawWithMaskFilter(fmesh.bounds, paint, paint.maskFilter.Value, fmesh, smesh, convex, 0, strokeMult, null, uiRectHelper.zero, null, false, this.___drawPathDrawMeshCallback); return; } this._drawPathDrawMeshCallback(paint, fmesh, smesh, convex, 1.0f, strokeMult, null, uiRectHelper.zero, null, false); } else { var state = this._currentLayer.currentState; float strokeWidth = (paint.strokeWidth * state.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; } strokeWidth = strokeWidth / state.scale * 0.5f; float strokeMult = (this._fringeWidth * 0.5f + strokeWidth * 0.5f) / this._fringeWidth; var cache = path.flatten(state.scale * this._devicePixelRatio); cache.computeStrokeMesh( strokeWidth, this._fringeWidth, paint.strokeCap, paint.strokeJoin, paint.strokeMiterLimit); var strokeMesh = cache.strokeMesh; var mesh = strokeMesh.transform(state.matrix); if (paint.maskFilter != null && paint.maskFilter.Value.sigma != 0) { this._drawWithMaskFilter(mesh.bounds, paint, paint.maskFilter.Value, null, mesh, false, alpha, strokeMult, null, uiRectHelper.zero, null, false, this.___drawPathDrawMeshCallback2); return; } this._drawPathDrawMeshCallback2(paint, null, mesh, false, alpha, strokeMult, null, uiRectHelper.zero, null, false); } }
void _saveLayer(uiRect bounds, uiPaint paint) { D.assert(bounds.width > 0); D.assert(bounds.height > 0); var parentLayer = this._currentLayer; var state = parentLayer.currentState; var textureWidth = Mathf.CeilToInt( bounds.width * state.scale * this._devicePixelRatio); if (textureWidth < 1) { textureWidth = 1; } var textureHeight = Mathf.CeilToInt( bounds.height * state.scale * this._devicePixelRatio); if (textureHeight < 1) { textureHeight = 1; } var layer = RenderLayer.create( rtID: Shader.PropertyToID(this._getNewRenderTextureKey()), width: textureWidth, height: textureHeight, layerBounds: bounds, layerPaint: paint ); parentLayer.addLayer(layer); this._layers.Add(layer); this._currentLayer = layer; if (paint.backdrop != null) { if (paint.backdrop is _uiBlurImageFilter) { var filter = (_uiBlurImageFilter)paint.backdrop; if (!(filter.sigmaX == 0 && filter.sigmaY == 0)) { _cachedPoints[0] = bounds.topLeft; _cachedPoints[1] = bounds.bottomLeft; _cachedPoints[2] = bounds.bottomRight; _cachedPoints[3] = bounds.topRight; state.matrix.Value.mapPoints(ref _cachedPoints); var parentBounds = parentLayer.layerBounds; for (int i = 0; i < 4; i++) { _cachedPoints[i] = new uiOffset( (_cachedPoints[i].dx - parentBounds.left) / parentBounds.width, (_cachedPoints[i].dy - parentBounds.top) / parentBounds.height ); } var mesh = ImageMeshGenerator.imageMesh( null, _cachedPoints[0], _cachedPoints[1], _cachedPoints[2], _cachedPoints[3], bounds); var renderDraw = CanvasShader.texRT(layer, layer.layerPaint.Value, mesh, parentLayer); layer.draws.Add(renderDraw); var blurLayer = this._createBlurLayer(layer, filter.sigmaX, filter.sigmaY, layer); var blurMesh = ImageMeshGenerator.imageMesh(null, uiRectHelper.one, bounds); layer.draws.Add(CanvasShader.texRT(layer, paint, blurMesh, blurLayer)); } } else if (paint.backdrop is _uiMatrixImageFilter) { var filter = (_uiMatrixImageFilter)paint.backdrop; if (!filter.transform.isIdentity()) { layer.filterMode = filter.filterMode; _cachedPoints[0] = bounds.topLeft; _cachedPoints[1] = bounds.bottomLeft; _cachedPoints[2] = bounds.bottomRight; _cachedPoints[3] = bounds.topRight; state.matrix.Value.mapPoints(ref _cachedPoints); var parentBounds = parentLayer.layerBounds; for (int i = 0; i < 4; i++) { _cachedPoints[i] = new uiOffset( (_cachedPoints[i].dx - parentBounds.left) / parentBounds.width, (_cachedPoints[i].dy - parentBounds.top) / parentBounds.height ); } var matrix = uiMatrix3.makeTrans(-bounds.left, -bounds.top); matrix.postConcat(filter.transform); matrix.postTranslate(bounds.left, bounds.top); var mesh = ImageMeshGenerator.imageMesh( matrix, _cachedPoints[0], _cachedPoints[1], _cachedPoints[2], _cachedPoints[3], bounds); var renderDraw = CanvasShader.texRT(layer, layer.layerPaint.Value, mesh, parentLayer); layer.draws.Add(renderDraw); } } } }