protected void fillGeometry(iPathGeometry path, SolidColorData color, bool vaa, int instance = 0) { getCurrentTransform(out Matrix3x2 tform, out float pixel); if (!path.testApproximateBounds(ref tform) || color.brushType == eBrushType.Null) { return; } eBuildFilledMesh fillOptions = DrawUtilsPrivate.fillFlagsFromColor(color.brushType, false); if (fillOptions == eBuildFilledMesh.None) { return; // Null brush } if (!vaa) { fillOptions &= ~eBuildFilledMesh.VAA; } sPendingDrawCall pdc = tesselatorThread.fill(path, ref tform, pixel, fillOptions, instance); flushIfNeeded(pdc.drawInfo.drawCallsCount); Order o = order(); drawMeshes.meshes.add(ref pdc); calls.add(sDrawCall.solidColorFill(o, ref tform, color.paletteIndex)); }
sDrawCommand(iPathGeometry geometry, eMesh command, sStrokeStyle ss, float w) { this.geometry = geometry; this.command = command; strokeStyle = ss; width = w; }
void iDrawContext.fillAndStroke(iGeometry geometry, iBrush fill, iBrush stroke, float strokeWidth, iStrokeStyle strokeStyle) { sStrokeStyle ss = strokeStyle?.strokeStyle ?? defaultStrokeStyle(); iPathGeometry path = (iPathGeometry)geometry; fillAndStrokeGeometry(path, fill.data(), stroke.data(), strokeWidth, ref ss); }
/// <summary>True if the transformed approximate bounds of the geometry + stroke intersects with a specified clipping rectangle</summary> public static bool testApproximateBounds(this iPathGeometry pathGeometry, ref Matrix3x2 transform, ref Rect clipRect, float strokeWidth = 0) { unsafe { fixed(Rect *pointer = &clipRect) return(pathGeometry.ioTestApproximateBounds(ref transform, strokeWidth, (IntPtr)pointer)); } }
/// <summary>Tessellate splines and clip the result to viewport, using specified clipping rectangle</summary> public static eClipResult buildClippedPolylines(this iPathGeometry path, iPolylinePath poly, Rect clipRect, Matrix3x2 transform, float precision, float strokeWidth = 0) { unsafe { Rect *clip = &clipRect; path.buildPolylines(precision, strokeWidth, ref transform, (IntPtr)clip, poly, out var res); return(res); } }
sPendingDrawCall iTesselator.fillAndStroke(iPathGeometry path, ref Matrix3x2 tform, float pixel, eBuildFilledMesh fillOptions, sStrokeInfo stroke, int instance) { var job = postJob(path, instance, ref tform, fillOptions, pixel, stroke); if (!fillOptions.HasFlag(eBuildFilledMesh.BrushHasTransparency)) { return(new sPendingDrawCall(job, eRenderPassFlags.Opaque | eRenderPassFlags.Transparent, 2)); } return(new sPendingDrawCall(job, eRenderPassFlags.Transparent, 1)); }
protected void fillAndStrokeGeometry(iPathGeometry path, SolidColorData?fill, SolidColorData stroke, float width, ref sStrokeStyle strokeStyle, int instance = 0) { if ((fill?.brushType ?? eBrushType.Null) == eBrushType.Null) { // No fill, just stroke if (stroke.brushType != eBrushType.Null) { strokeGeometry(path, stroke, null, width, ref strokeStyle, instance); } return; } if (stroke.brushType == eBrushType.Null) { // No stroke, just fill fillGeometry(path, fill.Value, true, instance); return; } getCurrentTransform(out Matrix3x2 tform, out float pixel); if (!path.testApproximateBounds(ref tform, width)) { return; } eBuildFilledMesh fillOptions = DrawUtilsPrivate.fillFlagsFromColor(fill.Value.brushType, true); StrokeRenderParams srp = StrokeRenderParams.strokedPath(stroke.paletteIndex, width, pixel); Order o; if (fill.Value == stroke) { // Colors are the same, combine the draw calls sPendingDrawCall pdc = tesselatorThread.fillAndStroke(path, ref tform, pixel, fillOptions, new sStrokeInfo(strokeStyle, srp.meshWidth), instance); flushIfNeeded(pdc.drawInfo.drawCallsCount); o = order(); drawMeshes.meshes.add(ref pdc); calls.add(sDrawCall.solidColorStroke(o, ref tform, ref srp)); return; } var pendingCalls = tesselatorThread.fillAndStrokeSeparate(path, ref tform, pixel, fillOptions, new sStrokeInfo(strokeStyle, srp.meshWidth), instance); byte dcc = pendingCalls.Item1.drawInfo.drawCallsCount; dcc += pendingCalls.Item2.drawInfo.drawCallsCount; flushIfNeeded(dcc); o = order(); drawMeshes.meshes.add(ref pendingCalls.Item1); calls.add(sDrawCall.solidColorFill(o, ref tform, fill.Value.paletteIndex)); o = order(); drawMeshes.meshes.add(ref pendingCalls.Item2); calls.add(sDrawCall.solidColorStroke(o, ref tform, ref srp)); }
iTessellatedMeshes postJob(iPathGeometry path, int instance, ref Matrix3x2 tform, eBuildFilledMesh filled, float pixel, sStrokeInfo?stroke) { if (null == path) { throw new ArgumentNullException(); } // We use MD4 to detect changes in re-tessellated polylines. To make that more efficient for small changes of scaling, rounding the precision and pixel values. float precision = pixel.floorBitwise(); Options options = new Options(ref tform, precision, pixel, filled, stroke, false); Meshes meshes; Table table; if (0 == instance) { table = zeroInstances; } else if (1 == instance) { table = oneInstances; } else { var dict = multiTable.GetOrCreateValue(path); if (dict.TryGetValue(instance, out meshes)) { return(updateOldJob(meshes, ref options)); } meshes = new Meshes(path, factory); dict.Add(instance, meshes); return(createNewJob(meshes, ref options)); } if (table.TryGetValue(path, out meshes)) { return(updateOldJob(meshes, ref options)); } meshes = new Meshes(path, factory); table.Add(path, meshes); return(createNewJob(meshes, ref options)); }
protected void strokeGeometry(iPathGeometry path, SolidColorData color, SolidColorData?filledColor, float width, ref sStrokeStyle strokeStyle, int instance = 0) { getCurrentTransform(out Matrix3x2 tform, out float pixel); if (!path.testApproximateBounds(ref tform, width) || color.brushType == eBrushType.Null) { return; } int fillColorIndex = filledColor?.paletteIndex ?? (int)eNamedColor.Transparent; StrokeRenderParams srp = StrokeRenderParams.strokedPath(color.paletteIndex, width, pixel, fillColorIndex); sPendingDrawCall pdc = tesselatorThread.stroke(path, ref tform, pixel, new sStrokeInfo(strokeStyle, srp.meshWidth), instance); flushIfNeeded(pdc.drawInfo.drawCallsCount); Order o = order(); drawMeshes.meshes.add(ref pdc); calls.add(sDrawCall.solidColorStroke(o, ref tform, ref srp)); }
sPendingDrawCall iTesselator.stroke(iPathGeometry path, ref Matrix3x2 tform, float pixel, sStrokeInfo stroke, int instance) { var job = postJob(path, instance, ref tform, eBuildFilledMesh.None, pixel, stroke); return(new sPendingDrawCall(job, eRenderPassFlags.Transparent, 1)); }
sPendingDrawCall iTesselator.fill(iPathGeometry path, ref Matrix3x2 tform, float pixel, eBuildFilledMesh fillOptions, int instance) { var job = postJob(path, instance, ref tform, fillOptions, pixel, null); return(new sPendingDrawCall(job, filledCallInfo(fillOptions))); }
/// <summary>True if the transformed approximate bounds of the geometry + stroke intersects with a default clipping rectangle [ -1, -1, +1, +1 ]</summary> public static bool testApproximateBounds(this iPathGeometry pathGeometry, ref Matrix3x2 transform, float strokeWidth = 0) { return(pathGeometry.ioTestApproximateBounds(ref transform, strokeWidth, IntPtr.Zero)); }
/// <summary>Tessellate splines and clip the result to viewport, using default clipping rectangle</summary> public static eClipResult buildClippedPolylines(this iPathGeometry path, iPolylinePath poly, Matrix3x2 transform, float precision, float strokeWidth = 0) { path.buildPolylines(precision, strokeWidth, ref transform, IntPtr.Zero, poly, out var res); return(res); }
public static sDrawCommand fill(iPathGeometry geometry) { return(new sDrawCommand(geometry, eMesh.Filled, default, default));
public Meshes(iPathGeometry path, iVrmacDraw factory) { this.path = path; frontBuffer = factory.createTriangleMesh(); backBuffer = factory.createTriangleMesh(); }