Jump to content
How to Install Plugins ×

Skew v1.0 (with source code)


BoltBait

Recommended Posts

BoltBait's Skew Plugin v1.0

 

Once installed, find Skew Horizontal and Skew Vertical in the Effects > Distort menu:

 

SkewUI.png

Skew Vertical pretty much looks the same.

 

Download:

 

Until I add this to my latest plugin pack, you can download it here...

 

Skew.zip

 

To install this plugin, download skew.zip to your desktop, unzip all files to your desktop, and run the Install_Skew.bat file to install the Skew.dll file to your Paint.NET Effects directory.

 

Source Code:

 

Spoiler


// Name: Skew Horizontal
// Submenu: Distort
// Author: BoltBait
// Title: BoltBait's Skew - Horizontal v1.0
// Version: 1.0
// Desc: Skew selection
// Keywords: skew
// URL: https://BoltBait.com/pdn
// Force Aliased Selection
#region UICode
IntSliderControl Amount1 = 0; // [-50,50] 
CheckboxControl Amount5 = true; // Fine Control
CheckboxControl Amount4 = true; // Clamp to Selection
CheckboxControl Amount2 = false; // Nearest Neighbor
CheckboxControl Amount3 = false; // Wrapped
#endregion

Surface selectionSurface = null;

void PreRender(Surface dst, Surface src)
{
    // Thanks to toe_head2001 for this PreRender code
    // which is useful for clamping to selection
    Rectangle selection = this.EnvironmentParameters.SelectionBounds;
    if (selectionSurface == null || selectionSurface.Size != selection.Size)
    {
        selectionSurface?.Dispose(); selectionSurface = null;
        selectionSurface = new Surface(selection.Size);
    }
    selectionSurface.CopySurface(src, Point.Empty, selection);
}

void Render(Surface dst, Surface src, Rectangle rect)
{
    int Step = Amount1;
    if (!Amount5)
    {
        Step *= 10;
    }
    Rectangle selection = EnvironmentParameters.SelectionBounds;
    float CenterY = ((selection.Bottom - selection.Top) / 2f) + selection.Top;
    for (int y = rect.Top; y < rect.Bottom; y++)
    {
        if (IsCancelRequested) return;
        float p = (CenterY - y) / (CenterY - selection.Top);
        for (int x = rect.Left; x < rect.Right; x++)
        {
            float XOffset = (-Step) * p;
            if (Amount3)
            {
                if (Amount2)
                {
                    if (Amount4)
                    {
                        dst[x,y] = selectionSurface.GetBilinearSampleWrapped((int)(x+XOffset-selection.Left), y-selection.Top);
                    }
                    else
                    {
                        dst[x,y] = src.GetBilinearSampleWrapped((int)(x+XOffset), y);
                    }
                }
                else
                {
                    if (Amount4)
                    {
                        dst[x,y] = selectionSurface.GetBilinearSampleWrapped(x+XOffset-selection.Left, y-selection.Top);
                    }
                    else
                    {
                        dst[x,y] = src.GetBilinearSampleWrapped(x+XOffset, y);
                    }
                }
            }
            else
            {
                if (Amount2)
                {
                    if (Amount4)
                    {
                        dst[x,y] = selectionSurface.GetBilinearSampleClamped((int)(x+XOffset-selection.Left), y-selection.Top);
                    }
                    else
                    {
                        dst[x,y] = src.GetBilinearSampleClamped((int)(x+XOffset), y);
                    }
                }
                else
                {
                    if (Amount4)
                    {
                        dst[x,y] = selectionSurface.GetBilinearSampleClamped(x+XOffset-selection.Left, y-selection.Top);
                    }
                    else
                    {
                        dst[x,y] = src.GetBilinearSampleClamped(x+XOffset, y);
                    }
                }
            }
        }
    }
}

 

 

Enjoy. 😎 :beer:

  • Like 3
  • Upvote 2
Link to comment
Share on other sites

  • BoltBait changed the title to Skew v1.0 (with source code)
  • 2 weeks later...

BoltBait  I Really like this.  I don't understand it, but I do like the pretty skews and skew-ers  and all.  I have enclosed a mighty work of artistic doggerel, to show you.

p.s. pay no attention to me, it is a great bit of program writer-ry  an all.

bolt-bait-skew-repair.jpg

  • Like 1

           Scooter

  Age is only  a number --in my case a Really BIG number, but there you have it

When the prefect paint.net image is created, I will still be wondering "How they Do that?"- sigh☺️

       

Link to comment
Share on other sites

  • 4 months later...

She want to define the skew based on angle (I think that is what she wants).

 

You can actually find the skew displacement at the end points based on angle by this formula (for horizontal skew):

 

#hh is Height - 1 or end y indice#

const dist=hh/2*tan(ang);

 

Edited by Reptillian

G'MIC Filter Developer

Link to comment
Share on other sites

10 hours ago, mszlazak said:

To horizontally "de-skew" this chart one needs more range than what your plugin provides.

 

I'm not seeing a problem with your chart. Are you seeing an optical illusion that bends the top of the chart downwards?

 

I tried drawing a purple rectangle around the chart, and it seems to be orthogonal (which actually surprised me).

 

Skew-or-not-skew.png

 

Link to comment
Share on other sites

On 4/16/2021 at 10:22 AM, BoltBait said:

 

Could you show me what you're trying to accomplish?  (Even a rough sketch would be fine.)

I had to uncheck your "Fine control" to get the extent of horizontal skewing i needed. Here are the before and after pictures. For example, the red dots should be vertical and all at the 2e-17 V/NBC value afterwards.

 

Screenshot (1818)-2.png

Screenshot (1818)-3.png

Link to comment
Share on other sites

On 4/16/2021 at 10:22 AM, BoltBait said:

 

Could you show me what you're trying to accomplish?  (Even a rough sketch would be fine.)

 

Addendum to my last post. I forgot to mention a request that would be helpful to control skewing. Would you consider a skew angle input since your slider scale doesn't seem to be in degree/radian units and allows decimals for finer control of angles? One could skew these curve traces to a better precision needed to extract data points from the curve with extraction software (e.g. webplotdigitizer) that is orthogonal axis based. There are probably other use cases besides this but that is currently mine. Thanks.

Link to comment
Share on other sites

1 hour ago, mszlazak said:

 

Addendum to my last post. I forgot to mention a request that would be helpful to control skewing. Would you consider a skew angle input since your slider scale doesn't seem to be in degree/radian units and allows decimals for finer control of angles? One could skew these curve traces to a better precision needed to extract data points from the curve with extraction software (e.g. webplotdigitizer) that is orthogonal axis based. There are probably other use cases besides this but that is currently mine. Thanks.

 

I'm actually working on porting my gmic skew filter into skew plugin using @BoltBait original code as a starting point. That filter has angle input.

 

EDIT: I got something here though I don't know how to work with this too well. Maybe the author will improve this code.

Spoiler

// Name: Skew Horizontal
// Submenu: Distort
// Author: BoltBait
// Title: BoltBait's Skew - Horizontal v1.0
// Version: 1.0
// Desc: Skew selection
// Keywords: skew
// URL: https://BoltBait.com/pdn
// Force Aliased Selection
#region UICode
DoubleSliderControl degree = 0; // [-89.99,89.99] Angle
DoubleSliderControl d_position = 50; // [0,100] Position(%)
CheckboxControl selection_clamp = true; // Clamp to Selection
CheckboxControl nearest_mode = false; // Nearest Neighbor
CheckboxControl wraparound_mode = false; // Wrapped
#endregion

Surface selectionSurface = null;

void PreRender(Surface dst, Surface src)
{
    // Thanks to toe_head2001 for this PreRender code
    // which is useful for clamping to selection
    Rectangle selection = this.EnvironmentParameters.SelectionBounds;
    if (selectionSurface == null || selectionSurface.Size != selection.Size)
    {
        selectionSurface?.Dispose(); selectionSurface = null;
        selectionSurface = new Surface(selection.Size);
    }
    selectionSurface.CopySurface(src, Point.Empty, selection);
}

void Render(Surface dst, Surface src, Rectangle rect)
{
    double ang = ( degree / 180 ) * Math.PI;
    float position = (float)(d_position / 100);

    Rectangle selection = EnvironmentParameters.SelectionBounds;

    int sel_diff = selection.Bottom - selection.Top ;
    float sel_py = selection.Top * (1 - position) + selection.Bottom * position;
    double sel_dist = (double)(sel_diff) * Math.Tan(ang);

    int rect_diff = rect.Bottom - rect.Top;
    float rect_py = rect.Top * (1 - position) + rect.Bottom * position ;
    double rect_dist = (double)(rect_diff) * Math.Tan(ang);

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

        float sel_pos = (y - sel_py) / (float) (sel_diff);
        float sel_off = sel_pos * (float)(sel_dist) ;
        float rect_pos = (y - rect_py) / (float) (rect_diff);
        float rect_off = rect_pos * (float)(rect_dist) ;

        for (int x = rect.Left; x < rect.Right; x++)
        {
            if (wraparound_mode)
            {
                if (nearest_mode)
                {
                    if (selection_clamp)
                    {
                        dst[x,y] = selectionSurface.GetBilinearSampleWrapped((int)(x+sel_off-selection.Left), y-selection.Top);
                    }
                    else
                    {
                        dst[x,y] = src.GetBilinearSampleWrapped((int)(x+rect_off), y);
                    }
                }
                else
                {
                    if (selection_clamp)
                    {
                        dst[x,y] = selectionSurface.GetBilinearSampleWrapped(x+sel_off-selection.Left, y-selection.Top);
                    }
                    else
                    {
                        dst[x,y] = src.GetBilinearSampleWrapped(x+rect_off, y);
                    }
                }
            }
            else
            {
                if (nearest_mode)
                {
                    if (selection_clamp)
                    {
                        dst[x,y] = selectionSurface.GetBilinearSampleClamped((int)(x+sel_off-selection.Left), y-selection.Top);
                    }
                    else
                    {
                        dst[x,y] = src.GetBilinearSampleClamped((int)(x+rect_off), y);
                    }
                }
                else
                {
                    if (selection_clamp)
                    {
                        dst[x,y] = selectionSurface.GetBilinearSampleClamped(x+sel_off-selection.Left, y-selection.Top);
                    }
                    else
                    {
                        dst[x,y] = src.GetBilinearSampleClamped(x+rect_off, y);
                    }
                }
            }
        }
    }
}

 

 

Edited by Reptillian

G'MIC Filter Developer

Link to comment
Share on other sites

3 hours ago, Reptillian said:

 

I'm actually working on porting my gmic skew filter into skew plugin using @BoltBait original code as a starting point. That filter has angle input.

 

EDIT: I got something here though I don't know how to work with this too well. Maybe the author will improve this code.

  Reveal hidden contents


// Name: Skew Horizontal
// Submenu: Distort
// Author: BoltBait
// Title: BoltBait's Skew - Horizontal v1.0
// Version: 1.0
// Desc: Skew selection
// Keywords: skew
// URL: https://BoltBait.com/pdn
// Force Aliased Selection
#region UICode
DoubleSliderControl degree = 0; // [-89.99,89.99] Angle
DoubleSliderControl d_position = 50; // [0,100] Position(%)
CheckboxControl selection_clamp = true; // Clamp to Selection
CheckboxControl nearest_mode = false; // Nearest Neighbor
CheckboxControl wraparound_mode = false; // Wrapped
#endregion

Surface selectionSurface = null;

void PreRender(Surface dst, Surface src)
{
    // Thanks to toe_head2001 for this PreRender code
    // which is useful for clamping to selection
    Rectangle selection = this.EnvironmentParameters.SelectionBounds;
    if (selectionSurface == null || selectionSurface.Size != selection.Size)
    {
        selectionSurface?.Dispose(); selectionSurface = null;
        selectionSurface = new Surface(selection.Size);
    }
    selectionSurface.CopySurface(src, Point.Empty, selection);
}

void Render(Surface dst, Surface src, Rectangle rect)
{
    double ang = ( degree / 180 ) * Math.PI;
    float position = (float)(d_position / 100);

    Rectangle selection = EnvironmentParameters.SelectionBounds;

    int sel_diff = selection.Bottom - selection.Top ;
    float sel_py = selection.Top * (1 - position) + selection.Bottom * position;
    double sel_dist = (double)(sel_diff) * Math.Tan(ang);

    int rect_diff = rect.Bottom - rect.Top;
    float rect_py = rect.Top * (1 - position) + rect.Bottom * position ;
    double rect_dist = (double)(rect_diff) * Math.Tan(ang);

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

        float sel_pos = (y - sel_py) / (float) (sel_diff);
        float sel_off = sel_pos * (float)(sel_dist) ;
        float rect_pos = (y - rect_py) / (float) (rect_diff);
        float rect_off = rect_pos * (float)(rect_dist) ;

        for (int x = rect.Left; x < rect.Right; x++)
        {
            if (wraparound_mode)
            {
                if (nearest_mode)
                {
                    if (selection_clamp)
                    {
                        dst[x,y] = selectionSurface.GetBilinearSampleWrapped((int)(x+sel_off-selection.Left), y-selection.Top);
                    }
                    else
                    {
                        dst[x,y] = src.GetBilinearSampleWrapped((int)(x+rect_off), y);
                    }
                }
                else
                {
                    if (selection_clamp)
                    {
                        dst[x,y] = selectionSurface.GetBilinearSampleWrapped(x+sel_off-selection.Left, y-selection.Top);
                    }
                    else
                    {
                        dst[x,y] = src.GetBilinearSampleWrapped(x+rect_off, y);
                    }
                }
            }
            else
            {
                if (nearest_mode)
                {
                    if (selection_clamp)
                    {
                        dst[x,y] = selectionSurface.GetBilinearSampleClamped((int)(x+sel_off-selection.Left), y-selection.Top);
                    }
                    else
                    {
                        dst[x,y] = src.GetBilinearSampleClamped((int)(x+rect_off), y);
                    }
                }
                else
                {
                    if (selection_clamp)
                    {
                        dst[x,y] = selectionSurface.GetBilinearSampleClamped(x+sel_off-selection.Left, y-selection.Top);
                    }
                    else
                    {
                        dst[x,y] = src.GetBilinearSampleClamped(x+rect_off, y);
                    }
                }
            }
        }
    }
}

 

 

Great! Also notice that when i skewed my original picture, the plugin did not enlarge the size of the resulting image automatically but instead cropped off parts of the curves on the right. Maybe an automatic enlargement option would be handy. Thanks.

Link to comment
Share on other sites

13 minutes ago, mszlazak said:

Great! Also notice that when i skewed my original picture, the plugin did not enlarge the size of the resulting image automatically but instead cropped off parts of the curves on the right. Maybe an automatic enlargement option would be handy. Thanks.

That's not possible with PDN. Also skewing with a large angle means that the enlarged picture would very big easily. If using 80 degree, the enlarged picture would add 5k to both side for 1k picture.

G'MIC Filter Developer

Link to comment
Share on other sites

10 hours ago, Reptillian said:

That's not possible with PDN. Also skewing with a large angle means that the enlarged picture would very big easily. If using 80 degree, the enlarged picture would add 5k to both side for 1k picture.

To bad about the PDN. I would imagine that even a limited enlargement would still help in a lot of cases. Anyway, thanks for your update and look forward to using the upgrade.

Edited by mszlazak
Link to comment
Share on other sites

  • 3 years later...

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...