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 <EdgeHolder> 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) { EdgeHolder edge = edges[ee]; double param; SignedDistance distance = edge.edgeSegment.signedDistance(p, out 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.edgeSegment.distanceToPseudoDistance(ref r.minDistance, p, r.nearParam); } if (g.nearEdge != null) { g.nearEdge.edgeSegment.distanceToPseudoDistance(ref g.minDistance, p, g.nearParam); } if (b.nearEdge != null) { b.nearEdge.edgeSegment.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.edgeSegment.distanceToPseudoDistance(ref sr.minDistance, p, sr.nearParam); } if (sg.nearEdge != null) { sg.nearEdge.edgeSegment.distanceToPseudoDistance(ref sg.minDistance, p, sg.nearParam); } if (sb.nearEdge != null) { sb.nearEdge.edgeSegment.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 <EdgeHolder> edges = contour.edges; int j = edges.Count; for (int i = 0; i < j; ++i) { EdgeHolder edge = edges[i]; double param; SignedDistance distance = edge.edgeSegment.signedDistance(p, out 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.edgeSegment.distanceToPseudoDistance(ref r.minDistance, p, r.nearParam); } if (g.nearEdge != null) { g.nearEdge.edgeSegment.distanceToPseudoDistance(ref g.minDistance, p, g.nearParam); } if (b.nearEdge != null) { b.nearEdge.edgeSegment.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) )); } } }