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)); } }
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(); } } } }