Jump to content

Converting from PdnRegion to Direct2D geometry


null54

Recommended Posts

I am trying to convert my Content Aware Fill plugin to a BitmapEffect.

 

The effect creates a new PdnRegion representing the area outside of the selection that the plugin uses for sampling, this region is then converted to a mask bitmap.

 

private PdnRegion CreateSampleRegion()
{
    if (this.sampleSize < 0)
    {
        throw new ArgumentOutOfRangeException(nameof(this.sampleSize), "The sample size cannot be negative.");
    }

    Rectangle[] expandedScans = this.selection.GetRegionScansInt();

    for (int i = 0; i < expandedScans.Length; i++)
    {
        // Expand each scan rectangle by the specified number of pixels.
        expandedScans[i].Inflate(this.sampleSize, this.sampleSize);
    }

    PdnRegion sampleRegion;

    using (PdnGraphicsPath path = new())
    {
        path.FillMode = System.Drawing.Drawing2D.FillMode.Winding;
        path.AddRectangles(expandedScans);

        sampleRegion = new PdnRegion(path);
    }

    // Clip the expanded region to the surface bounds and exclude the original selection.
    // Excluding the original selection prevents sampling from the area that is to be replaced.
    sampleRegion.Intersect(this.sourceBounds);
    sampleRegion.Exclude(this.selection);

    return sampleRegion;
}

 

What would the equivalent IGeometry code be?

ID2D1GeometrySink does not have an AddRectangle method, so I assume that the rectangles would have to be converted to lines.

PdnSig.png

Plugin Pack | PSFilterPdn | Content Aware Fill | G'MICPaint Shop Pro Filetype | RAW Filetype | WebP Filetype

The small increase in performance you get coding in C++ over C# is hardly enough to offset the headache of coding in the C++ language. ~BoltBait

 

Link to comment
Share on other sites

You're using this for a mask bitmap? If so then I would create an Alpha8 bitmap and draw into it using FillRectangle() calls. Geometry operations can be quite slow for a complicated selection, but deviceContext.DrawRectangle(RectInt32.Inflate(rect, this.sampleSize, this.sampleSize), brush) should be reasonably fast for this scenario.

 

IImagingFactory imagingFactory = this.GetService<IImagingFactory>();
IDirect2DFactory d2dFactory = this.GetService<IDirect2DFactory>();

IBitmap<ColorAlpha8> maskBitmap = imagingFactory.CreateBitmap<ColorAlpha8>(width, height);

using (IDeviceContext deviceContext = d2dFactory.CreateBitmapDeviceContext(maskBitmap))
{
    using ISolidColorBrush fillBrush = deviceContext.CreateSolidColorBrush(Colors.White);
    using (deviceContext.UseBeginDraw())
    {
        deviceContext.Clear(Colors.Black);
        foreach (RectInt32 rect in selectionRects)
        {
            deviceContext.FillRectangle(RectInt32.Inflate(rect, this.sampleSize, this.sampleSize), fillBrush);
        }
    }
}

 

The Paint.NET Blog: https://blog.getpaint.net/

Donations are always appreciated! https://www.getpaint.net/donate.html

forumSig_bmwE60.jpg

Link to comment
Share on other sites

1 hour ago, Rick Brewster said:

You're using this for a mask bitmap?

 

Yes, but my mask bitmap is custom MaskSurface type a not a WIC Bitmap. This avoid having to lock and unlock the bitmap when using it.

 

One issue that I see with your proposed code is that the original selection rectangle is not excluded, but that could be solved with a second FillRectangle call.

Does FillRectangle already handle clipping the rectangle to the image bounds?

 

I also need the bounds of the expanded region, but I can get that from the scan rectangles.

PdnSig.png

Plugin Pack | PSFilterPdn | Content Aware Fill | G'MICPaint Shop Pro Filetype | RAW Filetype | WebP Filetype

The small increase in performance you get coding in C++ over C# is hardly enough to offset the headache of coding in the C++ language. ~BoltBait

 

Link to comment
Share on other sites

@Rick Brewster I got your FillRectange code sample working, but Direct2D ignores the second FilllRectangle call that I am trying to use to mask out the selection.

 

IBitmap<ColorAlpha8> sourceMask = this.imagingFactory.CreateBitmap<ColorAlpha8>(this.environment.Document.Size);
IDirect2DFactory d2dFactory = this.serviceProvider.GetService<IDirect2DFactory>();

RectInt32 maskBounds = RectInt32.Empty;

using (PaintDotNet.Direct2D1.IDeviceContext deviceContext = d2dFactory.CreateBitmapDeviceContext(this.sourceMask))
{
    using (ISolidColorBrush selectedRegionBrush = deviceContext.CreateSolidColorBrush(Colors.White))
    using (ISolidColorBrush unselectedRegionBrush = deviceContext.CreateSolidColorBrush(Colors.TransparentBlack))
    using (deviceContext.UseBeginDraw())
    {
        IReadOnlyList<RectInt32> selectionRects = this.environment.Selection.RenderScans;
        RectInt32 surfaceBounds = this.sourceMask.Bounds();

        deviceContext.Clear(Colors.TransparentBlack);
        foreach (RectInt32 rect in selectionRects)
        {
            RectInt32 expandedRect = RectInt32.Inflate(rect, this.sampleSize, this.sampleSize);
            RectInt32 selectedArea = RectInt32.Intersect(expandedRect, surfaceBounds);

            deviceContext.FillRectangle(selectedArea, selectedRegionBrush);
            // Exclude the pixels in the original selection.
            // Direct2D ignores this command.
            deviceContext.FillRectangle(rect, unselectedRegionBrush);

            maskBounds = RectInt32.Union(maskBounds, selectedArea);
        }
    }
}

 

The alternative approach I have been considering is drawing the mask outline using 4 rectangles, but I would like to avoid that if possible.

PdnSig.png

Plugin Pack | PSFilterPdn | Content Aware Fill | G'MICPaint Shop Pro Filetype | RAW Filetype | WebP Filetype

The small increase in performance you get coding in C++ over C# is hardly enough to offset the headache of coding in the C++ language. ~BoltBait

 

Link to comment
Share on other sites

3 hours ago, null54 said:

@Rick Brewster I got your FillRectange code sample working, but Direct2D ignores the second FilllRectangle call that I am trying to use to mask out the selection.

That's because it's transparent and blending is enabled by default -- you need to set the PrimitiveBlend property to PrimitiveBlend.Copy

The Paint.NET Blog: https://blog.getpaint.net/

Donations are always appreciated! https://www.getpaint.net/donate.html

forumSig_bmwE60.jpg

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...