xod

Unfinished plugins

Recommended Posts

Just now, MJW said:

 

Assuming dst can't be written in PreRender, you've got to figure out a way to do it in Render. It that's impossible, you need to use an auxiliary surface, do the rendering to it in PreRender, and cop the resuts to dst in Render. That's not ideal, but it's necessary for algorithms that only go in the source-to-destination direction; that is, algorithms where you can't take the destination coordinates, and figure out what color should be there.

 

In other words, I have to start over again. Okay, I'll attempt that some time later.

Share this post


Link to post
Share on other sites
21 minutes ago, MJW said:

As far as I know, you can only write to the destination buffer in Render, and then only within the Rectangle-Of-Interest.

 

You can write to the destination buffer in both PreRender and Render, but you have to restrict your writes to the selection bounds in both cases.

Writing outside of the selection breaks the undo command.

 

2 minutes ago, MJW said:

It that's impossible, you need to use an auxiliary surface, do the rendering to it in PreRender, and cop the resuts to dst in Render. That's not ideal, but it's necessary for algorithms that only go in the source-to-destination direction; that is, algorithms where you can't take the destination coordinates, and figure out what color should be there.

 

After looking at the code, that is what I would suggest. 

I will test that after I update CodeLab.

Share this post


Link to post
Share on other sites
14 minutes ago, null54 said:

You can write to the destination buffer in both PreRender and Render, but you have to restrict your writes to the selection bounds in both cases.

Writing outside of the selection breaks the undo command.

 

I didn't know that. Or if I knew it at one time, I'd forgotten it.

 

In the plugin under discussion, I doubt writing to dst in PreRender() makes sense, anyhow. If the algorithm itself can be done in Render(), so can the clears. I doubt the clears in PreRender() even make sense. I bet they're just a band aid that reduces what ever problem Reptillian is seeing.

 

 

 

 

Share this post


Link to post
Share on other sites
18 minutes ago, Reptillian said:

In other words, I have to start over again. Okay, I'll attempt that some time later.

 

Not really start over. It would mostly be a matter of moving code from one routine to another.

 

I wish I understood better what the code is doing; but I have great difficulty reading poorly-formatted code. (In fact, I have difficulty reading code not formatted in by preferred indentation style.)

Share this post


Link to post
Share on other sites
19 minutes ago, null54 said:

You can write to the destination buffer in ...PreRender

 

Don't do that.

 

I used to think it was ok. Now, I don't.

 

Currently CodeLab allows you do design plugins this way using the File > New templates... I will be correcting this for the next release.

 

Do not use DST as another scratch surface.  Stuff doesn't work right when you do this.

 

 

 

Share this post


Link to post
Share on other sites

After looking through the code more carefully, I notice:

dst[fx,y] = src[x,y];

That's a sure sign the call to Render() may write outside its ROI, which shouldn't be done. Either the algorithm needs to be redone (unlikely), or you need to use an aux surface.

 

Usually, the Render() code when using an aux surface is trivial. Inside the render loops is simply: dst[x, y] = aux[x, y].

 

In some cases, it's possible to save some of the computations for Render() to do. It's good to do as much in Render as possible, since it's done in parallel, where PreRender() is not. Also Render() properly updates the progress bar.

 

Also, aux doesn't have to be an actual surface. In many cases a two-dimensional array of ColorBgras works just as well, and doesn't need to be carefully disposed of. Sometimes a 2D array of floats or ints is better, leaving it to Render() to compute the color from the value in the array. For this algorithm, it could be an array of 2D indexes that tell where in the source buffer the proper color is located. (That's just an example, not a recommendation.)

 

 

Share this post


Link to post
Share on other sites

@MJW Your solution worked!

 

Releasing the code here for testing purpose.

 

 

// Name: Axis-Based Shift Grouped Pixel to Boundary
// Submenu: Effects / Distortion
// Author: Reptorian
// Title: Axis-Based Shift Grouped Pixel to Boundary
// Version: 1.0
// Desc: Extended version of Gravity by MadJik. Converted from G'MIC-QT rep_sptbwgp cli filter which is also created by Reptorian.
// Keywords: Gravity
// URL: https://forums.getpaint.net/profile/85868-reptillian/
// Help:
#region UICode
DoubleSliderControl positionperc = -100; // [-100,100] Position of Pixels (%)
DoubleSliderControl influenceperc = 100; // [0,100] Influence Factor (%)
IntSliderControl alpha_threshold = 255; // [1,255] Alpha Threshold
ListBoxControl axis = 1; // Axis|Horizontal|Vertical
#endregion

 

int[] rowNonZeroAlphas = null;
int[] columnNonZeroAlphas = null;
ColorBgra[,] altsrf = null;

 

void PreRender(Surface dst, Surface src)
{
    int w=src.Width,h=src.Height;
    columnNonZeroAlphas = new int[w];rowNonZeroAlphas = new int[h];
    altsrf = new ColorBgra[w,h];
    int non_alpha_count = 0;
    int tx,ty,nx,ny,fx,fy,N;
    double tny,tnx;
    
    double position = (positionperc / 100 + 1) / 2;
    double influence_factor = influenceperc / 100;
    double invert_factor = 1 - influence_factor;
    
    if (axis==1)
    {
        for (int x = 0 ; x < w ; x++)
        {
            non_alpha_count = 0 ;            
            for (int y = 0; y < h; y++){if (src[x,y].A >= alpha_threshold){non_alpha_count++;}}
            columnNonZeroAlphas[x] = non_alpha_count ;
        }
        
        for (int x = 0 ; x < w ; x++)
        {
            ny = h - 1;
            if (columnNonZeroAlphas[x] != 0)
            {
                N = columnNonZeroAlphas[x];
                ty = h - N;
                ny-=(int)(Math.Round(ty * position));
                for (int y = h - 1 ; y >= 0 ; y--)
                {
                    if (N != 0)
                    {
                        if (src[x,y].A >= alpha_threshold)
                        {
                            tny = influence_factor * (double)(ny) + invert_factor * (double)(y);
                            
                            if (position == 1)
                            { fy = (int)(Math.Floor(tny)) ; }
                            else
                            { fy = (int)(Math.Ceiling(tny)) ; }
                            
                            N--;
                            altsrf[x,fy] = src[x,y] ;
                            ny--;                            
                        }
                    }
                    else {break;}
                }
            }
        }
    }
    else
    {
        for (int y = 0 ; y < h ; y++)
        {
            non_alpha_count = 0 ;            
            for (int x = 0; x < w; x++){if (src[x,y].A >= alpha_threshold){non_alpha_count++;}}
            rowNonZeroAlphas[y] = non_alpha_count ;
        }
        
        for (int y = 0 ; y < h ; y++)
        {
            nx = 0;
            if (rowNonZeroAlphas[y] != 0)
            {
                N = rowNonZeroAlphas[y];
                tx = w - N;
                nx+=(int)(Math.Round(tx * position));
                for (int x = 0 ; x < w ; x++)
                {
                    if (N != 0)
                    {
                        if (src[x,y].A >= alpha_threshold)
                        {
                            tnx = influence_factor * (double)(nx) + invert_factor * (double)(x);
                            
                            if (position == 1)
                            { fx = (int)(Math.Ceiling(tnx)) ; }
                            else
                            { fx = (int)(Math.Floor(tnx)) ; }
                            
                            N--;
                            altsrf[fx,y] = src[x,y] ;
                            nx++;                            
                        }
                    }
                    else {break;}
                }
            }
        }
    }
}

 

void Render(Surface dst, Surface src, Rectangle rect)
{
    Rectangle selection = EnvironmentParameters.SelectionBounds;
    for (int y = rect.Top; y < rect.Bottom; y++)
    {
        if (IsCancelRequested) return;
        for (int x = rect.Left; x < rect.Right; x++)
        {
            dst[x,y] = altsrf[x,y] ;
        }
    }
}

 

 

 

Edited by Reptillian

Share this post


Link to post
Share on other sites
4 hours ago, null54 said:

Writing outside of the selection breaks the undo command.

 

That was fixed in paint.net 4.1.6.

Obviously, writing outside of the selection is still a bad practice, and just wastes compute resources.

Share this post


Link to post
Share on other sites
2 hours ago, toe_head2001 said:

Obviously, writing outside of the selection is still a bad practice, and just wastes compute resources.

 

Would you expand of this. Do you just mean the write has no effect, so the effort is wasted, or does it waste resources in some other way? If it just wastes the effort to compute the unused pixels, is that behavior that's intended to remain that way, so that it can be relied on? There are probably lots of cases where it would be convenient to clip at most to the selection's bounding rectangle, and not worry that there will be some wasted computations if the user happens to have an elliptical selection.

 

Also, does  is writing outside the ROI in Render() allowed, in the sense that it will properly write into pixels if they're selected? To paraphrase Ghostbusters, are the ROI bounds more guidelines than rules?

Share this post


Link to post
Share on other sites
26 minutes ago, MJW said:

Do you just mean the write has no effect, so the effort is wasted

 

Yeah, pretty much.

 

28 minutes ago, MJW said:

is that behavior that's intended to remain that way, so that it can be relied on?

 

I believe so. If pixels outside of the selection are modified, that would be considered a bug.

 

30 minutes ago, MJW said:

Also, does writing outside the ROI in Render() allowed, in the sense that it will properly write into pixels if they're selected?

 

Yes, it will work. Using the ROI is, of course, the recommended route, and probably the most efficient.

  • Like 1

Share this post


Link to post
Share on other sites

Swirl can be found in Effects > Distort submenu.

Spoiler

// Name: Swirl
// Submenu: Distort
// Author: NSD
// Title: Swirl
// Version: 1.0
// Desc:
// Keywords:
// URL:

#region UICode
DoubleSliderControl intensity = 0.0; // [-1,1] Amount / Direction
DoubleSliderControl zoom = 0; // [0.0,0.999] Zoom
PanSliderControl pan = Pair.Create(0.0, 0.0); // Center
#endregion

void Render(Surface dst, Surface src, Rectangle rect)
{
    int x, y;
    double offx, offy, dx, dy, th, rd, ct, offset, xPan, yPan, zm;
    ColorBgra CurrentPixel;

    Rectangle sel = EnvironmentParameters.SelectionBounds;
    dst.CopySurface(src, rect.Location, rect);

    ct = intensity * 0.2;
    offset = Math.PI / 2.0;

    xPan = (pan.First + 1) / 2 * (sel.Right - sel.Left) + sel.Left;
    yPan = (pan.Second + 1) / 2 * (sel.Bottom - sel.Top) + sel.Top;

    zm  = 1 - zoom;

    for (y = rect.Top; y < rect.Bottom; y++)
    {
        if (IsCancelRequested) return;

        for (x = rect.Left; x < rect.Right; x++)
        {
            dx = x - xPan;
            dy = y - yPan;

            rd = Math.Sqrt((dx * dx + dy * dy) * zm);
            th = Math.Atan2(dx, -dy) - offset;

            offx = rd * Math.Cos(th + ct * rd);
            offy = rd * Math.Sin(th + ct * rd);

            offx += xPan;
            offy += yPan;

            CurrentPixel = src.GetBilinearSampleWrapped((float)offx, (float)offy);
            dst[x,y] = CurrentPixel;
        }
    }
}

 

 

 

Swirl.zip

  • Like 1
  • Upvote 1

Share this post


Link to post
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.