private unsafe void ProcessTearDropContourSplitAndColor(Contour contour, EdgeColor *colors) { var corner = _corners[0]; var parts = new EdgeSegment[] { null, null, null, null, null, null, null }; contour[0].SplitInThirds(out parts[0 + 3 * corner], out parts[1 + 3 * corner], out parts[2 + 3 * corner]); if (contour.Count >= 2) { contour[1].SplitInThirds(out parts[3 - 3 * corner], out parts[4 - 3 * corner], out parts[5 - 3 * corner]); parts[0].Color = parts[1].Color = colors[0]; parts[2].Color = parts[3].Color = colors[1]; parts[4].Color = parts[5].Color = colors[2]; } else { parts[0].Color = colors[0]; parts[1].Color = colors[1]; parts[2].Color = colors[2]; } contour.Clear(); for (var i = 0; parts[i] != null; ++i) { contour.Add(parts[i]); } }
internal EdgeBmpLut(List <ContourCorner> corners, List <EdgeSegment> flattenEdges, List <int> segOfNextContours, List <int> cornerOfNextContours) { //move first to last int startAt = 0; for (int i = 0; i < segOfNextContours.Count; ++i) { int nextStartAt = segOfNextContours[i]; // EdgeSegment firstSegment = flattenEdges[startAt]; flattenEdges.RemoveAt(startAt); if (i == segOfNextContours.Count - 1) { flattenEdges.Add(firstSegment); } else { flattenEdges.Insert(nextStartAt - 1, firstSegment); } startAt = nextStartAt; } _corners = corners; _flattenEdges = flattenEdges; EdgeOfNextContours = segOfNextContours; CornerOfNextContours = cornerOfNextContours; }
public EdgeStructure(EdgeSegment[] edgeSegments, AreaKind areaKind) { _isEmpty = false; _edgeSegment = null; _edgeSegments = edgeSegments; _areaKind = areaKind; }
public Vec2Info(EdgeSegment owner, Vec2PointKind kind, Vector2 point) { this.owner = owner; this.x = point.x; this.y = point.y; Kind = kind; }
static void FlattenPoints(EdgeSegment segment, List <Vec2Info> points) { switch (segment.SegmentKind) { default: throw new NotSupportedException(); case EdgeSegmentKind.LineSegment: { LinearSegment seg = (LinearSegment)segment; points.Add(new Vec2Info(segment, Vec2PointKind.Touch1, seg.P0)); } break; case EdgeSegmentKind.QuadraticSegment: { QuadraticSegment seg = (QuadraticSegment)segment; points.Add(new Vec2Info(segment, Vec2PointKind.Touch1, seg.P0)); points.Add(new Vec2Info(segment, Vec2PointKind.C2, seg.P1)); } break; case EdgeSegmentKind.CubicSegment: { CubicSegment seg = (CubicSegment)segment; points.Add(new Vec2Info(segment, Vec2PointKind.Touch1, seg.P0)); points.Add(new Vec2Info(segment, Vec2PointKind.C3, seg.P1)); points.Add(new Vec2Info(segment, Vec2PointKind.C3, seg.P2)); } break; } }
public EdgeStructure(EdgeSegment edgeSegment, AreaKind areaKind) { _isEmpty = false; _edgeSegment = edgeSegment; _areaKind = areaKind; // _edgeSegments = null; }
public override void SplitInThirds(out EdgeSegment part1, out EdgeSegment part2, out EdgeSegment part3) { part1 = new QuadraticSegment(Color, _p[0], Arithmetic.Mix(_p[0], _p[1], 1.0 / 3.0), Point(1.0 / 3.0)); part2 = new QuadraticSegment(Color, Point(1.0 / 3.0), Arithmetic.Mix(Arithmetic.Mix(_p[0], _p[1], 5.0 / 9.0), Arithmetic.Mix(_p[1], _p[2], 4.0 / 9.0), 0.5), Point(2 / 3.0)); part3 = new QuadraticSegment(Color, Point(2.0 / 3.0), Arithmetic.Mix(_p[1], _p[2], 2.0 / 3.0), _p[2]); }
public override void splitInThirds(out EdgeSegment part1, out EdgeSegment part2, out EdgeSegment part3) { part1 = new CubicSegment(p[0], Vector2.IsEq(p[0], p[1]) ? p[0] : mix(p[0], p[1], 1 / 3.0), mix(mix(p[0], p[1], 1 / 3.0), mix(p[1], p[2], 1 / 3.0), 1 / 3.0), point(1 / 3.0), color); part2 = new CubicSegment(point(1 / 3.0), mix(mix(mix(p[0], p[1], 1 / 3.0), mix(p[1], p[2], 1 / 3.0), 1 / 3.0), mix(mix(p[1], p[2], 1 / 3.0), mix(p[2], p[3], 1 / 3.0), 1 / 3.0), 2 / 3.0), mix(mix(mix(p[0], p[1], 2 / 3.0), mix(p[1], p[2], 2 / 3.0), 2 / 3.0), mix(mix(p[1], p[2], 2 / 3.0), mix(p[2], p[3], 2 / 3.0), 2 / 3.0), 1 / 3.0), point(2 / 3.0), color); part3 = new CubicSegment(point(2 / 3.0), mix(mix(p[1], p[2], 2 / 3.0), mix(p[2], p[3], 2 / 3.0), 2 / 3.0), Vector2.IsEq(p[2], p[3]) ? p[3] : mix(p[2], p[3], 2 / 3.0), p[3], color); }
public override void splitInThirds(out EdgeSegment part1, out EdgeSegment part2, out EdgeSegment part3) { part1 = new CubicSegment(_p0, Vector2.IsEq(_p0, _p1) ? _p0 : mix(_p0, _p1, 1 / 3.0), mix(mix(_p0, _p1, 1 / 3.0), mix(_p1, _p2, 1 / 3.0), 1 / 3.0), point(1 / 3.0), color); part2 = new CubicSegment(point(1 / 3.0), mix(mix(mix(_p0, _p1, 1 / 3.0), mix(_p1, _p2, 1 / 3.0), 1 / 3.0), mix(mix(_p1, _p2, 1 / 3.0), mix(_p2, _p3, 1 / 3.0), 1 / 3.0), 2 / 3.0), mix(mix(mix(_p0, _p1, 2 / 3.0), mix(_p1, _p2, 2 / 3.0), 2 / 3.0), mix(mix(_p1, _p2, 2 / 3.0), mix(_p2, _p3, 2 / 3.0), 2 / 3.0), 1 / 3.0), point(2 / 3.0), color); part3 = new CubicSegment(point(2 / 3.0), mix(mix(_p1, _p2, 2 / 3.0), mix(_p2, _p3, 2 / 3.0), 2 / 3.0), Vector2.IsEq(_p2, _p3) ? _p3 : mix(_p2, _p3, 2 / 3.0), _p3, color); }
/// Normalizes the shape geometry for distance field generation. public void Normalize() { foreach (var contour in this) { if (contour.Count == 1) { var parts = new EdgeSegment[3]; contour[0].SplitInThirds(out parts[0], out parts[1], out parts[2]); contour.Clear(); contour.Add(parts[0]); contour.Add(parts[1]); contour.Add(parts[2]); } } }
public override void SplitInThirds(out EdgeSegment part1, out EdgeSegment part2, out EdgeSegment part3) { part1 = new CubicSegment(Color, _p[0], _p[0] == _p[1] ? _p[0] : Arithmetic.Mix(_p[0], _p[1], 1.0 / 3.0), Arithmetic.Mix(Arithmetic.Mix(_p[0], _p[1], 1.0 / 3.0), Arithmetic.Mix(_p[1], _p[2], 1.0 / 3.0), 1.0 / 3.0), Point(1.0 / 3.0)); part2 = new CubicSegment(Color, Point(1.0 / 3.0), Arithmetic.Mix( Arithmetic.Mix(Arithmetic.Mix(_p[0], _p[1], 1.0 / 3.0), Arithmetic.Mix(_p[1], _p[2], 1.0 / 3.0), 1.0 / 3.0), Arithmetic.Mix(Arithmetic.Mix(_p[1], _p[2], 1.0 / 3.0), Arithmetic.Mix(_p[2], _p[3], 1.0 / 3.0), 1.0 / 3.0), 2.0 / 3.0), Arithmetic.Mix( Arithmetic.Mix(Arithmetic.Mix(_p[0], _p[1], 2.0 / 3.0), Arithmetic.Mix(_p[1], _p[2], 2.0 / 3.0), 2.0 / 3.0), Arithmetic.Mix(Arithmetic.Mix(_p[1], _p[2], 2.0 / 3.0), Arithmetic.Mix(_p[2], _p[3], 2.0 / 3.0), 2.0 / 3.0), 1.0 / 3.0), Point(2.0 / 3.0)); part3 = new CubicSegment(Color, Point(2.0 / 3.0), Arithmetic.Mix(Arithmetic.Mix(_p[1], _p[2], 2.0 / 3.0), Arithmetic.Mix(_p[2], _p[3], 2.0 / 3.0), 2.0 / 3.0), _p[2] == _p[3] ? _p[3] : Arithmetic.Mix(_p[2], _p[3], 2.0 / 3.0), _p[3]); }
public static void GenerateSdf_legacy(FloatBmp output, Shape shape, double range, Vector2 scale, Vector2 translate) { int w = output.Width; int h = output.Height; for (int y = 0; y < h; ++y) { int row = shape.InverseYAxis ? h - y - 1 : y; for (int x = 0; x < w; ++x) { double dummy = 0; Vector2 p = (new Vector2(x + 0.5f, y + 0.5) * scale) - translate; SignedDistance minDistance = SignedDistance.INFINITE; //TODO: review here List <Contour> contours = shape.contours; int m = contours.Count; for (int n = 0; n < m; ++n) { Contour contour = contours[n]; List <EdgeSegment> edges = contour.edges; int nn = edges.Count; for (int i = 0; i < nn; ++i) { EdgeSegment edge = edges[i]; SignedDistance distance = edge.signedDistance(p, out dummy); if (distance < minDistance) { minDistance = distance; } } } output.SetPixel(x, row, (float)(minDistance.distance / (range + 0.5f))); } } }
internal void SetOverlappedList(List <CornerList> overlappedList) { int m = overlappedList.Count; _overlappedEdgeList = new List <EdgeSegment[]>(m); for (int i = 0; i < m; ++i) { #if DEBUG if (i == 124 || i == 389) { } #endif CornerList cornerList = overlappedList[i]; int count = cornerList.Count; EdgeSegment[] corners = new EdgeSegment[count];//overlapping corner region for (int a = 0; a < count; ++a) { //ushort x = cornerList[a]; corners[a] = _corners[cornerList[a]].CenterSegment; } _overlappedEdgeList.Add(corners); } }
static Vertex2d ConvToV2d(PointD p) => new Vertex2d(p.X, p.Y); //temp /// <summary> /// fill inner and outer border from corner0 to corner1 /// </summary> /// <param name="painter"></param> /// <param name="c0"></param> /// <param name="c1"></param> void FillBorders(AggPainter painter, ContourCorner c0, ContourCorner c1) { //counter-clockwise if (!c0.MiddlePoint_IsTouchPoint) { return; } //with a given corner, have have information of 3 points //left-point of the corner,=> from vertex //middle-point, current vertex //right-point,=> next vertex //a vertex may be touch-curve vertext, or 'not-touch-curve' vertex //'is not touch-curve point', => this vertex is a control point of C3 or C4 curve, //------------------------------------------------------- if (c0.RightPoint_IsTouchPoint) { //c0 => touch curve //c1 => touch curve, //we create an imaginary line from c0 to c1 //then we create an 'inner border' of a line from c0 to c1 //and we create an 'outer border' of a line from c0 to c1 // using (Tools.BorrowVxs(out var v1)) { //1. inner-border, set fill mode to inform proper color encoding of inner border _msdfEdgePxBlender.FillMode = MsdfEdgePixelBlender.BlenderFillMode.InnerBorder; //2020-03-13, version 3 fill is still better than v3.1, //TODO: review version v3.1 if (_use_v3_1) { //version 3.1 fill technique CreateBorder(v1, ConvToV2d(c1.RightPoint), ConvToV2d(c1.MiddlePoint), ConvToV2d(c0.MiddlePoint), ConvToV2d(c0.LeftPoint)); } else { //version 3 fill technique CreateInnerBorder(v1, c0.MiddlePoint.X, c0.MiddlePoint.Y, c1.MiddlePoint.X, c1.MiddlePoint.Y, INNER_BORDER_W); } painter.Fill(v1, c0.InnerColor); //------------- v1.Clear(); //reuse //2. outer-border, set fill mode too. _msdfEdgePxBlender.FillMode = MsdfEdgePixelBlender.BlenderFillMode.OuterBorder; if (_use_v3_1) { //version 3.1 fill technique CreateBorder(v1, ConvToV2d(c0.LeftPoint), ConvToV2d(c0.MiddlePoint), ConvToV2d(c1.MiddlePoint), ConvToV2d(c1.RightPoint)); } else { //version 3 fill technique CreateOuterBorder(v1, c0.MiddlePoint.X, c0.MiddlePoint.Y, c1.MiddlePoint.X, c1.MiddlePoint.Y, OUTER_BORDER_W); } painter.Fill(v1, c0.OuterColor); } } else { //painter.CurrentBxtBlendOp = null; //** //c0 is touch line, //but c1 is not, this means=> next segment will be a curve(C3 or C4 curve) // EdgeSegment ownerSeg = c1.CenterSegment; switch (ownerSeg.SegmentKind) { default: throw new NotSupportedException(); case EdgeSegmentKind.CubicSegment: { //approximate CubicSegment cs = (CubicSegment)ownerSeg; using (Tools.BorrowVxs(out var v1)) using (Tools.BorrowShapeBuilder(out var b)) using (Tools.BorrowStroke(out var strk)) { b.MoveTo(cs.P0.x + _dx, cs.P0.y + _dy) //... .Curve4To(cs.P1.x + _dx, cs.P1.y + _dy, cs.P2.x + _dx, cs.P2.y + _dy, cs.P3.x + _dx, cs.P3.y + _dy) .NoMore() .Flatten(); //----------------------- //fill outside part of the curve strk.Width = CURVE_STROKE_EACHSIDE * 2; strk.StrokeSideForOpenShape = StrokeSideForOpenShape.Outside; strk.MakeVxs(b.CurrentSharedVxs, v1); painter.Fill(v1, c0.OuterColor); //----------------------- //fill inside part of the curve v1.Clear(); //reuse strk.StrokeSideForOpenShape = StrokeSideForOpenShape.Inside; strk.MakeVxs(b.CurrentSharedVxs, v1); painter.Fill(v1, c0.InnerColor); //----------------------- } } break; case EdgeSegmentKind.QuadraticSegment: { QuadraticSegment qs = (QuadraticSegment)ownerSeg; using (Tools.BorrowVxs(out var v1)) using (Tools.BorrowShapeBuilder(out var b)) using (Tools.BorrowStroke(out var strk)) { b.MoveTo(qs.P0.x + _dx, qs.P0.y + _dy)//... .Curve3To(qs.P1.x + _dx, qs.P1.y + _dy, qs.P2.x + _dx, qs.P2.y + _dy) .NoMore() .Flatten(); //----------------------- //fill outside part of the curve strk.Width = CURVE_STROKE_EACHSIDE * 2; strk.StrokeSideForOpenShape = StrokeSideForOpenShape.Outside; strk.MakeVxs(b.CurrentSharedVxs, v1); painter.Fill(v1, c0.OuterColor); //----------------------- //fill inside part of the curve v1.Clear();//reuse strk.StrokeSideForOpenShape = StrokeSideForOpenShape.Inside; strk.MakeVxs(b.CurrentSharedVxs, v1); painter.Fill(v1, c0.InnerColor); //----------------------- } } break; } } }
public abstract void splitInThirds( out EdgeSegment part1, out EdgeSegment part2, out EdgeSegment part3);
//siged distance field generator public static void GenerateSdf(FloatBmp output, Shape shape, double range, Vector2 scale, Vector2 translate) { List <Contour> contours = shape.contours; int contourCount = contours.Count; int w = output.Width, h = output.Height; List <int> windings = new List <int>(contourCount); for (int i = 0; i < contourCount; ++i) { windings.Add(contours[i].winding()); } //# ifdef MSDFGEN_USE_OPENMP //#pragma omp parallel //#endif { //# ifdef MSDFGEN_USE_OPENMP //#pragma omp for //#endif double[] contourSD = new double[contourCount]; for (int y = 0; y < h; ++y) { int row = shape.InverseYAxis ? h - y - 1 : y; for (int x = 0; x < w; ++x) { double dummy = 0; Vector2 p = (new Vector2(x + .5, y + .5) / scale) - translate; double negDist = -SignedDistance.INFINITE.distance; double posDist = SignedDistance.INFINITE.distance; int winding = 0; for (int i = 0; i < contourCount; ++i) { Contour contour = contours[i]; SignedDistance minDistance = SignedDistance.INFINITE; List <EdgeSegment> edges = contour.edges; int edgeCount = edges.Count; for (int ee = 0; ee < edgeCount; ++ee) { EdgeSegment edge = edges[ee]; SignedDistance distance = edge.signedDistance(p, out dummy); if (distance < minDistance) { minDistance = distance; } } contourSD[i] = minDistance.distance; if (windings[i] > 0 && minDistance.distance >= 0 && Math.Abs(minDistance.distance) < Math.Abs(posDist)) { posDist = minDistance.distance; } if (windings[i] < 0 && minDistance.distance <= 0 && Math.Abs(minDistance.distance) < Math.Abs(negDist)) { negDist = minDistance.distance; } } double sd = SignedDistance.INFINITE.distance; if (posDist >= 0 && Math.Abs(posDist) <= Math.Abs(negDist)) { sd = posDist; winding = 1; for (int i = 0; i < contourCount; ++i) { if (windings[i] > 0 && contourSD[i] > sd && Math.Abs(contourSD[i]) < Math.Abs(negDist)) { sd = contourSD[i]; } } } else if (negDist <= 0 && Math.Abs(negDist) <= Math.Abs(posDist)) { sd = negDist; winding = -1; for (int i = 0; i < contourCount; ++i) { if (windings[i] < 0 && contourSD[i] < sd && Math.Abs(contourSD[i]) < Math.Abs(posDist)) { sd = contourSD[i]; } } } for (int i = 0; i < contourCount; ++i) { if (windings[i] != winding && Math.Abs(contourSD[i]) < Math.Abs(sd)) { sd = contourSD[i]; } } output.SetPixel(x, row, (float)(sd / range + .5)); } } } }
public override void splitInThirds(out EdgeSegment part1, out EdgeSegment part2, out EdgeSegment part3) { part1 = new QuadraticSegment(p[0], mix(p[0], p[1], 1 / 3.0), point(1 / 3.0), this.color); part2 = new QuadraticSegment(point(1 / 3.0), mix(mix(p[0], p[1], 5 / 9.0), mix(p[1], p[2], 4 / 9.0), .5), point(2 / 3.0), this.color); part3 = new QuadraticSegment(point(2 / 3.0), mix(p[1], p[2], 2 / 3.0), p[2], this.color); }
public static void generateMSDF(FloatRGBBmp output, Shape shape, double range, Vector2 scale, Vector2 translate, double edgeThreshold) { List <Contour> contours = shape.contours; int contourCount = contours.Count; int w = output.Width; int h = output.Height; List <int> windings = new List <int>(contourCount); for (int i = 0; i < contourCount; ++i) { windings.Add(contours[i].winding()); } var contourSD = new MultiDistance[contourCount]; for (int y = 0; y < h; ++y) { int row = shape.InverseYAxis ? h - y - 1 : y; for (int x = 0; x < w; ++x) { Vector2 p = (new Vector2(x + .5, y + .5) / scale) - translate; EdgePoint sr = new EdgePoint { minDistance = SignedDistance.INFINITE }, sg = new EdgePoint { minDistance = SignedDistance.INFINITE }, sb = new EdgePoint { minDistance = SignedDistance.INFINITE }; double d = Math.Abs(SignedDistance.INFINITE.distance); double negDist = -Math.Abs(SignedDistance.INFINITE.distance); double posDist = Math.Abs(SignedDistance.INFINITE.distance); int winding = 0; for (int n = 0; n < contourCount; ++n) { //for-each contour Contour contour = contours[n]; List <EdgeSegment> edges = contour.edges; int edgeCount = edges.Count; EdgePoint r = new EdgePoint { minDistance = SignedDistance.INFINITE }, g = new EdgePoint { minDistance = SignedDistance.INFINITE }, b = new EdgePoint { minDistance = SignedDistance.INFINITE }; for (int ee = 0; ee < edgeCount; ++ee) { EdgeSegment edge = edges[ee]; SignedDistance distance = edge.signedDistance(p, out double param); if (edge.HasComponent(EdgeColor.RED) && distance < r.minDistance) { r.minDistance = distance; r.nearEdge = edge; r.nearParam = param; } if (edge.HasComponent(EdgeColor.GREEN) && distance < g.minDistance) { g.minDistance = distance; g.nearEdge = edge; g.nearParam = param; } if (edge.HasComponent(EdgeColor.BLUE) && distance < b.minDistance) { b.minDistance = distance; b.nearEdge = edge; b.nearParam = param; } } //---------------- if (r.minDistance < sr.minDistance) { sr = r; } if (g.minDistance < sg.minDistance) { sg = g; } if (b.minDistance < sb.minDistance) { sb = b; } //---------------- double medMinDistance = Math.Abs(median(r.minDistance.distance, g.minDistance.distance, b.minDistance.distance)); if (medMinDistance < d) { d = medMinDistance; winding = -windings[n]; } if (r.nearEdge != null) { r.nearEdge.distanceToPseudoDistance(ref r.minDistance, p, r.nearParam); } if (g.nearEdge != null) { g.nearEdge.distanceToPseudoDistance(ref g.minDistance, p, g.nearParam); } if (b.nearEdge != null) { b.nearEdge.distanceToPseudoDistance(ref b.minDistance, p, b.nearParam); } //-------------- medMinDistance = median(r.minDistance.distance, g.minDistance.distance, b.minDistance.distance); contourSD[n].r = r.minDistance.distance; contourSD[n].g = g.minDistance.distance; contourSD[n].b = b.minDistance.distance; contourSD[n].med = medMinDistance; if (windings[n] > 0 && medMinDistance >= 0 && Math.Abs(medMinDistance) < Math.Abs(posDist)) { posDist = medMinDistance; } if (windings[n] < 0 && medMinDistance <= 0 && Math.Abs(medMinDistance) < Math.Abs(negDist)) { negDist = medMinDistance; } } if (sr.nearEdge != null) { sr.nearEdge.distanceToPseudoDistance(ref sr.minDistance, p, sr.nearParam); } if (sg.nearEdge != null) { sg.nearEdge.distanceToPseudoDistance(ref sg.minDistance, p, sg.nearParam); } if (sb.nearEdge != null) { sb.nearEdge.distanceToPseudoDistance(ref sb.minDistance, p, sb.nearParam); } MultiDistance msd; msd.r = msd.g = msd.b = msd.med = SignedDistance.INFINITE.distance; if (posDist >= 0 && Math.Abs(posDist) <= Math.Abs(negDist)) { msd.med = SignedDistance.INFINITE.distance; winding = 1; for (int i = 0; i < contourCount; ++i) { if (windings[i] > 0 && contourSD[i].med > msd.med && Math.Abs(contourSD[i].med) < Math.Abs(negDist)) { msd = contourSD[i]; } } } else if (negDist <= 0 && Math.Abs(negDist) <= Math.Abs(posDist)) { msd.med = -SignedDistance.INFINITE.distance; winding = -1; for (int i = 0; i < contourCount; ++i) { if (windings[i] < 0 && contourSD[i].med < msd.med && Math.Abs(contourSD[i].med) < Math.Abs(posDist)) { msd = contourSD[i]; } } } for (int i = 0; i < contourCount; ++i) { if (windings[i] != winding && Math.Abs(contourSD[i].med) < Math.Abs(msd.med)) { msd = contourSD[i]; } } if (median(sr.minDistance.distance, sg.minDistance.distance, sb.minDistance.distance) == msd.med) { msd.r = sr.minDistance.distance; msd.g = sg.minDistance.distance; msd.b = sb.minDistance.distance; } output.SetPixel(x, row, new FloatRGB( (float)(msd.r / range + .5), (float)(msd.g / range + .5), (float)(msd.b / range + .5) )); } } if (edgeThreshold > 0) { msdfErrorCorrection(output, edgeThreshold / (scale * range)); } }
public static void generateMSDF_legacy(FloatRGBBmp output, Shape shape, double range, Vector2 scale, Vector2 translate, double edgeThreshold) { int w = output.Width; int h = output.Height; //#ifdef MSDFGEN_USE_OPENMP // #pragma omp parallel for //#endif for (int y = 0; y < h; ++y) { int row = shape.InverseYAxis ? h - y - 1 : y; for (int x = 0; x < w; ++x) { Vector2 p = (new Vector2(x + .5, y + .5) / scale) - translate; EdgePoint r = new EdgePoint { minDistance = SignedDistance.INFINITE }, g = new EdgePoint { minDistance = SignedDistance.INFINITE }, b = new EdgePoint { minDistance = SignedDistance.INFINITE }; //r.nearEdge = g.nearEdge = b.nearEdge = null; //r.nearParam = g.nearParam = b.nearParam = 0; List <Contour> contours = shape.contours; int m = contours.Count; for (int n = 0; n < m; ++n) { Contour contour = contours[n]; List <EdgeSegment> edges = contour.edges; int j = edges.Count; for (int i = 0; i < j; ++i) { EdgeSegment edge = edges[i]; SignedDistance distance = edge.signedDistance(p, out double param); if (edge.HasComponent(EdgeColor.RED) && distance < r.minDistance) { r.minDistance = distance; r.nearEdge = edge; r.nearParam = param; } if (edge.HasComponent(EdgeColor.GREEN) && distance < g.minDistance) { g.minDistance = distance; g.nearEdge = edge; g.nearParam = param; } if (edge.HasComponent(EdgeColor.BLUE) && distance < b.minDistance) { b.minDistance = distance; b.nearEdge = edge; b.nearParam = param; } } if (r.nearEdge != null) { r.nearEdge.distanceToPseudoDistance(ref r.minDistance, p, r.nearParam); } if (g.nearEdge != null) { g.nearEdge.distanceToPseudoDistance(ref g.minDistance, p, g.nearParam); } if (b.nearEdge != null) { b.nearEdge.distanceToPseudoDistance(ref b.minDistance, p, b.nearParam); } output.SetPixel(x, row, new FloatRGB( (float)(r.minDistance.distance / range + .5), (float)(g.minDistance.distance / range + .5), (float)(b.minDistance.distance / range + .5) )); } } } if (edgeThreshold > 0) { msdfErrorCorrection(output, edgeThreshold / (scale * range)); } }
static void GenerateMSDF3(FloatRGBBmp output, Shape shape, double range, Vector2 scale, Vector2 translate, double edgeThreshold, EdgeBmpLut lut) { //---------------------- //this is our extension, //we use lookup bitmap (lut) to check //what is the nearest contour of a given pixel. //---------------------- int w = output.Width; int h = output.Height; EdgeSegment[] singleSegment = new EdgeSegment[1];//temp array for for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { //PER-PIXEL-OPERATION //check preview pixel int lutPix = lut.GetPixel(x, y); int lutPixR = (lutPix & 0xFF); int lutPixG = (lutPix >> 8) & 0xff; int lutPixB = (lutPix >> 16) & 0xff; if (lutPixG == 0) { continue; //black=> completely outside, skip } if (lutPixG == EdgeBmpLut.AREA_INSIDE_COVERAGE100 || lutPixG == EdgeBmpLut.AREA_INSIDE_COVERAGE50 || lutPixG == EdgeBmpLut.AREA_INSIDE_COVERAGEX) { //inside the contour => fill all with white output.SetPixel(x, y, new FloatRGB(1f, 1f, 1f)); continue; } //reset variables EdgePoint r = new EdgePoint { minDistance = SignedDistance.INFINITE }, g = new EdgePoint { minDistance = SignedDistance.INFINITE }, b = new EdgePoint { minDistance = SignedDistance.INFINITE }; bool useR, useG, useB; useR = useG = useB = true; //------ Vector2 p = (new Vector2(x + .5, y + .5) / scale) - translate; EdgeStructure edgeStructure = lut.GetEdgeStructure(x, y); #if DEBUG if (edgeStructure.IsEmpty) { //should not occurs throw new NotSupportedException(); } #endif EdgeSegment[] edges = null; if (edgeStructure.HasOverlappedSegments) { edges = edgeStructure.Segments; } else { singleSegment[0] = edgeStructure.Segment; edges = singleSegment; } //------------- for (int i = 0; i < edges.Length; ++i) { EdgeSegment edge = edges[i]; SignedDistance distance = edge.signedDistance(p, out double param);//*** if (edge.HasComponent(EdgeColor.RED) && distance < r.minDistance) { r.minDistance = distance; r.nearEdge = edge; r.nearParam = param; useR = false; } if (edge.HasComponent(EdgeColor.GREEN) && distance < g.minDistance) { g.minDistance = distance; g.nearEdge = edge; g.nearParam = param; useG = false; } if (edge.HasComponent(EdgeColor.BLUE) && distance < b.minDistance) { b.minDistance = distance; b.nearEdge = edge; b.nearParam = param; useB = false; } } double contour_r = r.CalculateContourColor(p); double contour_g = g.CalculateContourColor(p); double contour_b = b.CalculateContourColor(p); if (useB && contour_b <= SignedDistance.INFINITE.distance) { contour_b = 1 * range; } if (useG && contour_g <= SignedDistance.INFINITE.distance) { contour_g = 1 * range; } if (useR && contour_r <= SignedDistance.INFINITE.distance) { contour_r = 1 * range; } output.SetPixel(x, y, new FloatRGB( (float)(contour_r / range + .5), (float)(contour_g / range + .5), (float)(contour_b / range + .5) )); } } }
public static void edgeColoringSimple(Shape shape, double angleThreshold, ulong seed = 0) { double crossThreshold = Math.Sin(angleThreshold); List <int> corners = new List <int>(); //TODO: review reusable list // for (std::vector<Contour>::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) foreach (Contour contour in shape.contours) { // Identify corners corners.Clear(); List <EdgeHolder> edges = contour.edges; int edgeCount = edges.Count; if (edgeCount != 0) { Vector2 prevDirection = edges[edgeCount - 1].direction(1);// (*(contour->edges.end() - 1))->direction(1); for (int i = 0; i < edgeCount; ++i) { EdgeHolder edge = edges[i]; if (isCorner(prevDirection.normalize(), edge.direction(0).normalize(), crossThreshold)) { corners.Add(i); } prevDirection = edge.direction(1); } } // Smooth contour if (corners.Count == 0) //is empty { for (int i = edgeCount - 1; i >= 0; --i) { edges[i].color = EdgeColor.WHITE; } } else if (corners.Count == 1) { // "Teardrop" case EdgeColor[] colors = { EdgeColor.WHITE, EdgeColor.WHITE, EdgeColor.BLACK }; switchColor(ref colors[0], ref seed); colors[2] = colors[0]; switchColor(ref colors[2], ref seed); int corner = corners[0]; if (edgeCount >= 3) { int m = edgeCount; for (int i = 0; i < m; ++i) { //TODO: review here contour.edges[(corner + i) % m].color = colors[((int)(3 + 2.875 * i / (m - 1) - 1.4375 + .5) - 3) + 1]; //(colors + 1)[int(3 + 2.875 * i / (m - 1) - 1.4375 + .5) - 3]; } } else if (edgeCount >= 1) { // Less than three edge segments for three colors => edges must be split EdgeSegment[] parts = new EdgeSegment[7]; //empty array, TODO: review array alloc here edges[0].edgeSegment.splitInThirds( out parts[0 + 3 * corner], out parts[1 + 3 * corner], out parts[2 + 3 * corner]); if (edgeCount >= 2) { edges[1].edgeSegment.splitInThirds( out parts[3 - 3 * corner], out parts[4 - 3 * corner], out parts[5 - 3 * corner] ); parts[0].color = parts[1].color = colors[0]; parts[2].color = parts[3].color = colors[1]; parts[4].color = parts[5].color = colors[2]; } else { parts[0].color = colors[0]; parts[1].color = colors[1]; parts[2].color = colors[2]; } contour.edges.Clear(); for (int i = 0; i < 7; ++i) { edges.Add(new EdgeHolder(parts[i])); } } } // Multiple corners else { int cornerCount = corners.Count; int spline = 0; int start = corners[0]; int m = contour.edges.Count; EdgeColor color = EdgeColor.WHITE; switchColor(ref color, ref seed); EdgeColor initialColor = color; for (int i = 0; i < m; ++i) { int index = (start + i) % m; if (spline + 1 < cornerCount && corners[spline + 1] == index) { ++spline; switchColor(ref color, ref seed, (EdgeColor)(((spline == cornerCount - 1) ? 1 : 0) * (int)initialColor)); } edges[index].color = color; } } } }
public void AddEdge(EdgeSegment edge) { EdgeHolder holder = new EdgeHolder(edge); edges.Add(holder); }
public override void splitInThirds(out EdgeSegment part1, out EdgeSegment part2, out EdgeSegment part3) { part1 = new QuadraticSegment(_p0, mix(_p0, _p1, 1 / 3.0), point(1 / 3.0), this.color); part2 = new QuadraticSegment(point(1 / 3.0), mix(mix(_p0, _p1, 5 / 9.0), mix(_p1, _p2, 4 / 9.0), .5), point(2 / 3.0), this.color); part3 = new QuadraticSegment(point(2 / 3.0), mix(_p1, _p2, 2 / 3.0), _p2, this.color); }
public override void splitInThirds(out EdgeSegment part1, out EdgeSegment part2, out EdgeSegment part3) { part1 = new LinearSegment(p[0], point(1 / 3.0), this.color); part2 = new LinearSegment(point(1 / 3.0), point(2 / 3.0), this.color); part3 = new LinearSegment(point(2 / 3.0), p[1], this.color); }
public static void edgeColoringSimple(Shape shape, double angleThreshold) { double crossThreshold = Math.Sin(angleThreshold); List <int> corners = new List <int>(); // for (std::vector<Contour>::iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) foreach (Contour contour in shape.contours) { // Identify corners corners.Clear(); List <EdgeHolder> edges = contour.edges; int edgeCount = edges.Count; if (edgeCount != 0) { Vector2 prevDirection = edges[edgeCount - 1].Direction(1);// (*(contour->edges.end() - 1))->direction(1); for (int i = 0; i < edgeCount; ++i) { EdgeHolder edge = edges[i]; if (isCorner(prevDirection.normalize(), edge.Direction(0).normalize(), crossThreshold)) { corners.Add(i); } prevDirection = edge.Direction(1); } } // Smooth contour if (corners.Count == 0) //is empty { for (int i = edgeCount - 1; i >= 0; --i) { edges[i].color = EdgeColor.WHITE; } } else if (corners.Count == 1) { // "Teardrop" case EdgeColor[] colors = { EdgeColor.MAGENTA, EdgeColor.WHITE, EdgeColor.YELLOW }; int corner = corners[0]; if (edgeCount >= 3) { int m = edgeCount; for (int i = 0; i < m; ++i) { //TODO: review here contour.edges[(corner + i) % m].color = colors[((int)(3 + 2.875 * i / (m - 1) - 1.4375 + .5) - 3) + 1]; //(colors + 1)[int(3 + 2.875 * i / (m - 1) - 1.4375 + .5) - 3]; } } else if (edgeCount >= 1) { // Less than three edge segments for three colors => edges must be split EdgeSegment[] parts = new EdgeSegment[7]; //empty array edges[0].edgeSegment.splitInThirds( out parts[0 + 3 * corner], out parts[1 + 3 * corner], out parts[2 + 3 * corner]); if (edgeCount >= 2) { edges[1].edgeSegment.splitInThirds( out parts[3 - 3 * corner], out parts[4 - 3 * corner], out parts[5 - 3 * corner] ); parts[0].color = parts[1].color = colors[0]; parts[2].color = parts[3].color = colors[1]; parts[4].color = parts[5].color = colors[2]; } else { parts[0].color = colors[0]; parts[1].color = colors[1]; parts[2].color = colors[2]; } contour.edges.Clear(); for (int i = 0; i < 7; ++i) { edges.Add(new EdgeHolder(parts[i])); } } } // Multiple corners else { int cornerCount = corners.Count; // CMYCMYCMYCMY / YMYCMYC if corner count % 3 == 1 EdgeColor[] colors = { cornerCount % 3 == 1 ? EdgeColor.YELLOW : EdgeColor.CYAN, EdgeColor.CYAN, EdgeColor.MAGENTA, EdgeColor.YELLOW }; int spline = 0; int start = corners[0]; int m = contour.edges.Count; for (int i = 0; i < m; ++i) { int index = (start + i) % m; if (cornerCount > spline + 1 && corners[spline + 1] == index) { ++spline; } int tmp = (spline % 3 - ((spline == 0) ? 1 : 0)); edges[index].color = colors[tmp + 1]; //contour->edges[index]->color = (colors + 1)[spline % 3 - !spline]; } } } }
public override void SplitInThirds(out EdgeSegment part1, out EdgeSegment part2, out EdgeSegment part3) { part1 = new LinearSegment(_p[0], Point(1.0 / 3.0), Color); part2 = new LinearSegment(Point(1.0 / 3.0), Point(2.0 / 3.0), Color); part3 = new LinearSegment(Point(2.0 / 3.0), _p[1], Color); }