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)); }
/// <summary>Build both meshes</summary> public static void createMesh(this iPolylinePath path, iTriangleMesh result, eBuildFilledMesh filledMesh, float pixel, sStrokeInfo strokeInfo) { unsafe { sStrokeInfo *si = &strokeInfo; path.createMesh(result, filledMesh, pixel, ( IntPtr)si); } }
public Options(ref Matrix3x2 transform, float precision, float pixel, eBuildFilledMesh fill, sStrokeInfo?stroke, bool strokeSeparate) { this.transform = transform; this.precision = precision; this.pixel = pixel; this.fill = fill; this.stroke = stroke ?? default; separateStrokeMesh = strokeSeparate; }
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)); }
public static eBuildFilledMesh fillFlagsFromColor(eBrushType brushType, bool hasStroke) { eBuildFilledMesh vaaBit = hasStroke ? eBuildFilledMesh.None : eBuildFilledMesh.VAA; switch (brushType) { case eBrushType.Null: return(eBuildFilledMesh.None); case eBrushType.Transparent: return(eBuildFilledMesh.FilledMesh | eBuildFilledMesh.BrushHasTransparency | vaaBit); case eBrushType.Opaque: return(eBuildFilledMesh.FilledMesh | vaaBit); } throw new ArgumentException(); }
static sDrawCallInfo filledCallInfo(eBuildFilledMesh fillOptions) { switch (fillOptions & (eBuildFilledMesh.VAA | eBuildFilledMesh.BrushHasTransparency)) { case eBuildFilledMesh.None: // Opaque brush without VAA return(new sDrawCallInfo(eRenderPassFlags.Opaque, 1)); case eBuildFilledMesh.VAA: // VAA emits both opaque and transparent triangles in that mode. It even partitions vertices in the buffer for better GPU cache utilization. return(new sDrawCallInfo(eRenderPassFlags.Opaque | eRenderPassFlags.Transparent, 2)); case eBuildFilledMesh.BrushHasTransparency: case eBuildFilledMesh.BrushHasTransparency | eBuildFilledMesh.VAA: // When the brush has transparency, VAA or not, the complete mesh will go to the transparent index buffer return(new sDrawCallInfo(eRenderPassFlags.Transparent, 1)); } throw new ApplicationException(); }
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)); }
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>Build just the filled mesh</summary> public static void createMesh(this iPolylinePath path, iTriangleMesh result, eBuildFilledMesh filledMesh, float pixel) { Debug.Assert(filledMesh != eBuildFilledMesh.None); path.createMesh(result, filledMesh, pixel, IntPtr.Zero); }
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)); }