示例#1
0
        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)
                                        ));
                }
            }
        }
示例#2
0
        internal static PixelFarm.CpuBlit.BitmapAtlas.BitmapAtlasItemSource CreateMsdfImage(Shape shape, MsdfGenParams genParams, int w, int h, Vector2 translate, EdgeBmpLut lutBuffer = null)
        {
            double edgeThreshold = genParams.edgeThreshold;

            if (edgeThreshold < 0)
            {
                edgeThreshold = 1.00000001; //use default if  edgeThreshold <0
            }

            var    scale = new Vector2(genParams.scaleX, genParams.scaleY); //scale
            double range = genParams.pxRange / Math.Min(scale.x, scale.y);
            //---------
            FloatRGBBmp frgbBmp = new FloatRGBBmp(w, h);

            EdgeColoring.edgeColoringSimple(shape, genParams.angleThreshold);

            bool flipY = false;

            if (lutBuffer != null)
            {
                GenerateMSDF3(frgbBmp,
                              shape,
                              range,
                              scale,
                              translate,//translate to positive quadrant
                              edgeThreshold,
                              lutBuffer);
                flipY = shape.InverseYAxis;
            }
            else
            {
                //use original msdf
                MsdfGenerator.generateMSDF(frgbBmp,
                                           shape,
                                           range,
                                           scale,
                                           translate,//translate to positive quadrant
                                           edgeThreshold);
            }

            return(new PixelFarm.CpuBlit.BitmapAtlas.BitmapAtlasItemSource(w, h)
            {
                Source = ConvertToIntBmp(frgbBmp, flipY),
                TextureXOffset = (float)translate.x,
                TextureYOffset = (float)translate.y
            });
        }
示例#3
0
        public PixelFarm.CpuBlit.BitmapAtlas.BitmapAtlasItemSource GenerateMsdfTexture(VertexStore vxs)
        {
            Shape shape = CreateShape(vxs, out EdgeBmpLut edgeBmpLut);

            if (MsdfGenParams == null)
            {
                MsdfGenParams = new MsdfGenParams();//use default
            }

            //---preview v1 bounds-----------
            PreviewSizeAndLocation(
                shape,
                MsdfGenParams,
                out int imgW, out int imgH,
                out Vector2 translateVec);

            _dx = translateVec.x;
            _dy = translateVec.y;
            //------------------------------------
            List <ContourCorner> corners = edgeBmpLut.Corners;

            TranslateCorners(corners, _dx, _dy);

            //[1] create lookup table (lut) bitmap that contains area/corner/shape information
            //each pixel inside it contains data that map to area/corner/shape

            //
            using (MemBitmap bmpLut = new MemBitmap(imgW, imgH))
                using (Tools.BorrowAggPainter(bmpLut, out var painter))
                    using (Tools.BorrowShapeBuilder(out var sh))
                    {
                        _msdfEdgePxBlender.ClearOverlapList();//reset
                        painter.RenderSurface.SetCustomPixelBlender(_msdfEdgePxBlender);

                        //1. clear all bg to black
                        painter.Clear(PixelFarm.Drawing.Color.Black);

                        sh.InitVxs(vxs) //...
                        .TranslateToNewVxs(_dx, _dy)
                        .Flatten();


                        //---------
                        //2. force fill the shape (this include hole(s) inside shape to)
                        //( we set threshold to 50 and do force fill)
                        painter.RenderSurface.SetGamma(_prebuiltThresholdGamma_50);
                        _msdfEdgePxBlender.FillMode = MsdfEdgePixelBlender.BlenderFillMode.Force;
                        painter.Fill(sh.CurrentSharedVxs, EdgeBmpLut.EncodeToColor(0, AreaKind.AreaInsideCoverage50));

                        painter.RenderSurface.SetGamma(_prebuiltThresholdGamma_50);//restore
#if DEBUG
                        //debug for output
                        //painter.Fill(v7, Color.Red);
                        //bmpLut.SaveImage("dbug_step0.png");
                        //int curr_step = 1;
#endif
                        //---------

                        int        cornerCount          = corners.Count;
                        List <int> cornerOfNextContours = edgeBmpLut.CornerOfNextContours;
                        int        startAt      = 0;
                        int        n            = 1;
                        int        corner_index = 1;

                        for (int cnt_index = 0; cnt_index < cornerOfNextContours.Count; ++cnt_index)
                        {
                            //contour scope
                            int next_corner_startAt = cornerOfNextContours[cnt_index];

                            //-----------
                            //AA-borders of the contour
                            painter.RenderSurface.SetGamma(_prebuiltThresholdGamma_OverlappedBorder); //this creates overlapped area

                            for (; n < next_corner_startAt; ++n)
                            {
                                //0-> 1
                                //1->2 ... n
                                FillBorders(painter, corners[n - 1], corners[n]);

#if DEBUG
                                //bmpLut.SaveImage("dbug_step" + curr_step + ".png");
                                //curr_step++;
#endif
                            }
                            {
                                //the last one
                                //close contour, n-> 0
                                FillBorders(painter, corners[next_corner_startAt - 1], corners[startAt]);
#if DEBUG
                                //bmpLut.SaveImage("dbug_step" + curr_step + ".png");
                                //curr_step++;
#endif
                            }

                            startAt = next_corner_startAt;
                            n++;
                            corner_index++;
                        }
#if DEBUG
                        //bmpLut.SaveImage("dbug_step2.png");
#endif


                        //painter.RenderSurface.SetGamma(_prebuiltThresholdGamma_100);
                        //_msdfEdgePxBlender.FillMode = MsdfEdgePixelBlender.BlenderFillMode.InnerAreaX;
                        //painter.Fill(sh.CurrentSharedVxs, EdgeBmpLut.EncodeToColor(0, AreaKind.AreaInsideCoverage100));



                        painter.RenderSurface.SetCustomPixelBlender(null);
                        painter.RenderSurface.SetGamma(null);

                        //
                        List <CornerList> overlappedList = MakeUniqueList(_msdfEdgePxBlender._overlapList);
                        edgeBmpLut.SetOverlappedList(overlappedList);

#if DEBUG
                        if (dbugWriteMsdfTexture)
                        {
                            //save for debug
                            //we save to msdf_shape_lut2.png
                            //and check it from external program
                            //but we generate msdf bitmap from msdf_shape_lut.png
                            bmpLut.SaveImage(dbug_msdf_shape_lutName);
                            var   bmp5       = MemBitmap.LoadBitmap(dbug_msdf_shape_lutName);
                            int[] lutBuffer5 = bmp5.CopyImgBuffer(bmpLut.Width, bmpLut.Height);
                            if (bmpLut.Width == 338 && bmpLut.Height == 477)
                            {
                                dbugBreak = true;
                            }
                            edgeBmpLut.SetBmpBuffer(bmpLut.Width, bmpLut.Height, lutBuffer5);
                            //generate actual sprite
                            PixelFarm.CpuBlit.BitmapAtlas.BitmapAtlasItemSource item = CreateMsdfImage(shape, MsdfGenParams, imgW, imgH, translateVec, edgeBmpLut);
                            //save msdf bitmap to file
                            using (MemBitmap memBmp = MemBitmap.CreateFromCopy(item.Width, item.Height, item.Source))
                            {
                                memBmp.SaveImage(dbug_msdf_output);
                            }

                            return(item);
                        }
#endif

                        //[B] after we have a lookup table
                        int[] lutBuffer = bmpLut.CopyImgBuffer(bmpLut.Width, bmpLut.Height);
                        edgeBmpLut.SetBmpBuffer(bmpLut.Width, bmpLut.Height, lutBuffer);

                        return(CreateMsdfImage(shape, MsdfGenParams, imgW, imgH, translateVec, edgeBmpLut));
                    }
        }
示例#4
0
        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);
        }
示例#5
0
        unsafe void CustomBlendPixel32(int *dstPtr, Color srcColor)
        {
            if (FillMode == BlenderFillMode.Force)
            {
                *dstPtr = srcColor.ToARGB();
                return;
            }
            //-------------------------------------------------------------
            int srcColorABGR  = (int)srcColor.ToABGR();
            int existingColor = *dstPtr;
            //int existing_R = (existingColor >> CO.R_SHIFT) & 0xFF;
            int existing_G = (existingColor >> PixelFarm.Drawing.Internal.CO.G_SHIFT) & 0xFF;

            //int existing_B = (existingColor >> CO.B_SHIFT) & 0xFF;


            if (FillMode == BlenderFillMode.InnerAreaX)
            {
                if (existing_G == EdgeBmpLut.BORDER_OUTSIDE || existing_G == EdgeBmpLut.BORDER_OVERLAP_OUTSIDE)
                {
                    *dstPtr = srcColor.ToARGB();
                }
                return;
            }

            if (existingColor == BLACK)
            {
                *dstPtr = srcColor.ToARGB();
                return;
            }


            if (existingColor == _areaInside100)
            {
                *dstPtr = srcColor.ToARGB();
                return;
            }
            if (srcColorABGR == existingColor)
            {
                return;
            }
            if (FillMode == BlenderFillMode.InnerArea50)
            {
                *dstPtr = srcColor.ToARGB();
                return;
            }

            //-------------------------------------------------------------
            //decode edge information
            //we use 2 bytes for encode edge number

            ushort existingEdgeNo = EdgeBmpLut.DecodeEdgeFromColor(existingColor, out AreaKind existingAreaKind);
            ushort newEdgeNo      = EdgeBmpLut.DecodeEdgeFromColor(srcColor, out AreaKind newEdgeAreaKind);


            if (newEdgeAreaKind == AreaKind.OverlapInside || newEdgeAreaKind == AreaKind.OverlapOutside)
            {
                //new color is overlap color
                if (existingAreaKind == AreaKind.OverlapInside || existingAreaKind == AreaKind.OverlapOutside)
                {
                    CornerList registerList = _overlapList[newEdgeNo];
                    _overlapList[existingEdgeNo].Append(registerList);
                }
                else
                {
                    CornerList registerList = _overlapList[newEdgeNo];
                    registerList.Append(existingEdgeNo);
                    *dstPtr = EdgeBmpLut.EncodeToColor(newEdgeNo, (existing_G == EdgeBmpLut.BORDER_INSIDE) ? AreaKind.OverlapInside : AreaKind.OverlapOutside).ToARGB();
                }
            }
            else
            {
                if (existingAreaKind == AreaKind.OverlapInside ||
                    existingAreaKind == AreaKind.OverlapOutside)
                {
                    _overlapList[existingEdgeNo].Append(newEdgeNo);
                }
                else
                {
                    OverlapPart overlapPart;
                    AreaKind    areaKind;
                    if (existingAreaKind == AreaKind.BorderInside || existingAreaKind == AreaKind.AreaInsideCoverage100)
                    {
                        if (newEdgeAreaKind == AreaKind.BorderInside)
                        {
                            areaKind    = AreaKind.OverlapInside;
                            overlapPart = new OverlapPart(
                                existingEdgeNo, (existing_G == EdgeBmpLut.BORDER_INSIDE) ? AreaKind.OverlapInside : AreaKind.OverlapOutside,
                                newEdgeNo, (srcColor.G == EdgeBmpLut.BORDER_INSIDE) ? AreaKind.OverlapInside : AreaKind.OverlapOutside);
                        }
                        else
                        {
                            areaKind    = AreaKind.OverlapInside;
                            overlapPart = new OverlapPart(
                                existingEdgeNo, (existing_G == EdgeBmpLut.BORDER_INSIDE) ? AreaKind.OverlapInside : AreaKind.OverlapOutside,
                                newEdgeNo, (existing_G == EdgeBmpLut.BORDER_INSIDE) ? AreaKind.OverlapInside : AreaKind.OverlapOutside);
                        }
                    }
                    else
                    {
                        //existing is outside
                        if (newEdgeAreaKind == AreaKind.BorderInside)
                        {
                            areaKind    = AreaKind.OverlapInside;
                            overlapPart = new OverlapPart(
                                existingEdgeNo, (existing_G == EdgeBmpLut.BORDER_INSIDE) ? AreaKind.OverlapInside : AreaKind.OverlapOutside,
                                newEdgeNo, (existing_G == EdgeBmpLut.BORDER_INSIDE) ? AreaKind.OverlapInside : AreaKind.OverlapOutside);
                        }
                        else
                        {
                            areaKind    = AreaKind.OverlapOutside;
                            overlapPart = new OverlapPart(
                                existingEdgeNo, (existing_G == EdgeBmpLut.BORDER_INSIDE) ? AreaKind.OverlapInside : AreaKind.OverlapOutside,
                                newEdgeNo, (srcColor.G == EdgeBmpLut.BORDER_INSIDE) ? AreaKind.OverlapInside : AreaKind.OverlapOutside);
                        }
                    }


                    if (!_overlapParts.TryGetValue(overlapPart, out ushort found))
                    {
                        if (_overlapList.Count >= ushort.MaxValue)
                        {
                            throw new NotSupportedException();
                        }
                        //
                        ushort newPartNo = (ushort)_overlapList.Count;
                        _overlapParts.Add(overlapPart, newPartNo);
                        //
                        CornerList cornerList = new CornerList();
#if DEBUG
                        if (_overlapList.Count >= 388)
                        {
                        }
#endif

                        _overlapList.Add(cornerList);

                        cornerList.Append(existingEdgeNo);
                        cornerList.Append(newEdgeNo);
                        //set new color
                        *dstPtr = EdgeBmpLut.EncodeToColor(newPartNo, areaKind).ToARGB();
                    }
                    else
                    {
                        //set new color
                        *dstPtr = EdgeBmpLut.EncodeToColor(found, areaKind).ToARGB();
                    }
                }
            }
        }