MJW

Offset Alternating Stripes

Recommended Posts

Here is the plugin you've been waiting for!!!
 
Just kidding. This is a plugin I developed for a specific purpose, that I decided to release in case anyone has a use for it.
 
As the Help menu says:
 
Offset Alternating Stripes offsets alternating stripes of pixels selected distances.

Stripe Width is the width of the stripes in pixels.
Odd Offset is the number of pixels to offset the first stripe and all other odd numbered stripes.
Even Offset is the number of pixels to offset the second stripe and all other even numbered stripes.
Stripe Phase moves the point at which the first stripe begins.
Stripe Angle rotates the direction of the stripes.
Edge Behavior specifies how to handle points that are shifted beyond the edge of the image. The options are None, Wrap, or Clamp.
Quality specifies the amount of antialiasing to use. A value of one disables antialiasing. Higher values increase the number of samples per pixel. Generally, no antialiasing is needed when the Width and Offsets are integer values.

 

Here is the user interface:

 

OffsetAlternatingStripesUI_zpsw0pdvwey.p

 

Here is an example of the plugin applied to an image:

OfsetAlternatingStripesExample_zpsk8mluk

 

Here is the CodeLab source:

Hidden Content:
// Name: Offset Alternating Stripes
// Submenu: Distort
// Author: MJW
// Title: Offset Alternating Stripes
// Version: 1.0.*
// Desc: Offsets alternating stripes of a selected width by specified amounts
// Keywords: offset alternating stripes
// URL: 
// Help:
#region UICode
DoubleSliderControl Amount1 = 20; // [1,1000] Stripe Width
DoubleSliderControl Amount2 = 0; // [-1000,1000] Odd Stripe Offset
DoubleSliderControl Amount3 = 20; // [-1000,1000] Even Stripe Offset
DoubleSliderControl Amount4 = 0.0; // [-0.5,0.5] Stripe Phase
AngleControl Amount5 = 0; // [-180,180] Stripe Angle
RadioButtonControl Amount6 = 0; // [1] Edge Behavior|None|Wrap|Clamp
IntSliderControl Amount7 = 1; // [1,5] Quality
#endregion


Surface Src;
int edgeBehavior;
float Syx, Syy, Syw;
float evenX, evenY, oddX, oddY;
void Render(Surface dst, Surface src, Rectangle rect)
{
    Src = src;
    
    bool antialias = (Amount7 != 1);
    if (antialias)
        SetupForSubpixels(Amount7, Amount7);
        
    edgeBehavior = Amount6;
    float width = (float)Amount1;     
    
    double ang = (Math.PI / 180.0) * Amount5;
    float Mxx = (float)Math.Cos(ang);
    float Myx = (float)Math.Sin(ang);
    float Mxy = -Myx;
    float Myy = Mxx;
       
    // Set up the offset distances. I tend to think of the initial line is even,
    // but then I can't really call it the first line.
    oddX = Mxx * (float)Amount2;
    oddY = Mxy * (float)Amount2;
    evenX = Mxx * (float)Amount3;
    evenY = Mxy * (float)Amount3;
    
    // Calculate the values used to calculate the stripe number. Only the low bit of the
    // result matters. It's used to determine if it's and odd or even stripe.
    float widthRecip = 1.0f / width;
    Syx = widthRecip * Myx;
    Syy = widthRecip * Myy;
    
    // Calculate the offset. Include the phase. Add a large number to always make
    // the result positive. Use an odd number so the first line is odd. Add half the
    // width-scaling value for rounding.
    Syw = (float)Amount4 + 40001.0f + 0.5f * widthRecip;
    
    for (int y = rect.Top; y < rect.Bottom; y++)
    {
        if (IsCancelRequested) return;
        float fy = (float)y;
        for (int x = rect.Left; x < rect.Right; x++)
        {
            float fx = (float)x;
            if (!antialias)
                dst[x, y] = GetTransformedPixel(fx, fy);
            else
                dst[x, y] = GetTransformedPixelAA(fx, fy);
        }
    }
}

ColorBgra GetTransformedPixel(float fx, float fy)
{
    // Get the rotated, scaled, and offset Y distance as an integer stripe number.
    int dy = (int)(Syx * fx + Syy * fy + Syw);
    if ((dy & 1) == 0)
    {
        fx -= evenX; fy -= evenY;
    }
    else
    {
         fx -= oddX; fy -= oddY;
    }
    if (edgeBehavior == 0)
        return Src.GetBilinearSample(fx, fy);
    else if (edgeBehavior == 1)
        return Src.GetBilinearSampleWrapped(fx, fy);
    else
        return Src.GetBilinearSampleClamped(fx, fy);      
}

ColorBgra GetTransformedPixelAA(float fx, float fy)
{
    int b = 0, g = 0, r = 0, a = 0;
    fy += ssYStart;
    float bx = fx + ssXStart;
    
    for (int iy = 0; iy < ssYSamples; iy++)
    {
        fx = bx;
        for (int ix = 0; ix < ssXSamples; ix++)
        {
            ColorBgra pixel = GetTransformedPixel(fx, fy);
            int alpha = pixel.A;
            if (alpha != 0)
            {
                b += alpha * pixel.B;
                g += alpha * pixel.G;
                r += alpha * pixel.R;
                a += alpha;
            }
            fx += ssXStep;    
        }
        fy += ssYStep;
    }

    if (a == 0)
    {
        return ColorBgra.Transparent;
    }
    else
    {
        // Compute the (rounded) averages.
        int twiceA = a << 1;
        b = ((b << 1) + a) / twiceA;
        g = ((g << 1) + a) / twiceA;
        r = ((r << 1) + a) / twiceA;
        a = (twiceA + ssSamples) / ssTwiceSamples;
        return ColorBgra.FromBgra((byte)b, (byte)g, (byte)r, (byte)a);
    }
}

int ssXSamples, ssYSamples, ssSamples, ssTwiceSamples;
float ssXStart, ssYStart, ssXStep, ssYStep;
void SetupForSubpixels(int xSamples, int ySamples)
{
    ssXSamples = xSamples;
    ssYSamples = ySamples;
    ssSamples = xSamples * ySamples;
    ssTwiceSamples = ssSamples << 1;
    ssXStep = 1.0f / (float)xSamples;
    ssYStep = 1.0f / (float)ySamples;
    ssXStart = -0.5f * (1.0f - ssXStep);
    ssYStart = -0.5f * (1.0f - ssYStep);  
}



 

Here is the plugin: OffsetAlternatingStripes.zip

 

Maximilian has a 3.5.11 build.

 

EDIT: Fixed double-stripe-for-negative-angles bug, and a multi-thread bug caused be forgetting to delete a line of code.

  • Upvote 8

Share this post


Link to post
Share on other sites

Hi MJW!  I see many possibilities for this plugin, however, I was playing around with it and noticed this:

 

offsetstripes_01.jpg

 

 

It works beautifully when the stripe angle is horizontal. If you apply any kind of angle, you get what's shown above.  Any way to fix it?

 

Other than that, the plugin is great.  Thanks so much!    :)

Edited by lynxster4
re-hosted image
  • Upvote 2

Share this post


Link to post
Share on other sites

lynxster4, off hand, I don't know what causes that, but I'm sure it can be fixed. I'll look into it. I suspect there are cases where the line-number calculation produces a negative value prior to integer truncation.

 

EDIT: I'm almost certain that's the problem. I'll try to fix it later tonight.

Share this post


Link to post
Share on other sites

I  believe I've fixed the bug. I also discovered and fixed a multi-threading bug caused by moving a line of code without deleting the original line.

  • Upvote 2

Share this post


Link to post
Share on other sites

 

EDIT: Fixed double-stripe-for-negative-angles bug, and a multi-thread bug caused be forgetting to delete a line of code.

 

Confirmed fixed!   Thanks MJW...you're the best!    :P

Share this post


Link to post
Share on other sites

MJW!

 

Thank you so much for the plugin and for your effort.   :cake:  :coffee:  :cookie:

 

02_07_2016.png

Edited by Seerose
  • Upvote 3

Share this post


Link to post
Share on other sites

Thanks, Maximilian! I'll add a link in my original comment.

 

Did you replace the angle control with a double or integer slider, or did you use the angle control with the 45° default?

Share this post


Link to post
Share on other sites

Super nice, Eli! appl.gif

 

Thanks, Maximilian! I'll add a link in my original comment.

 

Did you replace the angle control with a double or integer slider, or did you use the angle control with the 45° default?

 

A double slider it is.

How can I use an angle control in the old Code Lab?

 

Edit 1: I've just discovered how to do it. Today I've learned something new (new for a non-professional coder, that is). I've updated my post above so that the 3.5.11 version also features an angle control, as I didn't want to alter the original interface so much, and also fixed a little typo I had made.

 

Edit 2: I've updated my post above to fix a little typo I had made, and switched back to the double slider control for the angle in order to preserve the original spirit of the plugin which is to offset horizontal lines.

Edited by Maximilian

Share this post


Link to post
Share on other sites

The double slider is, I think, the best solution. A double slider isn't as elegant as an angle control, but isn't functionally much worse.  I'm very happy BoltBait eliminated the old 45° restriction, which really reduced the usefulness of angle controls.

Share this post


Link to post
Share on other sites

Oops, hadn't spotted your post while I was doing my edits, MJW... Hmm, should I better switch back to the double slider then?

Share this post


Link to post
Share on other sites

I probably would, because I don't think 45° is the best default. On the other hand, I may be looking at the plugin with my original purpose in mind, which was to offset horizontal stripes, with the angle control just added as a bonus. Perhaps to others, 45° is as logical a default as any. It's up to you. Either would be perfectly usable.

Share this post


Link to post
Share on other sites

I've decided to switch back to the double slider so as to preserve the original 0 degrees default. That way we're all seeing the same defaults at startup.

Share this post


Link to post
Share on other sites

Thank you!  MJW. 

Very interesting effect.

 

@Eli!

 

Fine !!! Very impressive, very good work.   :cookie-chocolate:  :cake:

Share this post


Link to post
Share on other sites

Many thanks for the latest Plugin @MJW :cake: .  A new toy to play with ;) .

 

LqPLOZh.png

 

 

 

 

 

 

  • Upvote 5

Share this post


Link to post
Share on other sites

To my considerable annoyance, I found another bug. With a -90° angle, the stripes are shifted half a pixel right. As soon as have time to figure out the best way to fix it, I will.

 

EDIT: Actually, it was a full pixel from what I expected, not half a pixel, but as I explain in a later comment, it isn't a bug.

Share this post


Link to post
Share on other sites

I was wrong about there being a bug for -90°. It works exactly as it's supposed to, just not how I expected. When rotated -90°, the leftmost column is differently shifted than the second column. I thought that was an error, but it's not. For that rotation, the leftmost column is x=0, so it should be an odd stripe  (since the stripes are numbered from 1), while the next column is x=-1, which is the last column of an even stripe. The Phase control can be used the shift the starting point left.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now