private void button1_Click(object sender, EventArgs e) { //1. MsdfGenParams msdfGenParams = new MsdfGenParams(); //GlyphImage glyphImg = MsdfGlyphGen.CreateMsdfImage(tx, msdfGenParams); Msdfgen.Shape shape1 = new Msdfgen.Shape(); // Msdfgen.Contour cnt = new Msdfgen.Contour(); //cnt.AddLine(0, 0, 50, 0); //cnt.AddLine(50, 0, 50, 50); //cnt.AddLine(50, 50, 0, 50); //cnt.AddLine(0, 50, 0, 0); //cnt.AddLine(10, 20, 50, 0); //cnt.AddLine(50, 0, 80, 20); //cnt.AddLine(80, 20, 50, 60); //cnt.AddLine(50, 60, 10, 20); //for msdf we draw shape clock-wise cnt.AddLine(10, 20, 50, 60); cnt.AddLine(50, 60, 80, 20); cnt.AddLine(80, 20, 50, 0); cnt.AddLine(50, 0, 10, 20); shape1.contours.Add(cnt); // // var genParams = new MsdfGenParams(); BitmapAtlasItemSource glyphImg = MsdfImageGen.CreateMsdfImageV1(shape1, genParams); using (Bitmap bmp = new Bitmap(glyphImg.Width, glyphImg.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)) { int[] buffer = glyphImg.GetImageBuffer(); var bmpdata = bmp.LockBits(new System.Drawing.Rectangle(0, 0, glyphImg.Width, glyphImg.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat); System.Runtime.InteropServices.Marshal.Copy(buffer, 0, bmpdata.Scan0, buffer.Length); bmp.UnlockBits(bmpdata); bmp.Save("msdf_shape.png"); // } }
//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 <EdgeHolder> edges = contour.edges; int edgeCount = edges.Count; for (int ee = 0; ee < edgeCount; ++ee) { EdgeHolder edge = edges[ee]; SignedDistance distance = edge.edgeSegment.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 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 Shape CreateShape(VertexStore vxs, out EdgeBmpLut bmpLut) { List <EdgeSegment> flattenEdges = new List <EdgeSegment>(); Shape shape = new Shape(); //start with blank shape int i = 0; double x, y; VertexCmd cmd; Contour cnt = null; double latestMoveToX = 0; double latestMoveToY = 0; double latestX = 0; double latestY = 0; List <ContourCorner> corners = new List <ContourCorner>(); List <int> edgeOfNextContours = new List <int>(); List <int> cornerOfNextContours = new List <int>(); while ((cmd = vxs.GetVertex(i, out x, out y)) != VertexCmd.NoMore) { switch (cmd) { case VertexCmd.Close: { //close current cnt if ((latestMoveToX != latestX) || (latestMoveToY != latestY)) { //add line to close the shape if (cnt != null) { flattenEdges.Add(cnt.AddLine(latestX, latestY, latestMoveToX, latestMoveToY)); } } if (cnt != null) { //*** CreateCorners(cnt, corners); edgeOfNextContours.Add(flattenEdges.Count); cornerOfNextContours.Add(corners.Count); shape.contours.Add(cnt); //*** cnt = null; } } break; case VertexCmd.C3: { //C3 curve (Quadratic) if (cnt == null) { cnt = new Contour(); } VertexCmd cmd1 = vxs.GetVertex(i + 1, out double x1, out double y1); i++; if (cmd1 != VertexCmd.LineTo) { throw new NotSupportedException(); } //in this version, //we convert Quadratic to Cubic (https://stackoverflow.com/questions/9485788/convert-quadratic-curve-to-cubic-curve) //Control1X = StartX + ((2f/3) * (ControlX - StartX)) //Control2X = EndX + ((2f/3) * (ControlX - EndX)) //flattenEdges.Add(cnt.AddCubicSegment( // latestX, latestY, // ((2f / 3) * (x - latestX)) + latestX, ((2f / 3) * (y - latestY)) + latestY, // ((2f / 3) * (x - x1)) + x1, ((2f / 3) * (y - y1)) + y1, // x1, y1)); flattenEdges.Add(cnt.AddQuadraticSegment(latestX, latestY, x, y, x1, y1)); latestX = x1; latestY = y1; } break; case VertexCmd.C4: { //C4 curve (Cubic) if (cnt == null) { cnt = new Contour(); } VertexCmd cmd1 = vxs.GetVertex(i + 1, out double x2, out double y2); VertexCmd cmd2 = vxs.GetVertex(i + 2, out double x3, out double y3); i += 2; if (cmd1 != VertexCmd.C4 || cmd2 != VertexCmd.LineTo) { throw new NotSupportedException(); } flattenEdges.Add(cnt.AddCubicSegment(latestX, latestY, x, y, x2, y2, x3, y3)); latestX = x3; latestY = y3; } break; case VertexCmd.LineTo: { if (cnt == null) { cnt = new Contour(); } LinearSegment lineseg = cnt.AddLine(latestX, latestY, x, y); flattenEdges.Add(lineseg); latestX = x; latestY = y; } break; case VertexCmd.MoveTo: { latestX = latestMoveToX = x; latestY = latestMoveToY = y; if (cnt != null) { shape.contours.Add(cnt); cnt = null; } } break; } i++; } if (cnt != null) { shape.contours.Add(cnt); CreateCorners(cnt, corners); edgeOfNextContours.Add(flattenEdges.Count); cornerOfNextContours.Add(corners.Count); cnt = null; } GroupingOverlapContours(shape); //from a given shape we create a corner-arm for each corner bmpLut = new EdgeBmpLut(corners, flattenEdges, edgeOfNextContours, cornerOfNextContours); return(shape); }